提交 80ed32ec 编写于 作者: P Paul Vick

IVT, new API and cleanup

上级 42282984
......@@ -89,7 +89,7 @@ protected static async Task RunMethodReferenceTest(XElement input)
foreach (var span in annotatedSpan.Value)
{
var declarationSyntaxNode = syntaxNode.FindNode(span);
var result = await new CodeLensReferenceService().FindMethodReferenceLocationsAsync(workspace.CurrentSolution,
var result = await new CodeLensReferenceService().FindReferenceMethodsAsync(workspace.CurrentSolution,
annotatedDocument.Id, declarationSyntaxNode, CancellationToken.None);
var count = result.Count();
Assert.Equal(expected, count);
......
......@@ -51,60 +51,48 @@ internal sealed class CodeLensFindReferencesProgress : IFindReferencesProgress,
_queriedSymbol = queriedDefinition;
_queriedNode = queriedNode;
_aggregateCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
_locations = new ConcurrentSet<Location>();
_locations = new ConcurrentSet<Location>(LocationComparer.Instance);
SearchCap = searchCap;
}
public void OnStarted()
{
}
public void OnCompleted()
{
}
/// <summary>
/// Returns partial symbol locations whose node does not match the given syntaxNode
/// </summary>
/// <param name="symbol">Symbol whose locations are queried</param>
/// <param name="syntaxNode">Syntax node to compare against to exclude location - actual location being queried</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Partial locations</returns>
private static IEnumerable<Location> GetPartialLocations(ISymbol symbol, SyntaxNode syntaxNode, CancellationToken cancellationToken)
public void OnFindInDocumentStarted(Document document)
{
// Returns nodes from source not equal to actual location and matches the definition locations to syntax references, ignores metadata locations
return from syntaxReference in symbol.DeclaringSyntaxReferences
let candidateSyntaxNode = syntaxReference.GetSyntax(cancellationToken)
where !(syntaxNode.Span == candidateSyntaxNode.Span &&
syntaxNode.SyntaxTree.FilePath.Equals(candidateSyntaxNode.SyntaxTree.FilePath, StringComparison.OrdinalIgnoreCase))
from sourceLocation in symbol.Locations
where !sourceLocation.IsInMetadata
&& candidateSyntaxNode.SyntaxTree.Equals(sourceLocation.SourceTree)
&& candidateSyntaxNode.Span.Contains(sourceLocation.SourceSpan)
select sourceLocation;
}
/// <summary>
/// Exclude the following kind of symbols:
/// 1. Implicitly declared symbols (such as implicit fields backing properties)
/// 2. Symbols that can't be referenced by name (such as property getters and setters).
/// 3. Metadata only symbols, i.e. symbols with no location in source.
/// </summary>
private static bool FilterReference(ISymbol queriedSymbol, ISymbol definition, ReferenceLocation reference)
public void OnFindInDocumentCompleted(Document document)
{
var isImplicitlyDeclared = definition.IsImplicitlyDeclared || definition.IsAccessor();
// FindRefs treats a constructor invocation as a reference to the constructor symbol and to the named type symbol that defines it.
// While we need to count the cascaded symbol definition from the named type to its constructor, we should not double count the
// reference location for the invocation while computing references count for the named type symbol.
var isImplicitReference = queriedSymbol.Kind == SymbolKind.NamedType &&
(definition as IMethodSymbol)?.MethodKind == MethodKind.Constructor;
return isImplicitlyDeclared ||
isImplicitReference ||
(!definition.Locations.Any(loc => loc.IsInSource) && !reference.Location.IsInSource);
}
private static bool FilterDefinition(ISymbol definition)
{
return definition.IsImplicitlyDeclared ||
(definition as IMethodSymbol)?.AssociatedSymbol != null ||
!definition.Locations.Any(loc => loc.IsInSource);
(definition as IMethodSymbol)?.AssociatedSymbol != null;
}
/// <summary>
/// Returns partial symbol locations whose node does not match the queried syntaxNode
/// </summary>
/// <param name="symbol">Symbol whose locations are queried</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Partial locations</returns>
private IEnumerable<Location> GetPartialLocations(ISymbol symbol, CancellationToken cancellationToken)
{
// Returns nodes from source not equal to actual location
return from syntaxReference in symbol.DeclaringSyntaxReferences
let candidateSyntaxNode = syntaxReference.GetSyntax(cancellationToken)
where !(_queriedNode.Span == candidateSyntaxNode.Span &&
_queriedNode.SyntaxTree.FilePath.Equals(candidateSyntaxNode.SyntaxTree.FilePath,
StringComparison.OrdinalIgnoreCase))
select syntaxReference.SyntaxTree.GetLocation(syntaxReference.Span);
}
public void OnDefinitionFound(ISymbol symbol)
......@@ -118,9 +106,11 @@ public void OnDefinitionFound(ISymbol symbol)
// Add remote locations for all the syntax references except the queried syntax node.
// To query for the partial locations, filter definition locations that occur in source whose span is part of
// span of any syntax node from Definition.DeclaringSyntaxReferences except for the queried syntax node.
_locations.AddRange(symbol.Locations.Intersect(_queriedSymbol.Locations, LocationComparer.Instance).Any()
? GetPartialLocations(symbol, _queriedNode, _aggregateCancellationTokenSource.Token)
: symbol.Locations);
var locations = symbol.Locations.Intersect(_queriedSymbol.Locations, LocationComparer.Instance).Any()
? GetPartialLocations(symbol, _aggregateCancellationTokenSource.Token)
: symbol.Locations;
_locations.AddRange(locations.Where(location => location.IsInSource));
if (SearchCapReached)
{
......@@ -128,17 +118,29 @@ public void OnDefinitionFound(ISymbol symbol)
}
}
public void OnFindInDocumentCompleted(Document document)
{
}
public void OnFindInDocumentStarted(Document document)
/// <summary>
/// Exclude the following kind of symbols:
/// 1. Implicitly declared symbols (such as implicit fields backing properties)
/// 2. Symbols that can't be referenced by name (such as property getters and setters).
/// 3. Metadata only symbols, i.e. symbols with no location in source.
/// </summary>
private bool FilterReference(ISymbol definition, ReferenceLocation reference)
{
var isImplicitlyDeclared = definition.IsImplicitlyDeclared || definition.IsAccessor();
// FindRefs treats a constructor invocation as a reference to the constructor symbol and to the named type symbol that defines it.
// While we need to count the cascaded symbol definition from the named type to its constructor, we should not double count the
// reference location for the invocation while computing references count for the named type symbol.
var isImplicitReference = _queriedSymbol.Kind == SymbolKind.NamedType &&
(definition as IMethodSymbol)?.MethodKind == MethodKind.Constructor;
return isImplicitlyDeclared ||
isImplicitReference ||
!reference.Location.IsInSource ||
!definition.Locations.Any(loc => loc.IsInSource);
}
public void OnReferenceFound(ISymbol symbol, ReferenceLocation location)
{
if (FilterReference(_queriedSymbol, symbol, location))
if (FilterReference(symbol, location))
{
return;
}
......@@ -151,10 +153,6 @@ public void OnReferenceFound(ISymbol symbol, ReferenceLocation location)
}
}
public void OnStarted()
{
}
public void ReportProgress(int current, int maximum)
{
}
......
......@@ -22,7 +22,7 @@ internal sealed class CodeLensReferenceService : ICodeLensReferencesService
new SymbolDisplayFormat(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
private async Task<T> FindAsync<T>(Solution solution, DocumentId documentId, SyntaxNode syntaxNode,
private static async Task<T> FindAsync<T>(Solution solution, DocumentId documentId, SyntaxNode syntaxNode,
Func<CodeLensFindReferencesProgress, Task<T>> onResults, Func<CodeLensFindReferencesProgress, Task<T>> onCapped,
int searchCap, CancellationToken cancellationToken) where T: class
{
......@@ -186,11 +186,7 @@ public async Task<IEnumerable<ReferenceLocationDescriptor>> FindReferenceLocatio
async progress =>
{
var referenceTasks = progress.Locations
.Where(location => location.Kind != LocationKind.MetadataFile && location.Kind != LocationKind.None)
.Distinct(LocationComparer.Instance)
.Select(
location =>
GetDescriptorOfEnclosingSymbolAsync(solution, location, cancellationToken))
.Select(location => GetDescriptorOfEnclosingSymbolAsync(solution, location, cancellationToken))
.ToArray();
await Task.WhenAll(referenceTasks).ConfigureAwait(false);
......@@ -225,7 +221,7 @@ private static ISymbol GetEnclosingMethod(SemanticModel semanticModel, Location
return null;
}
private static async Task<ReferenceMethodDescriptor> TryCreateMethodDescriptorAsync(Location commonLocation, Solution solution, CancellationToken cancellationToken)
private static async Task<ReferenceMethodDescriptor> GetMethodDescriptorAsync(Location commonLocation, Solution solution, CancellationToken cancellationToken)
{
var doc = solution.GetDocument(commonLocation.SourceTree);
if (doc == null)
......@@ -245,16 +241,14 @@ private static async Task<ReferenceMethodDescriptor> TryCreateMethodDescriptorAs
return !string.IsNullOrEmpty(fullName) ? new ReferenceMethodDescriptor(fullName, document.FilePath) : null;
}
public async Task<IEnumerable<ReferenceMethodDescriptor>> FindMethodReferenceLocationsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, CancellationToken cancellationToken)
public async Task<IEnumerable<ReferenceMethodDescriptor>> FindReferenceMethodsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, CancellationToken cancellationToken)
{
return await FindAsync(solution, documentId, syntaxNode,
async progress =>
{
var descriptorTasks =
progress.Locations
.Where(location => location.Kind != LocationKind.MetadataFile && location.Kind != LocationKind.None)
.Select(location =>
TryCreateMethodDescriptorAsync(location, solution, cancellationToken))
.Select(location => GetMethodDescriptorAsync(location, solution, cancellationToken))
.ToArray();
await Task.WhenAll(descriptorTasks).ConfigureAwait(false);
......@@ -262,5 +256,25 @@ public async Task<IEnumerable<ReferenceMethodDescriptor>> FindMethodReferenceLoc
return descriptorTasks.Where(task => task.Result != null).Select(task => task.Result);
}, onCapped: null, searchCap: 0, cancellationToken: cancellationToken).ConfigureAwait(false);
}
public async Task<string> GetFullyQualifiedName(Solution solution, DocumentId documentId, SyntaxNode syntaxNode,
CancellationToken cancellationToken)
{
var commonLocation = syntaxNode.GetLocation();
var doc = solution.GetDocument(commonLocation.SourceTree);
if (doc == null)
{
return null;
}
var document = solution.GetDocument(doc.Id);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
if (semanticModel == null)
{
return null;
}
return GetEnclosingMethod(semanticModel, commonLocation)?.ToDisplayString(MethodDisplayFormat);
}
}
}
......@@ -26,7 +26,13 @@ internal interface ICodeLensReferencesService : IWorkspaceService
/// <summary>
/// Given a document and syntax node, returns a collection of locations of methods that refer to the located node.
/// </summary>
Task<IEnumerable<ReferenceMethodDescriptor>> FindMethodReferenceLocationsAsync(Solution solution,
Task<IEnumerable<ReferenceMethodDescriptor>> FindReferenceMethodsAsync(Solution solution,
DocumentId documentId, SyntaxNode syntaxNode, CancellationToken cancellationToken);
/// <summary>
/// Given a document and syntax node, returns the fully qualified name of the located node's declaration.
/// </summary>
Task<string> GetFullyQualifiedName(Solution solution, DocumentId documentId, SyntaxNode syntaxNode,
CancellationToken cancellationToken);
}
}
......@@ -81,6 +81,7 @@
<InternalsVisibleToTypeScript Include="Roslyn.Services.Editor.TypeScript.UnitTests" />
<InternalsVisibleToFSharp Include="FSharp.Editor" />
<InternalsVisibleToMoq Include="DynamicProxyGenAssembly2" />
<InternalsVisibleToVisualStudio Include="Microsoft.VisualStudio.Alm.Shared.CodeAnalysisClient" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\Compilers\Shared\DesktopShim.cs">
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册