From 3b42c794901e4c10c15eafcf2dae30ab021e76c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Eric=20M=C3=BCller?= <mueller@kip.uni-heidelberg.de>
Date: Mon, 3 Feb 2025 19:09:15 +0100
Subject: [PATCH] fix(llvm+visionary): port patches to llvm@14:15

---
 ...ion-to-keep-whitespace-when-tokenizi.patch | 238 ++++++++++
 ...ow-visiting-of-implicit-declarations.patch | 416 ++++++++++++++++++
 ...blic-ClangToolingCommonOptionsParser.patch |  12 +
 packages/llvm/package.py                      |   8 +-
 4 files changed, 673 insertions(+), 1 deletion(-)
 create mode 100644 packages/llvm/llvm14-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch
 create mode 100644 packages/llvm/llvm14-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch
 create mode 100644 packages/llvm/llvm14-public-ClangToolingCommonOptionsParser.patch

diff --git a/packages/llvm/llvm14-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch b/packages/llvm/llvm14-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch
new file mode 100644
index 00000000..88250852
--- /dev/null
+++ b/packages/llvm/llvm14-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)
+ 
+@@ -1875,13 +1882,16 @@ class Cursor(Structure):
+             for descendant in child.walk_preorder(**kwargs):
+                 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."""
+@@ -3105,18 +3115,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/llvm14-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch b/packages/llvm/llvm14-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch
new file mode 100644
index 00000000..1380b699
--- /dev/null
+++ b/packages/llvm/llvm14-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch
@@ -0,0 +1,416 @@
+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/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,10 +203,11 @@ 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))
+-      return false;
++    // 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;
+   }
+ 
+   // If we have a range of interest, and this cursor doesn't intersect with it,
+@@ -713,10 +715,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 +730,7 @@ bool CursorVisitor::VisitClassTemplateSp
+     break;
+ 
+   case TSK_ExplicitSpecialization:
++    // Always visit body of explicit specializations
+     ShouldVisitBody = true;
+     break;
+   }
+@@ -945,7 +951,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) {
+@@ -955,7 +985,40 @@ bool CursorVisitor::VisitClassTemplateDe
+     return true;
+ 
+   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/llvm14-public-ClangToolingCommonOptionsParser.patch b/packages/llvm/llvm14-public-ClangToolingCommonOptionsParser.patch
new file mode 100644
index 00000000..7641f8a3
--- /dev/null
+++ b/packages/llvm/llvm14-public-ClangToolingCommonOptionsParser.patch
@@ -0,0 +1,12 @@
+diff -pur spack-src/clang/include/clang/Tooling/CommonOptionsParser.h spack-src-new/clang/include/clang/Tooling/CommonOptionsParser.h
+--- spack-src/clang/include/clang/Tooling/CommonOptionsParser.h	2025-02-17 12:35:27.296733912 +0100
++++ spack-src-new/clang/include/clang/Tooling/CommonOptionsParser.h	2025-02-17 12:35:59.393070972 +0100
+@@ -64,7 +64,7 @@ namespace tooling {
+ /// \endcode
+ class CommonOptionsParser {
+ 
+-protected:
++public:
+   /// Parses command-line, initializes a compilation database.
+   ///
+   /// This constructor can change argc and argv contents, e.g. consume
diff --git a/packages/llvm/package.py b/packages/llvm/package.py
index 80e72b37..1af09da1 100644
--- a/packages/llvm/package.py
+++ b/packages/llvm/package.py
@@ -395,7 +395,7 @@ class Llvm(CMakePackage, CudaPackage):
             "bindings generator")
 
     conflicts("@:8", when="+visionary")
-    conflicts("@14:", when="+visionary")
+    conflicts("@16:", when="+visionary")
 
     patch('llvm9-0001-Tooling-Fully-qualify-template-parameters-of-nested-.patch', when='@9:12 +visionary', level=2)
 
@@ -416,7 +416,13 @@ class Llvm(CMakePackage, CudaPackage):
     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
+
+    # 0001-Tooling-Fully-qualify-template-parameters-of-nested-.patch from above
+    # 0002-libclang-Add-support-for-obtaining-fully-qualified-n.patch from above
+    patch('llvm14-0003-libclang-Add-option-to-keep-whitespace-when-tokenizi.patch', when='@14:15 +visionary', level=1)
+    patch('llvm14-0004-libclang-WIP-Allow-visiting-of-implicit-declarations.patch', when='@14:15 +visionary', level=1)
     # 0005-libclang-WIP-Fix-get_tokens-in-macro-expansion.patch from above
+    patch('llvm14-public-ClangToolingCommonOptionsParser.patch', when='@14: +visionary', level=1)
 
     # disable check for member `mode` size in `struct ipc_perm`; newer glibc changed width
     patch('llvm9-disable-check-for-ipc_perm-mode.patch', when='@9.0.0:9.0.999', level=2)
-- 
GitLab