free quality report for PLCopenXML exports
← check another file

Report: DemoBoiler

demo.xml · version n/a · POUs: 4 · globals: 0
👋 This is a demo report on a synthetic project — to show what you'll get. No need to upload your own code. Want to check yours?
🔎 1 POU in graphical languages (CFC 1) — line-based checks were NOT applied to them; names are partially covered (occurrences in blocks/expressions). This is a tool limitation, not a sign these POUs are clean.
0
Quality score: 0/100
Save the report to your profile — you'll build a verified quality track. The code is not stored, only the aggregate.
Analyze your export
7
Critical
9
Warning
7
Minor

Severity distribution

Critical 7 Warning 9 Minor 7

Top rules

naming 4
div-by-zero 3
implicit-check-disabled 3
dead-pou 3
cfc-div-zero 1
broken-implicit-check 1

Every rule has a noise? button — click it if the rule gives irrelevant hits on your code. The signal helps us tune the checks: lower the severity, narrow the condition or disable extras. Your code is not sent.

Critical 7 findings

CFC: division by a literal zero 1 · cfc-div-zero
In graphical code (CFC/FBD) the divisor of a DIV/MOD block is wired to a literal constant 0 — a runtime exception when the block executes. Checked against the connection graph.
Suggested fixes
  1. Wire a variable/setpoint into the divisor input instead of the literal 0 — usually a debug stub left behind.
  2. If the divisor can go to zero at runtime — pick a safe value before DIV/MOD (a SEL on a compare-with-0).
  • CfcDiv — the divisor of the DIV block is wired to a literal 0 → a guaranteed division by zero
Implicit check does not protect 1 · broken-implicit-check
An implicit check function (Check…) exists, but its protective logic is gutted: an empty body, or the check (divisor=0 / bounds) removed. The runtime calls it, but it catches nothing — a false sense of safety, more dangerous than its absence.
Suggested fixes
  1. Restore the protective logic: CheckDivReal must test IF divisor = 0 THEN … := 1;
  2. CheckBounds/CheckRange… must clamp the value to lower..upper. Compare with the default CODESYS implementation.
  • CheckBounds — no lower/upper bounds comparison — the value is not clamped — the check exists but doesn't work (a false sense of safety)
Division by a literal zero 2 · div-by-zero
Division or MOD by a literal zero — a guaranteed runtime exception (division by zero) whenever this line is reached.
Suggested fixes
  1. Remove the division by 0 — probably a typo in the divisor.
  2. If the divisor can be zero dynamically — guard it: IF d <> 0 THEN r := x / d; END_IF
  • DivZeroVar:1 — division by znever — the variable is 0 (init 0/empty) and never assigned → a guaranteed division by zero
    qa := 5 / znever;
  • Main:2 — division/MOD by a literal zero
    d := d / 0;
Global name typo (gvl.*) 1 · undeclared-global
A gvl.<name> reference has no declaration in the GVL — almost always a typo; the variable is silently created or the code won't compile.
Suggested fixes
  1. Check the spelling against the GVL declaration (case, underscores, digits).
  2. If the variable is really needed — add it to the GVL with a type and a comment.
  • Main — gvl.missing — not in the GVL (likely a typo)
Array index out of bounds 1 · array-bounds
A constant index falls outside the declared ARRAY[lo..hi] bounds — reaching this line reads/writes memory that isn't yours. Without active implicit checks the runtime won't intercept it. Checked for one-dimensional arrays with numeric bounds and a literal index.
Suggested fixes
  1. Bring the index into the declared range or widen the array bounds.
  2. Check before access: IF idx >= LO AND idx <= HI THEN arr[idx] … END_IF
  • Main:1 — arr[5] out of bounds [0..3]
    arr[5] := 1;
Infinite loop with no exit 1 · infinite-loop
WHILE TRUE / REPEAT … UNTIL FALSE with no EXIT or RETURN inside — the scan never finishes: the task blocks and the watchdog fires (and if the watchdog is off, the controller stalls). In a PLC, long processing is spread across scans, not spun in a loop.
Suggested fixes
  1. Add an exit condition and EXIT, or bound it with an iteration counter.
  2. Split heavy processing across scans — an infinite loop trips the task watchdog.
  • Main:11 — WHILE TRUE without EXIT/RETURN

Warning 9 findings

