diff --git a/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs b/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs index c4d0ee781..a21a814d7 100644 --- a/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs +++ b/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs @@ -38,7 +38,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator readonly record struct MemberMatch(string Member, string TypeName, bool RecursiveMatch, bool MatchAny, bool Nullable); // One child slot's schema: a single AstNode-typed child, an AstNodeCollection, or the backing - // Identifier token of a string name. KindName is the shared SlotKind; IsPartial is false only for + // Identifier token of a string name. KindName is the shared slot-kind name; IsPartial is false only for // the generator-owned token slot behind a name (the source does not declare that property). readonly record struct SlotInfo(bool IsCollection, string PropertyName, string PropertyType, string ElementType, bool IsOverride, bool IsNullable, string KindName, bool IsPartial); @@ -132,9 +132,9 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator if (slotAttr != null) { slots ??= new(); - // The [Slot] argument is already the bare SlotKind name (e.g. "Body", "Expression"); kinds + // The [Slot] argument is already the bare slot-kind name (e.g. "Body", "Expression"); kinds // shared across nodes (aliases, or the same logical slot on different node types) deliberately - // collapse to one name, so node.Slot.Kind is shared and consumers compare against SlotKind.X. + // collapse to one name, so node.Slot.Kind is shared and consumers compare against Slots.X. string kindName = (string)slotAttr.ConstructorArguments[0].Value!; // A [Slot] on a string property is a name: a convenience string accessor over a backing // Identifier token slot. Child slots are AstNode-typed, so the property type disambiguates - @@ -354,7 +354,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator if (isCollection) { builder.AppendLine($"\tAstNodeCollection<{elementType}>? {field};"); - builder.AppendLine($"\tpublic {(isOverride ? "override " : "")}{partialKw}AstNodeCollection<{elementType}> {name} => {field} ??= new AstNodeCollection<{elementType}>(this, SlotKind.{kindName});"); + builder.AppendLine($"\tpublic {(isOverride ? "override " : "")}{partialKw}AstNodeCollection<{elementType}> {name} => {field} ??= new AstNodeCollection<{elementType}>(this, Slots.{kindName});"); } else { @@ -611,7 +611,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator // One typed CSharpSlotInfo static per slot; node.Slot compares against these by object // identity, and the typed child accessors infer the child type from the slot. foreach (var s in slots) - builder.AppendLine($"\tpublic static readonly CSharpSlotInfo<{s.ElementType}> {s.PropertyName}Slot = new CSharpSlotInfo<{s.ElementType}>(\"{s.PropertyName}\", {(s.IsCollection ? "true" : "false")}, SlotKind.{s.KindName}, {(s.IsCollection || s.IsNullable ? "true" : "false")});"); + builder.AppendLine($"\tpublic static readonly CSharpSlotInfo<{s.ElementType}> {s.PropertyName}Slot = new CSharpSlotInfo<{s.ElementType}>(\"{s.PropertyName}\", {(s.IsCollection ? "true" : "false")}, Slots.{s.KindName}, {(s.IsCollection || s.IsNullable ? "true" : "false")});"); builder.AppendLine(); builder.AppendLine("\tinternal override CSharpSlotInfo GetChildSlotInfo(int index)"); @@ -622,10 +622,10 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator if (slots.Any(s => s.IsCollection)) { - builder.AppendLine("\tinternal override AstNodeCollection? GetCollectionByKind(SlotKind kind)"); + builder.AppendLine("\tinternal override AstNodeCollection? GetCollectionByKind(CSharpSlotInfo kind)"); builder.AppendLine("\t{"); foreach (var s in slots.Where(s => s.IsCollection)) - builder.AppendLine($"\t\tif (kind == SlotKind.{s.KindName}) return {s.PropertyName};"); + builder.AppendLine($"\t\tif (kind == Slots.{s.KindName}) return {s.PropertyName};"); builder.AppendLine("\t\treturn base.GetCollectionByKind(kind);"); builder.AppendLine("\t}"); builder.AppendLine(); @@ -776,9 +776,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax 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. + // Emits the Slots holder: one typed CSharpSlotInfo constant per distinct slot kind across all + // nodes. Each is its own canonical kind (constructed with a null Kind); a node's per-node slot points + // back at it, and consumers compare node.Slot.Kind == Slots.X by object identity -- shared across node + // types, replacing the old polymorphic node.Role == Roles.X comparisons. void WriteSlotKinds(SourceProductionContext context, ImmutableArray source) { // Per kind: the element types seen (to choose a typed slot's T) and whether it is a collection. @@ -809,29 +810,27 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax builder.AppendLine(); 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 kindTypes.Keys) - builder.AppendLine($"\t{k},"); - builder.AppendLine("}"); - builder.AppendLine(); - // Typed kind constants for polymorphic access: node.GetChild(Slots.X) infers the child type. A - // kind reused with several child types across node types widens to AstNode (only its concrete - // per-node slots carry the precise type); such kinds are only reached through those per-node slots. - builder.AppendLine("/// Typed slot kinds for polymorphic child access; the child type is inferred from the slot."); + // Each constant is its own canonical kind (null Kind): node.GetChild(Slots.X) infers the child + // type, and node.Slot.Kind == Slots.X identifies a position polymorphically. A kind reused with + // several child types across node types widens to AstNode (only its concrete per-node slots carry + // the precise type); such kinds are only reached through those per-node slots. For the same reason + // the shared kind's IsCollection flag and its hard-coded isOptional: false are not authoritative + // when a kind is a collection on one node and a single/optional child on another: such dual-use + // kinds carry IsCollection false (no arity claim), and the precise per-position flags live on the + // per-node slots, which is where consumers read them; the shared constant carries identity, not + // those flags. + builder.AppendLine("/// The shared slot kinds, one per distinct child position across the AST node types."); builder.AppendLine("public static class Slots"); builder.AppendLine("{"); foreach (var kv in kindTypes) { string type = kv.Value.Count == 1 ? kv.Value.Min : "AstNode"; string isColl = kindIsCollection[kv.Key] == true ? "true" : "false"; - builder.AppendLine($"\tpublic static readonly CSharpSlotInfo<{type}> {kv.Key} = new CSharpSlotInfo<{type}>(\"{kv.Key}\", {isColl}, SlotKind.{kv.Key}, false);"); + builder.AppendLine($"\tpublic static readonly CSharpSlotInfo<{type}> {kv.Key} = new CSharpSlotInfo<{type}>(\"{kv.Key}\", {isColl}, null, false);"); } builder.AppendLine("}"); - context.AddSource("SlotKind.g.cs", SourceText.From(builder.ToString().Replace("\r\n", "\n"), Encoding.UTF8)); + context.AddSource("Slots.g.cs", SourceText.From(builder.ToString().Replace("\r\n", "\n"), Encoding.UTF8)); } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 798f5c022..a807a74f1 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -812,7 +812,7 @@ namespace ICSharpCode.Decompiler.CSharp } currentNamespace = typeDef.Namespace; var typeDecl = DoDecompile(typeDef, decompileRun, decompilationContext.WithCurrentTypeDefinition(typeDef)); - groupNode!.AddChild(typeDecl, SlotKind.Member); + groupNode!.AddChild(typeDecl, Slots.Member); } } @@ -2062,7 +2062,7 @@ namespace ICSharpCode.Decompiler.CSharp var commentStatement = new EmptyStatement(); commentStatement.AddTrailingTrivia(new Comment("Invalid MethodBodyBlock: " + ex.Message)); body.Statements.Add(commentStatement); - entityDecl.AddChild(body, SlotKind.Body); + entityDecl.AddChild(body, Slots.Body); return; } var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: CancellationToken); @@ -2121,7 +2121,7 @@ namespace ICSharpCode.Decompiler.CSharp body.Statements.Add(warningStatement); } - entityDecl.AddChild(body, SlotKind.Body); + entityDecl.AddChild(body, Slots.Body); } CleanUpMethodDeclaration(entityDecl, body, function, localSettings.DecompileMemberBodies); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 22df73dd5..4970a2f57 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -213,7 +213,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor protected virtual void Semicolon() { // get the slot of the current node - SlotKind? kind = containerStack.Peek().Slot?.Kind; + CSharpSlotInfo? kind = containerStack.Peek().Slot?.Kind; if (!SkipToken()) { WriteToken(Roles.Semicolon); @@ -225,16 +225,16 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor bool SkipToken() { - return kind == SlotKind.Initializer - || kind == SlotKind.Iterator - || kind == SlotKind.ResourceAcquisition; + return kind == Slots.Initializer + || kind == Slots.Iterator + || kind == Slots.ResourceAcquisition; } bool SkipNewLine() { if (containerStack.Peek() is not Accessor accessor) return false; - if (!(kind == SlotKind.Getter || kind == SlotKind.Setter)) + if (!(kind == Slots.Getter || kind == Slots.Setter)) return false; bool isAutoProperty = accessor.Body is null && !accessor.Attributes.Any() @@ -654,11 +654,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } if (node.Parent is ObjectCreateExpression) { - return node.Slot?.Kind == SlotKind.Initializer; + return node.Slot?.Kind == Slots.Initializer; } if (node.Parent is NamedExpression) { - return node.Slot?.Kind == SlotKind.Expression; + return node.Slot?.Kind == Slots.Expression; } return false; } @@ -1316,7 +1316,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitQueryExpression(QueryExpression queryExpression) { StartNode(queryExpression); - if (queryExpression.Slot?.Kind != SlotKind.PrecedingQuery) + if (queryExpression.Slot?.Kind != Slots.PrecedingQuery) writer.Indent(); bool first = true; foreach (var clause in queryExpression.Clauses) @@ -1334,7 +1334,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } clause.AcceptVisitor(this); } - if (queryExpression.Slot?.Kind != SlotKind.PrecedingQuery) + if (queryExpression.Slot?.Kind != Slots.PrecedingQuery) writer.Unindent(); EndNode(queryExpression); } @@ -2265,12 +2265,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteAttributes(accessor.Attributes); WriteModifiers(accessor.Modifiers); BraceStyle style = policy.StatementBraceStyle; - if (accessor.Slot?.Kind == SlotKind.Getter) + if (accessor.Slot?.Kind == Slots.Getter) { WriteKeyword("get"); style = policy.PropertyGetBraceStyle; } - else if (accessor.Slot?.Kind == SlotKind.Setter) + else if (accessor.Slot?.Kind == Slots.Setter) { if (accessor.Kind == AccessorKind.Init) { @@ -2282,12 +2282,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } style = policy.PropertySetBraceStyle; } - else if (accessor.Slot?.Kind == SlotKind.AddAccessor) + else if (accessor.Slot?.Kind == Slots.AddAccessor) { WriteKeyword("add"); style = policy.EventAddBraceStyle; } - else if (accessor.Slot?.Kind == SlotKind.RemoveAccessor) + else if (accessor.Slot?.Kind == Slots.RemoveAccessor) { WriteKeyword("remove"); style = policy.EventRemoveBraceStyle; @@ -2432,7 +2432,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // output add/remove in their original order for (AstNode? node = customEventDeclaration.FirstChild; node != null; node = node.NextSibling) { - if (node.Slot?.Kind == SlotKind.AddAccessor || node.Slot?.Kind == SlotKind.RemoveAccessor) + if (node.Slot?.Kind == Slots.AddAccessor || node.Slot?.Kind == Slots.RemoveAccessor) { node.AcceptVisitor(this); } @@ -2509,7 +2509,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // output get/set in their original order for (AstNode? node = indexerDeclaration.FirstChild; node != null; node = node.NextSibling) { - if (node.Slot?.Kind == SlotKind.Getter || node.Slot?.Kind == SlotKind.Setter) + if (node.Slot?.Kind == Slots.Getter || node.Slot?.Kind == Slots.Setter) { node.AcceptVisitor(this); } @@ -2671,7 +2671,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // output get/set in their original order for (AstNode? node = propertyDeclaration.FirstChild; node != null; node = node.NextSibling) { - if (node.Slot?.Kind == SlotKind.Getter || node.Slot?.Kind == SlotKind.Setter) + if (node.Slot?.Kind == Slots.Getter || node.Slot?.Kind == Slots.Setter) { node.AcceptVisitor(this); } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs index 8217ce10c..470cde19b 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs @@ -96,13 +96,13 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // A parentless child is a print-only artifact with no slot in the tree -- e.g. the // detached clone of a type's name token that a renamed constructor/destructor prints // (CSharpOutputVisitor.VisitConstructorDeclaration). It cannot be re-attached by kind, - // and the real name token is already a child, so leave it out rather than route - // SlotKind.None into the throwing child setter. + // and the real name token is already a child, so leave it out rather than route a + // missing kind into the throwing child setter. if (child.Slot is not { } slot) continue; // Slot is derived from the child's index in its parent, so it must be read before - // Remove() detaches the child (which would otherwise leave it as SlotKind.None). - SlotKind kind = slot.Kind; + // Remove() detaches the child (which would otherwise leave it without a slot). + CSharpSlotInfo kind = slot.Kind!; child.Remove(); node.AddChildUnsafe(child, kind); } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index 439ad6641..01148bc5f 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -521,7 +521,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor void HandleLambdaOrQuery(Expression expr) { - if (expr.Slot?.Kind == SlotKind.Left) + if (expr.Slot?.Kind == Slots.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 911423dc5..787ad43d5 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs @@ -292,15 +292,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } /// - /// Gets the first child with the specified role, or null if this node has no such child (the - /// slot is empty or the node declares no slot of that kind). + /// Gets the first child occupying , or null if the slot is empty (or the + /// node declares no such slot). is a canonical Slots kind; the + /// result type is inferred from it. /// - public T? GetChildByRole(SlotKind kind) where T : AstNode + public T? GetChild(CSharpSlotInfo slot) where T : AstNode { int count = GetChildCount(); for (int i = 0; i < count; i++) { - if (GetChildSlotInfo(i).Kind == kind) + if (GetChildSlotInfo(i).Kind == slot) { return GetChild(i) as T; } @@ -308,12 +309,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return null; } - /// - /// Gets the first child occupying , or null if the slot is empty (or the - /// node declares no such slot). The result type is inferred from the typed slot. - /// - public T? GetChild(CSharpSlotInfo slot) where T : AstNode => GetChildByRole(slot.Kind); - public T? GetParent() where T : AstNode { return Ancestors.OfType().FirstOrDefault(); @@ -324,25 +319,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return pred != null ? Ancestors.FirstOrDefault(pred) : Ancestors.FirstOrDefault(); } - public AstNodeCollection GetChildrenByRole(SlotKind kind) where T : AstNode + /// Gets the collection occupying ; the element type is inferred from the slot. + public AstNodeCollection GetChildren(CSharpSlotInfo slot) where T : AstNode { - AstNodeCollection? collection = GetCollectionByKind(kind); + AstNodeCollection? collection = GetCollectionByKind(slot); // A node has no children of a kind it does not declare a collection slot for. Reads of such // a kind (e.g. the parameters of a non-indexer property) get a detached empty collection; - // writes go through AddChild/SetChildByRole, which reject a missing slot. - return collection != null ? (AstNodeCollection)collection : new AstNodeCollection(this, kind); - } - - /// Gets the collection occupying ; the element type is inferred from the slot. - public AstNodeCollection GetChildren(CSharpSlotInfo slot) where T : AstNode => GetChildrenByRole(slot.Kind); - - protected void SetChildByRole(SlotKind kind, T? newChild) where T : AstNode - { - SetChildByRoleUntyped(kind, newChild); + // writes go through AddChild/SetChild, which reject a missing slot. + return collection != null ? (AstNodeCollection)collection : new AstNodeCollection(this, slot); } /// Sets the single child occupying ; the child type is inferred from the slot. - protected void SetChild(CSharpSlotInfo slot, T? newChild) where T : AstNode => SetChildByRole(slot.Kind, newChild); + protected void SetChild(CSharpSlotInfo slot, T? newChild) where T : AstNode => SetChildByKindUntyped(slot, newChild); #region Slot storage contract // Each concrete node's slots form a flattened child-index space, in source-declaration order. @@ -375,7 +363,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax // Returns the collection occupying the slot with the given kind, or null if there is none. // Overridden by the generator for nodes that have collection slots. - internal virtual AstNodeCollection? GetCollectionByKind(SlotKind kind) => null; + internal virtual AstNodeCollection? GetCollectionByKind(CSharpSlotInfo kind) => null; // Deep-copies this node's children into the (memberwise-cloned) copy, which initially shares // this node's child references. Overridden by the generator for nodes that have slots. @@ -489,7 +477,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } #endregion - public void AddChild(T child, SlotKind kind) where T : AstNode + public void AddChild(T child, CSharpSlotInfo kind) where T : AstNode { if (child == null) return; @@ -500,19 +488,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// Adds a child into the slot matching (appending to a collection slot, /// or filling a single slot). /// - internal void AddChildUnsafe(AstNode child, SlotKind kind) + internal void AddChildUnsafe(AstNode child, CSharpSlotInfo kind) { AstNodeCollection? collection = GetCollectionByKind(kind); if (collection != null) collection.AddNode(child); else - SetChildByRoleUntyped(kind, child); + SetChildByKindUntyped(kind, child); } // Sets the single slot matching the kind (used by the non-generic mutation API). Unlike - // GetChildByRole, which returns null when this node declares no slot of that kind, writing a + // GetChild, which returns null when this node declares no slot of that kind, writing a // child to a kind the node has no slot for throws. - internal void SetChildByRoleUntyped(SlotKind kind, AstNode? child) + internal void SetChildByKindUntyped(CSharpSlotInfo kind, AstNode? child) { int count = GetChildCount(); for (int i = 0; i < count; i++) @@ -526,7 +514,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax throw new InvalidOperationException($"{GetType().Name} has no slot of kind '{kind}'."); } - public void InsertChildBefore(AstNode? nextSibling, T child, SlotKind kind) where T : AstNode + public void InsertChildBefore(AstNode? nextSibling, T child, CSharpSlotInfo kind) where T : AstNode { if (child == null) return; @@ -534,19 +522,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax if (collection != null) collection.InsertNodeBefore(nextSibling, child); else - SetChildByRoleUntyped(kind, child); + SetChildByKindUntyped(kind, child); } - internal void InsertChildBeforeUnsafe(AstNode nextSibling, AstNode child, SlotKind kind) + internal void InsertChildBeforeUnsafe(AstNode nextSibling, AstNode child, CSharpSlotInfo kind) { AstNodeCollection? collection = GetCollectionByKind(kind); if (collection != null) collection.InsertNodeBefore(nextSibling, child); else - SetChildByRoleUntyped(kind, child); + SetChildByKindUntyped(kind, child); } - public void InsertChildAfter(AstNode? prevSibling, T child, SlotKind kind) where T : AstNode + public void InsertChildAfter(AstNode? prevSibling, T child, CSharpSlotInfo kind) where T : AstNode { if (child == null) return; @@ -554,7 +542,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax if (collection != null) collection.InsertNodeAfter(prevSibling, child); else - SetChildByRoleUntyped(kind, child); + SetChildByKindUntyped(kind, child); } /// @@ -565,7 +553,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax if (parent == null) return; parent.EnsureChildIndices(); - SlotKind kind = parent.GetChildSlotInfo(childIndex).Kind; + CSharpSlotInfo kind = parent.GetChildSlotInfo(childIndex).Kind!; AstNodeCollection? collection = parent.GetCollectionByKind(kind); if (collection != null) collection.RemoveNode(this); @@ -625,12 +613,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax AstNode oldParent = parent; AstNode? oldSuccessor = NextSibling; CSharpSlotInfo? oldSlot = this.Slot; - SlotKind oldKind = oldSlot?.Kind ?? SlotKind.None; + CSharpSlotInfo? oldKind = oldSlot?.Kind; Remove(); AstNode? replacement = replaceFunction(this); if (oldSuccessor != null && oldSuccessor.parent != oldParent) throw new InvalidOperationException("replace function changed nextSibling of node being replaced?"); - if (replacement != null) + if (replacement != null && oldKind != null) { if (replacement.parent != null) throw new InvalidOperationException("replace function must return the root of a tree"); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs index d2f225660..7d6643487 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs @@ -72,10 +72,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax where T : AstNode { readonly AstNode parent; - readonly SlotKind kind; + readonly CSharpSlotInfo kind; readonly List list = new List(); - public AstNodeCollection(AstNode parent, SlotKind kind) + public AstNodeCollection(AstNode parent, CSharpSlotInfo kind) { this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); this.kind = kind; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs index 5ff696233..988f59fbe 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { return NameLookupMode.TypeInUsingDeclaration; } - else if (outermostType.Slot?.Kind == SlotKind.BaseType) + else if (outermostType.Slot?.Kind == Slots.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 index 355714f8f..3fd41e242 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/CSharpSlotInfo.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/CSharpSlotInfo.cs @@ -41,10 +41,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public bool IsCollection { 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). + /// The slot's kind: the canonical shared slot for this child position, identifying it across node + /// types by object identity (so node.Slot.Kind == Slots.X identifies a node's position the + /// way node.Role == Roles.X did, including polymorphically). A per-node slot points at its + /// shared Slots constant; a Slots constant is itself the kind, so its own + /// is null (and is never read -- only per-node slots are asked for their kind). /// - public SlotKind Kind { get; } + public CSharpSlotInfo? Kind { get; } /// /// Whether the slot may be empty: a single slot whose child is nullable, or any collection slot @@ -53,7 +56,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// public bool IsOptional { get; } - internal CSharpSlotInfo(string name, Type childType, bool isCollection, SlotKind kind, bool isOptional) + internal CSharpSlotInfo(string name, Type childType, bool isCollection, CSharpSlotInfo? kind, bool isOptional) { Name = name; ChildType = childType; @@ -73,7 +76,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// public sealed class CSharpSlotInfo : CSharpSlotInfo where T : AstNode { - internal CSharpSlotInfo(string name, bool isCollection, SlotKind kind, bool isOptional) + internal CSharpSlotInfo(string name, bool isCollection, CSharpSlotInfo? kind, bool isOptional) : base(name, typeof(T), isCollection, kind, isOptional) { } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs index 941ca72bd..ffbe59771 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs @@ -111,7 +111,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public override AstType MakeArrayType(int dimensions) { - InsertChildBefore(this.ArraySpecifiers.FirstOrDefault(), new ArraySpecifier(dimensions), SlotKind.ArraySpecifier); + InsertChildBefore(this.ArraySpecifiers.FirstOrDefault(), new ArraySpecifier(dimensions), Slots.ArraySpecifier); return this; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs index 20d7ad5ae..6e69bed0c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs @@ -118,7 +118,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public void AddMember(AstNode child) { - AddChild(child, SlotKind.Member); + AddChild(child, Slots.Member); } } }; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs index f327ca9b0..b0fe2ad93 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs @@ -44,8 +44,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public UsingAliasDeclaration(string alias, string nameSpace) { - AddChild(Identifier.Create(alias), SlotKind.Alias); - AddChild(new SimpleType(nameSpace), SlotKind.Import); + AddChild(Identifier.Create(alias), Slots.Alias); + AddChild(new SimpleType(nameSpace), Slots.Import); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs index 47010c567..d41e194eb 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs @@ -81,7 +81,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public UsingDeclaration(string nameSpace) { - AddChild(AstType.Create(nameSpace), SlotKind.Import); + AddChild(AstType.Create(nameSpace), Slots.Import); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs index 4643c5676..6981b2836 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs @@ -41,12 +41,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public void Add(Statement statement) { - AddChild(statement, SlotKind.Statement); + AddChild(statement, Slots.Statement); } public void Add(Expression expression) { - AddChild(new ExpressionStatement(expression), SlotKind.Statement); + AddChild(new ExpressionStatement(expression), Slots.Statement); } IEnumerator IEnumerable.GetEnumerator() diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs index b2d02dd3a..8ded15c8f 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs @@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax foreach (var child in curNode.Children) { if (!(child is Statement || child is Expression) && - (child.Slot?.Kind != SlotKind.TypeMember || ((child is TypeDeclaration || child is DelegateDeclaration) && includeInnerTypes))) + (child.Slot?.Kind != Slots.TypeMember || ((child is TypeDeclaration || child is DelegateDeclaration) && includeInnerTypes))) nodeStack.Push(child); } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 331371d39..25192aaac 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -636,11 +636,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { if (ConvertUnboundTypeArguments && typeArguments[i].Kind == TypeKind.UnboundTypeArgument) { - result.AddChild(MakeSimpleType(typeParameters[i].Name), SlotKind.TypeArgument); + result.AddChild(MakeSimpleType(typeParameters[i].Name), Slots.TypeArgument); } else { - result.AddChild(ConvertType(typeArguments[i]), SlotKind.TypeArgument); + result.AddChild(ConvertType(typeArguments[i]), Slots.TypeArgument); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index 36de87f5e..5f231aba2 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -587,7 +587,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.Slot?.Kind == SlotKind.Initializer) + if (v.InsertionPoint.nextNode.Slot?.Kind == Slots.Initializer) return true; // for-statement initializers always should combine declaration and initialization. return !context.Settings.SeparateLocalVariableDeclarations; @@ -699,7 +699,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { v.InsertionPoint = v.InsertionPoint.Up(); } - Debug.Assert(v.InsertionPoint.nextNode.Slot?.Kind == SlotKind.Statement); + Debug.Assert(v.InsertionPoint.nextNode.Slot?.Kind == Slots.Statement); // The insertion point is a statement within a block, so it always has a parent. AstNode insertionNode = v.InsertionPoint.nextNode; AstNode insertionParent = insertionNode.Parent @@ -725,14 +725,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } }, - SlotKind.Statement); + Slots.Statement); } else { insertionParent.InsertChildBefore( v.InsertionPoint.nextNode, vds, - SlotKind.Statement); + Slots.Statement); insertionParent.InsertChildBefore( v.InsertionPoint.nextNode, new ExpressionStatement { @@ -750,7 +750,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } }, - SlotKind.Statement); + Slots.Statement); } } else @@ -758,7 +758,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms insertionParent.InsertChildBefore( v.InsertionPoint.nextNode, vds, - SlotKind.Statement); + Slots.Statement); } } } @@ -780,7 +780,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return false; for (AstNode? node = v.FirstUse; node != null; node = node.Parent) { - if (node.Slot?.Kind == SlotKind.EmbeddedStatement) + if (node.Slot?.Kind == Slots.EmbeddedStatement) { return false; } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs index 132eb6444..020c31038 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs @@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { resolvedNamespaces.Add(resolvedNamespace); } - rootNode.InsertChildAfter(insertionPoint, new UsingDeclaration { Import = nsType }, SlotKind.Member); + rootNode.InsertChildAfter(insertionPoint, new UsingDeclaration { Import = nsType }, Slots.Member); } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs index 0f731678f..394c4fde1 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs @@ -117,7 +117,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms bool IsElseIf(Statement statement, Statement parent) { - return parent is IfElseStatement && statement.Slot?.Kind == SlotKind.False; + return parent is IfElseStatement && statement.Slot?.Kind == Slots.False; } static void InsertBlock(Statement statement) @@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms switch (statement) { case IfElseStatement ies: - return parent is IfElseStatement && ies.Slot?.Kind == SlotKind.False; + return parent is IfElseStatement && ies.Slot?.Kind == Slots.False; case VariableDeclarationStatement vds: case WhileStatement ws: case DoWhileStatement dws: diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index fcb5549ab..2e1e07db8 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -192,7 +192,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (next is ForStatement forStatement && ForStatementUsesVariable(forStatement, variable)) { node.Remove(); - next.InsertChildAfter(null, node, SlotKind.Initializer); + next.InsertChildAfter(null, node, Slots.Initializer); return (ForStatement)next; } Match m3 = forPattern.Match(next); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index 6d9bf30d2..fa0612156 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -236,7 +236,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms ); return; } - if (method.Name == "op_True" && arguments.Length == 1 && invocationExpression.Slot?.Kind == SlotKind.Condition) + if (method.Name == "op_True" && arguments.Length == 1 && invocationExpression.Slot?.Kind == Slots.Condition) { invocationExpression.ReplaceWith(arguments[0].UnwrapInDirectionExpression()); return; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs index 1c138b4fb..e568a5132 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs @@ -619,7 +619,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms foreach (var param in PrimaryConstructorDecl.Parameters) { param.Remove(); - this.TypeDeclaration.InsertChildAfter(insertionPoint, param, SlotKind.Parameter); + this.TypeDeclaration.InsertChildAfter(insertionPoint, param, Slots.Parameter); insertionPoint = param; } } diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index dc473111e..7cfd12dbe 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.Slot?.Kind == SlotKind.TargetExpression && node.Parent is InvocationExpression) + if (symbol == null && node.Slot?.Kind == Slots.TargetExpression && node.Parent is InvocationExpression) { symbol = node.Parent.GetSymbol(); } - if (symbol != null && node.Slot?.Kind == SlotKind.Type && node.Parent is ObjectCreateExpression) + if (symbol != null && node.Slot?.Kind == Slots.Type && node.Parent is ObjectCreateExpression) { var ctorSymbol = node.Parent.GetSymbol(); if (ctorSymbol != null) symbol = ctorSymbol; } - if (node is IdentifierExpression && node.Slot?.Kind == SlotKind.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) + if (node is IdentifierExpression && node.Slot?.Kind == Slots.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.Slot?.Kind == SlotKind.TargetExpression && node.Parent is InvocationExpression) + if (node.Slot?.Kind == Slots.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.Slot?.Kind == SlotKind.IntoIdentifier || id.Slot?.Kind == SlotKind.JoinIdentifier) + if (id.Slot?.Kind == Slots.IntoIdentifier || id.Slot?.Kind == Slots.JoinIdentifier) { var variable = id.Annotation()?.Variable; if (variable != null) @@ -428,7 +428,7 @@ namespace ICSharpCode.Decompiler case "object": var node = nodeStack.Peek(); ISymbol symbol; - if (node.Slot?.Kind == SlotKind.Type && node.Parent is ObjectCreateExpression) + if (node.Slot?.Kind == Slots.Type && node.Parent is ObjectCreateExpression) { symbol = node.Parent.GetSymbol(); } diff --git a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs index 2efe84690..eb84056d1 100644 --- a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs +++ b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs @@ -347,7 +347,7 @@ namespace ICSharpCode.ILSpy.Languages { if (identifier.Name == "value" && identifier.Ancestors.OfType().FirstOrDefault() is { } accessor - && accessor.Slot?.Kind != SlotKind.Getter) + && accessor.Slot?.Kind != Slots.Getter) { color = valueKeywordColor; } @@ -473,17 +473,17 @@ namespace ICSharpCode.ILSpy.Languages AstNode node = nodeStack.Peek(); var symbol = node.GetSymbol(); - if (symbol == null && node.Slot?.Kind == SlotKind.TargetExpression && node.Parent is InvocationExpression) + if (symbol == null && node.Slot?.Kind == Slots.TargetExpression && node.Parent is InvocationExpression) { symbol = node.Parent.GetSymbol(); } - if (symbol != null && node.Slot?.Kind == SlotKind.Type && node.Parent is ObjectCreateExpression) + if (symbol != null && node.Slot?.Kind == Slots.Type && node.Parent is ObjectCreateExpression) { var ctorSymbol = node.Parent.GetSymbol(); if (ctorSymbol != null) symbol = ctorSymbol; } - if (node is IdentifierExpression && node.Slot?.Kind == SlotKind.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) + if (node is IdentifierExpression && node.Slot?.Kind == Slots.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) { var declaringType = member.DeclaringType; if (declaringType != null && declaringType.Kind == TypeKind.Delegate)