原文: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-bin-5.4.1【PMD可执行版本】

    • bin

      • designer.bat【界面工具,能将java源代码转化为AST(抽象语法树),个人推荐使用】
      • bgastviewer.bat【界面工具,与designer.bat功能相似】
      • cpd.bat【用来查找重复代码的工具,命令行版】
      • cpdgui.bat【用来查找重复代码的工具,GUI版】
      • pmd.bat【Window平台下运行PMD需要使用的文件】
      • run.sh【Linux平台下运行PMD需要使用的文件】
    • lib【该目录存放PMD运行依赖的jar包,包括第三方jar包和各种语言的模块jar包】

  • pmd-src-5.4.1【PMD源代码版本】

    • pmd-core【PMD的核心执行调度模块】
    • pmd-java 【针对java语言的检测模块】
      • src ->main

        • java -> net -> sourceforge -> pmd -> lang->java【目录太深,在此处聚合】
        • rule 【该目录下存放已经编写好的java规则文件】
          • basic【基础类规则】
          • AvoidBranchingStatementAsLastInLoopRule.java【避免在循环的最后使用分支语句】
          • AvoidMultipleUnaryOperatorsRule.java【避免一元运算符的多重使用】
          • ...【其他基础类的规则文件】
          • codesize【代码体积类规则】
          • ...【各种规则类别的目录,包含该类别的java编写的规则文件】
      • resources
        • rulesets【java规则对应的xml文件】

          • java
          • android.xml【PMD运行时使用该文件会调用安卓类规则进行扫描】
          • basic.xml【PMD运行时使用该文件会调用基础类规则进行扫描】
          • ...【其他类别的规则xml文件】
      • etc
        • grammar

          • Java.jjt【AST抽象语法树生成所需的语法文件】
    • pmd-java8 【新增对java1.8版本的支持模块】
    • pmd-javascript 【针对javascript语言的检测模块】
    • pmd-jsp 【针对jsp语言的检测模块】
    • ...【其余的主要是针对不同语言实现的独立的检测模块】

自定义规则实现思路

  1. 明确想要自定义的规则。
  2. 列举会触犯这种规则的所有不同的写法。
  3. 使用designer.bat分析所有写法的抽象语法树的特点。
  4. 编写规则代码捕捉这种特点。
  5. 创建自己的xml规则文件,内容包括规则的相关信息。
  6. 运行PMD扫描错误代码,验证是否能触发自定义规则。

下面以一个比较简单的规则举例,详细的阐述一下实现这个规则的具体步骤,帮助大家快速上手。
目前PMD支持两种编写规则的方法:
1. 使用Java进行编写
2. 使用XPath表达式
我首先选择第一种Java编写方式进行讲解。


1. 明确想要自定义的规则

需要自定义的规则:While循环必须使用括号,While循环没有括号很容易困惑代码结构。所以下面以“While循环必须使用括号”这条规则为例。

2. 列举会触犯这种规则的所有不同的写法

写出问题样例的代码写法。

class Example {void bar() {while (baz)buz.doSomething();}
}

弄清楚样例代码是什么样子的,就成功了一半。

3. 使用designer.bat分析所有写法的抽象语法树的特点

PMD扫描时并不是直接使用源码;它使用JavaCC生成解析器来解析源代码并生成AST(抽象语法树)。你可以使用PMD自带的designer工具进行解析代码。
该工具所在目录:pmd-bin-5.4.1/bin/designer.bat
双击designer.bat后出现一个界面,在Source code中填入源代码,点击Go按钮:

以上样例代码解析成抽象语法树后如下:

CompilationUnitTypeDeclarationClassDeclaration:(package private)UnmodifiedClassDeclaration(Example)ClassBodyClassBodyDeclarationMethodDeclaration:(package private)ResultTypeMethodDeclarator(bar)FormalParametersBlockBlockStatementStatementWhileStatementExpressionPrimaryExpressionPrimaryPrefixName:bazStatementStatementExpression:nullPrimaryExpressionPrimaryPrefixName:buz.doSomethingPrimarySuffixArguments

图片中Abstract Syntax Tree/XPath/Symbol Table的位置就是抽象后的树形结构,这个树形结构和源代码是有对应关系的。
其中我们需要重点关注的WhileStatement的抽象树结构如下:

