117 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 "LambdaFunctionNameCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/DeclCXX.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/ASTMatchers/ASTMatchers.h"14#include "clang/Frontend/CompilerInstance.h"15#include "clang/Lex/MacroInfo.h"16#include "clang/Lex/Preprocessor.h"17 18using namespace clang::ast_matchers;19 20namespace clang::tidy::bugprone {21 22namespace {23 24static constexpr bool DefaultIgnoreMacros = false;25 26// Keep track of macro expansions that contain both __FILE__ and __LINE__. If27// such a macro also uses __func__ or __FUNCTION__, we don't want to issue a28// warning because __FILE__ and __LINE__ may be useful even if __func__ or29// __FUNCTION__ is not, especially if the macro could be used in the context of30// either a function body or a lambda body.31class MacroExpansionsWithFileAndLine : public PPCallbacks {32public:33 explicit MacroExpansionsWithFileAndLine(34 LambdaFunctionNameCheck::SourceRangeSet *SME)35 : SuppressMacroExpansions(SME) {}36 37 void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,38 SourceRange Range, const MacroArgs *Args) override {39 bool HasFile = false;40 bool HasLine = false;41 for (const Token &T : MD.getMacroInfo()->tokens()) {42 if (T.is(tok::identifier)) {43 const StringRef IdentName = T.getIdentifierInfo()->getName();44 if (IdentName == "__FILE__") {45 HasFile = true;46 } else if (IdentName == "__LINE__") {47 HasLine = true;48 }49 }50 }51 if (HasFile && HasLine) {52 SuppressMacroExpansions->insert(Range);53 }54 }55 56private:57 LambdaFunctionNameCheck::SourceRangeSet *SuppressMacroExpansions;58};59 60AST_MATCHER(CXXMethodDecl, isInLambda) { return Node.getParent()->isLambda(); }61 62} // namespace63 64LambdaFunctionNameCheck::LambdaFunctionNameCheck(StringRef Name,65 ClangTidyContext *Context)66 : ClangTidyCheck(Name, Context),67 IgnoreMacros(Options.get("IgnoreMacros", DefaultIgnoreMacros)) {}68 69void LambdaFunctionNameCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {70 Options.store(Opts, "IgnoreMacros", IgnoreMacros);71}72 73void LambdaFunctionNameCheck::registerMatchers(MatchFinder *Finder) {74 Finder->addMatcher(75 cxxMethodDecl(isInLambda(),76 hasBody(forEachDescendant(77 predefinedExpr(hasAncestor(cxxMethodDecl().bind("fn")))78 .bind("E"))),79 equalsBoundNode("fn")),80 this);81}82 83void LambdaFunctionNameCheck::registerPPCallbacks(84 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {85 PP->addPPCallbacks(std::make_unique<MacroExpansionsWithFileAndLine>(86 &SuppressMacroExpansions));87}88 89void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult &Result) {90 const auto *E = Result.Nodes.getNodeAs<PredefinedExpr>("E");91 if (E->getIdentKind() != PredefinedIdentKind::Func &&92 E->getIdentKind() != PredefinedIdentKind::Function) {93 // We don't care about other PredefinedExprs.94 return;95 }96 if (E->getLocation().isMacroID()) {97 if (IgnoreMacros)98 return;99 100 auto ER =101 Result.SourceManager->getImmediateExpansionRange(E->getLocation());102 if (SuppressMacroExpansions.find(ER.getAsRange()) !=103 SuppressMacroExpansions.end()) {104 // This is a macro expansion for which we should not warn.105 return;106 }107 }108 109 diag(E->getLocation(),110 "inside a lambda, '%0' expands to the name of the function call "111 "operator; consider capturing the name of the enclosing function "112 "explicitly")113 << PredefinedExpr::getIdentKindName(E->getIdentKind());114}115 116} // namespace clang::tidy::bugprone117