How can I lint C++ code to find all unused return values?

c++, static-analysis

Solution

This can be done using `clang-query`. Here is a shell script that invokes `clang-query` to find calls that return a value that is not used:

#!/bin/sh
# cmd.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")))'

clang-query -c="$dump" -c="$query" "$@"

To run this on, say, `test1.cc`:

$ ./cmd.sh test1.cc --

The basic idea of the query is to look for call expressions whose immediate parent is a compound statement. That is expanded to handle an immediate parent that is a control flow statement, being careful not to report when the call appears as the conditional expression.

Some other complications the query deals with:

This only reports in the main file of a translation unit in order to eliminate the voluminous noise from headers. Remove the `isExpansionInMainFile` filter to drink from the fire hose.

In C++ templates, we might not know what the type is, so suppress reporting all calls with dependent types.

Some functions like `memset` have useless or only rarely useful return values. They have to be filtered out to see any useful signal. The list of function names in the query is just the tip of that iceberg.

C++ overloaded operators, including `operator<<` and `operator=`, usually return a value, but that value is most often ignored. So suppress reports for all overloaded operators.

I've tested this lightly (with `clang-query` from clang+llvm-8.0.1) on some files in a utility library of mine, which is how I found some of the things that need to be filtered out for this to be useful. There are probably many more things that need filtering, depending on your application.

The query language is described at https://clang.llvm.org/docs/LibASTMatchersReference.html . See this answer of mine for some more links and information about `clang-query`.

Problem

I would like to statically inspect all calls to non-void functions where the return value is not used. In effect this would be like applying `__attribute__ ((warn_unused_result))` to all non-void functions, but of course for a large project that is not practical to do. Is there any static analysis tool that can provide this information?

Original source