예제 #1
0
        protected override async Task AddCompletionItemsAsync(
            CompletionContext completionContext,
            SyntaxContext syntaxContext,
            HashSet <string> namespaceInScope,
            bool isExpandedCompletion,
            CancellationToken cancellationToken)
        {
            using (Logger.LogBlock(FunctionId.Completion_ExtensionMethodImportCompletionProvider_GetCompletionItemsAsync, cancellationToken))
            {
                var syntaxFacts = completionContext.Document.GetRequiredLanguageService <ISyntaxFactsService>();
                if (TryGetReceiverTypeSymbol(syntaxContext, syntaxFacts, cancellationToken, out var receiverTypeSymbol))
                {
                    var items = await ExtensionMethodImportCompletionHelper.GetUnimportedExtensionMethodsAsync(
                        completionContext.Document,
                        completionContext.Position,
                        receiverTypeSymbol,
                        namespaceInScope,
                        forceIndexCreation : isExpandedCompletion,
                        cancellationToken).ConfigureAwait(false);

                    var receiverTypeKey = SymbolKey.CreateString(receiverTypeSymbol, cancellationToken);
                    completionContext.AddItems(items.Select(i => Convert(i, receiverTypeKey)));
                }
                else
                {
                    // If we can't get a valid receiver type, then we don't show expander as available.
                    // We need to set this explicitly here because we didn't do the (more expensive) symbol check inside
                    // `ShouldProvideCompletion` method above, which is intended for quick syntax based check.
                    completionContext.ExpandItemsAvailable = false;
                }
            }
        }
예제 #2
0
        public async Task <Solution> EncapsulateFieldsAsync(
            Document document, ImmutableArray <IFieldSymbol> fields,
            bool updateReferences, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            using (Logger.LogBlock(FunctionId.Renamer_FindRenameLocationsAsync, cancellationToken))
            {
                var solution = document.Project.Solution;
                var client   = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false);

                if (client != null)
                {
                    var fieldSymbolKeys = fields.SelectAsArray(f => SymbolKey.CreateString(f, cancellationToken));

                    var result = await client.TryInvokeAsync <IRemoteEncapsulateFieldService, ImmutableArray <(DocumentId, ImmutableArray <TextChange>)> >(
                        solution,
                        (service, solutionInfo, cancellationToken) => service.EncapsulateFieldsAsync(solutionInfo, document.Id, fieldSymbolKeys, updateReferences, cancellationToken),
                        callbackTarget : null,
                        cancellationToken).ConfigureAwait(false);

                    if (!result.HasValue)
                    {
                        return(solution);
                    }

                    return(await RemoteUtilities.UpdateSolutionAsync(
                               solution, result.Value, cancellationToken).ConfigureAwait(false));
                }
            }

            return(await EncapsulateFieldsInCurrentProcessAsync(
                       document, fields, updateReferences, cancellationToken).ConfigureAwait(false));
        }
