[clang] [Clang] Add warning for non-portable include paths with trailing whitespace or dots (PR #190610)
Amelia Jochna via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 7 09:19:02 PDT 2026
https://github.com/ameliajochna updated https://github.com/llvm/llvm-project/pull/190610
>From 3e9557c29ac850ac816d1d359dc27d3924dfa075 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CAmelia?= <“ameliajochna1 at gmail.com”>
Date: Mon, 6 Apr 2026 17:38:58 +0200
Subject: [PATCH 1/2] [Clang] Add warning for non-portable include paths with
trailing whitespace or dots
---
clang/include/clang/Basic/DiagnosticGroups.td | 2 ++
.../include/clang/Basic/DiagnosticLexKinds.td | 14 ++++++++++---
clang/lib/Lex/PPDirectives.cpp | 5 +++++
.../nonportable-trailing-whitespace-win.c | 18 +++++++++++++++++
.../nonportable-trailing-whitespace.c | 20 +++++++++++++++++++
5 files changed, 56 insertions(+), 3 deletions(-)
create mode 100644 clang/test/Preprocessor/nonportable-trailing-whitespace-win.c
create mode 100644 clang/test/Preprocessor/nonportable-trailing-whitespace.c
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index dc7280c66ea79..e4491603df76c 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -944,6 +944,8 @@ def MemsetTransposedArgs : DiagGroup<"memset-transposed-args">;
def DynamicClassMemaccess : DiagGroup<"dynamic-class-memaccess">;
def NonTrivialMemcall : DiagGroup<"nontrivial-memcall">;
def NonTrivialMemaccess : DiagGroup<"nontrivial-memaccess", [NonTrivialMemcall]>;
+def NonportableIncludePath : DiagGroup<"nonportable-include-path">;
+def NonportableSystemIncludePath : DiagGroup<"nonportable-system-include-path">;
def SuspiciousBzero : DiagGroup<"suspicious-bzero">;
def SuspiciousMemaccess : DiagGroup<"suspicious-memaccess",
[SizeofPointerMemaccess, DynamicClassMemaccess,
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index bea0aafac98cf..deb1111b46134 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -373,13 +373,21 @@ def ext_missing_whitespace_after_macro_name : ExtWarn<
def warn_missing_whitespace_after_macro_name : Warning<
"whitespace recommended after macro name">;
-class NonportablePath : Warning<
+class NonportablePath : Warning<
"non-portable path to file '%0'; specified path differs in case from file"
" name on disk">;
def pp_nonportable_path : NonportablePath,
- InGroup<DiagGroup<"nonportable-include-path">>;
+ InGroup<NonportableIncludePath>;
def pp_nonportable_system_path : NonportablePath, DefaultIgnore,
- InGroup<DiagGroup<"nonportable-system-include-path">>;
+ InGroup<NonportableSystemIncludePath>;
+
+class NonportablePathTrailing : Warning<
+ "non-portable path to file '%0'; specified path contains trailing"
+ " %select{whitespace|dots}1">;
+def pp_nonportable_path_trailing : NonportablePathTrailing,
+ InGroup<NonportableIncludePath>;
+def pp_nonportable_system_path_trailing : NonportablePathTrailing, DefaultIgnore,
+ InGroup<NonportableSystemIncludePath>;
def pp_pragma_once_in_main_file : Warning<"#pragma once in main file">,
InGroup<DiagGroup<"pragma-once-outside-header">>;
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index b90c04776ff9e..8fee9104797f8 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2380,6 +2380,11 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
// error.
if (Filename.empty())
return {ImportAction::None};
+ if (Filename.ends_with(" ") || Filename.ends_with(".")) {
+ unsigned Selection = Filename.ends_with(".") ? 1 : 0;
+ Diag(FilenameTok, diag::pp_nonportable_path_trailing)
+ << Filename << Selection;
+ }
bool IsImportDecl = HashLoc.isInvalid();
SourceLocation StartLoc = IsImportDecl ? IncludeTok.getLocation() : HashLoc;
diff --git a/clang/test/Preprocessor/nonportable-trailing-whitespace-win.c b/clang/test/Preprocessor/nonportable-trailing-whitespace-win.c
new file mode 100644
index 0000000000000..2a31d6bb755ee
--- /dev/null
+++ b/clang/test/Preprocessor/nonportable-trailing-whitespace-win.c
@@ -0,0 +1,18 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: touch %t/simple.h
+// RUN: %clang_cc1 -fsyntax-only -I%t -Wnonportable-include-path -verify %s
+// REQUIRES: system-windows
+
+// On Windows, the filesystem silently strips trailing whitespace and dots
+// from filenames, so the include succeeds. We should still emit a
+// portability warning but no file-not-found error.
+
+// Trailing whitespace: warn about non-portable path, but file is found.
+#include "simple.h " // expected-warning {{non-portable path to file 'simple.h '; specified path contains trailing whitespace}}
+
+// Trailing dots: warn about non-portable path, but file is found.
+#include "simple.h." // expected-warning {{non-portable path to file 'simple.h.'; specified path contains trailing dots}}
+
+// Correct path: no diagnostics expected.
+#include "simple.h" // no-warning
diff --git a/clang/test/Preprocessor/nonportable-trailing-whitespace.c b/clang/test/Preprocessor/nonportable-trailing-whitespace.c
new file mode 100644
index 0000000000000..d35e21d0ae01b
--- /dev/null
+++ b/clang/test/Preprocessor/nonportable-trailing-whitespace.c
@@ -0,0 +1,20 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: touch %t/simple.h
+// RUN: %clang_cc1 -fsyntax-only -I%t -Wnonportable-include-path -verify %s
+// UNSUPPORTED: system-windows
+
+// On non-Windows systems, trailing whitespace and dots in include paths
+// produce both a portability warning and a file-not-found error,
+// because the filesystem treats them as part of the filename.
+
+// Trailing whitespace: warn about non-portable path, error because file not found.
+#include "simple.h " // expected-warning {{non-portable path to file 'simple.h '; specified path contains trailing whitespace}} \
+ // expected-error {{'simple.h ' file not found}}
+
+// Trailing dots: warn about non-portable path, error because file not found.
+#include "simple.h." // expected-warning {{non-portable path to file 'simple.h.'; specified path contains trailing dots}} \
+ // expected-error {{'simple.h.' file not found}}
+
+// Correct path: no diagnostics expected.
+#include "simple.h" // no-warning
>From bc4ccda0def77462fd38a6f07f77a3b800ed82c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CAmelia?= <“ameliajochna1 at gmail.com”>
Date: Tue, 7 Apr 2026 18:18:49 +0200
Subject: [PATCH 2/2] review fixes
---
clang/docs/ReleaseNotes.rst | 2 ++
clang/include/clang/Basic/DiagnosticLexKinds.td | 2 +-
clang/lib/Lex/PPDirectives.cpp | 4 ++--
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 1601be699a604..6a9bbadfb270e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -360,6 +360,8 @@ Improvements to Clang's diagnostics
- Clang now emits an error when implicitly casting a complex type to a built-in vector type. (#GH186805)
+- Extended ``-Wnonportable-include-path`` to warn about trailing whitespace and dots in ``#include`` paths. (#GH190610)
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index deb1111b46134..b7b1673d05f92 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -383,7 +383,7 @@ def pp_nonportable_system_path : NonportablePath, DefaultIgnore,
class NonportablePathTrailing : Warning<
"non-portable path to file '%0'; specified path contains trailing"
- " %select{whitespace|dots}1">;
+ " %select{whitespace|dot}1">;
def pp_nonportable_path_trailing : NonportablePathTrailing,
InGroup<NonportableIncludePath>;
def pp_nonportable_system_path_trailing : NonportablePathTrailing, DefaultIgnore,
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 8fee9104797f8..30a2ddc793123 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2380,8 +2380,8 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
// error.
if (Filename.empty())
return {ImportAction::None};
- if (Filename.ends_with(" ") || Filename.ends_with(".")) {
- unsigned Selection = Filename.ends_with(".") ? 1 : 0;
+ if (Filename.ends_with(' ') || Filename.ends_with('.')) {
+ unsigned Selection = Filename.ends_with('.') ? 1 : 0;
Diag(FilenameTok, diag::pp_nonportable_path_trailing)
<< Filename << Selection;
}
More information about the cfe-commits
mailing list