xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/EmptyCatchCheck.cpp (revision 11a411a49b62c129bba551df4587dd446fcdc660)
1 //===--- EmptyCatchCheck.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 "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"
15 #include <algorithm>
16 
17 using namespace clang::ast_matchers;
18 using ::clang::ast_matchers::internal::Matcher;
19 
20 namespace clang::tidy::bugprone {
21 
22 namespace {
AST_MATCHER(CXXCatchStmt,isInMacro)23 AST_MATCHER(CXXCatchStmt, isInMacro) {
24   return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID() ||
25          Node.getCatchLoc().isMacroID();
26 }
27 
AST_MATCHER_P(CXXCatchStmt,hasHandler,Matcher<Stmt>,InnerMatcher)28 AST_MATCHER_P(CXXCatchStmt, hasHandler, Matcher<Stmt>, InnerMatcher) {
29   Stmt *Handler = Node.getHandlerBlock();
30   if (!Handler)
31     return false;
32   return InnerMatcher.matches(*Handler, Finder, Builder);
33 }
34 
AST_MATCHER_P(CXXCatchStmt,hasCaughtType,Matcher<QualType>,InnerMatcher)35 AST_MATCHER_P(CXXCatchStmt, hasCaughtType, Matcher<QualType>, InnerMatcher) {
36   return InnerMatcher.matches(Node.getCaughtType(), Finder, Builder);
37 }
38 
AST_MATCHER_P(CompoundStmt,hasAnyTextFromList,std::vector<llvm::StringRef>,List)39 AST_MATCHER_P(CompoundStmt, hasAnyTextFromList, std::vector<llvm::StringRef>,
40               List) {
41   if (List.empty())
42     return false;
43 
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 llvm::any_of(List, [&](const StringRef &Str) {
50     return Text.contains_insensitive(Str);
51   });
52 }
53 
54 } // namespace
55 
EmptyCatchCheck(StringRef Name,ClangTidyContext * Context)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", ""))) {}
62 
storeOptions(ClangTidyOptions::OptionMap & Opts)63 void EmptyCatchCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
64   Options.store(Opts, "IgnoreCatchWithKeywords",
65                 utils::options::serializeStringList(IgnoreCatchWithKeywords));
66   Options.store(
67       Opts, "AllowEmptyCatchForExceptions",
68       utils::options::serializeStringList(AllowEmptyCatchForExceptions));
69 }
70 
isLanguageVersionSupported(const LangOptions & LangOpts) const71 bool EmptyCatchCheck::isLanguageVersionSupported(
72     const LangOptions &LangOpts) const {
73   return LangOpts.CPlusPlus;
74 }
75 
getCheckTraversalKind() const76 std::optional<TraversalKind> EmptyCatchCheck::getCheckTraversalKind() const {
77   return TK_IgnoreUnlessSpelledInSource;
78 }
79 
registerMatchers(MatchFinder * Finder)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)));
90 
91   Finder->addMatcher(
92       cxxCatchStmt(unless(isExpansionInSystemHeader()), unless(isInMacro()),
93                    unless(hasCaughtType(IgnoredExceptionType)),
94                    hasHandler(compoundStmt(
95                        statementCountIs(0),
96                        unless(hasAnyTextFromList(IgnoreCatchWithKeywords)))))
97           .bind("catch"),
98       this);
99 }
100 
check(const MatchFinder::MatchResult & Result)101 void EmptyCatchCheck::check(const MatchFinder::MatchResult &Result) {
102   const auto *MatchedCatchStmt = Result.Nodes.getNodeAs<CXXCatchStmt>("catch");
103 
104   diag(
105       MatchedCatchStmt->getCatchLoc(),
106       "empty catch statements hide issues; to handle exceptions appropriately, "
107       "consider re-throwing, handling, or avoiding catch altogether");
108 }
109 
110 } // namespace clang::tidy::bugprone
111