|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.SymbolSearch;
using Microsoft.CodeAnalysis.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.AddImport;
internal abstract partial class AbstractAddImportFeatureService<TSimpleNameSyntax>
{
private sealed partial class SymbolReferenceFinder
{
internal async Task FindNugetOrReferenceAssemblyReferencesAsync(
ConcurrentQueue<Reference> allReferences, CancellationToken cancellationToken)
{
// Only do this if none of the project or metadata searches produced
// any results. We always consider source and local metadata to be
// better than any NuGet/assembly-reference results.
if (allReferences.Count > 0)
return;
if (!_owner.CanAddImportForType(_diagnosticId, _node, out var nameNode))
{
return;
}
CalculateContext(
nameNode, _syntaxFacts,
out var name, out var arity, out var inAttributeContext, out _, out _);
if (ExpressionBinds(nameNode, checkForExtensionMethods: false, cancellationToken: cancellationToken))
{
return;
}
await FindNugetOrReferenceAssemblyTypeReferencesAsync(
allReferences, nameNode, name, arity, inAttributeContext, cancellationToken).ConfigureAwait(false);
}
private async Task FindNugetOrReferenceAssemblyTypeReferencesAsync(
ConcurrentQueue<Reference> allReferences, TSimpleNameSyntax nameNode,
string name, int arity, bool inAttributeContext,
CancellationToken cancellationToken)
{
if (arity == 0 && inAttributeContext)
{
await FindNugetOrReferenceAssemblyTypeReferencesWorkerAsync(
allReferences, nameNode, name + AttributeSuffix, arity,
isAttributeSearch: true, cancellationToken: cancellationToken).ConfigureAwait(false);
}
await FindNugetOrReferenceAssemblyTypeReferencesWorkerAsync(
allReferences, nameNode, name, arity,
isAttributeSearch: false, cancellationToken: cancellationToken).ConfigureAwait(false);
}
private async Task FindNugetOrReferenceAssemblyTypeReferencesWorkerAsync(
ConcurrentQueue<Reference> allReferences, TSimpleNameSyntax nameNode,
string name, int arity, bool isAttributeSearch, CancellationToken cancellationToken)
{
if (_options.SearchOptions.SearchReferenceAssemblies)
{
cancellationToken.ThrowIfCancellationRequested();
await FindReferenceAssemblyTypeReferencesAsync(
allReferences, nameNode, name, arity, isAttributeSearch, cancellationToken).ConfigureAwait(false);
}
var packageSources = PackageSourceHelper.GetPackageSources(_packageSources);
foreach (var (sourceName, sourceUrl) in packageSources)
{
cancellationToken.ThrowIfCancellationRequested();
await FindNugetTypeReferencesAsync(
sourceName, sourceUrl, allReferences,
nameNode, name, arity, isAttributeSearch, cancellationToken).ConfigureAwait(false);
}
}
private async Task FindReferenceAssemblyTypeReferencesAsync(
ConcurrentQueue<Reference> allReferences,
TSimpleNameSyntax nameNode,
string name,
int arity,
bool isAttributeSearch,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var results = await _symbolSearchService.FindReferenceAssembliesWithTypeAsync(
name, arity, cancellationToken).ConfigureAwait(false);
var project = _document.Project;
foreach (var result in results)
{
cancellationToken.ThrowIfCancellationRequested();
await HandleReferenceAssemblyReferenceAsync(
allReferences, nameNode, project,
isAttributeSearch, result, weight: allReferences.Count,
cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
private async Task FindNugetTypeReferencesAsync(
string sourceName,
string sourceUrl,
ConcurrentQueue<Reference> allReferences,
TSimpleNameSyntax nameNode,
string name,
int arity,
bool isAttributeSearch,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var results = await _symbolSearchService.FindPackagesWithTypeAsync(
sourceName, name, arity, cancellationToken).ConfigureAwait(false);
foreach (var result in results)
{
cancellationToken.ThrowIfCancellationRequested();
HandleNugetReference(
sourceUrl, allReferences, nameNode,
isAttributeSearch, result,
weight: allReferences.Count);
}
}
private async Task HandleReferenceAssemblyReferenceAsync(
ConcurrentQueue<Reference> allReferences,
TSimpleNameSyntax nameNode,
Project project,
bool isAttributeSearch,
ReferenceAssemblyWithTypeResult result,
int weight,
CancellationToken cancellationToken)
{
foreach (var reference in project.MetadataReferences)
{
cancellationToken.ThrowIfCancellationRequested();
var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
var assemblySymbol = compilation.GetAssemblyOrModuleSymbol(reference) as IAssemblySymbol;
if (assemblySymbol?.Name == result.AssemblyName)
{
// Project already has a reference to an assembly with this name.
return;
}
}
var desiredName = GetDesiredName(isAttributeSearch, result.TypeName);
allReferences.Enqueue(new AssemblyReference(
_owner, new SearchResult(desiredName, nameNode, result.ContainingNamespaceNames.ToReadOnlyList(), weight), result));
}
private void HandleNugetReference(
string source,
ConcurrentQueue<Reference> allReferences,
TSimpleNameSyntax nameNode,
bool isAttributeSearch,
PackageWithTypeResult result,
int weight)
{
var desiredName = GetDesiredName(isAttributeSearch, result.TypeName);
allReferences.Enqueue(new PackageReference(_owner,
new SearchResult(desiredName, nameNode, result.ContainingNamespaceNames.ToReadOnlyList(), weight),
source, result.PackageName, result.Version));
}
private static string? GetDesiredName(bool isAttributeSearch, string typeName)
=> isAttributeSearch ? typeName.GetWithoutAttributeSuffix(isCaseSensitive: false) : typeName;
}
}
|