Skip to content

Commit ad49b87

Browse files
committed
Merge branch 'main' into rvermeulen/fix-issue-#311
2 parents 4225420 + e300f67 commit ad49b87

31 files changed

+292
-106
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- `A18-0-1` - `CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.ql`:
2+
- Fix issue #7 - improve query logic to only match on exact standard library names (e.g., now excludes sys/header.h type headers from the results as those are not C standard libraries).
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- `M0-1-4` - `SingleUseMemberPODVariable.ql`:
2+
- Address FP reported in #388. Include aggregrate initialization as a use of a member.
3+
- Include indirect initialization of members. For example, casting a pointer to a buffer to a struct pointer.
4+
- Reformat the alert message to adhere to the style-guide.
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- `M0-1-3` - `UnusedMemberVariable.ql`, `UnusedGlobalOrNamespaceVariable.ql`:
2+
- Address FP reported in #384. Exclude variables with compile time values that may have been used as a template argument.
3+
- Exclude uninstantiated template members.
4+
- Reformat the alert message to adhere to the style-guide.

cpp/autosar/src/rules/A18-0-1/CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.ql

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ where
2828
* not use any of 'signal.h's facilities, for example.
2929
*/
3030

31-
filename = i.getIncludedFile().getBaseName() and
31+
filename = i.getIncludeText().substring(1, i.getIncludeText().length() - 1) and
3232
filename in [
3333
"assert.h", "ctype.h", "errno.h", "fenv.h", "float.h", "inttypes.h", "limits.h", "locale.h",
3434
"math.h", "setjmp.h", "signal.h", "stdarg.h", "stddef.h", "stdint.h", "stdio.h", "stdlib.h",

cpp/autosar/src/rules/M0-1-3/UnusedGlobalOrNamespaceVariable.ql

+4-2
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,7 @@ from PotentiallyUnusedGlobalOrNamespaceVariable v
2222
where
2323
not isExcluded(v, DeadCodePackage::unusedGlobalOrNamespaceVariableQuery()) and
2424
// No variable access
25-
not exists(v.getAnAccess())
26-
select v, "Variable " + v.getQualifiedName() + " is unused."
25+
not exists(v.getAnAccess()) and
26+
// Exclude members whose value is compile time and is potentially used to inintialize a template
27+
not maybeACompileTimeTemplateArgument(v)
28+
select v, "Variable '" + v.getQualifiedName() + "' is unused."

cpp/autosar/src/rules/M0-1-3/UnusedLocalVariable.ql

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@ where
5050
// Local variable is never accessed
5151
not exists(v.getAnAccess()) and
5252
getUseCountConservatively(v) = 0
53-
select v, "Local variable " + v.getName() + " in " + v.getFunction().getName() + " is not used."
53+
select v, "Local variable '" + v.getName() + "' in '" + v.getFunction().getName() + "' is not used."

cpp/autosar/src/rules/M0-1-3/UnusedMemberVariable.ql

+4-2
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,7 @@ where
2525
// No variable access
2626
not exists(v.getAnAccess()) and
2727
// No explicit initialization in a constructor
28-
not exists(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v)
29-
select v, "Member variable " + v.getName() + " is unused."
28+
not exists(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v) and
29+
// Exclude members whose value is compile time and is potentially used to inintialize a template
30+
not maybeACompileTimeTemplateArgument(v)
31+
select v, "Member variable '" + v.getName() + "' is unused."

cpp/autosar/src/rules/M0-1-4/SingleUseMemberPODVariable.ql

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ where
2424
not isExcluded(v, DeadCodePackage::singleUseMemberPODVariableQuery()) and
2525
isSingleUseNonVolatilePODVariable(v)
2626
select v,
27-
"Member POD variable " + v.getName() + " in " + v.getDeclaringType().getName() + " is only $@.",
28-
getSingleUse(v), "used once"
27+
"Member POD variable '" + v.getName() + "' in '" + v.getDeclaringType().getName() +
28+
"' is only $@.", getSingleUse(v), "used once"

cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll

+60-15
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,68 @@ private string getConstExprValue(Variable v) {
1010
v.isConstexpr()
1111
}
1212

13+
/**
14+
* Gets the number of uses of variable `v` in an opaque assignment, where an opaqua assignment for example a cast from one type to the other and `v` is assumed to be a member of the resulting type.
15+
* e.g.,
16+
* struct foo {
17+
* int bar;
18+
* }
19+
*
20+
* struct foo * v = (struct foo*)buffer;
21+
*/
22+
Expr getIndirectSubObjectAssignedValue(MemberVariable subobject) {
23+
// struct foo * ptr = (struct foo*)buffer;
24+
exists(Struct someStruct, Variable instanceOfSomeStruct | someStruct.getAMember() = subobject |
25+
instanceOfSomeStruct.getType().(PointerType).getBaseType() = someStruct and
26+
exists(Cast assignedValue |
27+
// Exclude cases like struct foo * v = nullptr;
28+
not assignedValue.isImplicit() and
29+
// `v` is a subobject of another type that reinterprets another object. We count that as a use of `v`.
30+
assignedValue.getExpr() = instanceOfSomeStruct.getAnAssignedValue() and
31+
result = assignedValue
32+
)
33+
or
34+
// struct foo; read(..., (char *)&foo);
35+
instanceOfSomeStruct.getType() = someStruct and
36+
exists(Call externalInitializerCall, Cast castToCharPointer, int n |
37+
externalInitializerCall.getArgument(n).(AddressOfExpr).getOperand() =
38+
instanceOfSomeStruct.getAnAccess() and
39+
externalInitializerCall.getArgument(n) = castToCharPointer.getExpr() and
40+
castToCharPointer.getType().(PointerType).getBaseType().getUnspecifiedType() instanceof
41+
CharType and
42+
result = externalInitializerCall
43+
)
44+
or
45+
// the object this subject is part of is initialized and we assumes this initializes the subobject.
46+
instanceOfSomeStruct.getType() = someStruct and
47+
result = instanceOfSomeStruct.getInitializer().getExpr()
48+
)
49+
}
50+
1351
/** Gets a "use" count according to rule M0-1-4. */
1452
int getUseCount(Variable v) {
15-
exists(int initializers |
16-
// We enforce that it's a POD type variable, so if it has an initializer it is explicit
17-
(if v.hasInitializer() then initializers = 1 else initializers = 0) and
18-
result =
19-
initializers +
20-
count(VariableAccess access | access = v.getAnAccess() and not access.isCompilerGenerated())
21-
+ count(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v) +
22-
// For constexpr variables used as template arguments, we don't see accesses (just the
23-
// appropriate literals). We therefore take a conservative approach and count the number of
24-
// template instantiations that use the given constant, and consider each one to be a use
25-
// of the variable
26-
count(ClassTemplateInstantiation cti |
27-
cti.getTemplateArgument(_).(Expr).getValue() = getConstExprValue(v)
28-
)
29-
)
53+
// We enforce that it's a POD type variable, so if it has an initializer it is explicit
54+
result =
55+
count(getAUserInitializedValue(v)) +
56+
count(VariableAccess access | access = v.getAnAccess() and not access.isCompilerGenerated()) +
57+
// For constexpr variables used as template arguments, we don't see accesses (just the
58+
// appropriate literals). We therefore take a conservative approach and count the number of
59+
// template instantiations that use the given constant, and consider each one to be a use
60+
// of the variable
61+
count(ClassTemplateInstantiation cti |
62+
cti.getTemplateArgument(_).(Expr).getValue() = getConstExprValue(v)
63+
) + count(getIndirectSubObjectAssignedValue(v))
64+
}
65+
66+
Expr getAUserInitializedValue(Variable v) {
67+
(
68+
result = v.getInitializer().getExpr()
69+
or
70+
exists(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v and result = cfi.getExpr())
71+
or
72+
exists(ClassAggregateLiteral l | not l.isCompilerGenerated() | result = l.getAFieldExpr(v))
73+
) and
74+
not result.isCompilerGenerated()
3075
}
3176

3277
/** Gets a single use of `v`, if `isSingleUseNonVolatilePODVariable` holds. */

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())

