Checks reference¶
Every check plc-st-review ships with, what it catches, why it exists, how to configure it, an ST trigger, what the bot posts, and a suggested fix. See check-limitations.md for what each check deliberately doesn't catch.
Two project-wide settings shape how every check resolves identifiers and which .st files it parses:
- Case sensitivity —
case_sensitive: true|false(defaultfalse). Pick the value that matches your toolchain (CODESYS / TwinCAT →false; B&R Automation Studio →true). Drives every identifier comparison and gatesIDENTIFIER_CASE_MISMATCH. - Parsing limits —
parsing.max_file_size_bytes(default 1 MB) and the--max-file-sizeCLI flag control the per-file source-length cap.0disables.
Live demo: every check in this document fires at least once on PR #1, where you can see the exact inline comments the bot posts.
Common settings (apply to every check)¶
Two knobs work on every check, set in .plc-st-review.yml:
severity_overrides:
CATEGORY_NAME: error # raise / lower the severity for this category
disabled_checks:
- CATEGORY_NAME # turn the check off entirely
Each per-check page below only lists additional knobs (check-specific config, prefix lists, etc.).
Metric thresholds¶
The metric-regression checks (COMPLEXITY_INCREASED, NESTING_INCREASED, LOC_SPIKE) read their bands from an optional metrics: block. Omit it and the defaults below apply:
metrics:
thresholds:
cyclomatic_complexity:
warn: 15
error: 25
nesting_depth:
warn: 5
error: 8
lines_of_code: # reserved for the standalone --metrics mode
warn: 300
error: 600
comment_ratio: # reserved for the standalone --metrics mode
warn_below: 10
fan_out: # reserved for the standalone --metrics mode
warn: 15
error: 25
cyclomatic_complexity and nesting_depth drive the two threshold-aware checks today; the rest of the block is accepted now so the config is stable ahead of the standalone metrics mode. LOC_SPIKE has no threshold, it fires on any single-PR growth over 50%.
Diff-based checks¶
These compare the before and after trees of a PR. Every finding implies a change happened in this PR.
- SIGNATURE_CHANGED
- CALL_SITE_OUTDATED
- TYPE_MISMATCH
- ENUM_VALUE_REMOVED
- ENUM_VALUE_ADDED
- TIMER_VALUE_CHANGED
- CONSTANT_VALUE_CHANGED
- COMMENT_ONLY
- ARRAY_BOUNDS_CHANGED
- LOOP_BOUNDS_CHANGED
- POU_DELETED
- POU_RENAMED
- METHOD_ADDED_TO_INTERFACE
- INHERITANCE_CHANGED
- PRAGMA_CHANGED
- UNUSED_VAR_INTRODUCED
- COUNTER_VALUE_CHANGED
- COMPLEXITY_INCREASED
- NESTING_INCREASED
- LOC_SPIKE
- DEAD_POU_INTRODUCED
COMPLEXITY_INCREASED, NESTING_INCREASED, and LOC_SPIKE are metric-regression checks: they compare a POU's complexity, nesting depth, and lines of code between revisions and fire when a metric degrades. Their bands live in a metrics: block (see Metric thresholds below). DEAD_POU_INTRODUCED is project-scoped: it needs a whole-repo parse and runs only with --project-scope.
Static integrity checks¶
These run on the after tree alone and surface bugs that compile but mis-behave. Each one filters out findings already present in the before tree so the check only flags new problems introduced in the PR.
- ENUM_VALUE_UNUSED
- ENUM_MEMBER_UNKNOWN
- ARRAY_INDEX_OUT_OF_BOUNDS
- DIVISION_BY_ZERO
- INFINITE_LOOP
- LOOP_BOUNDS_REVERSED
- STATE_UNHANDLED
- UNREACHABLE_CODE
- FOR_LOOP_VAR_MODIFIED
- FOR_LOOP_VAR_USED_AFTER
- POINTER_ARITHMETIC
- POINTER_COMPARED
- UNINITIALIZED_VAR_USED
- TIME_EQUALITY
- MULTI_WRITER_GLOBAL — project-scoped (needs
--project-scope).
FB-instance checks¶
These target standard IEC 61131-3 function-block patterns (TON, CTU, R_TRIG, SR/RS, etc.), wiring mistakes that won't trip a normal compiler but produce wrong runtime behavior.
- COUNTER_PV_ZERO
- TIMER_PT_ZERO
- TIMER_NOT_DRIVEN
- EDGE_TRIG_REUSED
- FB_INSTANCE_DOUBLE_CALL
- FB_INSTANCE_NEVER_CALLED
- BISTABLE_DOMINANCE_MISMATCH
Code-quality and style checks¶
These are stylistic / hygiene checks. Most ship at info severity and stay off your blocking gate by default; raise them in .plc-st-review.yml once your team agrees on a convention.
- EMPTY_STATEMENT
- UNUSED_RETURN_VALUE
- ARRAY_SINGLE_ELEMENT
- VARIABLE_SHADOWING
- UNQUALIFIED_ENUM_CONSTANT
- IDENTIFIER_CASE_MISMATCH
- UNUSED_INPUT_VAR
- INPUT_VAR_WRITTEN
- BOOL_COMPARISON
- REAL_EQUALITY
- MULTIPLE_EXIT_POINTS
- ASSIGNMENT_IN_CONDITION
- COMMENTED_OUT_CODE
- RECURSIVE_CALL
- FORBIDDEN_SYMBOL
- ADDRESS_OF_CONSTANT
- UNUSED_OUTPUT_VAR
- OUTPUT_VAR_READ_INTERNALLY
- NESTED_COMMENTS
- NAMING_CONVENTION
- DIRECT_ADDRESS_USED
- IF_WITHOUT_ELSE
- FORBIDDEN_STATEMENT
- POU_NOT_COMMENTED
- NAME_REUSED_DIFFERENT_KIND
- INDIRECT_RECURSIVE_CALL
- IDENTIFIER_TOO_LONG
- IDENTIFIER_CHARSET
- TOO_MANY_PARAMETERS
- TOO_MANY_GLOBALS_USED
- EXTERNAL_VAR_IN_FUNCTION
- IMPLICIT_TYPE_CONVERSION
Several of the above also appear in the PLCopen Coding Guidelines; the PLCopen preset bumps their severities and turns on the limit-gated ones. They're not PLCopen-exclusive though — they're general checks the engine ships standalone.
Security (IEC 62443) checks¶
These five checks target IEC 62443 industrial-cybersecurity concerns that are statically inferable from ST source — hard-coded credentials, hard-coded network endpoints, unguarded use of VAR_INPUT in privileged operations, debug pragmas left in production, and persistent storage of secret-named variables.
- HARDCODED_CREDENTIALS — secret-named variable with a literal STRING initialiser.
- HARDCODED_NETWORK_ENDPOINT — STRING global / local whose literal value is an IPv4 address or a
tcp:///opc.tcp:///mqtt(s)/modbus/http(s)URL. - UNVALIDATED_INPUT_USE —
VAR_INPUTused as an array subscript or divisor with no in-POU relational guard. - DEBUG_PRAGMA_IN_PRODUCTION — debug / monitoring / force-init attribute pragmas in non-test source paths.
- PERSISTENT_PLAINTEXT_SECRET —
VAR_GLOBAL PERSISTENT/RETAINof a secret-named variable.
See the IEC 62443 mapping page for the per-clause cross-reference and suggested severity policy by Security Level.
Using a check in your PR¶
plc-st-review runs automatically once you've set up the GitLab or GitHub integration (see gitlab-setup.md / github-setup.md). Every check above lands as either an inline comment on the relevant .st line or as part of the summary issue / discussion comment when the affected line falls outside the PR's diff hunks.
To suppress a check for a single repo, add it to disabled_checks in .plc-st-review.yml. To raise or lower its severity, use severity_overrides. See tuning-severities.md for the tuning ramp.
To compose policy across many repos, naming conventions, severity profiles, forbidden symbols, use extends: to pull from shared preset files. See preset-packs.md.