[clang] 9560abb - [clang][analyzer] Test cases for CTU import failures

via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 26 09:16:59 PDT 2026


Author: Arseniy Zaostrovnykh
Date: 2026-03-26T17:16:52+01:00
New Revision: 9560abb68aece7200fbb5e4da1bb7b7499663378

URL: https://github.com/llvm/llvm-project/commit/9560abb68aece7200fbb5e4da1bb7b7499663378
DIFF: https://github.com/llvm/llvm-project/commit/9560abb68aece7200fbb5e4da1bb7b7499663378.diff

LOG: [clang][analyzer] Test cases for CTU import failures

Add tests for all kinds of CTU import failures expressed in the
`cross_tu::index_error_code` enumeration. Many of the test cases have no
diagnostics, because the respective errors are silently ignored in
`CrossTranslationUnitContext::emitCrossTUDiagnostics`.

I expect to improve this part, and these tests should demonstrate how
the improvements manifest to the user.

--
CPP-7804

Added: 
    clang/test/Analysis/ctu/Inputs/invalid-ast-other.cpp
    clang/test/Analysis/ctu/diag/Inputs/bar.cpp
    clang/test/Analysis/ctu/diag/Inputs/simple-extern-c.cpp
    clang/test/Analysis/ctu/diag/Inputs/simple.cpp
    clang/test/Analysis/ctu/diag/invalid-index.cpp
    clang/test/Analysis/ctu/diag/invlist-ambiguous.cpp
    clang/test/Analysis/ctu/diag/invlist-empty.cpp
    clang/test/Analysis/ctu/diag/invlist-lookup-miss.cpp
    clang/test/Analysis/ctu/diag/invlist-missing.cpp
    clang/test/Analysis/ctu/diag/invlist-wrong-format.cpp
    clang/test/Analysis/ctu/diag/lang-dialect-mismatch.cpp
    clang/test/Analysis/ctu/diag/lang-mismatch.c
    clang/test/Analysis/ctu/diag/load-threshold.cpp
    clang/test/Analysis/ctu/diag/missing-index.cpp
    clang/test/Analysis/ctu/invalid-ast.cpp
    clang/test/Analysis/ctu/missing-ast.cpp

Modified: 
    

Removed: 
    


################################################################################
diff  --git a/clang/test/Analysis/ctu/Inputs/invalid-ast-other.cpp b/clang/test/Analysis/ctu/Inputs/invalid-ast-other.cpp
new file mode 100644
index 0000000000000..06440e3a86f7d
--- /dev/null
+++ b/clang/test/Analysis/ctu/Inputs/invalid-ast-other.cpp
@@ -0,0 +1 @@
+void external() {}

diff  --git a/clang/test/Analysis/ctu/diag/Inputs/bar.cpp b/clang/test/Analysis/ctu/diag/Inputs/bar.cpp
new file mode 100644
index 0000000000000..51fc652a8c1a7
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/Inputs/bar.cpp
@@ -0,0 +1 @@
+int bar(int x) { return x + 2; }

diff  --git a/clang/test/Analysis/ctu/diag/Inputs/simple-extern-c.cpp b/clang/test/Analysis/ctu/diag/Inputs/simple-extern-c.cpp
new file mode 100644
index 0000000000000..dc6193b4a3d51
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/Inputs/simple-extern-c.cpp
@@ -0,0 +1 @@
+extern "C" int foo(int x) { return x + 1; }

diff  --git a/clang/test/Analysis/ctu/diag/Inputs/simple.cpp b/clang/test/Analysis/ctu/diag/Inputs/simple.cpp
new file mode 100644
index 0000000000000..83a87cb20ca99
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/Inputs/simple.cpp
@@ -0,0 +1 @@
+int foo(int x) { return x + 1; }

diff  --git a/clang/test/Analysis/ctu/diag/invalid-index.cpp b/clang/test/Analysis/ctu/diag/invalid-index.cpp
new file mode 100644
index 0000000000000..640e6ceee654c
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/invalid-index.cpp
@@ -0,0 +1,18 @@
+// Test that a malformed externalDefMap.txt produces err_extdefmap_parsing error.
+//
+// RUN: rm -rf %t && mkdir %t
+// RUN: echo 'this is invalid' > %t/externalDefMap.txt
+// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -verify %s
+
+// We expect an error in this file, but without a location.
+// expected-error-re at ./invalid-index.cpp:*{{error parsing index file: '{{.+}}externalDefMap.txt' line: 1 '<USR-Length>:<USR> <File-Path>' format expected}}
+
+int foo(int);
+
+void test() {
+  foo(1);
+}

