1 | //===--- LambdaFunctionNameCheck.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 "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 | |
18 | using namespace clang::ast_matchers; |
19 | |
20 | namespace clang::tidy::bugprone { |
21 | |
22 | namespace { |
23 | |
24 | static constexpr bool DefaultIgnoreMacros = false; |
25 | |
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 { |
32 | public: |
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(K: tok::identifier)) { |
43 | 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(x: Range); |
53 | } |
54 | } |
55 | |
56 | private: |
57 | LambdaFunctionNameCheck::SourceRangeSet *SuppressMacroExpansions; |
58 | }; |
59 | |
60 | AST_MATCHER(CXXMethodDecl, isInLambda) { return Node.getParent()->isLambda(); } |
61 | |
62 | } // namespace |
63 | |
64 | LambdaFunctionNameCheck::LambdaFunctionNameCheck(StringRef Name, |
65 | ClangTidyContext *Context) |
66 | : ClangTidyCheck(Name, Context), |
67 | IgnoreMacros( |
68 | Options.getLocalOrGlobal(LocalName: "IgnoreMacros" , Default: DefaultIgnoreMacros)) {} |
69 | |
70 | void LambdaFunctionNameCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
71 | Options.store(Options&: Opts, LocalName: "IgnoreMacros" , Value: IgnoreMacros); |
72 | } |
73 | |
74 | void LambdaFunctionNameCheck::registerMatchers(MatchFinder *Finder) { |
75 | Finder->addMatcher( |
76 | NodeMatch: cxxMethodDecl(isInLambda(), |
77 | hasBody(InnerMatcher: forEachDescendant( |
78 | predefinedExpr(hasAncestor(cxxMethodDecl().bind(ID: "fn" ))) |
79 | .bind(ID: "E" ))), |
80 | equalsBoundNode(ID: "fn" )), |
81 | Action: this); |
82 | } |
83 | |
84 | void LambdaFunctionNameCheck::registerPPCallbacks( |
85 | const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { |
86 | PP->addPPCallbacks(C: std::make_unique<MacroExpansionsWithFileAndLine>( |
87 | args: &SuppressMacroExpansions)); |
88 | } |
89 | |
90 | void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult &Result) { |
91 | const auto *E = Result.Nodes.getNodeAs<PredefinedExpr>(ID: "E" ); |
92 | if (E->getIdentKind() != PredefinedIdentKind::Func && |
93 | E->getIdentKind() != PredefinedIdentKind::Function) { |
94 | // We don't care about other PredefinedExprs. |
95 | return; |
96 | } |
97 | if (E->getLocation().isMacroID()) { |
98 | if (IgnoreMacros) |
99 | return; |
100 | |
101 | auto ER = |
102 | Result.SourceManager->getImmediateExpansionRange(Loc: E->getLocation()); |
103 | if (SuppressMacroExpansions.find(x: ER.getAsRange()) != |
104 | SuppressMacroExpansions.end()) { |
105 | // This is a macro expansion for which we should not warn. |
106 | return; |
107 | } |
108 | } |
109 | |
110 | diag(Loc: E->getLocation(), |
111 | Description: "inside a lambda, '%0' expands to the name of the function call " |
112 | "operator; consider capturing the name of the enclosing function " |
113 | "explicitly" ) |
114 | << PredefinedExpr::getIdentKindName(IK: E->getIdentKind()); |
115 | } |
116 | |
117 | } // namespace clang::tidy::bugprone |
118 | |