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

Add info for receiver of complex array type in syntax index

上级 fda2b7db
......@@ -1379,7 +1379,7 @@ public void M(int x)
[InlineData(ReferenceType.Metadata, "[,]", "ExtentionMethod4")]
[InlineData(ReferenceType.Metadata, "[][,]", "ExtentionMethod5")]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TestExtensionMethodsForArrayType(ReferenceType refType, string rank, string expectedName)
public async Task TestExtensionMethodsForSimpleArrayType(ReferenceType refType, string rank, string expectedName)
{
var refDoc = $@"
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""Project1"")]
......@@ -1429,6 +1429,68 @@ public void M(int{rank} x)
glyph: (int)Glyph.ExtensionMethodPublic,
inlineDescription: "Foo");
}
[InlineData(ReferenceType.Project, "[]", "ExtentionMethod2")]
[InlineData(ReferenceType.Project, "[][]", "ExtentionMethod3")]
[InlineData(ReferenceType.Project, "[,]", "ExtentionMethod4")]
[InlineData(ReferenceType.Project, "[][,]", "ExtentionMethod5")]
[InlineData(ReferenceType.Metadata, "[]", "ExtentionMethod2")]
[InlineData(ReferenceType.Metadata, "[][]", "ExtentionMethod3")]
[InlineData(ReferenceType.Metadata, "[,]", "ExtentionMethod4")]
[InlineData(ReferenceType.Metadata, "[][,]", "ExtentionMethod5")]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TestExtensionMethodsForGenericArrayType(ReferenceType refType, string rank, string expectedName)
{
var refDoc = $@"
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""Project1"")]
namespace Foo
{{
public static class ExtensionClass
{{
public static bool ExtentionMethod1<T>(this T x)
=> true;
public static bool ExtentionMethod2<T>(this T[] x)
=> true;
public static bool ExtentionMethod3<T>(this T[][] x)
=> true;
public static bool ExtentionMethod4<T>(this T[,] x)
=> true;
public static bool ExtentionMethod5<T>(this T[][,] x)
=> true;
}}
}}";
var srcDoc = $@"
namespace Baz
{{
public class Bat
{{
public void M(int{rank} x)
{{
x.$$
}}
}}
}}";
var markup = refType switch
{
ReferenceType.Project => CreateMarkupForProjectWithProjectReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp),
ReferenceType.Metadata => CreateMarkupForProjectWithMetadataReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp),
_ => null,
};
await VerifyImportItemExistsAsync(
markup,
expectedName,
displayTextSuffix: "<>",
glyph: (int)Glyph.ExtensionMethodPublic,
inlineDescription: "Foo");
}
private Task VerifyImportItemExistsAsync(string markup, string expectedItem, int glyph, string inlineDescription, string displayTextSuffix = null, string expectedDescriptionOrNull = null)
=> VerifyItemExistsAsync(markup, expectedItem, displayTextSuffix: displayTextSuffix, glyph: glyph, inlineDescription: inlineDescription, expectedDescriptionOrNull: expectedDescriptionOrNull);
......
......@@ -6,7 +6,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
......@@ -14,7 +13,6 @@
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
......@@ -28,39 +26,33 @@ internal static partial class ExtensionMethodImportCompletionHelper
public string Language { get; }
/// <summary>
/// Mapping from the name of target type to extension method symbol infos.
/// Mapping from the name of receiver type to extension method symbol infos.
/// </summary>
public readonly MultiDictionary<string, DeclaredSymbolInfo> SimpleExtensionMethodInfo { get; }
public readonly ImmutableArray<DeclaredSymbolInfo> ComplexExtensionMethodInfo { get; }
public readonly MultiDictionary<string, DeclaredSymbolInfo> ReceiverTypeNameToExtensionMethodMap { get; }
private CacheEntry(
Checksum checksum,
string language,
MultiDictionary<string, DeclaredSymbolInfo> simpleExtensionMethodInfo,
ImmutableArray<DeclaredSymbolInfo> complexExtensionMethodInfo)
MultiDictionary<string, DeclaredSymbolInfo> receiverTypeNameToExtensionMethodMap)
{
Checksum = checksum;
Language = language;
SimpleExtensionMethodInfo = simpleExtensionMethodInfo;
ComplexExtensionMethodInfo = complexExtensionMethodInfo;
ReceiverTypeNameToExtensionMethodMap = receiverTypeNameToExtensionMethodMap;
}
public class Builder : IDisposable
public class Builder
{
private readonly Checksum _checksum;
private readonly string _language;
private readonly MultiDictionary<string, DeclaredSymbolInfo> _simpleItemBuilder;
private readonly ArrayBuilder<DeclaredSymbolInfo> _complexItemBuilder;
private readonly MultiDictionary<string, DeclaredSymbolInfo> _mapBuilder;
public Builder(Checksum checksum, string langauge, IEqualityComparer<string> comparer)
{
_checksum = checksum;
_language = langauge;
_simpleItemBuilder = new MultiDictionary<string, DeclaredSymbolInfo>(comparer);
_complexItemBuilder = ArrayBuilder<DeclaredSymbolInfo>.GetInstance();
_mapBuilder = new MultiDictionary<string, DeclaredSymbolInfo>(comparer);
}
public CacheEntry ToCacheEntry()
......@@ -68,28 +60,19 @@ public CacheEntry ToCacheEntry()
return new CacheEntry(
_checksum,
_language,
_simpleItemBuilder,
_complexItemBuilder.ToImmutable());
_mapBuilder);
}
public void AddItem(SyntaxTreeIndex syntaxIndex)
{
foreach (var (targetType, symbolInfoIndices) in syntaxIndex.SimpleExtensionMethodInfo)
foreach (var (receiverType, symbolInfoIndices) in syntaxIndex.ReceiverTypeNameToExtensionMethodMap)
{
foreach (var index in symbolInfoIndices)
{
_simpleItemBuilder.Add(targetType, syntaxIndex.DeclaredSymbolInfos[index]);
_mapBuilder.Add(receiverType, syntaxIndex.DeclaredSymbolInfos[index]);
}
}
foreach (var index in syntaxIndex.ComplexExtensionMethodInfo)
{
_complexItemBuilder.Add(syntaxIndex.DeclaredSymbolInfos[index]);
}
}
public void Dispose()
=> _complexItemBuilder.Free();
}
}
......@@ -125,7 +108,7 @@ public CacheServiceFactory()
cacheEntry.Language != project.Language)
{
var syntaxFacts = project.LanguageServices.GetRequiredService<ISyntaxFactsService>();
using var builder = new CacheEntry.Builder(checksum, project.Language, syntaxFacts.StringComparer);
var builder = new CacheEntry.Builder(checksum, project.Language, syntaxFacts.StringComparer);
foreach (var document in project.Documents)
{
......
......@@ -163,7 +163,7 @@ private static string GetReceiverTypeName(ITypeSymbol typeSymbol)
private static async Task<ImmutableArray<SerializableImportCompletionItem>> GetExtensionMethodItemsAsync(
Document document,
ITypeSymbol receiverTypeSymbol,
ImmutableArray<string> targetTypeNames,
ImmutableArray<string> receiverTypeNames,
GetIndicesResult indices,
int position,
ISet<string> namespaceFilter,
......@@ -180,13 +180,15 @@ private static string GetReceiverTypeName(ITypeSymbol typeSymbol)
var currentCompilation = semanticModel.Compilation;
var currentAssembly = currentCompilation.Assembly;
using var _1 = ArrayBuilder<SerializableImportCompletionItem>.GetInstance(out var builder);
using var _1 = ArrayBuilder<SerializableImportCompletionItem>.GetInstance(out var completionItemsbuilder);
using var _2 = PooledDictionary<INamespaceSymbol, string>.GetInstance(out var namespaceNameCache);
receiverTypeNames = AttachComplexTypes(receiverTypeNames);
// Get extension method items from source
foreach (var (project, syntaxIndex) in indices.SyntaxIndices!)
{
var filter = CreateAggregatedFilter(targetTypeNames, syntaxIndex);
var filter = CreateAggregatedFilter(receiverTypeNames, syntaxIndex);
var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
var assembly = compilation.Assembly;
......@@ -199,13 +201,13 @@ private static string GetReceiverTypeName(ITypeSymbol typeSymbol)
var isSymbolFromCurrentCompilation = project == currentProject;
GetExtensionMethodItemsWorker(
position, semanticModel, receiverTypeSymbol, matchingMethodSymbols, isSymbolFromCurrentCompilation,
builder, namespaceNameCache, cancellationToken);
completionItemsbuilder, namespaceNameCache, cancellationToken);
}
// Get extension method items from PE
foreach (var (peReference, symbolInfo) in indices.SymbolInfos!)
{
var filter = CreateAggregatedFilter(targetTypeNames, symbolInfo);
var filter = CreateAggregatedFilter(receiverTypeNames, symbolInfo);
if (currentCompilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol assembly)
{
var internalsVisible = currentAssembly.IsSameAssemblyOrHasFriendAccessTo(assembly);
......@@ -216,11 +218,23 @@ private static string GetReceiverTypeName(ITypeSymbol typeSymbol)
GetExtensionMethodItemsWorker(
position, semanticModel, receiverTypeSymbol, matchingMethodSymbols,
isSymbolFromCurrentCompilation: true, builder, namespaceNameCache, cancellationToken);
isSymbolFromCurrentCompilation: true, completionItemsbuilder, namespaceNameCache, cancellationToken);
}
}
return builder.ToImmutable();
return completionItemsbuilder.ToImmutable();
// Add string represent complex types (i.e. "" and "[]") to the receiver type, so we would include in the filter
// info about extension methods with complex receiver type.
static ImmutableArray<string> AttachComplexTypes(ImmutableArray<string> receiverTypeNames)
{
using var _ = ArrayBuilder<string>.GetInstance(receiverTypeNames.Length + 2, out var receiverTypeNamesBuilder);
receiverTypeNamesBuilder.AddRange(receiverTypeNames);
receiverTypeNamesBuilder.Add(string.Empty);
receiverTypeNamesBuilder.Add("[]");
return receiverTypeNamesBuilder.ToImmutable();
}
}
private static void GetExtensionMethodItemsWorker(
......@@ -314,7 +328,7 @@ private static string GetFullyQualifiedNamespaceName(INamespaceSymbol symbol, Di
continue;
}
foreach (var (methodName, targetTypeName) in methodNames)
foreach (var (methodName, receiverTypeName) in methodNames)
{
cancellationToken.ThrowIfCancellationRequested();
......@@ -324,7 +338,7 @@ private static string GetFullyQualifiedNamespaceName(INamespaceSymbol symbol, Di
{
counter.TotalExtensionMethodsChecked++;
if (MatchExtensionMethod(methodSymbol, targetTypeName, internalsVisible))
if (MatchExtensionMethod(methodSymbol, receiverTypeName, internalsVisible))
{
// Find a potential match.
builder.Add(methodSymbol);
......@@ -418,14 +432,13 @@ static bool MatchExtensionMethod(IMethodSymbol method, string filterReceiverType
}
// Create filter for extension methods from source.
private static MultiDictionary<string, (string, string)> CreateAggregatedFilter(ImmutableArray<string> targetTypeNames, CacheEntry syntaxIndex)
private static MultiDictionary<string, (string, string)> CreateAggregatedFilter(ImmutableArray<string> receiverTypeNames, CacheEntry syntaxIndex)
{
var results = new MultiDictionary<string, (string, string)>();
// Add simple extension methods with matching target type name
foreach (var targetTypeName in targetTypeNames)
foreach (var receiverTypeName in receiverTypeNames)
{
var methodInfos = syntaxIndex.SimpleExtensionMethodInfo[targetTypeName];
var methodInfos = syntaxIndex.ReceiverTypeNameToExtensionMethodMap[receiverTypeName];
if (methodInfos.Count == 0)
{
continue;
......@@ -433,32 +446,21 @@ static bool MatchExtensionMethod(IMethodSymbol method, string filterReceiverType
foreach (var methodInfo in methodInfos)
{
results.Add(methodInfo.FullyQualifiedContainerName, (methodInfo.Name, targetTypeName));
results.Add(methodInfo.FullyQualifiedContainerName, (methodInfo.Name, receiverTypeName));
}
}
// Add all complex extension methods, we will need to completely rely on symbols to match them.
foreach (var methodInfo in syntaxIndex.ComplexExtensionMethodInfo)
{
results.Add(methodInfo.FullyQualifiedContainerName, (methodInfo.Name, string.Empty));
}
return results;
}
// Create filter for extension methods from metadata
private static MultiDictionary<string, (string, string)> CreateAggregatedFilter(ImmutableArray<string> targetTypeNames, SymbolTreeInfo symbolInfo)
private static MultiDictionary<string, (string, string)> CreateAggregatedFilter(ImmutableArray<string> receiverTypeNames, SymbolTreeInfo symbolInfo)
{
var results = new MultiDictionary<string, (string, string)>();
using var _ = ArrayBuilder<string>.GetInstance(targetTypeNames.Length + 2, out var builder);
builder.AddRange(targetTypeNames);
builder.Add(string.Empty);
builder.Add("[]");
foreach (var targetTypeName in builder)
foreach (var receiverTypeName in receiverTypeNames)
{
var methodInfos = symbolInfo.GetExtensionMethodInfoForReceiverType(targetTypeName);
var methodInfos = symbolInfo.GetExtensionMethodInfoForReceiverType(receiverTypeName);
if (methodInfos.Count == 0)
{
continue;
......@@ -466,7 +468,7 @@ static bool MatchExtensionMethod(IMethodSymbol method, string filterReceiverType
foreach (var methodInfo in methodInfos)
{
results.Add(methodInfo.FullyQualifiedContainerName, (methodInfo.Name, targetTypeName));
results.Add(methodInfo.FullyQualifiedContainerName, (methodInfo.Name, receiverTypeName));
}
}
......
......@@ -506,8 +506,8 @@ public override bool TryGetAliasesFromUsingDirective(SyntaxNode node, out Immuta
{
if (node is UsingDirectiveSyntax usingDirectiveNode && usingDirectiveNode.Alias != null)
{
if (TryGetSimpleTypeName(usingDirectiveNode.Alias.Name, typeParameterNames: null, out var aliasName) &&
TryGetSimpleTypeName(usingDirectiveNode.Name, typeParameterNames: null, out var name))
if (TryGetSimpleTypeName(usingDirectiveNode.Alias.Name, typeParameterNames: null, out var aliasName, out _) &&
TryGetSimpleTypeName(usingDirectiveNode.Name, typeParameterNames: null, out var name, out _))
{
aliases = ImmutableArray.Create<(string, string)>((aliasName, name));
return true;
......@@ -518,18 +518,20 @@ public override bool TryGetAliasesFromUsingDirective(SyntaxNode node, out Immuta
return false;
}
public override string GetTargetTypeName(SyntaxNode node)
public override string GetReceiverTypeName(SyntaxNode node)
{
var methodDeclaration = (MethodDeclarationSyntax)node;
Debug.Assert(IsExtensionMethod(methodDeclaration));
var typeParameterNames = methodDeclaration.TypeParameterList?.Parameters.SelectAsArray(p => p.Identifier.Text);
TryGetSimpleTypeName(methodDeclaration.ParameterList.Parameters[0].Type, typeParameterNames, out var targetTypeName);
return targetTypeName;
TryGetSimpleTypeName(methodDeclaration.ParameterList.Parameters[0].Type, typeParameterNames, out var targetTypeName, out var isArray);
return CreateReceiverTypeString(targetTypeName, isArray);
}
private static bool TryGetSimpleTypeName(SyntaxNode node, ImmutableArray<string>? typeParameterNames, out string simpleTypeName)
private static bool TryGetSimpleTypeName(SyntaxNode node, ImmutableArray<string>? typeParameterNames, out string simpleTypeName, out bool isArray)
{
isArray = false;
if (node is TypeSyntax typeNode)
{
switch (typeNode)
......@@ -541,12 +543,8 @@ private static bool TryGetSimpleTypeName(SyntaxNode node, ImmutableArray<string>
return simpleTypeName != null;
case ArrayTypeSyntax arrayTypeNode:
// We do not differentiate array of different kinds for simplicity.
// e.g. int[], int[][], int[,], etc. are all represented as int[] in the index.
simpleTypeName = TryGetSimpleTypeName(arrayTypeNode.ElementType, typeParameterNames, out var elementTypeName)
? CreateTargetTypeStringForArray(elementTypeName)
: null;
return simpleTypeName != null;
isArray = true;
return TryGetSimpleTypeName(arrayTypeNode.ElementType, typeParameterNames, out simpleTypeName, out _);
case GenericNameSyntax genericNameNode:
var name = genericNameNode.Identifier.Text;
......@@ -559,17 +557,17 @@ private static bool TryGetSimpleTypeName(SyntaxNode node, ImmutableArray<string>
return simpleTypeName != null;
case AliasQualifiedNameSyntax aliasQualifiedNameNode:
return TryGetSimpleTypeName(aliasQualifiedNameNode.Name, typeParameterNames, out simpleTypeName);
return TryGetSimpleTypeName(aliasQualifiedNameNode.Name, typeParameterNames, out simpleTypeName, out _);
case QualifiedNameSyntax qualifiedNameNode:
// For an identifier to the right of a '.', it can't be a type parameter,
// so we don't need to check for it further.
return TryGetSimpleTypeName(qualifiedNameNode.Right, typeParameterNames: null, out simpleTypeName);
return TryGetSimpleTypeName(qualifiedNameNode.Right, typeParameterNames: null, out simpleTypeName, out _);
case NullableTypeSyntax nullableNode:
// Ignore nullability, becase nullable reference type might not be enabled universally.
// In the worst case we just include more methods to check in out filter.
return TryGetSimpleTypeName(nullableNode.ElementType, typeParameterNames, out simpleTypeName);
return TryGetSimpleTypeName(nullableNode.ElementType, typeParameterNames, out simpleTypeName, out isArray);
}
}
......
......@@ -95,7 +95,7 @@ private string GetDebuggerDisplay()
/// <summary>
/// Similar to <see cref="SyntaxTreeIndex.ExtensionMethodInfo"/>, we divide extension methods into simple
/// and complex categories for filtering purpose. Whether a method is simple is determined based on if we
/// can determine it's target type easily with a pure text matching. For complex methods, we will need to
/// can determine it's receiver type easily with a pure text matching. For complex methods, we will need to
/// rely on symbol to decide if it's feasible.
///
/// Simple types include:
......
......@@ -230,7 +230,7 @@ private struct MetadataInfoCreator : IDisposable
private readonly List<MetadataDefinition> _allTypeDefinitions;
// Map from node represents extension method to list of possible parameter type info.
// We can have more than one if there's multiple methods with same name but different target type.
// We can have more than one if there's multiple methods with same name but different receiver type.
// e.g.
//
// public static bool AnotherExtensionMethod1(this int x);
......@@ -371,8 +371,8 @@ private void GenerateMetadataNodes()
{
if (definition.Kind == MetadataDefinitionKind.Member)
{
// We need to support having multiple methods with same name but different target type.
_extensionMethodToParameterTypeInfo.Add(childNode, definition.TargetTypeInfo);
// We need to support having multiple methods with same name but different receiver type.
_extensionMethodToParameterTypeInfo.Add(childNode, definition.ReceiverTypeInfo);
}
LookupMetadataDefinitions(definition, definitionMap);
......@@ -434,7 +434,7 @@ private void GenerateMetadataNodes()
method.GetParameters().Count > 0 &&
method.GetCustomAttributes().Count > 0)
{
// Decode method signature to get the target type name (i.e. type name for the first parameter)
// Decode method signature to get the receiver type name (i.e. type name for the first parameter)
var blob = _metadataReader.GetBlobReader(method.Signature);
var decoder = new SignatureDecoder<ParameterTypeInfo, object>(ParameterTypeInfoProvider.Instance, _metadataReader, genericContext: null);
var signature = decoder.DecodeMethodSignature(ref blob);
......@@ -797,17 +797,17 @@ private struct MetadataDefinition
/// <summary>
/// Only applies to member kind. Represents the type info of the first parameter.
/// </summary>
public ParameterTypeInfo TargetTypeInfo { get; }
public ParameterTypeInfo ReceiverTypeInfo { get; }
public NamespaceDefinition Namespace { get; private set; }
public TypeDefinition Type { get; private set; }
public MetadataDefinition(MetadataDefinitionKind kind, string name, ParameterTypeInfo targetTypeInfo = default)
public MetadataDefinition(MetadataDefinitionKind kind, string name, ParameterTypeInfo receiverTypeInfo = default)
: this()
{
Kind = kind;
Name = name;
TargetTypeInfo = targetTypeInfo;
ReceiverTypeInfo = receiverTypeInfo;
}
public static MetadataDefinition Create(
......
......@@ -16,45 +16,47 @@ internal partial class SyntaxTreeIndex
private readonly struct ExtensionMethodInfo
{
// We divide extension methods into two categories, simple and complex, for filtering purpose.
// Whether a method is simple is determined based on if we can determine it's target type easily
// Whether a method is simple is determined based on if we can determine it's receiver type easily
// with a pure text matching. For complex methods, we will need to rely on symbol to decide if it's
// feasible.
//
// Complex methods include:
// - Method declared in the document which includes using alias directive
// - Generic method where the target type is a type-paramter (e.g. List<T> would be considered simple, not complex)
// - If the target type name is one of the following (i.e. name of the type for the first parameter)
// - Generic method where the receiver type is a type-paramter (e.g. List<T> would be considered simple, not complex)
// - If the receiver type name is one of the following (i.e. name of the type for the first parameter)
// 1. ValueTuple type
// 2. Pointer type
//
// The rest of methods are considered simple.
/// <summary>
/// Name of the simple method's target type name to the index of its DeclaredSymbolInfo in `_declarationInfo`.
/// All predefined types are converted to its metadata form. e.g. int => Int32. For generic types, type parameters are ignored.
/// Name of the extension method's receiver type to the index of its DeclaredSymbolInfo in `_declarationInfo`.
///
/// For simple types, the receiver type name is it's metadata name. All predefined types are converted to its metadata form.
/// e.g. int => Int32. For generic types, type parameters are ignored.
///
/// For complex types, the receiver type name is "".
///
/// For any kind of array types, it's "{element's receiver type name}[]".
/// e.g.
/// int[][,] => "Int32[]"
/// T (where T is a type parameter) => ""
/// T[,] (where T is a type parameter) => "T[]"
/// </summary>
public readonly ImmutableDictionary<string, ImmutableArray<int>> SimpleExtensionMethodInfo { get; }
public readonly ImmutableDictionary<string, ImmutableArray<int>> ReceiverTypeNameToExtensionMethodMap { get; }
/// <summary>
/// Indices of to all complex methods' DeclaredSymbolInfo in `_declarationInfo`.
/// </summary>
public readonly ImmutableArray<int> ComplexExtensionMethodInfo { get; }
public bool ContainsExtensionMethod => SimpleExtensionMethodInfo.Count > 0 || ComplexExtensionMethodInfo.Length > 0;
public bool ContainsExtensionMethod => ReceiverTypeNameToExtensionMethodMap.Count > 0;
public ExtensionMethodInfo(
ImmutableDictionary<string, ImmutableArray<int>> simpleExtensionMethodInfo,
ImmutableArray<int> complexExtensionMethodInfo)
public ExtensionMethodInfo(ImmutableDictionary<string, ImmutableArray<int>> receiverTypeNameToExtensionMethodMap)
{
SimpleExtensionMethodInfo = simpleExtensionMethodInfo;
ComplexExtensionMethodInfo = complexExtensionMethodInfo;
ReceiverTypeNameToExtensionMethodMap = receiverTypeNameToExtensionMethodMap;
}
public void WriteTo(ObjectWriter writer)
{
writer.WriteInt32(SimpleExtensionMethodInfo.Count);
writer.WriteInt32(ReceiverTypeNameToExtensionMethodMap.Count);
foreach (var kvp in SimpleExtensionMethodInfo)
foreach (var kvp in ReceiverTypeNameToExtensionMethodMap)
{
writer.WriteString(kvp.Key);
writer.WriteInt32(kvp.Value.Length);
......@@ -64,19 +66,13 @@ public void WriteTo(ObjectWriter writer)
writer.WriteInt32(declaredSymbolInfoIndex);
}
}
writer.WriteInt32(ComplexExtensionMethodInfo.Length);
foreach (var declaredSymbolInfoIndex in ComplexExtensionMethodInfo)
{
writer.WriteInt32(declaredSymbolInfoIndex);
}
}
public static ExtensionMethodInfo? TryReadFrom(ObjectReader reader)
{
try
{
var simpleExtensionMethodInfo = ImmutableDictionary.CreateBuilder<string, ImmutableArray<int>>();
var receiverTypeNameToExtensionMethodMapBuilder = ImmutableDictionary.CreateBuilder<string, ImmutableArray<int>>();
var count = reader.ReadInt32();
for (var i = 0; i < count; ++i)
......@@ -91,18 +87,10 @@ public void WriteTo(ObjectWriter writer)
arrayBuilder.Add(declaredSymbolInfoIndex);
}
simpleExtensionMethodInfo[typeName] = arrayBuilder.ToImmutableAndFree();
}
count = reader.ReadInt32();
var complexExtensionMethodInfo = ArrayBuilder<int>.GetInstance(count);
for (var i = 0; i < count; ++i)
{
var declaredSymbolInfoIndex = reader.ReadInt32();
complexExtensionMethodInfo.Add(declaredSymbolInfoIndex);
receiverTypeNameToExtensionMethodMapBuilder[typeName] = arrayBuilder.ToImmutableAndFree();
}
return new ExtensionMethodInfo(simpleExtensionMethodInfo.ToImmutable(), complexExtensionMethodInfo.ToImmutableAndFree());
return new ExtensionMethodInfo(receiverTypeNameToExtensionMethodMapBuilder.ToImmutable());
}
catch (Exception)
{
......
......@@ -25,9 +25,9 @@ internal interface IDeclaredSymbolInfoFactoryService : ILanguageService
// otherwise we would not be able to get correct data from syntax.
bool TryGetDeclaredSymbolInfo(StringTable stringTable, SyntaxNode node, string rootNamespace, out DeclaredSymbolInfo declaredSymbolInfo);
// Get the name of the target type of specified extension method declaration node.
// The returned value would be null for complex type.
string GetTargetTypeName(SyntaxNode node);
// Get the name of the receiver type of specified extension method declaration node.
// The returned value would be "" or "[]" for complex types.
string GetReceiverTypeName(SyntaxNode node);
bool TryGetAliasesFromUsingDirective(SyntaxNode node, out ImmutableArray<(string aliasName, string name)> aliases);
......@@ -71,8 +71,7 @@ internal sealed partial class SyntaxTreeIndex
var longLiterals = LongLiteralHashSetPool.Allocate();
var declaredSymbolInfos = ArrayBuilder<DeclaredSymbolInfo>.GetInstance();
var complexExtensionMethodInfoBuilder = ArrayBuilder<int>.GetInstance();
var simpleExtensionMethodInfoBuilder = PooledDictionary<string, ArrayBuilder<int>>.GetInstance();
var extensionMethodInfoBuilder = PooledDictionary<string, ArrayBuilder<int>>.GetInstance();
using var _ = PooledDictionary<string, string>.GetInstance(out var usingAliases);
try
......@@ -171,8 +170,7 @@ internal sealed partial class SyntaxTreeIndex
usingAliases,
declaredSymbolInfoIndex,
declaredSymbolInfo,
simpleExtensionMethodInfoBuilder,
complexExtensionMethodInfoBuilder);
extensionMethodInfoBuilder);
}
else
{
......@@ -273,8 +271,7 @@ internal sealed partial class SyntaxTreeIndex
new DeclarationInfo(
declaredSymbolInfos.ToImmutable()),
new ExtensionMethodInfo(
simpleExtensionMethodInfoBuilder.ToImmutableDictionary(s_getKey, s_getValuesAsImmutableArray),
complexExtensionMethodInfoBuilder.ToImmutable()));
extensionMethodInfoBuilder.ToImmutableDictionary(s_getKey, s_getValuesAsImmutableArray)));
}
finally
{
......@@ -282,13 +279,12 @@ internal sealed partial class SyntaxTreeIndex
StringLiteralHashSetPool.ClearAndFree(stringLiterals);
LongLiteralHashSetPool.ClearAndFree(longLiterals);
foreach (var (_, builder) in simpleExtensionMethodInfoBuilder)
foreach (var (_, builder) in extensionMethodInfoBuilder)
{
builder.Free();
}
simpleExtensionMethodInfoBuilder.Free();
complexExtensionMethodInfoBuilder.Free();
extensionMethodInfoBuilder.Free();
declaredSymbolInfos.Free();
}
}
......@@ -302,43 +298,35 @@ internal sealed partial class SyntaxTreeIndex
PooledDictionary<string, string> aliases,
int declaredSymbolInfoIndex,
DeclaredSymbolInfo declaredSymbolInfo,
PooledDictionary<string, ArrayBuilder<int>> simpleInfoBuilder,
ArrayBuilder<int> complexInfoBuilder)
PooledDictionary<string, ArrayBuilder<int>> extensionMethodsInfoBuilder)
{
if (declaredSymbolInfo.Kind != DeclaredSymbolInfoKind.ExtensionMethod)
{
return;
}
var targetTypeName = infoFactory.GetTargetTypeName(node);
// complex method
if (targetTypeName == null)
{
complexInfoBuilder.Add(declaredSymbolInfoIndex);
return;
}
var receiverTypeName = infoFactory.GetReceiverTypeName(node);
// Target type is an alias
if (aliases.TryGetValue(targetTypeName, out var originalName))
if (aliases.TryGetValue(receiverTypeName, out var originalName))
{
// it is an alias of multiple with identical name,
// simply treat it as a complex method.
if (originalName == null)
{
complexInfoBuilder.Add(declaredSymbolInfoIndex);
return;
receiverTypeName = string.Empty;
}
else
{
// replace the alias with its original name.
receiverTypeName = originalName;
}
// replace the alias with its original name.
targetTypeName = originalName;
}
// So we've got a simple method.
if (!simpleInfoBuilder.TryGetValue(targetTypeName, out var arrayBuilder))
if (!extensionMethodsInfoBuilder.TryGetValue(string.Empty, out var arrayBuilder))
{
arrayBuilder = ArrayBuilder<int>.GetInstance();
simpleInfoBuilder[targetTypeName] = arrayBuilder;
extensionMethodsInfoBuilder[receiverTypeName] = arrayBuilder;
}
arrayBuilder.Add(declaredSymbolInfoIndex);
......
......@@ -11,11 +11,8 @@ internal sealed partial class SyntaxTreeIndex
{
public ImmutableArray<DeclaredSymbolInfo> DeclaredSymbolInfos => _declarationInfo.DeclaredSymbolInfos;
public ImmutableDictionary<string, ImmutableArray<int>> SimpleExtensionMethodInfo
=> _extensionMethodInfo.SimpleExtensionMethodInfo;
public ImmutableArray<int> ComplexExtensionMethodInfo
=> _extensionMethodInfo.ComplexExtensionMethodInfo;
public ImmutableDictionary<string, ImmutableArray<int>> ReceiverTypeNameToExtensionMethodMap
=> _extensionMethodInfo.ReceiverTypeNameToExtensionMethodMap;
public bool ContainsExtensionMethod => _extensionMethodInfo.ContainsExtensionMethod;
......
......@@ -32,8 +32,19 @@ internal abstract class AbstractDeclaredSymbolInfoFactoryService : IDeclaredSymb
protected static List<Dictionary<string, string>> AllocateAliasMapList()
=> s_aliasMapListPool.Allocate();
protected static string CreateTargetTypeStringForArray(string elementTypeName)
=> elementTypeName + "[]";
// We do not differentiate arrays of different kinds for simplicity.
// e.g. int[], int[][], int[,], etc. are all represented as int[] in the index.
protected static string CreateReceiverTypeString(string typeName, bool isArray)
{
if (typeName == null)
{
return isArray ? "[]" : string.Empty;
}
else
{
return isArray ? typeName + "[]" : typeName;
}
}
protected static void FreeAliasMapList(List<Dictionary<string, string>> list)
{
......@@ -98,7 +109,7 @@ public static string GetMetadataAritySuffix(int arity)
/// on `node` should return a `DeclaredSymbolInfo` of kind `ExtensionMethod`.
/// If the return value is null, then it means this is a "complex" method (as described at <see cref="SyntaxTreeIndex.ExtensionMethodInfo"/>).
/// </summary>
public abstract string GetTargetTypeName(SyntaxNode node);
public abstract string GetReceiverTypeName(SyntaxNode node);
public abstract bool TryGetAliasesFromUsingDirective(SyntaxNode node, out ImmutableArray<(string aliasName, string name)> aliases);
......
......@@ -463,15 +463,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols
Next
End Sub
Public Overrides Function GetTargetTypeName(node As SyntaxNode) As String
Public Overrides Function GetReceiverTypeName(node As SyntaxNode) As String
Dim funcDecl = CType(node, MethodBlockSyntax)
Debug.Assert(IsExtensionMethod(funcDecl))
Dim typeParameterNames = funcDecl.SubOrFunctionStatement.TypeParameterList?.Parameters.SelectAsArray(Function(p) p.Identifier.Text)
Dim targetTypeName As String = Nothing
TryGetSimpleTypeNameWorker(funcDecl.BlockStatement.ParameterList.Parameters(0).AsClause?.Type, typeParameterNames, targetTypeName)
Dim isArray As Boolean = False
Return targetTypeName
TryGetSimpleTypeNameWorker(funcDecl.BlockStatement.ParameterList.Parameters(0).AsClause?.Type, typeParameterNames, targetTypeName, isArray)
Return CreateReceiverTypeString(targetTypeName, isArray)
End Function
Public Overrides Function TryGetAliasesFromUsingDirective(node As SyntaxNode, ByRef aliases As ImmutableArray(Of (aliasName As String, name As String))) As Boolean
......@@ -488,8 +489,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols
#Disable Warning BC42030 ' Variable is passed by reference before it has been assigned a value
If simpleImportsClause.Alias IsNot Nothing AndAlso
TryGetSimpleTypeNameWorker(simpleImportsClause.Alias, Nothing, aliasName) AndAlso
TryGetSimpleTypeNameWorker(simpleImportsClause, Nothing, name) Then
TryGetSimpleTypeNameWorker(simpleImportsClause.Alias, Nothing, aliasName, False) AndAlso
TryGetSimpleTypeNameWorker(simpleImportsClause, Nothing, name, False) Then
#Enable Warning BC42030 ' Variable is passed by reference before it has been assigned a value
builder.Add((aliasName, name))
......@@ -505,7 +506,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols
Return False
End Function
Private Shared Function TryGetSimpleTypeNameWorker(node As SyntaxNode, typeParameterNames As ImmutableArray(Of String)?, ByRef simpleTypeName As String) As Boolean
Private Shared Function TryGetSimpleTypeNameWorker(node As SyntaxNode, typeParameterNames As ImmutableArray(Of String)?, ByRef simpleTypeName As String, ByRef isArray As Boolean) As Boolean
isArray = False
If TypeOf node Is IdentifierNameSyntax Then
Dim identifierName = DirectCast(node, IdentifierNameSyntax)
Dim text = identifierName.Identifier.Text
......@@ -513,14 +517,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols
Return simpleTypeName IsNot Nothing
ElseIf TypeOf node Is ArrayTypeSyntax Then
' We do Not differentiate array of different kinds for simplicity.
' e.g. int(), int()(), int(,), etc. are all represented as int[] in the index.
isArray = True
Dim arrayType = DirectCast(node, ArrayTypeSyntax)
Dim elementTypeName As String
#Disable Warning BC42030 ' Variable is passed by reference before it has been assigned a value
simpleTypeName = If(TryGetSimpleTypeNameWorker(arrayType.ElementType, typeParameterNames, elementTypeName), CreateTargetTypeStringForArray(elementTypeName), Nothing)
#Enable Warning BC42030 ' Variable is passed by reference before it has been assigned a value
Return simpleTypeName IsNot Nothing
Return TryGetSimpleTypeNameWorker(arrayType.ElementType, typeParameterNames, simpleTypeName, False)
ElseIf TypeOf node Is GenericNameSyntax Then
Dim genericName = DirectCast(node, GenericNameSyntax)
......@@ -533,10 +532,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols
' For an identifier to the right of a '.', it can't be a type parameter,
' so we don't need to check for it further.
Dim qualifiedName = DirectCast(node, QualifiedNameSyntax)
Return TryGetSimpleTypeNameWorker(qualifiedName.Right, Nothing, simpleTypeName)
Return TryGetSimpleTypeNameWorker(qualifiedName.Right, Nothing, simpleTypeName, False)
ElseIf TypeOf node Is NullableTypeSyntax Then
Return TryGetSimpleTypeNameWorker(DirectCast(node, NullableTypeSyntax).ElementType, typeParameterNames, simpleTypeName)
Return TryGetSimpleTypeNameWorker(DirectCast(node, NullableTypeSyntax).ElementType, typeParameterNames, simpleTypeName, isArray)
ElseIf TypeOf node Is PredefinedTypeSyntax Then
simpleTypeName = GetSpecialTypeName(DirectCast(node, PredefinedTypeSyntax))
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册