https://github.com/rosefromthedead updated https://github.com/llvm/llvm-project/pull/186770
>From 2f77ea965125e60481e81c9a14f3f0931340232a Mon Sep 17 00:00:00 2001 From: Rose Hudson <[email protected]> Date: Wed, 25 Mar 2026 10:15:01 +0000 Subject: [PATCH 1/5] [clang][Lex] add -Wnonportable-include-path-separator Emit an warning when #include paths contain backslashes, with a fixit to convert them all to '/'. This can help users that build only on Windows to automatically make their #includes more portable. The warning is off by default due to being noisy and not always desirable. --- clang/docs/ReleaseNotes.rst | 6 ++++++ .../include/clang/Basic/DiagnosticLexKinds.td | 3 +++ clang/include/clang/Basic/SourceManager.h | 9 +++++++++ clang/lib/Lex/PPDirectives.cpp | 11 +++++++++++ clang/test/Lexer/backslash-include-win.c | 19 +++++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 clang/test/Lexer/backslash-include-win.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0dbe667e4f07a..3ee154b7028d0 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -323,6 +323,12 @@ Improvements to Clang's diagnostics ``-Wunused-private-field`` no longer emits a warning for annotated private fields. +- Added ``-Wnonportable-include-path-separator`` (off by default) to catch + #include directives that use backslashes as a path separator. The warning + includes a FixIt to change all the backslashes to forward slashes, so that the + code can automatically be made portable to other host platforms that don't + support backslashes. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 5eceeced311f2..3525cc91d6f61 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -380,6 +380,9 @@ def pp_nonportable_path : NonportablePath, InGroup<DiagGroup<"nonportable-include-path">>; def pp_nonportable_system_path : NonportablePath, DefaultIgnore, InGroup<DiagGroup<"nonportable-system-include-path">>; +def pp_nonportable_path_separator : Warning< + "non-portable path to file '%0'; specified path contains backslashes">, + DefaultIgnore, InGroup<DiagGroup<"nonportable-include-path-separator">>; def pp_pragma_once_in_main_file : Warning<"#pragma once in main file">, InGroup<DiagGroup<"pragma-once-outside-header">>; diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h index bc9e97863556d..4217b8683da1e 100644 --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -1526,6 +1526,15 @@ class SourceManager : public RefCountedBase<SourceManager> { return Filename == "<scratch space>"; } + /// Returns whether \p Loc is located in a <module-includes> file. + bool isWrittenInModuleIncludes(SourceLocation Loc) const { + PresumedLoc Presumed = getPresumedLoc(Loc); + if (Presumed.isInvalid()) + return false; + StringRef Filename(Presumed.getFilename()); + return Filename == "<module-includes>"; + } + /// Returns whether \p Loc is located in a built-in or command line source. bool isInPredefinedFile(SourceLocation Loc) const { PresumedLoc Presumed = getPresumedLoc(Loc); diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index c89402fa137c0..65a7438eca130 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -2728,6 +2728,17 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport( Diag(FilenameTok, DiagId) << Path << FixItHint::CreateReplacement(FilenameRange, Path); } + + bool SuppressBackslashDiag = + FilenameLoc.isMacroID() || + SourceMgr.isWrittenInBuiltinFile(FilenameLoc) || + SourceMgr.isWrittenInModuleIncludes(FilenameLoc); + if (!SuppressBackslashDiag && OriginalFilename.contains('\\')) { + std::string SuggestedPath = OriginalFilename.str(); + std::replace(SuggestedPath.begin(), SuggestedPath.end(), '\\', '/'); + Diag(FilenameTok, diag::pp_nonportable_path_separator) + << Name << FixItHint::CreateReplacement(FilenameRange, SuggestedPath); + } } switch (Action) { diff --git a/clang/test/Lexer/backslash-include-win.c b/clang/test/Lexer/backslash-include-win.c new file mode 100644 index 0000000000000..31e282c48e69c --- /dev/null +++ b/clang/test/Lexer/backslash-include-win.c @@ -0,0 +1,19 @@ +// REQUIRES: system-windows +// RUN: mkdir -p %t/backslash +// RUN: cp %S/Inputs/case-insensitive-include.h %t/backslash/case-insensitive-include.h +// RUN: %clang_cc1 -fsyntax-only -Wnonportable-include-path-separator -I%t %s 2>&1 | FileCheck %s + +#include "backslash\case-insensitive-include.h" +// CHECK: non-portable path to file +// CHECK: specified path contains backslashes +// CHECK: "backslash/case-insensitive-include.h" + +// Despite fixing the same span, nonportable-include-path is still a separate diagnostic +// that can fire at the same time. +#include "backslash\CASE-insensitive-include.h" +// CHECK: non-portable path to file +// CHECK: specified path differs in case from file name on disk +// CHECK: "backslash\case-insensitive-include.h" +// CHECK: non-portable path to file +// CHECK: specified path contains backslashes +// CHECK: "backslash/CASE-insensitive-include.h" >From 5fcefb07ec2569535b78ab5e4234c39fc3478f79 Mon Sep 17 00:00:00 2001 From: Rose Hudson <[email protected]> Date: Wed, 1 Apr 2026 13:53:51 +0100 Subject: [PATCH 2/5] make it a subgroup of -Wnonportable-include-path --- clang/include/clang/Basic/DiagnosticGroups.td | 3 +++ clang/include/clang/Basic/DiagnosticLexKinds.td | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index e440c9d2fb982..6035da0eba182 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -176,6 +176,9 @@ def NullConversion : DiagGroup<"null-conversion">; def ImplicitConversionFloatingPointToBool : DiagGroup<"implicit-conversion-floating-point-to-bool">; def ObjCLiteralConversion : DiagGroup<"objc-literal-conversion">; +def NonportableIncludePath : DiagGroup<"nonportable-include-path", + [NonportableIncludePathSeparator]>; +def NonportableIncludePathSeparator : DiagGroup<"nonportable-include-path-separator">; def MacroRedefined : DiagGroup<"macro-redefined">; def BuiltinMacroRedefined : DiagGroup<"builtin-macro-redefined">; def BuiltinRequiresHeader : DiagGroup<"builtin-requires-header">; diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 3525cc91d6f61..69f53e5a5e4ce 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -377,12 +377,12 @@ 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">>; def pp_nonportable_path_separator : Warning< "non-portable path to file '%0'; specified path contains backslashes">, - DefaultIgnore, InGroup<DiagGroup<"nonportable-include-path-separator">>; + DefaultIgnore, InGroup<NonportableIncludePathSeparator>; def pp_pragma_once_in_main_file : Warning<"#pragma once in main file">, InGroup<DiagGroup<"pragma-once-outside-header">>; >From a92feeb78686e4daecbe9066b24115ae6d98ff60 Mon Sep 17 00:00:00 2001 From: Rose Hudson <[email protected]> Date: Wed, 1 Apr 2026 14:06:45 +0100 Subject: [PATCH 3/5] fix tablegen mistake --- clang/include/clang/Basic/DiagnosticGroups.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 827d4ca86a6b3..130f85a4d8c35 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -176,9 +176,9 @@ def NullConversion : DiagGroup<"null-conversion">; def ImplicitConversionFloatingPointToBool : DiagGroup<"implicit-conversion-floating-point-to-bool">; def ObjCLiteralConversion : DiagGroup<"objc-literal-conversion">; +def NonportableIncludePathSeparator : DiagGroup<"nonportable-include-path-separator">; def NonportableIncludePath : DiagGroup<"nonportable-include-path", [NonportableIncludePathSeparator]>; -def NonportableIncludePathSeparator : DiagGroup<"nonportable-include-path-separator">; def MacroRedefined : DiagGroup<"macro-redefined">; def BuiltinMacroRedefined : DiagGroup<"builtin-macro-redefined">; def BuiltinRequiresHeader : DiagGroup<"builtin-requires-header">; >From b255c6c94e5e719071a94290fe68f6d1a3437389 Mon Sep 17 00:00:00 2001 From: Rose Hudson <[email protected]> Date: Tue, 7 Apr 2026 10:19:44 +0100 Subject: [PATCH 4/5] test that the diagnostic can be disabled separately from its group --- clang/test/Lexer/backslash-include-win.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/clang/test/Lexer/backslash-include-win.c b/clang/test/Lexer/backslash-include-win.c index 31e282c48e69c..a07325550093f 100644 --- a/clang/test/Lexer/backslash-include-win.c +++ b/clang/test/Lexer/backslash-include-win.c @@ -1,19 +1,22 @@ // REQUIRES: system-windows // RUN: mkdir -p %t/backslash // RUN: cp %S/Inputs/case-insensitive-include.h %t/backslash/case-insensitive-include.h -// RUN: %clang_cc1 -fsyntax-only -Wnonportable-include-path-separator -I%t %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -Wnonportable-include-path-separator -I%t %s 2>&1 | FileCheck --check-prefixes=CHECK-ENABLED,CHECK-ALL %s +// RUN: %clang_cc1 -fsyntax-only -Wnonportable-include-path -Wno-nonportable-include-path-separator -I%t %s 2>&1 | FileCheck --check-prefix=CHECK-DISABLED,CHECK-ALL %s #include "backslash\case-insensitive-include.h" -// CHECK: non-portable path to file -// CHECK: specified path contains backslashes -// CHECK: "backslash/case-insensitive-include.h" +// CHECK-ENABLED: non-portable path to file +// CHECK-ENABLED: specified path contains backslashes +// CHECK-ENABLED: "backslash/case-insensitive-include.h" +// CHECK-DISABLED-NOT: non-portable path to file // Despite fixing the same span, nonportable-include-path is still a separate diagnostic // that can fire at the same time. #include "backslash\CASE-insensitive-include.h" -// CHECK: non-portable path to file -// CHECK: specified path differs in case from file name on disk -// CHECK: "backslash\case-insensitive-include.h" -// CHECK: non-portable path to file -// CHECK: specified path contains backslashes -// CHECK: "backslash/CASE-insensitive-include.h" +// CHECK-ALL: non-portable path to file +// CHECK-ALL: specified path differs in case from file name on disk +// CHECK-ALL: "backslash\case-insensitive-include.h" +// CHECK-ENABLED: non-portable path to file +// CHECK-ENABLED: specified path contains backslashes +// CHECK-ENABLED: "backslash/CASE-insensitive-include.h" +// CHECK-DISABLED-NOT: non-portable path to file >From 3d273ffcd5ae250feacfa9e59bda895dc1c6b079 Mon Sep 17 00:00:00 2001 From: Rose Hudson <[email protected]> Date: Tue, 7 Apr 2026 13:51:58 +0100 Subject: [PATCH 5/5] std::replace -> llvm::replace --- clang/lib/Lex/PPDirectives.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 1fa4de0edac60..172c2fa57db00 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -2738,7 +2738,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport( SourceMgr.isWrittenInModuleIncludes(FilenameLoc); if (!SuppressBackslashDiag && OriginalFilename.contains('\\')) { std::string SuggestedPath = OriginalFilename.str(); - std::replace(SuggestedPath.begin(), SuggestedPath.end(), '\\', '/'); + llvm::replace(SuggestedPath, '\\', '/'); Diag(FilenameTok, diag::pp_nonportable_path_separator) << Name << FixItHint::CreateReplacement(FilenameRange, SuggestedPath); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
