首页 > 其他分享 >编写代码检查规则的神器,解读CodeNavi语法结构

编写代码检查规则的神器,解读CodeNavi语法结构

时间:2024-07-08 14:32:58浏览次数:12  
标签:运算符 语法结构 神器 CodeNavi fd 规则 字符串 where 节点

本文分享自华为云社区《CodeNavi 规则的语法结构》,作者: Uncle_Tom。

1. 代码和检查规则

1.1. 代码的构成

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

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

程序在编译过程中,词法分析器(Lexer)读取源代码并将其分解成一系列的token。语法分析器(Parser)会使用这些 token 来构建一个抽象语法树(Abstract Syntax Tree, AST),这个树结构表示了代码的语法结构。这个时候每个 token 也可以称为抽象语法树的节点,树上某个节点的分支就是这个节点的子节点。每个节点都会有节点类型、属性、值。

这些是程序员非常容易理解的。

1.2. 检查规则

检查规则是查找代码节点中符合缺陷模式的检查规则。

通常静态分析工具的规则编写采用程序语言,这样开发人员除了要了解检查的问题本省,还需要理解静态分析理论,以及静态分析工具的框架,无形中增加了开发的难度。所以我们一直期望寻找到一种更适合编写静态分析规则的语言,让开发人员能够更好的专注于理解检查问题的本身,而不需要花更多的时间在如何实现上。

《寻找适合编写静态分析规则的语言》中我们给出了两个问题的解决,来描述我们对编写静态分析规则的期望。

例如:

检查问题:

  • 生产环境中不应该有调试代码。

问题检查条件:

  • 查找所有函数声明
  • 并且(And):函数名以"debug"开头
  • 并且(And):函数只有一个参数
  • 并且(And):参数类型为"java.util.List"
问题代码样例
package com.dsl;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.List;

/**
 * 检查问题:生产环境中不应该有调试代码。
 * 问题检查条件:
 * - 查找所有函数声明;
 * - 并且(And):函数名以"debug"开头;
 * - 并且(And):函数只有一个参数;
 * - 并且(And):参数类型为"java.util.List"。
 */
public class CheckDebug {
    private static final Logger LOG = LogManager.getLogger(CheckDebug.class);

    // 应检查出的问题函数
    public void debugFunction(List<String> msgs) {
        for (String msg : msgs) {
            LOG.error("print debug info: {}", msg);
        }
    }
}

编写检查规则

DSL 写的检查规则

/**
 * 检查问题:生产环境中不应该有调试代码。
 * 问题检查条件:
 * - 查找所有函数声明;
 * - 并且(And):函数名以"debug"开头;
 * - 并且(And):函数只有一个参数;
 * - 并且(And):参数类型为"java.util.List"。
 */

functionDeclaration fd where
    and(
        fd.name startWith "debug",
        fd.parameters.size() == 1,
        fd.parameters[0].type.name == "java.util.List"
    );

2. 规则语法结构

下面我们进一步给出这种用于编写代码检查规则的工具 CodeNavi 的语法结构。

2.1. 规则的语法

结构图

语法说明

规则语句是以分号 (;) 结尾, 作为规则的终止符。

2.2. 语句结构

结构图

 

语法说明

节点(node): 为待检测代码节点(token);

as 别名:给要查找的节点一个别名,便于后面的使用。as 可以省略。别名的命名规则如下:

  • 只能包含大小写英文字母和数字;
  • 数字只能出现在末尾;
  • 不能和规则内置的节点或属性同名。
  • 例如:va1、fd、Field1都是合法的别名,1fc、v2a是非法别名。
查询条件(condition):筛选节点的条件;

2.3. 节点和节点属性

结构图

 

语法说明

  • 节点(node):是代码对应语法树的节点或子节点;
  • 属性(attribute):是代码对应语法树的节点或子节点的属性;
  • . : 节点、子节点、属性之间的连接符;

2.4. 条件表达式结构

结构图

语法说明

  • where: 条件表达式的标识;
  • and:括号内的查询条件必须都为真;
  • or:括号内的查询条件至少有一个为真;
  • not:括号内的查询条件为假;

逻辑的连接祠是一种作用于查询条件的关键词。规则支持andor 和 not 三个关联词。

