Skip to content

Conversation

balazske
Copy link
Collaborator

When a type is imported with ASTImporter, the "original declaration"
of the type is imported. In some cases this is not the definition
(of the class). Before the fix the definition was only imported if
there was an other reference to it in the AST to import. This is not
always the case (like in the added test case), if not the definition
was missing in the "To" AST which can cause the assertion later.

…uring CTU static analysis

When a type is imported with `ASTImporter`, the "original declaration"
of the type is imported. In some cases this is not the definition
(of the class). Before the fix the definition was only imported if
there was an other reference to it in the AST to import. This is not
always the case (like in the added test case), if not the definition
was missing in the "To" AST which can cause the assertion later.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:static analyzer labels Aug 29, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 29, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Balázs Kéri (balazske)

Changes

When a type is imported with ASTImporter, the "original declaration"
of the type is imported. In some cases this is not the definition
(of the class). Before the fix the definition was only imported if
there was an other reference to it in the AST to import. This is not
always the case (like in the added test case), if not the definition
was missing in the "To" AST which can cause the assertion later.


Full diff: https://github.com/llvm/llvm-project/pull/156056.diff

2 Files Affected:

  • (modified) clang/lib/AST/ASTImporter.cpp (+12-1)
  • (added) clang/test/Analysis/ctu-import-type-decl-definition.c (+43)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 6299efaf6bbfc..d2ea60896094f 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1740,10 +1740,21 @@ ExpectedType ASTNodeImporter::VisitDeducedTemplateSpecializationType(
 }
 
 ExpectedType ASTNodeImporter::VisitTagType(const TagType *T) {
-  Expected<TagDecl *> ToDeclOrErr = import(T->getOriginalDecl());
+  TagDecl *DeclForType = T->getOriginalDecl();
+  Expected<TagDecl *> ToDeclOrErr = import(DeclForType);
   if (!ToDeclOrErr)
     return ToDeclOrErr.takeError();
 
+  if (DeclForType->isUsed()) {
+    // If there is a definition of the 'OriginalDecl', it should be imported to
+    // have all information for the type in the "To" AST. (In some cases no
+    // other reference may exist to the definition decl and it would not be
+    // imported otherwise.)
+    Expected<TagDecl *> ToDefDeclOrErr = import(DeclForType->getDefinition());
+    if (!ToDefDeclOrErr)
+      return ToDefDeclOrErr.takeError();
+  }
+
   if (T->isCanonicalUnqualified())
     return Importer.getToContext().getCanonicalTagType(*ToDeclOrErr);
 
diff --git a/clang/test/Analysis/ctu-import-type-decl-definition.c b/clang/test/Analysis/ctu-import-type-decl-definition.c
new file mode 100644
index 0000000000000..6d95fd5686cb4
--- /dev/null
+++ b/clang/test/Analysis/ctu-import-type-decl-definition.c
@@ -0,0 +1,43 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -emit-pch -o %t/import.c.ast %t/import.c
+
+// RUN: %clang_extdef_map -- -x c %t/import.c >> %t/externalDefMap.txt
+// RUN: sed -i'' 's/$/.ast/' %t/externalDefMap.txt
+
+// RUN: %clang_cc1 -analyze \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config display-ctu-progress=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -verify %t/main.c
+
+//--- main.c
+
+// expected-no-diagnostics
+
+typedef struct X_s X_t;
+unsigned long f_import(struct X_s *xPtr);
+
+static void freeWriteFileResources(struct X_s *xPtr) {
+  f_import(xPtr);
+}
+
+//--- import.c
+
+typedef struct Y_s Y_t;
+
+struct Y_s {
+};
+
+struct X_s {
+  Y_t y;
+};
+
+unsigned long f_import(struct X_s *xPtr) {
+  if (xPtr != 0) {
+  }
+  return 0;
+}

@dyung
Copy link
Collaborator

dyung commented Aug 29, 2025

I'll take one of the bots offline to test your change today and let you know how it goes.

@dyung
Copy link
Collaborator

dyung commented Aug 29, 2025

This is still failing on my MacOS bot:

-- Testing: 1 tests, 1 workers --
FAIL: Clang :: Analysis/ctu-import-type-decl-definition.c (1 of 1)
******************** TEST 'Clang :: Analysis/ctu-import-type-decl-definition.c' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
rm -rf /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp # RUN: at line 1
+ rm -rf /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp
mkdir -p /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp # RUN: at line 2
+ mkdir -p /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp
split-file /Users/buildbot/buildbot-root/llvm-project/clang/test/Analysis/ctu-import-type-decl-definition.c /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp # RUN: at line 3
+ split-file /Users/buildbot/buildbot-root/llvm-project/clang/test/Analysis/ctu-import-type-decl-definition.c /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp
/Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/clang -cc1 -internal-isystem /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/lib/clang/22/include -nostdsysteminc -emit-pch -o /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c.ast /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c # RUN: at line 5
+ /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/clang -cc1 -internal-isystem /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/lib/clang/22/include -nostdsysteminc -emit-pch -o /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c.ast /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c
/Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/clang-extdef-mapping -- -x c /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c >> /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/externalDefMap.txt # RUN: at line 7
+ /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/bin/clang-extdef-mapping -- -x c /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c
sed -i'' 's/$/.ast/' /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/externalDefMap.txt # RUN: at line 8
+ sed -i 's/$/.ast/' /Volumes/RAMDisk/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/externalDefMap.txt
sed: 1: "/Volumes/RAMDisk/buildb ...": invalid command code R

--

********************
********************
Failed Tests (1):
  Clang :: Analysis/ctu-import-type-decl-definition.c

// RUN: %clang_cc1 -emit-pch -o %t/import.c.ast %t/import.c

// RUN: %clang_extdef_map -- -x c %t/import.c >> %t/externalDefMap.txt
// RUN: sed -i'' 's/$/.ast/' %t/externalDefMap.txt
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From this page it might not be able to make a portable command line that replaces the file in-place with no backup that works on linux and MacOS.

If you are okay with creating an extra file in the test directory, one of the suggestions on that page seems to work:

// RUN: sed -i='' -e 's/$/.ast/' %t/externalDefMap.txt

This creates a file externalDefMap.txt=, but does seem to work on linux and MacOS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants