当前位置: 首页 > news >正文

CDTJDT是开发SAST工具的有力引擎

CDT(Eclipse JDT的C/C++扩展)在编译不通过的情况下可以生成AST,但生成的AST可能是不完整或包含错误节点的。

CDT使用预处理器和解析器来构建AST:

  1. 预处理器:处理#include、#define等指令,生成翻译单元。
  2. 解析器:将预处理后的代码转换为AST节点。

即使代码存在语法错误,CDT的解析器仍会尝试生成尽可能完整的AST,并在错误位置插入特殊的错误节点。

对于编译不通过的代码,CDT会:

  1. 继续解析后续代码:遇到错误时,解析器会尝试恢复并继续处理后续代码。
  2. 标记错误位置:在AST中插入错误节点,记录错误类型和位置。

例如,对于以下有语法错误的代码:

int main() {

    int a = 10;

    if (a > 5  // 缺少右括号

        printf("Hello");

    return 0;

}

CDT可能生成包含错误节点的AST,其中`if`语句的条件表达式节点会被标记为不完整。

下面我们看一下再CDT的Java API中,如何通过获取带有错误的AST:

//创建AST解析器

ASTParser parser = ASTParser.newParser(ICodeAssist.CTTYPE_C);

parser.setSource(code.toCharArray());  // code是包含错误的代码

parser.setProject(project);            // 设置项目以解析头文件等

// 获取AST(即使有错误也会返回)

AST ast = parser.createAST(null);

获取AST后,可以通过遍历节点检查是否存在错误:

// 检查AST中的错误

IProblem[] problems = ast.getProblems();

for (IProblem problem : problems) {

    if (problem.isError()) {

        System.out.println("错误: " + problem.getMessage());

        System.out.println("位置: " + problem.getSourceLineNumber());

    }

}

这种机制在IDE编码过程中错误即时提示和代码分析中非常有用,例如:

  1. 实时错误提示:IDE可以在用户输入代码时立即显示错误,而不需要等待完整编译。
  2. 代码静态分析工具:静态分析工具可以基于不完整的AST提取部分信息(如函数调用关系)。

基于CDT生成的AST可以编写代码缺陷检查规则(Checker),这也是静态代码分析工具的核心原理。CDT提供的AST结构包含了代码的语法信息(如函数调用、变量声明、控制流等),通过遍历和分析这些节点,可以识别出潜在的代码缺陷。

CDT AST在代码检查中的应用

CDT的AST节点包含丰富的元数据,例如:

  1. 节点类型
  2. 位置信息(行号、列号)
  3. 关联的符号(如变量、函数名)
  4. 子节点结构(如表达式的组成部分)

这些信息使得我们可以编写规则来检查:

  1. 语法层面的缺陷(如未使用的变量、空指针解引用)
  2. 风格和规范问题(如未遵循命名约定、缺少注释)
  3. 潜在的逻辑错误(如无限循环、资源泄漏)

以下是一个基于CDT AST的简单Checker示例,用于检测未使用的局部变量:

import org.eclipse.cdt.core.dom.ast.*;

import org.eclipse.cdt.core.dom.ast.cpp.*;

import org.eclipse.cdt.core.model.ICProject;

import org.eclipse.cdt.core.parser.*;

import org.eclipse.core.runtime.CoreException;

public class UnusedVariableChecker {

 // 检查给定代码中的未使用局部变量

    public void checkUnusedVariables(String code, ICProject project) throws CoreException {

        // 创建AST解析器

        ASTParser parser = ASTParser.newParser(ICodeAssist.CTTYPE_C);

        parser.setSource(code.toCharArray());

        parser.setProject(project);

        parser.setResolveBindings(true); // 启用符号解析

        // 生成AST

        IASTTranslationUnit tu = parser.createAST(null);

       

        // 遍历AST,查找未使用的变量

        tu.accept(new ASTVisitor() {

            {

                // 只关注变量声明节点

                shouldVisitDeclarations = true;

            }

           

            @Override

            public int visit(IASTDeclaration declaration) {

                if (declaration instanceof IASTSimpleDeclaration) {

                    IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) declaration;

                    IASTDeclarator[] declarators = simpleDecl.getDeclarators();

                   

                    for (IASTDeclarator declarator : declarators) {

                        if (declarator.getName() instanceof IASTName) {

                            IASTName name = (IASTName) declarator.getName();

                            // 获取变量的绑定(即符号信息)

                            IBinding binding = name.resolveBinding();

                           

                            if (binding instanceof IVariable) {

                                IVariable variable = (IVariable) binding;

                                // 检查是否为局部变量

                                if (isLocalVariable(variable)) {

                                    // 检查变量是否未使用

                                    if (!isVariableUsed(variable, tu)) {

                                        System.out.println("未使用的局部变量: " +

                                                name.getRawSignature() +

                                                " (行号: " + name.getFileLocation().getStartingLineNumber() + ")");

                                    }

                                }

                            }

                        }

                    }

                }

                return PROCESS_CONTINUE;

            }

           

            // 判断是否为局部变量

            private boolean isLocalVariable(IVariable variable) {

                if (variable instanceof ICPPVariable) {

                    ICPPVariable cppVar = (ICPPVariable) variable;

                    return cppVar.isLocal();

                }

                return false;

            }

           

            // 判断变量是否被使用

            private boolean isVariableUsed(IVariable variable, IASTTranslationUnit tu) {

                // 创建引用查找器

                IASTName[] references = tu.getReferences(variable);

                return references.length > 1; // 声明本身也算一次引用

            }

        });

    }

}

