https://github.com/xxxxbc updated https://github.com/llvm/llvm-project/pull/208762
>From ba0ed0b9c401624b778d89a10e6e780eadc3347b Mon Sep 17 00:00:00 2001 From: hehuan <[email protected]> Date: Fri, 10 Jul 2026 23:50:13 +0800 Subject: [PATCH 1/2] [clang-tidy] Fix invalid code generation for member typedefs in readability-use-std-min-max When the two compared operands have different types, the check emits an explicit template argument for std::min/std::max. For class member typedefs (e.g. std::string::size_type), the bare typedef name is not usable at the fix location, producing code that does not compile. Fix getNonTemplateAlias() to keep desugaring when the typedef is declared inside a record (class/struct), so the underlying type is used instead. Fixes #208693. --- .../readability/UseStdMinMaxCheck.cpp | 10 +++++++--- clang-tools-extra/docs/ReleaseNotes.rst | 5 ++++- .../checkers/readability/use-std-min-max.cpp | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp index 38e6dfb5328fa..67396c58736a9 100644 --- a/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp @@ -63,9 +63,13 @@ static QualType getNonTemplateAlias(QualType QT) { while (true) { // cast to a TypedefType if (const auto *TT = dyn_cast<TypedefType>(QT)) { - // check if the typedef is a template and if it is dependent - if (!TT->getDecl()->getDescribedTemplate() && - !TT->getDecl()->getDeclContext()->isDependentContext()) + const TypedefNameDecl *TD = TT->getDecl(); + // Check if the typedef is a template and if it is dependent. A class + // member typedef (e.g. std::string::size_type) is not usable by its + // bare name at the location of the fix, so keep desugaring in that case. + if (!TD->getDescribedTemplate() && + !TD->getDeclContext()->isDependentContext() && + !TD->getDeclContext()->isRecord()) return QT; QT = TT->desugar(); } else { diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 66ce08534cae8..8d1edfc0b842e 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -123,7 +123,10 @@ Changes in existing checks - Improved :doc:`readability-use-std-min-max <clang-tidy/checks/readability/use-std-min-max>` check by fixing spurious - trailing semicolons and lost comments when the ``if`` body has no braces. + trailing semicolons and lost comments when the ``if`` body has no braces, and + by fixing invalid code generation when the explicit template argument resolved + to a class member typedef (for example ``std::string::size_type``); the + underlying type is now used instead. Removed checks ^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp index ebc3783e84650..19a2f7b94548f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp @@ -265,6 +265,23 @@ void f(int &n) { } } // namespace gh208708 +namespace gh208693 { +struct B { +protected: + typedef unsigned long bsize; +}; +struct S : B { + typedef B::bsize size_type; + size_type size() const; +}; +void f(const S &s, unsigned &n) { + // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>` [readability-use-std-min-max] + // CHECK-FIXES: n = std::max<unsigned long>(s.size(), n); + if (s.size() > n) + n = s.size(); +} +} // namespace gh208693 + namespace gh121676 { void useLeft() { >From b51406f2a6f275cca5becce3b5e475c90b353edb Mon Sep 17 00:00:00 2001 From: hehuan <[email protected]> Date: Tue, 22 Sep 2026 15:44:25 +0800 Subject: [PATCH 2/2] [clang-tidy] Fully desugar unusable member typedefs --- .../readability/UseStdMinMaxCheck.cpp | 12 ++++++---- .../checkers/readability/use-std-min-max.cpp | 23 +++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp index 67396c58736a9..f6855c35b725b 100644 --- a/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp @@ -60,23 +60,27 @@ static bool maxCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs, } static QualType getNonTemplateAlias(QualType QT) { + bool DesugaredRecordAlias = false; while (true) { // cast to a TypedefType if (const auto *TT = dyn_cast<TypedefType>(QT)) { const TypedefNameDecl *TD = TT->getDecl(); - // Check if the typedef is a template and if it is dependent. A class - // member typedef (e.g. std::string::size_type) is not usable by its - // bare name at the location of the fix, so keep desugaring in that case. + // Check if the typedef is a template and if it is dependent. if (!TD->getDescribedTemplate() && !TD->getDeclContext()->isDependentContext() && !TD->getDeclContext()->isRecord()) return QT; + DesugaredRecordAlias |= TD->getDeclContext()->isRecord(); QT = TT->desugar(); } else { break; } } - return QT; + // A class member typedef (e.g. std::string::size_type) is not usable by its + // bare name at the location of the fix. If desugaring it did not reveal a + // usable non-member alias, use the canonical type to also remove dependent + // member types exposed by its implementation. + return DesugaredRecordAlias ? QT.getCanonicalType() : QT; } static QualType getReplacementCastType(const Expr *CondLhs, const Expr *CondRhs, diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp index 19a2f7b94548f..413eeca3c4012 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp @@ -274,12 +274,35 @@ struct S : B { typedef B::bsize size_type; size_type size() const; }; +template <class T> +struct MakeUnsigned { + typedef unsigned long type; +}; +template <class Allocator, class DifferenceType> +struct SizeType : MakeUnsigned<DifferenceType> {}; +template <class Allocator> +struct AllocatorTraits { + typedef Allocator allocator_type; + typedef long difference_type; + typedef typename SizeType<allocator_type, difference_type>::type size_type; +}; +struct StringLike { + typedef AllocatorTraits<int> traits_type; + typedef typename traits_type::size_type size_type; + size_type size() const; +}; void f(const S &s, unsigned &n) { // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>` [readability-use-std-min-max] // CHECK-FIXES: n = std::max<unsigned long>(s.size(), n); if (s.size() > n) n = s.size(); } +void f(const StringLike &s, unsigned &n) { + // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>` [readability-use-std-min-max] + // CHECK-FIXES: n = std::max<unsigned long>(s.size(), n); + if (s.size() > n) + n = s.size(); +} } // namespace gh208693 namespace gh121676 { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
