diff --git a/src/EditorFeatures/CSharp/BraceMatching/StringLiteralBraceMatcher.cs b/src/EditorFeatures/CSharp/BraceMatching/StringLiteralBraceMatcher.cs index aadd11b6a2b89..5d95adebcb969 100644 --- a/src/EditorFeatures/CSharp/BraceMatching/StringLiteralBraceMatcher.cs +++ b/src/EditorFeatures/CSharp/BraceMatching/StringLiteralBraceMatcher.cs @@ -34,18 +34,11 @@ public StringLiteralBraceMatcher() { if (token.IsKind(SyntaxKind.StringLiteralToken)) { - if (token.IsVerbatimStringLiteral()) - { - return new BraceMatchingResult( - new TextSpan(token.SpanStart, 2), - new TextSpan(token.Span.End - 1, 1)); - } - else - { - return new BraceMatchingResult( - new TextSpan(token.SpanStart, 1), - new TextSpan(token.Span.End - 1, 1)); - } + return GetSimpleStringBraceMatchingResult(token, endTokenLength: 1); + } + else if (token.IsKind(SyntaxKind.UTF8StringLiteralToken)) + { + return GetSimpleStringBraceMatchingResult(token, endTokenLength: 3); } else if (token.IsKind(SyntaxKind.InterpolatedStringStartToken, SyntaxKind.InterpolatedVerbatimStringStartToken)) { @@ -65,5 +58,21 @@ public StringLiteralBraceMatcher() return null; } + + private static BraceMatchingResult GetSimpleStringBraceMatchingResult(SyntaxToken token, int endTokenLength) + { + if (token.IsVerbatimStringLiteral()) + { + return new BraceMatchingResult( + new TextSpan(token.SpanStart, 2), + new TextSpan(token.Span.End - endTokenLength, endTokenLength)); + } + else + { + return new BraceMatchingResult( + new TextSpan(token.SpanStart, 1), + new TextSpan(token.Span.End - endTokenLength, endTokenLength)); + } + } } } diff --git a/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs b/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs index 8cef5deb3a2b4..2bf8763b7eb7f 100644 --- a/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs +++ b/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs @@ -11,6 +11,7 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Utilities; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.CSharp.TextStructureNavigation { @@ -36,12 +37,15 @@ protected override bool IsWithinNaturalLanguage(SyntaxToken token, int position) switch (token.Kind()) { case SyntaxKind.StringLiteralToken: + case SyntaxKind.UTF8StringLiteralToken: // This, in combination with the override of GetExtentOfWordFromToken() below, treats the closing // quote as a separate token. This maintains behavior with VS2013. return !IsAtClosingQuote(token, position); case SyntaxKind.SingleLineRawStringLiteralToken: case SyntaxKind.MultiLineRawStringLiteralToken: + case SyntaxKind.UTF8SingleLineRawStringLiteralToken: + case SyntaxKind.UTF8MultiLineRawStringLiteralToken: { // Like with normal string literals, treat the closing quotes as as the end of the string so that // navigation ends there and doesn't go past them. @@ -66,6 +70,13 @@ private static int GetStartOfRawStringLiteralEndDelimiter(SyntaxToken token) var text = token.ToString(); var start = 0; var end = text.Length; + + if (token.IsKind(SyntaxKind.UTF8MultiLineRawStringLiteralToken, SyntaxKind.UTF8SingleLineRawStringLiteralToken)) + { + // Skip past the u8 suffix + end -= "u8".Length; + } + while (start < end && text[start] == '"') start++; @@ -76,19 +87,27 @@ private static int GetStartOfRawStringLiteralEndDelimiter(SyntaxToken token) } private static bool IsAtClosingQuote(SyntaxToken token, int position) - => position == token.Span.End - 1 && token.Text[^1] == '"'; + => token.Kind() switch + { + SyntaxKind.StringLiteralToken => position == token.Span.End - 1 && token.Text[^1] == '"', + SyntaxKind.UTF8StringLiteralToken => position == token.Span.End - 3 && token.Text is [.., '"', 'u' or 'U', '8'], + _ => throw ExceptionUtilities.Unreachable + }; protected override TextExtent GetExtentOfWordFromToken(SyntaxToken token, SnapshotPoint position) { - if (token.Kind() == SyntaxKind.StringLiteralToken && IsAtClosingQuote(token, position.Position)) + if (token.IsKind(SyntaxKind.StringLiteralToken, SyntaxKind.UTF8StringLiteralToken) && IsAtClosingQuote(token, position.Position)) { // Special case to treat the closing quote of a string literal as a separate token. This allows the // cursor to stop during word navigation (Ctrl+LeftArrow, etc.) immediately before AND after the // closing quote, just like it did in VS2013 and like it currently does for interpolated strings. - var span = new Span(position.Position, 1); + var span = new Span(position.Position, token.Span.End - position.Position); return new TextExtent(new SnapshotSpan(position.Snapshot, span), isSignificant: true); } - else if (token.Kind() is SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken) + else if (token.IsKind(SyntaxKind.SingleLineRawStringLiteralToken, + SyntaxKind.MultiLineRawStringLiteralToken, + SyntaxKind.UTF8SingleLineRawStringLiteralToken, + SyntaxKind.UTF8MultiLineRawStringLiteralToken)) { var delimiterStart = GetStartOfRawStringLiteralEndDelimiter(token); return new TextExtent(new SnapshotSpan(position.Snapshot, Span.FromBounds(delimiterStart, token.Span.End)), isSignificant: true); diff --git a/src/EditorFeatures/CSharpTest/BraceMatching/CSharpBraceMatcherTests.cs b/src/EditorFeatures/CSharpTest/BraceMatching/CSharpBraceMatcherTests.cs index 8df3c36ae251c..27aa83f07adb2 100644 --- a/src/EditorFeatures/CSharpTest/BraceMatching/CSharpBraceMatcherTests.cs +++ b/src/EditorFeatures/CSharpTest/BraceMatching/CSharpBraceMatcherTests.cs @@ -505,6 +505,123 @@ public async Task TestInterpolatedString13() await TestAsync(code, expected); } + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestUTF8String1() + { + var code = @"public class C { string s = $$""Goo""u8; }"; + var expected = @"public class C { string s = ""Goo[|""u8|]; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestUTF8String2() + { + var code = @"public class C { string s = ""$$Goo""u8; }"; + var expected = @"public class C { string s = ""Goo[|""u8|]; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestUTF8String3() + { + var code = @"public class C { string s = ""Goo$$""u8; }"; + var expected = @"public class C { string s = [|""|]Goo""u8; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestUTF8String4() + { + var code = @"public class C { string s = ""Goo""$$u8; }"; + var expected = @"public class C { string s = [|""|]Goo""u8; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestUTF8String5() + { + var code = @"public class C { string s = ""Goo""u$$8; }"; + var expected = @"public class C { string s = [|""|]Goo""u8; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestUTF8String6() + { + var code = @"public class C { string s = ""Goo""u8$$; }"; + var expected = @"public class C { string s = [|""|]Goo""u8; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestVerbatimUTF8String1() + { + var code = @"public class C { string s = $$@""Goo""u8; }"; + var expected = @"public class C { string s = @""Goo[|""u8|]; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestVerbatimUTF8String2() + { + var code = @"public class C { string s = @$$""Goo""u8; }"; + var expected = @"public class C { string s = @""Goo[|""u8|]; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestVerbatimUTF8String3() + { + var code = @"public class C { string s = @""$$Goo""u8; }"; + var expected = @"public class C { string s = @""Goo[|""u8|]; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestVerbatimUTF8String4() + { + var code = @"public class C { string s = @""Goo$$""u8; }"; + var expected = @"public class C { string s = [|@""|]Goo""u8; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestVerbatimUTF8String5() + { + var code = @"public class C { string s = @""Goo""$$u8; }"; + var expected = @"public class C { string s = [|@""|]Goo""u8; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestVerbatimUTF8String6() + { + var code = @"public class C { string s = @""Goo""u$$8; }"; + var expected = @"public class C { string s = [|@""|]Goo""u8; }"; + + await TestAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.BraceMatching)] + public async Task TestVerbatimUTF8String7() + { + var code = @"public class C { string s = @""Goo""u8$$; }"; + var expected = @"public class C { string s = [|@""|]Goo""u8; }"; + + await TestAsync(code, expected); + } + [WorkItem(7120, "https://github.com/dotnet/roslyn/issues/7120")] [WpfFact, Trait(Traits.Feature, Traits.Features.BraceMatching)] public async Task TestConditionalDirectiveWithSingleMatchingDirective() diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs index b35b1f88e270d..55e701b957214 100644 --- a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs @@ -677,6 +677,102 @@ void M() }.RunAsync(); } + [Fact] + public async Task TestConvertToFileScopedWithMultiLineRawString() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N|] +{ + class C + { + void M() + { + System.Console.WriteLine("""""" + a + b + c + d + e + """"""); + } + } +} +", + FixedCode = @" +namespace $$N; + +class C +{ + void M() + { + System.Console.WriteLine("""""" + a + b + c + d + e + """"""); + } +} +", + LanguageVersion = LanguageVersion.Preview, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithUTF8MultiLineRawString() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N|] +{ + class C + { + void M() + { + System.Console.WriteLine("""""" + a + b + c + d + e + """"""u8); + } + } +} +", + FixedCode = @" +namespace $$N; + +class C +{ + void M() + { + System.Console.WriteLine("""""" + a + b + c + d + e + """"""u8); + } +} +", + LanguageVersion = LanguageVersion.Preview, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + [Fact] public async Task TestConvertToFileScopedSingleLineNamespace1() { diff --git a/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs index 850b60b06f653..5759ffa2d5234 100644 --- a/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs @@ -149,6 +149,19 @@ void M() }"); } + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] + public void TestMissingBeforeUTF8String() + { + TestNotHandled( +@"class C +{ + void M() + { + var v = [||]""""u8; + } +}"); + } + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] public void TestMissingBeforeInterpolatedString() { @@ -266,6 +279,71 @@ void M() }"); } + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] + public void TestMissingAfterUTF8String_1() + { + TestNotHandled( +@"class C +{ + void M() + { + var v = """"[||]u8; + } +}"); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] + public void TestMissingAfterUTF8String_2() + { + TestNotHandled( +@"class C +{ + void M() + { + var v = """"u8[||]; + } +}"); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] + public void TestMissingAfterUTF8String_3() + { + TestNotHandled( +@"class C +{ + void M() + { + var v = """"u8[||] + } +}"); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] + public void TestMissingAfterUTF8String_4() + { + TestNotHandled( +@"class C +{ + void M() + { + var v = $""""u8 [||] + } +}"); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] + public void TestMissingAfterUTF8String_5() + { + TestNotHandled( +@"class C +{ + void M() + { + var v = """"u[||]8; + } +}"); + } + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] public void TestMissingInVerbatimString() { @@ -279,6 +357,19 @@ void M() }"); } + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] + public void TestMissingInUTF8VerbatimString() + { + TestNotHandled( +@"class C +{ + void M() + { + var v = @""a[||]b""u8; + } +}"); + } + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] public void TestMissingInInterpolatedVerbatimString() { @@ -450,6 +541,48 @@ void M() }"); } + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] + public void TestUTF8String_1() + { + TestHandled( +@"class C +{ + void M() + { + var v = ""now is [||]the time""u8; + } +}", +@"class C +{ + void M() + { + var v = ""now is ""u8 + + ""[||]the time""u8; + } +}"); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] + public void TestUTF8String_2() + { + TestHandled( +@"class C +{ + void M() + { + var v = ""now is [||]the time""U8; + } +}", +@"class C +{ + void M() + { + var v = ""now is ""U8 + + ""[||]the time""U8; + } +}"); + } + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] public void TestInterpolatedString1() { @@ -944,6 +1077,21 @@ void M() world """"""; } +}"); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] + public void TestMissingInRawUTF8StringLiteral() + { + TestNotHandled( +@"class C +{ + void M() + { + var v = """"""Hello[||]there +world +""""""u8; + } }"); } } diff --git a/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs b/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs index 4c431f1d41e9a..f1d3e82a3264f 100644 --- a/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs +++ b/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs @@ -249,6 +249,28 @@ public void String() @"class Test { private string s1 = "" () test ""{|Significant:$$;|} }"); } + [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] + public void UTF8String() + { + AssertExtent( + @"class Test { private string s1 = {|Significant:$$""|} () test ""u8; }"); + + AssertExtent( + @"class Test { private string s1 = ""{|Insignificant:$$ |}() test ""u8; }"); + + AssertExtent( + @"class Test { private string s1 = "" {|Significant:$$()|} test ""u8; }"); + + AssertExtent( + @"class Test { private string s1 = "" () test{|Insignificant:$$ |}""u8; }"); + + AssertExtent( + @"class Test { private string s1 = "" () test {|Significant:$$""u8|}; }"); + + AssertExtent( + @"class Test { private string s1 = "" () test ""u8{|Significant:$$;|} }"); + } + [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void InterpolatedString1() { @@ -382,6 +404,45 @@ public void TestRawStringDelimeter2() {|Significant:""""$$""|};"); } + [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] + public void TestUTF8RawStringDelimeter() + { + AssertExtent( + @"string s = """""" + Hello + World! + :) + {|Significant:$$""""""u8|};"); + + AssertExtent( + @"string s = """""" + Hello + World! + :) + {|Significant:""$$""""u8|};"); + + AssertExtent( + @"string s = """""" + Hello + World! + :) + {|Significant:""""$$""u8|};"); + + AssertExtent( + @"string s = """""" + Hello + World! + :) + {|Significant:""""""$$u8|};"); + + AssertExtent( + @"string s = """""" + Hello + World! + :) + {|Significant:""""""u$$8|};"); + } + private static void TestNavigator( string code, Func func, diff --git a/src/Features/CSharp/Portable/SplitStringLiteral/SimpleStringSplitter.cs b/src/Features/CSharp/Portable/SplitStringLiteral/SimpleStringSplitter.cs index 5ed690b31fe9c..0573e92b3dd00 100644 --- a/src/Features/CSharp/Portable/SplitStringLiteral/SimpleStringSplitter.cs +++ b/src/Features/CSharp/Portable/SplitStringLiteral/SimpleStringSplitter.cs @@ -29,8 +29,14 @@ public SimpleStringSplitter( } // Don't split @"" strings. They already support directly embedding newlines. + // Don't split UTF8 strings if the cursor is after the quote. protected override bool CheckToken() - => !_token.IsVerbatimStringLiteral(); + => !_token.IsVerbatimStringLiteral() && !CursorIsAfterQuotesInUTF8String(); + + private bool CursorIsAfterQuotesInUTF8String() + { + return _token.IsKind(SyntaxKind.UTF8StringLiteralToken) && CursorPosition >= _token.Span.End - "u8".Length; + } protected override SyntaxNode GetNodeToReplace() => _token.Parent; @@ -40,10 +46,16 @@ protected override BinaryExpressionSyntax CreateSplitString() var prefix = SourceText.GetSubText(TextSpan.FromBounds(_token.SpanStart, CursorPosition)).ToString(); var suffix = SourceText.GetSubText(TextSpan.FromBounds(CursorPosition, _token.Span.End)).ToString(); + // If we're spliting a UTF8 string we need to keep the u8 suffix on the first part. We copy whatever + // the user had on the second part, for consistency. + var firstTokenSuffix = _token.Kind() == SyntaxKind.UTF8StringLiteralToken + ? SourceText.GetSubText(TextSpan.FromBounds(_token.Span.End - "u8".Length, _token.Span.End)).ToString() + : ""; + var firstToken = SyntaxFactory.Token( _token.LeadingTrivia, _token.Kind(), - text: prefix + QuoteCharacter, + text: prefix + QuoteCharacter + firstTokenSuffix, valueText: "", trailing: SyntaxFactory.TriviaList(SyntaxFactory.ElasticSpace)); diff --git a/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs b/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs index 9062054e2faa0..dd6e37afd7d45 100644 --- a/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs +++ b/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs @@ -58,7 +58,8 @@ public static StringSplitter TryCreate( var token = root.FindToken(position); - if (token.IsKind(SyntaxKind.StringLiteralToken)) + if (token.IsKind(SyntaxKind.StringLiteralToken) || + token.IsKind(SyntaxKind.UTF8StringLiteralToken)) { return new SimpleStringSplitter( document, position, root, diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs index f573e365c371d..65fc71ee8dc02 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs @@ -133,6 +133,21 @@ private static bool TryGetTextForSpecialCharacters(SyntaxToken token, [NotNullWh return true; } + if (token.IsKind(SyntaxKind.UTF8StringLiteralToken) || + token.IsKind(SyntaxKind.UTF8SingleLineRawStringLiteralToken) || + token.IsKind(SyntaxKind.UTF8MultiLineRawStringLiteralToken)) + { + text = Keyword("UTF8StringLiteral"); + return true; + } + + if (token.IsKind(SyntaxKind.SingleLineRawStringLiteralToken) || + token.IsKind(SyntaxKind.MultiLineRawStringLiteralToken)) + { + text = Keyword("RawStringLiteral"); + return true; + } + text = null; return false; } diff --git a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs index e2b8609e5555d..e7ae818178945 100644 --- a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs +++ b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs @@ -942,6 +942,51 @@ static void Main(string[] args) }", "$_CSharpKeyword"); } + [Fact, Trait(Traits.Feature, Traits.Features.F1Help)] + public async Task TestUTF8String() + { + await TestAsync( +@"using System; + +class Program +{ + static void Main(string[] args) + { + var x = ""Hel[||]lo""u8; + } +}", "UTF8StringLiteral_CSharpKeyword"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.F1Help)] + public async Task TestRawString() + { + await TestAsync( +@"using System; + +class Program +{ + static void Main(string[] args) + { + var x = """"""Hel[||]lo""""""; + } +}", "RawStringLiteral_CSharpKeyword"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.F1Help)] + public async Task TestUTF8RawString() + { + await TestAsync( +@"using System; + +class Program +{ + static void Main(string[] args) + { + var x = """"""Hel[||]lo""""""u8; + } +}", "UTF8StringLiteral_CSharpKeyword"); + } + [WorkItem(46986, "https://github.com/dotnet/roslyn/issues/46986")] [Fact, Trait(Traits.Feature, Traits.Features.F1Help)] public async Task TestVerbatimString() diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs index 0c1615e89cc9a..f884f905f3025 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs @@ -417,7 +417,12 @@ public static bool IsEntirelyWithinStringLiteral( token = token.GetPreviousToken(includeSkipped: true, includeDirectives: true); } - if (token.Kind() is SyntaxKind.StringLiteralToken or SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken) + if (token.IsKind(SyntaxKind.StringLiteralToken, + SyntaxKind.SingleLineRawStringLiteralToken, + SyntaxKind.MultiLineRawStringLiteralToken, + SyntaxKind.UTF8StringLiteralToken, + SyntaxKind.UTF8SingleLineRawStringLiteralToken, + SyntaxKind.UTF8MultiLineRawStringLiteralToken)) { var span = token.Span;