12 #include "clang/AST/ASTContext.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 15 #include "llvm/ADT/SmallSet.h" 16 #include "llvm/ADT/StringSet.h" 21 typedef llvm::SmallVector<const clang::Type *, 8> TypeVec;
26 static bool isBaseOf(
const Type *DerivedType,
const Type *BaseType) {
27 const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
28 const auto *BaseClass = BaseType->getAsCXXRecordDecl();
29 if (!DerivedClass || !BaseClass)
32 return !DerivedClass->forallBases(
33 [BaseClass](
const CXXRecordDecl *Cur) {
return Cur != BaseClass; });
38 llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
42 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
43 if (CallStack.count(Func))
46 if (
const Stmt *Body = Func->getBody()) {
47 CallStack.insert(Func);
49 CallStack.erase(Func);
54 if (
const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
55 for (
const QualType Ex : FPT->exceptions()) {
56 Result.push_back(Ex.getTypePtr());
64 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
70 if (
const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
71 if (
const auto *ThrownExpr = Throw->getSubExpr()) {
72 const auto *ThrownType =
73 ThrownExpr->getType()->getUnqualifiedDesugaredType();
74 if (ThrownType->isReferenceType()) {
75 ThrownType = ThrownType->castAs<ReferenceType>()
77 ->getUnqualifiedDesugaredType();
79 if (
const auto *TD = ThrownType->getAsTagDecl()) {
80 if (TD->getDeclName().isIdentifier() && TD->getName() ==
"bad_alloc" 81 && TD->isInStdNamespace())
84 Results.push_back(ThrownExpr->getType()->getUnqualifiedDesugaredType());
86 Results.append(Caught.begin(), Caught.end());
88 }
else if (
const auto *Try = dyn_cast<CXXTryStmt>(St)) {
89 TypeVec Uncaught =
throwsException(Try->getTryBlock(), Caught, CallStack);
90 for (
unsigned i = 0; i < Try->getNumHandlers(); ++i) {
91 const CXXCatchStmt *Catch = Try->getHandler(i);
92 if (!Catch->getExceptionDecl()) {
93 const TypeVec Rethrown =
95 Results.append(Rethrown.begin(), Rethrown.end());
98 const auto *CaughtType =
99 Catch->getCaughtType()->getUnqualifiedDesugaredType();
100 if (CaughtType->isReferenceType()) {
101 CaughtType = CaughtType->castAs<ReferenceType>()
103 ->getUnqualifiedDesugaredType();
106 llvm::remove_if(Uncaught, [&CaughtType](
const Type *ThrownType) {
107 return ThrownType == CaughtType ||
110 if (NewEnd != Uncaught.end()) {
111 Uncaught.erase(NewEnd, Uncaught.end());
113 Catch->getHandlerBlock(), TypeVec(1, CaughtType), CallStack);
114 Results.append(Rethrown.begin(), Rethrown.end());
118 Results.append(Uncaught.begin(), Uncaught.end());
119 }
else if (
const auto *Call = dyn_cast<CallExpr>(St)) {
120 if (
const FunctionDecl *Func = Call->getDirectCallee()) {
122 Results.append(Excs.begin(), Excs.end());
125 for (
const Stmt *Child : St->children()) {
127 Results.append(Excs.begin(), Excs.end());
134 llvm::SmallSet<const FunctionDecl *, 32> CallStack;
138 namespace ast_matchers {
139 AST_MATCHER_P(FunctionDecl,
throws, internal::Matcher<Type>, InnerMatcher) {
141 auto NewEnd = llvm::remove_if(
142 ExceptionList, [
this, Finder, Builder](
const Type *Exception) {
143 return !InnerMatcher.matches(*Exception, Finder, Builder);
145 ExceptionList.erase(NewEnd, ExceptionList.end());
146 return ExceptionList.size();
150 if (
const auto *TD = Node.getAsTagDecl()) {
151 if (TD->getDeclName().isIdentifier())
152 return IgnoredExceptions.count(TD->getName()) > 0;
158 FunctionsThatShouldNotThrow) {
159 return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0;
166 ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef
Name,
168 :
ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get(
169 "FunctionsThatShouldNotThrow",
"")),
170 RawIgnoredExceptions(Options.get(
"IgnoredExceptions",
"")) {
171 llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec,
172 IgnoredExceptionsVec;
173 StringRef(RawFunctionsThatShouldNotThrow)
174 .split(FunctionsThatShouldNotThrowVec,
",", -1,
false);
175 FunctionsThatShouldNotThrow.insert(FunctionsThatShouldNotThrowVec.begin(),
176 FunctionsThatShouldNotThrowVec.end());
177 StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec,
",", -1,
false);
178 IgnoredExceptions.insert(IgnoredExceptionsVec.begin(),
179 IgnoredExceptionsVec.end());
184 RawFunctionsThatShouldNotThrow);
185 Options.
store(Opts,
"IgnoredExceptions", RawIgnoredExceptions);
193 functionDecl(anyOf(isNoThrow(), cxxDestructorDecl(),
194 cxxConstructorDecl(isMoveConstructor()),
195 cxxMethodDecl(isMoveAssignmentOperator()),
196 hasName(
"main"), hasName(
"swap"),
197 isEnabled(FunctionsThatShouldNotThrow)),
198 throws(unless(isIgnored(IgnoredExceptions))))
204 const FunctionDecl *MatchedDecl =
205 Result.Nodes.getNodeAs<FunctionDecl>(
"thrower");
211 diag(MatchedDecl->getLocation(),
"an exception may be thrown in function %0 " 212 "which should not throw exceptions") << MatchedDecl;
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
LangOptions getLangOpts() const
Returns the language options from the context.
static bool isBaseOf(const Type *DerivedType, const Type *BaseType)
std::vector< CodeCompletionResult > Results
Base class for all clang-tidy checks.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
AST_MATCHER_P(FunctionDecl, throws, internal::Matcher< Type >, InnerMatcher)
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static const TypeVec throwsException(const Stmt *St, const TypeVec &Caught, llvm::SmallSet< const FunctionDecl *, 32 > &CallStack)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.