 pmeerw's blog
 
pmeerw's blog
05 May 2020
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