不执行也不加载代码,只看语法树就能统计复杂度、找出方法调用、定位源码位置。这篇用 JDK 自带的 Tree API 对一段真实的 Java 代码做分析,看 parse 与 analyze 各自能得到什么,以及语法树里没有什么。
包 = demo,import 1 个,顶层类型 1 个 方法 take 返回 boolean 参数 [String sku, int n] 第 7–13 行,修饰符 [public] 方法 sum 返回 int 参数 [List<Integer> xs] 第 14–18 行,修饰符 [] 方法 kind 返回 String 参数 [Object o] 第 19–21 行,修饰符 [] 方法 run 返回 void 参数 [Runnable r] 第 22–22 行,修饰符 [] 方法 go 返回 void 参数 [] 第 23–23 行,修饰符 [] 语法树节点总数 184,种类 41 种;其中 IF 4,METHOD_INVOCATION 8,LAMBDA_EXPRESSION 1,MEMBER_REFERENCE 1,SWITCH_EXPRESSION 1,BINDING_PATTERN 3,CONDITIONAL_EXPRESSION 1 take 方法第一条语句:Tree.toString() = "if (n <= 0) return false;",按源码位置截取 = "if (n <= 0) return false;"
方法 take 圈复杂度(1 + if/for/?:/case/&&/||)= 4 方法 sum 圈复杂度(1 + if/for/?:/case/&&/||)= 6 方法 kind 圈复杂度(1 + if/for/?:/case/&&/||)= 5 方法 run 圈复杂度(1 + if/for/?:/case/&&/||)= 1 方法 go 圈复杂度(1 + if/for/?:/case/&&/||)= 1 所有方法调用及行号:[stock.get @第9行, System.out.println @第10行, stock.put @第11行, xs.isEmpty @第17行, r.run @第22行, run @第23行, System.out.println @第23行, run @第23行] 其中 System.out.println 调用 2 处 stock.get(...) 的源码字符区间 [263,277) = "stock.get(sku)",第 9 行第 24 列
类 Target 的文档注释:订单服务。 方法 take 的文档注释:扣库存:不足时返回 false。 方法 sum 的文档注释:(无) 方法 kind 的文档注释:(无) 方法 run 的文档注释:(无) 方法 go 的文档注释:(无) 语法树里有没有普通的 // 注释:无。仅 /** */ 文档注释能经 DocTrees 取到。 语义错误(未定义变量、类型不匹配):只做 parse() → 类型声明 1 个,诊断 0 条[] 语法错误(缺括号与分号):只做 parse() → 类型声明 1 个,诊断 2 条[ [1:18 compiler.err.illegal.start.of.type], [1:28 compiler.err.expected]] 同一段语义错误代码再 analyze():诊断 2 条[ [1:28 compiler.err.cant.resolve.location], [1:69 compiler.err.prob.found.req]]
变量 stock 声明类型 Map<String, Integer> → 类型镜像 java.util.Map<java.lang.String,java.lang.Integer>(种类 DECLARED)
变量 have 声明类型 Integer → 类型镜像 java.lang.Integer(种类 DECLARED)
stock.get 解析到 java.util.Map.get(java.lang.Object),返回类型 V,所属类型种类 INTERFACE
变量 xs 声明类型 List<Integer> → 类型镜像 java.util.List<java.lang.Integer>(种类 DECLARED)
变量 s 声明类型 int → 类型镜像 int(种类 INT)
xs.isEmpty 解析到 java.util.List.isEmpty(),返回类型 boolean,所属类型种类 INTERFACE
变量 s 声明类型 String → 类型镜像 java.lang.String(种类 DECLARED)
r.run 解析到 java.lang.Runnable.run(),返回类型 void,所属类型种类 INTERFACE
lambda () -> println("hi") 的目标类型 = java.lang.Runnable
方法引用 this::go 的目标类型 = java.lang.Runnable
同一段 24 行代码(新建任务):parse 2.4 ms,analyze 22.8 ms