xref: /llvm-project/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.cpp (revision 26078f33bdfa30f35bb880feb0c088b8bd2169c8)
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