Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -4,6 +4,7 @@ ArgumentCommentCheck.cpp AssertSideEffectCheck.cpp ForwardingReferenceOverloadCheck.cpp + LambdaFunctionNameCheck.cpp MisplacedConstCheck.cpp UnconventionalAssignOperatorCheck.cpp BoolPointerImplicitConversionCheck.cpp Index: clang-tidy/misc/LambdaFunctionNameCheck.h =================================================================== --- clang-tidy/misc/LambdaFunctionNameCheck.h +++ clang-tidy/misc/LambdaFunctionNameCheck.h @@ -0,0 +1,41 @@ +//===--- LambdaFunctionNameCheck.h - clang-tidy------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// Detect when __func__ or __FUNCTION__ is being used from within a lambda. In +/// that context, those expressions expand to the name of the call operator +/// (i.e., `operator()`). +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-lambda-function-name.html +class LambdaFunctionNameCheck : public ClangTidyCheck { +public: + LambdaFunctionNameCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void registerPPCallbacks(CompilerInstance &Compiler) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + std::vector SuppressMacroExpansions; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H Index: clang-tidy/misc/LambdaFunctionNameCheck.cpp =================================================================== --- clang-tidy/misc/LambdaFunctionNameCheck.cpp +++ clang-tidy/misc/LambdaFunctionNameCheck.cpp @@ -0,0 +1,91 @@ +//===--- LambdaFunctionNameCheck.cpp - clang-tidy--------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LambdaFunctionNameCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/MacroInfo.h" +#include "clang/Lex/Preprocessor.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +namespace { + +// Keep track of macro expansions that contain both __FILE__ and __LINE__. If +// such a macro also uses __func__ or __FUNCTION__, we don't want to issue a +// warning because __FILE__ and __LINE__ may be useful even if __func__ or +// __FUNCTION__ is not, especially if the macro could be used in the context of +// either a function body or a lambda body. +class MacroExpansionsWithFileAndLine : public PPCallbacks { +public: + explicit MacroExpansionsWithFileAndLine(std::vector* SME) + : SuppressMacroExpansions(SME) {} + + void MacroExpands(const Token &MacroNameTok, + const MacroDefinition &MD, SourceRange Range, + const MacroArgs *Args) override { + for (const auto& T : MD.getMacroInfo()->tokens()) { + if (T.is(tok::identifier)) { + StringRef IdentName = T.getIdentifierInfo()->getName(); + if (IdentName == "__FILE__" || IdentName == "__LINE__") { + SuppressMacroExpansions->push_back(Range); + } + } + } + } + +private: + std::vector* SuppressMacroExpansions; +}; + +} // namespace + +void LambdaFunctionNameCheck::registerMatchers(MatchFinder *Finder) { + // Match on PredefinedExprs inside a lambda. + Finder->addMatcher(predefinedExpr(hasAncestor(lambdaExpr())).bind("E"), + this); +} + +void LambdaFunctionNameCheck::registerPPCallbacks(CompilerInstance &Compiler) { + Compiler.getPreprocessor().addPPCallbacks(std::unique_ptr( + new MacroExpansionsWithFileAndLine(&SuppressMacroExpansions))); +} + +void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult &Result) { + const auto *E = Result.Nodes.getNodeAs("E"); + if (E->getIdentType() == PredefinedExpr::Func || + E->getIdentType() == PredefinedExpr::Function) { + if (E->getLocation().isMacroID()) { + auto ER = + Result.SourceManager->getImmediateExpansionRange(E->getLocation()); + SourceRange AsRange(ER.first, ER.second); + for (const auto& R : SuppressMacroExpansions) { + if (R == AsRange) { + // This is a macro expansion for which we should not warn. + return; + } + } + } + diag(E->getLocation(), + "inside a lambda, '%0' expands to the name of the function call " + "operator; consider capturing the name of the enclosing function " + "explicitly") + << PredefinedExpr::getIdentTypeName(E->getIdentType()); + } + // We don't care about other PredefinedExprs. +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -21,6 +21,7 @@ #include "InaccurateEraseCheck.h" #include "IncorrectRoundings.h" #include "InefficientAlgorithmCheck.h" +#include "LambdaFunctionNameCheck.h" #include "MacroParenthesesCheck.h" #include "MacroRepeatedSideEffectsCheck.h" #include "MisplacedConstCheck.h" @@ -68,6 +69,8 @@ "misc-assert-side-effect"); CheckFactories.registerCheck( "misc-forwarding-reference-overload"); + CheckFactories.registerCheck( + "misc-lambda-function-name"); CheckFactories.registerCheck("misc-misplaced-const"); CheckFactories.registerCheck( "misc-unconventional-assign-operator"); Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -77,6 +77,11 @@ Finds perfect forwarding constructors that can unintentionally hide copy or move constructors. +- New `misc-lambda-function-name + `_ check + + Finds uses of __func__ or __FUNCTION__ inside lambdas. + - New `modernize-replace-random-shuffle `_ check Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -82,6 +82,7 @@ misc-inaccurate-erase misc-incorrect-roundings misc-inefficient-algorithm + misc-lambda-function-name misc-macro-parentheses misc-macro-repeated-side-effects misc-misplaced-const Index: docs/clang-tidy/checks/misc-lambda-function-name.rst =================================================================== --- docs/clang-tidy/checks/misc-lambda-function-name.rst +++ docs/clang-tidy/checks/misc-lambda-function-name.rst @@ -0,0 +1,27 @@ +.. title:: clang-tidy - misc-lambda-function-name + +misc-lambda-function-name +========================= + +Checks for attempts to get the name of a function from within a lambda +expression. The name of a lambda is always something like ``operator()``, which +is almost never what was intended. + +Example: + +.. code-block:: c++ + + void FancyFunction() { + [] { printf("Called from %s\n", __func__); }(); + [] { printf("Now called from %s\n", __FUNCTION__); }(); + } + +Output:: + + Called from operator() + Now called from operator() + +Likely intended output:: + + Called from FancyFunction + Now called from FancyFunction Index: test/clang-tidy/misc-lambda-function-name.cpp =================================================================== --- test/clang-tidy/misc-lambda-function-name.cpp +++ test/clang-tidy/misc-lambda-function-name.cpp @@ -0,0 +1,41 @@ +// RUN: %check_clang_tidy %s misc-lambda-function-name %t + +void Foo(const char* a, const char* b, int c) {} + +#define FUNC_MACRO Foo(__func__, "", 0) +#define FUNCTION_MACRO Foo(__FUNCTION__, "", 0) +#define EMBED_IN_ANOTHER_MACRO1 FUNC_MACRO + +void Positives() { + [] { __func__; }(); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name] + [] { __FUNCTION__; }(); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__FUNCTION__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name] + [] { FUNC_MACRO; }(); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name] + [] { FUNCTION_MACRO; }(); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__FUNCTION__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name] + [] { EMBED_IN_ANOTHER_MACRO1; }(); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name] +} + +#define FUNC_MACRO_WITH_FILE_AND_LINE Foo(__func__, __FILE__, __LINE__) +#define FUNCTION_MACRO_WITH_FILE_AND_LINE Foo(__FUNCTION__, __FILE__, __LINE__) +#define EMBED_IN_ANOTHER_MACRO2 FUNC_MACRO_WITH_FILE_AND_LINE + +void Negatives() { + __func__; + __FUNCTION__; + + // __PRETTY_FUNCTION__ should not trigger a warning because its value is + // actually potentially useful. + __PRETTY_FUNCTION__; + [] { __PRETTY_FUNCTION__; }(); + + // Don't warn if __func__/__FUNCTION is used inside a macro that also uses + // __FILE__ and __LINE__, on the assumption that __FILE__ and __LINE__ will + // be useful even if __func__/__FUNCTION__ is not. + [] { FUNC_MACRO_WITH_FILE_AND_LINE; }(); + [] { FUNCTION_MACRO_WITH_FILE_AND_LINE; }(); + [] { EMBED_IN_ANOTHER_MACRO2; }(); +}