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 方法对该节点进行检查。

java
//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 方法如下:

java
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 包中。

AST.png

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

Node.png

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

Designer.png

2.2 访问者模式

JavaCC 、JJTree 的处理结果中还包含了对语法树结构进行访问的 Visitor。通过访问者模式,我们可以在遍历语法树的同时,对自己需要的节点进行处理。

java
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 规则与规则集

Rules.png

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

Rule.png

AbstractJavaRule 的 applyRule 方法:

java
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 方法访问自己的关注的语法元素,并对其进行检查。

Visitor.png

我们以 AvoidDollarSignsRule 为例:

java
public class AvoidDollarSignsRule extends AbstractJavaRule {

    public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
        if (node.getImage().indexOf('

规则集,顾名思义就是规则的集合,它以 xml 的形式存储了归属于同一类的规则,这些 xml 文件被放在 resource 目录中。

RuleSetXML.png

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

RuleSet.png

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 目录中。

RuleSetXML.png

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

RuleSet.png

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 目录中。

RuleSetXML.png

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

RuleSet.png

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 目录中。

RuleSetXML.png

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

RuleSet.png

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 目录中。

RuleSetXML.png

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

RuleSet.png

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 目录中。

RuleSetXML.png

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

RuleSet.png

3. 输出结果

与 CPD 类似,PMD 也定义了多个 Render 以不同的格式进行输出,这里就不详细说明了。

) != -1) {
addViolation(data, node); return data; } return super.visit(node, data); } }

规则集,顾名思义就是规则的集合,它以 xml 的形式存储了归属于同一类的规则,这些 xml 文件被放在 resource 目录中。

RuleSetXML.png

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

RuleSet.png

3. 输出结果

与 CPD 类似,PMD 也定义了多个 Render 以不同的格式进行输出,这里就不详细说明了。

评论

后继续评论需要管理员审核后可见

暂无评论,来发表第一条评论吧