11#include "../utils/OptionsUtils.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
15#include "llvm/ADT/STLExtras.h"
27 SourceManager &SM = Context.getSourceManager();
29 Loc = SM.getSpellingLoc(
Loc);
33 const CharSourceRange TokenRange = CharSourceRange::getTokenRange(
Loc);
34 return Lexer::getSourceText(TokenRange, SM, Context.getLangOpts());
39AST_MATCHER_P2(BinaryOperator, hasInvalidBinaryOperatorRepresentation,
40 BinaryOperatorKind, Kind, llvm::StringRef,
41 ExpectedRepresentation) {
42 if (
Node.getOpcode() != Kind || ExpectedRepresentation.empty())
47 return !Spelling.empty() && Spelling != ExpectedRepresentation;
50AST_MATCHER_P2(UnaryOperator, hasInvalidUnaryOperatorRepresentation,
51 UnaryOperatorKind, Kind, llvm::StringRef,
52 ExpectedRepresentation) {
53 if (
Node.getOpcode() != Kind || ExpectedRepresentation.empty())
58 return !Spelling.empty() && Spelling != ExpectedRepresentation;
61AST_MATCHER_P2(CXXOperatorCallExpr, hasInvalidOverloadedOperatorRepresentation,
62 OverloadedOperatorKind, Kind, llvm::StringRef,
63 ExpectedRepresentation) {
64 if (
Node.getOperator() != Kind || ExpectedRepresentation.empty())
69 return !Spelling.empty() && Spelling != ExpectedRepresentation;
74constexpr std::array<std::pair<llvm::StringRef, llvm::StringRef>, 2U>
77constexpr std::array<std::pair<llvm::StringRef, llvm::StringRef>, 9U>
88static llvm::StringRef
translate(llvm::StringRef Value) {
90 if (Value == Traditional)
92 if (Value == Alternative)
97 if (Value == Traditional)
99 if (Value == Alternative)
110 constexpr llvm::StringRef Separators(
" \t\r\n\0()<>{};,");
111 return Separators.contains(
C);
115 switch (Operator[0]) {
127static llvm::StringRef
129 llvm::StringRef Traditional, llvm::StringRef Alternative) {
130 if (llvm::is_contained(
Config, Traditional))
132 if (llvm::is_contained(
Config, Alternative))
139 const T &Operators) {
140 for (
const auto &[traditional, alternative] : Operators) {
151 utils::options::parseStringList(Options.get(
"BinaryOperators",
""))),
152 OverloadedOperators(utils::options::parseStringList(
153 Options.get(
"OverloadedOperators",
""))) {
166std::optional<TraversalKind>
168 return TK_IgnoreUnlessSpelledInSource;
172 const LangOptions &LangOpts)
const {
173 return LangOpts.CPlusPlus;
176void OperatorsRepresentationCheck::registerBinaryOperatorMatcher(
177 MatchFinder *Finder) {
183 unless(isExpansionInSystemHeader()),
184 anyOf(hasInvalidBinaryOperatorRepresentation(
186 hasInvalidBinaryOperatorRepresentation(
188 hasInvalidBinaryOperatorRepresentation(
190 hasInvalidBinaryOperatorRepresentation(
192 hasInvalidBinaryOperatorRepresentation(
194 hasInvalidBinaryOperatorRepresentation(
196 hasInvalidBinaryOperatorRepresentation(
199 hasInvalidBinaryOperatorRepresentation(
202 hasInvalidBinaryOperatorRepresentation(
209void OperatorsRepresentationCheck::registerUnaryOperatorMatcher(
210 MatchFinder *Finder) {
216 unless(isExpansionInSystemHeader()),
217 anyOf(hasInvalidUnaryOperatorRepresentation(
219 hasInvalidUnaryOperatorRepresentation(
225void OperatorsRepresentationCheck::registerOverloadedOperatorMatcher(
226 MatchFinder *Finder) {
233 unless(isExpansionInSystemHeader()),
235 hasInvalidOverloadedOperatorRepresentation(
238 hasInvalidOverloadedOperatorRepresentation(
241 hasInvalidOverloadedOperatorRepresentation(
244 hasInvalidOverloadedOperatorRepresentation(
247 hasInvalidOverloadedOperatorRepresentation(
249 hasInvalidOverloadedOperatorRepresentation(
252 hasInvalidOverloadedOperatorRepresentation(
255 hasInvalidOverloadedOperatorRepresentation(
258 hasInvalidOverloadedOperatorRepresentation(
261 hasInvalidOverloadedOperatorRepresentation(
264 hasInvalidOverloadedOperatorRepresentation(
267 .bind(
"overloaded_op"),
272 registerBinaryOperatorMatcher(Finder);
273 registerUnaryOperatorMatcher(Finder);
274 registerOverloadedOperatorMatcher(Finder);
278 const MatchFinder::MatchResult &Result) {
282 if (
const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(
"binary_op"))
283 Loc = Op->getOperatorLoc();
284 else if (
const auto *Op = Result.Nodes.getNodeAs<UnaryOperator>(
"unary_op"))
285 Loc = Op->getOperatorLoc();
286 else if (
const auto *Op =
287 Result.Nodes.getNodeAs<CXXOperatorCallExpr>(
"overloaded_op"))
288 Loc = Op->getOperatorLoc();
293 Loc = Result.SourceManager->getSpellingLoc(
Loc);
294 if (
Loc.isInvalid() ||
Loc.isMacroID())
297 const CharSourceRange TokenRange = CharSourceRange::getTokenRange(
Loc);
298 if (TokenRange.isInvalid())
301 StringRef Spelling = Lexer::getSourceText(TokenRange, *Result.SourceManager,
302 Result.Context->getLangOpts());
303 StringRef TranslatedSpelling =
translate(Spelling);
305 if (TranslatedSpelling.empty())
308 std::string FixSpelling = TranslatedSpelling.str();
310 StringRef SourceRepresentation =
"an alternative";
311 StringRef TargetRepresentation =
"a traditional";
313 SourceRepresentation =
"a traditional";
314 TargetRepresentation =
"an alternative";
316 StringRef SpellingEx = Lexer::getSourceText(
317 CharSourceRange::getCharRange(
318 TokenRange.getBegin().getLocWithOffset(-1),
319 TokenRange.getBegin().getLocWithOffset(Spelling.size() + 1U)),
320 *Result.SourceManager, Result.Context->getLangOpts());
321 if (SpellingEx.empty() || !
isSeparator(SpellingEx.front()))
322 FixSpelling.insert(FixSpelling.begin(),
' ');
323 if (SpellingEx.empty() || !
isSeparator(SpellingEx.back()))
324 FixSpelling.push_back(
' ');
329 "'%0' is %1 token spelling, consider using %2 token '%3' for consistency")
330 << Spelling << SourceRepresentation << TargetRepresentation
331 << TranslatedSpelling
332 << FixItHint::CreateReplacement(TokenRange, FixSpelling);
llvm::SmallString< 256U > Name
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))
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
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
ClangTidyChecks that register ASTMatchers should do the actual work in here.
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override
Override this to disable registering matchers and PP callbacks if an invalid language version is bein...
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
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