예제 #3
0
 static SerializableImportCompletionItem CreateItem(IMethodSymbol methodSymbol, string containingNamespace, int additionalOverloadCount, CancellationToken cancellationToken)
 => new SerializableImportCompletionItem(
     SymbolKey.CreateString(methodSymbol, cancellationToken),
     methodSymbol.Name,
     methodSymbol.Arity,
     methodSymbol.GetGlyph(),
     containingNamespace,
     additionalOverloadCount);
        protected override async Task AddCompletionItemsAsync(
            CompletionContext completionContext,
            SyntaxContext syntaxContext,
            HashSet <string> namespaceInScope,
            bool isExpandedCompletion,
            CancellationToken cancellationToken)
        {
            using (Logger.LogBlock(FunctionId.Completion_ExtensionMethodImportCompletionProvider_GetCompletionItemsAsync, cancellationToken))
            {
                var syntaxFacts = completionContext.Document.GetRequiredLanguageService <ISyntaxFactsService>();
                if (TryGetReceiverTypeSymbol(syntaxContext, syntaxFacts, cancellationToken, out var receiverTypeSymbol))
                {
                    using var nestedTokenSource = new CancellationTokenSource();
                    using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(nestedTokenSource.Token, cancellationToken);
                    var inferredTypes = completionContext.CompletionOptions.TargetTypedCompletionFilter
                        ? syntaxContext.InferredTypes
                        : ImmutableArray <ITypeSymbol> .Empty;

                    var getItemsTask = Task.Run(() => ExtensionMethodImportCompletionHelper.GetUnimportedExtensionMethodsAsync(
                                                    completionContext.Document,
                                                    completionContext.Position,
                                                    receiverTypeSymbol,
                                                    namespaceInScope,
                                                    inferredTypes,
                                                    forceIndexCreation: isExpandedCompletion,
                                                    hideAdvancedMembers: completionContext.CompletionOptions.HideAdvancedMembers,
                                                    linkedTokenSource.Token));

                    var timeoutInMilliseconds = completionContext.CompletionOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion;

                    // Timebox is enabled if timeout value is >= 0 and we are not triggered via expander
                    if (timeoutInMilliseconds >= 0 && !isExpandedCompletion)
                    {
                        // timeout == 0 means immediate timeout (for testing purpose)
                        if (timeoutInMilliseconds == 0 || await Task.WhenAny(getItemsTask, Task.Delay(timeoutInMilliseconds, linkedTokenSource.Token)).ConfigureAwait(false) != getItemsTask)
                        {
                            nestedTokenSource.Cancel();
                            CompletionProvidersLogger.LogExtensionMethodCompletionTimeoutCount();
                            return;
                        }
                    }

                    // Either the timebox is not enabled, so we need to wait until the operation for complete,
                    // or there's no timeout, and we now have all completion items ready.
                    var items = await getItemsTask.ConfigureAwait(false);

                    var receiverTypeKey = SymbolKey.CreateString(receiverTypeSymbol, cancellationToken);
                    completionContext.AddItems(items.Select(i => Convert(i, receiverTypeKey)));
                }
                else
                {
                    // If we can't get a valid receiver type, then we don't show expander as available.
                    // We need to set this explicitly here because we didn't do the (more expensive) symbol check inside
                    // `ShouldProvideCompletion` method above, which is intended for quick syntax based check.
                    completionContext.ExpandItemsAvailable = false;
                }
            }
        }
        public static async Task <ImmutableArray <SerializableImportCompletionItem> > GetUnimportedExtensionMethodsAsync(
            Document document,
            int position,
            ITypeSymbol receiverTypeSymbol,
            ISet <string> namespaceInScope,
            bool forceIndexCreation,
            CancellationToken cancellationToken)
        {
            SerializableUnimportedExtensionMethods items;

            var ticks = Environment.TickCount;

            var project = document.Project;
            var client  = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false);

            if (client != null)
            {
                var receiverTypeSymbolKeyData = SymbolKey.CreateString(receiverTypeSymbol, cancellationToken);

                var result = await client.TryInvokeAsync <IRemoteExtensionMethodImportCompletionService, SerializableUnimportedExtensionMethods>(
                    project.Solution,
                    (service, solutionInfo, cancellationToken) => service.GetUnimportedExtensionMethodsAsync(
                        solutionInfo, document.Id, position, receiverTypeSymbolKeyData, namespaceInScope.ToImmutableArray(), forceIndexCreation, cancellationToken),
                    callbackTarget : null,
                    cancellationToken).ConfigureAwait(false);

                if (!result.HasValue)
                {
                    return(ImmutableArray <SerializableImportCompletionItem> .Empty);
                }

                items = result.Value;
            }
            else
            {
                items = await GetUnimportedExtensionMethodsInCurrentProcessAsync(document, position, receiverTypeSymbol, namespaceInScope, forceIndexCreation, cancellationToken).ConfigureAwait(false);
            }

            // report telemetry:
            var totalTicks = Environment.TickCount - ticks;

            CompletionProvidersLogger.LogExtensionMethodCompletionTicksDataPoint(totalTicks);
            CompletionProvidersLogger.LogExtensionMethodCompletionMethodsProvidedDataPoint(items.CompletionItems.Length);
            CompletionProvidersLogger.LogExtensionMethodCompletionGetSymbolsTicksDataPoint(items.GetSymbolsTicks);
            CompletionProvidersLogger.LogExtensionMethodCompletionCreateItemsTicksDataPoint(items.CreateItemsTicks);

            if (items.IsPartialResult)
            {
                CompletionProvidersLogger.LogExtensionMethodCompletionPartialResultCount();
            }

            return(items.CompletionItems);
        }
        private static ImmutableArray <SerializableImportCompletionItem> ConvertSymbolsToCompletionItems(ImmutableArray <IMethodSymbol> extentsionMethodSymbols, CancellationToken cancellationToken)
        {
            using var _1 = PooledDictionary <INamespaceSymbol, string> .GetInstance(out var namespaceNameCache);

            using var _2 = PooledDictionary <(string containingNamespace, string methodName, bool isGeneric), (IMethodSymbol bestSymbol, int overloadCount)> .GetInstance(out var overloadMap);

            // Aggregate overloads
            foreach (var symbol in extentsionMethodSymbols)
            {
                IMethodSymbol bestSymbol;
                int           overloadCount;

                var containingNamespacename = GetFullyQualifiedNamespaceName(symbol.ContainingNamespace, namespaceNameCache);
                var overloadKey             = (containingNamespacename, symbol.Name, isGeneric : symbol.Arity > 0);

                // Select the overload with minimum number of parameters to display
                if (overloadMap.TryGetValue(overloadKey, out var currentValue))
                {
                    bestSymbol    = currentValue.bestSymbol.Parameters.Length > symbol.Parameters.Length ? symbol : currentValue.bestSymbol;
                    overloadCount = currentValue.overloadCount + 1;
                }
                else
                {
                    bestSymbol    = symbol;
                    overloadCount = 1;
                }

                overloadMap[overloadKey] = (bestSymbol, overloadCount);
            }

            // Then convert symbols into completion items
            using var _3 = ArrayBuilder <SerializableImportCompletionItem> .GetInstance(out var itemsBuilder);

            foreach (var((containingNamespace, _, _), (bestSymbol, overloadCount)) in overloadMap)
            {
                // To display the count of of additional overloads, we need to substract total by 1.
                var item = new SerializableImportCompletionItem(
                    SymbolKey.CreateString(bestSymbol, cancellationToken),
                    bestSymbol.Name,
                    bestSymbol.Arity,
                    bestSymbol.GetGlyph(),
                    containingNamespace,
                    additionalOverloadCount: overloadCount - 1);

                itemsBuilder.Add(item);
            }

            return(itemsBuilder.ToImmutable());
        }
