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 Finder->addMatcher( 56 functionDecl(anyOf(isNoThrow(), cxxDestructorDecl(), 57 cxxConstructorDecl(isMoveConstructor()), 58 cxxMethodDecl(isMoveAssignmentOperator()), 59 hasName("main"), hasName("swap"), 60 isEnabled(FunctionsThatShouldNotThrow))) 61 .bind("thrower"), 62 this); 63 } 64 65 void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) { 66 const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("thrower"); 67 68 if (!MatchedDecl) 69 return; 70 71 if (Tracer.analyze(MatchedDecl).getBehaviour() == 72 utils::ExceptionAnalyzer::State::Throwing) 73 // FIXME: We should provide more information about the exact location where 74 // the exception is thrown, maybe the full path the exception escapes 75 diag(MatchedDecl->getLocation(), "an exception may be thrown in function " 76 "%0 which should not throw exceptions") 77 << MatchedDecl; 78 } 79 80 } // namespace bugprone 81 } // namespace tidy 82 } // namespace clang 83