提交 c26f4339 编写于 作者: G Gen Lu

Address review comments

上级 6b7e67f6
......@@ -189,7 +189,7 @@ private static async Task<bool> IsInImportsDirectiveAsync(Document document, int
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var leftToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives: true);
return leftToken.GetAncestor(syntaxFacts.IsUsingOrImport) != null;
return leftToken.GetAncestor(syntaxFacts.IsUsingOrExternOrImport) != null;
}
protected static bool IsAddingImportsSupported(Document document)
......
......@@ -87,8 +87,7 @@ internal static partial class ExtensionMethodImportCompletionHelper
var counter = new StatisticCounter();
var ticks = Environment.TickCount;
var project = document.Project;
var assembly = (await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false))!;
var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
// Get the metadata name of all the base types and interfaces this type derived from.
using var _ = PooledHashSet<string>.GetInstance(out var allTypeNamesBuilder);
......@@ -103,14 +102,11 @@ internal static partial class ExtensionMethodImportCompletionHelper
}
var allTypeNames = allTypeNamesBuilder.ToImmutableArray();
var matchedMethods = await GetPossibleExtensionMethodMatchesAsync(
project, allTypeNames, forceIndexCreation, isPrecalculation: false, cancellationToken).ConfigureAwait(false);
counter.GetFilterTicks = Environment.TickCount - ticks;
counter.NoFilter = matchedMethods == null;
var indicesResult = await TryGetIndicesAsync(
document.Project, forceIndexCreation, cancellationToken).ConfigureAwait(false);
// Don't show unimported extension methods if the index isn't ready.
if (matchedMethods == null)
if (!indicesResult.HasResult)
{
// We use a very simple approach to build the cache in the background:
// queue a new task only if the previous task is completed, regardless of what
......@@ -119,17 +115,22 @@ internal static partial class ExtensionMethodImportCompletionHelper
{
if (s_indexingTask.IsCompleted)
{
s_indexingTask = Task.Run(() => GetPossibleExtensionMethodMatchesAsync(project, allTypeNames, forceIndexCreation: false, isPrecalculation: true, CancellationToken.None));
s_indexingTask = Task.Run(() => TryGetIndicesAsync(document.Project, forceIndexCreation: true, CancellationToken.None));
}
}
return (ImmutableArray<SerializableImportCompletionItem>.Empty, counter);
}
var matchedMethods = CreateAggregatedFilter(allTypeNames, indicesResult.SyntaxIndices, indicesResult.SymbolInfos);
counter.GetFilterTicks = Environment.TickCount - ticks;
counter.NoFilter = !indicesResult.HasResult;
ticks = Environment.TickCount;
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var items = GetExtensionMethodItems(assembly.GlobalNamespace, receiverTypeSymbol,
var items = GetExtensionMethodItems(compilation.GlobalNamespace, receiverTypeSymbol,
semanticModel!, position, namespaceInScope, matchedMethods, counter, cancellationToken);
counter.GetSymbolTicks = Environment.TickCount - ticks;
......@@ -137,11 +138,9 @@ internal static partial class ExtensionMethodImportCompletionHelper
return (items, counter);
}
private static async Task<MultiDictionary<string, string>?> GetPossibleExtensionMethodMatchesAsync(
private static async Task<GetIndicesResult> TryGetIndicesAsync(
Project currentProject,
ImmutableArray<string> targetTypeNames,
bool forceIndexCreation,
bool isPrecalculation,
CancellationToken cancellationToken)
{
var solution = currentProject.Solution;
......@@ -152,105 +151,92 @@ internal static partial class ExtensionMethodImportCompletionHelper
using var syntaxDisposer = ArrayBuilder<CacheEntry>.GetInstance(out var syntaxBuilder);
using var symbolDisposer = ArrayBuilder<SymbolTreeInfo>.GetInstance(out var symbolBuilder);
var peReferences = PooledHashSet<PortableExecutableReference>.GetInstance();
// We will only create missing indices in the following cases, neither would block completion.
// 1. We are asked explicitly to create missing indices (e.g. via expander)
// 2. We are trying to populate the data in background.
var shouldCreateIndex = forceIndexCreation || isPrecalculation;
try
foreach (var projectId in relevantProjectIds)
{
foreach (var projectId in relevantProjectIds)
var project = solution.GetProject(projectId);
if (project == null || !project.SupportsCompilation)
{
var project = solution.GetProject(projectId);
if (project == null || !project.SupportsCompilation)
{
continue;
}
// Transitively get all the PE references
peReferences.AddRange(project.MetadataReferences.OfType<PortableExecutableReference>());
continue;
}
// By default, don't trigger index creation except for documents in current project.
var loadOnly = !shouldCreateIndex && projectId != currentProject.Id;
var cacheEntry = await GetCacheEntryAsync(project, loadOnly, cacheService, cancellationToken).ConfigureAwait(false);
// By default, don't trigger index creation except for documents in current project.
var loadOnly = !forceIndexCreation && projectId != currentProject.Id;
var cacheEntry = await GetCacheEntryAsync(project, loadOnly, cacheService, cancellationToken).ConfigureAwait(false);
if (cacheEntry == null)
{
// Don't provide anything if we don't have all the required SyntaxTreeIndex created.
if (cacheEntry == null)
{
return null;
}
syntaxBuilder.Add(cacheEntry.Value);
return GetIndicesResult.NoneResult;
}
foreach (var peReference in peReferences)
{
var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
solution, peReference, loadOnly: !shouldCreateIndex, cancellationToken).ConfigureAwait(false);
syntaxBuilder.Add(cacheEntry.Value);
}
// Don't provide anything if we don't have all the required SymbolTreeInfo created.
if (info == null)
{
return null;
}
// Search through all direct PE references.
foreach (var peReference in currentProject.MetadataReferences.OfType<PortableExecutableReference>())
{
var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
solution, peReference, loadOnly: !forceIndexCreation, cancellationToken).ConfigureAwait(false);
if (info.ContainsExtensionMethod)
{
symbolBuilder.Add(info);
}
if (info == null)
{
// Don't provide anything if we don't have all the required SymbolTreeInfo created.
return GetIndicesResult.NoneResult;
}
// We are just trying to populate the cache in background, no need to return any results.
if (isPrecalculation)
if (info.ContainsExtensionMethod)
{
return null;
symbolBuilder.Add(info);
}
}
var results = new MultiDictionary<string, string>();
var syntaxIndices = syntaxBuilder.ToImmutable();
var symbolInfos = symbolBuilder.ToImmutable();
return new GetIndicesResult(hasResult: true, syntaxIndices, symbolInfos);
}
// Find matching extension methods from source.
foreach (var info in syntaxBuilder)
private static MultiDictionary<string, string> CreateAggregatedFilter(ImmutableArray<string> targetTypeNames, ImmutableArray<CacheEntry> syntaxIndices, ImmutableArray<SymbolTreeInfo> symbolInfos)
{
var results = new MultiDictionary<string, string>();
// Find matching extension methods from source.
foreach (var index in syntaxIndices)
{
// Add simple extension methods with matching target type name
foreach (var targetTypeName in targetTypeNames)
{
// Add simple extension methods with matching target type name
foreach (var targetTypeName in targetTypeNames)
var methodInfos = index.SimpleExtensionMethodInfo[targetTypeName];
if (methodInfos.Count == 0)
{
var methodInfos = info.SimpleExtensionMethodInfo[targetTypeName];
if (methodInfos.Count == 0)
{
continue;
}
foreach (var methodInfo in methodInfos)
{
results.Add(methodInfo.FullyQualifiedContainerName, methodInfo.Name);
}
continue;
}
// Add all complex extension methods, we will need to completely rely on symbols to match them.
foreach (var methodInfo in info.ComplexExtensionMethodInfo)
foreach (var methodInfo in methodInfos)
{
results.Add(methodInfo.FullyQualifiedContainerName, methodInfo.Name);
}
}
// Find matching extension methods from metadata
foreach (var info in symbolBuilder)
// Add all complex extension methods, we will need to completely rely on symbols to match them.
foreach (var methodInfo in index.ComplexExtensionMethodInfo)
{
var methodInfos = info.GetMatchingExtensionMethodInfo(targetTypeNames);
foreach (var methodInfo in methodInfos)
{
results.Add(methodInfo.FullyQualifiedContainerName, methodInfo.Name);
}
results.Add(methodInfo.FullyQualifiedContainerName, methodInfo.Name);
}
return results;
}
finally
// Find matching extension methods from metadata
foreach (var info in symbolInfos)
{
peReferences.Free();
var methodInfos = info.GetMatchingExtensionMethodInfo(targetTypeNames);
foreach (var methodInfo in methodInfos)
{
results.Add(methodInfo.FullyQualifiedContainerName, methodInfo.Name);
}
}
return results;
}
private static ImmutableArray<SerializableImportCompletionItem> GetExtensionMethodItems(
......@@ -334,7 +320,6 @@ internal static partial class ExtensionMethodImportCompletionHelper
IMethodSymbol? reducedMethodSymbol = null;
if (methodSymbol.IsExtensionMethod &&
(methodNames.Count == 0 || methodNames.Contains(methodSymbol.Name)) &&
IsSymbolAccessible(methodSymbol, position, semanticModel))
{
reducedMethodSymbol = methodSymbol.ReduceExtensionMethod(receiverTypeSymbol);
......@@ -450,6 +435,22 @@ public void Dispose()
Children.Free();
}
}
private readonly struct GetIndicesResult
{
public bool HasResult { get; }
public ImmutableArray<CacheEntry> SyntaxIndices { get; }
public ImmutableArray<SymbolTreeInfo> SymbolInfos { get; }
public GetIndicesResult(bool hasResult, ImmutableArray<CacheEntry> syntaxIndices = default, ImmutableArray<SymbolTreeInfo> symbolInfos = default)
{
HasResult = hasResult;
SyntaxIndices = syntaxIndices;
SymbolInfos = symbolInfos;
}
public static GetIndicesResult NoneResult => new GetIndicesResult(hasResult: false);
}
}
internal sealed class StatisticCounter
......
......@@ -4,7 +4,7 @@
namespace Microsoft.CodeAnalysis.Completion.Providers
{
internal sealed class SerializableImportCompletionItem
internal readonly struct SerializableImportCompletionItem
{
public readonly string SymbolKeyData;
public readonly int Arity;
......
......@@ -1371,11 +1371,6 @@ public bool IsUsingOrExternOrImport(SyntaxNode node)
node.IsKind(SyntaxKind.ExternAliasDirective);
}
public bool IsUsingOrImport(SyntaxNode node)
{
return node.IsKind(SyntaxKind.UsingDirective);
}
public bool IsGlobalAttribute(SyntaxNode node)
{
return node.IsKind(SyntaxKind.Attribute) && node.Parent.IsKind(SyntaxKind.AttributeList) &&
......
......@@ -39,6 +39,8 @@ internal partial class SyntaxTreeIndex
/// </summary>
public readonly ImmutableArray<int> ComplexExtensionMethodInfo { get; }
public bool ContainsExtensionMethod => SimpleExtensionMethodInfo.Count > 0 || ComplexExtensionMethodInfo.Length > 0;
public ExtensionMethodInfo(
ImmutableDictionary<string, ImmutableArray<int>> simpleExtensionMethodInfo,
ImmutableArray<int> complexExtensionMethodInfo)
......
......@@ -15,7 +15,7 @@ internal sealed partial class SyntaxTreeIndex
public ImmutableArray<int> ComplexExtensionMethodInfo
=> _extensionMethodInfo.ComplexExtensionMethodInfo;
public bool ContainsExtensionMethod => SimpleExtensionMethodInfo.Count > 0 || ComplexExtensionMethodInfo.Length > 0;
public bool ContainsExtensionMethod => _extensionMethodInfo.ContainsExtensionMethod;
public bool ProbablyContainsIdentifier(string identifier) => _identifierInfo.ProbablyContainsIdentifier(identifier);
public bool ProbablyContainsEscapedIdentifier(string identifier) => _identifierInfo.ProbablyContainsEscapedIdentifier(identifier);
......
......@@ -89,7 +89,6 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsTypeNamedVarInVariableOrFieldDeclaration(SyntaxToken token, SyntaxNode parent);
bool IsTypeNamedDynamic(SyntaxToken token, SyntaxNode parent);
bool IsUsingOrExternOrImport(SyntaxNode node);
bool IsUsingOrImport(SyntaxNode node);
bool IsUsingAliasDirective(SyntaxNode node);
bool IsGlobalAttribute(SyntaxNode node);
bool IsDeclaration(SyntaxNode node);
......
......@@ -23,7 +23,7 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions
{
internal static partial class DocumentExtensions
{
// ⚠ Verify IVTs do not use this method before removing it.314104
// ⚠ Verify IVTs do not use this method before removing it.
public static TLanguageService? GetLanguageService<TLanguageService>(this Document? document) where TLanguageService : class, ILanguageService
=> document?.Project?.LanguageServices?.GetService<TLanguageService>();
......
......@@ -1402,10 +1402,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Function
Public Function IsUsingOrExternOrImport(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsUsingOrExternOrImport
Return IsUsingOrImport(node)
End Function
Public Function IsUsingOrImport(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsUsingOrImport
Return node.IsKind(SyntaxKind.ImportsStatement)
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册