Browse Source

Add support for await using statements.

pull/1730/head
Siegfried Pammer 6 years ago
parent
commit
c7f98a4db7
  1. 4
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  2. 22
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  3. 60
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs
  4. 17
      ICSharpCode.Decompiler/DecompilerSettings.cs
  5. 8
      ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs
  6. 107
      ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
  7. 13
      ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs
  8. 9
      ILSpy/Properties/Resources.Designer.cs
  9. 6
      ILSpy/Properties/Resources.resx

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

@ -1112,6 +1112,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType); var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType);
if (opType == UnaryOperatorType.Await) { if (opType == UnaryOperatorType.Await) {
WriteKeyword(opSymbol); WriteKeyword(opSymbol);
Space();
} else if (!IsPostfixOperator(opType) && opSymbol != null) { } else if (!IsPostfixOperator(opType) && opSymbol != null) {
WriteToken(opSymbol); WriteToken(opSymbol);
} }
@ -1863,6 +1864,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitUsingStatement(UsingStatement usingStatement) public virtual void VisitUsingStatement(UsingStatement usingStatement)
{ {
StartNode(usingStatement); StartNode(usingStatement);
if (usingStatement.IsAsync) {
WriteKeyword(UsingStatement.AwaitRole);
}
WriteKeyword(UsingStatement.UsingKeywordRole); WriteKeyword(UsingStatement.UsingKeywordRole);
Space(policy.SpaceBeforeUsingParentheses); Space(policy.SpaceBeforeUsingParentheses);
LPar(); LPar();

22
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -411,14 +411,29 @@ namespace ICSharpCode.Decompiler.CSharp
return transformed; return transformed;
AstNode usingInit = resource; AstNode usingInit = resource;
var var = inst.Variable; var var = inst.Variable;
if (!inst.ResourceExpression.MatchLdNull() && !NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IDisposable))) { KnownTypeCode knownTypeCode;
IType disposeType;
string disposeTypeMethodName;
if (inst.IsAsync) {
knownTypeCode = KnownTypeCode.IAsyncDisposable;
disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IAsyncDisposable);
disposeTypeMethodName = "DisposeAsync";
} else {
knownTypeCode = KnownTypeCode.IDisposable;
disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable);
disposeTypeMethodName = "Dispose";
}
if (!inst.ResourceExpression.MatchLdNull() && !NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(knownTypeCode))) {
Debug.Assert(var.Kind == VariableKind.UsingLocal); Debug.Assert(var.Kind == VariableKind.UsingLocal);
var.Kind = VariableKind.Local; var.Kind = VariableKind.Local;
var disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable);
var disposeVariable = currentFunction.RegisterVariable( var disposeVariable = currentFunction.RegisterVariable(
VariableKind.Local, disposeType, VariableKind.Local, disposeType,
AssignVariableNames.GenerateVariableName(currentFunction, disposeType) AssignVariableNames.GenerateVariableName(currentFunction, disposeType)
); );
Expression disposeInvocation = new InvocationExpression(new MemberReferenceExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, disposeTypeMethodName));
if (inst.IsAsync) {
disposeInvocation = new UnaryOperatorExpression { Expression = disposeInvocation, Operator = UnaryOperatorType.Await };
}
return new BlockStatement { return new BlockStatement {
new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(var).Expression, resource.Detach())), new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(var).Expression, resource.Detach())),
new TryCatchStatement { new TryCatchStatement {
@ -427,7 +442,7 @@ namespace ICSharpCode.Decompiler.CSharp
new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, new AsExpression(exprBuilder.ConvertVariable(var).Expression, exprBuilder.ConvertType(disposeType)))), new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, new AsExpression(exprBuilder.ConvertVariable(var).Expression, exprBuilder.ConvertType(disposeType)))),
new IfElseStatement { new IfElseStatement {
Condition = new BinaryOperatorExpression(exprBuilder.ConvertVariable(disposeVariable), BinaryOperatorType.InEquality, new NullReferenceExpression()), Condition = new BinaryOperatorExpression(exprBuilder.ConvertVariable(disposeVariable), BinaryOperatorType.InEquality, new NullReferenceExpression()),
TrueStatement = new ExpressionStatement(new InvocationExpression(new MemberReferenceExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, "Dispose"))) TrueStatement = new ExpressionStatement(disposeInvocation)
} }
} }
}, },
@ -441,6 +456,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
return new UsingStatement { return new UsingStatement {
ResourceAcquisition = usingInit, ResourceAcquisition = usingInit,
IsAsync = inst.IsAsync,
EmbeddedStatement = ConvertAsBlock(inst.Body) EmbeddedStatement = ConvertAsBlock(inst.Body)
}; };
} }

