https://github.com/ameliajochna created 
https://github.com/llvm/llvm-project/pull/190610

This patch extends -Wnonportable-include-path to detect and warn about trailing 
whitespace and dots in #include directives. Such paths are non-portable and can 
lead to build failures on different operating systems.

The warning is triggered when an include filename ends with a space or a dot, 
which is common when copy-pasting paths or due to typos.


>From befa35cfdee0c694f717146627441f4082e592f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CAmelia?= <“[email protected]”>
Date: Mon, 6 Apr 2026 16:10:20 +0200
Subject: [PATCH] [Clang] Add warning for non-portable include paths with
 trailing whitespace or dots

This patch extends -Wnonportable-include-path to detect and warn about
trailing whitespace and dots in #include directives. Such paths are
non-portable and can lead to build failures on different operating systems.

The warning is triggered when an include filename ends with a space or a dot,
which is common when copy-pasting paths or due to typos.

Differential Revision: https://reviews.llvm.org/DXXXXX
---
 clang/include/clang/Basic/DiagnosticGroups.td |   2 +
 .../include/clang/Basic/DiagnosticLexKinds.td |  14 +-
 clang/lib/Lex/PPDirectives.cpp                | 280 ++++++++++--------
 .../nonportable-trailing-whitespace.c         |  15 +
 4 files changed, 180 insertions(+), 131 deletions(-)
 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..036e6f2556529 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -111,11 +111,7 @@ enum MacroDiag {
 
 /// Enumerates possible %select values for the pp_err_elif_after_else and
 /// pp_err_elif_without_if diagnostics.
-enum PPElifDiag {
-  PED_Elif,
-  PED_Elifdef,
-  PED_Elifndef
-};
+enum PPElifDiag { PED_Elif, PED_Elifdef, PED_Elifndef };
 
 static bool isFeatureTestMacro(StringRef MacroName) {
   // list from:
@@ -227,13 +223,14 @@ static MacroDiag shouldWarnOnMacroUndef(Preprocessor &PP, 
IdentifierInfo *II) {
 // and Boost headers. Improper case for these #includes is a
 // potential portability issue.
 static bool warnByDefaultOnWrongCase(StringRef Include) {
-  // If the first component of the path is "boost", treat this like a standard 
header
-  // for the purposes of diagnostics.
+  // If the first component of the path is "boost", treat this like a standard
+  // header for the purposes of diagnostics.
   if (::llvm::sys::path::begin(Include)->equals_insensitive("boost"))
     return true;
 
   // "condition_variable" is the longest standard header name at 18 characters.
-  // If the include file name is longer than that, it can't be a standard 
header.
+  // If the include file name is longer than that, it can't be a standard
+  // header.
   static const size_t MaxStdHeaderNameLen = 18u;
   if (Include.size() > MaxStdHeaderNameLen)
     return false;
@@ -403,8 +400,7 @@ bool Preprocessor::CheckMacroName(Token &MacroNameTok, 
MacroUse isDefineUndef,
     MacroDiag D = MD_NoWarn;
     if (isDefineUndef == MU_Define) {
       D = shouldWarnOnMacroDef(*this, II);
-    }
-    else if (isDefineUndef == MU_Undef)
+    } else if (isDefineUndef == MU_Undef)
       D = shouldWarnOnMacroUndef(*this, II);
     if (D == MD_KeywordDef) {
       // We do not want to warn on some patterns widely used in configuration
@@ -484,7 +480,7 @@ Preprocessor::CheckEndOfDirective(StringRef DirType, bool 
EnableMacros,
 
   // There should be no tokens after the directive, but we allow them as an
   // extension.
-  while (Tmp.is(tok::comment))  // Skip comments in -C mode.
+  while (Tmp.is(tok::comment)) // Skip comments in -C mode.
     ReadNextTok(&Preprocessor::LexUnexpandedToken);
 
   if (Tmp.is(tok::eod))
@@ -497,7 +493,7 @@ Preprocessor::CheckEndOfDirective(StringRef DirType, bool 
EnableMacros,
   FixItHint Hint;
   if ((LangOpts.GNUMode || LangOpts.C99 || LangOpts.CPlusPlus) &&
       !CurTokenLexer)
-    Hint = FixItHint::CreateInsertion(Tmp.getLocation(),"//");
+    Hint = FixItHint::CreateInsertion(Tmp.getLocation(), "//");
 
   unsigned DiagID = diag::ext_pp_extra_tokens_at_eol;
   // C++20 import or module directive has no '#' prefix.
@@ -513,11 +509,11 @@ void Preprocessor::SuggestTypoedDirective(const Token 
&Tok,
                                           StringRef Directive) const {
   // If this is a `.S` file, treat unknown # directives as non-preprocessor
   // directives.
-  if (getLangOpts().AsmPreprocessor) return;
+  if (getLangOpts().AsmPreprocessor)
+    return;
 
-  std::vector<StringRef> Candidates = {
-      "if", "ifdef", "ifndef", "elif", "else", "endif"
-  };
+  std::vector<StringRef> Candidates = {"if",   "ifdef", "ifndef",
+                                       "elif", "else",  "endif"};
   if (LangOpts.C23 || LangOpts.CPlusPlus23)
     Candidates.insert(Candidates.end(), {"elifdef", "elifndef"});
 
@@ -708,7 +704,8 @@ void 
Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
     // directive mode.  Tell the lexer this so any newlines we see will be
     // converted into an EOD token (this terminates the macro).
     CurPPLexer->ParsingPreprocessorDirective = true;
-    if (CurLexer) CurLexer->SetKeepWhitespaceMode(false);
+    if (CurLexer)
+      CurLexer->SetKeepWhitespaceMode(false);
 
     assert(Tok.is(tok::hash));
     const char *Hashptr = CurLexer->getBufferLocation() - Tok.getLength();
@@ -722,7 +719,8 @@ void 
Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
     if (Tok.isNot(tok::raw_identifier)) {
       CurPPLexer->ParsingPreprocessorDirective = false;
       // Restore comment saving mode.
-      if (CurLexer) CurLexer->resetExtendedTokenMode();
+      if (CurLexer)
+        CurLexer->resetExtendedTokenMode();
       continue;
     }
 
@@ -734,11 +732,12 @@ void 
Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
     StringRef RI = Tok.getRawIdentifier();
 
     char FirstChar = RI[0];
-    if (FirstChar >= 'a' && FirstChar <= 'z' &&
-        FirstChar != 'i' && FirstChar != 'e') {
+    if (FirstChar >= 'a' && FirstChar <= 'z' && FirstChar != 'i' &&
+        FirstChar != 'e') {
       CurPPLexer->ParsingPreprocessorDirective = false;
       // Restore comment saving mode.
-      if (CurLexer) CurLexer->resetExtendedTokenMode();
+      if (CurLexer)
+        CurLexer->resetExtendedTokenMode();
       continue;
     }
 
@@ -755,7 +754,8 @@ void 
Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
       if (IdLen >= 20) {
         CurPPLexer->ParsingPreprocessorDirective = false;
         // Restore comment saving mode.
-        if (CurLexer) CurLexer->resetExtendedTokenMode();
+        if (CurLexer)
+          CurLexer->resetExtendedTokenMode();
         continue;
       }
       memcpy(DirectiveBuf, &DirectiveStr[0], IdLen);
@@ -765,24 +765,25 @@ void 
Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
     if (Directive.starts_with("if")) {
       StringRef Sub = Directive.substr(2);
       if (Sub.empty() ||   // "if"
-          Sub == "def" ||   // "ifdef"
-          Sub == "ndef") {  // "ifndef"
+          Sub == "def" ||  // "ifdef"
+          Sub == "ndef") { // "ifndef"
         // We know the entire #if/#ifdef/#ifndef block will be skipped, don't
         // bother parsing the condition.
         DiscardUntilEndOfDirective();
-        CurPPLexer->pushConditionalLevel(Tok.getLocation(), 
/*wasskipping*/true,
-                                       /*foundnonskip*/false,
-                                       /*foundelse*/false);
+        CurPPLexer->pushConditionalLevel(Tok.getLocation(),
+                                         /*wasskipping*/ true,
+                                         /*foundnonskip*/ false,
+                                         /*foundelse*/ false);
       } else {
         SuggestTypoedDirective(Tok, Directive);
       }
     } else if (Directive[0] == 'e') {
       StringRef Sub = Directive.substr(1);
-      if (Sub == "ndif") {  // "endif"
+      if (Sub == "ndif") { // "endif"
         PPConditionalInfo CondInfo;
         CondInfo.WasSkipping = true; // Silence bogus warning.
         bool InCond = CurPPLexer->popConditionalLevel(CondInfo);
-        (void)InCond;  // Silence warning in no-asserts mode.
+        (void)InCond; // Silence warning in no-asserts mode.
         assert(!InCond && "Can't be skipping if not in a conditional!");
 
         // If we popped the outermost skipping block, we're done skipping!
@@ -828,9 +829,9 @@ void 
Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
             Callbacks->Else(Tok.getLocation(), CondInfo.IfLoc);
           break;
         } else {
-          DiscardUntilEndOfDirective();  // C99 6.10p4.
+          DiscardUntilEndOfDirective(); // C99 6.10p4.
         }
-      } else if (Sub == "lif") {  // "elif".
+      } else if (Sub == "lif") { // "elif".
         PPConditionalInfo &CondInfo = CurPPLexer->peekConditionalLevel();
 
         if (!CondInfo.WasSkipping)
@@ -954,7 +955,8 @@ void 
Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
 
     CurPPLexer->ParsingPreprocessorDirective = false;
     // Restore comment saving mode.
-    if (CurLexer) CurLexer->resetExtendedTokenMode();
+    if (CurLexer)
+      CurLexer->resetExtendedTokenMode();
   }
 
   // Finally, if we are out of the conditional (saw an #endif or ran off the 
end
@@ -1268,14 +1270,12 @@ OptionalFileEntryRef 
Preprocessor::LookupEmbedFile(StringRef Filename,
 class Preprocessor::ResetMacroExpansionHelper {
 public:
   ResetMacroExpansionHelper(Preprocessor *pp)
-    : PP(pp), save(pp->DisableMacroExpansion) {
+      : PP(pp), save(pp->DisableMacroExpansion) {
     if (pp->MacroExpansionInDirectivesOverride)
       pp->DisableMacroExpansion = false;
   }
 
-  ~ResetMacroExpansionHelper() {
-    PP->DisableMacroExpansion = save;
-  }
+  ~ResetMacroExpansionHelper() { PP->DisableMacroExpansion = save; }
 
 private:
   Preprocessor *PP;
@@ -1320,7 +1320,8 @@ void Preprocessor::HandleDirective(Token &Result) {
   // mode.  Tell the lexer this so any newlines we see will be converted into 
an
   // EOD token (which terminates the directive).
   CurPPLexer->ParsingPreprocessorDirective = true;
-  if (CurLexer) CurLexer->SetKeepWhitespaceMode(false);
+  if (CurLexer)
+    CurLexer->SetKeepWhitespaceMode(false);
 
   bool ImmediatelyAfterTopLevelIfndef =
       CurPPLexer->MIOpt.getImmediatelyAfterTopLevelIfndef();
@@ -1331,7 +1332,8 @@ void Preprocessor::HandleDirective(Token &Result) {
   // We are about to read a token.  For the multiple-include optimization FA to
   // work, we have to remember if we had read any tokens *before* this
   // pp-directive.
-  bool ReadAnyTokensBeforeDirective 
=CurPPLexer->MIOpt.getHasReadAnyTokensVal();
+  bool ReadAnyTokensBeforeDirective =
+      CurPPLexer->MIOpt.getHasReadAnyTokensVal();
 
   // Save the directive-introducing token('#' and import/module in C++20) in
   // case we need to return it later.
@@ -1392,14 +1394,14 @@ void Preprocessor::HandleDirective(Token &Result) {
     // optimization, i.e. allow the null directive to appear outside of the
     // include guard and still enable the multiple-include optimization.
     CurPPLexer->MIOpt.SetReadToken(ReadAnyTokensBeforeDirective);
-    return;   // null directive.
+    return; // null directive.
   case tok::code_completion:
     setCodeCompletionReached();
     if (CodeComplete)
       CodeComplete->CodeCompleteDirective(
-                                    CurPPLexer->getConditionalStackDepth() > 
0);
+          CurPPLexer->getConditionalStackDepth() > 0);
     return;
-  case tok::numeric_constant:  // # 7  GNU line marker directive.
+  case tok::numeric_constant: // # 7  GNU line marker directive.
     // In a .S file "# 4" may be a comment so don't treat it as a preprocessor
     // directive. However do permit it in the predefines file, as we use line
     // markers to mark the builtin macros as being in a system header.
@@ -1409,11 +1411,13 @@ void Preprocessor::HandleDirective(Token &Result) {
     return HandleDigitDirective(Result);
   default:
     IdentifierInfo *II = Result.getIdentifierInfo();
-    if (!II) break; // Not an identifier.
+    if (!II)
+      break; // Not an identifier.
 
     // Ask what the preprocessor keyword ID is.
     switch (II->getPPKeywordID()) {
-    default: break;
+    default:
+      break;
     // C99 6.10.1 - Conditional Inclusion.
     case tok::pp_if:
       return HandleIfDirective(Result, Introducer,
@@ -1494,10 +1498,10 @@ void Preprocessor::HandleDirective(Token &Result) {
     case tok::pp_embed:
       return HandleEmbedDirective(Introducer.getLocation(), Result);
     case tok::pp_assert:
-      //isExtension = true;  // FIXME: implement #assert
+      // isExtension = true;  // FIXME: implement #assert
       break;
     case tok::pp_unassert:
-      //isExtension = true;  // FIXME: implement #unassert
+      // isExtension = true;  // FIXME: implement #unassert
       break;
 
     case tok::pp___public_macro:
@@ -1531,7 +1535,7 @@ void Preprocessor::HandleDirective(Token &Result) {
     // Enter this token stream so that we re-lex the tokens.  Make sure to
     // enable macro expansion, in case the token after the # is an identifier
     // that is expanded.
-    EnterTokenStream(std::move(Toks), 2, false, /*IsReinject*/false);
+    EnterTokenStream(std::move(Toks), 2, false, /*IsReinject*/ false);
     return;
   }
 
@@ -1547,9 +1551,8 @@ void Preprocessor::HandleDirective(Token &Result) {
 
 /// GetLineValue - Convert a numeric token into an unsigned value, emitting
 /// Diagnostic DiagID if it is invalid, and returning the value in Val.
-static bool GetLineValue(Token &DigitTok, unsigned &Val,
-                         unsigned DiagID, Preprocessor &PP,
-                         bool IsGNULineDirective=false) {
+static bool GetLineValue(Token &DigitTok, unsigned &Val, unsigned DiagID,
+                         Preprocessor &PP, bool IsGNULineDirective = false) {
   if (DigitTok.isNot(tok::numeric_constant)) {
     PP.Diag(DigitTok, DiagID);
 
@@ -1578,12 +1581,13 @@ static bool GetLineValue(Token &DigitTok, unsigned &Val,
 
     if (!isDigit(DigitTokBegin[i])) {
       PP.Diag(PP.AdvanceToTokenCharacter(DigitTok.getLocation(), i),
-              diag::err_pp_line_digit_sequence) << IsGNULineDirective;
+              diag::err_pp_line_digit_sequence)
+          << IsGNULineDirective;
       PP.DiscardUntilEndOfDirective();
       return true;
     }
 
-    unsigned NextVal = Val*10+(DigitTokBegin[i]-'0');
+    unsigned NextVal = Val * 10 + (DigitTokBegin[i] - '0');
     if (NextVal < Val) { // overflow.
       PP.Diag(DigitTok, DiagID);
       PP.DiscardUntilEndOfDirective();
@@ -1594,7 +1598,7 @@ static bool GetLineValue(Token &DigitTok, unsigned &Val,
 
   if (DigitTokBegin[0] == '0' && Val)
     PP.Diag(DigitTok.getLocation(), diag::warn_pp_line_decimal)
-      << IsGNULineDirective;
+        << IsGNULineDirective;
 
   return false;
 }
@@ -1614,7 +1618,7 @@ void Preprocessor::HandleLineDirective() {
 
   // Validate the number and convert it to an unsigned.
   unsigned LineNo;
-  if (GetLineValue(DigitTok, LineNo, diag::err_pp_line_requires_integer,*this))
+  if (GetLineValue(DigitTok, LineNo, diag::err_pp_line_requires_integer, 
*this))
     return;
 
   if (LineNo == 0)
@@ -1690,7 +1694,8 @@ static bool ReadLineMarkerFlags(bool &IsFileEntry, bool 
&IsFileExit,
   unsigned FlagVal;
   Token FlagTok;
   PP.Lex(FlagTok);
-  if (FlagTok.is(tok::eod)) return false;
+  if (FlagTok.is(tok::eod))
+    return false;
   if (GetLineValue(FlagTok, FlagVal, diag::err_pp_linemarker_invalid_flag, PP))
     return true;
 
@@ -1698,8 +1703,10 @@ static bool ReadLineMarkerFlags(bool &IsFileEntry, bool 
&IsFileExit,
     IsFileEntry = true;
 
     PP.Lex(FlagTok);
-    if (FlagTok.is(tok::eod)) return false;
-    if (GetLineValue(FlagTok, FlagVal, 
diag::err_pp_linemarker_invalid_flag,PP))
+    if (FlagTok.is(tok::eod))
+      return false;
+    if (GetLineValue(FlagTok, FlagVal, diag::err_pp_linemarker_invalid_flag,
+                     PP))
       return true;
   } else if (FlagVal == 2) {
     IsFileExit = true;
@@ -1708,7 +1715,7 @@ static bool ReadLineMarkerFlags(bool &IsFileEntry, bool 
&IsFileExit,
     // If we are leaving the current presumed file, check to make sure the
     // presumed include stack isn't empty!
     FileID CurFileID =
-      SM.getDecomposedExpansionLoc(FlagTok.getLocation()).first;
+        SM.getDecomposedExpansionLoc(FlagTok.getLocation()).first;
     PresumedLoc PLoc = SM.getPresumedLoc(FlagTok.getLocation());
     if (PLoc.isInvalid())
       return true;
@@ -1724,8 +1731,10 @@ static bool ReadLineMarkerFlags(bool &IsFileEntry, bool 
&IsFileExit,
     }
 
     PP.Lex(FlagTok);
-    if (FlagTok.is(tok::eod)) return false;
-    if (GetLineValue(FlagTok, FlagVal, 
diag::err_pp_linemarker_invalid_flag,PP))
+    if (FlagTok.is(tok::eod))
+      return false;
+    if (GetLineValue(FlagTok, FlagVal, diag::err_pp_linemarker_invalid_flag,
+                     PP))
       return true;
   }
 
@@ -1739,7 +1748,8 @@ static bool ReadLineMarkerFlags(bool &IsFileEntry, bool 
&IsFileExit,
   FileKind = SrcMgr::C_System;
 
   PP.Lex(FlagTok);
-  if (FlagTok.is(tok::eod)) return false;
+  if (FlagTok.is(tok::eod))
+    return false;
   if (GetLineValue(FlagTok, FlagVal, diag::err_pp_linemarker_invalid_flag, PP))
     return true;
 
@@ -1753,7 +1763,8 @@ static bool ReadLineMarkerFlags(bool &IsFileEntry, bool 
&IsFileExit,
   FileKind = SrcMgr::C_ExternCSystem;
 
   PP.Lex(FlagTok);
-  if (FlagTok.is(tok::eod)) return false;
+  if (FlagTok.is(tok::eod))
+    return false;
 
   // There are no more valid flags here.
   PP.Diag(FlagTok, diag::err_pp_linemarker_invalid_flag);
@@ -1843,8 +1854,7 @@ void Preprocessor::HandleDigitDirective(Token &DigitTok) {
 
 /// HandleUserDiagnosticDirective - Handle a #warning or #error directive.
 ///
-void Preprocessor::HandleUserDiagnosticDirective(Token &Tok,
-                                                 bool isWarning) {
+void Preprocessor::HandleUserDiagnosticDirective(Token &Tok, bool isWarning) {
   // Read the rest of the line raw.  We do this because we don't want macros
   // to be expanded and we don't require that the tokens be valid preprocessing
   // tokens.  For example, this is allowed: "#warning `   'foo".  GCC does
@@ -1923,7 +1933,7 @@ void Preprocessor::HandleMacroPublicDirective(Token &Tok) 
{
 
   // Note that this macro has now been exported.
   appendMacroDirective(II, AllocateVisibilityMacroDirective(
-                                MacroNameTok.getLocation(), 
/*isPublic=*/true));
+                               MacroNameTok.getLocation(), /*isPublic=*/true));
 }
 
 /// Handle a #private directive.
@@ -2006,13 +2016,12 @@ bool 
Preprocessor::GetIncludeFilenameSpelling(SourceLocation Loc,
   }
 
   // Skip the brackets.
-  Buffer = Buffer.substr(1, Buffer.size()-2);
+  Buffer = Buffer.substr(1, Buffer.size() - 2);
   return isAngled;
 }
 
 /// Push a token onto the token stream containing an annotation.
-void Preprocessor::EnterAnnotationToken(SourceRange Range,
-                                        tok::TokenKind Kind,
+void Preprocessor::EnterAnnotationToken(SourceRange Range, tok::TokenKind Kind,
                                         void *AnnotationVal) {
   // FIXME: Produce this as the current token directly, rather than
   // allocating a new token for it.
@@ -2214,8 +2223,8 @@ void Preprocessor::HandleIncludeDirective(SourceLocation 
HashLoc,
   case ImportAction::SkippedModuleImport:
     break;
   case ImportAction::ModuleBegin:
-    EnterAnnotationToken(SourceRange(HashLoc, EndLoc),
-                         tok::annot_module_begin, Action.ModuleForHeader);
+    EnterAnnotationToken(SourceRange(HashLoc, EndLoc), tok::annot_module_begin,
+                         Action.ModuleForHeader);
     break;
   case ImportAction::HeaderUnitImport:
     EnterAnnotationToken(SourceRange(HashLoc, EndLoc), tok::annot_header_unit,
@@ -2370,16 +2379,21 @@ Preprocessor::ImportAction 
Preprocessor::HandleHeaderIncludeOrImport(
   StringRef Filename = getSpelling(FilenameTok, FilenameBuffer);
   SourceLocation CharEnd = FilenameTok.getEndLoc();
 
-  CharSourceRange FilenameRange
-    = CharSourceRange::getCharRange(FilenameTok.getLocation(), CharEnd);
+  CharSourceRange FilenameRange =
+      CharSourceRange::getCharRange(FilenameTok.getLocation(), CharEnd);
   StringRef OriginalFilename = Filename;
   bool isAngled =
-    GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename);
+      GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename);
 
   // If GetIncludeFilenameSpelling set the start ptr to null, there was an
   // 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;
@@ -2633,7 +2647,7 @@ Preprocessor::ImportAction 
Preprocessor::HandleHeaderIncludeOrImport(
   // module corresponding to the named header.
   if (IsImportDecl && !ModuleToImport) {
     Diag(FilenameTok, diag::err_header_import_not_header_unit)
-      << OriginalFilename << File->getName();
+        << OriginalFilename << File->getName();
     return {ImportAction::None};
   }
 
@@ -2680,7 +2694,7 @@ Preprocessor::ImportAction 
Preprocessor::HandleHeaderIncludeOrImport(
 
     if (trySimplifyPath(Components, RealPathName, BackslashStyle)) {
       SmallString<128> Path;
-      Path.reserve(Name.size()+2);
+      Path.reserve(Name.size() + 2);
       Path.push_back(isAngled ? '<' : '"');
 
       const auto IsSep = [BackslashStyle](char c) {
@@ -2709,11 +2723,11 @@ Preprocessor::ImportAction 
Preprocessor::HandleHeaderIncludeOrImport(
           Path.push_back(isAngled ? '>' : '"');
           continue;
         }
-        assert(IsSep(NameWithoriginalSlashes[Path.size()-1]));
+        assert(IsSep(NameWithoriginalSlashes[Path.size() - 1]));
         do
-          Path.push_back(NameWithoriginalSlashes[Path.size()-1]);
+          Path.push_back(NameWithoriginalSlashes[Path.size() - 1]);
         while (Path.size() <= NameWithoriginalSlashes.size() &&
-               IsSep(NameWithoriginalSlashes[Path.size()-1]));
+               IsSep(NameWithoriginalSlashes[Path.size() - 1]));
       }
 
 #if defined(_WIN32)
@@ -2728,8 +2742,8 @@ Preprocessor::ImportAction 
Preprocessor::HandleHeaderIncludeOrImport(
           (FileCharacter == SrcMgr::C_User || warnByDefaultOnWrongCase(Name))
               ? diag::pp_nonportable_path
               : diag::pp_nonportable_system_path;
-      Diag(FilenameTok, DiagId) << Path <<
-        FixItHint::CreateReplacement(FilenameRange, Path);
+      Diag(FilenameTok, DiagId)
+          << Path << FixItHint::CreateReplacement(FilenameRange, Path);
     }
   }
 
@@ -2763,7 +2777,7 @@ Preprocessor::ImportAction 
Preprocessor::HandleHeaderIncludeOrImport(
   }
 
   // Check that we don't have infinite #include recursion.
-  if (IncludeMacroStack.size() == MaxAllowedIncludeStackDepth-1) {
+  if (IncludeMacroStack.size() == MaxAllowedIncludeStackDepth - 1) {
     Diag(FilenameTok, diag::err_pp_include_too_deep);
     HasReachedMaxIncludeDepth = true;
     return {ImportAction::None};
@@ -2854,7 +2868,7 @@ void Preprocessor::HandleMicrosoftImportDirective(Token 
&Tok) {
   // does, so we ignore it and error out.  However, #import can optionally have
   // trailing attributes that span multiple lines.  We're going to eat those
   // so we can continue processing from there.
-  Diag(Tok, diag::err_pp_import_directive_ms );
+  Diag(Tok, diag::err_pp_import_directive_ms);
 
   // Read tokens until we get to the end of the directive.  Note that the
   // directive can be split over multiple lines using the backslash character.
@@ -2865,7 +2879,7 @@ void Preprocessor::HandleMicrosoftImportDirective(Token 
&Tok) {
 ///
 void Preprocessor::HandleImportDirective(SourceLocation HashLoc,
                                          Token &ImportTok) {
-  if (!LangOpts.ObjC) {  // #import is standard for ObjC.
+  if (!LangOpts.ObjC) { // #import is standard for ObjC.
     if (LangOpts.MSVCCompat)
       return HandleMicrosoftImportDirective(ImportTok);
     Diag(ImportTok, diag::ext_pp_import_directive);
@@ -2909,23 +2923,22 @@ void 
Preprocessor::HandleIncludeMacrosDirective(SourceLocation HashLoc,
 /// closing ), updating MI with what we learn.  Return true if an error occurs
 /// parsing the param list.
 bool Preprocessor::ReadMacroParameterList(MacroInfo *MI, Token &Tok) {
-  SmallVector<IdentifierInfo*, 32> Parameters;
+  SmallVector<IdentifierInfo *, 32> Parameters;
 
   while (true) {
     LexUnexpandedNonComment(Tok);
     switch (Tok.getKind()) {
     case tok::r_paren:
       // Found the end of the parameter list.
-      if (Parameters.empty())  // #define FOO()
+      if (Parameters.empty()) // #define FOO()
         return false;
       // Otherwise we have #define FOO(A,)
       Diag(Tok, diag::err_pp_expected_ident_in_arg_list);
       return true;
-    case tok::ellipsis:  // #define X(... -> C99 varargs
+    case tok::ellipsis: // #define X(... -> C99 varargs
       if (!LangOpts.C99)
-        Diag(Tok, LangOpts.CPlusPlus11 ?
-             diag::warn_cxx98_compat_variadic_macro :
-             diag::ext_variadic_macro);
+        Diag(Tok, LangOpts.CPlusPlus11 ? diag::warn_cxx98_compat_variadic_macro
+                                       : diag::ext_variadic_macro);
 
       // OpenCL v1.2 s6.9.e: variadic macros are not supported.
       if (LangOpts.OpenCL && !LangOpts.OpenCLCPlusPlus) {
@@ -2943,7 +2956,7 @@ bool Preprocessor::ReadMacroParameterList(MacroInfo *MI, 
Token &Tok) {
       MI->setIsC99Varargs();
       MI->setParameterList(Parameters, BP);
       return false;
-    case tok::eod:  // #define X(
+    case tok::eod: // #define X(
       Diag(Tok, diag::err_pp_missing_rparen_in_macro_def);
       return true;
     default:
@@ -2970,15 +2983,15 @@ bool Preprocessor::ReadMacroParameterList(MacroInfo 
*MI, Token &Tok) {
       LexUnexpandedNonComment(Tok);
 
       switch (Tok.getKind()) {
-      default:          // #define X(A B
+      default: // #define X(A B
         Diag(Tok, diag::err_pp_expected_comma_in_arg_list);
         return true;
       case tok::r_paren: // #define X(A)
         MI->setParameterList(Parameters, BP);
         return false;
-      case tok::comma:  // #define X(A,
+      case tok::comma: // #define X(A,
         break;
-      case tok::ellipsis:  // #define X(A... -> GCC extension
+      case tok::ellipsis: // #define X(A... -> GCC extension
         // Diagnose extension.
         Diag(Tok, diag::ext_named_variadic_macro);
 
@@ -3245,7 +3258,7 @@ MacroInfo 
*Preprocessor::ReadOptionalMacroParameterListAndBody(
           continue;
         } else {
           Diag(Tok, diag::err_pp_stringize_not_parameter)
-            << LastTok.is(tok::hashat);
+              << LastTok.is(tok::hashat);
           return nullptr;
         }
       }
@@ -3267,8 +3280,9 @@ MacroInfo 
*Preprocessor::ReadOptionalMacroParameterListAndBody(
     if (VAOCtx.isInVAOpt()) {
       assert(Tok.is(tok::eod) && "Must be at End Of preprocessing Directive");
       Diag(Tok, diag::err_pp_expected_after)
-        << LastTok.getKind() << tok::r_paren;
-      Diag(VAOCtx.getUnmatchedOpeningParenLoc(), diag::note_matching) << 
tok::l_paren;
+          << LastTok.getKind() << tok::r_paren;
+      Diag(VAOCtx.getUnmatchedOpeningParenLoc(), diag::note_matching)
+          << tok::l_paren;
       return nullptr;
     }
   }
@@ -3305,12 +3319,14 @@ void Preprocessor::HandleDefineDirective(
 
   // If we are supposed to keep comments in #defines, reenable comment saving
   // mode.
-  if (CurLexer) CurLexer->SetCommentRetentionState(KeepMacroComments);
+  if (CurLexer)
+    CurLexer->SetCommentRetentionState(KeepMacroComments);
 
   MacroInfo *const MI = ReadOptionalMacroParameterListAndBody(
       MacroNameTok, ImmediatelyAfterHeaderGuard);
 
-  if (!MI) return;
+  if (!MI)
+    return;
 
   if (MacroShadowsKeyword &&
       !isConfigurationPattern(MacroNameTok, MI, getLangOpts())) {
@@ -3324,8 +3340,8 @@ void Preprocessor::HandleDefineDirective(
       Diag(MI->getReplacementToken(0), diag::err_paste_at_start);
       return;
     }
-    if (MI->getReplacementToken(NumTokens-1).is(tok::hashhash)) {
-      Diag(MI->getReplacementToken(NumTokens-1), diag::err_paste_at_end);
+    if (MI->getReplacementToken(NumTokens - 1).is(tok::hashhash)) {
+      Diag(MI->getReplacementToken(NumTokens - 1), diag::err_paste_at_end);
       return;
     }
   }
@@ -3334,7 +3350,7 @@ void Preprocessor::HandleDefineDirective(
   if (SkippingUntilPCHThroughHeader) {
     const MacroInfo *OtherMI = getMacroInfo(MacroNameTok.getIdentifierInfo());
     if (!OtherMI || !MI->isIdenticalTo(*OtherMI, *this,
-                             /*Syntactic=*/LangOpts.MicrosoftExt))
+                                       /*Syntactic=*/LangOpts.MicrosoftExt))
       Diag(MI->getDefinitionLoc(), diag::warn_pp_macro_def_mismatch_with_pch)
           << MacroNameTok.getIdentifierInfo();
     // Issue the diagnostic but allow the change if msvc extensions are enabled
@@ -3344,7 +3360,8 @@ void Preprocessor::HandleDefineDirective(
 
   // Finally, if this identifier already had a macro defined for it, verify 
that
   // the macro bodies are identical, and issue diagnostics if they are not.
-  if (const MacroInfo *OtherMI=getMacroInfo(MacroNameTok.getIdentifierInfo())) 
{
+  if (const MacroInfo *OtherMI =
+          getMacroInfo(MacroNameTok.getIdentifierInfo())) {
     // Final macros are hard-mode: they always warn. Even if the bodies are
     // identical. Even if they are in system headers. Even if they are things 
we
     // would silently allow in the past.
@@ -3385,9 +3402,10 @@ void Preprocessor::HandleDefineDirective(
       // Macros must be identical.  This means all tokens and whitespace
       // separation must be the same.  C99 6.10.3p2.
       else if (!OtherMI->isAllowRedefinitionsWithoutWarning() &&
-               !MI->isIdenticalTo(*OtherMI, *this, 
/*Syntactic=*/LangOpts.MicrosoftExt)) {
+               !MI->isIdenticalTo(*OtherMI, *this,
+                                  /*Syntactic=*/LangOpts.MicrosoftExt)) {
         Diag(MI->getDefinitionLoc(), diag::ext_pp_macro_redef)
-          << MacroNameTok.getIdentifierInfo();
+            << MacroNameTok.getIdentifierInfo();
         Diag(OtherMI->getDefinitionLoc(), diag::note_previous_definition);
       }
     }
@@ -3472,8 +3490,7 @@ void Preprocessor::HandleUndefDirective() {
 /// true if any tokens have been returned or pp-directives activated before 
this
 /// \#ifndef has been lexed.
 ///
-void Preprocessor::HandleIfdefDirective(Token &Result,
-                                        const Token &HashToken,
+void Preprocessor::HandleIfdefDirective(Token &Result, const Token &HashToken,
                                         bool isIfndef,
                                         bool ReadAnyTokensBeforeDirective) {
   ++NumIf;
@@ -3514,7 +3531,7 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
   }
 
   // If there is a macro, process it.
-  if (MI)  // Mark it used.
+  if (MI) // Mark it used.
     markMacroAsUsed(MI);
 
   if (Callbacks) {
@@ -3524,16 +3541,17 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
       Callbacks->Ifdef(DirectiveTok.getLocation(), MacroNameTok, MD);
   }
 
-  bool RetainExcludedCB = PPOpts.RetainExcludedConditionalBlocks &&
-    getSourceManager().isInMainFile(DirectiveTok.getLocation());
+  bool RetainExcludedCB =
+      PPOpts.RetainExcludedConditionalBlocks &&
+      getSourceManager().isInMainFile(DirectiveTok.getLocation());
 
   // Should we include the stuff contained by this directive?
   if (PPOpts.SingleFileParseMode && !MI) {
     // In 'single-file-parse mode' undefined identifiers trigger parsing of all
     // the directive blocks.
     CurPPLexer->pushConditionalLevel(DirectiveTok.getLocation(),
-                                     /*wasskip*/false, /*foundnonskip*/false,
-                                     /*foundelse*/false);
+                                     /*wasskip*/ false, /*foundnonskip*/ false,
+                                     /*foundelse*/ false);
   } else if (PPOpts.SingleModuleParseMode && !MI) {
     // In 'single-module-parse mode' undefined identifiers trigger skipping of
     // all the directive blocks. We lie here and set FoundNonSkipPortion so 
that
@@ -3544,8 +3562,8 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
   } else if (!MI == isIfndef || RetainExcludedCB) {
     // Yes, remember that we are inside a conditional, then lex the next token.
     CurPPLexer->pushConditionalLevel(DirectiveTok.getLocation(),
-                                     /*wasskip*/false, /*foundnonskip*/true,
-                                     /*foundelse*/false);
+                                     /*wasskip*/ false, /*foundnonskip*/ true,
+                                     /*foundelse*/ false);
   } else {
     // No, skip the contents of this block.
     SkipExcludedConditionalBlock(HashToken.getLocation(),
@@ -3557,8 +3575,7 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
 
 /// HandleIfDirective - Implements the \#if directive.
 ///
-void Preprocessor::HandleIfDirective(Token &IfToken,
-                                     const Token &HashToken,
+void Preprocessor::HandleIfDirective(Token &IfToken, const Token &HashToken,
                                      bool ReadAnyTokensBeforeDirective) {
   ++NumIf;
 
@@ -3586,15 +3603,17 @@ void Preprocessor::HandleIfDirective(Token &IfToken,
         IfToken.getLocation(), DER.ExprRange,
         (ConditionalTrue ? PPCallbacks::CVK_True : PPCallbacks::CVK_False));
 
-  bool RetainExcludedCB = PPOpts.RetainExcludedConditionalBlocks &&
-    getSourceManager().isInMainFile(IfToken.getLocation());
+  bool RetainExcludedCB =
+      PPOpts.RetainExcludedConditionalBlocks &&
+      getSourceManager().isInMainFile(IfToken.getLocation());
 
   // Should we include the stuff contained by this directive?
   if (PPOpts.SingleFileParseMode && DER.IncludedUndefinedIds) {
     // In 'single-file-parse mode' undefined identifiers trigger parsing of all
     // the directive blocks.
-    CurPPLexer->pushConditionalLevel(IfToken.getLocation(), /*wasskip*/false,
-                                     /*foundnonskip*/false, 
/*foundelse*/false);
+    CurPPLexer->pushConditionalLevel(IfToken.getLocation(), /*wasskip*/ false,
+                                     /*foundnonskip*/ false,
+                                     /*foundelse*/ false);
   } else if (PPOpts.SingleModuleParseMode && DER.IncludedUndefinedIds) {
     // In 'single-module-parse mode' undefined identifiers trigger skipping of
     // all the directive blocks. We lie here and set FoundNonSkipPortion so 
that
@@ -3604,8 +3623,9 @@ void Preprocessor::HandleIfDirective(Token &IfToken,
                                  /*FoundElse=*/false);
   } else if (ConditionalTrue || RetainExcludedCB) {
     // Yes, remember that we are inside a conditional, then lex the next token.
-    CurPPLexer->pushConditionalLevel(IfToken.getLocation(), /*wasskip*/false,
-                                   /*foundnonskip*/true, /*foundelse*/false);
+    CurPPLexer->pushConditionalLevel(IfToken.getLocation(), /*wasskip*/ false,
+                                     /*foundnonskip*/ true,
+                                     /*foundelse*/ false);
   } else {
     // No, skip the contents of this block.
     SkipExcludedConditionalBlock(HashToken.getLocation(), 
IfToken.getLocation(),
@@ -3659,19 +3679,21 @@ void Preprocessor::HandleElseDirective(Token &Result, 
const Token &HashToken) {
     CurPPLexer->MIOpt.EnterTopLevelConditional();
 
   // If this is a #else with a #else before it, report the error.
-  if (CI.FoundElse) Diag(Result, diag::pp_err_else_after_else);
+  if (CI.FoundElse)
+    Diag(Result, diag::pp_err_else_after_else);
 
   if (Callbacks)
     Callbacks->Else(Result.getLocation(), CI.IfLoc);
 
   bool RetainExcludedCB = PPOpts.RetainExcludedConditionalBlocks &&
-    getSourceManager().isInMainFile(Result.getLocation());
+                          
getSourceManager().isInMainFile(Result.getLocation());
 
   if ((PPOpts.SingleFileParseMode && !CI.FoundNonSkip) || RetainExcludedCB) {
     // In 'single-file-parse mode' undefined identifiers trigger parsing of all
     // the directive blocks.
-    CurPPLexer->pushConditionalLevel(CI.IfLoc, /*wasskip*/false,
-                                     /*foundnonskip*/false, /*foundelse*/true);
+    CurPPLexer->pushConditionalLevel(CI.IfLoc, /*wasskip*/ false,
+                                     /*foundnonskip*/ false,
+                                     /*foundelse*/ true);
     return;
   }
 
@@ -3744,14 +3766,16 @@ void Preprocessor::HandleElifFamilyDirective(Token 
&ElifToken,
     }
   }
 
-  bool RetainExcludedCB = PPOpts.RetainExcludedConditionalBlocks &&
-    getSourceManager().isInMainFile(ElifToken.getLocation());
+  bool RetainExcludedCB =
+      PPOpts.RetainExcludedConditionalBlocks &&
+      getSourceManager().isInMainFile(ElifToken.getLocation());
 
   if ((PPOpts.SingleFileParseMode && !CI.FoundNonSkip) || RetainExcludedCB) {
     // In 'single-file-parse mode' undefined identifiers trigger parsing of all
     // the directive blocks.
-    CurPPLexer->pushConditionalLevel(ElifToken.getLocation(), /*wasskip*/false,
-                                     /*foundnonskip*/false, 
/*foundelse*/false);
+    CurPPLexer->pushConditionalLevel(ElifToken.getLocation(), /*wasskip*/ 
false,
+                                     /*foundnonskip*/ false,
+                                     /*foundelse*/ false);
     return;
   }
 
diff --git a/clang/test/Preprocessor/nonportable-trailing-whitespace.c 
b/clang/test/Preprocessor/nonportable-trailing-whitespace.c
new file mode 100644
index 0000000000000..4b1b62a6a2094
--- /dev/null
+++ b/clang/test/Preprocessor/nonportable-trailing-whitespace.c
@@ -0,0 +1,15 @@
+// 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
+
+// Trailing whitespace in include path should warn.
+#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 in include path should warn.
+#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 should not produce any warnings.
+#include "simple.h" // no-warning
\ No newline at end of file

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to