11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
23struct IntegerLiteralCheck {
24 using type = clang::IntegerLiteral;
25 static constexpr llvm::StringLiteral Name = llvm::StringLiteral(
"integer");
27 static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral(
"");
31 static constexpr llvm::StringLiteral Suffixes =
32 llvm::StringLiteral(
"uUlLzZwWiIjJ");
35struct FloatingLiteralCheck {
36 using type = clang::FloatingLiteral;
37 static constexpr llvm::StringLiteral Name =
38 llvm::StringLiteral(
"floating point");
45 static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral(
"pP");
48 static constexpr llvm::StringLiteral Suffixes =
49 llvm::StringLiteral(
"fFlLbBdDhHqQiIjJ");
53 SourceRange LiteralLocation;
55 std::optional<FixItHint> FixIt;
58std::optional<SourceLocation> getMacroAwareLocation(SourceLocation Loc,
59 const SourceManager &SM) {
64 SourceLocation SpellingLoc = SM.getSpellingLoc(Loc);
65 if (SpellingLoc.isInvalid())
70std::optional<SourceRange> getMacroAwareSourceRange(SourceRange Loc,
71 const SourceManager &SM) {
72 std::optional<SourceLocation> Begin =
73 getMacroAwareLocation(Loc.getBegin(), SM);
74 std::optional<SourceLocation> End = getMacroAwareLocation(Loc.getEnd(), SM);
77 return SourceRange(*Begin, *End);
80std::optional<std::string>
81getNewSuffix(llvm::StringRef OldSuffix,
82 const std::vector<StringRef> &NewSuffixes) {
84 if (NewSuffixes.empty())
85 return OldSuffix.upper();
88 llvm::find_if(NewSuffixes, [OldSuffix](StringRef PotentialNewSuffix) {
89 return OldSuffix.equals_insensitive(PotentialNewSuffix);
92 if (NewSuffix != NewSuffixes.end())
93 return NewSuffix->str();
98template <
typename LiteralType>
99std::optional<NewSuffix>
100shouldReplaceLiteralSuffix(
const Expr &Literal,
101 const std::vector<StringRef> &NewSuffixes,
102 const SourceManager &SM,
const LangOptions &LO) {
103 NewSuffix ReplacementDsc;
105 const auto &L = cast<typename LiteralType::type>(Literal);
108 ReplacementDsc.LiteralLocation = L.getSourceRange();
111 bool RangeCanBeFixed =
115 std::optional<SourceRange> Range =
116 getMacroAwareSourceRange(ReplacementDsc.LiteralLocation, SM);
121 ReplacementDsc.LiteralLocation = *Range;
126 const StringRef LiteralSourceText = Lexer::getSourceText(
127 CharSourceRange::getTokenRange(*Range), SM, LO, &Invalid);
128 assert(!Invalid &&
"Failed to retrieve the source text.");
132 if (!std::isdigit(
static_cast<unsigned char>(LiteralSourceText.front())))
138 if (!LiteralType::SkipFirst.empty()) {
142 Skip = LiteralSourceText.find_first_of(LiteralType::SkipFirst);
144 if (Skip == StringRef::npos)
151 Skip = LiteralSourceText.find_first_of(LiteralType::Suffixes, Skip);
155 if (Skip == StringRef::npos)
159 Range->setBegin(Range->getBegin().getLocWithOffset(Skip));
161 ReplacementDsc.OldSuffix = LiteralSourceText.drop_front(Skip);
162 assert(!ReplacementDsc.OldSuffix.empty() &&
163 "We still should have some chars left.");
166 std::optional<std::string> NewSuffix =
167 getNewSuffix(ReplacementDsc.OldSuffix, NewSuffixes);
168 if (!NewSuffix || ReplacementDsc.OldSuffix == *NewSuffix)
172 ReplacementDsc.FixIt = FixItHint::CreateReplacement(*Range, *NewSuffix);
174 return ReplacementDsc;
183 utils::options::parseStringList(Options.get(
"NewSuffixes",
""))),
184 IgnoreMacros(Options.get(
"IgnoreMacros", true)) {}
188 Options.store(Opts,
"NewSuffixes",
190 Options.store(Opts,
"IgnoreMacros", IgnoreMacros);
198 stmt(eachOf(integerLiteral().bind(IntegerLiteralCheck::Name),
199 floatLiteral().bind(FloatingLiteralCheck::Name)),
200 unless(anyOf(hasParent(userDefinedLiteral()),
201 hasAncestor(substNonTypeTemplateParmExpr())))),
205template <
typename LiteralType>
206bool UppercaseLiteralSuffixCheck::checkBoundMatch(
207 const MatchFinder::MatchResult &Result) {
208 const auto *Literal =
209 Result.Nodes.getNodeAs<
typename LiteralType::type>(LiteralType::Name);
215 if (
auto Details = shouldReplaceLiteralSuffix<LiteralType>(
216 *Literal, NewSuffixes, *Result.SourceManager, getLangOpts())) {
217 if (Details->LiteralLocation.getBegin().isMacroID() && IgnoreMacros)
219 auto Complaint = diag(Details->LiteralLocation.getBegin(),
220 "%0 literal has suffix '%1', which is not uppercase")
221 << LiteralType::Name << Details->OldSuffix;
223 Complaint << *(Details->FixIt);
230 const MatchFinder::MatchResult &Result) {
231 if (checkBoundMatch<IntegerLiteralCheck>(Result))
233 checkBoundMatch<FloatingLiteralCheck>(Result);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
UppercaseLiteralSuffixCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
@ Invalid
Sentinel bit pattern. DO NOT USE!
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
bool rangeCanBeFixed(SourceRange Range, const SourceManager *SM)
llvm::StringMap< ClangTidyValue > OptionMap