xref: /llvm-project/clang-tools-extra/clang-tidy/cppcoreguidelines/MacroUsageCheck.cpp (revision 74f201c37ece0c7d221844e99cb1bdeb1ea42a0a)
1 //===--- MacroUsageCheck.cpp - clang-tidy----------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "MacroUsageCheck.h"
11 #include "clang/Frontend/CompilerInstance.h"
12 #include "clang/Lex/PPCallbacks.h"
13 #include "llvm/ADT/STLExtras.h"
14 #include "llvm/Support/Regex.h"
15 #include <algorithm>
16 #include <cctype>
17 
18 namespace clang {
19 namespace tidy {
20 namespace cppcoreguidelines {
21 
22 namespace {
23 
24 bool isCapsOnly(StringRef Name) {
25   return std::all_of(Name.begin(), Name.end(), [](const char c) {
26     if (std::isupper(c) || std::isdigit(c) || c == '_')
27       return true;
28     return false;
29   });
30 }
31 
32 class MacroUsageCallbacks : public PPCallbacks {
33 public:
34   MacroUsageCallbacks(MacroUsageCheck *Check, StringRef RegExp, bool CapsOnly)
35       : Check(Check), RegExp(RegExp), CheckCapsOnly(CapsOnly) {}
36   void MacroDefined(const Token &MacroNameTok,
37                     const MacroDirective *MD) override {
38     if (MD->getMacroInfo()->isUsedForHeaderGuard() ||
39         MD->getMacroInfo()->getNumTokens() == 0)
40       return;
41 
42     StringRef MacroName = MacroNameTok.getIdentifierInfo()->getName();
43     if (!CheckCapsOnly && !llvm::Regex(RegExp).match(MacroName))
44       Check->warnMacro(MD);
45 
46     if (CheckCapsOnly && !isCapsOnly(MacroName))
47       Check->warnNaming(MD);
48   }
49 
50 private:
51   MacroUsageCheck *Check;
52   StringRef RegExp;
53   bool CheckCapsOnly;
54 };
55 } // namespace
56 
57 void MacroUsageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
58   Options.store(Opts, "AllowedRegexp", AllowedRegexp);
59   Options.store(Opts, "CheckCapsOnly", CheckCapsOnly);
60 }
61 
62 void MacroUsageCheck::registerPPCallbacks(CompilerInstance &Compiler) {
63   if (!getLangOpts().CPlusPlus11)
64     return;
65 
66   Compiler.getPreprocessor().addPPCallbacks(
67       llvm::make_unique<MacroUsageCallbacks>(this, AllowedRegexp,
68                                              CheckCapsOnly));
69 }
70 
71 void MacroUsageCheck::warnMacro(const MacroDirective *MD) {
72   StringRef Message =
73       "macro used to declare a constant; consider using a 'constexpr' "
74       "constant";
75 
76   /// A variadic macro is function-like at the same time. Therefore variadic
77   /// macros are checked first and will be excluded for the function-like
78   /// diagnostic.
79   if (MD->getMacroInfo()->isVariadic())
80     Message = "variadic macro used; consider using a 'constexpr' "
81               "variadic template function";
82   else if (MD->getMacroInfo()->isFunctionLike())
83     Message = "function-like macro used; consider a 'constexpr' template "
84               "function";
85 
86   diag(MD->getLocation(), Message);
87 }
88 
89 void MacroUsageCheck::warnNaming(const MacroDirective *MD) {
90   diag(MD->getLocation(), "macro definition does not define the macro name "
91                           "using all uppercase characters");
92 }
93 
94 } // namespace cppcoreguidelines
95 } // namespace tidy
96 } // namespace clang
97