diff options
author | Naveen Seth Hanig <naveen.hanig@outlook.com> | 2025-06-13 19:48:05 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-06-13 10:48:05 -0700 |
commit | 9d49b82de077c730d687593604dfa00770f11965 (patch) | |
tree | 7a82c3dca36e1dd54a890ca78f2c3b2e96eb2d1e /clang | |
parent | 09c54c2e9e044fa0857831e6ce1bf77c8ce16ecc (diff) | |
download | llvm-9d49b82de077c730d687593604dfa00770f11965.zip llvm-9d49b82de077c730d687593604dfa00770f11965.tar.gz llvm-9d49b82de077c730d687593604dfa00770f11965.tar.bz2 |
[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;
```
Diffstat (limited to 'clang')
-rw-r--r-- | clang/lib/Lex/DependencyDirectivesScanner.cpp | 32 | ||||
-rw-r--r-- | clang/unittests/Lex/DependencyDirectivesScannerTest.cpp | 91 |
2 files changed, 113 insertions, 10 deletions
diff --git a/clang/lib/Lex/DependencyDirectivesScanner.cpp b/clang/lib/Lex/DependencyDirectivesScanner.cpp index 4606b85..1b6b16c 100644 --- a/clang/lib/Lex/DependencyDirectivesScanner.cpp +++ b/clang/lib/Lex/DependencyDirectivesScanner.cpp @@ -323,10 +323,6 @@ static unsigned skipNewline(const char *&First, const char *End) { return Len; } -static bool wasLineContinuation(const char *First, unsigned EOLLen) { - return *(First - (int)EOLLen - 1) == '\\'; -} - static void skipToNewlineRaw(const char *&First, const char *const End) { for (;;) { if (First == End) @@ -336,13 +332,16 @@ static void skipToNewlineRaw(const char *&First, const char *const End) { if (Len) return; + char LastNonWhitespace = ' '; do { + if (!isHorizontalWhitespace(*First)) + LastNonWhitespace = *First; if (++First == End) return; Len = isEOL(First, End); } while (!Len); - if (First[-1] != '\\') + if (LastNonWhitespace != '\\') return; First += Len; @@ -394,6 +393,7 @@ static bool isQuoteCppDigitSeparator(const char *const Start, } void Scanner::skipLine(const char *&First, const char *const End) { + char LastNonWhitespace = ' '; for (;;) { assert(First <= End); if (First == End) @@ -419,6 +419,8 @@ void Scanner::skipLine(const char *&First, const char *const End) { // Iterate over comments correctly. if (*First != '/' || End - First < 2) { LastTokenPtr = First; + if (!isWhitespace(*First)) + LastNonWhitespace = *First; ++First; continue; } @@ -431,6 +433,8 @@ void Scanner::skipLine(const char *&First, const char *const End) { if (First[1] != '*') { LastTokenPtr = First; + if (!isWhitespace(*First)) + LastNonWhitespace = *First; ++First; continue; } @@ -442,8 +446,9 @@ void Scanner::skipLine(const char *&First, const char *const End) { return; // Skip over the newline. - unsigned Len = skipNewline(First, End); - if (!wasLineContinuation(First, Len)) // Continue past line-continuations. + skipNewline(First, End); + + if (LastNonWhitespace != '\\') break; } } @@ -468,9 +473,16 @@ static void skipWhitespace(const char *&First, const char *const End) { if (End - First < 2) return; - if (First[0] == '\\' && isVerticalWhitespace(First[1])) { - skipNewline(++First, End); - continue; + if (*First == '\\') { + const char *Ptr = First + 1; + while (Ptr < End && isHorizontalWhitespace(*Ptr)) + ++Ptr; + if (Ptr != End && isVerticalWhitespace(*Ptr)) { + skipNewline(Ptr, End); + First = Ptr; + continue; + } + return; } // Check for a non-comment character. diff --git a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp index 377c066..61f7492 100644 --- a/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp +++ b/clang/unittests/Lex/DependencyDirectivesScannerTest.cpp @@ -789,6 +789,97 @@ TEST(MinimizeSourceToDependencyDirectivesTest, Out.data()); } +TEST(MinimizeSourceToDependencyDirectivesTest, + WhitespaceAfterLineContinuationSlashLineComment) { + SmallVector<char, 128> Out; + + ASSERT_FALSE(minimizeSourceToDependencyDirectives("// some comment \\ \n" + "module A;\n", + Out)); + EXPECT_STREQ("", Out.data()); +} + +TEST(MinimizeSourceToDependencyDirectivesTest, + WhitespaceAfterLineContinuationSlashAllDirectives) { + SmallVector<char, 512> Out; + SmallVector<dependency_directives_scan::Token, 16> Tokens; + SmallVector<Directive, 16> Directives; + + StringRef Input = "#define \\ \n" + "A\n" + "#undef\t\\ \n" + "A\n" + "#endif \\\t\t\n" + "\n" + "#if \\ \t\n" + "A\n" + "#ifdef\t\\ \n" + "A\n" + "#ifndef \\ \t\n" + "A\n" + "#elifdef \\ \n" + "A\n" + "#elifndef \\ \n" + "A\n" + "#elif \\\t\t \n" + "A\n" + "#else \\\t \t\n" + "\n" + "#include \\ \n" + "<A>\n" + "#include_next \\ \n" + "<A>\n" + "#__include_macros\\ \n" + "<A>\n" + "#import \\ \t\n" + "<A>\n" + "@import \\\t \n" + "A;\n" + "#pragma clang \\ \n" + "module \\ \n" + "import A\n" + "#pragma \\ \n" + "push_macro(A)\n" + "#pragma \\\t \n" + "pop_macro(A)\n" + "#pragma \\ \n" + "include_alias(<A>,\\ \n" + "<B>)\n" + "export \\ \n" + "module m;\n" + "import\t\\\t \n" + "m;\n" + "#pragma\t\\ \n" + "clang\t\\ \t\n" + "system_header\n"; + ASSERT_FALSE( + minimizeSourceToDependencyDirectives(Input, Out, Tokens, Directives)); + + EXPECT_EQ(pp_define, Directives[0].Kind); + EXPECT_EQ(pp_undef, Directives[1].Kind); + EXPECT_EQ(pp_endif, Directives[2].Kind); + EXPECT_EQ(pp_if, Directives[3].Kind); + EXPECT_EQ(pp_ifdef, Directives[4].Kind); + EXPECT_EQ(pp_ifndef, Directives[5].Kind); + EXPECT_EQ(pp_elifdef, Directives[6].Kind); + EXPECT_EQ(pp_elifndef, Directives[7].Kind); + EXPECT_EQ(pp_elif, Directives[8].Kind); + EXPECT_EQ(pp_else, Directives[9].Kind); + EXPECT_EQ(pp_include, Directives[10].Kind); + EXPECT_EQ(pp_include_next, Directives[11].Kind); + EXPECT_EQ(pp___include_macros, Directives[12].Kind); + EXPECT_EQ(pp_import, Directives[13].Kind); + EXPECT_EQ(decl_at_import, Directives[14].Kind); + EXPECT_EQ(pp_pragma_import, Directives[15].Kind); + EXPECT_EQ(pp_pragma_push_macro, Directives[16].Kind); + EXPECT_EQ(pp_pragma_pop_macro, Directives[17].Kind); + EXPECT_EQ(pp_pragma_include_alias, Directives[18].Kind); + EXPECT_EQ(cxx_export_module_decl, Directives[19].Kind); + EXPECT_EQ(cxx_import_decl, Directives[20].Kind); + EXPECT_EQ(pp_pragma_system_header, Directives[21].Kind); + EXPECT_EQ(pp_eof, Directives[22].Kind); +} + TEST(MinimizeSourceToDependencyDirectivesTest, PoundWarningAndError) { SmallVector<char, 128> Out; |