Skip to content

Commit 20a3711

Browse files
authored
Merge pull request #725 from rak3-sh/rp/m0-1-10-711
Fix false positives of M0-1-10 (#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
}

cpp/common/src/codingstandards/cpp/exclusions/cpp/DeadCode.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ newtype DeadCodeQuery =
1212
TUnusedTypeDeclarationsQuery() or
1313
TUnreachableCodeQuery() or
1414
TUnusedFunctionQuery() or
15+
TUnusedSplMemberFunctionQuery() or
1516
TInfeasiblePathQuery() or
1617
TUnusedLocalVariableQuery() or
1718
TUnusedGlobalOrNamespaceVariableQuery() or
@@ -94,6 +95,15 @@ predicate isDeadCodeQueryMetadata(Query query, string queryId, string ruleId, st
9495
ruleId = "M0-1-10" and
9596
category = "advisory"
9697
or
98+
query =
99+
// `Query` instance for the `unusedSplMemberFunction` query
100+
DeadCodePackage::unusedSplMemberFunctionQuery() and
101+
queryId =
102+
// `@id` for the `unusedSplMemberFunction` query
103+
"cpp/autosar/unused-spl-member-function" and
104+
ruleId = "M0-1-10" and
105+
category = "advisory"
106+
or
97107
query =
98108
// `Query` instance for the `infeasiblePath` query
99109
DeadCodePackage::infeasiblePathQuery() and
@@ -224,6 +234,13 @@ module DeadCodePackage {
224234
TQueryCPP(TDeadCodePackageQuery(TUnusedFunctionQuery()))
225235
}
226236

237+
Query unusedSplMemberFunctionQuery() {
238+
//autogenerate `Query` type
239+
result =
240+
// `Query` type for `unusedSplMemberFunction` query
241+
TQueryCPP(TDeadCodePackageQuery(TUnusedSplMemberFunctionQuery()))
242+
}
243+
227244
Query infeasiblePathQuery() {
228245
//autogenerate `Query` type
229246
result =
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
2+
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
3+
4+
#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
5+
test_suite_name##_##test_name##_Test
6+
7+
#define GTEST_TEST_(test_suite_name, test_name, parent_class) \
8+
class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
9+
: public parent_class { \
10+
public: \
11+
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \
12+
~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \
13+
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
14+
(const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete; \
15+
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \
16+
const GTEST_TEST_CLASS_NAME_(test_suite_name, \
17+
test_name) &) = delete; /* NOLINT */ \
18+
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
19+
(GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) noexcept = delete; \
20+
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \
21+
GTEST_TEST_CLASS_NAME_(test_suite_name, \
22+
test_name) &&) noexcept = delete; /* NOLINT */ \
23+
\
24+
private: \
25+
void TestBody() override; \
26+
}; \
27+
void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() \
28+
29+
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_H_
2+
#define GOOGLETEST_INCLUDE_GTEST_GTEST_H_
3+
4+
#include "gtest/gtest-internal.h"
5+
6+
namespace testing {
7+
8+
class Test
9+
{
10+
public:
11+
virtual ~Test();
12+
protected:
13+
// Creates a Test object.
14+
Test();
15+
private:
16+
virtual void TestBody() = 0;
17+
Test(const Test&) = delete;
18+
Test& operator=(const Test&) = delete;
19+
};
20+
21+
#define GTEST_TEST(test_suite_name, test_name) \
22+
GTEST_TEST_(test_suite_name, test_name, ::testing::Test)
23+
24+
#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)
25+
26+
} // namespace testing
27+
28+
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_H_

cpp/options

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
semmle-extractor-options:--clang -std=c++14 -nostdinc++ -I../../../../common/test/includes/standard-library -I../../../../common/test/includes/custom-library
1+
semmle-extractor-options:--clang -std=c++14 -nostdinc++ -I../../../../common/test/includes/standard-library -I../../../../common/test/includes/custom-library

rule_packages/cpp/DeadCode.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,21 @@
194194
"readability",
195195
"maintainability"
196196
]
197+
},
198+
{
199+
"description": "Uncalled functions complicate the program and can indicate a possible mistake on the part of the programmer.",
200+
"kind": "problem",
201+
"name": "Every defined function should be called at least once",
202+
"precision": "medium",
203+
"severity": "warning",
204+
"short_name": "UnusedSplMemberFunction",
205+
"tags": [
206+
"readability",
207+
"maintainability"
208+
],
209+
"implementation_scope": {
210+
"description": "In limited cases, this query can raise false-positives for special member function calls invoked from the C++ Metaprogramming library."
211+
}
197212
}
198213
],
199214
"title": "Every defined function should be called at least once."

0 commit comments

Comments
 (0)