本文分享自华为云社区《CodeNavi 中代码表达式的节点和节点属性》,作者: Uncle_Tom。
根据代码检查中的一些痛点,提出了盼望探求一种适合编写静态分析规则的语言。
- 可以满足用户对代码检查不绝增长的各种需求;
- 使用户可以大概通过增长或淘汰对检查约束条件的控制,实现快速调整检查中出现的误报和漏报;
- 这种检查语言可以大概有较低的使用门槛,使用户更专注于检查业务,而不需要关注工具是怎样实现的。
我们称这种检查规则语言为:CodeNavi。本文将继续介绍 CodeNavi 检查规则语言怎样描述代码中的表达式。这些节点重要包括:
- 3.1. 对象创建表达式(objectCreationExpression)
- 3.2. 强制类型转换(castExpression)
- 3.3. 类型判断表达式(instanceofExpression)
- 3.4. 一元表达式(unaryOperation)
- 3.5. 二元表达式(binaryOperation)
- 3.6. 条件表达式/三目运算(ternaryOperation)
- 3.7. 方法引用表达式(methodReferenceExpression)
- 3.8. lambda表达式(lambdaExpression)
- 3.9. 匿名内部类表达式(anonymousInnerClassExpression)
2. CodeNavi 中的节点和节点属性
程序是由空格分隔的字符串组成的序列。在程序分析中,这一个个的字符串被称为"token",是源代码中的最小语法单位,是构成编程语言语法的根本元素。
Token可以分为多种类型,常见的有关键字(如if、while)、标识符(变量名、函数名)、字面量(如数字、字符串)、运算符(如+、-、*、/)、分隔符(如逗号,、分号;)等。
我们只需要给代码的差别节点给出一个定义,然后通过条件语句来描述对这些节点的要求,使之符合缺陷检查的模式,就可以完成检查规则的定义。
2.1. 规则节点和节点属性图例
2.1.1. 节点
图例
节点和子节点都使用个图例
规则语言中使用节点的 “英文名”,这样便于规则的编写。
2.2. 节点集
图例
节点的聚集。
2.3. 属性
图例
规则语言中使用属性的 “英文名”,这样便于规则的编写。
3. 表达式
表达式是程序中用于产生一个值或执行一个操作的代码片段。表达式可以简单到只有一个字面量或变量,也可以复杂到包含多个操作符和子表达式。
表达式在程序中的作用非常广泛,它们是程序逻辑和数据处置惩罚的基础。表达式是构成程序的根本元素之一,它用于表现数据的计算或操作, 资助开发者构建程序的逻辑和功能。
以下是表达式的一些重要作用:
- 产生值,表达式的重要作用是产生一个值。例如,5 + 3 产生值 8。
- 算术运算,表达式可以执行根本的算术运算,如加(+)、减(-)、乘(*)、除(/)、模(%)。
- 关系运算,表达式可以进行比较,产生布尔值(true或false)。例如,5 > 3 产生 true。
- 逻辑运算,表达式可以进行逻辑运算,如逻辑与(&&)、逻辑或(||)、逻辑非(!)。
- 条件运算,三元条件运算符(? :)答应基于条件表达式的结果选择两个值中的一个。例如,max = (a > b) ? a : b;。
- 创建数组,表达式可以用于声明和初始化数组。例如,new int[10];。
- 创建对象,表达式可以用于创建对象实例。例如,new Integer(10)。
- 类型转换,表达式可以进行类型转换,将一个类型的值转换为另一个类型。例如,(int) 3.14。
- 创建和使用Lambda表达式,Lambda表达式,答应以简洁的语法表现匿名函数。例如,() -> System.out.println(“Hello”);。
- 控制流语句,表达式的结果可以用于控制流语句,如if、while、for等。
- 异常处置惩罚,表达式可以与异常处置惩罚结合使用,如在try-catch块中。
3.1. 对象创建表达式(objectCreationExpression)
表达式用于创建对象实例。
代码样例图例
名称描述值类型示例DSL 规则name方法名字符串new MethodRefTypeTest();objectCreationExpression obc where
obc.name == “MethodRefTypeTest”;type返回值类型objectType节点new MethodRefTypeTest();objectCreationExpression obc where
obc.type.name == “com.huawei.secbrella.kirin.test.sourcefile.methodRefType.MethodRefTypeTest”;function调用的构造方法functionDeclaration节点public class MethodRefTypeTest {
MethodRefTypeTest() {}
public static void main(String[] args) {
new MethodRefTypeTest();
}objectCreationExpression obc where
obc.function.name == “MethodRefTypeTest”;arguments入参聚集valueAccess类节点、functionCall节点等的聚集new String(“Hello”);objectCreationExpression obc where
obc.arguments.size() == 1;arguments[n]第n个入参valueAccess类节点、functionCall节点等的聚集new String(“Hello”);objectCreationExpression obc where
obc.arguments[0].value == “Hello”;3.2. 强制类型转换(castExpression)
类型强制转换(Type Casting)是指将一个类型的对象转换成另一个类型的对象。
代码样例- double d = 10.5;
- // 强制转换,需要显式指定目标类型
- int i = (int) d;
- Object obj = new String("Hello");
- // 安全的向下转型
- String str = (String) obj;
复制代码 图例
名称描述值类型示例DSL 规则castType目标类型nodeArrayList second = (ArrayList) list;castExpression ce where
ce.castType.name == “java.util.ArrayList”;operand操作对象nodeArrayList second = (ArrayList) list;castExpression ce where
ce.operand.name == “list”;3.3. 类型判断表达式(instanceofExpression)
instanceof 关键字用于检查一个对象是否是特定类的实例大概是其子类的实例。
代码样例- if (animal instanceof Dog) {
- ((Dog) animal).makeSound();
- }
- Object[] objects = new Object[10];
- if (objects instanceof Object[]) {
- System.out.println("objects is an array of Object");
- }
复制代码 图例
名称描述值类型示例DSL 规则lhs左值任意节点father instanceof FatherinstanceofExpression ie where
ie.lhs.name == “father”;rhs右值,类型值全类名常量list instanceof ListinstanceofExpression ie where
ie.rhs.name == “java.util.List”;3.4. 一元表达式(unaryOperation)
一元表达式包含单个操作数。
代码样例- // 一元算术表达式
- // 递增:++x(将 x 的值增加 1)
- // 递减:--x(将 x 的值减少 1)
- // 前置递增/递减:
- int a = 5;
- int b = ++a; // a 和 b 都是 6
- int c = a--; // a 是 6, c 是 5
- // 后置递增/递减:
- int a = 5;
- int b = a++; // a 是 6, b 是 5
- int c = a--; // a 是 5, c 是 6
- // 一元逻辑表达式
- // 逻辑非:!x(如果 x 为 true,则结果为 false,反之亦然)
- boolean flag = true;
- boolean notFlag = !flag; // notFlag 是 false
- // 一元位运算表达式
- // 按位取反:~x(将 x 的每个位取反)
- int num = 5; // 二进制表示为 101
- int bitNotNum = ~num; // 结果是 -6, 二进制表示为 110...(32 个 1),因为整数溢出
复制代码 图例
名称描述值类型示例DSL 规则isPrefix是否运算符前置布尔值a++;unaryOperation uo where uo.isPrefix == false;operand操作对象任意节点i++;unaryOperation uo where uo.operand.name == “i”;operator运算符字符串i++;unaryOperation uo where uo.operator == “++”;3.5. 二元表达式(binaryOperation)
二元表达式包含两个操作数,而且返回一个单一的值。
代码样例- // 算术二元表达式
- // 加法:x + y
- // 减法:x - y
- // 乘法:x * y
- // 除法:x / y
- // 取模(求余数):x % y
- int a = 10;
- int b = 3;
- int sum = a + b; // 13
- int difference = a - b; // 7
- int product = a * b; // 30
- int quotient = a / b; // 3
- int remainder = a % b; // 1
- // 比较二元表达式
- // 等于:x == y
- // 不等于:x != y
- // 大于:x > y
- // 小于:x < y
- // 大于等于:x >= y
- // 小于等于:x <= y
- int x = 5;
- int y = 10;
- boolean isEqual = (x == y); // false
- boolean isNotEqual = (x != y); // true
- boolean isLessThan = (x < y); // true
- boolean isGreaterThan = (x > y); // false
- boolean isLessThanOrEqual = (x <= y); // true
- boolean isGreaterThanOrEqual = (x >= y); // false
- // 逻辑二元表达式
- // 逻辑与:x && y
- // 逻辑或:x || y
- // 逻辑异或:x ^ y
- // 条件与(&):x & y(当 x 为 true 时才判断 y)
- // 条件或(|):x | y(当 x 为 true 时,y 的结果将被忽略)
- boolean condition1 = true;
- boolean condition2 = false;
- boolean andResult = condition1 && condition2; // false
- boolean orResult = condition1 || condition2; // true
- boolean xorResult = condition1 ^ condition2; // true
- int num1 = 5;
- int num2 = 3;
- boolean bitAndResult = num1 & num2; // true, 因为 5 & 3 的二进制表示都是 101
- // 位运算二元表达式
- // 位与:x & y
- // 位或:x | y
- // 位异或:x ^ y
- // 位左移:x << n(将 x 的二进制表示向左移动 n 位)
- // 位右移(算术):x >> n(将 x 的二进制表示向右移动 n 位,右边用符号位填充)
- // 位右移(逻辑):x >>> n(将 x 的二进制表示向右移动 n 位,右边用0填充)
- int number = 5; // 在内存中的表示是 101
- int resultBitwiseAnd = number & 3; // 结果是 1, 因为 101 & 011 = 001
- int resultBitwiseOr = number | 3; // 结果是 7, 因为 101 | 011 = 111
- int resultBitwiseXor = number ^ 3; // 结果是 2, 因为 101 ^ 011 = 110
- int resultLeftShift = number << 1; // 结果是 10, 因为 101 向左移动一位变成 1010
- int resultRightShift = number >> 1; // 结果是 2, 因为 101 向右移动一位变成 10
- int resultRightShiftLogical = number >>> 1; // 结果是 2, 逻辑右移,忽略符号位
- // 赋值二元表达式
- // 简单赋值:x = y
- // 加等于:x += y
- // 减等于:x -= y
- // 乘等于:x *= y
- // 除等于:x /= y
- // 模等于:x %= y
- // 位与等于:x &= y
- // 位或等于:x |= y
- // 位异或等于:x ^= y
- // 位左移等于:x <<= n
- // 位右移等于:x >>= n
- // 位右移逻辑等于:x >>>= n
- int num = 5;
- num += 3; // num 现在是 8
- num *= 2; // num 现在是 16
- num /= 4; // num 现在是 4
- num %= 3; // num 现在是 1
复制代码 图例
名称描述值类型示例DSL 规则lhs二元表达式的左值literal类节点、valueAccess类节点、functionCall类节点int i = 1;binaryOperation bo where bo.lhs.name == “i”;operator二元表达式的操作符字符串while (i == 1)binaryOperation bo where bo.operator == “==”;rhs二元表达式的右值literal类节点、valueAccess类节点、functionCall类节点a > b;binaryOperation bo where bo.rhs.name == “b”;operands二元表达的操作对象(左值和右值)节点聚集a > b;binaryOperation bo where
bo.operands contain op where
op.name == “a”;3.6. 条件表达式/三目运算(ternaryOperation)
条件表达式,也称为三目运算符(Ternary Operator),是一种简洁的条件语句,格式如下:- result = condition ? value_if_true : value_if_false;
复制代码 这里的 condition 是一个布尔表达式,value_if_true 是当条件为 true 时的结果,而 value_if_false 是当条件为 false 时的结果。这个表达式的结果 result 将是两个值中的一个,取决于条件的真假。
样例代码- // 基本使用
- int a = 10;
- int b = 20;
- // max 将被赋值为 20,因为 20 大于 10
- int max = (a > b) ? a : b;
- // 嵌套使用
- int x = 5;
- // result 将是 "x 大于 5"
- String result = (x > 10) ? "x 大于 10" : ((x > 5) ? "x 大于 5" : "x 小于等于 5");
- // 与赋值结合
- int score = 85;
- // grade 将被赋值为 "B"
- String grade = (score >= 90) ? "A" : ((score >= 80) ? "B" : "C");
- // 用于方法调用
- // outcome 将随机是 "Heads" 或 "Tails"
- String outcome = (Math.random() > 0.5) ? "Heads" : "Tails";
- // 与循环结合
- for (int i = 0; i < 10; i++) {
- // 将打印出 0, -1, 4, -3, 等等
- int value = (i % 2 == 0) ? i * i : -i;
- System.out.println(value);
- }
复制代码 图例
名称描述值类型示例DSL 规则condition判断条件binaryOperationboolean res = num > 1 ? true : false;ternaryOperation tp where
tp.condition.lhs.name == “num”;thenExpression真的操作任意节点boolean res = num > 1 ? true : false;ternaryOperation tp where
tp.thenExpression contain literal ll where
ll.value == true;elseExpression假的操作任意节点String str = num > 1 ? “true” : “false”;ternaryOperation tp where
tp.elseExpression contain literal ll where
ll.value == “false”;3.7. 方法引用表达式(methodReferenceExpression)
方法引用(Method References),它是一种快捷的Lambda表达式,答应你直接引用已有方法或构造函数。方法引用通常用于实现简单的函数式接口。
样例代码- // 静态方法引用
- List<String> list = Arrays.asList("a", "b", "c");
- // System.out::println 是一个方法引用,它引用了 System.out 对象的 println 静态方法。
- list.forEach(System.out::println);
- // 实例方法引用
- // 当Lambda体需要调用一个实例方法时,第一个参数将成为方法的调用者:
- List<String> list = Arrays.asList("hello", "world");
- // String::toLowerCase 是一个方法引用,它引用了 String 类的 toLowerCase 实例方法。
- String result = list.stream()
- .map(String::toLowerCase)
- .collect(Collectors.joining(" "));
- // result 将是 "hello world"
- // 构造函数引用
- // StringBuilder::new 是一个构造函数引用,它引用了 StringBuilder 类的构造函数。
- Function<String, StringBuilder> constructor = StringBuilder::new;
- StringBuilder sb = constructor.apply("Hello World");
- // 特定类的任意对象的实例方法引用
- // 当Lambda的第一个参数是某个类的对象时,可以使用类名加 :: 加方法名的方式引用该类的实例方法:
- List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
- // upperCaseNames 包含了 "ALICE", "BOB", "CHARLIE"
- List<String> upperCaseNames = names.stream()
- .map(String::toUpperCase)
- .collect(Collectors.toList());
- // 数组的实例方法引用
- // 对于数组,可以使用数组类型加 ::new 来引用数组的构造方法:
- // String[]::new 是一个方法引用,它引用了 String[] 类型的构造方法。
- Function<Integer, String[]> creator = String[]::new;
- // 创建了一个长度为3的String数组
- String[] strings = creator.apply(3);
复制代码 图例
名称描述值类型示例DSL 规则name方法名字符串Supplier size = list::size;methodReferenceExpression mf where
mf.name == “size”;function调用的方法functionDeclaration节点Supplier size = list::size;methodReferenceExpression mf where
mf.function.name == “size”;type方法返回值类型objectType节点Supplier size = list::size;methodReferenceExpression mf where
mf.type.name == “int”;3.8. lambda表达式(lambdaExpression)
Lambda 表达式是一种简洁的匿名函数表达式,答应你将举动作为参数传递给方法或存储在变量中。Lambda 表达式提供了一种非常机动和强大的方式来处置惩罚函数式编程,使得代码更加简洁和表达性强。
样例代码- // 基本 Lambda 表达式
- // 这个 Lambda 表达式没有参数,执行的操作是打印 "Hello, World!"。
- () -> System.out.println("Hello, World!");
- // 带参数的 Lambda 表达式
- // 这个 Lambda 表达式接受两个参数 x 和 y,执行的操作是将它们相加。
- (x, y) -> x + y
- // 使用 Lambda 表达式实现 Runnable
- // 这里创建了一个 Runnable 实例,它将在调用 run 方法时执行 Lambda 表达式。
- Runnable runnable = () -> System.out.println("I'm running on a thread!");
- runnable.run();
- // 使用 Lambda 表达式排序集合
- // 在这个例子中,Lambda 表达式实现了 Comparator 接口,用于按字典顺序排序字符串。
- List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
- Collections.sort(names, (name1, name2) -> name1.compareTo(name2));
- // 使用 Lambda 表达式过滤集合
- // 这里使用 Lambda 表达式创建了一个流,过滤出长度小于 5 的名字。
- List<String> shortNames = names.stream()
- .filter(name -> name.length() < 5)
- .collect(Collectors.toList());
- // 输出集合中的单数
- list.forEach(integer -> {
- if (integer % 2 == 1) {
- System.out.println("单数");
- }
- });
- // 使用 Lambda 表达式转换集合
- // 在这个例子中,Lambda 表达式用于将每个名字转换为其长度。
- List<Integer> lengths = names.stream()
- .map(name -> name.length())
- .collect(Collectors.toList());
- // 使用 Lambda 表达式实现简单的函数
- // Lambda 表达式 name -> name.length() 实现了 Function 接口,用于返回字符串的长度。
- Function<String, Integer> lengthFunction = name -> name.length();
- int length = lengthFunction.apply("Hello");
- // 使用 Lambda 表达式实现消费者
- // Lambda 表达式 name -> System.out.println(name.toUpperCase()) 实现了 Consumer 接口,用于打印字符串的大写形式。
- Consumer<String> consumer = name -> System.out.println(name.toUpperCase());
- consumer.accept("Alice");
- // 使用 Lambda 表达式实现供应商
- // Lambda 表达式 () -> "Hello, Lambda!" 实现了 Supplier 接口,用于提供字符串。
- Supplier<String> supplier = () -> "Hello, Lambda!";
- String message = supplier.get();
复制代码 图例
名称描述值类型示例DSL 规则parameters参数paramDeclaration节点list.forEach(integer -> {
if (integer == 1) {
System.out.println(“单数”);
}
});lambdaExpression le where
le.parameters contain p where p.name == “integer”;body方法体block语句块list.forEach(integer -> {
if (integer == 1) {
System.out.println(“单数”);
}
});lambdaExpression le where
le.body contain variableAccess va where va.name == “integer”;body.statementNum语句数量数值list.forEach(integer -> {
if (integer == 1) {
System.out.println(“单数”);
}
});lambdaExpression le where
le.body.statementNum == 1;firstStatement第一条语句任意节点list.forEach(integer -> {
if (integer == 2) {
System.out.println(“单数”);
}
});lambdaExpression le where
le.firstStatement contain ifBlock;lastStatement末了一条语句任意节点list.forEach(integer -> {
if (integer == 3) {
System.out.println(“单数”);
}
System.out.print(“双数”);
});lambdaExpression le where
le.lastStatement contain functionCall fc where
fc.name == “print”;3.9. 匿名内部类表达式(anonymousInnerClassExpression)
匿名内部类是当需要创建一个仅用于一次使用的类时使用的一种特别类。它没有名称,而且通常是在声明的同时实例化。
匿名内部类提供了一种快速实现接口或继承类的方法,而无需定义一个具体的类名。这在创建一次性使用的类时非常有用,可以淘汰代码的冗余。
样例代码- // 作为方法参数
- // 调用方法时使用匿名内部类
- // 假设有一个方法需要一个 List 作为参数,我们可以直接在调用时创建一个匿名内部类:
- // 这里,我们创建了一个实现了 ArrayList 的匿名内部类,并在构造器中添加了元素。
- process(new ArrayList<String>() {
- {
- add("Item 1");
- add("Item 2");
- }
- });
- // 实现接口
- // 在这个例子中,我们创建了一个实现了 Runnable 接口的匿名内部类,并重写了 run 方法。
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("Hello from a thread!");
- }
- }).start();
- // 重写接口的多个方法
- // 使用 Comparator 进行排序
- // 在这个例子中,我们创建了一个实现了 Comparator 接口的匿名内部类,用于按字典顺序比较字符串。
- List<String> names = Arrays.asList("Bob", "Alice", "Eve");
- Collections.sort(names, new Comparator<String>() {
- @Override
- public int compare(String o1, String o2) {
- return o1.compareTo(o2);
- }
- });
- // 如果需要实现一个接口并重写多个方法,也可以使用匿名内部类:
- new SomeInterface() {
- @Override
- public void method1() {
- // 实现细节
- }
- @Override
- public void method2() {
- // 实现细节
- }
- };
- // 继承一个类
- // 匿名内部类也可以继承一个类,并重写其方法:
- // 在这个例子中,我们创建了一个继承自 SomeClass 的匿名内部类,并重写了 someMethod 方法。
- new SomeClass() {
- @Override
- public void someMethod() {
- // 重写方法
- }
- };
- new MemberClass() { // 告警行
- @Override
- public void f() {
- int i = 0;
- System.out.println("aaaaa");
- }
- }.f();
复制代码 图例
名称描述值类型示例DSL 规则name名字字符串public static void main(String[] args) {
new MemberClass() {
@Override
public void f() {
}
}.f();
}anonymousInnerClassExpression ac where and(
ac.name == “MemberClass”,
ac.enclosingFunctionName == “main”
);anonymousClassBody类代码块任意节点聚集public static void main(String[] args) {
new MemberClass() {
@Override
public void f() {
}
}.f();
}anonymousInnerClassExpression ac where
ac.anonymousClassBody ab contain functionDeclaration;4. CodeNavi插件
在Vscode 的插件中,查询:codenavi,并安装。
使用插件的链接安装: https://marketplace.visualstudio.com/items?itemName=HuaweiCloud.codenavi
点击关注,第一时间相识华为云新鲜技术~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |