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

Implement filter for extension methods in PE references based on SymbolTreeIndex

上级 d4a44d3a
......@@ -20,20 +20,21 @@ internal class ExtensionMethodImportCompletionService
{
// return a multi-dictionary of mappings "FQN of containing type" => "extension method names"
public async Task<MultiDictionary<string, string>> GetPossibleExtensionMethodMatchesAsync(
Project project,
Project currentProject,
ISet<string> targetTypeNames,
CancellationToken cancellationToken)
{
var solution = project.Solution;
var graph = project.Solution.GetProjectDependencyGraph();
var dependencies = graph.GetProjectsThatThisProjectTransitivelyDependsOn(project.Id);
var solution = currentProject.Solution;
var graph = currentProject.Solution.GetProjectDependencyGraph();
var dependencies = graph.GetProjectsThatThisProjectTransitivelyDependsOn(currentProject.Id);
var relevantProjects = dependencies.Select(solution.GetProject)
.Where(p => p.SupportsCompilation)
.Concat(project);
.Concat(currentProject);
var results = new MultiDictionary<string, string>();
// Find matching extension methods from source.
foreach (var proj in relevantProjects)
foreach (var project in relevantProjects)
{
foreach (var document in project.Documents)
{
......@@ -72,10 +73,24 @@ internal class ExtensionMethodImportCompletionService
}
// Find matching extension methods from metadata
foreach (var peReference in project.MetadataReferences.OfType<PortableExecutableReference>())
foreach (var peReference in currentProject.MetadataReferences.OfType<PortableExecutableReference>())
{
var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
solution, peReference, loadOnly: false, cancellationToken).ConfigureAwait(false);
if (!info.ContainsExtensionMethod)
{
continue;
}
// Add simple extension methods with matching target type name
foreach (var targetTypeName in targetTypeNames)
{
foreach (var methodInfo in info.GetMatchingExtensionMethodInfo(targetTypeName))
{
results.Add(methodInfo.FullyQualifiedContainerName, methodInfo.Name);
}
}
}
return results;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindSymbols
{
......@@ -19,15 +23,17 @@ internal partial class SymbolTreeInfo
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
private struct BuilderNode
{
public static readonly BuilderNode RootNode = new BuilderNode("", RootNodeParentIndex);
public static readonly BuilderNode RootNode = new BuilderNode("", RootNodeParentIndex, default);
public readonly string Name;
public readonly int ParentIndex;
public readonly MultiDictionary<MetadataNode, ParameterTypeInfo>.ValueSet ParameterTypeInfos;
public BuilderNode(string name, int parentIndex)
public BuilderNode(string name, int parentIndex, MultiDictionary<MetadataNode, ParameterTypeInfo>.ValueSet parameterTypeInfos = default)
{
Name = name;
ParentIndex = parentIndex;
ParameterTypeInfos = parameterTypeInfos;
}
public bool IsRoot => ParentIndex == RootNodeParentIndex;
......@@ -72,5 +78,87 @@ private string GetDebuggerDisplay()
return NameSpan + ", " + ParentIndex;
}
}
public readonly struct ParameterTypeInfo
{
public string Name { get; }
public bool IsComplexType { get; }
public ParameterTypeInfo(string name, bool isComplex)
{
Name = name;
IsComplexType = isComplex;
}
public override string ToString()
=> $"{Name}, {IsComplexType}";
}
public readonly struct ExtensionMethodInfo
{
public string FullyQualifiedContainerName { get; }
public string Name { get; }
public ExtensionMethodInfo(string fullyQualifiedContainerName, string name)
{
FullyQualifiedContainerName = fullyQualifiedContainerName;
Name = name;
}
}
private sealed class ParameterTypeInfoProvider : ISignatureTypeProvider<ParameterTypeInfo, object>
{
public static readonly ParameterTypeInfoProvider Instance = new ParameterTypeInfoProvider();
private static ParameterTypeInfo ComplexInfo
=> new ParameterTypeInfo(string.Empty, isComplex: true);
public ParameterTypeInfo GetPrimitiveType(PrimitiveTypeCode typeCode)
=> new ParameterTypeInfo(typeCode.ToString(), isComplex: false);
public ParameterTypeInfo GetArrayType(ParameterTypeInfo elementType, ArrayShape shape) => ComplexInfo;
public ParameterTypeInfo GetSZArrayType(ParameterTypeInfo elementType) => ComplexInfo;
public ParameterTypeInfo GetGenericInstantiation(ParameterTypeInfo genericType, ImmutableArray<ParameterTypeInfo> typeArguments)
=> genericType.IsComplexType
? ComplexInfo
: new ParameterTypeInfo(genericType.Name, isComplex: false);
public ParameterTypeInfo GetByReferenceType(ParameterTypeInfo elementType) => ComplexInfo;
public ParameterTypeInfo GetFunctionPointerType(MethodSignature<ParameterTypeInfo> signature) => ComplexInfo;
public ParameterTypeInfo GetGenericMethodParameter(object genericContext, int index) => ComplexInfo;
public ParameterTypeInfo GetGenericTypeParameter(object genericContext, int index) => ComplexInfo;
public ParameterTypeInfo GetModifiedType(ParameterTypeInfo modifier, ParameterTypeInfo unmodifiedType, bool isRequired) => ComplexInfo;
public ParameterTypeInfo GetPinnedType(ParameterTypeInfo elementType) => ComplexInfo;
public ParameterTypeInfo GetPointerType(ParameterTypeInfo elementType) => ComplexInfo;
public ParameterTypeInfo GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
var type = reader.GetTypeDefinition(handle);
var name = reader.GetString(type.Name);
return new ParameterTypeInfo(name, isComplex: false);
}
public ParameterTypeInfo GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
var type = reader.GetTypeReference(handle);
var name = reader.GetString(type.Name);
return new ParameterTypeInfo(name, isComplex: false);
}
public ParameterTypeInfo GetTypeFromSpecification(MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
var sigReader = reader.GetBlobReader(reader.GetTypeSpecification(handle).Signature);
return new SignatureDecoder<ParameterTypeInfo, object>(Instance, reader, genericContext).DecodeType(ref sigReader);
}
}
}
}
......@@ -49,6 +49,27 @@ internal partial class SymbolTreeInfo : IChecksummedObject
/// </summary>
private readonly OrderPreservingMultiDictionary<int, int> _inheritanceMap;
private readonly MultiDictionary<string, ExtensionMethodInfo> _simpleTypeNameToExtensionMethodMap;
private readonly ImmutableArray<ExtensionMethodInfo> _extensionMethodOfComplexType;
public bool ContainsExtensionMethod => _simpleTypeNameToExtensionMethodMap?.Count > 0 || _extensionMethodOfComplexType.Length > 0;
public ImmutableArray<ExtensionMethodInfo> GetMatchingExtensionMethodInfo(string parameterTypeName)
{
var simpleMethods = _simpleTypeNameToExtensionMethodMap[parameterTypeName];
if (simpleMethods.Count == 0)
{
return _extensionMethodOfComplexType;
}
using var disposer = ArrayBuilder<ExtensionMethodInfo>.GetInstance(out var builder);
builder.AddRange(_extensionMethodOfComplexType);
builder.AddRange(simpleMethods);
return builder.ToImmutable();
}
/// <summary>
/// The task that produces the spell checker we use for fuzzy match queries.
/// We use a task so that we can generate the <see cref="SymbolTreeInfo"/>
......@@ -85,9 +106,12 @@ internal partial class SymbolTreeInfo : IChecksummedObject
string concatenatedNames,
ImmutableArray<Node> sortedNodes,
Task<SpellChecker> spellCheckerTask,
OrderPreservingMultiDictionary<string, string> inheritanceMap)
OrderPreservingMultiDictionary<string, string> inheritanceMap,
ImmutableArray<ExtensionMethodInfo> extensionMethodOfComplexType,
MultiDictionary<string, ExtensionMethodInfo> simpleTypeNameToExtensionMethodMap)
: this(checksum, concatenatedNames, sortedNodes, spellCheckerTask,
CreateIndexBasedInheritanceMap(concatenatedNames, sortedNodes, inheritanceMap))
CreateIndexBasedInheritanceMap(concatenatedNames, sortedNodes, inheritanceMap),
extensionMethodOfComplexType, simpleTypeNameToExtensionMethodMap)
{
}
......@@ -96,13 +120,33 @@ internal partial class SymbolTreeInfo : IChecksummedObject
string concatenatedNames,
ImmutableArray<Node> sortedNodes,
Task<SpellChecker> spellCheckerTask,
OrderPreservingMultiDictionary<int, int> inheritanceMap)
OrderPreservingMultiDictionary<int, int> inheritanceMap,
ImmutableArray<ExtensionMethodInfo> extensionMethodOfComplexType,
MultiDictionary<string, ExtensionMethodInfo> simpleTypeNameToExtensionMethodMap)
{
Checksum = checksum;
_concatenatedNames = concatenatedNames;
_nodes = sortedNodes;
_spellCheckerTask = spellCheckerTask;
_inheritanceMap = inheritanceMap;
_extensionMethodOfComplexType = extensionMethodOfComplexType;
_simpleTypeNameToExtensionMethodMap = simpleTypeNameToExtensionMethodMap;
}
private static MultiDictionary<string, ParameterTypeInfo> CreateTypeNameToTypeMap(MultiDictionary<ParameterTypeInfo, int> parameterTypeToNodeMap)
{
if (parameterTypeToNodeMap == null)
{
return null;
}
var parameterTypeNameToTypeMap = new MultiDictionary<string, ParameterTypeInfo>();
foreach (var info in parameterTypeToNodeMap.Keys)
{
parameterTypeNameToTypeMap.Add(info.Name, info);
}
return parameterTypeNameToTypeMap;
}
public static SymbolTreeInfo CreateEmpty(Checksum checksum)
......@@ -112,13 +156,15 @@ public static SymbolTreeInfo CreateEmpty(Checksum checksum)
return new SymbolTreeInfo(checksum, concatenatedNames, sortedNodes,
CreateSpellCheckerAsync(checksum, concatenatedNames, sortedNodes),
new OrderPreservingMultiDictionary<string, string>());
new OrderPreservingMultiDictionary<string, string>(),
ImmutableArray<ExtensionMethodInfo>.Empty,
new MultiDictionary<string, ExtensionMethodInfo>());
}
public SymbolTreeInfo WithChecksum(Checksum checksum)
{
return new SymbolTreeInfo(
checksum, _concatenatedNames, _nodes, _spellCheckerTask, _inheritanceMap);
checksum, _concatenatedNames, _nodes, _spellCheckerTask, _inheritanceMap, _extensionMethodOfComplexType, _simpleTypeNameToExtensionMethodMap);
}
public Task<ImmutableArray<SymbolAndProjectId>> FindAsync(
......@@ -528,7 +574,9 @@ internal void AssertEquivalentTo(SymbolTreeInfo other)
private static SymbolTreeInfo CreateSymbolTreeInfo(
Solution solution, Checksum checksum,
string filePath, ImmutableArray<BuilderNode> unsortedNodes,
OrderPreservingMultiDictionary<string, string> inheritanceMap)
OrderPreservingMultiDictionary<string, string> inheritanceMap,
MultiDictionary<string, ExtensionMethodInfo> simpleMethods,
ImmutableArray<ExtensionMethodInfo> complexMethods)
{
SortNodes(unsortedNodes, out var concatenatedNames, out var sortedNodes);
var createSpellCheckerTask = GetSpellCheckerTask(
......@@ -536,7 +584,8 @@ internal void AssertEquivalentTo(SymbolTreeInfo other)
return new SymbolTreeInfo(
checksum, concatenatedNames,
sortedNodes, createSpellCheckerTask, inheritanceMap);
sortedNodes, createSpellCheckerTask, inheritanceMap,
complexMethods, simpleMethods);
}
private static OrderPreservingMultiDictionary<int, int> CreateIndexBasedInheritanceMap(
......
......@@ -5,13 +5,13 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Serialization;
using Microsoft.CodeAnalysis.Utilities;
......@@ -229,6 +229,11 @@ private struct MetadataInfoCreator : IDisposable
// The set of type definitions we've read out of the current metadata reader.
private readonly List<MetadataDefinition> _allTypeDefinitions;
// Map from node represents extension method to list of possible parameter type info
// (which can be more than one because we might have multiple overloads).
private MultiDictionary<MetadataNode, ParameterTypeInfo> _extensionMethodToParameterTypeInfo;
private bool _containsExtensionsMethod;
public MetadataInfoCreator(
Solution solution, Checksum checksum, PortableExecutableReference reference, CancellationToken cancellationToken)
{
......@@ -238,9 +243,11 @@ private struct MetadataInfoCreator : IDisposable
_cancellationToken = cancellationToken;
_metadataReader = null;
_allTypeDefinitions = new List<MetadataDefinition>();
_containsExtensionsMethod = false;
_inheritanceMap = OrderPreservingMultiDictionary<string, string>.GetInstance();
_parentToChildren = OrderPreservingMultiDictionary<MetadataNode, MetadataNode>.GetInstance();
_extensionMethodToParameterTypeInfo = new MultiDictionary<MetadataNode, ParameterTypeInfo>();
_rootNode = MetadataNode.Allocate(name: "");
}
......@@ -295,9 +302,12 @@ internal SymbolTreeInfo Create()
}
}
var unsortedNodes = GenerateUnsortedNodes();
var simpleMap = new MultiDictionary<string, ExtensionMethodInfo>();
var complexBuilder = ArrayBuilder<ExtensionMethodInfo>.GetInstance();
var unsortedNodes = GenerateUnsortedNodes(complexBuilder, simpleMap);
return CreateSymbolTreeInfo(
_solution, _checksum, _reference.FilePath, unsortedNodes, _inheritanceMap);
_solution, _checksum, _reference.FilePath, unsortedNodes, _inheritanceMap, simpleMap, complexBuilder.ToImmutableAndFree());
}
public void Dispose()
......@@ -356,6 +366,11 @@ private void GenerateMetadataNodes()
{
foreach (var definition in definitionsWithSameName)
{
if (definition.Kind == MetadataDefinitionKind.Member)
{
_extensionMethodToParameterTypeInfo.Add(childNode, definition.TargetTypeInfo);
}
LookupMetadataDefinitions(definition, definitionMap);
}
......@@ -407,10 +422,18 @@ private void GenerateMetadataNodes()
// we just pull in methods that have attributes on them.
if ((method.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public &&
(method.Attributes & MethodAttributes.Static) != 0 &&
method.GetParameters().Count > 0 &&
method.GetCustomAttributes().Count > 0)
{
var definition = new MetadataDefinition(
MetadataDefinitionKind.Member, _metadataReader.GetString(method.Name));
_containsExtensionsMethod = true;
// Decode method signature to get the target 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);
var firstParameterTypeInfo = signature.ParameterTypes[0];
var definition = new MetadataDefinition(MetadataDefinitionKind.Member, _metadataReader.GetString(method.Name), firstParameterTypeInfo);
definitionMap.Add(definition.Name, definition);
}
......@@ -665,26 +688,55 @@ private void EnsureParentsAndChildren(List<string> simpleNames)
return newChildNode;
}
private ImmutableArray<BuilderNode> GenerateUnsortedNodes()
private ImmutableArray<BuilderNode> GenerateUnsortedNodes(ArrayBuilder<ExtensionMethodInfo> complexBuilder, MultiDictionary<string, ExtensionMethodInfo> simpleTypeNameToMethodMap)
{
var unsortedNodes = ArrayBuilder<BuilderNode>.GetInstance();
unsortedNodes.Add(BuilderNode.RootNode);
AddUnsortedNodes(unsortedNodes, parentNode: _rootNode, parentIndex: 0);
AddUnsortedNodes(unsortedNodes, simpleTypeNameToMethodMap, complexBuilder, parentNode: _rootNode, parentIndex: 0, fullyQualifiedContainerName: _containsExtensionsMethod ? "" : null);
return unsortedNodes.ToImmutableAndFree();
}
private void AddUnsortedNodes(
ArrayBuilder<BuilderNode> unsortedNodes, MetadataNode parentNode, int parentIndex)
ArrayBuilder<BuilderNode> unsortedNodes, MultiDictionary<string, ExtensionMethodInfo> simpleBuilder, ArrayBuilder<ExtensionMethodInfo> complexBuilder, MetadataNode parentNode, int parentIndex, string fullyQualifiedContainerName)
{
foreach (var child in _parentToChildren[parentNode])
{
var childNode = new BuilderNode(child.Name, parentIndex);
var childNode = new BuilderNode(child.Name, parentIndex, _extensionMethodToParameterTypeInfo[child]);
var childIndex = unsortedNodes.Count;
unsortedNodes.Add(childNode);
AddUnsortedNodes(unsortedNodes, child, childIndex);
if (fullyQualifiedContainerName != null)
{
foreach (var parameterTypeInfo in _extensionMethodToParameterTypeInfo[child])
{
if (parameterTypeInfo.IsComplexType)
{
complexBuilder.Add(new ExtensionMethodInfo(fullyQualifiedContainerName, child.Name));
}
else
{
simpleBuilder.Add(parameterTypeInfo.Name, new ExtensionMethodInfo(fullyQualifiedContainerName, child.Name));
}
}
}
AddUnsortedNodes(unsortedNodes, simpleBuilder, complexBuilder, child, childIndex, Concat(fullyQualifiedContainerName, child.Name));
}
static string Concat(string containerName, string name)
{
if (containerName == null)
{
return null;
}
if (containerName.Length == 0)
{
return name;
}
return containerName + "." + name;
}
}
}
......@@ -727,14 +779,16 @@ private struct MetadataDefinition
public string Name { get; }
public MetadataDefinitionKind Kind { get; }
public ParameterTypeInfo TargetTypeInfo { get; }
public NamespaceDefinition Namespace { get; private set; }
public TypeDefinition Type { get; private set; }
public MetadataDefinition(MetadataDefinitionKind kind, string name)
public MetadataDefinition(MetadataDefinitionKind kind, string name, ParameterTypeInfo targetTypeInfo = default)
: this()
{
Kind = kind;
Name = name;
TargetTypeInfo = targetTypeInfo;
}
public static MetadataDefinition Create(
......
......@@ -10,7 +10,6 @@
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Serialization;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Utilities;
using Roslyn.Utilities;
......@@ -153,6 +152,35 @@ public void WriteTo(ObjectWriter writer)
writer.WriteInt32(v);
}
}
if (_simpleTypeNameToExtensionMethodMap == null)
{
writer.WriteInt32(0);
}
else
{
writer.WriteInt32(_simpleTypeNameToExtensionMethodMap.Count);
foreach (var key in _simpleTypeNameToExtensionMethodMap.Keys)
{
writer.WriteString(key);
var values = _simpleTypeNameToExtensionMethodMap[key];
writer.WriteInt32(values.Count);
foreach (var value in values)
{
writer.WriteString(value.FullyQualifiedContainerName);
writer.WriteString(value.Name);
}
}
}
writer.WriteInt32(_extensionMethodOfComplexType.Length);
foreach (var methodInfo in _extensionMethodOfComplexType)
{
writer.WriteString(methodInfo.FullyQualifiedContainerName);
writer.WriteString(methodInfo.Name);
}
}
internal static SymbolTreeInfo ReadSymbolTreeInfo_ForTestingPurposesOnly(
......@@ -197,9 +225,56 @@ public void WriteTo(ObjectWriter writer)
}
}
MultiDictionary<string, ExtensionMethodInfo> simpleTypeNameToExtensionMethodMap;
ImmutableArray<ExtensionMethodInfo> extensionMethodOfComplexType;
var keyCount = reader.ReadInt32();
if (keyCount == 0)
{
simpleTypeNameToExtensionMethodMap = null;
}
else
{
simpleTypeNameToExtensionMethodMap = new MultiDictionary<string, ExtensionMethodInfo>();
for (var i = 0; i < keyCount; i++)
{
var typeName = reader.ReadString();
var valueCount = reader.ReadInt32();
for (var j = 0; j < valueCount; j++)
{
var containerName = reader.ReadString();
var name = reader.ReadString();
simpleTypeNameToExtensionMethodMap.Add(typeName, new ExtensionMethodInfo(containerName, name));
}
}
}
var arrayLength = reader.ReadInt32();
if (arrayLength == 0)
{
extensionMethodOfComplexType = ImmutableArray<ExtensionMethodInfo>.Empty;
}
else
{
var builder = ArrayBuilder<ExtensionMethodInfo>.GetInstance(arrayLength);
for (var i = 0; i < arrayLength; ++i)
{
var containerName = reader.ReadString();
var name = reader.ReadString();
builder.Add(new ExtensionMethodInfo(containerName, name));
}
extensionMethodOfComplexType = builder.ToImmutableAndFree();
}
var nodeArray = nodes.ToImmutableAndFree();
var spellCheckerTask = createSpellCheckerTask(concatenatedNames, nodeArray);
return new SymbolTreeInfo(checksum, concatenatedNames, nodeArray, spellCheckerTask, inheritanceMap);
return new SymbolTreeInfo(
checksum, concatenatedNames, nodeArray, spellCheckerTask, inheritanceMap,
extensionMethodOfComplexType, simpleTypeNameToExtensionMethodMap);
}
catch
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
......@@ -117,7 +118,9 @@ private static async Task<Checksum> ComputeSourceSymbolsChecksumAsync(ProjectSta
return CreateSymbolTreeInfo(
project.Solution, checksum, project.FilePath, unsortedNodes.ToImmutableAndFree(),
inheritanceMap: new OrderPreservingMultiDictionary<string, string>());
inheritanceMap: new OrderPreservingMultiDictionary<string, string>(),
null,
ImmutableArray<ExtensionMethodInfo>.Empty);
}
// generate nodes for the global namespace an all descendants
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册