From ee584f3260317a26aaacd6e2c541c2778d1d63fd Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 4 Nov 2018 16:31:24 +0100 Subject: [PATCH] Fix bug in string interpolation handling. --- .../Pretty/CS6_StringInterpolation.cs | 6 ++++ .../CS6_StringInterpolation.opt.roslyn.il | 17 ++++++++++ .../Pretty/CS6_StringInterpolation.roslyn.il | 20 +++++++++++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 33 ++++++++++++++++--- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.cs index 18dbfd1d5..027fd1ff0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.cs @@ -21,6 +21,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine($"\ta{$"a{args.Length}" == args[0]}"); } + public static void ArrayExpansionSpecialCases(object[] args) + { + Console.WriteLine($"args: {args}"); + Console.WriteLine(string.Format("args: {0}", args)); + } + public static void InvalidFormatString(string[] args) { Console.WriteLine(string.Format("", args.Length)); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.opt.roslyn.il index d971dcadf..362395e88 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.opt.roslyn.il @@ -169,6 +169,23 @@ IL_0115: ret } // end of method CS6_StringInterpolation::General + .method public hidebysig static void ArrayExpansionSpecialCases(object[] args) cil managed + { + // Code size 33 (0x21) + .maxstack 8 + IL_0000: ldstr "args: {0}" + IL_0005: ldarg.0 + IL_0006: call string [mscorlib]System.String::Format(string, + object) + IL_000b: call void [mscorlib]System.Console::WriteLine(string) + IL_0010: ldstr "args: {0}" + IL_0015: ldarg.0 + IL_0016: call string [mscorlib]System.String::Format(string, + object[]) + IL_001b: call void [mscorlib]System.Console::WriteLine(string) + IL_0020: ret + } // end of method CS6_StringInterpolation::ArrayExpansionSpecialCases + .method public hidebysig static void InvalidFormatString(string[] args) cil managed { // Code size 556 (0x22c) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.roslyn.il index 06695b28b..0049393e9 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.roslyn.il @@ -180,6 +180,26 @@ IL_011f: ret } // end of method CS6_StringInterpolation::General + .method public hidebysig static void ArrayExpansionSpecialCases(object[] args) cil managed + { + // Code size 36 (0x24) + .maxstack 8 + IL_0000: nop + IL_0001: ldstr "args: {0}" + IL_0006: ldarg.0 + IL_0007: call string [mscorlib]System.String::Format(string, + object) + IL_000c: call void [mscorlib]System.Console::WriteLine(string) + IL_0011: nop + IL_0012: ldstr "args: {0}" + IL_0017: ldarg.0 + IL_0018: call string [mscorlib]System.String::Format(string, + object[]) + IL_001d: call void [mscorlib]System.Console::WriteLine(string) + IL_0022: nop + IL_0023: ret + } // end of method CS6_StringInterpolation::ArrayExpansionSpecialCases + .method public hidebysig static void InvalidFormatString(string[] args) cil managed { // Code size 581 (0x245) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 236e79efb..c45879e98 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -235,22 +235,41 @@ namespace ICSharpCode.Decompiler.CSharp argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm)); } - if (settings.StringInterpolation && IsInterpolatedStringCreation(method) && + if (settings.StringInterpolation && IsInterpolatedStringCreation(method, argumentList) && TryGetStringInterpolationTokens(argumentList, out string format, out var tokens)) { var arguments = argumentList.Arguments; var content = new List(); + + bool unpackSingleElementArray = !argumentList.IsExpandedForm && argumentList.Length == 2 + && argumentList.Arguments[1].Expression is ArrayCreateExpression ace + && ace.Initializer?.Elements.Count == 1; + + void UnpackSingleElementArray(ref TranslatedExpression argument) + { + if (!unpackSingleElementArray) return; + var arrayCreation = (ArrayCreateExpression)argumentList.Arguments[1].Expression; + var arrayCreationRR = (ArrayCreateResolveResult)argumentList.Arguments[1].ResolveResult; + var element = arrayCreation.Initializer.Elements.First().Detach(); + argument = new TranslatedExpression(element, arrayCreationRR.InitializerElements.First()); + } + if (tokens.Count > 0) { foreach (var (kind, index, text) in tokens) { + TranslatedExpression argument; switch (kind) { case TokenKind.String: content.Add(new InterpolatedStringText(text)); break; case TokenKind.Argument: - content.Add(new Interpolation(arguments[index + 1])); + argument = arguments[index + 1]; + UnpackSingleElementArray(ref argument); + content.Add(new Interpolation(argument)); break; case TokenKind.ArgumentWithFormat: - content.Add(new Interpolation(arguments[index + 1], text)); + argument = arguments[index + 1]; + UnpackSingleElementArray(ref argument); + content.Add(new Interpolation(argument, text)); break; } } @@ -405,12 +424,18 @@ namespace ICSharpCode.Decompiler.CSharp return new ExpressionWithResolveResult(((AssignmentExpression)assignment).Left.Detach()); } - private static bool IsInterpolatedStringCreation(IMethod method) + private static bool IsInterpolatedStringCreation(IMethod method, ArgumentList argumentList) { return method.IsStatic && ( (method.DeclaringType.IsKnownType(KnownTypeCode.String) && method.Name == "Format") || (method.Name == "Create" && method.DeclaringType.Name == "FormattableStringFactory" && method.DeclaringType.Namespace == "System.Runtime.CompilerServices") + ) + && argumentList.ArgumentNames == null // Argument names are not allowed + && ( + argumentList.IsExpandedForm // Must be expanded form + || !method.Parameters.Last().IsParams // -or- not a params overload + || (argumentList.Length == 2 && argumentList.Arguments[1].Expression is ArrayCreateExpression) // -or- an array literal ); }