提交 6ac9071a 编写于 作者: G Gen Lu

Address SymbolKey resolution issue when projects targeting different framework

Set ignoreAssemblyKey to true during SymbolKey resolution
上级 9a29e6af
......@@ -1623,7 +1623,6 @@ public void M({tupleType} x)
inlineDescription: "NS2");
}
[InlineData(ReferenceType.Project)]
[InlineData(ReferenceType.Metadata)]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
......
......@@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
......@@ -216,10 +217,18 @@ private static string GetReceiverTypeName(ITypeSymbol typeSymbol)
compilation.Assembly, filter, namespaceFilter, internalsVisible,
counter, cancellationToken);
var isSymbolFromCurrentCompilation = project == currentProject;
GetExtensionMethodItemsWorker(
position, semanticModel, receiverTypeSymbol, matchingMethodSymbols, isSymbolFromCurrentCompilation,
completionItemsbuilder, namespaceNameCache, checkedReceiverTypes, cancellationToken);
if (project == currentProject)
{
GetExtensionMethodItemsForSymbolsFromSameCompilation(
position, semanticModel, receiverTypeSymbol, matchingMethodSymbols,
completionItemsbuilder, namespaceNameCache, checkedReceiverTypes, cancellationToken);
}
else
{
GetExtensionMethodItemsForSymbolsFromDifferentCompilation(
position, semanticModel, receiverTypeSymbol, matchingMethodSymbols,
completionItemsbuilder, namespaceNameCache, checkedReceiverTypes, cancellationToken);
}
}
// Get extension method items from PE
......@@ -234,9 +243,9 @@ private static string GetReceiverTypeName(ITypeSymbol typeSymbol)
assembly, filter, namespaceFilter, internalsVisible,
counter, cancellationToken);
GetExtensionMethodItemsWorker(
GetExtensionMethodItemsForSymbolsFromSameCompilation(
position, semanticModel, receiverTypeSymbol, matchingMethodSymbols,
isSymbolFromCurrentCompilation: true, completionItemsbuilder, namespaceNameCache,
completionItemsbuilder, namespaceNameCache,
checkedReceiverTypes, cancellationToken);
}
}
......@@ -256,83 +265,120 @@ static ImmutableArray<string> AttachComplexTypes(ImmutableArray<string> receiver
}
}
private static void GetExtensionMethodItemsWorker(
private static void GetExtensionMethodItemsForSymbolsFromDifferentCompilation(
int position,
SemanticModel semanticModel,
ITypeSymbol receiverTypeSymbol,
MultiDictionary<ITypeSymbol, IMethodSymbol> matchingMethodSymbols,
bool isSymbolFromCurrentCompilation,
ArrayBuilder<SerializableImportCompletionItem> builder,
Dictionary<INamespaceSymbol, string> stringCache,
Dictionary<ITypeSymbol, bool> checkedReceiverTypes,
CancellationToken cancellationToken)
{
// Matching extension method symbols are grouped based on their receiver type.
foreach (var (receiverType, methodSymbols) in matchingMethodSymbols)
foreach (var (declaredReceiverType, methodSymbols) in matchingMethodSymbols)
{
cancellationToken.ThrowIfCancellationRequested();
var receiverTypeInCurrentCompilation = isSymbolFromCurrentCompilation
? receiverType
: SymbolFinder.FindSimilarSymbols(receiverType, semanticModel.Compilation).OfType<ITypeSymbol>().FirstOrDefault();
var declaredReceiverTypeInCurrentCompilation = SymbolFinder.FindSimilarSymbols(declaredReceiverType, semanticModel.Compilation, ignoreAssemblyKey: true, cancellationToken).FirstOrDefault();
if (declaredReceiverTypeInCurrentCompilation == null)
{
if (!Debugger.IsAttached)
Debugger.Launch();
continue;
}
// If we already checked an extension method with same receiver type before, and we know it can't be applied
// to the receiverTypeSymbol, then no need to proceed further.
if (checkedReceiverTypes.TryGetValue(receiverTypeInCurrentCompilation, out var cachedResult) && !cachedResult)
if (checkedReceiverTypes.TryGetValue(declaredReceiverTypeInCurrentCompilation, out var cachedResult) && !cachedResult)
{
// If we already checked an extension method with same receiver type before, and we know it can't be applied
// to the receiverTypeSymbol, then no need to proceed methods from this group..
continue;
}
if (isSymbolFromCurrentCompilation)
var methodsInCurrentCompilation = methodSymbols.Select(s => SymbolFinder.FindSimilarSymbols(s, semanticModel.Compilation, ignoreAssemblyKey: false, cancellationToken).FirstOrDefault()).WhereNotNull();
var enumerator = methodsInCurrentCompilation.GetEnumerator();
if (!enumerator.MoveNext())
{
// We haven't seen this type yet. Try to check by reducing one extension method
// to the given receiver type and save the result.
if (!cachedResult)
{
var reducedMethodSymbol = methodSymbols.First().ReduceExtensionMethod(receiverTypeSymbol);
cachedResult = reducedMethodSymbol != null;
checkedReceiverTypes[receiverType] = cachedResult;
}
continue;
}
// Get the first method so we can check if the receiver type applies.
var methodInCurrentCompilation = enumerator.Current;
// We haven't seen this type yet. Try to check by reducing one extension method
// to the given receiver type and save the result.
if (!cachedResult)
{
// If this is the first symbol we retrived from current compilation,
// try to check if we can apply it to given receiver type, and save result to our cache.
var reducedMethodSymbol = methodInCurrentCompilation.ReduceExtensionMethod(receiverTypeSymbol);
cachedResult = reducedMethodSymbol != null;
checkedReceiverTypes[declaredReceiverTypeInCurrentCompilation] = cachedResult;
}
// Now, cachedResult being false means the receiver type doesn't match,
// stop processing methods from this group.
if (!cachedResult)
{
continue;
}
// Add first method to the item list.
if (semanticModel.IsAccessible(position, methodInCurrentCompilation))
{
CreateAndAddItem(methodInCurrentCompilation, builder, stringCache);
}
// Receiver type matches the receiver type of the extension method declaration.
// We can add accessible ones to the item builder.
if (cachedResult)
// Then add the rest to item list.
while (enumerator.MoveNext())
{
methodInCurrentCompilation = enumerator.Current;
if (semanticModel.IsAccessible(position, methodInCurrentCompilation))
{
foreach (var methodSymbol in methodSymbols)
{
if (semanticModel.IsAccessible(position, methodSymbol))
{
CreateAndAddItem(methodSymbol, builder, stringCache);
}
}
CreateAndAddItem(methodInCurrentCompilation, builder, stringCache);
}
}
else
}
}
private static void GetExtensionMethodItemsForSymbolsFromSameCompilation(
int position,
SemanticModel semanticModel,
ITypeSymbol receiverTypeSymbol,
MultiDictionary<ITypeSymbol, IMethodSymbol> matchingMethodSymbols,
ArrayBuilder<SerializableImportCompletionItem> builder,
Dictionary<INamespaceSymbol, string> stringCache,
Dictionary<ITypeSymbol, bool> checkedReceiverTypes,
CancellationToken cancellationToken)
{
// Matching extension method symbols are grouped based on their receiver type.
foreach (var (receiverType, methodSymbols) in matchingMethodSymbols)
{
cancellationToken.ThrowIfCancellationRequested();
// If we already checked an extension method with same receiver type before, and we know it can't be applied
// to the receiverTypeSymbol, then no need to proceed further.
if (checkedReceiverTypes.TryGetValue(receiverType, out var cachedResult) && !cachedResult)
{
// Symbols could be from a different compilation.
// Need to find the matching one in current compilation before any further checks is done.
foreach (var methodSymbol in methodSymbols.Select(s => SymbolFinder.FindSimilarSymbols(s, semanticModel.Compilation).FirstOrDefault()).WhereNotNull())
{
// We haven't seen this type yet. Try to check by reducing one extension method
// to the given receiver type and save the result.
// Note we will only hit this condition at most once, which is the first time we find a non-null
// similar symbol from current compilation.
if (!cachedResult)
{
// If this is the first symbol we retrived from current compilation,
// try to check if we can apply it to given receiver type, and save result to our cache.
var reducedMethodSymbol = methodSymbol.ReduceExtensionMethod(receiverTypeSymbol);
cachedResult = reducedMethodSymbol != null;
checkedReceiverTypes[receiverType] = cachedResult;
// Now, cachedResult being false means the receiver type doesn't match,
// stop processing any more methods.
if (!cachedResult)
{
break;
}
}
continue;
}
// We haven't seen this type yet. Try to check by reducing one extension method
// to the given receiver type and save the result.
if (!cachedResult)
{
var reducedMethodSymbol = methodSymbols.First().ReduceExtensionMethod(receiverTypeSymbol);
cachedResult = reducedMethodSymbol != null;
checkedReceiverTypes[receiverType] = cachedResult;
}
// Receiver type matches the receiver type of the extension method declaration.
// We can add accessible ones to the item builder.
if (cachedResult)
{
foreach (var methodSymbol in methodSymbols)
{
if (semanticModel.IsAccessible(position, methodSymbol))
{
CreateAndAddItem(methodSymbol, builder, stringCache);
......@@ -340,16 +386,16 @@ static ImmutableArray<string> AttachComplexTypes(ImmutableArray<string> receiver
}
}
}
static void CreateAndAddItem(IMethodSymbol methodSymbol, ArrayBuilder<SerializableImportCompletionItem> builder, Dictionary<INamespaceSymbol, string> stringCache)
=> builder.Add(new SerializableImportCompletionItem(
SymbolKey.CreateString(methodSymbol),
methodSymbol.Name,
methodSymbol.Arity,
methodSymbol.GetGlyph(),
GetFullyQualifiedNamespaceName(methodSymbol.ContainingNamespace, stringCache)));
}
private static void CreateAndAddItem(IMethodSymbol methodSymbol, ArrayBuilder<SerializableImportCompletionItem> builder, Dictionary<INamespaceSymbol, string> stringCache)
=> builder.Add(new SerializableImportCompletionItem(
SymbolKey.CreateString(methodSymbol),
methodSymbol.Name,
methodSymbol.Arity,
methodSymbol.GetGlyph(),
GetFullyQualifiedNamespaceName(methodSymbol.ContainingNamespace, stringCache)));
private static string GetFullyQualifiedNamespaceName(INamespaceSymbol symbol, Dictionary<INamespaceSymbol, string> stringCache)
{
if (symbol.ContainingNamespace == null || symbol.ContainingNamespace.IsGlobalNamespace)
......
......@@ -198,6 +198,10 @@ private static bool InSource(ISymbol symbol)
/// <returns></returns>
public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, Compilation compilation, CancellationToken cancellationToken = default)
where TSymbol : ISymbol
=> FindSimilarSymbols(symbol, compilation, ignoreAssemblyKey: false, cancellationToken);
internal static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, Compilation compilation, bool ignoreAssemblyKey, CancellationToken cancellationToken = default)
where TSymbol : ISymbol
{
if (symbol == null)
{
......@@ -213,7 +217,7 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C
// We may be talking about different compilations. So do not try to resolve locations.
var result = new HashSet<TSymbol>();
var resolution = key.Resolve(compilation, cancellationToken: cancellationToken);
var resolution = key.Resolve(compilation, ignoreAssemblyKey, cancellationToken);
foreach (var current in resolution.OfType<TSymbol>())
{
result.Add(current);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册