@salesforce/afv-skills 1.28.0 → 1.29.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.
Files changed (31) hide show
  1. package/package.json +1 -1
  2. package/skills/dx-code-analyzer-configure/SKILL.md +31 -13
  3. package/skills/dx-code-analyzer-custom-rule-create/SKILL.md +484 -0
  4. package/skills/dx-code-analyzer-custom-rule-create/assets/pmd-ruleset-template.xml +31 -0
  5. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-fields-api.md +87 -0
  6. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-flows.md +105 -0
  7. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-permissions.md +95 -0
  8. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-examples.md +84 -0
  9. package/skills/dx-code-analyzer-custom-rule-create/examples/regex-examples.md +127 -0
  10. package/skills/dx-code-analyzer-custom-rule-create/examples/xpath-examples.md +227 -0
  11. package/skills/dx-code-analyzer-custom-rule-create/references/advanced-pmd-patterns.md +288 -0
  12. package/skills/dx-code-analyzer-custom-rule-create/references/apex-ast-reference.md +127 -0
  13. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-custom-plugins.md +247 -0
  14. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-rules-discovery.md +188 -0
  15. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier2-configurable.md +114 -0
  16. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier3-custom-plugins.md +113 -0
  17. package/skills/dx-code-analyzer-custom-rule-create/references/metadata-xml-rules.md +285 -0
  18. package/skills/dx-code-analyzer-custom-rule-create/references/regex-rule-schema.md +174 -0
  19. package/skills/dx-code-analyzer-custom-rule-create/references/troubleshooting.md +141 -0
  20. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-governor-limits.md +83 -0
  21. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-method-calls.md +108 -0
  22. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-security.md +45 -0
  23. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-structure.md +127 -0
  24. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns.md +131 -0
  25. package/skills/dx-code-analyzer-custom-rule-create/scripts/create-pmd-rule.js +209 -0
  26. package/skills/dx-code-analyzer-custom-rule-create/scripts/create-regex-rule.js +220 -0
  27. package/skills/dx-code-analyzer-run/SKILL.md +41 -8
  28. package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +3 -3
  29. package/skills/platform-custom-field-generate/SKILL.md +86 -126
  30. package/skills/platform-custom-field-generate/references/advanced-picklists.md +590 -0
  31. package/skills/platform-value-set-generate/SKILL.md +305 -0
