CodeNavi 中代码函数的节点和节点属性

举报
Uncle_Tom 发表于 2024/07/13 19:19:47 2024/07/13
【摘要】 CodeNavi 是一种用于编写静态检查规则的DSL语言。本篇将介绍 CodeNavi 中代码里函数、类/接口声明、注解,以及注释的节点和节点属性。

1. 前期回顾

1.1. 《寻找适合编写静态分析规则的语言》

根据代码检查中的一些痛点,提出了希望寻找一种适合编写静态分析规则的语言。

  • 可以满足用户对代码检查不断增加的各种需求;
  • 使用户能够通过增加或减少对检查约束条件的控制,实现快速调整检查中出现的误报和漏报;
  • 这种检查语言能够有较低的使用门槛,使用户更专注于检查业务,而不需要关注工具是如何实现的。

我们称这种检查规则语言为:CodeNavi。文中给出了这种检查规则语言的两个应用场景,以此来说明这种检查规则语言如何满足用户在编写静态检查规则时的期望。

1.2. 《CodeNavi 规则的语法结构》

介绍 CodeNavi 检查规则语言在编写规则的基本语法格式。CodeNavi 检查规则语言,通过对代码节点和节点属性的逻辑条件的组合来完成检查的约束条件,从而完成代码检查中需要满足的缺陷模式适配,找到满足要求的代码点。

1.3. 《CodeNavi 规则的基础节点和节点属性》

介绍 CodeNavi 检查规则语言在编写时需要使用基础节点和节点属性,这些基础节点包括:节点类型、字面量、一般变量、枚举、成员变量(字段),以及数组。

1.4. 《CodeNavi 中代码表达式的节点和节点属性》

介绍 CodeNavi 检查规则语言如何描述代码中的表达式。这些节点主要包括:对象创建表达式、强制类型转换、类型判断表达式、一元表达式、二元表达式、条件表达式/三目运算、方法引用表达式、lambda表达式,以及匿名内部类表达式。

1.5. CodeNavi 中代码语句的节点和节点属性

介绍 CodeNavi 中代码里语句的节点和节点属性。

  • 语句节点包括:赋值语句,
  • 控制流语句包括:跳转语句、条件控制、Switch控制、循环控制、异常处理、静态语句、同步代码块。

1.6. 本篇概要

介绍 CodeNavi 中代码里函数、类/接口声明、注解,以及注释的节点和节点属性。

2. CodeNavi 中的节点和节点属性

程序是由空格分隔的字符串组成的序列。在程序分析中,这一个个的字符串被称为"token",是源代码中的最小语法单位,是构成编程语言语法的基本元素。

Token可以分为多种类型,常见的有关键字(如if、while)、标识符(变量名、函数名)、字面量(如数字、字符串)、运算符(如+、-、*、/)、分隔符(如逗号,、分号;)等。

我们只需要给代码的不同节点给出一个定义,然后通过条件语句来描述对这些节点的要求,使之符合缺陷检查的模式,就可以完成检查规则的定义。

2.1. CodeNavi中的节点和节点属性

程序是由空格分隔的字符串组成的序列。在程序分析中,这一个个的字符串被称为"token",是源代码中的最小语法单位,是构成编程语言语法的基本元素。

Token可以分为多种类型,常见的有关键字(如if、while)、标识符(变量名、函数名)、字面量(如数字、字符串)、运算符(如+、-、*、/)、分隔符(如逗号,、分号;)等。

我们只需要给代码的不同节点给出一个定义,然后通过条件语句来描述对这些节点的要求,使之符合缺陷检查的模式,就可以完成检查规则的定义。

2.2. 节点

  • 图例

  • 节点和子节点都使用个图例

  • 规则语言中使用节点的 “英文名”,这样便于规则的编写。

2.3. 节点集

  • 图例

  • 节点的集合。

2.4. 属性

  • 图例

  • 规则语言中使用属性的 “英文名”,这样便于规则的编写。

3. 函数

在程序设计中,函数是一段封装好的代码块,它可以接受输入(参数),执行特定的任务,并可能返回结果(返回值)。

以下是函数的一些主要作用:

  • 模块化
    函数将程序分解成更小的、更易于管理和理解的部分。

  • 参数化
    函数可以接受不同的参数,使得同一段代码能够处理不同的数据。

  • 错误隔离
    将特定功能封装在函数中,有助于隔离错误,当函数出错时,不会影响到程序的其他部分。

  • 代码复用
    通过将重复使用的代码封装成函数,可以避免代码冗余,提高代码的可维护性。

  • 抽象
    函数隐藏了实现细节,只暴露接口,使得调用者无需了解函数内部的实现即可使用它。

  • 多态性
    在支持面向对象编程的语言中,函数(方法)可以展示多态性,即同一个函数名可以有不同的实现,具体使用哪个实现取决于调用它的对象类型。

  • 可维护性
    当需要修改某个功能时,只需修改对应的函数,而不必在程序的多个地方进行更改。

  • 可测试性
    函数可以独立于程序的其他部分进行测试,这有助于确保代码的正确性。

