Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fc00564

Browse files
committedMay 20, 2025·
Implement package exceptions3 for MISRA C++
Adds support for: - RULE-18-3-1 - RULE-18-3-2 - RULE-18-4-1
1 parent 01b11af commit fc00564

33 files changed

+802
-5
lines changed
 

‎cpp/common/src/codingstandards/cpp/Function.qll

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,74 @@ import cpp
88
class PopCount extends Function {
99
PopCount() { this.getName().toLowerCase().matches("%popc%nt%") }
1010
}
11+
12+
/**
13+
* A function the user explicitly declared, though its body may be compiler-generated.
14+
*/
15+
class UserDeclaredFunction extends Function {
16+
UserDeclaredFunction() {
17+
not isDeleted() and
18+
(
19+
// Not compiler generated is explicitly declared
20+
not isCompilerGenerated()
21+
or
22+
// Closure functions are compiler generated, but the user
23+
// explicitly declared the lambda expression.
24+
exists(Closure c | c.getLambdaFunction() = this)
25+
or
26+
// Indicates users specifically wrote "= default"
27+
isDefaulted()
28+
)
29+
}
30+
}
31+
32+
/**
33+
* An expression that is effectively a function access.
34+
*
35+
* There are more ways to effectively access a function than a basic `FunctionAcess`, for instance
36+
* taking the address of a function access, and declaring/converting a lambda function.
37+
*/
38+
abstract class FunctionAccessLikeExpr extends Expr {
39+
abstract Function getFunction();
40+
}
41+
42+
/**
43+
* A basic function access expression.
44+
*/
45+
class FunctionAccessExpr extends FunctionAccess, FunctionAccessLikeExpr {
46+
override Function getFunction() { result = this.getTarget() }
47+
}
48+
49+
/**
50+
* Taking an address of a function access in effectively a function access.
51+
*/
52+
class FunctionAddressExpr extends AddressOfExpr, FunctionAccessLikeExpr {
53+
Function func;
54+
55+
FunctionAddressExpr() { func = getOperand().(FunctionAccessLikeExpr).getFunction() }
56+
57+
override Function getFunction() { result = func }
58+
}
59+
60+
/**
61+
* An expression that declares a lambda function is essentially a function access of the lambda
62+
* function body.
63+
*/
64+
class LambdaValueExpr extends LambdaExpression, FunctionAccessLikeExpr {
65+
override Function getFunction() { result = this.(LambdaExpression).getLambdaFunction() }
66+
}
67+
68+
/**
69+
* When a lambda is converted via conversion operator to a function pointer, it is
70+
* effectively a function access of the lambda function.
71+
*/
72+
class LambdaConversionExpr extends FunctionCall, FunctionAccessLikeExpr {
73+
Function func;
74+
75+
LambdaConversionExpr() {
76+
getTarget() instanceof ConversionOperator and
77+
func = getQualifier().(LambdaValueExpr).getFunction()
78+
}
79+
80+
override Function getFunction() { result = func }
81+
}

‎cpp/common/src/codingstandards/cpp/exceptions/ExceptionFlow.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ TryStmt getNearestTry(Stmt s) {
121121
else result = getNearestTry(s.getParent())
122122
}
123123