diff  --git a/clang/test/Analysis/ctu/diag/invlist-ambiguous.cpp b/clang/test/Analysis/ctu/diag/invlist-ambiguous.cpp
new file mode 100644
index 0000000000000..570c6c7fe2585
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/invlist-ambiguous.cpp
@@ -0,0 +1,19 @@
+// Test that duplicate keys in the invocation list silently fails CTU import.
+//
+// RUN: rm -rf %t && mkdir %t
+// RUN: echo '11:c:@F at foo#I# simple.cpp' > %t/externalDefMap.txt
+// RUN: echo '"/some/path.cpp": ["clang", "/some/path.cpp"]' > %t/invocations.yaml
+// RUN: echo '"/some/path.cpp": ["clang", "/some/path.cpp"]' >> %t/invocations.yaml
+// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -analyzer-config ctu-invocation-list=%t/invocations.yaml \
+// RUN:   -verify %s
+
+int foo(int);
+
+void test() {
+  // expected-no-diagnostics
+  foo(1); // no-warning. Ignoring "Invocation list file contains multiple references to the same source file."
+}

diff  --git a/clang/test/Analysis/ctu/diag/invlist-empty.cpp b/clang/test/Analysis/ctu/diag/invlist-empty.cpp
new file mode 100644
index 0000000000000..b0504960b98b2
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/invlist-empty.cpp
@@ -0,0 +1,23 @@
+// Test that an empty invocation list file silently fails CTU import.
+//
+// Note: invocation_list_empty (index_error_code::invocation_list_empty) is
+// dead code: llvm::yaml::Stream::begin() always creates at least one Document,
+// so FirstInvocationFile == InvocationFile.end() is never true. An empty file
+// reaches the !DocumentRoot branch instead, producing invocation_list_wrong_format.
+//
+// RUN: rm -rf %t && mkdir %t
+// RUN: echo '11:c:@F at foo#I# simple.cpp' > %t/externalDefMap.txt
+// RUN: touch %t/invocations.yaml
+// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -analyzer-config ctu-invocation-list=%t/invocations.yaml \
+// RUN:   -verify %s
+
+int foo(int);
+
+void test() {
+  // expected-no-diagnostics
+  foo(1); // no-warning. Ignoring "Invocation list file is in wrong format."
+}

diff  --git a/clang/test/Analysis/ctu/diag/invlist-lookup-miss.cpp b/clang/test/Analysis/ctu/diag/invlist-lookup-miss.cpp
new file mode 100644
index 0000000000000..3adc035dff681
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/invlist-lookup-miss.cpp
@@ -0,0 +1,21 @@
+// Test that a valid invocation list without the requested source file silently fails CTU import.
+//
+// The invocation list has a valid entry for a 
diff erent path, but not for the
+// source file referenced in externalDefMap.txt.
+//
+// RUN: rm -rf %t && mkdir %t
+// RUN: echo '11:c:@F at foo#I# diag-simple.cpp' > %t/externalDefMap.txt
+// RUN: echo '"/nonexistent.cpp": ["clang", "/nonexistent.cpp"]' > %t/invocations.yaml
+// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -analyzer-config ctu-invocation-list=%t/invocations.yaml \
+// RUN:   -verify %s
+
+int foo(int);
+
+void test() {
+  // expected-no-diagnostics
+  foo(1); // no-warning. Ignoring "Invocation list file does not contain the requested source file."
+}

diff  --git a/clang/test/Analysis/ctu/diag/invlist-missing.cpp b/clang/test/Analysis/ctu/diag/invlist-missing.cpp
new file mode 100644
index 0000000000000..bac264ab10b4e
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/invlist-missing.cpp
@@ -0,0 +1,20 @@
+// Test that a missing invocation list silently fails CTU import.
+//
+// The externalDefMap.txt maps foo to a non-.ast source path, triggering
+// loadFromSource(). The invocation list path points to a nonexistent file.
+//
+// RUN: rm -rf %t && mkdir %t
+// RUN: echo '11:c:@F at foo#I# simple.cpp' > %t/externalDefMap.txt
+// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -analyzer-config ctu-invocation-list=%t/nonexistent.yaml \
+// RUN:   -verify %s
+
+int foo(int);
+
+void test() {
+  // expected-no-diagnostics
+  foo(1); // no-warning. Ignoring "Invocation list file is not found."
+}

diff  --git a/clang/test/Analysis/ctu/diag/invlist-wrong-format.cpp b/clang/test/Analysis/ctu/diag/invlist-wrong-format.cpp
new file mode 100644
index 0000000000000..cfa3cc65e8d06
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/invlist-wrong-format.cpp
@@ -0,0 +1,21 @@
+// Test that a malformed invocation list produces silently fails CTU import.
+//
+// A YAML sequence (- item) at the root is not a mapping, so dyn_cast to
+// MappingNode fails, triggering invocation_list_wrong_format.
+//
+// RUN: rm -rf %t && mkdir %t
+// RUN: echo '11:c:@F at foo#I# simple.cpp' > %t/externalDefMap.txt
+// RUN: echo '- just_a_list_item' > %t/invocations.yaml
+// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -analyzer-config ctu-invocation-list=%t/invocations.yaml \
+// RUN:   -verify %s
+
+int foo(int);
+
+void test() {
+  // expected-no-diagnostics
+  foo(1); // no-warning. Ignoring "Invocation list file is in wrong format."
+}

diff  --git a/clang/test/Analysis/ctu/diag/lang-dialect-mismatch.cpp b/clang/test/Analysis/ctu/diag/lang-dialect-mismatch.cpp
new file mode 100644
index 0000000000000..c41072d46df02
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/lang-dialect-mismatch.cpp
@@ -0,0 +1,21 @@
+// Test that loading a C++17 AST from a C++14 translation unit produces
+// lang_dialect_mismatch. Both are C++ (no lang_mismatch), but CPlusPlus17
+// 
diff ers, triggering the dialect check, and silently failing CTU import.
+//
+// RUN: rm -rf %t && mkdir %t
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-pc-linux-gnu \
+// RUN:   -emit-pch -o %t/simple.cpp.ast \
+// RUN:   %S/Inputs/simple.cpp
+// RUN: echo '11:c:@F at foo#I# simple.cpp.ast' > %t/externalDefMap.txt
+// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -verify %s
+
+int foo(int);
+
+void test() {
+  // expected-no-diagnostics
+  foo(1); // no-warning. Ignoring "Invocation list file contains multiple references to the same source file."
+}

diff  --git a/clang/test/Analysis/ctu/diag/lang-mismatch.c b/clang/test/Analysis/ctu/diag/lang-mismatch.c
new file mode 100644
index 0000000000000..deee7077ec495
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/lang-mismatch.c
@@ -0,0 +1,23 @@
+// Test that loading a C++ AST from a C translation unit produces lang_mismatch.
+//
+// The external definition is compiled as C++ with extern "C" linkage, so the
+// USR matches the C function declaration (both use the no-parameter-type USR
+// format: c:@F at foo, 8 chars). The lang mismatch leads to a silent CTU import failure.
+//
+// RUN: rm -rf %t && mkdir %t
+// RUN: %clang_cc1 -x c++ -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -emit-pch -o %t/simple-extern-c.cpp.ast \
+// RUN:   %S/Inputs/simple-extern-c.cpp
+// RUN: echo '8:c:@F at foo simple-extern-c.cpp.ast' > %t/externalDefMap.txt
+// RUN: %clang_analyze_cc1 -x c -triple x86_64-pc-linux-gnu \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -verify %s
+
+int foo(int);
+
+void test(void) {
+  // expected-no-diagnostics
+  foo(1); // no-warning. Ignoring "Language mismatch."
+}