3.1. 函数声明(functionDeclaration)

  • 代码样例
public static String sayHello(String name) {
    // 注释
    Sting say = "Hello "+ name;
    System.out.println(say);
    return say;
}
  • 节点和属性结构

3.1.1. 函数声明基本信息

  • 节点属性
名称 描述 值类型 示例 DSL 规则
name 方法名 字符串 public static void debugFirst() {} functionDeclaration fd where
  fd.name == “debugFirst”;
parameters 参数节点集 parameterDeclaration节点集合 public void loopBlocks_for(boolean flag) {} functionDeclaration fd where
  fd.parameters contain pa where
    pa.name == “flag”;
type 返回值的类型 objectType节点 public static String sayHello() {
    return “hello”;
}
functionDeclaration fd where
  fd.type.name == “java.lang.String”;

3.1.2. 函数声明体相关的信息

  • 节点属性
名称 描述 值类型 示例 DSL 规则
firstStatement 方法的第一个完整的语句 任意节点 public String functionCase() {
  Boolean result = validNull();
  return “”;
}
functionDeclaration fd where
  fd.firstStatement contain variableDeclaration;
lastStatement 方法的最后一个完整的语句 任意节点 public String functionCase() {
  Boolean result = validNull();
  return “”;
}
functionDeclaration fd where
  fd.lastStatement contain returnStatement;
assignStatements 赋值语句集 assignStatement节点集合 public void functionCalls_first_argument() {
  String s = “hello”;

  StringBuilder sb = new StringBuilder("");
}
functionDeclaration fd where fd.assignStatements.size() == 2;
returnStatements 返回语句集 returnStatement节点集合 public int returnStatements_return_int(int m) {
  if (m > 10) {
    if (m < 100) {
      return randomInt();
    }
  }
  return 0;
}
functionDeclaration fd where
  fd.returnStatements contain rs where
    rs in ifBlock;
comments 方法的注释 comments节点 public static String sayHello() {
  // 注释
}
functionDeclaration fd where
   fd.comments contain lineComment;
ifBlocks if集 ifBlock节点集合 public void loopBlocks_for(boolean flag) {
  if (flag) {
    for (int i = 0; i < 10; i++) {
      System.out.println(i);
    }
  }
}
functionDeclaration fd where
  fd.ifBlocks contain ib where
    ib contain forBlock;
loopBlocks 循环集 loopBlock节点集合 public void loopBlocks_while(boolean flag) {
  int m = 10;
  while (m > 0) {
    m–;
  }
}
functionDeclaration fd where
  fd.loopBlocks contain ib where
    ib is whileBlock;
fieldAccesses 成员变量访问集 fieldAccesses节点集合 public void fieldAccesses_boolean() {
  System.out.println(this.testFlag);
}
functionDeclaration fd where
  fd.fieldAccesses contain fa where
    fa.name == “testFlag”;
variableAccesses 变量访问集 variableAccess节点集合 public void loopBlocks_while(boolean flag) {
  int m = 10;
  while (m > 0) {
    m–;
  }
}
functionDeclaration fd where
  fd.variableAccesses contain va where
    va.name == “m”;
functionCalls 函数调用集 functionCall节点集合 public void assignStatements_function_call_to_variable() {
  boolean result = “hello”.equals(“no”);
  System.out.println(result);
}
functionDeclaration fd where
  fd.functionCalls contain fc where
    fc.name == “equals”;

3.1.3. 函数继承、扩展的信息

  • 节点属性
名称 描述 值类型 示例 DSL 规则
subMethods 方法重写的子类方法集 functionDecalration节点集合 public class FatherDemo {
  public void fatherDemoMethod() {
  // 告警行
  System.out.println(“father class”);
}

  public void demoMethod() {
  }
}

public class SonDemo extends FatherDemo {
  public void fatherDemoMethod() {
    System.out.println(“Son class”);
  }
}
functionDeclaration fd where
  and(
    fd.name == “fatherDemoMethod”,
    fd.subMethods contain fd2 where
      fd2.name == “fatherDemoMethod”
  );
