MacroUsageCheck.cpp
3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//===--- MacroUsageCheck.cpp - clang-tidy----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "MacroUsageCheck.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Regex.h"
#include <algorithm>
#include <cctype>
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, const SourceManager &SM,
StringRef RegExp, bool CapsOnly, bool IgnoreCommandLine)
: Check(Check), SM(SM), RegExp(RegExp), CheckCapsOnly(CapsOnly),
IgnoreCommandLineMacros(IgnoreCommandLine) {}
void MacroDefined(const Token &MacroNameTok,
const MacroDirective *MD) override {
if (SM.isWrittenInBuiltinFile(MD->getLocation()) ||
MD->getMacroInfo()->isUsedForHeaderGuard() ||
MD->getMacroInfo()->getNumTokens() == 0)
return;
if (IgnoreCommandLineMacros &&
SM.isWrittenInCommandLineFile(MD->getLocation()))
return;
StringRef MacroName = MacroNameTok.getIdentifierInfo()->getName();
if (!CheckCapsOnly && !llvm::Regex(RegExp).match(MacroName))
Check->warnMacro(MD, MacroName);
if (CheckCapsOnly && !isCapsOnly(MacroName))
Check->warnNaming(MD, MacroName);
}
private:
MacroUsageCheck *Check;
const SourceManager &SM;
StringRef RegExp;
bool CheckCapsOnly;
bool IgnoreCommandLineMacros;
};
} // namespace
void MacroUsageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "AllowedRegexp", AllowedRegexp);
Options.store(Opts, "CheckCapsOnly", CheckCapsOnly);
Options.store(Opts, "IgnoreCommandLineMacros", IgnoreCommandLineMacros);
}
void MacroUsageCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
PP->addPPCallbacks(std::make_unique<MacroUsageCallbacks>(
this, SM, AllowedRegexp, CheckCapsOnly, IgnoreCommandLineMacros));
}
void MacroUsageCheck::warnMacro(const MacroDirective *MD, StringRef MacroName) {
StringRef Message =
"macro '%0' 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 '%0' used; consider using a 'constexpr' "
"variadic template function";
else if (MD->getMacroInfo()->isFunctionLike())
Message = "function-like macro '%0' used; consider a 'constexpr' template "
"function";
diag(MD->getLocation(), Message) << MacroName;
}
void MacroUsageCheck::warnNaming(const MacroDirective *MD,
StringRef MacroName) {
diag(MD->getLocation(), "macro definition does not define the macro name "
"'%0' using all uppercase characters")
<< MacroName;
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang