提交 d1a672eb 编写于 作者: C Cyrus Najmabadi

Find-Refs on an accessor should only produce results for that accessor.

上级 96f0c6d8
......@@ -134,7 +134,7 @@ internal abstract partial class AbstractFindUsagesService : IFindUsagesService
await context.SetSearchTitleAsync(string.Format(EditorFeaturesResources._0_references,
FindUsagesHelpers.GetDisplayName(symbol))).ConfigureAwait(false);
var options = FindReferencesSearchOptions.Default;
var options = FindReferencesSearchOptions.GetFeatureOptionsForStartingSymbol(symbol);
var progressAdapter = new FindReferencesProgressAdapter(project.Solution, context, options);
// Now call into the underlying FAR engine to find reference. The FAR
......
......@@ -111,7 +111,7 @@ private static async Task<ISymbol> GetSymbolToSearchAsync(Document document, int
var progress = new StreamingProgressCollector(
StreamingFindReferencesProgress.Instance);
var options = FindReferencesSearchOptions.Default;
var options = FindReferencesSearchOptions.GetFeatureOptionsForStartingSymbol(symbol);
await SymbolFinder.FindReferencesAsync(
symbolAndProjectId, document.Project.Solution, progress,
documentsToSearch, options, cancellationToken).ConfigureAwait(false);
......
......@@ -21,11 +21,43 @@ namespace Microsoft.CodeAnalysis.FindSymbols
internal class FindReferencesSearchOptions
{
public static readonly FindReferencesSearchOptions Default =
new FindReferencesSearchOptions();
new FindReferencesSearchOptions(associatePropertyReferencesWithSpecificAccessor: false);
public FindReferencesSearchOptions()
/// <summary>
/// When searching for property, associate specific references we find to the relevant
/// accessor symbol (if there is one). For example, in C#, this would result in:
///
/// P = 0; // A reference to the P.set accessor
/// var v = P; // A reference to the P.get accessor
/// P++; // A reference to P.get and P.set accessors
/// nameof(P); // A reference only to P. Not associated with a particular accessor.
///
/// The default for this is false. With that default, all of the above references
/// are associated with the property P and not the accessors.
/// </summary>
public bool AssociatePropertyReferencesWithSpecificAccessor { get; }
public FindReferencesSearchOptions(
bool associatePropertyReferencesWithSpecificAccessor)
{
AssociatePropertyReferencesWithSpecificAccessor = associatePropertyReferencesWithSpecificAccessor;
}
public FindReferencesSearchOptions WithAssociatePropertyReferencesWithSpecificAccessor(
bool associatePropertyReferencesWithSpecificAccessor)
{
return new FindReferencesSearchOptions(associatePropertyReferencesWithSpecificAccessor);
}
/// <summary>
/// For IDE features, if the user starts searching on an accessor, then we want to give
/// results associated with the specific accessor. Otherwise, if they search on a property,
/// then associate everything with the property.
/// </summary>
public static FindReferencesSearchOptions GetFeatureOptionsForStartingSymbol(ISymbol symbol)
=> symbol.IsPropertyAccessor()
? new FindReferencesSearchOptions(associatePropertyReferencesWithSpecificAccessor: true)
: FindReferencesSearchOptions.Default;
}
internal partial class FindReferencesSearchEngine
......
......@@ -57,5 +57,27 @@ protected AbstractMethodOrPropertyOrEventSymbolReferenceFinder()
return ImmutableArray<SymbolAndProjectId>.Empty;
}
protected ImmutableArray<IMethodSymbol> GetReferencedAccessorSymbols(
ISemanticFactsService semanticFacts, SemanticModel model,
IPropertySymbol property, SyntaxNode node, CancellationToken cancellationToken)
{
if (semanticFacts.IsWrittenTo(model, node, cancellationToken))
{
// if it was only written to, then only the setter was referenced.
// if it was written *and* read, then both accessors were referenced.
return semanticFacts.IsOnlyWrittenTo(model, node, cancellationToken)
? ImmutableArray.Create(property.SetMethod)
: ImmutableArray.Create(property.GetMethod, property.SetMethod);
}
else
{
// Wasn't written. This could be a normal read, or used through something
// like nameof().
return semanticFacts.IsNameOfContext(model, node.SpanStart, cancellationToken)
? ImmutableArray<IMethodSymbol>.Empty
: ImmutableArray.Create(property.GetMethod);
}
}
}
}
// 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.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindSymbols.Finders
{
internal class PropertyAccessorSymbolReferenceFinder : AbstractMethodOrPropertyOrEventSymbolReferenceFinder<IMethodSymbol>
{
protected override bool CanFind(IMethodSymbol symbol)
{
return symbol.MethodKind.IsPropertyAccessor();
}
=> symbol.MethodKind.IsPropertyAccessor();
protected override async Task<ImmutableArray<SymbolAndProjectId>> DetermineCascadedSymbolsAsync(
SymbolAndProjectId<IMethodSymbol> symbolAndProjectId,
......@@ -26,27 +23,70 @@ protected override bool CanFind(IMethodSymbol symbol)
var result = await base.DetermineCascadedSymbolsAsync(
symbolAndProjectId, solution, projects, options, cancellationToken).ConfigureAwait(false);
var symbol = symbolAndProjectId.Symbol;
if (symbol.AssociatedSymbol != null)
// If we've been asked to search for specific accessors, then do not cascade.
// We don't want to produce results for the associated property.
if (!options.AssociatePropertyReferencesWithSpecificAccessor)
{
result = result.Add(symbolAndProjectId.WithSymbol(symbol.AssociatedSymbol));
var symbol = symbolAndProjectId.Symbol;
if (symbol.AssociatedSymbol != null)
{
result = result.Add(symbolAndProjectId.WithSymbol(symbol.AssociatedSymbol));
}
}
return result;
}
protected override Task<ImmutableArray<Document>> DetermineDocumentsToSearchAsync(
protected override async Task<ImmutableArray<Document>> DetermineDocumentsToSearchAsync(
IMethodSymbol symbol, Project project, IImmutableSet<Document> documents,
FindReferencesSearchOptions options, CancellationToken cancellationToken)
{
return FindDocumentsAsync(project, documents, cancellationToken, symbol.Name);
var result = await FindDocumentsAsync(
project, documents, cancellationToken, symbol.Name).ConfigureAwait(false);
if (options.AssociatePropertyReferencesWithSpecificAccessor)
{
// we want to associate normal property references with the specific
// accessor being referenced. So we also need to include documents
// with our property's name.
var propDocuments = await FindDocumentsAsync(
project, documents, cancellationToken, symbol.AssociatedSymbol.Name).ConfigureAwait(false);
result = result.AddRange(propDocuments);
}
return result;
}
protected override Task<ImmutableArray<ReferenceLocation>> FindReferencesInDocumentAsync(
protected override async Task<ImmutableArray<ReferenceLocation>> FindReferencesInDocumentAsync(
IMethodSymbol symbol, Document document, SemanticModel semanticModel,
FindReferencesSearchOptions options, CancellationToken cancellationToken)
{
return FindReferencesInDocumentUsingSymbolNameAsync(symbol, document, semanticModel, cancellationToken);
var references = await FindReferencesInDocumentUsingSymbolNameAsync(
symbol, document, semanticModel, cancellationToken).ConfigureAwait(false);
if (symbol.AssociatedSymbol is IPropertySymbol property &&
options.AssociatePropertyReferencesWithSpecificAccessor)
{
var propertyReferences = await ReferenceFinders.Property.FindReferencesInDocumentAsync(
SymbolAndProjectId.Create(property, document.Project.Id),
document, semanticModel, options.WithAssociatePropertyReferencesWithSpecificAccessor(false),
cancellationToken).ConfigureAwait(false);
var semanticFacts = document.GetLanguageService<ISemanticFactsService>();
var accessorReferences = propertyReferences.WhereAsArray(
loc =>
{
var accessors = GetReferencedAccessorSymbols(
semanticFacts, semanticModel, property,
loc.Location.FindNode(getInnermostNodeForTie: true, cancellationToken),
cancellationToken);
return accessors.Contains(symbol);
});
references = references.AddRange(accessorReferences);
}
return references;
}
}
}
......@@ -93,6 +93,23 @@ private static bool IsForEachProperty(IPropertySymbol symbol)
var nameReferences = await FindReferencesInDocumentUsingSymbolNameAsync(
symbol, document, semanticModel, cancellationToken).ConfigureAwait(false);
if (options.AssociatePropertyReferencesWithSpecificAccessor)
{
// We want to associate property references to a specific accessor (if an accessor
// is being referenced). Check if this reference would match an accessor. if so, do
// not add it. It will be added by PropertyAccessorSymbolReferenceFinder.
var semanticFacts = document.GetLanguageService<ISemanticFactsService>();
nameReferences = nameReferences.WhereAsArray(loc =>
{
var accessors = GetReferencedAccessorSymbols(
semanticFacts, semanticModel, symbol,
loc.Location.FindNode(getInnermostNodeForTie: true, cancellationToken),
cancellationToken);
return accessors.Length == 0;
});
}
var forEachReferences = IsForEachProperty(symbol)
? await FindReferencesInForEachStatementsAsync(symbol, document, semanticModel, cancellationToken).ConfigureAwait(false)
: ImmutableArray<ReferenceLocation>.Empty;
......
......@@ -130,7 +130,8 @@ public static partial class SymbolFinder
{
foreach (var interfaceType in GetAllInterfaces(type))
{
if (interfaceType.Symbol.MemberNames.Contains(symbol.Name))
if (interfaceType.Symbol.MemberNames.Contains(symbol.Name) ||
symbol.IsPropertyAccessor())
{
foreach (var m in GetMembers(interfaceType, symbol.Name))
{
......
......@@ -14,16 +14,19 @@ namespace Microsoft.CodeAnalysis.Remote
internal class SerializableFindReferencesSearchOptions
{
public bool AssociatePropertyReferencesWithSpecificAccessor;
public static SerializableFindReferencesSearchOptions Dehydrate(FindReferencesSearchOptions options)
{
return new SerializableFindReferencesSearchOptions
{
AssociatePropertyReferencesWithSpecificAccessor = options.AssociatePropertyReferencesWithSpecificAccessor
};
}
public FindReferencesSearchOptions Rehydrate()
{
return new FindReferencesSearchOptions();
return new FindReferencesSearchOptions(AssociatePropertyReferencesWithSpecificAccessor);
}
}
......
......@@ -50,10 +50,23 @@ internal static partial class IFindReferencesResultExtensions
return false;
}
// We don't want to clutter the UI with property accessors if there are no direct
if (definition.IsPropertyAccessor())
// If we're associating property references with an accessor, then we don't want to show
// a property if it is has no references. Similarly, if we're associated associating
// everything with the property, then we don't want to include accessors if there are no
// references to them.
if (options.AssociatePropertyReferencesWithSpecificAccessor)
{
return false;
if (definition.Kind == SymbolKind.Property)
{
return false;
}
}
else
{
if (definition.IsPropertyAccessor())
{
return false;
}
}
// Otherwise we still show the item even if there are no references to it.
......
......@@ -135,9 +135,15 @@ public static bool IsImplementableMember(this ISymbol symbol)
return true;
}
if (symbol.Kind == SymbolKind.Method && ((IMethodSymbol)symbol).MethodKind == MethodKind.Ordinary)
if (symbol.Kind == SymbolKind.Method)
{
return true;
var methodSymbol = (IMethodSymbol)symbol;
if (methodSymbol.MethodKind == MethodKind.Ordinary ||
methodSymbol.MethodKind == MethodKind.PropertyGet ||
methodSymbol.MethodKind == MethodKind.PropertySet)
{
return true;
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册