PMD 源码阅读(3)— 规则检查
PMD 使用内置的编码规则对代码进行静态检查,它通过 JavaCC 和 JJTree 得到代码的抽象语法树(AST),然后对语法树的节点进行检查。
注:本文研究的 PMD 版本是 5.5.2 ,使用的根据代码自动生成类图的工具为 Eclipse 插件 AmaterasUML。
1. 程序入口
我们选择从命令行开始执行,程序入口为 pmd-core 子项目的 net.sourceforge.pmd.PMD 类中的 main 方法。经过一系列跳转之后,我们来到了 SourceCodeProcessor 的 processSource 方法。parse 方法将一个代码文件作为一个节点,然后使用 RuleSets 的 apply 方法对该节点进行检查。
//SourceCodeProcessor 的 processSource 方法(删除部分无关代码)
private void processSource(Reader sourceCode, RuleSets ruleSets, RuleContext ctx) {
LanguageVersion languageVersion = ctx.getLanguageVersion();
Parser parser = PMD.parserFor(languageVersion, configuration);
Node rootNode = parse(ctx, sourceCode, parser);
Language language = languageVersion.getLanguage();
List<Node> acus = Collections.singletonList(rootNode);
ruleSets.apply(acus, ctx, language);
}
//SourceCodeProcessor 的 processSource 方法(删除部分无关代码)
private void processSource(Reader sourceCode, RuleSets ruleSets, RuleContext ctx) {
LanguageVersion languageVersion = ctx.getLanguageVersion();
Parser parser = PMD.parserFor(languageVersion, configuration);
Node rootNode = parse(ctx, sourceCode, parser);
Language language = languageVersion.getLanguage();
List<Node> acus = Collections.singletonList(rootNode);
ruleSets.apply(acus, ctx, language);
}
RuleSets 的 apply 方法会调用 RuleSets 中每个 RuleSet 的 apply 方法,RuleSet 的 apply 方法又会调用 RuleSet 中每个 Rule 的 apply 方法。Rule 的 apply 方法如下:
void apply(List<? extends Node> nodes, RuleContext ctx);
void apply(List<? extends Node> nodes, RuleContext ctx);
2. 规则检查
2.1 抽象语法树
抽象语法树是源代码的抽象语法结构的树状表现形式。pmd 使用 JavaCC 和 JJTree 生成每种 Java 语言元素的节点表示。生成的代码在 net.sourceforge.pmd.lang.java.ast 包中。

语法树节点的继承关系图如下:

JavaCC 和 JJTree 生成的代码可以为一段 Java 代码生成对应的树结构。 我们可以运行 pmd 提供的 designer.bat 通过这个工具我们可以很直观的看到代码的树状结构。

2.2 访问者模式
JavaCC 、JJTree 的处理结果中还包含了对语法树结构进行访问的 Visitor。通过访问者模式,我们可以在遍历语法树的同时,对自己需要的节点进行处理。
public interface JavaParserVisitor
{
public Object visit(ASTCompilationUnit node, Object data);
public Object visit(ASTPackageDeclaration node, Object data);
public Object visit(ASTImportDeclaration node, Object data);
public Object visit(ASTTypeDeclaration node, Object data);
public Object visit(ASTClassOrInterfaceDeclaration node, Object data);
public Object visit(ASTExtendsList node, Object data);
public Object visit(ASTImplementsList node, Object data);
public Object visit(ASTEnumDeclaration node, Object data);
public Object visit(ASTEnumBody node, Object data);
public Object visit(ASTEnumConstant node, Object data);
public Object visit(ASTTypeParameters node, Object data);
public Object visit(ASTTypeParameter node, Object data);
public Object visit(ASTTypeBound node, Object data);
public Object visit(ASTClassOrInterfaceBody node, Object data);
public Object visit(ASTClassOrInterfaceBodyDeclaration node, Object data);
public Object visit(ASTFieldDeclaration node, Object data);
public Object visit(ASTVariableDeclarator node, Object data);
//以下省略
}
public interface JavaParserVisitor
{
public Object visit(ASTCompilationUnit node, Object data);
public Object visit(ASTPackageDeclaration node, Object data);
public Object visit(ASTImportDeclaration node, Object data);
public Object visit(ASTTypeDeclaration node, Object data);
public Object visit(ASTClassOrInterfaceDeclaration node, Object data);
public Object visit(ASTExtendsList node, Object data);
public Object visit(ASTImplementsList node, Object data);
public Object visit(ASTEnumDeclaration node, Object data);
public Object visit(ASTEnumBody node, Object data);
public Object visit(ASTEnumConstant node, Object data);
public Object visit(ASTTypeParameters node, Object data);
public Object visit(ASTTypeParameter node, Object data);
public Object visit(ASTTypeBound node, Object data);
public Object visit(ASTClassOrInterfaceBody node, Object data);
public Object visit(ASTClassOrInterfaceBodyDeclaration node, Object data);
public Object visit(ASTFieldDeclaration node, Object data);
public Object visit(ASTVariableDeclarator node, Object data);
//以下省略
}
2.3 规则与规则集

