Browse Source

Merge pull request #1012 from icsharpcode/string-interpolation

C# 6 String interpolation
pull/1051/head
Siegfried Pammer 8 years ago committed by GitHub
parent
commit
728bc7236b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  2. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  4. 55
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.cs
  5. 373
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.opt.roslyn.il
  6. 409
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.roslyn.il
  7. 38
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  8. 51
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  9. 169
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs
  10. 16
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  11. 159
      ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
  12. 15
      ICSharpCode.Decompiler/DecompilerSettings.cs
  13. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

17
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -16,6 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System;
using System.CodeDom.Compiler; using System.CodeDom.Compiler;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -254,7 +255,7 @@ namespace ICSharpCode.Decompiler.Tests
try { try {
outputFile = Tester.CompileCSharp(Path.Combine(TestCasePath, testFileName), options, outputFile = Tester.CompileCSharp(Path.Combine(TestCasePath, testFileName), options,
outputFileName: Path.Combine(TestCasePath, testOutputFileName)); outputFileName: Path.Combine(TestCasePath, testOutputFileName));
string decompiledCodeFile = Tester.DecompileCSharp(outputFile.PathToAssembly); string decompiledCodeFile = Tester.DecompileCSharp(outputFile.PathToAssembly, GetSettings(options));
decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options); decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options);
Tester.RunAndCompareOutput(testFileName, outputFile.PathToAssembly, decompiledOutputFile.PathToAssembly, decompiledCodeFile); Tester.RunAndCompareOutput(testFileName, outputFile.PathToAssembly, decompiledOutputFile.PathToAssembly, decompiledCodeFile);
@ -268,7 +269,7 @@ namespace ICSharpCode.Decompiler.Tests
decompiledOutputFile.TempFiles.Delete(); decompiledOutputFile.TempFiles.Delete();
} }
} }
void RunIL(string testFileName, CompilerOptions options = CompilerOptions.UseDebug, AssemblerOptions asmOptions = AssemblerOptions.None) void RunIL(string testFileName, CompilerOptions options = CompilerOptions.UseDebug, AssemblerOptions asmOptions = AssemblerOptions.None)
{ {
string outputFile = null; string outputFile = null;
@ -276,7 +277,7 @@ namespace ICSharpCode.Decompiler.Tests
try { try {
outputFile = Tester.AssembleIL(Path.Combine(TestCasePath, testFileName), asmOptions); outputFile = Tester.AssembleIL(Path.Combine(TestCasePath, testFileName), asmOptions);
string decompiledCodeFile = Tester.DecompileCSharp(outputFile); string decompiledCodeFile = Tester.DecompileCSharp(outputFile, GetSettings(options));
decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options); decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options);
Tester.RunAndCompareOutput(testFileName, outputFile, decompiledOutputFile.PathToAssembly, decompiledCodeFile); Tester.RunAndCompareOutput(testFileName, outputFile, decompiledOutputFile.PathToAssembly, decompiledCodeFile);
@ -288,5 +289,15 @@ namespace ICSharpCode.Decompiler.Tests
decompiledOutputFile.TempFiles.Delete(); decompiledOutputFile.TempFiles.Delete();
} }
} }
DecompilerSettings GetSettings(CompilerOptions options)
{
if (!options.HasFlag(CompilerOptions.UseRoslyn)) {
return new DecompilerSettings {
StringInterpolation = false
};
}
return new DecompilerSettings();
}
} }
} }

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -65,6 +65,7 @@
<Compile Include="TestCases\ILPretty\Issue982.cs" /> <Compile Include="TestCases\ILPretty\Issue982.cs" />
<Compile Include="TestCases\Pretty\AsyncMain.cs" /> <Compile Include="TestCases\Pretty\AsyncMain.cs" />
<Compile Include="TestCases\Pretty\CS72_PrivateProtected.cs" /> <Compile Include="TestCases\Pretty\CS72_PrivateProtected.cs" />
<Compile Include="TestCases\Pretty\CS6_StringInterpolation.cs" />
<Compile Include="TestCases\Pretty\ExpressionTrees.cs" /> <Compile Include="TestCases\Pretty\ExpressionTrees.cs" />
<Compile Include="TestCases\Pretty\VariableNaming.cs" /> <Compile Include="TestCases\Pretty\VariableNaming.cs" />
<Compile Include="TestCases\Pretty\VariableNamingWithoutSymbols.cs" /> <Compile Include="TestCases\Pretty\VariableNamingWithoutSymbols.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -241,6 +241,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings); Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings);
} }
[Test]
public void CS6_StringInterpolation([ValueSource("roslynOnlyOptions")] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
void Run([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null) void Run([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null)
{ {
var ilFile = Path.Combine(TestCasePath, testName) + Tester.GetSuffix(cscOptions) + ".il"; var ilFile = Path.Combine(TestCasePath, testName) + Tester.GetSuffix(cscOptions) + ".il";

55
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.cs

@ -0,0 +1,55 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class CS6_StringInterpolation
{
public static void Main(string[] args)
{
}
public static void General(string[] args)
{
Console.WriteLine($"{args.Length}");
Console.WriteLine($"a{{0{args.Length}");
Console.WriteLine($"{args.Length:x}");
Console.WriteLine($"\ta{args.Length}b");
Console.WriteLine($"\ta{args.Length}ba{args[0]}a{args[args.Length]}a{args.Length}");
Console.WriteLine($"\ta{((args.Length != 0) ? 5 : 0)}");
Console.WriteLine($"\ta{(object)(args ?? args)}");
Console.WriteLine($"\ta{args[0][0] == 'a'}");
// This is legal, but we cannot create a pretty test for it, as it would require us
// to convert string literals to string interpolation literals even without a string.Format call.
// I do not think, this is worth the effort.
//Console.WriteLine($"\ta{$"a" == args[0]}");
}
public static void InvalidFormatString(string[] args)
{
Console.WriteLine(string.Format("", args.Length));
Console.WriteLine(string.Format("a", args.Length));
Console.WriteLine(string.Format("}", args.Length));
Console.WriteLine(string.Format("{", args.Length));
Console.WriteLine(string.Format(":", args.Length));
Console.WriteLine(string.Format("\t", args.Length));
Console.WriteLine(string.Format("\\", args.Length));
Console.WriteLine(string.Format("\"", args.Length));
Console.WriteLine(string.Format("aa", args.Length));
Console.WriteLine(string.Format("a}", args.Length));
Console.WriteLine(string.Format("a{", args.Length));
Console.WriteLine(string.Format("a:", args.Length));
Console.WriteLine(string.Format("a\t", args.Length));
Console.WriteLine(string.Format("a\\", args.Length));
Console.WriteLine(string.Format("a\"", args.Length));
Console.WriteLine(string.Format("a{:", args.Length));
Console.WriteLine(string.Format("a{0", args.Length));
Console.WriteLine(string.Format("a{{0", args.Length));
Console.WriteLine(string.Format("}a{{0", args.Length));
Console.WriteLine(string.Format("}{", args.Length));
Console.WriteLine(string.Format("{}", args.Length));
Console.WriteLine(string.Format("{0:}", args.Length));
Console.WriteLine(string.Format("{0{a}0}", args.Length));
Console.WriteLine(string.Format("test: {0}", string.Join(",", args)));
}
}
}

373
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.opt.roslyn.il

@ -0,0 +1,373 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly CS6_StringInterpolation
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module CS6_StringInterpolation.exe
// MVID: {EAC42E43-E444-4B60-84F6-6239B79599C4}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x03670000
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.CS6_StringInterpolation
extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method CS6_StringInterpolation::Main
.method public hidebysig static void General(string[] args) cil managed
{
// Code size 232 (0xe8)
.maxstack 6
IL_0000: ldstr "{0}"
IL_0005: ldarg.0
IL_0006: ldlen
IL_0007: conv.i4
IL_0008: box [mscorlib]System.Int32
IL_000d: call string [mscorlib]System.String::Format(string,
object)
IL_0012: call void [mscorlib]System.Console::WriteLine(string)
IL_0017: ldstr "a{{0{0}"
IL_001c: ldarg.0
IL_001d: ldlen
IL_001e: conv.i4
IL_001f: box [mscorlib]System.Int32
IL_0024: call string [mscorlib]System.String::Format(string,
object)
IL_0029: call void [mscorlib]System.Console::WriteLine(string)
IL_002e: ldstr "{0:x}"
IL_0033: ldarg.0
IL_0034: ldlen
IL_0035: conv.i4
IL_0036: box [mscorlib]System.Int32
IL_003b: call string [mscorlib]System.String::Format(string,
object)
IL_0040: call void [mscorlib]System.Console::WriteLine(string)
IL_0045: ldstr "\ta{0}b"
IL_004a: ldarg.0
IL_004b: ldlen
IL_004c: conv.i4
IL_004d: box [mscorlib]System.Int32
IL_0052: call string [mscorlib]System.String::Format(string,
object)
IL_0057: call void [mscorlib]System.Console::WriteLine(string)
IL_005c: ldstr "\ta{0}ba{1}a{2}a{3}"
IL_0061: ldc.i4.4
IL_0062: newarr [mscorlib]System.Object
IL_0067: dup
IL_0068: ldc.i4.0
IL_0069: ldarg.0
IL_006a: ldlen
IL_006b: conv.i4
IL_006c: box [mscorlib]System.Int32
IL_0071: stelem.ref
IL_0072: dup
IL_0073: ldc.i4.1
IL_0074: ldarg.0
IL_0075: ldc.i4.0
IL_0076: ldelem.ref
IL_0077: stelem.ref
IL_0078: dup
IL_0079: ldc.i4.2
IL_007a: ldarg.0
IL_007b: ldarg.0
IL_007c: ldlen
IL_007d: conv.i4
IL_007e: ldelem.ref
IL_007f: stelem.ref
IL_0080: dup
IL_0081: ldc.i4.3
IL_0082: ldarg.0
IL_0083: ldlen
IL_0084: conv.i4
IL_0085: box [mscorlib]System.Int32
IL_008a: stelem.ref
IL_008b: call string [mscorlib]System.String::Format(string,
object[])
IL_0090: call void [mscorlib]System.Console::WriteLine(string)
IL_0095: ldstr "\ta{0}"
IL_009a: ldarg.0
IL_009b: ldlen
IL_009c: brtrue.s IL_00a1
IL_009e: ldc.i4.0
IL_009f: br.s IL_00a2
IL_00a1: ldc.i4.5
IL_00a2: box [mscorlib]System.Int32
IL_00a7: call string [mscorlib]System.String::Format(string,
object)
IL_00ac: call void [mscorlib]System.Console::WriteLine(string)
IL_00b1: ldstr "\ta{0}"
IL_00b6: ldarg.0
IL_00b7: dup
IL_00b8: brtrue.s IL_00bc
IL_00ba: pop
IL_00bb: ldarg.0
IL_00bc: call string [mscorlib]System.String::Format(string,
object)
IL_00c1: call void [mscorlib]System.Console::WriteLine(string)
IL_00c6: ldstr "\ta{0}"
IL_00cb: ldarg.0
IL_00cc: ldc.i4.0
IL_00cd: ldelem.ref
IL_00ce: ldc.i4.0
IL_00cf: callvirt instance char [mscorlib]System.String::get_Chars(int32)
IL_00d4: ldc.i4.s 97
IL_00d6: ceq
IL_00d8: box [mscorlib]System.Boolean
IL_00dd: call string [mscorlib]System.String::Format(string,
object)
IL_00e2: call void [mscorlib]System.Console::WriteLine(string)
IL_00e7: ret
} // end of method CS6_StringInterpolation::General
.method public hidebysig static void InvalidFormatString(string[] args) cil managed
{
// Code size 556 (0x22c)
.maxstack 3
IL_0000: ldstr ""
IL_0005: ldarg.0
IL_0006: ldlen
IL_0007: conv.i4
IL_0008: box [mscorlib]System.Int32
IL_000d: call string [mscorlib]System.String::Format(string,
object)
IL_0012: call void [mscorlib]System.Console::WriteLine(string)
IL_0017: ldstr "a"
IL_001c: ldarg.0
IL_001d: ldlen
IL_001e: conv.i4
IL_001f: box [mscorlib]System.Int32
IL_0024: call string [mscorlib]System.String::Format(string,
object)
IL_0029: call void [mscorlib]System.Console::WriteLine(string)
IL_002e: ldstr "}"
IL_0033: ldarg.0
IL_0034: ldlen
IL_0035: conv.i4
IL_0036: box [mscorlib]System.Int32
IL_003b: call string [mscorlib]System.String::Format(string,
object)
IL_0040: call void [mscorlib]System.Console::WriteLine(string)
IL_0045: ldstr "{"
IL_004a: ldarg.0
IL_004b: ldlen
IL_004c: conv.i4
IL_004d: box [mscorlib]System.Int32
IL_0052: call string [mscorlib]System.String::Format(string,
object)
IL_0057: call void [mscorlib]System.Console::WriteLine(string)
IL_005c: ldstr ":"
IL_0061: ldarg.0
IL_0062: ldlen
IL_0063: conv.i4
IL_0064: box [mscorlib]System.Int32
IL_0069: call string [mscorlib]System.String::Format(string,
object)
IL_006e: call void [mscorlib]System.Console::WriteLine(string)
IL_0073: ldstr "\t"
IL_0078: ldarg.0
IL_0079: ldlen
IL_007a: conv.i4
IL_007b: box [mscorlib]System.Int32
IL_0080: call string [mscorlib]System.String::Format(string,
object)
IL_0085: call void [mscorlib]System.Console::WriteLine(string)
IL_008a: ldstr "\\"
IL_008f: ldarg.0
IL_0090: ldlen
IL_0091: conv.i4
IL_0092: box [mscorlib]System.Int32
IL_0097: call string [mscorlib]System.String::Format(string,
object)
IL_009c: call void [mscorlib]System.Console::WriteLine(string)
IL_00a1: ldstr "\""
IL_00a6: ldarg.0
IL_00a7: ldlen
IL_00a8: conv.i4
IL_00a9: box [mscorlib]System.Int32
IL_00ae: call string [mscorlib]System.String::Format(string,
object)
IL_00b3: call void [mscorlib]System.Console::WriteLine(string)
IL_00b8: ldstr "aa"
IL_00bd: ldarg.0
IL_00be: ldlen
IL_00bf: conv.i4
IL_00c0: box [mscorlib]System.Int32
IL_00c5: call string [mscorlib]System.String::Format(string,
object)
IL_00ca: call void [mscorlib]System.Console::WriteLine(string)
IL_00cf: ldstr "a}"
IL_00d4: ldarg.0
IL_00d5: ldlen
IL_00d6: conv.i4
IL_00d7: box [mscorlib]System.Int32
IL_00dc: call string [mscorlib]System.String::Format(string,
object)
IL_00e1: call void [mscorlib]System.Console::WriteLine(string)
IL_00e6: ldstr "a{"
IL_00eb: ldarg.0
IL_00ec: ldlen
IL_00ed: conv.i4
IL_00ee: box [mscorlib]System.Int32
IL_00f3: call string [mscorlib]System.String::Format(string,
object)
IL_00f8: call void [mscorlib]System.Console::WriteLine(string)
IL_00fd: ldstr "a:"
IL_0102: ldarg.0
IL_0103: ldlen
IL_0104: conv.i4
IL_0105: box [mscorlib]System.Int32
IL_010a: call string [mscorlib]System.String::Format(string,
object)
IL_010f: call void [mscorlib]System.Console::WriteLine(string)
IL_0114: ldstr "a\t"
IL_0119: ldarg.0
IL_011a: ldlen
IL_011b: conv.i4
IL_011c: box [mscorlib]System.Int32
IL_0121: call string [mscorlib]System.String::Format(string,
object)
IL_0126: call void [mscorlib]System.Console::WriteLine(string)
IL_012b: ldstr "a\\"
IL_0130: ldarg.0
IL_0131: ldlen
IL_0132: conv.i4
IL_0133: box [mscorlib]System.Int32
IL_0138: call string [mscorlib]System.String::Format(string,
object)
IL_013d: call void [mscorlib]System.Console::WriteLine(string)
IL_0142: ldstr "a\""
IL_0147: ldarg.0
IL_0148: ldlen
IL_0149: conv.i4
IL_014a: box [mscorlib]System.Int32
IL_014f: call string [mscorlib]System.String::Format(string,
object)
IL_0154: call void [mscorlib]System.Console::WriteLine(string)
IL_0159: ldstr "a{:"
IL_015e: ldarg.0
IL_015f: ldlen
IL_0160: conv.i4
IL_0161: box [mscorlib]System.Int32
IL_0166: call string [mscorlib]System.String::Format(string,
object)
IL_016b: call void [mscorlib]System.Console::WriteLine(string)
IL_0170: ldstr "a{0"
IL_0175: ldarg.0
IL_0176: ldlen
IL_0177: conv.i4
IL_0178: box [mscorlib]System.Int32
IL_017d: call string [mscorlib]System.String::Format(string,
object)
IL_0182: call void [mscorlib]System.Console::WriteLine(string)
IL_0187: ldstr "a{{0"
IL_018c: ldarg.0
IL_018d: ldlen
IL_018e: conv.i4
IL_018f: box [mscorlib]System.Int32
IL_0194: call string [mscorlib]System.String::Format(string,
object)
IL_0199: call void [mscorlib]System.Console::WriteLine(string)
IL_019e: ldstr "}a{{0"
IL_01a3: ldarg.0
IL_01a4: ldlen
IL_01a5: conv.i4
IL_01a6: box [mscorlib]System.Int32
IL_01ab: call string [mscorlib]System.String::Format(string,
object)
IL_01b0: call void [mscorlib]System.Console::WriteLine(string)
IL_01b5: ldstr "}{"
IL_01ba: ldarg.0
IL_01bb: ldlen
IL_01bc: conv.i4
IL_01bd: box [mscorlib]System.Int32
IL_01c2: call string [mscorlib]System.String::Format(string,
object)
IL_01c7: call void [mscorlib]System.Console::WriteLine(string)
IL_01cc: ldstr "{}"
IL_01d1: ldarg.0
IL_01d2: ldlen
IL_01d3: conv.i4
IL_01d4: box [mscorlib]System.Int32
IL_01d9: call string [mscorlib]System.String::Format(string,
object)
IL_01de: call void [mscorlib]System.Console::WriteLine(string)
IL_01e3: ldstr "{0:}"
IL_01e8: ldarg.0
IL_01e9: ldlen
IL_01ea: conv.i4
IL_01eb: box [mscorlib]System.Int32
IL_01f0: call string [mscorlib]System.String::Format(string,
object)
IL_01f5: call void [mscorlib]System.Console::WriteLine(string)
IL_01fa: ldstr "{0{a}0}"
IL_01ff: ldarg.0
IL_0200: ldlen
IL_0201: conv.i4
IL_0202: box [mscorlib]System.Int32
IL_0207: call string [mscorlib]System.String::Format(string,
object)
IL_020c: call void [mscorlib]System.Console::WriteLine(string)
IL_0211: ldstr "test: {0}"
IL_0216: ldstr ","
IL_021b: ldarg.0
IL_021c: call string [mscorlib]System.String::Join(string,
string[])
IL_0221: call string [mscorlib]System.String::Format(string,
object)
IL_0226: call void [mscorlib]System.Console::WriteLine(string)
IL_022b: ret
} // end of method CS6_StringInterpolation::InvalidFormatString
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method CS6_StringInterpolation::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CS6_StringInterpolation
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

409
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS6_StringInterpolation.roslyn.il

@ -0,0 +1,409 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly CS6_StringInterpolation
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module CS6_StringInterpolation.exe
// MVID: {AA7B0A68-2032-492A-914B-49D8291C620F}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x00C60000
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.CS6_StringInterpolation
extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
} // end of method CS6_StringInterpolation::Main
.method public hidebysig static void General(string[] args) cil managed
{
// Code size 241 (0xf1)
.maxstack 6
IL_0000: nop
IL_0001: ldstr "{0}"
IL_0006: ldarg.0
IL_0007: ldlen
IL_0008: conv.i4
IL_0009: box [mscorlib]System.Int32
IL_000e: call string [mscorlib]System.String::Format(string,
object)
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: nop
IL_0019: ldstr "a{{0{0}"
IL_001e: ldarg.0
IL_001f: ldlen
IL_0020: conv.i4
IL_0021: box [mscorlib]System.Int32
IL_0026: call string [mscorlib]System.String::Format(string,
object)
IL_002b: call void [mscorlib]System.Console::WriteLine(string)
IL_0030: nop
IL_0031: ldstr "{0:x}"
IL_0036: ldarg.0
IL_0037: ldlen
IL_0038: conv.i4
IL_0039: box [mscorlib]System.Int32
IL_003e: call string [mscorlib]System.String::Format(string,
object)
IL_0043: call void [mscorlib]System.Console::WriteLine(string)
IL_0048: nop
IL_0049: ldstr "\ta{0}b"
IL_004e: ldarg.0
IL_004f: ldlen
IL_0050: conv.i4
IL_0051: box [mscorlib]System.Int32
IL_0056: call string [mscorlib]System.String::Format(string,
object)
IL_005b: call void [mscorlib]System.Console::WriteLine(string)
IL_0060: nop
IL_0061: ldstr "\ta{0}ba{1}a{2}a{3}"
IL_0066: ldc.i4.4
IL_0067: newarr [mscorlib]System.Object
IL_006c: dup
IL_006d: ldc.i4.0
IL_006e: ldarg.0
IL_006f: ldlen
IL_0070: conv.i4
IL_0071: box [mscorlib]System.Int32
IL_0076: stelem.ref
IL_0077: dup
IL_0078: ldc.i4.1
IL_0079: ldarg.0
IL_007a: ldc.i4.0
IL_007b: ldelem.ref
IL_007c: stelem.ref
IL_007d: dup
IL_007e: ldc.i4.2
IL_007f: ldarg.0
IL_0080: ldarg.0
IL_0081: ldlen
IL_0082: conv.i4
IL_0083: ldelem.ref
IL_0084: stelem.ref
IL_0085: dup
IL_0086: ldc.i4.3
IL_0087: ldarg.0
IL_0088: ldlen
IL_0089: conv.i4
IL_008a: box [mscorlib]System.Int32
IL_008f: stelem.ref
IL_0090: call string [mscorlib]System.String::Format(string,
object[])
IL_0095: call void [mscorlib]System.Console::WriteLine(string)
IL_009a: nop
IL_009b: ldstr "\ta{0}"
IL_00a0: ldarg.0
IL_00a1: ldlen
IL_00a2: brtrue.s IL_00a7
IL_00a4: ldc.i4.0
IL_00a5: br.s IL_00a8
IL_00a7: ldc.i4.5
IL_00a8: box [mscorlib]System.Int32
IL_00ad: call string [mscorlib]System.String::Format(string,
object)
IL_00b2: call void [mscorlib]System.Console::WriteLine(string)
IL_00b7: nop
IL_00b8: ldstr "\ta{0}"
IL_00bd: ldarg.0
IL_00be: dup
IL_00bf: brtrue.s IL_00c3
IL_00c1: pop
IL_00c2: ldarg.0
IL_00c3: call string [mscorlib]System.String::Format(string,
object)
IL_00c8: call void [mscorlib]System.Console::WriteLine(string)
IL_00cd: nop
IL_00ce: ldstr "\ta{0}"
IL_00d3: ldarg.0
IL_00d4: ldc.i4.0
IL_00d5: ldelem.ref
IL_00d6: ldc.i4.0
IL_00d7: callvirt instance char [mscorlib]System.String::get_Chars(int32)
IL_00dc: ldc.i4.s 97
IL_00de: ceq
IL_00e0: box [mscorlib]System.Boolean
IL_00e5: call string [mscorlib]System.String::Format(string,
object)
IL_00ea: call void [mscorlib]System.Console::WriteLine(string)
IL_00ef: nop
IL_00f0: ret
} // end of method CS6_StringInterpolation::General
.method public hidebysig static void InvalidFormatString(string[] args) cil managed
{
// Code size 581 (0x245)
.maxstack 3
IL_0000: nop
IL_0001: ldstr ""
IL_0006: ldarg.0
IL_0007: ldlen
IL_0008: conv.i4
IL_0009: box [mscorlib]System.Int32
IL_000e: call string [mscorlib]System.String::Format(string,
object)
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: nop
IL_0019: ldstr "a"
IL_001e: ldarg.0
IL_001f: ldlen
IL_0020: conv.i4
IL_0021: box [mscorlib]System.Int32
IL_0026: call string [mscorlib]System.String::Format(string,
object)
IL_002b: call void [mscorlib]System.Console::WriteLine(string)
IL_0030: nop
IL_0031: ldstr "}"
IL_0036: ldarg.0
IL_0037: ldlen
IL_0038: conv.i4
IL_0039: box [mscorlib]System.Int32
IL_003e: call string [mscorlib]System.String::Format(string,
object)
IL_0043: call void [mscorlib]System.Console::WriteLine(string)
IL_0048: nop
IL_0049: ldstr "{"
IL_004e: ldarg.0
IL_004f: ldlen
IL_0050: conv.i4
IL_0051: box [mscorlib]System.Int32
IL_0056: call string [mscorlib]System.String::Format(string,
object)
IL_005b: call void [mscorlib]System.Console::WriteLine(string)
IL_0060: nop
IL_0061: ldstr ":"
IL_0066: ldarg.0
IL_0067: ldlen
IL_0068: conv.i4
IL_0069: box [mscorlib]System.Int32
IL_006e: call string [mscorlib]System.String::Format(string,
object)
IL_0073: call void [mscorlib]System.Console::WriteLine(string)
IL_0078: nop
IL_0079: ldstr "\t"
IL_007e: ldarg.0
IL_007f: ldlen
IL_0080: conv.i4
IL_0081: box [mscorlib]System.Int32
IL_0086: call string [mscorlib]System.String::Format(string,
object)
IL_008b: call void [mscorlib]System.Console::WriteLine(string)
IL_0090: nop
IL_0091: ldstr "\\"
IL_0096: ldarg.0
IL_0097: ldlen
IL_0098: conv.i4
IL_0099: box [mscorlib]System.Int32
IL_009e: call string [mscorlib]System.String::Format(string,
object)
IL_00a3: call void [mscorlib]System.Console::WriteLine(string)
IL_00a8: nop
IL_00a9: ldstr "\""
IL_00ae: ldarg.0
IL_00af: ldlen
IL_00b0: conv.i4
IL_00b1: box [mscorlib]System.Int32
IL_00b6: call string [mscorlib]System.String::Format(string,
object)
IL_00bb: call void [mscorlib]System.Console::WriteLine(string)
IL_00c0: nop
IL_00c1: ldstr "aa"
IL_00c6: ldarg.0
IL_00c7: ldlen
IL_00c8: conv.i4
IL_00c9: box [mscorlib]System.Int32
IL_00ce: call string [mscorlib]System.String::Format(string,
object)
IL_00d3: call void [mscorlib]System.Console::WriteLine(string)
IL_00d8: nop
IL_00d9: ldstr "a}"
IL_00de: ldarg.0
IL_00df: ldlen
IL_00e0: conv.i4
IL_00e1: box [mscorlib]System.Int32
IL_00e6: call string [mscorlib]System.String::Format(string,
object)
IL_00eb: call void [mscorlib]System.Console::WriteLine(string)
IL_00f0: nop
IL_00f1: ldstr "a{"
IL_00f6: ldarg.0
IL_00f7: ldlen
IL_00f8: conv.i4
IL_00f9: box [mscorlib]System.Int32
IL_00fe: call string [mscorlib]System.String::Format(string,
object)
IL_0103: call void [mscorlib]System.Console::WriteLine(string)
IL_0108: nop
IL_0109: ldstr "a:"
IL_010e: ldarg.0
IL_010f: ldlen
IL_0110: conv.i4
IL_0111: box [mscorlib]System.Int32
IL_0116: call string [mscorlib]System.String::Format(string,
object)
IL_011b: call void [mscorlib]System.Console::WriteLine(string)
IL_0120: nop
IL_0121: ldstr "a\t"
IL_0126: ldarg.0
IL_0127: ldlen
IL_0128: conv.i4
IL_0129: box [mscorlib]System.Int32
IL_012e: call string [mscorlib]System.String::Format(string,
object)
IL_0133: call void [mscorlib]System.Console::WriteLine(string)
IL_0138: nop
IL_0139: ldstr "a\\"
IL_013e: ldarg.0
IL_013f: ldlen
IL_0140: conv.i4
IL_0141: box [mscorlib]System.Int32
IL_0146: call string [mscorlib]System.String::Format(string,
object)
IL_014b: call void [mscorlib]System.Console::WriteLine(string)
IL_0150: nop
IL_0151: ldstr "a\""
IL_0156: ldarg.0
IL_0157: ldlen
IL_0158: conv.i4
IL_0159: box [mscorlib]System.Int32
IL_015e: call string [mscorlib]System.String::Format(string,
object)
IL_0163: call void [mscorlib]System.Console::WriteLine(string)
IL_0168: nop
IL_0169: ldstr "a{:"
IL_016e: ldarg.0
IL_016f: ldlen
IL_0170: conv.i4
IL_0171: box [mscorlib]System.Int32
IL_0176: call string [mscorlib]System.String::Format(string,
object)
IL_017b: call void [mscorlib]System.Console::WriteLine(string)
IL_0180: nop
IL_0181: ldstr "a{0"
IL_0186: ldarg.0
IL_0187: ldlen
IL_0188: conv.i4
IL_0189: box [mscorlib]System.Int32
IL_018e: call string [mscorlib]System.String::Format(string,
object)
IL_0193: call void [mscorlib]System.Console::WriteLine(string)
IL_0198: nop
IL_0199: ldstr "a{{0"
IL_019e: ldarg.0
IL_019f: ldlen
IL_01a0: conv.i4
IL_01a1: box [mscorlib]System.Int32
IL_01a6: call string [mscorlib]System.String::Format(string,
object)
IL_01ab: call void [mscorlib]System.Console::WriteLine(string)
IL_01b0: nop
IL_01b1: ldstr "}a{{0"
IL_01b6: ldarg.0
IL_01b7: ldlen
IL_01b8: conv.i4
IL_01b9: box [mscorlib]System.Int32
IL_01be: call string [mscorlib]System.String::Format(string,
object)
IL_01c3: call void [mscorlib]System.Console::WriteLine(string)
IL_01c8: nop
IL_01c9: ldstr "}{"
IL_01ce: ldarg.0
IL_01cf: ldlen
IL_01d0: conv.i4
IL_01d1: box [mscorlib]System.Int32
IL_01d6: call string [mscorlib]System.String::Format(string,
object)
IL_01db: call void [mscorlib]System.Console::WriteLine(string)
IL_01e0: nop
IL_01e1: ldstr "{}"
IL_01e6: ldarg.0
IL_01e7: ldlen
IL_01e8: conv.i4
IL_01e9: box [mscorlib]System.Int32
IL_01ee: call string [mscorlib]System.String::Format(string,
object)
IL_01f3: call void [mscorlib]System.Console::WriteLine(string)
IL_01f8: nop
IL_01f9: ldstr "{0:}"
IL_01fe: ldarg.0
IL_01ff: ldlen
IL_0200: conv.i4
IL_0201: box [mscorlib]System.Int32
IL_0206: call string [mscorlib]System.String::Format(string,
object)
IL_020b: call void [mscorlib]System.Console::WriteLine(string)
IL_0210: nop
IL_0211: ldstr "{0{a}0}"
IL_0216: ldarg.0
IL_0217: ldlen
IL_0218: conv.i4
IL_0219: box [mscorlib]System.Int32
IL_021e: call string [mscorlib]System.String::Format(string,
object)
IL_0223: call void [mscorlib]System.Console::WriteLine(string)
IL_0228: nop
IL_0229: ldstr "test: {0}"
IL_022e: ldstr ","
IL_0233: ldarg.0
IL_0234: call string [mscorlib]System.String::Join(string,
string[])
IL_0239: call string [mscorlib]System.String::Format(string,
object)
IL_023e: call void [mscorlib]System.Console::WriteLine(string)
IL_0243: nop
IL_0244: ret
} // end of method CS6_StringInterpolation::InvalidFormatString
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method CS6_StringInterpolation::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CS6_StringInterpolation
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

38
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -980,8 +980,44 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
isAfterSpace = false; isAfterSpace = false;
EndNode(primitiveExpression); EndNode(primitiveExpression);
} }
public virtual void VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression)
{
StartNode(interpolatedStringExpression);
writer.WriteToken(Roles.Error, "$\"");
foreach (var element in interpolatedStringExpression.Content) {
element.AcceptVisitor(this);
}
writer.WriteToken(Roles.Error, "\"");
isAfterSpace = false;
EndNode(interpolatedStringExpression);
}
public virtual void VisitInterpolation(Interpolation interpolation)
{
StartNode(interpolation);
writer.WriteToken(Roles.LBrace, "{");
interpolation.Expression.AcceptVisitor(this);
if (interpolation.Suffix != null) {
writer.WriteToken(Roles.Colon, ":");
writer.WritePrimitiveValue("", interpolation.Suffix);
}
writer.WriteToken(Roles.RBrace, "}");
EndNode(interpolation);
}
public virtual void VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText)
{
StartNode(interpolatedStringText);
writer.WritePrimitiveValue("", TextWriterTokenWriter.ConvertString(interpolatedStringText.Text));
EndNode(interpolatedStringText);
}
#endregion #endregion
public virtual void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) public virtual void VisitSizeOfExpression(SizeOfExpression sizeOfExpression)
{ {
StartNode(sizeOfExpression); StartNode(sizeOfExpression);

51
ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs

@ -445,7 +445,22 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
VisitChildren (indexerExpression); VisitChildren (indexerExpression);
} }
public virtual void VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression)
{
VisitChildren(interpolatedStringExpression);
}
public virtual void VisitInterpolation(Interpolation interpolation)
{
VisitChildren(interpolation);
}
public virtual void VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText)
{
VisitChildren(interpolatedStringText);
}
public virtual void VisitInvocationExpression (InvocationExpression invocationExpression) public virtual void VisitInvocationExpression (InvocationExpression invocationExpression)
{ {
VisitChildren (invocationExpression); VisitChildren (invocationExpression);
@ -1062,7 +1077,22 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
return VisitChildren (indexerExpression); return VisitChildren (indexerExpression);
} }
public virtual T VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression)
{
return VisitChildren(interpolatedStringExpression);
}
public virtual T VisitInterpolation(Interpolation interpolation)
{
return VisitChildren(interpolation);
}
public virtual T VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText)
{
return VisitChildren(interpolatedStringText);
}
public virtual T VisitInvocationExpression (InvocationExpression invocationExpression) public virtual T VisitInvocationExpression (InvocationExpression invocationExpression)
{ {
return VisitChildren (invocationExpression); return VisitChildren (invocationExpression);
@ -1679,7 +1709,22 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
return VisitChildren (indexerExpression, data); return VisitChildren (indexerExpression, data);
} }
public virtual S VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression, T data)
{
return VisitChildren(interpolatedStringExpression, data);
}
public virtual S VisitInterpolation(Interpolation interpolation, T data)
{
return VisitChildren(interpolation, data);
}
public virtual S VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText, T data)
{
return VisitChildren(interpolatedStringText, data);
}
public virtual S VisitInvocationExpression (InvocationExpression invocationExpression, T data) public virtual S VisitInvocationExpression (InvocationExpression invocationExpression, T data)
{ {
return VisitChildren (invocationExpression, data); return VisitChildren (invocationExpression, data);

169
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs

@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using System.Text;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public class InterpolatedStringExpression : Expression
{
public AstNodeCollection<InterpolatedStringContent> Content {
get { return GetChildrenByRole(InterpolatedStringContent.Role); }
}
public InterpolatedStringExpression()
{
}
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitInterpolatedStringExpression(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitInterpolatedStringExpression(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitInterpolatedStringExpression(this, data);
}
protected internal override bool DoMatch(AstNode other, Match match)
{
InterpolatedStringExpression o = other as InterpolatedStringExpression;
return o != null && !o.IsNull && this.Content.DoMatch(o.Content, match);
}
}
public abstract class InterpolatedStringContent : AstNode
{
#region Null
public new static readonly InterpolatedStringContent Null = new NullInterpolatedStringContent();
sealed class NullInterpolatedStringContent : InterpolatedStringContent
{
public override bool IsNull {
get {
return true;
}
}
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitNullNode(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitNullNode(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitNullNode(this, data);
}
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
return other == null || other.IsNull;
}
}
#endregion
public new static readonly Role<InterpolatedStringContent> Role = new Role<InterpolatedStringContent>("InterpolatedStringContent", Syntax.InterpolatedStringContent.Null);
public override NodeType NodeType => NodeType.Unknown;
}
/// <summary>
/// { Expression }
/// </summary>
public class Interpolation : InterpolatedStringContent
{
public CSharpTokenNode LBraceToken {
get { return GetChildByRole(Roles.LBrace); }
}
public Expression Expression {
get { return GetChildByRole(Roles.Expression); }
set { SetChildByRole(Roles.Expression, value); }
}
public string Suffix { get; }
public CSharpTokenNode RBraceToken {
get { return GetChildByRole(Roles.RBrace); }
}
public Interpolation()
{
}
public Interpolation(Expression expression, string suffix = null)
{
Expression = expression;
Suffix = suffix;
}
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitInterpolation(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitInterpolation(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitInterpolation(this, data);
}
protected internal override bool DoMatch(AstNode other, Match match)
{
Interpolation o = other as Interpolation;
return o != null && this.Expression.DoMatch(o.Expression, match);
}
}
public class InterpolatedStringText : InterpolatedStringContent
{
public string Text { get; set; }
public InterpolatedStringText()
{
}
public InterpolatedStringText(string text)
{
Text = text;
}
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitInterpolatedStringText(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitInterpolatedStringText(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitInterpolatedStringText(this, data);
}
protected internal override bool DoMatch(AstNode other, Match match)
{
InterpolatedStringText o = other as InterpolatedStringText;
return o != null && o.Text == this.Text;
}
}
}

16
ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs

@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void VisitDirectionExpression(DirectionExpression directionExpression); void VisitDirectionExpression(DirectionExpression directionExpression);
void VisitIdentifierExpression(IdentifierExpression identifierExpression); void VisitIdentifierExpression(IdentifierExpression identifierExpression);
void VisitIndexerExpression(IndexerExpression indexerExpression); void VisitIndexerExpression(IndexerExpression indexerExpression);
void VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression);
void VisitInvocationExpression(InvocationExpression invocationExpression); void VisitInvocationExpression(InvocationExpression invocationExpression);
void VisitIsExpression(IsExpression isExpression); void VisitIsExpression(IsExpression isExpression);
void VisitLambdaExpression(LambdaExpression lambdaExpression); void VisitLambdaExpression(LambdaExpression lambdaExpression);
@ -147,6 +148,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void VisitConstraint(Constraint constraint); void VisitConstraint(Constraint constraint);
void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode); void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode);
void VisitIdentifier(Identifier identifier); void VisitIdentifier(Identifier identifier);
void VisitInterpolation(Interpolation interpolation);
void VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText);
void VisitNullNode(AstNode nullNode); void VisitNullNode(AstNode nullNode);
void VisitErrorNode(AstNode errorNode); void VisitErrorNode(AstNode errorNode);
@ -173,6 +177,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitDirectionExpression(DirectionExpression directionExpression); S VisitDirectionExpression(DirectionExpression directionExpression);
S VisitIdentifierExpression(IdentifierExpression identifierExpression); S VisitIdentifierExpression(IdentifierExpression identifierExpression);
S VisitIndexerExpression(IndexerExpression indexerExpression); S VisitIndexerExpression(IndexerExpression indexerExpression);
S VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression);
S VisitInvocationExpression(InvocationExpression invocationExpression); S VisitInvocationExpression(InvocationExpression invocationExpression);
S VisitIsExpression(IsExpression isExpression); S VisitIsExpression(IsExpression isExpression);
S VisitLambdaExpression(LambdaExpression lambdaExpression); S VisitLambdaExpression(LambdaExpression lambdaExpression);
@ -281,7 +286,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitConstraint(Constraint constraint); S VisitConstraint(Constraint constraint);
S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode); S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode);
S VisitIdentifier(Identifier identifier); S VisitIdentifier(Identifier identifier);
S VisitInterpolation(Interpolation interpolation);
S VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText);
S VisitNullNode(AstNode nullNode); S VisitNullNode(AstNode nullNode);
S VisitErrorNode(AstNode errorNode); S VisitErrorNode(AstNode errorNode);
S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern); S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern);
@ -307,6 +315,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitDirectionExpression(DirectionExpression directionExpression, T data); S VisitDirectionExpression(DirectionExpression directionExpression, T data);
S VisitIdentifierExpression(IdentifierExpression identifierExpression, T data); S VisitIdentifierExpression(IdentifierExpression identifierExpression, T data);
S VisitIndexerExpression(IndexerExpression indexerExpression, T data); S VisitIndexerExpression(IndexerExpression indexerExpression, T data);
S VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression, T data);
S VisitInvocationExpression(InvocationExpression invocationExpression, T data); S VisitInvocationExpression(InvocationExpression invocationExpression, T data);
S VisitIsExpression(IsExpression isExpression, T data); S VisitIsExpression(IsExpression isExpression, T data);
S VisitLambdaExpression(LambdaExpression lambdaExpression, T data); S VisitLambdaExpression(LambdaExpression lambdaExpression, T data);
@ -415,7 +424,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitConstraint(Constraint constraint, T data); S VisitConstraint(Constraint constraint, T data);
S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode, T data); S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode, T data);
S VisitIdentifier(Identifier identifier, T data); S VisitIdentifier(Identifier identifier, T data);
S VisitInterpolation(Interpolation interpolation, T data);
S VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText, T data);
S VisitNullNode(AstNode nullNode, T data); S VisitNullNode(AstNode nullNode, T data);
S VisitErrorNode(AstNode errorNode, T data); S VisitErrorNode(AstNode errorNode, T data);
S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern, T data); S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern, T data);

