Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider additional use-cases to conclude a function as "used" #347

Merged
merged 9 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions change_notes/2023-06-28-unused-local-function-use-cases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- `A0-1-3` - Considered the following additional use cases while reporting a local function as "unused".
- The address of a function is taken
- The operand of an expression in an unevaluated context
- Functions marked with [[maybe_unused]]
- Explicitly deleted functions e.g. =delete
- Use of any overload of a function in an overload set constitute a use of all members of the set. An overload set is a set of functions with the same name that differ in the number, type and/or qualifiers of their parameters, and, for the purpose of this query, are limited to functions which are declared in the same scope (namespace or class).
28 changes: 27 additions & 1 deletion cpp/autosar/src/rules/A0-1-3/UnusedLocalFunction.ql
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ import codingstandards.cpp.autosar
import codingstandards.cpp.DynamicCallGraph
import codingstandards.cpp.deadcode.UnusedFunctions


/** Checks if an overloaded function of
* the function passed in the arguments, is called.
*/
predicate overloadedFunctionIsCalled(Function unusedFunction) {
exists (Function f | f = unusedFunction.getAnOverload() and f = getTarget(_))
}

/** Checks if a Function's address was taken. */
predicate addressBeenTaken(Function unusedFunction)
{
exists (FunctionAccess fa | fa.getTarget() = unusedFunction)
}

/** A `Function` nested in an anonymous namespace. */
class AnonymousNamespaceFunction extends Function {
AnonymousNamespaceFunction() { getNamespace().getParentNamespace*().isAnonymous() }
Expand Down Expand Up @@ -75,7 +89,19 @@ where
// There exists an instantiation which is called
functionFromInstantiatedTemplate.isConstructedFrom(functionFromUninstantiatedTemplate) and
functionFromInstantiatedTemplate = getTarget(_)
) and
)
and
// A function is defined as "used" if any one of the following holds true:
// - It's an explicitly deleted functions e.g. =delete
// - It's annotated as "[[maybe_unused]]"
// - It's part of an overloaded set and any one of the overloaded instance
// is called.
// - It's an operand of an expression in an unevaluated context.
not unusedLocalFunction.isDeleted() and
not unusedLocalFunction.getAnAttribute().getName() = "maybe_unused" and
not overloadedFunctionIsCalled(unusedLocalFunction) and
not addressBeenTaken(unusedLocalFunction)
and
// Get a printable name
(
if exists(unusedLocalFunction.getQualifiedName())
Expand Down
53 changes: 52 additions & 1 deletion cpp/autosar/test/rules/A0-1-3/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,55 @@ namespace bar {
void h3() {} // NON_COMPLIANT
} // namespace bar
} // namespace foo
} // namespace
} // namespace

static int unevaluatedContextFn(int x) {
x++;
return x;
} // COMPLIANT - called in an unevaluated context.
#include <typeinfo>
static int unevalContextCaller() // COMPLIANT - address taken
{

typeid(unevaluatedContextFn(0));
sizeof(unevaluatedContextFn(1));
noexcept(unevaluatedContextFn(2));
decltype(unevaluatedContextFn(2)) n = 42;
return 0;
}
int (*ptr_unevalContextCaller)(void) = unevalContextCaller;

class X {
private:
[[maybe_unused]] void maybeUnused();
void deleted() = delete; // COMPLIANT - Deleted Function
};

void X::maybeUnused() {} // COMPLIANT - [[maybe_unused]]

static int overload1(int c) // COMPLIANT - called
{
return ++c;
}

static int overload1(int c, int d) // COMPLIANT - overload1(int) is called.
{
return c + d;
}

int overload = overload1(5);

class classWithOverloads {
public:
int caller(int x) { return overloadMember(x, 0); }

private:
int overloadMember(int c) // COMPLIANT - overloadMember(int, int) is called.
{
return ++c;
}
int overloadMember(int c, int d) // COMPLIANT - called.
{
return c + d;
}
};
5 changes: 4 additions & 1 deletion rule_packages/cpp/DeadCode.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@
"tags": [
"readability",
"maintainability"
]
],
"implementation_scope": {
"description": "Use of any overload of a function in an overload set constitute a use of all members of the set. An overload set is a set of functions with the same name that differ in the number, type and/or qualifiers of their parameters, and, for the purpose of this query, are limited to functions which are declared in the same scope (namespace or class). Functions defined in anonymous (unnamed) namespaces and global namespaces are therefore not currently considered to be part of the same overload set."
}
}
],
"title": "Every function defined in an anonymous namespace, or static function with internal linkage, or private member function shall be used."
Expand Down