Skip to content

Commit c5f0a59

Browse files
committed
Preprocessor6: add DIR-4-9
1 parent 4081fc8 commit c5f0a59

File tree

9 files changed

+154
-1
lines changed

9 files changed

+154
-1
lines changed

.vscode/tasks.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@
251251
"Preprocessor3",
252252
"Preprocessor4",
253253
"Preprocessor5",
254+
"Preprocessor6",
254255
"IntegerConversion",
255256
"Expressions",
256257
"DeadCode",
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* @id c/misra/function-over-function-like-macro
3+
* @name DIR-4-9: A function should be used in preference to a function-like macro where they are interchangeable
4+
* @description Using a function-like macro instead of a function can lead to unexpected program
5+
* behaviour.
6+
* @kind problem
7+
* @precision medium
8+
* @problem.severity recommendation
9+
* @tags external/misra/id/dir-4-9
10+
* external/cert/audit
11+
* maintainability
12+
* readability
13+
* external/misra/obligation/advisory
14+
*/
15+
16+
import cpp
17+
import codingstandards.c.misra
18+
import codingstandards.cpp.FunctionLikeMacro
19+
import codingstandards.cpp.Naming
20+
21+
predicate isOperator(string possible) {
22+
possible in [
23+
"+", "-", "*", "/", "%", "^", "&", "|", "~", "!", "=", "<", ">", "+=", "-=", "*=", "/=", "%=",
24+
"^=", "&=", "|=", "<<", ">>", ">>=", "<<=", "==", "!=", "<=", ">=", "<=>", "&&", "||", "++",
25+
"--", "->*", "->", "()", "[]"
26+
]
27+
}
28+
29+
//cases where we trust the choice
30+
predicate omission(MacroInvocation i) {
31+
i.getFile() instanceof HeaderFile or
32+
Naming::Cpp14::hasStandardLibraryMacroName(i.getMacroName())
33+
}
34+
35+
class UnsafeMacro extends FunctionLikeMacro {
36+
UnsafeMacro() {
37+
//parameter not used - has false positives on args that are not used but are substrings of other args
38+
exists(string p |
39+
p = this.getAParameter() and
40+
not this.getBody().regexpMatch(".*(\\s*|\\(||\\))" + p + "(\\s*||\\)|\\().*")
41+
)
42+
or
43+
//parameter used more than once
44+
exists(string p |
45+
p = this.getAParameter() and
46+
exists(int i, string newstr |
47+
newstr = this.getBody().replaceAll(p, "") and
48+
i = ((this.getBody().length() - newstr.length()) / p.length()) and
49+
i > 1
50+
)
51+
)
52+
}
53+
}
54+
55+
from MacroInvocation i
56+
where
57+
not isExcluded(i, Preprocessor6Package::functionOverFunctionLikeMacroQuery()) and
58+
not omission(i) and
59+
i.getMacro() instanceof UnsafeMacro and
60+
//heuristic - macros with one arg only are easier to replace
61+
not exists(i.getUnexpandedArgument(1)) and
62+
//operator as arg omits function applicability
63+
not isOperator(i.getUnexpandedArgument(_)) and
64+
//static storage duration can only be initialized with constant
65+
not exists(StaticStorageDurationVariable v | i.getExpr() = v.getAnAssignedValue()) and
66+
//function call not allowed in a constant expression (where constant expr is parent)
67+
not exists(Expr e |
68+
e.isConstant() and
69+
not i.getExpr() = e and
70+
i.getExpr().getParent+() = e
71+
) and
72+
forall(string arg | arg = i.getUnexpandedArgument(_) | exists(Expr e | arg = e.toString()))
73+
select i, "Macro invocation used when function call would be preferred."
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| test.c:19:12:19:20 | MACRO4(L) | Macro invocation used when function call would be preferred. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/DIR-4-9/FunctionOverFunctionLikeMacro.ql

c/misra/test/rules/DIR-4-9/test.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#define MACRO(OP, L, R) ((L)OP(R))
2+
#define MACRO2(L, R) (L + R)
3+
#define MACRO3(L, R) (L + R + L)
4+
#define MACRO4(L) (1 + 1)
5+
#define MACRO5(L, LR) (LR + 1)
6+
#define MACRO6(X, LR) (LR + 1)
7+
8+
const char a1[MACRO2(1, 1) + 6]; // COMPLIANT
9+
10+
void f() {
11+
int i = MACRO(+, 1, 1); // COMPLIANT
12+
13+
int i2 = MACRO2(7, 10); // COMPLIANT - exception
14+
15+
static i3 = MACRO2(1, 1); // COMPLIANT
16+
17+
int i4 = MACRO3(7, 10); // COMPLIANT - exception
18+
19+
int i5 = MACRO4(1); // NON_COMPLIANT
20+
21+
int i6 = MACRO5(1, 1); // NON_COMPLIANT[FALSE_NEGATIVE]
22+
23+
int i7 = MACRO6(1, 1); // COMPLIANT - exception
24+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype Preprocessor6Query = TFunctionOverFunctionLikeMacroQuery()
7+
8+
predicate isPreprocessor6QueryMetadata(Query query, string queryId, string ruleId) {
9+
query =
10+
// `Query` instance for the `functionOverFunctionLikeMacro` query
11+
Preprocessor6Package::functionOverFunctionLikeMacroQuery() and
12+
queryId =
13+
// `@id` for the `functionOverFunctionLikeMacro` query
14+
"c/misra/function-over-function-like-macro" and
15+
ruleId = "DIR-4-9"
16+
}
17+
18+
module Preprocessor6Package {
19+
Query functionOverFunctionLikeMacroQuery() {
20+
//autogenerate `Query` type
21+
result =
22+
// `Query` type for `functionOverFunctionLikeMacro` query
23+
TQueryC(TPreprocessor6PackageQuery(TFunctionOverFunctionLikeMacroQuery()))
24+
}
25+
}

cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import Preprocessor2
2525
import Preprocessor3
2626
import Preprocessor4
2727
import Preprocessor5
28+
import Preprocessor6
2829
import SideEffects1
2930
import SideEffects2
3031
import Strings1
@@ -57,6 +58,7 @@ newtype TCQuery =
5758
TPreprocessor3PackageQuery(Preprocessor3Query q) or
5859
TPreprocessor4PackageQuery(Preprocessor4Query q) or
5960
TPreprocessor5PackageQuery(Preprocessor5Query q) or
61+
TPreprocessor6PackageQuery(Preprocessor6Query q) or
6062
TSideEffects1PackageQuery(SideEffects1Query q) or
6163
TSideEffects2PackageQuery(SideEffects2Query q) or
6264
TStrings1PackageQuery(Strings1Query q) or
@@ -89,6 +91,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId) {
8991
isPreprocessor3QueryMetadata(query, queryId, ruleId) or
9092
isPreprocessor4QueryMetadata(query, queryId, ruleId) or
9193
isPreprocessor5QueryMetadata(query, queryId, ruleId) or
94+
isPreprocessor6QueryMetadata(query, queryId, ruleId) or
9295
isSideEffects1QueryMetadata(query, queryId, ruleId) or
9396
isSideEffects2QueryMetadata(query, queryId, ruleId) or
9497
isStrings1QueryMetadata(query, queryId, ruleId) or

rule_packages/c/Preprocessor6.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"MISRA-C-2012": {
3+
"DIR-4-9": {
4+
"properties": {
5+
"obligation": "advisory"
6+
},
7+
"queries": [
8+
{
9+
"description": "Using a function-like macro instead of a function can lead to unexpected program behaviour.",
10+
"kind": "problem",
11+
"name": "A function should be used in preference to a function-like macro where they are interchangeable",
12+
"precision": "medium",
13+
"severity": "recommendation",
14+
"short_name": "FunctionOverFunctionLikeMacro",
15+
"tags": [
16+
"external/misra/audit",
17+
"maintainability",
18+
"readability"
19+
]
20+
}
21+
],
22+
"title": "A function should be used in preference to a function-like macro where they are interchangeable"
23+
}
24+
}
25+
}

rules.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ c,MISRA-C-2012,DIR-4-5,Yes,Advisory,,,Identifiers in the same name space with ov
610610
c,MISRA-C-2012,RULE-4-6,Yes,Advisory,,,typedefs that indicate size and signedness should be used in place of the basic numerical types,,Types,Hard,
611611
c,MISRA-C-2012,RULE-4-7,Yes,Required,,,"If a function returns error information, then that error information shall be tested",M0-3-2,Contracts,Import,
612612
c,MISRA-C-2012,RULE-4-8,Yes,Advisory,,,"If a pointer to a structure or union is never dereferenced within a translation unit, then the implementation of the object should be hidden",,Pointers1,Medium,
613-
c,MISRA-C-2012,RULE-4-9,Yes,Advisory,,,A function should be used in preference to a function-like macro where they are interchangeable,,Preprocessor,Medium,
613+
c,MISRA-C-2012,DIR-4-9,Yes,Advisory,,,A function should be used in preference to a function-like macro where they are interchangeable,,Preprocessor6,Medium,Audit
614614
c,MISRA-C-2012,RULE-4-10,Yes,Required,,,Precautions shall be taken in order to prevent the contents of a header file being included more than once,M16-2-3,Preprocessor2,Medium,
615615
c,MISRA-C-2012,RULE-4-11,Yes,Required,,,The validity of values passed to library functions shall be checked,,Contracts,Hard,
616616
c,MISRA-C-2012,RULE-4-12,Yes,Required,,,Dynamic memory allocation shall not be used,,Banned,Medium,

0 commit comments

Comments
 (0)