PMD 中定义了大量的规则,继承关系图如下:

AbstractJavaRule 的 applyRule 方法:
public void apply(List<? extends Node> nodes, RuleContext ctx) {
visitAll(nodes, ctx);
}
public void apply(List<? extends Node> nodes, RuleContext ctx) {
visitAll(nodes, ctx);
}
AbstractJavaRule 中实现了 JavaParserVistor 接口,因此 PMD 中每个规则都是一个 Visitor,这些 Visitor 通过重写各种 visit 方法访问自己的关注的语法元素,并对其进行检查。

我们以 AvoidDollarSignsRule 为例:
public class AvoidDollarSignsRule extends AbstractJavaRule {
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
if (node.getImage().indexOf('
规则集,顾名思义就是规则的集合,它以 xml 的形式存储了归属于同一类的规则,这些 xml 文件被放在 resource 目录中。
以 basic.xml 为例,文件内容如下:
3. 输出结果
与 CPD 类似,PMD 也定义了多个 Render 以不同的格式进行输出,这里就不详细说明了。
) != -1) {
addViolation(data, node);
return data;
}
return super.visit(node, data);
}
public Object visit(ASTVariableDeclaratorId node, Object data) {
if (node.getImage().indexOf('
规则集,顾名思义就是规则的集合,它以 xml 的形式存储了归属于同一类的规则,这些 xml 文件被放在 resource 目录中。
以 basic.xml 为例,文件内容如下:
3. 输出结果
与 CPD 类似,PMD 也定义了多个 Render 以不同的格式进行输出,这里就不详细说明了。
) != -1) {
addViolation(data, node);
return data;
}
return super.visit(node, data);
}
public Object visit(ASTMethodDeclarator node, Object data) {
if (node.getImage().indexOf('
规则集,顾名思义就是规则的集合,它以 xml 的形式存储了归属于同一类的规则,这些 xml 文件被放在 resource 目录中。
以 basic.xml 为例,文件内容如下:
3. 输出结果
与 CPD 类似,PMD 也定义了多个 Render 以不同的格式进行输出,这里就不详细说明了。
) != -1) {
addViolation(data, node);
return data;
}
return super.visit(node, data);
}
}
public class AvoidDollarSignsRule extends AbstractJavaRule {
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
if (node.getImage().indexOf('
规则集,顾名思义就是规则的集合,它以 xml 的形式存储了归属于同一类的规则,这些 xml 文件被放在 resource 目录中。
以 basic.xml 为例,文件内容如下:
3. 输出结果
与 CPD 类似,PMD 也定义了多个 Render 以不同的格式进行输出,这里就不详细说明了。
) != -1) {
addViolation(data, node);
return data;
}
return super.visit(node, data);
}
public Object visit(ASTVariableDeclaratorId node, Object data) {
if (node.getImage().indexOf('
规则集,顾名思义就是规则的集合,它以 xml 的形式存储了归属于同一类的规则,这些 xml 文件被放在 resource 目录中。
以 basic.xml 为例,文件内容如下:
3. 输出结果
与 CPD 类似,PMD 也定义了多个 Render 以不同的格式进行输出,这里就不详细说明了。
) != -1) {
addViolation(data, node);
return data;
}
return super.visit(node, data);
}
public Object visit(ASTMethodDeclarator node, Object data) {
if (node.getImage().indexOf('
规则集,顾名思义就是规则的集合,它以 xml 的形式存储了归属于同一类的规则,这些 xml 文件被放在 resource 目录中。
以 basic.xml 为例,文件内容如下:
3. 输出结果
与 CPD 类似,PMD 也定义了多个 Render 以不同的格式进行输出,这里就不详细说明了。
) != -1) {
addViolation(data, node);
return data;
}
return super.visit(node, data);
}
}
规则集,顾名思义就是规则的集合,它以 xml 的形式存储了归属于同一类的规则,这些 xml 文件被放在 resource 目录中。

以 basic.xml 为例,文件内容如下:

3. 输出结果
与 CPD 类似,PMD 也定义了多个 Render 以不同的格式进行输出,这里就不详细说明了。
评论
暂无评论,来发表第一条评论吧