11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
14#include "llvm/ADT/STLExtras.h"
26 const SourceManager &SM = Context.getSourceManager();
28 Loc = SM.getSpellingLoc(Loc);
32 const CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc);
33 return Lexer::getSourceText(TokenRange, SM, Context.getLangOpts());
38AST_MATCHER_P2(BinaryOperator, hasInvalidBinaryOperatorRepresentation,
39 BinaryOperatorKind, Kind, StringRef, ExpectedRepresentation) {
40 if (Node.getOpcode() != Kind || ExpectedRepresentation.empty())
43 const StringRef Spelling =
45 return !Spelling.empty() && Spelling != ExpectedRepresentation;
48AST_MATCHER_P2(UnaryOperator, hasInvalidUnaryOperatorRepresentation,
49 UnaryOperatorKind, Kind, StringRef, ExpectedRepresentation) {
50 if (Node.getOpcode() != Kind || ExpectedRepresentation.empty())
53 const StringRef Spelling =
55 return !Spelling.empty() && Spelling != ExpectedRepresentation;
58AST_MATCHER_P2(CXXOperatorCallExpr, hasInvalidOverloadedOperatorRepresentation,
59 OverloadedOperatorKind, Kind, StringRef,
60 ExpectedRepresentation) {
61 if (Node.getOperator() != Kind || ExpectedRepresentation.empty())
64 const StringRef Spelling =
66 return !Spelling.empty() && Spelling != ExpectedRepresentation;
72 {{
"!",
"not"}, {
"~",
"compl"}}};
74constexpr std::array<std::pair<StringRef, StringRef>, 9U>
87 if (Value == Traditional)
89 if (Value == Alternative)
94 if (Value == Traditional)
96 if (Value == Alternative)
107 constexpr StringRef Separators(
" \t\r\n\0()<>{};,");
108 return Separators.contains(C);
112 switch (Operator[0]) {
125 StringRef Traditional,
126 StringRef Alternative) {
127 if (llvm::is_contained(
Config, Traditional))
129 if (llvm::is_contained(
Config, Alternative))
136 const T &Operators) {
137 return llvm::any_of(Operators, [&](
const auto &Op) {
146 utils::options::parseStringList(Options.get(
"BinaryOperators",
""))),
147 OverloadedOperators(
utils::options::parseStringList(
148 Options.get(
"OverloadedOperators",
""))) {
155 Options.store(Opts,
"BinaryOperators",
157 Options.store(Opts,
"OverloadedOperators",
161std::optional<TraversalKind>
163 return TK_IgnoreUnlessSpelledInSource;
167 const LangOptions &LangOpts)
const {
168 return LangOpts.CPlusPlus;
171void OperatorsRepresentationCheck::registerBinaryOperatorMatcher(
172 MatchFinder *Finder) {
178 anyOf(hasInvalidBinaryOperatorRepresentation(
180 hasInvalidBinaryOperatorRepresentation(
182 hasInvalidBinaryOperatorRepresentation(
184 hasInvalidBinaryOperatorRepresentation(
186 hasInvalidBinaryOperatorRepresentation(
188 hasInvalidBinaryOperatorRepresentation(
190 hasInvalidBinaryOperatorRepresentation(
193 hasInvalidBinaryOperatorRepresentation(
196 hasInvalidBinaryOperatorRepresentation(
203void OperatorsRepresentationCheck::registerUnaryOperatorMatcher(
204 MatchFinder *Finder) {
210 anyOf(hasInvalidUnaryOperatorRepresentation(
212 hasInvalidUnaryOperatorRepresentation(
218void OperatorsRepresentationCheck::registerOverloadedOperatorMatcher(
219 MatchFinder *Finder) {
227 hasInvalidOverloadedOperatorRepresentation(
230 hasInvalidOverloadedOperatorRepresentation(
233 hasInvalidOverloadedOperatorRepresentation(
236 hasInvalidOverloadedOperatorRepresentation(
239 hasInvalidOverloadedOperatorRepresentation(
241 hasInvalidOverloadedOperatorRepresentation(
244 hasInvalidOverloadedOperatorRepresentation(
247 hasInvalidOverloadedOperatorRepresentation(
250 hasInvalidOverloadedOperatorRepresentation(
253 hasInvalidOverloadedOperatorRepresentation(
256 hasInvalidOverloadedOperatorRepresentation(
259 .bind(
"overloaded_op"),
264 registerBinaryOperatorMatcher(Finder);
265 registerUnaryOperatorMatcher(Finder);
266 registerOverloadedOperatorMatcher(Finder);
270 const MatchFinder::MatchResult &Result) {
273 if (
const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(
"binary_op"))
274 Loc = Op->getOperatorLoc();
275 else if (
const auto *Op = Result.Nodes.getNodeAs<UnaryOperator>(
"unary_op"))
276 Loc = Op->getOperatorLoc();
277 else if (
const auto *Op =
278 Result.Nodes.getNodeAs<CXXOperatorCallExpr>(
"overloaded_op"))
279 Loc = Op->getOperatorLoc();
284 Loc = Result.SourceManager->getSpellingLoc(Loc);
285 if (Loc.isInvalid() || Loc.isMacroID())
288 const CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc);
289 if (TokenRange.isInvalid())
292 const StringRef Spelling = Lexer::getSourceText(
293 TokenRange, *Result.SourceManager, Result.Context->getLangOpts());
294 const StringRef TranslatedSpelling =
translate(Spelling);
296 if (TranslatedSpelling.empty())
299 std::string FixSpelling = TranslatedSpelling.str();
301 StringRef SourceRepresentation =
"an alternative";
302 StringRef TargetRepresentation =
"a traditional";
304 SourceRepresentation =
"a traditional";
305 TargetRepresentation =
"an alternative";
307 const StringRef SpellingEx = Lexer::getSourceText(
308 CharSourceRange::getCharRange(
309 TokenRange.getBegin().getLocWithOffset(-1),
310 TokenRange.getBegin().getLocWithOffset(Spelling.size() + 1U)),
311 *Result.SourceManager, Result.Context->getLangOpts());
312 if (SpellingEx.empty() || !
isSeparator(SpellingEx.front()))
313 FixSpelling.insert(FixSpelling.begin(),
' ');
314 if (SpellingEx.empty() || !
isSeparator(SpellingEx.back()))
315 FixSpelling.push_back(
' ');
320 "'%0' is %1 token spelling, consider using %2 token '%3' for consistency")
321 << Spelling << SourceRepresentation << TargetRepresentation
322 << TranslatedSpelling
323 << FixItHint::CreateReplacement(TokenRange, FixSpelling);
static cl::opt< std::string > Config("config", desc(R"(
Specifies a configuration in YAML/JSON format:
-config="{Checks:' *', CheckOptions:{x:y}}"
When the value is empty, clang-tidy will
attempt to find a file named .clang-tidy for
each source file in its parent directories.
)"), cl::init(""), cl::cat(ClangTidyCategory))
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
std::optional< TraversalKind > getCheckTraversalKind() const override
OperatorsRepresentationCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static StringRef getOperatorSpelling(SourceLocation Loc, ASTContext &Context)
constexpr std::array< std::pair< StringRef, StringRef >, 9U > OperatorsRepresentation
static StringRef translate(StringRef Value)
static bool needEscaping(StringRef Operator)
static bool isNotOperatorStr(StringRef Value)
static bool isSeparator(char C) noexcept
static StringRef getRepresentation(const std::vector< StringRef > &Config, StringRef Traditional, StringRef Alternative)
constexpr std::array< std::pair< StringRef, StringRef >, 2U > UnaryRepresentation
static bool isAnyOperatorEnabled(const std::vector< StringRef > &Config, const T &Operators)
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap