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, llvm::StringRef,
40 ExpectedRepresentation) {
41 if (Node.getOpcode() != Kind || ExpectedRepresentation.empty())
44 const StringRef Spelling =
46 return !Spelling.empty() && Spelling != ExpectedRepresentation;
49AST_MATCHER_P2(UnaryOperator, hasInvalidUnaryOperatorRepresentation,
50 UnaryOperatorKind, Kind, llvm::StringRef,
51 ExpectedRepresentation) {
52 if (Node.getOpcode() != Kind || ExpectedRepresentation.empty())
55 const StringRef Spelling =
57 return !Spelling.empty() && Spelling != ExpectedRepresentation;
60AST_MATCHER_P2(CXXOperatorCallExpr, hasInvalidOverloadedOperatorRepresentation,
61 OverloadedOperatorKind, Kind, llvm::StringRef,
62 ExpectedRepresentation) {
63 if (Node.getOperator() != Kind || ExpectedRepresentation.empty())
66 const StringRef Spelling =
68 return !Spelling.empty() && Spelling != ExpectedRepresentation;
73constexpr std::array<std::pair<llvm::StringRef, llvm::StringRef>, 2U>
76constexpr std::array<std::pair<llvm::StringRef, llvm::StringRef>, 9U>
87static llvm::StringRef
translate(llvm::StringRef Value) {
89 if (Value == Traditional)
91 if (Value == Alternative)
96 if (Value == Traditional)
98 if (Value == Alternative)
109 constexpr llvm::StringRef Separators(
" \t\r\n\0()<>{};,");
110 return Separators.contains(C);
114 switch (Operator[0]) {
126static llvm::StringRef
128 llvm::StringRef Traditional, llvm::StringRef Alternative) {
129 if (llvm::is_contained(
Config, Traditional))
131 if (llvm::is_contained(
Config, Alternative))
138 const T &Operators) {
139 return llvm::any_of(Operators, [&](
const auto &Op) {
148 utils::options::parseStringList(Options.get(
"BinaryOperators",
""))),
149 OverloadedOperators(
utils::options::parseStringList(
150 Options.get(
"OverloadedOperators",
""))) {
157 Options.store(Opts,
"BinaryOperators",
159 Options.store(Opts,
"OverloadedOperators",
163std::optional<TraversalKind>
165 return TK_IgnoreUnlessSpelledInSource;
169 const LangOptions &LangOpts)
const {
170 return LangOpts.CPlusPlus;
173void OperatorsRepresentationCheck::registerBinaryOperatorMatcher(
174 MatchFinder *Finder) {
180 unless(isExpansionInSystemHeader()),
181 anyOf(hasInvalidBinaryOperatorRepresentation(
183 hasInvalidBinaryOperatorRepresentation(
185 hasInvalidBinaryOperatorRepresentation(
187 hasInvalidBinaryOperatorRepresentation(
189 hasInvalidBinaryOperatorRepresentation(
191 hasInvalidBinaryOperatorRepresentation(
193 hasInvalidBinaryOperatorRepresentation(
196 hasInvalidBinaryOperatorRepresentation(
199 hasInvalidBinaryOperatorRepresentation(
206void OperatorsRepresentationCheck::registerUnaryOperatorMatcher(
207 MatchFinder *Finder) {
213 unless(isExpansionInSystemHeader()),
214 anyOf(hasInvalidUnaryOperatorRepresentation(
216 hasInvalidUnaryOperatorRepresentation(
222void OperatorsRepresentationCheck::registerOverloadedOperatorMatcher(
223 MatchFinder *Finder) {
230 unless(isExpansionInSystemHeader()),
232 hasInvalidOverloadedOperatorRepresentation(
235 hasInvalidOverloadedOperatorRepresentation(
238 hasInvalidOverloadedOperatorRepresentation(
241 hasInvalidOverloadedOperatorRepresentation(
244 hasInvalidOverloadedOperatorRepresentation(
246 hasInvalidOverloadedOperatorRepresentation(
249 hasInvalidOverloadedOperatorRepresentation(
252 hasInvalidOverloadedOperatorRepresentation(
255 hasInvalidOverloadedOperatorRepresentation(
258 hasInvalidOverloadedOperatorRepresentation(
261 hasInvalidOverloadedOperatorRepresentation(
264 .bind(
"overloaded_op"),
269 registerBinaryOperatorMatcher(Finder);
270 registerUnaryOperatorMatcher(Finder);
271 registerOverloadedOperatorMatcher(Finder);
275 const MatchFinder::MatchResult &Result) {
278 if (
const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(
"binary_op"))
279 Loc = Op->getOperatorLoc();
280 else if (
const auto *Op = Result.Nodes.getNodeAs<UnaryOperator>(
"unary_op"))
281 Loc = Op->getOperatorLoc();
282 else if (
const auto *Op =
283 Result.Nodes.getNodeAs<CXXOperatorCallExpr>(
"overloaded_op"))
284 Loc = Op->getOperatorLoc();
289 Loc = Result.SourceManager->getSpellingLoc(Loc);
290 if (Loc.isInvalid() || Loc.isMacroID())
293 const CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc);
294 if (TokenRange.isInvalid())
297 const StringRef Spelling = Lexer::getSourceText(
298 TokenRange, *Result.SourceManager, Result.Context->getLangOpts());
299 const StringRef TranslatedSpelling =
translate(Spelling);
301 if (TranslatedSpelling.empty())
304 std::string FixSpelling = TranslatedSpelling.str();
306 StringRef SourceRepresentation =
"an alternative";
307 StringRef TargetRepresentation =
"a traditional";
309 SourceRepresentation =
"a traditional";
310 TargetRepresentation =
"an alternative";
312 const StringRef SpellingEx = Lexer::getSourceText(
313 CharSourceRange::getCharRange(
314 TokenRange.getBegin().getLocWithOffset(-1),
315 TokenRange.getBegin().getLocWithOffset(Spelling.size() + 1U)),
316 *Result.SourceManager, Result.Context->getLangOpts());
317 if (SpellingEx.empty() || !
isSeparator(SpellingEx.front()))
318 FixSpelling.insert(FixSpelling.begin(),
' ');
319 if (SpellingEx.empty() || !
isSeparator(SpellingEx.back()))
320 FixSpelling.push_back(
' ');
325 "'%0' is %1 token spelling, consider using %2 token '%3' for consistency")
326 << Spelling << SourceRepresentation << TargetRepresentation
327 << TranslatedSpelling
328 << 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 bool isAnyOperatorEnabled(const std::vector< llvm::StringRef > &Config, const T &Operators)
static StringRef getOperatorSpelling(SourceLocation Loc, ASTContext &Context)
static bool isNotOperatorStr(llvm::StringRef Value)
constexpr std::array< std::pair< llvm::StringRef, llvm::StringRef >, 9U > OperatorsRepresentation
static llvm::StringRef getRepresentation(const std::vector< llvm::StringRef > &Config, llvm::StringRef Traditional, llvm::StringRef Alternative)
static llvm::StringRef translate(llvm::StringRef Value)
static bool isSeparator(char C) noexcept
constexpr std::array< std::pair< llvm::StringRef, llvm::StringRef >, 2U > UnaryRepresentation
static bool needEscaping(llvm::StringRef Operator)
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap