Skip to content

Commit 52a02f3

Browse files
committed
1 parent 02dfac9 commit 52a02f3

File tree

10 files changed

+166
-5
lines changed

10 files changed

+166
-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 UnusedFunctions::isASpecialMemberFunction(unusedFunction)
3031
select unusedFunction, "Function " + name + " is " + unusedFunction.getDeadCodeType()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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. This query specifically looks for unused Special Member
6+
* Functions.
7+
* @kind problem
8+
* @precision medium
9+
* @problem.severity warning
10+
* @tags external/autosar/id/m0-1-10
11+
* readability
12+
* maintainability
13+
* external/autosar/allocated-target/implementation
14+
* external/autosar/enforcement/automated
15+
* external/autosar/obligation/advisory
16+
*/
17+
18+
import cpp
19+
import codingstandards.cpp.autosar
20+
import codingstandards.cpp.deadcode.UnusedFunctions
21+
22+
from UnusedFunctions::UnusedSplMemberFunction unusedSplMemFunction, string name
23+
where
24+
not isExcluded(unusedSplMemFunction, DeadCodePackage::unusedFunctionQuery()) and
25+
(
26+
if exists(unusedSplMemFunction.getQualifiedName())
27+
then name = unusedSplMemFunction.getQualifiedName()
28+
else name = unusedSplMemFunction.getName()
29+
) and
30+
not unusedSplMemFunction.isDeleted()
31+
select unusedSplMemFunction,
32+
"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: 50 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,13 @@ 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+
};
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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,39 @@ class MainFunction extends MainLikeFunction {
1818
}
1919
}
2020

21+
/**
22+
* A test function from the GoogleTest infrastructure.
23+
*
24+
* Such functions can be treated as valid EntryPoint functions during analysis
25+
* of "called" or "unused" functions. It is not straightforward to identify
26+
* such functions, however, they have certain features that can be used for
27+
* identification. This can be refined based on experiments/real-world use.
28+
*/
29+
class GTestFunction extends MainLikeFunction {
30+
GTestFunction() {
31+
// A GoogleTest function is named "TestBody" and
32+
this.hasName("TestBody") and
33+
// is enclosed by a class that inherits from a base class
34+
this.getEnclosingAccessHolder() instanceof Class and
35+
exists(Class base |
36+
base = this.getEnclosingAccessHolder().(Class).getABaseClass() and
37+
(
38+
// called "Test" or
39+
exists(Class c | base.getABaseClass() = c and c.hasName("Test"))
40+
or
41+
// defined under a namespace called "testing" or
42+
exists(Namespace n | n = base.getNamespace() | n.hasName("testing"))
43+
or
44+
// is templatized by a parameter called "gtest_TypeParam_"
45+
exists(TemplateParameter tp |
46+
tp = base.getATemplateArgument() and
47+
tp.hasName("gtest_TypeParam_")
48+
)
49+
)
50+
)
51+
}
52+
}
53+
2154
/**
2255
* A "task main" function.
2356
*/

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

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ module UnusedFunctions {
7575
*/
7676

7777
private class MainLikeFunctionEntryPoint extends EntryPoint, MainLikeFunction {
78-
MainLikeFunctionEntryPoint() { this instanceof MainLikeFunction }
78+
MainLikeFunctionEntryPoint() {
79+
this instanceof MainLikeFunction or this instanceof GTestFunction
80+
}
7981

8082
override Function getAReachableFunction() { reachable*(this, result) }
8183
}
@@ -111,6 +113,26 @@ module UnusedFunctions {
111113
}
112114
}
113115

116+
/**
117+
* A `MemberFunction` which is either a Default constructor, Destructor
118+
* CopyConstructor, CopyAssingmentOperator, MoveConstructor or a
119+
* MoveAssignmentOperator
120+
*/
121+
predicate isASpecialMemberFunction(MemberFunction f) {
122+
// Default constructor
123+
f instanceof NoArgConstructor
124+
or
125+
f instanceof Destructor
126+
or
127+
f instanceof CopyConstructor
128+
or
129+
f instanceof CopyAssignmentOperator
130+
or
131+
f instanceof MoveConstructor
132+
or
133+
f instanceof MoveAssignmentOperator
134+
}
135+
114136
/**
115137
* A `Function` which is not used from an `EntryPoint`.
116138
*
@@ -119,7 +141,12 @@ module UnusedFunctions {
119141
class UnusedFunction extends UsableFunction {
120142
UnusedFunction() {
121143
// This function, or an equivalent function, is not reachable from any entry point
122-
not exists(EntryPoint ep | getAnEquivalentFunction(this) = ep.getAReachableFunction())
144+
not exists(EntryPoint ep | getAnEquivalentFunction(this) = ep.getAReachableFunction()) and
145+
// and it is not a constexpr. Refer issue #646.
146+
// The usages of constexpr is not well tracked and hence
147+
// to avoid false positives, this is added. In case there is an improvement in
148+
// handling constexpr in CodeQL, we can consider removing it.
149+
not this.isConstexpr()
123150
}
124151

125152
string getDeadCodeType() {
@@ -128,4 +155,13 @@ module UnusedFunctions {
128155
else result = "never called."
129156
}
130157
}
158+
159+
/**
160+
* A Special `MemberFunction` which is an `UnusedFunction`.
161+
*
162+
* Refer isASpecialMemberFunction predicate.
163+
*/
164+
class UnusedSplMemberFunction extends UnusedFunction {
165+
UnusedSplMemberFunction() { isASpecialMemberFunction(this) }
166+
}
131167
}

0 commit comments

Comments
 (0)