60
ICSharpCode.Decompiler/CSharp/Syntax/Statements/UsingStatement.cs

@ -28,57 +28,67 @@
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
/// <summary> /// <summary>
/// using (ResourceAcquisition) EmbeddedStatement /// [ await ] using (ResourceAcquisition) EmbeddedStatement
/// </summary> /// </summary>
public class UsingStatement : Statement public class UsingStatement : Statement
{ {
public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); public static readonly TokenRole UsingKeywordRole = new TokenRole("using");
public static readonly TokenRole AwaitRole = UnaryOperatorExpression.AwaitRole;
public static readonly Role<AstNode> ResourceAcquisitionRole = new Role<AstNode>("ResourceAcquisition", AstNode.Null); public static readonly Role<AstNode> ResourceAcquisitionRole = new Role<AstNode>("ResourceAcquisition", AstNode.Null);
public CSharpTokenNode UsingToken { public CSharpTokenNode UsingToken {
get { return GetChildByRole (UsingKeywordRole); } get { return GetChildByRole(UsingKeywordRole); }
}
public CSharpTokenNode AwaitToken {
get { return GetChildByRole(AwaitRole); }
} }
public bool IsAsync {
get { return !GetChildByRole(AwaitRole).IsNull; }
set { SetChildByRole(AwaitRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); }
}
public CSharpTokenNode LParToken { public CSharpTokenNode LParToken {
get { return GetChildByRole (Roles.LPar); } get { return GetChildByRole(Roles.LPar); }
} }
/// <summary> /// <summary>
/// Either a VariableDeclarationStatement, or an Expression. /// Either a VariableDeclarationStatement, or an Expression.
/// </summary> /// </summary>
public AstNode ResourceAcquisition { public AstNode ResourceAcquisition {
get { return GetChildByRole (ResourceAcquisitionRole); } get { return GetChildByRole(ResourceAcquisitionRole); }
set { SetChildByRole (ResourceAcquisitionRole, value); } set { SetChildByRole(ResourceAcquisitionRole, value); }
} }
public CSharpTokenNode RParToken { public CSharpTokenNode RParToken {
get { return GetChildByRole (Roles.RPar); } get { return GetChildByRole(Roles.RPar); }
} }
public Statement EmbeddedStatement { public Statement EmbeddedStatement {
get { return GetChildByRole (Roles.EmbeddedStatement); } get { return GetChildByRole(Roles.EmbeddedStatement); }
set { SetChildByRole (Roles.EmbeddedStatement, value); } set { SetChildByRole(Roles.EmbeddedStatement, value); }
} }
public override void AcceptVisitor (IAstVisitor visitor) public override void AcceptVisitor(IAstVisitor visitor)
{ {
visitor.VisitUsingStatement (this); visitor.VisitUsingStatement(this);
} }
public override T AcceptVisitor<T> (IAstVisitor<T> visitor) public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{ {
return visitor.VisitUsingStatement (this); return visitor.VisitUsingStatement(this);
} }
public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data) public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{ {
return visitor.VisitUsingStatement (this, data); return visitor.VisitUsingStatement(this, data);
} }
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{ {
UsingStatement o = other as UsingStatement; UsingStatement o = other as UsingStatement;
return o != null && this.ResourceAcquisition.DoMatch(o.ResourceAcquisition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); return o != null && this.IsAsync == o.IsAsync && this.ResourceAcquisition.DoMatch(o.ResourceAcquisition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match);
} }
} }
} }

17
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -108,13 +108,14 @@ namespace ICSharpCode.Decompiler
if (languageVersion < CSharp.LanguageVersion.CSharp8_0) { if (languageVersion < CSharp.LanguageVersion.CSharp8_0) {
nullableReferenceTypes = false; nullableReferenceTypes = false;
readOnlyMethods = false; readOnlyMethods = false;
asyncUsingAndForEachStatement = false;
asyncEnumerator = false; asyncEnumerator = false;
} }
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator) if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement)
return CSharp.LanguageVersion.CSharp8_0; return CSharp.LanguageVersion.CSharp8_0;
if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers) if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers)
return CSharp.LanguageVersion.CSharp7_3; return CSharp.LanguageVersion.CSharp7_3;
@ -876,6 +877,20 @@ namespace ICSharpCode.Decompiler
} }
} }
bool asyncUsingAndForEachStatement = true;
[Category("C# 8.0 / VS 2019")]
[Description("DecompilerSettings.DetectAsyncUsingAndForeachStatements")]
public bool AsyncUsingAndForEachStatement {
get { return asyncUsingAndForEachStatement; }
set {
if (asyncUsingAndForEachStatement != value) {
asyncUsingAndForEachStatement = value;
OnPropertyChanged();
}
}
}
bool introduceUnmanagedConstraint = true; bool introduceUnmanagedConstraint = true;
/// <summary> /// <summary>

8
ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs

@ -36,10 +36,16 @@ namespace ICSharpCode.Decompiler.IL
/// </remarks> /// </remarks>
partial class UsingInstruction partial class UsingInstruction
{ {
public bool IsAsync { get; set; }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{ {
WriteILRange(output, options); WriteILRange(output, options);
output.Write("using ("); output.Write("using");
if (IsAsync) {
output.Write(".async");
}
output.Write(" (");
Variable.WriteTo(output); Variable.WriteTo(output);
output.Write(" = "); output.Write(" = ");
ResourceExpression.WriteTo(output, options); ResourceExpression.WriteTo(output, options);

107
ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs

@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!context.Settings.UsingStatement) return; if (!context.Settings.UsingStatement) return;
this.context = context; this.context = context;
for (int i = block.Instructions.Count - 1; i >= 0; i--) { for (int i = block.Instructions.Count - 1; i >= 0; i--) {
if (!TransformUsing(block, i) && !TransformUsingVB(block, i)) if (!TransformUsing(block, i) && !TransformUsingVB(block, i) && !TransformAsyncUsing(block, i))
continue; continue;
// This happens in some cases: // This happens in some cases:
// Use correct index after transformation. // Use correct index after transformation.
@ -167,7 +167,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
bool MatchDisposeBlock(BlockContainer container, ILVariable objVar, bool usingNull) bool MatchDisposeBlock(BlockContainer container, ILVariable objVar, bool usingNull, in string disposeMethodFullName = "System.IDisposable.Dispose", KnownTypeCode disposeTypeCode = KnownTypeCode.IDisposable)
{ {
var entryPoint = container.EntryPoint; var entryPoint = container.EntryPoint;
if (entryPoint.Instructions.Count < 2 || entryPoint.Instructions.Count > 3 || entryPoint.IncomingEdgeCount != 1) if (entryPoint.Instructions.Count < 2 || entryPoint.Instructions.Count > 3 || entryPoint.IncomingEdgeCount != 1)
@ -180,17 +180,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (castIndex > -1) { if (castIndex > -1) {
if (!entryPoint.Instructions[castIndex].MatchStLoc(out var tempVar, out var isinst)) if (!entryPoint.Instructions[castIndex].MatchStLoc(out var tempVar, out var isinst))
return false; return false;
if (!isinst.MatchIsInst(out var load, out var disposableType) || !load.MatchLdLoc(objVar) || !disposableType.IsKnownType(KnownTypeCode.IDisposable)) if (!isinst.MatchIsInst(out var load, out var disposableType) || !load.MatchLdLoc(objVar) || !disposableType.IsKnownType(disposeTypeCode))
return false; return false;
if (!tempVar.IsSingleDefinition) if (!tempVar.IsSingleDefinition)
return false; return false;
isReference = true; isReference = true;
if (!MatchDisposeCheck(tempVar, checkInst, isReference, usingNull, out int numObjVarLoadsInCheck)) if (!MatchDisposeCheck(tempVar, checkInst, isReference, usingNull, out int numObjVarLoadsInCheck, disposeMethodFullName, disposeTypeCode))
return false; return false;
if (tempVar.LoadCount != numObjVarLoadsInCheck) if (tempVar.LoadCount != numObjVarLoadsInCheck)
return false; return false;
} else { } else {
if (!MatchDisposeCheck(objVar, checkInst, isReference, usingNull, out _)) if (!MatchDisposeCheck(objVar, checkInst, isReference, usingNull, out _, disposeMethodFullName, disposeTypeCode))
return false; return false;
} }
if (!entryPoint.Instructions[leaveIndex].MatchLeave(container, out var returnValue) || !returnValue.MatchNop()) if (!entryPoint.Instructions[leaveIndex].MatchLeave(container, out var returnValue) || !returnValue.MatchNop())
@ -198,9 +198,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
bool MatchDisposeCheck(ILVariable objVar, ILInstruction checkInst, bool isReference, bool usingNull, out int numObjVarLoadsInCheck) bool MatchDisposeCheck(ILVariable objVar, ILInstruction checkInst, bool isReference, bool usingNull, out int numObjVarLoadsInCheck, in string disposeMethodFullName, KnownTypeCode disposeTypeCode)
{ {
numObjVarLoadsInCheck = 2; numObjVarLoadsInCheck = 2;
ILInstruction disposeInvocation;
CallVirt callVirt; CallVirt callVirt;
if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT)) { if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT)) {
if (checkInst.MatchIfInstruction(out var condition, out var disposeInst)) { if (checkInst.MatchIfInstruction(out var condition, out var disposeInst)) {
@ -208,22 +209,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1) if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1)
return false; return false;
callVirt = disposeBlock.Instructions[0] as CallVirt; disposeInvocation = disposeBlock.Instructions[0];
} else if (checkInst.MatchNullableRewrap(out disposeInst)) { } else if (checkInst.MatchNullableRewrap(out disposeInst)) {
callVirt = disposeInst as CallVirt; disposeInvocation = disposeInst;
} else { } else {
return false; return false;
} }
if (disposeTypeCode == KnownTypeCode.IAsyncDisposable) {
if (!UnwrapAwait(ref disposeInvocation))
return false;
}
callVirt = disposeInvocation as CallVirt;
if (callVirt == null) if (callVirt == null)
return false; return false;
if (callVirt.Method.FullName != "System.IDisposable.Dispose") if (callVirt.Method.FullName != disposeMethodFullName)
return false; return false;
if (callVirt.Method.Parameters.Count > 0) if (callVirt.Method.Parameters.Count > 0)
return false; return false;
if (callVirt.Arguments.Count != 1) if (callVirt.Arguments.Count != 1)
return false; return false;
var firstArg = callVirt.Arguments.FirstOrDefault(); var firstArg = callVirt.Arguments.FirstOrDefault();
if (!(firstArg.MatchUnboxAny(out var innerArg1, out var unboxType) && unboxType.IsKnownType(KnownTypeCode.IDisposable))) { if (!(firstArg.MatchUnboxAny(out var innerArg1, out var unboxType) && unboxType.IsKnownType(disposeTypeCode))) {
if (!firstArg.MatchAddressOf(out var innerArg2, out _)) if (!firstArg.MatchAddressOf(out var innerArg2, out _))
return false; return false;
return NullableLiftingTransform.MatchGetValueOrDefault(innerArg2, objVar) return NullableLiftingTransform.MatchGetValueOrDefault(innerArg2, objVar)
@ -255,7 +261,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1) if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1)
return false; return false;
if (!(disposeBlock.Instructions[0] is CallVirt cv)) disposeInvocation = disposeBlock.Instructions[0];
if (disposeTypeCode == KnownTypeCode.IAsyncDisposable) {
if (!UnwrapAwait(ref disposeInvocation))
return false;
}
if (!(disposeInvocation is CallVirt cv))
return false; return false;
target = cv.Arguments.FirstOrDefault(); target = cv.Arguments.FirstOrDefault();
if (target == null) if (target == null)
@ -264,6 +275,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
target = newTarget; target = newTarget;
callVirt = cv; callVirt = cv;
} else { } else {
if (disposeTypeCode == KnownTypeCode.IAsyncDisposable) {
if (!UnwrapAwait(ref checkInst))
return false;
}
if (!(checkInst is CallVirt cv)) if (!(checkInst is CallVirt cv))
return false; return false;
target = cv.Arguments.FirstOrDefault(); target = cv.Arguments.FirstOrDefault();
@ -275,7 +290,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
callVirt = cv; callVirt = cv;
} }
if (callVirt.Method.FullName != "System.IDisposable.Dispose") if (callVirt.Method.FullName != disposeMethodFullName)
return false; return false;
if (callVirt.Method.Parameters.Count > 0) if (callVirt.Method.Parameters.Count > 0)
return false; return false;
@ -286,8 +301,74 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|| (usingNull && callVirt.Arguments[0].MatchLdNull()) || (usingNull && callVirt.Arguments[0].MatchLdNull())
|| (isReference && checkInst is NullableRewrap || (isReference && checkInst is NullableRewrap
&& target.MatchIsInst(out var arg, out var type2) && target.MatchIsInst(out var arg, out var type2)
&& arg.MatchLdLoc(objVar) && type2.IsKnownType(KnownTypeCode.IDisposable)); && arg.MatchLdLoc(objVar) && type2.IsKnownType(disposeTypeCode));
} }
} }
/// <summary>
/// stloc test(resourceExpression)
/// .try BlockContainer {
/// Block IL_002b (incoming: 1) {
/// call Use(ldloc test)
/// leave IL_002b (nop)
/// }
///
/// } finally BlockContainer {
/// Block IL_0045 (incoming: 1) {
/// if (comp.o(ldloc test == ldnull)) leave IL_0045 (nop)
/// br IL_00ae
/// }
///
/// Block IL_00ae (incoming: 1) {
/// await(addressof System.Threading.Tasks.ValueTask(callvirt DisposeAsync(ldloc test)))
/// leave IL_0045 (nop)
/// }
///
/// }
/// </summary>
private bool TransformAsyncUsing(Block block, int i)
{
if (i < 1 || !context.Settings.AsyncUsingAndForEachStatement) return false;
if (!(block.Instructions[i] is TryFinally tryFinally) || !(block.Instructions[i - 1] is StLoc storeInst))
return false;
if (!CheckAsyncResourceType(storeInst.Variable.Type))
return false;
if (storeInst.Variable.Kind != VariableKind.Local)
return false;
if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally)))
return false;
if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la))))
return false;
if (storeInst.Variable.StoreInstructions.Count > 1)
return false;
if (!(tryFinally.FinallyBlock is BlockContainer container) || !MatchDisposeBlock(container, storeInst.Variable, usingNull: false, "System.IAsyncDisposable.DisposeAsync", KnownTypeCode.IAsyncDisposable))
return false;
context.Step("AsyncUsingTransform", tryFinally);
storeInst.Variable.Kind = VariableKind.UsingLocal;
block.Instructions.RemoveAt(i);
block.Instructions[i - 1] = new UsingInstruction(storeInst.Variable, storeInst.Value, tryFinally.TryBlock) { IsAsync = true }
.WithILRange(storeInst);
return true;
}
bool CheckAsyncResourceType(IType type)
{
if (NullableType.GetUnderlyingType(type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IAsyncDisposable)))
return true;
return false;
}
bool UnwrapAwait(ref ILInstruction awaitInstruction)
{
if (awaitInstruction == null)
return false;
if (!awaitInstruction.MatchAwait(out var arg))
return false;
if (!arg.MatchAddressOf(out awaitInstruction, out var type))
return false;
if (!type.IsKnownType(KnownTypeCode.ValueTask))
return false;
return true;
}
} }
} }