159
ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs

@ -17,8 +17,10 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
@ -53,7 +55,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (method == null) if (method == null)
return; return;
var arguments = invocationExpression.Arguments.ToArray(); var arguments = invocationExpression.Arguments.ToArray();
// Reduce "String.Concat(a, b)" to "a + b" // Reduce "String.Concat(a, b)" to "a + b"
if (method.Name == "Concat" && method.DeclaringType.FullName == "System.String" && CheckArgumentsForStringConcat(arguments)) { if (method.Name == "Concat" && method.DeclaringType.FullName == "System.String" && CheckArgumentsForStringConcat(arguments)) {
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
@ -65,7 +67,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
invocationExpression.ReplaceWith(expr); invocationExpression.ReplaceWith(expr);
return; return;
} }
switch (method.FullName) { switch (method.FullName) {
case "System.Type.GetTypeFromHandle": case "System.Type.GetTypeFromHandle":
if (arguments.Length == 1) { if (arguments.Length == 1) {
@ -106,8 +108,67 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
invocationExpression.ReplaceWith(new ObjectCreateExpression(context.TypeSystemAstBuilder.ConvertType(method.TypeArguments.First()))); invocationExpression.ReplaceWith(new ObjectCreateExpression(context.TypeSystemAstBuilder.ConvertType(method.TypeArguments.First())));
} }
break; break;
case "System.String.Format":
if (context.Settings.StringInterpolation && arguments.Length > 1
&& arguments[0] is PrimitiveExpression stringExpression && stringExpression.Value is string
&& arguments.Skip(1).All(a => !a.DescendantsAndSelf.OfType<PrimitiveExpression>().Any(p => p.Value is string)))
{
var tokens = new List<(TokenKind, int, string)>();
int i = 0;
foreach (var (kind, data) in TokenizeFormatString((string)stringExpression.Value)) {
int index;
switch (kind) {
case TokenKind.Error:
return;
case TokenKind.String:
tokens.Add((kind, -1, data));
break;
case TokenKind.Argument:
if (!int.TryParse(data, out index) || index != i)
return;
i++;
tokens.Add((kind, index, null));
break;
case TokenKind.ArgumentWithFormat:
string[] arg = data.Split(new[] { ':' }, 2);
if (arg.Length != 2 || arg[1].Length == 0)
return;
if (!int.TryParse(arg[0], out index) || index != i)
return;
i++;
tokens.Add((kind, index, arg[1]));
break;
default:
return;
}
}
if (i != arguments.Length - 1)
return;
List<InterpolatedStringContent> content = new List<InterpolatedStringContent>();
if (tokens.Count > 0) {
foreach (var (kind, index, text) in tokens) {
switch (kind) {
case TokenKind.String:
content.Add(new InterpolatedStringText(text));
break;
case TokenKind.Argument:
content.Add(new Interpolation(WrapInParens(arguments[index + 1].Detach())));
break;
case TokenKind.ArgumentWithFormat:
content.Add(new Interpolation(WrapInParens(arguments[index + 1].Detach()), text));
break;
}
}
var expr = new InterpolatedStringExpression();
expr.Content.AddRange(content);
expr.CopyAnnotationsFrom(invocationExpression);
invocationExpression.ReplaceWith(expr);
return;
}
}
break;
} }
BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name); BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name);
if (bop != null && arguments.Length == 2) { if (bop != null && arguments.Length == 2) {
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
@ -136,10 +197,90 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
invocationExpression.ReplaceWith(arguments[0]); invocationExpression.ReplaceWith(arguments[0]);
return; return;
} }
return; return;
} }
Expression WrapInParens(Expression expression)
{
if (expression is ConditionalExpression)
return new ParenthesizedExpression(expression);
return expression;
}
enum TokenKind
{
Error,
String,
Argument,
ArgumentWithFormat
}
private IEnumerable<(TokenKind, string)> TokenizeFormatString(string value)
{
int pos = -1;
int Peek(int steps = 1)
{
if (pos + steps < value.Length)
return value[pos + steps];
return -1;
}
int Next()
{
int val = Peek();
pos++;
return val;
}
int next;
TokenKind kind = TokenKind.String;
StringBuilder sb = new StringBuilder();
while ((next = Next()) > -1) {
switch ((char)next) {
case '{':
if (Peek() == '{') {
kind = TokenKind.String;
sb.Append("{{");
Next();
} else {
if (sb.Length > 0) {
yield return (kind, sb.ToString());
}
kind = TokenKind.Argument;
sb.Clear();
}
break;
case '}':
if (kind != TokenKind.String) {
yield return (kind, sb.ToString());
sb.Clear();
kind = TokenKind.String;
} else {
sb.Append((char)next);
}
break;
case ':':
if (kind == TokenKind.Argument) {
kind = TokenKind.ArgumentWithFormat;
}
sb.Append(':');
break;
default:
sb.Append((char)next);
break;
}
}
if (sb.Length > 0) {
if (kind == TokenKind.String)
yield return (kind, sb.ToString());
else
yield return (TokenKind.Error, null);
}
}
bool CheckArgumentsForStringConcat(Expression[] arguments) bool CheckArgumentsForStringConcat(Expression[] arguments)
{ {
if (arguments.Length < 2) if (arguments.Length < 2)
@ -188,13 +329,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return null; return null;
} }
} }
static UnaryOperatorType? GetUnaryOperatorTypeFromMetadataName(string name) static UnaryOperatorType? GetUnaryOperatorTypeFromMetadataName(string name)
{ {
switch (name) { switch (name) {
case "op_LogicalNot": case "op_LogicalNot":
return UnaryOperatorType.Not; return UnaryOperatorType.Not;
case "op_OnesComplement": case "op_OnesComplement":
return UnaryOperatorType.BitNot; return UnaryOperatorType.BitNot;
case "op_UnaryNegation": case "op_UnaryNegation":
return UnaryOperatorType.Minus; return UnaryOperatorType.Minus;
@ -208,7 +349,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return null; return null;
} }
} }
static readonly Expression getMethodOrConstructorFromHandlePattern = static readonly Expression getMethodOrConstructorFromHandlePattern =
new CastExpression(new Choice { new CastExpression(new Choice {
new TypePattern(typeof(MethodInfo)), new TypePattern(typeof(MethodInfo)),
@ -217,7 +358,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
new NamedNode("ldtokenNode", new MemberReferenceExpression(new LdTokenPattern("method").ToExpression(), "MethodHandle")), new NamedNode("ldtokenNode", new MemberReferenceExpression(new LdTokenPattern("method").ToExpression(), "MethodHandle")),
new OptionalNode(new MemberReferenceExpression(new TypeOfExpression(new AnyNode("declaringType")), "TypeHandle")) new OptionalNode(new MemberReferenceExpression(new TypeOfExpression(new AnyNode("declaringType")), "TypeHandle"))
)); ));
public override void VisitCastExpression(CastExpression castExpression) public override void VisitCastExpression(CastExpression castExpression)
{ {
base.VisitCastExpression(castExpression); base.VisitCastExpression(castExpression);
@ -233,7 +374,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
castExpression.ReplaceWith(m.Get<AstNode>("ldtokenNode").Single().CopyAnnotationsFrom(castExpression)); castExpression.ReplaceWith(m.Get<AstNode>("ldtokenNode").Single().CopyAnnotationsFrom(castExpression));
} }
} }
void IAstTransform.Run(AstNode rootNode, TransformContext context) void IAstTransform.Run(AstNode rootNode, TransformContext context)
{ {
try { try {

15
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -318,6 +318,21 @@ namespace ICSharpCode.Decompiler
} }
} }
bool stringInterpolation = true;
/// <summary>
/// Gets/Sets whether to use C# 6.0 string interpolation
/// </summary>
public bool StringInterpolation {
get { return stringInterpolation; }
set {
if (stringInterpolation != value) {
stringInterpolation = value;
OnPropertyChanged();
}
}
}
bool showXmlDocumentation = true; bool showXmlDocumentation = true;
/// <summary> /// <summary>

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -88,6 +88,7 @@
<Compile Include="CSharp\Syntax\Expressions\Expression.cs" /> <Compile Include="CSharp\Syntax\Expressions\Expression.cs" />
<Compile Include="CSharp\Syntax\Expressions\IdentifierExpression.cs" /> <Compile Include="CSharp\Syntax\Expressions\IdentifierExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\IndexerExpression.cs" /> <Compile Include="CSharp\Syntax\Expressions\IndexerExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\InterpolatedStringExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\InvocationExpression.cs" /> <Compile Include="CSharp\Syntax\Expressions\InvocationExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\IsExpression.cs" /> <Compile Include="CSharp\Syntax\Expressions\IsExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\LambdaExpression.cs" /> <Compile Include="CSharp\Syntax\Expressions\LambdaExpression.cs" />

Loading…
Cancel
Save