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 @@ -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 @@ -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 @@ -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 @@ -611,7 +611,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
// 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.
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 @@ -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 @@ -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<T> 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<AstNodeAdditions> 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 @@ -809,29 +810,27 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
builder.AppendLine();
builder.AppendLine("namespace ICSharpCode.Decompiler.CSharp.Syntax;");
builder.AppendLine();
builder.AppendLine("/// <summary>Identifies the kind of an AST child slot, shared across node types.</summary>");
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("/// <summary>Typed slot kinds for polymorphic child access; the child type is inferred from the slot.</summary>");
// 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("/// <summary>The shared slot kinds, one per distinct child position across the AST node types.</summary>");
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));
}
}

6
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -812,7 +812,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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 @@ -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 @@ -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);

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

@ -213,7 +213,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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);
}

8
ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs

@ -96,13 +96,13 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -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);
}

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

@ -521,7 +521,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -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);

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

@ -292,15 +292,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -292,15 +292,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
/// <summary>
/// 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 <paramref name="slot"/>, or null if the slot is empty (or the
/// node declares no such slot). <paramref name="slot"/> is a canonical <c>Slots</c> kind; the
/// result type is inferred from it.
/// </summary>
public T? GetChildByRole<T>(SlotKind kind) where T : AstNode
public T? GetChild<T>(CSharpSlotInfo<T> 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 @@ -308,12 +309,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
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
{
return Ancestors.OfType<T>().FirstOrDefault();
@ -324,25 +319,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -324,25 +319,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
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 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<T>)collection : new AstNodeCollection<T>(this, kind);
}
/// <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);
// writes go through AddChild/SetChild, which reject a missing slot.
return collection != null ? (AstNodeCollection<T>)collection : new AstNodeCollection<T>(this, slot);
}
/// <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
// Each concrete node's slots form a flattened child-index space, in source-declaration order.
@ -375,7 +363,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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 @@ -489,7 +477,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
#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)
return;
@ -500,19 +488,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -500,19 +488,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// Adds a child into the slot matching <paramref name="kind"/> (appending to a collection slot,
/// or filling a single slot).
/// </summary>
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 @@ -526,7 +514,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
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)
return;
@ -534,19 +522,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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<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)
return;
@ -554,7 +542,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -554,7 +542,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (collection != null)
collection.InsertNodeAfter(prevSibling, child);
else
SetChildByRoleUntyped(kind, child);
SetChildByKindUntyped(kind, child);
}
/// <summary>
@ -565,7 +553,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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 @@ -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");

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

@ -72,10 +72,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -72,10 +72,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
where T : AstNode
{
readonly AstNode parent;
readonly SlotKind kind;
readonly CSharpSlotInfo kind;
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.kind = kind;

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

@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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.

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

@ -41,10 +41,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -41,10 +41,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public bool IsCollection { get; }
/// <summary>
/// The slot's kind, shared across node types (so <c>node.Slot.Kind == SlotKind.X</c> identifies a
/// node's position the way <c>node.Role == Roles.X</c> did, including polymorphically).
/// The slot's kind: the canonical shared slot for this child position, identifying it across node
/// 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>
public SlotKind Kind { get; }
public CSharpSlotInfo? Kind { get; }
/// <summary>
/// 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 @@ -53,7 +56,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary>
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 @@ -73,7 +76,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary>
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)
{
}

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

@ -111,7 +111,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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;
}

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

@ -118,7 +118,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -118,7 +118,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
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 @@ -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);
}
}
}

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

@ -81,7 +81,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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);
}
}
}

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

@ -41,12 +41,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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<Statement> IEnumerable<Statement>.GetEnumerator()

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

@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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);
}
}

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

@ -636,11 +636,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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);
}
}
}

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

@ -587,7 +587,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -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 @@ -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 @@ -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 @@ -750,7 +750,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
},
SlotKind.Statement);
Slots.Statement);
}
}
else
@ -758,7 +758,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -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 @@ -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;
}

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

@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -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);
}
}

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

@ -117,7 +117,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -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 @@ -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:

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

@ -192,7 +192,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -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);

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

@ -236,7 +236,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -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;

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

@ -619,7 +619,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -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;
}
}

12
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -112,18 +112,18 @@ namespace ICSharpCode.Decompiler @@ -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 @@ -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 @@ -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<ILVariableResolveResult>()?.Variable;
if (variable != null)
@ -428,7 +428,7 @@ namespace ICSharpCode.Decompiler @@ -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();
}

8
ILSpy/Languages/CSharpHighlightingTokenWriter.cs

@ -347,7 +347,7 @@ namespace ICSharpCode.ILSpy.Languages @@ -347,7 +347,7 @@ namespace ICSharpCode.ILSpy.Languages
{
if (identifier.Name == "value"
&& identifier.Ancestors.OfType<Accessor>().FirstOrDefault() is { } accessor
&& accessor.Slot?.Kind != SlotKind.Getter)
&& accessor.Slot?.Kind != Slots.Getter)
{
color = valueKeywordColor;
}
@ -473,17 +473,17 @@ namespace ICSharpCode.ILSpy.Languages @@ -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)

Loading…
Cancel
Save