Detect Functions with Return Immediately Discarded
This example shows how to use Polyspace Query Language (PQL) to check for violation of this rule: If a function takes at least one input, do not immediately discard its returned value.
This topic focuses on application of PQL. For more details about creating semantic defect checkers, see Detect Semantic Issues Using Polyspace Query Language Semantic Classes.
Analyze Rule
To implement the rule in PQL, analyze the components of the rule:
Immediately discarded return value — The predicate
immediatelyDiscardedofCallSitedetects function calls where the return value is immediately discarded.Excluding operators — Operators are considered call sites. Exclude the operator based on name.
Number of argument — This information can be found using the
numberOfArgumentspredicate ofCallSite.
Implement Rule in PQL
The defect can be defined like this:
defect returnimmediatelydiscarded =
when
Cpp.CallSite.is(&cs)
and cs.immediatelyDiscarded()
and cs.numberOfArguments(&n)
and n>0
and cs.callee(&func)
and func.name(&name)
and not name.contains("operator")
raise "Output of function with no input {name} is explicitly cast to void with number of arguments {n}."
on csTest Defect
To test and verify the defect:
Initialize a test standard in a writable location:
polyspace-query-language init
In
main.pql, insert the defect in a test standard:package main /// Main PQL file defines the catalog of your PQL project. /// The catalog is a collection of sections. catalog PQL_ImmediatelyDiscarded = { #[Description("MySection")] section mysection = { #[Description("myRule"), Id(Rule1)] rule myRule = { defect returnimmediatelydiscarded = when Cpp.CallSite.is(&cs) and cs.immediatelyDiscarded() and cs.numberOfArguments(&n) and n>0 and cs.callee(&func) and func.name(&name) and not name.contains("operator") raise "Output of function with no input {name} is explicitly cast to void with number of arguments {n}." on cs } } }Package the standard in a
pschkfile:polyspace-query-language package
Create
example.cppin the same folder:This code explicitly states where a violation of the rule is expected using annotation#include <iostream> #include <string> int add(int a, int b) { return a + b; // returns a value } void log_message(const std::string& m) { std::cout << "[LOG] " << m << '\n'; //expect-0-Rule1 } struct Accumulator { int sum = 0; int add(int x) { sum += x; return sum; } //expect-0-Rule1 void reset() { sum = 0; } // no return }; int main() { // --- Return values that are used --- int s = add(2, 3); //expect-0-Rule1 std::cout << "sum = " << s << '\n'; // --- Calls with discarded return values --- add(10, 20); //expect-1-Rule1 Accumulator acc; acc.add(5); //expect-1-Rule1 acc.add(7); //expect-1-Rule1 // --- Calls that have no return value (void) --- log_message("starting..."); //expect-0-Rule1 acc.reset(); //expect-0-Rule1 // Force a temporary object and discard a returning method call Accumulator{}.add(42); //expect-1-Rule1 return 0; }expect-1-Rule1. Absence of the violation is also annotated usingexpect-0-Rule1.Run a test for the defect on the example code:
The test verifies the expected violations as well as the expected absence of violations:polyspace-query-language test example.cpp
Number of actual defects: 4 Number of expected annotations: 9 (including 5 expected absence of defects). _______________________________________________ Checking expected defects with actuals... ----------------------------------------- _______________________________________________ Looking for unexpected defects... ------------------------------------------- _______________________________________________ Tests passed