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