afv-library/skills/dx-code-analyzer-custom-rule-create/references/regex-rule-schema.md

6.9 KiB

Regex Rule Schema Reference

Custom regex rules are defined inline in code-analyzer.yml under engines.regex.custom_rules.

Complete Schema

engines:
  regex:
    custom_rules:
      RuleName:                          # Must match: /^[A-Za-z@][A-Za-z_0-9@\-/]*$/
        regex: "/pattern/flags"          # REQUIRED — JavaScript regex literal format
        description: "What this checks"  # REQUIRED — Purpose of the rule
        violation_message: "Fix this"    # Optional — Message shown on violation
        severity: 3                      # Optional — 1=Critical, 2=High, 3=Moderate, 4=Low, 5=Info
        tags:                            # Optional — Default: ['Recommended', 'Custom']
          - "Custom"
          - "Security"
        file_extensions:                 # Optional — Default: all text files
          - ".cls"
          - ".trigger"
        regex_ignore: "/exception/i"     # Optional — Matches excluded from violations

Field Details

regex (Required)

JavaScript regex literal format: /pattern/flags

Format Example Meaning
/pattern/g /TODO/g Case-sensitive, global (find all matches) — minimum required
/pattern/gi /todo/gi Global + case-insensitive
/pattern/gm /^import/gm Global + multiline (^ matches each line start)

Common pitfalls:

  • The /g (global) flag is REQUIRED. Code Analyzer rejects regex patterns without at least one flag after the closing /. Always include /g (or /gi, /gm, etc.).
  • Backslashes must be escaped: \d → write as /\\d/
  • Dots match any character: use /\\.cls/ not /.cls/
  • Regex is tested against file content line by line

Named capture groups narrow the violation highlight to a specific portion of the match:

# Without named group — entire match is highlighted:
regex: "/System\\.debug\\([^)]*\\)/g"
# Highlights: "System.debug('hello')" (entire expression)

# With named group — only captured portion highlighted:
regex: "/(?<target>System\\.debug)\\([^)]*\\)/g"
# Highlights: "System.debug" (just the method name)

Use (?<target>...) when the regex needs surrounding context to match correctly, but the violation should point to a narrower location. Common patterns:

# Highlight just the hardcoded ID, not the surrounding quotes
regex: "/['\"](?<target>[0-9a-zA-Z]{15,18})['\"]/g"

# Highlight the annotation, not the whole line
regex: "/(?<target>@SuppressWarnings\\([^)]*\\))/g"

# Highlight the method call within a larger expression
regex: "/(?<target>Database\\.query)\\(/g"

description (Required)

Brief explanation of what the rule enforces. Used in rule listings and generated messages.

violation_message (Optional)

Message shown when a violation is found. If omitted, auto-generated as:

"A match of the regular expression <regex> was found for rule '': "

Good violation messages tell the user HOW to fix, not just WHAT's wrong:

  • "Hardcoded ID found"
  • "Replace hardcoded Salesforce ID with a Custom Label or Custom Metadata reference"

severity (Optional, default: 3)

Value Name Meaning
1 Critical Security vulnerability, must fix before deploy
2 High Significant quality issue, should fix
3 Moderate Recommended improvement
4 Low Minor style issue
5 Info Informational, no action required

Also accepts string names: "Critical", "High", "Moderate", "Low", "Info"

Array of tag strings. Used for --rule-selector filtering. Common tags:

  • Custom — auto-applied to user-created rules
  • Security — security-related rules
  • BestPractices — coding standards
  • Performance — performance anti-patterns
  • CodeStyle — formatting/naming rules

file_extensions (Optional, default: all text files)

Array of file extensions to scan. Each must start with . and match: /^([.][a-zA-Z0-9-_]+)+$/

Common values for Salesforce:

  • .cls — Apex classes
  • .trigger — Apex triggers
  • .js — JavaScript/LWC
  • .html — LWC HTML templates
  • .xml — Metadata files
  • .page — Visualforce pages
  • .component — Visualforce components

regex_ignore (Optional)

A second regex pattern — matches that ALSO match this pattern are excluded. Useful for reducing false positives.

⚠️ regex_ignore operates per-LINE, not per-FILE. It only skips a match if the same line also matches the ignore pattern. It does NOT exclude entire files or classes. For example, adding /@isTest/ only suppresses violations on lines that literally contain @isTest — a hardcoded ID on line 50 of a test class still flags because line 50 doesn't contain @isTest.

To exclude entire files, use ignores.files in code-analyzer.yml:

ignores:
  files:
    - "**/*Test.cls"
    - "**/test/**"
# Flag System.debug everywhere EXCEPT in lines with "// OK" comment
regex: "/System\\.debug/g"
regex_ignore: "/\\/\\/ OK/"

Validation Rules

Rule Error if violated
Name matches /^[A-Za-z@][A-Za-z_0-9@\-/]*$/ "Invalid rule name"
Regex starts and ends with / "Invalid regex format"
File extensions start with . "Invalid file extension"
Severity is 1-5 or valid name "Invalid severity"
No duplicate rule names "Rule already exists"

Multiple Rules Example

engines:
  regex:
    custom_rules:
      NoHardcodedIds:
        regex: "/['\"][0-9a-zA-Z]{15,18}['\"]/g"
        description: "Detects hardcoded Salesforce record IDs"
        violation_message: "Use Custom Labels or Custom Metadata instead of hardcoded IDs"
        severity: 2
        tags: ["Custom", "Security"]
        file_extensions: [".cls", ".trigger"]

      NoTodos:
        regex: "/TODO|FIXME|HACK/gi"
        description: "Flags TODO/FIXME/HACK comments that should be resolved"
        violation_message: "Resolve or remove this TODO/FIXME/HACK before merging"
        severity: 4
        tags: ["Custom", "BestPractices"]

      NoSuppressWarnings:
        regex: "/(?<target>@SuppressWarnings\\([^)]*\\))/g"
        description: "Bans @SuppressWarnings annotations — violations must be fixed, not suppressed"
        violation_message: "Remove @SuppressWarnings and fix the underlying violation instead"
        severity: 2
        tags: ["Recommended", "Custom", "BestPractices"]
        file_extensions: [".cls", ".trigger"]

⚠️ Note on test-class exclusion: The regex_ignore field is per-LINE only. Adding regex_ignore: "/@isTest/i" does NOT exclude entire test classes — it only skips lines that literally contain @isTest. For SOQL rules that should skip test classes, use PMD/XPath instead (which can structurally check [not(ancestor::UserClass[ModifierNode[@Test = true()]])]). See the "Excluding Test Classes" section in SKILL.md.