Division by a literal zero 1 · div-by-zero
Division or MOD by a literal zero — a guaranteed runtime exception (division by zero) whenever this line is reached.
Suggested fixes
  1. Remove the division by 0 — probably a typo in the divisor.
  2. If the divisor can be zero dynamically — guard it: IF d <> 0 THEN r := x / d; END_IF
  • DivZeroVar:3 — division by zcond — the variable is assigned 0 on some branch and there's no zcond <> 0 guard → a possible division by zero
    qb := 5 / zcond;
Integer division (lost fraction?) 1 · int-division
Dividing an integer by an integer constant drops the fraction. Use /N.0 or *_TO_REAL if precision matters.
Suggested fixes
  1. Cast to REAL: r := INT_TO_REAL(x) / N.0;
  2. Or divide by a real constant: x / 10.0 instead of x / 10.
  • Main:3 — Integer division (lost fraction?)
    iq := iSum / 10;
Timer with zero delay (PT := T#0) 1 · timer-zero-pt
PT is set to zero (T#0S) — the timer fires in the same scan, effectively no delay. Usually a forgotten or wrong time setpoint.
Suggested fixes
  1. Set a non-zero delay: tmr(IN := cond, PT := T#5S);
  2. If no delay is needed — remove the timer and act on the condition directly.
  • Main:4 — PT := T#0 — the timer fires instantly (no delay)
    t1(IN := c, PT := T#0S);
Assignment of a variable to itself 1 · self-assignment
A line like `x := x;` — assigning a variable to itself does nothing. Usually a typo on the right-hand side (another variable was meant) or a leftover from deleted logic.
Suggested fixes
  1. The right-hand side probably should be another variable (typo) — check the intent.
  2. If the line is redundant — remove it.
  • Main:6 — assignment to itself — no effect
    x := x;
Empty IF/loop branch 1 · empty-branch
An IF/ELSIF/ELSE branch or a loop body is empty (`THEN END_IF`, `DO END_FOR`) — the logic was either never written or cut out during an edit and the construct left behind.
Suggested fixes
  1. Fill the branch with logic or remove the empty IF/loop.
  2. If empty on purpose — leave an explanatory comment.
  • Main:7 — empty IF … THEN … END_IF
CASE without an ELSE branch (no default) 1 · case-without-else
CASE without an ELSE branch: if the selector takes a value not among the labels, nothing runs — a silent skip (a common state-machine bug).
Suggested fixes
  1. Add an ELSE branch with default handling (signal/log/safe state).
  2. If all selector values are truly covered — keep ELSE with an explanatory comment.
  • Main:8 — CASE without ELSE — unhandled selector values are silently ignored
Check present but not active 3 · implicit-check-disabled
A check function exists in the project but is not active: either not registered (no CheckFunction tag — the runtime never calls it), or an entire category (bounds/division/range/pointers) has no active check.
Suggested fixes
  1. Regenerate the POUs for Implicit Checks via the standard CODESYS dialog — that gives the check its CheckFunction registration so the runtime calls it.
  2. Add the missing category (bounds/division/range/pointer) via Add Object → POUs for Implicit Checks.
  • project — the runtime doesn't check 'division by zero' — no active check function for this category
  • project — the runtime doesn't check 'range overflow' — no active check function for this category
  • project — the runtime doesn't check 'pointer dereference' — no active check function for this category

Minor 7 findings

Dead code — POU is never called 3 · dead-pou
The POU is never called and never used as a type — dead code or a forgotten name change (check against the spec before deleting).
Suggested fixes
  1. Not needed — delete the POU.
  2. A forgotten version — check the call name against the spec, possibly a typo in the name.
  • CfcDiv — CfcDiv — never called and not used as a type
  • DivZeroVar — DivZeroVar — never called and not used as a type
  • Main — Main — never called and not used as a type
Cryptic variable name 4 · naming
Single-character names (other than counters i/j/k/n) are unreadable during maintenance. I/O tags and FB pins are left alone.
Suggested fixes
  1. Give it a meaningful name by purpose (e.g. iRetryCount instead of n).
  • CheckBoundsx — single-character name, give it a meaningful one
  • Maind — single-character name, give it a meaningful one
  • Mainx — single-character name, give it a meaningful one
  • Mainc — single-character name, give it a meaningful one
What the linter does NOT see (needs an eye and a cross-check with the spec): signal polarity, swapped hysteresis (< set+hyst / > set−hyst), copy-paste of someone else's loop. The report is a reason to re-check, not a verdict.
Processing happens in memory — the uploaded code is not stored. The tool advises; code changes are up to the engineer (see what the linter can't see). · Feedback · GitHub