1 //===--- ExceptionEscapeCheck.cpp - clang-tidy ----------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ExceptionEscapeCheck.h"
10 
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "llvm/ADT/SmallSet.h"
14 #include "llvm/ADT/StringSet.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace {
20 AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>,
21               FunctionsThatShouldNotThrow) {
22   return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0;
23 }
24 } // namespace
25 
26 namespace tidy {
27 namespace bugprone {
28 ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name,
29                                            ClangTidyContext *Context)
30     : ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get(
31                                          "FunctionsThatShouldNotThrow", "")),
32       RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) {
33   llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec,
34       IgnoredExceptionsVec;
35   StringRef(RawFunctionsThatShouldNotThrow)
36       .split(FunctionsThatShouldNotThrowVec, ",", -1, false);
37   FunctionsThatShouldNotThrow.insert(FunctionsThatShouldNotThrowVec.begin(),
38                                      FunctionsThatShouldNotThrowVec.end());
39 
40   llvm::StringSet<> IgnoredExceptions;
41   StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false);
42   IgnoredExceptions.insert(IgnoredExceptionsVec.begin(),
43                            IgnoredExceptionsVec.end());
44   Tracer.ignoreExceptions(std::move(IgnoredExceptions));
45   Tracer.ignoreBadAlloc(true);
46 }
47 
48 void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
49   Options.store(Opts, "FunctionsThatShouldNotThrow",
50                 RawFunctionsThatShouldNotThrow);
51   Options.store(Opts, "IgnoredExceptions", RawIgnoredExceptions);
52 }
53 
54 void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) {
55   if (!getLangOpts().CPlusPlus || !getLangOpts().CXXExceptions)
56     return;
57 
58   Finder->addMatcher(
59       functionDecl(anyOf(isNoThrow(), cxxDestructorDecl(),
60                          cxxConstructorDecl(isMoveConstructor()),
61                          cxxMethodDecl(isMoveAssignmentOperator()),
62                          hasName("main"), hasName("swap"),
63                          isEnabled(FunctionsThatShouldNotThrow)))
64           .bind("thrower"),
65       this);
66 }
67 
68 void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) {
69   const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("thrower");
70 
71   if (!MatchedDecl)
72     return;
73 
74   if (Tracer.analyze(MatchedDecl).getBehaviour() ==
75       utils::ExceptionAnalyzer::State::Throwing)
76     // FIXME: We should provide more information about the exact location where
77     // the exception is thrown, maybe the full path the exception escapes
78     diag(MatchedDecl->getLocation(),
79          "an exception may be thrown in function %0 "
80 
81          "which should not throw exceptions")
82         << MatchedDecl;
83 }
84 
85 } // namespace bugprone
86 } // namespace tidy
87 } // namespace clang
88