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::tidy::bugprone { 19 namespace { 20 21 AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>, 22 FunctionsThatShouldNotThrow) { 23 return FunctionsThatShouldNotThrow.contains(Node.getNameAsString()); 24 } 25 26 AST_MATCHER(FunctionDecl, isExplicitThrow) { 27 return isExplicitThrowExceptionSpec(Node.getExceptionSpecType()) && 28 Node.getExceptionSpecSourceRange().isValid(); 29 } 30 31 AST_MATCHER(FunctionDecl, hasAtLeastOneParameter) { 32 return Node.getNumParams() > 0; 33 } 34 35 } // namespace 36 37 ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name, 38 ClangTidyContext *Context) 39 : ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get( 40 "FunctionsThatShouldNotThrow", "")), 41 RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) { 42 llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec, 43 IgnoredExceptionsVec; 44 StringRef(RawFunctionsThatShouldNotThrow) 45 .split(FunctionsThatShouldNotThrowVec, ",", -1, false); 46 FunctionsThatShouldNotThrow.insert(FunctionsThatShouldNotThrowVec.begin(), 47 FunctionsThatShouldNotThrowVec.end()); 48 49 llvm::StringSet<> IgnoredExceptions; 50 StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false); 51 IgnoredExceptions.insert(IgnoredExceptionsVec.begin(), 52 IgnoredExceptionsVec.end()); 53 Tracer.ignoreExceptions(std::move(IgnoredExceptions)); 54 Tracer.ignoreBadAlloc(true); 55 } 56 57 void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 58 Options.store(Opts, "FunctionsThatShouldNotThrow", 59 RawFunctionsThatShouldNotThrow); 60 Options.store(Opts, "IgnoredExceptions", RawIgnoredExceptions); 61 } 62 63 void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) { 64 Finder->addMatcher( 65 functionDecl( 66 isDefinition(), 67 anyOf(isNoThrow(), 68 allOf(anyOf(cxxDestructorDecl(), 69 cxxConstructorDecl(isMoveConstructor()), 70 cxxMethodDecl(isMoveAssignmentOperator()), isMain(), 71 allOf(hasAnyName("swap", "iter_swap", "iter_move"), 72 hasAtLeastOneParameter())), 73 unless(isExplicitThrow())), 74 isEnabled(FunctionsThatShouldNotThrow))) 75 .bind("thrower"), 76 this); 77 } 78 79 void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) { 80 const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("thrower"); 81 82 if (!MatchedDecl) 83 return; 84 85 if (Tracer.analyze(MatchedDecl).getBehaviour() == 86 utils::ExceptionAnalyzer::State::Throwing) 87 // FIXME: We should provide more information about the exact location where 88 // the exception is thrown, maybe the full path the exception escapes 89 diag(MatchedDecl->getLocation(), "an exception may be thrown in function " 90 "%0 which should not throw exceptions") 91 << MatchedDecl; 92 } 93 94 } // namespace clang::tidy::bugprone 95