11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "llvm/ADT/StringSet.h"
20 FunctionsThatShouldNotThrow) {
21 return FunctionsThatShouldNotThrow.contains(Node.getNameAsString());
25 return isExplicitThrowExceptionSpec(Node.getExceptionSpecType()) &&
26 Node.getExceptionSpecSourceRange().isValid();
30 return Node.getNumParams() > 0;
37 :
ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get(
38 "FunctionsThatShouldNotThrow",
"")),
39 RawIgnoredExceptions(Options.get(
"IgnoredExceptions",
"")),
40 RawCheckedSwapFunctions(
41 Options.get(
"CheckedSwapFunctions",
"swap,iter_swap,iter_move")),
42 CheckDestructors(Options.get(
"CheckDestructors", true)),
43 CheckMoveMemberFunctions(Options.get(
"CheckMoveMemberFunctions", true)),
44 CheckMain(Options.get(
"CheckMain", true)),
45 CheckNothrowFunctions(Options.get(
"CheckNothrowFunctions", true)) {
46 llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec,
47 IgnoredExceptionsVec, CheckedSwapFunctionsVec;
48 RawFunctionsThatShouldNotThrow.split(FunctionsThatShouldNotThrowVec,
",", -1,
50 FunctionsThatShouldNotThrow.insert_range(FunctionsThatShouldNotThrowVec);
52 RawCheckedSwapFunctions.split(CheckedSwapFunctionsVec,
",", -1,
false);
53 CheckedSwapFunctions.insert_range(CheckedSwapFunctionsVec);
55 llvm::StringSet<> IgnoredExceptions;
56 RawIgnoredExceptions.split(IgnoredExceptionsVec,
",", -1,
false);
57 IgnoredExceptions.insert_range(IgnoredExceptionsVec);
58 Tracer.ignoreExceptions(std::move(IgnoredExceptions));
59 Tracer.ignoreBadAlloc(
true);
63 Options.store(Opts,
"FunctionsThatShouldNotThrow",
64 RawFunctionsThatShouldNotThrow);
65 Options.store(Opts,
"IgnoredExceptions", RawIgnoredExceptions);
66 Options.store(Opts,
"CheckedSwapFunctions", RawCheckedSwapFunctions);
67 Options.store(Opts,
"CheckDestructors", CheckDestructors);
68 Options.store(Opts,
"CheckMoveMemberFunctions", CheckMoveMemberFunctions);
69 Options.store(Opts,
"CheckMain", CheckMain);
70 Options.store(Opts,
"CheckNothrowFunctions", CheckNothrowFunctions);
74 auto MatchIf = [](
bool Enabled,
const auto &Matcher) {
75 ast_matchers::internal::Matcher<FunctionDecl> Nothing = unless(anything());
76 return Enabled ? Matcher : Nothing;
82 MatchIf(CheckNothrowFunctions, isNoThrow()),
83 allOf(anyOf(MatchIf(CheckDestructors, cxxDestructorDecl()),
85 CheckMoveMemberFunctions,
86 anyOf(cxxConstructorDecl(isMoveConstructor()),
87 cxxMethodDecl(isMoveAssignmentOperator()))),
88 MatchIf(CheckMain, isMain()),
89 allOf(isEnabled(CheckedSwapFunctions),
90 hasAtLeastOneParameter())),
91 unless(isExplicitThrow())),
92 isEnabled(FunctionsThatShouldNotThrow)))
98 const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>(
"thrower");
103 const utils::ExceptionAnalyzer::ExceptionInfo Info =
109 diag(MatchedDecl->getLocation(),
"an exception may be thrown in function "
110 "%0 which should not throw exceptions")
113 const auto &[ThrowType, ThrowInfo] = *Info.getExceptions().begin();
115 if (ThrowInfo.Loc.isInvalid())
120 "frame #0: unhandled exception of type %0 may be thrown in function %1 "
123 << QualType(ThrowType, 0U) << Stack.back().first;
126 for (
auto CurrIt = ++Stack.rbegin(), PrevIt = Stack.rbegin();
127 CurrIt != Stack.rend(); ++CurrIt, ++PrevIt) {
128 const FunctionDecl *CurrFunction = CurrIt->first;
129 const FunctionDecl *PrevFunction = PrevIt->first;
130 const SourceLocation PrevLocation = PrevIt->second;
131 if (PrevLocation.isValid()) {
132 diag(PrevLocation,
"frame #%0: function %1 calls function %2 here",
134 << FrameNo << CurrFunction << PrevFunction;
136 diag(CurrFunction->getLocation(),
137 "frame #%0: function %1 calls function %2", DiagnosticIDs::Note)
138 << FrameNo << CurrFunction << PrevFunction;
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
ExceptionEscapeCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
@ Throwing
The function can definitely throw given an AST.
llvm::MapVector< const FunctionDecl *, SourceLocation > CallStack
We use a MapVector to preserve the order of the functions in the call stack as well as have fast look...
ExceptionInfo analyze(const FunctionDecl *Func)
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)
llvm::StringMap< ClangTidyValue > OptionMap