Browse Source

Add support for C# 9: foreach with GetEnumerator extension methods.

pull/2160/head
Siegfried Pammer 5 years ago
parent
commit
13636c89cc
  1. 7
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  2. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  4. 57
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS9_ExtensionGetEnumerator.cs
  5. 9
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  6. 9
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  7. 71
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  8. 103
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs
  9. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  10. 9
      ILSpy/Properties/Resources.Designer.cs
  11. 3
      ILSpy/Properties/Resources.resx

7
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -296,14 +296,17 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
if (flags.HasFlag(CompilerOptions.UseRoslyn)) if (flags.HasFlag(CompilerOptions.UseRoslyn))
{ {
var languageVersion = flags.HasFlag(CompilerOptions.Preview)
? Microsoft.CodeAnalysis.CSharp.LanguageVersion.Preview
: Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8;
var parseOptions = new CSharpParseOptions( var parseOptions = new CSharpParseOptions(
preprocessorSymbols: preprocessorSymbols.ToArray(), preprocessorSymbols: preprocessorSymbols.ToArray(),
languageVersion: flags.HasFlag(CompilerOptions.Preview) ? Microsoft.CodeAnalysis.CSharp.LanguageVersion.Preview : Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8 languageVersion: languageVersion
); );
var syntaxTrees = sourceFileNames.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), parseOptions, path: f, encoding: Encoding.UTF8)); var syntaxTrees = sourceFileNames.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), parseOptions, path: f, encoding: Encoding.UTF8));
if (flags.HasFlag(CompilerOptions.ReferenceCore)) if (flags.HasFlag(CompilerOptions.ReferenceCore))
{ {
syntaxTrees = syntaxTrees.Concat(new[] { SyntaxFactory.ParseSyntaxTree(targetFrameworkAttributeSnippet) }); syntaxTrees = syntaxTrees.Concat(new[] { SyntaxFactory.ParseSyntaxTree(targetFrameworkAttributeSnippet, parseOptions) });
} }
IEnumerable<MetadataReference> references; IEnumerable<MetadataReference> references;
if (flags.HasFlag(CompilerOptions.ReferenceCore)) if (flags.HasFlag(CompilerOptions.ReferenceCore))

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

@ -96,6 +96,7 @@
<Compile Include="TestCases\Correctness\DeconstructionTests.cs" /> <Compile Include="TestCases\Correctness\DeconstructionTests.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" /> <Compile Include="TestCases\Correctness\StringConcat.cs" />
<None Include="TestCases\Pretty\FunctionPointers.cs" /> <None Include="TestCases\Pretty\FunctionPointers.cs" />
<None Include="TestCases\Pretty\CS9_ExtensionGetEnumerator.cs" />
<None Include="TestCases\Pretty\UsingVariables.cs" /> <None Include="TestCases\Pretty\UsingVariables.cs" />
<None Include="TestCases\Pretty\AsyncForeach.cs" /> <None Include="TestCases\Pretty\AsyncForeach.cs" />
<Compile Include="TestCases\Pretty\DeconstructionTests.cs" /> <Compile Include="TestCases\Pretty\DeconstructionTests.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -534,6 +534,12 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions); RunForLibrary(cscOptions: cscOptions);
} }
[Test]
public void CS9_ExtensionGetEnumerator([ValueSource(nameof(dotnetCoreOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}
void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null) void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null)
{ {
Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings); Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings);

57
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS9_ExtensionGetEnumerator.cs

@ -0,0 +1,57 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class CS9_ExtensionGetEnumerator
{
public class NonGeneric
{
}
public class Generic<T>
{
}
public void Test(NonGeneric c)
{
foreach (object? item in c)
{
Console.WriteLine(item);
}
}
public void Test(Generic<int> c)
{
foreach (int item in c)
{
Console.WriteLine(item);
}
}
public async void TestAsync(Generic<int> c)
{
await foreach (int item in c)
{
Console.WriteLine(item);
}
}
}
public static class CS9_ExtensionGetEnumerator_Ext
{
public static IEnumerator GetEnumerator(this CS9_ExtensionGetEnumerator.NonGeneric c)
{
throw null;
}
public static IEnumerator<T> GetEnumerator<T>(this CS9_ExtensionGetEnumerator.Generic<T> c)
{
throw null;
}
public static IAsyncEnumerator<T> GetAsyncEnumerator<T>(this CS9_ExtensionGetEnumerator.Generic<T> c)
{
throw null;
}
}
}

9
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -1456,7 +1456,14 @@ namespace ICSharpCode.Decompiler.CSharp
if (localSettings.DecompileMemberBodies) if (localSettings.DecompileMemberBodies)
{ {
AddDefinesForConditionalAttributes(function, decompileRun); AddDefinesForConditionalAttributes(function, decompileRun);
var statementBuilder = new StatementBuilder(typeSystem, decompilationContext, function, localSettings, CancellationToken); var statementBuilder = new StatementBuilder(
typeSystem,
decompilationContext,
function,
localSettings,
decompileRun,
CancellationToken
);
body = statementBuilder.ConvertAsBlock(function.Body); body = statementBuilder.ConvertAsBlock(function.Body);
Comment prev = null; Comment prev = null;

9
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -2205,7 +2205,14 @@ namespace ICSharpCode.Decompiler.CSharp
ame.IsAsync = function.IsAsync; ame.IsAsync = function.IsAsync;
ame.Parameters.AddRange(MakeParameters(function.Parameters, function)); ame.Parameters.AddRange(MakeParameters(function.Parameters, function));
ame.HasParameterList = ame.Parameters.Count > 0; ame.HasParameterList = ame.Parameters.Count > 0;
StatementBuilder builder = new StatementBuilder(typeSystem, this.decompilationContext, function, settings, cancellationToken); var builder = new StatementBuilder(
typeSystem,
this.decompilationContext,
function,
settings,
statementBuilder.decompileRun,
cancellationToken
);
var body = builder.ConvertAsBlock(function.Body); var body = builder.ConvertAsBlock(function.Body);
Comment prev = null; Comment prev = null;

71
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -24,6 +24,8 @@ using System.Threading;
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.CSharp.Transforms;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Semantics;
@ -37,6 +39,7 @@ namespace ICSharpCode.Decompiler.CSharp
internal readonly ExpressionBuilder exprBuilder; internal readonly ExpressionBuilder exprBuilder;
readonly ILFunction currentFunction; readonly ILFunction currentFunction;
readonly IDecompilerTypeSystem typeSystem; readonly IDecompilerTypeSystem typeSystem;
internal readonly DecompileRun decompileRun;
readonly DecompilerSettings settings; readonly DecompilerSettings settings;
readonly CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
@ -44,16 +47,28 @@ namespace ICSharpCode.Decompiler.CSharp
internal IType currentResultType; internal IType currentResultType;
internal bool currentIsIterator; internal bool currentIsIterator;
public StatementBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, CancellationToken cancellationToken) public StatementBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext,
ILFunction currentFunction, DecompilerSettings settings, DecompileRun decompileRun,
CancellationToken cancellationToken)
{ {
Debug.Assert(typeSystem != null && decompilationContext != null); Debug.Assert(typeSystem != null && decompilationContext != null);
this.exprBuilder = new ExpressionBuilder(this, typeSystem, decompilationContext, currentFunction, settings, cancellationToken); this.exprBuilder = new ExpressionBuilder(
this,
typeSystem,
decompilationContext,
currentFunction,
settings,
cancellationToken
);
this.currentFunction = currentFunction; this.currentFunction = currentFunction;
this.currentReturnContainer = (BlockContainer)currentFunction.Body; this.currentReturnContainer = (BlockContainer)currentFunction.Body;
this.currentIsIterator = currentFunction.IsIterator; this.currentIsIterator = currentFunction.IsIterator;
this.currentResultType = currentFunction.IsAsync ? currentFunction.AsyncReturnType : currentFunction.ReturnType; this.currentResultType = currentFunction.IsAsync
? currentFunction.AsyncReturnType
: currentFunction.ReturnType;
this.typeSystem = typeSystem; this.typeSystem = typeSystem;
this.settings = settings; this.settings = settings;
this.decompileRun = decompileRun;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
} }
@ -462,6 +477,13 @@ namespace ICSharpCode.Decompiler.CSharp
new MemberReferenceExpression(new AnyNode("collection").ToExpression(), "GetAsyncEnumerator") new MemberReferenceExpression(new AnyNode("collection").ToExpression(), "GetAsyncEnumerator")
} }
); );
static readonly InvocationExpression extensionGetEnumeratorPattern = new InvocationExpression(
new Choice {
new MemberReferenceExpression(new AnyNode("type").ToExpression(), "GetEnumerator"),
new MemberReferenceExpression(new AnyNode("type").ToExpression(), "GetAsyncEnumerator")
},
new AnyNode("collection")
);
static readonly Expression moveNextConditionPattern = new Choice { static readonly Expression moveNextConditionPattern = new Choice {
new InvocationExpression(new MemberReferenceExpression(new NamedNode("enumerator", new IdentifierExpression(Pattern.AnyString)), "MoveNext")), new InvocationExpression(new MemberReferenceExpression(new NamedNode("enumerator", new IdentifierExpression(Pattern.AnyString)), "MoveNext")),
new UnaryOperatorExpression(UnaryOperatorType.Await, new InvocationExpression(new MemberReferenceExpression(new NamedNode("enumerator", new IdentifierExpression(Pattern.AnyString)), "MoveNextAsync"))) new UnaryOperatorExpression(UnaryOperatorType.Await, new InvocationExpression(new MemberReferenceExpression(new NamedNode("enumerator", new IdentifierExpression(Pattern.AnyString)), "MoveNextAsync")))
@ -549,10 +571,38 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
return null; return null;
} }
// Check if the using resource matches the GetEnumerator pattern. Match m;
var m = getEnumeratorPattern.Match(resource); if (settings.ExtensionMethods && settings.ForEachWithGetEnumeratorExtension)
{
// Check if the using resource matches the GetEnumerator pattern ...
m = getEnumeratorPattern.Match(resource);
if (!m.Success)
{
// ... or the extension GetEnumeratorPattern.
m = extensionGetEnumeratorPattern.Match(resource);
if (!m.Success)
return null;
// Validate that the invocation is an extension method invocation.
var context = new CSharpTypeResolveContext(
typeSystem.MainModule,
decompileRun.UsingScope.Resolve(typeSystem)
);
if (!IntroduceExtensionMethods.CanTransformToExtensionMethodCall(context,
(InvocationExpression)resource))
{
return null;
}
}
}
else
{
// Check if the using resource matches the GetEnumerator pattern.
m = getEnumeratorPattern.Match(resource);
if (!m.Success)
return null;
}
// The using body must be a BlockContainer. // The using body must be a BlockContainer.
if (!(inst.Body is BlockContainer container) || !m.Success) if (!(inst.Body is BlockContainer container))
return null; return null;
bool isAsync = ((MemberReferenceExpression)((InvocationExpression)resource).Target).MemberName == "GetAsyncEnumerator"; bool isAsync = ((MemberReferenceExpression)((InvocationExpression)resource).Target).MemberName == "GetAsyncEnumerator";
if (isAsync != inst.IsAsync) if (isAsync != inst.IsAsync)
@ -1220,7 +1270,14 @@ namespace ICSharpCode.Decompiler.CSharp
LocalFunctionDeclarationStatement TranslateFunction(ILFunction function) LocalFunctionDeclarationStatement TranslateFunction(ILFunction function)
{ {
var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken); var nestedBuilder = new StatementBuilder(
typeSystem,
exprBuilder.decompilationContext,
function,
settings,
decompileRun,
cancellationToken
);
var astBuilder = exprBuilder.astBuilder; var astBuilder = exprBuilder.astBuilder;
var method = (MethodDeclaration)astBuilder.ConvertEntity(function.ReducedMethod); var method = (MethodDeclaration)astBuilder.ConvertEntity(function.ReducedMethod);
method.Body = nestedBuilder.ConvertAsBlock(function.Body); method.Body = nestedBuilder.ConvertAsBlock(function.Body);

103
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs

@ -106,11 +106,59 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public override void VisitInvocationExpression(InvocationExpression invocationExpression) public override void VisitInvocationExpression(InvocationExpression invocationExpression)
{ {
base.VisitInvocationExpression(invocationExpression); base.VisitInvocationExpression(invocationExpression);
if (!CanTransformToExtensionMethodCall(resolver, invocationExpression, out var memberRefExpr,
out var target, out var firstArgument))
{
return;
}
var method = (IMethod)invocationExpression.GetSymbol();
if (firstArgument is DirectionExpression dirExpr)
{
if (!context.Settings.RefExtensionMethods || dirExpr.FieldDirection == FieldDirection.Out)
return;
firstArgument = dirExpr.Expression;
target = firstArgument.GetResolveResult();
dirExpr.Detach();
}
else if (firstArgument is NullReferenceExpression)
{
Debug.Assert(context.RequiredNamespacesSuperset.Contains(method.Parameters[0].Type.Namespace));
firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach()));
}
if (invocationExpression.Target is IdentifierExpression identifierExpression)
{
identifierExpression.Detach();
memberRefExpr = new MemberReferenceExpression(firstArgument.Detach(), method.Name, identifierExpression.TypeArguments.Detach());
invocationExpression.Target = memberRefExpr;
}
else
{
memberRefExpr.Target = firstArgument.Detach();
}
if (invocationExpression.GetResolveResult() is CSharpInvocationResolveResult irr)
{
// do not forget to update the CSharpInvocationResolveResult => set IsExtensionMethodInvocation == true
invocationExpression.RemoveAnnotations<CSharpInvocationResolveResult>();
var newResolveResult = new CSharpInvocationResolveResult(
irr.TargetResult, irr.Member, irr.Arguments, irr.OverloadResolutionErrors,
isExtensionMethodInvocation: true, irr.IsExpandedForm, irr.IsDelegateInvocation,
irr.GetArgumentToParameterMap(), irr.InitializerStatements);
invocationExpression.AddAnnotation(newResolveResult);
}
}
static bool CanTransformToExtensionMethodCall(CSharpResolver resolver,
InvocationExpression invocationExpression, out MemberReferenceExpression memberRefExpr,
out ResolveResult target,
out Expression firstArgument)
{
var method = invocationExpression.GetSymbol() as IMethod; var method = invocationExpression.GetSymbol() as IMethod;
memberRefExpr = null;
target = null;
firstArgument = null;
if (method == null || !method.IsExtensionMethod || !invocationExpression.Arguments.Any()) if (method == null || !method.IsExtensionMethod || !invocationExpression.Arguments.Any())
return; return false;
IReadOnlyList<IType> typeArguments; IReadOnlyList<IType> typeArguments;
MemberReferenceExpression memberRefExpr;
switch (invocationExpression.Target) switch (invocationExpression.Target)
{ {
case MemberReferenceExpression mre: case MemberReferenceExpression mre:
@ -122,13 +170,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
memberRefExpr = null; memberRefExpr = null;
break; break;
default: default:
return; return false;
} }
var firstArgument = invocationExpression.Arguments.First(); firstArgument = invocationExpression.Arguments.First();
if (firstArgument is NamedArgumentExpression) if (firstArgument is NamedArgumentExpression)
return; return false;
var target = firstArgument.GetResolveResult(); target = firstArgument.GetResolveResult();
if (target is ConstantResolveResult crr && crr.ConstantValue == null) if (target is ConstantResolveResult crr && crr.ConstantValue == null)
{ {
target = new ConversionResolveResult(method.Parameters[0].Type, crr, Conversion.NullLiteralConversion); target = new ConversionResolveResult(method.Parameters[0].Type, crr, Conversion.NullLiteralConversion);
@ -153,41 +201,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
pos++; pos++;
} }
if (!CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames)) return CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames);
return; }
if (firstArgument is DirectionExpression dirExpr)
{ public static bool CanTransformToExtensionMethodCall(CSharpTypeResolveContext resolveContext,
if (!context.Settings.RefExtensionMethods || dirExpr.FieldDirection == FieldDirection.Out) InvocationExpression invocationExpression)
return; {
firstArgument = dirExpr.Expression; return CanTransformToExtensionMethodCall(new CSharpResolver(resolveContext),
target = firstArgument.GetResolveResult(); invocationExpression, out _, out _, out _);
dirExpr.Detach();
}
else if (firstArgument is NullReferenceExpression)
{
Debug.Assert(context.RequiredNamespacesSuperset.Contains(method.Parameters[0].Type.Namespace));
firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach()));
}
if (invocationExpression.Target is IdentifierExpression identifierExpression)
{
identifierExpression.Detach();
memberRefExpr = new MemberReferenceExpression(firstArgument.Detach(), method.Name, identifierExpression.TypeArguments.Detach());
invocationExpression.Target = memberRefExpr;
}
else
{
memberRefExpr.Target = firstArgument.Detach();
}
if (invocationExpression.GetResolveResult() is CSharpInvocationResolveResult irr)
{
// do not forget to update the CSharpInvocationResolveResult => set IsExtensionMethodInvocation == true
invocationExpression.RemoveAnnotations<CSharpInvocationResolveResult>();
var newResolveResult = new CSharpInvocationResolveResult(
irr.TargetResult, irr.Member, irr.Arguments, irr.OverloadResolutionErrors,
isExtensionMethodInvocation: true, irr.IsExpandedForm, irr.IsDelegateInvocation,
irr.GetArgumentToParameterMap(), irr.InitializerStatements);
invocationExpression.AddAnnotation(newResolveResult);
}
} }
public static bool CanTransformToExtensionMethodCall(CSharpResolver resolver, IMethod method, public static bool CanTransformToExtensionMethodCall(CSharpResolver resolver, IMethod method,

21
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -134,12 +134,13 @@ namespace ICSharpCode.Decompiler
nativeIntegers = false; nativeIntegers = false;
initAccessors = false; initAccessors = false;
functionPointers = false; functionPointers = false;
forEachWithGetEnumeratorExtension = false;
} }
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (nativeIntegers || initAccessors || functionPointers) if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension)
return CSharp.LanguageVersion.Preview; return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement
|| staticLocalFunctions || ranges || switchExpressions) || staticLocalFunctions || ranges || switchExpressions)
@ -586,6 +587,24 @@ namespace ICSharpCode.Decompiler
} }
} }
bool forEachWithGetEnumeratorExtension = true;
/// <summary>
/// Support GetEnumerator extension methods in foreach.
/// </summary>
[Category("C# 9.0 (experimental)")]
[Description("DecompilerSettings.DecompileForEachWithGetEnumeratorExtension")]
public bool ForEachWithGetEnumeratorExtension {
get { return forEachWithGetEnumeratorExtension; }
set {
if (forEachWithGetEnumeratorExtension != value)
{
forEachWithGetEnumeratorExtension = value;
OnPropertyChanged();
}
}
}
bool lockStatement = true; bool lockStatement = true;
/// <summary> /// <summary>

9
ILSpy/Properties/Resources.Designer.cs generated

@ -801,6 +801,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Decompile foreach statements with GetEnumerator extension methods.
/// </summary>
public static string DecompilerSettings_DecompileForEachWithGetEnumeratorExtension {
get {
return ResourceManager.GetString("DecompilerSettings.DecompileForEachWithGetEnumeratorExtension", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Decompile use of the &apos;dynamic&apos; type. /// Looks up a localized string similar to Decompile use of the &apos;dynamic&apos; type.
/// </summary> /// </summary>

3
ILSpy/Properties/Resources.resx

@ -297,6 +297,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.DecompileExpressionTrees" xml:space="preserve"> <data name="DecompilerSettings.DecompileExpressionTrees" xml:space="preserve">
<value>Decompile expression trees</value> <value>Decompile expression trees</value>
</data> </data>
<data name="DecompilerSettings.DecompileForEachWithGetEnumeratorExtension" xml:space="preserve">
<value>Decompile foreach statements with GetEnumerator extension methods</value>
</data>
<data name="DecompilerSettings.DecompileUseOfTheDynamicType" xml:space="preserve"> <data name="DecompilerSettings.DecompileUseOfTheDynamicType" xml:space="preserve">
<value>Decompile use of the 'dynamic' type</value> <value>Decompile use of the 'dynamic' type</value>
</data> </data>

Loading…
Cancel
Save