@h2loop/safe-c-coding-plugin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.hydron-plugin/plugin.json +5 -0
- package/README.md +44 -0
- package/agents/safe-c-reviewer.md +58 -0
- package/package.json +33 -0
- package/skills/safe-c-coding/SKILL.md +137 -0
- package/skills/safe-c-coding/guidelines.md +212 -0
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# safe-c-coding-plugin
|
|
2
|
+
|
|
3
|
+
A [Hydron](https://hydron.h2loop.ai) plugin that ships defensive C/C++ coding discipline —
|
|
4
|
+
**MISRA C:2012-aligned** guidelines for writing reliable, deterministic, undefined-behaviour-free
|
|
5
|
+
C for embedded, automotive, medical, aerospace, and other safety-critical or resource-constrained
|
|
6
|
+
targets.
|
|
7
|
+
|
|
8
|
+
It bundles two surfaces:
|
|
9
|
+
|
|
10
|
+
- **`safe-c-coding` skill** — an always-on domain layer. Its description auto-triggers whenever
|
|
11
|
+
you write, generate, modify, or review C/C++ (`.c`, `.h`, `.cpp`, `.hpp`), so Hydron produces
|
|
12
|
+
compliant code the first time instead of relying on a later review pass. The full guideline
|
|
13
|
+
catalogue lives in [`skills/safe-c-coding/guidelines.md`](skills/safe-c-coding/guidelines.md).
|
|
14
|
+
- **`safe-c-reviewer` subagent** — an on-demand reviewer that audits a file, diff, or range
|
|
15
|
+
against the guidelines and reports findings as `severity — file:line — what's wrong -> safe fix`,
|
|
16
|
+
Critical (must fix) separated from Standard (fix or justify).
|
|
17
|
+
|
|
18
|
+
## Layout
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
.hydron-plugin/plugin.json # engines manifest (hydron ^1.0.0)
|
|
22
|
+
skills/safe-c-coding/
|
|
23
|
+
SKILL.md # skill entry — severity model, preferred patterns, review format
|
|
24
|
+
guidelines.md # full MISRA C:2012-aligned guideline catalogue
|
|
25
|
+
agents/safe-c-reviewer.md # subagent that reviews C/C++ against the guidelines
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
# From the marketplace once published, or directly from this checkout:
|
|
32
|
+
hydron plugin validate .
|
|
33
|
+
hydron plugin add .
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Once added, the skill loads automatically on C/C++ work. To run a review explicitly, ask Hydron
|
|
37
|
+
to review a file with the safe-c-reviewer (e.g. "review `foo.c` with the safe-c reviewer").
|
|
38
|
+
|
|
39
|
+
## Scope note
|
|
40
|
+
|
|
41
|
+
These guidelines paraphrase well-established critical-systems C practice (aligned with the intent
|
|
42
|
+
of MISRA C:2012) in original wording. They are an engineering aid, **not** a formal compliance
|
|
43
|
+
certification and not affiliated with or endorsed by MISRA. For formal compliance and authoritative
|
|
44
|
+
guidance, consult the official MISRA C:2012 publication.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
mode: subagent
|
|
3
|
+
displayName: Safe-C Reviewer
|
|
4
|
+
description: >-
|
|
5
|
+
Reviews C / C++ (.c, .h, .cpp, .hpp) against the safe-c-coding (MISRA C:2012-aligned)
|
|
6
|
+
guidelines and reports findings by severity. Given a file, diff, or code range, it scans
|
|
7
|
+
for undefined behaviour, dynamic allocation, unchecked returns, implicit conversions,
|
|
8
|
+
missing switch defaults, recursion, and other hazardous constructs, and reports each as
|
|
9
|
+
"severity — file:line — what's wrong -> safe fix". Use it to audit hand-written or
|
|
10
|
+
generated C/C++ before it lands, especially for embedded / safety-critical targets.
|
|
11
|
+
permission:
|
|
12
|
+
# deny-all FIRST, then allow specifics: PermissionNext resolves with findLast
|
|
13
|
+
# (last matching rule wins), so "*": deny MUST precede the allows or it overrides them.
|
|
14
|
+
"*": deny
|
|
15
|
+
read: allow
|
|
16
|
+
grep: allow
|
|
17
|
+
glob: allow
|
|
18
|
+
list: allow
|
|
19
|
+
bash: allow
|
|
20
|
+
skill: { "*": deny, "safe-c-coding": allow }
|
|
21
|
+
---
|
|
22
|
+
You are a safe-C code reviewer. Your only job is to audit C / C++ against the
|
|
23
|
+
`safe-c-coding` guidelines and report findings — you do not modify code, and you do not
|
|
24
|
+
review anything outside these safety guidelines (style, architecture, and performance are
|
|
25
|
+
out of scope unless they cause undefined behaviour or non-determinism).
|
|
26
|
+
|
|
27
|
+
Procedure:
|
|
28
|
+
|
|
29
|
+
1. **Load the rules first.** Invoke the `safe-c-coding` skill and read its `guidelines.md`
|
|
30
|
+
catalogue before you look at any code. Review against the catalogue, not from memory.
|
|
31
|
+
2. **Get the code.** You are given a file, a diff, or a line range. Read exactly that scope
|
|
32
|
+
with `read`/`grep`; do not wander into unrelated files. If the scope is a diff, judge the
|
|
33
|
+
changed lines and the code they directly touch.
|
|
34
|
+
3. **Scan section by section** against the guidelines: undefined behaviour, dynamic memory
|
|
35
|
+
(`malloc`/`free`/VLAs), unchecked return values, implicit / narrowing conversions,
|
|
36
|
+
signed-unsigned mixing, uninitialised reads, missing `default` in `switch`, recursion,
|
|
37
|
+
unbraced bodies, `goto`/`longjmp`/`exit`/`abort`, multiple points of exit, etc.
|
|
38
|
+
4. **Every finding gets a safe fix.** A finding without the corrective idiom is incomplete —
|
|
39
|
+
name the safe replacement (static buffer / fixed-size pool, checked status return, explicit
|
|
40
|
+
cast, added `default`, single point of exit) and, where useful, the substitute pattern from
|
|
41
|
+
the skill.
|
|
42
|
+
|
|
43
|
+
Report exactly in this format, Critical (must fix) separated from Standard (fix or justify):
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
Safe-C findings
|
|
47
|
+
Critical:
|
|
48
|
+
foo.c:42 `x` read before assignment -> initialise at declaration
|
|
49
|
+
foo.c:27 double free of `buf` -> free once, then set to NULL
|
|
50
|
+
Standard:
|
|
51
|
+
foo.c:12 malloc used -> use a static buffer or fixed-size pool
|
|
52
|
+
foo.c:30 switch has no default -> add a default clause
|
|
53
|
+
Advisory:
|
|
54
|
+
foo.c:8 magic number 4096 -> name it with a #define / const
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If a scanned scope is clean, say so explicitly rather than inventing findings. Bias toward
|
|
58
|
+
citing the exact rule and the concrete fix over vague warnings.
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@h2loop/safe-c-coding-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Safe-C coding plugin for Hydron — MISRA C:2012-aligned defensive C/C++ guidelines as an always-on skill plus a safe-c-reviewer subagent",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"private": false,
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"registry": "https://registry.npmjs.org",
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"hydron",
|
|
14
|
+
"hydron-plugin",
|
|
15
|
+
"misra",
|
|
16
|
+
"misra-c-2012",
|
|
17
|
+
"embedded",
|
|
18
|
+
"safety-critical",
|
|
19
|
+
"c",
|
|
20
|
+
"cpp"
|
|
21
|
+
],
|
|
22
|
+
"files": [
|
|
23
|
+
".hydron-plugin",
|
|
24
|
+
"skills",
|
|
25
|
+
"agents",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/h2loop/safe-c-misra-skill-plugin.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/h2loop/safe-c-misra-skill-plugin#readme"
|
|
33
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: safe-c-coding
|
|
3
|
+
description: Use when writing, generating, modifying, or reviewing C or C++ code (.c, .h, .cpp, .hpp), especially for embedded, automotive, medical, aerospace, or other safety-critical or resource-constrained targets where reliability, determinism, and freedom from undefined behaviour matter.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Safe C Coding
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
A set of defensive coding guidelines for writing C (and C-style C++) that is reliable, deterministic, and free of undefined behaviour — the qualities required in safety-critical and embedded systems. These guidelines are aligned with the intent of the MISRA C:2012 guidelines for critical-systems C, expressed here as plain engineering practice.
|
|
11
|
+
|
|
12
|
+
**Core principle:** write the safe form by default. Avoid the language's hazardous corners (dynamic memory, undefined behaviour, implicit conversions, unbounded operations) instead of relying on review to catch them later.
|
|
13
|
+
|
|
14
|
+
> **Apply this as you write.** Whenever you write, generate, or modify C or C++ — even a one-line snippet or a quick example — follow these MISRA C:2012-aligned guidelines proactively, without waiting to be asked to review. Produce compliant code the first time; do not emit a hazardous construct now and plan to fix it later. If a guideline genuinely cannot be met, flag it and justify the deviation rather than silently violating it.
|
|
15
|
+
|
|
16
|
+
The full guideline catalogue is in **[guidelines.md](guidelines.md)**. Consult it rather than working from memory.
|
|
17
|
+
|
|
18
|
+
## Severity
|
|
19
|
+
|
|
20
|
+
- **Critical** — never violate. These guard against undefined behaviour and memory corruption.
|
|
21
|
+
- **Standard** — follow as the default; deviate only deliberately, with a documented reason, scope, and risk note.
|
|
22
|
+
- **Advisory** — recommended for clarity, maintainability, and portability; apply with engineering judgement. Listed at the end of guidelines.md.
|
|
23
|
+
|
|
24
|
+
## When writing or modifying C/C++
|
|
25
|
+
|
|
26
|
+
1. Choose the safe pattern from the start: static allocation over `malloc`, braced bodies, explicit types, checked return values, a `default` in every `switch`, no recursion.
|
|
27
|
+
2. After producing code, scan it against guidelines.md and fix anything before presenting it.
|
|
28
|
+
3. If a hazardous construct is genuinely unavoidable, don't hide it — call it out and explain why, with a documented justification.
|
|
29
|
+
|
|
30
|
+
## Preferred patterns
|
|
31
|
+
|
|
32
|
+
When the task tempts a banned construct, reach for these idioms instead of improvising. Copy and adapt them. Each uses a **single point of exit** (one `return` at the end, via a status variable and structured nesting) per Rule 15.5 — prefer this over early-return guard clauses.
|
|
33
|
+
|
|
34
|
+
**Caller-provided output buffer** — never return allocated memory; the caller owns storage, the function returns a status.
|
|
35
|
+
|
|
36
|
+
```c
|
|
37
|
+
typedef enum { OK = 0, ERR_ARG = 1, ERR_RANGE = 2 } status_t;
|
|
38
|
+
|
|
39
|
+
/* Bounded copy into a caller-owned buffer. Never allocates, never overflows.
|
|
40
|
+
* Single point of exit. */
|
|
41
|
+
status_t copy_label(char *out, size_t out_size, const char *src)
|
|
42
|
+
{
|
|
43
|
+
status_t st = ERR_ARG;
|
|
44
|
+
if ((out != NULL) && (src != NULL) && (out_size != 0U)) {
|
|
45
|
+
size_t len = strnlen(src, out_size); /* bounded scan */
|
|
46
|
+
if (len < out_size) { /* room for NUL */
|
|
47
|
+
(void)memcpy(out, src, len);
|
|
48
|
+
out[len] = '\0';
|
|
49
|
+
st = OK;
|
|
50
|
+
} else {
|
|
51
|
+
st = ERR_RANGE;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return st;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Fixed-size pool** — deterministic object reuse instead of `malloc`/`free`.
|
|
59
|
+
|
|
60
|
+
```c
|
|
61
|
+
#define POOL_SIZE 8U
|
|
62
|
+
|
|
63
|
+
typedef struct {
|
|
64
|
+
uint8_t used[POOL_SIZE];
|
|
65
|
+
widget_t slot[POOL_SIZE];
|
|
66
|
+
} pool_t;
|
|
67
|
+
|
|
68
|
+
static pool_t pool; /* static storage, internal linkage — no heap */
|
|
69
|
+
|
|
70
|
+
widget_t *widget_acquire(void)
|
|
71
|
+
{
|
|
72
|
+
widget_t *found = NULL;
|
|
73
|
+
for (size_t i = 0U; (i < POOL_SIZE) && (found == NULL); ++i) {
|
|
74
|
+
if (pool.used[i] == 0U) {
|
|
75
|
+
pool.used[i] = 1U;
|
|
76
|
+
found = &pool.slot[i]; /* loop condition stops the scan */
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return found; /* single exit; NULL if pool exhausted */
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
void widget_release(widget_t *w)
|
|
83
|
+
{
|
|
84
|
+
for (size_t i = 0U; i < POOL_SIZE; ++i) {
|
|
85
|
+
if (&pool.slot[i] == w) {
|
|
86
|
+
pool.used[i] = 0U;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Error propagation, not `exit`/`abort`/`longjmp`** — return status codes up the call chain to a defined fault handler.
|
|
94
|
+
|
|
95
|
+
```c
|
|
96
|
+
status_t process(const config_t *cfg)
|
|
97
|
+
{
|
|
98
|
+
status_t st = ERR_ARG;
|
|
99
|
+
if (cfg != NULL) {
|
|
100
|
+
st = validate(cfg);
|
|
101
|
+
if (st == OK) {
|
|
102
|
+
st = run(cfg); /* only run once validation passed */
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return st; /* single exit; propagate status, no abort()/longjmp() */
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
See guidelines.md for the full substitute list (banned facility → safe replacement).
|
|
110
|
+
|
|
111
|
+
## When reviewing C/C++
|
|
112
|
+
|
|
113
|
+
1. Read guidelines.md, then scan the code section by section.
|
|
114
|
+
2. Report each finding as: **severity — file:line — what's wrong → safe fix**.
|
|
115
|
+
3. Separate Critical findings (must fix) from Standard findings (fix or justify).
|
|
116
|
+
|
|
117
|
+
## Reporting format
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
Safe-C findings
|
|
121
|
+
Critical:
|
|
122
|
+
foo.c:42 `x` read before assignment → initialise at declaration
|
|
123
|
+
foo.c:27 double free of `buf` → free once, then set to NULL
|
|
124
|
+
Standard:
|
|
125
|
+
foo.c:12 malloc used → use a static buffer or fixed-size pool
|
|
126
|
+
foo.c:30 switch has no default → add a default clause
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Common mistakes
|
|
130
|
+
|
|
131
|
+
- Flagging a problem without giving the safe fix.
|
|
132
|
+
- Allowing dynamic allocation, recursion, or VLAs "just this once" without a documented justification.
|
|
133
|
+
- Letting implicit narrowing or signed/unsigned mixing slip through.
|
|
134
|
+
|
|
135
|
+
## Scope note
|
|
136
|
+
|
|
137
|
+
These guidelines paraphrase well-established critical-systems C practice (aligned with the intent of MISRA C:2012) in original wording. They are an engineering aid, not a formal compliance certification and not affiliated with or endorsed by MISRA. For formal compliance and authoritative guidance, consult the official MISRA C:2012 publication.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Safe C Coding Guidelines
|
|
2
|
+
|
|
3
|
+
Defensive C guidelines for safety-critical and embedded code, aligned with the intent of MISRA C:2012, in original wording. **Critical** = never violate (guards against undefined behaviour / memory corruption). **Standard** = default practice; deviate only with a documented justification. **Advisory** = recommended for clarity, maintainability, and portability; apply with engineering judgement (see the Advisory section at the end).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Memory and resources
|
|
8
|
+
|
|
9
|
+
- **(Critical)** Never read a local variable before it has been assigned a value. Initialise at declaration.
|
|
10
|
+
- **(Critical)** Only free memory that was obtained from a standard allocator, and free it exactly once. After freeing, set the pointer to `NULL`. No double-free, no freeing stack or static pointers.
|
|
11
|
+
- **(Critical)** Never copy or assign between objects whose storage overlaps (e.g. overlapping source/destination in a copy).
|
|
12
|
+
- Do not use dynamic memory allocation (`malloc`, `calloc`, `realloc`, `free`). Prefer static allocation, fixed-size pools, or bounded stack objects.
|
|
13
|
+
- Always explicitly release any resource (memory, file, handle) obtained at run time.
|
|
14
|
+
- Validate arguments (range, sign, non-null, buffer size) before passing them to any library function.
|
|
15
|
+
|
|
16
|
+
## Files and streams
|
|
17
|
+
|
|
18
|
+
- **(Critical)** Never dereference a `FILE` pointer directly.
|
|
19
|
+
- **(Critical)** Never use a `FILE` pointer after its stream has been closed.
|
|
20
|
+
- **(Critical)** Never write to a stream that was opened read-only.
|
|
21
|
+
- Do not open the same file for read and write at the same time on different streams.
|
|
22
|
+
|
|
23
|
+
## Pointers and arrays
|
|
24
|
+
|
|
25
|
+
- Keep pointer arithmetic within the bounds of a single array; a computed pointer must address an element of that array (or one past its end).
|
|
26
|
+
- Subtract pointers only when both address elements of the same array.
|
|
27
|
+
- Apply relational comparisons (`<`, `>`, `<=`, `>=`) to pointers only when they point into the same object.
|
|
28
|
+
- Never let the address of a local (automatic) object outlive that object — don't return `&local` or store it somewhere persistent.
|
|
29
|
+
- Do not declare flexible array members.
|
|
30
|
+
- Do not use variable-length arrays.
|
|
31
|
+
|
|
32
|
+
## Types and conversions
|
|
33
|
+
|
|
34
|
+
- Always specify types explicitly; never rely on implicit `int`.
|
|
35
|
+
- Do not assign a value to an object of narrower type or a different type category — no implicit narrowing.
|
|
36
|
+
- Both operands of an arithmetic operator should share the same type category; don't mix signed/unsigned/float/character categories.
|
|
37
|
+
- Don't assign or cast a composite (sub-)expression into a wider or different-category type, which hides where truncation or overflow occurs.
|
|
38
|
+
- Use operators only on appropriate operand types — no bitwise operators on signed or Boolean values; no arithmetic on Boolean or enum values.
|
|
39
|
+
- Use character-typed expressions in `+`/`-` only as `char ± integer` or `char - char`.
|
|
40
|
+
- Keep a shift count within `0 .. width-1` of the promoted left operand.
|
|
41
|
+
|
|
42
|
+
## Pointer conversions
|
|
43
|
+
|
|
44
|
+
- Do not convert between a function pointer and any other type.
|
|
45
|
+
- Do not convert between a pointer to an incomplete type and any other type.
|
|
46
|
+
- Do not cast between pointers to different object types.
|
|
47
|
+
- Do not cast between `void *` and an arithmetic type, or between an object pointer and a non-integer arithmetic type.
|
|
48
|
+
- A cast must never strip `const` or `volatile` from the pointed-to type.
|
|
49
|
+
- Use `NULL` as the only null-pointer constant.
|
|
50
|
+
|
|
51
|
+
## Initialization and declarations
|
|
52
|
+
|
|
53
|
+
- Enclose aggregate and union initialisers in braces.
|
|
54
|
+
- Initialise arrays fully; do not leave them partially initialised, and do not initialise any element more than once.
|
|
55
|
+
- When using designated initialisers for an array, state the array size explicitly.
|
|
56
|
+
- Declare functions in prototype form with named parameters — no empty `()` or K&R style.
|
|
57
|
+
- Use identical names and qualifiers across all declarations of the same object or function.
|
|
58
|
+
- Declare each external object or function once, in a single header, with exactly one definition; ensure a compatible declaration is visible at the definition.
|
|
59
|
+
- Mark internal-linkage objects and functions `static`; declare `inline` functions `static`.
|
|
60
|
+
- Specify the size of an externally-linked array explicitly.
|
|
61
|
+
- Ensure implicitly-valued enum constants don't collide with other values in the list.
|
|
62
|
+
- Do not use the `restrict` qualifier.
|
|
63
|
+
- Declare bit-fields only with an explicitly signed/unsigned integer type or `_Bool`; a single-bit named bit-field must not be signed.
|
|
64
|
+
|
|
65
|
+
## Control flow
|
|
66
|
+
|
|
67
|
+
- Always use braces for the body of an `if`, `else`, loop, or other selection/iteration statement — even a single statement.
|
|
68
|
+
- Terminate every multi-branch `if … else if` chain with a final `else`. This applies only when at least one `else if` is present; a standalone `if` with no `else if` does not require an `else`.
|
|
69
|
+
- A controlling expression of an `if` or loop should be genuinely Boolean — not a bare integer or pointer.
|
|
70
|
+
- Avoid controlling expressions that are always true or always false.
|
|
71
|
+
- A loop counter must not be a floating-point value.
|
|
72
|
+
- Keep `for` loops well-formed: one clear loop counter, with predictable init/test/update, not altered unexpectedly in the body.
|
|
73
|
+
- Use `goto` sparingly; if used, jump only forward to a label later in the same function and within an enclosing block.
|
|
74
|
+
- Do not write recursive functions (direct or indirect).
|
|
75
|
+
|
|
76
|
+
## Switch statements
|
|
77
|
+
|
|
78
|
+
- Give every `switch` a `default` label, placed first or last.
|
|
79
|
+
- Terminate every switch-clause with an unconditional `break`; no unintended fall-through.
|
|
80
|
+
- A `switch` should have at least two clauses, and its labels must sit directly in the switch body.
|
|
81
|
+
- Don't switch on an essentially Boolean expression — use `if`/`else`.
|
|
82
|
+
|
|
83
|
+
## Functions
|
|
84
|
+
|
|
85
|
+
- **(Critical)** Never call a function without a visible declaration.
|
|
86
|
+
- **(Critical)** Every exit path of a non-void function must return a value explicitly.
|
|
87
|
+
- **(Critical)** Do not place `static` between the `[ ]` of an array parameter.
|
|
88
|
+
- Test the return/status value of any function that reports errors; use (or explicitly `(void)`-cast) the return value of a non-void function.
|
|
89
|
+
- Do not use the variadic facilities of `<stdarg.h>`.
|
|
90
|
+
|
|
91
|
+
## Expressions and side effects
|
|
92
|
+
|
|
93
|
+
- **(Critical)** The operand of `sizeof` must not contain an expression with side effects (it is not evaluated).
|
|
94
|
+
- An expression's value and side effects must be the same under any permitted evaluation order — avoid order-of-evaluation dependence.
|
|
95
|
+
- The right operand of `&&` or `||` must not have persistent side effects (it may be skipped by short-circuiting).
|
|
96
|
+
- Initialiser lists must not contain persistent side effects.
|
|
97
|
+
|
|
98
|
+
## Preprocessor
|
|
99
|
+
|
|
100
|
+
- Protect every header against multiple inclusion (include guard or `#pragma once`).
|
|
101
|
+
- Wrap each macro-parameter use in parentheses, e.g. `#define SQ(x) ((x) * (x))`.
|
|
102
|
+
- Do not define a macro with the same name as a keyword, or on/as a reserved identifier or reserved macro name.
|
|
103
|
+
- Follow `#include` with `<filename>` or `"filename"` only; don't put `'`, `"`, `\`, `/*`, or `//` in a header name.
|
|
104
|
+
- Keep `#else`, `#elif`, `#endif` in the same file as their opening `#if`/`#ifdef`/`#ifndef`.
|
|
105
|
+
- A `#if`/`#elif` controlling expression must evaluate to 0 or 1, and every identifier in it must be `#define`d first.
|
|
106
|
+
- A line beginning with `#` must be a valid preprocessing directive.
|
|
107
|
+
- Observe the restrictions on `#`/`##` operators: don't follow a `#`-prefixed parameter immediately with `##`, and use a `#`/`##` operand subject to further expansion only as an operand of those operators.
|
|
108
|
+
- Don't let tokens that look like preprocessing directives appear inside a macro argument.
|
|
109
|
+
|
|
110
|
+
## Comments, literals, identifiers
|
|
111
|
+
|
|
112
|
+
- Don't place `/*` or `//` inside a comment, and don't use line-continuation inside a `//` comment.
|
|
113
|
+
- Avoid octal constants (leading-zero literals like `010`); terminate octal/hex escape sequences unambiguously.
|
|
114
|
+
- Suffix unsigned integer constants with `u`/`U`; use uppercase `L` (never lowercase `l`) for long suffixes.
|
|
115
|
+
- Assign a string literal only to a pointer-to-`const char`.
|
|
116
|
+
- Keep identifiers distinct: external names, same-scope names, macro names, typedef names, and tag names must each be unique, and inner-scope names must not shadow outer-scope ones.
|
|
117
|
+
|
|
118
|
+
## Banned standard-library facilities — and what to use instead
|
|
119
|
+
|
|
120
|
+
A prohibition alone tends to get violated when the functionality is genuinely needed. Each banned facility below has a safe substitute — use it.
|
|
121
|
+
|
|
122
|
+
| Don't use | Use instead |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| `malloc`, `calloc`, `realloc`, `free` | Static objects, a fixed-size pool, or a caller-provided buffer (see SKILL.md Preferred patterns). |
|
|
125
|
+
| `sprintf`, `strcpy`, `strcat`, `gets` (unbounded) | A bounded copy: `memcpy`/`memmove` with a checked length, or a length-limited copy that always NUL-terminates. |
|
|
126
|
+
| `<stdio.h>` I/O (`printf`, `scanf`, `fopen`, …) | A driver/BSP abstraction layer; move data via caller buffers and status codes. If formatted output is genuinely required, use a **bounded** `snprintf` with an explicit size — and treat it as a documented deviation, since `<stdio.h>` is itself restricted. Never `sprintf`. |
|
|
127
|
+
| `atof`, `atoi`, `atol`, `atoll` | `strtol`, `strtoul`, `strtod` with explicit error and range checking (these are not on the banned list). |
|
|
128
|
+
| `getenv`, `system` | Compile-time configuration, or configuration passed in by the caller. No environment or process access. |
|
|
129
|
+
| `abort`, `exit` | Propagate a status code up the call chain to a defined fault handler (see SKILL.md Preferred patterns). |
|
|
130
|
+
| `<setjmp.h>` (`setjmp`/`longjmp`) | Structured control flow with status-return error handling. |
|
|
131
|
+
| `<signal.h>` | A platform/RTOS event or interrupt-handling mechanism provided by the BSP. |
|
|
132
|
+
| `bsearch`, `qsort` | A bounded, type-specific search/sort over a known element count. |
|
|
133
|
+
| `<time.h>` date/time routines | A platform timer/RTC abstraction provided by the BSP. |
|
|
134
|
+
| `<tgmath.h>` | The explicitly-typed `<math.h>` functions (e.g. `sqrtf` for `float`, `sqrt` for `double`). |
|
|
135
|
+
|
|
136
|
+
## General
|
|
137
|
+
|
|
138
|
+
- All code should trace to a documented requirement; don't add unrequested functionality.
|
|
139
|
+
- Every source file must compile cleanly with no errors and no reliance on undefined or critical unspecified behaviour.
|
|
140
|
+
- Don't depend on implementation-defined behaviour unless it is identified and documented.
|
|
141
|
+
- Remove unreachable and dead code.
|
|
142
|
+
- Encapsulate and isolate any assembly language (in a dedicated function/macro/intrinsic), rather than scattering it inline.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
# Advisory guidelines (recommended)
|
|
147
|
+
|
|
148
|
+
Lower-priority recommendations that improve clarity, maintainability, and portability. Apply them by default, but they may be set aside with engineering judgement when a Critical or Standard guideline, or readability, is better served.
|
|
149
|
+
|
|
150
|
+
## Types and naming
|
|
151
|
+
|
|
152
|
+
- Use size- and signedness-indicating types (e.g. `uint32_t`, `int16_t`) in place of the plain numeric types (`int`, `long`, …).
|
|
153
|
+
- Make a pointer point to a `const`-qualified type whenever the pointee is not modified through it.
|
|
154
|
+
- Keep identifiers typographically unambiguous within a namespace — avoid names that differ only by case, or by easily-confused glyphs (`l`/`1`, `O`/`0`).
|
|
155
|
+
- Keep internal-linkage identifiers unique across the project.
|
|
156
|
+
- Don't cast a value to an inappropriate essential type (e.g. casting that misrepresents signedness or category).
|
|
157
|
+
|
|
158
|
+
## Linkage and scope
|
|
159
|
+
|
|
160
|
+
- Give a function or object internal linkage (`static`) if it is referenced in only one translation unit.
|
|
161
|
+
- Define an object at block scope if its identifier is used within only a single function.
|
|
162
|
+
- If a pointer to a struct/union is never dereferenced within a translation unit, hide the type's implementation (use an opaque/incomplete type in the header).
|
|
163
|
+
|
|
164
|
+
## Unused declarations
|
|
165
|
+
|
|
166
|
+
- Remove unused type, tag, and macro declarations.
|
|
167
|
+
- Remove unused labels and unused function parameters.
|
|
168
|
+
|
|
169
|
+
## Expressions and operators
|
|
170
|
+
|
|
171
|
+
- Make operator precedence explicit with parentheses rather than relying on the reader to recall it.
|
|
172
|
+
- Do not use the comma operator.
|
|
173
|
+
- Do not rely on the result (value) of an assignment, e.g. avoid `x = (y = z);` or `if ((p = next()) != NULL)`.
|
|
174
|
+
- A full expression containing `++` or `--` should have no other side effects.
|
|
175
|
+
- Constant-expression evaluation should not cause unsigned integer wrap-around.
|
|
176
|
+
|
|
177
|
+
## Pointers
|
|
178
|
+
|
|
179
|
+
- Prefer array indexing over pointer arithmetic; avoid `+`, `-`, `+=`, `-=` applied directly to pointer-typed expressions.
|
|
180
|
+
- Keep pointer nesting to at most two levels (e.g. avoid `int ***`).
|
|
181
|
+
- Avoid converting between an object pointer and an integer type, and from `void *` into a pointer to object.
|
|
182
|
+
|
|
183
|
+
## Control flow
|
|
184
|
+
|
|
185
|
+
- Give each function a single point of exit at the end: one `return` statement, reached by structuring the body with a status variable and nested conditions rather than early returns. Prefer this over early-return guard clauses — see SKILL.md Preferred patterns for the idiom.
|
|
186
|
+
- Avoid the `goto` statement.
|
|
187
|
+
- Use at most one `break` or `goto` to terminate a given loop.
|
|
188
|
+
|
|
189
|
+
## Functions
|
|
190
|
+
|
|
191
|
+
- Do not modify a function parameter; copy it to a local first if a mutable working value is needed.
|
|
192
|
+
- When a parameter is declared with an array type, ensure the supplied argument actually has enough elements.
|
|
193
|
+
- Call functions that operate on a resource in the correct sequence (e.g. initialise before use, acquire before release).
|
|
194
|
+
- Prefer a real function over a function-like macro where the two are interchangeable (functions give type checking and easier debugging).
|
|
195
|
+
|
|
196
|
+
## Preprocessor
|
|
197
|
+
|
|
198
|
+
- Precede `#include` directives only with other preprocessor directives or comments.
|
|
199
|
+
- Avoid `#undef`.
|
|
200
|
+
- Avoid the `#` (stringize) and `##` (token-paste) operators.
|
|
201
|
+
|
|
202
|
+
## Language features and portability
|
|
203
|
+
|
|
204
|
+
- Do not use non-standard language extensions; keep to standard C.
|
|
205
|
+
- Do not use trigraphs.
|
|
206
|
+
- Avoid the `union` keyword (overlapping members defeat type safety).
|
|
207
|
+
- Do not use the floating-point exception features of `<fenv.h>`.
|
|
208
|
+
|
|
209
|
+
## Style and process
|
|
210
|
+
|
|
211
|
+
- Document every use of assembly language.
|
|
212
|
+
- Don't leave blocks of code "commented out" — delete them and rely on version control, or use conditional compilation deliberately.
|