예제 #7
0
        private static void GetItemsFromTypeContainsPotentialMatches(
            INamedTypeSymbol containerSymbol,
            string qualifiedNamespaceName,
            MultiDictionary <string, string> .ValueSet methodNames,
            ITypeSymbol receiverTypeSymbol,
            SemanticModel semanticModel,
            int position,
            StatisticCounter counter,
            ArrayBuilder <SerializableImportCompletionItem> builder)
        {
            counter.TotalTypesChecked++;

            if (containerSymbol == null ||
                !containerSymbol.MightContainExtensionMethods ||
                !IsSymbolAccessible(containerSymbol, position, semanticModel))
            {
                return;
            }

            foreach (var methodName in methodNames)
            {
                var methodSymbols = containerSymbol.GetMembers(methodName).OfType <IMethodSymbol>();

                foreach (var methodSymbol in methodSymbols)
                {
                    counter.TotalExtensionMethodsChecked++;
                    IMethodSymbol?reducedMethodSymbol = null;

                    if (methodSymbol.IsExtensionMethod &&
                        IsSymbolAccessible(methodSymbol, position, semanticModel))
                    {
                        reducedMethodSymbol = methodSymbol.ReduceExtensionMethod(receiverTypeSymbol);
                    }

                    if (reducedMethodSymbol != null)
                    {
                        var symbolKeyData = SymbolKey.CreateString(reducedMethodSymbol);
                        builder.Add(new SerializableImportCompletionItem(
                                        symbolKeyData,
                                        reducedMethodSymbol.Name,
                                        reducedMethodSymbol.Arity,
                                        reducedMethodSymbol.GetGlyph(),
                                        qualifiedNamespaceName));
                    }
                }
            }
        }
