1 //===--- EmptyCatchCheck.cpp - clang-tidy ---------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "EmptyCatchCheck.h"
10 #include "../utils/Matchers.h"
11 #include "../utils/OptionsUtils.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Lex/Lexer.h"
17 using namespace clang::ast_matchers
;
18 using ::clang::ast_matchers::internal::Matcher
;
20 namespace clang::tidy::bugprone
{
23 AST_MATCHER(CXXCatchStmt
, isInMacro
) {
24 return Node
.getBeginLoc().isMacroID() || Node
.getEndLoc().isMacroID() ||
25 Node
.getCatchLoc().isMacroID();
28 AST_MATCHER_P(CXXCatchStmt
, hasHandler
, Matcher
<Stmt
>, InnerMatcher
) {
29 Stmt
*Handler
= Node
.getHandlerBlock();
32 return InnerMatcher
.matches(*Handler
, Finder
, Builder
);
35 AST_MATCHER_P(CXXCatchStmt
, hasCaughtType
, Matcher
<QualType
>, InnerMatcher
) {
36 return InnerMatcher
.matches(Node
.getCaughtType(), Finder
, Builder
);
39 AST_MATCHER_P(CompoundStmt
, hasAnyTextFromList
, std::vector
<llvm::StringRef
>,
44 ASTContext
&Context
= Finder
->getASTContext();
45 SourceManager
&SM
= Context
.getSourceManager();
46 StringRef Text
= Lexer::getSourceText(
47 CharSourceRange::getTokenRange(Node
.getSourceRange()), SM
,
48 Context
.getLangOpts());
49 return std::any_of(List
.begin(), List
.end(), [&](const StringRef
&Str
) {
50 return Text
.contains_insensitive(Str
);
56 EmptyCatchCheck::EmptyCatchCheck(StringRef Name
, ClangTidyContext
*Context
)
57 : ClangTidyCheck(Name
, Context
),
58 IgnoreCatchWithKeywords(utils::options::parseStringList(
59 Options
.get("IgnoreCatchWithKeywords", "@TODO;@FIXME"))),
60 AllowEmptyCatchForExceptions(utils::options::parseStringList(
61 Options
.get("AllowEmptyCatchForExceptions", ""))) {}
63 void EmptyCatchCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
64 Options
.store(Opts
, "IgnoreCatchWithKeywords",
65 utils::options::serializeStringList(IgnoreCatchWithKeywords
));
67 Opts
, "AllowEmptyCatchForExceptions",
68 utils::options::serializeStringList(AllowEmptyCatchForExceptions
));
71 bool EmptyCatchCheck::isLanguageVersionSupported(
72 const LangOptions
&LangOpts
) const {
73 return LangOpts
.CPlusPlus
;
76 std::optional
<TraversalKind
> EmptyCatchCheck::getCheckTraversalKind() const {
77 return TK_IgnoreUnlessSpelledInSource
;
80 void EmptyCatchCheck::registerMatchers(MatchFinder
*Finder
) {
81 auto AllowedNamedExceptionDecl
=
82 namedDecl(matchers::matchesAnyListedName(AllowEmptyCatchForExceptions
));
83 auto AllowedNamedExceptionTypes
=
84 qualType(anyOf(hasDeclaration(AllowedNamedExceptionDecl
),
85 references(AllowedNamedExceptionDecl
),
86 pointsTo(AllowedNamedExceptionDecl
)));
87 auto IgnoredExceptionType
=
88 qualType(anyOf(AllowedNamedExceptionTypes
,
89 hasCanonicalType(AllowedNamedExceptionTypes
)));
92 cxxCatchStmt(unless(isExpansionInSystemHeader()), unless(isInMacro()),
93 unless(hasCaughtType(IgnoredExceptionType
)),
94 hasHandler(compoundStmt(
96 unless(hasAnyTextFromList(IgnoreCatchWithKeywords
)))))
101 void EmptyCatchCheck::check(const MatchFinder::MatchResult
&Result
) {
102 const auto *MatchedCatchStmt
= Result
.Nodes
.getNodeAs
<CXXCatchStmt
>("catch");
105 MatchedCatchStmt
->getCatchLoc(),
106 "empty catch statements hide issues; to handle exceptions appropriately, "
107 "consider re-throwing, handling, or avoiding catch altogether");
110 } // namespace clang::tidy::bugprone