Skip to content

Commit 7b93bb8

Browse files
committed
First draft of UnusedReturnValue
1 parent 54886ef commit 7b93bb8

File tree

4 files changed

+80
-48
lines changed

4 files changed

+80
-48
lines changed
Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,48 @@
11
/**
2-
* @id cpp/autosar/unused-return-value
3-
* @name A0-1-2: Unused return value
4-
* @description The value returned by a function having a non-void return type that is not an
5-
* overloaded operator shall be used.
6-
* @kind problem
7-
* @precision very-high
8-
* @problem.severity warning
9-
* @tags external/autosar/id/a0-1-2
10-
* readability
11-
* maintainability
12-
* external/autosar/allocated-target/implementation
13-
* external/autosar/enforcement/automated
14-
* external/autosar/obligation/required
15-
*/
2+
* @id cpp/autosar/unused-return-value
3+
* @name A0-1-2: Unused return value
4+
* @description The value returned by a function having a non-void return type that is not an
5+
* overloaded operator shall be used.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity warning
9+
* @tags external/autosar/id/a0-1-2
10+
* readability
11+
* maintainability
12+
* external/autosar/allocated-target/implementation
13+
* external/autosar/enforcement/automated
14+
* external/autosar/obligation/required
15+
*/
1616

1717
import cpp
1818
import codingstandards.cpp.autosar
1919
import semmle.code.cpp.dataflow.DataFlow
2020

21-
// Type isEdgeCase(Expr expr) {
22-
// // 1. c-style casts to void.
23-
// expr.(CStyleCast).getUnderlyingType()
24-
// // 2. Assignment to std::ignore
25-
// }
21+
predicate isStdIgnore(Element element) {
22+
exists(NameQualifier nq |
23+
nq.getQualifiedElement().toString() = "ignore" and
24+
nq.toString() = "std::" and
25+
element.toString() = "ignore"
26+
)
27+
}
28+
29+
/* The statement std::ignore = f() is not recognized an assignment; therefore, we do some painful acrobatics. */
30+
predicate isAssignment(FunctionCall assignment) {
31+
exists(Operator operator |
32+
assignment.getTarget() = operator and
33+
operator.getName() = "operator=" and
34+
// check if this is indeed an operator for assignment by checking if there are no overloads
35+
not exists(operator.getAnOverload())
36+
)
37+
}
38+
39+
predicate isAssignmentOperand(Expr operand) {
40+
exists(FunctionCall assignment | isAssignment(assignment) and operand = assignment.getAChild())
41+
}
2642

27-
from CStyleCast expr
28-
where any()
29-
select expr, expr.getType()
43+
predicate returnValueIsAssignedToStdIgnore(FunctionCall fc) {
44+
isAssignmentOperand(fc) and exists(Element stdIgnore | isStdIgnore(stdIgnore))
45+
}
3046

3147
/*
3248
* This query performs a simple syntactic check to ensure that the return value of the function is
@@ -36,21 +52,29 @@ select expr, expr.getType()
3652
* access of `ret_val`. However, such a case _would_ be flagged by A0-1-1 - Useless assignment.
3753
*/
3854

39-
// from FunctionCall fc, Function f
40-
// where
41-
// not isExcluded(fc, DeadCodePackage::unusedReturnValueQuery()) and
42-
// // Find function calls in `ExprStmt`s, which indicate the return value is ignored
43-
// fc.getParent() instanceof ExprStmt and
44-
// // Ignore calls to void functions, which don't return values
45-
// not fc.getUnderlyingType() instanceof VoidType and
46-
// // Get the function target
47-
// f = fc.getTarget() and
48-
// // Overloaded (i.e. user defined) operators should behave in the same way as built-in operators,
49-
// // so the rule does not require the use of the return value
50-
// not f instanceof Operator and
51-
// // Exclude cases where the function call is generated within a macro, as the user of the macro is
52-
// // not necessarily able to address thoes results
53-
// not fc.isAffectedByMacro() and
54-
// // Rule allows disabling this rule where a static_cast<void> is applied
55-
// not fc.getExplicitlyConverted().(StaticCast).getActualType() instanceof VoidType
56-
// select fc, "Return value from call to $@ is unused.", f, f.getName()
55+
from FunctionCall fc, Function f
56+
where
57+
not isExcluded(fc, DeadCodePackage::unusedReturnValueQuery()) and
58+
// Find function calls in `ExprStmt`s, which indicate the return value is ignored
59+
fc.getParent() instanceof ExprStmt and
60+
// Ignore calls to void functions, which don't return values
61+
not fc.getUnderlyingType() instanceof VoidType and
62+
// Get the function target
63+
f = fc.getTarget() and
64+
// Overloaded (i.e. user defined) operators should behave in the same way as built-in operators,
65+
// so the rule does not require the use of the return value
66+
not f instanceof Operator and
67+
// Exclude cases where the function call is generated within a macro, as the user of the macro is
68+
// not necessarily able to address those results
69+
not fc.isAffectedByMacro() and
70+
// Rule allows disabling this rule where a static_cast<void> or a C-style cast to void is applied
71+
not (
72+
fc.getExplicitlyConverted().(StaticCast).getActualType() instanceof VoidType
73+
or
74+
exists(CStyleCast cast |
75+
not cast.isCompilerGenerated() and
76+
cast.getExpr() = fc
77+
)
78+
) and
79+
not returnValueIsAssignedToStdIgnore(fc)
80+
select fc, "Return value from call to $@ is unused.", f, f.getName()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +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 |
1+
| test.cpp:12:3:12:3 | call to f | Return value from call to $@ is unused. | test.cpp:3:5:3:5 | f | f |

cpp/autosar/test/rules/A0-1-2/test.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#include <tuple>
2+
13
int f();
24
void g(int x);
35

@@ -8,7 +10,8 @@ class A {
810

911
void test_return_val() {
1012
f(); // NON_COMPLIANT - return value never read
11-
static_cast<void>(f()); // COMPLIANT
13+
static_cast<void>(f()); // COMPLIANT - explicitly ignoring the return value by
14+
// static_cast to void.
1215
int x = f(); // COMPLIANT - according to the rule, even though it's not in
1316
// practice used because the unused assignment would be flagged
1417
// by A0-1-1
@@ -17,5 +20,9 @@ void test_return_val() {
1720
A a2;
1821
a1 + a2; // COMPLIANT - `+` is a call to operator+, but is permitted by the
1922
// rule
20-
int y = (int)3;
23+
24+
(void)f(); // COMPLIANT - explicitly ignoring the return value by C-style cast
25+
// to void.
26+
std::ignore = f(); // COMPLIANT - explicitly ignoring the return value by
27+
// assigning to std::ignore.
2128
}
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
namespace std {
22
template <class... Types> class tuple {};
33
template <class... Types> std::tuple<Types...> make_tuple(Types &&...args);
4-
// TODO change this to example from cpp standard
4+
// TODO change this to example from cpp standard
55
struct ignore_t {
6-
template <typename T>
7-
constexpr // required since C++14
8-
void operator=(T&&) const noexcept {}
6+
template <typename T>
7+
constexpr // required since C++14
8+
void
9+
operator=(T &&) const noexcept {}
910
};
1011
inline constexpr std::ignore_t ignore; // 'const' only until C++17
11-
} // namespace std
12+
} // namespace std

0 commit comments

Comments
 (0)