The diff below moves clazy, kdevelop, qt[56]/qttools, openmw, pyside6
to MODCLANG_VERSION=22.

This is mostly trivial, except for a big upstream diff for qt6/qttools:
https://github.com/qt/qttools/commit/f75f4090ed8e5c9dc898a313a5dd9b6878c2f034
and a tweak for the qdoc supported clang versions.

OpenMW needs a small patch from upstream to build
https://gitlab.com/OpenMW/openmw/-/commit/307a131ca09f705b97f15ede1f702630cca6b396

In pyside6/tools there is a MODCLANG_LIB_DEPENDS which evaluates to the
empty string since the clang module isn't in scope.

I don't run any of this software, so this diff only went through a bulk
build. It would be nice if something like this could go in.


We would then be in the following situation with older llvms:

MODCLANG_VERSION 21:
devel/c2ffi                     could be patched to use 22
lang/deno                       could be patched to use 22
lang/rust                       can use 22
lang/zig                        hard dependency on 21
textproc/ruby-commonmarker      could update to 1.8 and use 22

MODCLANG_VERSION 20:
devel/py-llvmlite               upstream seems stuck

MODCLANG_VERSION 19:
devel/woboq_codebrowser         switch to https://github.com/KDAB/codebrowser?
lang/crystal                    crystal 1.20 supports llvm 22

In short, dropping 19 should be feasible. Since the maintainre seems
mostly absent these days, I really wonder if we need to keep llvm/20
for py-llvmlite and py-miasm.

>From 7d3fbf2a5edc69cf8f8e2db96a3af39c2f270efe Mon Sep 17 00:00:00 2001
From: Theo Buehler <[email protected]>
Date: Sat, 6 Jun 2026 20:20:32 +0200
Subject: [PATCH] Move qt to llvm/22

---
 devel/kdevelop/Makefile                       |   4 +-
 games/openmw/Makefile                         |   4 +-
 ...patch-components_sceneutil_texturetype_hpp |  14 +
 lang/clazy/Makefile                           |   3 +-
 x11/qt5/qttools/Makefile                      |   3 +-
 x11/qt6/pyside6/pyside/Makefile               |   3 +-
 x11/qt6/pyside6/shiboken/Makefile             |   3 +-
 x11/qt6/pyside6/shiboken_generator/Makefile   |   2 +-
 x11/qt6/qttools/Makefile                      |   3 +-
 ...tch-src_qdoc_cmake_QDocConfiguration_cmake |   2 +-
 ...oc_qdoc_src_qdoc_clang_AST_QualTypeNames_h | 512 ++++++++++++++++++
 11 files changed, 543 insertions(+), 10 deletions(-)
 create mode 100644 
games/openmw/patches/patch-components_sceneutil_texturetype_hpp
 create mode 100644 
x11/qt6/qttools/patches/patch-src_qdoc_qdoc_src_qdoc_clang_AST_QualTypeNames_h

diff --git a/devel/kdevelop/Makefile b/devel/kdevelop/Makefile
index 2e077e1d793..c0b9d43cd8e 100644
--- a/devel/kdevelop/Makefile
+++ b/devel/kdevelop/Makefile
@@ -5,6 +5,8 @@ COMMENT =       IDE for C, C++, Python, QML/JavaScript and PHP
 CATEGORIES =   devel x11
 DISTNAME =     kdevelop-${MODKDE_GEAR_VERSION}
 
+REVISION =     0
+
 HOMEPAGE =     https://www.kdevelop.org/
 
 SHARED_LIBS +=  KDevPlatformDebugger      10.0 # 6.0
@@ -54,7 +56,7 @@ MODULES =     x11/kde \
                lang/clang \
                lang/python
 
-MODCLANG_VERSION =     19
+MODCLANG_VERSION =     22
 MODCLANG_COMPILER_LINKS=No
 MODCLANG_BUILDDEP=     No
 MODPY_BUILDDEP =       No
diff --git a/games/openmw/Makefile b/games/openmw/Makefile
index 3b22444f72b..a0c719a9510 100644
--- a/games/openmw/Makefile
+++ b/games/openmw/Makefile
@@ -5,7 +5,7 @@ ONLY_FOR_ARCHS =        amd64 i386
 COMMENT =              open source implementation of TES III: Morrowind
 
 V =                    0.50.0
-REVISION =             1
+REVISION =             2
 GH_ACCOUNT =           OpenMW
 GH_PROJECT =           openmw
 GH_TAGNAME =           openmw-$V
