Skip to content

Commit 20a3711

Browse files
authored
Merge pull request github#725 from rak3-sh/rp/m0-1-10-711
Fix false positives of M0-1-10 (github#711)
2 parents dcab086 + a2943f6 commit 20a3711

File tree

15 files changed

+250
-5
lines changed

15 files changed

+250
-5
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- `M0-1-10` - `UnusedFunction.ql`:
2+
- Fixes #711. Excludes constexpr functions, considers functions from GoogleTest as an EntryPoint and does not consider special member functions. Another query called UnusedSplMemberFunction.ql is created that reports unused special member functions. This is done so as to enable deviations to be applied to this case.

cpp/autosar/src/rules/M0-1-10/UnusedFunction.ql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ where
2626
then name = unusedFunction.getQualifiedName()
2727
else name = unusedFunction.getName()
2828
) and
29-
not unusedFunction.isDeleted()
29+
not unusedFunction.isDeleted() and
30+
not unusedFunction instanceof SpecialMemberFunction
3031
select unusedFunction, "Function " + name + " is " + unusedFunction.getDeadCodeType()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @id cpp/autosar/unused-spl-member-function
3+
* @name M0-1-10: Every defined function should be called at least once
4+
* @description Uncalled functions complicate the program and can indicate a possible mistake on the
5+
* part of the programmer.
6+
* @kind problem
7+
* @precision medium
8+
* @problem.severity warning
9+
* @tags external/autosar/id/m0-1-10
10+
* readability
11+
* maintainability
12+
* external/autosar/allocated-target/implementation
13+
* external/autosar/enforcement/automated
14+
* external/autosar/obligation/advisory
15+
*/
16+
17+
import cpp
18+
import codingstandards.cpp.autosar
19+
import codingstandards.cpp.deadcode.UnusedFunctions
20+
21+
from UnusedFunctions::UnusedSplMemberFunction unusedSplMemFunction, string name
22+
where
23+
not isExcluded(unusedSplMemFunction, DeadCodePackage::unusedFunctionQuery()) and
24+
(
25+
if exists(unusedSplMemFunction.getQualifiedName())
26+
then name = unusedSplMemFunction.getQualifiedName()
27+
else name = unusedSplMemFunction.getName()
28+
) and
29+
not unusedSplMemFunction.isDeleted()
30+
select unusedSplMemFunction,
31+
"Special member function " + name + " is " + unusedSplMemFunction.getDeadCodeType()

cpp/autosar/test/rules/M0-1-10/UnusedFunction.expected

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010
| test.cpp:50:5:50:6 | i3 | Function C<T>::i3 is never called. |
1111
| test.cpp:51:8:51:9 | i4 | Function C<T>::i4 is never called. |
1212
| test.cpp:52:15:52:16 | i5 | Function C<T>::i5 is never called. |
13-
| test.cpp:69:17:69:18 | g4 | Function g4 is never called. |
13+
| test.cpp:79:6:79:21 | anUnusedFunction | Function anUnusedFunction is never called. |
14+
| test.cpp:113:17:113:18 | g4 | Function g4 is never called. |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| test.cpp:71:5:71:16 | ANestedClass | Special member function ANestedClass is never called. |
2+
| test.cpp:82:5:82:22 | AnotherNestedClass | Special member function AnotherNestedClass is never called from a main function or entry point. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/M0-1-10/UnusedSplMemberFunction.ql

cpp/autosar/test/rules/M0-1-10/test.cpp

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,50 @@ template <class T> class C {
5252
inline void i5() {} // NON_COMPLIANT - never used in any instantiation
5353
};
5454

55+
#include "test.hpp"
56+
#include <type_traits>
57+
58+
template <typename T1, typename T2>
59+
constexpr bool aConstExprFunc() noexcept { // COMPLIANT
60+
static_assert(std::is_trivially_copy_constructible<T1>() &&
61+
std::is_trivially_copy_constructible<T2>(),
62+
"assert");
63+
return true;
64+
}
65+
66+
template <typename T, int val> class AClass { T anArr[val]; };
67+
68+
void aCalledFunc1() // COMPLIANT
69+
{
70+
struct ANestedClass {
71+
ANestedClass() noexcept(false) { // COMPLIANT: False Positive!
72+
static_cast<void>(0);
73+
}
74+
};
75+
static_assert(std::is_trivially_copy_constructible<AClass<ANestedClass, 5>>(),
76+
"Must be trivially copy constructible");
77+
}
78+
79+
void anUnusedFunction() // NON_COMPLIANT
80+
{
81+
struct AnotherNestedClass {
82+
AnotherNestedClass() noexcept(false) { // NON_COMPLAINT
83+
static_cast<void>(0);
84+
}
85+
};
86+
AnotherNestedClass d;
87+
}
88+
89+
void aCalledFunc2() // COMPLIANT
90+
{
91+
struct YetAnotherNestedClass {
92+
YetAnotherNestedClass() noexcept(false) {
93+
static_cast<void>(0);
94+
} // COMPLIANT
95+
};
96+
YetAnotherNestedClass d;
97+
};
98+
5599
int main() { // COMPLIANT - this is a main like function which acts as an entry
56100
// point
57101
f3();
@@ -88,8 +132,37 @@ int main() { // COMPLIANT - this is a main like function which acts as an entry
88132
c1.getAT();
89133
S s;
90134
c2.i1(s);
135+
136+
int aVar;
137+
aConstExprFunc<decltype(aCalledFuncInHeader(aVar)), int>();
138+
aCalledFunc1();
139+
aCalledFunc2();
91140
}
92141
class M {
93142
public:
94143
M(const M &) = delete; // COMPLIANT - ignore if deleted
95-
};
144+
};
145+
146+
#include <gtest/gtest.h>
147+
int called_from_google_test_function(
148+
int a_param) // COMPLIANT - called from TEST
149+
{
150+
int something = a_param;
151+
something++;
152+
return something;
153+
}
154+
155+
TEST(sample_test,
156+
called_from_google_test_function) // COMPLIANT - Google Test function
157+
{
158+
bool pass = false;
159+
if (called_from_google_test_function(0) >= 10)
160+
pass = true;
161+
struct a_nested_class_in_gtest {
162+
a_nested_class_in_gtest() noexcept(false) {
163+
static_cast<void>(0);
164+
} // COMPLIANT
165+
};
166+
static_assert(std::is_trivially_copy_constructible<a_nested_class_in_gtest>(),
167+
"assert");
168+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
template <class T>
2+
constexpr T aCalledFuncInHeader(T value) noexcept { // COMPLIANT
3+
return static_cast<T>(value);
4+
}

cpp/common/src/codingstandards/cpp/EncapsulatingFunctions.qll

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import cpp
6+
import codingstandards.cpp.Class
67

78
/** A function which represents the entry point into a specific thread of execution in the program. */
89
abstract class MainLikeFunction extends Function { }
@@ -18,6 +19,37 @@ class MainFunction extends MainLikeFunction {
1819
}
1920
}
2021

22+
/**
23+
* A test function from the GoogleTest infrastructure.
24+
*
25+
* Such functions can be treated as valid EntryPoint functions during analysis
26+
* of "called" or "unused" functions. It is not straightforward to identify
27+
* such functions, however, they have certain features that can be used for
28+
* identification. This can be refined based on experiments/real-world use.
29+
*/
30+
class GoogleTestFunction extends MainLikeFunction {
31+
GoogleTestFunction() {
32+
// A GoogleTest function is named "TestBody" and
33+
(
34+
this.hasName("TestBody") or
35+
this instanceof SpecialMemberFunction
36+
) and
37+
// it's parent class inherits a base class
38+
exists(Class base |
39+
base = this.getEnclosingAccessHolder+().(Class).getABaseClass+() and
40+
(
41+
// with a name "Test" inside a namespace called "testing"
42+
base.hasName("Test") and
43+
base.getNamespace().hasName("testing")
44+
or
45+
// or at a location in a file called gtest.h (or gtest-internal.h,
46+
// gtest-typed-test.h etc).
47+
base.getDefinitionLocation().getFile().getBaseName().regexpMatch("gtest*.h")
48+
)
49+
)
50+
}
51+
}
52+
2153
/**
2254
* A "task main" function.
2355
*/

cpp/common/src/codingstandards/cpp/deadcode/UnusedFunctions.qll

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import cpp
1313
import codingstandards.cpp.DynamicCallGraph
1414
import codingstandards.cpp.EncapsulatingFunctions
1515
import codingstandards.cpp.FunctionEquivalence
16+
import codingstandards.cpp.Class
1617

1718
module UnusedFunctions {
1819
/**
@@ -119,7 +120,12 @@ module UnusedFunctions {
119120
class UnusedFunction extends UsableFunction {
120121
UnusedFunction() {
121122
// This function, or an equivalent function, is not reachable from any entry point
122-
not exists(EntryPoint ep | getAnEquivalentFunction(this) = ep.getAReachableFunction())
123+
not exists(EntryPoint ep | getAnEquivalentFunction(this) = ep.getAReachableFunction()) and
124+
// and it is not a constexpr. Refer issue #646.
125+
// The usages of constexpr is not well tracked and hence
126+
// to avoid false positives, this is added. In case there is an improvement in
127+
// handling constexpr in CodeQL, we can consider removing it.
128+
not this.isConstexpr()
123129
}
124130

125131
string getDeadCodeType() {
@@ -128,4 +134,7 @@ module UnusedFunctions {
128134
else result = "never called."
129135
}
130136
}
137+
138+
/** A `SpecialMemberFunction` which is an `UnusedFunction`. */
139+
class UnusedSplMemberFunction extends UnusedFunction, SpecialMemberFunction { }
131140
}

0 commit comments

Comments
 (0)