> Agent-readable docs index: /llms.txt. Download /docs.zip to grep all markdown files locally.

---
$schema: https://holocron.so/frontmatter.json
title: Diagnostics
description: Compiler error codes (RBCPP-*) for the CLI, CI pipelines, and RoboC++ Studio.
icon: lucide:bug
---

Diagnostics are collected across **parsing, semantic checking, runtime, C generation, and PLCopen import**. The `iec_diagnostics` crate renders human-readable text to stderr or JSON to stdout depending on the command.

**RoboC++ Studio** and **`iec_language_service`** consume the same diagnostic shape in JSON (`severity`, `code`, `stableCode`, `message`, `span`, `help`). The Studio diagnostics table maps directly to CLI **`--json`** output. Gate tooling on **`stableCode`**, not on human stderr text.

The compiler repo **`validation/DIAGNOSTICS.md`** defines diagnostic compatibility policy for corpus review and IDE clients:

* **Stable by default:** severity, code family, rejection behavior, and primary named entity in the message.
* **Allowed with review:** clearer wording (update `.diag` sidecars), improved spans, additional diagnostics when rejection remains.
* **Not allowed without a migration note:** downgrading errors to warnings, removing the only rejection diagnostic, or letting invalid input become executable.

Run **`cargo run -p xtask -- validate-corpus`** to check rejected fixtures against **`.diag`** sidecar expectations.

## Severities

| Severity  | Meaning                                                              |
| --------- | -------------------------------------------------------------------- |
| `error`   | Blocks the current operation (check failure, run abort, and similar) |
| `warning` | Issue reported but operation may continue where applicable           |
| `note`    | Additional context attached to another finding                       |

Communication function block instances typically produce a **warning** that a target runtime hook is required. **`import-plcopen`** may print warnings while still exiting successfully.

## Stable codes

Each diagnostic carries a short **`code`** field and a stable **`RBCPP-*`** identifier:

| Code          | Stable ID           | Typical source                     |
| ------------- | ------------------- | ---------------------------------- |
| `io`          | `RBCPP-IO`          | File read/write                    |
| `lexical`     | `RBCPP-LEXICAL`     | Lexer                              |
| `syntax`      | `RBCPP-SYNTAX`      | Parser                             |
| `semantic`    | `RBCPP-SEMANTIC`    | Type and symbol checking           |
| `compliance`  | `RBCPP-COMPLIANCE`  | Profile limits and pragma policy   |
| `runtime`     | `RBCPP-RUNTIME`     | Interpreter                        |
| `unsupported` | `RBCPP-UNSUPPORTED` | Intentionally unavailable features |

Human stderr output uses the short **`code`** (for example `semantic`). JSON output includes both **`code`** and **`stableCode`**.

There is one stable ID per **category**, not per individual message. Gate CI on **`stableCode`** and message substrings, not on numeric RBCPP-#### codes.

Human output includes source location when a span is available (`file:line:column`).

<Aside>
  <Tip>
    Use **`--json`** in CI to gate merges on specific **`RBCPP-*`** codes without brittle string matching on human output.
  </Tip>
</Aside>

## JSON diagnostic shape

Each diagnostic serializes as:

```json
{
  "severity": "error",
  "code": "semantic",
  "stableCode": "RBCPP-SEMANTIC",
  "message": "variable 'X' is not declared",
  "span": {
    "source": "examples/counter.st",
    "start": 42,
    "end": 43,
    "line": 3,
    "column": 5
  },
  "help": null
}
```

| Field        | Meaning                                   |
| ------------ | ----------------------------------------- |
| `severity`   | `error`, `warning`, or `note`             |
| `code`       | Short category string                     |
| `stableCode` | `RBCPP-*` category id                     |
| `message`    | Human-readable text                       |
| `span`       | Source location object, or JSON `null`    |
| `help`       | Optional extra guidance string, or `null` |

Commands that accept **`--json`** emit diagnostic arrays for failures:

```bash
cargo run -p rbcpp_cli -- check examples/counter.st --json
```

An empty diagnostic list prints `[]` on success with `--json`.

## Common negative-test categories

The compliance row **`diagnostics.annex_e`** tracks stable diagnostics for conditions including:

* Duplicate or unknown names, illegal `EXIT` placement, overlapping `CASE` labels
* **`VAR_EXTERNAL`** without a matching **`VAR_GLOBAL`**, or CONSTANT mismatch
* Recursive user **`FUNCTION`** call cycles
* Invalid **`VAR_ACCESS`** targets (`VAR_TEMP`, `VAR_EXTERNAL`, `VAR_IN_OUT`)
* **`R_EDGE`** / **`F_EDGE`** on non-FB or non-BOOL inputs
* SFC action-control timed-association contention
* IL duplicate labels, missing function returns, array bounds
* Conversion and subrange range violations, disabled pragma use

Run **`cargo test -p iec_semantics annex_e`** in the compiler repo to see the full regression set.

## Unsupported IR boundary

Invalid constructs must not silently become executable behavior through parser recovery. The **`diagnostics.unsupported_ir_boundary`** compliance row requires rejection before runtime or generated C. Recovered invalid IR is treated as non-executable in later pipeline stages.

## Compliance-related diagnostics

Implementation limit violations (expression depth, scan cycle count, comment length, disabled pragmas, nested framed comments on **`2003-strict`**, and similar) use **`RBCPP-COMPLIANCE`**. See [Language support](/language-support#implementation-limits) for default limits.

## Related

* [CLI reference](/cli)
* [Compliance](/compliance)
* [RoboC++ Studio](/studio)


---

*Powered by [holocron.so](https://holocron.so)*