superMethods 方法重写的父类方法集 functionDecalration节点集合 public class FatherDemo {
  public void fatherDemoMethod() {
    // 告警行
    System.out.println(“father class”);
  }

  public void demoMethod() {
  }
}

br>public class SonDemo extends FatherDemo {
  public void fatherDemoMethod() {
    System.out.println(“Son class”);
  }
}
functionDeclaration fd where
   and(
    fd.name == “fatherDemoMethod”,
    fd.superMethods contain fd2 where
    fd2.name == “fatherDemoMethod”
);

3.1.4. 函数调用信息

  • 节点属性
名称 描述 值类型 示例 DSL 规则
functionUsages 调用方法的位置集 functionCall节点集合 public static void debugFirst() {}

public void debugSecond(int param) {
  FunctionTest.debugFirst();
}
functionDeclaration fd where and(
  fd.name == “debugFirst”,
  fd.functionUsages contain fc where
    fc.enclosingFunctionName == “debugSecond”
);
callers 调用方法的方法声明集 functionDeclaration节点集合 public static void debugFirst() {}

public void debugSecond(int param) {
  FunctionTest.debugFirst();
}
functionDeclaration fd where and(
  fd.name == “debugFirst”,
  fd.callers contain cc where cc.name == “debugSecond”
);

3.2. 函数入参(argument)

  • 图例

  • 节点属性

名称 描述 值类型 示例 DSL 规则
name 参数名 字符串 System.out.println(i) argument arg where arg.name == “i”;
argumentIndex 参数的index 数值 System.out.println(i) argument arg where arg.argumentIndex == 0;
argumentHolder 使用该参数的方法调用 functionCall节点 System.out.println(i) argument arg where arg.argumentHolder.name == “println”;

3.3. 方法调用(functionCall)

  • 图例

  • 节点属性

名称 描述 值类型 示例 DSL 规则
name 方法名 字符串 Objects.isNull(str); functionCall fc where
  fc.name == “isNull”;
function 调用的方法 functionDeclaration节点 public String functionCase() {
  Boolean result = validNull();
    return “”;
  }
  public Boolean validNull() {
    return false;
  }
}
functionCall fc where fc.function fd where and(
  fd is functionDeclaration,
  fd.name == “validNull”,
  fd.returnStatements contain rs where
    rs.returnValue.value == false
  );
possibleFunctions 调用的方法的所有方法。调用的方法及其重写与被重写的方法。
多态类创建对象时,声明类型为父类,实例化类型为子类型,调用对象方法时function属性应定位到子类的方法中。
functionDeclaration节点 public static void main(String[] args) {
  Son sg = new GrandSon();
  sg.run();  //父类和子类都有这个方法
}
functionCall fc2 where and(
  fc2.base.name == “sg”,
  fc2.possibleFuncs contain fun where and(
  fun.enclosingClass.name endWith “GrandSon”,
  fun contain stringLiteral sl1 where
  sl1.value == “Grandson is running”
  )
);
base 直接调用者 valueAccess类节点、functionCall节点等 MyRandom.aa(); functionCall fc where and(
  fc.name == “aa”,
  fc.base.name == “MyRandom”
);
rootBase 根调用者 valueAccess类节点、functionCall节点等 a().b().c(); functionCall fc where and(
  fc.name == “c”,
  fc.rootBase is functionCall,
  fc.rootBase.name == “a”
);
arguments 参数集 valueAccess类节点、functionCall节点等的集合 sb.append(s.toUpperCase(Locale.ENGLISH)); functionCall fc where fc.arguments contain arg where
  and(
  arg is functionCall,
  arg.name == “toUpperCase”,
  arg.arguments contain fieldAccess
  );
arguments[n] 第n个入参 valueAccess类节点、functionCall节点等的集合 add(a, b); functionCall fc where
  fc.arguments[1].name == “b”;

4. 类/接口声明(recordDeclaration)

类(Class)和接口(Interface)是面向对象编程(OOP)的两个基本构件,它们各自有着不同的作用和特点:

  • 类(Class)

    • 定义数据结构
      类可以定义一组属性(变量)和方法(函数),这些属性和方法共同描述了一个对象的状态和行为。

    • 创建对象
      通过类可以实例化对象,每个对象都是类的一个实例,拥有自己的状态和行为。

    • 封装
      类可以将数据和操作数据的方法组合在一起,并隐藏内部实现细节,只提供必要的接口给外部访问。

    • 继承
      类可以继承其他类的属性和方法,实现代码复用和层次结构的建立。

    • 多态
      类可以通过重写(Override)父类的方法实现多态,使得同一个接口可以有不同的实现。

    • 实现功能
      类可以包含业务逻辑和功能实现,是程序功能的具体体现。

  • 接口(Interface)

    • 定义规范
      接口定义了一组方法规范,但不实现这些方法,它规定了实现类必须遵循的契约。

    • 实现多态
      接口允许不同的类实现同一个接口,但可以有不同的实现方式,这是多态的另一种体现。

    • 解耦
      接口作为类之间的契约,可以降低类之间的耦合度,提高系统的灵活性和可扩展性。

    • 扩展性
      通过接口,可以在不修改现有代码的情况下引入新功能,只需实现相应的接口即可。

    • 规范约束
      接口可以作为规范约束,确保实现类遵循一定的设计原则和编程模式。

  • 样例代码

