当今的软件开发需要使用许多不同的工具和技术来确保代码质量和稳定性。PMD是一个流行的静态代码分析工具,可以帮助开发者在编译代码之前发现潜在的问题。在本文中,我们将讨论如何在Gradle中使用PMD,并介绍一些最佳实践。

什么是PMD?

PMD是一个用于Java代码的静态代码分析工具。它可以帮助开发者找出潜在的问题,如代码重复、未使用的变量、错误的异常处理等。PMD支持多种规则,可以根据具体项目的需要进行配置。其工作原理参考How PMD Works

PMD支持通过命令行界面(CLI, Command Line Interface for batch scripting)和其他多种集成方式,比如MavenGradleJava API等等。

PMD在Gradle中配置和使用

Gradle中自带了PMD插件,插件的默认版本可以通过源码DEFAULT_PMD_VERSION知道。使用和配置可以参考The PMD Plugin,页面左上角可以选择Gradle版本,确保查看的版本和你使用的Gradle版本一致,因为很多PMD的配置属性或者功能不一定在每个版本都有。

通过页面左上角选了其他版本后跳转的地址是Gradle文档的首页,而不是PMD插件的文档页。我们可以通过修改The PMD Plugin链接中的8.0.2为其他版本号即可跳转到对应Gradle版本包含的PMD插件文档的页面。比如:

当前最新版:https://docs.gradle.org/current/userguide/pmd_plugin.html

7.3.3版本:https://docs.gradle.org/7.3.3/userguide/pmd_plugin.html

在项目build.gradle文件中增加以下内容应用插件和扩展PMD,参考UsageConfiguration,更多的配置属性可以参考PmdExtension

plugins {
id 'pmd'
} pmd {
// 是否将 PMD 结果输出到终端
consoleOutput = true
// 要使用的PMD版本
toolVersion = "6.21.0"
// 规则优先级阈值,低于这个优先级则会被忽略
rulesMinimumPriority = 5
// 使用的规则集配置文件路径
ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]
}

插件会生成两个主要的PMD TaskpmdMainpmdTest分别对main和test两个项目源文件目录使用PMD进行代码检查。

找到IDEA Gradle窗口 > Tasks > other,双击生成的Task;或者在项目根目录运行./gradlew pmdMain都可以运行PMD。检查结果将输出到终端中(前提是配置了consoleOutput = true),违反了PMD规则的类会给出完整的跳转路径以及规则提示信息。

最后还会给出一个报告的地址,内容包含了输出到终端的信息。Problem列出了规则的提示,点击可以跳转到PMD规则描述文档对应的位置。

Gradle PMD Plugin扩展属性

在这里我们将PMD插件的扩展属性作用进行说明,参考PmdExtension,这个文档详细说明了各个属性的作用、默认值和配置示例。如果文档描述的不是很清楚也可以参考PMD CLI options的对应描述。

consoleOutput

是否将结果输出到终端(System.out)允许值为true|false

ignoreFailures

如果出现了警告,是否允许继续构建,允许值为true|false

配置为否(false),在执行build的时候(build任务中默认包含了pmdMain和pmdTest),如果发现了代码有违反规则,将会中断构建过程;配置为是(true),将不会中断构建,只是输出报告信息。

maxFailures

停止构建前允许的最大失败次数。

incrementalAnalysis

是否开启增量分析,允许值为true|false。在pmd docs Incremental Analysis中详细描述了增量分析的相关信息。简单来说,开启了增量分析,PMD会缓存分析数据和结果,后续分析仅查看那些新的/已更改的文件,以此显著减少分析的时间,在Gradle中,这个功能使用PMD6.0.0及以上版本才有。

但是有一些情况会导致增量分析的缓存失效:使用PMD的版本发生了变化;使用的规则集已更改;被分析的代码的类路径已更改;被分析代码依赖的库的类路径已更改。具体参考When is the cache invalidated?

在以上前提下,即使切换分支缓存也是有效的,甚至还支持在不同的机器重复使用缓存文件。参考Can I reuse a cache created on branch A for analyzing my project on branch B?Can I reuse a cache file across different machines?

reportsDir

报告生成的路径。

ruleSetFiles

要使用的自定义规则集文件路径,可以在files()中填多个路径。

ruleSetFiles = files("config/pmd/myRuleSet.xml")

ruleSetConfig

ruleSetFiles的作用一样,不过只能填一个文件路径。

ruleSetConfig = resources.text.fromFile("config/pmd/myRuleSet.xml")

ruleSets