cpp/autosar/test/rules/A18-0-1/CLibraryFacilitiesNotAccessedThroughCPPLibraryHeaders.expected

+1
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@
1919
| test.cpp:19:1:19:18 | #include <uchar.h> | C library "uchar.h" is included instead of the corresponding C++ library <cuchar>. |
2020
| test.cpp:20:1:20:18 | #include <wchar.h> | C library "wchar.h" is included instead of the corresponding C++ library <cwchar>. |
2121
| test.cpp:21:1:21:19 | #include <wctype.h> | C library "wctype.h" is included instead of the corresponding C++ library <cwctype>. |
22+
| test.cpp:45:1:45:17 | #include "time.h" | C library "time.h" is included instead of the corresponding C++ library <ctime>. |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#ifndef LIB_EXAMPLE_H_
2+
#define LIB_EXAMPLE_H_
3+
4+
#endif

cpp/autosar/test/rules/A18-0-1/test.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,7 @@
3939
#include <ctime> // COMPLIANT
4040
#include <cuchar> // COMPLIANT
4141
#include <cwchar> // COMPLIANT
42-
#include <cwctype> // COMPLIANT
42+
#include <cwctype> // COMPLIANT
43+
44+
#include "lib/example.h" // COMPLIANT
45+
#include "time.h" // NON_COMPLIANT

