diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -184,15 +184,33 @@ bool FormatTokenLexer::tryMergeCSharpVerbatimStringLiteral() { if (Tokens.size() < 2) return false; - auto &At = *(Tokens.end() - 2); + auto &String = *(Tokens.end() - 1); + if (!String->is(tok::string_literal)) + return false; + + // verbatim strings could contain "" which C# sees as an escaped ". + // @"""Hello""" will have been tokenized as @"" "Hello" "" and needs + // merging into a single string literal. + auto &CSharpStringLiteral = *(Tokens.end() - 2); + if (CSharpStringLiteral->Type == TT_CSharpStringLiteral && + (CSharpStringLiteral->TokenText.startswith(R"(@")") || + CSharpStringLiteral->TokenText.startswith(R"($@")"))) { + CSharpStringLiteral->TokenText = StringRef( + CSharpStringLiteral->TokenText.begin(), + String->TokenText.end() - CSharpStringLiteral->TokenText.begin()); + CSharpStringLiteral->ColumnWidth += String->ColumnWidth; + Tokens.erase(Tokens.end() - 1); + return true; + } + + auto &At = *(Tokens.end() - 2); - // Look for $"aaaaaa" @"aaaaaa". - if (!(At->is(tok::at) || At->TokenText == "$") || - !String->is(tok::string_literal)) + // Look for @"aaaaaa" or $"aaaaaa". + if (!(At->is(tok::at) || At->TokenText == "$")) return false; - if (Tokens.size() >= 2 && At->is(tok::at)) { + if (Tokens.size() > 2 && At->is(tok::at)) { auto &Dollar = *(Tokens.end() - 3); if (Dollar->TokenText == "$") { // This looks like $@"aaaaa" so we need to combine all 3 tokens. diff --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp --- a/clang/unittests/Format/FormatTestCSharp.cpp +++ b/clang/unittests/Format/FormatTestCSharp.cpp @@ -409,5 +409,13 @@ verifyFormat("(int) x / y;", Style); } +TEST_F(FormatTestCSharp, CSharpEscapedQuotesInVerbatimStrings) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); + + verifyFormat(R"(string str = @"""")", Style); + verifyFormat(R"(string str = @"""Hello world""")", Style); + verifyFormat(R"(string str = $@"""Hello {friend}""")", Style); +} + } // namespace format } // end namespace clang