[clang-tidy] implement cppcoreguidelines macro rules

Summary:
In short macros are discouraged by multiple rules (and sometimes reference randomly). [Enum.1], [ES.30], [ES.31]
This check allows only headerguards and empty macros for annotation.

Reviewers: aaron.ballman, hokein

Reviewed By: aaron.ballman

Subscribers: jbcoe, Eugene.Zelenko, klimek, nemanjai, mgorny, xazax.hun, kbarton, cfe-commits

Differential Revision: https://reviews.llvm.org/D41648

llvm-svn: 344940
This commit is contained in:
Jonas Toth 2018-10-22 19:20:01 +00:00
parent c2e58e7265
commit 552b62ed1f
10 changed files with 252 additions and 0 deletions

View File

@ -4,6 +4,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule
AvoidGotoCheck.cpp
CppCoreGuidelinesTidyModule.cpp
InterfacesGlobalInitCheck.cpp
MacroUsageCheck.cpp
NarrowingConversionsCheck.cpp
NoMallocCheck.cpp
OwningMemoryCheck.cpp

View File

@ -15,6 +15,7 @@
#include "../readability/MagicNumbersCheck.h"
#include "AvoidGotoCheck.h"
#include "InterfacesGlobalInitCheck.h"
#include "MacroUsageCheck.h"
#include "NarrowingConversionsCheck.h"
#include "NoMallocCheck.h"
#include "OwningMemoryCheck.h"
@ -45,6 +46,8 @@ public:
"cppcoreguidelines-avoid-magic-numbers");
CheckFactories.registerCheck<InterfacesGlobalInitCheck>(
"cppcoreguidelines-interfaces-global-init");
CheckFactories.registerCheck<MacroUsageCheck>(
"cppcoreguidelines-macro-usage");
CheckFactories.registerCheck<NarrowingConversionsCheck>(
"cppcoreguidelines-narrowing-conversions");
CheckFactories.registerCheck<NoMallocCheck>("cppcoreguidelines-no-malloc");

View File

@ -0,0 +1,95 @@
//===--- 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 <algorithm>
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<MacroUsageCallbacks>(this, AllowedRegexp,
CheckCapsOnly));
}
void MacroUsageCheck::warnMacro(const MacroDirective *MD) {
StringRef Message =
"macro used to declare a constant; consider using a 'constexpr' "
"constant";
/// A variadic macro is function-like at the same time. Therefore variadic
/// macros are checked first and will be excluded for the function-like
/// diagnostic.
if (MD->getMacroInfo()->isVariadic())
Message = "variadic macro used; consider using a 'constexpr' "
"variadic template function";
else if (MD->getMacroInfo()->isFunctionLike())
Message = "function-like macro used; consider a 'constexpr' template "
"function";
diag(MD->getLocation(), Message);
}
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

View File

@ -0,0 +1,48 @@
//===--- 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 <string>
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// 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

View File

@ -103,6 +103,12 @@ Improvements to clang-tidy
Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests
``absl::StrAppend()`` should be used instead.
- New :doc:`cppcoreguidelines-macro-usage
<clang-tidy/checks/cppcoreguidelines-macro-usage>` check.
Find macro usage that is considered problematic because better language
constructs exist for the task.
- New :doc:`misc-non-private-member-variables-in-classes
<clang-tidy/checks/misc-non-private-member-variables-in-classes>` check.

View File

@ -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 <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#enum1-prefer-enumerations-over-macros>`_,
`ES.30 <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es30-dont-use-macros-for-program-text-manipulation>`_,
`ES.31 <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es31-dont-use-macros-for-constants-or-functions>`_ and
`ES.33 <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es33-if-you-must-use-macros-give-them-unique-names>`_.
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 on 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`.

View File

@ -89,6 +89,7 @@ Clang-Tidy Checks
cppcoreguidelines-avoid-magic-numbers (redirects to readability-magic-numbers) <cppcoreguidelines-avoid-magic-numbers>
cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) <cppcoreguidelines-c-copy-assignment-signature>
cppcoreguidelines-interfaces-global-init
cppcoreguidelines-macro-usage
cppcoreguidelines-narrowing-conversions
cppcoreguidelines-no-malloc
cppcoreguidelines-non-private-member-variables-in-classes (redirects to misc-non-private-member-variables-in-classes) <cppcoreguidelines-non-private-member-variables-in-classes>

View File

@ -0,0 +1,24 @@
// 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
//
#define problematic_variadic2(x, ...) (__VA_ARGS__)
// CHECK-MESSAGES: [[@LINE-1]]:9: warning: macro definition does not define the macro name using all uppercase characters
#define OKISH_CONSTANT 42
#define OKISH_FUNCTION(x, y) ((a) > (b) ? (a) : (b))
#define OKISH_VARIADIC(...) (__VA_ARGS__)
#endif

View File

@ -0,0 +1,28 @@
// 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 PROBLEMATIC_VARIADIC2(x, ...) (__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__)
#define TEST_VARIADIC2(x, ...) (__VA_ARGS__)
#endif

View File

@ -0,0 +1,18 @@
// 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
#define PROBLEMATIC_VARIADIC2(x, ...) (__VA_ARGS__)
// CHECK-MESSAGES: [[@LINE-1]]:9: warning: variadic macro used; consider using a 'constexpr' variadic template function
#endif