@@ -0,0 +1,227 @@
1
+ # XPath Rule Examples
2
+
3
+ Real-world custom PMD/XPath rules for Apex, derived from community requests and common enforcement needs.
4
+
5
+ ## Example 1: Ban System.debug in Production Code
6
+
7
+ **Problem:** Debug statements clutter logs and expose sensitive data.
8
+
9
+ **Sample violating code:**
10
+ ```apex
11
+ public class MyService {
12
+ public void doWork() {
13
+ System.debug('Sensitive: ' + record.SSN__c);
14
+ }
15
+ }
16
+ ```
17
+
18
+ **AST nodes (from ast-dump):**
19
+ ```xml
20
+ <MethodCallExpression FullMethodName='System.debug' InputParametersSize='1' ...>
21
+ ```
22
+
23
+ **XPath:**
24
+ ```xpath
25
+ //MethodCallExpression[@FullMethodName='System.debug']
26
+ [not(ancestor::UserClass[ModifierNode[@Test = true()]])]
27
+ ```
28
+
29
+ **PMD ruleset:**
30
+ ```xml
31
+ <rule name="NoSystemDebugInProduction" language="apex"
32
+ message="System.debug statements are not allowed in production code"
33
+ class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
34
+ <priority>3</priority>
35
+ <properties>
36
+ <property name="xpath">
37
+ <value>//MethodCallExpression[@FullMethodName='System.debug'][not(ancestor::UserClass[ModifierNode[@Test = true()]])]</value>
38
+ </property>
39
+ </properties>
40
+ </rule>
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Example 2: SOQL Query Inside a Loop
46
+
47
+ **Problem:** Governor limit risk — N+1 queries.
48
+
49
+ **Sample violating code:**
50
+ ```apex
51
+ public class AccountProcessor {
52
+ public void process(List<Account> accounts) {
53
+ for (Account acc : accounts) {
54
+ List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ **AST nodes:**
61
+ ```xml
62
+ <ForEachStatement>
63
+ ...
64
+ <BlockStatement>
65
+ <VariableDeclarationStatements>
66
+ <VariableDeclaration>
67
+ <SoqlExpression Query='SELECT Id FROM Contact...' />
68
+ ```
69
+
70
+ **XPath:**
71
+ ```xpath
72
+ //ForEachStatement/BlockStatement//SoqlExpression
73
+ |
74
+ //ForLoopStatement/BlockStatement//SoqlExpression
75
+ |
76
+ //WhileLoopStatement/BlockStatement//SoqlExpression
77
+ ```
78
+
79
+ > ⚠️ **Why `/BlockStatement//`?** ForEachStatement has the iterable SOQL as a direct child alongside BlockStatement. Without scoping to `/BlockStatement//`, the XPath also matches `for (Contact c : [SELECT...])` — a valid Apex idiom that is NOT a governor limit risk.
80
+
81
+ ---
82
+
83
+ ## Example 3: Enforce @IsTest(testFor) Annotation
84
+
85
+ **Problem:** Test classes should specify which class they test for traceability.
86
+ (GitHub issue #2008 — real community request)
87
+
88
+ **Sample violating code:**
89
+ ```apex
90
+ @IsTest
91
+ public class MyServiceTest {
92
+ // Missing testFor parameter
93
+ }
94
+ ```
95
+
96
+ **AST nodes:**
97
+ ```xml
98
+ <UserClass Image='MyServiceTest'>
99
+ <ModifierNode Test='true'>
100
+ <Annotation Image='IsTest'>
101
+ <!-- No AnnotationParameter with Name='testFor' -->
102
+ </Annotation>
103
+ </ModifierNode>
104
+ ```
105
+
106
+ **XPath:**
107
+ ```xpath
108
+ /ApexFile/UserClass/ModifierNode/Annotation[
109
+ @Image='IsTest'
110
+ and count(AnnotationParameter[@Name='testFor']) = 0
111
+ ]
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Example 4: DML Operations Without Try-Catch
117
+
118
+ **Problem:** Unhandled DML exceptions cause runtime failures.
119
+ (GitHub issue #738 — user requested "detect missing try-catch")
120
+
121
+ **Sample violating code:**
122
+ ```apex
123
+ public class AccountService {
124
+ public void createAccount(String name) {
125
+ Account acc = new Account(Name = name);
126
+ insert acc; // No try-catch!
127
+ }
128
+ }
129
+ ```
130
+
131
+ **AST nodes:**
132
+ ```xml
133
+ <DmlInsertStatement>
134
+ <VariableExpression Image='acc' />
135
+ </DmlInsertStatement>
136
+ <!-- No TryCatchFinallyBlockStatement ancestor -->
137
+ ```
138
+
139
+ **XPath:**
140
+ ```xpath
141
+ //DmlInsertStatement[not(ancestor::TryCatchFinallyBlockStatement)]
142
+ |
143
+ //DmlUpdateStatement[not(ancestor::TryCatchFinallyBlockStatement)]
144
+ |
145
+ //DmlDeleteStatement[not(ancestor::TryCatchFinallyBlockStatement)]
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Example 5: Nested If Statements (Max Depth 3)
151
+
152
+ **Problem:** Deep nesting reduces readability and maintainability.
153
+
154
+ **Sample violating code:**
155
+ ```apex
156
+ public class ComplexLogic {
157
+ public void process(Integer a, Integer b, Integer c, Integer d) {
158
+ if (a > 0) {
159
+ if (b > 0) {
160
+ if (c > 0) {
161
+ if (d > 0) { // 4th level — violation!
162
+ doWork();
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+ ```
170
+
171
+ **XPath (flags depth 4+):**
172
+ ```xpath
173
+ //IfBlockStatement[
174
+ ancestor::IfBlockStatement[
175
+ ancestor::IfBlockStatement[
176
+ ancestor::IfBlockStatement
177
+ ]
178
+ ]
179
+ ]
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Example 6: Class Without Explicit Sharing Declaration
185
+
186
+ **Problem:** Classes without sharing default to "without sharing" — security risk.
187
+
188
+ **Sample violating code:**
189
+ ```apex
190
+ public class UnsafeService { // No "with sharing" or "without sharing"
191
+ public List<Account> getAccounts() {
192
+ return [SELECT Id FROM Account];
193
+ }
194
+ }
195
+ ```
196
+
197
+ **AST nodes:**
198
+ ```xml
199
+ <UserClass Image='UnsafeService'>
200
+ <ModifierNode WithSharing='false' WithoutSharing='false' InheritedSharing='false' ...>
201
+ ```
202
+
203
+ **XPath:**
204
+ ```xpath
205
+ //UserClass[
206
+ ModifierNode[
207
+ @WithSharing = false()
208
+ and @WithoutSharing = false()
209
+ and @InheritedSharing = false()
210
+ ]
211
+ and not(ModifierNode[@Test = true()])
212
+ ]
213
+ ```
214
+
215
+ ---
216
+
217
+ ## How to Create These
218
+
219
+ For each example above, the creation process was:
220
+
221
+ 1. Write the sample violating code (5-10 lines)
222
+ 2. Run: `sf code-analyzer ast-dump --file sample.cls`
223
+ 3. Find the relevant node in the XML output
224
+ 4. Write XPath using the exact node names and attributes
225
+ 5. Create the ruleset XML using the template
226
+ 6. Validate: `sf code-analyzer rules --rule-selector pmd:RuleName`
227
+ 7. Test: `sf code-analyzer run --rule-selector pmd:RuleName --target sample.cls`
@@ -0,0 +1,288 @@
1
+ # Advanced PMD Patterns
2
+
3
+ Multi-rule rulesets, overriding built-in rules, exclusion patterns, Java-based rules, and sharing rules across projects.
4
+
5
+ ## Multiple Rules in One Ruleset File
6
+
7
+ A single ruleset XML can contain any number of rules. This is the recommended approach for teams maintaining a shared rule set:
8
+
9
+ ```xml
10
+ <?xml version="1.0" encoding="UTF-8"?>
11
+ <ruleset name="TeamStandards"
12
+ xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
13
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
14
+ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
15
+
16
+ <description>Team coding standards</description>
17
+
18
+ <!-- Rule 1: Custom XPath rule -->
19
+ <rule name="NoSystemDebug" language="apex"
20
+ message="Remove System.debug before merging"
21
+ class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
22
+ <priority>3</priority>
23
+ <properties>
24
+ <property name="xpath">
25
+ <value><![CDATA[
26
+ //MethodCallExpression[@FullMethodName='System.debug']
27
+ [not(ancestor::UserClass[ModifierNode[@Test = true()]])]
28
+ ]]></value>
29
+ </property>
30
+ </properties>
31
+ </rule>
32
+
33
+ <!-- Rule 2: Another custom XPath rule -->
34
+ <rule name="SoqlInLoop" language="apex"
35
+ message="SOQL query inside a loop — move query before the loop"
36
+ class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
37
+ <priority>2</priority>
38
+ <properties>
39
+ <property name="xpath">
40
+ <value><![CDATA[
41
+ //ForEachStatement//SoqlExpression | //ForLoopStatement//SoqlExpression | //WhileLoopStatement//SoqlExpression
42
+ ]]></value>
43
+ </property>
44
+ </properties>
45
+ </rule>
46
+
47
+ <!-- Rule 3: XML metadata rule in the same file -->
48
+ <rule name="MinApiVersion" language="xml"
49
+ message="Update API version to 60.0+"
50
+ class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
51
+ <priority>3</priority>
52
+ <properties>
53
+ <property name="xpath">
54
+ <value><![CDATA[
55
+ //*[local-name()='apiVersion'][number(substring-before(text(), '.')) < 60]
56
+ ]]></value>
57
+ </property>
58
+ </properties>
59
+ </rule>
60
+ </ruleset>
61
+ ```
62
+
63
+ Rules with different `language` values (apex, xml, visualforce, etc.) can coexist in one ruleset. PMD applies each rule only to files matching its language.
64
+
65
+ ## Overriding Built-in PMD Rules
66
+
67
+ Override severity, priority, or configurable properties on PMD's built-in rules without writing new ones:
68
+
69
+ ```xml
70
+ <?xml version="1.0" encoding="UTF-8"?>
71
+ <ruleset name="CustomizedBuiltins"
72
+ xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
73
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
74
+ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
75
+
76
+ <description>Customized built-in rule thresholds</description>
77
+
78
+ <!-- Lower the complexity threshold from default 25 to 10 -->
79
+ <rule ref="category/apex/design.xml/CyclomaticComplexity">
80
+ <priority>2</priority>
81
+ <properties>
82
+ <property name="classReportLevel" value="40" />
83
+ <property name="methodReportLevel" value="10" />
84
+ </properties>
85
+ </rule>
86
+
87
+ <!-- Change excessive parameter count from default 4 to 3 -->
88
+ <rule ref="category/apex/design.xml/ExcessiveParameterList">
89
+ <properties>
90
+ <property name="minimum" value="3" />
91
+ </properties>
92
+ </rule>
93
+
94
+ <!-- Make debug statement rule high-severity instead of moderate -->
95
+ <rule ref="category/apex/bestpractices.xml/DebugsShouldUseLoggingLevel">
96
+ <priority>2</priority>
97
+ <properties>
98
+ <property name="strictMode" value="true" />
99
+ </properties>
100
+ </rule>
101
+
102
+ <!-- Include an entire category with one exclusion -->
103
+ <rule ref="category/apex/security.xml">
104
+ <exclude name="ApexSuggestUsingNamedCred" />
105
+ </rule>
106
+ </ruleset>
107
+ ```
108
+
109
+ **Configuration:**
110
+ ```yaml
111
+ # code-analyzer.yml
112
+ engines:
113
+ pmd:
114
+ custom_rulesets:
115
+ - "custom-rules/customized-builtins.xml"
116
+ ```
117
+
118
+ ### How rule ref works
119
+
120
+ | Pattern | Effect |
121
+ |---------|--------|
122
+ | `<rule ref="category/apex/design.xml/CyclomaticComplexity">` | Override one specific rule |
123
+ | `<rule ref="category/apex/security.xml">` | Include entire category |
124
+ | `<rule ref="..."><exclude name="RuleName"/></rule>` | Include category minus specific rules |
125
+ | Nested `<priority>` | Override severity (1=Critical, 2=High, 3=Moderate, 4=Low, 5=Info) |
126
+ | Nested `<properties>` | Override configurable thresholds/options |
127
+
128
+ ### Common built-in rules to customize
129
+
130
+ | Rule | Useful Properties |
131
+ |------|-------------------|
132
+ | `CyclomaticComplexity` | `classReportLevel` (default: 40), `methodReportLevel` (default: 25) |
133
+ | `ExcessiveParameterList` | `minimum` (default: 4) |
134
+ | `ExcessiveClassLength` | `minimum` (default: 1000) |
135
+ | `TooManyFields` | `maxfields` (default: 15) |
136
+ | `NcssMethodCount` | `minimum` (default: 60) |
137
+ | `DebugsShouldUseLoggingLevel` | `strictMode` (default: false) |
138
+ | `ApexUnitTestClassShouldHaveAsserts` | (no properties — exclude if unwanted) |
139
+
140
+ ## Exclusion Patterns
141
+
142
+ Three mechanisms for excluding files and rules:
143
+
144
+ ### 1. File exclusions in `code-analyzer.yml`
145
+
146
+ Exclude entire directories or file patterns from all engines:
147
+
148
+ ```yaml
149
+ # code-analyzer.yml
150
+ ignores:
151
+ files:
152
+ - "**/node_modules/**"
153
+ - "**/fflib_*"
154
+ - "**/*Test*.cls"
155
+ - "**/generated/**"
156
+ ```
157
+
158
+ Supports glob patterns: `*`, `**`, `?`, `[...]`, `{...}`
159
+
160
+ ### 2. Rule exclusions in PMD rulesets
161
+
162
+ Exclude specific rules when including an entire category:
163
+
164
+ ```xml
165
+ <rule ref="category/apex/bestpractices.xml">
166
+ <exclude name="ApexUnitTestClassShouldHaveAsserts" />
167
+ <exclude name="ApexAssertionsShouldIncludeMessage" />
168
+ </rule>
169
+ ```
170
+
171
+ ### 3. Exclude patterns in PMD rulesets
172
+
173
+ Exclude files at the PMD level (applies only to rules in that ruleset):
174
+
175
+ ```xml
176
+ <ruleset name="MyRules" ...>
177
+ <exclude-pattern>.*/fflib_.*</exclude-pattern>
178
+ <exclude-pattern>.*/test/.*</exclude-pattern>
179
+
180
+ <rule name="..." ...>
181
+ ...
182
+ </rule>
183
+ </ruleset>
184
+ ```
185
+
186
+ ### 4. Disabling individual rules via config
187
+
188
+ Disable any rule without editing rulesets:
189
+
190
+ ```yaml
191
+ # code-analyzer.yml
192
+ rules:
193
+ pmd:
194
+ ApexUnitTestClassShouldHaveAsserts:
195
+ disabled: true
196
+ CyclomaticComplexity:
197
+ severity: "Low" # Downgrade instead of disable
198
+ ```
199
+
200
+ ## Java-Based Custom Rules (Advanced)
201
+
202
+ For rules that need logic beyond XPath (complex data flow, cross-file analysis), write Java PMD rules:
203
+
204
+ 1. **Create a Java class** extending `net.sourceforge.pmd.lang.apex.rule.AbstractApexRule`
205
+ 2. **Compile into a JAR** file
206
+ 3. **Reference in config:**
207
+
208
+ ```yaml
209
+ # code-analyzer.yml
210
+ engines:
211
+ pmd:
212
+ java_classpath_entries:
213
+ - "custom-rules/my-rules.jar" # JAR with compiled rule classes
214
+ - "custom-rules/lib/" # Folder of JARs (all JARs inside loaded)
215
+ custom_rulesets:
216
+ - "category/myteam/rules.xml" # Ruleset inside the JAR (classpath resource)
217
+ ```
218
+
219
+ The JAR's ruleset XML references the Java class:
220
+ ```xml
221
+ <rule name="AvoidFutureAnnotation" language="apex"
222
+ message="Use Queueable instead of @Future"
223
+ class="com.myteam.pmd.rules.AvoidFutureRule">
224
+ <priority>3</priority>
225
+ </rule>
226
+ ```
227
+
228
+ **Requirements:**
229
+ - Java 11+ for compilation
230
+ - PMD 7.x API (use `net.sourceforge.pmd.lang.rule.xpath.XPathRule` for XPath, or extend `AbstractApexRule` for visitor-pattern Java rules)
231
+
232
+ ## Sharing Rules Across Projects
233
+
234
+ ### Approach 1: Relative paths with shared config
235
+
236
+ If projects share a parent directory or monorepo:
237
+ ```yaml
238
+ # code-analyzer.yml
239
+ engines:
240
+ pmd:
241
+ custom_rulesets:
242
+ - "../shared-rules/team-standards.xml" # Relative to config_root
243
+ ```
244
+
245
+ `config_root` is the directory containing `code-analyzer.yml`. All relative paths resolve from there.
246
+
247
+ ### Approach 2: JAR-based distribution
248
+
249
+ Package rules into a JAR and distribute via artifact repository:
250
+
251
+ 1. Build a JAR containing your ruleset XML at a classpath resource path (e.g., `category/myteam/standards.xml`)
252
+ 2. Place the JAR in each project's `custom-rules/` directory (or download in CI)
253
+ 3. Reference in config:
254
+
255
+ ```yaml
256
+ engines:
257
+ pmd:
258
+ java_classpath_entries:
259
+ - "custom-rules/myteam-rules-1.0.jar"
260
+ custom_rulesets:
261
+ - "category/myteam/standards.xml" # Resource inside the JAR
262
+ ```
263
+
264
+ ### Approach 3: Git submodule or symlink
265
+
266
+ For simpler setups, share the ruleset XML via git submodule:
267
+ ```bash
268
+ git submodule add https://github.com/myteam/pmd-rules.git custom-rules/shared
269
+ ```
270
+
271
+ ```yaml
272
+ engines:
273
+ pmd:
274
+ custom_rulesets:
275
+ - "custom-rules/shared/team-standards.xml"
276
+ ```
277
+
278
+ ### CI/CD Integration
279
+
280
+ All approaches work in CI because paths are relative to `config_root`. Example GitHub Actions step:
281
+
282
+ ```yaml
283
+ - name: Run Code Analyzer
284
+ run: |
285
+ sf code-analyzer run --rule-selector "pmd:2,regex:2" --target force-app/
286
+ ```
287
+
288
+ No absolute paths needed — the config file handles resolution.
@@ -0,0 +1,127 @@
1
+ # Apex AST Node Reference
2
+
3
+ PMD 7.x Apex AST node types and their attributes. Use `sf code-analyzer ast-dump --file <file.cls>` to see the actual tree for your code.
4
+
5
+ ## Node Hierarchy (Common Structure)
6
+
7
+ ```text
8
+ ApexFile
9
+ ├── UserClass / UserInterface / UserTrigger / UserEnum
10
+ │ ├── FormalComment (Javadoc)
11
+ │ ├── ModifierNode (public, private, static, etc.)
12
+ │ ├── Field (class-level variables)
13
+ │ │ └── VariableDeclaration
14
+ │ └── Method
15
+ │ ├── ModifierNode
16
+ │ ├── Parameter
17
+ │ └── BlockStatement
18
+ │ ├── VariableDeclarationStatements
19
+ │ ├── ExpressionStatement
20
+ │ │ └── MethodCallExpression
21
+ │ ├── IfElseBlockStatement
22
+ │ │ └── IfBlockStatement
23
+ │ ├── ForEachStatement / ForLoopStatement / WhileLoopStatement
24
+ │ ├── TryCatchFinallyBlockStatement
25
+ │ │ ├── BlockStatement (try body)
26
+ │ │ ├── CatchBlockStatement
27
+ │ │ └── BlockStatement (finally body)
28
+ │ ├── DmlInsertStatement / DmlUpdateStatement / DmlDeleteStatement
29
+ │ ├── ReturnStatement
30
+ │ └── ThrowStatement
31
+ ```
32
+
33
+ ## ModifierNode Attributes
34
+
35
+ Every class, method, field, and parameter has a `ModifierNode` with these boolean attributes:
36
+
37
+ | Attribute | Meaning |
38
+ |-----------|---------|
39
+ | `Public` | public access |
40
+ | `Private` | private access |
41
+ | `Protected` | protected access |
42
+ | `Global` | global access |
43
+ | `Static` | static member |
44
+ | `Final` | final/const |
45
+ | `Abstract` | abstract method/class |
46
+ | `Virtual` | virtual method/class |
47
+ | `Override` | overrides parent method |
48
+ | `Test` | @IsTest annotated |
49
+ | `TestOrTestSetup` | @IsTest or @TestSetup |
50
+ | `WebService` | webservice method |
51
+ | `WithSharing` | with sharing class |
52
+ | `WithoutSharing` | without sharing class |
53
+ | `InheritedSharing` | inherited sharing class |
54
+ | `Transient` | transient field |
55
+ | `Modifiers` | Bitmask integer of all modifiers |
56
+
57
+ ⚠️ **PMD 7 Boolean Attribute Comparison:**
58
+
59
+ In PMD 7, these boolean attributes are **always present** on the node — they are never absent. This means:
60
+ - ✅ `@WithSharing = false()` — correct (XPath boolean function)
61
+ - ✅ `@WithSharing = true()` — correct
62
+ - ❌ `@WithSharing = 'false'` — WRONG (string comparison, won't match)
63
+ - ❌ `not(@WithSharing)` — WRONG (attribute always exists, so this is always false)
64
+ - ❌ `@WithSharing` (as existence check) — WRONG (always true, attribute always present)
65
+
66
+ Use the XPath `true()`/`false()` functions for boolean comparisons:
67
+ ```xpath
68
+ <!-- Classes without sharing declaration -->
69
+ //UserClass[ModifierNode[@WithSharing = false() and @WithoutSharing = false() and @InheritedSharing = false()]]
70
+
71
+ <!-- Abstract classes -->
72
+ //UserClass[ModifierNode[@Abstract = true()]]
73
+ ```
74
+
75
+ ## MethodCallExpression Attributes
76
+
77
+ | Attribute | Example | Notes |
78
+ |-----------|---------|-------|
79
+ | `FullMethodName` | `'System.debug'`, `'Database.query'` | Most reliable for matching |
80
+ | `MethodName` | `'debug'`, `'query'` | Just the method part |
81
+ | `InputParametersSize` | `'1'`, `'0'` | Number of arguments |
82
+
83
+ ## LiteralExpression Attributes
84
+
85
+ | Attribute | Values | Notes |
86
+ |-----------|--------|-------|
87
+ | `LiteralType` | `STRING`, `INTEGER`, `DECIMAL`, `DOUBLE`, `LONG`, `BOOLEAN` | Type of literal |
88
+ | `Image` | The literal value | For strings, excludes quotes |
89
+ | `String` | `'true'` / `'false'` | Boolean shorthand |
90
+ | `Null` | `'true'` / `'false'` | Is it a null literal |
91
+
92
+ ## SoqlExpression Attributes
93
+
94
+ | Attribute | Example | Notes |
95
+ |-----------|---------|-------|
96
+ | `Query` | `'SELECT Id FROM Account WHERE Name = :name'` | Original SOQL as written |
97
+ | `CanonicalQuery` | `'SELECT Id FROM Account WHERE Name = :tmpVar1'` | Normalized (bind vars replaced) |
98
+
99
+ ## Annotation Node
100
+
101
+ ```xml
102
+ <Annotation Image="IsTest">
103
+ <AnnotationParameter Name="testFor" Value="'ApexClass:MyService'" />
104
+ </Annotation>
105
+ ```
106
+
107
+ Use: `//Annotation[@Image='IsTest']` to find test annotations.
108
+
109
+ ## DML Nodes
110
+
111
+ | Statement | Node | Notes |
112
+ |-----------|------|-------|
113
+ | `insert x;` | `DmlInsertStatement` | |
114
+ | `update x;` | `DmlUpdateStatement` | |
115
+ | `delete x;` | `DmlDeleteStatement` | |
116
+ | `upsert x;` | `DmlUpsertStatement` | |
117
+ | `undelete x;` | `DmlUndeleteStatement` | |
118
+ | `Database.insert(x)` | `MethodCallExpression[@FullMethodName='Database.insert']` | Method-form DML |
119
+
120
+ ## Tips for Reading AST Dumps
121
+
122
+ 1. **`Image` attribute** = the actual source name (variable name, class name, method name)
123
+ 2. **`DefiningType` attribute** = which class this node belongs to (present on every node)
124
+ 3. **`RealLoc='true'`** = this node has a real source position (not compiler-generated)
125
+ 4. **`RealLoc='false'`** = compiler-generated node (e.g., implicit modifiers, empty references)
126
+ 5. **`EmptyReferenceExpression`** = placeholder for unqualified variable access (ignore these)
127
+ 6. **`Type` attribute on VariableDeclaration** = the declared type (e.g., `'Account'`, `'List<Contact>'`)