Performing a path-sensitive check only when a function return value is ignored

I have a C function that follows the convention of returning 0 upon successful return and -1 if it can’t successfully complete its job. I have written a pre-call checker to check if the function’s preconditions are known to be met. Now I want to call it only when the return value of the function call is ignored, figuring it’s okay for the caller to not check the return value if the calling context guarantees that the call can be successful, and it’s okay for the call to fail as long as the caller detects and handles the situation.

I see that the security.insecureAPI.UncheckedReturn checker is called by an AST visitor only for function calls where the return value is ignored:

void WalkAST::VisitCompoundStmt(CompoundStmt *S) {

for (Stmt *Child : S->children())

if (Child) {

if (CallExpr *CE = dyn_cast(Child))

checkUncheckedReturnValue(CE);

Visit(Child);

}

}

I want to do something similar, but with a path-sensitive checker that takes a CheckerContext as well as the CallExpr (or even better, the CallEvent). I tried structuring a PreStmt or PostStmt callback like the statement visitor above, but these callbacks appear to be called for individual statements in a block, not for compound statements. I also tried having the PreStmt callback check to see if the individual statement is a function call, and if so call the precondition checker, but this does not limit the checker to calls that aren’t part of a larger statement where the return value is checked. So I’m hoping to get some help about how to do this.

Thanks in advance!

For implementing your approach, i’d suggest something like that (untested!):

void YourChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr());
if (!CE)
return;
// Find the parent statement of the call expression, which should be somewhere in the caller’s body.
const Stmt *Parent = C.getLocationContext()->getCurrentStackFrame()->getParent()->getParentMap().getParent(CE);
if (!isa(Parent)) // Return value not discarded?
return;
// Proceed with your checks.
}

However, you might also consider the path-sensitive approach to finding discarded/unchecked return values. That is, if the return value was saved into a variable, it might have still been discarded, so a syntax-only check is not enough, you’d need to see how the value flows. For that, you’d need to subscribe to checkDeadSymbols, and see if upon the death of the symbol there are no range constraints on it; then, if the value was truly unchecked, you can throw the warning on the original call expression. But that’s more complicated.