CDTJDT是开发SAST工具的有力引擎
CDT(Eclipse JDT的C/C++扩展)在编译不通过的情况下可以生成AST,但生成的AST可能是不完整或包含错误节点的。
CDT使用预处理器和解析器来构建AST:
- 预处理器:处理#include、#define等指令,生成翻译单元。
- 解析器:将预处理后的代码转换为AST节点。
即使代码存在语法错误,CDT的解析器仍会尝试生成尽可能完整的AST,并在错误位置插入特殊的错误节点。
对于编译不通过的代码,CDT会:
- 继续解析后续代码:遇到错误时,解析器会尝试恢复并继续处理后续代码。
- 标记错误位置:在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编码过程中错误即时提示和代码分析中非常有用,例如:
- 实时错误提示:IDE可以在用户输入代码时立即显示错误,而不需要等待完整编译。
- 代码静态分析工具:静态分析工具可以基于不完整的AST提取部分信息(如函数调用关系)。
基于CDT生成的AST可以编写代码缺陷检查规则(Checker),这也是静态代码分析工具的核心原理。CDT提供的AST结构包含了代码的语法信息(如函数调用、变量声明、控制流等),通过遍历和分析这些节点,可以识别出潜在的代码缺陷。
CDT AST在代码检查中的应用
CDT的AST节点包含丰富的元数据,例如:
- 节点类型
- 位置信息(行号、列号)
- 关联的符号(如变量、函数名)
- 子节点结构(如表达式的组成部分)
这些信息使得我们可以编写规则来检查:
- 语法层面的缺陷(如未使用的变量、空指针解引用)
- 风格和规范问题(如未遵循命名约定、缺少注释)
- 潜在的逻辑错误(如无限循环、资源泄漏)
以下是一个基于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可以成为强大的代码质量保障工具的底层引擎。
——————————————————————————————————————————
(结束)