Index: clang-tidy/cert/CERTTidyModule.cpp =================================================================== --- clang-tidy/cert/CERTTidyModule.cpp +++ clang-tidy/cert/CERTTidyModule.cpp @@ -16,6 +16,7 @@ #include "../misc/StaticAssertCheck.h" #include "../misc/ThrowByValueCatchByReferenceCheck.h" #include "../performance/MoveConstructorInitCheck.h" +#include "../readability/UppercaseLiteralSuffixCheck.h" #include "CommandProcessorCheck.h" #include "DontModifyStdNamespaceCheck.h" #include "FloatLoopCounter.h" @@ -65,6 +66,8 @@ // C checkers // DCL CheckFactories.registerCheck("cert-dcl03-c"); + CheckFactories.registerCheck( + "cert-dcl16-c"); // ENV CheckFactories.registerCheck("cert-env33-c"); // FLP Index: clang-tidy/cert/CMakeLists.txt =================================================================== --- clang-tidy/cert/CMakeLists.txt +++ clang-tidy/cert/CMakeLists.txt @@ -24,5 +24,6 @@ clangTidyGoogleModule clangTidyMiscModule clangTidyPerformanceModule + clangTidyReadabilityModule clangTidyUtils ) Index: clang-tidy/hicpp/HICPPTidyModule.cpp =================================================================== --- clang-tidy/hicpp/HICPPTidyModule.cpp +++ clang-tidy/hicpp/HICPPTidyModule.cpp @@ -35,6 +35,7 @@ #include "../readability/BracesAroundStatementsCheck.h" #include "../readability/FunctionSizeCheck.h" #include "../readability/IdentifierNamingCheck.h" +#include "../readability/UppercaseLiteralSuffixCheck.h" #include "ExceptionBaseclassCheck.h" #include "MultiwayPathsCoveredCheck.h" #include "NoAssemblerCheck.h" @@ -100,6 +101,8 @@ "hicpp-use-nullptr"); CheckFactories.registerCheck( "hicpp-use-override"); + CheckFactories.registerCheck( + "hicpp-uppercase-literal-suffix"); CheckFactories.registerCheck( "hicpp-vararg"); } Index: clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tidy/readability/CMakeLists.txt +++ clang-tidy/readability/CMakeLists.txt @@ -31,6 +31,7 @@ StaticDefinitionInAnonymousNamespaceCheck.cpp StringCompareCheck.cpp UniqueptrDeleteReleaseCheck.cpp + UppercaseLiteralSuffixCheck.cpp LINK_LIBS clangAST Index: clang-tidy/readability/IdentifierNamingCheck.cpp =================================================================== --- clang-tidy/readability/IdentifierNamingCheck.cpp +++ clang-tidy/readability/IdentifierNamingCheck.cpp @@ -9,6 +9,7 @@ #include "IdentifierNamingCheck.h" +#include "../utils/ASTUtils.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/PPCallbacks.h" @@ -657,25 +658,7 @@ if (!Failure.ShouldFix) return; - // Check if the range is entirely contained within a macro argument. - SourceLocation MacroArgExpansionStartForRangeBegin; - SourceLocation MacroArgExpansionStartForRangeEnd; - bool RangeIsEntirelyWithinMacroArgument = - SourceMgr && - SourceMgr->isMacroArgExpansion(Range.getBegin(), - &MacroArgExpansionStartForRangeBegin) && - SourceMgr->isMacroArgExpansion(Range.getEnd(), - &MacroArgExpansionStartForRangeEnd) && - MacroArgExpansionStartForRangeBegin == MacroArgExpansionStartForRangeEnd; - - // Check if the range contains any locations from a macro expansion. - bool RangeContainsMacroExpansion = RangeIsEntirelyWithinMacroArgument || - Range.getBegin().isMacroID() || - Range.getEnd().isMacroID(); - - bool RangeCanBeFixed = - RangeIsEntirelyWithinMacroArgument || !RangeContainsMacroExpansion; - Failure.ShouldFix = RangeCanBeFixed; + Failure.ShouldFix = utils::rangeCanBeFixed(Range, SourceMgr); } /// Convenience method when the usage to be added is a NamedDecl Index: clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tidy/readability/ReadabilityTidyModule.cpp @@ -38,6 +38,7 @@ #include "StaticDefinitionInAnonymousNamespaceCheck.h" #include "StringCompareCheck.h" #include "UniqueptrDeleteReleaseCheck.h" +#include "UppercaseLiteralSuffixCheck.h" namespace clang { namespace tidy { @@ -84,6 +85,8 @@ "readability-static-definition-in-anonymous-namespace"); CheckFactories.registerCheck( "readability-string-compare"); + CheckFactories.registerCheck( + "readability-uppercase-literal-suffix"); CheckFactories.registerCheck( "readability-named-parameter"); CheckFactories.registerCheck( Index: clang-tidy/readability/UppercaseLiteralSuffixCheck.h =================================================================== --- /dev/null +++ clang-tidy/readability/UppercaseLiteralSuffixCheck.h @@ -0,0 +1,39 @@ +//===--- UppercaseLiteralSuffixCheck.h - clang-tidy -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UPPERCASELITERALSUFFIXCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UPPERCASELITERALSUFFIXCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// Detects when the integral literal or floating point literal has +/// non-uppercase suffix, and suggests to make the suffix uppercase. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-uppercase-literal-suffix.html +class UppercaseLiteralSuffixCheck : public ClangTidyCheck { + template + bool checkBoundMatch(const ast_matchers::MatchFinder::MatchResult &Result); + +public: + UppercaseLiteralSuffixCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UPPERCASELITERALSUFFIXCHECK_H Index: clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp @@ -0,0 +1,217 @@ +//===--- UppercaseLiteralSuffixCheck.cpp - clang-tidy ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UppercaseLiteralSuffixCheck.h" +#include "../utils/ASTUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +namespace { + +struct IntegerLiteral { + using type = clang::IntegerLiteral; + static constexpr llvm::StringLiteral Name = llvm::StringLiteral("integer"); + // What should be skipped before looking for the Suffixes? (Nothing here.) + static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral(""); + // Integer suffix can only consist of 'u' and 'l' chars. + static constexpr llvm::StringLiteral Suffixes = llvm::StringLiteral("uUlL"); +}; +constexpr llvm::StringLiteral IntegerLiteral::Name; +constexpr llvm::StringLiteral IntegerLiteral::SkipFirst; +constexpr llvm::StringLiteral IntegerLiteral::Suffixes; + +struct FloatingLiteral { + using type = clang::FloatingLiteral; + static constexpr llvm::StringLiteral Name = + llvm::StringLiteral("floating point"); + // C++17 introduced hexadecimal floating-point literals, and 'f' is both + // 15 in decimal and is 'f' as in 'floating point suffix'. + // So we can't just "skip to the chars that can be in the suffix". + // Since the exponent ('p'/'P') is mandatory for hexadecimal floating-point + // literals, we first skip everything before the exponent. + static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral("pP"); + // Floating suffix can only consist of 'u' and 'l' chars. + static constexpr llvm::StringLiteral Suffixes = llvm::StringLiteral("fFlL"); +}; +constexpr llvm::StringLiteral FloatingLiteral::Name; +constexpr llvm::StringLiteral FloatingLiteral::SkipFirst; +constexpr llvm::StringLiteral FloatingLiteral::Suffixes; + +AST_MATCHER(clang::IntegerLiteral, intHasSuffix) { + switch (cast(Node.getType().getTypePtr())->getKind()) { + default: + case BuiltinType::Kind::Int: + return false; // No suffix. + case BuiltinType::Kind::UInt: // 'u' + case BuiltinType::Kind::Long: // 'l' + case BuiltinType::Kind::ULong: // 'ul' + case BuiltinType::Kind::LongLong: // 'll' + case BuiltinType::Kind::ULongLong: // 'ull' + return true; + } +} + +AST_MATCHER(clang::FloatingLiteral, fpHasSuffix) { + switch (cast(Node.getType().getTypePtr())->getKind()) { + default: + case BuiltinType::Kind::Double: + return false; // No suffix. + case BuiltinType::Kind::Float: // 'f' + case BuiltinType::Kind::LongDouble: // 'l' + return true; + } +} + +struct NewSuffix { + SourceLocation LiteralLocation; + StringRef OldSuffix; + llvm::Optional FixIt; +}; + +llvm::Optional GetMacroAwareLocation(SourceLocation Loc, + const SourceManager &SM) { + // Do nothing if the provided location is invalid. + if (Loc.isInvalid()) + return llvm::None; + // Look where the location was *actually* written. + SourceLocation SpellingLoc = SM.getSpellingLoc(Loc); + if (SpellingLoc.isInvalid()) + return llvm::None; + return SpellingLoc; +} + +llvm::Optional GetMacroAwareSourceRange(SourceRange Loc, + const SourceManager &SM) { + auto Begin = GetMacroAwareLocation(Loc.getBegin(), SM); + auto End = GetMacroAwareLocation(Loc.getEnd(), SM); + if (!Begin || !End) + return llvm::None; + return SourceRange(*Begin, *End); +} + +template +llvm::Optional shouldReplaceLiteralSuffix(const Expr &Literal, + const SourceManager &SM, + const LangOptions &LO) { + NewSuffix ReplacementDsc; + + const auto &L = cast(Literal); + + // The naive location of the literal. Is always valid. + ReplacementDsc.LiteralLocation = L.getLocation(); + + // Was this literal fully spelled or is it a product of macro expansion? + bool RangeCanBeFixed = + utils::rangeCanBeFixed(ReplacementDsc.LiteralLocation, &SM); + + // The literal may have macro expansion, we need the final expanded src range. + auto Range = GetMacroAwareSourceRange(ReplacementDsc.LiteralLocation, SM); + if (!Range) + return llvm::None; + + if (RangeCanBeFixed) + ReplacementDsc.LiteralLocation = Range->getBegin(); + // Else keep the naive literal location! + + // Get the whole literal from the source buffer. + bool Invalid; + const StringRef LiteralSourceText = Lexer::getSourceText( + CharSourceRange::getTokenRange(*Range), SM, LO, &Invalid); + assert(!Invalid && "Failed to retrieve the source text."); + + size_t Skip = 0; + + // Do we need to ignore something before actually looking for the suffix? + if (!LiteralType::SkipFirst.empty()) { + // E.g. we can't look for 'f' suffix in hexadecimal floating-point literals + // until after we skip to the exponent (which is mandatory there), + // because hex-digit-sequence may contain 'f'. + Skip = LiteralSourceText.find_first_of(LiteralType::SkipFirst); + // We could be in non-hexadecimal floating-point literal, with no exponent. + if (Skip == StringRef::npos) + Skip = 0; + } + + // Find the beginning of the suffix by looking for the first char that is + // one of these chars that can be in the suffix, potentially starting looking + // in the exponent, if we are skipping hex-digit-sequence. + Skip = LiteralSourceText.find_first_of(LiteralType::Suffixes, /*From=*/Skip); + assert( + Skip != StringRef::npos && + "we should not get here unless there was some suffix as per the type " + "system. Also, these two places should accept the same set of suffixes."); + + // Move the cursor in the source range to the beginning of the suffix. + Range->setBegin(Range->getBegin().getLocWithOffset(Skip)); + // And in our textual representation too. + ReplacementDsc.OldSuffix = LiteralSourceText.drop_front(Skip); + assert(!ReplacementDsc.OldSuffix.empty() && + "We still should have some chars left."); + + // And get the replacement suffix, with upper-case chars. + std::string NewSuffix = ReplacementDsc.OldSuffix.upper(); + if (ReplacementDsc.OldSuffix == NewSuffix) + return llvm::None; // The suffix was already fully uppercase. + + if (RangeCanBeFixed) + ReplacementDsc.FixIt = FixItHint::CreateReplacement(*Range, NewSuffix); + + return ReplacementDsc; +} + +} // namespace + +void UppercaseLiteralSuffixCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + stmt(allOf( + eachOf(integerLiteral(intHasSuffix()).bind(IntegerLiteral::Name), + floatLiteral(fpHasSuffix()).bind(FloatingLiteral::Name)), + unless(hasParent(userDefinedLiteral())))), + this); +} + +template +bool UppercaseLiteralSuffixCheck::checkBoundMatch( + const MatchFinder::MatchResult &Result) { + const auto *Literal = + Result.Nodes.getNodeAs(LiteralType::Name); + if (!Literal) + return false; + + // We won't *always* want to diagnose. We might have already-uppercase suffix. + if (auto Details = shouldReplaceLiteralSuffix( + *Literal, *Result.SourceManager, getLangOpts())) { + auto Complaint = diag(Details->LiteralLocation, + "%0 literal has suffix '%1', which is not upper-case") + << LiteralType::Name << Details->OldSuffix; + if (Details->FixIt) // Similarly, a fix-it is not always possible. + Complaint << *(Details->FixIt); + } + + return true; +} + +void UppercaseLiteralSuffixCheck::check( + const MatchFinder::MatchResult &Result) { + if (checkBoundMatch(Result)) + return; // If it *was* IntegerLiteral, don't check for FloatingLiteral. + checkBoundMatch(Result); +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: clang-tidy/utils/ASTUtils.h =================================================================== --- clang-tidy/utils/ASTUtils.h +++ clang-tidy/utils/ASTUtils.h @@ -27,6 +27,18 @@ bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM, const LangOptions &LangOpts, StringRef FlagName); + +// Check if the range is entirely contained within a macro argument. +bool rangeIsEntirelyWithinMacroArgument(SourceRange Range, + const SourceManager *SM); + +// Check if the range contains any locations from a macro expansion. +bool rangeContainsMacroExpansion(SourceRange Range, const SourceManager *SM); + +// Can a fix-it be issued for this whole Range? +// FIXME: false-negative if the entire range is fully expanded from a macro. +bool rangeCanBeFixed(SourceRange Range, const SourceManager *SM); + } // namespace utils } // namespace tidy } // namespace clang Index: clang-tidy/utils/ASTUtils.cpp =================================================================== --- clang-tidy/utils/ASTUtils.cpp +++ clang-tidy/utils/ASTUtils.cpp @@ -67,6 +67,32 @@ return true; } +bool rangeIsEntirelyWithinMacroArgument(SourceRange Range, + const SourceManager *SM) { + // Check if the range is entirely contained within a macro argument. + SourceLocation MacroArgExpansionStartForRangeBegin; + SourceLocation MacroArgExpansionStartForRangeEnd; + bool RangeIsEntirelyWithinMacroArgument = + SM && + SM->isMacroArgExpansion(Range.getBegin(), + &MacroArgExpansionStartForRangeBegin) && + SM->isMacroArgExpansion(Range.getEnd(), + &MacroArgExpansionStartForRangeEnd) && + MacroArgExpansionStartForRangeBegin == MacroArgExpansionStartForRangeEnd; + + return RangeIsEntirelyWithinMacroArgument; +} + +bool rangeContainsMacroExpansion(SourceRange Range, const SourceManager *SM) { + return rangeIsEntirelyWithinMacroArgument(Range, SM) || + Range.getBegin().isMacroID() || Range.getEnd().isMacroID(); +} + +bool rangeCanBeFixed(SourceRange Range, const SourceManager *SM) { + return utils::rangeIsEntirelyWithinMacroArgument(Range, SM) || + !utils::rangeContainsMacroExpansion(Range, SM); +} + } // namespace utils } // namespace tidy } // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -106,6 +106,23 @@ Detects usage of magic numbers, numbers that are used as literals instead of introduced via constants or symbols. +- New :doc:`readability-uppercase-literal-suffix + ` check. + + Detects when the integral literal or floating point literal has non-uppercase + suffix, and suggests to make the suffix uppercase. + +- New alias :doc:`cert-dcl16-c + ` to :doc:`readability-uppercase-literal-suffix + ` + added. + +- New alias :doc:`hicpp-uppercase-literal-suffix + ` to + :doc:`readability-uppercase-literal-suffix + ` + added. + Improvements to include-fixer ----------------------------- Index: docs/clang-tidy/checks/cert-dcl16-c.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/cert-dcl16-c.rst @@ -0,0 +1,9 @@ +.. title:: clang-tidy - cert-dcl16-c +.. meta:: + :http-equiv=refresh: 5;URL=readability-uppercase-literal-suffix.html + +cert-dcl16-c +============ + +The cert-dcl16-c check is an alias, please see +`readability-uppercase-literal-suffix `_ for more information. Index: docs/clang-tidy/checks/hicpp-uppercase-literal-suffix.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/hicpp-uppercase-literal-suffix.rst @@ -0,0 +1,9 @@ +.. title:: clang-tidy - hicpp-uppercase-literal-suffix +.. meta:: + :http-equiv=refresh: 5;URL=readability-uppercase-literal-suffix.html + +hicpp-uppercase-literal-suffix +============================== + +The hicpp-uppercase-literal-suffix check is an alias, please see +`readability-uppercase-literal-suffix `_ for more information. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -65,6 +65,7 @@ bugprone-use-after-move bugprone-virtual-near-miss cert-dcl03-c (redirects to misc-static-assert) + cert-dcl16-c (redirects to readability-uppercase-literal-suffix) cert-dcl21-cpp cert-dcl50-cpp cert-dcl54-cpp (redirects to misc-new-delete-overloads) @@ -155,6 +156,7 @@ hicpp-use-nullptr (redirects to modernize-use-nullptr) hicpp-use-override (redirects to modernize-use-override) hicpp-vararg (redirects to cppcoreguidelines-pro-type-vararg) + hicpp-uppercase-literal-suffix (redirects to readability-uppercase-literal-suffix) llvm-header-guard llvm-include-order llvm-namespace-comment @@ -244,4 +246,5 @@ readability-static-definition-in-anonymous-namespace readability-string-compare readability-uniqueptr-delete-release + readability-uppercase-literal-suffix zircon-temporary-objects Index: docs/clang-tidy/checks/readability-uppercase-literal-suffix.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/readability-uppercase-literal-suffix.rst @@ -0,0 +1,24 @@ +.. title:: clang-tidy - readability-uppercase-literal-suffix + +readability-uppercase-literal-suffix +==================================== + +`cert-dcl16-c` redirects here as an alias for this check. + +`hicpp-uppercase-literal-suffix` redirects here as an alias for this check. + +Detects when the integral literal or floating point (decimal or hexadecimal) +literal has a non-uppercase suffix and provides a fix-it-hint +with the uppercase suffix. + +All valid combinations of suffixes are supported. + +.. code:: c + + auto x = 1; // OK, no suffix. + + auto x = 1u; // warning: integer literal suffix 'u' is not upper-case + + auto x = 1U; // OK, suffix is uppercase. + + ... Index: test/clang-tidy/readability-uppercase-literal-suffix-floating-point.cpp =================================================================== --- /dev/null +++ test/clang-tidy/readability-uppercase-literal-suffix-floating-point.cpp @@ -0,0 +1,84 @@ +// RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -I %S +// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp +// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -fix -- -I %S +// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -- -I %S + +#include "readability-uppercase-literal-suffix.h" + +void integer_suffix() { + static constexpr auto v0 = 1.; // no literal + static_assert(is_same::value, ""); + static_assert(v0 == 1., ""); + + static constexpr auto v1 = 1.e0; // no literal + static_assert(is_same::value, ""); + static_assert(v1 == 1., ""); + + // Float + + static constexpr auto v2 = 1.f; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v2 = 1.f; + // CHECK-MESSAGES-NEXT: ^ ~ + // CHECK-MESSAGES-NEXT: {{^ *}}F{{$}} + // CHECK-FIXES: static constexpr auto v2 = 1.F; + static_assert(is_same::value, ""); + static_assert(v2 == 1.0F, ""); + + static constexpr auto v3 = 1.e0f; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 1.e0f; + // CHECK-MESSAGES-NEXT: ^ ~ + // CHECK-MESSAGES-NEXT: {{^ *}}F{{$}} + // CHECK-FIXES: static constexpr auto v3 = 1.e0F; + static_assert(is_same::value, ""); + static_assert(v3 == 1.0F, ""); + + static constexpr auto v4 = 1.F; // OK. + static_assert(is_same::value, ""); + static_assert(v4 == 1.0F, ""); + + static constexpr auto v5 = 1.e0F; // OK. + static_assert(is_same::value, ""); + static_assert(v5 == 1.0F, ""); + + // Long double + + static constexpr auto v6 = 1.l; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'l', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v6 = 1.l; + // CHECK-MESSAGES-NEXT: ^ ~ + // CHECK-MESSAGES-NEXT: {{^ *}}L{{$}} + // CHECK-FIXES: static constexpr auto v6 = 1.L; + static_assert(is_same::value, ""); + static_assert(v6 == 1., ""); + + static constexpr auto v7 = 1.e0l; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'l', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v7 = 1.e0l; + // CHECK-MESSAGES-NEXT: ^ ~ + // CHECK-MESSAGES-NEXT: {{^ *}}L{{$}} + // CHECK-FIXES: static constexpr auto v7 = 1.e0L; + static_assert(is_same::value, ""); + static_assert(v7 == 1., ""); + + static constexpr auto v8 = 1.L; // OK. + static_assert(is_same::value, ""); + static_assert(v8 == 1., ""); + + static constexpr auto v9 = 1.e0L; // OK. + static_assert(is_same::value, ""); + static_assert(v9 == 1., ""); +} + +void macros() { +#define PASSTHROUGH(X) X + static constexpr auto m0 = PASSTHROUGH(1.f); + // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: floating point literal has suffix 'f', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto m0 = PASSTHROUGH(1.f); + // CHECK-MESSAGES-NEXT: ^ ~ + // CHECK-MESSAGES-NEXT: {{^ *}}F{{$}} + // CHECK-FIXES: static constexpr auto m0 = PASSTHROUGH(1.F); + static_assert(is_same::value, ""); + static_assert(m0 == 1.0F, ""); +} Index: test/clang-tidy/readability-uppercase-literal-suffix-hexadecimal-floating-point.cpp =================================================================== --- /dev/null +++ test/clang-tidy/readability-uppercase-literal-suffix-hexadecimal-floating-point.cpp @@ -0,0 +1,78 @@ +// RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -I %S +// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp +// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -fix -- -I %S +// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -- -I %S + +#include "readability-uppercase-literal-suffix.h" + +void integer_suffix() { + static constexpr auto v0 = 0x0p0; // no literal + static_assert(is_same::value, ""); + static_assert(v0 == 0, ""); + + // Float + + static constexpr auto v1 = 0xfp0f; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v1 = 0xfp0f; + // CHECK-MESSAGES-NEXT: ^ ~ + // CHECK-MESSAGES-NEXT: {{^ *}}F{{$}} + // CHECK-FIXES: static constexpr auto v1 = 0xfp0F; + static_assert(is_same::value, ""); + static_assert(v1 == 15, ""); + + static constexpr auto v2 = 0xfp0F; // OK + static_assert(is_same::value, ""); + static_assert(v2 == 15, ""); + + static constexpr auto v3 = 0xfP0f; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 0xfP0f; + // CHECK-MESSAGES-NEXT: ^ ~ + // CHECK-MESSAGES-NEXT: {{^ *}}F{{$}} + // CHECK-FIXES: static constexpr auto v3 = 0xfP0F; + static_assert(is_same::value, ""); + static_assert(v3 == 15, ""); + + static constexpr auto v4 = 0xfP0F; // OK + static_assert(is_same::value, ""); + static_assert(v4 == 15, ""); + + static constexpr auto v5 = 0xFP0f; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 0xFP0f; + // CHECK-MESSAGES-NEXT: ^ ~ + // CHECK-MESSAGES-NEXT: {{^ *}}F{{$}} + // CHECK-FIXES: static constexpr auto v5 = 0xFP0F; + static_assert(is_same::value, ""); + static_assert(v5 == 15, ""); + + static constexpr auto v6 = 0xFP0F; // OK + static_assert(is_same::value, ""); + static_assert(v6 == 15, ""); + + static constexpr auto v7 = 0xFp0f; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v7 = 0xFp0f; + // CHECK-MESSAGES-NEXT: ^ ~ + // CHECK-MESSAGES-NEXT: {{^ *}}F{{$}} + // CHECK-FIXES: static constexpr auto v7 = 0xFp0F; + static_assert(is_same::value, ""); + static_assert(v7 == 15, ""); + + static constexpr auto v8 = 0xFp0F; // OK + static_assert(is_same::value, ""); + static_assert(v8 == 15, ""); +} + +void macros() { +#define PASSTHROUGH(X) X + static constexpr auto m0 = PASSTHROUGH(0x0p0f); + // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: floating point literal has suffix 'f', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto m0 = PASSTHROUGH(0x0p0f); + // CHECK-MESSAGES-NEXT: ^ ~ + // CHECK-MESSAGES-NEXT: {{^ *}}F{{$}} + // CHECK-FIXES: static constexpr auto m0 = PASSTHROUGH(0x0p0F); + static_assert(is_same::value, ""); + static_assert(m0 == 0x0p0F, ""); +} Index: test/clang-tidy/readability-uppercase-literal-suffix-integer-macro.cpp =================================================================== --- /dev/null +++ test/clang-tidy/readability-uppercase-literal-suffix-integer-macro.cpp @@ -0,0 +1,25 @@ +// RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -I %S + +void macros() { +#define INMACRO(X) 1.f + static constexpr auto m1 = INMACRO(); + // CHECK-NOTES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not upper-case + // CHECK-NOTES: :[[@LINE-3]]:20: note: expanded from macro 'INMACRO' + // CHECK-FIXES: #define INMACRO(X) 1.f + // CHECK-FIXES: static constexpr auto m1 = INMACRO(); + // ^ so no fix-its here. +} + +void horrible_macros() { +#define MAKE_UNSIGNED(x) x##u +#define ONE MAKE_UNSIGNED(1) + static constexpr auto hm0 = ONE; + // CHECK-NOTES: :[[@LINE-1]]:31: warning: integer literal has suffix 'u', which is not upper-case + // CHECK-NOTES: :[[@LINE-3]]:13: note: expanded from macro 'ONE' + // CHECK-NOTES: :[[@LINE-5]]:26: note: expanded from macro 'MAKE_UNSIGNED' + // CHECK-NOTES: note: expanded from here + // CHECK-FIXES: #define MAKE_UNSIGNED(x) x##u + // CHECK-FIXES: #define ONE MAKE_UNSIGNED(1) + // CHECK-FIXES: static constexpr auto hm0 = ONE; + // Certainly no fix-its. +} Index: test/clang-tidy/readability-uppercase-literal-suffix-integer.cpp =================================================================== --- /dev/null +++ test/clang-tidy/readability-uppercase-literal-suffix-integer.cpp @@ -0,0 +1,213 @@ +// RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -I %S +// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp +// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -fix -- -I %S +// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -- -I %S + +#include "readability-uppercase-literal-suffix.h" + +void integer_suffix() { + static constexpr auto v0 = __LINE__; // synthetic + static_assert(v0 == 9 || v0 == 5, ""); + + static constexpr auto v1 = __cplusplus; // synthetic, long + + static constexpr auto v2 = 1; // no literal + static_assert(is_same::value, ""); + static_assert(v2 == 1, ""); + + // Unsigned + + static constexpr auto v3 = 1u; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'u', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 1u; + // CHECK-MESSAGES-NEXT: ^~ + // CHECK-MESSAGES-NEXT: {{^ *}}U{{$}} + // CHECK-FIXES: static constexpr auto v3 = 1U; + static_assert(is_same::value, ""); + static_assert(v3 == 1, ""); + + static constexpr auto v4 = 1U; // OK. + static_assert(is_same::value, ""); + static_assert(v4 == 1, ""); + + // Long + + static constexpr auto v5 = 1l; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'l', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1l; + // CHECK-MESSAGES-NEXT: ^~ + // CHECK-MESSAGES-NEXT: {{^ *}}L{{$}} + // CHECK-FIXES: static constexpr auto v5 = 1L; + static_assert(is_same::value, ""); + static_assert(v5 == 1, ""); + + static constexpr auto v6 = 1L; // OK. + static_assert(is_same::value, ""); + static_assert(v6 == 1, ""); + + // Long Long + + static constexpr auto v7 = 1ll; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'll', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v7 = 1ll; + // CHECK-MESSAGES-NEXT: ^~~ + // CHECK-MESSAGES-NEXT: {{^ *}}LL{{$}} + // CHECK-FIXES: static constexpr auto v7 = 1LL; + static_assert(is_same::value, ""); + static_assert(v7 == 1, ""); + + static constexpr auto v8 = 1LL; // OK. + static_assert(is_same::value, ""); + static_assert(v8 == 1, ""); + + // Unsigned Long + + static constexpr auto v9 = 1ul; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'ul', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v9 = 1ul; + // CHECK-MESSAGES-NEXT: ^~~ + // CHECK-MESSAGES-NEXT: {{^ *}}UL{{$}} + // CHECK-FIXES: static constexpr auto v9 = 1UL; + static_assert(is_same::value, ""); + static_assert(v9 == 1, ""); + + static constexpr auto v10 = 1uL; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'uL', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v10 = 1uL; + // CHECK-MESSAGES-NEXT: ^~~ + // CHECK-MESSAGES-NEXT: {{^ *}}UL{{$}} + // CHECK-FIXES: static constexpr auto v10 = 1UL; + static_assert(is_same::value, ""); + static_assert(v10 == 1, ""); + + static constexpr auto v11 = 1Ul; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'Ul', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v11 = 1Ul; + // CHECK-MESSAGES-NEXT: ^~~ + // CHECK-MESSAGES-NEXT: {{^ *}}UL{{$}} + // CHECK-FIXES: static constexpr auto v11 = 1UL; + static_assert(is_same::value, ""); + static_assert(v11 == 1, ""); + + static constexpr auto v12 = 1UL; // OK. + static_assert(is_same::value, ""); + static_assert(v12 == 1, ""); + + // Long Unsigned + + static constexpr auto v13 = 1lu; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'lu', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v13 = 1lu; + // CHECK-MESSAGES-NEXT: ^~~ + // CHECK-MESSAGES-NEXT: {{^ *}}LU{{$}} + // CHECK-FIXES: static constexpr auto v13 = 1LU; + static_assert(is_same::value, ""); + static_assert(v13 == 1, ""); + + static constexpr auto v14 = 1Lu; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'Lu', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v14 = 1Lu; + // CHECK-MESSAGES-NEXT: ^~~ + // CHECK-MESSAGES-NEXT: {{^ *}}LU{{$}} + // CHECK-FIXES: static constexpr auto v14 = 1LU; + static_assert(is_same::value, ""); + static_assert(v14 == 1, ""); + + static constexpr auto v15 = 1lU; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'lU', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v15 = 1lU; + // CHECK-MESSAGES-NEXT: ^~~ + // CHECK-MESSAGES-NEXT: {{^ *}}LU{{$}} + // CHECK-FIXES: static constexpr auto v15 = 1LU; + static_assert(is_same::value, ""); + static_assert(v15 == 1, ""); + + static constexpr auto v16 = 1LU; // OK. + static_assert(is_same::value, ""); + static_assert(v16 == 1, ""); + + // Unsigned Long Long + + static constexpr auto v17 = 1ull; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'ull', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v17 = 1ull; + // CHECK-MESSAGES-NEXT: ^~~~ + // CHECK-MESSAGES-NEXT: {{^ *}}ULL{{$}} + // CHECK-FIXES: static constexpr auto v17 = 1ULL; + static_assert(is_same::value, ""); + static_assert(v17 == 1, ""); + + static constexpr auto v18 = 1uLL; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'uLL', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v18 = 1uLL; + // CHECK-MESSAGES-NEXT: ^~~~ + // CHECK-MESSAGES-NEXT: {{^ *}}ULL{{$}} + // CHECK-FIXES: static constexpr auto v18 = 1ULL; + static_assert(is_same::value, ""); + static_assert(v18 == 1, ""); + + static constexpr auto v19 = 1Ull; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'Ull', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v19 = 1Ull; + // CHECK-MESSAGES-NEXT: ^~~~ + // CHECK-MESSAGES-NEXT: {{^ *}}ULL{{$}} + // CHECK-FIXES: static constexpr auto v19 = 1ULL; + static_assert(is_same::value, ""); + static_assert(v19 == 1, ""); + + static constexpr auto v20 = 1ULL; // OK. + static_assert(is_same::value, ""); + static_assert(v20 == 1, ""); + + // Long Long Unsigned + + static constexpr auto v21 = 1llu; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'llu', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v21 = 1llu; + // CHECK-MESSAGES-NEXT: ^~~~ + // CHECK-MESSAGES-NEXT: {{^ *}}LLU{{$}} + // CHECK-FIXES: static constexpr auto v21 = 1LLU; + static_assert(is_same::value, ""); + static_assert(v21 == 1, ""); + + static constexpr auto v22 = 1LLu; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'LLu', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v22 = 1LLu; + // CHECK-MESSAGES-NEXT: ^~~~ + // CHECK-MESSAGES-NEXT: {{^ *}}LLU{{$}} + // CHECK-FIXES: static constexpr auto v22 = 1LLU; + static_assert(is_same::value, ""); + static_assert(v22 == 1, ""); + + static constexpr auto v23 = 1llU; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'llU', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto v23 = 1llU; + // CHECK-MESSAGES-NEXT: ^~~~ + // CHECK-MESSAGES-NEXT: {{^ *}}LLU{{$}} + // CHECK-FIXES: static constexpr auto v23 = 1LLU; + static_assert(is_same::value, ""); + static_assert(v23 == 1, ""); + + static constexpr auto v24 = 1LLU; // OK. + static_assert(is_same::value, ""); + static_assert(v24 == 1, ""); +} + +void macros() { +#define PASSTHROUGH(X) X + static constexpr auto m0 = PASSTHROUGH(1u); + // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: integer literal has suffix 'u', which is not upper-case + // CHECK-MESSAGES-NEXT: static constexpr auto m0 = PASSTHROUGH(1u); + // CHECK-MESSAGES-NEXT: ^~ + // CHECK-MESSAGES-NEXT: {{^ *}}U{{$}} + // CHECK-FIXES: static constexpr auto m0 = PASSTHROUGH(1U); + static_assert(is_same::value, ""); + static_assert(m0 == 1, ""); +} + +// Check that user-defined literals do not cause any diags. + +unsigned long long int operator"" _ull(unsigned long long int); +void user_defined_literals() { + 1_ull; +} Index: test/clang-tidy/readability-uppercase-literal-suffix.h =================================================================== --- /dev/null +++ test/clang-tidy/readability-uppercase-literal-suffix.h @@ -0,0 +1,16 @@ +template +struct integral_constant { + static constexpr T value = v; + typedef T value_type; + typedef integral_constant type; // using injected-class-name + constexpr operator value_type() const noexcept { return value; } +}; + +using false_type = integral_constant; +using true_type = integral_constant; + +template +struct is_same : false_type {}; + +template +struct is_same : true_type {};