WhileStatementExpressionStatementStatementExpression

这个是错误的代码示例的抽象树结构,如果While循环加上了括号,抽象树的结构就会变成:

WhileStatementExpressionStatementBlockBlockStatementStatementStatementExpression

这下能明显的看到了比之前多处了BlockBlockStatement这两个节点。
这样我们只需要写一个规则检查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节点后面所有的可能的节点类型。

4. 编写规则代码捕捉这种特点

写一个新的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);语句记录触犯该规则的节点相关数据

5. 创建自己的xml规则文件,内容包括规则的相关信息

现在规则已经写完了,我们需要告诉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 - 通过代码片段展示违反的规则样例

<?xml version="1.0"?>
<ruleset 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"><rule name="WhileLoopsMustUseBracesRule"message="Avoid using 'while' statements without curly braces"class="WhileLoopsMustUseBracesRule"><description>Avoid using 'while' statements without using curly braces</description><priority>3</priority><example>
<![CDATA[public void doSomething() {while (true)x++;}
]]></example></rule>
</ruleset>

6. 运行PMD扫描错误代码,验证是否能触发自定义规则

在执行前需要把你修改后的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
可在命令行界面中查看结果。

成功!使用Java编写的自定义规则完成!

##使用XPath表达式编写该规则
PMD是支持XPath引擎的,这条“While循环必须使用括号”也可以使用XPath表达式实现。
//WhileStatement[not(Statement/Block)]
意思是匹配查找整个抽象语法树中WhileStatement节点下不存在Statement/Block这种结构的情况。
XPath表达式完成后,不需要写java代码,只写一个xml规则文件就行了。

<?xml version="1.0"?>
<ruleset 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"><rule  name="WhileLoopsMustUseBracesRule"language="java"message="Avoid using 'while' statements without curly braces"class="net.sourceforge.pmd.lang.rule.XPathRule"><description>Avoid using 'while' statements without using curly braces</description><properties><property name="xpath"><value>
<![CDATA[
//WhileStatement[not(Statement/Block)]
]]></value></property></properties><priority>3</priority><example>
<![CDATA[
class Example {void bar() {while (baz)buz.doSomething();}
}
]]></example>
</rule>
</ruleset>

运行时指向这个新编写的xml,查看结果:

成功!
小技巧:PMD自带的designer.bat工具可以快速生成一个xpath rule xml。
1. 打开designer界面工具,输入源代码,输入XPath表达式,点击Go按钮,确认右下方的结果输出正确。
2. 点击左上方Actions->Create rule XML
3. 在新的页面输入Rule name,Rule msg,Rule desc后,点击Create rule XML按钮,查看输出的结果。
注意:目前版本的designer有一个小BUG,需要自己在XML中的rule标签中指定被测代码的属性language="java"

这篇文章只是教大家快速的上手自定义PMD规则,下一篇文章将重点分析复杂规则如何编写,敬请期待。

参考文献

PMD site. How to write a PMD rule
ONJava.com.Custom PMD Rules
CSDN.静态分析工具PMD使用说明