上面只是一个举例,实际上基于CDT AST可以实现多种Checker,例如:

1. 空指针解引用检查

   // 检查空指针解引用

   if (expr instanceof IASTUnaryExpression) {

       IASTUnaryExpression unaryExpr = (IASTUnaryExpression) expr;

       if (unaryExpr.getOperator() == IASTUnaryExpression.op_star) { // *操作符(解引用)

           IASTExpression operand = unaryExpr.getOperand();

           // 分析operand是否可能为null

       }

   }

2. 资源泄漏检查

   // 检查fopen后是否有对应的fclose

   if (call.getName().toString().equals("fopen")) {

       // 记录文件句柄变量

   } else if (call.getName().toString().equals("fclose")) {

       // 标记文件句柄已关闭

   }

   // 最后检查是否有未关闭的文件句柄

3. 数组越界检查

   // 检查数组访问

   if (expr instanceof IASTArraySubscriptExpression) {

       IASTArraySubscriptExpression arrayExpr = (IASTArraySubscriptExpression) expr;

       IASTExpression index = arrayExpr.getArgument();

       // 分析index是否超出数组边界

   }

相对于通过ClangTidy等作为引擎开发SAST工具,必须完整编译才能生成AST,进行后续的分析,而利用CDT、JDT具有编译不通过情况下也可以检测分析更具有优势,CDT的AST可以成为强大的代码质量保障工具的底层引擎。

——————————————————————————————————————————

(结束)

http://www.xdnf.cn/news/925759.html

相关文章:

  • Java 并发编程系列(上篇):多线程深入解析
  • Java Map完全指南:从基础到高级应用
  • Cell-o1:强化学习训练LLM解决单细胞推理问题
  • 基于Ubuntu22.04安装SVN服务器之仓库迁移
  • 基于IDA的bindiff使用
  • Java并发编程之并发编程的调试与测试
  • 【MLLM】字节BAGEL多模态理解和生成统一模型
  • 数字通信复习
  • RAG检索系统的两大核心利器——Embedding模型和Rerank模型
  • ELF文件,静态链接(Linux)
  • 算法练习-回溯
  • 指针与函数参数传递详解 —— 值传递与地址传递的区别及应用
  • Postman测试学习(1)
  • ABAP EXCEL导入换行符
  • A Survey on the Memory Mechanism of Large Language Model based Agents
  • 【Go语言基础【12】】指针:声明、取地址、解引用
  • 策略模式实战:Spring中动态选择商品处理策略的实现
  • 软件测试—学习Day10
  • 开疆智能Ethernet/IP转Modbus网关连接MAG8000电池流量计配置案例
  • python版若依框架开发:集成Dash应⽤
  • 将 Elastic 的数据摄取转向 OpenTelemetry
  • SWE-Dev:开启自主特征驱动软件开发新纪元,重新定义大模型编码能力边界
  • 理解 RAG_HYBRID_BM25_WEIGHT:打造更智能的混合检索增强生成系统
  • 【Go核心编程】第十三章:接口与多态——灵活性的艺术
  • FUSSNet复现
  • vue注册自定义指令
  • 黄柏基因组-小檗碱生物合成的趋同进化-文献精读142
  • h5 安卓手机去掉滚动条问题
  • compose 组件 ---无ui组件
  • 基于TarNet、CFRNet与DragonNet的深度因果推断模型全解析