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. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs
  4. 24
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  5. 2
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  6. 5
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs

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

@ -33,11 +33,11 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <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>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <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> </PropertyGroup>
<Import Project="..\packages.props" /> <Import Project="..\packages.props" />

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

@ -20,8 +20,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; 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 public static class DelegateConstruction
{ {
@ -527,6 +530,46 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return (int _, int _, int _) => 0; return (int _, int _, int _) => 0;
} }
#endif #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 public class Issue1867
@ -551,4 +594,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return () => m1.value + 1 == 4 && m2.value > 5; return () => m1.value + 1 == 4 && m2.value > 5;
} }
} }
[AttributeUsage(AttributeTargets.All)]
internal class MyAttribute : Attribute
{
}
} }

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs

@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
} }
void DefaultMethod() void DefaultMethod()
{ {
Method(); Method();

24
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -2302,6 +2302,24 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment); 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; bool isLambda = false;
if (ame.Parameters.Any(p => p.Type.IsNull)) if (ame.Parameters.Any(p => p.Type.IsNull))
@ -2309,6 +2327,11 @@ namespace ICSharpCode.Decompiler.CSharp
// if there is an anonymous type involved, we are forced to use a lambda expression. // if there is an anonymous type involved, we are forced to use a lambda expression.
isLambda = true; 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)) else if (settings.UseLambdaSyntax && ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None))
{ {
// otherwise use lambda only if an expression lambda is possible // otherwise use lambda only if an expression lambda is possible
@ -2331,6 +2354,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (isLambda) if (isLambda)
{ {
LambdaExpression lambda = new LambdaExpression(); LambdaExpression lambda = new LambdaExpression();
lambda.Attributes.AddRange(attributeSections);
lambda.IsAsync = ame.IsAsync; lambda.IsAsync = ame.IsAsync;
lambda.CopyAnnotationsFrom(ame); lambda.CopyAnnotationsFrom(ame);
ame.Parameters.MoveTo(lambda.Parameters); ame.Parameters.MoveTo(lambda.Parameters);

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

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

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

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

Loading…
Cancel
Save