原文:https://testerhome.com/topics/4918
首先在PMD官网下载最新版本的文件,目前最新版本是5.4.1。
下载pmd-bin-5.4.1.zip和pmd-src-5.4.1.zip之后解压备用。
pmd-src-5.4.1是PMD源码包,是无法直接执行的。
pmd-bin-5.4.1是PMD的可执行包。
下面以一个比较简单的规则举例,详细的阐述一下实现这个规则的具体步骤,帮助大家快速上手。
目前PMD支持两种编写规则的方法:
1. 使用Java进行编写
2. 使用XPath表达式
我首先选择第一种Java编写方式进行讲解。
需要自定义的规则:While循环必须使用括号,While循环没有括号很容易困惑代码结构。所以下面以“While循环必须使用括号”这条规则为例。
写出问题样例的代码写法。
class Example {
void bar() {
while (baz)
buz.doSomething();
}
}
弄清楚样例代码是什么样子的,就成功了一半。
PMD扫描时并不是直接使用源码;它使用JavaCC
生成解析器来解析源代码并生成AST(抽象语法树)。你可以使用PMD自带的designer工具进行解析代码。
该工具所在目录:pmd-bin-5.4.1/bin/designer.bat
双击designer.bat后出现一个界面,在Source code中填入源代码,点击Go按钮:
以上样例代码解析成抽象语法树后如下:
CompilationUnit
TypeDeclaration
ClassDeclaration:(package private)
UnmodifiedClassDeclaration(Example)
ClassBody
ClassBodyDeclaration
MethodDeclaration:(package private)
ResultType
MethodDeclarator(bar)
FormalParameters
Block
BlockStatement
Statement
WhileStatement
Expression
PrimaryExpression
PrimaryPrefix
Name:baz
Statement
StatementExpression:null
PrimaryExpression
PrimaryPrefix
Name:buz.doSomething
PrimarySuffix
Arguments
图片中Abstract Syntax Tree/XPath/Symbol Table的位置就是抽象后的树形结构,这个树形结构和源代码是有对应关系的。
其中我们需要重点关注的WhileStatement
的抽象树结构如下:
WhileStatement
Expression
Statement
StatementExpression
这个是错误的代码示例的抽象树结构,如果While循环加上了括号,抽象树的结构就会变成:
WhileStatement
Expression
Statement
Block
BlockStatement
Statement
StatementExpression
这下能明显的看到了比之前多处了Block
和BlockStatement
这两个节点。
这样我们只需要写一个规则检查WhileStatement
下没有Block
节点,只有Statement
节点时,就可以报警告知这里是有问题的。
顺便提一句,所有的结构信息,比如一个Statement
节点后面可能跟着一个Block
节点,这些都是在EBNF grammar中定义的。比如在这个语法定义中,一个Statement
的定义是这样的:
void Statement() :
{}
{
LOOKAHEAD( { isNextTokenAnAssert() } ) AssertStatement()
| LOOKAHEAD(2) LabeledStatement()
| Block()
| EmptyStatement()
| StatementExpression() ";"
| SwitchStatement()
| IfStatement()
| WhileStatement()
| DoStatement()
| ForStatement()
| BreakStatement()
| ContinueStatement()
| ReturnStatement()
| ThrowStatement()
| SynchronizedStatement()
| TryStatement()
}
以上代码列出了一个Statement
节点后面所有的可能的节点类型。
写一个新的Java类继承net.sourceforge.pmd.lang.java.rule.AbstractJavaRule
:
import net.sourceforge.pmd.lang.java.rule.*;
public class WhileLoopsMustUseBracesRule extends AbstractJavaRule {
}
PMD工作原理就是在生成的抽象语法树中递归的遍历,直到找出要找的目标,然后返回结果。
接下来我们的目标就是在抽象语法树中找出WhileStatement
节点下不存在Statement/Block
这种结构的情况。
import net.sourceforge.pmd.lang.ast.*;
import net.sourceforge.pmd.lang.java.ast.*;
import net.sourceforge.pmd.lang.java.rule.*;
public class WhileLoopsMustUseBracesRule extends AbstractJavaRule {
public Object visit(ASTWhileStatement node, Object data) {
Node firstStmt = node.jjtGetChild(1);
if (!hasBlockAsFirstChild(firstStmt)) {
addViolation(data, node);
}
return super.visit(node,data);
}
private boolean hasBlockAsFirstChild(Node node) {
return (node.jjtGetNumChildren() != 0 && (node.jjtGetChild(0) instanceof ASTBlock));
}
}
这段代码的主要意思:
1. 访问文件中的ASTWhileStatement
节点
2. 获取ASTWhileStatement
节点下第二个子节点
3. 判断第二个子节点的第一个子节点是不是ASTBlock节点
4. 如果不是ASTBlock节点,说明我们的目标实现,触犯规则
5. 使用addViolation(data, node);
语句记录触犯该规则的节点相关数据
现在规则已经写完了,我们需要告诉PMD运行时执行这条规则,就得将这个规则文件的相关信息放在XML规则集文件中。例如:pmd-java/src/main/resources/rulesets/java/basic.xml
;这里面有很多规则的定义,复制粘贴一下,改成一个新的规则集文件,名字自己随便取:mycustomrules.xml
,自己填充一下元素和属性。
name - WhileLoopsMustUseBracesRule
message - Use braces for while loops
class - 放哪都行. 注意,没有必要放在net.sourceforge.pmd
目录下,可以放在com.yourcompany.util.pmd
description - Use braces for while loops
example - 通过代码片段展示违反的规则样例
name="My custom rules"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
name="WhileLoopsMustUseBracesRule"
message="Avoid using 'while' statements without curly braces"
class="WhileLoopsMustUseBracesRule">
Avoid using 'while' statements without using curly braces
3
在执行前需要把你修改后的pmd-java重新打包:
在命令行中进入pmd-src-5.4.1\pmd-java
目录中,执行mvn clean package
。
打包成功后,将pmd-src-5.4.1\pmd-java\target
中的pmd-java-5.4.1.jar替换pmd-bin-5.4.1\lib
目录中对应的jar包。
最后在pmd-bin-5.4.1\bin
目录中执行pmd.bat -d c:\path\to\my\src -f xml -R c:\path\to\mycustomrules.xml
可在命令行界面中查看结果。
##使用XPath表达式编写该规则
PMD是支持XPath引擎的,这条“While循环必须使用括号”也可以使用XPath表达式实现。//WhileStatement[not(Statement/Block)]
意思是匹配查找整个抽象语法树中WhileStatement
节点下不存在Statement/Block
这种结构的情况。
XPath表达式完成后,不需要写java代码,只写一个xml规则文件就行了。
name="My XPathRule rules"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
name="WhileLoopsMustUseBracesRule"
language="java"
message="Avoid using 'while' statements without curly braces"
class="net.sourceforge.pmd.lang.rule.XPathRule">
Avoid using 'while' statements without using curly braces
name="xpath">
3
运行时指向这个新编写的xml,查看结果:
这篇文章只是教大家快速的上手自定义PMD规则,下一篇文章将重点分析复杂规则如何编写,敬请期待。
PMD site. How to write a PMD rule
ONJava.com.Custom PMD Rules
CSDN.静态分析工具PMD使用说明