10#include "../utils/ASTUtils.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
14#include "llvm/ADT/SmallString.h"
24struct IntegerLiteralCheck {
25 using type = clang::IntegerLiteral;
26 static constexpr llvm::StringLiteral
Name = llvm::StringLiteral(
"integer");
28 static constexpr llvm::StringLiteral
SkipFirst = llvm::StringLiteral(
"");
31 static constexpr llvm::StringLiteral
Suffixes =
32 llvm::StringLiteral(
"uUlLiIjJ");
34constexpr llvm::StringLiteral IntegerLiteralCheck::Name;
35constexpr llvm::StringLiteral IntegerLiteralCheck::SkipFirst;
36constexpr llvm::StringLiteral IntegerLiteralCheck::Suffixes;
38struct FloatingLiteralCheck {
39 using type = clang::FloatingLiteral;
40 static constexpr llvm::StringLiteral
Name =
41 llvm::StringLiteral(
"floating point");
48 static constexpr llvm::StringLiteral
SkipFirst = llvm::StringLiteral(
"pP");
51 static constexpr llvm::StringLiteral
Suffixes =
52 llvm::StringLiteral(
"fFlLhHqQiIjJ");
54constexpr llvm::StringLiteral FloatingLiteralCheck::Name;
55constexpr llvm::StringLiteral FloatingLiteralCheck::SkipFirst;
56constexpr llvm::StringLiteral FloatingLiteralCheck::Suffixes;
64std::optional<SourceLocation> getMacroAwareLocation(SourceLocation
Loc,
65 const SourceManager &SM) {
70 SourceLocation SpellingLoc = SM.getSpellingLoc(
Loc);
71 if (SpellingLoc.isInvalid())
76std::optional<SourceRange> getMacroAwareSourceRange(SourceRange
Loc,
77 const SourceManager &SM) {
78 std::optional<SourceLocation> Begin =
79 getMacroAwareLocation(
Loc.getBegin(), SM);
80 std::optional<SourceLocation> End = getMacroAwareLocation(
Loc.getEnd(), SM);
83 return SourceRange(*Begin, *End);
86std::optional<std::string>
88 const std::vector<StringRef> &NewSuffixes) {
90 if (NewSuffixes.empty())
94 llvm::find_if(NewSuffixes, [
OldSuffix](StringRef PotentialNewSuffix) {
95 return OldSuffix.equals_insensitive(PotentialNewSuffix);
98 if (NewSuffix != NewSuffixes.end())
99 return NewSuffix->str();
104template <
typename LiteralType>
105std::optional<NewSuffix>
106shouldReplaceLiteralSuffix(
const Expr &Literal,
107 const std::vector<StringRef> &NewSuffixes,
108 const SourceManager &SM,
const LangOptions &LO) {
109 NewSuffix ReplacementDsc;
111 const auto &L = cast<typename LiteralType::type>(Literal);
114 ReplacementDsc.LiteralLocation = L.getSourceRange();
117 bool RangeCanBeFixed =
121 std::optional<SourceRange>
Range =
122 getMacroAwareSourceRange(ReplacementDsc.LiteralLocation, SM);
127 ReplacementDsc.LiteralLocation = *
Range;
132 const StringRef LiteralSourceText = Lexer::getSourceText(
133 CharSourceRange::getTokenRange(*Range), SM, LO, &Invalid);
134 assert(!Invalid &&
"Failed to retrieve the source text.");
138 if (!std::isdigit(
static_cast<unsigned char>(LiteralSourceText.front())))
144 if (!LiteralType::SkipFirst.empty()) {
148 Skip = LiteralSourceText.find_first_of(LiteralType::SkipFirst);
150 if (Skip == StringRef::npos)
157 Skip = LiteralSourceText.find_first_of(LiteralType::Suffixes, Skip);
161 if (Skip == StringRef::npos)
165 Range->setBegin(
Range->getBegin().getLocWithOffset(Skip));
167 ReplacementDsc.OldSuffix = LiteralSourceText.drop_front(Skip);
168 assert(!ReplacementDsc.OldSuffix.empty() &&
169 "We still should have some chars left.");
172 std::optional<std::string> NewSuffix =
173 getNewSuffix(ReplacementDsc.OldSuffix, NewSuffixes);
174 if (!NewSuffix || ReplacementDsc.OldSuffix == *NewSuffix)
178 ReplacementDsc.FixIt = FixItHint::CreateReplacement(*Range, *NewSuffix);
180 return ReplacementDsc;
189 utils::options::parseStringList(Options.get(
"NewSuffixes",
""))),
190 IgnoreMacros(Options.getLocalOrGlobal(
"IgnoreMacros", true)) {}
204 stmt(eachOf(integerLiteral().bind(IntegerLiteralCheck::Name),
205 floatLiteral().bind(FloatingLiteralCheck::Name)),
206 unless(anyOf(hasParent(userDefinedLiteral()),
207 hasAncestor(substNonTypeTemplateParmExpr())))),
211template <
typename LiteralType>
212bool UppercaseLiteralSuffixCheck::checkBoundMatch(
213 const MatchFinder::MatchResult &Result) {
214 const auto *Literal =
215 Result.Nodes.getNodeAs<
typename LiteralType::type>(LiteralType::Name);
221 if (
auto Details = shouldReplaceLiteralSuffix<LiteralType>(
222 *Literal, NewSuffixes, *Result.SourceManager,
getLangOpts())) {
223 if (
Details->LiteralLocation.getBegin().isMacroID() && IgnoreMacros)
225 auto Complaint =
diag(
Details->LiteralLocation.getBegin(),
226 "%0 literal has suffix '%1', which is not uppercase")
227 << LiteralType::Name <<
Details->OldSuffix;
229 Complaint << *(
Details->FixIt);
236 const MatchFinder::MatchResult &Result) {
237 if (checkBoundMatch<IntegerLiteralCheck>(Result))
239 checkBoundMatch<FloatingLiteralCheck>(Result);
llvm::SmallString< 256U > Name
SmallVector< Detail, DefaultLimit > Details
CharSourceRange Range
SourceRange for the file name.
static constexpr llvm::StringLiteral Suffixes
std::optional< FixItHint > FixIt
SourceRange LiteralLocation
static constexpr llvm::StringLiteral SkipFirst
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.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
UppercaseLiteralSuffixCheck(StringRef Name, ClangTidyContext *Context)
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.
@ 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