https://github.com/guillem-bartrina-sonarsource updated 
https://github.com/llvm/llvm-project/pull/214008

>From 315175d6c1f863b90e6983f238f71c32e8a67432 Mon Sep 17 00:00:00 2001
From: guillem-bartrina-sonarsource <[email protected]>
Date: Wed, 29 Jul 2026 15:22:39 +0200
Subject: [PATCH 1/9] [ASTImporter] Invalidate ImportedTypes cache on Decl
 import failure

A TagDecl's type can get cached as successfully imported in
ASTImporter::ImportedTypes before the Decl's own import fails, if a
member referencing the type (e.g. an implicit copy constructor) is
imported first. That stale entry was never invalidated, so later
references to the same type -- directly, or via structural-equivalence
comparisons on lambda closures -- could silently resolve to a half-built
Decl and crash instead of failing cleanly.

Add a unit test and a CTU regression test reproducing the crash.
---
 clang/lib/AST/ASTImporter.cpp                 |   6 +
 .../regression/lambda-import-corruption.cpp   | 114 ++++++++++++++++++
 clang/unittests/AST/ASTImporterTest.cpp       |  36 ++++++
 3 files changed, 156 insertions(+)
 create mode 100644 
clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp

diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 3ad71a223903c..0b85636a06598 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -10038,6 +10038,12 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
       auto *ToD = CreatedToD;
       ImportedDecls.erase(Pos);
 
+      // Also scrub the imported type mapping, if applicable. Import(Type*) can
+      // cache a type mapping to a declaration that ultimately fails.
+      if (const auto *FromTD = dyn_cast<TagDecl>(FromD))
+        if (const Type *FromTy = 
getFromContext().getCanonicalTagType(FromTD).getTypePtr())
+          ImportedTypes.erase(FromTy);
+
       // ImportedDecls and ImportedFromDecls are not symmetric.  It may happen
       // (e.g. with namespaces) that several decls from the 'from' context are
       // mapped to the same decl in the 'to' context.  If we removed entries
diff --git a/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp 
b/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp
new file mode 100644
index 0000000000000..0ecc0a6c03662
--- /dev/null
+++ b/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp
@@ -0,0 +1,114 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// Pathological case: a lambda's closure (the anonymous class that
+// implements its operator()) is created as a Decl before its members are
+// imported. If a member unrelated to the eventual failure -- e.g. the
+// implicit copy constructor, whose parameter type is `const ClosureType&`
+// -- imports successfully first, that success permanently caches the
+// closure's type as "imported" in ASTImporter::ImportedTypes, before the
+// member that actually fails (here, operator(), due to an unsupported
+// trailing requires-clause) is even reached. That cache is never
+// invalidated when the closure's own import later fails, so anything that
+// subsequently needs the same type (the DeclRefExpr inside
+// `decltype(func(...))` on `rudolf`, below) silently gets the half-built
+// closure back instead of a clean failure, producing an inconsistent node
+// that crashes downstream.
+
+
+// RUN: %clang_cc1 -std=c++20 -emit-pch -o %t/api.cpp.ast %t/api.cpp
+// RUN: %clang_cc1 -std=c++20 -emit-pch -o %t/isolate.cpp.ast %t/isolate.cpp
+
+// RUN: %clang_extdef_map %t/api.cpp -- -std=c++20 > %t/externalDefMap.tmp.txt
+// RUN: %clang_extdef_map %t/isolate.cpp -- -std=c++20 >> 
%t/externalDefMap.tmp.txt
+// On windows, absolute paths generated by extdef_map are not recognized,
+// so CSA prepends the workdir path to them. Force relative paths to work
+// around this issue.
+// RUN: sed -e 's| .*api\.cpp| api.cpp.ast|' -e 's| .*isolate\.cpp| 
isolate.cpp.ast|' \
+// RUN:   %t/externalDefMap.tmp.txt > %t/externalDefMap.txt
+
+// RUN: %clang_analyze_cc1 -std=c++20 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -verify %t/main.cpp
+
+//--- main.cpp
+
+namespace ns {
+
+inline constexpr auto func = []<class T>(const T p) {};
+
+}
+
+void import_api(int v);
+void trigger_api();
+void trigger_isolate();
+
+void entrypoint() {
+  import_api(0); // [email protected]:20 {{Division by zero}}
+}
+
+void trigger1() {
+  trigger_api();
+}
+
+void trigger2() {
+  trigger_isolate();
+}
+
+//--- api.cpp
+
+template <class> int declval();
+
+namespace ns {
+int import_ns;
+
+// This closure fails to import: its call operator's trailing
+// requires-clause has no importer support.
+inline constexpr auto func = []<class T>(const T p) requires requires { 0; } 
{};
+
+// The DeclRefExpr for `func` in this decltype independently re-resolves
+// the closure's type after `func` itself was merged away above.
+template <class K> decltype(func(declval<K>())) rudolf(int v);
+
+} // namespace ns
+
+void import_isolate(int v);
+
+void import_api(int v) {
+  (void)ns::import_ns;
+  import_isolate(v);
+}
+
+void trigger_api() {
+  ns::rudolf<void>(0); // fails to import
+}
+
+//--- isolate.cpp
+
+template <class> int declval();
+
+namespace ns {
+int import_ns;
+
+constexpr auto func = []<class T>(const T p) requires requires { 0; } {};
+
+// Structural equivalence of the return type accesses the closure's
+// definition through its type -- an access that assumes the closure is
+// intact.
+template <class K> decltype(func(declval<K>())) rudolf(int v) { // no-crash
+  (void)(42 / v);
+}
+
+} // namespace ns
+
+void import_isolate(int v) {
+  (void)ns::import_ns;
+  (void)(42 / v); // raises "Division by zero"
+}
+
+void trigger_isolate() {
+  ns::rudolf<void>(0); // fails to import
+}
diff --git a/clang/unittests/AST/ASTImporterTest.cpp 
b/clang/unittests/AST/ASTImporterTest.cpp
index 503f5da8af90f..038b86edb40fd 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -6608,6 +6608,42 @@ TEST_P(ErrorHandlingTest, 
ErrorIsPropagatedFromMemberToClass) {
   EXPECT_FALSE(ImportedOK);
 }
 
+// A member whose signature refers back to the enclosing class (e.g. a
+// copy constructor's `const Self&` parameter) can succeed and cache the
+// class's *type* before a later, failing member causes the class's own
+// Decl import to fail as a whole. Check that this doesn't leave a stale,
+// "successfully imported" entry for the class's type behind: any later,
+// independent request to import that type must also fail, not silently
+// hand back the half-built class.
+TEST_P(ErrorHandlingTest, ImportedTypeCacheIsInvalidatedOnFailure) {
+  TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
+      class X {
+        void ok(const X &) {}          // Succeeds; imports X's own type
+                                        // as a side effect, before X's
+                                        // own import is known to fail.
+        void bad() { )") + ErroneousStmt + R"( }  // Fails to import.
+      };
+      )",
+                                          Lang_CXX03);
+  auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
+      FromTU, cxxRecordDecl(hasName("X")));
+
+  CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX03);
+  EXPECT_FALSE(ImportedX); // X itself fails to import.
+
+  // The bug: without the fix, a later, independent request to import X's
+  // type silently succeeds, returning the half-built X as if nothing had
+  // gone wrong, because ASTImporter::ImportedTypes was never scrubbed
+  // when X's own Decl import failed.
+  ASTImporter *Importer = findFromTU(FromX)->Importer.get();
+  const Type *FromXTy = 
FromTU->getASTContext().getCanonicalTagType(FromX)->getTypePtr();
+  ASSERT_TRUE(FromXTy);
+  Expected<const Type *> ToTyOrErr = Importer->Import(FromXTy);
+  EXPECT_FALSE(static_cast<bool>(ToTyOrErr));
+  if (!ToTyOrErr)
+    llvm::consumeError(ToTyOrErr.takeError());
+}
+
 // Check that an error propagates to the dependent AST nodes.
 // In the below code it means that an error in X should propagate to A.
 // And even to F since the containing A is erroneous.

>From 58467bc1704e632749ab49029fbd55f84e0c1a22 Mon Sep 17 00:00:00 2001
From: guillem-bartrina-sonarsource <[email protected]>
Date: Wed, 5 Aug 2026 15:11:23 +0200
Subject: [PATCH 2/9] format

---
 clang/lib/AST/ASTImporter.cpp           | 3 ++-
 clang/unittests/AST/ASTImporterTest.cpp | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 0b85636a06598..ffaca93b5d2ac 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -10041,7 +10041,8 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
       // Also scrub the imported type mapping, if applicable. Import(Type*) can
       // cache a type mapping to a declaration that ultimately fails.
       if (const auto *FromTD = dyn_cast<TagDecl>(FromD))
-        if (const Type *FromTy = 
getFromContext().getCanonicalTagType(FromTD).getTypePtr())
+        if (const Type *FromTy =
+                getFromContext().getCanonicalTagType(FromTD).getTypePtr())
           ImportedTypes.erase(FromTy);
 
       // ImportedDecls and ImportedFromDecls are not symmetric.  It may happen
diff --git a/clang/unittests/AST/ASTImporterTest.cpp 
b/clang/unittests/AST/ASTImporterTest.cpp
index 038b86edb40fd..d5fd9ec1d17d2 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -6636,7 +6636,8 @@ TEST_P(ErrorHandlingTest, 
ImportedTypeCacheIsInvalidatedOnFailure) {
   // gone wrong, because ASTImporter::ImportedTypes was never scrubbed
   // when X's own Decl import failed.
   ASTImporter *Importer = findFromTU(FromX)->Importer.get();
-  const Type *FromXTy = 
FromTU->getASTContext().getCanonicalTagType(FromX)->getTypePtr();
+  const Type *FromXTy =
+      FromTU->getASTContext().getCanonicalTagType(FromX)->getTypePtr();
   ASSERT_TRUE(FromXTy);
   Expected<const Type *> ToTyOrErr = Importer->Import(FromXTy);
   EXPECT_FALSE(static_cast<bool>(ToTyOrErr));

>From 38015cd6fe3241b7a8f71287ce2954d6764aa0f4 Mon Sep 17 00:00:00 2001
From: guillem-bartrina-sonarsource <[email protected]>
Date: Wed, 5 Aug 2026 15:19:15 +0200
Subject: [PATCH 3/9] clean up lit test

---
 .../regression/lambda-import-corruption.cpp   | 35 +++++--------------
 1 file changed, 8 insertions(+), 27 deletions(-)

diff --git a/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp 
b/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp
index 0ecc0a6c03662..ce9a1e355bf1f 100644
--- a/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp
+++ b/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp
@@ -2,19 +2,16 @@
 // RUN: mkdir -p %t
 // RUN: split-file %s %t
 
-// Pathological case: a lambda's closure (the anonymous class that
-// implements its operator()) is created as a Decl before its members are
-// imported. If a member unrelated to the eventual failure -- e.g. the
-// implicit copy constructor, whose parameter type is `const ClosureType&`
-// -- imports successfully first, that success permanently caches the
-// closure's type as "imported" in ASTImporter::ImportedTypes, before the
-// member that actually fails (here, operator(), due to an unsupported
-// trailing requires-clause) is even reached. That cache is never
+// Pathological case: a lambda's closure is created as a Decl before its
+// members are imported. If a member unrelated to the eventual failure
+// (e.g. the implicit copy constructor) imports successfully first, that
+// success permanently caches the closure's type as "imported", before the
+// member that actually fails is even reached. That cache was never
 // invalidated when the closure's own import later fails, so anything that
-// subsequently needs the same type (the DeclRefExpr inside
-// `decltype(func(...))` on `rudolf`, below) silently gets the half-built
+// subsequently needs the same type (e.g. the DeclRefExpr inside
+// `decltype(func(...))` on `rudolf`, below) silently got the half-built
 // closure back instead of a clean failure, producing an inconsistent node
-// that crashes downstream.
+// that crashed.
 
 
 // RUN: %clang_cc1 -std=c++20 -emit-pch -o %t/api.cpp.ast %t/api.cpp
@@ -50,14 +47,6 @@ void entrypoint() {
   import_api(0); // [email protected]:20 {{Division by zero}}
 }
 
-void trigger1() {
-  trigger_api();
-}
-
-void trigger2() {
-  trigger_isolate();
-}
-
 //--- api.cpp
 
 template <class> int declval();
@@ -82,10 +71,6 @@ void import_api(int v) {
   import_isolate(v);
 }
 
-void trigger_api() {
-  ns::rudolf<void>(0); // fails to import
-}
-
 //--- isolate.cpp
 
 template <class> int declval();
@@ -108,7 +93,3 @@ void import_isolate(int v) {
   (void)ns::import_ns;
   (void)(42 / v); // raises "Division by zero"
 }
-
-void trigger_isolate() {
-  ns::rudolf<void>(0); // fails to import
-}

>From 5c4f13e528d21b53069e5ab375409ecaa5e629fd Mon Sep 17 00:00:00 2001
From: guillem-bartrina-sonarsource <[email protected]>
Date: Wed, 5 Aug 2026 15:20:49 +0200
Subject: [PATCH 4/9] clean up lit test

---
 clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp 
b/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp
index ce9a1e355bf1f..2d81b9697bdf2 100644
--- a/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp
+++ b/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp
@@ -40,8 +40,6 @@ inline constexpr auto func = []<class T>(const T p) {};
 }
 
 void import_api(int v);
-void trigger_api();
-void trigger_isolate();
 
 void entrypoint() {
   import_api(0); // [email protected]:20 {{Division by zero}}

>From 117b33b4449ab664136df3892f459236044fa792 Mon Sep 17 00:00:00 2001
From: guillem-bartrina-sonarsource <[email protected]>
Date: Fri, 7 Aug 2026 18:05:23 +0200
Subject: [PATCH 5/9] Rework main comments and move lit test

---
 ...pp => invalid-lambda-type-equivalence.cpp} | 16 ++------
 clang/unittests/AST/ASTImporterTest.cpp       | 38 +++++++++----------
 2 files changed, 21 insertions(+), 33 deletions(-)
 rename clang/test/Analysis/ctu/{regression/lambda-import-corruption.cpp => 
invalid-lambda-type-equivalence.cpp} (74%)

diff --git a/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp 
b/clang/test/Analysis/ctu/invalid-lambda-type-equivalence.cpp
similarity index 74%
rename from clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp
rename to clang/test/Analysis/ctu/invalid-lambda-type-equivalence.cpp
index 2d81b9697bdf2..eed5bd6362a3f 100644
--- a/clang/test/Analysis/ctu/regression/lambda-import-corruption.cpp
+++ b/clang/test/Analysis/ctu/invalid-lambda-type-equivalence.cpp
@@ -2,18 +2,6 @@
 // RUN: mkdir -p %t
 // RUN: split-file %s %t
 
-// Pathological case: a lambda's closure is created as a Decl before its
-// members are imported. If a member unrelated to the eventual failure
-// (e.g. the implicit copy constructor) imports successfully first, that
-// success permanently caches the closure's type as "imported", before the
-// member that actually fails is even reached. That cache was never
-// invalidated when the closure's own import later fails, so anything that
-// subsequently needs the same type (e.g. the DeclRefExpr inside
-// `decltype(func(...))` on `rudolf`, below) silently got the half-built
-// closure back instead of a clean failure, producing an inconsistent node
-// that crashed.
-
-
 // RUN: %clang_cc1 -std=c++20 -emit-pch -o %t/api.cpp.ast %t/api.cpp
 // RUN: %clang_cc1 -std=c++20 -emit-pch -o %t/isolate.cpp.ast %t/isolate.cpp
 
@@ -33,6 +21,8 @@
 
 //--- main.cpp
 
+// Check that importing 'api' and then 'isolate' does not cause crash.
+
 namespace ns {
 
 inline constexpr auto func = []<class T>(const T p) {};
@@ -79,7 +69,7 @@ int import_ns;
 constexpr auto func = []<class T>(const T p) requires requires { 0; } {};
 
 // Structural equivalence of the return type accesses the closure's
-// definition through its type -- an access that assumes the closure is
+// definition through its type, an access that assumes the closure is
 // intact.
 template <class K> decltype(func(declval<K>())) rudolf(int v) { // no-crash
   (void)(42 / v);
diff --git a/clang/unittests/AST/ASTImporterTest.cpp 
b/clang/unittests/AST/ASTImporterTest.cpp
index d5fd9ec1d17d2..31f76776ba12f 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -6608,20 +6608,22 @@ TEST_P(ErrorHandlingTest, 
ErrorIsPropagatedFromMemberToClass) {
   EXPECT_FALSE(ImportedOK);
 }
 
-// A member whose signature refers back to the enclosing class (e.g. a
-// copy constructor's `const Self&` parameter) can succeed and cache the
-// class's *type* before a later, failing member causes the class's own
-// Decl import to fail as a whole. Check that this doesn't leave a stale,
-// "successfully imported" entry for the class's type behind: any later,
-// independent request to import that type must also fail, not silently
-// hand back the half-built class.
-TEST_P(ErrorHandlingTest, ImportedTypeCacheIsInvalidatedOnFailure) {
+// Check that the imported types, and not only the decls, are invalidated 
+// (removed from ImportedTypes) upon an import failure. It can happen, for 
+// instance with a member whose signature refers back to the enclosing class, 
+// that the type is successfully imported and pointing to the decl being
+// imported, but that the decl import then fails further on. 
+// The decl mapping is correctly invalidated, but if the connected type is not 
+// invalidated as well, the half-built decl (which unavoidably remains 
+// in the 'To' AST) could be accessed through the type during later 
operations, 
+// like structural equivalence checks.
+TEST_P(ErrorHandlingTest, ImportedTypeMappingIsInvalidatedOnFailure) {
   TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
       class X {
-        void ok(const X &) {}          // Succeeds; imports X's own type
-                                        // as a side effect, before X's
-                                        // own import is known to fail.
-        void bad() { )") + ErroneousStmt + R"( }  // Fails to import.
+        void ok(const X &) {} // Succeeds; imports X's own type
+                              // as a side effect, before X's
+                              // own import is known to fail.
+        void bad() { )") + ErroneousStmt + R"(} // Fails to import.
       };
       )",
                                           Lang_CXX03);
@@ -6629,20 +6631,16 @@ TEST_P(ErrorHandlingTest, 
ImportedTypeCacheIsInvalidatedOnFailure) {
       FromTU, cxxRecordDecl(hasName("X")));
 
   CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX03);
-  EXPECT_FALSE(ImportedX); // X itself fails to import.
+  // Class X fails to import
+  EXPECT_FALSE(ImportedX);
 
-  // The bug: without the fix, a later, independent request to import X's
-  // type silently succeeds, returning the half-built X as if nothing had
-  // gone wrong, because ASTImporter::ImportedTypes was never scrubbed
-  // when X's own Decl import failed.
   ASTImporter *Importer = findFromTU(FromX)->Importer.get();
   const Type *FromXTy =
       FromTU->getASTContext().getCanonicalTagType(FromX)->getTypePtr();
   ASSERT_TRUE(FromXTy);
   Expected<const Type *> ToTyOrErr = Importer->Import(FromXTy);
-  EXPECT_FALSE(static_cast<bool>(ToTyOrErr));
-  if (!ToTyOrErr)
-    llvm::consumeError(ToTyOrErr.takeError());
+  // And its type should fail to import as well
+  EXPECT_TRUE(ToTyOrErr.errorIsA<clang::ASTImportError>());
 }
 
 // Check that an error propagates to the dependent AST nodes.

>From 5243a5c8e90d0ee4a697e092fe81253a67d3f0ae Mon Sep 17 00:00:00 2001
From: guillem-bartrina-sonarsource <[email protected]>
Date: Fri, 7 Aug 2026 18:13:39 +0200
Subject: [PATCH 6/9] Cast to TypeDecl instead

---
 clang/lib/AST/ASTImporter.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index ffaca93b5d2ac..2f4c7568be2f0 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -10038,12 +10038,10 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
       auto *ToD = CreatedToD;
       ImportedDecls.erase(Pos);
 
-      // Also scrub the imported type mapping, if applicable. Import(Type*) can
-      // cache a type mapping to a declaration that ultimately fails.
-      if (const auto *FromTD = dyn_cast<TagDecl>(FromD))
-        if (const Type *FromTy =
-                getFromContext().getCanonicalTagType(FromTD).getTypePtr())
-          ImportedTypes.erase(FromTy);
+      // Scrub the imported type mapping as well. Import(Type*) can add a
+      // type mapping linked to a declaration that ultimately fails.
+      if (const auto *FromTD = dyn_cast<TypeDecl>(FromD))
+          ImportedTypes.erase(FromTD->getTypeForDecl());
 
       // ImportedDecls and ImportedFromDecls are not symmetric.  It may happen
       // (e.g. with namespaces) that several decls from the 'from' context are

>From 5242abdd8b4a2cb46c5c54fa2d2fad02ee4e95c4 Mon Sep 17 00:00:00 2001
From: guillem-bartrina-sonarsource <[email protected]>
Date: Fri, 7 Aug 2026 18:17:31 +0200
Subject: [PATCH 7/9] Remove additional potentially failure cases

