https://github.com/cor3ntin created https://github.com/llvm/llvm-project/pull/211512
We treated line directives as being unevaluated string literal. However - The standard has no such restriction - or rather the standard has different, unclear restriction (wg21.link/P2693) - Because of Windows paths, it's common for "C:\foo\bar" to be used, and these should not form escape sequences. To remain consistent with other implementations, we do not allow `#line 1 <>` at this time. We do however support `#line 1 ""` to avoid a breaking change. Fixes a regression introduced by #201413 >From 5498ac5dac4b6753a224cff8df46896cd391c38b Mon Sep 17 00:00:00 2001 From: Corentin Jabot <[email protected]> Date: Thu, 23 Jul 2026 11:51:19 +0200 Subject: [PATCH] [Clang] Treat line directives as header names. We treated line directives as being unevaluated string literal. However - The standard has no such restriction - or rather the standard has different, unclear restriction (wg21.link/P2693) - Because of Windows paths, it's common for "C:\foo\bar" to be used, and these should not form escape sequences. To remain consistent with other implementations, we do not allow `#line 1 <>` at this time. We do however support `#line 1 ""` to avoid a breaking change. Fix a regression introduced by #201413 --- clang/lib/Lex/PPDirectives.cpp | 70 ++++++++----------- .../test/Frontend/linemarker-invalid-escape.c | 5 -- .../Parser/cxx11-user-defined-literals.cpp | 4 +- .../test/Preprocessor/line-directive-output.c | 14 +++- clang/test/Preprocessor/line-directive.c | 4 +- 5 files changed, 45 insertions(+), 52 deletions(-) delete mode 100644 clang/test/Frontend/linemarker-invalid-escape.c diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index c161f6a03593e..46649eae044b2 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1599,6 +1599,21 @@ static bool GetLineValue(Token &DigitTok, unsigned &Val, return false; } +// Unlike header-names, line directive only support filenames in double quotes +// but does support empty filenames. +static void GetFilenameSpellingForLineDirective(const Preprocessor &PP, + SourceLocation Loc, + StringRef &Buffer) { + // Get the text form of the filename. + assert(!Buffer.empty() && "Can't have tokens with empty spellings!"); + if (Buffer.size() < 2 || Buffer.front() != '"' || Buffer.back() != '"') { + PP.Diag(Loc, diag::err_pp_line_invalid_filename); + Buffer = StringRef(); + return; + } + Buffer = Buffer.substr(1, Buffer.size() - 2); +} + /// Handle a \#line directive: C99 6.10.4. /// /// The two acceptable forms are: @@ -1632,35 +1647,21 @@ void Preprocessor::HandleLineDirective() { int FilenameID = -1; Token StrTok; - Lex(StrTok); + LexHeaderName(StrTok); // If the StrTok is "eod", then it wasn't present. Otherwise, it must be a // string followed by eod. if (StrTok.is(tok::eod)) ; // ok - else if (StrTok.isNot(tok::string_literal)) { + else if (StrTok.isNot(tok::header_name)) { Diag(StrTok, diag::err_pp_line_invalid_filename); DiscardUntilEndOfDirective(); return; - } else if (StrTok.hasUDSuffix()) { - Diag(StrTok, diag::err_invalid_string_udl); - DiscardUntilEndOfDirective(); - return; } else { - // Parse and validate the string, converting it into a unique ID. - StringLiteralParser Literal(StrTok, *this, - StringLiteralEvalMethod::Unevaluated); - assert(Literal.isOrdinary() && "Didn't allow wide strings in"); - if (Literal.hadError) { - DiscardUntilEndOfDirective(); - return; - } - if (Literal.Pascal) { - Diag(StrTok, diag::err_pp_linemarker_invalid_filename); - DiscardUntilEndOfDirective(); - return; - } - FilenameID = SourceMgr.getLineTableFilenameID(Literal.GetString()); + SmallString<128> FilenameBuffer; + StringRef Filename = getSpelling(StrTok, FilenameBuffer); + GetFilenameSpellingForLineDirective(*this, StrTok.getLocation(), Filename); + FilenameID = SourceMgr.getLineTableFilenameID(Filename); // Verify that there is nothing after the string, other than EOD. Because // of C99 6.10.4p5, macros that expand to empty tokens are ok. @@ -1778,7 +1779,7 @@ void Preprocessor::HandleDigitDirective(Token &DigitTok) { return; Token StrTok; - Lex(StrTok); + LexHeaderName(StrTok); bool IsFileEntry = false, IsFileExit = false; int FilenameID = -1; @@ -1790,29 +1791,14 @@ void Preprocessor::HandleDigitDirective(Token &DigitTok) { Diag(StrTok, diag::ext_pp_gnu_line_directive); // Treat this like "#line NN", which doesn't change file characteristics. FileKind = SourceMgr.getFileCharacteristic(DigitTok.getLocation()); - } else if (StrTok.isNot(tok::string_literal)) { + } else if (StrTok.isNot(tok::header_name)) { Diag(StrTok, diag::err_pp_linemarker_invalid_filename); DiscardUntilEndOfDirective(); return; - } else if (StrTok.hasUDSuffix()) { - Diag(StrTok, diag::err_invalid_string_udl); - DiscardUntilEndOfDirective(); - return; } else { - // Parse and validate the string, converting it into a unique ID. - StringLiteralParser Literal(StrTok, *this, - StringLiteralEvalMethod::Unevaluated); - assert(Literal.isOrdinary() && "Didn't allow wide strings in"); - if (Literal.hadError) { - DiscardUntilEndOfDirective(); - return; - } - if (Literal.Pascal) { - Diag(StrTok, diag::err_pp_linemarker_invalid_filename); - DiscardUntilEndOfDirective(); - return; - } - + SmallString<128> FilenameBuffer; + StringRef Filename = getSpelling(StrTok, FilenameBuffer); + GetFilenameSpellingForLineDirective(*this, StrTok.getLocation(), Filename); // If a filename was present, read any flags that are present. if (ReadLineMarkerFlags(IsFileEntry, IsFileExit, FileKind, *this)) return; @@ -1821,8 +1807,8 @@ void Preprocessor::HandleDigitDirective(Token &DigitTok) { // Exiting to an empty string means pop to the including file, so leave // FilenameID as -1 in that case. - if (!(IsFileExit && Literal.GetString().empty())) - FilenameID = SourceMgr.getLineTableFilenameID(Literal.GetString()); + if (!(IsFileExit && Filename.empty())) + FilenameID = SourceMgr.getLineTableFilenameID(Filename); } // Create a line note with this information. diff --git a/clang/test/Frontend/linemarker-invalid-escape.c b/clang/test/Frontend/linemarker-invalid-escape.c deleted file mode 100644 index a8239f81f152d..0000000000000 --- a/clang/test/Frontend/linemarker-invalid-escape.c +++ /dev/null @@ -1,5 +0,0 @@ -// RUN: %clang_cc1 -emit-llvm -o - -verify %s - -# 1 "original\x12source.c" // expected-error {{invalid escape sequence '\x12' in an unevaluated string literal}} - -int x = 0; diff --git a/clang/test/Parser/cxx11-user-defined-literals.cpp b/clang/test/Parser/cxx11-user-defined-literals.cpp index 968868509de19..6c15b8133b86f 100644 --- a/clang/test/Parser/cxx11-user-defined-literals.cpp +++ b/clang/test/Parser/cxx11-user-defined-literals.cpp @@ -3,8 +3,8 @@ // A ud-suffix cannot be used on string literals in a whole bunch of contexts: #include "foo"_bar // expected-error {{expected "FILENAME" or <FILENAME>}} -#line 1 "foo"_bar // expected-error {{user-defined suffix cannot be used here}} -# 1 "foo"_bar 1 // expected-error {{user-defined suffix cannot be used here}} +#line 1 "foo"_bar // expected-error {{invalid filename for #line directive}} +# 1 "foo"_bar 1 // expected-error {{invalid filename for #line directive}} #ident "foo"_bar // expected-error {{user-defined suffix cannot be used here}} _Pragma("foo"_bar) // expected-error {{user-defined suffix cannot be used here}} #pragma comment(lib, "foo"_bar) // expected-error {{user-defined suffix cannot be used here}} diff --git a/clang/test/Preprocessor/line-directive-output.c b/clang/test/Preprocessor/line-directive-output.c index 0e66487bd20c9..7774cc7c9cb31 100644 --- a/clang/test/Preprocessor/line-directive-output.c +++ b/clang/test/Preprocessor/line-directive-output.c @@ -75,9 +75,21 @@ extern int z; # 44 "A.c" # 49 "A.c" -// CHECK: # 50 "a\n.c" +// CHECK: # 50 "a\\n.c" # 50 "a\n.c" +// CHECK: # 1 "c:\\moo\\zar\\haz.h" +#line 1 "c:\moo\zar\haz.h" + +// CHECK # 1 "original\x12source.c" +# 1 "original\x12source.c" + +// CHECK # 1 "original\u1234" +# 1 "original\u1234" + +// CHECK # 1 "original\u{1234}" +# 1 "original\u{1234}" + # 1 "system.h" 3 # 2 void sys_foo(void); diff --git a/clang/test/Preprocessor/line-directive.c b/clang/test/Preprocessor/line-directive.c index 4fb05a4ea2f6e..530ddd686fdc8 100644 --- a/clang/test/Preprocessor/line-directive.c +++ b/clang/test/Preprocessor/line-directive.c @@ -127,6 +127,6 @@ undefined t; // expected-error {{unknown type name 'undefined'}} // expected-error@-1{{MAIN2}} #line 129 L"wide" // expected-error {{invalid filename for #line directive}} -#line 130 "\x12" // expected-error {{invalid escape sequence '\x12' in an unevaluated string literal}} +#line 130 "\x12" # 131 U"hello" // expected-error {{invalid filename for line marker directive}} -# 132 "\x13" // expected-error {{invalid escape sequence '\x13' in an unevaluated string literal}} +# 132 "\x13" // expected-warning {{this style of line directive is a GNU extension}} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
