Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- `M0-1-1`, `RULE-2-1` - `UnreachableCode.ql`:
- Updated detection of compiler generated code to include "handler" blocks, part of EDG's IR.
- "handler" blocks generated for `catch(...)` blocks are not excluded for technical reasons related to how the CFG is constructed.
- `M15-3-6`, `ERR54-CPP` - `CatchBlockShadowingMisra.ql`, `CathcBlockShadowingCert.ql`:
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in filename: 'CathcBlockShadowingCert.ql' should be 'CatchBlockShadowingCert.ql' (missing 't' in 'Catch').

Suggested change
- `M15-3-6`, `ERR54-CPP` - `CatchBlockShadowingMisra.ql`, `CathcBlockShadowingCert.ql`:
- `M15-3-6`, `ERR54-CPP` - `CatchBlockShadowingMisra.ql`, `CatchBlockShadowingCert.ql`:

Copilot uses AI. Check for mistakes.
- Altered semantics to detect shadowing for a catch block involving type `T` preceding another catch block involving the same type `T`, such as `catch(T&)` shadowing `catch(T)` and vice versa. Previously, the involved types had to have a subtype relationship.
- Refactored catch block shadowing into a shared library for use in `RULE-0-0-1`.
17 changes: 17 additions & 0 deletions cpp/common/src/codingstandards/cpp/ast/Catch.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import cpp

CatchBlock getEarlierCatchBlock(CatchBlock cb) {
exists(TryStmt try, int i, int j |
cb = try.getCatchClause(j) and
i < j and
result = try.getCatchClause(i)
)
}

/**
* Get the body of a catch block, ie, the block of statements executed when the catch block is
* entered.
*
* This is useful, for instance, for CFG traversal
*/
Stmt getCatchBody(CatchBlock cb) { result = cb.getChild(0) }
10 changes: 10 additions & 0 deletions cpp/common/src/codingstandards/cpp/deadcode/UnreachableCode.qll
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ predicate isCompilerGenerated(BasicBlock b) {
b.(Stmt).isCompilerGenerated()
or
b.(Expr).isCompilerGenerated()
or
// Catch blocks come with a generated 'Handler' that owns the basic block of the catch body,
// however, `isCompilerGenerated` does not hold these generated handlers.
//
// We must also handle the case of `CatchAnyBlock`s. Unlike other catch blocks, for these the
// handler dominates the non-generated basic block, so in our CFG the `Handler` node is the entity
// used in the CFG rather than the basic block itself. Therefore we must not exclude these
// `BasicBlock`s.
b instanceof Handler and
not b.(Handler).getBlock() instanceof CatchAnyBlock
}

/**
Expand Down
50 changes: 50 additions & 0 deletions cpp/common/src/codingstandards/cpp/exceptions/Shadowing.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import cpp
import codingstandards.cpp.ast.Catch
import codingstandards.cpp.exceptions.ExceptionFlow

/**
* A catch block which is shadowed by an earlier catch block for a base type.
*
* For example:
*
* ```cpp
* class Base { };
* class Derived : public Base { };
*
* try {
* // ...
* } catch (const Base& b) { // This catch block shadows the next one
* // Handle Base
* } catch (const Derived& d) { // This catch block is shadowed
* // Handle Derived
* }
* ```
*/
class ShadowedCatchBlock extends CatchBlock {
CatchBlock cbBase;
Class baseType;
Class derivedType;

ShadowedCatchBlock() {
cbBase = getEarlierCatchBlock(this) and
baseType = simplifyHandlerType(cbBase.getParameter().getType()) and
derivedType = simplifyHandlerType(this.getParameter().getType()) and
baseType.getADerivedClass*() = derivedType
}

/**
* Get the earlier catch block which shadows this catch block.
*/
CatchBlock getShadowingBlock() { result = cbBase }

/**
* Get the type of this catch block's derived class whose catch block is shadowed by an earlier
* catch block.
*/
Class getShadowedType() { result = derivedType }

/**
* Get the type of the base class whose catch block precedes, and thus shadows, this catch block.
*/
Class getShadowingType() { result = baseType }
}
26 changes: 26 additions & 0 deletions cpp/common/src/codingstandards/cpp/exclusions/cpp/DeadCode3.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
import cpp
import RuleMetadata
import codingstandards.cpp.exclusions.RuleMetadata

