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 #include "clang/AST/ASTContext.h" 11 #include "clang/AST/OpenMPClause.h" 12 #include "clang/AST/Stmt.h" 13 #include "clang/AST/StmtOpenMP.h" 14 #include "clang/ASTMatchers/ASTMatchFinder.h" 15 #include "clang/ASTMatchers/ASTMatchers.h" 16 #include "clang/ASTMatchers/ASTMatchersMacros.h" 17 18 using namespace clang::ast_matchers; 19 20 namespace clang { 21 namespace tidy { 22 namespace openmp { 23 24 ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name, 25 ClangTidyContext *Context) 26 : ClangTidyCheck(Name, Context), 27 RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) { 28 llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec, 29 IgnoredExceptionsVec; 30 31 llvm::StringSet<> IgnoredExceptions; 32 StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false); 33 llvm::transform(IgnoredExceptionsVec, IgnoredExceptionsVec.begin(), 34 [](StringRef S) { return S.trim(); }); 35 IgnoredExceptions.insert(IgnoredExceptionsVec.begin(), 36 IgnoredExceptionsVec.end()); 37 Tracer.ignoreExceptions(std::move(IgnoredExceptions)); 38 Tracer.ignoreBadAlloc(true); 39 } 40 41 void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 42 Options.store(Opts, "IgnoredExceptions", RawIgnoredExceptions); 43 } 44 45 void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) { 46 // Don't register the check if OpenMP is not enabled; the OpenMP pragmas are 47 // completely ignored then, so no OpenMP entires will be present in the AST. 48 if (!getLangOpts().OpenMP) 49 return; 50 // Similarly, if C++ Exceptions are not enabled, nothing to do. 51 if (!getLangOpts().CPlusPlus || !getLangOpts().CXXExceptions) 52 return; 53 54 Finder->addMatcher(ompExecutableDirective( 55 unless(isStandaloneDirective()), 56 hasStructuredBlock(stmt().bind("structured-block"))) 57 .bind("directive"), 58 this); 59 } 60 61 void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) { 62 const auto *Directive = 63 Result.Nodes.getNodeAs<OMPExecutableDirective>("directive"); 64 assert(Directive && "Expected to match some OpenMP Executable directive."); 65 const auto *StructuredBlock = 66 Result.Nodes.getNodeAs<Stmt>("structured-block"); 67 assert(StructuredBlock && "Expected to get some OpenMP Structured Block."); 68 69 if (Tracer.analyze(StructuredBlock).getBehaviour() != 70 utils::ExceptionAnalyzer::State::Throwing) 71 return; // No exceptions have been proven to escape out of the struc. block. 72 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 76 diag(StructuredBlock->getBeginLoc(), 77 "an exception thrown inside of the OpenMP '%0' region is not caught in " 78 "that same region") 79 << getOpenMPDirectiveName(Directive->getDirectiveKind()); 80 } 81 82 } // namespace openmp 83 } // namespace tidy 84 } // namespace clang 85