PMD 自定义规则实践入门样例

PMD 自定义规则实践入门样例PMD 自定义规则实践入门样例

原文:https://testerhome.com/topics/4918

准备工作

目录简介

  • 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按钮:



这里写图片描述

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

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
WhileStatement Expression Statement StatementExpression

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

WhileStatement Expression Statement Block BlockStatement Statement StatementExpression

这下能明显的看到了比之前多处了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 { 
       }
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)); } }

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扫描错误代码,验证是否能触发自定义规则

这里写图片描述


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

<?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>
这里写图片描述


成功!
小技巧: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规则,下一篇文章将重点分析复杂规则如何编写,敬请期待。

参考文献









版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/230363.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月3日 上午8:01
下一篇 2026年3月3日 上午8:22


相关推荐

  • C语言 文件读写的实现

    C语言 文件读写的实现关于C语言的文件读写,我将介绍下面这几种方式:字符的读写:使用fgetc()函数和fputc()函数;字符串的读写:使用fgets()函数和fputs()函数;格式化的读写(主要用于文本文件):使用fscanf()函数和fprintf()函数。字符读写:1.fputc()函数fputc(c,fp);//用于将一个字符写入文件其中,…

    2022年5月5日
    54
  • 23机房工程

    23机房工程

    2021年8月25日
    60
  • 微信小程序开发实战1 微信小程序开发概述

    微信小程序开发实战1 微信小程序开发概述1.微信小程序开发概述1.1微信小程序的特点微信小程序是微信平台提供的一种开放技术,微信小程序为企业用户服务,用于建立一种移动端的“轻应用”,这种应用是不需要下载安装即可使用的应用,用户扫一扫或者搜一下即可打开应用。用户也不用关心是否安装了太多应用的而造成手机空间不足问题。微信小程序的推出后,与订阅号、服务号、企业号并列成为微信的企业应用体系。图1-1微信公众平台产品类型微信小程序运行在微信平台之上,微信平台对不同的手机平台已经做了兼容。使用微信小程序开发的应用,不需要兼容多个平台,开发完成后可

    2022年7月16日
    34
  • HTTP.SYS远程代码执行漏洞(CVE-2015-1635,MS15-034)

    HTTP.SYS远程代码执行漏洞(CVE-2015-1635,MS15-034)漏洞描述及渗透过程HTTP协议堆栈(HTTP.sys)中存在一个远程执行代码漏洞,该漏洞是在HTTP.sys不正确地分析特制HTTP请求时引起的。漏洞危害攻击者只需要发送恶意的http请求数据包,就可能远程读取IIS服务器的内存数据,或使服务器系统蓝屏崩溃。修复建议1)微软官方已经给出修复补丁(KB3042553),用户安装修复补丁即可。变通办法,禁用IIS内核缓存(可能降低IIS…

    2022年7月18日
    21
  • FARPOINT 常见用法

    FARPOINT 常见用法1 AllowCellOve 获取或设置单元格里的内容超出时 是否放在邻近的单元格 2 AllowColumnM 获取或设置是否可以移动列 3 AllowDragDro 获取或设置是否可以拖动选定的对象 如行 单元格 选择的区域 里面的值也会跟着变化 4

    2026年3月19日
    2
  • java 基础高级面试题及答案_20个高级Java开发面试题及答案,干货!!!

    java 基础高级面试题及答案_20个高级Java开发面试题及答案,干货!!!这是一个高级Java面试系列题中的第一部分。这一部分论述了可变参数,断言,垃圾回收,初始化器,令牌化,日期,日历等等Java核心问题。1.什么是可变参数?可变参数允许调用参数数量不同的方法。请看下面例子中的求和方法。此方法可以调用1个int参数,或2个int参数,或多个int参数。//int(type)followed…(threedot’s)issyntaxofavari…

    2022年5月22日
    38

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号