llvm-project
110 строк · 3.9 Кб
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
17using namespace clang::ast_matchers;18using ::clang::ast_matchers::internal::Matcher;19
20namespace clang::tidy::bugprone {21
22namespace {23AST_MATCHER(CXXCatchStmt, isInMacro) {24return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID() ||25Node.getCatchLoc().isMacroID();26}
27
28AST_MATCHER_P(CXXCatchStmt, hasHandler, Matcher<Stmt>, InnerMatcher) {29Stmt *Handler = Node.getHandlerBlock();30if (!Handler)31return false;32return InnerMatcher.matches(*Handler, Finder, Builder);33}
34
35AST_MATCHER_P(CXXCatchStmt, hasCaughtType, Matcher<QualType>, InnerMatcher) {36return InnerMatcher.matches(Node.getCaughtType(), Finder, Builder);37}
38
39AST_MATCHER_P(CompoundStmt, hasAnyTextFromList, std::vector<llvm::StringRef>,40List) {41if (List.empty())42return false;43
44ASTContext &Context = Finder->getASTContext();45SourceManager &SM = Context.getSourceManager();46StringRef Text = Lexer::getSourceText(47CharSourceRange::getTokenRange(Node.getSourceRange()), SM,48Context.getLangOpts());49return llvm::any_of(List, [&](const StringRef &Str) {50return Text.contains_insensitive(Str);51});52}
53
54} // namespace55
56EmptyCatchCheck::EmptyCatchCheck(StringRef Name, ClangTidyContext *Context)57: ClangTidyCheck(Name, Context),58IgnoreCatchWithKeywords(utils::options::parseStringList(59Options.get("IgnoreCatchWithKeywords", "@TODO;@FIXME"))),60AllowEmptyCatchForExceptions(utils::options::parseStringList(61Options.get("AllowEmptyCatchForExceptions", ""))) {}62
63void EmptyCatchCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {64Options.store(Opts, "IgnoreCatchWithKeywords",65utils::options::serializeStringList(IgnoreCatchWithKeywords));66Options.store(67Opts, "AllowEmptyCatchForExceptions",68utils::options::serializeStringList(AllowEmptyCatchForExceptions));69}
70
71bool EmptyCatchCheck::isLanguageVersionSupported(72const LangOptions &LangOpts) const {73return LangOpts.CPlusPlus;74}
75
76std::optional<TraversalKind> EmptyCatchCheck::getCheckTraversalKind() const {77return TK_IgnoreUnlessSpelledInSource;78}
79
80void EmptyCatchCheck::registerMatchers(MatchFinder *Finder) {81auto AllowedNamedExceptionDecl =82namedDecl(matchers::matchesAnyListedName(AllowEmptyCatchForExceptions));83auto AllowedNamedExceptionTypes =84qualType(anyOf(hasDeclaration(AllowedNamedExceptionDecl),85references(AllowedNamedExceptionDecl),86pointsTo(AllowedNamedExceptionDecl)));87auto IgnoredExceptionType =88qualType(anyOf(AllowedNamedExceptionTypes,89hasCanonicalType(AllowedNamedExceptionTypes)));90
91Finder->addMatcher(92cxxCatchStmt(unless(isExpansionInSystemHeader()), unless(isInMacro()),93unless(hasCaughtType(IgnoredExceptionType)),94hasHandler(compoundStmt(95statementCountIs(0),96unless(hasAnyTextFromList(IgnoreCatchWithKeywords)))))97.bind("catch"),98this);99}
100
101void EmptyCatchCheck::check(const MatchFinder::MatchResult &Result) {102const auto *MatchedCatchStmt = Result.Nodes.getNodeAs<CXXCatchStmt>("catch");103
104diag(105MatchedCatchStmt->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::bugprone111