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/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Lex/MacroInfo.h"
14 #include "clang/Lex/Preprocessor.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace tidy {
20 namespace bugprone {
21 
22 namespace {
23 
24 // Keep track of macro expansions that contain both __FILE__ and __LINE__. If
25 // such a macro also uses __func__ or __FUNCTION__, we don't want to issue a
26 // warning because __FILE__ and __LINE__ may be useful even if __func__ or
27 // __FUNCTION__ is not, especially if the macro could be used in the context of
28 // either a function body or a lambda body.
29 class MacroExpansionsWithFileAndLine : public PPCallbacks {
30 public:
MacroExpansionsWithFileAndLine(LambdaFunctionNameCheck::SourceRangeSet * SME)31   explicit MacroExpansionsWithFileAndLine(
32       LambdaFunctionNameCheck::SourceRangeSet *SME)
33       : SuppressMacroExpansions(SME) {}
34 
MacroExpands(const Token & MacroNameTok,const MacroDefinition & MD,SourceRange Range,const MacroArgs * Args)35   void MacroExpands(const Token &MacroNameTok,
36                     const MacroDefinition &MD, SourceRange Range,
37                     const MacroArgs *Args) override {
38     bool has_file = false;
39     bool has_line = false;
40     for (const auto& T : MD.getMacroInfo()->tokens()) {
41       if (T.is(tok::identifier)) {
42         StringRef IdentName = T.getIdentifierInfo()->getName();
43         if (IdentName == "__FILE__") {
44           has_file = true;
45         } else if (IdentName == "__LINE__") {
46           has_line = true;
47         }
48       }
49     }
50     if (has_file && has_line) {
51       SuppressMacroExpansions->insert(Range);
52     }
53   }
54 
55 private:
56   LambdaFunctionNameCheck::SourceRangeSet* SuppressMacroExpansions;
57 };
58 
59 } // namespace
60 
registerMatchers(MatchFinder * Finder)61 void LambdaFunctionNameCheck::registerMatchers(MatchFinder *Finder) {
62   // Match on PredefinedExprs inside a lambda.
63   Finder->addMatcher(predefinedExpr(hasAncestor(lambdaExpr())).bind("E"),
64                      this);
65 }
66 
registerPPCallbacks(const SourceManager & SM,Preprocessor * PP,Preprocessor * ModuleExpanderPP)67 void LambdaFunctionNameCheck::registerPPCallbacks(
68     const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
69   PP->addPPCallbacks(std::make_unique<MacroExpansionsWithFileAndLine>(
70       &SuppressMacroExpansions));
71 }
72 
check(const MatchFinder::MatchResult & Result)73 void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult &Result) {
74   const auto *E = Result.Nodes.getNodeAs<PredefinedExpr>("E");
75   if (E->getIdentKind() != PredefinedExpr::Func &&
76       E->getIdentKind() != PredefinedExpr::Function) {
77     // We don't care about other PredefinedExprs.
78     return;
79   }
80   if (E->getLocation().isMacroID()) {
81     auto ER =
82         Result.SourceManager->getImmediateExpansionRange(E->getLocation());
83     if (SuppressMacroExpansions.find(ER.getAsRange()) !=
84         SuppressMacroExpansions.end()) {
85       // This is a macro expansion for which we should not warn.
86       return;
87     }
88   }
89   diag(E->getLocation(),
90        "inside a lambda, '%0' expands to the name of the function call "
91        "operator; consider capturing the name of the enclosing function "
92        "explicitly")
93       << PredefinedExpr::getIdentKindName(E->getIdentKind());
94 }
95 
96 } // namespace bugprone
97 } // namespace tidy
98 } // namespace clang
99