Skip to content

Commit 9d49b82

Browse files
authored
[clang-scan-deps] Implement P2223R2 for DependencyDirectiveScanner.cpp (#143950)
P2223R2 allows the line-continuation slash `\` to be followed by additional whitespace. The Clang lexer already follows this behavior, also for versions prior to C++23. The dependency directive scanner however only implements it for `#define` directives (15d5f5d). This fully implements P2223R2 for the dependency directive scanner (for any C++ standard) and aligns the dependency directive scanner's splicing behavior with that of the Clang lexer. For example, the following code was previously not scanned correctly by `clang-scan-deps` but now works as expected: ```cpp import \<whitespace here> A; ```
1 parent 09c54c2 commit 9d49b82

File tree

2 files changed

+113
-10
lines changed

2 files changed

+113
-10
lines changed

clang/lib/Lex/DependencyDirectivesScanner.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,6 @@ static unsigned skipNewline(const char *&First, const char *End) {
323323
return Len;
324324
}
325325

326-
static bool wasLineContinuation(const char *First, unsigned EOLLen) {
327-
return *(First - (int)EOLLen - 1) == '\\';
328-
}
329-
330326
static void skipToNewlineRaw(const char *&First, const char *const End) {
331327
for (;;) {
332328
if (First == End)
@@ -336,13 +332,16 @@ static void skipToNewlineRaw(const char *&First, const char *const End) {
336332
if (Len)
337333
return;
338334

335+
char LastNonWhitespace = ' ';
339336
do {
337+
if (!isHorizontalWhitespace(*First))
338+
LastNonWhitespace = *First;
340339
if (++First == End)
341340
return;
342341
Len = isEOL(First, End);
343342
} while (!Len);
344343

345-
if (First[-1] != '\\')
344+
if (LastNonWhitespace != '\\')
346345
return;
347346

348347
First += Len;
@@ -394,6 +393,7 @@ static bool isQuoteCppDigitSeparator(const char *const Start,
394393
}
395394

396395
void Scanner::skipLine(const char *&First, const char *const End) {
396+
char LastNonWhitespace = ' ';
397397
for (;;) {
398398
assert(First <= End);
399399
if (First == End)
@@ -419,6 +419,8 @@ void Scanner::skipLine(const char *&First, const char *const End) {
419419
// Iterate over comments correctly.
420420
if (*First != '/' || End - First < 2) {
421421
LastTokenPtr = First;
422+
if (!isWhitespace(*First))
423+
LastNonWhitespace = *First;
422424
++First;
423425
continue;
424426
}
@@ -431,6 +433,8 @@ void Scanner::skipLine(const char *&First, const char *const End) {
431433

432434
if (First[1] != '*') {
433435
LastTokenPtr = First;
436+
if (!isWhitespace(*First))
437+
LastNonWhitespace = *First;
434438
++First;
435439
continue;
436440
}
@@ -442,8 +446,9 @@ void Scanner::skipLine(const char *&First, const char *const End) {
442446
return;
443447

444448
// Skip over the newline.
445-
unsigned Len = skipNewline(First, End);
446-
if (!wasLineContinuation(First, Len)) // Continue past line-continuations.
449+
skipNewline(First, End);
450+
451+
if (LastNonWhitespace != '\\')
447452
break;
448453
}
449454
}
@@ -468,9 +473,16 @@ static void skipWhitespace(const char *&First, const char *const End) {
468473
if (End - First < 2)
469474
return;
470475

471-
if (First[0] == '\\' && isVerticalWhitespace(First[1])) {
472-
skipNewline(++First, End);
473-
continue;
476+
if (*First == '\\') {
477+
const char *Ptr = First + 1;
478+
while (Ptr < End && isHorizontalWhitespace(*Ptr))
479+
++Ptr;
480+
if (Ptr != End && isVerticalWhitespace(*Ptr)) {
481+
skipNewline(Ptr, End);
482+
First = Ptr;
483+
continue;
484+
}
485+
return;
474486
}
475487

476488
// Check for a non-comment character.

clang/unittests/Lex/DependencyDirectivesScannerTest.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,97 @@ TEST(MinimizeSourceToDependencyDirectivesTest,
789789
Out.data());
790790
}
791791

792+
TEST(MinimizeSourceToDependencyDirectivesTest,
793+
WhitespaceAfterLineContinuationSlashLineComment) {
794+
SmallVector<char, 128> Out;
795+
796+
ASSERT_FALSE(minimizeSourceToDependencyDirectives("// some comment \\ \n"
797+
"module A;\n",
798+
Out));
799+
EXPECT_STREQ("", Out.data());
800+
}
801+
802+
TEST(MinimizeSourceToDependencyDirectivesTest,
803+
WhitespaceAfterLineContinuationSlashAllDirectives) {
804+
SmallVector<char, 512> Out;
805+
SmallVector<dependency_directives_scan::Token, 16> Tokens;
806+
SmallVector<Directive, 16> Directives;
807+
808+
StringRef Input = "#define \\ \n"
809+
"A\n"
810+
"#undef\t\\ \n"
811+
"A\n"
812+
"#endif \\\t\t\n"
813+
"\n"
814+
"#if \\ \t\n"
815+
"A\n"
816+
"#ifdef\t\\ \n"
817+
"A\n"
818+
"#ifndef \\ \t\n"
819+
"A\n"
820+
"#elifdef \\ \n"
821+
"A\n"
822+
"#elifndef \\ \n"
823+
"A\n"
824+
"#elif \\\t\t \n"
825+
"A\n"
826+
"#else \\\t \t\n"
827+
"\n"
828+
"#include \\ \n"
829+
"<A>\n"
830+
"#include_next \\ \n"
831+
"<A>\n"
832+
"#__include_macros\\ \n"
833+
"<A>\n"
834+
"#import \\ \t\n"
835+
"<A>\n"
836+
"@import \\\t \n"
837+
"A;\n"
838+
"#pragma clang \\ \n"
839+
"module \\ \n"
840+
"import A\n"
841+
"#pragma \\ \n"
842+
"push_macro(A)\n"
843+
"#pragma \\\t \n"
844+
"pop_macro(A)\n"
845+
"#pragma \\ \n"
846+
"include_alias(<A>,\\ \n"
847+
"<B>)\n"
848+
"export \\ \n"
849+
"module m;\n"
850+
"import\t\\\t \n"
851+
"m;\n"
852+
"#pragma\t\\ \n"
853+
"clang\t\\ \t\n"
854+
"system_header\n";
855+
ASSERT_FALSE(
856+
minimizeSourceToDependencyDirectives(Input, Out, Tokens, Directives));
857+
858+
EXPECT_EQ(pp_define, Directives[0].Kind);
859+
EXPECT_EQ(pp_undef, Directives[1].Kind);
860+
EXPECT_EQ(pp_endif, Directives[2].Kind);
861+
EXPECT_EQ(pp_if, Directives[3].Kind);
862+
EXPECT_EQ(pp_ifdef, Directives[4].Kind);
863+
EXPECT_EQ(pp_ifndef, Directives[5].Kind);
864+
EXPECT_EQ(pp_elifdef, Directives[6].Kind);
865+
EXPECT_EQ(pp_elifndef, Directives[7].Kind);
866+
EXPECT_EQ(pp_elif, Directives[8].Kind);
867+
EXPECT_EQ(pp_else, Directives[9].Kind);
868+
EXPECT_EQ(pp_include, Directives[10].Kind);
869+
EXPECT_EQ(pp_include_next, Directives[11].Kind);
870+
EXPECT_EQ(pp___include_macros, Directives[12].Kind);
871+
EXPECT_EQ(pp_import, Directives[13].Kind);
872+
EXPECT_EQ(decl_at_import, Directives[14].Kind);
873+
EXPECT_EQ(pp_pragma_import, Directives[15].Kind);
874+
EXPECT_EQ(pp_pragma_push_macro, Directives[16].Kind);
875+
EXPECT_EQ(pp_pragma_pop_macro, Directives[17].Kind);
876+
EXPECT_EQ(pp_pragma_include_alias, Directives[18].Kind);
877+
EXPECT_EQ(cxx_export_module_decl, Directives[19].Kind);
878+
EXPECT_EQ(cxx_import_decl, Directives[20].Kind);
879+
EXPECT_EQ(pp_pragma_system_header, Directives[21].Kind);
880+
EXPECT_EQ(pp_eof, Directives[22].Kind);
881+
}
882+
792883
TEST(MinimizeSourceToDependencyDirectivesTest, PoundWarningAndError) {
793884
SmallVector<char, 128> Out;
794885

0 commit comments

Comments
 (0)