diff --git a/packages/llvm/llvm13-0001-Tooling-Fully-qualify-template-parameters-of-nested-.patch b/packages/llvm/llvm13-0001-Tooling-Fully-qualify-template-parameters-of-nested-.patch
new file mode 100644
index 0000000000000000000000000000000000000000..56ff9ccb374506f1b3a8fcbbcbd01d58a405642b
--- /dev/null
+++ b/packages/llvm/llvm13-0001-Tooling-Fully-qualify-template-parameters-of-nested-.patch
@@ -0,0 +1,32 @@
+diff -pur spack-src/clang/unittests/Tooling/QualTypeNamesTest.cpp spack-src-new/clang/unittests/Tooling/QualTypeNamesTest.cpp
+--- spack-src/clang/unittests/Tooling/QualTypeNamesTest.cpp	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/unittests/Tooling/QualTypeNamesTest.cpp	2025-02-03 18:25:49.156686779 +0100
+@@ -66,6 +66,10 @@ TEST(QualTypeNameTest, getFullyQualified
+   // Template parameter expansion.
+   Visitor.ExpectedQualTypeNames["CheckC"] =
+       "A::B::Template0<A::B::C::MyInt, A::B::AnotherClass>";
++  // Template parameters of nested name specifier should also be fully expanded.
++  Visitor.ExpectedQualTypeNames["CheckNested"] =
++      // "typename A::B::Template0<A::B::C::MyInt, A::B::AnotherClass>::nested";
++      "typename A::B::Template0<int, A::B::Class0>::nested";
+   // Recursive template parameter expansion.
+   Visitor.ExpectedQualTypeNames["CheckD"] =
+       "A::B::Template0<A::B::Template1<A::B::C::MyInt, A::B::AnotherClass>, "
+@@ -111,7 +115,7 @@ TEST(QualTypeNameTest, getFullyQualified
+       "     InnerAlias<int> AliasTypeVal;\n"
+       "     InnerAlias<Class0>::Inner AliasInnerTypeVal;\n"
+       "   }\n"
+-      "   template<class X, class Y> class Template0;"
++      "   template<class X, class Y> struct Template0 { typedef int nested; };"
+       "   template<class X, class Y> class Template1;"
+       "   typedef B::Class0 AnotherClass;\n"
+       "   void Function1(Template0<C::MyInt,\n"
+@@ -119,6 +123,8 @@ TEST(QualTypeNameTest, getFullyQualified
+       "   void Function2(Template0<Template1<C::MyInt, AnotherClass>,\n"
+       "                            Template0<int, long> > CheckD);\n"
+       "   void Function3(const B::Class0* CheckM);\n"
++      "   void Function4(typename Template0<C::MyInt,\n"
++      "                  AnotherClass>::nested CheckNested);\n"
+       "  }\n"
+       "template<typename... Values> class Variadic {};\n"
+       "Variadic<int, B::Template0<int, char>, "
diff --git a/packages/llvm/llvm13-0002-libclang-Add-support-for-obtaining-fully-qualified-n.patch b/packages/llvm/llvm13-0002-libclang-Add-support-for-obtaining-fully-qualified-n.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b27ac4c016e3048ea5c003609a2c322e442a7995
--- /dev/null
+++ b/packages/llvm/llvm13-0002-libclang-Add-support-for-obtaining-fully-qualified-n.patch
@@ -0,0 +1,136 @@
+diff -pur spack-src/clang/bindings/python/clang/cindex.py spack-src-new/clang/bindings/python/clang/cindex.py
+--- spack-src/clang/bindings/python/clang/cindex.py	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/bindings/python/clang/cindex.py	2025-02-03 18:35:08.767069975 +0100
+@@ -2428,6 +2428,14 @@ class Type(Structure):
+         """Retrieve the spelling of this Type."""
+         return conf.lib.clang_getTypeSpelling(self)
+ 
++    @property
++    def fully_qualified_name(self):
++        """Retrieve the fully qualified name of this Type."""
++        if not hasattr(self, '_fully_qualified_name'):
++            self._fully_qualified_name = conf.lib.clang_getFullyQualifiedTypeName(self)
++
++        return self._fully_qualified_name
++
+     def __eq__(self, other):
+         if type(other) != type(self):
+             return False
+@@ -3869,6 +3877,11 @@ functionList = [
+    [Type],
+    _CXString,
+    _CXString.from_result),
++
++  ("clang_getFullyQualifiedTypeName",
++   [Type],
++   _CXString,
++   _CXString.from_result),
+ 
+   ("clang_hashCursor",
+    [Cursor],
+diff -pur spack-src/clang/bindings/python/tests/cindex/test_cursor.py spack-src-new/clang/bindings/python/tests/cindex/test_cursor.py
+--- spack-src/clang/bindings/python/tests/cindex/test_cursor.py	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/bindings/python/tests/cindex/test_cursor.py	2025-02-03 18:35:08.767069975 +0100
+@@ -316,6 +316,14 @@ class TestCursor(unittest.TestCase):
+         underlying = typedef.underlying_typedef_type
+         self.assertEqual(underlying.kind, TypeKind.INT)
+ 
++    def test_fully_qualified_type_name():
++        source = 'namespace uiae { struct X { typedef int sometype; }; }'
++        tu = get_tu(source, lang='cpp')
++
++        cls = get_cursor(tu, 'sometype')
++        fqn = cls.type.fully_qualified_name
++        self.assertTrue(fqn.endswith("uiae::X::sometype"), fqn)
++
+     def test_semantic_parent(self):
+         tu = get_tu(kParentTest, 'cpp')
+         curs = get_cursors(tu, 'f')
+diff -pur spack-src/clang/include/clang-c/Index.h spack-src-new/clang/include/clang-c/Index.h
+--- spack-src/clang/include/clang-c/Index.h	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/include/clang-c/Index.h	2025-02-03 18:35:52.971255790 +0100
+@@ -33,7 +33,7 @@
+  * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
+  */
+ #define CINDEX_VERSION_MAJOR 0
+-#define CINDEX_VERSION_MINOR 62
++#define CINDEX_VERSION_MINOR 63
+ 
+ #define CINDEX_VERSION_ENCODE(major, minor) (((major)*10000) + ((minor)*1))
+ 
+@@ -3447,6 +3447,14 @@ CINDEX_LINKAGE CXType clang_getCursorTyp
+ CINDEX_LINKAGE CXString clang_getTypeSpelling(CXType CT);
+ 
+ /**
++ * Retrieve the fully qualified name of the underlying type.
++ * This includes full qualification of all template parameters etc.
++ *
++ * If the type is invalid, an empty string is returned.
++ */
++CINDEX_LINKAGE CXString clang_getFullyQualifiedTypeName(CXType CT);
++
++/**
+  * Retrieve the underlying type of a typedef declaration.
+  *
+  * If the cursor does not reference a typedef declaration, an invalid type is
+diff -pur spack-src/clang/tools/libclang/CMakeLists.txt spack-src-new/clang/tools/libclang/CMakeLists.txt
+--- spack-src/clang/tools/libclang/CMakeLists.txt	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/tools/libclang/CMakeLists.txt	2025-02-03 18:35:11.263080474 +0100
+@@ -52,6 +52,7 @@ set(LIBS
+   clangSema
+   clangSerialization
+   clangTooling
++  clangToolingCore
+ )
+ 
+ if (CLANG_ENABLE_ARCMT)
+diff -pur spack-src/clang/tools/libclang/CXType.cpp spack-src-new/clang/tools/libclang/CXType.cpp
+--- spack-src/clang/tools/libclang/CXType.cpp	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/tools/libclang/CXType.cpp	2025-02-03 18:35:11.263080474 +0100
+@@ -19,6 +19,7 @@
+ #include "clang/AST/DeclObjC.h"
+ #include "clang/AST/DeclTemplate.h"
+ #include "clang/AST/Expr.h"
++#include "clang/AST/QualTypeNames.h"
+ #include "clang/AST/Type.h"
+ #include "clang/Basic/AddressSpaces.h"
+ #include "clang/Frontend/ASTUnit.h"
+@@ -303,6 +304,27 @@ CXString clang_getTypeSpelling(CXType CT
+   return cxstring::createDup(OS.str());
+ }
+ 
++CXString clang_getFullyQualifiedTypeName(CXType CT) {
++  QualType T = GetQualType(CT);
++  if (T.isNull())
++    return cxstring::createEmpty();
++
++  // For builtin types (but not typedefs pointing to builtin types) return their
++  // spelling.  Otherwise "bool" will be turned into "_Bool".
++  const Type *TP = T.getTypePtrOrNull();
++  if (TP && TP->isBuiltinType() && T->getAs<TypedefType>() == nullptr)
++    return clang_getTypeSpelling(CT);
++
++  CXTranslationUnit TU = GetTU(CT);
++  ASTContext &Ctx = cxtu::getASTUnit(TU)->getASTContext();
++  PrintingPolicy Policy(Ctx.getPrintingPolicy());
++  Policy.SuppressScope = false;
++  Policy.AnonymousTagLocations = false;
++  Policy.PolishForDeclaration = true;
++  std::string name = TypeName::getFullyQualifiedName(T, Ctx, Policy, /*WithGlobalNsPrefix=*/true);
++  return cxstring::createDup(name.c_str());
++}
++
+ CXType clang_getTypedefDeclUnderlyingType(CXCursor C) {
+   using namespace cxcursor;
+   CXTranslationUnit TU = cxcursor::getCursorTU(C);
+diff -pur spack-src/clang/tools/libclang/libclang.map spack-src-new/clang/tools/libclang/libclang.map
+--- spack-src/clang/tools/libclang/libclang.map	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/tools/libclang/libclang.map	2025-02-03 18:36:14.531346336 +0100
+@@ -303,6 +303,7 @@ LLVM_13 {
+     clang_getFileName;
+     clang_getFileTime;
+     clang_getFileUniqueID;
++    clang_getFullyQualifiedTypeName;
+     clang_getFunctionTypeCallingConv;
+     clang_getIBOutletCollectionType;
+     clang_getIncludedFile;
diff --git a/packages/llvm/llvm13-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch b/packages/llvm/llvm13-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3771cfc6813d56e69d73add9473b6e3700b0ca2b
--- /dev/null
+++ b/packages/llvm/llvm13-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch
@@ -0,0 +1,238 @@
+diff -pur spack-src/clang/bindings/python/clang/cindex.py spack-src-new/clang/bindings/python/clang/cindex.py
+--- spack-src/clang/bindings/python/clang/cindex.py	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/bindings/python/clang/cindex.py	2025-02-03 18:37:54.447765317 +0100
+@@ -529,6 +529,13 @@ class TokenGroup(object):
+ 
+     You should not instantiate this class outside of this module.
+     """
++
++    # Default tokenization mode.
++    TOKENIZE_NONE = 0
++
++    # Used to indicate that tokens for whitespace should be returned.
++    TOKENIZE_KEEP_WHITESPACE = 1
++
+     def __init__(self, tu, memory, count):
+         self._tu = tu
+         self._memory = memory
+@@ -538,7 +545,7 @@ class TokenGroup(object):
+         conf.lib.clang_disposeTokens(self._tu, self._memory, self._count)
+ 
+     @staticmethod
+-    def get_tokens(tu, extent):
++    def get_tokens(tu, extent, options=0):
+         """Helper method to return all tokens in an extent.
+ 
+         This functionality is needed multiple places in this module. We define
+@@ -547,8 +554,8 @@ class TokenGroup(object):
+         tokens_memory = POINTER(Token)()
+         tokens_count = c_uint()
+ 
+-        conf.lib.clang_tokenize(tu, extent, byref(tokens_memory),
+-                byref(tokens_count))
++        conf.lib.clang_tokenizeRange(
++            tu, extent, byref(tokens_memory), byref(tokens_count), options)
+ 
+         count = int(tokens_count.value)
+ 
+@@ -1852,13 +1859,16 @@ class Cursor(Structure):
+             for descendant in child.walk_preorder():
+                 yield descendant
+ 
+-    def get_tokens(self):
++    def get_tokens(self, options=0):
+         """Obtain Token instances formulating that compose this Cursor.
+ 
+         This is a generator for Token instances. It returns all tokens which
+         occupy the extent this cursor occupies.
++
++        options is a bitwise or of TokenGroup.TOKENIZE_XXX flags which will
++        control tokenization behavior.
+         """
+-        return TokenGroup.get_tokens(self._tu, self.extent)
++        return TokenGroup.get_tokens(self._tu, self.extent, options)
+ 
+     def get_field_offsetof(self):
+         """Returns the offsetof the FIELD_DECL pointed by this Cursor."""
+@@ -3073,18 +3091,21 @@ class TranslationUnit(ClangObject):
+             return CodeCompletionResults(ptr)
+         return None
+ 
+-    def get_tokens(self, locations=None, extent=None):
++    def get_tokens(self, locations=None, extent=None, options=0):
+         """Obtain tokens in this translation unit.
+ 
+         This is a generator for Token instances. The caller specifies a range
+         of source code to obtain tokens for. The range can be specified as a
+         2-tuple of SourceLocation or as a SourceRange. If both are defined,
+         behavior is undefined.
++
++        options is a bitwise or of TokenGroup.TOKENIZE_XXX flags which will
++        control tokenization behavior.
+         """
+         if locations is not None:
+             extent = SourceRange(start=locations[0], end=locations[1])
+ 
+-        return TokenGroup.get_tokens(self, extent)
++        return TokenGroup.get_tokens(self, extent, options)
+ 
+ class File(ClangObject):
+     """
+@@ -3957,6 +3983,10 @@ functionList = [
+   ("clang_tokenize",
+    [TranslationUnit, SourceRange, POINTER(POINTER(Token)), POINTER(c_uint)]),
+ 
++  ("clang_tokenizeRange",
++   [TranslationUnit, SourceRange, POINTER(POINTER(Token)), POINTER(c_uint),
++    c_uint]),
++
+   ("clang_visitChildren",
+    [Cursor, callbacks['cursor_visit'], py_object],
+    c_uint),
+diff -pur spack-src/clang/bindings/python/tests/cindex/test_cursor.py spack-src-new/clang/bindings/python/tests/cindex/test_cursor.py
+--- spack-src/clang/bindings/python/tests/cindex/test_cursor.py	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/bindings/python/tests/cindex/test_cursor.py	2025-02-03 18:37:54.447765317 +0100
+@@ -10,6 +10,7 @@ import unittest
+ from clang.cindex import AvailabilityKind
+ from clang.cindex import CursorKind
+ from clang.cindex import TemplateArgumentKind
++from clang.cindex import TokenGroup
+ from clang.cindex import TranslationUnit
+ from clang.cindex import TypeKind
+ from .util import get_cursor
+@@ -480,6 +489,14 @@ class TestCursor(unittest.TestCase):
+         self.assertEqual(tokens[0].spelling, 'int')
+         self.assertEqual(tokens[1].spelling, 'foo')
+ 
++    def test_get_tokens_with_whitespace():
++        source = 'class C { void f(); }\nvoid C::f() { }'
++        tu = get_tu(source)
++
++        tokens = list(tu.cursor.get_tokens(TokenGroup.TOKENIZE_KEEP_WHITESPACE))
++        self.assertEqual(''.join(t.spelling for t in tokens), source)
++        self.assertEqual(len(tokens), 27, [t.spelling for t in tokens])
++
+     def test_get_token_cursor(self):
+         """Ensure we can map tokens to cursors."""
+         tu = get_tu('class A {}; int foo(A var = A());', lang='cpp')
+diff -pur spack-src/clang/include/clang-c/Index.h spack-src-new/clang/include/clang-c/Index.h
+--- spack-src/clang/include/clang-c/Index.h	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/include/clang-c/Index.h	2025-02-03 18:38:17.919863604 +0100
+@@ -33,7 +33,7 @@
+  * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
+  */
+ #define CINDEX_VERSION_MAJOR 0
+-#define CINDEX_VERSION_MINOR 63
++#define CINDEX_VERSION_MINOR 64
+ 
+ #define CINDEX_VERSION_ENCODE(major, minor) (((major)*10000) + ((minor)*1))
+ 
+@@ -5036,6 +5044,28 @@ CINDEX_LINKAGE CXSourceLocation clang_ge
+  */
+ CINDEX_LINKAGE CXSourceRange clang_getTokenExtent(CXTranslationUnit, CXToken);
+ 
++typedef enum {
++  /**
++   * \brief Used to indicate that no special tokenization options are needed.
++   */
++  CXTokenize_None = 0x0,
++
++  /**
++   * \brief Used to indicate that tokens for whitespace should be returned.
++   */
++  CXTokenize_KeepWhitespace = 0x1
++} CXTokenize_Flags;
++
++/**
++ * \brief Tokenize the source code described by the given range into raw
++ * lexical tokens.
++ *
++ * \see clang_tokenizeRange
++ *
++ */
++CINDEX_LINKAGE void clang_tokenize(CXTranslationUnit TU, CXSourceRange Range,
++                                   CXToken **Tokens, unsigned *NumTokens);
++
+ /**
+  * Tokenize the source code described by the given range into raw
+  * lexical tokens.
+@@ -5052,9 +5082,13 @@ CINDEX_LINKAGE CXSourceRange clang_getTo
+  * \param NumTokens will be set to the number of tokens in the \c *Tokens
+  * array.
+  *
++ * \param options A bitmask of options that affects tokenization. This should be
++ * a bitwise OR of the CXTokenize_XXX flags.
++ *
+  */
+-CINDEX_LINKAGE void clang_tokenize(CXTranslationUnit TU, CXSourceRange Range,
+-                                   CXToken **Tokens, unsigned *NumTokens);
++CINDEX_LINKAGE void clang_tokenizeRange(CXTranslationUnit TU,
++                                        CXSourceRange Range, CXToken **Tokens,
++                                        unsigned *NumTokens, unsigned options);
+ 
+ /**
+  * Annotate the given set of tokens by providing cursors for each token
+diff -pur spack-src/clang/tools/libclang/CIndex.cpp spack-src-new/clang/tools/libclang/CIndex.cpp
+--- spack-src/clang/tools/libclang/CIndex.cpp	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/tools/libclang/CIndex.cpp	2025-02-03 18:37:55.855771214 +0100
+@@ -6882,7 +6882,7 @@ CXSourceRange clang_getTokenExtent(CXTra
+ }
+ 
+ static void getTokens(ASTUnit *CXXUnit, SourceRange Range,
+-                      SmallVectorImpl<CXToken> &CXTokens) {
++                      SmallVectorImpl<CXToken> &CXTokens, unsigned options) {
+   SourceManager &SourceMgr = CXXUnit->getSourceManager();
+   std::pair<FileID, unsigned> BeginLocInfo =
+       SourceMgr.getDecomposedSpellingLoc(Range.getBegin());
+@@ -6903,6 +6903,9 @@ static void getTokens(ASTUnit *CXXUnit,
+             CXXUnit->getASTContext().getLangOpts(), Buffer.begin(),
+             Buffer.data() + BeginLocInfo.second, Buffer.end());
+   Lex.SetCommentRetentionState(true);
++  if (options & CXTokenize_KeepWhitespace) {
++    Lex.SetKeepWhitespaceMode(true);
++  }
+ 
+   // Lex tokens until we hit the end of the range.
+   const char *EffectiveBufferEnd = Buffer.data() + EndLocInfo.second;
+@@ -6973,7 +6976,7 @@ CXToken *clang_getToken(CXTranslationUni
+       SM.getComposedLoc(DecomposedEnd.first, DecomposedEnd.second);
+ 
+   SmallVector<CXToken, 32> CXTokens;
+-  getTokens(CXXUnit, SourceRange(Begin, End), CXTokens);
++  getTokens(CXXUnit, SourceRange(Begin, End), CXTokens, CXTokenize_None);
+ 
+   if (CXTokens.empty())
+     return NULL;
+@@ -6987,6 +6990,12 @@ CXToken *clang_getToken(CXTranslationUni
+ 
+ void clang_tokenize(CXTranslationUnit TU, CXSourceRange Range, CXToken **Tokens,
+                     unsigned *NumTokens) {
++  return clang_tokenizeRange(TU, Range, Tokens, NumTokens, CXTokenize_None);
++}
++
++void clang_tokenizeRange(CXTranslationUnit TU, CXSourceRange Range,
++                         CXToken **Tokens, unsigned *NumTokens,
++                         unsigned options) {
+   LOG_FUNC_SECTION { *Log << TU << ' ' << Range; }
+ 
+   if (Tokens)
+@@ -7010,7 +7019,7 @@ void clang_tokenize(CXTranslationUnit TU
+     return;
+ 
+   SmallVector<CXToken, 32> CXTokens;
+-  getTokens(CXXUnit, R, CXTokens);
++  getTokens(CXXUnit, R, CXTokens, options);
+ 
+   if (CXTokens.empty())
+     return;
+diff -pur spack-src/clang/tools/libclang/libclang.map spack-src-new/clang/tools/libclang/libclang.map
+--- spack-src/clang/tools/libclang/libclang.map	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/tools/libclang/libclang.map	2025-02-03 18:38:37.647946177 +0100
+@@ -398,6 +399,7 @@ LLVM_13 {
+     clang_suspendTranslationUnit;
+     clang_toggleCrashRecovery;
+     clang_tokenize;
++    clang_tokenizeRange;
+     clang_uninstall_llvm_fatal_error_handler;
+     clang_visitChildren;
+     clang_visitChildrenWithBlock;
diff --git a/packages/llvm/llvm13-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch b/packages/llvm/llvm13-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c9f997dc8a6bb852dcf8d73ee2516f007beba7e2
--- /dev/null
+++ b/packages/llvm/llvm13-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch
@@ -0,0 +1,504 @@
+diff -pur spack-src/clang/bindings/python/clang/cindex.py spack-src-new/clang/bindings/python/clang/cindex.py
+--- spack-src/clang/bindings/python/clang/cindex.py	2025-02-03 18:51:43.871219854 +0100
++++ spack-src-new/clang/bindings/python/clang/cindex.py	2025-02-03 18:39:41.616213701 +0100
+@@ -1426,6 +1426,15 @@ class Cursor(Structure):
+     """
+     _fields_ = [("_kind_id", c_int), ("xdata", c_int), ("data", c_void_p * 3)]
+ 
++    # Default behavior.
++    GET_CHILDREN_NONE = 0
++
++    # Used to indicate that implicit cursors should be visited.
++    GET_CHILDREN_WITH_IMPLICIT = 1
++
++    # Used to indicate that template instantiations should be visited.
++    GET_CHILDREN_WITH_TEMPLATE_INSTANTIATIONS = 2
++
+     @staticmethod
+     def from_location(tu, location):
+         # We store a reference to the TU in the instance so the TU won't get
+@@ -1515,6 +1524,10 @@ class Cursor(Structure):
+         """
+         return conf.lib.clang_EnumDecl_isScoped(self)
+ 
++    def is_implicit(self):
++        """Test whether the cursor refers to an implicit declaration."""
++        return conf.lib.clang_isImplicit(self)
++
+     def get_definition(self):
+         """
+         If the cursor is a reference to a declaration or a declaration of
+@@ -1831,8 +1844,12 @@ class Cursor(Structure):
+         """Returns the value of the indicated arg as an unsigned 64b integer."""
+         return conf.lib.clang_Cursor_getTemplateArgumentUnsignedValue(self, num)
+ 
+-    def get_children(self):
+-        """Return an iterator for accessing the children of this cursor."""
++    def get_children(self, with_implicit=False, with_template_instantiations=False):
++        """Return an iterator for accessing the children of this cursor.
++
++        By default, cursors representing implicit declarations or template instantiations
++        will be skipped.
++        """
+ 
+         # FIXME: Expose iteration from CIndex, PR6125.
+         def visitor(child, parent, children):
+@@ -1845,18 +1862,24 @@ class Cursor(Structure):
+             children.append(child)
+             return 1 # continue
+         children = []
+-        conf.lib.clang_visitChildren(self, callbacks['cursor_visit'](visitor),
+-            children)
++        dispatch = conf.lib.clang_visitChildren
++        options = Cursor.GET_CHILDREN_NONE
++        if with_implicit:
++            options |= Cursor.GET_CHILDREN_WITH_IMPLICIT
++        if with_template_instantiations:
++            options |= Cursor.GET_CHILDREN_WITH_TEMPLATE_INSTANTIATIONS
++        conf.lib.clang_visitChildrenWithOptions(
++            self, callbacks['cursor_visit'](visitor), children, options)
+         return iter(children)
+ 
+-    def walk_preorder(self):
++    def walk_preorder(self, **kwargs):
+         """Depth-first preorder walk over the cursor and its descendants.
+ 
+         Yields cursors.
+         """
+         yield self
+-        for child in self.get_children():
+-            for descendant in child.walk_preorder():
++        for child in self.get_children(**kwargs):
++            for descendant in child.walk_preorder(**kwargs):
+                 yield descendant
+ 
+     def get_tokens(self, options=0):
+@@ -3928,6 +3951,10 @@ functionList = [
+    [Type],
+    bool),
+ 
++  ("clang_isImplicit",
++   [Cursor],
++   bool),
++
+   ("clang_isInvalid",
+    [CursorKind],
+    bool),
+@@ -3991,6 +4018,10 @@ functionList = [
+    [Cursor, callbacks['cursor_visit'], py_object],
+    c_uint),
+ 
++  ("clang_visitChildrenWithOptions",
++   [Cursor, callbacks['cursor_visit'], py_object, c_uint],
++   c_uint),
++
+   ("clang_Cursor_getNumArguments",
+    [Cursor],
+    c_int),
+diff -pur spack-src/clang/bindings/python/clang/cindex.py.orig spack-src-new/clang/bindings/python/clang/cindex.py.orig
+--- spack-src/clang/bindings/python/clang/cindex.py.orig	2025-02-03 18:51:19.639119257 +0100
++++ spack-src-new/clang/bindings/python/clang/cindex.py.orig	2025-02-03 18:37:54.447765317 +0100
+@@ -529,6 +529,13 @@ class TokenGroup(object):
+ 
+     You should not instantiate this class outside of this module.
+     """
++
++    # Default tokenization mode.
++    TOKENIZE_NONE = 0
++
++    # Used to indicate that tokens for whitespace should be returned.
++    TOKENIZE_KEEP_WHITESPACE = 1
++
+     def __init__(self, tu, memory, count):
+         self._tu = tu
+         self._memory = memory
+@@ -538,7 +545,7 @@ class TokenGroup(object):
+         conf.lib.clang_disposeTokens(self._tu, self._memory, self._count)
+ 
+     @staticmethod
+-    def get_tokens(tu, extent):
++    def get_tokens(tu, extent, options=0):
+         """Helper method to return all tokens in an extent.
+ 
+         This functionality is needed multiple places in this module. We define
+@@ -547,8 +554,8 @@ class TokenGroup(object):
+         tokens_memory = POINTER(Token)()
+         tokens_count = c_uint()
+ 
+-        conf.lib.clang_tokenize(tu, extent, byref(tokens_memory),
+-                byref(tokens_count))
++        conf.lib.clang_tokenizeRange(
++            tu, extent, byref(tokens_memory), byref(tokens_count), options)
+ 
+         count = int(tokens_count.value)
+ 
+@@ -1852,13 +1859,16 @@ class Cursor(Structure):
+             for descendant in child.walk_preorder():
+                 yield descendant
+ 
+-    def get_tokens(self):
++    def get_tokens(self, options=0):
+         """Obtain Token instances formulating that compose this Cursor.
+ 
+         This is a generator for Token instances. It returns all tokens which
+         occupy the extent this cursor occupies.
++
++        options is a bitwise or of TokenGroup.TOKENIZE_XXX flags which will
++        control tokenization behavior.
+         """
+-        return TokenGroup.get_tokens(self._tu, self.extent)
++        return TokenGroup.get_tokens(self._tu, self.extent, options)
+ 
+     def get_field_offsetof(self):
+         """Returns the offsetof the FIELD_DECL pointed by this Cursor."""
+@@ -3081,18 +3091,21 @@ class TranslationUnit(ClangObject):
+             return CodeCompletionResults(ptr)
+         return None
+ 
+-    def get_tokens(self, locations=None, extent=None):
++    def get_tokens(self, locations=None, extent=None, options=0):
+         """Obtain tokens in this translation unit.
+ 
+         This is a generator for Token instances. The caller specifies a range
+         of source code to obtain tokens for. The range can be specified as a
+         2-tuple of SourceLocation or as a SourceRange. If both are defined,
+         behavior is undefined.
++
++        options is a bitwise or of TokenGroup.TOKENIZE_XXX flags which will
++        control tokenization behavior.
+         """
+         if locations is not None:
+             extent = SourceRange(start=locations[0], end=locations[1])
+ 
+-        return TokenGroup.get_tokens(self, extent)
++        return TokenGroup.get_tokens(self, extent, options)
+ 
+ class File(ClangObject):
+     """
+@@ -3970,6 +3983,10 @@ functionList = [
+   ("clang_tokenize",
+    [TranslationUnit, SourceRange, POINTER(POINTER(Token)), POINTER(c_uint)]),
+ 
++  ("clang_tokenizeRange",
++   [TranslationUnit, SourceRange, POINTER(POINTER(Token)), POINTER(c_uint),
++    c_uint]),
++
+   ("clang_visitChildren",
+    [Cursor, callbacks['cursor_visit'], py_object],
+    c_uint),
+diff -pur spack-src/clang/bindings/python/tests/cindex/test_cursor.py spack-src-new/clang/bindings/python/tests/cindex/test_cursor.py
+--- spack-src/clang/bindings/python/tests/cindex/test_cursor.py	2025-02-03 18:51:43.871219854 +0100
++++ spack-src-new/clang/bindings/python/tests/cindex/test_cursor.py	2025-02-03 18:39:41.616213701 +0100
+@@ -94,6 +94,39 @@ class TestCursor(unittest.TestCase):
+         self.assertEqual(tu_nodes[2].displayname, 'f0(int, int)')
+         self.assertEqual(tu_nodes[2].is_definition(), True)
+ 
++    def test_get_children_with_implicit():
++        tu = get_tu('struct X {}; X x;', lang='cpp')
++        cursor = get_cursor(tu, 'X')
++
++        children = list(cursor.get_children())
++        self.assertEqual(len(children), 0, [(c.kind, c.spelling) for c in children])
++
++        children = list(cursor.get_children(with_implicit=True))
++        self.assertNotEqual(len(children), 0)
++        for child in children:
++            self.assertTrue(child.is_implicit())
++            self.assertEqual(child.spelling, "X")
++            self.assertIn(child.kind, [CursorKind.CONSTRUCTOR, CursorKind.STRUCT_DECL])
++
++    def test_get_children_with_template_instantiations():
++        tu = get_tu(
++            'template <typename T> T frobnicate(T val);'
++            'extern template int frobnicate<int>(int);',
++            lang='cpp')
++        cursor = get_cursor(tu, 'frobnicate')
++        self.assertEqual(cursor.kind, CursorKind.FUNCTION_TEMPLATE)
++
++        for child in cursor.get_children():
++            # should not return an instantiation:
++            self.assertNotEqual(child.kind, CursorKind.FUNCTION_DECL)
++
++        for child in cursor.get_children(with_template_instantiations=True):
++            if child.kind == CursorKind.FUNCTION_DECL:
++                self.assertEqual(child.spelling, 'frobnicate')
++                break
++        else:
++            self.fail("Couldn't find template instantiation")
++
+     def test_references(self):
+         """Ensure that references to TranslationUnit are kept."""
+         tu = get_tu('int x;')
+diff -pur spack-src/clang/include/clang-c/Index.h spack-src-new/clang/include/clang-c/Index.h
+--- spack-src/clang/include/clang-c/Index.h	2025-02-03 18:51:43.871219854 +0100
++++ spack-src-new/clang/include/clang-c/Index.h	2025-02-03 18:40:48.276492163 +0100
+@@ -33,7 +33,7 @@
+  * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
+  */
+ #define CINDEX_VERSION_MAJOR 0
+-#define CINDEX_VERSION_MINOR 64
++#define CINDEX_VERSION_MINOR 65
+ 
+ #define CINDEX_VERSION_ENCODE(major, minor) (((major)*10000) + ((minor)*1))
+ 
+@@ -2817,6 +2817,11 @@ CINDEX_LINKAGE unsigned clang_isPreproce
+  */
+ CINDEX_LINKAGE unsigned clang_isUnexposed(enum CXCursorKind);
+ 
++/***
++ * \brief Determine whether the given cursor represents an implicit declaration.
++ */
++CINDEX_LINKAGE unsigned clang_isImplicit(CXCursor);
++
+ /**
+  * Describe the linkage of the entity referred to by a cursor.
+  */
+@@ -4274,6 +4279,32 @@ clang_visitChildrenWithBlock(CXCursor pa
+ #endif
+ #endif
+ 
++typedef enum {
++  /**
++   * \brief Default behavior.
++   */
++  CXVisitChildren_None = 0x0,
++
++  /**
++   * \brief Used to indicate that implicit cursors should be visited.
++   */
++  CXVisitChildren_WithImplicit = 0x1,
++
++  /**
++   * \brief Used to indicate that template instantiations should be visited.
++   */
++  CXVisitChildren_WithTemplateInstantiations = 0x2
++} CXVisitChildren_Flags;
++
++/**
++ * \brief Visits the children of a cursor, allowing to pass extra options.
++ * Behaves identically to clang_visitChildren() in all other respects.
++ */
++CINDEX_LINKAGE unsigned clang_visitChildrenWithOptions(CXCursor parent,
++                                                       CXCursorVisitor visitor,
++                                                       CXClientData client_data,
++                                                       unsigned options);
++
+ /**
+  * @}
+  */
+diff -pur spack-src/clang/tools/libclang/CIndex.cpp spack-src-new/clang/tools/libclang/CIndex.cpp
+--- spack-src/clang/tools/libclang/CIndex.cpp	2025-02-03 18:51:43.871219854 +0100
++++ spack-src-new/clang/tools/libclang/CIndex.cpp	2025-02-03 18:39:43.000219486 +0100
+@@ -203,9 +203,10 @@ bool CursorVisitor::Visit(CXCursor Curso
+       return true; // abort.
+     }
+ 
+-    // Ignore implicit declarations, unless it's an objc method because
+-    // currently we should report implicit methods for properties when indexing.
+-    if (D->isImplicit() && !isa<ObjCMethodDecl>(D))
++    // Unless instructed otherwise we ignore implicit declarations.
++    // ObjC methods are currently visited in any case, because implicit methods
++    // for properties should be reported when indexing.
++    if (!VisitImplicitDeclarations && D->isImplicit() && !isa<ObjCMethodDecl>(D))
+       return false;
+   }
+ 
+@@ -713,10 +714,13 @@ bool CursorVisitor::VisitTagDecl(TagDecl
+ 
+ bool CursorVisitor::VisitClassTemplateSpecializationDecl(
+     ClassTemplateSpecializationDecl *D) {
+-  bool ShouldVisitBody = false;
++  bool ShouldVisitBody = VisitTemplateInstantiations;
+   switch (D->getSpecializationKind()) {
+-  case TSK_Undeclared:
+   case TSK_ImplicitInstantiation:
++    if (VisitTemplateInstantiations && VisitImplicitDeclarations) {
++      break;
++    }
++  case TSK_Undeclared:
+     // Nothing to visit
+     return false;
+ 
+@@ -725,6 +729,7 @@ bool CursorVisitor::VisitClassTemplateSp
+     break;
+ 
+   case TSK_ExplicitSpecialization:
++    // Always visit body of explicit specializations
+     ShouldVisitBody = true;
+     break;
+   }
+@@ -945,7 +950,31 @@ bool CursorVisitor::VisitFunctionTemplat
+     return true;
+ 
+   auto *FD = D->getTemplatedDecl();
+-  return VisitAttributes(FD) || VisitFunctionDecl(FD);
++  if (VisitAttributes(FD) || VisitFunctionDecl(FD))
++    return true;
++
++  if (VisitTemplateInstantiations && D == D->getCanonicalDecl()) {
++    for (auto *FD : D->specializations()) {
++      for (auto *RD : FD->redecls()) {
++        switch (RD->getTemplateSpecializationKind()) {
++        case TSK_Undeclared:
++        case TSK_ImplicitInstantiation:
++        case TSK_ExplicitInstantiationDeclaration:
++        case TSK_ExplicitInstantiationDefinition: {
++          const Optional<bool> V = handleDeclForVisitation(RD);
++          if (!V.hasValue())
++            continue;
++          return V.getValue();
++        }
++
++        case TSK_ExplicitSpecialization:
++          break;
++        }
++      }
++    }
++  }
++
++  return false;
+ }
+ 
+ bool CursorVisitor::VisitClassTemplateDecl(ClassTemplateDecl *D) {
+@@ -956,6 +985,40 @@ bool CursorVisitor::VisitClassTemplateDe
+ 
+   auto *CD = D->getTemplatedDecl();
+   return VisitAttributes(CD) || VisitCXXRecordDecl(CD);
++  if (VisitAttributes(CD) || VisitCXXRecordDecl(CD))
++    return true;
++
++  if (VisitTemplateInstantiations && D == D->getCanonicalDecl()) {
++    for (auto *SD : D->specializations()) {
++      for (auto *RD : SD->redecls()) {
++        // We don't want to visit injected-class-names in this traversal.
++        if (cast<CXXRecordDecl>(RD)->isInjectedClassName())
++          continue;
++
++        switch (
++            cast<ClassTemplateSpecializationDecl>(RD)->getSpecializationKind()) {
++          // Visit the implicit instantiations with the requested pattern.
++        case TSK_Undeclared:
++        case TSK_ImplicitInstantiation: {
++          const Optional<bool> V = handleDeclForVisitation(RD);
++          if (!V.hasValue())
++            continue;
++          return V.getValue();
++        }
++
++          // We don't need to do anything on an explicit instantiation
++          // or explicit specialization because there will be an explicit
++          // node for it elsewhere.
++        case TSK_ExplicitInstantiationDeclaration:
++        case TSK_ExplicitInstantiationDefinition:
++        case TSK_ExplicitSpecialization:
++          break;
++        }
++      }
++    }
++  }
++
++  return false;
+ }
+ 
+ bool CursorVisitor::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
+@@ -4596,6 +4659,24 @@ unsigned clang_visitChildrenWithBlock(CX
+   return clang_visitChildren(parent, visitWithBlock, block);
+ }
+ 
++unsigned clang_visitChildrenWithOptions(CXCursor parent,
++                                        CXCursorVisitor visitor,
++                                        CXClientData client_data,
++                                        unsigned options) {
++  CursorVisitor CursorVis(
++      getCursorTU(parent), visitor, client_data,
++      /*VisitPreprocessorLast=*/false,
++      /*VisitIncludedPreprocessingEntries=*/false,
++      /*RegionOfInterest=*/SourceRange(),
++      /*VisitDeclsOnly=*/false,
++      /*PostChildrenVisitor=*/nullptr,
++      /*VisitImplicitDeclarations=*/(options & CXVisitChildren_WithImplicit),
++      /*VisitTemplateInstantiations=*/
++      (options & CXVisitChildren_WithTemplateInstantiations));
++
++  return CursorVis.VisitChildren(parent);
++}
++
+ static CXString getDeclSpelling(const Decl *D) {
+   if (!D)
+     return cxstring::createEmpty();
+@@ -5983,6 +6064,22 @@ unsigned clang_isUnexposed(enum CXCursor
+   }
+ }
+ 
++unsigned clang_isImplicit(CXCursor Cursor) {
++  if (clang_isInvalid(Cursor.kind))
++    return false;
++
++  if (!clang_isDeclaration(Cursor.kind))
++    return false;
++
++  const Decl *D = getCursorDecl(Cursor);
++  if (!D) {
++    assert(0 && "Invalid declaration cursor");
++    return true; // abort.
++  }
++
++  return D->isImplicit();
++}
++
+ CXCursorKind clang_getCursorKind(CXCursor C) { return C.kind; }
+ 
+ CXSourceLocation clang_getCursorLocation(CXCursor C) {
+diff -pur spack-src/clang/tools/libclang/CursorVisitor.h spack-src-new/clang/tools/libclang/CursorVisitor.h
+--- spack-src/clang/tools/libclang/CursorVisitor.h	2022-01-20 22:31:59.000000000 +0100
++++ spack-src-new/clang/tools/libclang/CursorVisitor.h	2025-02-03 18:39:43.000219486 +0100
+@@ -104,6 +104,12 @@ private:
+   /// record entries.
+   bool VisitDeclsOnly;
+ 
++  /// \brief Whether we should visit implicit declarations.
++  bool VisitImplicitDeclarations;
++
++  /// \brief Whether we should recurse into template instantiations.
++  bool VisitTemplateInstantiations;
++
+   // FIXME: Eventually remove.  This part of a hack to support proper
+   // iteration over all Decls contained lexically within an ObjC container.
+   DeclContext::decl_iterator *DI_current;
+@@ -152,12 +158,16 @@ public:
+                 bool VisitIncludedPreprocessingEntries = false,
+                 SourceRange RegionOfInterest = SourceRange(),
+                 bool VisitDeclsOnly = false,
+-                PostChildrenVisitorTy PostChildrenVisitor = nullptr)
++                PostChildrenVisitorTy PostChildrenVisitor = nullptr,
++                bool VisitImplicitDeclarations = false,
++                bool VisitTemplateInstantiations = false)
+       : TU(TU), AU(cxtu::getASTUnit(TU)), Visitor(Visitor),
+         PostChildrenVisitor(PostChildrenVisitor), ClientData(ClientData),
+         VisitPreprocessorLast(VisitPreprocessorLast),
+         VisitIncludedEntities(VisitIncludedPreprocessingEntries),
+         RegionOfInterest(RegionOfInterest), VisitDeclsOnly(VisitDeclsOnly),
++        VisitImplicitDeclarations(VisitImplicitDeclarations),
++        VisitTemplateInstantiations(VisitTemplateInstantiations),
+         DI_current(nullptr), FileDI_current(nullptr) {
+     Parent.kind = CXCursor_NoDeclFound;
+     Parent.data[0] = nullptr;
+diff -pur spack-src/clang/tools/libclang/libclang.map spack-src-new/clang/tools/libclang/libclang.map
+--- spack-src/clang/tools/libclang/libclang.map	2025-02-03 18:51:43.871219854 +0100
++++ spack-src-new/clang/tools/libclang/libclang.map	2025-02-03 18:40:21.600380765 +0100
+@@ -375,6 +375,7 @@ LLVM_13 {
+     clang_isExpression;
+     clang_isFileMultipleIncludeGuarded;
+     clang_isFunctionTypeVariadic;
++    clang_isImplicit;
+     clang_isInvalid;
+     clang_isInvalidDeclaration;
+     clang_isPODType;
+@@ -403,6 +404,7 @@ LLVM_13 {
+     clang_uninstall_llvm_fatal_error_handler;
+     clang_visitChildren;
+     clang_visitChildrenWithBlock;
++    clang_visitChildrenWithOptions;
+ 
+   local: *;
+ };
diff --git a/packages/llvm/package.py b/packages/llvm/package.py
index 3be46cd0ce0a9d3d737525aa5c75e78f1421ac1b..80e72b37881624956c8d5fa8e5d129e6510982d6 100644
--- a/packages/llvm/package.py
+++ b/packages/llvm/package.py
@@ -395,20 +395,27 @@ class Llvm(CMakePackage, CudaPackage):
             "bindings generator")
 
     conflicts("@:8", when="+visionary")
-    conflicts("@13:", when="+visionary")
+    conflicts("@14:", when="+visionary")
 
-    patch('llvm9-0001-Tooling-Fully-qualify-template-parameters-of-nested-.patch', when='@9.0.0:12.0.999 +visionary', level=2)
+    patch('llvm9-0001-Tooling-Fully-qualify-template-parameters-of-nested-.patch', when='@9:12 +visionary', level=2)
 
     # 0001-Tooling-Fully-qualify-template-parameters-of-nested-.patch from above
-    patch('llvm11-0002-libclang-Add-support-for-obtaining-fully-qualified-n.patch', when='@11.0.0:11.0.999 +visionary', level=2)
-    patch('llvm11-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch', when='@11.0.0:11.0.999 +visionary', level=2)
-    patch('llvm11-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch', when='@11.0.0:11.0.999 +visionary', level=2)
-    patch('llvm11-0005-libclang-WIP-Fix-get_tokens-in-macro-expansion.patch',       when='@11.0.0:12.0.999 +visionary', level=2)
+    patch('llvm11-0002-libclang-Add-support-for-obtaining-fully-qualified-n.patch', when='@11  +visionary', level=2)
+    patch('llvm11-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch', when='@11  +visionary', level=2)
+    patch('llvm11-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch', when='@11  +visionary', level=2)
+    patch('llvm11-0005-libclang-WIP-Fix-get_tokens-in-macro-expansion.patch',       when='@11: +visionary', level=2)
 
     # 0001-Tooling-Fully-qualify-template-parameters-of-nested-.patch from above
-    patch('llvm11_1-0002-libclang-Add-support-for-obtaining-fully-qualified-n.patch', when='@11.1.0:12.0.999 +visionary', level=2)
-    patch('llvm11_1-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch', when='@11.1.0:12.0.999 +visionary', level=2)
-    patch('llvm11_1-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch', when='@11.1.0:12.0.999 +visionary', level=2)
+    patch('llvm11_1-0002-libclang-Add-support-for-obtaining-fully-qualified-n.patch', when='@11.1:12.0 +visionary', level=2)
+    patch('llvm11_1-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch', when='@11.1:12.0 +visionary', level=2)
+    patch('llvm11_1-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch', when='@11.1:12.0 +visionary', level=2)
+    # 0005-libclang-WIP-Fix-get_tokens-in-macro-expansion.patch from above
+
+    patch('llvm13-0001-Tooling-Fully-qualify-template-parameters-of-nested-.patch', when='@13: +visionary', level=1)
+    patch('llvm13-0002-libclang-Add-support-for-obtaining-fully-qualified-n.patch', when='@13:15 +visionary', level=1)
+    patch('llvm13-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch', when='@13  +visionary', level=1)
+    patch('llvm13-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch', when='@13  +visionary', level=1)
+    # 0005-libclang-WIP-Fix-get_tokens-in-macro-expansion.patch from above
     # 0005-libclang-WIP-Fix-get_tokens-in-macro-expansion.patch from above
 
     # disable check for member `mode` size in `struct ipc_perm`; newer glibc changed width