Qtest专栏

静态代码扫描 (一)——PMD 自定义规则入门

2017-06-05  本文已影响200人  Qtest

阅读该文章前,最好已经对PMD有了初步的认识和了解,可参考静态分析工具PMD使用说明

准备工作

首先在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的可执行包。

目录简介


自定义规则实现思路

  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

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

WhileStatement
 Expression
 Statement
  StatementExpression

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

WhileStatement
 Expression
 Statement
  Block
   BlockStatement
    Statement
     StatementExpression

这下能明显的看到了比之前多处了BlockBlockStatement这两个节点。
这样我们只需要写一个规则检查WhileStatement下有没有Block节点,如果没有Block节点,那就说明While语句后面没有大括号,就可以报警告知这里是有问题的。
顺便提一句,所有的结构信息,比如一个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类,名称为WhileLoopsMustUseBracesRule.java。
新建的位置也是有要求的,以我在上文中介绍的目录结构为例:
新的规则类位置:pmd-src-5.4.1\pmd-java\src\main\java\net\sourceforge\pmd\lang\java\rule\这个目录下即可。
截图如下,标红处就是汇聚了所有规则文件的rule目录:

这里写图片描述
成功!使用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使用说明

Qtest团队公众号

关注公众号,第一时间收到我们推送的新文章~


360Qtest二维码.jpg
上一篇下一篇

猜你喜欢

热点阅读