@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,83 @@
1
+ # XPath Patterns for Governor Limits
2
+
3
+ [← Back to XPath Patterns Index](xpath-patterns.md)
4
+
5
+ ## Governor Limit Patterns
6
+
7
+ ### SOQL inside loops
8
+
9
+ ⚠️ **Must scope to `/BlockStatement//`** — ForEachStatement has the iterable SOQL as a direct child alongside BlockStatement. Without this, `for (Contact c : [SELECT...])` is a false positive.
10
+
11
+ **ForEachStatement child structure (from ast-dump):**
12
+ ```text
13
+ ForEachStatement
14
+ ├── VariableDeclarationStatements ← loop variable declaration
15
+ ├── VariableExpression ← loop variable reference
16
+ ├── BlockStatement ← LOOP BODY — only match here
17
+ └── SoqlExpression (or VariableExpression) ← ITERABLE — do NOT flag
18
+ ```
19
+
20
+ ```xpath
21
+ //ForEachStatement/BlockStatement//SoqlExpression
22
+ |
23
+ //ForLoopStatement/BlockStatement//SoqlExpression
24
+ |
25
+ //WhileLoopStatement/BlockStatement//SoqlExpression
26
+ ```
27
+
28
+ **Catches:** `for (Id accId : ids) { Account acc = [SELECT ...]; }` (SOQL in body)
29
+ **Does NOT catch (correct):** `for (Contact c : [SELECT Id FROM Contact]) { ... }` (SOQL as iterable)
30
+
31
+ ### DML inside loops
32
+
33
+ Same principle — scope to BlockStatement:
34
+
35
+ ```xpath
36
+ //ForEachStatement/BlockStatement//DmlInsertStatement
37
+ |
38
+ //ForEachStatement/BlockStatement//DmlUpdateStatement
39
+ |
40
+ //ForEachStatement/BlockStatement//DmlDeleteStatement
41
+ |
42
+ //ForEachStatement/BlockStatement//DmlUpsertStatement
43
+ |
44
+ //ForLoopStatement/BlockStatement//DmlInsertStatement
45
+ |
46
+ //ForLoopStatement/BlockStatement//DmlUpdateStatement
47
+ |
48
+ //ForLoopStatement/BlockStatement//DmlDeleteStatement
49
+ |
50
+ //ForLoopStatement/BlockStatement//DmlUpsertStatement
51
+ |
52
+ //WhileLoopStatement/BlockStatement//DmlInsertStatement
53
+ |
54
+ //WhileLoopStatement/BlockStatement//DmlUpdateStatement
55
+ |
56
+ //WhileLoopStatement/BlockStatement//DmlDeleteStatement
57
+ |
58
+ //WhileLoopStatement/BlockStatement//DmlUpsertStatement
59
+ ```
60
+
61
+ **Shorter variant** (ForEach only, most common):
62
+ ```xpath
63
+ //ForEachStatement/BlockStatement//DmlInsertStatement
64
+ | //ForEachStatement/BlockStatement//DmlUpdateStatement
65
+ | //ForEachStatement/BlockStatement//DmlDeleteStatement
66
+ | //ForEachStatement/BlockStatement//DmlUpsertStatement
67
+ ```
68
+
69
+ ### Database methods inside loops
70
+
71
+ ```xpath
72
+ //ForEachStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.query']
73
+ |
74
+ //ForEachStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.insert']
75
+ |
76
+ //ForEachStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.update']
77
+ |
78
+ //ForEachStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.delete']
79
+ |
80
+ //ForLoopStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.query']
81
+ |
82
+ //WhileLoopStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.query']
83
+ ```
@@ -0,0 +1,108 @@
1
+ # XPath Patterns for Method Calls and Annotations
2
+
3
+ [← Back to XPath Patterns Index](xpath-patterns.md)
4
+
5
+ ## Method Call Patterns
6
+
7
+ ### Ban System.debug (non-test classes)
8
+
9
+ ```xpath
10
+ //MethodCallExpression[@FullMethodName='System.debug']
11
+ [not(ancestor::UserClass[ModifierNode[@Test = true()]])]
12
+ ```
13
+
14
+ **AST evidence:** `MethodCallExpression` has `@FullMethodName='System.debug'`. Test classes have `ModifierNode[@Test = true()]`.
15
+
16
+ **Catches:** `System.debug('anything');` in production classes
17
+ **Does NOT catch (correct):** Same call inside `@IsTest` classes
18
+
19
+ ### Ban Database.query (dynamic SOQL — SOQL injection risk)
20
+
21
+ ```xpath
22
+ //MethodCallExpression[@FullMethodName='Database.query']
23
+ ```
24
+
25
+ **AST evidence:** `MethodCallExpression[@FullMethodName='Database.query']` (line 118 of verified AST)
26
+
27
+ To exclude test classes:
28
+ ```xpath
29
+ //MethodCallExpression[@FullMethodName='Database.query']
30
+ [not(ancestor::UserClass[ModifierNode[@Test = true()]])]
31
+ ```
32
+
33
+ ### Ban Test.isRunningTest() in production code
34
+
35
+ ```xpath
36
+ //MethodCallExpression[@FullMethodName='Test.isRunningTest']
37
+ [not(ancestor::UserClass[ModifierNode[@Test = true()]])]
38
+ ```
39
+
40
+ ### Ban specific method calls (generic pattern)
41
+
42
+ ```xpath
43
+ //MethodCallExpression[@FullMethodName='ClassName.methodName']
44
+ ```
45
+
46
+ Common examples:
47
+ - `@FullMethodName='System.debug'`
48
+ - `@FullMethodName='Database.query'`
49
+ - `@FullMethodName='Test.isRunningTest'`
50
+ - `@FullMethodName='UserInfo.getUserId'`
51
+ - `@FullMethodName='Limits.getQueries'`
52
+
53
+ ---
54
+
55
+ ## Annotation Patterns
56
+
57
+ ### @AuraEnabled without cacheable=true
58
+
59
+ ```xpath
60
+ //Method/ModifierNode/Annotation[@Name='AuraEnabled'
61
+ and not(AnnotationParameter[@Name='cacheable' and @Value='true'])]
62
+ ```
63
+
64
+ **AST evidence:** `Annotation[@Name='AuraEnabled']` with child `AnnotationParameter[@Name='cacheable'][@Value='true']`
65
+
66
+ **Catches:** `@AuraEnabled public static ...` (no cacheable)
67
+ **Does NOT catch (correct):** `@AuraEnabled(cacheable=true) public static ...`
68
+
69
+ ### @future methods (recommend Queueable instead)
70
+
71
+ ```xpath
72
+ //Method/ModifierNode/Annotation[@Name='Future']
73
+ ```
74
+
75
+ **AST evidence:** Annotation `@Name='Future'` (note: PMD normalizes `@future` → `Future` in the Name attribute, but `@RawName='future'`)
76
+
77
+ ### @SuppressWarnings usage (audit/ban)
78
+
79
+ ```xpath
80
+ //Annotation[@Name='SuppressWarnings']
81
+ ```
82
+
83
+ **AST evidence:** `Annotation[@Name='SuppressWarnings']` with `AnnotationParameter[@Name='value'][@Value='PMD.RuleName']`
84
+
85
+ To flag specific suppressions:
86
+ ```xpath
87
+ //Annotation[@Name='SuppressWarnings']/AnnotationParameter[contains(@Value, 'ApexCRUDViolation')]
88
+ ```
89
+
90
+ ### @IsTest without testFor parameter
91
+
92
+ From GitHub issue #2008:
93
+ ```xpath
94
+ //UserClass/ModifierNode/Annotation[
95
+ @Name='IsTest'
96
+ and not(AnnotationParameter[@Name='testFor'])
97
+ ]
98
+ ```
99
+
100
+ ### @IsTest class without System.runAs
101
+
102
+ ```xpath
103
+ //UserClass[ModifierNode[@Test = true()]]
104
+ /Method[ModifierNode[@Test = true()]]
105
+ /BlockStatement[not(.//RunAsBlockStatement)]
106
+ ```
107
+
108
+ **AST evidence:** `RunAsBlockStatement` is the node for `System.runAs(...) { }` blocks.
@@ -0,0 +1,45 @@
1
+ # XPath Patterns for Security
2
+
3
+ [← Back to XPath Patterns Index](xpath-patterns.md)
4
+
5
+ ## Security Patterns
6
+
7
+ ### Class without sharing declaration
8
+
9
+ ```xpath
10
+ //UserClass[
11
+ @Nested = false()
12
+ and ModifierNode[@WithSharing = false() and @WithoutSharing = false() and @InheritedSharing = false()]
13
+ ]
14
+ ```
15
+
16
+ **AST evidence:** `ModifierNode` has `@WithSharing`, `@WithoutSharing`, `@InheritedSharing` — all `false()` means no sharing keyword declared (implicit without sharing).
17
+
18
+ Added `@Nested = false()` to exclude inner classes (which inherit from parent).
19
+
20
+ > ⚠️ **PMD 7 boolean attributes:** `@WithSharing`, `@WithoutSharing`, `@InheritedSharing`, and `@Nested` are boolean-typed in PMD 7 — string comparison (`='false'`) errors with "Cannot compare xs:boolean to xs:string". Always use `= false()` / `= true()`. See [xpath-patterns.md](xpath-patterns.md) for the full list.
21
+
22
+ ### SOQL without WITH USER_MODE or SECURITY_ENFORCED
23
+
24
+ ```xpath
25
+ //SoqlExpression[
26
+ not(contains(@CanonicalQuery, 'WITH USER_MODE'))
27
+ and not(contains(@CanonicalQuery, 'WITH SECURITY_ENFORCED'))
28
+ ]
29
+ [not(ancestor::UserClass[ModifierNode[@Test = true()]])]
30
+ ```
31
+
32
+ **AST evidence:** `SoqlExpression[@CanonicalQuery]` contains the full normalized query text.
33
+
34
+ ### Hardcoded Salesforce IDs (structural — string literals 15-18 chars)
35
+
36
+ ```xpath
37
+ //LiteralExpression[
38
+ @LiteralType='STRING'
39
+ and string-length(@Image) >= 15
40
+ and string-length(@Image) <= 18
41
+ ]
42
+ [not(ancestor::UserClass[ModifierNode[@Test = true()]])]
43
+ ```
44
+
45
+ **Note:** `matches()` function may not be available in all PMD XPath versions. The string-length check catches most cases. For precision, use Regex engine instead.
@@ -0,0 +1,127 @@
1
+ # XPath Patterns for Code Structure, Tests, and Naming
2
+
3
+ [← Back to XPath Patterns Index](xpath-patterns.md)
4
+
5
+ ## Code Structure Patterns
6
+
7
+ ### DML without try-catch
8
+
9
+ ```xpath
10
+ //DmlInsertStatement[not(ancestor::TryCatchFinallyBlockStatement)]
11
+ |
12
+ //DmlUpdateStatement[not(ancestor::TryCatchFinallyBlockStatement)]
13
+ |
14
+ //DmlDeleteStatement[not(ancestor::TryCatchFinallyBlockStatement)]
15
+ |
16
+ //DmlUpsertStatement[not(ancestor::TryCatchFinallyBlockStatement)]
17
+ ```
18
+
19
+ **AST evidence:** DML nodes sit inside `TryCatchFinallyBlockStatement` when wrapped in try-catch.
20
+
21
+ To exclude test classes:
22
+ ```xpath
23
+ //DmlInsertStatement[
24
+ not(ancestor::TryCatchFinallyBlockStatement)
25
+ and not(ancestor::UserClass[ModifierNode[@Test = true()]])
26
+ ]
27
+ ```
28
+
29
+ ### Empty catch blocks (swallowed exceptions)
30
+
31
+ ```xpath
32
+ //CatchBlockStatement[BlockStatement[not(*)]]
33
+ ```
34
+
35
+ **AST evidence:** `CatchBlockStatement` contains a `BlockStatement`. Empty body = `BlockStatement` with no children.
36
+
37
+ ### Nested if statements (max depth 3)
38
+
39
+ ```xpath
40
+ //IfBlockStatement[
41
+ ancestor::IfBlockStatement[
42
+ ancestor::IfBlockStatement[
43
+ ancestor::IfBlockStatement
44
+ ]
45
+ ]
46
+ ]
47
+ ```
48
+
49
+ ### Methods with too many parameters
50
+
51
+ ```xpath
52
+ //Method[@Arity >= 5 and @Constructor = false()]
53
+ ```
54
+
55
+ **AST evidence:** `Method[@Arity]` gives parameter count directly.
56
+
57
+ ### Logic in trigger (should delegate to handler)
58
+
59
+ For trigger files (UserTrigger node):
60
+ ```xpath
61
+ //UserTrigger/BlockStatement//DmlInsertStatement
62
+ |
63
+ //UserTrigger/BlockStatement//DmlUpdateStatement
64
+ |
65
+ //UserTrigger/BlockStatement//SoqlExpression
66
+ |
67
+ //UserTrigger/BlockStatement//MethodCallExpression[@FullMethodName='Database.query']
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Test Quality Patterns
73
+
74
+ ### Test method without assertions
75
+
76
+ ```xpath
77
+ //Method[ModifierNode[@Test = true()]]
78
+ /BlockStatement[
79
+ not(.//MethodCallExpression[
80
+ contains(@FullMethodName, 'System.assert')
81
+ or contains(@FullMethodName, 'Assert.')
82
+ ])
83
+ ]
84
+ ```
85
+
86
+ **Catches:** Test methods with no `System.assert*` or `Assert.*` calls anywhere in their body.
87
+
88
+ ### Test method without System.runAs
89
+
90
+ ```xpath
91
+ //Method[ModifierNode[@Test = true()]]
92
+ /BlockStatement[not(.//RunAsBlockStatement)]
93
+ ```
94
+
95
+ **AST evidence:** `System.runAs(user) { ... }` becomes `RunAsBlockStatement` in the AST.
96
+
97
+ ### Test using seeAllData=true
98
+
99
+ ```xpath
100
+ //Annotation[@Name='IsTest']/AnnotationParameter[@Name='seeAllData' and @Value='true']
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Naming Convention Patterns
106
+
107
+ ### Class name doesn't match file (non-test)
108
+
109
+ This is better enforced by the built-in rule, but the XPath pattern for a specific convention:
110
+ ```xpath
111
+ //UserClass[@Nested = false() and not(starts-with(@Image, 'Test')) and not(ends-with(@Image, 'Test'))]
112
+ ```
113
+
114
+ ### Method naming (non-standard)
115
+
116
+ ```xpath
117
+ //Method[
118
+ @Constructor = false()
119
+ and not(ModifierNode[@Test = true()])
120
+ and not(starts-with(@Image, 'get'))
121
+ and not(starts-with(@Image, 'set'))
122
+ and not(starts-with(@Image, 'is'))
123
+ and matches(@Image, '^[A-Z]')
124
+ ]
125
+ ```
126
+
127
+ **Catches:** Methods starting with uppercase in non-test code (Apex convention is camelCase).
@@ -0,0 +1,131 @@
1
+ # XPath Patterns for Apex Custom Rules
2
+
3
+ Pre-validated PMD XPath patterns for Salesforce Apex code. Every pattern below has been verified against actual `sf code-analyzer ast-dump` output. Use these directly — but still run `ast-dump` on YOUR code to confirm node names haven't changed in newer PMD versions.
4
+
5
+ ## XPath Syntax Quick Reference
6
+
7
+ | Syntax | Meaning |
8
+ |--------|---------|
9
+ | `//Node` | Find Node anywhere in tree |
10
+ | `//Node[@attr='value']` | Node with specific string attribute value |
11
+ | `//Node[@attr = true()]` | Node with boolean attribute = true (**PMD 7 requirement**) |
12
+ | `//Parent//Child` | Child anywhere inside Parent (any descendant) |
13
+ | `//Parent/Child` | **Direct child only** — use this to avoid false positives |
14
+ | `//Node[not(...)]` | Node where condition is NOT true |
15
+ | `//Node[ancestor::Other]` | Node that has Other as an ancestor |
16
+ | `//Node[.//Other]` | Node that contains Other somewhere inside |
17
+ | `@Image` | The name/text of the node |
18
+ | `@FullMethodName` | Full qualified method name (e.g., 'System.debug') |
19
+ | `@LiteralType` | Type of literal: STRING, INTEGER, BOOLEAN, TRUE, FALSE, NULL |
20
+
21
+ ### PMD 7 Boolean Attributes — MUST use `true()` / `false()`
22
+
23
+ In PMD 7, many node attributes are **boolean typed**, not strings — even though `ast-dump` renders them as `Nested='false'` or `Test='true'` in the XML output. String attributes (`@FullMethodName`, `@Name`, `@Image`, `@LiteralType`, etc.) are safe to copy from the AST dump as-is. But **attributes whose values are `true` or `false` in the dump are boolean-typed** — comparing them with string literals (`@Nested='false'`) causes the error: "Cannot compare xs:boolean to xs:string".
24
+
25
+ Known boolean attributes by node:
26
+
27
+ | Node | Boolean Attributes |
28
+ |------|--------------------|
29
+ | `ModifierNode` | `@Test`, `@Public`, `@Private`, `@Protected`, `@Static`, `@Abstract`, `@Final`, `@Global`, `@WithSharing`, `@WithoutSharing`, `@InheritedSharing` |
30
+ | `UserClass` | `@Nested` |
31
+ | `Method` | `@Constructor` |
32
+
33
+ | WRONG | CORRECT |
34
+ |-------|---------|
35
+ | `UserClass[@Nested='false']` | `UserClass[@Nested = false()]` |
36
+ | `UserClass[@Nested='true']` | `UserClass[@Nested = true()]` |
37
+ | `ModifierNode[@Test='true']` | `ModifierNode[@Test = true()]` |
38
+ | `ModifierNode[@Static='false']` | `ModifierNode[@Static = false()]` |
39
+ | `Method[@Constructor = false()]` | `Method[@Constructor = false()]` |
40
+
41
+ `true()` and `false()` are XPath boolean functions. If you see an attribute that looks boolean in the AST dump, assume it is typed as boolean and use `true()`/`false()`.
42
+
43
+ ---
44
+
45
+ ### `/` vs `//` — The Most Common Source of False Positives
46
+
47
+ | XPath | Meaning | Risk |
48
+ |-------|---------|------|
49
+ | `//ForEachStatement//SoqlExpression` | SOQL anywhere inside ForEachStatement | ❌ Matches iterable position too |
50
+ | `//ForEachStatement/BlockStatement//SoqlExpression` | SOQL inside loop **body** only | ✅ Correct |
51
+
52
+ **Rule of thumb:** When matching inside a structural node (loop, if, try), always scope to `/BlockStatement//` to target the body, not sibling children like iterables or conditions.
53
+
54
+ ---
55
+
56
+ ## Pattern Categories (By Topic)
57
+
58
+ For detailed patterns in each category, see the dedicated files below:
59
+
60
+ | Category | File | Contents |
61
+ |----------|------|----------|
62
+ | Governor Limits | [xpath-patterns-governor-limits.md](xpath-patterns-governor-limits.md) | SOQL/DML in loops, Database methods in loops |
63
+ | Method Calls & Annotations | [xpath-patterns-method-calls.md](xpath-patterns-method-calls.md) | Ban specific methods, @AuraEnabled, @future, @IsTest, @SuppressWarnings patterns |
64
+ | Security | [xpath-patterns-security.md](xpath-patterns-security.md) | Sharing declarations, SOQL security modes, hardcoded IDs |
65
+ | Code Structure, Tests & Naming | [xpath-patterns-structure.md](xpath-patterns-structure.md) | DML error handling, empty catches, test assertions, naming conventions |
66
+
67
+ ---
68
+
69
+ ## Key Apex AST Node Names
70
+
71
+ | Apex Construct | AST Node | Key Attributes |
72
+ |---|---|---|
73
+ | Class | `UserClass` | `Image`, `SuperClassName`, `InterfaceNames`, `Nested` |
74
+ | Interface | `UserInterface` | `Image`, `SuperInterfaceName` |
75
+ | Method | `Method` | `Image`, `Arity`, `ReturnType`, `Constructor` |
76
+ | Trigger | `UserTrigger` | `Image`, `TargetName` |
77
+ | SOQL query | `SoqlExpression` | `Query`, `CanonicalQuery` |
78
+ | DML insert | `DmlInsertStatement` | |
79
+ | DML update | `DmlUpdateStatement` | |
80
+ | DML delete | `DmlDeleteStatement` | |
81
+ | DML upsert | `DmlUpsertStatement` | |
82
+ | Method call | `MethodCallExpression` | `FullMethodName`, `MethodName`, `InputParametersSize` |
83
+ | For-each loop | `ForEachStatement` | children: VariableDeclarationStatements, VariableExpression, **BlockStatement** (body), iterable |
84
+ | For loop | `ForLoopStatement` | children: init, condition, update, **BlockStatement** (body) |
85
+ | While loop | `WhileLoopStatement` | children: condition, **BlockStatement** (body) |
86
+ | If/else | `IfElseBlockStatement` > `IfBlockStatement` | `ElseStatement` |
87
+ | Try-catch | `TryCatchFinallyBlockStatement` | |
88
+ | Catch block | `CatchBlockStatement` | `ExceptionType`, `VariableName` |
89
+ | RunAs | `RunAsBlockStatement` | |
90
+ | String literal | `LiteralExpression` | `@LiteralType='STRING'`, `@Image` (value without quotes) |
91
+ | Integer literal | `LiteralExpression` | `@LiteralType='INTEGER'`, `@Image` |
92
+ | Boolean true | `LiteralExpression` | `@LiteralType='TRUE'` |
93
+ | Boolean false | `LiteralExpression` | `@LiteralType='FALSE'` |
94
+ | Null | `LiteralExpression` | `@LiteralType='NULL'` |
95
+ | Variable | `VariableExpression` | `@Image` (name) |
96
+ | Assignment | `AssignmentExpression` | `@Op` (=, +=, etc.) |
97
+ | Binary expression | `BinaryExpression` | `@Op` (+, -, *, /) |
98
+ | Boolean expression | `BooleanExpression` | `@Op` (>, <, ==, !=, >=, <=) |
99
+ | New object | `NewKeyValueObjectExpression` | `@Type` |
100
+ | New object (no-arg) | `NewObjectExpression` | `@Type` |
101
+ | Return | `ReturnStatement` | |
102
+ | Annotation | `Annotation` | `@Name` (IsTest, AuraEnabled, Future, SuppressWarnings) |
103
+ | Annotation param | `AnnotationParameter` | `@Name`, `@Value` |
104
+ | Modifier | `ModifierNode` | `Public`, `Private`, `Static`, `Test`, `WithSharing`, `Global`, etc. |
105
+ | Parameter | `Parameter` | `@Image` (name), `@Type` |
106
+ | New list | `NewListInitExpression` | |
107
+ | New map | `NewMapInitExpression` | |
108
+
109
+ ---
110
+
111
+ ## XPath Best Practices
112
+
113
+ 1. **ALWAYS run `ast-dump` first** — never guess node names, even for patterns listed here
114
+ 2. **Use `/BlockStatement//` for loop/if body** — avoids matching iterables, conditions, etc.
115
+ 3. **Use `@FullMethodName`** for method calls — not `@Image` or `@MethodName` alone
116
+ 4. **Exclude test classes** with `[not(ancestor::UserClass[ModifierNode[@Test = true()]])]`
117
+ 5. **Test with BOTH positive AND negative cases** — ensure no false positives
118
+ 6. **Prefer `//Node` over absolute paths** — code structure varies
119
+ 7. **Use `ancestor::` / `not(ancestor::)`** for structural exclusions (try-catch, test class)
120
+ 8. **Keep XPath simple** — complex expressions are fragile and hard to maintain
121
+
122
+ ## Common False Positive Traps
123
+
124
+ | Pattern | Trap | Fix |
125
+ |---------|------|-----|
126
+ | SOQL in loop | `//ForEachStatement//SoqlExpression` matches iterable | Use `/BlockStatement//` |
127
+ | DML in loop | Same as above | Use `/BlockStatement//` |
128
+ | Ban method in all code | Flags test code too | Add `[not(ancestor::UserClass[ModifierNode[@Test = true()]])]` |
129
+ | Empty block detection | Matches intentional empty constructors | Add `[@Constructor = false()]` or exclude specific patterns |
130
+ | No sharing declaration | Flags inner classes (which inherit) | Add `[@Nested = false()]` |
131
+ | String literal length check | Matches test data strings | Exclude test classes |