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

Share more data in the SyntaxTreeIndex #54057

Merged
merged 15 commits into from
Jun 15, 2021

Conversation

CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Jun 12, 2021

I recommend reviewing this one commit at a time. The two main changes are:

  1. instead of descending into all nodes in a tree and seeing which can become DeclaredSymbolInfo, we do an explicit loop over the top level items in a file that produce these.
  2. as we descend into the top level items we compute and cache once strings that will be used by all children. this saves a lot of wasted duplicate string allocations.

@CyrusNajmabadi CyrusNajmabadi requested review from genlu and sharwell June 12, 2021 04:18
@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner June 12, 2021 04:18
}
}

private static void AddExtensionMethodInfo(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this moved into the IDeclaredSYmbolInfoService

{
private const string GenericTypeNameManglingString = "`";
private static readonly string[] s_aritySuffixesOneToNine = { "`1", "`2", "`3", "`4", "`5", "`6", "`7", "`8", "`9" };
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these moved to another (non-generic) helper type.

Dim item = (Await _aggregator.GetItemsAsync("M")).Single
VerifyNavigateToResultItem(item, "M", "[|M|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic, additionalInfo:=String.Format(FeaturesResources.in_0_project_1, "A", "Test"))
End Function)
End Function
Copy link
Member Author

@CyrusNajmabadi CyrusNajmabadi Jun 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abstract methods never worked in VB navigate to. who knew.

GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
containerDisplayName,
fullyQualifiedContainerName,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these values are computed once per container and passed in.

@@ -108,6 +111,9 @@ public static async ValueTask<SyntaxTreeIndex> GetRequiredIndexAsync(Document do
bool loadOnly,
CancellationToken cancellationToken)
{
if (!document.SupportsSyntaxTree)
return null;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding these checks simplified CreateIndexAsync later on.

bool TryGetAliasesFromUsingDirective(SyntaxNode node, out ImmutableArray<(string aliasName, string name)> aliases);

string GetRootNamespace(CompilationOptions compilationOptions);
void AddDeclaredSymbolInfos(Document document, SyntaxNode root, ArrayBuilder<DeclaredSymbolInfo> declaredSymbolInfos, Dictionary<string, ArrayBuilder<int>> extensionMethodInfo, CancellationToken cancellationToken);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all the above methods still exist, but are only used int he impl of AddDeclaredSymbolInfos, so they dont' need be exposed externally.

var stringTable = GetStringTable(project);
Contract.ThrowIfFalse(document.SupportsSyntaxTree);

var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pulled this await up, and put everything else in a synchronous method to prevent pooled collections from being used across awaits (As per @sharwell ).

@@ -123,69 +113,6 @@ internal sealed partial class SyntaxTreeIndex
containsImplicitObjectCreation = containsImplicitObjectCreation || syntaxFacts.IsImplicitObjectCreationExpression(node);
containsGlobalAttributes = containsGlobalAttributes || syntaxFacts.IsGlobalAttribute(node);
containsConversion = containsConversion || syntaxFacts.IsConversionExpression(node);

if (syntaxFacts.IsUsingAliasDirective(node) && infoFactory.TryGetAliasesFromUsingDirective(node, out var aliases))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aliases were used only inside the 'DeclaredSymbolInfo' computation. so all this moved into the other type.

protected abstract SyntaxList<TMemberDeclarationSyntax> GetChildren(TCompilationUnitSyntax node);
protected abstract SyntaxList<TMemberDeclarationSyntax> GetChildren(TNamespaceDeclarationSyntax node);
protected abstract SyntaxList<TMemberDeclarationSyntax> GetChildren(TTypeDeclarationSyntax node);
protected abstract IEnumerable<TMemberDeclarationSyntax> GetChildren(TEnumDeclarationSyntax node);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enums are a SeparatedSyntaxList in C#, and a SyntaxList in VB. so we need an IEnumerable here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i considered making this generic and adding TEnumMemberDeclarationListSyntax ... where TEnumMemberDeclarationListSyntax : IEnumerable<TMemberDeclarationSyntax>. However, that doesn't help as foreaching will still allocate the IEnumerator. We'd need to have abstracts for the count/indexing to avoid any allocations here, and it just seemed to overwrought to be worth it.

{
if (this.TryGetAliasesFromUsingDirective(usingAlias, out var current))
AddAliases(aliases, current);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic is incorrect (but was incorrect originally as well). If you have multiple nested namespaces (possibly as symbols) one namespace's aliases with bleed into the others. HOwever, this is probably very rare in practice (certainly no one has reported an issue there), so it hasn't been an issue.

cancellationToken);
}

protected void AddExtensionMethodInfo(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this moved unchanged.

arrayBuilder.Add(declaredSymbolInfoIndex);
}

private static void AddAliases(Dictionary<string, string> allAliases, ImmutableArray<(string aliasName, string name)> aliases)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this moved unchanged.

Debug.Assert(arity > 0);
return (arity <= s_aritySuffixesOneToNine.Length)
? s_aritySuffixesOneToNine[arity - 1]
: string.Concat(GenericTypeNameManglingString, arity.ToString(CultureInfo.InvariantCulture));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this moved into ArityUtilities

Dictionary<string, string> aliases,
Dictionary<string, ArrayBuilder<int>> extensionMethodInfo,
string containerDisplayName,
string fullyQualifiedContainerName,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a core change is that these two strings are now computed by teh caller and passed into all children, saving 5% of allocs in NavTo as reported by @sharwell .

@@ -147,7 +154,16 @@ private static void ProcessUsings(List<Dictionary<string, string>> aliasMaps, Sy
}
}

public override bool TryGetDeclaredSymbolInfo(StringTable stringTable, SyntaxNode node, string rootNamespace, out DeclaredSymbolInfo declaredSymbolInfo)
protected override void AddDeclaredSymbolInfosWorker(
SyntaxNode container,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by passing in the container, we save a lot of ugly computation in the VB side trying to figure out waht the container is for any particular member/type.

case SyntaxKind.FieldDeclaration:
case SyntaxKind.EventFieldDeclaration:
var fieldDeclaration = (BaseFieldDeclarationSyntax)node;
foreach (var variableDeclarator in fieldDeclaration.Declaration.Variables)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic got much simpler as we don't have to hit every vardecl in the file (which might be everywhere) and then filter to the subset that are fields/events. instead we know if we've hit a field/event and can just process its vardecls directly.

ElseIf node.Kind() = SyntaxKind.EventBlock Then
node = DirectCast(node, EventBlockSyntax).EventStatement
ElseIf TypeOf node Is MethodBlockBaseSyntax Then
node = DirectCast(node, MethodBlockBaseSyntax).BlockStatement
End If
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the additional of MethodBlockBaseSyntax here is new. We effectively did not support abstract VB method before in navto. i fixed that as part of this change.

typeBlock.BlockStatement.Modifiers.Any(SyntaxKind.PartialKeyword),
If(kind = SyntaxKind.ClassBlock, DeclaredSymbolInfoKind.Class,
If(kind = SyntaxKind.InterfaceBlock, DeclaredSymbolInfoKind.Interface,
If(kind = SyntaxKind.ModuleBlock, DeclaredSymbolInfoKind.Module,
DeclaredSymbolInfoKind.Struct))),
GetAccessibility(typeBlock, typeBlock.BlockStatement.Modifiers),
GetAccessibility(container, typeBlock, typeBlock.BlockStatement.Modifiers),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passing the container in greatly simplified accessibility logic in several of these blocks.

End Function

Protected Overrides Function GetUsingAliases(node As NamespaceBlockSyntax) As SyntaxList(Of ImportsStatementSyntax)
Return Nothing
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will doc that VB does not have imports inside a namespace.

@CyrusNajmabadi CyrusNajmabadi merged commit a34b3be into dotnet:main Jun 15, 2021
@ghost ghost added this to the Next milestone Jun 15, 2021
@CyrusNajmabadi CyrusNajmabadi deleted the declaredInfos branch June 15, 2021 03:21
@RikkiGibson RikkiGibson modified the milestones: Next, 17.0.P2 Jun 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants