llvm-project/clang/test/Preprocessor/header-shadowing.c
Jinjie Huang 689b3cc7c7
[clang] Support header shadowing diagnostics in Clang header search (#162491)
When including a header file, multiple files with the same name may
exist across different search paths, like:
   |-- main.cpp
  |-- **header.h**
  |-- include
  |  └── **header.h**
The compiler usually picks the first match it finds (typically following
MSVC rules for current/include-chain paths first, then regular -I
paths), which may not be the user’s intended header.
This silent behavior can lead to subtle runtime API mismatches or
increase the cost of resolving errors such as “error: use of undeclared
identifier”, especially in large projects.

Therefore, this patch tries to provide a diagnostic message without
changing the current header selection. It does this by performing an
additional search for duplicate filenames across all search paths (both
MSVC rules and standard paths). This informs the user about a potential
"header shadowing" issue and clarifies which header path was actually
used.

Since header searching is much cheaper than file loading, the added
overhead should be within an acceptable range -- assuming the diagnostic
message is valuable.
2025-12-03 14:08:20 +08:00

58 lines
2.1 KiB
C

// RUN: rm -rf %t
// RUN: split-file %s %t
/// Check that:
/// - Quoted includes ("...") trigger the diagnostic.
/// - System headers are ignored.
/// - #include_next does not cause a duplicate warning.
// RUN: %clang_cc1 -Wshadow-header -Eonly %t/main.c -I %t/include1 -I %t/include2 \
// RUN: -isystem %t/system1 -isystem %t/system2 2>&1 | FileCheck %s --check-prefix=SHADOWING
// SHADOWING: {{.*}} warning: multiple candidates for header 'header.h' found; directory '{{.*}}include1' chosen, ignoring others including '{{.*}}include2' [-Wshadow-header]
// SHADOWING: warning: include1/header.h included!
// SHADOWING-NOT: {{.*}} warning: multiple candidates for header 'header.h' found; directory '{{.*}}include2' chosen, ignoring others including '{{.*}}include1' [-Wshadow-header]
// SHADOWING: warning: include2/header.h included!
// SHADOWING-NOT: {{.*}} warning: multiple candidates for header 'stdio.h' found; directory '{{.*}}system1' chosen, ignoring others including '{{.*}}system2' [-Wshadow-header]
// SHADOWING: warning: system1/stdio.h included!
/// Check that the diagnostic is only performed once in MSVC compatibility mode.
// RUN: %clang_cc1 -fms-compatibility -Wshadow-header -Eonly %t/t.c 2>&1 | FileCheck %s --check-prefix=SHADOWING-MS
// SHADOWING-MS: {{.*}} warning: multiple candidates for header 't3.h' found; directory '{{.*}}foo' chosen, ignoring others including '{{.*}}' [-Wshadow-header]
// SHADOWING-MS-NOT: {{.*}} warning: multiple candidates for header 't3.h' found; directory '{{.*}}' chosen, ignoring others including '{{.*}}foo' [-Wshadow-header]
// SHADOWING-MS: warning: Found foo/t3.h.
//--- main.c
#include "header.h"
#include <stdio.h>
//--- include1/header.h
#warning include1/header.h included!
#include_next "header.h"
//--- include2/header.h
#warning include2/header.h included!
//--- system1/stdio.h
#warning system1/stdio.h included!
//--- system2/stdio.h
#warning system2/stdio.h included!
/// Used to test when running in MSVC compatibility
//--- t.c
#include "foo/t1.h"
//--- foo/t1.h
#include "bar/t2.h"
//--- foo/bar/t2.h
#include "t3.h"
//--- foo/t3.h
#warning Found foo/t3.h.
//--- t3.h
#warning Found t3.h.