指定使用的规则集,默认值为["category/java/errorprone.xml"]。建议如果配置了ruleSetFiles或者ruleSetConfig,就将ruleSets配置为空(ruleSets = []),以免互相干扰,官方文档Custom ruleset给出的例子也是如此。

ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]

rulesMinimumPriority

每个规则都有个优先级,是从 1 到 5 的整数,其中 1 是最高优先级,参考[Message and priority overriding,每个规则的优先级参考Java RulesrulesMinimumPriority的作用是配置报告的最低优先级,低于这个优先级的规则将被忽略。比如配置rulesMinimumPriority = 4,优先级为 5 的规则将被忽略。

sourceSets

作为 checkbuild 任务的一部分进行分析的源代码集合,配置方式参考SourceSet

targetJdk

PMD使用的JDK版本。有些规则可能会要求JDK的最低或者最高版本,具体要求参考Java Rules

threads

PMD 运行时使用的线程数。

toolVersion

要使用的PMD的版本。

为项目自定义合适的规则集

规则分类和查找

PMD能检测的语音有很多种(后面内容以Java为例),针对不同的语音,PMD内置了很多检测规则,并归为了以下几个类别:

  1. 最佳实践(Best Practices):这些规则执行普遍接受的最佳实践。
  2. 代码风格(Code Style):这些规则强制执行特定的编码风格。
  3. 设计(Design):帮助您发现设计问题的规则。
  4. 文档(Documentation):这些规则与代码文档有关。
  5. 容易出错(Error Prone):用于检测损坏、极度混乱或容易出现运行时错误的构造的规则。
  6. 多线程(Multithreading):这些是在处理多个执行线程时标记问题的规则。
  7. 性能(Performance):标记次优代码的规则。
  8. 安全性(Security):标记潜在安全漏洞的规则。

Java Rules列出了所有相关的规则,点击蓝色字符可以跳转到规则的详细描述页面。

下图是规则AbstractClassWithoutAbstractMethod文档描述的信息,其他规则的描述可能还会包含JDK版本的要求,其他可配置属性等等。

需要注意有的规则可能被标记为Deprecated代表被弃用了。

配置规则集

我们可以编辑XML格式的规则集文件,指定我们项目要执行的规则,参考Making rulesets。下面是没有包含任何规则的规则集文件的模版。

<?xml version="1.0"?>

<ruleset name="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 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <description>
My custom rules
</description> <!-- Your rules will come here --> </ruleset>

从上文我们可以知道PMD内置的每个规则都会提供引用实例,我们引用单个规则的时候,只需要将示例的XML代码复制到规则集文件中即可。

<rule ref="category/java/errorprone.xml/EmptyCatchBlock" />

ref中填写的路径category/java/bestpractices.xml/AbstractClassWithoutAbstractMethod我们可以明显看到它是按照内置规则集文件路径/规则名称的格式组织的,一个内置规则集文件对应了一个分类。

我们可以引用内置规则集文件实现批量引入分类下的所有规则,每个分类对应的XML文件名可以参考GitHub pmd-java resources。再通过exclude指定规则的名称来排除某些规则。

<rule ref="category/java/codestyle.xml">
<exclude name="WhileLoopsMustUseBraces"/>
<exclude name="IfElseStmtsMustUseBraces"/>
</rule>

我们可以使用exclude-pattern排除某些文件,使其不被PMD检查,也可以使用include-pattern包含的方式。如果两种方式都包含相同的文件,最终这个文件会被PMD检查。

<?xml version="1.0"?>
<ruleset name="myruleset"
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 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>My ruleset</description> <exclude-pattern>.*/some/package/.*</exclude-pattern>
<exclude-pattern>.*/some/other/package/FunkyClassNamePrefix.*</exclude-pattern>
<include-pattern>.*/some/package/ButNotThisClass.*</include-pattern> <!-- Rules here ... --> </ruleset>

规则集文件编辑好后,使用ruleSetFiles或者ruleSetConfig配置路径。比如下面配置的意思是指向了项目根目录下的/code-analysis/pmd/rulesets/custom-rule.xml

ruleSetFiles = files("${project.rootDir}/code-analysis/pmd/rulesets/custom-rule.xml"

配置规则

规则引用的同时,我们可以覆盖其原有的一些配置,比如提示消息message和优先级priority

<rule ref="category/java/errorprone.xml/EmptyCatchBlock"
message="Empty catch blocks should be avoided" >
<priority>5</priority>
</rule>

某些规则可能有特定的属性,我们也可以将其覆盖。这些特定的属性Java Rules中都有提供,比如下面这个例子参考NPathComplexity

<rule ref="category/java/design.xml/NPathComplexity">
<properties>
<property name="reportLevel" value="150"/>
</properties>
</rule>

有些属性可以提供多个值,这种情况下可以通过分隔符来提供,比如竖线(|)或逗号(,)。

<property name="legalCollectionTypes"
value="java.util.ArrayList|java.util.Vector|java.util.HashMap"/>

抑制警告

有时候PMD可能会产生误报,这种时候我们可以通过抑制警告让PMD跳过对这些代码的检查。

从Java 1.5开始可以使用注解@SuppressWarnings来标记类或者方法。

  • @SuppressWarnings('PMD')抑制所有PMD的警告。
  • @SuppressWarnings("PMD.UnusedLocalVariable")抑制规则UnusedLocalVariable的警告。
  • @SuppressWarnings({"PMD.UnusedLocalVariable", "PMD.UnusedPrivateMethod"})抑规则UnusedLocalVariableUnusedPrivateMethod的警告。
  • @SuppressWarnings("unused")JDK里面的unusedPMD也遵守,抑制所有跟未使用相关的警告。比如:UnusedLocalVariableUnusedPrivateMethod

在警告提示的代码行的末尾加上注释// NOPMD也可以抑制这一行引起的警告,参考NOPMD

在规则集文件中也可以配置要抑制警告的文件,匹配的方式可以是正则表达式或者XPath,具体可以了解The property violationSuppressRegexThe property violationSuppressXPath

第三方规则集

除了PMD内置的规则集,我们还可以引入第三方规则集。在3rd party rulesets中列出了一些,还有阿里Java开发规范p3c也基于PMD开发一套规则集,从它的pom.xml可以了解到是基于PMD6.15.0版本。

参考Dependency management引入规则集依赖,在规则集配置中引入提供的规则即可。

dependencies {
pmd "com.alibaba.p3c:p3c-pmd:2.1.1"
}

需要注意的是,第三方的规则集很可能没有按照PMD内置规则集那样分类,它们提供的规则配置文件目录也可能不一样,比如p3c的规则配置文件都在/resources/rulesets/目录下并独自定义了一套分类。

其他技巧

PMD的最新官方文档地址是:https://docs.pmd-code.org/latest/pmd_userdocs_tools.html。链接中的latest对应了版本号,指向的是当前最新版本,如果想查看其他版本的文档,修改为对应的版号即可。比如6.39.0版本的链接为:https://docs.pmd-code.org/pmd-doc-6.39.0/index.html。不过可能只有比较新的一些版本才能看到对应的文档。

官方提供了一个PMD的最佳实践可以了解下。

PMD还有跟特定语言相关的文档,比如Java support,里面有支持的JDK版本等信息。

如果使用过程中遇到了问题,可以参考Getting Help从这些网站里面寻找帮助github discussionsgithub issuesstackoverflow tagged pmd

PMD官方文档还提供了Copy/Paste Detector (CPD)关信息,CPD可以用于检测重复代码。还提及了Duplicate Code教我们遇到重复代码如何消除,以及一个关于设计模式的网站Design Patterns

关于PMD这个名字,并没有特殊的含义,作者纯粹只是觉得这几个字母放一起作为名称挺好的,来自What does 'PMD' mean?

参考

GitHub - pmd

PMD - docs

Gradle - The PMD Plugin

PMD插件:你必须掌握的代码质量工具!的更多相关文章

  1. JSLint是一个JavaScript的代码质量工具

    JSLint是一个JavaScript的代码质量工具 可能都或多或少的知道JSLint是一个JavaScript的代码质量工具,一个JavaScript语法检查器和校验器,它能分析JavaScript ...

  2. Docker+Jenkins持续集成环境(3)集成PMD、FindBugs、Checkstyle静态代码检查工具并邮件发送检查结果

    为了规范代码,我们一般会集成静态代码检测工具,比如PMD.FindBugs.Checkstyle,那么Jenkins如何集成这些检查工具,并把检查结果放到构建邮件里呢? 今天做了调研和实现,过程如下 ...

  3. RubyCritic:一款不错的检测代码质量工具

    关注代码质量是高效开发必须要做的一件事,那么在 Ruby 开发的过程中,是否有什么好的代码质量检测工具呢?下面由 Ruby 工程师路英瑞介绍一下 RubyCritic--一款还不错的代码质量检测工具. ...

  4. 用 Eclipse 插件提高代码质量

    如果能在构建代码前发现代码中潜在的问题会怎么样呢?很有趣的是,Eclipse 插件中就有这样的工具,比如 JDepend 和 CheckStyle,它们能帮您在软件问题暴露前发现这些问题.在 让开发自 ...

  5. 代码静态分析工具--PMD,Findbugs,CheckStyle

    最近学习Mybatis的官方文档,看到了[项目文档]一节有很多内容没有见过,做个笔记,理解一下. PMD 扫描Java源代码,查找潜在的问题,如: 可能的bugs,如空的try/catch/final ...

  6. 使用JSLint提高JS代码质量

    随着富 Web 前端应用的出现,开发人员不得不重新审视并重视 JavaScript 语言的能力和使用,抛弃过去那种只靠“复制 / 粘贴”常用脚本完成简单前端任务的模式.JavaScript 语言本身是 ...

  7. Findbug在项目中的运用--提高代码质量

     FindBugs是一个静态分析工具,它检查类或者 JAR文件,将字节码与一组缺陷模式进行对比以发现可能的问题.有了静态分析工具,就可以在不实际运行程序的情况对软件进行分析 第一 手动安装 在Ec ...

  8. 提高代码质量 CheckStyle FindBugs PMD

    提高代码质量-工具篇 注:这是一篇翻译文章,原文:How to improve quality and syntax of your Android code,为了理解连贯,翻译过程中我修改了一些陈述 ...

  9. 提高Java代码质量的Eclipse插件之Checkstyle的使用详解

    提高Java代码质量的Eclipse插件之Checkstyle的使用详解 CheckStyle是SourceForge下的一个项目,提供了一个帮助JAVA开发人员遵守某些编码规范的工具.它能够自动化代 ...

  10. 提高Java代码质量的Eclipse插件之Checkstyle的使用具体解释

    CheckStyle是SourceForge下的一个项目,提供了一个帮助JAVA开发者遵守某些编码规范的工具.它可以自己主动化代码规范检查过程.从而使得开发者从这项重要可是枯燥的任务中解脱出来. Ch ...

随机推荐

  1. Spring系列之基于注解的容器配置7

    目录 基于注解的容器配置 @Required(弃用) `@Autowired` `@Primary` @Qualifier 使用泛型作为自动装配限定符 `@Resource` `@Value` 使用` ...

  2. 如何找到并使用makecert.exe

    如果安装visual studio 后,visual studio command  仍然无法识别 makecert.exe 命令. 则需要手动安装 Windows Software Developm ...

  3. 使用python的turtle库画一个冰墩墩

    目录 先看效果图 设置一个画布 画左手和手内 画轮廓和其他部分 画细节(眼睛.鼻子.嘴巴等) 画头部彩虹 画五环标志 最后(别忘记还有一个结束) 先看效果图 设置一个画布 点击查看代码 import ...

  4. maven工程入门

    1. 为什么要使用maven? 毕业开始工作,项目组用的maven-spring开发的,不得不了解一下,看过很多介绍,其中maven最大的特点就是 管理jar包和版本管理 (参考:https://ww ...

  5. 字符串替换Replace仅替换第一个匹配项

    C#里面的String.Replace(string,string)方法替换的时候是替换所有的匹配项,但是有时候我们会遇到这样的需求,就是只替换第一个匹配项. 我这里自己写另一个方法来实现这个功能,求 ...

  6. element的el-table使用模板插槽在火狐和IE无法显示tooltip(浏览器兼容)

    el-table中使用show-overflow-tooltip属性,配合tooltip出现的浏览器兼容性问题 el-table中使用show-overflow-tooltip属性内容过长被隐藏时显示 ...

  7. JS样式获取的封装方法

    样式获取 style属性 只能获取标签内容style属性里面存在的一些样式 如果你需要获取对应的全局所有地方设置样式 我们就需要采用一些方法 getComputedStyle 方法属于window的方 ...

  8. mongoose Schema字段的含义

    var schema3 = new Schema({ test: { type: String, lowercase: true, // 总是将test的值转化为小写 uppercase: true, ...

  9. 代理模式_v1

    代理模式 概念: 1.真实对象:要被代理的对象 2.代理对象 3.代理模式 : 代理对象代理真实对象,达到增强真实对象功能的作用 实现方式: 1.静态代理:有一个类文件描述代理模式 2.动态代理:在内 ...

  10. css卡片样式

    .view-1-user-card { margin: 20px 5% 10px 5%; height: 124px; width: 90%; background: linear-gradient( ...