agent-docs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/plans/OPTIMISE.md +379 -0
- package/.cursor/plans/VERSIONING.md +207 -0
- package/.cursor/rules/IMPORTANT.mdc +97 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +13 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
- package/.github/dependabot.yml +38 -0
- package/.github/pull_request_template.md +10 -0
- package/.github/workflows/format.yml +35 -0
- package/CODE_OF_CONDUCT.md +64 -0
- package/CONTRIBUTING.md +52 -0
- package/LICENSE.md +20 -0
- package/PLAN.md +707 -0
- package/README.md +133 -0
- package/SECURITY.md +21 -0
- package/docs/APEXANNOTATIONS.md +472 -0
- package/docs/APEXDOC.md +198 -0
- package/docs/CML.md +877 -0
- package/docs/CODEANALYZER.md +435 -0
- package/docs/CONTEXTDEFINITIONS.md +617 -0
- package/docs/ESLINT.md +827 -0
- package/docs/ESLINTJSDOC.md +520 -0
- package/docs/FIELDSERVICE.md +4452 -0
- package/docs/GRAPHBINARY.md +208 -0
- package/docs/GRAPHENGINE.md +616 -0
- package/docs/GRAPHML.md +337 -0
- package/docs/GRAPHSON.md +302 -0
- package/docs/GREMLIN.md +490 -0
- package/docs/GRYO.md +232 -0
- package/docs/HUSKY.md +106 -0
- package/docs/JEST.md +387 -0
- package/docs/JORJE.md +537 -0
- package/docs/JSDOC.md +621 -0
- package/docs/PMD.md +910 -0
- package/docs/PNPM.md +409 -0
- package/docs/PRETTIER.md +716 -0
- package/docs/PRETTIERAPEX.md +874 -0
- package/docs/REVENUETRANSACTIONMANAGEMENT.md +887 -0
- package/docs/TINKERPOP.md +252 -0
- package/docs/VITEST.md +706 -0
- package/docs/VSCODE.md +231 -0
- package/docs/XPATH31.md +213 -0
- package/package.json +32 -0
- package/postinstall.mjs +51 -0
- package/prettier.config.js +18 -0
package/docs/PMD.md
ADDED
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
````markdown
|
|
2
|
+
# PMD Quick Reference
|
|
3
|
+
|
|
4
|
+
> **Version**: 1.0.0
|
|
5
|
+
|
|
6
|
+
Condensed PMD guide for Salesforce Code Analyzer integration.
|
|
7
|
+
|
|
8
|
+
**Reference:**
|
|
9
|
+
[PMD Engine](https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/engine-pmd.html)
|
|
10
|
+
|
|
11
|
+
**Schema:**
|
|
12
|
+
[PMD Ruleset XML Schema](https://pmd.sourceforge.io/ruleset_2_0_0.xsd)
|
|
13
|
+
|
|
14
|
+
## Related Docs
|
|
15
|
+
|
|
16
|
+
- [Code Analyzer Config](CODEANALYZER.md) - `code-analyzer.yml` reference
|
|
17
|
+
- [XPath 3.1](XPATH31.md) - XPath syntax for rules
|
|
18
|
+
- [Apex AST](#apex-ast-reference) - AST node types/patterns
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Download from [GitHub releases](https://github.com/pmd/pmd/releases), extract,
|
|
23
|
+
add `bin/` to PATH. Salesforce Code Analyzer bundles PMD—direct install only for
|
|
24
|
+
standalone CLI.
|
|
25
|
+
|
|
26
|
+
## Rulesets
|
|
27
|
+
|
|
28
|
+
XML files defining rules to execute. Reference in `code-analyzer.yml`:
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
engines:
|
|
32
|
+
pmd:
|
|
33
|
+
rulesets:
|
|
34
|
+
- rulesets/design/InnerClassesCannotBeStatic.xml
|
|
35
|
+
```
|
|
36
|
+
````
|
|
37
|
+
|
|
38
|
+
### Design
|
|
39
|
+
|
|
40
|
+
```xml
|
|
41
|
+
<?xml version="1.0" ?>
|
|
42
|
+
<ruleset
|
|
43
|
+
name="Custom Rules"
|
|
44
|
+
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
|
45
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
46
|
+
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"
|
|
47
|
+
>
|
|
48
|
+
<description>My custom rules</description>
|
|
49
|
+
<exclude-pattern>.*/test/.*</exclude-pattern>
|
|
50
|
+
<include-pattern>.*/src/.*</include-pattern>
|
|
51
|
+
<!-- Rules here -->
|
|
52
|
+
</ruleset>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Elements:** `<description>` (required), `<exclude-pattern>` (optional,
|
|
56
|
+
multiple), `<include-pattern>` (optional, multiple), `<rule>` (required,
|
|
57
|
+
multiple)
|
|
58
|
+
|
|
59
|
+
### Referencing Rules
|
|
60
|
+
|
|
61
|
+
```xml
|
|
62
|
+
<!-- Single rule -->
|
|
63
|
+
<rule ref="category/apex/codestyle.xml/NoMethodCallsInConditionals" />
|
|
64
|
+
|
|
65
|
+
<!-- Category with exclusions -->
|
|
66
|
+
<rule ref="category/apex/codestyle.xml">
|
|
67
|
+
<exclude name="WhileLoopsMustUseBraces"/>
|
|
68
|
+
</rule>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Rule Configuration
|
|
72
|
+
|
|
73
|
+
### Priority
|
|
74
|
+
|
|
75
|
+
1=High, 5=Low. Filter via `--minimum-priority`:
|
|
76
|
+
|
|
77
|
+
```xml
|
|
78
|
+
<rule ref="category/apex/errorprone.xml/EmptyCatchBlock">
|
|
79
|
+
<priority>5</priority>
|
|
80
|
+
</rule>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Properties
|
|
84
|
+
|
|
85
|
+
**Property attributes:** `name` (required), `value` (optional—attr or child),
|
|
86
|
+
`description`, `type`, `delimiter`, `min`, `max`
|
|
87
|
+
|
|
88
|
+
**Important:** Code Analyzer only supports `severity`/`tags` overrides in
|
|
89
|
+
`code-analyzer.yml`. Property overrides require ruleset XML.
|
|
90
|
+
|
|
91
|
+
```xml
|
|
92
|
+
<!-- Value as child element (recommended) -->
|
|
93
|
+
<property name="reportLevel"><value>150</value></property>
|
|
94
|
+
|
|
95
|
+
<!-- Value as attribute -->
|
|
96
|
+
<property name="reportLevel" value="150" />
|
|
97
|
+
|
|
98
|
+
<!-- Multivalued (comma-separated) -->
|
|
99
|
+
<property name="legalCollectionTypes" value="java.util.ArrayList,java.util.Vector"/>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Override custom rules:**
|
|
103
|
+
|
|
104
|
+
```xml
|
|
105
|
+
<rule ref="rulesets/design/EnumMinimumValues.xml/EnumMinimumValues">
|
|
106
|
+
<properties>
|
|
107
|
+
<property name="minValues">
|
|
108
|
+
<value>4</value>
|
|
109
|
+
</property>
|
|
110
|
+
</properties>
|
|
111
|
+
</rule>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**code-analyzer.yml (severity/tags only):**
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
rules:
|
|
118
|
+
pmd:
|
|
119
|
+
NPathComplexity:
|
|
120
|
+
severity: 'High'
|
|
121
|
+
tags: ['Recommended']
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Complete Override Example:**
|
|
125
|
+
|
|
126
|
+
1. Create `rulesets/custom-overrides.xml`:
|
|
127
|
+
|
|
128
|
+
```xml
|
|
129
|
+
<?xml version="1.0" ?>
|
|
130
|
+
<ruleset
|
|
131
|
+
name="Custom Property Overrides"
|
|
132
|
+
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
|
133
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
134
|
+
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"
|
|
135
|
+
>
|
|
136
|
+
<description>Custom property overrides</description>
|
|
137
|
+
<rule ref="rulesets/design/EnumMinimumValues.xml/EnumMinimumValues">
|
|
138
|
+
<properties>
|
|
139
|
+
<property name="minValues">
|
|
140
|
+
<value>4</value>
|
|
141
|
+
</property>
|
|
142
|
+
</properties>
|
|
143
|
+
</rule>
|
|
144
|
+
<rule
|
|
145
|
+
ref="rulesets/design/PreferSwitchOverIfElseChains.xml/PreferSwitchOverIfElseChains"
|
|
146
|
+
>
|
|
147
|
+
<properties>
|
|
148
|
+
<property name="minElseIfStatements">
|
|
149
|
+
<value>4</value>
|
|
150
|
+
</property>
|
|
151
|
+
</properties>
|
|
152
|
+
</rule>
|
|
153
|
+
</ruleset>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
2. Reference in `code-analyzer.yml`:
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
engines:
|
|
160
|
+
pmd:
|
|
161
|
+
custom_rulesets:
|
|
162
|
+
- rulesets/design/EnumMinimumValues.xml # Original first
|
|
163
|
+
- rulesets/design/PreferSwitchOverIfElseChains.xml
|
|
164
|
+
- rulesets/custom-overrides.xml # Override after
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Ref format:** `{ruleset-path}/{rule-name}`
|
|
168
|
+
|
|
169
|
+
**XPathRule Custom Properties:** PMD 7.x doesn't validate custom XPathRule
|
|
170
|
+
properties. Use substitution pattern:
|
|
171
|
+
|
|
172
|
+
```xpath
|
|
173
|
+
if ('${propertyName}' = '${propertyName}') then 'defaultValue' else '${propertyName}'
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
When undefined, `${propertyName}` stays literal → check true → default used.
|
|
177
|
+
|
|
178
|
+
### Examples
|
|
179
|
+
|
|
180
|
+
```xml
|
|
181
|
+
<rule name="MyRule" language="apex" ...>
|
|
182
|
+
<description>Rule description</description>
|
|
183
|
+
<priority>3</priority>
|
|
184
|
+
<properties><!-- XPath --></properties>
|
|
185
|
+
<example><![CDATA[
|
|
186
|
+
// Violation
|
|
187
|
+
public void badExample() { }
|
|
188
|
+
// Valid
|
|
189
|
+
public void goodExample() { }
|
|
190
|
+
]]></example>
|
|
191
|
+
</rule>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Custom Messages
|
|
195
|
+
|
|
196
|
+
```xml
|
|
197
|
+
<rule
|
|
198
|
+
ref="category/apex/errorprone.xml/EmptyCatchBlock"
|
|
199
|
+
message="Empty catch blocks should be avoided"
|
|
200
|
+
/>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Rule Element Structure
|
|
204
|
+
|
|
205
|
+
**Child elements (order required):** `description` → `priority` → `properties` →
|
|
206
|
+
`exclude` → `example`
|
|
207
|
+
|
|
208
|
+
Verify: `pnpm check-xml-order` | Fix: `pnpm fix-xml-order`
|
|
209
|
+
|
|
210
|
+
**Attributes:** `name`, `language`, `minimumLanguageVersion`,
|
|
211
|
+
`maximumLanguageVersion`, `ref`, `message`, `class`, `since`, `externalInfoUrl`,
|
|
212
|
+
`deprecated` (default: false), `dfa`, `typeResolution` (default: false)
|
|
213
|
+
|
|
214
|
+
## Suppressing Warnings
|
|
215
|
+
|
|
216
|
+
Quick reference for suppressing PMD rule violations in Apex code. Use
|
|
217
|
+
suppressions sparingly—prefer fixing issues or improving rules when possible.
|
|
218
|
+
|
|
219
|
+
### Suppression Methods (Priority Order)
|
|
220
|
+
|
|
221
|
+
1. **Fix the issue or improve the rule** (preferred)
|
|
222
|
+
2. **Annotations** - Class/method level
|
|
223
|
+
3. **NOPMD comment** - Line level
|
|
224
|
+
4. **Rule properties** - Global suppression via `violationSuppressRegex` or
|
|
225
|
+
`violationSuppressXPath`
|
|
226
|
+
|
|
227
|
+
### Annotations
|
|
228
|
+
|
|
229
|
+
#### Suppress All PMD Warnings
|
|
230
|
+
|
|
231
|
+
```apex
|
|
232
|
+
@SuppressWarnings('PMD')
|
|
233
|
+
public class MyClass {
|
|
234
|
+
// All PMD warnings suppressed in this class
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Suppress Specific Rule
|
|
239
|
+
|
|
240
|
+
```apex
|
|
241
|
+
@SuppressWarnings('PMD.UnusedLocalVariable')
|
|
242
|
+
public class MyClass {
|
|
243
|
+
void method() {
|
|
244
|
+
int unused = 42; // No violation
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### Suppress Multiple Rules
|
|
250
|
+
|
|
251
|
+
```apex
|
|
252
|
+
@SuppressWarnings('PMD.UnusedLocalVariable, PMD.UnusedPrivateMethod')
|
|
253
|
+
public class MyClass {
|
|
254
|
+
private int unused;
|
|
255
|
+
private void unusedMethod() {}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Note:** Apex uses single quotes and comma-separated values (not JSON array
|
|
260
|
+
syntax like Java).
|
|
261
|
+
|
|
262
|
+
### NOPMD Comment
|
|
263
|
+
|
|
264
|
+
Suppress a single line violation:
|
|
265
|
+
|
|
266
|
+
```apex
|
|
267
|
+
public class MyClass {
|
|
268
|
+
private int bar; // NOPMD - accessed by native method
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Important:** The `// NOPMD` marker must be on the same line as the violation.
|
|
273
|
+
Optional message after marker appears in reports:
|
|
274
|
+
|
|
275
|
+
```apex
|
|
276
|
+
if (condition) { // NOPMD - temporary workaround
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
#### Custom Suppression Marker
|
|
281
|
+
|
|
282
|
+
Change the marker via CLI `--suppress-marker` option (default: `NOPMD`):
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
sf code-analyzer run --suppress-marker "TURN_OFF_WARNINGS"
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Rule Properties (Global Suppression)
|
|
289
|
+
|
|
290
|
+
**Important:** Salesforce Code Analyzer does not support property overrides via
|
|
291
|
+
`code-analyzer.yml`. Configure suppression properties via custom ruleset XML
|
|
292
|
+
files using `ref=` syntax.
|
|
293
|
+
|
|
294
|
+
Create a custom ruleset XML file to suppress violations matching patterns.
|
|
295
|
+
|
|
296
|
+
#### violationSuppressRegex
|
|
297
|
+
|
|
298
|
+
Suppress violations where the message matches a regex:
|
|
299
|
+
|
|
300
|
+
**Custom ruleset XML:**
|
|
301
|
+
|
|
302
|
+
```xml
|
|
303
|
+
<rule ref="category/apex/bestpractices.xml/UnusedFormalParameter">
|
|
304
|
+
<properties>
|
|
305
|
+
<property name="violationSuppressRegex">
|
|
306
|
+
<value>.*'mySpecialParameter'.*</value>
|
|
307
|
+
</property>
|
|
308
|
+
</properties>
|
|
309
|
+
</rule>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**For custom rules:**
|
|
313
|
+
|
|
314
|
+
```xml
|
|
315
|
+
<rule ref="rulesets/design/SomeRule.xml/SomeRule">
|
|
316
|
+
<properties>
|
|
317
|
+
<property name="violationSuppressRegex">
|
|
318
|
+
<value>.*'mySpecialParameter'.*</value>
|
|
319
|
+
</property>
|
|
320
|
+
</properties>
|
|
321
|
+
</rule>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### violationSuppressXPath
|
|
325
|
+
|
|
326
|
+
Suppress violations where XPath query matches (XPath 3.1, context node is the
|
|
327
|
+
violation node):
|
|
328
|
+
|
|
329
|
+
**Custom ruleset XML:**
|
|
330
|
+
|
|
331
|
+
```xml
|
|
332
|
+
<?xml version="1.0" ?>
|
|
333
|
+
<ruleset
|
|
334
|
+
name="Suppression Rules"
|
|
335
|
+
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
|
336
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
337
|
+
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"
|
|
338
|
+
>
|
|
339
|
+
<description>Suppression rules for specific patterns</description>
|
|
340
|
+
|
|
341
|
+
<!-- Suppress String parameters -->
|
|
342
|
+
<rule ref="category/apex/bestpractices.xml/UnusedFormalParameter">
|
|
343
|
+
<properties>
|
|
344
|
+
<property name="violationSuppressXPath">
|
|
345
|
+
<value>.[pmd-apex:typeIs('String')]</value>
|
|
346
|
+
</property>
|
|
347
|
+
</properties>
|
|
348
|
+
</rule>
|
|
349
|
+
|
|
350
|
+
<!-- Suppress in classes containing "Bean" -->
|
|
351
|
+
<rule ref="rulesets/design/SomeRule.xml/SomeRule">
|
|
352
|
+
<properties>
|
|
353
|
+
<property name="violationSuppressXPath">
|
|
354
|
+
<value>
|
|
355
|
+
./ancestor-or-self::ClassDeclaration[contains(@SimpleName, 'Bean')]
|
|
356
|
+
</value>
|
|
357
|
+
</property>
|
|
358
|
+
</properties>
|
|
359
|
+
</rule>
|
|
360
|
+
|
|
361
|
+
<!-- Suppress in equals/hashCode methods -->
|
|
362
|
+
<rule ref="rulesets/design/AnotherRule.xml/AnotherRule">
|
|
363
|
+
<properties>
|
|
364
|
+
<property name="violationSuppressXPath">
|
|
365
|
+
<value>
|
|
366
|
+
./ancestor-or-self::MethodDeclaration[@Name = ('equals', 'hashCode')]
|
|
367
|
+
</value>
|
|
368
|
+
</property>
|
|
369
|
+
</properties>
|
|
370
|
+
</rule>
|
|
371
|
+
|
|
372
|
+
<!-- Suppress in classes ending with "Bean" (regex match) -->
|
|
373
|
+
<rule ref="rulesets/design/YetAnotherRule.xml/YetAnotherRule">
|
|
374
|
+
<properties>
|
|
375
|
+
<property name="violationSuppressXPath">
|
|
376
|
+
<value>
|
|
377
|
+
./ancestor-or-self::ClassDeclaration[matches(@SimpleName, '^.*Bean$')]
|
|
378
|
+
</value>
|
|
379
|
+
</property>
|
|
380
|
+
</properties>
|
|
381
|
+
</rule>
|
|
382
|
+
</ruleset>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Then reference in `code-analyzer.yml`:
|
|
386
|
+
|
|
387
|
+
```yaml
|
|
388
|
+
engines:
|
|
389
|
+
pmd:
|
|
390
|
+
custom_rulesets:
|
|
391
|
+
- rulesets/design/SomeRule.xml
|
|
392
|
+
- rulesets/design/AnotherRule.xml
|
|
393
|
+
- rulesets/design/YetAnotherRule.xml
|
|
394
|
+
- rulesets/suppression-rules.xml # Custom suppression ruleset
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**XPath Notes:**
|
|
398
|
+
|
|
399
|
+
- Use `.` to reference the context node (the violation node)
|
|
400
|
+
- Prefer `./ancestor-or-self::` over `//` to avoid over-suppressing
|
|
401
|
+
- Context node varies by rule (check rule implementation)
|
|
402
|
+
- XPath 3.1 syntax supported (since PMD 7)
|
|
403
|
+
|
|
404
|
+
### Best Practices
|
|
405
|
+
|
|
406
|
+
1. **Fix or improve rules first** - Suppressions hide issues; better to fix root
|
|
407
|
+
cause
|
|
408
|
+
2. **Be specific** - Suppress only what's necessary (specific rules, not all PMD
|
|
409
|
+
warnings)
|
|
410
|
+
3. **Document why** - Add comments explaining suppression reason
|
|
411
|
+
4. **Review periodically** - Suppressions may become unnecessary after code/rule
|
|
412
|
+
changes
|
|
413
|
+
|
|
414
|
+
## CLI Usage
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
pmd check -d <source> -R <ruleset> -f <format>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Options:** | Option | Description | |--------|-------------| | `-d`, `--dir` |
|
|
421
|
+
Source directory | | `-R`, `--rulesets` | Ruleset file | | `-f`, `--format` |
|
|
422
|
+
Report format | | `--minimum-priority` | Filter priority (1-5 or
|
|
423
|
+
High/Medium/Low) | | `-l`, `--language` | Language (apex, java, etc.) | |
|
|
424
|
+
`--use-version` | Language version (PMD 7+) | | `--fail-on-violation` | Exit
|
|
425
|
+
with error on violations | | `--no-cache` | Disable cache | | `--cache` | Enable
|
|
426
|
+
cache with file |
|
|
427
|
+
|
|
428
|
+
**PMD 6→7 changes:** `-no-cache`→`--no-cache`,
|
|
429
|
+
`-failOnViolation`→`--fail-on-violation`, `-reportfile`→`--report-file`,
|
|
430
|
+
`-language`→`--use-version`
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
pmd check -d src/main/apex -R rulesets/all.xml -f html -l apex --use-version 60
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Report Formats
|
|
437
|
+
|
|
438
|
+
`text`, `xml`, `html`, `csv`, `json`, `sarif`, `codeclimate`, `junit`
|
|
439
|
+
|
|
440
|
+
## CPD (Copy-Paste Detector)
|
|
441
|
+
|
|
442
|
+
Finds duplicated code for refactoring.
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
pmd cpd --minimum-tokens 100 --language apex --files src/
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**Options:** `--minimum-tokens` (required), `--language`, `--format`
|
|
449
|
+
(text/xml/csv/csv_with_linecount_per_file/vs/json), `--ignore-identifiers`,
|
|
450
|
+
`--ignore-literals`, `--ignore-annotations`, `--skip-duplicate-files`
|
|
451
|
+
|
|
452
|
+
**Suppression:**
|
|
453
|
+
|
|
454
|
+
```java
|
|
455
|
+
// CPD-OFF
|
|
456
|
+
public void duplicateCode() { }
|
|
457
|
+
// CPD-ON
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**GUI:** `pmd cpd-gui`
|
|
461
|
+
|
|
462
|
+
## Incremental Analysis
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
pmd --cache <cache_file> -d <source> -R <ruleset>
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
Code Analyzer handles automatically.
|
|
469
|
+
|
|
470
|
+
## Rule Categories
|
|
471
|
+
|
|
472
|
+
1. Best Practices 2. Code Style 3. Design 4. Documentation 5. Error Prone 6.
|
|
473
|
+
Multithreading 7. Performance 8. Security
|
|
474
|
+
|
|
475
|
+
## GitHub Actions
|
|
476
|
+
|
|
477
|
+
```yaml
|
|
478
|
+
steps:
|
|
479
|
+
- uses: actions/checkout@v4
|
|
480
|
+
- uses: actions/setup-java@v4
|
|
481
|
+
with:
|
|
482
|
+
distribution: 'temurin'
|
|
483
|
+
java-version: '11'
|
|
484
|
+
- uses: pmd/pmd-github-action@v2
|
|
485
|
+
with:
|
|
486
|
+
rulesets: 'rulesets/design/InnerClassesCannotBeStatic.xml'
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
**Inputs:** `rulesets` (required), `version` (default: "latest"), `sourcePath`
|
|
490
|
+
(default: "."), `analyzeModifiedFilesOnly` (default: "true"),
|
|
491
|
+
`createGitHubAnnotations` (default: "true"), `uploadSarifReport` (default:
|
|
492
|
+
"true")
|
|
493
|
+
|
|
494
|
+
**Output:** `violations` (count)
|
|
495
|
+
|
|
496
|
+
```yaml
|
|
497
|
+
- uses: pmd/pmd-github-action@v2
|
|
498
|
+
id: pmd
|
|
499
|
+
with:
|
|
500
|
+
rulesets: 'rulesets/all.xml'
|
|
501
|
+
- name: Fail on violations
|
|
502
|
+
if: steps.pmd.outputs.violations != 0
|
|
503
|
+
run: exit 1
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
**Limits:** No `auxclasspath`, XPath rules only, no custom env vars, max 300
|
|
507
|
+
files with `analyzeModifiedFilesOnly`
|
|
508
|
+
|
|
509
|
+
## Language Versions
|
|
510
|
+
|
|
511
|
+
Use `--use-version` to specify. Rules can set
|
|
512
|
+
`minimumLanguageVersion`/`maximumLanguageVersion`. Check `pmd check --help` for
|
|
513
|
+
available versions.
|
|
514
|
+
|
|
515
|
+
## Code Metrics
|
|
516
|
+
|
|
517
|
+
**ApexMetrics:** `CYCLO`, `COGNITIVE_COMPLEXITY`, `NCSS`, `WEIGHED_METHOD_COUNT`
|
|
518
|
+
|
|
519
|
+
```java
|
|
520
|
+
import net.sourceforge.pmd.lang.apex.metrics.ApexMetrics;
|
|
521
|
+
import net.sourceforge.pmd.lang.metrics.MetricsUtil;
|
|
522
|
+
|
|
523
|
+
int cyclo = MetricsUtil.computeMetric(ApexMetrics.CYCLO, node);
|
|
524
|
+
int cognitive = MetricsUtil.computeMetric(ApexMetrics.COGNITIVE_COMPLEXITY, node);
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
**Refs:**
|
|
528
|
+
[ApexMetrics API](https://docs.pmd-code.org/apidocs/pmd-apex/7.20.0-SNAPSHOT/net/sourceforge/pmd/lang/apex/metrics/ApexMetrics.html),
|
|
529
|
+
[MetricsUtil API](https://docs.pmd-code.org/apidocs/pmd-core/7.20.0-SNAPSHOT/net/sourceforge/pmd/lang/metrics/MetricsUtil.html)
|
|
530
|
+
|
|
531
|
+
## Apex Support
|
|
532
|
+
|
|
533
|
+
Built-in rules, Summit AST parser (PMD 7+), metrics, language-specific config.
|
|
534
|
+
|
|
535
|
+
**Refs:**
|
|
536
|
+
[Apex Language Docs](https://pmd.github.io/pmd/pmd_languages_apex.html)
|
|
537
|
+
|
|
538
|
+
## Apex AST Reference
|
|
539
|
+
|
|
540
|
+
**Note:** This reference is for PMD 7.0.0+, which uses Summit AST parser
|
|
541
|
+
(replaced Jorje parser in PMD 6).
|
|
542
|
+
|
|
543
|
+
### PMD 7 Apex AST Changes
|
|
544
|
+
|
|
545
|
+
PMD 7.0.0 switched from Jorje to Summit AST parser. Key changes:
|
|
546
|
+
|
|
547
|
+
#### Removed Attributes
|
|
548
|
+
|
|
549
|
+
- **`Method/@Synthetic`** - Removed. Summit AST doesn't generate synthetic
|
|
550
|
+
methods, so this attribute would always be false.
|
|
551
|
+
- **`Method/@Namespace`** - Removed. This attribute was never fully implemented
|
|
552
|
+
and always returned an empty string.
|
|
553
|
+
- **`ReferenceExpression/@Context`** - Removed. This attribute was not used and
|
|
554
|
+
always returned null.
|
|
555
|
+
|
|
556
|
+
#### Removed Nodes
|
|
557
|
+
|
|
558
|
+
- **`BridgeMethodCreator`** - Removed. This was an artificially generated node
|
|
559
|
+
by Jorje. Summit AST doesn't generate synthetic methods.
|
|
560
|
+
- **Methods named `<clinit>` and `<init>`** - No longer generated by Summit AST.
|
|
561
|
+
|
|
562
|
+
#### Impact on XPath Rules
|
|
563
|
+
|
|
564
|
+
When migrating XPath rules from PMD 6 to PMD 7:
|
|
565
|
+
|
|
566
|
+
1. **Remove references to removed attributes:**
|
|
567
|
+
- Remove `@Synthetic` checks on `Method` nodes
|
|
568
|
+
- Remove `@Namespace` checks on any nodes
|
|
569
|
+
- Remove `@Context` checks on `ReferenceExpression` nodes
|
|
570
|
+
|
|
571
|
+
2. **Remove references to removed nodes:**
|
|
572
|
+
- Remove XPath expressions matching `BridgeMethodCreator` nodes
|
|
573
|
+
- Remove checks for method names `<clinit>` or `<init>`
|
|
574
|
+
|
|
575
|
+
3. **Test thoroughly:** The AST structure is mostly compatible, but some edge
|
|
576
|
+
cases may differ.
|
|
577
|
+
|
|
578
|
+
**References:**
|
|
579
|
+
|
|
580
|
+
- [PMD 7 Migration Guide - Apex AST](https://pmd.github.io/pmd/pmd_userdocs_migrating_to_pmd7.html#apex-ast)
|
|
581
|
+
- [Apex Language Documentation](https://pmd.github.io/pmd/pmd_languages_apex.html)
|
|
582
|
+
|
|
583
|
+
### Core Structure Nodes
|
|
584
|
+
|
|
585
|
+
**UserClass** - Class declaration
|
|
586
|
+
|
|
587
|
+
- `@Name`, `@SimpleName` - Class name
|
|
588
|
+
- `@Abstract`, `@Interface` - Type flags
|
|
589
|
+
- `@Nested` - Inner class flag
|
|
590
|
+
|
|
591
|
+
**ApexFile** - File/compilation unit (root)
|
|
592
|
+
|
|
593
|
+
**Method** - Method declaration
|
|
594
|
+
|
|
595
|
+
- `@Image`, `@MethodName`, `@FullMethodName` - Method name
|
|
596
|
+
- `@ReturnType` - Return type
|
|
597
|
+
- `@Constructor` - Constructor flag
|
|
598
|
+
- `@InputParametersSize` - Parameter count
|
|
599
|
+
- Children: `ModifierNode`, `Parameter`, `BlockStatement`
|
|
600
|
+
|
|
601
|
+
**Field** / **FieldDeclaration** / **FieldDeclarationStatements** - Field
|
|
602
|
+
declarations
|
|
603
|
+
|
|
604
|
+
- `@Name` - Field name
|
|
605
|
+
- Children: `ModifierNode`, `VariableExpression`
|
|
606
|
+
|
|
607
|
+
**Property** - Property declaration (getter/setter)
|
|
608
|
+
|
|
609
|
+
**Parameter** - Method/constructor parameter
|
|
610
|
+
|
|
611
|
+
- `@Image` - Parameter name
|
|
612
|
+
- `@Type` - Parameter type
|
|
613
|
+
|
|
614
|
+
**ModifierNode** - Access/type modifiers
|
|
615
|
+
|
|
616
|
+
- `@Static`, `@Final`, `@Abstract`, `@Public`, `@Private`, `@Protected`
|
|
617
|
+
- `@Override`, `@Global`, `@WebService`
|
|
618
|
+
|
|
619
|
+
**Annotation** - Annotation
|
|
620
|
+
|
|
621
|
+
- `@Name` - Annotation name (e.g., `'SuppressWarnings'`, `'IsTest'`)
|
|
622
|
+
- Note: Path is `Field/ModifierNode/Annotation[@Name]` (not
|
|
623
|
+
`Field/Annotation[@Name]`)
|
|
624
|
+
|
|
625
|
+
### Control Flow Nodes
|
|
626
|
+
|
|
627
|
+
**IfBlockStatement** - if statement
|
|
628
|
+
|
|
629
|
+
- Children: `StandardCondition`, `BlockStatement`
|
|
630
|
+
|
|
631
|
+
**IfElseBlockStatement** - if-else chain
|
|
632
|
+
|
|
633
|
+
- Children: multiple `IfBlockStatement`
|
|
634
|
+
|
|
635
|
+
**SwitchStatement** - switch statement
|
|
636
|
+
|
|
637
|
+
- Children: `StandardCondition`, case blocks
|
|
638
|
+
|
|
639
|
+
**WhileLoopStatement** - while loop
|
|
640
|
+
|
|
641
|
+
- Children: `StandardCondition`, `BlockStatement`
|
|
642
|
+
|
|
643
|
+
**ForLoopStatement** - for loop
|
|
644
|
+
|
|
645
|
+
- Children: `StandardCondition`, `BlockStatement`
|
|
646
|
+
|
|
647
|
+
**ForEachStatement** - for-each loop
|
|
648
|
+
|
|
649
|
+
- Children: `StandardCondition`, `BlockStatement`
|
|
650
|
+
|
|
651
|
+
**DoLoopStatement** - do-while loop
|
|
652
|
+
|
|
653
|
+
**CatchBlockStatement** - catch block
|
|
654
|
+
|
|
655
|
+
**StandardCondition** - Condition expression (if, while, etc.)
|
|
656
|
+
|
|
657
|
+
- Children: `BooleanExpression`, `PrimaryExpression`
|
|
658
|
+
|
|
659
|
+
### Expression Nodes
|
|
660
|
+
|
|
661
|
+
**PrimaryExpression** - Primary expression (method calls, field access, etc.)
|
|
662
|
+
|
|
663
|
+
- Children: `MethodCallExpression`, `ReferenceExpression`, `VariableExpression`,
|
|
664
|
+
etc.
|
|
665
|
+
|
|
666
|
+
**MethodCallExpression** - Method invocation
|
|
667
|
+
|
|
668
|
+
- `@Image` - Method name
|
|
669
|
+
- Children: arguments
|
|
670
|
+
|
|
671
|
+
**ReferenceExpression** - Field/method reference
|
|
672
|
+
|
|
673
|
+
- `@Image` - Reference name
|
|
674
|
+
|
|
675
|
+
**VariableExpression** - Variable reference
|
|
676
|
+
|
|
677
|
+
- `@Image` - Variable name
|
|
678
|
+
|
|
679
|
+
**ThisVariableExpression** - `this` reference
|
|
680
|
+
|
|
681
|
+
**AssignmentExpression** - Assignment
|
|
682
|
+
|
|
683
|
+
- `@Op` - Operator (`=`, `+=`, `-=`, `*=`, `/=`, `%=`)
|
|
684
|
+
|
|
685
|
+
**BinaryExpression** - Binary operation
|
|
686
|
+
|
|
687
|
+
- `@Op` - Operator (`+`, `-`, `*`, `/`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `and`,
|
|
688
|
+
`or`, etc.)
|
|
689
|
+
|
|
690
|
+
**UnaryExpression** - Unary operation
|
|
691
|
+
|
|
692
|
+
- `@Op` - Operator (`++`, `--`, `+`, `-`, `!`, `not`)
|
|
693
|
+
|
|
694
|
+
**TernaryExpression** - Ternary operator (`? :`)
|
|
695
|
+
|
|
696
|
+
- Children: `StandardCondition`, then/else expressions
|
|
697
|
+
|
|
698
|
+
**BooleanExpression** - Boolean expression
|
|
699
|
+
|
|
700
|
+
- `@Op` - Operator (`==`, `!=`, `<`, `>`, `and`, `or`, etc.)
|
|
701
|
+
- Children: `VariableExpression`, `LiteralExpression`, etc.
|
|
702
|
+
|
|
703
|
+
**LiteralExpression** - Literal value
|
|
704
|
+
|
|
705
|
+
- `@Image` - Literal text
|
|
706
|
+
- `@Null` - Null literal flag
|
|
707
|
+
- `@String` - String literal flag
|
|
708
|
+
- `@LiteralType` - Type (e.g., `'Integer'`, `'String'`, `'Boolean'`)
|
|
709
|
+
|
|
710
|
+
**InstanceOfExpression** - `instanceof` check
|
|
711
|
+
|
|
712
|
+
**NewListLiteralExpression** - List literal `new List<Type>{...}`
|
|
713
|
+
|
|
714
|
+
- Children: elements
|
|
715
|
+
|
|
716
|
+
**NewMapLiteralExpression** - Map literal `new Map<K,V>{...}`
|
|
717
|
+
|
|
718
|
+
- Children: `MapEntryNode`
|
|
719
|
+
|
|
720
|
+
**MapEntryNode** - Map entry (key-value pair)
|
|
721
|
+
|
|
722
|
+
- `@BeginLine`, `@EndLine` - Line numbers
|
|
723
|
+
|
|
724
|
+
### Statement Nodes
|
|
725
|
+
|
|
726
|
+
**BlockStatement** - Code block `{...}`
|
|
727
|
+
|
|
728
|
+
- `@BeginLine`, `@EndLine` - Line numbers
|
|
729
|
+
|
|
730
|
+
**ReturnStatement** - return statement
|
|
731
|
+
|
|
732
|
+
- Children: expression
|
|
733
|
+
|
|
734
|
+
**ThrowStatement** - throw statement
|
|
735
|
+
|
|
736
|
+
- Children: exception expression
|
|
737
|
+
|
|
738
|
+
**BreakStatement** - break statement
|
|
739
|
+
|
|
740
|
+
**ContinueStatement** - continue statement
|
|
741
|
+
|
|
742
|
+
### Other Nodes
|
|
743
|
+
|
|
744
|
+
**FormalComment** - Javadoc/comment
|
|
745
|
+
|
|
746
|
+
**Important:** PMD's Apex parser only includes block comments (`/* */`) and
|
|
747
|
+
ApexDoc comments (`/** */`) in the AST as `FormalComment` nodes. Single-line
|
|
748
|
+
comments (`//`) are **not** included in the AST and cannot be detected using
|
|
749
|
+
XPath expressions. For rules that need to detect single-line comments (e.g.,
|
|
750
|
+
`// prettier-ignore`, `// NOPMD`), use regex-based rules instead of PMD XPath
|
|
751
|
+
rules. See [Regex Engine](CODEANALYZER.md#regex-engine) for creating regex-based
|
|
752
|
+
rules.
|
|
753
|
+
|
|
754
|
+
**UserEnum** - Enum declaration
|
|
755
|
+
|
|
756
|
+
- Children: `Field` (enum values)
|
|
757
|
+
|
|
758
|
+
**VariableDeclaration** - Variable declaration
|
|
759
|
+
|
|
760
|
+
- `@Image` - Variable name
|
|
761
|
+
- Children: `VariableExpression`, `VariableInitializer`
|
|
762
|
+
|
|
763
|
+
**VariableDeclarationStatements** - Variable declaration statement
|
|
764
|
+
|
|
765
|
+
- Children: `VariableDeclaration`, `ModifierNode`
|
|
766
|
+
|
|
767
|
+
**VariableInitializer** - Variable initialization
|
|
768
|
+
|
|
769
|
+
- Children: `Expression`
|
|
770
|
+
|
|
771
|
+
**Expression** - Generic expression wrapper
|
|
772
|
+
|
|
773
|
+
### Common Attributes
|
|
774
|
+
|
|
775
|
+
**Identity/Name:**
|
|
776
|
+
|
|
777
|
+
- `@Image` - Text/image (name, value, operator symbol)
|
|
778
|
+
- `@Name`, `@SimpleName` - Node name
|
|
779
|
+
- `@MethodName`, `@FullMethodName` - Method names
|
|
780
|
+
- `@VariableName` - Variable name
|
|
781
|
+
|
|
782
|
+
**Location:**
|
|
783
|
+
|
|
784
|
+
- `@BeginLine`, `@EndLine` - Line numbers
|
|
785
|
+
- `@BeginColumn`, `@EndColumn` - Column numbers
|
|
786
|
+
- `@CurlyBrace` - Has curly braces
|
|
787
|
+
|
|
788
|
+
**Type/Flags:**
|
|
789
|
+
|
|
790
|
+
- `@Type`, `@ReturnType`, `@LiteralType` - Types
|
|
791
|
+
- `@Static`, `@Final`, `@Abstract`, `@Public`, `@Private`, `@Protected` -
|
|
792
|
+
Modifiers
|
|
793
|
+
- `@Constructor`, `@Interface`, `@Nested` - Structure flags
|
|
794
|
+
- `@Override`, `@Null`, `@String` - Special flags
|
|
795
|
+
|
|
796
|
+
**Operators:**
|
|
797
|
+
|
|
798
|
+
- `@Op` - Operator symbol (`+`, `-`, `==`, `!=`, `++`, `--`, `+=`, etc.)
|
|
799
|
+
|
|
800
|
+
**Counts:**
|
|
801
|
+
|
|
802
|
+
- `@InputParametersSize` - Parameter count
|
|
803
|
+
|
|
804
|
+
### Common Patterns
|
|
805
|
+
|
|
806
|
+
#### Find methods
|
|
807
|
+
|
|
808
|
+
```xpath
|
|
809
|
+
//Method[@Image = 'methodName']
|
|
810
|
+
//Method[@Static = true()]
|
|
811
|
+
//Method[@Constructor = true()]
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
#### Find classes
|
|
815
|
+
|
|
816
|
+
```xpath
|
|
817
|
+
//UserClass[@Nested = true()]
|
|
818
|
+
//UserClass[@Abstract = true()]
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
#### Find variables
|
|
822
|
+
|
|
823
|
+
```xpath
|
|
824
|
+
//VariableDeclaration[ancestor::Method] # Local vars only
|
|
825
|
+
//VariableExpression[@Image = 'varName']
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
#### Find method calls
|
|
829
|
+
|
|
830
|
+
```xpath
|
|
831
|
+
//MethodCallExpression[@Image = 'methodName']
|
|
832
|
+
//PrimaryExpression/MethodCallExpression
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
#### Check modifiers
|
|
836
|
+
|
|
837
|
+
```xpath
|
|
838
|
+
//Method[ModifierNode[@Static = true()]]
|
|
839
|
+
//Field[ModifierNode[@Final = true()]]
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
#### Check annotations
|
|
843
|
+
|
|
844
|
+
```xpath
|
|
845
|
+
//Annotation[@Name = 'SuppressWarnings']
|
|
846
|
+
//Method/ModifierNode/Annotation[@Name = 'IsTest']
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
#### Find control flow
|
|
850
|
+
|
|
851
|
+
```xpath
|
|
852
|
+
//IfBlockStatement[StandardCondition//MethodCallExpression]
|
|
853
|
+
//WhileLoopStatement[StandardCondition//VariableExpression]
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
#### Check operators
|
|
857
|
+
|
|
858
|
+
```xpath
|
|
859
|
+
//BinaryExpression[@Op = '==']
|
|
860
|
+
//UnaryExpression[@Op = '++']
|
|
861
|
+
//AssignmentExpression[@Op = '+=']
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
#### Check expressions
|
|
865
|
+
|
|
866
|
+
```xpath
|
|
867
|
+
//LiteralExpression[@Null = true()]
|
|
868
|
+
//BooleanExpression[@Op = 'and']
|
|
869
|
+
//TernaryExpression
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
#### Line number checks
|
|
873
|
+
|
|
874
|
+
```xpath
|
|
875
|
+
//Method[@BeginLine = @EndLine] # Single line
|
|
876
|
+
//BlockStatement[@BeginLine != @EndLine] # Multi-line
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
#### Count checks
|
|
880
|
+
|
|
881
|
+
```xpath
|
|
882
|
+
//Method[count(Parameter) > 2]
|
|
883
|
+
//IfElseBlockStatement[count(IfBlockStatement) >= 3]
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
### Important Notes
|
|
887
|
+
|
|
888
|
+
- **Annotations:** Use `Field/ModifierNode/Annotation[@Name]` path (ModifierNode
|
|
889
|
+
is required)
|
|
890
|
+
- **Local vars:** Use `VariableDeclaration[ancestor::Method]` to exclude class
|
|
891
|
+
fields
|
|
892
|
+
- **Nested expressions:** Use `//` or `.//` to find nested nodes
|
|
893
|
+
- **Context:** Use `ancestor::`, `descendant::`, `parent::`, `child::` axes for
|
|
894
|
+
navigation
|
|
895
|
+
- **Predicates:** Use `[condition]` to filter nodes
|
|
896
|
+
|
|
897
|
+
## Code Analyzer Integration
|
|
898
|
+
|
|
899
|
+
Configure via `code-analyzer.yml`:
|
|
900
|
+
|
|
901
|
+
- **Rulesets:** `engines.pmd.rulesets` (array)
|
|
902
|
+
- **Properties:** `rules.pmd.{RuleName}.properties`
|
|
903
|
+
- **Severity/Tags:** `rules.pmd.{RuleName}.severity`,
|
|
904
|
+
`rules.pmd.{RuleName}.tags`
|
|
905
|
+
|
|
906
|
+
See [CODEANALYZER.md](CODEANALYZER.md).
|
|
907
|
+
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
```
|