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