PMD 自定义规则实践入门样例相关推荐

  1. pmd java规则_静态代码扫描 (一)——PMD 自定义规则入门

    阅读该文章前,最好已经对 PMD 有了初步的认识和了解,可参考静态分析工具 PMD 使用说明 准备工作 首先在PMD 官网下载最新版本的文件,目前最新版本是 5.4.1. 下载 pmd-bin-5.4 ...

  2. AKKA框架持久化入门样例

    背景 我们在开发的过程中可能会在内存中操作数据,但是可能会遇到突然服务器断电.网线被挖等情况.这就需要将内存中的数据持久化,在程序重启的时候依然能够恢复. AKKA介绍 Akka is a toolk ...

  3. springboot+websocket+layui制作的实时聊天室,后端开发入门样例

    实时聊天室 前言 效果图 涉及技术 springboot layui websocket 实现思路 websocket在springboot下的实现 前端实现 建立websocket连接 前端对应的w ...

  4. 详细讲解如何使用Java连接Kafka构建生产者和消费者(带测试样例)

    1 缘起 学习消息队列的过程中,先补习了RabbitMQ相关知识, 接着又重温了Kafka相关的知识, 发现,我并没有积累Java原生操作Kafka的文章, 只使用SpringBoot集成过Kafka ...

  5. BlackBerry PlayBook NDK 官方样例介绍。

    在PlayBook NDK环境中可以通过导入样例的方式创建新项目,所创建的项目中会包括样例的源代码和相关配置文件,开发人员可以直接进行测试,是很好的PlayBook NDK 开发入门资料.现在的样例不 ...

  6. Jackson 使用样例

    目录 前言 一.反序列化日期参数 二.反序列化本地多态参数 1. 通用Request和通用的RequestParam 2. 按ID请求参数 3. 按时间范围请求 4.具体使用 三.反序列化第三方包中的 ...

  7. java自定义findbugs规则_静态代码扫描 (三)——FindBugs 自定义规则入门

    准备工作 由于 FindBugs 是分析编译后的 class 文件,也就是字节码文件.我们需要了解 FindBugs 底层的处理机制.根据FindBugs 官网文档描述,FindBugs 使用了BCE ...

  8. eBPF动手实践系列一:解构内核源码eBPF样例编译过程

    作者:闻茂泉 他山之石 了解和掌握纯c语言的ebpf编译和使用,有助于我们加深对于eBPF技术原理的进一步掌握,也有助于开发符合自己业务需求的高性能的ebpf程序.目前常见和主流的纯c语言的ebpf编 ...

  9. 一文入门HTML+CSS+JS(样例后续更新)

    一文入门HTML+CSS+JS(样例后续更新) 前言 HTML,CSS和JS的关系 HTML head元素 title link meta body元素 设置网页正文颜色与背景颜色 添加网页背景图片 ...

最新文章

  1. 这个宝藏工具,给你一种黑客般的感觉
  2. KeystoneERROR 1045 (28000):Access denied for user 'keystone'@'controller'(using password YES)HTTP500
  3. 团队作业5-Alpha版本测试报告(彼岸芳华队)
  4. 小米平板2更新bios版本_【军团战争5.25】新版本更新预告第一弹,2级兵3次升级!...
  5. msg批量转html,SysTools MSG Converter(MSG格式转换器)
  6. 框架画Button的入口
  7. Linux系统时间和java的时间不一致
  8. Opencv之缺少api-ms-win-downlevel-shlwapi-l1-1-0
  9. 鼠标左键长按功能的实现
  10. 非旋转Treap及可持久化[Merge,Split]
  11. python-socket作业
  12. Atitit Spring事务配置不起作用可能出现的问题: .是否是数据库引擎设置不对造成的【笔者就遇到了这个问题,由于笔者使用的是mysql数据,但是在创建表的时候引擎默认(mysql中引擎默认为
  13. 2019年泰迪杯技能赛A题心得
  14. PX4固件通过UART连接串口读取超声波,和树莓派3通信(似乎讲了怎么添加驱动程序,添加自定义msg,还有uORB消息订阅,佩服,感觉做了我想做的!)
  15. 优酷KUX1080转码工具如何将KUX视频转换成MP4格式 1
  16. 【12c】直方图Histograms
  17. Context-aware Attentional Pooling (CAP) for Fine-grained Visual Classification
  18. NR PRACH(六) type 2(2-step) RA基本过程及时频域映射
  19. shell wc命令
  20. win7计算机无法拖到任务栏,win7系统程序图标无法拖动至任务栏的解决方法

热门文章

  1. win10下 yolov8 tensorrt模型加速部署【实战】
  2. 加拿大学者:乔布斯不是爱迪生 其实没那么伟大
  3. software reporter tool占用高_裸车18万喜提沃尔沃S60L,车主:档次不输迈腾,性价比很高_搜狐汽车...
  4. 网站尾部文件版权声明几个写法
  5. 2022年航空行业报告:航空复苏数据透视,海外航司恢复节奏的背后
  6. 【计算机视觉OpenCV基础】实验四 尺寸测量
  7. CSS入门(网页皮肉)——知识汇总①
  8. Python numpy.vander函数方法的使用
  9. Pb菜单工具栏图标顺序的设置
  10. MySQL分组统计及占比分析的方法实现