// 类的定义
class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }

    public void eat() {
        System.out.println(name + " is eating.");
    }
}

// 接口的定义
interface Flyable {
    void fly();
}

// 实现接口的类
class Bird implements Flyable {
    private String name;

    public Bird(String name) {
        this.name = name;
    }

    @Override
    public void fly() {
        System.out.println(name + " is flying.");
    }
}
  • 图例

  • 节点属性

名称 描述 值类型 示例 DSL 规则
name 包名/类名 字符串 package com.dsl.sourcefile.record;

public class TypeTest {
}
recordDeclaration rd where
  rd.name == “com.dsl.sourcefile.record.TypeTest”;
comments 注释集 comment节点集合 // 注释
public class TypeTest {
}
recordDeclaration rd where
  rd.comments contain lineComment;
kind 类类型,可能是interface或class 字符串 public class TypeTest {
}
recordDeclaration rd where
  rd.kind == “class”;
superTypes 父类和接口集 recordDeclartion节点集合 class MyList extends ArrayList<String> {}

class SecondMyList extends MyList {}
recordDeclaration rd where
  rd.superTypes contain sType where
    sType.name endWith “ArrayList”;
directSuperTypes 父类和接口的直接集 recordDeclartion节点集合 class MyList extends ArrayList<String> {}

class SecondMyList extends MyList {}
recordDeclaration rd where
  rd.directSuperTypes contain sType where
    sType.name endWith “ArrayList”;
definedFields 字段集 fieldDeclaration节点集合 class RecordDeclarationDemo extends Father {
  private static int COUNT = 0;
}
recordDeclaration rd where
  rd.definedFields.size() == 1;
definedConstructors 构造方法集 functionCall节点集合 class RecordDeclarationDemo extends Father {
  private static int COUNT = 0;
  RecordDeclarationDemo() {}
}
recordDeclaration rd where
  rd.definedConstructors.size() == 1;
definedMethods 方法集 functionCall节点集合 class RecordDeclarationDemo extends Father {
  private static int COUNT = 0;
  RecordDeclarationDemo() {}
  public void recordDeclarationMethod01() {}
}
recordDeclaration rd where
  and(
    rd.definedMethods.size() == 1,
    rd.definedMethods contain fd where
      fd.name == “recordDeclarationMethod01”
  );

5. 注解(annotation)

注解(Annotation)是一种特殊的标记,它为我们提供了一种元数据(metadata)的方式,可以用于类、方法、变量、参数以及包等元素上。注解不会直接影响程序的运行,但它们可以被编译器或运行时环境用来生成额外的代码或者改变代码的行为。

注解提供了一种灵活的方式来扩展语言的能力,而不需要修改语言本身。

以下是注解的一些主要作用:

  • 提供元数据
    注解可以为代码提供额外的信息,这些信息可以被编译器、运行时环境或其他工具使用。

  • 编译时检查
    某些注解可以用来指示编译器执行特定的检查,例如@Override注解确保被注解的方法确实是覆盖了父类中的方法。

  • 代码生成
    注解可以用于代码生成,例如使用@Entity注解的类可以由JPA(Java Persistence API)提供者用来生成数据库表。

  • 依赖注入
    在依赖注入框架中,注解用于标记需要被框架自动注入的对象,如Google Guice的@Inject。

  • 配置框架
    许多现代框架使用注解来配置框架的行为,如Spring的@Controller、@Service、@Repository等。

  • 安全性
    注解可以用于标记安全相关的代码,如Java EE中的@RolesAllowed注解,用来定义哪些角色可以访问特定的方法。

  • 运行时处理
    有些注解可以在程序运行时被框架或库读取,并根据注解信息改变程序的行为,如Spring框架中的@Autowired。

  • 测试支持
    注解可以用于测试框架中,例如JUnit的@Test、@Before、@After等,用来标记测试方法和测试前的准备工作。

  • 代码样例

public class Test extends ArrayList {
    @TheAnnotation(isOk = false, num = 3, str = Test.test1)
    public void functionWithAnnotation() {

    }
}

  • 图例

  • 节点属性

名称 描述 值类型 示例 DSL 规则
name 名称 字符串 @TheAnnotation(isOk = false) annotation ann where ann.name == “TheAnnotation”;
annoMembers 注解成员集 annomember节点集合 @TheAnnotation(isOk = false, num = 3, str = Test.test1) annotation ann where
  ann.annoMembers contain am where
    am.name == “isOk”;

5.1. 注解项(annoMember)

  • 节点属性
名称 描述 值类型 示例 DSL规则
name 名称 字符串 @TheAnnotation(isOk = false, num = 3, str = Test.test1) annotation ann where
  ann.annoMembers contain am where
    am.name == “str”;
annoValue 注解项的值 literal、enumConstantAccess、fieldAcccess节点 @TheAnnotation(isOk = false, num = 3, str = Test.test1) annotation ann where
  ann.annoMembers contain am where
    am.annoValue amv where
      and(
  amv is fieldAccess,
  amv.name == “test1”
      );

6. 注释

在程序中,注释是一个非常重要的组成部分,它对代码的可读性、可维护性和团队协作有着重要的作用。

以下是注释的一些主要作用:

  • 解释代码
    注释可以解释复杂的代码段或算法,帮助其他开发者或未来的自己理解代码的意图。

  • 标记TODO
    开发者可以在代码中使用注释标记需要完成的任务(TODO),以便后续进行跟进。

  • 法律声明
    在软件的版权声明或许可证声明中,注释用来提供法律信息。

  • 版本控制信息
    在某些情况下,注释可以用来记录代码的版本信息或与版本控制系统中的提交相关联。

  • 配置信息
    在配置文件或脚本中,注释可以解释配置选项的作用。

  • 提醒注意事项
    在代码中可能存在一些特殊情况或陷阱,注释可以用来提醒开发者注意这些细节。

  • 记录变更
    在代码修改时,注释可以用来记录变更的原因和背景,方便追踪历史和理解变更的动机。

  • 提供示例
    在某些情况下,注释可以提供代码使用的示例,尤其是公共API或库的文档中。

  • 文档生成
    某些注释(如JavaDoc)可以被工具自动解析,生成API文档或其他形式的文档。

  • 提高代码可读性
    良好的注释可以使代码更加易于阅读和理解,尤其是对于复杂的逻辑或不常见的实现。

6.1. 注释(comment)

  • 代码样例
// function description

/* function description */
  • 图例

  • 节点属性

名称 描述 值类型 示例 DSL 规则
content 注释的内容 字符串 // function declaration
public void getInstance(String des) {}
comment cm where cm.content match “.*”;
commentedNode 被注释的节点 节点 // function declaration
public void getInstance(String des) {}
lineComment cm where
  cm.commentedNode is functionDeclaration;

6.2. 行注释(lineComment)

  • 代码样例
// function description
public void getInstance(String des) {
      // todosomething
}
  • 图例

  • 节点属性

名称 描述 值类型 示例 DSL 规则
content 注释的内容 字符串 // function declaration
public void getInstance(String des) {}
comment cm where cm.content match “.function.”;
commentedNode 被注释的节点 节点 // function declaration
public void getInstance(String des) {}
lineComment cm where cm.commentedNode is functionDeclaration;

6.3. 块注释(blockComment)

  • 代码样例
/* function description */
public void getInstance(String des) {
      // todosomething
}
  • 图例

  • 节点属性

名称 描述 值类型 示例 DSL 规则
content 注释的内容 字符串 /* block comment */ blockComment bc where bc.content contain “block comment”;
commentedNode 被注释的节点 节点 /* block comment */
int a = 2;
blockComment bc where bc.commentedNode contain variableDeclaration;

6.4. javadoc注释(javadocComment)

  • 代码样例
/**
* 功能描述
*
* @since 2023-03-20
*/
public class EncryptCommon {
      // todosomething
}
  • 图例

  • 节点属性

名称 描述 值类型 示例 DSL 规则
content 注释的内容 字符串 /**
* 功能描述
*
* @since 2023-03-20
*/
public class EncryptCommon {}
javadocComment jd where jd.content contain “@since”;
commentedNode 被注释的节点 节点 /**
* 功能描述
*
* @since 2023-03-20
*/
public class EncryptCommon {}
javadocComment jd where
  jd.commentedNode is recordDeclaration;

7. CodeNavi插件

8. CodeNavi 介绍:

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。