Skip to content

Commit f1417b0

Browse files
Implement Rule 0.2.4, unused functions with limited visibility.
1 parent b007dcd commit f1417b0

File tree

15 files changed

+351
-90
lines changed

15 files changed

+351
-90
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- `A0-1-3` - `UnusedLocalFunction.ql`:
2+
- Query now reports unused public members of classes in anonymous namespaces, which have internal linkage.

cpp/autosar/src/rules/A0-1-3/UnusedLocalFunction.ql

Lines changed: 4 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -16,95 +16,10 @@
1616

1717
import cpp
1818
import codingstandards.cpp.autosar
19-
import codingstandards.cpp.DynamicCallGraph
20-
import codingstandards.cpp.deadcode.UnusedFunctions
19+
import codingstandards.cpp.rules.unusedlocalfunction.UnusedLocalFunction
2120

22-
/**
23-
* Checks if an overloaded function of
24-
* the function passed in the arguments, is called.
25-
*/
26-
predicate overloadedFunctionIsCalled(Function unusedFunction) {
27-
exists(Function f | f = unusedFunction.getAnOverload() and f = getTarget(_))
28-
}
29-
30-
/** Checks if a Function's address was taken. */
31-
predicate addressBeenTaken(Function unusedFunction) {
32-
exists(FunctionAccess fa | fa.getTarget() = unusedFunction)
33-
}
34-
35-
/** A `Function` nested in an anonymous namespace. */
36-
class AnonymousNamespaceFunction extends Function {
37-
AnonymousNamespaceFunction() { getNamespace().getParentNamespace*().isAnonymous() }
38-
}
39-
40-
/**
41-
* A function which is "local" to a particular scope or translation unit.
42-
*/
43-
class LocalFunction extends UnusedFunctions::UsableFunction {
44-
string localFunctionType;
45-
46-
LocalFunction() {
47-
this.(MemberFunction).isPrivate() and
48-
localFunctionType = "Private member"
49-
or
50-
// A function in an anonymous namespace (which is deduced to have internal linkage)
51-
this instanceof AnonymousNamespaceFunction and
52-
// Not member functions, which don't have internal linkage
53-
not this instanceof MemberFunction and
54-
localFunctionType = "Anonymous namespace"
55-
or
56-
// Static functions with internal linkage
57-
this.isStatic() and
58-
// Member functions never have internal linkage
59-
not this instanceof MemberFunction and
60-
// Functions in anonymous namespaces automatically have the "static" specifier added by the
61-
// extractor. We therefore excluded them from this case, and instead report them in the
62-
// anonymous namespace, as we don't know whether the "static" specifier was explicitly
63-
// provided by the user.
64-
not this instanceof AnonymousNamespaceFunction and
65-
localFunctionType = "Static"
66-
}
67-
68-
/** Gets the type of local function. */
69-
string getLocalFunctionType() { result = localFunctionType }
21+
module UnusedLocalFunctionConfig implements UnusedLocalFunctionConfigSig {
22+
Query getQuery() { result = DeadCodePackage::unusedLocalFunctionQuery() }
7023
}
7124

72-
from LocalFunction unusedLocalFunction, string name
73-
where
74-
not isExcluded(unusedLocalFunction, DeadCodePackage::unusedLocalFunctionQuery()) and
75-
// No static or dynamic call target for this function
76-
not unusedLocalFunction = getTarget(_) and
77-
// If this is a TemplateFunction or an instantiation of a template, then only report it as unused
78-
// if all other instantiations of the template are unused
79-
not exists(
80-
Function functionFromUninstantiatedTemplate, Function functionFromInstantiatedTemplate
81-
|
82-
// `unusedLocalFunction` is a template instantiation from `functionFromUninstantiatedTemplate`
83-
unusedLocalFunction.isConstructedFrom(functionFromUninstantiatedTemplate)
84-
or
85-
// `unusedLocalFunction` is from an uninstantiated template
86-
unusedLocalFunction = functionFromUninstantiatedTemplate
87-
|
88-
// There exists an instantiation which is called
89-
functionFromInstantiatedTemplate.isConstructedFrom(functionFromUninstantiatedTemplate) and
90-
functionFromInstantiatedTemplate = getTarget(_)
91-
) and
92-
// A function is defined as "used" if any one of the following holds true:
93-
// - It's an explicitly deleted functions e.g. =delete
94-
// - It's annotated as "[[maybe_unused]]"
95-
// - It's part of an overloaded set and any one of the overloaded instance
96-
// is called.
97-
// - It's an operand of an expression in an unevaluated context.
98-
not unusedLocalFunction.isDeleted() and
99-
not unusedLocalFunction.getAnAttribute().getName() = "maybe_unused" and
100-
not overloadedFunctionIsCalled(unusedLocalFunction) and
101-
not addressBeenTaken(unusedLocalFunction) and
102-
// Get a printable name
103-
(
104-
if exists(unusedLocalFunction.getQualifiedName())
105-
then name = unusedLocalFunction.getQualifiedName()
106-
else name = unusedLocalFunction.getName()
107-
)
108-
select unusedLocalFunction,
109-
unusedLocalFunction.getLocalFunctionType() + " function " + name +
110-
" is not statically called, or is in an unused template."
25+
import UnusedLocalFunction<UnusedLocalFunctionConfig>
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* @id cpp/autosar/unused-local-function
3+
* @name A0-1-3: Unused local function
4+
* @description Every function defined in an anonymous namespace, or static function with internal
5+
* linkage, or private member function shall be used.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity warning
9+
* @tags external/autosar/id/a0-1-3
10+
* readability
11+
* maintainability
12+
* external/autosar/allocated-target/implementation
13+
* external/autosar/enforcement/automated
14+
* external/autosar/obligation/required
15+
*/
16+
17+
import cpp
18+
import codingstandards.cpp.autosar
19+
import codingstandards.cpp.DynamicCallGraph
20+
import codingstandards.cpp.deadcode.UnusedFunctions
21+
22+
/**
23+
* Checks if an overloaded function of
24+
* the function passed in the arguments, is called.
25+
*/
26+
predicate overloadedFunctionIsCalled(Function unusedFunction) {
27+
exists(Function f | f = unusedFunction.getAnOverload() and f = getTarget(_))
28+
}
29+
30+
/** Checks if a Function's address was taken. */
31+
predicate addressBeenTaken(Function unusedFunction) {
32+
exists(FunctionAccess fa | fa.getTarget() = unusedFunction)
33+
}
34+
35+
/** A `Function` nested in an anonymous namespace. */
36+
class AnonymousNamespaceFunction extends Function {
37+
AnonymousNamespaceFunction() { getNamespace().getParentNamespace*().isAnonymous() }
38+
}
39+
40+
/**
41+
* A function which is "local" to a particular scope or translation unit.
42+
*/
43+
class LocalFunction extends UnusedFunctions::UsableFunction {
44+
string localFunctionType;
45+
46+
LocalFunction() {
47+
this.(MemberFunction).isPrivate() and
48+
localFunctionType = "Private member"
49+
or
50+
// A function in an anonymous namespace (which is deduced to have internal linkage)
51+
this instanceof AnonymousNamespaceFunction and
52+
// Not member functions, which don't have internal linkage
53+
not this instanceof MemberFunction and
54+
localFunctionType = "Anonymous namespace"
55+
or
56+
// Static functions with internal linkage
57+
this.isStatic() and
58+
// Member functions never have internal linkage
59+
not this instanceof MemberFunction and
60+
// Functions in anonymous namespaces automatically have the "static" specifier added by the
61+
// extractor. We therefore excluded them from this case, and instead report them in the
62+
// anonymous namespace, as we don't know whether the "static" specifier was explicitly
63+
// provided by the user.
64+
not this instanceof AnonymousNamespaceFunction and
65+
localFunctionType = "Static"
66+
}
67+
68+
/** Gets the type of local function. */
69+
string getLocalFunctionType() { result = localFunctionType }
70+
}
71+
72+
from LocalFunction unusedLocalFunction, string name
73+
where
74+
not isExcluded(unusedLocalFunction, DeadCodePackage::unusedLocalFunctionQuery()) and
75+
// No static or dynamic call target for this function
76+
not unusedLocalFunction = getTarget(_) and
77+
// If this is a TemplateFunction or an instantiation of a template, then only report it as unused
78+
// if all other instantiations of the template are unused
79+
not exists(
80+
Function functionFromUninstantiatedTemplate, Function functionFromInstantiatedTemplate
81+
|
82+
// `unusedLocalFunction` is a template instantiation from `functionFromUninstantiatedTemplate`
83+
unusedLocalFunction.isConstructedFrom(functionFromUninstantiatedTemplate)
84+
or
85+
// `unusedLocalFunction` is from an uninstantiated template
86+
unusedLocalFunction = functionFromUninstantiatedTemplate
87+
|
88+
// There exists an instantiation which is called
89+
functionFromInstantiatedTemplate.isConstructedFrom(functionFromUninstantiatedTemplate) and
90+
functionFromInstantiatedTemplate = getTarget(_)
91+
) and
92+
// A function is defined as "used" if any one of the following holds true:
93+
// - It's an explicitly deleted functions e.g. =delete
94+
// - It's annotated as "[[maybe_unused]]"
95+
// - It's part of an overloaded set and any one of the overloaded instance
96+
// is called.
97+
// - It's an operand of an expression in an unevaluated context.
98+
not unusedLocalFunction.isDeleted() and
99+
not unusedLocalFunction.getAnAttribute().getName() = "maybe_unused" and
100+
not overloadedFunctionIsCalled(unusedLocalFunction) and
101+
not addressBeenTaken(unusedLocalFunction) and
102+
// Get a printable name
103+
(
104+
if exists(unusedLocalFunction.getQualifiedName())
105+
then name = unusedLocalFunction.getQualifiedName()
106+
else name = unusedLocalFunction.getName()
107+
)
108+
select unusedLocalFunction,
109+
unusedLocalFunction.getLocalFunctionType() + " function " + name +
110+
" is not statically called, or is in an unused template."
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cpp/common/test/rules/unusedlocalfunction/UnusedLocalFunction.ql
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype DeadCode10Query = TUnusedLimitedVisibilityFunctionQuery()
7+
8+
predicate isDeadCode10QueryMetadata(Query query, string queryId, string ruleId, string category) {
9+
query =
10+
// `Query` instance for the `unusedLimitedVisibilityFunction` query
11+
DeadCode10Package::unusedLimitedVisibilityFunctionQuery() and
12+
queryId =
13+
// `@id` for the `unusedLimitedVisibilityFunction` query
14+
"cpp/misra/unused-limited-visibility-function" and
15+
ruleId = "RULE-0-2-4" and
16+
category = "advisory"
17+
}
18+
19+
module DeadCode10Package {
20+
Query unusedLimitedVisibilityFunctionQuery() {
21+
//autogenerate `Query` type
22+
result =
23+
// `Query` type for `unusedLimitedVisibilityFunction` query
24+
TQueryCPP(TDeadCode10PackageQuery(TUnusedLimitedVisibilityFunctionQuery()))
25+
}
26+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Const
1717
import Conversions
1818
import Conversions2
1919
import DeadCode
20+
import DeadCode10
2021
import DeadCode3
2122
import DeadCode4
2223
import Declarations
@@ -89,6 +90,7 @@ newtype TCPPQuery =
8990
TConversionsPackageQuery(ConversionsQuery q) or
9091
TConversions2PackageQuery(Conversions2Query q) or
9192
TDeadCodePackageQuery(DeadCodeQuery q) or
93+
TDeadCode10PackageQuery(DeadCode10Query q) or
9294
TDeadCode3PackageQuery(DeadCode3Query q) or
9395
TDeadCode4PackageQuery(DeadCode4Query q) or
9496
TDeclarationsPackageQuery(DeclarationsQuery q) or
@@ -161,6 +163,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
161163
isConversionsQueryMetadata(query, queryId, ruleId, category) or
162164
isConversions2QueryMetadata(query, queryId, ruleId, category) or
163165
isDeadCodeQueryMetadata(query, queryId, ruleId, category) or
166+
isDeadCode10QueryMetadata(query, queryId, ruleId, category) or
164167
isDeadCode3QueryMetadata(query, queryId, ruleId, category) or
165168
isDeadCode4QueryMetadata(query, queryId, ruleId, category) or
166169
isDeclarationsQueryMetadata(query, queryId, ruleId, category) or
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* Provides a configurable module UnusedLocalFunction with a `problems` predicate
3+
* for the following issue:
4+
* Unused functions may indicate a coding error or require maintenance; functions that
5+
* are unused with certain visibility have no effect on the program and should be
6+
* removed.
7+
*/
8+
9+
import cpp
10+
import codingstandards.cpp.Customizations
11+
import codingstandards.cpp.Exclusions
12+
import codingstandards.cpp.DynamicCallGraph
13+
import codingstandards.cpp.deadcode.UnusedFunctions
14+
15+
/**
16+
* Checks if an overloaded function of
17+
* the function passed in the arguments, is called.
18+
*/
19+
predicate overloadedFunctionIsCalled(Function unusedFunction) {
20+
exists(Function f | f = unusedFunction.getAnOverload() and f = getTarget(_))
21+
}
22+
23+
/** Checks if a Function's address was taken. */
24+
predicate addressBeenTaken(Function unusedFunction) {
25+
exists(FunctionAccess fa | fa.getTarget() = unusedFunction)
26+
}
27+
28+
/** A `Function` nested in an anonymous namespace. */
29+
class AnonymousNamespaceFunction extends Function {
30+
AnonymousNamespaceFunction() { getNamespace().getParentNamespace*().isAnonymous() }
31+
}
32+
33+
/**
34+
* A function which is "local" to a particular scope or translation unit.
35+
*/
36+
class LocalFunction extends UnusedFunctions::UsableFunction {
37+
string localFunctionType;
38+
39+
LocalFunction() {
40+
this.(MemberFunction).isPrivate() and
41+
localFunctionType = "Private member"
42+
or
43+
// A function in an anonymous namespace (which is deduced to have internal linkage)
44+
this instanceof AnonymousNamespaceFunction and
45+
not this instanceof MemberFunction and
46+
localFunctionType = "Anonymous namespace"
47+
or
48+
// Class members in anonymous namespaces also have internal linkage.
49+
this instanceof AnonymousNamespaceFunction and
50+
this instanceof MemberFunction and
51+
localFunctionType = "Anonymous namespace class member"
52+
or
53+
// Static functions with internal linkage
54+
this.isStatic() and
55+
// Member functions never have internal linkage
56+
not this instanceof MemberFunction and
57+
// Functions in anonymous namespaces automatically have the "static" specifier added by the
58+
// extractor. We therefore excluded them from this case, and instead report them in the
59+
// anonymous namespace, as we don't know whether the "static" specifier was explicitly
60+
// provided by the user.
61+
not this instanceof AnonymousNamespaceFunction and
62+
localFunctionType = "Static"
63+
}
64+
65+
/** Gets the type of local function. */
66+
string getLocalFunctionType() { result = localFunctionType }
67+
}
68+
69+
signature module UnusedLocalFunctionConfigSig {
70+
Query getQuery();
71+
}
72+
73+
module UnusedLocalFunction<UnusedLocalFunctionConfigSig Config> {
74+
query predicate problems(LocalFunction unusedLocalFunction, string message) {
75+
exists(string name |
76+
not isExcluded(unusedLocalFunction, Config::getQuery()) and
77+
// No static or dynamic call target for this function
78+
not unusedLocalFunction = getTarget(_) and
79+
// If this is a TemplateFunction or an instantiation of a template, then only report it as unused
80+
// if all other instantiations of the template are unused
81+
not exists(
82+
Function functionFromUninstantiatedTemplate, Function functionFromInstantiatedTemplate
83+
|
84+
// `unusedLocalFunction` is a template instantiation from `functionFromUninstantiatedTemplate`
85+
unusedLocalFunction.isConstructedFrom(functionFromUninstantiatedTemplate)
86+
or
87+
// `unusedLocalFunction` is from an uninstantiated template
88+
unusedLocalFunction = functionFromUninstantiatedTemplate
89+
|
90+
// There exists an instantiation which is called
91+
functionFromInstantiatedTemplate.isConstructedFrom(functionFromUninstantiatedTemplate) and
92+
functionFromInstantiatedTemplate = getTarget(_)
93+
) and
94+
// A function is defined as "used" if any one of the following holds true:
95+
// - It's an explicitly deleted functions e.g. =delete
96+
// - It's annotated as "[[maybe_unused]]"
97+
// - It's part of an overloaded set and any one of the overloaded instance
98+
// is called.
99+
// - It's an operand of an expression in an unevaluated context.
100+
not unusedLocalFunction.isDeleted() and
101+
not unusedLocalFunction.getAnAttribute().getName() = "maybe_unused" and
102+
not overloadedFunctionIsCalled(unusedLocalFunction) and
103+
not addressBeenTaken(unusedLocalFunction) and
104+
// Get a printable name
105+
(
106+
if exists(unusedLocalFunction.getQualifiedName())
107+
then name = unusedLocalFunction.getQualifiedName()
108+
else name = unusedLocalFunction.getName()
109+
) and
110+
message =
111+
unusedLocalFunction.getLocalFunctionType() + " function " + name +
112+
" is not statically called, or is in an unused template."
113+
)
114+
}
115+
}

cpp/autosar/test/rules/A0-1-3/UnusedLocalFunction.expected renamed to cpp/common/test/rules/unusedlocalfunction/UnusedLocalFunction.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@
44
| test.cpp:60:5:60:9 | getAT | Private member function C<T>::getAT is not statically called, or is in an unused template. |
55
| test.cpp:81:6:81:7 | h2 | Anonymous namespace function (unnamed namespace)::h2 is not statically called, or is in an unused template. |
66
| test.cpp:85:6:85:7 | h3 | Anonymous namespace function (unnamed namespace)::foo::bar::h3 is not statically called, or is in an unused template. |
7+
| test.cpp:144:8:144:8 | f | Anonymous namespace class member function (unnamed namespace)::C1::f is not statically called, or is in an unused template. |
8+
| test.cpp:150:8:150:8 | f | Anonymous namespace class member function (unnamed namespace)::named::C2::f is not statically called, or is in an unused template. |
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// GENERATED FILE - DO NOT MODIFY
2+
import codingstandards.cpp.rules.unusedlocalfunction.UnusedLocalFunction
3+
4+
module TestFileConfig implements UnusedLocalFunctionConfigSig {
5+
Query getQuery() { result instanceof TestQuery }
6+
}
7+
8+
import UnusedLocalFunction<TestFileConfig>

0 commit comments

Comments
 (0)