예제 #8
0
        public static bool TryCreate(
            ISymbol symbol, Project project, CancellationToken cancellationToken,
            out SerializableSymbolAndProjectId result)
        {
            if (!SymbolKey.CanCreate(symbol, cancellationToken))
            {
                result = null;
                return(false);
            }

            result = new SerializableSymbolAndProjectId
            {
                SymbolKeyData = SymbolKey.CreateString(symbol, cancellationToken),
                ProjectId     = project.Id,
            };
            return(true);
        }
        protected override async Task AddCompletionItemsAsync(
            CompletionContext completionContext,
            SyntaxContext syntaxContext,
            HashSet <string> namespaceInScope,
            CancellationToken cancellationToken)
        {
            using (Logger.LogBlock(FunctionId.Completion_ExtensionMethodImportCompletionProvider_GetCompletionItemsAsync, cancellationToken))
            {
                var syntaxFacts = completionContext.Document.GetRequiredLanguageService <ISyntaxFactsService>();
                if (TryGetReceiverTypeSymbol(syntaxContext, syntaxFacts, cancellationToken, out var receiverTypeSymbol))
                {
                    var totalTime = SharedStopwatch.StartNew();

                    var inferredTypes = completionContext.CompletionOptions.TargetTypedCompletionFilter
                        ? syntaxContext.InferredTypes
                        : ImmutableArray <ITypeSymbol> .Empty;

                    var result = await ExtensionMethodImportCompletionHelper.GetUnimportedExtensionMethodsAsync(
                        completionContext.Document,
                        completionContext.Position,
                        receiverTypeSymbol,
                        namespaceInScope,
                        inferredTypes,
                        forceCacheCreation : completionContext.CompletionOptions.ForceExpandedCompletionIndexCreation,
                        hideAdvancedMembers : completionContext.CompletionOptions.HideAdvancedMembers,
                        cancellationToken).ConfigureAwait(false);

                    if (result is null)
                    {
                        return;
                    }

                    var receiverTypeKey = SymbolKey.CreateString(receiverTypeSymbol, cancellationToken);
                    completionContext.AddItems(result.CompletionItems.Select(i => Convert(i, receiverTypeKey)));

                    // report telemetry:
                    CompletionProvidersLogger.LogExtensionMethodCompletionTicksDataPoint(
                        totalTime.Elapsed, result.GetSymbolsTime, result.CreateItemsTime, result.IsRemote);

                    if (result.IsPartialResult)
                    {
                        CompletionProvidersLogger.LogExtensionMethodCompletionPartialResultCount();
                    }
                }
            }
        }
예제 #10
0
        public static async Task <ImmutableArray <SerializableImportCompletionItem> > GetUnimportedExtensionMethodsAsync(
            Document document,
            int position,
            ITypeSymbol receiverTypeSymbol,
            ISet <string> namespaceInScope,
            bool forceIndexCreation,
            CancellationToken cancellationToken)
        {
            async Task <(ImmutableArray <SerializableImportCompletionItem>, StatisticCounter)> GetItemsAsync()
            {
                var project = document.Project;

                var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false);

                if (client != null)
                {
                    var result = await client.TryRunRemoteAsync <(IList <SerializableImportCompletionItem> items, StatisticCounter counter)>(
                        WellKnownServiceHubServices.CodeAnalysisService,
                        nameof(IRemoteExtensionMethodImportCompletionService.GetUnimportedExtensionMethodsAsync),
                        project.Solution,
                        new object[] { document.Id, position, SymbolKey.CreateString(receiverTypeSymbol), namespaceInScope.ToArray(), forceIndexCreation },
                        callbackTarget : null,
                        cancellationToken).ConfigureAwait(false);

                    if (result.HasValue)
                    {
                        return(result.Value.items.ToImmutableArray(), result.Value.counter);
                    }
                }

                return(await GetUnimportedExtensionMethodsInCurrentProcessAsync(document, position, receiverTypeSymbol, namespaceInScope, forceIndexCreation, cancellationToken).ConfigureAwait(false));
            }

            var ticks = Environment.TickCount;

            var(items, counter) = await GetItemsAsync().ConfigureAwait(false);

            counter.TotalTicks = Environment.TickCount - ticks;
            counter.TotalExtensionMethodsProvided = items.Length;
            counter.Report();

            return(items);
        }
