237 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "UppercaseLiteralSuffixCheck.h"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 <cctype>15#include <optional>16 17using namespace clang::ast_matchers;18 19namespace clang::tidy::readability {20 21namespace {22 23struct IntegerLiteralCheck {24 using type = clang::IntegerLiteral;25 static constexpr llvm::StringLiteral Name = llvm::StringLiteral("integer");26 // What should be skipped before looking for the Suffixes? (Nothing here.)27 static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral("");28 // Suffix can only consist of 'u', 'l', and 'z' chars, can be a bit-precise29 // integer (wb), and can be a complex number ('i', 'j'). In MS compatibility30 // mode, suffixes like i32 are supported.31 static constexpr llvm::StringLiteral Suffixes =32 llvm::StringLiteral("uUlLzZwWiIjJ");33};34 35struct FloatingLiteralCheck {36 using type = clang::FloatingLiteral;37 static constexpr llvm::StringLiteral Name =38 llvm::StringLiteral("floating point");39 // C++17 introduced hexadecimal floating-point literals, and 'f' is both a40 // valid hexadecimal digit in a hex float literal and a valid floating-point41 // literal suffix.42 // So we can't just "skip to the chars that can be in the suffix".43 // Since the exponent ('p'/'P') is mandatory for hexadecimal floating-point44 // literals, we first skip everything before the exponent.45 static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral("pP");46 // Suffix can only consist of 'f', 'l', "f16", "bf16", "df", "dd", "dl",47 // 'h', 'q' chars, and can be a complex number ('i', 'j').48 static constexpr llvm::StringLiteral Suffixes =49 llvm::StringLiteral("fFlLbBdDhHqQiIjJ");50};51 52struct NewSuffix {53 SourceRange LiteralLocation;54 StringRef OldSuffix;55 std::optional<FixItHint> FixIt;56};57 58} // namespace59 60static std::optional<SourceLocation>61getMacroAwareLocation(SourceLocation Loc, const SourceManager &SM) {62 // Do nothing if the provided location is invalid.63 if (Loc.isInvalid())64 return std::nullopt;65 // Look where the location was *actually* written.66 SourceLocation SpellingLoc = SM.getSpellingLoc(Loc);67 if (SpellingLoc.isInvalid())68 return std::nullopt;69 return SpellingLoc;70}71 72static std::optional<SourceRange>73getMacroAwareSourceRange(SourceRange Loc, const SourceManager &SM) {74 std::optional<SourceLocation> Begin =75 getMacroAwareLocation(Loc.getBegin(), SM);76 std::optional<SourceLocation> End = getMacroAwareLocation(Loc.getEnd(), SM);77 if (!Begin || !End)78 return std::nullopt;79 return SourceRange(*Begin, *End);80}81 82static std::optional<std::string>83getNewSuffix(llvm::StringRef OldSuffix,84 const std::vector<StringRef> &NewSuffixes) {85 // If there is no config, just uppercase the entirety of the suffix.86 if (NewSuffixes.empty())87 return OldSuffix.upper();88 // Else, find matching suffix, case-*insensitive*ly.89 auto NewSuffix =90 llvm::find_if(NewSuffixes, [OldSuffix](StringRef PotentialNewSuffix) {91 return OldSuffix.equals_insensitive(PotentialNewSuffix);92 });93 // Have a match, return it.94 if (NewSuffix != NewSuffixes.end())95 return NewSuffix->str();96 // Nope, I guess we have to keep it as-is.97 return std::nullopt;98}99 100template <typename LiteralType>101static std::optional<NewSuffix>102shouldReplaceLiteralSuffix(const Expr &Literal,103 const std::vector<StringRef> &NewSuffixes,104 const SourceManager &SM, const LangOptions &LO) {105 NewSuffix ReplacementDsc;106 107 const auto &L = cast<typename LiteralType::type>(Literal);108 109 // The naive location of the literal. Is always valid.110 ReplacementDsc.LiteralLocation = L.getSourceRange();111 112 // Was this literal fully spelled or is it a product of macro expansion?113 const bool RangeCanBeFixed =114 utils::rangeCanBeFixed(ReplacementDsc.LiteralLocation, &SM);115 116 // The literal may have macro expansion, we need the final expanded src range.117 std::optional<SourceRange> Range =118 getMacroAwareSourceRange(ReplacementDsc.LiteralLocation, SM);119 if (!Range)120 return std::nullopt;121 122 if (RangeCanBeFixed)123 ReplacementDsc.LiteralLocation = *Range;124 // Else keep the naive literal location!125 126 // Get the whole literal from the source buffer.127 bool Invalid = false;128 const StringRef LiteralSourceText = Lexer::getSourceText(129 CharSourceRange::getTokenRange(*Range), SM, LO, &Invalid);130 assert(!Invalid && "Failed to retrieve the source text.");131 132 // Make sure the first character is actually a digit, instead of133 // something else, like a non-type template parameter.134 if (!std::isdigit(static_cast<unsigned char>(LiteralSourceText.front())))135 return std::nullopt;136 137 size_t Skip = 0;138 139 // Do we need to ignore something before actually looking for the suffix?140 if (!LiteralType::SkipFirst.empty()) {141 // E.g. we can't look for 'f' suffix in hexadecimal floating-point literals142 // until after we skip to the exponent (which is mandatory there),143 // because hex-digit-sequence may contain 'f'.144 Skip = LiteralSourceText.find_first_of(LiteralType::SkipFirst);145 // We could be in non-hexadecimal floating-point literal, with no exponent.146 if (Skip == StringRef::npos)147 Skip = 0;148 }149 150 // Find the beginning of the suffix by looking for the first char that is151 // one of these chars that can be in the suffix, potentially starting looking152 // in the exponent, if we are skipping hex-digit-sequence.153 Skip = LiteralSourceText.find_first_of(LiteralType::Suffixes, /*From=*/Skip);154 155 // We can't check whether the *Literal has any suffix or not without actually156 // looking for the suffix. So it is totally possible that there is no suffix.157 if (Skip == StringRef::npos)158 return std::nullopt;159 160 // Move the cursor in the source range to the beginning of the suffix.161 Range->setBegin(Range->getBegin().getLocWithOffset(Skip));162 // And in our textual representation too.163 ReplacementDsc.OldSuffix = LiteralSourceText.drop_front(Skip);164 assert(!ReplacementDsc.OldSuffix.empty() &&165 "We still should have some chars left.");166 167 // And get the replacement suffix.168 std::optional<std::string> NewSuffix =169 getNewSuffix(ReplacementDsc.OldSuffix, NewSuffixes);170 if (!NewSuffix || ReplacementDsc.OldSuffix == *NewSuffix)171 return std::nullopt; // The suffix was already the way it should be.172 173 if (RangeCanBeFixed)174 ReplacementDsc.FixIt = FixItHint::CreateReplacement(*Range, *NewSuffix);175 176 return ReplacementDsc;177}178 179UppercaseLiteralSuffixCheck::UppercaseLiteralSuffixCheck(180 StringRef Name, ClangTidyContext *Context)181 : ClangTidyCheck(Name, Context),182 NewSuffixes(183 utils::options::parseStringList(Options.get("NewSuffixes", ""))),184 IgnoreMacros(Options.get("IgnoreMacros", true)) {}185 186void UppercaseLiteralSuffixCheck::storeOptions(187 ClangTidyOptions::OptionMap &Opts) {188 Options.store(Opts, "NewSuffixes",189 utils::options::serializeStringList(NewSuffixes));190 Options.store(Opts, "IgnoreMacros", IgnoreMacros);191}192 193void UppercaseLiteralSuffixCheck::registerMatchers(MatchFinder *Finder) {194 // Sadly, we can't check whether the literal has suffix or not.195 // E.g. i32 suffix still results in 'BuiltinType::Kind::Int'.196 // And such an info is not stored in the *Literal itself.197 Finder->addMatcher(198 stmt(eachOf(integerLiteral().bind(IntegerLiteralCheck::Name),199 floatLiteral().bind(FloatingLiteralCheck::Name)),200 unless(anyOf(hasParent(userDefinedLiteral()),201 hasAncestor(substNonTypeTemplateParmExpr())))),202 this);203}204 205template <typename LiteralType>206bool UppercaseLiteralSuffixCheck::checkBoundMatch(207 const MatchFinder::MatchResult &Result) {208 const auto *Literal =209 Result.Nodes.getNodeAs<typename LiteralType::type>(LiteralType::Name);210 if (!Literal)211 return false;212 213 // We won't *always* want to diagnose.214 // We might have a suffix that is already uppercase.215 if (auto Details = shouldReplaceLiteralSuffix<LiteralType>(216 *Literal, NewSuffixes, *Result.SourceManager, getLangOpts())) {217 if (Details->LiteralLocation.getBegin().isMacroID() && IgnoreMacros)218 return true;219 auto Complaint = diag(Details->LiteralLocation.getBegin(),220 "%0 literal has suffix '%1', which is not uppercase")221 << LiteralType::Name << Details->OldSuffix;222 if (Details->FixIt) // Similarly, a fix-it is not always possible.223 Complaint << *(Details->FixIt);224 }225 226 return true;227}228 229void UppercaseLiteralSuffixCheck::check(230 const MatchFinder::MatchResult &Result) {231 if (checkBoundMatch<IntegerLiteralCheck>(Result))232 return; // If it *was* IntegerLiteral, don't check for FloatingLiteral.233 checkBoundMatch<FloatingLiteralCheck>(Result);234}235 236} // namespace clang::tidy::readability237