Index: clang-tidy/cppcoreguidelines/CMakeLists.txt =================================================================== --- clang-tidy/cppcoreguidelines/CMakeLists.txt +++ clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -4,6 +4,7 @@ AvoidGotoCheck.cpp CppCoreGuidelinesTidyModule.cpp InterfacesGlobalInitCheck.cpp + MacroUsageCheck.cpp NarrowingConversionsCheck.cpp NoMallocCheck.cpp OwningMemoryCheck.cpp Index: clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp =================================================================== --- clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -14,6 +14,7 @@ #include "../readability/MagicNumbersCheck.h" #include "AvoidGotoCheck.h" #include "InterfacesGlobalInitCheck.h" +#include "MacroUsageCheck.h" #include "NarrowingConversionsCheck.h" #include "NoMallocCheck.h" #include "OwningMemoryCheck.h" @@ -40,10 +41,12 @@ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "cppcoreguidelines-avoid-goto"); - CheckFactories.registerCheck( - "cppcoreguidelines-interfaces-global-init"); CheckFactories.registerCheck( "cppcoreguidelines-avoid-magic-numbers"); + CheckFactories.registerCheck( + "cppcoreguidelines-interfaces-global-init"); + CheckFactories.registerCheck( + "cppcoreguidelines-macro-usage"); CheckFactories.registerCheck( "cppcoreguidelines-narrowing-conversions"); CheckFactories.registerCheck("cppcoreguidelines-no-malloc"); Index: clang-tidy/cppcoreguidelines/MacroUsageCheck.h =================================================================== --- /dev/null +++ clang-tidy/cppcoreguidelines/MacroUsageCheck.h @@ -0,0 +1,49 @@ +//===--- MacroUsageCheck.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_CPPCOREGUIDELINES_MACROUSAGECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_MACROUSAGECHECK_H + +#include "../ClangTidy.h" +#include "clang/Lex/Preprocessor.h" +#include + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +enum class MacroUsageMode { Constant, Function, Variadic }; +/// Find macro usage that is considered problematic because better language +/// constructs exist for the task. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-macro-usage.html +class MacroUsageCheck : public ClangTidyCheck { +public: + MacroUsageCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + AllowedRegexp(Options.get("AllowedRegexp", "^DEBUG_*")), + CheckCapsOnly(Options.get("CheckCapsOnly", 0)) {} + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerPPCallbacks(CompilerInstance &Compiler) override; + void warnMacro(const MacroDirective *MD); + void warnNaming(const MacroDirective *MD); + +private: + /// A regular expression that defines how allowed macros must look like. + std::string AllowedRegexp; + /// Control if only the check shall only test on CAPS_ONLY macros. + bool CheckCapsOnly; +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_MACROUSAGECHECK_H Index: clang-tidy/cppcoreguidelines/MacroUsageCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/cppcoreguidelines/MacroUsageCheck.cpp @@ -0,0 +1,91 @@ +//===--- MacroUsageCheck.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 "MacroUsageCheck.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/PPCallbacks.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Regex.h" +#include + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +namespace { + +bool isCapsOnly(StringRef Name) { + return std::all_of(Name.begin(), Name.end(), [](const char c) { + if (std::isupper(c) || std::isdigit(c) || c == '_') + return true; + return false; + }); +} + +class MacroUsageCallbacks : public PPCallbacks { +public: + MacroUsageCallbacks(MacroUsageCheck *Check, StringRef RegExp, bool CapsOnly) + : Check(Check), RegExp(RegExp), CheckCapsOnly(CapsOnly) {} + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override { + if (MD->getMacroInfo()->isUsedForHeaderGuard() || + MD->getMacroInfo()->getNumTokens() == 0) + return; + + StringRef MacroName = MacroNameTok.getIdentifierInfo()->getName(); + if (!CheckCapsOnly && !llvm::Regex(RegExp).match(MacroName)) + Check->warnMacro(MD); + + if (CheckCapsOnly && !isCapsOnly(MacroName)) + Check->warnNaming(MD); + } + +private: + MacroUsageCheck *Check; + StringRef RegExp; + bool CheckCapsOnly; +}; +} // namespace + +void MacroUsageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AllowedRegexp", AllowedRegexp); + Options.store(Opts, "CheckCapsOnly", CheckCapsOnly); +} + +void MacroUsageCheck::registerPPCallbacks(CompilerInstance &Compiler) { + if (!getLangOpts().CPlusPlus11) + return; + + Compiler.getPreprocessor().addPPCallbacks( + llvm::make_unique(this, AllowedRegexp, + CheckCapsOnly)); +} + +void MacroUsageCheck::warnMacro(const MacroDirective *MD) { + std::string DiagnosticMessage = + "macro used to declare a constant; consider using a 'constexpr' constant"; + + const MacroInfo *Info = MD->getMacroInfo(); + if (Info->isFunctionLike()) + DiagnosticMessage = + "function-like macro used; consider a 'constexpr' template function"; + if (Info->isVariadic()) + DiagnosticMessage = "variadic macro used; consider using a 'constexpr' " + "variadic template function"; + + diag(MD->getLocation(), DiagnosticMessage); +} + +void MacroUsageCheck::warnNaming(const MacroDirective *MD) { + diag(MD->getLocation(), "macro definition does not define the macro name using all uppercase characters"); +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang Index: docs/clang-tidy/checks/cppcoreguidelines-macro-usage.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/cppcoreguidelines-macro-usage.rst @@ -0,0 +1,28 @@ +.. title:: clang-tidy - cppcoreguidelines-macro-usage + +cppcoreguidelines-macro-usage +============================= + +Find macro usage that is considered problematic because better language +constructs exist for the task. + +The relevant sections in the C++ Core Guidelines are +`Enum.1 `_, +`ES.30 `_, +`ES.31 `_ and +`ES.33 `_. + +Options +------- + +.. option:: AllowedRegexp + + A regular expression to filter allowed macros. For example + `DEBUG*|LIBTORRENT*|TORRENT*|UNI*` could be applied to filter `libtorrent`. + Default value is `^DEBUG_*`. + +.. option:: CheckCapsOnly + + Boolean flag to warn all macros except those with CAPS_ONLY names. + This option is intended to ease introduction of this check into older + code bases. Default value is `0`/`false`. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -88,6 +88,7 @@ cppcoreguidelines-avoid-magic-numbers (redirects to readability-magic-numbers) cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) cppcoreguidelines-interfaces-global-init + cppcoreguidelines-macro-usage cppcoreguidelines-narrowing-conversions cppcoreguidelines-no-malloc cppcoreguidelines-owning-memory Index: test/clang-tidy/cppcoreguidelines-macro-usage-caps-only.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cppcoreguidelines-macro-usage-caps-only.cpp @@ -0,0 +1,17 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-macro-usage %t \ +// RUN: -config='{CheckOptions: \ +// RUN: [{key: cppcoreguidelines-macro-usage.CheckCapsOnly, value: 1}]}' -- + +#ifndef INCLUDE_GUARD +#define INCLUDE_GUARD + +#define problematic_constant 0 +// CHECK-MESSAGES: [[@LINE-1]]:9: warning: macro definition does not define the macro name using all uppercase characters + +#define problematic_function(x, y) ((a) > (b) ? (a) : (b)) +// CHECK-MESSAGES: [[@LINE-1]]:9: warning: macro definition does not define the macro name using all uppercase characters + +#define problematic_variadic(...) (__VA_ARGS__) +// CHECK-MESSAGES: [[@LINE-1]]:9: warning: macro definition does not define the macro name using all uppercase characters + +#endif Index: test/clang-tidy/cppcoreguidelines-macro-usage-custom.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cppcoreguidelines-macro-usage-custom.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-macro-usage %t \ +// RUN: -config='{CheckOptions: \ +// RUN: [{key: cppcoreguidelines-macro-usage.AllowedRegexp, value: "DEBUG_*|TEST_*"}]}' -- + +#ifndef INCLUDE_GUARD +#define INCLUDE_GUARD + +#define PROBLEMATIC_CONSTANT 0 +// CHECK-MESSAGES: [[@LINE-1]]:9: warning: macro used to declare a constant; consider using a 'constexpr' constant + +#define PROBLEMATIC_FUNCTION(x, y) ((a) > (b) ? (a) : (b)) +// CHECK-MESSAGES: [[@LINE-1]]:9: warning: function-like macro used; consider a 'constexpr' template function + +#define PROBLEMATIC_VARIADIC(...) (__VA_ARGS__) +// CHECK-MESSAGES: [[@LINE-1]]:9: warning: variadic macro used; consider using a 'constexpr' variadic template function + +#define DEBUG_CONSTANT 0 +#define DEBUG_FUNCTION(x, y) ((a) > (b) ? (a) : (b)) +#define DEBUG_VARIADIC(...) (__VA_ARGS__) +#define TEST_CONSTANT 0 +#define TEST_FUNCTION(x, y) ((a) > (b) ? (a) : (b)) +#define TEST_VARIADIC(...) (__VA_ARGS__) + +#endif Index: test/clang-tidy/cppcoreguidelines-macro-usage.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cppcoreguidelines-macro-usage.cpp @@ -0,0 +1,15 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-macro-usage %t + +#ifndef INCLUDE_GUARD +#define INCLUDE_GUARD + +#define PROBLEMATIC_CONSTANT 0 +// CHECK-MESSAGES: [[@LINE-1]]:9: warning: macro used to declare a constant; consider using a 'constexpr' constant + +#define PROBLEMATIC_FUNCTION(x, y) ((a) > (b) ? (a) : (b)) +// CHECK-MESSAGES: [[@LINE-1]]:9: warning: function-like macro used; consider a 'constexpr' template function + +#define PROBLEMATIC_VARIADIC(...) (__VA_ARGS__) +// CHECK-MESSAGES: [[@LINE-1]]:9: warning: variadic macro used; consider using a 'constexpr' variadic template function + +#endif