124+
/** Gets a try statement that contains the given statement. */
125+
TryStmt getATryStmt(Stmt s) {
126+
result.getStmt() = s.getEnclosingStmt().getEnclosingBlock*()
127+
}
128+
124129
/** Gets the nearest parent catch block for the given statement. */
125130
CatchBlock getNearestCatch(Stmt s) {
126131
if s.getParent() instanceof CatchBlock

‎cpp/common/src/codingstandards/cpp/exceptions/SpecialFunctionExceptions.qll

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,102 @@
1616

1717
import cpp
1818
private import ExceptionFlow
19+
private import codingstandards.cpp.Function
1920
private import codingstandards.cpp.Swap
2021
private import codingstandards.cpp.EncapsulatingFunctions
2122
private import codingstandards.cpp.exceptions.ExceptionFlow
2223

2324
/** A special function. */
2425
class SpecialFunction extends Function {
26+
string specialDescription;
27+
2528
SpecialFunction() {
26-
this instanceof MoveAssignmentOperator
29+
this instanceof MoveAssignmentOperator and
30+
specialDescription = "move assignment operator"
2731
or
28-
this instanceof MoveConstructor
32+
this instanceof MoveConstructor and
33+
specialDescription = "move constructor"
2934
or
30-
this instanceof Destructor
35+
this instanceof Destructor and
36+
specialDescription = "destructor"
3137
or
32-
this instanceof DeallocationFunction
38+
this instanceof DeallocationFunction and
39+
specialDescription = "deallocation function"
3340
or
34-
this instanceof SwapFunction
41+
this instanceof SwapFunction and
42+
specialDescription = "swap function"
43+
}
44+
45+
string getSpecialDescription() { result = specialDescription }
46+
}
47+
48+
/**
49+
* A function which isn't special by itself, but is used in a special context.
50+
*
51+
* For example, functions which are reachable from initializers of static or thread-local
52+
* variables can result in implementation-defined behavior if they throw exceptions.
53+
*/
54+
abstract class SpecialUseOfFunction extends Expr {
55+
abstract Function getFunction();
56+
57+
abstract string getSpecialDescription(Locatable extra, string extraString);
58+
}
59+
60+
class FunctionUsedInInitializer extends FunctionCall, SpecialUseOfFunction {
61+
Variable var;
62+
63+
FunctionUsedInInitializer() {
64+
(var.isStatic() or var.isThreadLocal() or var.isTopLevel()) and
65+
exists(Expr initializer |
66+
var.getInitializer().getExpr() = initializer and
67+
getParent*() = initializer
68+
)
69+
}
70+
71+
override Function getFunction() { result = this.getTarget() }
72+
73+
override string getSpecialDescription(Locatable extra, string extraString) {
74+
result = "used to initialize variable $@" and
75+
extra = var and
76+
extraString = var.getName()
77+
}
78+
}
79+
80+
class FunctionGivenToStdExitHandler extends FunctionAccess, SpecialUseOfFunction {
81+
Function exitHandler;
82+
FunctionCall exitHandlerCall;
83+
84+
FunctionGivenToStdExitHandler() {
85+
exitHandler.hasGlobalOrStdName(["atexit", "at_quick_exit", "set_terminate"]) and
86+
exitHandlerCall.getTarget() = exitHandler and
87+
exitHandlerCall.getArgument(0) = this
88+
}
89+
90+
override Function getFunction() { result = getTarget() }
91+
92+
override string getSpecialDescription(Locatable extra, string extraString) {
93+
result = "$@" and
94+
extra = exitHandlerCall and
95+
extraString = "passed exit handler std::" + exitHandler.getName()
96+
}
97+
}
98+
99+
class FunctionPassedToExternC extends FunctionAccessLikeExpr, SpecialUseOfFunction {
100+
Function cFunction;
101+
FunctionCall cFunctionCall;
102+
103+
FunctionPassedToExternC() {
104+
cFunction.hasCLinkage() and
105+
cFunction = cFunctionCall.getTarget() and
106+
cFunctionCall.getAnArgument() = this
107+
}
108+
109+
override Function getFunction() { result = this.(FunctionAccessLikeExpr).getFunction() }
110+
111+
override string getSpecialDescription(Locatable extra, string extraString) {
112+
result = "$@ extern \"C\" function '" + cFunction.getName() + "'" and
113+
extra = cFunctionCall and
114+
extraString = "passed to"
35115
}
36116
}
37117

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype Exceptions3Query =
7+
TMissingCatchAllExceptionHandlerInMainQuery() or
8+
TClassExceptionCaughtByValueQuery() or
9+
TExceptionUnfriendlyFunctionMustBeNoexceptQuery()
10+
11+
predicate isExceptions3QueryMetadata(Query query, string queryId, string ruleId, string category) {
12+
query =
13+
// `Query` instance for the `missingCatchAllExceptionHandlerInMain` query
14+
Exceptions3Package::missingCatchAllExceptionHandlerInMainQuery() and
15+
queryId =
16+
// `@id` for the `missingCatchAllExceptionHandlerInMain` query
17+
"cpp/misra/missing-catch-all-exception-handler-in-main" and
18+
ruleId = "RULE-18-3-1" and
19+
category = "advisory"
20+
or
21+
query =
22+
// `Query` instance for the `classExceptionCaughtByValue` query
23+
Exceptions3Package::classExceptionCaughtByValueQuery() and
24+
queryId =
25+
// `@id` for the `classExceptionCaughtByValue` query
26+
"cpp/misra/class-exception-caught-by-value" and
27+
ruleId = "RULE-18-3-2" and
28+
category = "required"
29+
or
30+
query =
31+
// `Query` instance for the `exceptionUnfriendlyFunctionMustBeNoexcept` query
32+
Exceptions3Package::exceptionUnfriendlyFunctionMustBeNoexceptQuery() and
33+
queryId =
34+
// `@id` for the `exceptionUnfriendlyFunctionMustBeNoexcept` query
35+
"cpp/misra/exception-unfriendly-function-must-be-noexcept" and
36+
ruleId = "RULE-18-4-1" and
37+
category = "required"
38+
}
39+
40+
module Exceptions3Package {
41+
Query missingCatchAllExceptionHandlerInMainQuery() {
42+
//autogenerate `Query` type
43+
result =
44+
// `Query` type for `missingCatchAllExceptionHandlerInMain` query
45+
TQueryCPP(TExceptions3PackageQuery(TMissingCatchAllExceptionHandlerInMainQuery()))
46+
}
47+
48+
Query classExceptionCaughtByValueQuery() {
49+
//autogenerate `Query` type
50+
result =
51+
// `Query` type for `classExceptionCaughtByValue` query
52+
TQueryCPP(TExceptions3PackageQuery(TClassExceptionCaughtByValueQuery()))
53+
}
54+
55+
Query exceptionUnfriendlyFunctionMustBeNoexceptQuery() {
56+
//autogenerate `Query` type
57+
result =
58+
// `Query` type for `exceptionUnfriendlyFunctionMustBeNoexcept` query
59+
TQueryCPP(TExceptions3PackageQuery(TExceptionUnfriendlyFunctionMustBeNoexceptQuery()))
60+
}
61+
}

‎cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Declarations
1717
import ExceptionSafety
1818
import Exceptions1
1919
import Exceptions2
20+
import Exceptions3
2021
import Expressions
2122
import FloatingPoint
2223
import Freed
@@ -72,6 +73,7 @@ newtype TCPPQuery =
7273
TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or
7374
TExceptions1PackageQuery(Exceptions1Query q) or
7475
TExceptions2PackageQuery(Exceptions2Query q) or
76+
TExceptions3PackageQuery(Exceptions3Query q) or
7577
TExpressionsPackageQuery(ExpressionsQuery q) or
7678
TFloatingPointPackageQuery(FloatingPointQuery q) or
7779
TFreedPackageQuery(FreedQuery q) or
@@ -127,6 +129,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
127129
isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or
128130
isExceptions1QueryMetadata(query, queryId, ruleId, category) or
129131
isExceptions2QueryMetadata(query, queryId, ruleId, category) or
132+
isExceptions3QueryMetadata(query, queryId, ruleId, category) or
130133
isExpressionsQueryMetadata(query, queryId, ruleId, category) or
131134
isFloatingPointQueryMetadata(query, queryId, ruleId, category) or
132135
isFreedQueryMetadata(query, queryId, ruleId, category) or
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @id cpp/misra/missing-catch-all-exception-handler-in-main
3+
* @name RULE-18-3-1: There should be at least one exception handler to catch all otherwise unhandled exceptions
4+
* @description The main function should have a catch-all exception handler (catch(...)) to catch
5+
* all otherwise unhandled exceptions.
6+
* @kind problem
7+
* @precision high
8+
* @problem.severity warning
9+
* @tags external/misra/id/rule-18-3-1
10+
* scope/single-translation-unit
11+
* maintainability
12+
* external/misra/enforcement/decidable
13+
* external/misra/obligation/advisory
14+
*/
15+
16+
import cpp
17+
import codingstandards.cpp.misra
18+
import codingstandards.cpp.exceptions.ExceptionFlow
19+
import codingstandards.cpp.exceptions.ExceptionSpecifications
20+
21+
/**
22+
* A function call in main that is not declared noexcept and is not within a catch-all
23+
* exception handler (catch(...)).
24+
*/
25+
class UncaughtFunctionCallInMain extends FunctionCall {
26+
UncaughtFunctionCallInMain() {
27+
getEnclosingFunction().hasName("main") and
28+
not isNoExceptTrue(getTarget()) and
29+
not exists(TryStmt try |
30+
try = getATryStmt(this.getEnclosingStmt()) and
31+
try.getCatchClause(_) instanceof CatchAnyBlock
32+
)
33+
}
34+
35+
/**
36+
* We only want to report one counter-example indicating a missing catch(...), so this holds only
37+
* for the first one we find.
38+
*/
39+
predicate isFirst() {
40+
this =
41+
rank[1](UncaughtFunctionCallInMain fc |
42+
any()
43+
|
44+
fc order by fc.getLocation().getStartLine(), fc.getLocation().getStartColumn()
45+
)
46+
}
47+
}
48+
49+
from Function f, UncaughtFunctionCallInMain fc
50+
where
51+
not isExcluded(f, Exceptions3Package::missingCatchAllExceptionHandlerInMainQuery()) and
52+
f.getName() = "main" and
53+
fc.isFirst()
54+
select f,
55+
"Main function has a $@ which is not within a try block with a catch-all ('catch(...)') handler.",
56+
fc, "function call that may throw"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @id cpp/misra/class-exception-caught-by-value
3+
* @name RULE-18-3-2: An exception of class type shall be caught by const reference or reference
4+
* @description Catching exception classes by value can lead to slicing, which can result in
5+
* unexpected behavior.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity error
9+
* @tags external/misra/id/rule-18-3-2
10+
* scope/single-translation-unit
11+
* correctness
12+
* maintainability
13+
* external/misra/enforcement/decidable
14+
* external/misra/obligation/required
15+
*/
16+
17+
import cpp
18+
import codingstandards.cpp.misra
19+
20+
from CatchBlock catch, Type type
21+
where
22+
not isExcluded(catch, Exceptions3Package::classExceptionCaughtByValueQuery()) and
23+
type = catch.getParameter().getType() and
24+
type.stripTopLevelSpecifiers() instanceof Class
25+
select catch, "Catch block catches a class type '" + type + "' by value, which can lead to slicing."
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @id cpp/misra/exception-unfriendly-function-must-be-noexcept
3+
* @name RULE-18-4-1: Exception-unfriendly functions shall be noexcept
4+
* @description Throwing exceptions in constructors, descructors, copy-constructors, move
5+
* constructors, assignments, and functions named swap, may result in
6+
* implementation-defined behavior.
7+
* @kind problem
8+
* @precision very-high
9+
* @problem.severity error
10+
* @tags external/misra/id/rule-18-4-1
11+
* scope/single-translation-unit
12+
* correctness
13+
* maintainability
14+
* external/misra/enforcement/decidable
15+
* external/misra/obligation/required
16+
*/
17+
18+
import cpp
19+
import codingstandards.cpp.misra
20+
import codingstandards.cpp.Function
21+
import codingstandards.cpp.exceptions.ExceptionFlow
22+
import codingstandards.cpp.exceptions.SpecialFunctionExceptions
23+
24+
from UserDeclaredFunction func, string message, Element extra, string extraString
25+
where
26+
not isExcluded([func, extra], Exceptions3Package::exceptionUnfriendlyFunctionMustBeNoexceptQuery()) and
27+
not isNoExceptTrue(func) and
28+
(
29+
message = "a " + func.(SpecialFunction).getSpecialDescription() and
30+
extra = func and
31+
extraString = ""
32+
or
33+
exists(SpecialUseOfFunction specialUse |
34+
specialUse.getFunction() = func and
35+
message = specialUse.getSpecialDescription(extra, extraString)
36+
)
37+
)
38+
select func, "Function '" + func.getName() + "' must be noexcept because it is " + message + ".",
39+
extra, extraString
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| test_missing.cpp:6:5:6:8 | main | Main function has a $@ which is not within a try block with a catch-all ('catch(...)') handler. | test_missing.cpp:9:5:9:5 | call to f | function call that may throw |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-18-3-1/MissingCatchAllExceptionHandlerInMain.ql
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <iostream>
2+
void f() noexcept(false);
3+
4+
void noex() noexcept;
5+
6+
int main() {
7+
noex();
8+
try {
9+
f();
10+
} catch (int x) {
11+
// NON-COMPLIANT: No catch(...) handler.
12+
noex();
13+
}
14+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| test_except_before_catch.cpp:7:5:7:8 | main | Main function has a $@ which is not within a try block with a catch-all ('catch(...)') handler. | test_except_before_catch.cpp:8:3:8:3 | call to f | function call that may throw |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-18-3-1/MissingCatchAllExceptionHandlerInMain.ql
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <iostream>
2+
3+
void f();
4+
5+
void noex() noexcept;
6+
7+
int main() {
8+
f(); // NON-COMPLIANT: f() can throw and is outside of the try-catch(...)
9+
// block.
10+
try {
11+
noex();
12+
f();
13+
} catch (...) {
14+
noex();
15+
}
16+
noex();
17+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| test_except_after_catch.cpp:7:5:7:8 | main | Main function has a $@ which is not within a try block with a catch-all ('catch(...)') handler. | test_except_after_catch.cpp:16:3:16:3 | call to f | function call that may throw |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-18-3-1/MissingCatchAllExceptionHandlerInMain.ql
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <iostream>
2+
3+
void f();
4+
5+
void noex() noexcept;
6+
7+
int main() {
8+
noex();
9+
try {
10+
noex();
11+
f();
12+
} catch (...) {
13+
noex();
14+
}
15+
// NON-COMPLIANT: f() can throw and is outside of the try-catch(...) block.
16+
f();
17+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| test_except_inside_catch.cpp:7:5:7:8 | main | Main function has a $@ which is not within a try block with a catch-all ('catch(...)') handler. | test_except_inside_catch.cpp:14:15:14:15 | call to operator<< | function call that may throw |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-18-3-1/MissingCatchAllExceptionHandlerInMain.ql
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <iostream>
2+
3+
void f();
4+
5+
void noex() noexcept;
6+
7+
int main() {
8+
noex();
9+
try {
10+
noex();
11+
f();
12+
} catch (...) {
13+
// NON-COMPLIANT: cout can throw and is not within a try block.
14+
std::cout << "Caught unknown exception" << std::endl;
15+
}
16+
noex();
17+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| test_noexcept_false.cpp:7:5:7:8 | main | Main function has a $@ which is not within a try block with a catch-all ('catch(...)') handler. | test_noexcept_false.cpp:8:3:8:3 | call to f | function call that may throw |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-18-3-1/MissingCatchAllExceptionHandlerInMain.ql
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <iostream>
2+
3+
void f() noexcept(false);
4+
5+
void noex() noexcept;
6+
7+
int main() {
8+
f(); // NON-COMPLIANT: f() can throw and is outside of the try-catch(...)
9+
// block.
10+
try {
11+
f();
12+
noex();
13+
} catch (...) {
14+
noex();
15+
}
16+
noex();
17+
}

‎cpp/misra/test/rules/RULE-18-3-1/MissingCatchAllExceptionHandlerInMain.expected

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-18-3-1/MissingCatchAllExceptionHandlerInMain.ql
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include <iostream>
2+
3+
void f();
4+
5+
void noex() noexcept;
6+
void noex2() noexcept(true);
7+
8+
int main() {
9+
noex();
10+
noex2();
11+
try {
12+
noex();
13+
noex2();
14+
f();
15+
} catch (...) {
16+
try {
17+
noex();
18+
noex2();
19+
std::cout << "Caught unknown exception" << std::endl;
20+
} catch (...) {
21+
// COMPLIANT: All exceptions caught via catch(...), even exceptions from
22+
// cout.
23+
noex();
24+
noex2();
25+
}
26+
}
27+
noex();
28+
noex2();
29+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
| test.cpp:25:18:27:3 | { ... } | Catch block catches a class type 's1' by value, which can lead to slicing. |
2+
| test.cpp:27:24:29:3 | { ... } | Catch block catches a class type 'const s1' by value, which can lead to slicing. |
3+
| test.cpp:37:24:39:3 | { ... } | Catch block catches a class type 'const C1' by value, which can lead to slicing. |
4+
| test.cpp:39:18:41:3 | { ... } | Catch block catches a class type 'C1' by value, which can lead to slicing. |
5+
| test.cpp:49:20:51:3 | { ... } | Catch block catches a class type 's1_t' by value, which can lead to slicing. |
6+
| test.cpp:51:26:53:3 | { ... } | Catch block catches a class type 's1_const_t' by value, which can lead to slicing. |
7+
| test.cpp:53:20:55:3 | { ... } | Catch block catches a class type 'C1_t' by value, which can lead to slicing. |
8+
| test.cpp:55:26:57:3 | { ... } | Catch block catches a class type 'C1_const_t' by value, which can lead to slicing. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-18-3-2/ClassExceptionCaughtByValue.ql
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
struct s1 {
2+
int m;
3+
};
4+
5+
class C1 {};
6+
7+
typedef s1 s1_t;
8+
typedef const s1 s1_const_t;
9+
typedef C1 C1_t;
10+
typedef const C1 C1_const_t;
11+
12+
void f() {
13+
try {
14+
15+
} catch (int i) {
16+
// COMPLIANT: Primitives will not be sliced when caught by value.
17+
} catch (long l) {
18+
// COMPLIANT: Primitives will not be sliced when caught by value.
19+
} catch (float f) {
20+
// COMPLIANT: Primitives will not be sliced when caught by value.
21+
} catch (char c) {
22+
// COMPLIANT: Primitives will not be sliced when caught by value.
23+
} catch (int *p) {
24+
// COMPLIANT: Pointers will not be sliced when caught by value.
25+
} catch (s1 s) {
26+
// NON-COMPLIANT: User-defined types will be sliced when caught by value.
27+
} catch (const s1 s) {
28+
// NON-COMPLIANT: User-defined types will be sliced when caught by value.
29+
} catch (s1 *p) {
30+
// COMPLIANT: Pointers will not be sliced when caught by value.
31+
} catch (s1 *const p) {
32+
// COMPLIANT: Pointers will not be sliced when caught by value.
33+
} catch (s1 &r) {
34+
// COMPLIANT: References will not be sliced when caught by value.
35+
} catch (s1 &const r) {
36+
// COMPLIANT: Const eferences will not be sliced when caught by value.
37+
} catch (const C1 c) {
38+
// NON-COMPLIANT: User-defined types will be sliced when caught by value.
39+
} catch (C1 c) {
40+
// NON-COMPLIANT: User-defined types will be sliced when caught by value.
41+
} catch (C1 *p) {
42+
// COMPLIANT: Pointers will not be sliced when caught by value.
43+
} catch (C1 *const p) {
44+
// COMPLIANT: Pointers will not be sliced when caught by value.
45+
} catch (C1 &r) {
46+
// COMPLIANT: References will not be sliced when caught by value.
47+
} catch (C1 &const r) {
48+
// COMPLIANT: Const eferences will not be sliced when caught by value.
49+
} catch (s1_t s) {
50+
// NON-COMPLIANT: User-defined types will be sliced when caught by value.
51+
} catch (s1_const_t s) {
52+
// NON-COMPLIANT: User-defined types will be sliced when caught by value.
53+
} catch (C1_t c) {
54+
// NON-COMPLIANT: User-defined types will be sliced when caught by value.
55+
} catch (C1_const_t c) {
56+
// NON-COMPLIANT: User-defined types will be sliced when caught by value.
57+
} catch (s1_t *p) {
58+
// COMPLIANT: Pointers will not be sliced when caught by value.
59+
} catch (s1_t *const p) {
60+
// COMPLIANT: Pointers will not be sliced when caught by value.
61+
} catch (s1_t &r) {
62+
// COMPLIANT: References will not be sliced when caught by value.
63+
} catch (s1_t &const r) {
64+
// COMPLIANT: Const eferences will not be sliced when caught by value.
65+
} catch (C1_t *p) {
66+
// COMPLIANT: Pointers will not be sliced when caught by value.
67+
} catch (C1_t *const p) {
68+
// COMPLIANT: Pointers will not be sliced when caught by value.
69+
} catch (C1_t &r) {
70+
// COMPLIANT: References will not be sliced when caught by value.
71+
} catch (C1_t &const r) {
72+
// COMPLIANT: Const eferences will not be sliced when caught by value.
73+
} catch (s1_const_t *p) {
74+
// COMPLIANT: Pointers will not be sliced when caught by value.
75+
} catch (s1_const_t *const p) {
76+
// COMPLIANT: Pointers will not be sliced when caught by value.
77+
} catch (s1_const_t &r) {
78+
// COMPLIANT: References will not be sliced when caught by value.
79+
} catch (s1_const_t &const r) {
80+
// COMPLIANT: Const eferences will not be sliced when caught by value.
81+
} catch (C1_const_t *p) {
82+
// COMPLIANT: Pointers will not be sliced when caught by value.
83+
} catch (C1_const_t *const p) {
84+
// COMPLIANT: Pointers will not be sliced when caught by value.
85+
} catch (C1_const_t &r) {
86+
// COMPLIANT: References will not be sliced when caught by value.
87+
} catch (C1_const_t &const r) {
88+
// COMPLIANT: Const eferences will not be sliced when caught by value.
89+
} catch (...) {
90+
// COMPLIANT: Catch-all handler.
91+
}
92+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
| test.cpp:38:3:38:20 | ~NonCompliantClass | Function '~NonCompliantClass' must be noexcept because it is a destructor. | test.cpp:38:3:38:20 | ~NonCompliantClass | |
2+
| test.cpp:40:3:40:19 | NonCompliantClass | Function 'NonCompliantClass' must be noexcept because it is a move constructor. | test.cpp:40:3:40:19 | NonCompliantClass | |
3+
| test.cpp:49:22:49:30 | operator= | Function 'operator=' must be noexcept because it is a move assignment operator. | test.cpp:49:22:49:30 | operator= | |
4+
| test.cpp:51:15:51:18 | swap | Function 'swap' must be noexcept because it is a swap function. | test.cpp:51:15:51:18 | swap | |
5+
| test.cpp:62:3:62:24 | ConstructorNotNoexcept | Function 'ConstructorNotNoexcept' must be noexcept because it is used to initialize variable $@. | test.cpp:67:5:67:6 | g2 | g2 |
6+
| test.cpp:62:3:62:24 | ConstructorNotNoexcept | Function 'ConstructorNotNoexcept' must be noexcept because it is used to initialize variable $@. | test.cpp:71:5:71:6 | g4 | g4 |
7+
| test.cpp:62:3:62:24 | ConstructorNotNoexcept | Function 'ConstructorNotNoexcept' must be noexcept because it is used to initialize variable $@. | test.cpp:74:25:74:26 | g6 | g6 |
8+
| test.cpp:62:3:62:24 | ConstructorNotNoexcept | Function 'ConstructorNotNoexcept' must be noexcept because it is used to initialize variable $@. | test.cpp:87:7:87:8 | l4 | l4 |
9+
| test.cpp:62:3:62:24 | ConstructorNotNoexcept | Function 'ConstructorNotNoexcept' must be noexcept because it is used to initialize variable $@. | test.cpp:91:7:91:8 | l6 | l6 |
10+
| test.cpp:97:5:97:23 | calledInInitializer | Function 'calledInInitializer' must be noexcept because it is used to initialize variable $@. | test.cpp:100:5:100:6 | g9 | g9 |
11+
| test.cpp:109:5:109:15 | some_func_t | Function 'some_func_t' must be noexcept because it is $@ extern "C" function 'take_int_c'. | test.cpp:116:3:116:12 | call to take_int_c | passed to |
12+
| test.cpp:123:16:123:16 | operator() | Function 'operator()' must be noexcept because it is $@ extern "C" function 'take_int_c'. | test.cpp:123:3:123:12 | call to take_int_c | passed to |
13+
| test.cpp:131:6:131:17 | exit_handler | Function 'exit_handler' must be noexcept because it is $@. | test.cpp:135:3:135:13 | call to atexit | passed exit handler std::atexit |
14+
| test.cpp:131:6:131:17 | exit_handler | Function 'exit_handler' must be noexcept because it is $@. | test.cpp:139:3:139:20 | call to at_quick_exit | passed exit handler std::at_quick_exit |
15+
| test.cpp:131:6:131:17 | exit_handler | Function 'exit_handler' must be noexcept because it is $@. | test.cpp:144:3:144:20 | call to set_terminate | passed exit handler std::set_terminate |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-18-4-1/ExceptionUnfriendlyFunctionMustBeNoexcept.ql
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#include <cstdlib>
2+
#include <exception>
3+
4+
class CompliantClass {
5+
public:
6+
CompliantClass() noexcept {} // COMPLIANT: constructor is noexcept
7+
~CompliantClass() {} // COMPLIANT: destructor defaults to noexcept
8+
9+
// COMPLIANT: copy constructor is noexcept
10+
CompliantClass(const CompliantClass &) noexcept {}
11+
12+
// COMPLIANT: move constructor is noexcept
13+
CompliantClass(CompliantClass &&) noexcept {}
14+
15+
// COMPLIANT: copy assignment operator is noexcept
16+
CompliantClass &operator=(const CompliantClass &) noexcept { return *this; }
17+
18+
// COMPLIANT: move assignment operator is noexcept
19+
CompliantClass &operator=(CompliantClass &&) noexcept { return *this; }
20+
21+
// COMPLIANT: swap function is noexcept
22+
friend void swap(CompliantClass &a, CompliantClass &b) noexcept;
23+
24+
void f() {} // COMPLIANT: Not a special function.
25+
};
26+
27+
class NonCompliantClass {
28+
public:
29+
// COMPLIANT: constructor doesn't need to be noexcept
30+
NonCompliantClass() {}
31+
32+
// COMPLIANT: copy constructor doesn't need to be noexcept. It seems the rule
33+
// is satisfied if the move constructor is noexcept. If the move constructor
34+
// is noexcept, the standard containers will safely use it instead of the copy
35+
// constructor.
36+
NonCompliantClass(const NonCompliantClass &) {} // COMPLIANT
37+
38+
~NonCompliantClass() noexcept(false) {} // NON-COMPLIANT
39+
40+
NonCompliantClass(NonCompliantClass &&) {} // NON-COMPLIANT
41+
42+
// COMPLIANT: copy assignment doesn't need to be noexcept. It seems the rule
43+
// is satisfied if the move assignment operator is noexcept. If the move
44+
// constructor is noexcept, the standard containers will safely use it instead
45+
// of the copy assignment operator.
46+
NonCompliantClass &operator=(const NonCompliantClass &) { return *this; }
47+
48+
// NON-COMPLIANT
49+
NonCompliantClass &operator=(NonCompliantClass &&) { return *this; }
50+
51+
friend void swap(NonCompliantClass &a, NonCompliantClass &b) {
52+
} // NON-COMPLIANT
53+
};
54+
55+
class DeleteDestructorOk {
56+
DeleteDestructorOk() = delete; // COMPLIANT
57+
};
58+
59+
class ConstructorNotNoexcept {
60+
public:
61+
// COMPLIANT: at definition, but checked at use site.
62+
ConstructorNotNoexcept() {}
63+
};
64+
65+
CompliantClass g1; // COMPLIANT: static initialization with noexcept
66+
ConstructorNotNoexcept
67+
g2; // NON-COMPLIANT: static initialization without noexcept
68+
thread_local CompliantClass
69+
g3; // COMPLIANT: thread-local initialization with noexcept
70+
thread_local ConstructorNotNoexcept
71+
g4; // NON-COMPLIANT: thread-local initialization without noexcept
72+
CompliantClass *g5 = new CompliantClass(); // COMPLIANT: initializing dynamic
73+
// allocation with noexcept
74+
ConstructorNotNoexcept *g6 =
75+
new ConstructorNotNoexcept(); // NON-COMPLIANT: initializing dynamic
76+
// allocation without noexcept
77+
78+
// Invalid cpp: cannot use constexpr with out noexcept constructor.
79+
// constexpr CompliantClass g7;
80+
// constexpr ConstructorNotNoexcept g8;
81+
82+
void f1() {
83+
CompliantClass l1; // COMPLIANT: local initialization with noexcept
84+
ConstructorNotNoexcept l2; // COMPLIANT: local initialization without noexcept
85+
static CompliantClass l3; // COMPLIANT: static initialization with noexcept
86+
static ConstructorNotNoexcept
87+
l4; // NON-COMPLIANT: static initialization without noexcept
88+
thread_local CompliantClass
89+
l5; // COMPLIANT: thread-local initialization with noexcept
90+
thread_local ConstructorNotNoexcept
91+
l6; // NON-COMPLIANT: thread-local initialization without noexcept
92+
new CompliantClass(); // COMPLIANT: dynamic allocation with noexcept
93+
new ConstructorNotNoexcept(); // COMPLIANT: not initializing allocation
94+
// without noexcept
95+
}
96+
97+
int calledInInitializer() { return 0; }
98+
int calledInInitializerNoexcept() noexcept { return 0; }
99+
100+
int g9 = calledInInitializer(); // NON-COMPLIANT
101+
int g10 = calledInInitializerNoexcept(); // COMPLIANT
102+
103+
typedef int func_t(int);
104+
void take_int_cpp(func_t f);
105+
extern "C" {
106+
void take_int_c(func_t f);
107+
}
108+
109+
int some_func_t(int) { return 0; }
110+
int some_func_t_noexcept(int) noexcept { return 0; }
111+
112+
void f2() {
113+
take_int_cpp(some_func_t); // COMPLIANT: passing function pointer to cpp
114+
take_int_cpp(some_func_t_noexcept); // COMPLIANT: passing noexcept function
115+
// pointer to cpp
116+
take_int_c(some_func_t); // NON-COMPLIANT: passing function pointer to c
117+
// without noexcept
118+
take_int_c(some_func_t_noexcept); // COMPLIANT: passing function pointer to c
119+
// with noexcept
120+
take_int_cpp([](int) {
121+
return 0;
122+
}); // COMPLIANT: passing noexcept function pointer to cpp
123+
take_int_c([](int) {
124+
return 0;
125+
}); // NON-COMPLIANT: passing function pointer to c without noexcept
126+
take_int_c([](int) noexcept {
127+
return 0;
128+
}); // COMPLIANT: passing function pointer to c with noexcept
129+
}
130+
131+
void exit_handler() {}
132+
void exit_handler_noexcept() noexcept {}
133+
134+
void f3() {
135+
std::atexit(exit_handler); // NON-COMPLIANT: passing function pointer to
136+
// atexit without noexcept
137+
std::atexit(exit_handler_noexcept); // COMPLIANT: passing function pointer to
138+
// atexit with noexcept
139+
std::at_quick_exit(exit_handler); // NON-COMPLIANT: passing function pointer
140+
// to at_quick_exit without noexcept
141+
std::at_quick_exit(
142+
exit_handler_noexcept); // COMPLIANT: passing function pointer to
143+
// at_quick_exit with noexcept
144+
std::set_terminate(exit_handler); // NON-COMPLIANT: passing function pointer
145+
// to set_terminate without noexcept
146+
std::set_terminate(
147+
exit_handler_noexcept); // COMPLIANT: passing function pointer to
148+
// set_terminate with noexcept
149+
}

‎rule_packages/cpp/Exceptions3.json

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{
2+
"MISRA-C++-2023": {
3+
"RULE-18-3-1": {
4+
"properties": {
5+
"enforcement": "decidable",
6+
"obligation": "advisory"
7+
},
8+
"queries": [
9+
{
10+
"description": "The main function should have a catch-all exception handler (catch(...)) to catch all otherwise unhandled exceptions.",
11+
"kind": "problem",
12+
"name": "There should be at least one exception handler to catch all otherwise unhandled exceptions",
13+
"precision": "high",
14+
"severity": "warning",
15+
"short_name": "MissingCatchAllExceptionHandlerInMain",
16+
"tags": [
17+
"scope/single-translation-unit",
18+
"maintainability"
19+
]
20+
}
21+
],
22+
"title": "There should be at least one exception handler to catch all otherwise unhandled exceptions"
23+
},
24+
"RULE-18-3-2": {
25+
"properties": {
26+
"enforcement": "decidable",
27+
"obligation": "required"
28+
},
29+
"queries": [
30+
{
31+
"description": "Catching exception classes by value can lead to slicing, which can result in unexpected behavior.",
32+
"kind": "problem",
33+
"name": "An exception of class type shall be caught by const reference or reference",
34+
"precision": "very-high",
35+
"severity": "error",
36+
"short_name": "ClassExceptionCaughtByValue",
37+
"tags": [
38+
"scope/single-translation-unit",
39+
"correctness",
40+
"maintainability"
41+
]
42+
}
43+
],
44+
"title": "An exception of class type shall be caught by const reference or reference"
45+
},
46+
"RULE-18-4-1": {
47+
"properties": {
48+
"enforcement": "decidable",
49+
"obligation": "required"
50+
},
51+
"queries": [
52+
{
53+
"description": "Throwing exceptions in constructors, descructors, copy-constructors, move constructors, assignments, and functions named swap, may result in implementation-defined behavior.",
54+
"kind": "problem",
55+
"name": "Exception-unfriendly functions shall be noexcept",
56+
"precision": "very-high",
57+
"severity": "error",
58+
"short_name": "ExceptionUnfriendlyFunctionMustBeNoexcept",
59+
"tags": [
60+
"scope/single-translation-unit",
61+
"correctness",
62+
"maintainability"
63+
]
64+
}
65+
],
66+
"title": "Exception-unfriendly functions shall be noexcept"
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)
Please sign in to comment.