diff  --git a/clang/test/Analysis/ctu/diag/load-threshold.cpp b/clang/test/Analysis/ctu/diag/load-threshold.cpp
new file mode 100644
index 0000000000000..9e40b8a42958f
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/load-threshold.cpp
@@ -0,0 +1,33 @@
+// Test that exceeding ctu-import-cpp-threshold produces load_threshold_reached,
+// which is silently fails AST import.
+//
+// With threshold=1, the first external AST (foo) is loaded successfully.
+// The second lookup (bar) finds the threshold exhausted and reports the error.
+//
+// RUN: rm -rf %t && mkdir %t
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -emit-pch -o %t/simple.cpp.ast \
+// RUN:   %S/Inputs/simple.cpp
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -emit-pch -o %t/bar.cpp.ast \
+// RUN:   %S/Inputs/bar.cpp
+// RUN: echo '11:c:@F at foo#I# simple.cpp.ast' > %t/externalDefMap.txt
+// RUN: echo '11:c:@F at bar#I# bar.cpp.ast' >> %t/externalDefMap.txt
+// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -analyzer-config ctu-import-cpp-threshold=1 \
+// RUN:   -verify %s
+
+// foo is loaded successfully (first load, within threshold).
+// bar hits the threshold (no telemetry emitted for it).
+
+int foo(int);
+int bar(int);
+
+void test() {
+  foo(1);
+  // expected-no-diagnostics
+  bar(1); // no-warning. Ignoring "Load threshold reached."
+}

diff  --git a/clang/test/Analysis/ctu/diag/missing-index.cpp b/clang/test/Analysis/ctu/diag/missing-index.cpp
new file mode 100644
index 0000000000000..e09042f9538f7
--- /dev/null
+++ b/clang/test/Analysis/ctu/diag/missing-index.cpp
@@ -0,0 +1,18 @@
+// Test that missing externalDefMap.txt produces err_ctu_error_opening at call site.
+//
+// RUN: rm -rf %t && mkdir %t
+// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -analyzer-config ctu-index-name=non-existing.txt \
+// RUN:   -verify %s
+
+// We expect an error in this file, but without a location.
+// expected-error-re at ./missing-index.cpp:*{{error opening '{{.+}}non-existing.txt': required by the CrossTU functionality}}
+
+int foo(int);
+
+void test() {
+  foo(1);
+}

diff  --git a/clang/test/Analysis/ctu/invalid-ast.cpp b/clang/test/Analysis/ctu/invalid-ast.cpp
new file mode 100644
index 0000000000000..9d0b850233dbe
--- /dev/null
+++ b/clang/test/Analysis/ctu/invalid-ast.cpp
@@ -0,0 +1,26 @@
+// Test that loading of invalid AST dump leads CTU import failure and a note on stderr.
+// RUN: rm -rf %t
+// RUN: mkdir %t
+
+// RUN: touch %t/invalid-ast-other.cpp.ast
+
+// RUN: cp %S/Inputs/invalid-ast-other.cpp.externalDefMap.ast-dump.txt %t/externalDefMap.txt
+
+// RUN: %clang_analyze_cc1 -triple x86_64-pc-linux-gnu -std=c++17 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -analyzer-config display-ctu-progress=true \
+// RUN:   -verify %s 2>&1 | FileCheck %s
+
+// CHECK: error: unable to load precompiled file
+
+// FIXME: this is misleading
+// CHECK: CTU loaded AST file: invalid-ast-other.cpp.ast
+
+void external();
+
+void trigger() {
+  // expected-no-diagnostics
+  external(); // no-warning
+}

diff  --git a/clang/test/Analysis/ctu/missing-ast.cpp b/clang/test/Analysis/ctu/missing-ast.cpp
new file mode 100644
index 0000000000000..d39d5d8f05bf6
--- /dev/null
+++ b/clang/test/Analysis/ctu/missing-ast.cpp
@@ -0,0 +1,24 @@
+// Test that when the referenced AST file is missing, CTU import fails with a note in stderr.
+// RUN: rm -rf %t
+// RUN: mkdir %t
+
+// RUN: cp %S/Inputs/missing-ast.cpp.externalDefMap.ast-dump.txt %t/externalDefMap.txt
+
+// RUN: %clang_analyze_cc1 -triple x86_64-pc-linux-gnu -std=c++17 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -analyzer-config display-ctu-progress=true \
+// RUN:   -verify %s 2>&1 | FileCheck %s
+
+// CHECK: error: unable to load precompiled file
+
+// FIXME: this is misleading
+// CHECK: CTU loaded AST file: wrong-missing-ast.cpp.ast
+
+void external();
+
+void trigger() {
+  // expected-no-diagnostics
+  external(); // no-warning
+}


        


More information about the cfe-commits mailing list