Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions change_notes/2026-06-19-fix-fp-rule-0-0-1-orphan-blocks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `RULE-0-0-1` - `UnreachableStatement.ql`:
- Fixed false positives caused by orphan BasicBlock nodes (disconnected from the CFG) that represent expressions rather than statements. These artifacts are generated by the GCC extractor for functions involving virtual calls returning `std::variant`-based types with non-trivially-destructible alternatives.
18 changes: 17 additions & 1 deletion cpp/misra/src/rules/RULE-0-0-1/UnreachableStatement.ql
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,28 @@ predicate isReachable(BasicBlock bb) {
)
}

/**
* Holds if `bb` is an orphan basic block that is disconnected from the CFG and
* does not represent a statement. These are artifacts of the extractor/CFG
* construction (e.g., variable access expressions, function name nodes, or
* temporary object reuse nodes) and not genuine unreachable statements.
* A block with no predecessors that is a Stmt is legitimately unreachable code
* (e.g., code after an infinite loop or noreturn call).
*/
predicate isDisconnectedNonStmtBlock(BasicBlock bb) {
not exists(bb.getAPredecessor()) and
not exists(bb.getASuccessor()) and
not bb = any(Function f).getEntryPoint() and
not bb instanceof Stmt
}

from BasicBlock bb
where
not isExcluded(bb, DeadCode3Package::unreachableStatementQuery()) and
not isReachable(bb) and
not isCompilerGenerated(bb) and
not affectedByMacro(bb)
not affectedByMacro(bb) and
not isDisconnectedNonStmtBlock(bb)
// Note that the location of a BasicBlock will in some cases have an incorrect end location, often
// preceding the end and including live code. We cast the block to an `Element` to get locations
// that are not broken.
Expand Down
36 changes: 36 additions & 0 deletions cpp/misra/test/rules/RULE-0-0-1/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,40 @@ void f12() {
int l1 = 0; // NON-COMPLIANT
}
}
}

// Regression test: virtual call returning variant-based expected type should not
// produce false positives from disconnected CFG nodes. With real GCC headers and
// codeql/cpp-all <= 5.0.0, the extractor creates orphan BasicBlock nodes (0
// predecessors, 0 successors) for expressions inside functions that use virtual
// calls returning std::variant-based types with non-trivially-destructible
// alternatives (e.g. containing std::string). The isDisconnectedNonStmtBlock
// predicate filters these artifacts. This test validates the pattern compiles
// and is not flagged; the actual orphan block generation requires real stdlib.
#include <variant>

struct TestError {
int code;
};

template <typename E> class test_expected_void {
std::variant<TestError, E> storage_;

public:
test_expected_void() {}
bool has_value() const noexcept;
};

class ITestService {
public:
virtual test_expected_void<TestError> DoWork(int x) const noexcept = 0;
virtual ~ITestService() = default;
};

bool f13(ITestService &svc) { // COMPLIANT
const auto result = svc.DoWork(42);
if (!result.has_value()) {
return false; // COMPLIANT
}
return true; // COMPLIANT
}