Author: Christian Kandeler Date: 2026-09-17T13:18:43+02:00 New Revision: 743442442b21040955cd76ba47ff9f3657132c31
URL: https://github.com/llvm/llvm-project/commit/743442442b21040955cd76ba47ff9f3657132c31 DIFF: https://github.com/llvm/llvm-project/commit/743442442b21040955cd76ba47ff9f3657132c31.diff LOG: [clang] Look through alias templates when visiting dependent base classes (#223713) When gathering the visible declarations of a class for code completion, a dependent base class is resolved by taking its `TemplateSpecializationType` and casting the template it names to a `ClassTemplateDecl`. If the base is named through an alias template, that cast fails, because the `TemplateName` refers to a `TypeAliasTemplateDecl`. The base is then skipped, along with everything it declares. libstdc++'s `std::allocator` runs into this: it derives from `__allocator_base<T>`, which is an alias template for `__new_allocator<T>`. Before C++20, `allocate()` and `deallocate()` are declared only in that base (the declarations in `allocator` itself are guarded by `__cpp_constexpr_dynamic_alloc`), so a dependent `std::allocator<T>` offered no members at all beyond the implicitly declared ones: ```c++ #include <memory> template <class T> void f(std::allocator<T> a) { a. // nothing useful was offered here } ``` Look through the alias to the class template it ultimately names. Assisted-by: Claude Opus 5 --------- Co-authored-by: Younan Zhang <[email protected]> Co-authored-by: Corentin Jabot <[email protected]> Added: clang/test/CodeCompletion/dependent-base-alias-template.cpp Modified: clang/docs/ReleaseNotes.md clang/lib/Sema/SemaLookup.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 89df8e80459f0..dfc03787678f9 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -882,6 +882,9 @@ features cannot lower the translation-unit ABI level; `set_x(decltype(x) val)`. This affects the completion strings produced by libclang as well as those used by clangd. +- Members inherited from a dependent base class that is named through an alias + template are suggested by code completion when relevant. + ### Static Analyzer #### Crash and bug fixes diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 78365323477c3..bc8a30d4e9dad 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4262,7 +4262,10 @@ class LookupVisibleHelper { // there anyway. continue; } - const auto *TST = BaseType->getAs<TemplateSpecializationType>(); + // Look through an alias template, whose TemplateName is a + // TypeAliasTemplateDecl rather than the ClassTemplateDecl we are + // after; otherwise the base is skipped entirely. + const auto *TST = BaseType->getAsNonAliasTemplateSpecializationType(); if (!TST) continue; TemplateName TN = TST->getTemplateName(); diff --git a/clang/test/CodeCompletion/dependent-base-alias-template.cpp b/clang/test/CodeCompletion/dependent-base-alias-template.cpp new file mode 100644 index 0000000000000..7aef777245def --- /dev/null +++ b/clang/test/CodeCompletion/dependent-base-alias-template.cpp @@ -0,0 +1,35 @@ +template <typename T> struct Base { + void fromBase(); +}; + +// A dependent base class can be named through an alias template. libstdc++'s +// std::allocator does exactly this: it derives from __allocator_base<T>, which +// is an alias template for __new_allocator<T>. +template <typename T> using AliasBase = Base<T>; +template <typename T> using AliasOfAlias = AliasBase<T>; + +template <typename T> struct Derived : AliasBase<T> { + void ownMember(); +}; + +template <typename T> struct DerivedTwice : AliasOfAlias<T> { + void ownMember(); +}; + +template <typename T> void f(Derived<T> d) { + d. +} +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):5 -std=c++17 %s | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: COMPLETION: Base (InBase) : Base:: +// CHECK-CC1: COMPLETION: Derived : Derived:: +// CHECK-CC1: COMPLETION: fromBase (InBase) : [#void#][#Base<T>::#]fromBase() +// CHECK-CC1: COMPLETION: ownMember : [#void#]ownMember() + +template <typename T> void g(DerivedTwice<T> d) { + d. +} +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):5 -std=c++17 %s | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: COMPLETION: Base (InBase) : Base:: +// CHECK-CC2: COMPLETION: DerivedTwice : DerivedTwice:: +// CHECK-CC2: COMPLETION: fromBase (InBase) : [#void#][#Base<T>::#]fromBase() +// CHECK-CC2: COMPLETION: ownMember : [#void#]ownMember() _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
