# Advanced PMD Patterns Multi-rule rulesets, overriding built-in rules, exclusion patterns, Java-based rules, and sharing rules across projects. ## Multiple Rules in One Ruleset File A single ruleset XML can contain any number of rules. This is the recommended approach for teams maintaining a shared rule set: ```xml Team coding standards 3 2 3 ``` Rules with different `language` values (apex, xml, visualforce, etc.) can coexist in one ruleset. PMD applies each rule only to files matching its language. ## Overriding Built-in PMD Rules Override severity, priority, or configurable properties on PMD's built-in rules without writing new ones: ```xml Customized built-in rule thresholds 2 2 ``` **Configuration:** ```yaml # code-analyzer.yml engines: pmd: custom_rulesets: - "custom-rules/customized-builtins.xml" ``` ### How rule ref works | Pattern | Effect | |---------|--------| | `` | Override one specific rule | | `` | Include entire category | | `` | Include category minus specific rules | | Nested `` | Override severity (1=Critical, 2=High, 3=Moderate, 4=Low, 5=Info) | | Nested `` | Override configurable thresholds/options | ### Common built-in rules to customize | Rule | Useful Properties | |------|-------------------| | `CyclomaticComplexity` | `classReportLevel` (default: 40), `methodReportLevel` (default: 25) | | `ExcessiveParameterList` | `minimum` (default: 4) | | `ExcessiveClassLength` | `minimum` (default: 1000) | | `TooManyFields` | `maxfields` (default: 15) | | `NcssMethodCount` | `minimum` (default: 60) | | `DebugsShouldUseLoggingLevel` | `strictMode` (default: false) | | `ApexUnitTestClassShouldHaveAsserts` | (no properties — exclude if unwanted) | ## Exclusion Patterns Three mechanisms for excluding files and rules: ### 1. File exclusions in `code-analyzer.yml` Exclude entire directories or file patterns from all engines: ```yaml # code-analyzer.yml ignores: files: - "**/node_modules/**" - "**/fflib_*" - "**/*Test*.cls" - "**/generated/**" ``` Supports glob patterns: `*`, `**`, `?`, `[...]`, `{...}` ### 2. Rule exclusions in PMD rulesets Exclude specific rules when including an entire category: ```xml ``` ### 3. Exclude patterns in PMD rulesets Exclude files at the PMD level (applies only to rules in that ruleset): ```xml .*/fflib_.* .*/test/.* ... ``` ### 4. Disabling individual rules via config Disable any rule without editing rulesets: ```yaml # code-analyzer.yml rules: pmd: ApexUnitTestClassShouldHaveAsserts: disabled: true CyclomaticComplexity: severity: "Low" # Downgrade instead of disable ``` ## Java-Based Custom Rules (Advanced) For rules that need logic beyond XPath (complex data flow, cross-file analysis), write Java PMD rules: 1. **Create a Java class** extending `net.sourceforge.pmd.lang.apex.rule.AbstractApexRule` 2. **Compile into a JAR** file 3. **Reference in config:** ```yaml # code-analyzer.yml engines: pmd: java_classpath_entries: - "custom-rules/my-rules.jar" # JAR with compiled rule classes - "custom-rules/lib/" # Folder of JARs (all JARs inside loaded) custom_rulesets: - "category/myteam/rules.xml" # Ruleset inside the JAR (classpath resource) ``` The JAR's ruleset XML references the Java class: ```xml 3 ``` **Requirements:** - Java 11+ for compilation - PMD 7.x API (use `net.sourceforge.pmd.lang.rule.xpath.XPathRule` for XPath, or extend `AbstractApexRule` for visitor-pattern Java rules) ## Sharing Rules Across Projects ### Approach 1: Relative paths with shared config If projects share a parent directory or monorepo: ```yaml # code-analyzer.yml engines: pmd: custom_rulesets: - "../shared-rules/team-standards.xml" # Relative to config_root ``` `config_root` is the directory containing `code-analyzer.yml`. All relative paths resolve from there. ### Approach 2: JAR-based distribution Package rules into a JAR and distribute via artifact repository: 1. Build a JAR containing your ruleset XML at a classpath resource path (e.g., `category/myteam/standards.xml`) 2. Place the JAR in each project's `custom-rules/` directory (or download in CI) 3. Reference in config: ```yaml engines: pmd: java_classpath_entries: - "custom-rules/myteam-rules-1.0.jar" custom_rulesets: - "category/myteam/standards.xml" # Resource inside the JAR ``` ### Approach 3: Git submodule or symlink For simpler setups, share the ruleset XML via git submodule: ```bash git submodule add https://github.com/myteam/pmd-rules.git custom-rules/shared ``` ```yaml engines: pmd: custom_rulesets: - "custom-rules/shared/team-standards.xml" ``` ### CI/CD Integration All approaches work in CI because paths are relative to `config_root`. Example GitHub Actions step: ```yaml - name: Run Code Analyzer run: | sf code-analyzer run --rule-selector "pmd:2,regex:2" --target force-app/ ``` No absolute paths needed — the config file handles resolution.