1 //===--- LambdaFunctionNameCheck.cpp - clang-tidy--------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
18 using namespace clang::ast_matchers
;
20 namespace clang::tidy::bugprone
{
24 static constexpr bool DefaultIgnoreMacros
= false;
26 // Keep track of macro expansions that contain both __FILE__ and __LINE__. If
27 // such a macro also uses __func__ or __FUNCTION__, we don't want to issue a
28 // warning because __FILE__ and __LINE__ may be useful even if __func__ or
29 // __FUNCTION__ is not, especially if the macro could be used in the context of
30 // either a function body or a lambda body.
31 class MacroExpansionsWithFileAndLine
: public PPCallbacks
{
33 explicit MacroExpansionsWithFileAndLine(
34 LambdaFunctionNameCheck::SourceRangeSet
*SME
)
35 : SuppressMacroExpansions(SME
) {}
37 void MacroExpands(const Token
&MacroNameTok
,
38 const MacroDefinition
&MD
, SourceRange Range
,
39 const MacroArgs
*Args
) override
{
42 for (const auto& T
: MD
.getMacroInfo()->tokens()) {
43 if (T
.is(tok::identifier
)) {
44 StringRef IdentName
= T
.getIdentifierInfo()->getName();
45 if (IdentName
== "__FILE__") {
47 } else if (IdentName
== "__LINE__") {
52 if (HasFile
&& HasLine
) {
53 SuppressMacroExpansions
->insert(Range
);
58 LambdaFunctionNameCheck::SourceRangeSet
* SuppressMacroExpansions
;
61 AST_MATCHER(CXXMethodDecl
, isInLambda
) { return Node
.getParent()->isLambda(); }
65 LambdaFunctionNameCheck::LambdaFunctionNameCheck(StringRef Name
,
66 ClangTidyContext
*Context
)
67 : ClangTidyCheck(Name
, Context
),
69 Options
.getLocalOrGlobal("IgnoreMacros", DefaultIgnoreMacros
)) {}
71 void LambdaFunctionNameCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
72 Options
.store(Opts
, "IgnoreMacros", IgnoreMacros
);
75 void LambdaFunctionNameCheck::registerMatchers(MatchFinder
*Finder
) {
77 cxxMethodDecl(isInLambda(),
78 hasBody(forEachDescendant(
79 predefinedExpr(hasAncestor(cxxMethodDecl().bind("fn")))
81 equalsBoundNode("fn")),
85 void LambdaFunctionNameCheck::registerPPCallbacks(
86 const SourceManager
&SM
, Preprocessor
*PP
, Preprocessor
*ModuleExpanderPP
) {
87 PP
->addPPCallbacks(std::make_unique
<MacroExpansionsWithFileAndLine
>(
88 &SuppressMacroExpansions
));
91 void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult
&Result
) {
92 const auto *E
= Result
.Nodes
.getNodeAs
<PredefinedExpr
>("E");
93 if (E
->getIdentKind() != PredefinedIdentKind::Func
&&
94 E
->getIdentKind() != PredefinedIdentKind::Function
) {
95 // We don't care about other PredefinedExprs.
98 if (E
->getLocation().isMacroID()) {
103 Result
.SourceManager
->getImmediateExpansionRange(E
->getLocation());
104 if (SuppressMacroExpansions
.find(ER
.getAsRange()) !=
105 SuppressMacroExpansions
.end()) {
106 // This is a macro expansion for which we should not warn.
111 diag(E
->getLocation(),
112 "inside a lambda, '%0' expands to the name of the function call "
113 "operator; consider capturing the name of the enclosing function "
115 << PredefinedExpr::getIdentKindName(E
->getIdentKind());
118 } // namespace clang::tidy::bugprone