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