55import re
66
77ERR1 = "The %1 operator was applied to an expression of pointer type"
8+ # Strict mode (MISRA C 2004 17.4): indexing only allowed on array-typed objects
9+ ERR2 = "Array indexing was applied to %1, which is not defined as an array type"
810
911# The ID for the check
1012def ids():
1113 return ('MISRA23_18.4', 'MISRA12_18.4', 'CPP_P062',
12- 'MISRA25_18.4'
14+ 'MISRA25_18.4', 'MISRA04_17.4'
1315 )
1416
1517# The short name of the check
@@ -24,6 +26,8 @@ All Checks/Language Specific/C and C++/Pointers/
2426The +, -, += and -= operators should not be applied to an expression of pointer
2527type""",
2628 'MISRA25_18.4': 'Published Standards/MISRA C 2025/18.4 The +, -, += and -= operators should not be applied to an expression of pointer type',
29+ 'MISRA04_17.4': 'Published Standards/MISRA-C 2004/' + """\
30+ 17.4 Array indexing shall be the only allowed form of pointer arithmetic""",
2731 }[id]
2832
2933
@@ -56,6 +60,12 @@ def tags(id):
5660 'Category: Advisory',
5761 'Pointers',
5862 ],
63+ 'MISRA04_17.4': [
64+ 'Language: C',
65+ 'Standard: MISRA C 2004',
66+ 'Category: Required',
67+ 'Pointers',
68+ ],
5969 }.get(id)
6070
6171# The long description of the check
@@ -201,6 +211,42 @@ void fn4 ( void )
201211 fn3 ( a1, a2 );
202212 fn3 ( &data, &a2[ 4 ] );
203213}</code></pre>
214+ """,
215+
216+ 'MISRA04_17.4': """\
217+ <p>Array indexing is the only acceptable form of pointer arithmetic, because it
218+ is clearer and hence less error prone than pointer manipulation. This rule bans
219+ the explicit calculation of pointer values. Array indexing shall only be applied
220+ to objects defined as an array type. Any explicitly calculated pointer value has
221+ the potential to access unintended or invalid memory addresses. Pointers may go
222+ out of bounds of arrays or structures, or may even point to effectively
223+ arbitrary locations. See also Rule 21.1.</p>
224+ <pre><code language="C++">void my_fn(uint8_t * p1, uint8_t p2[])
225+ {
226+ uint8_t index = 0U;
227+ uint8_t * p3;
228+ uint8_t * p4;
229+ *p1 = 0U;
230+ p1 ++; /* not compliant - pointer increment */
231+ p1 = p1 + 5; /* not compliant - pointer increment */
232+ p1[5] = 0U; /* not compliant - p1 was not declared as an array */
233+ p3 = &p1[5]; /* not compliant - p1 was not declared as an array */
234+ p2[0] = 0U;
235+ index ++;
236+ index = index + 5U;
237+ p2[index] = 0U; /* compliant */
238+ p4 = &p2[5]; /* compliant */
239+ }
240+
241+ uint8_t a1[16];
242+ uint8_t a2[16];
243+ my_fn(a1, a2);
244+ my_fn(&a1[4], &a2[4]);
245+ uint8_t a[10];
246+ uint8_t * p;
247+ p = a;
248+ *(p+5) = 0U; /* not compliant */
249+ p[5] = 0U; /* not compliant */</code></pre>
204250""",
205251 }[id]
206252
@@ -210,11 +256,27 @@ def test_entity(file):
210256def test_language(language):
211257 return language == 'C++'
212258
259+ def define_options(check):
260+ # MISRA C 2004 17.4 is stricter than 18.4: it also bans pointer ++/-- and
261+ # indexing applied to objects not defined as an array type. Default on only
262+ # for MISRA04_17.4.
263+ strict = check.id() == 'MISRA04_17.4'
264+ check.option().checkbox('strict',
265+ 'Also flag pointer ++/-- and array indexing on objects not defined as '
266+ 'an array type (MISRA C 2004 Rule 17.4)',
267+ strict)
268+
213269def check(check, file):
214270 lexer = None
215271 lexers = {}
272+ strict = check.option().lookup('strict')
216273 # Operators to check around pointer usage
217274 operators = ['+', '-', '+=', '-=']
275+ # In strict mode also flag ++/--; these appear as Modify references
276+ useKinds = 'Deref Setby, Useby'
277+ if strict:
278+ operators = operators + ['++', '--']
279+ useKinds = useKinds + ', Modifyby'
218280
219281 # Get all references to defined objects or parameters in the file
220282 refs = file.filerefs('Define', 'Object, Parameter')
@@ -224,8 +286,12 @@ def check(check, file):
224286 if '*' not in str(ref.ent().freetext("underlyingtype")):
225287 continue
226288
289+ # An object defined as an array type (including a decayed array
290+ # parameter such as p2[]) keeps '[' in its declared type
291+ arrayDeclared = '[' in str(ref.ent().type())
292+
227293 # Get all dereference and usage references of the pointer
228- useRefs = ref.ent().refs('Deref Setby, Useby' )
294+ useRefs = ref.ent().refs(useKinds )
229295
230296 for useRef in useRefs:
231297 # Reuse lexer for each file to avoid reloading
@@ -241,8 +307,14 @@ def check(check, file):
241307 prevLex = lexeme.previous(True, True)
242308 nextLex = lexeme.next(True, True)
243309
244- # Skip if followed by array indexing
310+ # Handle array indexing
245311 if nextLex and nextLex.text() == '[':
312+ # In strict mode, indexing is only allowed on objects
313+ # defined as an array type
314+ if strict and not arrayDeclared:
315+ check.violation(useRef.ent(), useRef.file(),
316+ useRef.line(), useRef.column(), ERR2,
317+ ref.ent().name())
246318 continue
247319
248320 # Skip if followed by member access (-> or .)
0 commit comments