diff --git a/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs b/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs index 2e3cf9755..0ba2b5c54 100644 --- a/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs +++ b/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs @@ -30,7 +30,21 @@ namespace ICSharpCode.Decompiler.Generators; [Generator] internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator { - record AstNodeAdditions(string NodeName, bool NeedsAcceptImpls, bool NeedsVisitor, bool NeedsNullNode, bool NeedsPatternPlaceholder, int NullNodeBaseCtorParamCount, bool IsTypeNode, string VisitMethodName, string VisitMethodParamType, EquatableArray<(string Member, string TypeName, bool RecursiveMatch, bool MatchAny)>? MembersToMatch, EquatableArray<(string RoleExpr, bool IsCollection, string PropertyName, string PropertyType, string ElementType, bool IsOverride, bool IsNullable)>? Slots); + record AstNodeAdditions(string NodeName, bool NeedsAcceptImpls, bool NeedsVisitor, bool NeedsNullNode, bool NeedsPatternPlaceholder, int NullNodeBaseCtorParamCount, bool IsTypeNode, string VisitMethodName, string VisitMethodParamType, EquatableArray<(string Member, string TypeName, bool RecursiveMatch, bool MatchAny)>? MembersToMatch, EquatableArray<(string RoleExpr, bool IsCollection, string PropertyName, string PropertyType, string ElementType, bool IsOverride, bool IsNullable, string KindName)>? Slots); + + // Derives the shared SlotKind name from a [Slot] role expression: the last dotted segment with a + // trailing "Role" removed (e.g. "Roles.EmbeddedStatement" -> "EmbeddedStatement", "LeftRole" -> + // "Left", "PropertyDeclaration.GetterRole" -> "Getter"). Roles that share a name (aliases, or the + // same logical slot across node types) intentionally collapse to one kind, so node.Slot.Kind matches + // node.Role comparisons; consumer sites derive the kind from their role the same way. + static string SlotKindName(string roleExpr) + { + int dot = roleExpr.LastIndexOf('.'); + string name = dot >= 0 ? roleExpr.Substring(dot + 1) : roleExpr; + if (name.EndsWith("Role") && name.Length > 4) + name = name.Substring(0, name.Length - 4); + return name; + } AstNodeAdditions GetAstNodeAdditions(GeneratorAttributeSyntaxContext context, CancellationToken ct) { @@ -78,7 +92,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator // Collect the slot schema: child properties tagged [Slot], in declaration order. The // attribute names the Role expression to use; single vs collection comes from the type. - List<(string RoleExpr, bool IsCollection, string PropertyName, string PropertyType, string ElementType, bool IsOverride, bool IsNullable)>? slots = null; + List<(string RoleExpr, bool IsCollection, string PropertyName, string PropertyType, string ElementType, bool IsOverride, bool IsNullable, string KindName)>? slots = null; foreach (var m in targetSymbol.GetMembers()) { if (m is not IPropertySymbol property) @@ -96,7 +110,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator string elementType = isCollection ? ((INamedTypeSymbol)property.Type).TypeArguments[0].ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) : propertyType; - slots.Add((roleExpr, isCollection, property.Name, propertyType, elementType, property.IsOverride, isNullable)); + slots.Add((roleExpr, isCollection, property.Name, propertyType, elementType, property.IsOverride, isNullable, SlotKindName(roleExpr))); } return new(targetSymbol.Name, !targetSymbol.MemberNames.Contains("AcceptVisitor"), @@ -293,7 +307,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator // substitutes the role's null object when empty (pre-NRT); a collection slot owns a lazily // created AstNodeCollection bound to this node. A slot re-declared from an inherited contract // member (Part I.3 flatten) is an override. - foreach (var (roleExpr, isCollection, name, type, elementType, isOverride, isNullable) in slots) + foreach (var (roleExpr, isCollection, name, type, elementType, isOverride, isNullable, kindName) in slots) { string field = FieldName(name); if (isCollection) @@ -367,6 +381,23 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator builder.AppendLine("\t\tthrow new System.ArgumentOutOfRangeException(nameof(index));"); builder.AppendLine("\t}"); + // One CSharpSlotInfo static per slot; node.Slot compares against these by object identity. + foreach (var s in slots) + builder.AppendLine($"\tpublic static readonly CSharpSlotInfo {s.PropertyName}Slot = new CSharpSlotInfo(\"{s.PropertyName}\", typeof({s.ElementType}), {(s.IsCollection ? "true" : "false")}, SlotKind.{s.KindName});"); + + builder.AppendLine("\tinternal override CSharpSlotInfo GetChildSlotInfo(int index)"); + builder.AppendLine("\t{"); + builder.AppendLine("\t\tint i = index;"); + foreach (var s in slots) + { + if (s.IsCollection) + builder.AppendLine($"\t\t{{ int n = {FieldName(s.PropertyName)}?.Count ?? 0; if (i < n) return {s.PropertyName}Slot; i -= n; }}"); + else + builder.AppendLine($"\t\tif (i == 0) return {s.PropertyName}Slot; i -= 1;"); + } + builder.AppendLine("\t\tthrow new System.ArgumentOutOfRangeException(nameof(index));"); + builder.AppendLine("\t}"); + if (slots.Any(s => s.IsCollection)) { builder.AppendLine("\tinternal override AstNodeCollection? GetCollectionByRole(Role role)"); @@ -489,6 +520,36 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax context.RegisterSourceOutput(astNodeAdditions, WriteGeneratedMembers); context.RegisterSourceOutput(visitorMembers, WriteVisitors); + context.RegisterSourceOutput(visitorMembers, WriteSlotKinds); + } + + // Emits the SlotKind enum: one value per distinct slot kind across all nodes. A node's CSharpSlotInfo + // carries its kind, and consumers compare node.Slot.Kind == SlotKind.X -- shared across node types, so + // it replaces the old polymorphic node.Role == Roles.X comparisons. + void WriteSlotKinds(SourceProductionContext context, ImmutableArray source) + { + var kinds = new SortedSet(StringComparer.Ordinal); + foreach (var node in source) + { + if (node.Slots is { } slots) + { + foreach (var s in slots) + kinds.Add(s.KindName); + } + } + + var builder = new StringBuilder(); + builder.AppendLine("namespace ICSharpCode.Decompiler.CSharp.Syntax;"); + builder.AppendLine(); + builder.AppendLine("/// Identifies the kind of an AST child slot, shared across node types."); + builder.AppendLine("public enum SlotKind"); + builder.AppendLine("{"); + builder.AppendLine("\tNone,"); + foreach (var k in kinds) + builder.AppendLine($"\t{k},"); + builder.AppendLine("}"); + + context.AddSource("SlotKind.g.cs", SourceText.From(builder.ToString(), Encoding.UTF8)); } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index ca3861fb4..9353f8869 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -765,14 +765,14 @@ namespace ICSharpCode.Decompiler.CSharp var astBuilder = CreateAstBuilder(decompileRun.Settings); var attrSection = new AttributeSection(astBuilder.ConvertAttribute(a)); attrSection.AttributeTarget = "assembly"; - syntaxTree.AddChild(attrSection, SyntaxTree.MemberRole); + syntaxTree.Members.Add(attrSection); } foreach (var a in typeSystem.MainModule.GetModuleAttributes()) { var astBuilder = CreateAstBuilder(decompileRun.Settings); var attrSection = new AttributeSection(astBuilder.ConvertAttribute(a)); attrSection.AttributeTarget = "module"; - syntaxTree.AddChild(attrSection, SyntaxTree.MemberRole); + syntaxTree.Members.Add(attrSection); } } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) @@ -801,7 +801,7 @@ namespace ICSharpCode.Decompiler.CSharp if (currentNamespace != typeDef.Namespace) { groupNode = new NamespaceDeclaration(typeDef.Namespace); - syntaxTree.AddChild(groupNode, SyntaxTree.MemberRole); + syntaxTree.Members.Add(groupNode); } } currentNamespace = typeDef.Namespace; diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 762041ccf..06a599b5a 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -252,8 +252,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor /// protected virtual void Semicolon() { - // get the role of the current node - Role role = containerStack.Peek().Role; + // get the slot of the current node + SlotKind? kind = containerStack.Peek().Slot?.Kind; if (!SkipToken()) { WriteToken(Roles.Semicolon); @@ -265,16 +265,16 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor bool SkipToken() { - return role == ForStatement.InitializerRole - || role == ForStatement.IteratorRole - || role == UsingStatement.ResourceAcquisitionRole; + return kind == SlotKind.Initializer + || kind == SlotKind.Iterator + || kind == SlotKind.ResourceAcquisition; } bool SkipNewLine() { if (containerStack.Peek() is not Accessor accessor) return false; - if (!(role == PropertyDeclaration.GetterRole || role == PropertyDeclaration.SetterRole)) + if (!(kind == SlotKind.Getter || kind == SlotKind.Setter)) return false; bool isAutoProperty = accessor.Body.IsNull && !accessor.Attributes.Any() @@ -693,11 +693,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } if (node.Parent is ObjectCreateExpression) { - return node.Role == ObjectCreateExpression.InitializerRole; + return node.Slot?.Kind == SlotKind.Initializer; } if (node.Parent is NamedExpression) { - return node.Role == Roles.Expression; + return node.Slot?.Kind == SlotKind.Expression; } return false; } @@ -1358,7 +1358,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitQueryExpression(QueryExpression queryExpression) { StartNode(queryExpression); - if (queryExpression.Role != QueryContinuationClause.PrecedingQueryRole) + if (queryExpression.Slot?.Kind != SlotKind.PrecedingQuery) writer.Indent(); bool first = true; foreach (var clause in queryExpression.Clauses) @@ -1376,7 +1376,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } clause.AcceptVisitor(this); } - if (queryExpression.Role != QueryContinuationClause.PrecedingQueryRole) + if (queryExpression.Slot?.Kind != SlotKind.PrecedingQuery) writer.Unindent(); EndNode(queryExpression); } @@ -1707,7 +1707,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(usingAliasDeclaration); WriteKeyword(UsingAliasDeclaration.UsingKeywordRole); - WriteIdentifier(usingAliasDeclaration.GetChildByRole(UsingAliasDeclaration.AliasRole)); + WriteIdentifier(usingAliasDeclaration.AliasToken); Space(policy.SpaceAroundEqualityOperator); WriteToken(Roles.Assign); Space(policy.SpaceAroundEqualityOperator); @@ -1912,7 +1912,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(gotoStatement); WriteKeyword(GotoStatement.GotoKeywordRole); - WriteIdentifier(gotoStatement.GetChildByRole(Roles.Identifier)); + WriteIdentifier(gotoStatement.NameToken); Semicolon(); EndNode(gotoStatement); } @@ -1957,7 +1957,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor bool foundLabelledStatement = false; for (AstNode tmp = labelStatement.NextSibling; tmp != null; tmp = tmp.NextSibling) { - if (tmp.Role == labelStatement.Role) + if (tmp.Slot?.Kind == labelStatement.Slot?.Kind) { foundLabelledStatement = true; } @@ -2312,12 +2312,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteAttributes(accessor.Attributes); WriteModifiers(accessor.Modifiers); BraceStyle style = policy.StatementBraceStyle; - if (accessor.Role == PropertyDeclaration.GetterRole) + if (accessor.Slot?.Kind == SlotKind.Getter) { WriteKeyword("get", PropertyDeclaration.GetKeywordRole); style = policy.PropertyGetBraceStyle; } - else if (accessor.Role == PropertyDeclaration.SetterRole) + else if (accessor.Slot?.Kind == SlotKind.Setter) { if (accessor.Kind == AccessorKind.Init) { @@ -2329,12 +2329,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } style = policy.PropertySetBraceStyle; } - else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) + else if (accessor.Slot?.Kind == SlotKind.AddAccessor) { WriteKeyword("add", CustomEventDeclaration.AddKeywordRole); style = policy.EventAddBraceStyle; } - else if (accessor.Role == CustomEventDeclaration.RemoveAccessorRole) + else if (accessor.Slot?.Kind == SlotKind.RemoveAccessor) { WriteKeyword("remove", CustomEventDeclaration.RemoveKeywordRole); style = policy.EventRemoveBraceStyle; @@ -2479,7 +2479,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // output add/remove in their original order foreach (AstNode node in customEventDeclaration.Children) { - if (node.Role == CustomEventDeclaration.AddAccessorRole || node.Role == CustomEventDeclaration.RemoveAccessorRole) + if (node.Slot?.Kind == SlotKind.AddAccessor || node.Slot?.Kind == SlotKind.RemoveAccessor) { node.AcceptVisitor(this); } @@ -2556,7 +2556,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // output get/set in their original order foreach (AstNode node in indexerDeclaration.Children) { - if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) + if (node.Slot?.Kind == SlotKind.Getter || node.Slot?.Kind == SlotKind.Setter) { node.AcceptVisitor(this); } @@ -2718,7 +2718,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // output get/set in their original order foreach (AstNode node in propertyDeclaration.Children) { - if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) + if (node.Slot?.Kind == SlotKind.Getter || node.Slot?.Kind == SlotKind.Setter) { node.AcceptVisitor(this); } @@ -3183,7 +3183,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } break; default: - WriteIdentifier(documentationReference.GetChildByRole(Roles.Identifier)); + WriteIdentifier(documentationReference.NameToken); break; } WriteTypeArguments(documentationReference.TypeArguments); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index bb79661e3..ace70d800 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -513,7 +513,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor void HandleLambdaOrQuery(Expression expr) { - if (expr.Role == BinaryOperatorExpression.LeftRole) + if (expr.Slot?.Kind == SlotKind.Left) Parenthesize(expr); if (expr.Parent is IsExpression || expr.Parent is AsExpression) Parenthesize(expr); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs index 4c5f8d5ef..fe4e2a203 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs @@ -403,6 +403,22 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax internal virtual Role GetChildSlot(int index) => throw new ArgumentOutOfRangeException(nameof(index)); + // The CSharpSlotInfo for the slot at the given flattened index; generator-emitted per leaf. + internal virtual CSharpSlotInfo GetChildSlotInfo(int index) => throw new ArgumentOutOfRangeException(nameof(index)); + + /// + /// The slot this node occupies in its parent, or null if it has no parent. Compare against a + /// node type's generated slot statics (e.g. node.Slot == BinaryOperatorExpression.LeftSlot). + /// + public CSharpSlotInfo? Slot { + get { + if (parent == null) + return null; + parent.EnsureChildIndices(); + return parent.GetChildSlotInfo(childIndex); + } + } + // Returns the collection occupying the slot with the given role, or null if there is none. // Overridden by the generator for nodes that have collection slots. internal virtual AstNodeCollection? GetCollectionByRole(Role role) => null; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs index 921ddd73e..9d8de1256 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs @@ -60,7 +60,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { return NameLookupMode.TypeInUsingDeclaration; } - else if (outermostType.Role == Roles.BaseType) + else if (outermostType.Slot?.Kind == SlotKind.BaseType) { // Use BaseTypeReference for a type's base type, and for a constraint on a type. // Do not use it for a constraint on a method. diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/CSharpSlotInfo.cs b/ICSharpCode.Decompiler/CSharp/Syntax/CSharpSlotInfo.cs new file mode 100644 index 000000000..2132b2c6f --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Syntax/CSharpSlotInfo.cs @@ -0,0 +1,66 @@ +// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#nullable enable + +using System; + +namespace ICSharpCode.Decompiler.CSharp.Syntax +{ + /// + /// Describes one child slot of a node type: its name, the declared child type, and whether it is a + /// collection. Each slot has a single generated static instance (e.g. + /// BinaryOperatorExpression.LeftSlot); node.Slot returns the slot a node occupies in + /// its parent, so node.Slot == X.YSlot identifies a node's position by object identity (the + /// successor to node.Role == X.YRole). + /// + public sealed class CSharpSlotInfo + { + /// The slot's property name, e.g. "Left", "Body", "Parameters". + public string Name { get; } + + /// The declared child type (the element type for a collection slot). + public Type ChildType { get; } + + /// Whether the slot holds a collection of children rather than a single child. + public bool IsCollection { get; } + + /// + /// Whether the slot is syntactically optional. Populated accurately once the null-object model + /// is replaced by nullable reference types; currently informational. + /// + public bool IsOptional { get; } + + /// + /// The slot's kind, shared across node types (so node.Slot.Kind == SlotKind.X identifies a + /// node's position the way node.Role == Roles.X did, including polymorphically). + /// + public SlotKind Kind { get; } + + internal CSharpSlotInfo(string name, Type childType, bool isCollection, SlotKind kind, bool isOptional = false) + { + Name = name; + ChildType = childType; + IsCollection = isCollection; + Kind = kind; + IsOptional = isOptional; + } + + public override string ToString() => Name; + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs index b81f0c9bf..9ba286657 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs @@ -108,7 +108,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax foreach (var child in curNode.Children) { if (!(child is Statement || child is Expression) && - (child.Role != Roles.TypeMemberRole || ((child is TypeDeclaration || child is DelegateDeclaration) && includeInnerTypes))) + (child.Slot?.Kind != SlotKind.TypeMember || ((child is TypeDeclaration || child is DelegateDeclaration) && includeInnerTypes))) nodeStack.Push(child); } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index 649b116e1..20ab03a6e 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -581,7 +581,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (v.Type.IsByRefLike) return true; // by-ref-like variables always must be initialized at their declaration. - if (v.InsertionPoint.nextNode.Role == ForStatement.InitializerRole) + if (v.InsertionPoint.nextNode.Slot?.Kind == SlotKind.Initializer) return true; // for-statement initializers always should combine declaration and initialization. return !context.Settings.SeparateLocalVariableDeclarations; @@ -693,7 +693,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { v.InsertionPoint = v.InsertionPoint.Up(); } - Debug.Assert(v.InsertionPoint.nextNode.Role == BlockStatement.StatementRole); + Debug.Assert(v.InsertionPoint.nextNode.Slot?.Kind == SlotKind.Statement); if (v.DefaultInitialization == VariableInitKind.NeedsSkipInit) { AstType unsafeType = context.TypeSystemAstBuilder.ConvertType( @@ -770,7 +770,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return false; for (AstNode node = v.FirstUse; node != null; node = node.Parent) { - if (node.Role == Roles.EmbeddedStatement) + if (node.Slot?.Kind == SlotKind.EmbeddedStatement) { return false; } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs index 7bce80282..4e65fc2c7 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs @@ -112,7 +112,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms bool IsElseIf(Statement statement, Statement parent) { - return parent is IfElseStatement && statement.Role == IfElseStatement.FalseRole; + return parent is IfElseStatement && statement.Slot?.Kind == SlotKind.False; } static void InsertBlock(Statement statement) @@ -139,7 +139,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms switch (statement) { case IfElseStatement ies: - return parent is IfElseStatement && ies.Role == IfElseStatement.FalseRole; + return parent is IfElseStatement && ies.Slot?.Kind == SlotKind.False; case VariableDeclarationStatement vds: case WhileStatement ws: case DoWhileStatement dws: diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index 5139ff6ae..7c7d469f8 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -232,7 +232,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms ); return; } - if (method.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) + if (method.Name == "op_True" && arguments.Length == 1 && invocationExpression.Slot?.Kind == SlotKind.Condition) { invocationExpression.ReplaceWith(arguments[0].UnwrapInDirectionExpression()); return; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs index c3b1244c3..ba8439317 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs @@ -679,7 +679,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var newBaseType = new InvocationAstType(); baseType.ReplaceWith(newBaseType); newBaseType.BaseType = baseType; - PrimaryConstructorDecl.Initializer.GetChildrenByRole(Roles.Argument).MoveTo(newBaseType.Arguments); + PrimaryConstructorDecl.Initializer.Arguments.MoveTo(newBaseType.Arguments); } PrimaryConstructorDecl.Remove(); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 165446c48..0b0de28b7 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -188,6 +188,7 @@ + diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index ade5bf66d..ea02a28f0 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -112,18 +112,18 @@ namespace ICSharpCode.Decompiler { AstNode node = nodeStack.Peek(); var symbol = node.GetSymbol(); - if (symbol == null && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) + if (symbol == null && node.Slot?.Kind == SlotKind.TargetExpression && node.Parent is InvocationExpression) { symbol = node.Parent.GetSymbol(); } - if (symbol != null && node.Role == Roles.Type && node.Parent is ObjectCreateExpression) + if (symbol != null && node.Slot?.Kind == SlotKind.Type && node.Parent is ObjectCreateExpression) { var ctorSymbol = node.Parent.GetSymbol(); if (ctorSymbol != null) symbol = ctorSymbol; } - if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) + if (node is IdentifierExpression && node.Slot?.Kind == SlotKind.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) { var declaringType = member.DeclaringType; if (declaringType != null && declaringType.Kind == TypeKind.Delegate) @@ -161,7 +161,7 @@ namespace ICSharpCode.Decompiler return method + gotoStatement.Label; } - if (node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) + if (node.Slot?.Kind == SlotKind.TargetExpression && node.Parent is InvocationExpression) { var symbol = node.Parent.GetSymbol(); if (symbol is LocalFunctionMethod) @@ -184,7 +184,7 @@ namespace ICSharpCode.Decompiler return variable; } - if (id.Role == QueryJoinClause.IntoIdentifierRole || id.Role == QueryJoinClause.JoinIdentifierRole) + if (id.Slot?.Kind == SlotKind.IntoIdentifier || id.Slot?.Kind == SlotKind.JoinIdentifier) { var variable = id.Annotation()?.Variable; if (variable != null) @@ -426,7 +426,7 @@ namespace ICSharpCode.Decompiler case "object": var node = nodeStack.Peek(); ISymbol symbol; - if (node.Role == Roles.Type && node.Parent is ObjectCreateExpression) + if (node.Slot?.Kind == SlotKind.Type && node.Parent is ObjectCreateExpression) { symbol = node.Parent.GetSymbol(); }