逻辑的连接词需要和括号一同使用,括号中为具体的查询条件。其中,and 和 or 可以作用于多个查询条件(即括号中可以包含多个查询条件,以逗号分隔开),not 只能作用于一个查询条件。

逻辑连接词是一类作用于查询条件的特殊运算符(普通的运算符作用于节点或属性)

逻辑的连接词作用的查询条件也可以包含逻辑连接词。

示例
/*
 * 问题检查条件:
 * - 查找函数名为 debug 或 foo;
 * - 并且(And): 参数数量为 1 或 3。  
*/
functionDeclaration fd where
    and(or(fd.name == "debug", fd.name == "foo"),
        or(fd.parameters.size() == 1, fd.parameters.size() == 3));

2.5. 条件判断(condition)

结构图

2.5.1. 运算符

查询条件通过运算符作用于具体的节点或属性从而完成节点筛选。

根据运算符作用的属性类别,我们可以将运算符分为:

  • 通用运算符
  • 算术运算符
  • 字符串运算符
  • 布尔运算符
  • 节点运算符

运算符的左值为:节点或节点的属性。

2.5.1.1. 通用运算符(==, contain)

等于(==)、不等于(!=

  • 数字
    • 运算符右值为:整数或小数。
    • 例如:arguments.size() == 3
  • 字符串
    • 运算符右值为:字符串常量。
    • 例如:name == “foo”
  • 布尔
    • 运算符右值为:布尔常量: true 或 false
    • 例如:isPublic == true
  • 节点
    • 运算符右值为:空常量null
    • 例如:initializer == null

包含(contain)、不包含(notContain

  • 字符串
    • 运算符右值为:字符串,且包含时,返回真。
    • 例如:recordDeclaration where name contain "debug";,返回函数名中包含"debug" 时,返回真。
  • 节点
    • 运算符右值为:子查询条件,当子查询返回的节点满足时,返回真。
    • 例如:functionDeclaration where parameters contain param where param.name == "i1";,返回包含名为i1参数的方法声明。

2.5.1.2. 算术运算符(>, <)

算术运算符操作数字。

大于(>)、小于(<)、大于等于(>=)、小于等于(<=

数字

运算符右值为:数字,且满足时,返回真。

2.5.1.3. 字符串运算符(startWith, endWith, match)

字符串运算符操作字符串。

字符串的开始字符串(startWith
  • 左值开始字符串等于右值字符串时,返回真。
  • 例如:functionDeclartion where name startWith "debug";,筛选出名字以debug开头的方法声明。
字符串的结束字符串(endWith
  • 左值结束字符串等于右值字符串时,返回真。
  • 例如:functionDeclartion where name endWith "hello",筛选出名字以hello结尾的方法声明。
字符串匹配(match
  • 左值符合右值的正则表达式时,返回真。
  • 例如:functionDeclaration where name match ".*(login).*",筛选出名字匹配正则表达式.*(login).*的方法声明。

2.5.1.4. 布尔运算符(!)

逻辑运算符操作布尔值。

非(!
  • 操作符位于布尔属性的左侧,表示布尔值为false时,返回真。
  • 例如:recordDeclaration where !isPublic 等价于 recordDelaration where isPublic == false

2.5.1.5. 节点运算符(contain,in,is)

节点运算符的左侧可以是节点属性 或 别名。

包含(contain)
  • 用于查询语法树结构上的子节点。
  • 例如: 筛选出方法体内调用了名为foo的无参方法的方法声明。
functionDeclaration fd where
    fd contain functionCall where
        and(name == "foo",
            arguments.size() == 0));
存在于(in)
  • 查询语法树结构上的父节点。
  • 例如: 筛选出位于赋值表达式中的数据成员访问节点。
fieldAccess fa1 where
    fa1 in assignStatement;
是(is)
  • 右侧节点属性或别名相同时,返回真。
  • 例如:筛选出出现在赋值表达式左侧的变量访问节点,且赋值表达式的右侧为方法调用节点。规则中的va是节点variableAccess的别名。
variableAccess va where
    va in assignStatement where
        and(lhs is va,
            rhs is functionCall);

2.5.2. 值类型

2.5.2.1. 布尔值

布尔值为布尔常量truefalse

布尔属性可以单独使用。单独使用时,布尔属性隐式等价于一个包含==的通用条件表达式。

例如,recordDeclaration where isPublic;等价于recordDeclaration where isPublic == true;

2.5.2.2. 数值

规则中的数值可以为整数或小数。

2.5.2.3. 字符串

字符串支持大小写英文字母、数字、以及正则表达式特殊字符。

2.5.2.4. 空值(null)

空值指的是源码中的null

在使用时,空值往往和== 或 != 一同出现。

例如:
// private String field = null
fieldDeclaration fd where fd.initializer == null;

// str = null
assignStatement as1 where as1.rhs == null;

3. CodeNavi插件

在Vscode 的插件中,查询:codenavi,并安装。

 

点击关注,第一时间了解华为云新鲜技术~

 

标签:运算符,语法结构,神器,CodeNavi,fd,规则,字符串,where,节点
From: https://www.cnblogs.com/huaweiyun/p/18289803

相关文章

  • CleanMyMac X4.15.8Mac电脑必备下载神器!
    嘿,亲爱的Mac用户们......
  • Guitar Pro8.2吉他打谱神器,轻松创作你的音乐世界!#吉他 #打谱神器 #GuitarPro #音乐爱
    嗨,亲爱的吉他手们!......
  • 朋友圈不知道发什么?这个发圈神器必须拥有!
    你是不是每天都为要在朋友圈里发什么内容而苦恼?是不是也遇到了朋友圈被折叠的问题?别担心!今天我为大家介绍一个非常实用的发圈神器,它能帮助你解决这些问题!首先,这个发圈神器提供超过10种文案类型供你选择。不管是节日祝福、每日心情还是生活感悟,你都可以直接一键将它们转发到自......
  • 不会精准拓客?神器偷偷用起来!!!
    你知道吗?其实精准拓客真的一点也不难!今天,就给大家分享一个非常实用的获客神器,只需简单几步就能得到你想要的客源!帮助大家实现精准拓客的目标。首先,打开这个获客神器,在工具箱这一板块找到自己想要的工具。这个获客神器提供了多种功能,包括目标客户搜索、营销推广等。点击进入工......
  • 好消息!数据库管理神器 Navicat 推出免费精简版:Navicat Premium Lite
    前言好消息,前不久Navicat推出了免费精简版的数据库管理工具NavicatPremiumLite,可用于商业和非商业目的,我们再也不需要付费、找破解版或者找其他免费平替工具了,有需要的同学可以马上下载使用起来。工具官方介绍NavicatPremiumLite是Navicat的精简版,它包含了用户执行主要......
  • 让我彻底沉迷的设计神器CorelDRAW2024中文版本发布啦!
    ......
  • 好久不见!写了一个自动截图神器~【附源码】
    文章目录前言新增功能介绍截图功能快捷键设置程序设计和使用介绍操作菜单栏选择点击坐标点选择图片选择截图区域快捷键设置表格循环次数状态栏使用案例源代码前言好久没更新文章了。上一次更新是在4月16日差不多,也只是写了一个错误集,没什么太多内容。这一次,算是复......
  • 外挂级OCR神器:免费文档解析、表格识别、手写识别、古籍识别、PDF转Word
    TextInTools是一款免费的在线OCR工具,支持快速准确的文字和表格识别,手写、古籍识别,提供PDF转Markdown大模型辅助工具,同时支持PDF、WORD、EXCEL、JPG、PPT等各类格式文件的转化。TextInTools特点免费:所有产品提供每日200页免费额度,覆盖日常使用需求。方便:无需下载安装,PC端......
  • Java中的JSON神器,如何轻松玩转复杂数据结构
    哈喽,大家好,我是木头左!一、揭秘JSON世界的基石在Java的世界中,JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式,它基于文本,易于阅读和编写,同时也易于机器解析和生成。JSON在日常开发中的应用非常广泛,无论是前后端的数据交互,还是配置文件的读取,都离不开JSON的身影。那......
  • 这个神器绝了!可视化 Python 打包 exe
     在Python开发中,如何将脚本打包成独立的可执行文件,是许多开发者面临的挑战。特别是在需要将应用程序分发给不具备Python环境的用户时,打包工具显得尤为重要。auto-py-to-exe作为一款强大的Python打包工具,为开发者提供了简便快捷的解决方案。那么,auto-py-to-exe究竟是如何简化......