[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
Mon Apr 6 08:33:53 PDT 2026
https://github.com/ameliajochna updated https://github.com/llvm/llvm-project/pull/190610
>From e1fc854af1fab1a2ba28d354f28d45526a5e5e58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CAmelia?= <“ameliajochna1 at gmail.com”>
Date: Mon, 6 Apr 2026 17:33:04 +0200
Subject: [PATCH] [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 | 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
More information about the cfe-commits
mailing list