@@ -34,7 +34,7 @@ WANTLIB += osgShadow osgSim osgText osgUtil osgViewer sqlite3
 WANTLIB += swresample swscale unshield yaml-cpp z
 
 MODULES =              devel/cmake lang/lua x11/qt6 lang/clang
-MODCLANG_VERSION =     19
+MODCLANG_VERSION =     22
 COMPILER =             ports-clang
 
 MODCMAKE_POLICY_VERSION_OVERRIDE=      Yes
diff --git a/games/openmw/patches/patch-components_sceneutil_texturetype_hpp 
b/games/openmw/patches/patch-components_sceneutil_texturetype_hpp
new file mode 100644
index 00000000000..2e079ebeb62
--- /dev/null
+++ b/games/openmw/patches/patch-components_sceneutil_texturetype_hpp
@@ -0,0 +1,14 @@
+https://gitlab.com/OpenMW/openmw/-/commit/307a131ca09f705b97f15ede1f702630cca6b396
+
+Index: components/sceneutil/texturetype.hpp
+--- components/sceneutil/texturetype.hpp.orig
++++ components/sceneutil/texturetype.hpp
+@@ -18,7 +18,7 @@ namespace SceneUtil
+         {
+         }
+ 
+-        static const osg::StateAttribute::Type AttributeType = 
static_cast<osg::StateAttribute::Type>(69420);
++        static const osg::StateAttribute::Type AttributeType = 
static_cast<osg::StateAttribute::Type>(69);
+         META_StateAttribute(SceneUtil, TextureType, AttributeType)
+ 
+         bool isTextureAttribute() const override { return true; }
diff --git a/lang/clazy/Makefile b/lang/clazy/Makefile
index 4bac9c6b219..0a3bef85372 100644
--- a/lang/clazy/Makefile
+++ b/lang/clazy/Makefile
@@ -5,6 +5,7 @@ COMMENT =       static source code analyzer for Qt-based C++
 V =            1.17.1
 DISTNAME =     clazy-v${V}
 PKGNAME =      clazy-${V}
+REVISION =     0
 
 CATEGORIES =   lang devel
 
@@ -24,7 +25,7 @@ COMPILER =            base-clang ports-gcc
 
 MODULES =              lang/clang devel/cmake
 
-MODCLANG_VERSION =             19
+MODCLANG_VERSION =             22
 MODCLANG_COMPILER_LINKS =      No
 MODCLANG_RUNDEP =              Yes
 
diff --git a/x11/qt5/qttools/Makefile b/x11/qt5/qttools/Makefile
index 3ffbf7d8347..4ae21372b44 100644
--- a/x11/qt5/qttools/Makefile
+++ b/x11/qt5/qttools/Makefile
@@ -4,6 +4,7 @@ COMMENT-main =          Qt development tools
 
 KDE_COMMIT =           58e16bdc8e3e05dcf1770475237cb876044abc36
 KDE_VERSION =          0
+REVISION =             0
 
 DPB_PROPERTIES =       parallel
 
@@ -22,7 +23,7 @@ WANTLIB-main += llvm${MODCLANG_VERSION}/lib/clang
 MODQT5_DEPS =  No
 
 MODULES+=                      lang/clang
-MODCLANG_VERSION =             19
+MODCLANG_VERSION =             22
 MODCLANG_COMPILER_LINKS =      No
 MODCLANG_BUILDDEP =            No
 MODCLANG_RUNDEP =              No
diff --git a/x11/qt6/pyside6/pyside/Makefile b/x11/qt6/pyside6/pyside/Makefile
index 49e3e2a38b8..d53990898b3 100644
--- a/x11/qt6/pyside6/pyside/Makefile
+++ b/x11/qt6/pyside6/pyside/Makefile
@@ -2,6 +2,7 @@ DPB_PROPERTIES =        parallel
 
 COMMENT =              Python Qt bindings for Qt 6
 PKGNAME =              pyside6-${VERSION}
+REVISOIN =             0
 
 SHARED_LIBS +=         pyside6.abi3            2.0 # 0.0
 SHARED_LIBS +=         pyside6qml.abi3         1.0 # 0.0
@@ -25,7 +26,7 @@ WANTLIB += Qt6Multimedia
 
 MODULES +=             lang/clang \
                        lang/python
-MODCLANG_VERSION =     19
+MODCLANG_VERSION =     22
 
 BUILD_DEPENDS =                devel/llvm/${MODCLANG_VERSION} \
                        x11/qt6/qttools