cpp/autosar/test/rules/A18-0-1/time.h

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#ifndef LIB_TIME_EXAMPLE_H_
2+
#define LIB_TIME_EXAMPLE_H_
3+
// may be a user lib or a std lib checked into a project
4+
#endif
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
| test_global_or_namespace.cpp:3:5:3:6 | g3 | Variable g3 is unused. |
2-
| test_global_or_namespace.cpp:18:4:18:4 | a | Variable a is unused. |
3-
| test_global_or_namespace.cpp:26:5:26:6 | x3 | Variable N1::x3 is unused. |
4-
| test_global_or_namespace.cpp:36:5:36:5 | a | Variable N1::a is unused. |
1+
| test_global_or_namespace.cpp:3:5:3:6 | g3 | Variable 'g3' is unused. |
2+
| test_global_or_namespace.cpp:18:4:18:4 | a | Variable 'a' is unused. |
3+
| test_global_or_namespace.cpp:26:5:26:6 | x3 | Variable 'N1::x3' is unused. |
4+
| test_global_or_namespace.cpp:36:5:36:5 | a | Variable 'N1::a' is unused. |
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
| test.cpp:7:7:7:7 | y | Local variable y in test_simple is not used. |
2-
| test.cpp:14:13:14:13 | y | Local variable y in test_const is not used. |
3-
| test.cpp:17:7:17:7 | z | Local variable z in test_const is not used. |
4-
| test.cpp:23:5:23:5 | t | Local variable t in f1 is not used. |
5-
| test.cpp:23:5:23:5 | t | Local variable t in f1 is not used. |
6-
| test.cpp:44:6:44:6 | a | Local variable a in test_side_effect_init is not used. |
1+
| test.cpp:7:7:7:7 | y | Local variable 'y' in 'test_simple' is not used. |
2+
| test.cpp:14:13:14:13 | y | Local variable 'y' in 'test_const' is not used. |
3+
| test.cpp:17:7:17:7 | z | Local variable 'z' in 'test_const' is not used. |
4+
| test.cpp:23:5:23:5 | t | Local variable 't' in 'f1' is not used. |
5+
| test.cpp:23:5:23:5 | t | Local variable 't' in 'f1' is not used. |
6+
| test.cpp:44:6:44:6 | a | Local variable 'a' in 'test_side_effect_init' is not used. |
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
| test_member.cpp:4:7:4:8 | m1 | Member variable m1 is unused. |
2-
| test_member.cpp:17:9:17:11 | pad | Member variable pad is unused. |
3-
| test_member.cpp:19:9:19:11 | sm2 | Member variable sm2 is unused. |
4-
| test_member.cpp:31:7:31:8 | m1 | Member variable m1 is unused. |
1+
| test_member.cpp:4:7:4:8 | m1 | Member variable 'm1' is unused. |
2+
| test_member.cpp:17:9:17:11 | pad | Member variable 'pad' is unused. |
3+
| test_member.cpp:19:9:19:11 | sm2 | Member variable 'sm2' is unused. |
4+
| test_member.cpp:31:7:31:8 | m1 | Member variable 'm1' is unused. |

cpp/autosar/test/rules/M0-1-3/test_global_or_namespace.cpp

+19-1
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,22 @@ void test_ns() { x2 = 1; }
4141
m1(); // ignore dead code in macros
4242
} // namespace N1
4343

44-
int test_access_variable() { return N1::x5; }
44+
int test_access_variable() { return N1::x5; }
45+
46+
template <int t> struct C1 {
47+
int array[t]; // COMPLIANT
48+
};
49+
50+
constexpr int g5 = 1; // COMPLIANT - used as template parameter
51+
52+
namespace ns1 {
53+
constexpr int m1 = 1; // COMPLIANT - used a template parameter
54+
}
55+
56+
void test_fp_reported_in_384() {
57+
struct C1<g5> l1;
58+
struct C1<ns1::m1> l2;
59+
60+
l1.array[0] = 1;
61+
l2.array[0] = 1;
62+
}

cpp/autosar/test/rules/M0-1-3/test_member.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,18 @@ void test_d() {
4747
d.getT();
4848
}
4949

50+
template <int t> struct C1 {
51+
int array[t]; // COMPLIANT
52+
};
53+
54+
struct C2 {
55+
static constexpr int m1 = 1; // COMPLIANT - used as template parameter
56+
};
57+
58+
void test_fp_reported_in_384() {
59+
struct C1<C2::m1> l1;
60+
61+
l1.array[0] = 1;
62+
}
63+
5064
} // namespace test

0 commit comments

Comments
 (0)