1 //===--- ExceptionEscapeCheck.cpp - clang-tidy ----------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "ExceptionEscapeCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "llvm/ADT/SmallSet.h"
14 #include "llvm/ADT/StringSet.h"
16 using namespace clang::ast_matchers
;
18 namespace clang::tidy::bugprone
{
21 AST_MATCHER_P(FunctionDecl
, isEnabled
, llvm::StringSet
<>,
22 FunctionsThatShouldNotThrow
) {
23 return FunctionsThatShouldNotThrow
.count(Node
.getNameAsString()) > 0;
26 AST_MATCHER(FunctionDecl
, isExplicitThrow
) {
27 return isExplicitThrowExceptionSpec(Node
.getExceptionSpecType()) &&
28 Node
.getExceptionSpecSourceRange().isValid();
33 ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name
,
34 ClangTidyContext
*Context
)
35 : ClangTidyCheck(Name
, Context
), RawFunctionsThatShouldNotThrow(Options
.get(
36 "FunctionsThatShouldNotThrow", "")),
37 RawIgnoredExceptions(Options
.get("IgnoredExceptions", "")) {
38 llvm::SmallVector
<StringRef
, 8> FunctionsThatShouldNotThrowVec
,
40 StringRef(RawFunctionsThatShouldNotThrow
)
41 .split(FunctionsThatShouldNotThrowVec
, ",", -1, false);
42 FunctionsThatShouldNotThrow
.insert(FunctionsThatShouldNotThrowVec
.begin(),
43 FunctionsThatShouldNotThrowVec
.end());
45 llvm::StringSet
<> IgnoredExceptions
;
46 StringRef(RawIgnoredExceptions
).split(IgnoredExceptionsVec
, ",", -1, false);
47 IgnoredExceptions
.insert(IgnoredExceptionsVec
.begin(),
48 IgnoredExceptionsVec
.end());
49 Tracer
.ignoreExceptions(std::move(IgnoredExceptions
));
50 Tracer
.ignoreBadAlloc(true);
53 void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
54 Options
.store(Opts
, "FunctionsThatShouldNotThrow",
55 RawFunctionsThatShouldNotThrow
);
56 Options
.store(Opts
, "IgnoredExceptions", RawIgnoredExceptions
);
59 void ExceptionEscapeCheck::registerMatchers(MatchFinder
*Finder
) {
61 functionDecl(isDefinition(),
63 allOf(anyOf(cxxDestructorDecl(),
64 cxxConstructorDecl(isMoveConstructor()),
65 cxxMethodDecl(isMoveAssignmentOperator()),
66 isMain(), hasName("swap")),
67 unless(isExplicitThrow())),
68 isEnabled(FunctionsThatShouldNotThrow
)))
73 void ExceptionEscapeCheck::check(const MatchFinder::MatchResult
&Result
) {
74 const auto *MatchedDecl
= Result
.Nodes
.getNodeAs
<FunctionDecl
>("thrower");
79 if (Tracer
.analyze(MatchedDecl
).getBehaviour() ==
80 utils::ExceptionAnalyzer::State::Throwing
)
81 // FIXME: We should provide more information about the exact location where
82 // the exception is thrown, maybe the full path the exception escapes
83 diag(MatchedDecl
->getLocation(), "an exception may be thrown in function "
84 "%0 which should not throw exceptions")
88 } // namespace clang::tidy::bugprone