Skip to content

Appendix F.4: Custom Code Analyzer Regex Rules

A regex rule is a pattern that the regex engine of Salesforce Code Analyzer matches against the text of a file. It sees text, not syntax, so it is the right tool where no engine exposes the thing to check, and a stopgap where PMD will expose it later. Where a PMD rule written in Java can make the check, this reference uses that instead (Choosing Between Them).

A regex rule is defined in code-analyzer.yml itself, under engines.regex.custom_rules, with its pattern, the file extensions it scans, a description, a violation message, a severity, and tags. There is no separate rules file: the YAML is both the definition and the configuration, so a regex rule runs under Code Analyzer only, and not under PMD on its own. The sample code-analyzer.yml carries the {N} custom regex rules of this reference. This page describes them, and the sample is the definition. The engine itself is described in Appendix E.

The Rules

The rules are listed most severe first, then by name. Each rule name is an anchor that a standard can link to. Which standard each rule enforces is in Appendix F.6.

Rule Severity What it matches Why regex
NoConsoleInVisualforce 2 A call to a method of console in a Visualforce page or component, except on a line that a // comment begins PMD does not parse the JavaScript inside a Visualforce script block, so no PMD rule can find the statement (Console Statements in Visualforce).
NoPolyfillIo 2 polyfill.io in Visualforce pages and components, Lightning Web Component templates, Aura components and applications, and metadata files The reference can sit in markup of three kinds and in metadata such as a CSP Trusted Site, and PMD reads each with a different language. One pattern covers them all (External Scripts and Stylesheets).
NoSfdocDefaultAuthor 2 The default username of the Salesforce Documenter (SFDoc) extension, ChangeMeIn@UserSettingsUnder.SFDoc, in any case, in Apex classes and triggers, Visualforce pages and components, Aura components and their JavaScript, and Lightning Web Component templates and scripts The extension writes the name as the author of a file header and a method header. It writes the header into all of these file types, and PMD cannot read a header comment in a trigger or a Visualforce page. One pattern on the string covers every type.
NoUnapprovedEslintSuppression 2 An eslint-disable comment of any form (a line comment or a block comment, for a line, the next line, or the rest of the file, with or without a rule name), and an inline configuration comment that turns a rule off, in JavaScript files The JavaScript twin of NoUnapprovedSuppression, under the same suppression policy. ESLint's own @lwc/lwc-platform/no-inline-disable reports only a line comment that names a rule, and a file-wide /* eslint-disable */ switches it off too; a pattern reads the comments whatever ESLint does. PMD's JavaScript parser has a node for a comment, so a PMD rule may be feasible in the future; it is not tested, and this reference does not include one today.
NoUnapprovedSuppression 2 @SuppressWarnings(, // NOPMD, and a code-analyzer-suppress marker in Apex classes and triggers A suppression is a blocking violation under the suppression policy: it is cleared by a fix or by an approving review from the owning team. A PMD rule could not report its own suppression marker reliably. A code-analyzer-suppress marker that switches this rule off, such as code-analyzer-suppress(all), also hides the rule's report of that marker, so reviewers look for it.
NoConsecutiveBlankLines 3 Two or more blank lines in a row, a line that holds only spaces or tabs counting as blank, in Apex classes and triggers No PMD tree keeps blank lines, so only the text of the file shows them. Prettier reduces them to one in code, so the rule finds a file committed without Prettier, and blank lines in a block comment, which Prettier keeps. Adapted from sca-extra, https://github.com/starch-uk/sca-extra (MIT licence).
NoEmptyBlockComments 3 A block comment with nothing in it, on one line or across several, in Apex classes and triggers PMD's XPath rules see the ApexDoc comments in Apex and no others, so an XPath rule cannot find an ordinary comment (the comment standards).
VisualforceHeaderComment 4 A Visualforce page or component that does not start with a comment, blank lines before it allowed PMD's Visualforce tree holds no comments, so no XPath rule can see whether a page starts with one. The pattern ^(?!\s*<!--) reads only the start of the file, because it has no m flag; the g flag is there because Code Analyzer requires it
NoUnresolvedTodosApex 5 TODO, TO-DO, FIXME, FIX-ME, or HACK as the first word of a line of a comment (after any * in a block comment), or anywhere in a comment with a colon after it, in Apex classes and triggers. TODO and FIXME in any case, and the other three in capitals The same limit: an XPath rule cannot read an ordinary comment. A PMD rule written in Java may be able to take a list of prohibited strings as a property, so that an organisation adds a word of its own without writing a pattern; this reference does not include one today.
NoUnresolvedTodosJavaScript 5 The same, in every JavaScript file, static resources included The same pattern as NoUnresolvedTodosApex, because JavaScript writes comments the same way. ESLint reads only the component folders that eslint.config.js names, and a pattern also reads the libraries in static resources (the comment standards).
NoUnresolvedTodosMarkup 5 TODO, TO-DO, FIXME, FIX-ME, or HACK as the first word of a line of a markup comment (<!-- ... -->), or anywhere in one with a colon after it, in Visualforce pages and components, Lightning Web Component templates, and Aura components. TODO and FIXME in any case, and the other three in capitals PMD's XPath rules can read a comment in Lightning Web Component and Aura markup but not in a Visualforce page, so no XPath rule could cover all three. One pattern does (the comment standard).

Every rule carries a severity in its definition, and the Severity Levels say what each severity means for the pipeline. The {N} rules at severity 5 report a comment that the team has chosen to leave for now; the seven at severity 2 and 3 report something to fix before the pull request merges, and the one at severity 4 reports a page or component with no header comment.

Using and Changing the Rules

These rules are a starting point, as the PMD rulesets are (Using and Changing the Ruleset). An organisation changes a rule in its definition: the severity, the pattern, the file extensions it scans, and the message are all there, and there is no property beside them. A rule the organisation does not want is removed from the file, or switched off with disabled: true under rules.regex. A rule of the organisation's own is a new entry under engines.regex.custom_rules, with the same parts. Regenerating the file with sf code-analyzer config --config-file code-analyzer.yml --output-file code-analyzer.yml keeps the block (Appendix E).

A change to a regex rule goes through the same change process as a change to a ruleset: a pull request, a review, and a sign-off, with the file protected by CODEOWNERS so that only the governance team can change it (Repository Structure).

Rules That Ship With the Regex Engine

The regex engine also ships {N} rules of its own, every one tagged Recommended, so Code Analyzer runs each unless the sample code-analyzer.yml disables it. {N} run:

  • AvoidOldSalesforceApiVersions (source), at severity 2. The standard it enforces, and the rule recommended in its place, are in Metadata Standards: API Version.
  • AvoidGetHeapSizeInLoop, at severity 2: a call to Limits.getHeapSize() inside a loop (Apex: CPU Time and Heap).
  • MinVersionForAbstractVirtualClassesWithPrivateMethod, at severity 2: an abstract or virtual class with a private method below API 61.0 (Apex: API Version).
  • AvoidTermsWithImplicitBias, at severity 4, one above the 5 it ships at: a term that carries an implicit bias, anywhere in the code (Principles: Inclusive for Everyone).
  • NoTrailingWhitespace, at severity 5, for the developer's information: a space or a tab at the end of a line, or a line of nothing but whitespace. Prettier removes both from every file it formats, and the Prettier gate runs before Code Analyzer on every pull request, so a report is a sign that Prettier did not format the file (Apex: Formatting).
How the API version rule sets its floor, and its January bug

In Code Analyzer 5.16.0 the rule works its floor out from the month, and each January it lands three versions too high, then drops back in February. The last row is what to expect if Code Analyzer has not changed by then:

When The rule reports Three or more years old on that date
October to December 2026 59.0 and below 59.0 and below
January 2027 62.0 and below 59.0 and below
February to May 2027 60.0 and below 60.0 and below
January 2028 65.0 and below 62.0 and below

The third column follows from the release calendar. The rule counts a new Salesforce release from the first of February, June, and October (source), which is every four months. So the newest version that is three or more years old on a date is:

floor(((year - 2007) * 12 + month - 2) / 4)

year and month are those of the date, with January as month 1, and floor rounds down. For January 2027 that is (20 * 12 + 1 - 2) / 4, or 59.75, which rounds down to 59. January falls in the same four months as the October to December before it, so it keeps December's floor, and the floor rises by one on the first of February.

{N} is disabled, with disabled: true in the sample code-analyzer.yml. NoMixedIndentation reports any line that is indented with a tab and then a space. Every continuation line of an ApexDoc comment inside a tab-indented class is such a line (a tab, a space, and an asterisk), so the rule reports every documented class written to these standards, which require both tabs and ApexDoc; and Prettier sets the indentation of every file before the gate runs.