aboutsummaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-tidy/bugprone/EmptyCatchCheck.cpp
blob: be0a2a1baa12a6cc4fbc6d4ba881d165c3fc1263 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//===--- EmptyCatchCheck.cpp - clang-tidy ---------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "EmptyCatchCheck.h"
#include "../utils/Matchers.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"

using namespace clang::ast_matchers;
using ::clang::ast_matchers::internal::Matcher;

namespace clang::tidy::bugprone {

namespace {
AST_MATCHER(CXXCatchStmt, isInMacro) {
  return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID() ||
         Node.getCatchLoc().isMacroID();
}

AST_MATCHER_P(CXXCatchStmt, hasHandler, Matcher<Stmt>, InnerMatcher) {
  Stmt *Handler = Node.getHandlerBlock();
  if (!Handler)
    return false;
  return InnerMatcher.matches(*Handler, Finder, Builder);
}

AST_MATCHER_P(CXXCatchStmt, hasCaughtType, Matcher<QualType>, InnerMatcher) {
  return InnerMatcher.matches(Node.getCaughtType(), Finder, Builder);
}

AST_MATCHER_P(CompoundStmt, hasAnyTextFromList, std::vector<llvm::StringRef>,
              List) {
  if (List.empty())
    return false;

  ASTContext &Context = Finder->getASTContext();
  SourceManager &SM = Context.getSourceManager();
  StringRef Text = Lexer::getSourceText(
      CharSourceRange::getTokenRange(Node.getSourceRange()), SM,
      Context.getLangOpts());
  return llvm::any_of(List, [&](const StringRef &Str) {
    return Text.contains_insensitive(Str);
  });
}

} // namespace

EmptyCatchCheck::EmptyCatchCheck(StringRef Name, ClangTidyContext *Context)
    : ClangTidyCheck(Name, Context),
      IgnoreCatchWithKeywords(utils::options::parseStringList(
          Options.get("IgnoreCatchWithKeywords", "@TODO;@FIXME"))),
      AllowEmptyCatchForExceptions(utils::options::parseStringList(
          Options.get("AllowEmptyCatchForExceptions", ""))) {}

void EmptyCatchCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  Options.store(Opts, "IgnoreCatchWithKeywords",
                utils::options::serializeStringList(IgnoreCatchWithKeywords));
  Options.store(
      Opts, "AllowEmptyCatchForExceptions",
      utils::options::serializeStringList(AllowEmptyCatchForExceptions));
}

bool EmptyCatchCheck::isLanguageVersionSupported(
    const LangOptions &LangOpts) const {
  return LangOpts.CPlusPlus;
}

std::optional<TraversalKind> EmptyCatchCheck::getCheckTraversalKind() const {
  return TK_IgnoreUnlessSpelledInSource;
}

void EmptyCatchCheck::registerMatchers(MatchFinder *Finder) {
  auto AllowedNamedExceptionDecl =
      namedDecl(matchers::matchesAnyListedName(AllowEmptyCatchForExceptions));
  auto AllowedNamedExceptionTypes =
      qualType(anyOf(hasDeclaration(AllowedNamedExceptionDecl),
                     references(AllowedNamedExceptionDecl),
                     pointsTo(AllowedNamedExceptionDecl)));
  auto IgnoredExceptionType =
      qualType(anyOf(AllowedNamedExceptionTypes,
                     hasCanonicalType(AllowedNamedExceptionTypes)));

  Finder->addMatcher(
      cxxCatchStmt(unless(isExpansionInSystemHeader()), unless(isInMacro()),
                   unless(hasCaughtType(IgnoredExceptionType)),
                   hasHandler(compoundStmt(
                       statementCountIs(0),
                       unless(hasAnyTextFromList(IgnoreCatchWithKeywords)))))
          .bind("catch"),
      this);
}

void EmptyCatchCheck::check(const MatchFinder::MatchResult &Result) {
  const auto *MatchedCatchStmt = Result.Nodes.getNodeAs<CXXCatchStmt>("catch");

  diag(
      MatchedCatchStmt->getCatchLoc(),
      "empty catch statements hide issues; to handle exceptions appropriately, "
      "consider re-throwing, handling, or avoiding catch altogether");
}

} // namespace clang::tidy::bugprone