diff --git a/x11/qt6/pyside6/shiboken/Makefile 
b/x11/qt6/pyside6/shiboken/Makefile
index e7eaea922d8..c6b0d3810b0 100644
--- a/x11/qt6/pyside6/shiboken/Makefile
+++ b/x11/qt6/pyside6/shiboken/Makefile
@@ -1,5 +1,6 @@
 COMMENT =              Python binding generator for C++ libraries
 PKGNAME =              shiboken6-${VERSION}
+REVISION =             0
 
 SHARED_LIBS +=         shiboken6.abi3  2.0 # 0.0
 
@@ -8,7 +9,7 @@ WANTLIB += llvm${MODCLANG_VERSION}/lib/clang
 
 MODULES +=             lang/clang \
                        lang/python
-MODCLANG_VERSION =     19
+MODCLANG_VERSION =     22
 
 
 BUILD_DEPENDS =                x11/qt6/qttools \
diff --git a/x11/qt6/pyside6/shiboken_generator/Makefile 
b/x11/qt6/pyside6/shiboken_generator/Makefile
index 1e18783aafb..15d734b2c4b 100644
--- a/x11/qt6/pyside6/shiboken_generator/Makefile
+++ b/x11/qt6/pyside6/shiboken_generator/Makefile
@@ -6,7 +6,7 @@ WANTLIB += llvm${MODCLANG_VERSION}/lib/clang
 
 MODULES +=             lang/clang \
                        lang/python
-MODCLANG_VERSION =     19
+MODCLANG_VERSION =     22
 
 BUILD_DEPENDS =                x11/qt6/qttools
 
diff --git a/x11/qt6/qttools/Makefile b/x11/qt6/qttools/Makefile
index dd195053e91..fabf69eba9c 100644
--- a/x11/qt6/qttools/Makefile
+++ b/x11/qt6/qttools/Makefile
@@ -1,6 +1,7 @@
 QT6NAME =      QtTools
 COMMENT =      Qt6 development tools
 PKGSPEC =      qt6-qttools-${QT6_PKGSPEC}
+REVISION =     0
 
 SHARED_LIBS +=  Qt6Designer             4.0 # 6.7
 SHARED_LIBS +=  Qt6DesignerComponents   2.0 # 6.7
@@ -22,7 +23,7 @@ MODQT6_DEPS = No
 
 MODULES +=                     lang/clang
 # see patch-src_qdoc_cmake_QDocConfiguration_cmake
-MODCLANG_VERSION =             19
+MODCLANG_VERSION =             22
 MODCLANG_COMPILER_LINKS =      Yes
 MODCLANG_BUILDDEP =            No
 MODCLANG_RUNDEP =              No
diff --git 
a/x11/qt6/qttools/patches/patch-src_qdoc_cmake_QDocConfiguration_cmake 
b/x11/qt6/qttools/patches/patch-src_qdoc_cmake_QDocConfiguration_cmake
index 364b75c3af9..89c7a85f739 100644
--- a/x11/qt6/qttools/patches/patch-src_qdoc_cmake_QDocConfiguration_cmake
+++ b/x11/qt6/qttools/patches/patch-src_qdoc_cmake_QDocConfiguration_cmake
@@ -6,7 +6,7 @@ Index: src/qdoc/cmake/QDocConfiguration.cmake
  # List of explicitly supported Clang versions for QDoc
  set(QDOC_SUPPORTED_CLANG_VERSIONS
 -    "21.1" "20.1" "19.1" "18.1" "17.0.6"
-+    "19.1"
++    "22.1"
  )
  
  # Check for QDoc coverage dependencies
diff --git 
a/x11/qt6/qttools/patches/patch-src_qdoc_qdoc_src_qdoc_clang_AST_QualTypeNames_h
 
