Browse Source

Replace the SlotKind enum with object-identity Slots kinds

The slot kind is now the canonical typed CSharpSlotInfo<T> in Slots, matched by
object identity (the IL SlotInfo model), instead of a parallel SlotKind enum.
CSharpSlotInfo.Kind points at the canonical shared slot; a Slots constant is its
own kind (null Kind, never read -- only per-node slots are asked for their kind),
so there is no self-reference. Child access (GetChild/GetChildren/SetChild,
GetCollectionByKind, AddChild/InsertChild) and the polymorphic
node.Slot.Kind == Slots.X comparisons key on the canonical reference; the
generated SlotKind enum is removed. No behavior change.

Assisted-by: Claude:claude-opus-4-8:Claude Code
pull/3807/head
Siegfried Pammer 2 weeks ago committed by Siegfried Pammer
parent
commit
ed61b8a14e
  1. 47
      ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs
  2. 6
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  3. 32
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  4. 8
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs
  5. 2
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  6. 64
      ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs
  7. 4
      ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs
  8. 2
      ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs
  9. 13
      ICSharpCode.Decompiler/CSharp/Syntax/CSharpSlotInfo.cs
  10. 2
      ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs
  11. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs
  12. 4
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs
  13. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs
  14. 4
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs
  15. 2
      ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs
  16. 4
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  17. 14
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  18. 2
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs
  19. 4
      ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs
  20. 2
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  21. 2
      ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
  22. 2
      ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs
  23. 12
      ICSharpCode.Decompiler/Output/TextTokenWriter.cs
  24. 8
      ILSpy/Languages/CSharpHighlightingTokenWriter.cs

47
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); 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 // 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). // 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); 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) if (slotAttr != null)
{ {
slots ??= new(); 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 // 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!; string kindName = (string)slotAttr.ConstructorArguments[0].Value!;
// A [Slot] on a string property is a name: a convenience string accessor over a backing // 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 - // Identifier token slot. Child slots are AstNode-typed, so the property type disambiguates -
@ -354,7 +354,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
if (isCollection) if (isCollection)
{ {
builder.AppendLine($"\tAstNodeCollection<{elementType}>? {field};"); 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 else
{ {
@ -611,7 +611,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
// One typed CSharpSlotInfo<T> static per slot; node.Slot compares against these by object // One typed CSharpSlotInfo<T> static per slot; node.Slot compares against these by object
// identity, and the typed child accessors infer the child type from the slot. // identity, and the typed child accessors infer the child type from the slot.
foreach (var s in slots) 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();
builder.AppendLine("\tinternal override CSharpSlotInfo GetChildSlotInfo(int index)"); builder.AppendLine("\tinternal override CSharpSlotInfo GetChildSlotInfo(int index)");
@ -622,10 +622,10 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
if (slots.Any(s => s.IsCollection)) 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{"); builder.AppendLine("\t{");
foreach (var s in slots.Where(s => s.IsCollection)) 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\treturn base.GetCollectionByKind(kind);");
builder.AppendLine("\t}"); builder.AppendLine("\t}");
builder.AppendLine(); builder.AppendLine();
@ -776,9 +776,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
context.RegisterSourceOutput(visitorMembers, WriteSlotKinds); context.RegisterSourceOutput(visitorMembers, WriteSlotKinds);
} }
// Emits the SlotKind enum: one value per distinct slot kind across all nodes. A node's CSharpSlotInfo // Emits the Slots holder: one typed CSharpSlotInfo<T> constant per distinct slot kind across all
// carries its kind, and consumers compare node.Slot.Kind == SlotKind.X -- shared across node types, so // nodes. Each is its own canonical kind (constructed with a null Kind); a node's per-node slot points
// it replaces the old polymorphic node.Role == Roles.X comparisons. // 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<AstNodeAdditions> source) void WriteSlotKinds(SourceProductionContext context, ImmutableArray<AstNodeAdditions> source)
{ {
// Per kind: the element types seen (to choose a typed slot's T) and whether it is a collection. // 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();
builder.AppendLine("namespace ICSharpCode.Decompiler.CSharp.Syntax;"); builder.AppendLine("namespace ICSharpCode.Decompiler.CSharp.Syntax;");
builder.AppendLine(); builder.AppendLine();
builder.AppendLine("/// <summary>Identifies the kind of an AST child slot, shared across node types.</summary>"); // Each constant is its own canonical kind (null Kind): node.GetChild(Slots.X) infers the child
builder.AppendLine("public enum SlotKind"); // type, and node.Slot.Kind == Slots.X identifies a position polymorphically. A kind reused with
builder.AppendLine("{"); // several child types across node types widens to AstNode (only its concrete per-node slots carry
builder.AppendLine("\tNone,"); // the precise type); such kinds are only reached through those per-node slots. For the same reason
foreach (var k in kindTypes.Keys) // the shared kind's IsCollection flag and its hard-coded isOptional: false are not authoritative
builder.AppendLine($"\t{k},"); // when a kind is a collection on one node and a single/optional child on another: such dual-use
builder.AppendLine("}"); // kinds carry IsCollection false (no arity claim), and the precise per-position flags live on the
builder.AppendLine(); // per-node slots, which is where consumers read them; the shared constant carries identity, not
// Typed kind constants for polymorphic access: node.GetChild(Slots.X) infers the child type. A // those flags.
// kind reused with several child types across node types widens to AstNode (only its concrete builder.AppendLine("/// <summary>The shared slot kinds, one per distinct child position across the AST node types.</summary>");
// per-node slots carry the precise type); such kinds are only reached through those per-node slots.
builder.AppendLine("/// <summary>Typed slot kinds for polymorphic child access; the child type is inferred from the slot.</summary>");
builder.AppendLine("public static class Slots"); builder.AppendLine("public static class Slots");
builder.AppendLine("{"); builder.AppendLine("{");
foreach (var kv in kindTypes) foreach (var kv in kindTypes)
{ {
string type = kv.Value.Count == 1 ? kv.Value.Min : "AstNode"; string type = kv.Value.Count == 1 ? kv.Value.Min : "AstNode";
string isColl = kindIsCollection[kv.Key] == true ? "true" : "false"; 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("}"); 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));
} }
} }

6
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -812,7 +812,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
currentNamespace = typeDef.Namespace; currentNamespace = typeDef.Namespace;
var typeDecl = DoDecompile(typeDef, decompileRun, decompilationContext.WithCurrentTypeDefinition(typeDef)); 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(); var commentStatement = new EmptyStatement();
commentStatement.AddTrailingTrivia(new Comment("Invalid MethodBodyBlock: " + ex.Message)); commentStatement.AddTrailingTrivia(new Comment("Invalid MethodBodyBlock: " + ex.Message));
body.Statements.Add(commentStatement); body.Statements.Add(commentStatement);
entityDecl.AddChild(body, SlotKind.Body); entityDecl.AddChild(body, Slots.Body);
return; return;
} }
var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: CancellationToken); var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: CancellationToken);
@ -2121,7 +2121,7 @@ namespace ICSharpCode.Decompiler.CSharp
body.Statements.Add(warningStatement); body.Statements.Add(warningStatement);
} }
entityDecl.AddChild(body, SlotKind.Body); entityDecl.AddChild(body, Slots.Body);
} }
CleanUpMethodDeclaration(entityDecl, body, function, localSettings.DecompileMemberBodies); CleanUpMethodDeclaration(entityDecl, body, function, localSettings.DecompileMemberBodies);

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

@ -213,7 +213,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
protected virtual void Semicolon() protected virtual void Semicolon()
{ {
// get the slot of the current node // get the slot of the current node
SlotKind? kind = containerStack.Peek().Slot?.Kind; CSharpSlotInfo? kind = containerStack.Peek().Slot?.Kind;
if (!SkipToken()) if (!SkipToken())
{ {
WriteToken(Roles.Semicolon); WriteToken(Roles.Semicolon);
@ -225,16 +225,16 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
bool SkipToken() bool SkipToken()
{ {
return kind == SlotKind.Initializer return kind == Slots.Initializer
|| kind == SlotKind.Iterator || kind == Slots.Iterator
|| kind == SlotKind.ResourceAcquisition; || kind == Slots.ResourceAcquisition;
} }
bool SkipNewLine() bool SkipNewLine()
{ {
if (containerStack.Peek() is not Accessor accessor) if (containerStack.Peek() is not Accessor accessor)
return false; return false;
if (!(kind == SlotKind.Getter || kind == SlotKind.Setter)) if (!(kind == Slots.Getter || kind == Slots.Setter))
return false; return false;
bool isAutoProperty = accessor.Body is null bool isAutoProperty = accessor.Body is null
&& !accessor.Attributes.Any() && !accessor.Attributes.Any()
@ -654,11 +654,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} }
if (node.Parent is ObjectCreateExpression) if (node.Parent is ObjectCreateExpression)
{ {
return node.Slot?.Kind == SlotKind.Initializer; return node.Slot?.Kind == Slots.Initializer;
} }
if (node.Parent is NamedExpression) if (node.Parent is NamedExpression)
{ {
return node.Slot?.Kind == SlotKind.Expression; return node.Slot?.Kind == Slots.Expression;
} }
return false; return false;
} }
@ -1316,7 +1316,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitQueryExpression(QueryExpression queryExpression) public virtual void VisitQueryExpression(QueryExpression queryExpression)
{ {
StartNode(queryExpression); StartNode(queryExpression);
if (queryExpression.Slot?.Kind != SlotKind.PrecedingQuery) if (queryExpression.Slot?.Kind != Slots.PrecedingQuery)
writer.Indent(); writer.Indent();
bool first = true; bool first = true;
foreach (var clause in queryExpression.Clauses) foreach (var clause in queryExpression.Clauses)
@ -1334,7 +1334,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} }
clause.AcceptVisitor(this); clause.AcceptVisitor(this);
} }
if (queryExpression.Slot?.Kind != SlotKind.PrecedingQuery) if (queryExpression.Slot?.Kind != Slots.PrecedingQuery)
writer.Unindent(); writer.Unindent();
EndNode(queryExpression); EndNode(queryExpression);
} }
@ -2265,12 +2265,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteAttributes(accessor.Attributes); WriteAttributes(accessor.Attributes);
WriteModifiers(accessor.Modifiers); WriteModifiers(accessor.Modifiers);
BraceStyle style = policy.StatementBraceStyle; BraceStyle style = policy.StatementBraceStyle;
if (accessor.Slot?.Kind == SlotKind.Getter) if (accessor.Slot?.Kind == Slots.Getter)
{ {
WriteKeyword("get"); WriteKeyword("get");
style = policy.PropertyGetBraceStyle; style = policy.PropertyGetBraceStyle;
} }
else if (accessor.Slot?.Kind == SlotKind.Setter) else if (accessor.Slot?.Kind == Slots.Setter)
{ {
if (accessor.Kind == AccessorKind.Init) if (accessor.Kind == AccessorKind.Init)
{ {
@ -2282,12 +2282,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} }
style = policy.PropertySetBraceStyle; style = policy.PropertySetBraceStyle;
} }
else if (accessor.Slot?.Kind == SlotKind.AddAccessor) else if (accessor.Slot?.Kind == Slots.AddAccessor)
{ {
WriteKeyword("add"); WriteKeyword("add");
style = policy.EventAddBraceStyle; style = policy.EventAddBraceStyle;
} }
else if (accessor.Slot?.Kind == SlotKind.RemoveAccessor) else if (accessor.Slot?.Kind == Slots.RemoveAccessor)
{ {
WriteKeyword("remove"); WriteKeyword("remove");
style = policy.EventRemoveBraceStyle; style = policy.EventRemoveBraceStyle;
@ -2432,7 +2432,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// output add/remove in their original order // output add/remove in their original order
for (AstNode? node = customEventDeclaration.FirstChild; node != null; node = node.NextSibling) 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); node.AcceptVisitor(this);
} }
@ -2509,7 +2509,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// output get/set in their original order // output get/set in their original order
for (AstNode? node = indexerDeclaration.FirstChild; node != null; node = node.NextSibling) 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); node.AcceptVisitor(this);
} }
@ -2671,7 +2671,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// output get/set in their original order // output get/set in their original order
for (AstNode? node = propertyDeclaration.FirstChild; node != null; node = node.NextSibling) 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); node.AcceptVisitor(this);
} }

8
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 // 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 // detached clone of a type's name token that a renamed constructor/destructor prints
// (CSharpOutputVisitor.VisitConstructorDeclaration). It cannot be re-attached by kind, // (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 // and the real name token is already a child, so leave it out rather than route a
// SlotKind.None into the throwing child setter. // missing kind into the throwing child setter.
if (child.Slot is not { } slot) if (child.Slot is not { } slot)
continue; continue;
// Slot is derived from the child's index in its parent, so it must be read before // 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). // Remove() detaches the child (which would otherwise leave it without a slot).
SlotKind kind = slot.Kind; CSharpSlotInfo kind = slot.Kind!;
child.Remove(); child.Remove();
node.AddChildUnsafe(child, kind); node.AddChildUnsafe(child, kind);
} }

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

@ -521,7 +521,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
void HandleLambdaOrQuery(Expression expr) void HandleLambdaOrQuery(Expression expr)
{ {
if (expr.Slot?.Kind == SlotKind.Left) if (expr.Slot?.Kind == Slots.Left)
Parenthesize(expr); Parenthesize(expr);
if (expr.Parent is IsExpression || expr.Parent is AsExpression) if (expr.Parent is IsExpression || expr.Parent is AsExpression)
Parenthesize(expr); Parenthesize(expr);

64
ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs

@ -292,15 +292,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
/// <summary> /// <summary>
/// Gets the first child with the specified role, or null if this node has no such child (the /// Gets the first child occupying <paramref name="slot"/>, or null if the slot is empty (or the
/// slot is empty or the node declares no slot of that kind). /// node declares no such slot). <paramref name="slot"/> is a canonical <c>Slots</c> kind; the
/// result type is inferred from it.
/// </summary> /// </summary>
public T? GetChildByRole<T>(SlotKind kind) where T : AstNode public T? GetChild<T>(CSharpSlotInfo<T> slot) where T : AstNode
{ {
int count = GetChildCount(); int count = GetChildCount();
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
if (GetChildSlotInfo(i).Kind == kind) if (GetChildSlotInfo(i).Kind == slot)
{ {
return GetChild(i) as T; return GetChild(i) as T;
} }
@ -308,12 +309,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return null; return null;
} }
/// <summary>
/// Gets the first child occupying <paramref name="slot"/>, or null if the slot is empty (or the
/// node declares no such slot). The result type is inferred from the typed slot.
/// </summary>
public T? GetChild<T>(CSharpSlotInfo<T> slot) where T : AstNode => GetChildByRole<T>(slot.Kind);
public T? GetParent<T>() where T : AstNode public T? GetParent<T>() where T : AstNode
{ {
return Ancestors.OfType<T>().FirstOrDefault(); return Ancestors.OfType<T>().FirstOrDefault();
@ -324,25 +319,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return pred != null ? Ancestors.FirstOrDefault(pred) : Ancestors.FirstOrDefault(); return pred != null ? Ancestors.FirstOrDefault(pred) : Ancestors.FirstOrDefault();
} }
public AstNodeCollection<T> GetChildrenByRole<T>(SlotKind kind) where T : AstNode /// <summary>Gets the collection occupying <paramref name="slot"/>; the element type is inferred from the slot.</summary>
public AstNodeCollection<T> GetChildren<T>(CSharpSlotInfo<T> 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 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; // 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. // writes go through AddChild/SetChild, which reject a missing slot.
return collection != null ? (AstNodeCollection<T>)collection : new AstNodeCollection<T>(this, kind); return collection != null ? (AstNodeCollection<T>)collection : new AstNodeCollection<T>(this, slot);
}
/// <summary>Gets the collection occupying <paramref name="slot"/>; the element type is inferred from the slot.</summary>
public AstNodeCollection<T> GetChildren<T>(CSharpSlotInfo<T> slot) where T : AstNode => GetChildrenByRole<T>(slot.Kind);
protected void SetChildByRole<T>(SlotKind kind, T? newChild) where T : AstNode
{
SetChildByRoleUntyped(kind, newChild);
} }
/// <summary>Sets the single child occupying <paramref name="slot"/>; the child type is inferred from the slot.</summary> /// <summary>Sets the single child occupying <paramref name="slot"/>; the child type is inferred from the slot.</summary>
protected void SetChild<T>(CSharpSlotInfo<T> slot, T? newChild) where T : AstNode => SetChildByRole(slot.Kind, newChild); protected void SetChild<T>(CSharpSlotInfo<T> slot, T? newChild) where T : AstNode => SetChildByKindUntyped(slot, newChild);
#region Slot storage contract #region Slot storage contract
// Each concrete node's slots form a flattened child-index space, in source-declaration order. // 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. // 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. // 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 // 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. // this node's child references. Overridden by the generator for nodes that have slots.
@ -489,7 +477,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
#endregion #endregion
public void AddChild<T>(T child, SlotKind kind) where T : AstNode public void AddChild<T>(T child, CSharpSlotInfo kind) where T : AstNode
{ {
if (child == null) if (child == null)
return; return;
@ -500,19 +488,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// Adds a child into the slot matching <paramref name="kind"/> (appending to a collection slot, /// Adds a child into the slot matching <paramref name="kind"/> (appending to a collection slot,
/// or filling a single slot). /// or filling a single slot).
/// </summary> /// </summary>
internal void AddChildUnsafe(AstNode child, SlotKind kind) internal void AddChildUnsafe(AstNode child, CSharpSlotInfo kind)
{ {
AstNodeCollection? collection = GetCollectionByKind(kind); AstNodeCollection? collection = GetCollectionByKind(kind);
if (collection != null) if (collection != null)
collection.AddNode(child); collection.AddNode(child);
else else
SetChildByRoleUntyped(kind, child); SetChildByKindUntyped(kind, child);
} }
// Sets the single slot matching the kind (used by the non-generic mutation API). Unlike // 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. // 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(); int count = GetChildCount();
for (int i = 0; i < count; i++) 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}'."); throw new InvalidOperationException($"{GetType().Name} has no slot of kind '{kind}'.");
} }
public void InsertChildBefore<T>(AstNode? nextSibling, T child, SlotKind kind) where T : AstNode public void InsertChildBefore<T>(AstNode? nextSibling, T child, CSharpSlotInfo kind) where T : AstNode
{ {
if (child == null) if (child == null)
return; return;
@ -534,19 +522,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (collection != null) if (collection != null)
collection.InsertNodeBefore(nextSibling, child); collection.InsertNodeBefore(nextSibling, child);
else 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); AstNodeCollection? collection = GetCollectionByKind(kind);
if (collection != null) if (collection != null)
collection.InsertNodeBefore(nextSibling, child); collection.InsertNodeBefore(nextSibling, child);
else else
SetChildByRoleUntyped(kind, child); SetChildByKindUntyped(kind, child);
} }
public void InsertChildAfter<T>(AstNode? prevSibling, T child, SlotKind kind) where T : AstNode public void InsertChildAfter<T>(AstNode? prevSibling, T child, CSharpSlotInfo kind) where T : AstNode
{ {
if (child == null) if (child == null)
return; return;
@ -554,7 +542,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (collection != null) if (collection != null)
collection.InsertNodeAfter(prevSibling, child); collection.InsertNodeAfter(prevSibling, child);
else else
SetChildByRoleUntyped(kind, child); SetChildByKindUntyped(kind, child);
} }
/// <summary> /// <summary>
@ -565,7 +553,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (parent == null) if (parent == null)
return; return;
parent.EnsureChildIndices(); parent.EnsureChildIndices();
SlotKind kind = parent.GetChildSlotInfo(childIndex).Kind; CSharpSlotInfo kind = parent.GetChildSlotInfo(childIndex).Kind!;
AstNodeCollection? collection = parent.GetCollectionByKind(kind); AstNodeCollection? collection = parent.GetCollectionByKind(kind);
if (collection != null) if (collection != null)
collection.RemoveNode(this); collection.RemoveNode(this);
@ -625,12 +613,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
AstNode oldParent = parent; AstNode oldParent = parent;
AstNode? oldSuccessor = NextSibling; AstNode? oldSuccessor = NextSibling;
CSharpSlotInfo? oldSlot = this.Slot; CSharpSlotInfo? oldSlot = this.Slot;
SlotKind oldKind = oldSlot?.Kind ?? SlotKind.None; CSharpSlotInfo? oldKind = oldSlot?.Kind;
Remove(); Remove();
AstNode? replacement = replaceFunction(this); AstNode? replacement = replaceFunction(this);
if (oldSuccessor != null && oldSuccessor.parent != oldParent) if (oldSuccessor != null && oldSuccessor.parent != oldParent)
throw new InvalidOperationException("replace function changed nextSibling of node being replaced?"); throw new InvalidOperationException("replace function changed nextSibling of node being replaced?");
if (replacement != null) if (replacement != null && oldKind != null)
{ {
if (replacement.parent != null) if (replacement.parent != null)
throw new InvalidOperationException("replace function must return the root of a tree"); throw new InvalidOperationException("replace function must return the root of a tree");

4
ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs

@ -72,10 +72,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
where T : AstNode where T : AstNode
{ {
readonly AstNode parent; readonly AstNode parent;
readonly SlotKind kind; readonly CSharpSlotInfo kind;
readonly List<T> list = new List<T>(); readonly List<T> list = new List<T>();
public AstNodeCollection(AstNode parent, SlotKind kind) public AstNodeCollection(AstNode parent, CSharpSlotInfo kind)
{ {
this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); this.parent = parent ?? throw new ArgumentNullException(nameof(parent));
this.kind = kind; this.kind = kind;

2
ICSharpCode.Decompiler/CSharp/Syntax/AstType.cs

@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
return NameLookupMode.TypeInUsingDeclaration; 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. // 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. // Do not use it for a constraint on a method.

13
ICSharpCode.Decompiler/CSharp/Syntax/CSharpSlotInfo.cs

@ -41,10 +41,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public bool IsCollection { get; } public bool IsCollection { get; }
/// <summary> /// <summary>
/// The slot's kind, shared across node types (so <c>node.Slot.Kind == SlotKind.X</c> identifies a /// The slot's kind: the canonical shared slot for this child position, identifying it across node
/// node's position the way <c>node.Role == Roles.X</c> did, including polymorphically). /// types by object identity (so <c>node.Slot.Kind == Slots.X</c> identifies a node's position the
/// way <c>node.Role == Roles.X</c> did, including polymorphically). A per-node slot points at its
/// shared <c>Slots</c> constant; a <c>Slots</c> constant is itself the kind, so its own
/// <see cref="Kind"/> is null (and is never read -- only per-node slots are asked for their kind).
/// </summary> /// </summary>
public SlotKind Kind { get; } public CSharpSlotInfo? Kind { get; }
/// <summary> /// <summary>
/// Whether the slot may be empty: a single slot whose child is nullable, or any collection slot /// 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
/// </summary> /// </summary>
public bool IsOptional { get; } 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; Name = name;
ChildType = childType; ChildType = childType;
@ -73,7 +76,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary> /// </summary>
public sealed class CSharpSlotInfo<T> : CSharpSlotInfo where T : AstNode public sealed class CSharpSlotInfo<T> : 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) : base(name, typeof(T), isCollection, kind, isOptional)
{ {
} }

2
ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs

@ -111,7 +111,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public override AstType MakeArrayType(int dimensions) 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; return this;
} }

2
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs

@ -118,7 +118,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public void AddMember(AstNode child) public void AddMember(AstNode child)
{ {
AddChild(child, SlotKind.Member); AddChild(child, Slots.Member);
} }
} }
}; };

4
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingAliasDeclaration.cs

@ -44,8 +44,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public UsingAliasDeclaration(string alias, string nameSpace) public UsingAliasDeclaration(string alias, string nameSpace)
{ {
AddChild(Identifier.Create(alias), SlotKind.Alias); AddChild(Identifier.Create(alias), Slots.Alias);
AddChild(new SimpleType(nameSpace), SlotKind.Import); AddChild(new SimpleType(nameSpace), Slots.Import);
} }
} }
} }

2
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/UsingDeclaration.cs

@ -81,7 +81,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public UsingDeclaration(string nameSpace) public UsingDeclaration(string nameSpace)
{ {
AddChild(AstType.Create(nameSpace), SlotKind.Import); AddChild(AstType.Create(nameSpace), Slots.Import);
} }
} }
} }

4
ICSharpCode.Decompiler/CSharp/Syntax/Statements/BlockStatement.cs

@ -41,12 +41,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public void Add(Statement statement) public void Add(Statement statement)
{ {
AddChild(statement, SlotKind.Statement); AddChild(statement, Slots.Statement);
} }
public void Add(Expression expression) public void Add(Expression expression)
{ {
AddChild(new ExpressionStatement(expression), SlotKind.Statement); AddChild(new ExpressionStatement(expression), Slots.Statement);
} }
IEnumerator<Statement> IEnumerable<Statement>.GetEnumerator() IEnumerator<Statement> IEnumerable<Statement>.GetEnumerator()

2
ICSharpCode.Decompiler/CSharp/Syntax/SyntaxTree.cs

@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
foreach (var child in curNode.Children) foreach (var child in curNode.Children)
{ {
if (!(child is Statement || child is Expression) && 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); nodeStack.Push(child);
} }
} }

4
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -636,11 +636,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
if (ConvertUnboundTypeArguments && typeArguments[i].Kind == TypeKind.UnboundTypeArgument) if (ConvertUnboundTypeArguments && typeArguments[i].Kind == TypeKind.UnboundTypeArgument)
{ {
result.AddChild(MakeSimpleType(typeParameters[i].Name), SlotKind.TypeArgument); result.AddChild(MakeSimpleType(typeParameters[i].Name), Slots.TypeArgument);
} }
else else
{ {
result.AddChild(ConvertType(typeArguments[i]), SlotKind.TypeArgument); result.AddChild(ConvertType(typeArguments[i]), Slots.TypeArgument);
} }
} }
} }

14
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -587,7 +587,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (v.Type.IsByRefLike) if (v.Type.IsByRefLike)
return true; // by-ref-like variables always must be initialized at their declaration. 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 true; // for-statement initializers always should combine declaration and initialization.
return !context.Settings.SeparateLocalVariableDeclarations; return !context.Settings.SeparateLocalVariableDeclarations;
@ -699,7 +699,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
v.InsertionPoint = v.InsertionPoint.Up(); 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. // The insertion point is a statement within a block, so it always has a parent.
AstNode insertionNode = v.InsertionPoint.nextNode; AstNode insertionNode = v.InsertionPoint.nextNode;
AstNode insertionParent = insertionNode.Parent AstNode insertionParent = insertionNode.Parent
@ -725,14 +725,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
} }
}, },
SlotKind.Statement); Slots.Statement);
} }
else else
{ {
insertionParent.InsertChildBefore( insertionParent.InsertChildBefore(
v.InsertionPoint.nextNode, v.InsertionPoint.nextNode,
vds, vds,
SlotKind.Statement); Slots.Statement);
insertionParent.InsertChildBefore( insertionParent.InsertChildBefore(
v.InsertionPoint.nextNode, v.InsertionPoint.nextNode,
new ExpressionStatement { new ExpressionStatement {
@ -750,7 +750,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
} }
}, },
SlotKind.Statement); Slots.Statement);
} }
} }
else else
@ -758,7 +758,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
insertionParent.InsertChildBefore( insertionParent.InsertChildBefore(
v.InsertionPoint.nextNode, v.InsertionPoint.nextNode,
vds, vds,
SlotKind.Statement); Slots.Statement);
} }
} }
} }
@ -780,7 +780,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return false; return false;
for (AstNode? node = v.FirstUse; node != null; node = node.Parent) for (AstNode? node = v.FirstUse; node != null; node = node.Parent)
{ {
if (node.Slot?.Kind == SlotKind.EmbeddedStatement) if (node.Slot?.Kind == Slots.EmbeddedStatement)
{ {
return false; return false;
} }

2
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs

@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{ {
resolvedNamespaces.Add(resolvedNamespace); resolvedNamespaces.Add(resolvedNamespace);
} }
rootNode.InsertChildAfter(insertionPoint, new UsingDeclaration { Import = nsType }, SlotKind.Member); rootNode.InsertChildAfter(insertionPoint, new UsingDeclaration { Import = nsType }, Slots.Member);
} }
} }

