A seemingly simple problem: check C/C++ code statically for unused return values, but surprisingly here is no easily available tooling. Let's look at some options:
[[nodiscard]]
, e.g. the following code (unused-return.cpp
)
int foo() { return 42; } [[nodiscard]] int bar() { return 23; } int main() { foo(); bar(); }when compiled with
g++-8 unused-return.cpp
, will result in
unused-return.cpp: In function ‘int main()’: unused-return.cpp:12:6: warning: ignoring return value of ‘int bar()’, declared with attribute nodiscard [-Wunused-result] bar(); ~~~^~ unused-return.cpp:6:5: note: declared here int bar() { ^~~(tested with GCC 8.4 / Ubuntu)
No warning will printed (foo()
), unless [[nodiscard]]
is annotated (bar()
).
unused-return.c
:
__attribute__ ((warn_unused_result)) int bar() { return 23; }resulting in a warning
unused-return.c: In function ‘main’: unused-return.c:12:3: warning: ignoring return value of ‘bar’, declared with attribute warn_unused_result [-Wunused-result] bar(); ^~~~~when compiled with
gcc unused-return.c
(GCC 8.4/Ubuntu).
It doesn't help to enable warnings to get a similar warning for function foo()
.
splint unused-return.c
, but the output is quite verbose and doesn't cover C++:
Splint 3.1.2 --- 20 Feb 2018 unused-return.c: (in function main) unused-return.c:11:3: Return value (type int) ignored: foo() Result returned by function call is not used. If this is intended, can cast result to (void) to eliminate message. (Use -retvalint to inhibit warning) unused-return.c:12:3: Return value (type int) ignored: bar() Finished checking --- 2 code warnings
clang-query
tool can be used to moreless interactively query the AST of the program. This is expored in more detail below...
Stackoverflow provides all the basics: a clang-query
script which matches call expressions
in the abstract syntax tree (AST) of the program, then restricting to 'intersting cases'.
For a nice intro to clang-query
, see this devblog article.
I've added the -w
switch to suppress clang warnings
when processing the input program, and some bind
trickery
to make the output a bit nicer.
#!/bin/sh # unused-return.sh: Run clang-query to report unused return values. # When --dump, print the AST of matching syntax. if [ "x$1" = "x--dump" ]; then dump="set output dump" shift fi query='m callExpr( isExpansionInMainFile(), hasParent(anyOf( compoundStmt(), ifStmt(hasCondition(expr().bind("cond"))), whileStmt(hasCondition(expr().bind("cond"))), doStmt(hasCondition(expr().bind("cond"))) )), unless(hasType(voidType())), unless(isTypeDependent()), unless(cxxOperatorCallExpr()), unless(callee(namedDecl(anyOf( hasName("memset"), hasName("setlength"), hasName("flags"), hasName("width"), hasName("__builtin_memcpy") )))), unless(equalsBoundNode("cond"))).bind("unused-return")' clang-query-9 -extra-arg="-w" -c="set bind-root false" -c="$dump" -c="$query" "$@" --The output should look like
Match #1: unused-return.c:11:3: note: "unused-return" binds here foo(); ^~~~~ Match #2: unused-return.c:12:3: note: "unused-return" binds here bar(); ^~~~~ 2 matches.A recent clang version is needed, tested with clang 9 / Ubuntu; clang 6 did not work.
All files for download: unused-return.zip.
Update (2020-05-05): MSVC has _Check_return_
and _Must_inspect_result_
, for good measure.
Update (2020-05-06): clang-tidy
has bugprone-unused-return-value
to check for missing return values of certain configured functions, such as std::async(), std::unique_ptr::release(), std::remove()
Update (2020-05-06): see reddit
posted at: 01:30 | path: /programming | permanent link