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 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())
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())
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())
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 for (
const auto &[traditional, alternative] : Operators) {
150 utils::options::parseStringList(Options.get(
"BinaryOperators",
""))),
151 OverloadedOperators(
utils::options::parseStringList(
152 Options.get(
"OverloadedOperators",
""))) {
159 Options.store(Opts,
"BinaryOperators",
161 Options.store(Opts,
"OverloadedOperators",
165std::optional<TraversalKind>
167 return TK_IgnoreUnlessSpelledInSource;
171 const LangOptions &LangOpts)
const {
172 return LangOpts.CPlusPlus;
175void OperatorsRepresentationCheck::registerBinaryOperatorMatcher(
176 MatchFinder *Finder) {
182 unless(isExpansionInSystemHeader()),
183 anyOf(hasInvalidBinaryOperatorRepresentation(
185 hasInvalidBinaryOperatorRepresentation(
187 hasInvalidBinaryOperatorRepresentation(
189 hasInvalidBinaryOperatorRepresentation(
191 hasInvalidBinaryOperatorRepresentation(
193 hasInvalidBinaryOperatorRepresentation(
195 hasInvalidBinaryOperatorRepresentation(
198 hasInvalidBinaryOperatorRepresentation(
201 hasInvalidBinaryOperatorRepresentation(
208void OperatorsRepresentationCheck::registerUnaryOperatorMatcher(
209 MatchFinder *Finder) {
215 unless(isExpansionInSystemHeader()),
216 anyOf(hasInvalidUnaryOperatorRepresentation(
218 hasInvalidUnaryOperatorRepresentation(
224void OperatorsRepresentationCheck::registerOverloadedOperatorMatcher(
225 MatchFinder *Finder) {
232 unless(isExpansionInSystemHeader()),
234 hasInvalidOverloadedOperatorRepresentation(
237 hasInvalidOverloadedOperatorRepresentation(
240 hasInvalidOverloadedOperatorRepresentation(
243 hasInvalidOverloadedOperatorRepresentation(
246 hasInvalidOverloadedOperatorRepresentation(
248 hasInvalidOverloadedOperatorRepresentation(
251 hasInvalidOverloadedOperatorRepresentation(
254 hasInvalidOverloadedOperatorRepresentation(
257 hasInvalidOverloadedOperatorRepresentation(
260 hasInvalidOverloadedOperatorRepresentation(
263 hasInvalidOverloadedOperatorRepresentation(
266 .bind(
"overloaded_op"),
271 registerBinaryOperatorMatcher(Finder);
272 registerUnaryOperatorMatcher(Finder);
273 registerOverloadedOperatorMatcher(Finder);
277 const MatchFinder::MatchResult &Result) {
281 if (
const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(
"binary_op"))
282 Loc = Op->getOperatorLoc();
283 else if (
const auto *Op = Result.Nodes.getNodeAs<UnaryOperator>(
"unary_op"))
284 Loc = Op->getOperatorLoc();
285 else if (
const auto *Op =
286 Result.Nodes.getNodeAs<CXXOperatorCallExpr>(
"overloaded_op"))
287 Loc = Op->getOperatorLoc();
292 Loc = Result.SourceManager->getSpellingLoc(Loc);
293 if (Loc.isInvalid() || Loc.isMacroID())
296 const CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc);
297 if (TokenRange.isInvalid())
300 StringRef Spelling = Lexer::getSourceText(TokenRange, *Result.SourceManager,
301 Result.Context->getLangOpts());
302 StringRef TranslatedSpelling =
translate(Spelling);
304 if (TranslatedSpelling.empty())
307 std::string FixSpelling = TranslatedSpelling.str();
309 StringRef SourceRepresentation =
"an alternative";
310 StringRef TargetRepresentation =
"a traditional";
312 SourceRepresentation =
"a traditional";
313 TargetRepresentation =
"an alternative";
315 StringRef SpellingEx = Lexer::getSourceText(
316 CharSourceRange::getCharRange(
317 TokenRange.getBegin().getLocWithOffset(-1),
318 TokenRange.getBegin().getLocWithOffset(Spelling.size() + 1U)),
319 *Result.SourceManager, Result.Context->getLangOpts());
320 if (SpellingEx.empty() || !
isSeparator(SpellingEx.front()))
321 FixSpelling.insert(FixSpelling.begin(),
' ');
322 if (SpellingEx.empty() || !
isSeparator(SpellingEx.back()))
323 FixSpelling.push_back(
' ');
328 "'%0' is %1 token spelling, consider using %2 token '%3' for consistency")
329 << Spelling << SourceRepresentation << TargetRepresentation
330 << TranslatedSpelling
331 << 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