diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index 0f100b873..ae7fa8328 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -33,11 +33,11 @@
- TRACE;DEBUG;NET46;ROSLYN;CS60;CS70;CS71;CS72;CS73
+ TRACE;DEBUG;ROSLYN;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100
- TRACE;NET46;ROSLYN;CS60;CS70;CS71;CS72;CS73
+ TRACE;ROSLYN;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
index ac50c619a..9b5123e8d 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
@@ -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
return (int _, int _, int _) => 0;
}
#endif
+
+#if CS100
+ public static Func LambdaWithAttribute0()
+ {
+ return [My] () => 0;
+ }
+
+ public static Func LambdaWithAttribute1()
+ {
+ return [My] (int x) => 0;
+ }
+
+ public static Func LambdaWithAttributeOnParam()
+ {
+ return ([My] int x) => 0;
+ }
+
+ public static Func> AsyncLambdaWithAttribute0()
+ {
+ return [My] async () => 0;
+ }
+ public static Action StatementLambdaWithAttribute0()
+ {
+ return [My] () => {
+ };
+ }
+
+ public static Action StatementLambdaWithAttribute1()
+ {
+ return [return: My] (int x) => {
+ Console.WriteLine(x);
+ };
+ }
+ public static Action StatementLambdaWithAttribute2()
+ {
+ return ([My] int x) => {
+ Console.WriteLine(x);
+ };
+ }
+#endif
}
public class Issue1867
@@ -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
+ {
+ }
}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs
index d80623929..7c070ed58 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs
@@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
}
-
+
void DefaultMethod()
{
Method();
diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
index 977536856..c354df411 100644
--- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
@@ -2302,6 +2302,24 @@ namespace ICSharpCode.Decompiler.CSharp
{
body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment);
}
+ var attributeSections = new List();
+ foreach (var attr in method?.GetAttributes() ?? Enumerable.Empty())
+ {
+ 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())
+ {
+ 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
// 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
if (isLambda)
{
LambdaExpression lambda = new LambdaExpression();
+ lambda.Attributes.AddRange(attributeSections);
lambda.IsAsync = ame.IsAsync;
lambda.CopyAnnotationsFrom(ame);
ame.Parameters.MoveTo(lambda.Parameters);
diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
index a38e56842..4b7bcd93f 100644
--- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
+++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
@@ -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
break;
case TypeParameterDeclaration _:
case ComposedType _:
+ case LambdaExpression _:
Space();
break;
default:
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs
index c3829d882..8782aa1d8 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs
@@ -31,11 +31,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
///
public class LambdaExpression : Expression
{
+ public static readonly Role AttributeRole = new Role("Attribute", null);
public readonly static TokenRole AsyncModifierRole = new TokenRole("async");
public static readonly Role BodyRole = new Role("Body", AstNode.Null);
bool isAsync;
+ public AstNodeCollection Attributes {
+ get { return base.GetChildrenByRole(AttributeRole); }
+ }
+
public bool IsAsync {
get { return isAsync; }
set { ThrowIfFrozen(); isAsync = value; }