Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid enumeration allocations in formatting #73475

Merged
merged 6 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,18 @@ public static SyntaxNode GetCommonRoot(this SyntaxNode node1, SyntaxNode node2)
{
Contract.ThrowIfTrue(node1.RawKind == 0 || node2.RawKind == 0);

// find common starting node from two nodes.
// as long as two nodes belong to same tree, there must be at least one common root (Ex, compilation unit)
var ancestors = node1.GetAncestorsOrThis<SyntaxNode>();
var set = new HashSet<SyntaxNode>(node2.GetAncestorsOrThis<SyntaxNode>());
// find common starting node from two nodes. as long as two nodes belong to same tree, there must be at least
// one common root (Ex, compilation unit)
using var _ = PooledHashSet<SyntaxNode>.GetInstance(out var set);
set.AddRange(node2.GetAncestorsOrThis<SyntaxNode>());

return ancestors.First(set.Contains);
foreach (var ancestor in node1.AncestorsAndSelf())
{
if (set.Contains(ancestor))
return ancestor;
}

throw ExceptionUtilities.Unreachable();
}

public static int Width(this SyntaxNode node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

Expand Down Expand Up @@ -63,50 +64,47 @@ public int CompareTo(TokenData other)
Contract.ThrowIfFalse(this.TokenStream == other.TokenStream);

if (this.IndexInStream >= 0 && other.IndexInStream >= 0)
{
return this.IndexInStream - other.IndexInStream;
}

if (this.Token == other.Token)
return 0;

var start = this.Token.SpanStart - other.Token.SpanStart;
if (start != 0)
{
return start;
}

var end = this.Token.Span.End - other.Token.Span.End;
if (end != 0)
{
return end;
}

// this is expansive check. but there is no other way to check.
// We have two different tokens, which are at the same location. This can happen with things like empty/missing
// tokens. In order to give a strict ordering, we need to walk up the tree to find the first common ancestor
// and see which token we hit first in that ancestor.
var commonRoot = this.Token.GetCommonRoot(other.Token);
RoslynDebug.Assert(commonRoot != null);
Contract.ThrowIfNull(commonRoot);

var tokens = commonRoot.DescendantTokens();
// Now, figure out the ancestor of each token parented by the common root.
var thisTokenAncestor = GetAncestorUnderRoot(this.Token, commonRoot);
var otherTokenAncestor = GetAncestorUnderRoot(other.Token, commonRoot);

var index1 = Index(tokens, this.Token);
var index2 = Index(tokens, other.Token);
Contract.ThrowIfFalse(index1 >= 0 && index2 >= 0);

return index1 - index2;
}
foreach (var child in commonRoot.ChildNodesAndTokens())
{
if (child == thisTokenAncestor)
return -1;
else if (child == otherTokenAncestor)
return 1;
}

private static int Index(IEnumerable<SyntaxToken> tokens, SyntaxToken token)
{
var index = 0;
throw ExceptionUtilities.Unreachable();

foreach (var current in tokens)
static SyntaxNodeOrToken GetAncestorUnderRoot(SyntaxNodeOrToken start, SyntaxNode root)
{
if (current == token)
{
return index;
}
var current = start;
while (current.Parent != root)
current = current.Parent;

index++;
return current;
}

return -1;
}

public static bool operator <(TokenData left, TokenData right)
Expand Down
Loading