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