diff --git a/cpp/autosar/src/rules/A0-1-2/UnusedReturnValue.ql b/cpp/autosar/src/rules/A0-1-2/UnusedReturnValue.ql index 8e8a91ae5a..891a44ed2a 100644 --- a/cpp/autosar/src/rules/A0-1-2/UnusedReturnValue.ql +++ b/cpp/autosar/src/rules/A0-1-2/UnusedReturnValue.ql @@ -16,7 +16,8 @@ import cpp import codingstandards.cpp.autosar -import semmle.code.cpp.dataflow.DataFlow +import codingstandards.cpp.Operator +import cpp /* * This query performs a simple syntactic check to ensure that the return value of the function is @@ -39,8 +40,11 @@ where // so the rule does not require the use of the return value not f instanceof Operator and // Exclude cases where the function call is generated within a macro, as the user of the macro is - // not necessarily able to address thoes results + // not necessarily able to address those results not fc.isAffectedByMacro() and - // Rule allows disabling this rule where a static_cast is applied - not fc.getExplicitlyConverted().(StaticCast).getActualType() instanceof VoidType + // Rule allows disabling this rule where a static_cast or a C-style cast to void is applied + not exists(Cast cast | cast instanceof StaticCast or cast instanceof CStyleCast | + fc.getExplicitlyConverted() = cast and + cast.getActualType() instanceof VoidType + ) select fc, "Return value from call to $@ is unused.", f, f.getName() diff --git a/cpp/autosar/test/rules/A0-1-2/UnusedReturnValue.expected b/cpp/autosar/test/rules/A0-1-2/UnusedReturnValue.expected index d24d36e070..480b6d75a3 100644 --- a/cpp/autosar/test/rules/A0-1-2/UnusedReturnValue.expected +++ b/cpp/autosar/test/rules/A0-1-2/UnusedReturnValue.expected @@ -1 +1 @@ -| test.cpp:10:3:10:3 | call to f | Return value from call to $@ is unused. | test.cpp:1:5:1:5 | f | f | +| test.cpp:12:3:12:3 | call to f | Return value from call to $@ is unused. | test.cpp:3:5:3:5 | f | f | \ No newline at end of file diff --git a/cpp/autosar/test/rules/A0-1-2/test.cpp b/cpp/autosar/test/rules/A0-1-2/test.cpp index 9990687bf4..2be7122128 100644 --- a/cpp/autosar/test/rules/A0-1-2/test.cpp +++ b/cpp/autosar/test/rules/A0-1-2/test.cpp @@ -1,3 +1,5 @@ +#include + int f(); void g(int x); @@ -8,7 +10,8 @@ class A { void test_return_val() { f(); // NON_COMPLIANT - return value never read - static_cast(f()); // COMPLIANT + static_cast(f()); // COMPLIANT - explicitly ignoring the return value by + // static_cast to void. int x = f(); // COMPLIANT - according to the rule, even though it's not in // practice used because the unused assignment would be flagged // by A0-1-1 @@ -17,4 +20,9 @@ void test_return_val() { A a2; a1 + a2; // COMPLIANT - `+` is a call to operator+, but is permitted by the // rule -} \ No newline at end of file + + (void)f(); // COMPLIANT - explicitly ignoring the return value by C-style cast + // to void. + std::ignore = f(); // COMPLIANT - explicitly ignoring the return value by + // assigning to std::ignore. +} diff --git a/cpp/common/src/codingstandards/cpp/Operator.qll b/cpp/common/src/codingstandards/cpp/Operator.qll index 55db1f9da2..72ee04b68f 100644 --- a/cpp/common/src/codingstandards/cpp/Operator.qll +++ b/cpp/common/src/codingstandards/cpp/Operator.qll @@ -8,10 +8,10 @@ class AnyAssignOperation extends Expr { AnyAssignOperation() { this instanceof AssignOperation or - // operator op, where op is +=, -=, *=, /=, %=, ^=, &=, |=, >>= + // operator op, where op is +=, -=, *=, /=, %=, ^=, &=, |=, >>=, <<= exists(string op | "operator" + op = this.(FunctionCall).getTarget().getName() and - op in ["+=", "-=", "*=", "/=", "%=", "^=", "&=", "|=", ">>="] + op in ["+=", "-=", "*=", "/=", "%=", "^=", "&=", "|=", ">>=", "<<="] ) } } @@ -121,10 +121,10 @@ class UserAssignmentOperator extends AssignmentOperator { /** An assignment operator of any sort */ class AssignmentOperator extends MemberFunction { AssignmentOperator() { - // operator op, where op is =, +=, -=, *=, /=, %=, ^=, &=, |=, >>= + // operator op, where op is =, +=, -=, *=, /=, %=, ^=, &=, |=, >>=, <<= exists(string op | "operator" + op = this.getName() and - op in ["=", "+=", "-=", "*=", "/=", "%=", "^=", "&=", "|=", ">>="] + op in ["=", "+=", "-=", "*=", "/=", "%=", "^=", "&=", "|=", ">>=", "<<="] ) } } diff --git a/cpp/common/src/codingstandards/cpp/rules/unusedparameter/UnusedParameter.qll b/cpp/common/src/codingstandards/cpp/rules/unusedparameter/UnusedParameter.qll index 4258bbf129..2bdbc3887d 100644 --- a/cpp/common/src/codingstandards/cpp/rules/unusedparameter/UnusedParameter.qll +++ b/cpp/common/src/codingstandards/cpp/rules/unusedparameter/UnusedParameter.qll @@ -11,11 +11,20 @@ abstract class UnusedParameterSharedQuery extends Query { } Query getQuery() { result instanceof UnusedParameterSharedQuery } +predicate isMaybeUnusedParameter(Parameter parameter) { + parameter.getAnAttribute().toString() = "maybe_unused" +} + +predicate isLambdaParameter(Parameter parameter) { + exists(LambdaExpression lambda | lambda.getLambdaFunction().getParameter(_) = parameter) +} + query predicate problems(UnusedParameter p, string message, Function f, string fName) { not isExcluded(p, getQuery()) and + not isMaybeUnusedParameter(p) and + (if isLambdaParameter(p) then fName = "lambda expression" else fName = f.getQualifiedName()) and f = p.getFunction() and // Virtual functions are covered by a different rule not f.isVirtual() and - message = "Unused parameter '" + p.getName() + "' for function $@." and - fName = f.getQualifiedName() + message = "Unused parameter '" + p.getName() + "' for function $@." } diff --git a/cpp/common/test/includes/standard-library/tuple.h b/cpp/common/test/includes/standard-library/tuple.h index 8602d4ca1e..e4ab473488 100644 --- a/cpp/common/test/includes/standard-library/tuple.h +++ b/cpp/common/test/includes/standard-library/tuple.h @@ -1,4 +1,11 @@ namespace std { template class tuple {}; template std::tuple make_tuple(Types &&...args); +struct ignore_t { + template + constexpr // required since C++14 + void + operator=(T &&) const noexcept {} +}; +inline const std::ignore_t ignore; // 'const' only until C++17 } // namespace std diff --git a/cpp/common/test/rules/unusedparameter/UnusedParameter.expected b/cpp/common/test/rules/unusedparameter/UnusedParameter.expected index 9264f421f9..eaeeeae4ca 100644 --- a/cpp/common/test/rules/unusedparameter/UnusedParameter.expected +++ b/cpp/common/test/rules/unusedparameter/UnusedParameter.expected @@ -1,2 +1,3 @@ -| test.cpp:6:22:6:22 | x | Unused parameter 'x' for function $@. | test.cpp:6:6:6:16 | test_unused | test_unused | -| test.cpp:14:14:14:14 | x | Unused parameter 'x' for function $@. | test.cpp:14:8:14:8 | b | A::b | +| test.cpp:8:22:8:22 | x | Unused parameter 'x' for function $@. | test.cpp:8:6:8:16 | test_unused | test_unused | +| test.cpp:16:14:16:14 | x | Unused parameter 'x' for function $@. | test.cpp:16:8:16:8 | b | A::b | +| test.cpp:35:14:35:14 | y | Unused parameter 'y' for function $@. | test.cpp:34:9:34:9 | operator() | lambda expression | \ No newline at end of file diff --git a/cpp/common/test/rules/unusedparameter/test.cpp b/cpp/common/test/rules/unusedparameter/test.cpp index 07d14bfe9e..a1c198f3a5 100644 --- a/cpp/common/test/rules/unusedparameter/test.cpp +++ b/cpp/common/test/rules/unusedparameter/test.cpp @@ -1,6 +1,8 @@ // NOTICE: SOME OF THE TEST CASES BELOW ARE ALSO INCLUDED IN THE C TEST CASE AND // CHANGES SHOULD BE REFLECTED THERE AS WELL. +#include + int test_used(int x) { return x; } // COMPLIANT void test_unused(int x) {} // NON_COMPLIANT @@ -16,4 +18,29 @@ class A { virtual void d(int x, int y) {} // virtual, not covered by this rule }; -void test_no_def(int x); // COMPLIANT - no definition, so cannot be "unused" \ No newline at end of file +void f( + int i, // COMPLIANT + int j, // COMPLIANT + int k, // COMPLIANT + [[maybe_unused]] int l // COMPLIANT: explicitly stated as [[maybe_unused]] +) { + static_cast(i); // COMPLIANT: explicitly ignored by static_cast to void + (void)j; // COMPLIANT: explicitly ignored by c-style cast to void + std::ignore = k; // COMPLIANT: explicitly ignored by assignment to std::ignore +} + +void test_lambda_expr() { + auto lambda = + [](int x, // COMPLIANT: used + int y, // NON_COMPLIANT: unused without explicit notice + [[maybe_unused]] int z, // COMPLIANT: stdattribute [[maybe_unused]] + int w, // COMPLIANT: static_cast to void + int u, // COMPLIANT: c-style cast to void + int) { // COMPLIANT: unnamed parameter + static_cast(w); + (void)u; + return x; + }; +} + +void test_no_def(int x); // COMPLIANT - no definition, so cannot be "unused"