newtype DeadCode3Query = TUnreachableStatementQuery()

predicate isDeadCode3QueryMetadata(Query query, string queryId, string ruleId, string category) {
query =
// `Query` instance for the `unreachableStatement` query
DeadCode3Package::unreachableStatementQuery() and
queryId =
// `@id` for the `unreachableStatement` query
"cpp/misra/unreachable-statement" and
ruleId = "RULE-0-0-1" and
category = "required"
}

module DeadCode3Package {
Query unreachableStatementQuery() {
//autogenerate `Query` type
result =
// `Query` type for `unreachableStatement` query
TQueryCPP(TDeadCode3PackageQuery(TUnreachableStatementQuery()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Const
import Conversions
import Conversions2
import DeadCode
import DeadCode3
import Declarations
import ExceptionSafety
import Exceptions1
Expand Down Expand Up @@ -78,6 +79,7 @@ newtype TCPPQuery =
TConversionsPackageQuery(ConversionsQuery q) or
TConversions2PackageQuery(Conversions2Query q) or
TDeadCodePackageQuery(DeadCodeQuery q) or
TDeadCode3PackageQuery(DeadCode3Query q) or
TDeclarationsPackageQuery(DeclarationsQuery q) or
TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or
TExceptions1PackageQuery(Exceptions1Query q) or
Expand Down Expand Up @@ -140,6 +142,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
isConversionsQueryMetadata(query, queryId, ruleId, category) or
isConversions2QueryMetadata(query, queryId, ruleId, category) or
isDeadCodeQueryMetadata(query, queryId, ruleId, category) or
isDeadCode3QueryMetadata(query, queryId, ruleId, category) or
isDeclarationsQueryMetadata(query, queryId, ruleId, category) or
isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or
isExceptions1QueryMetadata(query, queryId, ruleId, category) or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,19 @@ import cpp
import codingstandards.cpp.Customizations
import codingstandards.cpp.Exclusions
import codingstandards.cpp.exceptions.ExceptionFlow
import codingstandards.cpp.exceptions.Shadowing

abstract class CatchBlockShadowingSharedQuery extends Query { }

Query getQuery() { result instanceof CatchBlockShadowingSharedQuery }

query predicate problems(
CatchBlock cbDerived, string message, CatchBlock cbBase, string cbBaseDescription
ShadowedCatchBlock cbDerived, string message, CatchBlock cbBase, string cbBaseDescription
) {
exists(TryStmt try, Class baseType, Class derivedType |
not isExcluded(cbDerived, getQuery()) and
exists(int i, int j |
cbBase = try.getCatchClause(i) and
cbDerived = try.getCatchClause(j) and
i < j
) and
baseType = simplifyHandlerType(cbBase.getParameter().getType()) and
derivedType = simplifyHandlerType(cbDerived.getParameter().getType()) and
baseType.getADerivedClass+() = derivedType and
message =
"Catch block for derived type " + derivedType +
" is shadowed by $@ earlier catch block for base type " + baseType + "." and
cbBaseDescription = "this"
)
not isExcluded(cbDerived, getQuery()) and
cbBase = cbDerived.getShadowingBlock() and
message =
"Catch block for derived type " + cbDerived.getShadowedType() +
" is shadowed by $@ earlier catch block for base type " + cbDerived.getShadowingType() + "." and
cbBaseDescription = "this"
}
71 changes: 71 additions & 0 deletions cpp/misra/src/rules/RULE-0-0-1/UnreachableStatement.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* @id cpp/misra/unreachable-statement
* @name RULE-0-0-1: A function shall not contain unreachable statements
* @description Dead code can indicate a logic error, potentially introduced during code edits, or
* it may be unnecessary code that can be deleted.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-0-0-1
* maintainability
* scope/single-translation-unit
* external/misra/enforcement/decidable
* external/misra/obligation/required
*/

import cpp
import codingstandards.cpp.misra
import codingstandards.cpp.deadcode.UnreachableCode
import codingstandards.cpp.exceptions.ExceptionFlow
import codingstandards.cpp.exceptions.ExceptionSpecifications
import codingstandards.cpp.exceptions.Shadowing

/**
* MISRA C++ defines its own notion of unreachable statements, which is similar to, but distinct
* from, the general concept of unreachable code.
*
* This is not a superset of `BasicBlock.isReachable()`, because that includes all catch blocks.
* However, it is a superset of the transitive closure of blocks reachable from function entry via
* `getASuccessor`.
*
* The superset relationship can be read below, with extra reachable cases added for `&&`, `||`,
* `?:`, and `constexpr if`, and catch blocks that aren't shadowed by prior catch blocks.
*/
predicate isReachable(BasicBlock bb) {
bb = any(Function f).getEntryPoint()
or
isReachable(bb.getAPredecessor())
or
exists(BinaryLogicalOperation op |
isReachable(op.getBasicBlock()) and
bb = op.getAnOperand().getBasicBlock()
)
or
exists(ConditionalExpr cond |
isReachable(cond.getBasicBlock()) and
bb = [cond.getThen(), cond.getElse()].getBasicBlock()
)
or
exists(FunctionCall call, TryStmt try, CatchBlock cb |
isReachable(call.getBasicBlock()) and
not isNoExceptTrue(call.getTarget()) and
try = getNearestTry(call.getEnclosingStmt()) and
cb = try.getACatchClause() and
not cb instanceof ShadowedCatchBlock and
bb = cb.getBasicBlock()
)
or
exists(ConstexprIfStmt ifStmt |
isReachable(ifStmt.getBasicBlock()) and
bb = [ifStmt.getThen(), ifStmt.getElse()].getBasicBlock()
)
}

from BasicBlock bb
where
not isExcluded(bb, DeadCode3Package::unreachableStatementQuery()) and
not isReachable(bb) and
not isCompilerGenerated(bb) and
not affectedByMacro(bb)
select bb, "Unreachable statement in function '$@'.", bb.getEnclosingFunction(),
bb.getEnclosingFunction().getName()
28 changes: 28 additions & 0 deletions cpp/misra/test/rules/RULE-0-0-1/UnreachableStatement.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
| test.cpp:7:3:7:12 | declaration | Unreachable statement in function '$@'. | test.cpp:5:6:5:7 | f2 | f2 |
| test.cpp:18:14:19:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:10:6:10:7 | f3 | f3 |
| test.cpp:26:10:27:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:10:6:10:7 | f3 | f3 |
| test.cpp:43:5:43:14 | declaration | Unreachable statement in function '$@'. | test.cpp:40:6:40:7 | f4 | f4 |
| test.cpp:52:5:52:14 | declaration | Unreachable statement in function '$@'. | test.cpp:40:6:40:7 | f4 | f4 |
| test.cpp:63:3:63:12 | declaration | Unreachable statement in function '$@'. | test.cpp:40:6:40:7 | f4 | f4 |
| test.cpp:70:10:71:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:66:6:66:7 | f5 | f5 |
| test.cpp:75:11:76:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:66:6:66:7 | f5 | f5 |
| test.cpp:89:10:90:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:88:24:88:24 | f6 | f6 |
| test.cpp:92:10:93:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:88:24:88:24 | f6 | f6 |
| test.cpp:114:5:114:14 | declaration | Unreachable statement in function '$@'. | test.cpp:109:6:109:7 | f8 | f8 |
| test.cpp:119:5:119:14 | declaration | Unreachable statement in function '$@'. | test.cpp:109:6:109:7 | f8 | f8 |
| test.cpp:131:3:131:12 | declaration | Unreachable statement in function '$@'. | test.cpp:109:6:109:7 | f8 | f8 |
| test.cpp:135:18:136:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:134:6:134:7 | f9 | f9 |
| test.cpp:146:5:146:14 | declaration | Unreachable statement in function '$@'. | test.cpp:134:6:134:7 | f9 | f9 |
| test.cpp:158:3:134:7 | declaration | Unreachable statement in function '$@'. | test.cpp:134:6:134:7 | f9 | f9 |
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The location range for this expected result is incorrect. The first line number should be '158' (the line of the unreachable statement) but instead it says '158:3:134:7' where the end location incorrectly references line 134 and column 7, which appears to be the function name location instead of the statement's end location. It should be '158:3:158:12' to correctly span the declaration on line 158.

Suggested change
| test.cpp:158:3:134:7 | declaration | Unreachable statement in function '$@'. | test.cpp:134:6:134:7 | f9 | f9 |
| test.cpp:158:3:158:12 | declaration | Unreachable statement in function '$@'. | test.cpp:134:6:134:7 | f9 | f9 |

Copilot uses AI. Check for mistakes.
| test.cpp:162:17:163:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:161:6:161:8 | f10 | f10 |
| test.cpp:173:5:173:14 | declaration | Unreachable statement in function '$@'. | test.cpp:161:6:161:8 | f10 | f10 |
| test.cpp:185:3:161:8 | declaration | Unreachable statement in function '$@'. | test.cpp:161:6:161:8 | f10 | f10 |
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The location range for this expected result is incorrect. The first line number should be '185' (the line of the unreachable statement) but instead it says '185:3:161:8' where the end location incorrectly references line 161 and column 8, which appears to be the function name location instead of the statement's end location. It should be '185:3:185:12' to correctly span the declaration on line 185.

Suggested change
| test.cpp:185:3:161:8 | declaration | Unreachable statement in function '$@'. | test.cpp:161:6:161:8 | f10 | f10 |
| test.cpp:185:3:185:12 | declaration | Unreachable statement in function '$@'. | test.cpp:161:6:161:8 | f10 | f10 |

Copilot uses AI. Check for mistakes.
| test.cpp:192:3:190:8 | declaration | Unreachable statement in function '$@'. | test.cpp:190:6:190:8 | f11 | f11 |
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The location range for this expected result is incorrect. The first line number should be '192' (the line of the unreachable statement) but instead it says '192:3:190:8' where the end location incorrectly references line 190 and column 8, which appears to be the function name location instead of the statement's end location. It should be '192:3:192:12' to correctly span the declaration on line 192.

Suggested change
| test.cpp:192:3:190:8 | declaration | Unreachable statement in function '$@'. | test.cpp:190:6:190:8 | f11 | f11 |
| test.cpp:192:3:192:12 | declaration | Unreachable statement in function '$@'. | test.cpp:190:6:190:8 | f11 | f11 |

Copilot uses AI. Check for mistakes.
| test.cpp:213:30:214:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:200:6:200:8 | f12 | f12 |
| test.cpp:215:27:216:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:200:6:200:8 | f12 | f12 |
| test.cpp:217:17:218:14 | <handler> | Unreachable statement in function '$@'. | test.cpp:200:6:200:8 | f12 | f12 |
| test.cpp:225:30:226:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:200:6:200:8 | f12 | f12 |
| test.cpp:233:30:234:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:200:6:200:8 | f12 | f12 |
| test.cpp:235:24:236:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:200:6:200:8 | f12 | f12 |
| test.cpp:237:23:238:14 | { ... } | Unreachable statement in function '$@'. | test.cpp:200:6:200:8 | f12 | f12 |
| test.cpp:246:19:247:16 | <handler> | Unreachable statement in function '$@'. | test.cpp:200:6:200:8 | f12 | f12 |
1 change: 1 addition & 0 deletions cpp/misra/test/rules/RULE-0-0-1/UnreachableStatement.qlref
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-0-0-1/UnreachableStatement.ql
Loading
Loading