4
ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs

@ -117,7 +117,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
bool IsElseIf(Statement statement, Statement parent) 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) static void InsertBlock(Statement statement)
@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
switch (statement) switch (statement)
{ {
case IfElseStatement ies: 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 VariableDeclarationStatement vds:
case WhileStatement ws: case WhileStatement ws:
case DoWhileStatement dws: case DoWhileStatement dws:

2
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -192,7 +192,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (next is ForStatement forStatement && ForStatementUsesVariable(forStatement, variable)) if (next is ForStatement forStatement && ForStatementUsesVariable(forStatement, variable))
{ {
node.Remove(); node.Remove();
next.InsertChildAfter(null, node, SlotKind.Initializer); next.InsertChildAfter(null, node, Slots.Initializer);
return (ForStatement)next; return (ForStatement)next;
} }
Match m3 = forPattern.Match(next); Match m3 = forPattern.Match(next);

2
ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs

@ -236,7 +236,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
); );
return; 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()); invocationExpression.ReplaceWith(arguments[0].UnwrapInDirectionExpression());
return; return;

2
ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs

@ -619,7 +619,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
foreach (var param in PrimaryConstructorDecl.Parameters) foreach (var param in PrimaryConstructorDecl.Parameters)
{ {
param.Remove(); param.Remove();
this.TypeDeclaration.InsertChildAfter(insertionPoint, param, SlotKind.Parameter); this.TypeDeclaration.InsertChildAfter(insertionPoint, param, Slots.Parameter);
insertionPoint = param; insertionPoint = param;
} }
} }

