Skip to content

Commit

Permalink
Output attributes on lambda expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
dgrunwald committed Jul 16, 2022
1 parent 1d22470 commit 21c3ec0
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
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
{
Expand Down Expand Up @@ -527,6 +530,46 @@ public static Func<int, int, int, int> LambdaParameterDiscard()
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
Expand All @@ -551,4 +594,9 @@ public Func<bool> TestLambda(Issue1867 x)
return () => m1.value + 1 == 4 && m2.value > 5;
}
}

[AttributeUsage(AttributeTargets.All)]
internal class MyAttribute : Attribute
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ static IA()
{

}

void DefaultMethod()
{
Method();
Expand Down
24 changes: 24 additions & 0 deletions ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2302,13 +2302,36 @@ internal ExpressionWithResolveResult TranslateFunction(IType delegateType, ILFun
{
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))
{
// 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
Expand All @@ -2331,6 +2354,7 @@ from ident in body.Descendants.OfType<IdentifierExpression>()
if (isLambda)
{
LambdaExpression lambda = new LambdaExpression();
lambda.Attributes.AddRange(attributeSections);
lambda.IsAsync = ame.IsAsync;
lambda.CopyAnnotationsFrom(ame);
ame.Parameters.MoveTo(lambda.Parameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,7 @@ public virtual void VisitIsExpression(IsExpression isExpression)
public virtual void VisitLambdaExpression(LambdaExpression lambdaExpression)
{
StartNode(lambdaExpression);
WriteAttributes(lambdaExpression.Attributes);
if (lambdaExpression.IsAsync)
{
WriteKeyword(LambdaExpression.AsyncModifierRole);
Expand Down Expand Up @@ -1502,6 +1503,7 @@ public virtual void VisitAttributeSection(AttributeSection attributeSection)
break;
case TypeParameterDeclaration _:
case ComposedType _:
case LambdaExpression _:
Space();
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down

0 comments on commit 21c3ec0

Please sign in to comment.