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?= <“[email protected]”> 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?= <“[email protected]”> 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; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