예제 #11
0
        public static async Task <(ImmutableArray <SerializableImportCompletionItem>, StatisticCounter)> GetUnimportedExtensionMethodsInRemoteProcessAsync(
            RemoteHostClient client,
            Document document,
            int position,
            ITypeSymbol receiverTypeSymbol,
            ISet <string> namespaceInScope,
            bool forceIndexCreation,
            CancellationToken cancellationToken)
        {
            var project = document.Project;

            var(serializableItems, counter) = await client.TryRunCodeAnalysisRemoteAsync <(IList <SerializableImportCompletionItem>, StatisticCounter)>(
                project.Solution,
                nameof(IRemoteExtensionMethodImportCompletionService.GetUnimportedExtensionMethodsAsync),
                new object[] { document.Id, position, SymbolKey.CreateString(receiverTypeSymbol), namespaceInScope.ToArray(), forceIndexCreation },
                cancellationToken).ConfigureAwait(false);

            return(serializableItems.ToImmutableArray(), counter);
        }
예제 #12
0
        private static void GetExtensionMethodItemsWorker(
            int position,
            SemanticModel semanticModel,
            ITypeSymbol receiverTypeSymbol,
            ImmutableArray <IMethodSymbol> matchingMethodSymbols,
            bool isSymbolFromCurrentCompilation,
            ArrayBuilder <SerializableImportCompletionItem> builder,
            Dictionary <INamespaceSymbol, string> stringCache)
        {
            foreach (var methodSymbol in matchingMethodSymbols)
            {
                // Symbols could be from a different compilation,
                // because we retrieved them on a per-assembly basis.
                // Need to find the matching one in current compilation
                // before any further checks is done.
                var methodSymbolInCurrentCompilation = isSymbolFromCurrentCompilation
                    ? methodSymbol
                    : SymbolFinder.FindSimilarSymbols(methodSymbol, semanticModel.Compilation).FirstOrDefault();

                if (methodSymbolInCurrentCompilation == null ||
                    !semanticModel.IsAccessible(position, methodSymbolInCurrentCompilation))
                {
                    continue;
                }

                var reducedMethodSymbol = methodSymbolInCurrentCompilation.ReduceExtensionMethod(receiverTypeSymbol);
                if (reducedMethodSymbol != null)
                {
                    var symbolKeyData = SymbolKey.CreateString(reducedMethodSymbol);
                    builder.Add(new SerializableImportCompletionItem(
                                    symbolKeyData,
                                    reducedMethodSymbol.Name,
                                    reducedMethodSymbol.Arity,
                                    reducedMethodSymbol.GetGlyph(),
                                    GetFullyQualifiedNamespaceName(reducedMethodSymbol.ContainingNamespace, stringCache)));
                }
            }
        }
        public static async Task <SerializableUnimportedExtensionMethods?> GetUnimportedExtensionMethodsAsync(
            Document document,
            int position,
            ITypeSymbol receiverTypeSymbol,
            ISet <string> namespaceInScope,
            ImmutableArray <ITypeSymbol> targetTypesSymbols,
            bool forceCacheCreation,
            bool hideAdvancedMembers,
            CancellationToken cancellationToken)
        {
            var project = document.Project;
            var client  = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false);

            if (client != null)
            {
                var receiverTypeSymbolKeyData = SymbolKey.CreateString(receiverTypeSymbol, cancellationToken);
                var targetTypesSymbolKeyData  = targetTypesSymbols.SelectAsArray(s => SymbolKey.CreateString(s, cancellationToken));

                // Call the project overload.  Add-import-for-extension-method doesn't search outside of the current
                // project cone.
                var result = await client.TryInvokeAsync <IRemoteExtensionMethodImportCompletionService, SerializableUnimportedExtensionMethods?>(
                    project,
                    (service, solutionInfo, cancellationToken) => service.GetUnimportedExtensionMethodsAsync(
                        solutionInfo, document.Id, position, receiverTypeSymbolKeyData, namespaceInScope.ToImmutableArray(),
                        targetTypesSymbolKeyData, forceCacheCreation, hideAdvancedMembers, cancellationToken),
                    cancellationToken).ConfigureAwait(false);

                return(result.HasValue ? result.Value : null);
            }
            else
            {
                return(await GetUnimportedExtensionMethodsInCurrentProcessAsync(
                           document, position, receiverTypeSymbol, namespaceInScope, targetTypesSymbols, forceCacheCreation, hideAdvancedMembers, isRemote : false, cancellationToken)
                       .ConfigureAwait(false));
            }
        }
 fields.Select(f => SymbolKey.CreateString(f, cancellationToken)).ToArray(),
        private static ImmutableArray <SerializableImportCompletionItem> ConvertSymbolsToCompletionItems(
            Compilation compilation, ImmutableArray <IMethodSymbol> extentsionMethodSymbols, ImmutableArray <ITypeSymbol> targetTypeSymbols, CancellationToken cancellationToken)
        {
            Dictionary <ITypeSymbol, bool> typeConvertibilityCache = new();

            using var _1 = PooledDictionary <INamespaceSymbol, string> .GetInstance(out var namespaceNameCache);

            using var _2 = PooledDictionary <(string containingNamespace, string methodName, bool isGeneric), (IMethodSymbol bestSymbol, int overloadCount, bool includeInTargetTypedCompletion)>
                           .GetInstance(out var overloadMap);

            // Aggregate overloads
            foreach (var symbol in extentsionMethodSymbols)
            {
                cancellationToken.ThrowIfCancellationRequested();

                IMethodSymbol bestSymbol;
                int           overloadCount;
                var           includeInTargetTypedCompletion = ShouldIncludeInTargetTypedCompletion(compilation, symbol, targetTypeSymbols, typeConvertibilityCache);

                var containingNamespacename = GetFullyQualifiedNamespaceName(symbol.ContainingNamespace, namespaceNameCache);
                var overloadKey             = (containingNamespacename, symbol.Name, isGeneric : symbol.Arity > 0);

                // Select the overload convertible to any targeted type (if any) and with minimum number of parameters to display
                if (overloadMap.TryGetValue(overloadKey, out var currentValue))
                {
                    if (currentValue.includeInTargetTypedCompletion == includeInTargetTypedCompletion)
                    {
                        bestSymbol = currentValue.bestSymbol.Parameters.Length > symbol.Parameters.Length ? symbol : currentValue.bestSymbol;
                    }
                    else if (currentValue.includeInTargetTypedCompletion)
                    {
                        bestSymbol = currentValue.bestSymbol;
                    }
                    else
                    {
                        bestSymbol = symbol;
                    }

                    overloadCount = currentValue.overloadCount + 1;
                    includeInTargetTypedCompletion = includeInTargetTypedCompletion || currentValue.includeInTargetTypedCompletion;
                }
                else
                {
                    bestSymbol    = symbol;
                    overloadCount = 1;
                }

                overloadMap[overloadKey] = (bestSymbol, overloadCount, includeInTargetTypedCompletion);
            }

            // Then convert symbols into completion items
            using var _3 = ArrayBuilder <SerializableImportCompletionItem> .GetInstance(out var itemsBuilder);

            foreach (var((containingNamespace, _, _), (bestSymbol, overloadCount, includeInTargetTypedCompletion)) in overloadMap)
            {
                // To display the count of additional overloads, we need to subtract total by 1.
                var item = new SerializableImportCompletionItem(
                    SymbolKey.CreateString(bestSymbol, cancellationToken),
                    bestSymbol.Name,
                    bestSymbol.Arity,
                    bestSymbol.GetGlyph(),
                    containingNamespace,
                    additionalOverloadCount: overloadCount - 1,
                    includeInTargetTypedCompletion);

                itemsBuilder.Add(item);
            }

            return(itemsBuilder.ToImmutable());
        }
예제 #16
0
 public static string EncodeSymbol(ISymbol symbol)
 {
     return(SymbolKey.CreateString(symbol));
 }
예제 #17
0
 public static string EncodeSymbol(ISymbol symbol)
 => SymbolKey.CreateString(symbol);