diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs index 9f38d48adfe5d12396e37de4d58af4ac759186d7..af63015b93afdd3f158cf35c5652aec371ef3975 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs @@ -284,7 +284,7 @@ internal static string FormatString(string str, bool useQuotes) builder.Append(quote); for (int i = 0; i < str.Length; i++) { - FormatStringChar(ref pooledBuilder, str, i, str[i], quote, useLanguageSpecificEscapes: useQuotes, useUnicodeEscapes: false); + FormatStringChar(ref pooledBuilder, str, i, str[i], quote, useLanguageSpecificEscapes: useQuotes, useUnicodeEscapes: useQuotes); } builder.Append(quote); return pooledBuilder.ToStringAndFree(); @@ -312,7 +312,7 @@ internal static string FormatLiteral(char c, ObjectDisplayOptions options) { builder.Append(quote); } - FormatStringChar(ref pooledBuilder, str: null, index: 0, c: c, quote: quote, useLanguageSpecificEscapes: useQuotes, useUnicodeEscapes: !includeCodePoints); + FormatStringChar(ref pooledBuilder, str: null, index: 0, c: c, quote: quote, useLanguageSpecificEscapes: useQuotes, useUnicodeEscapes: useQuotes); if (useQuotes) { builder.Append(quote); diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/ObjectDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/ObjectDisplayTests.cs index 53c4f9357ad221d02c27ce2cc7ebd9a8a6282e5f..72393112593ae2cff2cb8df13bad8ca9bfafe772 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/ObjectDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/ObjectDisplayTests.cs @@ -113,7 +113,7 @@ public void Characters() Assert.Equal("39 '\\''", ObjectDisplay.FormatLiteral('\'', ObjectDisplayOptions.UseQuotes | ObjectDisplayOptions.IncludeCodePoints)); Assert.Equal("39 '", ObjectDisplay.FormatLiteral('\'', ObjectDisplayOptions.IncludeCodePoints)); - Assert.Equal("0x001e '\u001e'", ObjectDisplay.FormatLiteral('\u001e', ObjectDisplayOptions.UseQuotes | ObjectDisplayOptions.IncludeCodePoints | ObjectDisplayOptions.UseHexadecimalNumbers)); + Assert.Equal("0x001e '\\u001e'", ObjectDisplay.FormatLiteral('\u001e', ObjectDisplayOptions.UseQuotes | ObjectDisplayOptions.IncludeCodePoints | ObjectDisplayOptions.UseHexadecimalNumbers)); Assert.Equal("0x001e \u001e", ObjectDisplay.FormatLiteral('\u001e', ObjectDisplayOptions.IncludeCodePoints | ObjectDisplayOptions.UseHexadecimalNumbers)); Assert.Equal("0x0008 '\\b'", ObjectDisplay.FormatLiteral('\b', ObjectDisplayOptions.UseQuotes | ObjectDisplayOptions.IncludeCodePoints | ObjectDisplayOptions.UseHexadecimalNumbers)); @@ -157,12 +157,12 @@ public void Strings() // Formatting with quotes should escape specific control characters. var expected = - "\"\\0\u0001\u0002\u0003\u0004\u0005\u0006\\a\\b\\t\\n\\v\\f\\r\u000e\u000f\u0010" + - "\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d" + - "\u001e\u001f !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[" + - "\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f\u0080\u0081\u0082\u0083\u0084\u0085" + - "\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092" + - "\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f" + + "\"\\0\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\a\\b\\t\\n\\v\\f\\r\\u000e\\u000f\\u0010" + + "\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d" + + "\\u001e\\u001f !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[" + + "\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\u007f\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085" + + "\\u0086\\u0087\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f\\u0090\\u0091\\u0092" + + "\\u0093\\u0094\\u0095\\u0096\\u0097\\u0098\\u0099\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f" + " ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèé" + "êëìíîïðñòóôõö÷øùúûüýþ\""; Assert.Equal( diff --git a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ExpansionTests.cs b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ExpansionTests.cs index f9d7be619695badbb2f610a0d513514ed5792bbf..ec5586d95cb0f2d089b970f5f0d184f8a83fa6dd 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ExpansionTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ExpansionTests.cs @@ -1679,7 +1679,7 @@ public void UnicodeChar() value = CreateDkmClrValue('\u001f'); evalResult = FormatResult("c", value, inspectionContext: CreateDkmInspectionContext(radix: 16)); Verify(evalResult, - EvalResult("c", "0x001f '\u001f'", "char", "c", editableValue: "'\\u001f'")); + EvalResult("c", "0x001f '\\u001f'", "char", "c", editableValue: "'\\u001f'")); // This char is not printable, but there is a specific escape character. value = CreateDkmClrValue('\u0007'); @@ -1688,6 +1688,16 @@ public void UnicodeChar() EvalResult("c", "0x0007 '\\a'", "char", "c", editableValue: "'\\a'")); } + [WorkItem(1138095)] + [Fact] + public void UnicodeString() + { + var value = CreateDkmClrValue("\u1234\u001f\u0007"); + var evalResult = FormatResult("s", value); + Verify(evalResult, + EvalResult("s", $"\"{'\u1234'}\\u001f\\a\"", "string", "s", editableValue: $"\"{'\u1234'}\\u001f\\a\"", flags: DkmEvaluationResultFlags.RawString)); + } + [WorkItem(1002381)] [Fact] public void BaseTypeEditableValue() diff --git a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/FormatSpecifierTests.cs b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/FormatSpecifierTests.cs index 196e7320a7e1b0cc35c1ca603b116a6a6cefc659..855cc12d2c05acf4b10ef0fcd1c9cf20fc97a33f 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/FormatSpecifierTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/FormatSpecifierTests.cs @@ -50,7 +50,7 @@ public void NoQuotes_String() value = CreateDkmClrValue("a\r\n\tb\v\b\u001ec", type: stringType); evalResult = FormatResult("s", value, inspectionContext: inspectionContext); Verify(evalResult, - EvalResult("s", "a\r\n\tb\v\b\u001ec", "string", "s", editableValue: "\"a\\r\\n\\tb\\v\\b\u001ec\"", flags: DkmEvaluationResultFlags.RawString)); + EvalResult("s", "a\r\n\tb\v\b\u001ec", "string", "s", editableValue: "\"a\\r\\n\\tb\\v\\b\\u001ec\"", flags: DkmEvaluationResultFlags.RawString)); // "a\0b" value = CreateDkmClrValue("a\0b", type: stringType); @@ -58,6 +58,12 @@ public void NoQuotes_String() Verify(evalResult, EvalResult("s", "a\0b", "string", "s", editableValue: "\"a\\0b\"", flags: DkmEvaluationResultFlags.RawString)); + // "\u007f\u009f" + value = CreateDkmClrValue("\u007f\u009f", type: stringType); + evalResult = FormatResult("s", value, inspectionContext: inspectionContext); + Verify(evalResult, + EvalResult("s", "\u007f\u009f", "string", "s", editableValue: "\"\\u007f\\u009f\"", flags: DkmEvaluationResultFlags.RawString)); + // " " with alias value = CreateDkmClrValue(" ", type: stringType, alias: "1", evalFlags: DkmEvaluationResultFlags.HasObjectId); evalResult = FormatResult("s", value, inspectionContext: inspectionContext); @@ -118,6 +124,12 @@ public void NoQuotes_Char() Verify(evalResult, EvalResult("c", "30 \u001e", "char", "c", editableValue: "'\\u001e'", flags: DkmEvaluationResultFlags.None)); + // '\u007f' + value = CreateDkmClrValue('\u007f', type: charType); + evalResult = FormatResult("c", value, inspectionContext: inspectionContext); + Verify(evalResult, + EvalResult("c", "127 \u007f", "char", "c", editableValue: "'\\u007f'", flags: DkmEvaluationResultFlags.None)); + // array value = CreateDkmClrValue(new char[] { '1' }, type: charType.MakeArrayType()); evalResult = FormatResult("a", value, inspectionContext: inspectionContext); diff --git a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ValueFormattingTests.cs b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ValueFormattingTests.cs index fe8b6eb4dc6f25dd0dcda6d7cadc05a8a0a75d10..2ecf0f21df261079d93ab582acd46eddaf0ebed2 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ValueFormattingTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ValueFormattingTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Globalization; using Microsoft.CodeAnalysis.ExpressionEvaluator; using Microsoft.VisualStudio.Debugger.Evaluation; using Xunit; @@ -83,7 +84,8 @@ public void Char() // as a few double-byte characters. Testing all possible characters takes too long. const string format = "{0} '{1}'"; const string formatUsingHex = "0x{0:x4} '{1}'"; - for (char ch = (char)0; ch < 0xff; ch++) + char ch; + for (ch = (char)0; ch < 0xff; ch++) { string expected; switch (ch) @@ -119,21 +121,28 @@ public void Char() expected = "\\\\"; break; default: - expected = ch.ToString(); + expected = FormatStringChar(ch); break; } Assert.Equal(string.Format(format, (int)ch, expected), FormatValue(ch)); Assert.Equal(string.Format(formatUsingHex, (int)ch, expected), FormatValue(ch, useHexadecimal: true)); } - foreach (char ch in new[] { (char)0xabcd, (char)0xfeef, (char)0xffef }) - { - Assert.Equal(string.Format(format, (int)ch, ch), FormatValue(ch)); - Assert.Equal(string.Format(formatUsingHex, (int)ch, ch), FormatValue(ch, useHexadecimal: true)); - } + ch = (char)0xabcd; + Assert.Equal(string.Format(format, (int)ch, ch), FormatValue(ch)); + Assert.Equal(string.Format(formatUsingHex, (int)ch, ch), FormatValue(ch, useHexadecimal: true)); - Assert.Equal("65535 '\uffff'", FormatValue(char.MaxValue)); - Assert.Equal("0xffff '\uffff'", FormatValue(char.MaxValue, useHexadecimal: true)); + ch = (char)0xfeef; + Assert.Equal(string.Format(format, (int)ch, ch), FormatValue(ch)); + Assert.Equal(string.Format(formatUsingHex, (int)ch, ch), FormatValue(ch, useHexadecimal: true)); + + ch = (char)0xffef; + Assert.Equal("65519 '\\uffef'", FormatValue(ch)); + Assert.Equal("0xffef '\\uffef'", FormatValue(ch, useHexadecimal: true)); + + ch = char.MaxValue; + Assert.Equal("65535 '\\uffff'", FormatValue(ch)); + Assert.Equal("0xffff '\\uffff'", FormatValue(ch, useHexadecimal: true)); } [Fact] @@ -181,27 +190,41 @@ public void String() expected = "\\\\"; break; default: - expected = ch.ToString(); + expected = FormatStringChar(ch); break; } Assert.Equal(string.Format(format, expected), FormatValue(ch.ToString())); Assert.Equal(string.Format(format, expected), FormatValue(ch.ToString(), useHexadecimal: true)); } - foreach (char ch in new[] { (char)0xabcd, (char)0xfeef, (char)0xffef }) - { - Assert.Equal(string.Format(format, ch), FormatValue(ch.ToString())); - Assert.Equal(string.Format(format, ch), FormatValue(ch.ToString(), useHexadecimal: true)); - } + var s = ((char)0xabcd).ToString(); + Assert.Equal(string.Format(format, s), FormatValue(s)); + Assert.Equal(string.Format(format, s), FormatValue(s, useHexadecimal: true)); + + s = ((char)0xfeef).ToString(); + Assert.Equal(string.Format(format, s), FormatValue(s)); + Assert.Equal(string.Format(format, s), FormatValue(s, useHexadecimal: true)); + + s = ((char)0xffef).ToString(); + Assert.Equal("\"\\uffef\"", FormatValue(s)); + Assert.Equal("\"\\uffef\"", FormatValue(s, useHexadecimal: true)); - Assert.Equal("\"\uffff\"", FormatValue(char.MaxValue.ToString())); - Assert.Equal("\"\uffff\"", FormatValue(char.MaxValue.ToString(), useHexadecimal: true)); + s = char.MaxValue.ToString(); + Assert.Equal("\"\\uffff\"", FormatValue(s)); + Assert.Equal("\"\\uffff\"", FormatValue(s, useHexadecimal: true)); string multiByte = "\ud83c\udfc8"; Assert.Equal(string.Format(format, "🏈"), FormatValue(multiByte)); Assert.Equal(string.Format(format, "🏈"), FormatValue(multiByte, useHexadecimal: true)); } + private static string FormatStringChar(char c) + { + return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Control) ? + $"\\u{((int)c).ToString("x4")}" : + c.ToString(); + } + [Fact] public void Void() { diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/ExpansionTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/ExpansionTests.vb index 4fd249dce8c53fd21454726c90240d2cc159dfc7..7801c4d810efa8f4b51d3c5bcfc12b8c9b39a117 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/ExpansionTests.vb +++ b/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/ExpansionTests.vb @@ -286,6 +286,15 @@ End Class EvalResult("c", quotedChar, "Char", "c", editableValue:=quotedChar)) End Sub + + Public Sub UnicodeString() + Const quotedString = """" & ChrW(&H1234) & """ & ChrW(7)" + Dim value = CreateDkmClrValue(New String({ChrW(&H1234), ChrW(&H0007)})) + Dim result = FormatResult("s", value) + Verify(result, + EvalResult("s", quotedString, "String", "s", editableValue:=quotedString, flags:=DkmEvaluationResultFlags.RawString)) + End Sub + Public Sub BaseTypeEditableValue() Dim source = "