---
 clang/lib/AST/ASTImporter.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 2f4c7568be2f0..29a16b7504eeb 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -10096,6 +10096,10 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
           break;
         PrevFromDi = FromDi;
         setImportDeclError(FromDi, ErrOut);
+        
+        if (const auto *FromTDi = dyn_cast<TypeDecl>(FromDi))
+          ImportedTypes.erase(FromTDi->getTypeForDecl());
+
         //FIXME Should we remove these Decls from ImportedDecls?
         // Set the error for the mapped to Decl, which is in the "to" context.
         auto Ii = ImportedDecls.find(FromDi);

>From d0ca7cbcf7934fd01389e416e9ebd427ef4769d8 Mon Sep 17 00:00:00 2001
From: guillem-bartrina-sonarsource <[email protected]>
Date: Fri, 7 Aug 2026 18:22:34 +0200
Subject: [PATCH 8/9] format

---
 clang/lib/AST/ASTImporter.cpp           |  4 ++--
 clang/unittests/AST/ASTImporterTest.cpp | 14 +++++++-------
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 29a16b7504eeb..e96372e7c9523 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -10041,7 +10041,7 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
       // Scrub the imported type mapping as well. Import(Type*) can add a
       // type mapping linked to a declaration that ultimately fails.
       if (const auto *FromTD = dyn_cast<TypeDecl>(FromD))
-          ImportedTypes.erase(FromTD->getTypeForDecl());
+        ImportedTypes.erase(FromTD->getTypeForDecl());
 
       // ImportedDecls and ImportedFromDecls are not symmetric.  It may happen
       // (e.g. with namespaces) that several decls from the 'from' context are
@@ -10096,7 +10096,7 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
           break;
         PrevFromDi = FromDi;
         setImportDeclError(FromDi, ErrOut);
-        
+
         if (const auto *FromTDi = dyn_cast<TypeDecl>(FromDi))
           ImportedTypes.erase(FromTDi->getTypeForDecl());
 
diff --git a/clang/unittests/AST/ASTImporterTest.cpp 
b/clang/unittests/AST/ASTImporterTest.cpp
index 31f76776ba12f..54c662dea94c4 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -6608,14 +6608,14 @@ TEST_P(ErrorHandlingTest, 
ErrorIsPropagatedFromMemberToClass) {
   EXPECT_FALSE(ImportedOK);
 }
 
-// Check that the imported types, and not only the decls, are invalidated 
-// (removed from ImportedTypes) upon an import failure. It can happen, for 
-// instance with a member whose signature refers back to the enclosing class, 
+// Check that the imported types, and not only the decls, are invalidated
+// (removed from ImportedTypes) upon an import failure. It can happen, for
+// instance with a member whose signature refers back to the enclosing class,
 // that the type is successfully imported and pointing to the decl being
-// imported, but that the decl import then fails further on. 
-// The decl mapping is correctly invalidated, but if the connected type is not 
-// invalidated as well, the half-built decl (which unavoidably remains 
-// in the 'To' AST) could be accessed through the type during later 
operations, 
+// imported, but that the decl import then fails further on.
+// The decl mapping is correctly invalidated, but if the connected type is not
+// invalidated as well, the half-built decl (which unavoidably remains
+// in the 'To' AST) could be accessed through the type during later operations,
 // like structural equivalence checks.
 TEST_P(ErrorHandlingTest, ImportedTypeMappingIsInvalidatedOnFailure) {
   TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(

>From 1484941fdfb5f53bd89120a0d271fed3ac00f7dd Mon Sep 17 00:00:00 2001
From: guillem-bartrina-sonarsource <[email protected]>
Date: Mon, 10 Aug 2026 09:11:39 +0200
Subject: [PATCH 9/9] Update clang/lib/AST/ASTImporter.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Kéri <[email protected]>
---
 clang/lib/AST/ASTImporter.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index e96372e7c9523..02c54e4d6f472 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -10038,8 +10038,8 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
       auto *ToD = CreatedToD;
       ImportedDecls.erase(Pos);
 
-      // Scrub the imported type mapping as well. Import(Type*) can add a
-      // type mapping linked to a declaration that ultimately fails.
+      // Remove the imported type mapping as well.
+      // The imported type can point to a declaration that failed to import 
later.
       if (const auto *FromTD = dyn_cast<TypeDecl>(FromD))
         ImportedTypes.erase(FromTD->getTypeForDecl());
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to