b/x11/qt6/qttools/patches/patch-src_qdoc_qdoc_src_qdoc_clang_AST_QualTypeNames_h
new file mode 100644
index 00000000000..38c3f921266
--- /dev/null
+++ 
b/x11/qt6/qttools/patches/patch-src_qdoc_qdoc_src_qdoc_clang_AST_QualTypeNames_h
@@ -0,0 +1,512 @@
+https://github.com/qt/qttools/commit/f75f4090ed8e5c9dc898a313a5dd9b6878c2f034
+
+Index: src/qdoc/qdoc/src/qdoc/clang/AST/QualTypeNames.h
+--- src/qdoc/qdoc/src/qdoc/clang/AST/QualTypeNames.h.orig
++++ src/qdoc/qdoc/src/qdoc/clang/AST/QualTypeNames.h
+@@ -36,9 +36,497 @@ namespace clang {
+ 
+ namespace TypeName {
+ 
++#if CLANG_VERSION_MAJOR >= 22
++
++// =========================================================================
++// LLVM 22+ implementation
++//
++// Adapted from upstream clang/lib/AST/QualTypeNames.cpp (release/22.x).
++// LLVM 22 changed NestedNameSpecifier from pointer to value type,
++// merged RecordType into TagType, and merged ElaboratedType into
++// TypedefType/TagType via TypeWithKeyword.
++//
++// QDoc divergences from upstream are marked with "QDoc divergence" comments.
++// =========================================================================
++
+ inline QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
+                                       bool WithGlobalNsPrefix);
+ 
++static inline NestedNameSpecifier
++createNestedNameSpecifier(const ASTContext &Ctx, const NamespaceDecl *Namesp,
++                          bool WithGlobalNsPrefix);
++
++static inline NestedNameSpecifier
++createNestedNameSpecifier(const ASTContext &Ctx, const TypeDecl *TD,
++                          bool FullyQualify, bool WithGlobalNsPrefix);
++
++static inline NestedNameSpecifier
++createNestedNameSpecifierForScopeOf(const ASTContext &Ctx, const Decl *decl,
++                                    bool FullyQualified,
++                                    bool WithGlobalNsPrefix);
++
++static inline NestedNameSpecifier
++getFullyQualifiedNestedNameSpecifier(const ASTContext &Ctx,
++                                     NestedNameSpecifier NNS,
++                                     bool WithGlobalNsPrefix);
++
++static inline bool getFullyQualifiedTemplateName(const ASTContext &Ctx,
++                                          TemplateName &TName,
++                                          bool WithGlobalNsPrefix) {
++  bool Changed = false;
++  NestedNameSpecifier NNS = std::nullopt;
++
++  TemplateDecl *ArgTDecl = TName.getAsTemplateDecl();
++  if (!ArgTDecl) // ArgTDecl can be null in dependent contexts.
++    return false;
++
++  QualifiedTemplateName *QTName = TName.getAsQualifiedTemplateName();
++
++  if (QTName &&
++      !QTName->hasTemplateKeyword() &&
++      (NNS = QTName->getQualifier())) {
++    NestedNameSpecifier QNNS =
++        getFullyQualifiedNestedNameSpecifier(Ctx, NNS, WithGlobalNsPrefix);
++    if (QNNS != NNS) {
++      Changed = true;
++      NNS = QNNS;
++    } else {
++      NNS = std::nullopt;
++    }
++  } else {
++    NNS = createNestedNameSpecifierForScopeOf(
++        Ctx, ArgTDecl, true, WithGlobalNsPrefix);
++  }
++  if (NNS) {
++    TemplateName UnderlyingTN(ArgTDecl);
++    if (UsingShadowDecl *USD = TName.getAsUsingShadowDecl())
++      UnderlyingTN = TemplateName(USD);
++    TName =
++        Ctx.getQualifiedTemplateName(NNS,
++                                     /*TemplateKeyword=*/false, UnderlyingTN);
++    Changed = true;
++  }
++  return Changed;
++}
++
++static inline bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx,
++                                              TemplateArgument &Arg,
++                                              bool WithGlobalNsPrefix) {
++  bool Changed = false;
++
++  // Note: we do not handle TemplateArgument::Expression, to replace it
++  // we need the information for the template instance decl.
++
++  if (Arg.getKind() == TemplateArgument::Template) {
++    TemplateName TName = Arg.getAsTemplate();
++    Changed = getFullyQualifiedTemplateName(Ctx, TName, WithGlobalNsPrefix);
++    if (Changed) {
++      Arg = TemplateArgument(TName);
++    }
++  } else if (Arg.getKind() == TemplateArgument::Type) {
++    QualType SubTy = Arg.getAsType();
++    // Check if the type needs more desugaring and recurse.
++    QualType QTFQ = getFullyQualifiedType(SubTy, Ctx, WithGlobalNsPrefix);
++    if (QTFQ != SubTy) {
++      Arg = TemplateArgument(QTFQ);
++      Changed = true;
++    }
++  }
++  return Changed;
++}
++
++static inline const Type *getFullyQualifiedTemplateType(
++    const ASTContext &Ctx,
++    const TagType *TSTRecord,
++    ElaboratedTypeKeyword Keyword,
++    NestedNameSpecifier Qualifier,
++    bool WithGlobalNsPrefix) {
++  // We are asked to fully qualify and we have a Record Type,
++  // which can point to a template instantiation with no sugar in any of
++  // its template argument, however we still need to fully qualify them.
++
++  const auto *TD = TSTRecord->getDecl();
++  const auto *TSTDecl = dyn_cast<ClassTemplateSpecializationDecl>(TD);
++  if (!TSTDecl)
++    return Ctx.getTagType(Keyword, Qualifier, TD, /*OwnsTag=*/false)
++        .getTypePtr();
++
++  const TemplateArgumentList &TemplateArgs = TSTDecl->getTemplateArgs();
++
++  bool MightHaveChanged = false;
++  SmallVector<TemplateArgument, 4> FQArgs;
++  for (unsigned int I = 0, E = TemplateArgs.size(); I != E; ++I) {
++    // cheap to copy and potentially modified by
++    // getFullyQualifedTemplateArgument
++    TemplateArgument Arg(TemplateArgs[I]);
++    MightHaveChanged |=
++        getFullyQualifiedTemplateArgument(Ctx, Arg, WithGlobalNsPrefix);
++    FQArgs.push_back(Arg);
++  }
++
++  if (!MightHaveChanged)
++    return Ctx.getTagType(Keyword, Qualifier, TD, /*OwnsTag=*/false)
++        .getTypePtr();
++  // If a fully qualified arg is different from the unqualified arg,
++  // allocate new type in the AST.
++  TemplateName TN = Ctx.getQualifiedTemplateName(
++      Qualifier, /*TemplateKeyword=*/false,
++      TemplateName(TSTDecl->getSpecializedTemplate()));
++  QualType QT = Ctx.getTemplateSpecializationType(
++      Keyword, TN, FQArgs,
++      /*CanonicalArgs=*/{}, TSTRecord->getCanonicalTypeInternal());
++  // getTemplateSpecializationType returns a fully qualified
++  // version of the specialization itself, so no need to qualify
++  // it.
++  return QT.getTypePtr();
++}
++
++static inline const Type *
++getFullyQualifiedTemplateType(const ASTContext &Ctx,
++                              const TemplateSpecializationType *TST,
++                              bool WithGlobalNsPrefix) {
++  TemplateName TName = TST->getTemplateName();
++  bool MightHaveChanged =
++      getFullyQualifiedTemplateName(Ctx, TName, WithGlobalNsPrefix);
++  SmallVector<TemplateArgument, 4> FQArgs;
++  // Cheap to copy and potentially modified by
++  // getFullyQualifedTemplateArgument.
++  for (TemplateArgument Arg : TST->template_arguments()) {
++    MightHaveChanged |=
++        getFullyQualifiedTemplateArgument(Ctx, Arg, WithGlobalNsPrefix);
++    FQArgs.push_back(Arg);
++  }
++
++  if (!MightHaveChanged)
++    return TST;
++
++  QualType NewQT =
++      Ctx.getTemplateSpecializationType(TST->getKeyword(), TName, FQArgs,
++                                        /*CanonicalArgs=*/{}, TST->desugar());
++  // getTemplateSpecializationType returns a fully qualified
++  // version of the specialization itself, so no need to qualify
++  // it.
++  return NewQT.getTypePtr();
++}
++
++static inline NestedNameSpecifier createOuterNNS(const ASTContext &Ctx,
++                                          const Decl *D,
++                                          bool FullyQualify,
++                                          bool WithGlobalNsPrefix) {
++  const DeclContext *DC = D->getDeclContext();
++  if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) {
++    while (NS && NS->isInline()) {
++      // Ignore inline namespace;
++      NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());
++    }
++    if (NS && NS->getDeclName()) {
++      return createNestedNameSpecifier(Ctx, NS, WithGlobalNsPrefix);
++    }
++    return std::nullopt; // no starting '::', no anonymous
++  }
++  if (const auto *TD = dyn_cast<TagDecl>(DC))
++    return createNestedNameSpecifier(Ctx, TD, FullyQualify, 
WithGlobalNsPrefix);
++  if (const auto *TDD = dyn_cast<TypedefNameDecl>(DC))
++    return createNestedNameSpecifier(Ctx, TDD, FullyQualify,
++                                     WithGlobalNsPrefix);
++  if (WithGlobalNsPrefix && DC->isTranslationUnit())
++    return NestedNameSpecifier::getGlobal();
++  return std::nullopt; // no starting '::' if |WithGlobalNsPrefix| is false
++}
++
++/// Return a fully qualified version of this name specifier.
++static inline NestedNameSpecifier getFullyQualifiedNestedNameSpecifier(
++    const ASTContext &Ctx, NestedNameSpecifier Scope,
++    bool WithGlobalNsPrefix) {
++  switch (Scope.getKind()) {
++  case NestedNameSpecifier::Kind::Null:
++    llvm_unreachable("can't fully qualify the empty nested name specifier");
++  case NestedNameSpecifier::Kind::Global:
++  case NestedNameSpecifier::Kind::MicrosoftSuper:
++    // Already fully qualified
++    return Scope;
++  case NestedNameSpecifier::Kind::Namespace:
++    return TypeName::createNestedNameSpecifier(
++        Ctx, Scope.getAsNamespaceAndPrefix().Namespace->getNamespace(),
++        WithGlobalNsPrefix);
++  case NestedNameSpecifier::Kind::Type: {
++    const Type *Type = Scope.getAsType();
++    // Find decl context.
++    const TypeDecl *TD;
++    if (const TagType *TagDeclType = Type->getAs<TagType>())
++      TD = TagDeclType->getDecl();
++    else if (const auto *D = dyn_cast<TypedefType>(Type))
++      TD = D->getDecl();
++    else
++      return Scope;
++    return TypeName::createNestedNameSpecifier(Ctx, TD, /*FullyQualify=*/true,
++                                               WithGlobalNsPrefix);
++  }
++  }
++  llvm_unreachable("bad NNS kind");
++}
++
++/// Create a nested name specifier for the declaring context of
++/// the type.
++static inline NestedNameSpecifier
++createNestedNameSpecifierForScopeOf(const ASTContext &Ctx, const Decl *Decl,
++                                    bool FullyQualified,
++                                    bool WithGlobalNsPrefix) {
++  assert(Decl);
++
++  // Some declaration cannot be qualified.
++  if (Decl->isTemplateParameter())
++    return std::nullopt;
++  const DeclContext *DC = Decl->getDeclContext()->getRedeclContext();
++  const auto *Outer = dyn_cast<NamedDecl>(DC);
++  const auto *OuterNS = dyn_cast<NamespaceDecl>(DC);
++  if (OuterNS && OuterNS->isAnonymousNamespace())
++    OuterNS = dyn_cast<NamespaceDecl>(OuterNS->getParent());
++  if (Outer) {
++#if 0
++    // QDoc divergence: upstream picks an arbitrary template specialization
++    // as the declaring context when a type is declared inside a class
++    // template but is not type-dependent. This produces unstable output
++    // (depends on specialization order) and is incorrect for QDoc's use
++    // case where we want the unspecialized template name.
++    // See QTBUG-144620.
++    if (const auto *CxxDecl = dyn_cast<CXXRecordDecl>(DC)) {
++      if (ClassTemplateDecl *ClassTempl =
++              CxxDecl->getDescribedClassTemplate()) {
++        if (!ClassTempl->specializations().empty()) {
++          Decl = *(ClassTempl->spec_begin());
++          Outer = dyn_cast<NamedDecl>(Decl);
++          OuterNS = dyn_cast<NamespaceDecl>(Decl);
++        }
++      }
++    }
++#endif
++
++    if (OuterNS) {
++      return createNestedNameSpecifier(Ctx, OuterNS, WithGlobalNsPrefix);
++    } else if (const auto *TD = dyn_cast<TagDecl>(Outer)) {
++      return createNestedNameSpecifier(
++          Ctx, TD, FullyQualified, WithGlobalNsPrefix);
++    } else if (isa<TranslationUnitDecl>(Outer)) {
++      // Context is the TU. Nothing needs to be done.
++      return std::nullopt;
++    } else {
++      // Decl's context was neither the TU, a namespace, nor a
++      // TagDecl, which means it is a type local to a scope, and not
++      // accessible at the end of the TU.
++      return std::nullopt;
++    }
++  } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
++    return NestedNameSpecifier::getGlobal();
++  }
++  return std::nullopt;
++}
++
++/// Create a nested name specifier for the declaring context of
++/// the type.
++static inline NestedNameSpecifier
++createNestedNameSpecifierForScopeOf(const ASTContext &Ctx, const Type 
*TypePtr,
++                                    bool FullyQualified,
++                                    bool WithGlobalNsPrefix) {
++  if (!TypePtr)
++    return std::nullopt;
++
++  Decl *Decl = nullptr;
++  // There are probably other cases ...
++  if (const auto *TDT = dyn_cast<TypedefType>(TypePtr)) {
++    Decl = TDT->getDecl();
++  } else if (const auto *TagDeclType = dyn_cast<TagType>(TypePtr)) {
++    Decl = TagDeclType->getDecl();
++  } else if (const auto *TST = dyn_cast<TemplateSpecializationType>(TypePtr)) 
{
++    Decl = TST->getTemplateName().getAsTemplateDecl();
++  } else {
++    Decl = TypePtr->getAsCXXRecordDecl();
++  }
++
++  if (!Decl)
++    return std::nullopt;
++
++  return createNestedNameSpecifierForScopeOf(
++      Ctx, Decl, FullyQualified, WithGlobalNsPrefix);
++}
++
++inline NestedNameSpecifier
++createNestedNameSpecifier(const ASTContext &Ctx, const NamespaceDecl 
*Namespace,
++                          bool WithGlobalNsPrefix) {
++  while (Namespace && Namespace->isInline()) {
++    // Ignore inline namespace;
++    Namespace = dyn_cast<NamespaceDecl>(Namespace->getDeclContext());
++  }
++  if (!Namespace)
++    return std::nullopt;
++
++  bool FullyQualify = true; // doesn't matter, DeclContexts are namespaces
++  return NestedNameSpecifier(
++      Ctx, Namespace,
++      createOuterNNS(Ctx, Namespace, FullyQualify, WithGlobalNsPrefix));
++}
++
++inline NestedNameSpecifier
++createNestedNameSpecifier(const ASTContext &Ctx, const TypeDecl *TD,
++                          bool FullyQualify, bool WithGlobalNsPrefix) {
++  const Type *TypePtr = Ctx.getTypeDeclType(TD).getTypePtr();
++  if (auto *RD = dyn_cast<TagType>(TypePtr)) {
++    // We are asked to fully qualify and we have a Record Type (which
++    // may point to a template specialization) or Template
++    // Specialization Type. We need to fully qualify their arguments.
++    TypePtr = getFullyQualifiedTemplateType(
++        Ctx, RD, ElaboratedTypeKeyword::None,
++        createOuterNNS(Ctx, TD, FullyQualify, WithGlobalNsPrefix),
++        WithGlobalNsPrefix);
++  } else if (auto *TST = dyn_cast<TemplateSpecializationType>(TypePtr)) {
++    TypePtr = getFullyQualifiedTemplateType(Ctx, TST, WithGlobalNsPrefix);
++  }
++  return NestedNameSpecifier(TypePtr);
++}
++
++/// Return the fully qualified type, including fully-qualified
++/// versions of any template parameters.
++inline QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
++                               bool WithGlobalNsPrefix = false) {
++  // In case of myType* we need to strip the pointer first, fully
++  // qualify and attach the pointer once again.
++  if (isa<PointerType>(QT.getTypePtr())) {
++    // Get the qualifiers.
++    Qualifiers Quals = QT.getQualifiers();
++    QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
++    QT = Ctx.getPointerType(QT);
++    // Add back the qualifiers.
++    QT = Ctx.getQualifiedType(QT, Quals);
++    return QT;
++  }
++
++  if (auto *MPT = dyn_cast<MemberPointerType>(QT.getTypePtr())) {
++    // Get the qualifiers.
++    Qualifiers Quals = QT.getQualifiers();
++    // Fully qualify the pointee and class types.
++    QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
++    NestedNameSpecifier Qualifier = getFullyQualifiedNestedNameSpecifier(
++        Ctx, MPT->getQualifier(), WithGlobalNsPrefix);
++    QT = Ctx.getMemberPointerType(QT, Qualifier,
++                                  MPT->getMostRecentCXXRecordDecl());
++    // Add back the qualifiers.
++    QT = Ctx.getQualifiedType(QT, Quals);
++    return QT;
++  }
++
++  // In case of myType& we need to strip the reference first, fully
++  // qualify and attach the reference once again.
++  if (isa<ReferenceType>(QT.getTypePtr())) {
++    // Get the qualifiers.
++    bool IsLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr());
++    Qualifiers Quals = QT.getQualifiers();
++    QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
++    // Add the r- or l-value reference type back to the fully
++    // qualified one.
++    if (IsLValueRefTy)
++      QT = Ctx.getLValueReferenceType(QT);
++    else
++      QT = Ctx.getRValueReferenceType(QT);
++    // Add back the qualifiers.
++    QT = Ctx.getQualifiedType(QT, Quals);
++    return QT;
++  }
++
++  // Handle types with attributes such as `unique_ptr<int> _Nonnull`.
++  if (auto *AT = dyn_cast<AttributedType>(QT.getTypePtr())) {
++    QualType NewModified =
++        getFullyQualifiedType(AT->getModifiedType(), Ctx, WithGlobalNsPrefix);
++    QualType NewEquivalent =
++        getFullyQualifiedType(AT->getEquivalentType(), Ctx, 
WithGlobalNsPrefix);
++    Qualifiers Qualifiers = QT.getLocalQualifiers();
++    return Ctx.getQualifiedType(
++        Ctx.getAttributedType(AT->getAttrKind(), NewModified, NewEquivalent),
++        Qualifiers);
++  }
++
++  // Remove the part of the type related to the type being a template
++  // parameter (we won't report it as part of the 'type name' and it
++  // is actually make the code below to be more complex (to handle
++  // those)
++  while (isa<SubstTemplateTypeParmType>(QT.getTypePtr())) {
++    // Get the qualifiers.
++    Qualifiers Quals = QT.getQualifiers();
++
++    QT = cast<SubstTemplateTypeParmType>(QT.getTypePtr())->desugar();
++
++    // Add back the qualifiers.
++    QT = Ctx.getQualifiedType(QT, Quals);
++  }
++
++  if (const auto *TST =
++          dyn_cast<const TemplateSpecializationType>(QT.getTypePtr())) {
++
++    const Type *T = getFullyQualifiedTemplateType(Ctx, TST, 
WithGlobalNsPrefix);
++    if (T == TST)
++      return QT;
++    return Ctx.getQualifiedType(T, QT.getQualifiers());
++  }
++
++  // Local qualifiers are attached to the QualType outside of the
++  // elaborated type.  Retrieve them before descending into the
++  // elaborated type.
++  Qualifiers PrefixQualifiers = QT.getLocalQualifiers();
++  QT = QualType(QT.getTypePtr(), 0);
++
++  // We don't consider the alias introduced by `using a::X` as a new type.
++  // The qualified name is still a::X.
++  if (const auto *UT = QT->getAs<UsingType>()) {
++    QT = Ctx.getQualifiedType(UT->desugar(), PrefixQualifiers);
++    return getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix);
++  }
++
++  // Create a nested name specifier if needed.
++  NestedNameSpecifier Prefix = createNestedNameSpecifierForScopeOf(
++      Ctx, QT.getTypePtr(), true /*FullyQualified*/, WithGlobalNsPrefix);
++
++  // In case of template specializations iterate over the arguments and
++  // fully qualify them as well.
++  if (const auto *TT = dyn_cast<TagType>(QT.getTypePtr())) {
++    // We are asked to fully qualify and we have a Record Type (which
++    // may point to a template specialization) or Template
++    // Specialization Type. We need to fully qualify their arguments.
++
++    const Type *TypePtr = getFullyQualifiedTemplateType(
++        Ctx, TT, TT->getKeyword(), Prefix, WithGlobalNsPrefix);
++    QT = QualType(TypePtr, 0);
++  } else if (const auto *TT = dyn_cast<TypedefType>(QT.getTypePtr())) {
++    // QDoc divergence: prefer the existing qualifier from the TypedefType
++    // when available, falling back to the computed Prefix. This preserves
++    // member type alias qualifiers (e.g., QList<QVariant>::parameter_type)
++    // that would otherwise be lost when the Prefix is recomputed from the
++    // declaring context. See QTBUG-144620.
++    NestedNameSpecifier TypedefPrefix = TT->getQualifier();
++    QT = Ctx.getTypedefType(
++        TT->getKeyword(), TypedefPrefix ? TypedefPrefix : Prefix,
++        TT->getDecl(),
++        getFullyQualifiedType(TT->desugar(), Ctx, WithGlobalNsPrefix));
++  } else {
++    // QDoc divergence: upstream asserts here (!Prefix && "Unhandled type 
node").
++    // QDoc encounters types (such as AutoType and BuiltinType) that may have
++    // a non-null Prefix but are not TagType or TypedefType. Silently dropping
++    // the prefix is safe — it only affects qualification of the printed name.
++  }
++  QT = Ctx.getQualifiedType(QT, PrefixQualifiers);
++  return QT;
++}
++
++#else // CLANG_VERSION_MAJOR < 22
++
++// =========================================================================
++// Pre-LLVM 22 implementation
++//
++// This block is the existing fork, unchanged. It supports LLVM 17–21
++// with version-specific guards for API differences between those releases.
++// =========================================================================
++
++inline QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
++                                      bool WithGlobalNsPrefix);
++
+ /// Create a NestedNameSpecifier for Namesp and its enclosing
+ /// scopes.
+ ///
+@@ -502,6 +990,8 @@ inline QualType getFullyQualifiedType(QualType QT, con
+   QT = Ctx.getQualifiedType(QT, PrefixQualifiers);
+   return QT;
+ }
++
++#endif // CLANG_VERSION_MAJOR >= 22
+ 
+ inline std::string getFullyQualifiedName(QualType QT,
+                                   const ASTContext &Ctx,
-- 
2.54.0

Reply via email to