12
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -112,18 +112,18 @@ namespace ICSharpCode.Decompiler
{ {
AstNode node = nodeStack.Peek(); AstNode node = nodeStack.Peek();
var symbol = node.GetSymbol(); 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(); 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(); var ctorSymbol = node.Parent.GetSymbol();
if (ctorSymbol != null) if (ctorSymbol != null)
symbol = ctorSymbol; 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; var declaringType = member.DeclaringType;
if (declaringType != null && declaringType.Kind == TypeKind.Delegate) if (declaringType != null && declaringType.Kind == TypeKind.Delegate)
@ -161,7 +161,7 @@ namespace ICSharpCode.Decompiler
return method + gotoStatement.Label; 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(); var symbol = node.Parent.GetSymbol();
if (symbol is LocalFunctionMethod) if (symbol is LocalFunctionMethod)
@ -184,7 +184,7 @@ namespace ICSharpCode.Decompiler
return variable; 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<ILVariableResolveResult>()?.Variable; var variable = id.Annotation<ILVariableResolveResult>()?.Variable;
if (variable != null) if (variable != null)
@ -428,7 +428,7 @@ namespace ICSharpCode.Decompiler
case "object": case "object":
var node = nodeStack.Peek(); var node = nodeStack.Peek();
ISymbol symbol; 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(); symbol = node.Parent.GetSymbol();
} }

8
ILSpy/Languages/CSharpHighlightingTokenWriter.cs

@ -347,7 +347,7 @@ namespace ICSharpCode.ILSpy.Languages
{ {
if (identifier.Name == "value" if (identifier.Name == "value"
&& identifier.Ancestors.OfType<Accessor>().FirstOrDefault() is { } accessor && identifier.Ancestors.OfType<Accessor>().FirstOrDefault() is { } accessor
&& accessor.Slot?.Kind != SlotKind.Getter) && accessor.Slot?.Kind != Slots.Getter)
{ {
color = valueKeywordColor; color = valueKeywordColor;
} }
@ -473,17 +473,17 @@ namespace ICSharpCode.ILSpy.Languages
AstNode node = nodeStack.Peek(); AstNode node = nodeStack.Peek();
var symbol = node.GetSymbol(); 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(); 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(); var ctorSymbol = node.Parent.GetSymbol();
if (ctorSymbol != null) if (ctorSymbol != null)
symbol = ctorSymbol; 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; var declaringType = member.DeclaringType;
if (declaringType != null && declaringType.Kind == TypeKind.Delegate) if (declaringType != null && declaringType.Kind == TypeKind.Delegate)

Loading…
Cancel
Save