1a0848542SPiotr Zegar //===--- OperatorsRepresentationCheck.cpp - clang-tidy
2a0848542SPiotr Zegar //--------------------------===//
3a0848542SPiotr Zegar //
4a0848542SPiotr Zegar // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5a0848542SPiotr Zegar // See https://llvm.org/LICENSE.txt for license information.
6a0848542SPiotr Zegar // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7a0848542SPiotr Zegar //
8a0848542SPiotr Zegar //===----------------------------------------------------------------------===//
9a0848542SPiotr Zegar
10a0848542SPiotr Zegar #include "OperatorsRepresentationCheck.h"
11a0848542SPiotr Zegar #include "../utils/OptionsUtils.h"
12a0848542SPiotr Zegar #include "clang/AST/ASTContext.h"
13a0848542SPiotr Zegar #include "clang/ASTMatchers/ASTMatchFinder.h"
14a0848542SPiotr Zegar #include "clang/Lex/Lexer.h"
15a0848542SPiotr Zegar #include "llvm/ADT/STLExtras.h"
16a0848542SPiotr Zegar #include <array>
17a0848542SPiotr Zegar #include <utility>
18a0848542SPiotr Zegar
19a0848542SPiotr Zegar using namespace clang::ast_matchers;
20a0848542SPiotr Zegar
21a0848542SPiotr Zegar namespace clang::tidy::readability {
22a0848542SPiotr Zegar
getOperatorSpelling(SourceLocation Loc,ASTContext & Context)23a0848542SPiotr Zegar static StringRef getOperatorSpelling(SourceLocation Loc, ASTContext &Context) {
24a0848542SPiotr Zegar if (Loc.isInvalid())
25a0848542SPiotr Zegar return {};
26a0848542SPiotr Zegar
27a0848542SPiotr Zegar SourceManager &SM = Context.getSourceManager();
28a0848542SPiotr Zegar
29a0848542SPiotr Zegar Loc = SM.getSpellingLoc(Loc);
30a0848542SPiotr Zegar if (Loc.isInvalid())
31a0848542SPiotr Zegar return {};
32a0848542SPiotr Zegar
33a0848542SPiotr Zegar const CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc);
34a0848542SPiotr Zegar return Lexer::getSourceText(TokenRange, SM, Context.getLangOpts());
35a0848542SPiotr Zegar }
36a0848542SPiotr Zegar
37a0848542SPiotr Zegar namespace {
38a0848542SPiotr Zegar
AST_MATCHER_P2(BinaryOperator,hasInvalidBinaryOperatorRepresentation,BinaryOperatorKind,Kind,llvm::StringRef,ExpectedRepresentation)39a0848542SPiotr Zegar AST_MATCHER_P2(BinaryOperator, hasInvalidBinaryOperatorRepresentation,
40a0848542SPiotr Zegar BinaryOperatorKind, Kind, llvm::StringRef,
41a0848542SPiotr Zegar ExpectedRepresentation) {
42a0848542SPiotr Zegar if (Node.getOpcode() != Kind || ExpectedRepresentation.empty())
43a0848542SPiotr Zegar return false;
44a0848542SPiotr Zegar
45a0848542SPiotr Zegar StringRef Spelling =
46a0848542SPiotr Zegar getOperatorSpelling(Node.getOperatorLoc(), Finder->getASTContext());
47a0848542SPiotr Zegar return !Spelling.empty() && Spelling != ExpectedRepresentation;
48a0848542SPiotr Zegar }
49a0848542SPiotr Zegar
AST_MATCHER_P2(UnaryOperator,hasInvalidUnaryOperatorRepresentation,UnaryOperatorKind,Kind,llvm::StringRef,ExpectedRepresentation)50a0848542SPiotr Zegar AST_MATCHER_P2(UnaryOperator, hasInvalidUnaryOperatorRepresentation,
51a0848542SPiotr Zegar UnaryOperatorKind, Kind, llvm::StringRef,
52a0848542SPiotr Zegar ExpectedRepresentation) {
53a0848542SPiotr Zegar if (Node.getOpcode() != Kind || ExpectedRepresentation.empty())
54a0848542SPiotr Zegar return false;
55a0848542SPiotr Zegar
56a0848542SPiotr Zegar StringRef Spelling =
57a0848542SPiotr Zegar getOperatorSpelling(Node.getOperatorLoc(), Finder->getASTContext());
58a0848542SPiotr Zegar return !Spelling.empty() && Spelling != ExpectedRepresentation;
59a0848542SPiotr Zegar }
60a0848542SPiotr Zegar
AST_MATCHER_P2(CXXOperatorCallExpr,hasInvalidOverloadedOperatorRepresentation,OverloadedOperatorKind,Kind,llvm::StringRef,ExpectedRepresentation)61a0848542SPiotr Zegar AST_MATCHER_P2(CXXOperatorCallExpr, hasInvalidOverloadedOperatorRepresentation,
62a0848542SPiotr Zegar OverloadedOperatorKind, Kind, llvm::StringRef,
63a0848542SPiotr Zegar ExpectedRepresentation) {
64a0848542SPiotr Zegar if (Node.getOperator() != Kind || ExpectedRepresentation.empty())
65a0848542SPiotr Zegar return false;
66a0848542SPiotr Zegar
67a0848542SPiotr Zegar StringRef Spelling =
68a0848542SPiotr Zegar getOperatorSpelling(Node.getOperatorLoc(), Finder->getASTContext());
69a0848542SPiotr Zegar return !Spelling.empty() && Spelling != ExpectedRepresentation;
70a0848542SPiotr Zegar }
71a0848542SPiotr Zegar
72a0848542SPiotr Zegar } // namespace
73a0848542SPiotr Zegar
74a0848542SPiotr Zegar constexpr std::array<std::pair<llvm::StringRef, llvm::StringRef>, 2U>
75a0848542SPiotr Zegar UnaryRepresentation{{{"!", "not"}, {"~", "compl"}}};
76a0848542SPiotr Zegar
77a0848542SPiotr Zegar constexpr std::array<std::pair<llvm::StringRef, llvm::StringRef>, 9U>
78a0848542SPiotr Zegar OperatorsRepresentation{{{"&&", "and"},
79a0848542SPiotr Zegar {"||", "or"},
80a0848542SPiotr Zegar {"^", "xor"},
81a0848542SPiotr Zegar {"&", "bitand"},
82a0848542SPiotr Zegar {"|", "bitor"},
83a0848542SPiotr Zegar {"&=", "and_eq"},
84a0848542SPiotr Zegar {"|=", "or_eq"},
85a0848542SPiotr Zegar {"!=", "not_eq"},
86a0848542SPiotr Zegar {"^=", "xor_eq"}}};
87a0848542SPiotr Zegar
translate(llvm::StringRef Value)88a0848542SPiotr Zegar static llvm::StringRef translate(llvm::StringRef Value) {
89a0848542SPiotr Zegar for (const auto &[Traditional, Alternative] : UnaryRepresentation) {
90a0848542SPiotr Zegar if (Value == Traditional)
91a0848542SPiotr Zegar return Alternative;
92a0848542SPiotr Zegar if (Value == Alternative)
93a0848542SPiotr Zegar return Traditional;
94a0848542SPiotr Zegar }
95a0848542SPiotr Zegar
96a0848542SPiotr Zegar for (const auto &[Traditional, Alternative] : OperatorsRepresentation) {
97a0848542SPiotr Zegar if (Value == Traditional)
98a0848542SPiotr Zegar return Alternative;
99a0848542SPiotr Zegar if (Value == Alternative)
100a0848542SPiotr Zegar return Traditional;
101a0848542SPiotr Zegar }
102a0848542SPiotr Zegar return {};
103a0848542SPiotr Zegar }
104a0848542SPiotr Zegar
isNotOperatorStr(llvm::StringRef Value)105a0848542SPiotr Zegar static bool isNotOperatorStr(llvm::StringRef Value) {
106a0848542SPiotr Zegar return translate(Value).empty();
107a0848542SPiotr Zegar }
108a0848542SPiotr Zegar
isSeparator(char C)109a0848542SPiotr Zegar static bool isSeparator(char C) noexcept {
110a0848542SPiotr Zegar constexpr llvm::StringRef Separators(" \t\r\n\0()<>{};,");
111cebdf206SKazu Hirata return Separators.contains(C);
112a0848542SPiotr Zegar }
113a0848542SPiotr Zegar
needEscaping(llvm::StringRef Operator)114a0848542SPiotr Zegar static bool needEscaping(llvm::StringRef Operator) {
115a0848542SPiotr Zegar switch (Operator[0]) {
116a0848542SPiotr Zegar case '&':
117a0848542SPiotr Zegar case '|':
118a0848542SPiotr Zegar case '!':
119a0848542SPiotr Zegar case '^':
120a0848542SPiotr Zegar case '~':
121a0848542SPiotr Zegar return false;
122a0848542SPiotr Zegar default:
123a0848542SPiotr Zegar return true;
124a0848542SPiotr Zegar }
125a0848542SPiotr Zegar }
126a0848542SPiotr Zegar
127a0848542SPiotr Zegar static llvm::StringRef
getRepresentation(const std::vector<llvm::StringRef> & Config,llvm::StringRef Traditional,llvm::StringRef Alternative)128a0848542SPiotr Zegar getRepresentation(const std::vector<llvm::StringRef> &Config,
129a0848542SPiotr Zegar llvm::StringRef Traditional, llvm::StringRef Alternative) {
130a0848542SPiotr Zegar if (llvm::is_contained(Config, Traditional))
131a0848542SPiotr Zegar return Traditional;
132a0848542SPiotr Zegar if (llvm::is_contained(Config, Alternative))
133a0848542SPiotr Zegar return Alternative;
134a0848542SPiotr Zegar return {};
135a0848542SPiotr Zegar }
136a0848542SPiotr Zegar
137a0848542SPiotr Zegar template <typename T>
isAnyOperatorEnabled(const std::vector<llvm::StringRef> & Config,const T & Operators)138a0848542SPiotr Zegar static bool isAnyOperatorEnabled(const std::vector<llvm::StringRef> &Config,
139*26078f33SPiotr Zegar const T &Operators) {
140a0848542SPiotr Zegar for (const auto &[traditional, alternative] : Operators) {
141a0848542SPiotr Zegar if (!getRepresentation(Config, traditional, alternative).empty())
142a0848542SPiotr Zegar return true;
143a0848542SPiotr Zegar }
144a0848542SPiotr Zegar return false;
145a0848542SPiotr Zegar }
146a0848542SPiotr Zegar
OperatorsRepresentationCheck(StringRef Name,ClangTidyContext * Context)147a0848542SPiotr Zegar OperatorsRepresentationCheck::OperatorsRepresentationCheck(
148a0848542SPiotr Zegar StringRef Name, ClangTidyContext *Context)
149a0848542SPiotr Zegar : ClangTidyCheck(Name, Context),
150a0848542SPiotr Zegar BinaryOperators(
151a0848542SPiotr Zegar utils::options::parseStringList(Options.get("BinaryOperators", ""))),
152a0848542SPiotr Zegar OverloadedOperators(utils::options::parseStringList(
153a0848542SPiotr Zegar Options.get("OverloadedOperators", ""))) {
154a0848542SPiotr Zegar llvm::erase_if(BinaryOperators, isNotOperatorStr);
155a0848542SPiotr Zegar llvm::erase_if(OverloadedOperators, isNotOperatorStr);
156a0848542SPiotr Zegar }
157a0848542SPiotr Zegar
storeOptions(ClangTidyOptions::OptionMap & Opts)158a0848542SPiotr Zegar void OperatorsRepresentationCheck::storeOptions(
159a0848542SPiotr Zegar ClangTidyOptions::OptionMap &Opts) {
160a0848542SPiotr Zegar Options.store(Opts, "BinaryOperators",
161a0848542SPiotr Zegar utils::options::serializeStringList(BinaryOperators));
162a0848542SPiotr Zegar Options.store(Opts, "OverloadedOperators",
163a0848542SPiotr Zegar utils::options::serializeStringList(OverloadedOperators));
164a0848542SPiotr Zegar }
165a0848542SPiotr Zegar
166a0848542SPiotr Zegar std::optional<TraversalKind>
getCheckTraversalKind() const167a0848542SPiotr Zegar OperatorsRepresentationCheck::getCheckTraversalKind() const {
168a0848542SPiotr Zegar return TK_IgnoreUnlessSpelledInSource;
169a0848542SPiotr Zegar }
170a0848542SPiotr Zegar
isLanguageVersionSupported(const LangOptions & LangOpts) const171a0848542SPiotr Zegar bool OperatorsRepresentationCheck::isLanguageVersionSupported(
172a0848542SPiotr Zegar const LangOptions &LangOpts) const {
173a0848542SPiotr Zegar return LangOpts.CPlusPlus;
174a0848542SPiotr Zegar }
175a0848542SPiotr Zegar
registerBinaryOperatorMatcher(MatchFinder * Finder)176a0848542SPiotr Zegar void OperatorsRepresentationCheck::registerBinaryOperatorMatcher(
177a0848542SPiotr Zegar MatchFinder *Finder) {
178a0848542SPiotr Zegar if (!isAnyOperatorEnabled(BinaryOperators, OperatorsRepresentation))
179a0848542SPiotr Zegar return;
180a0848542SPiotr Zegar
181a0848542SPiotr Zegar Finder->addMatcher(
182a0848542SPiotr Zegar binaryOperator(
183a0848542SPiotr Zegar unless(isExpansionInSystemHeader()),
184a0848542SPiotr Zegar anyOf(hasInvalidBinaryOperatorRepresentation(
185a0848542SPiotr Zegar BO_LAnd, getRepresentation(BinaryOperators, "&&", "and")),
186a0848542SPiotr Zegar hasInvalidBinaryOperatorRepresentation(
187a0848542SPiotr Zegar BO_LOr, getRepresentation(BinaryOperators, "||", "or")),
188a0848542SPiotr Zegar hasInvalidBinaryOperatorRepresentation(
189a0848542SPiotr Zegar BO_NE, getRepresentation(BinaryOperators, "!=", "not_eq")),
190a0848542SPiotr Zegar hasInvalidBinaryOperatorRepresentation(
191a0848542SPiotr Zegar BO_Xor, getRepresentation(BinaryOperators, "^", "xor")),
192a0848542SPiotr Zegar hasInvalidBinaryOperatorRepresentation(
193a0848542SPiotr Zegar BO_And, getRepresentation(BinaryOperators, "&", "bitand")),
194a0848542SPiotr Zegar hasInvalidBinaryOperatorRepresentation(
195a0848542SPiotr Zegar BO_Or, getRepresentation(BinaryOperators, "|", "bitor")),
196a0848542SPiotr Zegar hasInvalidBinaryOperatorRepresentation(
197a0848542SPiotr Zegar BO_AndAssign,
198a0848542SPiotr Zegar getRepresentation(BinaryOperators, "&=", "and_eq")),
199a0848542SPiotr Zegar hasInvalidBinaryOperatorRepresentation(
200a0848542SPiotr Zegar BO_OrAssign,
201a0848542SPiotr Zegar getRepresentation(BinaryOperators, "|=", "or_eq")),
202a0848542SPiotr Zegar hasInvalidBinaryOperatorRepresentation(
203a0848542SPiotr Zegar BO_XorAssign,
204a0848542SPiotr Zegar getRepresentation(BinaryOperators, "^=", "xor_eq"))))
205a0848542SPiotr Zegar .bind("binary_op"),
206a0848542SPiotr Zegar this);
207a0848542SPiotr Zegar }
208a0848542SPiotr Zegar
registerUnaryOperatorMatcher(MatchFinder * Finder)209a0848542SPiotr Zegar void OperatorsRepresentationCheck::registerUnaryOperatorMatcher(
210a0848542SPiotr Zegar MatchFinder *Finder) {
211a0848542SPiotr Zegar if (!isAnyOperatorEnabled(BinaryOperators, UnaryRepresentation))
212a0848542SPiotr Zegar return;
213a0848542SPiotr Zegar
214a0848542SPiotr Zegar Finder->addMatcher(
215a0848542SPiotr Zegar unaryOperator(
216a0848542SPiotr Zegar unless(isExpansionInSystemHeader()),
217a0848542SPiotr Zegar anyOf(hasInvalidUnaryOperatorRepresentation(
218a0848542SPiotr Zegar UO_LNot, getRepresentation(BinaryOperators, "!", "not")),
219a0848542SPiotr Zegar hasInvalidUnaryOperatorRepresentation(
220a0848542SPiotr Zegar UO_Not, getRepresentation(BinaryOperators, "~", "compl"))))
221a0848542SPiotr Zegar .bind("unary_op"),
222a0848542SPiotr Zegar this);
223a0848542SPiotr Zegar }
224a0848542SPiotr Zegar
registerOverloadedOperatorMatcher(MatchFinder * Finder)225a0848542SPiotr Zegar void OperatorsRepresentationCheck::registerOverloadedOperatorMatcher(
226a0848542SPiotr Zegar MatchFinder *Finder) {
227a0848542SPiotr Zegar if (!isAnyOperatorEnabled(OverloadedOperators, OperatorsRepresentation) &&
228a0848542SPiotr Zegar !isAnyOperatorEnabled(OverloadedOperators, UnaryRepresentation))
229a0848542SPiotr Zegar return;
230a0848542SPiotr Zegar
231a0848542SPiotr Zegar Finder->addMatcher(
232a0848542SPiotr Zegar cxxOperatorCallExpr(
233a0848542SPiotr Zegar unless(isExpansionInSystemHeader()),
234a0848542SPiotr Zegar anyOf(
235a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
236a0848542SPiotr Zegar OO_AmpAmp,
237a0848542SPiotr Zegar getRepresentation(OverloadedOperators, "&&", "and")),
238a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
239a0848542SPiotr Zegar OO_PipePipe,
240a0848542SPiotr Zegar getRepresentation(OverloadedOperators, "||", "or")),
241a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
242a0848542SPiotr Zegar OO_Exclaim,
243a0848542SPiotr Zegar getRepresentation(OverloadedOperators, "!", "not")),
244a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
245a0848542SPiotr Zegar OO_ExclaimEqual,
246a0848542SPiotr Zegar getRepresentation(OverloadedOperators, "!=", "not_eq")),
247a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
248a0848542SPiotr Zegar OO_Caret, getRepresentation(OverloadedOperators, "^", "xor")),
249a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
250a0848542SPiotr Zegar OO_Amp,
251a0848542SPiotr Zegar getRepresentation(OverloadedOperators, "&", "bitand")),
252a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
253a0848542SPiotr Zegar OO_Pipe,
254a0848542SPiotr Zegar getRepresentation(OverloadedOperators, "|", "bitor")),
255a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
256a0848542SPiotr Zegar OO_AmpEqual,
257a0848542SPiotr Zegar getRepresentation(OverloadedOperators, "&=", "and_eq")),
258a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
259a0848542SPiotr Zegar OO_PipeEqual,
260a0848542SPiotr Zegar getRepresentation(OverloadedOperators, "|=", "or_eq")),
261a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
262a0848542SPiotr Zegar OO_CaretEqual,
263a0848542SPiotr Zegar getRepresentation(OverloadedOperators, "^=", "xor_eq")),
264a0848542SPiotr Zegar hasInvalidOverloadedOperatorRepresentation(
265a0848542SPiotr Zegar OO_Tilde,
266a0848542SPiotr Zegar getRepresentation(OverloadedOperators, "~", "compl"))))
267a0848542SPiotr Zegar .bind("overloaded_op"),
268a0848542SPiotr Zegar this);
269a0848542SPiotr Zegar }
270a0848542SPiotr Zegar
registerMatchers(MatchFinder * Finder)271a0848542SPiotr Zegar void OperatorsRepresentationCheck::registerMatchers(MatchFinder *Finder) {
272a0848542SPiotr Zegar registerBinaryOperatorMatcher(Finder);
273a0848542SPiotr Zegar registerUnaryOperatorMatcher(Finder);
274a0848542SPiotr Zegar registerOverloadedOperatorMatcher(Finder);
275a0848542SPiotr Zegar }
276a0848542SPiotr Zegar
check(const MatchFinder::MatchResult & Result)277a0848542SPiotr Zegar void OperatorsRepresentationCheck::check(
278a0848542SPiotr Zegar const MatchFinder::MatchResult &Result) {
279a0848542SPiotr Zegar
280a0848542SPiotr Zegar SourceLocation Loc;
281a0848542SPiotr Zegar
282a0848542SPiotr Zegar if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))
283a0848542SPiotr Zegar Loc = Op->getOperatorLoc();
284a0848542SPiotr Zegar else if (const auto *Op = Result.Nodes.getNodeAs<UnaryOperator>("unary_op"))
285a0848542SPiotr Zegar Loc = Op->getOperatorLoc();
286a0848542SPiotr Zegar else if (const auto *Op =
287a0848542SPiotr Zegar Result.Nodes.getNodeAs<CXXOperatorCallExpr>("overloaded_op"))
288a0848542SPiotr Zegar Loc = Op->getOperatorLoc();
289a0848542SPiotr Zegar
290a0848542SPiotr Zegar if (Loc.isInvalid())
291a0848542SPiotr Zegar return;
292a0848542SPiotr Zegar
293a0848542SPiotr Zegar Loc = Result.SourceManager->getSpellingLoc(Loc);
294a0848542SPiotr Zegar if (Loc.isInvalid() || Loc.isMacroID())
295a0848542SPiotr Zegar return;
296a0848542SPiotr Zegar
297a0848542SPiotr Zegar const CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc);
298a0848542SPiotr Zegar if (TokenRange.isInvalid())
299a0848542SPiotr Zegar return;
300a0848542SPiotr Zegar
301a0848542SPiotr Zegar StringRef Spelling = Lexer::getSourceText(TokenRange, *Result.SourceManager,
302a0848542SPiotr Zegar Result.Context->getLangOpts());
303a0848542SPiotr Zegar StringRef TranslatedSpelling = translate(Spelling);
304a0848542SPiotr Zegar
305a0848542SPiotr Zegar if (TranslatedSpelling.empty())
306a0848542SPiotr Zegar return;
307a0848542SPiotr Zegar
308a0848542SPiotr Zegar std::string FixSpelling = TranslatedSpelling.str();
309a0848542SPiotr Zegar
310a0848542SPiotr Zegar StringRef SourceRepresentation = "an alternative";
311a0848542SPiotr Zegar StringRef TargetRepresentation = "a traditional";
312a0848542SPiotr Zegar if (needEscaping(TranslatedSpelling)) {
313a0848542SPiotr Zegar SourceRepresentation = "a traditional";
314a0848542SPiotr Zegar TargetRepresentation = "an alternative";
315a0848542SPiotr Zegar
316a0848542SPiotr Zegar StringRef SpellingEx = Lexer::getSourceText(
317a0848542SPiotr Zegar CharSourceRange::getCharRange(
318a0848542SPiotr Zegar TokenRange.getBegin().getLocWithOffset(-1),
319a0848542SPiotr Zegar TokenRange.getBegin().getLocWithOffset(Spelling.size() + 1U)),
320a0848542SPiotr Zegar *Result.SourceManager, Result.Context->getLangOpts());
321a0848542SPiotr Zegar if (SpellingEx.empty() || !isSeparator(SpellingEx.front()))
322a0848542SPiotr Zegar FixSpelling.insert(FixSpelling.begin(), ' ');
323a0848542SPiotr Zegar if (SpellingEx.empty() || !isSeparator(SpellingEx.back()))
324a0848542SPiotr Zegar FixSpelling.push_back(' ');
325a0848542SPiotr Zegar }
326a0848542SPiotr Zegar
327a0848542SPiotr Zegar diag(
328a0848542SPiotr Zegar Loc,
329a0848542SPiotr Zegar "'%0' is %1 token spelling, consider using %2 token '%3' for consistency")
330a0848542SPiotr Zegar << Spelling << SourceRepresentation << TargetRepresentation
331a0848542SPiotr Zegar << TranslatedSpelling
332a0848542SPiotr Zegar << FixItHint::CreateReplacement(TokenRange, FixSpelling);
333a0848542SPiotr Zegar }
334a0848542SPiotr Zegar
335a0848542SPiotr Zegar } // namespace clang::tidy::readability
336