diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index 1dc72a59fe..f3c9536d09 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -257,6 +257,9 @@ public static List GetPreprocessorSymbols(CompilerOptions flags) preprocessorSymbols.Add("LEGACY_CSC"); preprocessorSymbols.Add("LEGACY_VBC"); } + if (flags.HasFlag(CompilerOptions.Preview)) { + preprocessorSymbols.Add("CS90"); + } return preprocessorSymbols; } diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index c5687aa856..b657ad8395 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -199,7 +199,7 @@ public void Loops([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions c [Test] public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { - RunForLibrary(cscOptions: cscOptions); + RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview); } [Test] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index 1f73795e2d..c38a81c177 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -23,6 +23,12 @@ namespace LocalFunctions { internal class LocalFunctions { + [AttributeUsage(AttributeTargets.All)] + internal class MyAttribute : Attribute + { + + } + public class Generic where T1 : struct, ICloneable, IConvertible { public int MixedLocalFunction() where T2 : ICloneable, IConvertible @@ -31,15 +37,21 @@ public int MixedLocalFunction() where T2 : ICloneable, IConvertible object z = this; for (int j = 0; j < 10; j++) { int i = 0; - i += NonStaticMethod6(); - int NonStaticMethod6() + i += NonStaticMethod6(0); +#if CS90 + [My] + [return: My] + int NonStaticMethod6<[My] T3>([My] int unused) +#else + int NonStaticMethod6(int unused) +#endif { t2 = default(T2); int l = 0; return NonStaticMethod6_1() + NonStaticMethod6_1() + z.GetHashCode(); int NonStaticMethod6_1() { - return i + l + NonStaticMethod6() + StaticMethod1(); + return i + l + NonStaticMethod6(0) + StaticMethod1(); } } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 70358a8bc5..0c18b39a3e 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1345,30 +1345,35 @@ void DecompileBody(IMethod method, EntityDeclaration entityDecl, DecompileRun de } entityDecl.AddAnnotation(function); - if (function.IsIterator) { - if (localSettings.DecompileMemberBodies && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) { - body.Add(new YieldBreakStatement()); - } - if (function.IsAsync) { - RemoveAttribute(entityDecl, KnownAttribute.AsyncIteratorStateMachine); - } else { - RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine); - } - if (function.StateMachineCompiledWithMono) { - RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden); - } + CleanUpMethodDeclaration(entityDecl, body, function, localSettings.DecompileMemberBodies); + } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) { + throw new DecompilerException(module, method, innerException); + } + } + + internal static void CleanUpMethodDeclaration(EntityDeclaration entityDecl, BlockStatement body, ILFunction function, bool decompileBody = true) + { + if (function.IsIterator) { + if (decompileBody && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) { + body.Add(new YieldBreakStatement()); } if (function.IsAsync) { - entityDecl.Modifiers |= Modifiers.Async; - RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine); - RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough); + RemoveAttribute(entityDecl, KnownAttribute.AsyncIteratorStateMachine); + } else { + RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine); } - } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) { - throw new DecompilerException(module, method, innerException); + if (function.StateMachineCompiledWithMono) { + RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden); + } + } + if (function.IsAsync) { + entityDecl.Modifiers |= Modifiers.Async; + RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine); + RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough); } } - bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType) + internal static bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType) { bool found = false; foreach (var section in entityDecl.Attributes) { diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 9eeec424cb..d1d633a0ce 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -197,8 +197,7 @@ public ExpressionWithResolveResult Build(OpCode callOpCode, IMethod method, } else if (localFunction != null) { var ide = new IdentifierExpression(localFunction.Name); if (method.TypeArguments.Count > 0) { - int skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters; - ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType)); + ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); } ide.AddAnnotation(localFunction); target = ide.WithoutILInstruction() @@ -1327,7 +1326,6 @@ ExpressionWithResolveResult BuildDelegateReference(IMethod method, IMethod invok target = default; targetType = default; methodName = localFunction.Name; - // TODO : think about how to handle generic local functions } else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) { step = 5; targetType = method.Parameters[0].Type; @@ -1406,11 +1404,7 @@ ExpressionWithResolveResult BuildDelegateReference(IMethod method, IMethod invok } else { var ide = new IdentifierExpression(methodName); if ((step & 2) != 0) { - int skipCount = 0; - if (localFunction != null && method.TypeArguments.Count > 0) { - skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters; - } - ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType)); + ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); } targetExpression = ide.WithRR(result); } @@ -1469,7 +1463,7 @@ static MethodGroupResolveResult ToMethodGroup(IMethod method, ILFunction localFu method.DeclaringType, new IParameterizedMember[] { method } ) - }, method.TypeArguments.Skip(localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters).ToArray() + }, method.TypeArguments ); } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c0c583b001..140329dcde 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -92,6 +92,7 @@ public ExpressionBuilder(StatementBuilder statementBuilder, IDecompilerTypeSyste this.astBuilder = new TypeSystemAstBuilder(resolver); this.astBuilder.AlwaysUseShortTypeNames = true; this.astBuilder.AddResolveResultAnnotations = true; + this.astBuilder.ShowAttributes = true; this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables; this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved }; } @@ -1983,7 +1984,7 @@ IType GetTaskType(IType resultType) return SpecialType.UnknownType; } - internal IEnumerable MakeParameters(IReadOnlyList parameters, ILFunction function) + IEnumerable MakeParameters(IReadOnlyList parameters, ILFunction function) { var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); int i = 0; @@ -1992,9 +1993,6 @@ internal IEnumerable MakeParameters(IReadOnlyList 0) { - var astBuilder = exprBuilder.astBuilder; - if (astBuilder.ShowTypeParameters) { - int skipCount = function.ReducedMethod.NumberOfCompilerGeneratedTypeParameters; - stmt.TypeParameters.AddRange(function.Method.TypeParameters.Skip(skipCount).Select(t => astBuilder.ConvertTypeParameter(t))); - if (astBuilder.ShowTypeParameterConstraints) { - stmt.Constraints.AddRange(function.Method.TypeParameters.Skip(skipCount).Select(t => astBuilder.ConvertTypeParameterConstraint(t)).Where(c => c != null)); - } - } - } - if (function.IsAsync) { - stmt.Modifiers |= Modifiers.Async; - } - if (settings.StaticLocalFunctions && function.ReducedMethod.IsStaticLocalFunction) { - stmt.Modifiers |= Modifiers.Static; - } + CSharpDecompiler.CleanUpMethodDeclaration(method, method.Body, function); + CSharpDecompiler.RemoveAttribute(method, KnownAttribute.CompilerGenerated); + var stmt = new LocalFunctionDeclarationStatement(method); stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod)); stmt.WithILInstruction(function); return stmt; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs index 47ff1641c1..105789bb3a 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs @@ -16,69 +16,22 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using System.Collections.Generic; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax { public class LocalFunctionDeclarationStatement : Statement { - public AstNodeCollection TypeParameters { - get { return GetChildrenByRole(Roles.TypeParameter); } - } - - public CSharpTokenNode LParToken { - get { return GetChildByRole(Roles.LPar); } - } - - public AstNodeCollection Parameters { - get { return GetChildrenByRole(Roles.Parameter); } - } + public static readonly Role MethodDeclarationRole = new Role("Method"); - public CSharpTokenNode RParToken { - get { return GetChildByRole(Roles.RPar); } + public MethodDeclaration Declaration { + get { return GetChildByRole(MethodDeclarationRole); } + set { SetChildByRole(MethodDeclarationRole, value); } } - public AstNodeCollection Constraints { - get { return GetChildrenByRole(Roles.Constraint); } - } - - public BlockStatement Body { - get { return GetChildByRole(Roles.Body); } - set { SetChildByRole(Roles.Body, value); } - } - - public Modifiers Modifiers { - get { return EntityDeclaration.GetModifiers(this); } - set { EntityDeclaration.SetModifiers(this, value); } - } - - public bool HasModifier(Modifiers mod) + public LocalFunctionDeclarationStatement(MethodDeclaration methodDeclaration) { - return (Modifiers & mod) == mod; - } - - public IEnumerable ModifierTokens { - get { return GetChildrenByRole(EntityDeclaration.ModifierRole); } - } - - public virtual string Name { - get { - return GetChildByRole(Roles.Identifier).Name; - } - set { - SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty)); - } - } - - public virtual Identifier NameToken { - get { return GetChildByRole(Roles.Identifier); } - set { SetChildByRole(Roles.Identifier, value); } - } - - public virtual AstType ReturnType { - get { return GetChildByRole(Roles.Type); } - set { SetChildByRole(Roles.Type, value); } + AddChild(methodDeclaration, MethodDeclarationRole); } public override void AcceptVisitor(IAstVisitor visitor) @@ -98,13 +51,7 @@ public override S AcceptVisitor(IAstVisitor visitor, T data) protected internal override bool DoMatch(AstNode other, Match match) { - LocalFunctionDeclarationStatement o = other as LocalFunctionDeclarationStatement; - return o != null && MatchString(this.Name, o.Name) - && (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers) - && this.ReturnType.DoMatch(o.ReturnType, match) - && this.TypeParameters.DoMatch(o.TypeParameters, match) - && this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match) - && this.Body.DoMatch(o.Body, match); + return other is LocalFunctionDeclarationStatement o && Declaration.DoMatch(o.Declaration, match); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 3fe6a5f026..33464c99ae 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1799,6 +1799,8 @@ bool NeedsAccessibility(IMember member) return !member.IsStatic; case SymbolKind.Destructor: return false; + case SymbolKind.Method: + return !((IMethod)member).IsLocalFunction; default: return true; } @@ -1811,7 +1813,11 @@ Modifiers GetMemberModifiers(IMember member) m |= ModifierFromAccessibility (member.Accessibility); } if (this.ShowModifiers) { - if (member.IsStatic) { + if (member is LocalFunctionMethod localFunction) { + if (localFunction.IsStaticLocalFunction) { + m |= Modifiers.Static; + } + } else if (member.IsStatic) { m |= Modifiers.Static; } else { var declaringType = member.DeclaringType; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs index adf6e3f22c..3f3015a33f 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs @@ -55,56 +55,56 @@ public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration) public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration) { - Debug.Assert(currentMethod == null); + var oldMethod = currentMethod; try { currentMethod = methodDeclaration.GetSymbol() as IMethod; return base.VisitMethodDeclaration(methodDeclaration); } finally { - currentMethod = null; + currentMethod = oldMethod; } } public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { - Debug.Assert(currentMethod == null); + var oldMethod = currentMethod; try { currentMethod = constructorDeclaration.GetSymbol() as IMethod; return base.VisitConstructorDeclaration(constructorDeclaration); } finally { - currentMethod = null; + currentMethod = oldMethod; } } public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { - Debug.Assert(currentMethod == null); + var oldMethod = currentMethod; try { currentMethod = destructorDeclaration.GetSymbol() as IMethod; return base.VisitDestructorDeclaration(destructorDeclaration); } finally { - currentMethod = null; + currentMethod = oldMethod; } } public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { - Debug.Assert(currentMethod == null); + var oldMethod = currentMethod; try { currentMethod = operatorDeclaration.GetSymbol() as IMethod; return base.VisitOperatorDeclaration(operatorDeclaration); } finally { - currentMethod = null; + currentMethod = oldMethod; } } public override TResult VisitAccessor(Accessor accessor) { - Debug.Assert(currentMethod == null); + var oldMethod = currentMethod; try { currentMethod = accessor.GetSymbol() as IMethod; return base.VisitAccessor(accessor); } finally { - currentMethod = null; + currentMethod = oldMethod; } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index d95cc63dae..86541e59a7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -20,6 +20,8 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Reflection.Metadata; + using Humanizer; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.TypeSystem; @@ -51,6 +53,7 @@ public class AssignVariableNames : IILTransform ILTransformContext context; string[] currentFieldNames; Dictionary reservedVariableNames; + Dictionary localFunctionMapping; HashSet loopCounters; const char maxLoopVariableName = 'n'; @@ -59,6 +62,7 @@ public void Run(ILFunction function, ILTransformContext context) this.context = context; currentFieldNames = function.Method.DeclaringTypeDefinition.Fields.Select(f => f.Name).ToArray(); reservedVariableNames = new Dictionary(); + localFunctionMapping = new Dictionary(); loopCounters = CollectLoopCounters(function); foreach (var f in function.Descendants.OfType()) { if (f.Method != null) { @@ -180,6 +184,7 @@ void AssignName() if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName)) newName = null; localFunction.Name = newName; + localFunction.ReducedMethod.Name = newName; } // Now generate names: var mapping = new Dictionary(ILVariableEqualityComparer.Instance); @@ -199,6 +204,25 @@ void AssignName() newName = GetAlternativeName("f"); } localFunction.Name = newName; + localFunction.ReducedMethod.Name = newName; + localFunctionMapping[(MethodDefinitionHandle)localFunction.ReducedMethod.MetadataToken] = newName; + } + foreach (var inst in function.Descendants) { + LocalFunctionMethod localFunction; + switch (inst) { + case Call call: + localFunction = call.Method as LocalFunctionMethod; + break; + case LdFtn ldftn: + localFunction = ldftn.Method as LocalFunctionMethod; + break; + default: + localFunction = null; + break; + } + if (localFunction == null || !localFunctionMapping.TryGetValue((MethodDefinitionHandle)localFunction.MetadataToken, out var name)) + continue; + localFunction.Name = name; } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index de7e36c489..0b9ae651ec 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -484,7 +484,7 @@ LocalFunctionMethod ReduceToLocalFunction(IMethod method, int typeParametersToRe break; parametersToRemove++; } - return new LocalFunctionMethod(method, parametersToRemove, typeParametersToRemove); + return new LocalFunctionMethod(method, method.Name, parametersToRemove, typeParametersToRemove); } static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite) diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index 1ca9a792af..82730d64fb 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; + using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Resolver; @@ -26,6 +27,7 @@ using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler { @@ -39,7 +41,7 @@ public class TextTokenWriter : TokenWriter bool inDocumentationComment = false; bool firstUsingDeclaration; bool lastUsingDeclaration; - + public TextTokenWriter(ITextOutput output, DecompilerSettings settings, IDecompilerTypeSystem typeSystem) { if (output == null) @@ -52,13 +54,13 @@ public TextTokenWriter(ITextOutput output, DecompilerSettings settings, IDecompi this.settings = settings; this.typeSystem = typeSystem; } - + public override void WriteIdentifier(Identifier identifier) { if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { output.Write('@'); } - + var definition = GetCurrentDefinition(); string name = TextWriterTokenWriter.EscapeIdentifier(identifier.Name); switch (definition) { @@ -69,7 +71,7 @@ public override void WriteIdentifier(Identifier identifier) output.WriteReference(m, name, true); return; } - + var member = GetCurrentMemberReference(); switch (member) { case IType t: @@ -110,6 +112,7 @@ ISymbol GetCurrentMemberReference() if (symbol != null && node.Role == Roles.Type && node.Parent is ObjectCreateExpression) { symbol = node.Parent.GetSymbol(); } + if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) { var declaringType = member.DeclaringType; if (declaringType != null && declaringType.Kind == TypeKind.Delegate) @@ -123,10 +126,8 @@ ISymbol FilterMember(ISymbol symbol) if (symbol == null) return null; - //if (settings.AutomaticEvents && member is FieldDefinition) { - // var field = (FieldDefinition)member; - // return field.DeclaringType.Events.FirstOrDefault(ev => ev.Name == field.Name) ?? member; - //} + if (symbol is LocalFunctionMethod) + return null; return symbol; } @@ -142,14 +143,18 @@ object GetCurrentLocalReference() if (letClauseVariable != null) return letClauseVariable; - var gotoStatement = node as GotoStatement; - if (gotoStatement != null) - { + if (node is GotoStatement gotoStatement) { var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null); if (method != null) return method + gotoStatement.Label; } + if (node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) { + var symbol = node.Parent.GetSymbol(); + if (symbol is LocalFunctionMethod) + return symbol; + } + return null; } @@ -177,29 +182,29 @@ object GetCurrentLocalDefinition() return method + label.Label; } - if (node is LocalFunctionDeclarationStatement) { - var localFunction = node.GetResolveResult() as MemberResolveResult; + if (node is MethodDeclaration && node.Parent is LocalFunctionDeclarationStatement) { + var localFunction = node.Parent.GetResolveResult() as MemberResolveResult; if (localFunction != null) return localFunction.Member; } return null; } - + ISymbol GetCurrentDefinition() { if (nodeStack == null || nodeStack.Count == 0) return null; - + var node = nodeStack.Peek(); if (node is Identifier) node = node.Parent; if (IsDefinition(ref node)) return node.GetSymbol(); - + return null; } - + public override void WriteKeyword(Role role, string keyword) { //To make reference for 'this' and 'base' keywords in the ClassName():this() expression @@ -211,7 +216,7 @@ public override void WriteKeyword(Role role, string keyword) } output.Write(keyword); } - + public override void WriteToken(Role role, string token) { switch (token) { @@ -253,22 +258,22 @@ public override void WriteToken(Role role, string token) break; } } - + public override void Space() { output.Write(' '); } - + public override void Indent() { output.Indent(); } - + public override void Unindent() { output.Unindent(); } - + public override void NewLine() { if (!firstUsingDeclaration && lastUsingDeclaration) { @@ -277,7 +282,7 @@ public override void NewLine() } output.WriteLine(); } - + public override void WriteComment(CommentType commentType, string content) { switch (commentType) { @@ -309,7 +314,7 @@ public override void WriteComment(CommentType commentType, string content) break; } } - + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) { // pre-processor directive must start on its own line @@ -321,7 +326,7 @@ public override void WritePreProcessorDirective(PreProcessorDirectiveType type, } output.WriteLine(); } - + public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { new TextWriterTokenWriter(new TextOutputWriter(output)).WritePrimitiveValue(value, format); @@ -376,7 +381,7 @@ public override void WritePrimitiveType(string type) break; } } - + public override void StartNode(AstNode node) { if (nodeStack.Count == 0) { @@ -390,7 +395,7 @@ public override void StartNode(AstNode node) } nodeStack.Push(node); } - + private bool IsUsingDeclaration(AstNode node) { return node is UsingDeclaration || node is UsingAliasDeclaration; @@ -401,10 +406,10 @@ public override void EndNode(AstNode node) if (nodeStack.Pop() != node) throw new InvalidOperationException(); } - + public static bool IsDefinition(ref AstNode node) { - if (node is EntityDeclaration) + if (node is EntityDeclaration && !(node.Parent is LocalFunctionDeclarationStatement)) return true; if (node is VariableInitializer && node.Parent is FieldDeclaration) { node = node.Parent; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs index 242b6b1939..57cce32dd0 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs @@ -31,11 +31,12 @@ class LocalFunctionMethod : IMethod { readonly IMethod baseMethod; - public LocalFunctionMethod(IMethod baseMethod, int numberOfCompilerGeneratedParameters, int numberOfCompilerGeneratedTypeParameters) + public LocalFunctionMethod(IMethod baseMethod, string name, int numberOfCompilerGeneratedParameters, int numberOfCompilerGeneratedTypeParameters) { if (baseMethod == null) throw new ArgumentNullException(nameof(baseMethod)); this.baseMethod = baseMethod; + this.Name = name; this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters; this.NumberOfCompilerGeneratedTypeParameters = numberOfCompilerGeneratedTypeParameters; } @@ -65,7 +66,7 @@ public override int GetHashCode() public override string ToString() { - return string.Format("[LocalFunctionMethod: ReducedFrom={0}, NumberOfGeneratedParameters={1}, NumberOfCompilerGeneratedTypeParameters={2}]", ReducedFrom, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters); + return string.Format("[LocalFunctionMethod: ReducedFrom={0}, Name={1}, NumberOfGeneratedParameters={2}, NumberOfCompilerGeneratedTypeParameters={3}]", ReducedFrom, Name, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters); } internal int NumberOfCompilerGeneratedParameters { get; } @@ -88,7 +89,7 @@ public IMethod Specialize(TypeParameterSubstitution substitution) { return new LocalFunctionMethod( baseMethod.Specialize(substitution), - NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters); + Name, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters); } IMember IMember.Specialize(TypeParameterSubstitution substitution) @@ -96,7 +97,6 @@ IMember IMember.Specialize(TypeParameterSubstitution substitution) return Specialize(substitution); } - public IReadOnlyList TypeParameters => baseMethod.TypeParameters; public bool IsExtensionMethod => baseMethod.IsExtensionMethod; public bool IsLocalFunction => true; public bool IsConstructor => baseMethod.IsConstructor; @@ -107,7 +107,24 @@ IMember IMember.Specialize(TypeParameterSubstitution substitution) public IMember AccessorOwner => baseMethod.AccessorOwner; public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind; public IMethod ReducedFrom => baseMethod; - public IReadOnlyList TypeArguments => baseMethod.TypeArguments; + + List typeParameters; + public IReadOnlyList TypeParameters { + get { + if (typeParameters == null) + typeParameters = new List(baseMethod.TypeParameters.Skip(NumberOfCompilerGeneratedTypeParameters)); + return typeParameters; + } + } + + List typeArguments; + public IReadOnlyList TypeArguments { + get { + if (typeArguments == null) + typeArguments = new List(baseMethod.TypeArguments.Skip(NumberOfCompilerGeneratedTypeParameters)); + return typeArguments; + } + } List parameters; public IReadOnlyList Parameters { @@ -137,8 +154,8 @@ public IReadOnlyList Parameters { public Accessibility Accessibility => baseMethod.Accessibility; - public string FullName => baseMethod.FullName; - public string Name => baseMethod.Name; + public string FullName => Name; + public string Name { get; set; } public string ReflectionName => baseMethod.ReflectionName; public string Namespace => baseMethod.Namespace; diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 8b02530266..96c22ce916 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -648,6 +648,9 @@ public override RichText GetRichTextTooltip(IEntity entity) if (!settings.LiftNullables) { flags &= ~ConversionFlags.UseNullableSpecifierForValueTypes; } + if (entity is IMethod m && m.IsLocalFunction) { + writer.WriteIdentifier(Identifier.Create("(local)")); + } new CSharpAmbience() { ConversionFlags = flags, }.ConvertSymbol(entity, writer, settings.CSharpFormattingOptions);