Skip to content

Commit e300f67

Browse files
authored
Merge pull request #471 from github/lcartey/m16-1-1
M16-1-1: Optimize query and improve detection of nested uses of `defined`
2 parents a1398a4 + 91034e3 commit e300f67

10 files changed

+61
-57
lines changed

c/misra/src/rules/RULE-20-8/ControllingExpressionIfDirective.ql

+6-11
Original file line numberDiff line numberDiff line change
@@ -14,40 +14,35 @@
1414

1515
import cpp
1616
import codingstandards.c.misra
17+
import codingstandards.cpp.PreprocessorDirective
1718

1819
/* A controlling expression is evaluated if it is not excluded (guarded by another controlling expression that is not taken). This translates to it either being taken or not taken. */
1920
predicate isEvaluated(PreprocessorBranch b) { b.wasTaken() or b.wasNotTaken() }
2021

21-
class IfOrElifPreprocessorBranch extends PreprocessorBranch {
22-
IfOrElifPreprocessorBranch() {
23-
this instanceof PreprocessorIf or this instanceof PreprocessorElif
24-
}
25-
}
26-
2722
/**
2823
* Looks like it contains a single macro, which may be undefined
2924
*/
30-
class SimpleMacroPreprocessorBranch extends IfOrElifPreprocessorBranch {
25+
class SimpleMacroPreprocessorBranch extends PreprocessorIfOrElif {
3126
SimpleMacroPreprocessorBranch() { this.getHead().regexpMatch("[a-zA-Z_][a-zA-Z0-9_]+") }
3227
}
3328

34-
class SimpleNumericPreprocessorBranch extends IfOrElifPreprocessorBranch {
29+
class SimpleNumericPreprocessorBranch extends PreprocessorIfOrElif {
3530
SimpleNumericPreprocessorBranch() { this.getHead().regexpMatch("[0-9]+") }
3631
}
3732

3833
class ZeroOrOnePreprocessorBranch extends SimpleNumericPreprocessorBranch {
3934
ZeroOrOnePreprocessorBranch() { this.getHead().regexpMatch("[0|1]") }
4035
}
4136

42-
predicate containsOnlySafeOperators(IfOrElifPreprocessorBranch b) {
37+
predicate containsOnlySafeOperators(PreprocessorIfOrElif b) {
4338
containsOnlyDefinedOperator(b)
4439
or
4540
//logic: comparison operators eval last, so they make it safe?
4641
b.getHead().regexpMatch(".*[\\&\\&|\\|\\||>|<|==].*")
4742
}
4843

4944
//all defined operators is definitely safe
50-
predicate containsOnlyDefinedOperator(IfOrElifPreprocessorBranch b) {
45+
predicate containsOnlyDefinedOperator(PreprocessorIfOrElif b) {
5146
forall(string portion |
5247
portion =
5348
b.getHead()
@@ -65,7 +60,7 @@ class BinaryValuedMacro extends Macro {
6560
BinaryValuedMacro() { this.getBody().regexpMatch("\\(?(0|1)\\)?") }
6661
}
6762

68-
from IfOrElifPreprocessorBranch b, string msg
63+
from PreprocessorIfOrElif b, string msg
6964
where
7065
not isExcluded(b, Preprocessor3Package::controllingExpressionIfDirectiveQuery()) and
7166
isEvaluated(b) and
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- `M16-1-1` - `DefinedPreProcessorOperatorGeneratedFromExpansionFound.ql`:
2+
- Optimize query to improve performance
3+
- Improve detection of macros whose body contains the `defined` operator after the start of the macro (e.g. `#define X Y || defined(Z)`).
4+
- Enable exclusions to be applied for this rule.

cpp/autosar/src/rules/M16-1-1/DefinedMacro.qll

+18-18
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,27 @@ import cpp
22
import codingstandards.cpp.autosar
33

44
/**
5-
* A helper class describing macros wrapping defined operator
5+
* A helper class describing macros wrapping the defined operator
66
*/
7-
class DefinedMacro extends Macro {
8-
DefinedMacro() {
9-
this.getBody().regexpMatch("defined\\s*\\(.*")
7+
class MacroUsesDefined extends Macro {
8+
MacroUsesDefined() {
9+
// Uses `defined` directly
10+
exists(this.getBody().regexpFind("\\bdefined\\b", _, _))
1011
or
11-
this.getBody().regexpMatch("defined[\\s]+|defined$")
12+
// Uses a macro that uses `defined` (directly or indirectly)
13+
exists(MacroUsesDefined dm | exists(this.getBody().regexpFind(dm.getRegexForMatch(), _, _)))
1214
}
1315

14-
Macro getAUse() {
15-
result = this or
16-
anyAliasing(result, this)
16+
/**
17+
* Gets a regex for matching uses of this macro.
18+
*/
19+
string getRegexForMatch() {
20+
exists(string arguments |
21+
// If there are arguments
22+
if getHead() = getName() then arguments = "" else arguments = "\\s*\\("
23+
|
24+
// Use word boundary matching to find identifiers that match
25+
result = "\\b" + getName() + "\\b" + arguments
26+
)
1727
}
1828
}
19-
20-
predicate directAlias(Macro alias, Macro aliased) {
21-
not alias.getBody() = alias.getBody().replaceAll(aliased.getHead(), "")
22-
}
23-
24-
predicate anyAliasing(Macro alias, Macro inQ) {
25-
directAlias(alias, inQ)
26-
or
27-
exists(Macro intermediate | anyAliasing(intermediate, inQ) and anyAliasing(alias, intermediate))
28-
}

cpp/autosar/src/rules/M16-1-1/DefinedPreProcessorOperatorGeneratedFromExpansionFound.ql

+5-11
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,12 @@
1414

1515
import cpp
1616
import codingstandards.cpp.autosar
17+
import codingstandards.cpp.PreprocessorDirective
1718
import DefinedMacro
1819

19-
from DefinedMacro m, PreprocessorBranch e
20+
from PreprocessorIfOrElif e, MacroUsesDefined m
2021
where
21-
(
22-
e instanceof PreprocessorIf or
23-
e instanceof PreprocessorElif
24-
) and
25-
(
26-
e.getHead().regexpMatch(m.getAUse().getHead() + "\\s*\\(.*")
27-
or
28-
e.getHead().regexpMatch(m.getAUse().getHead().replaceAll("(", "\\(").replaceAll(")", "\\)"))
29-
) and
30-
not isExcluded(e)
22+
not isExcluded(e, MacrosPackage::definedPreProcessorOperatorInOneOfTheTwoStandardFormsQuery()) and
23+
// A`#if` or `#elif` which uses a macro which uses `defined`
24+
exists(e.getHead().regexpFind(m.getRegexForMatch(), _, _))
3125
select e, "The macro $@ expands to 'defined'", m, m.getName()

cpp/autosar/src/rules/M16-1-1/DefinedPreProcessorOperatorInOneOfTheTwoStandardForms.ql

+2-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import cpp
1717
import codingstandards.cpp.autosar
18+
import codingstandards.cpp.PreprocessorDirective
1819

1920
//get what comes after each 'defined' used with or without parenth
2021
string matchesDefinedOperator(PreprocessorBranch e) {
@@ -34,12 +35,8 @@ string matchesDefinedOperator(PreprocessorBranch e) {
3435
)
3536
}
3637

37-
from PreprocessorBranch e, string arg
38+
from PreprocessorIfOrElif e, string arg
3839
where
39-
(
40-
e instanceof PreprocessorIf or
41-
e instanceof PreprocessorElif
42-
) and
4340
arg = matchesDefinedOperator(e) and
4441
not arg.regexpMatch("^\\w*$") and
4542
not isExcluded(e, MacrosPackage::definedPreProcessorOperatorInOneOfTheTwoStandardFormsQuery())
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
| test.cpp:22:1:22:18 | #if DBLWRAPUSES(X) | The macro $@ expands to 'defined' | test.cpp:18:1:18:22 | #define BADDEF defined | BADDEF |
1+
| test.cpp:22:1:22:18 | #if DBLWRAPUSES(X) | The macro $@ expands to 'defined' | test.cpp:21:1:21:24 | #define DBLWRAPUSES USES | DBLWRAPUSES |
22
| test.cpp:26:1:26:16 | #if BADDEFTWO(X) | The macro $@ expands to 'defined' | test.cpp:25:1:25:31 | #define BADDEFTWO(X) defined(X) | BADDEFTWO |
3+
| test.cpp:29:1:29:16 | #if BADDEFTWO(Y) | The macro $@ expands to 'defined' | test.cpp:25:1:25:31 | #define BADDEFTWO(X) defined(X) | BADDEFTWO |
4+
| test.cpp:42:1:42:11 | #if WRAPPER | The macro $@ expands to 'defined' | test.cpp:40:1:40:35 | #define WRAPPER X < Y \|\| defined(z) | WRAPPER |

cpp/autosar/test/rules/M16-1-1/DefinedPreProcessorOperatorInOneOfTheTwoStandardForms.expected

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
| test.cpp:9:1:9:19 | #elif defined X < Y | Use of defined with non-standard form: X < Y. |
33
| test.cpp:13:1:13:18 | #if defined(X > Y) | Use of defined with non-standard form: X > Y. |
44
| test.cpp:14:1:14:20 | #elif defined(X < Y) | Use of defined with non-standard form: X < Y. |
5-
| test.cpp:34:1:34:47 | #if defined(X) \|\| defined _Y_ + X && defined(Y) | Use of defined with non-standard form: _Y_ + X. |
5+
| test.cpp:37:1:37:47 | #if defined(X) \|\| defined _Y_ + X && defined(Y) | Use of defined with non-standard form: _Y_ + X. |

cpp/autosar/test/rules/M16-1-1/test.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,18 @@
2626
#if BADDEFTWO(X) // NON_COMPLIANT
2727
#endif
2828

29+
#if BADDEFTWO(Y) // NON_COMPLIANT
30+
#endif
31+
2932
// clang-format off
3033
#if defined (X) || (defined(_Y_)) // COMPLIANT
3134
// clang-format on
3235
#endif
3336

3437
#if defined(X) || defined _Y_ + X && defined(Y) // NON_COMPLIANT
38+
#endif
39+
40+
#define WRAPPER X < Y || defined(z)
41+
42+
#if WRAPPER // NON_COMPLIANT
3543
#endif

cpp/common/src/codingstandards/cpp/PreprocessorDirective.qll

+10
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,13 @@ PreprocessorDirective isLocatedInAMacroInvocation(MacroInvocation m) {
3030
result = p
3131
)
3232
}
33+
34+
/**
35+
* An `if` or `elif` preprocessor branch.
36+
*/
37+
class PreprocessorIfOrElif extends PreprocessorBranch {
38+
PreprocessorIfOrElif() {
39+
this instanceof PreprocessorIf or
40+
this instanceof PreprocessorElif
41+
}
42+
}

cpp/common/src/codingstandards/cpp/rules/undefinedmacroidentifiers/UndefinedMacroIdentifiers.qll

+4-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import cpp
22
import codingstandards.cpp.Exclusions
3+
import codingstandards.cpp.PreprocessorDirective
34

45
abstract class UndefinedMacroIdentifiersSharedQuery extends Query { }
56

@@ -76,17 +77,10 @@ string getAnIfDefdMacroIdentifier(PreprocessorBranch pb) {
7677
)
7778
}
7879

79-
class IfAndElifs extends PreprocessorBranch {
80-
IfAndElifs() {
81-
this instanceof PreprocessorIf or
82-
this instanceof PreprocessorElif
83-
}
84-
}
85-
86-
class BadIfAndElifs extends IfAndElifs {
80+
class UndefinedIdIfOrElif extends PreprocessorIfOrElif {
8781
string undefinedMacroIdentifier;
8882

89-
BadIfAndElifs() {
83+
UndefinedIdIfOrElif() {
9084
exists(string defRM |
9185
defRM =
9286
this.getHead()
@@ -113,7 +107,7 @@ class BadIfAndElifs extends IfAndElifs {
113107
string getAnUndefinedMacroIdentifier() { result = undefinedMacroIdentifier }
114108
}
115109

116-
query predicate problems(BadIfAndElifs b, string message) {
110+
query predicate problems(UndefinedIdIfOrElif b, string message) {
117111
not isExcluded(b, getQuery()) and
118112
message =
119113
"#if/#elif that uses a macro identifier " + b.getAnUndefinedMacroIdentifier() +

0 commit comments

Comments
 (0)