diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs index 4a98052c5..8f61e62e5 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstNode.cs @@ -539,10 +539,20 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax throw new ArgumentException("Node is already used in another tree.", nameof(value)); } } - field?.ClearParentAndIndex(); + T? oldField = field; + int oldChildIndex = oldField?.childIndex ?? -1; + oldField?.ClearParentAndIndex(); field = newValue; newValue?.SetParent(this); - InvalidateChildIndices(); + // A single slot always occupies the same flattened index, so replacing its child in place + // changes no index: carry the slot's index to the new child instead of invalidating (and + // rebuilding) every child's index. Setting or clearing the slot still invalidates, since the + // new child's index is then not known here. When the indices are already stale the carried + // value is corrected by the next EnsureChildIndices anyway. + if (oldField != null && newValue != null) + newValue.childIndex = oldChildIndex; + else + InvalidateChildIndices(); } internal void SetParent(AstNode newParent) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs b/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs index 3b1ce699b..6ba98154b 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/AstNodeCollection.cs @@ -98,10 +98,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax if (old == value) return; ValidateNewChild(value); + int oldChildIndex = old.childIndex; old.ClearParentAndIndex(); list[index] = value; value.SetParent(parent); - parent.InvalidateChildIndices(); + // Replacing an element in place keeps its flattened position, so no index changes: carry + // the old element's index to the new one rather than invalidating (and rebuilding) the + // parent's whole index set. A stale carried value is corrected by the next renumber. + value.childIndex = oldChildIndex; } }