Browse Source

Output attributes on lambda expressions

pull/2749/head
Daniel Grunwald 3 years ago
parent
commit
21c3ec046f
  1. 4
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 50
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  3. 24
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 2
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  5. 5
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs

4
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -33,11 +33,11 @@ @@ -33,11 +33,11 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;NET46;ROSLYN;CS60;CS70;CS71;CS72;CS73</DefineConstants>
<DefineConstants>TRACE;DEBUG;ROSLYN;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;NET46;ROSLYN;CS60;CS70;CS71;CS72;CS73</DefineConstants>
<DefineConstants>TRACE;ROSLYN;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100</DefineConstants>
</PropertyGroup>
<Import Project="..\packages.props" />

50
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs

@ -20,8 +20,11 @@ using System; @@ -20,8 +20,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
#if CS100
using System.Threading.Tasks;
#endif
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
{
public static class DelegateConstruction
{
@ -527,6 +530,46 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -527,6 +530,46 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return (int _, int _, int _) => 0;
}
#endif
#if CS100
public static Func<int> LambdaWithAttribute0()
{
return [My] () => 0;
}
public static Func<int, int> LambdaWithAttribute1()
{
return [My] (int x) => 0;
}
public static Func<int, int> LambdaWithAttributeOnParam()
{
return ([My] int x) => 0;
}
public static Func<Task<int>> AsyncLambdaWithAttribute0()
{
return [My] async () => 0;
}
public static Action StatementLambdaWithAttribute0()
{
return [My] () => {
};
}
public static Action<int> StatementLambdaWithAttribute1()
{
return [return: My] (int x) => {
Console.WriteLine(x);
};
}
public static Action<int> StatementLambdaWithAttribute2()
{
return ([My] int x) => {
Console.WriteLine(x);
};
}
#endif
}
public class Issue1867
@ -551,4 +594,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -551,4 +594,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return () => m1.value + 1 == 4 && m2.value > 5;
}
}
[AttributeUsage(AttributeTargets.All)]
internal class MyAttribute : Attribute
{
}
}

24
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -2302,6 +2302,24 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2302,6 +2302,24 @@ namespace ICSharpCode.Decompiler.CSharp
{
body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment);
}
var attributeSections = new List<AttributeSection>();
foreach (var attr in method?.GetAttributes() ?? Enumerable.Empty<IAttribute>())
{
if (attr.AttributeType.IsKnownType(KnownAttribute.CompilerGenerated))
continue;
if (function.IsAsync)
{
if (attr.AttributeType.IsKnownType(KnownAttribute.AsyncStateMachine))
continue;
if (attr.AttributeType.IsKnownType(KnownAttribute.DebuggerStepThrough))
continue;
}
attributeSections.Add(new AttributeSection(astBuilder.ConvertAttribute(attr)));
}
foreach (var attr in method?.GetReturnTypeAttributes() ?? Enumerable.Empty<IAttribute>())
{
attributeSections.Add(new AttributeSection(astBuilder.ConvertAttribute(attr)) { AttributeTarget = "return" });
}
bool isLambda = false;
if (ame.Parameters.Any(p => p.Type.IsNull))
@ -2309,6 +2327,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2309,6 +2327,11 @@ namespace ICSharpCode.Decompiler.CSharp
// if there is an anonymous type involved, we are forced to use a lambda expression.
isLambda = true;
}
else if (attributeSections.Count > 0 || ame.Parameters.Any(p => p.Attributes.Any()))
{
// C# 10 lambdas can have attributes, but anonymous methods cannot
isLambda = true;
}
else if (settings.UseLambdaSyntax && ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None))
{
// otherwise use lambda only if an expression lambda is possible
@ -2331,6 +2354,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2331,6 +2354,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (isLambda)
{
LambdaExpression lambda = new LambdaExpression();
lambda.Attributes.AddRange(attributeSections);
lambda.IsAsync = ame.IsAsync;
lambda.CopyAnnotationsFrom(ame);
ame.Parameters.MoveTo(lambda.Parameters);

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

@ -1007,6 +1007,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1007,6 +1007,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitLambdaExpression(LambdaExpression lambdaExpression)
{
StartNode(lambdaExpression);
WriteAttributes(lambdaExpression.Attributes);
if (lambdaExpression.IsAsync)
{
WriteKeyword(LambdaExpression.AsyncModifierRole);
@ -1502,6 +1503,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1502,6 +1503,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
break;
case TypeParameterDeclaration _:
case ComposedType _:
case LambdaExpression _:
Space();
break;
default:

5
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs

@ -31,11 +31,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -31,11 +31,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary>
public class LambdaExpression : Expression
{
public static readonly Role<AttributeSection> AttributeRole = new Role<AttributeSection>("Attribute", null);
public readonly static TokenRole AsyncModifierRole = new TokenRole("async");
public static readonly Role<AstNode> BodyRole = new Role<AstNode>("Body", AstNode.Null);
bool isAsync;
public AstNodeCollection<AttributeSection> Attributes {
get { return base.GetChildrenByRole(AttributeRole); }
}
public bool IsAsync {
get { return isAsync; }
set { ThrowIfFrozen(); isAsync = value; }

Loading…
Cancel
Save