13
ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs

@ -115,10 +115,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
Task, Task,
/// <summary><c>System.Threading.Tasks.Task{T}</c></summary> /// <summary><c>System.Threading.Tasks.Task{T}</c></summary>
TaskOfT, TaskOfT,
/// <summary><c>System.Threading.Tasks.ValueTask</c></summary>
ValueTask,
/// <summary><c>System.Threading.Tasks.ValueTask{T}</c></summary>
ValueTaskOfT,
/// <summary><c>System.Nullable{T}</c></summary> /// <summary><c>System.Nullable{T}</c></summary>
NullableOfT, NullableOfT,
/// <summary><c>System.IDisposable</c></summary> /// <summary><c>System.IDisposable</c></summary>
IDisposable, IDisposable,
/// <summary><c>System.IAsyncDisposable</c></summary>
IAsyncDisposable,
/// <summary><c>System.Runtime.CompilerServices.INotifyCompletion</c></summary> /// <summary><c>System.Runtime.CompilerServices.INotifyCompletion</c></summary>
INotifyCompletion, INotifyCompletion,
/// <summary><c>System.Runtime.CompilerServices.ICriticalNotifyCompletion</c></summary> /// <summary><c>System.Runtime.CompilerServices.ICriticalNotifyCompletion</c></summary>
@ -193,10 +199,13 @@ namespace ICSharpCode.Decompiler.TypeSystem
new KnownTypeReference(KnownTypeCode.IReadOnlyCollectionOfT, TypeKind.Interface, "System.Collections.Generic", "IReadOnlyCollection", 1), new KnownTypeReference(KnownTypeCode.IReadOnlyCollectionOfT, TypeKind.Interface, "System.Collections.Generic", "IReadOnlyCollection", 1),
new KnownTypeReference(KnownTypeCode.IReadOnlyListOfT, TypeKind.Interface, "System.Collections.Generic", "IReadOnlyList", 1), new KnownTypeReference(KnownTypeCode.IReadOnlyListOfT, TypeKind.Interface, "System.Collections.Generic", "IReadOnlyList", 1),
new KnownTypeReference(KnownTypeCode.Task, TypeKind.Class, "System.Threading.Tasks", "Task"), new KnownTypeReference(KnownTypeCode.Task, TypeKind.Class, "System.Threading.Tasks", "Task"),
new KnownTypeReference(KnownTypeCode.TaskOfT, TypeKind.Class, "System.Threading.Tasks", "Task", 1, baseType: KnownTypeCode.Task), new KnownTypeReference(KnownTypeCode.TaskOfT, TypeKind.Class, "System.Threading.Tasks", "Task", 1, baseType: KnownTypeCode.Task),
new KnownTypeReference(KnownTypeCode.ValueTask, TypeKind.Struct, "System.Threading.Tasks", "ValueTask"),
new KnownTypeReference(KnownTypeCode.ValueTaskOfT, TypeKind.Struct, "System.Threading.Tasks", "ValueTask", 1),
new KnownTypeReference(KnownTypeCode.NullableOfT, TypeKind.Struct, "System", "Nullable", 1), new KnownTypeReference(KnownTypeCode.NullableOfT, TypeKind.Struct, "System", "Nullable", 1),
new KnownTypeReference(KnownTypeCode.IDisposable, TypeKind.Interface, "System", "IDisposable"), new KnownTypeReference(KnownTypeCode.IDisposable, TypeKind.Interface, "System", "IDisposable"),
new KnownTypeReference(KnownTypeCode.IAsyncDisposable, TypeKind.Interface, "System", "IAsyncDisposable"),
new KnownTypeReference(KnownTypeCode.INotifyCompletion, TypeKind.Interface, "System.Runtime.CompilerServices", "INotifyCompletion"), new KnownTypeReference(KnownTypeCode.INotifyCompletion, TypeKind.Interface, "System.Runtime.CompilerServices", "INotifyCompletion"),
new KnownTypeReference(KnownTypeCode.ICriticalNotifyCompletion, TypeKind.Interface, "System.Runtime.CompilerServices", "ICriticalNotifyCompletion"), new KnownTypeReference(KnownTypeCode.ICriticalNotifyCompletion, TypeKind.Interface, "System.Runtime.CompilerServices", "ICriticalNotifyCompletion"),

