ESLint: +29 条 P1/P2 规则 + 12 条 TS 专属规则(含 no-shadow/no-array-constructor 冲突处理) Stylelint: 集成 stylelint-config-recommended + 27 条额外规则 PMD: 排除 20 弃用 + 17 噪音规则,补启 Security/Multithreading,274+12 条精选 SQL-lint: 内置精选 57 条规则配置 + 按 tier 分级 severity + 无项目配置时自动注入临时配置 模板导出: export-service.ts 导出 2-sheet xlsx(复用 xlsx 零新依赖) 模板导入: template-converter.ts 固定列映射解析 + dedup-prompt.ts AI 语义去重 导入预览增强: 错误规则分组置顶只读、跳过行提示、空数据提示 i18n: 新增 18 条模板导入/导出相关翻译 WebView: 静态分析/自定义规则项默认可展开显示 suggestion
33 KiB
PMD 规则增强设计书
一、背景与目标
1.1 现状
当前 PmdAdapter(src/adapters/pmd.ts)通过内置 JAR 包调用 PMD 7.26.0,使用 XML ruleset 文件配置规则。Java 规则集(jars/pmd/pmd-java-ruleset.xml)采用全分类引用方式启用规则:
<ruleset name="java-ruleset" ...>
<description>Java code review rules</description>
<rule ref="category/java/bestpractices.xml"/>
<rule ref="category/java/codestyle.xml"/>
<rule ref="category/java/design.xml"/>
<rule ref="category/java/errorprone.xml"/>
<rule ref="category/java/multithreading.xml"/>
<rule ref="category/java/performance.xml"/>
</ruleset>
这种方式启用了 6 个分类共 309 条规则(活跃 289 + 已弃用 20),存在以下问题:
- 启用 20 条已弃用规则:这些规则已标记为弃用,计划在 PMD 8.0.0 中移除,继续启用增加噪音并为未来迁移增加负担
- 启用 17 条误报率高的噪音规则:如
TooManyMethods、CyclomaticComplexity、ShortVariable等阈值类规则,在真实项目中产生大量噪音诊断 - 未启用 Security 分类:Java Security 分类包含 2 条高价值安全规则(
HardCodedCryptoKey、InsecureCryptoIv),当前未启用 - 全分类引用缺乏精细控制:无法排除特定规则,所有规则(含弃用和噪音)均被启用
JSP 规则集(jars/pmd/pmd-jsp-ruleset.xml)已启用全部 5 个分类共 12 条规则,全部活跃,无需调整。
1.2 目标
将 Java 规则集从"全分类引用(309 条)"优化为"精选规则集(274 条活跃规则)":
- 补启 Security 分类(+2 条):新增 2 条高价值安全规则
- 排除 20 条已弃用规则(-20 条):为 PMD 8.0.0 迁移做准备
- 排除 17 条噪音规则(-17 条):减少误报,提升诊断信噪比
- 同步更新
static-rules.json:确保去重检测覆盖完整
最终 Java 启用规则数:289(当前活跃)- 17(噪音)+ 2(安全)= 274 条活跃规则,覆盖 7 个分类。
1.3 设计原则
- 从粗到精:PMD 的增强方向与 ESLint/Stylelint 相反——不是"从少到多"地添加规则,而是"从粗到精"地从全分类引用中排除弃用和噪音规则。这一特点贯穿整个设计
- 不破坏现有配置优先级:全局配置 > 项目配置 > 内置配置,三层择一逻辑不变
- 不引入新依赖:PMD 通过内置 JAR 包调用,无需修改
package.json - 不修改适配器代码:
PmdAdapter读取 ruleset XML 文件,仅需修改 XML 文件本身 - 同步更新去重数据:
static-rules.json必须同步追加 Security 规则、移除已弃用规则 - 不修改规则 ID 前缀格式:诊断结果仍使用
pmd:{ruleName}(Java)和pmd-jsp:{ruleName}(JSP)格式
二、影响范围分析
2.1 需要修改的文件
| 文件 | 修改类型 | 修改内容 |
|---|---|---|
jars/pmd/pmd-java-ruleset.xml |
配置修改 | 为 6 个分类添加 <exclude> 排除弃用和噪音规则,新增 Security 分类引用 |
src/rules/static-rules.json |
数据修改 | rules.pmd 数组追加 2 条 Security 规则,移除 20 条已弃用规则条目 |
2.2 不需要修改的文件
| 文件 | 原因 |
|---|---|
package.json |
PMD 通过内置 JAR 包调用,不依赖 npm 包,无需添加依赖 |
src/adapters/pmd.ts |
适配器读取 ruleset XML 文件路径,不涉及规则配置逻辑,无需修改 |
scripts/build.mjs |
JAR 包和 XML 文件作为静态资源打包,构建脚本无需修改 |
jars/pmd/pmd-jsp-ruleset.xml |
JSP 规则集已完整启用 5 个分类,无需调整 |
src/adapters/jsp.ts |
JSP 适配器复用 PMD JAR,内部读取 JSP ruleset,不受 Java ruleset变更影响 |
src/orchestrator/orchestrator.ts |
仅做调度,不涉及配置逻辑 |
src/config/linter.ts |
配置读取层不变 |
2.3 不受影响的功能
- 全局配置(
linters.pmdConfigPath):用户指定 ruleset 文件时完全替代内置 ruleset,不受影响 - 项目配置(项目根目录下的
.xmlruleset 文件):存在时完全替代内置配置,不受影响 - JSP 审查功能:JSP 规则集无变化,审查行为不变
- 规则 ID 输出格式:Java 仍为
pmd:{ruleName},JSP 仍为pmd-jsp:{ruleName},不变 - PMD JAR 包版本:仍为 7.26.0,不升级
三、详细设计
3.1 修改 jars/pmd/pmd-java-ruleset.xml
3.1.1 当前代码
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="java-ruleset"
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>Java code review rules</description>
<rule ref="category/java/bestpractices.xml"/>
<rule ref="category/java/codestyle.xml"/>
<rule ref="category/java/design.xml"/>
<rule ref="category/java/errorprone.xml"/>
<rule ref="category/java/multithreading.xml"/>
<rule ref="category/java/performance.xml"/>
</ruleset>
3.1.2 修改后代码
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="java-ruleset"
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>Java code review rules (274 active rules, 7 categories)</description>
<!-- Best Practices:排除 9 条已弃用规则 -->
<rule ref="category/java/bestpractices.xml">
<exclude name="DefaultLabelNotLastInSwitchStmt"/>
<exclude name="JUnit4TestShouldUseAfterAnnotation"/>
<exclude name="JUnit4TestShouldUseBeforeAnnotation"/>
<exclude name="JUnit4TestShouldUseTestAnnotation"/>
<exclude name="JUnit5TestShouldBePackagePrivate"/>
<exclude name="JUnitAssertionsShouldIncludeMessage"/>
<exclude name="JUnitTestContainsTooManyAsserts"/>
<exclude name="JUnitTestsShouldIncludeAssert"/>
<exclude name="SwitchStmtsShouldHaveDefault"/>
</rule>
<!-- Code Style:排除 2 条已弃用 + 4 条噪音规则 -->
<rule ref="category/java/codestyle.xml">
<exclude name="GenericsNaming"/>
<exclude name="UnnecessaryLocalBeforeReturn"/>
<exclude name="LongVariable"/>
<exclude name="ShortVariable"/>
<exclude name="ShortMethodName"/>
<exclude name="ShortClassName"/>
</rule>
<!-- Design:排除 2 条已弃用 + 10 条噪音规则 -->
<rule ref="category/java/design.xml">
<exclude name="AvoidCatchingGenericException"/>
<exclude name="UseObjectForClearerAPI"/>
<exclude name="TooManyMethods"/>
<exclude name="CyclomaticComplexity"/>
<exclude name="NPathComplexity"/>
<exclude name="CognitiveComplexity"/>
<exclude name="NcssCount"/>
<exclude name="TooManyFields"/>
<exclude name="ExcessiveParameterList"/>
<exclude name="ExcessivePublicCount"/>
<exclude name="ExcessiveImports"/>
<exclude name="CouplingBetweenObjects"/>
</rule>
<!-- Error Prone:排除 6 条已弃用规则 -->
<rule ref="category/java/errorprone.xml">
<exclude name="AvoidCatchingNPE"/>
<exclude name="AvoidCatchingThrowable"/>
<exclude name="AvoidLosingExceptionInformation"/>
<exclude name="DontImportSun"/>
<exclude name="NonCaseLabelInSwitchStatement"/>
<exclude name="UselessOperationOnImmutable"/>
</rule>
<!-- Multithreading:排除 3 条噪音规则 -->
<rule ref="category/java/multithreading.xml">
<exclude name="AvoidUsingVolatile"/>
<exclude name="DoNotUseThreads"/>
<exclude name="AvoidSynchronizedStatement"/>
</rule>
<!-- Performance:排除 1 条已弃用规则 -->
<rule ref="category/java/performance.xml">
<exclude name="TooFewBranchesForASwitchStatement"/>
</rule>
<!-- Security:新增,2 条高价值安全规则 -->
<rule ref="category/java/security.xml"/>
</ruleset>
3.1.3 设计说明
为什么仍使用分类引用 + exclude,而非逐条引用?
PMD 的分类引用(<rule ref="category/java/X.xml"/>)会启用该分类下的全部规则,通过 <exclude> 排除特定规则。与 ESLint/Stylelint 逐条列出规则名的方式相比,PMD 的分类引用方式有以下优势:
- 维护成本低:PMD 版本升级新增规则时,分类引用自动包含新规则,无需手动添加。逐条引用则需要每次升级都对比新增规则并手动追加
- 排除列表更稳定:需要排除的规则(弃用 + 噪音)远少于需要保留的规则(37 vs 272),维护 exclude 列表比维护 include 列表更简洁
- 符合 PMD 官方推荐:PMD 官方文档推荐使用分类引用作为规则集的基础
为什么不为排除规则配置 <properties> 阈值,而是直接排除?
部分噪音规则(如 CyclomaticComplexity、TooManyMethods)支持通过 <properties> 调整阈值。但本设计选择直接排除而非调阈值,原因如下:
- 阈值类规则的"合理值"因项目而异,无法找到一个通用默认值
- 即使调高阈值,仍会在边界处产生误报
- 代码审查工具的目标是发现确定性 bug,而非度量代码复杂度。复杂度度量更适合 SonarQube 等专门的代码质量平台
- 直接排除减少配置复杂度,用户如有需求可通过自定义 ruleset 单独启用并配置阈值
AvoidCatchingGenericException 的跨分类特殊处理
该规则名在 Design 和 Error Prone 两个分类中各存在一条:
- Design 分类中的为已弃用版本(PMD 7 起迁移至 Error Prone 分类)
- Error Prone 分类中的为活跃版本(引入版本 4.2.6)
在 Design 分类中排除弃用版本后,Error Prone 分类中的活跃版本仍然生效。两版本功能一致,不会出现规则缺失。
3.2 修改 src/rules/static-rules.json
3.2.1 追加 Security 规则
在 rules.pmd 数组中追加 2 条 Security 分类规则条目:
{"id": "pmd/HardCodedCryptoKey", "description": "Do not use hard coded encryption keys"},
{"id": "pmd/InsecureCryptoIv", "description": "Do not use hard coded initialization vectors in encryption operations"}
3.2.2 移除已弃用规则
从 rules.pmd 数组中移除以下 20 条已弃用规则条目(如果存在):
{"id": "pmd/DefaultLabelNotLastInSwitchStmt", "description": "..."},
{"id": "pmd/JUnit4TestShouldUseAfterAnnotation", "description": "..."},
{"id": "pmd/JUnit4TestShouldUseBeforeAnnotation", "description": "..."},
{"id": "pmd/JUnit4TestShouldUseTestAnnotation", "description": "..."},
{"id": "pmd/JUnit5TestShouldBePackagePrivate", "description": "..."},
{"id": "pmd/JUnitAssertionsShouldIncludeMessage", "description": "..."},
{"id": "pmd/JUnitTestContainsTooManyAsserts", "description": "..."},
{"id": "pmd/JUnitTestsShouldIncludeAssert", "description": "..."},
{"id": "pmd/SwitchStmtsShouldHaveDefault", "description": "..."},
{"id": "pmd/GenericsNaming", "description": "..."},
{"id": "pmd/UnnecessaryLocalBeforeReturn", "description": "..."},
{"id": "pmd/AvoidCatchingGenericException", "description": "..."}, // 仅移除 Design 弃用版本的条目
{"id": "pmd/UseObjectForClearerAPI", "description": "..."},
{"id": "pmd/AvoidCatchingNPE", "description": "..."},
{"id": "pmd/AvoidCatchingThrowable", "description": "..."},
{"id": "pmd/AvoidLosingExceptionInformation", "description": "..."},
{"id": "pmd/DontImportSun", "description": "..."},
{"id": "pmd/NonCaseLabelInSwitchStatement", "description": "..."},
{"id": "pmd/UselessOperationOnImmutable", "description": "..."},
{"id": "pmd/TooFewBranchesForASwitchStatement", "description": "..."}
注:
AvoidCatchingGenericException在static-rules.json中可能仅有一条条目(对应 Error Prone 活跃版本)。如果该条目存在,应保留(对应活跃版本)。仅当存在明确标注为 Design 弃用版本的条目时才移除。实际操作时应检查条目内容确认。
3.2.3 噪音规则的处理
17 条噪音规则不从 static-rules.json 中移除。原因:
- 噪音规则仍是 PMD 的有效规则,用户可能通过自定义 ruleset 启用并配置阈值
static-rules.json的用途是展示 linter 支持的规则清单和去重检测,保留噪音规则条目有助于用户了解这些规则的存在- 噪音规则仅在默认 ruleset XML 中排除,不影响
static-rules.json的完整性
3.2.4 修改 linterVersion 字段
"linterVersion": {
"pmd": "7.26.0 (274 Java rules + 12 JSP rules)",
...
}
将原有标记更新为反映实际启用规则数量。
3.2.5 为什么必须同步更新 static-rules.json
static-rules.json 有两个用途:
- UI 展示:在设置面板中展示当前 linter 支持的规则清单
- 去重检测:自定义规则导入时,按 ID 匹配
static-rules.json中的规则,若已存在则提示重复
如果不更新:
- 追加 Security 规则:用户新增的
HardCodedCryptoKey自定义规则不会被识别为重复 - 移除弃用规则:已弃用规则仍出现在规则清单中,误导用户认为这些规则仍被推荐使用
3.3 不需要修改 package.json
PMD 通过内置 JAR 包调用,不依赖 npm 包。当前 package.json 中与 PMD 相关的配置仅为 JAR 包路径(通过 jars/pmd/ 目录引用),无需添加任何 npm 依赖。
// package.json 中无 PMD 相关依赖项,PMD 完全由内置 JAR 包提供
"dependencies": {
"eslint": "^9.39.3",
"stylelint": "^17.14.0",
"typescript-eslint": "^8.56.1",
"xlsx": "^0.18.5"
// 无 PMD 相关条目
}
这与 ESLint/Stylelint 适配器需要添加 @eslint/js 或 stylelint-config-recommended 依赖形成鲜明对比——PMD 的依赖管理完全脱离 npm 体系。
四、新增/排除规则分类详解
4.1 新增:Security 分类(+2 条)
当前 Java 规则集未启用 Security 分类。该分类仅含 2 条规则,均为高价值安全规则。
| 规则 | 检测场景 | 误报评估 |
|---|---|---|
HardCodedCryptoKey |
SecretKeySpec("mykey".getBytes(), "AES") 等硬编码密钥 |
零误报,硬编码密钥几乎一定是安全问题 |
InsecureCryptoIv |
IvParameterSpec(new byte[16]) 等硬编码/空 IV |
零误报,硬编码 IV 破坏加密语义安全 |
Security 分类无需排除任何规则,直接全分类引用
<rule ref="category/java/security.xml"/>。
4.2 排除:已弃用规则(-20 条)
4.2.1 Best Practices 弃用规则(9 条)
均为 JUnit 相关规则在 PMD 7 中的重命名,旧名称已弃用,新名称在同类分类中活跃。
| 弃用规则 | 替代规则 | 弃用原因 |
|---|---|---|
DefaultLabelNotLastInSwitchStmt |
DefaultLabelNotLastInSwitch |
PMD 7.7.0 起支持 switch 表达式,重命名 |
JUnit4TestShouldUseAfterAnnotation |
UnitTestShouldUseAfterAnnotation |
PMD 7 起泛化为通用测试框架 |
JUnit4TestShouldUseBeforeAnnotation |
UnitTestShouldUseBeforeAnnotation |
同上 |
JUnit4TestShouldUseTestAnnotation |
UnitTestShouldUseTestAnnotation |
同上 |
JUnit5TestShouldBePackagePrivate |
JUnitJupiterTestShouldBePackagePrivate |
PMD 7 起重命名 |
JUnitAssertionsShouldIncludeMessage |
UnitTestAssertionsShouldIncludeMessage |
同上 |
JUnitTestContainsTooManyAsserts |
UnitTestContainsTooManyAsserts |
同上 |
JUnitTestsShouldIncludeAssert |
UnitTestShouldIncludeAssert |
同上 |
SwitchStmtsShouldHaveDefault |
NonExhaustiveSwitch |
PMD 7 起重命名,支持 switch 表达式 |
排除弃用规则后,替代规则(活跃版本)仍随分类引用自动启用,功能不受影响。
4.2.2 Code Style 弃用规则(2 条)
| 弃用规则 | 替代规则 | 弃用原因 |
|---|---|---|
GenericsNaming |
TypeParameterNamingConventions |
PMD 7.17.0 起用更通用的命名约定规则替代 |
UnnecessaryLocalBeforeReturn |
无 | 规则价值低,PMD 7.17.0 起移除 |
4.2.3 Design 弃用规则(2 条)
| 弃用规则 | 替代规则 | 弃用原因 |
|---|---|---|
AvoidCatchingGenericException(Design 版) |
AvoidCatchingGenericException(Error Prone 版,活跃) |
迁移至 Error Prone 分类 |
UseObjectForClearerAPI |
无 | PMD 7.26.0 起弃用,规则建议过于主观 |
4.2.4 Error Prone 弃用规则(6 条)
| 弃用规则 | 替代规则 | 弃用原因 |
|---|---|---|
AvoidCatchingNPE |
AvoidCatchingGenericException |
PMD 7.18.0 起合并至通用规则 |
AvoidCatchingThrowable |
AvoidCatchingGenericException |
同上 |
AvoidLosingExceptionInformation |
UselessPureMethodCall |
PMD 7.17.0 起用更通用的规则替代 |
DontImportSun |
UnsupportedJdkApiUsage |
PMD 7.21.0 起用更通用的规则替代 |
NonCaseLabelInSwitchStatement |
NonCaseLabelInSwitch |
PMD 7.7.0 起重命名 |
UselessOperationOnImmutable |
UselessPureMethodCall |
PMD 7.17.0 起用更通用的规则替代 |
4.2.5 Performance 弃用规则(1 条)
| 弃用规则 | 替代规则 | 弃用原因 |
|---|---|---|
TooFewBranchesForASwitchStatement |
TooFewBranchesForSwitch |
PMD 7.7.0 起重命名 |
4.3 排除:噪音规则(-17 条)
4.3.1 Design 噪音规则(10 条)
这些规则均为阈值类复杂度/规模度量规则,误报率高,不适合作为代码审查工具的默认规则。
| 规则 | 默认阈值 | 排除理由 |
|---|---|---|
TooManyMethods |
10 | 工具类、控制器类天然方法多,阈值过于机械 |
CyclomaticComplexity |
10 | 复杂业务逻辑合理需要高复杂度 |
NPathComplexity |
200 | 与圈复杂度类似,机械阈值弊大于利 |
CognitiveComplexity |
15 | 适合作为度量指标而非 lint 规则 |
NcssCount |
100 | 方法/类长度限制因场景而异 |
TooManyFields |
15 | DTO/Entity 类天然字段多 |
ExcessiveParameterList |
10 | 构建器模式、配置类合理需要多参数 |
ExcessivePublicCount |
45 | API 类天然公共成员多 |
ExcessiveImports |
30 | 整合层/门面类合理需要多导入 |
CouplingBetweenObjects |
20 | 耦合度难以精确度量,更适合架构审查工具 |
4.3.2 Code Style 噪音规则(4 条)
这些规则基于名称长度阈值,过于主观,在真实项目中误报率高。
| 规则 | 默认阈值 | 排除理由 |
|---|---|---|
LongVariable |
30 字符 | 描述性变量名通常更长更清晰 |
ShortVariable |
3 字符 | i、x、y 等短名是惯用写法 |
ShortMethodName |
3 字符 | get、set、add、run 等是标准写法 |
ShortClassName |
4 字符 | URL、URI、Map 等是标准缩写 |
4.3.3 Multithreading 噪音规则(3 条)
这些规则过于激进,在通用 Java 项目中误报率高。
| 规则 | 排除理由 |
|---|---|
AvoidUsingVolatile |
volatile 在双重检查锁等场景有合理用途 |
DoNotUseThreads |
面向 J2EE webapp 容器场景,不适用于通用项目 |
AvoidSynchronizedStatement |
synchronized 在简单并发场景有合理用途 |
4.4 不启用:Documentation 分类(6 条)
Documentation 分类共 6 条规则,不适合作为代码审查工具的默认规则。详见推荐规则文档中的"不推荐启用"部分。如团队有强制 Javadoc 需求,可通过自定义 ruleset 单独启用 DanglingJavadoc 等规则。
五、规则级别设计
5.1 PMD priority 体系
PMD 使用 5 级优先级体系(priority 1-5),每条规则有默认优先级,在 ruleset XML 中可通过 <priority> 元素覆盖:
| 优先级 | 名称 | 含义 | 适配器映射(建议) |
|---|---|---|---|
| 1 | Blocker | 高概率的代码错误,运行时必崩 | Error |
| 2 | Critical | 设计问题、严重的最佳实践违反 | Error |
| 3 | Major | 开发实践问题、潜在 bug | Warning |
| 4 | Minor | 代码风格、命名约定 | Info / Hint |
| 5 | Info | 文档、注释相关 | Info / Hint |
5.2 当前优先级策略
本设计不覆盖任何规则的默认优先级。原因:
- PMD 为每条规则设置的默认优先级已经过社区验证,与规则的严重程度匹配
- 分类引用方式自动继承规则的默认优先级,无需逐条配置
- 适配器(
pmd.ts)在解析 PMD 输出时,根据 priority 值映射为 VS Code 诊断严重级别
5.3 排除规则对优先级分布的影响
排除 37 条规则后,剩余 274 条规则的优先级分布(估算):
| 优先级 | 排除前(含弃用/噪音) | 排除后 | 变化 |
|---|---|---|---|
| 1(Blocker) | ~40 | ~40 | 无变化(弃用和噪音规则多为 P2-P4) |
| 2(Critical) | ~60 | ~50 | -10(排除 Design 噪音规则,多为 P2) |
| 3(Major) | ~90 | ~80 | -10(部分噪音规则为 P3) |
| 4(Minor) | ~100 | ~90 | -10(排除 Short/Long 命名规则等 P4) |
| 5(Info) | ~19 | ~14 | -5(Documentation 未启用,弃用规则含部分 P5) |
| 合计 | ~309 | 274 | -35 |
排除的规则主要集中在 P2-P4 级别,这些是误报率最高的级别。P1(Blocker)级规则全部保留,确保高危 bug 检测能力不受影响。
5.4 与 ESLint/Stylelint 级别设计的差异
| 维度 | ESLint | Stylelint | PMD |
|---|---|---|---|
| 级别体系 | error / warn / off |
true(error)/ null(off) |
priority 1-5 |
| 级别配置 | 逐条规则配置 | 逐条规则配置 | 规则默认优先级,可覆盖 |
| 适配器映射 | 直接映射 | true → error | priority 1-2 → Error,3 → Warning,4-5 → Info |
| 本设计操作 | 追加规则配置级别 | 追加规则配置级别 | 不修改优先级,仅排除规则 |
六、兼容性分析
6.1 对现有用户代码的影响
排除规则后,之前被噪音规则标记的诊断将消失,审查结果更加精简:
| 影响程度 | 规则 | 说明 |
|---|---|---|
| 诊断大幅减少 | TooManyMethods 等 10 条 Design 噪音规则 |
大型类不再被标记,减少噪音诊断 |
| 诊断大幅减少 | ShortVariable 等 4 条 Code Style 噪音规则 |
短变量名不再被标记 |
| 诊断大幅减少 | DoNotUseThreads 等 3 条 Multithreading 噪音规则 |
合理使用线程/volatile/synchronized 不再被标记 |
| 诊断少量减少 | 20 条已弃用规则 | 替代规则(活跃版本)仍生效,功能等价 |
| 诊断少量新增 | HardCodedCryptoKey、InsecureCryptoIv |
新增 Security 规则,仅检测硬编码密钥/IV,误报率极低 |
6.2 对 JSP 审查的影响
JSP 规则集无变化,JSP 审查行为完全不受影响。
6.3 对去重功能的影响
static-rules.json 同步更新后:
- 追加 2 条 Security 规则:去重检测范围扩展,用户新增的
HardCodedCryptoKey自定义规则将被正确识别为重复 - 移除 20 条已弃用规则:已弃用规则不再出现在规则清单中,避免误导用户
6.4 对 PMD 8.0.0 迁移的影响
排除 20 条已弃用规则后,未来升级到 PMD 8.0.0(计划移除弃用规则)时:
- ruleset XML 中的
<exclude>引用的弃用规则名将不存在,PMD 会输出警告但不影响其他规则 - 迁移时只需移除这些无效的
<exclude>条目即可 - 提前排除弃用规则可减少迁移时的规则数量变化,降低用户感知
6.5 对构建的影响
- ruleset XML 文件作为静态资源打包,修改不影响构建流程
- JAR 包版本不变,无需更新 JAR 文件
static-rules.json修改不影响构建产物体积(JSON 数据文件)- 无新增 npm 依赖,
package.json和node_modules不变
七、与 ESLint/Stylelint/ts-eslint 适配器的架构对比
7.1 增强方向对比
| 维度 | ESLint / Stylelint / ts-eslint | PMD |
|---|---|---|
| 增强方向 | 从少到多(从 recommended 扩展到更多规则) | 从粗到精(从全分类引用中排除弃用和噪音) |
| 起点 | 官方 recommended 配置(61/41/24 条) | 全分类引用(309 条) |
| 终点 | recommended + 额外规则(92/68/~40 条) | 精选规则集(274 条) |
| 规则数变化 | 增加(+31/+27/~16) | 减少(-35,含排除弃用和噪音,+2 安全) |
| 核心操作 | 追加规则到配置对象 | 从分类引用中排除规则 |
这是 PMD 适配器与其他 linter 适配器最本质的差异。其他 linter 从"太少"走向"刚好",PMD 从"太多"走向"刚好"。
7.2 配置方式对比
| 维度 | ESLint | Stylelint | PMD |
|---|---|---|---|
| 配置格式 | JS 对象(Flat Config) | JS 对象 | XML ruleset |
| 规则引用 | 逐条规则名 + 级别 | 逐条规则名 + 级别 | 分类引用 + exclude |
| 排除方式 | 不列入规则对象 | 不列入规则对象 | <exclude name="..."/> |
| 新增方式 | 追加规则到对象 | 追加规则到对象 | 追加 <rule ref="category/..."/> |
| 官方配置包 | @eslint/js recommended |
stylelint-config-recommended |
无(分类文件随 JAR 提供) |
| 合并机制 | Flat Config 数组后者覆盖 | 对象展开 | XML 引用叠加 |
7.3 依赖管理对比
| 维度 | ESLint / Stylelint | PMD |
|---|---|---|
| 依赖来源 | npm 包 | 内置 JAR 包 |
| 版本管理 | package.json + package-lock.json |
JAR 文件版本(jars/pmd/lib/) |
| 配置包 | @eslint/js、stylelint-config-recommended |
无(分类 XML 随 JAR 内置) |
| 升级方式 | npm update |
替换 JAR 文件 |
package.json 修改 |
需要添加依赖 | 不需要 |
7.4 适配器代码修改对比
| 适配器 | 是否修改代码 | 修改内容 |
|---|---|---|
| ESLint | 是 | getDefaultConfig() 追加 extraRules 对象 |
| Stylelint | 是 | DEFAULT_CONFIG 替换为 recommended + extraRules |
| ts-eslint | 是 | getDefaultConfig() 追加 ts-eslint 额外规则 |
| PMD | 否 | 仅修改 XML 文件和 JSON 数据,适配器代码不变 |
PMD 适配器的设计将规则配置完全外部化到 XML 文件,使得规则增强无需修改任何 TypeScript 代码。这是 PMD 适配器的架构优势。
八、实施步骤
步骤 1:修改 jars/pmd/pmd-java-ruleset.xml
- 为
bestpractices.xml添加 9 条<exclude>(弃用规则) - 为
codestyle.xml添加 6 条<exclude>(2 弃用 + 4 噪音) - 为
design.xml添加 12 条<exclude>(2 弃用 + 10 噪音) - 为
errorprone.xml添加 6 条<exclude>(弃用规则) - 为
multithreading.xml添加 3 条<exclude>(噪音规则) - 为
performance.xml添加 1 条<exclude>(弃用规则) - 在文件末尾追加
<rule ref="category/java/security.xml"/> - 更新
<description>内容
步骤 2:修改 src/rules/static-rules.json
- 在
rules.pmd数组中追加 2 条 Security 规则条目 - 从
rules.pmd数组中移除 20 条已弃用规则条目(逐条检查是否存在) - 更新
linterVersion.pmd为"7.26.0 (274 Java rules + 12 JSP rules)"
步骤 3:验证
- 执行
npm run lint确认项目自身代码无新增报错 - 执行
npm run compile确认编译通过 - 执行
npm run build确认打包成功 - 检查
static-rules.json中 PMD Java 规则数量变化(+2 Security,-20 弃用) - 用测试 Java 文件验证:
- 硬编码密钥触发
pmd:HardCodedCryptoKey - 硬编码 IV 触发
pmd:InsecureCryptoIv - 短变量名不再触发
pmd:ShortVariable - 方法过多不再触发
pmd:TooManyMethods
- 硬编码密钥触发
九、测试要点
9.1 单元测试
| 测试项 | 验证内容 |
|---|---|
| ruleset XML 格式校验 | XML 格式合法,所有 <exclude> 的 name 属性值正确 |
| Security 规则启用 | ruleset 中包含 <rule ref="category/java/security.xml"/> |
| 弃用规则排除 | 20 条弃用规则均在对应分类的 <exclude> 列表中 |
| 噪音规则排除 | 17 条噪音规则均在对应分类的 <exclude> 列表中 |
static-rules.json Security 规则 |
包含 pmd/HardCodedCryptoKey 和 pmd/InsecureCryptoIv |
static-rules.json 弃用规则移除 |
不包含 pmd/JUnit4TestShouldUseTestAnnotation 等已移除的弃用规则 |
9.2 集成测试
| 测试项 | 验证内容 |
|---|---|
| 硬编码密钥触发 Security 规则 | 诊断结果包含 pmd:HardCodedCryptoKey |
| 硬编码 IV 触发 Security 规则 | 诊断结果包含 pmd:InsecureCryptoIv |
短变量名不触发 ShortVariable |
int i = 0; 不产生 pmd:ShortVariable 诊断 |
方法过多不触发 TooManyMethods |
含 15 个方法的类不产生 pmd:TooManyMethods 诊断 |
| 弃用规则不触发 | 代码不产生 pmd:JUnit4TestShouldUseTestAnnotation 等弃用规则诊断 |
| 替代规则仍生效 | switch 缺少 default 触发 pmd:NonExhaustiveSwitch(替代 SwitchStmtsShouldHaveDefault) |
AvoidCatchingGenericException 仍生效 |
catch (Exception e) 触发 pmd:AvoidCatchingGenericException(Error Prone 活跃版本) |
| 全局配置存在时忽略内置配置 | 使用用户 ruleset,不触发新规则 |
| JSP 审查不受影响 | JSP 文件审查行为与修改前一致 |
9.3 去重测试
| 测试项 | 验证内容 |
|---|---|
导入 HardCodedCryptoKey 自定义规则 |
提示与已有规则重复 |
导入 InsecureCryptoIv 自定义规则 |
提示与已有规则重复 |
导入已移除的弃用规则(如 JUnit4TestShouldUseTestAnnotation) |
不提示重复(已从 static-rules.json 移除) |
十、风险评估
10.1 主要风险
| 风险 | 级别 | 缓解措施 |
|---|---|---|
| 用户依赖被排除的噪音规则 | 低 | 噪音规则仍在 static-rules.json 中,用户可通过自定义 ruleset 启用并配置阈值 |
| 用户依赖被排除的弃用规则 | 低 | 替代规则(活跃版本)自动生效,功能等价。如确需旧名称,可通过自定义 ruleset 引用 |
PMD 8.0.0 移除弃用规则后 <exclude> 失效 |
低 | PMD 8.0.0 升级时移除无效的 <exclude> 条目即可,不影响其他规则 |
| Security 规则误报 | 极低 | HardCodedCryptoKey 和 InsecureCryptoIv 检测的是确定性的硬编码模式,误报率极低 |
用户未更新 static-rules.json 导致去重失效 |
低 | 实施步骤中明确要求同步更新,且有测试用例覆盖 |
10.2 回滚方案
如果增强后导致严重问题,回滚步骤:
- 将
jars/pmd/pmd-java-ruleset.xml恢复为修改前的全分类引用版本(移除所有<exclude>和 Security 引用) - 将
src/rules/static-rules.json恢复为修改前的状态(移除 Security 规则,恢复弃用规则) - 恢复
linterVersion.pmd为原值
回滚仅需还原 2 个文件,不涉及代码修改和依赖变更,操作简单安全。
十一、附录
11.1 规则来源参考
- PMD 7.26.0 官方规则文档:https://docs.pmd-code.org/pmd-doc-7.26.0/pmd_rules_java.html
- 推荐规则分析文件:
/workspace/pmd-recommended-rules.md - 完整规则清单:
/workspace/pmd-7-rules.md - 适配器配置分析:
/workspace/custom-rule-analysis.md
11.2 规则数量统计
| 分类 | 数量 | 操作 | 来源 |
|---|---|---|---|
| P0 当前已启用(保留) | 272 | 保留 | 6 个分类的活跃规则,排除噪音后 |
| P1 新增推荐 | 2 | 新增 | Security 分类 |
| P2 建议排除(弃用) | 20 | 排除 | 5 个分类的已弃用规则 |
| P2 建议排除(噪音) | 17 | 排除 | 3 个分类的误报率高的阈值规则 |
| 不推荐 | 6 | 不启用 | Documentation 分类 |
| 推荐启用合计 | 274 | — | P0 + P1 |
11.3 排除规则分类汇总
| 分类 | 弃用排除 | 噪音排除 | 合计排除 | 保留 |
|---|---|---|---|---|
| Best Practices | 9 | 0 | 9 | 60 |
| Code Style | 2 | 4 | 6 | 57 |
| Design | 2 | 10 | 12 | 30 |
| Error Prone | 6 | 0 | 6 | 92 |
| Multithreading | 0 | 3 | 3 | 9 |
| Performance | 1 | 0 | 1 | 24 |
| Security | 0 | 0 | 0 | 2(新增) |
| 合计 | 20 | 17 | 37 | 274 |
11.4 JSP 规则集(无变化)
JSP 规则集当前已完整启用 5 个分类共 12 条规则,全部活跃,无弃用规则,无需任何修改。
11.5 与其他适配器增强方案对比
| 适配器 | 增强方向 | 修改文件数 | 代码修改 | 依赖修改 | 最终规则数 |
|---|---|---|---|---|---|
| ESLint | 从少到多 | 3 | 是(eslint.ts) |
是(@eslint/js) |
92 |
| Stylelint | 从少到多 | 3 | 是(stylelint.ts) |
是(stylelint-config-recommended) |
68 |
| ts-eslint | 从少到多 | 2 | 是(eslint.ts) |
否 | ~40 |
| PMD | 从粗到精 | 2 | 否 | 否 | 274 |
PMD 适配器的增强方案修改文件最少(仅 XML 和 JSON),无需修改 TypeScript 代码和 npm 依赖,实施成本最低。这得益于 PMD 将规则配置完全外部化到 XML ruleset 文件的架构设计。