9
ILSpy/Properties/Resources.Designer.cs generated

@ -673,6 +673,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Detect awaited using and foreach statements.
/// </summary>
public static string DecompilerSettings_DetectAsyncUsingAndForeachStatements {
get {
return ResourceManager.GetString("DecompilerSettings.DetectAsyncUsingAndForeachStatements", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Detect foreach statements. /// Looks up a localized string similar to Detect foreach statements.
/// </summary> /// </summary>

6
ILSpy/Properties/Resources.resx

@ -447,9 +447,6 @@
<data name="Version" xml:space="preserve"> <data name="Version" xml:space="preserve">
<value>Version</value> <value>Version</value>
</data> </data>
<data name="Culture" xml:space="preserve">
<value>Culture</value>
</data>
<data name="PublicToken" xml:space="preserve"> <data name="PublicToken" xml:space="preserve">
<value>Public Key Token</value> <value>Public Key Token</value>
</data> </data>
@ -775,4 +772,7 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.ReadOnlyMethods" xml:space="preserve"> <data name="DecompilerSettings.ReadOnlyMethods" xml:space="preserve">
<value>Read-only methods</value> <value>Read-only methods</value>
</data> </data>
<data name="DecompilerSettings.DetectAsyncUsingAndForeachStatements" xml:space="preserve">
<value>Detect awaited using and foreach statements</value>
</data>
</root> </root>
Loading…
Cancel
Save