Skip to content

Commit

Permalink
Merge pull request dotnet#54654 from CyrusNajmabadi/farOrder2
Browse files Browse the repository at this point in the history
Update FAR engine to be able to work without having access to all projects at the same time.
  • Loading branch information
CyrusNajmabadi authored Jul 15, 2021
2 parents 9087236 + 53896ea commit debd668
Show file tree
Hide file tree
Showing 33 changed files with 716 additions and 707 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,17 @@ internal class DelegateInvokeMethodReferenceFinder : AbstractReferenceFinder<IMe
protected override bool CanFind(IMethodSymbol symbol)
=> symbol.MethodKind == MethodKind.DelegateInvoke;

protected override async Task<ImmutableArray<(ISymbol symbol, FindReferencesCascadeDirection cascadeDirection)>> DetermineCascadedSymbolsAsync(
protected override async Task<ImmutableArray<ISymbol>> DetermineCascadedSymbolsAsync(
IMethodSymbol symbol,
Solution solution,
IImmutableSet<Project> projects,
FindReferencesSearchOptions options,
FindReferencesCascadeDirection cascadeDirection,
CancellationToken cancellationToken)
{
using var _ = ArrayBuilder<(ISymbol symbol, FindReferencesCascadeDirection cascadeDirection)>.GetInstance(out var result);
using var _ = ArrayBuilder<ISymbol>.GetInstance(out var result);

var beginInvoke = symbol.ContainingType.GetMembers(WellKnownMemberNames.DelegateBeginInvokeName).FirstOrDefault();
if (beginInvoke != null)
result.Add((beginInvoke, cascadeDirection));
result.Add(beginInvoke);

// All method group references
foreach (var project in solution.Projects)
Expand All @@ -55,7 +53,7 @@ protected override bool CanFind(IMethodSymbol symbol)
var changeSignatureService = document.GetLanguageService<AbstractChangeSignatureService>();
var cascaded = await changeSignatureService.DetermineCascadedSymbolsFromDelegateInvokeAsync(
symbol, document, cancellationToken).ConfigureAwait(false);
result.AddRange(cascaded.SelectAsArray(s => (s, cascadeDirection)));
result.AddRange(cascaded);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,33 @@ private static async Task<ImmutableArray<INamedTypeSymbol>> DescendInheritanceTr
// are passed in. There is no need to check D as there's no way it could
// contribute an intermediate type that affects A or C. We only need to check
// A, B and C
//
// An exception to the above rule is if we're just searching a single project.
// in that case there can be no intermediary projects that could add types.
// So we can just limit ourselves to that single project.

// First find all the projects that could potentially reference this type.
var projectsThatCouldReferenceType = await GetProjectsThatCouldReferenceTypeAsync(
type, solution, searchInMetadata, cancellationToken).ConfigureAwait(false);
List<Project> orderedProjectsToExamine;

// Now, based on the list of projects that could actually reference the type,
// and the list of projects the caller wants to search, find the actual list of
// projects we need to search through.
//
// This list of projects is properly topologically ordered. Because of this we
// can just process them in order from first to last because we know no project
// in this list could affect a prior project.
var orderedProjectsToExamine = GetOrderedProjectsToExamine(
solution, projects, projectsThatCouldReferenceType);
if (projects.Count == 1)
{
orderedProjectsToExamine = projects.ToList();
}
else
{
var projectsThatCouldReferenceType = await GetProjectsThatCouldReferenceTypeAsync(
type, solution, searchInMetadata, cancellationToken).ConfigureAwait(false);

// Now, based on the list of projects that could actually reference the type,
// and the list of projects the caller wants to search, find the actual list of
// projects we need to search through.
//
// This list of projects is properly topologically ordered. Because of this we
// can just process them in order from first to last because we know no project
// in this list could affect a prior project.
orderedProjectsToExamine = GetOrderedProjectsToExamine(
solution, projects, projectsThatCouldReferenceType);
}

// The final set of results we'll be returning.
using var _1 = GetSymbolSet(out var result);
Expand Down Expand Up @@ -278,7 +291,7 @@ private static IEnumerable<ProjectId> GetProjectsThatCouldReferenceType(
{
// Get all the projects that depend on 'project' as well as 'project' itself.
return dependencyGraph.GetProjectsThatTransitivelyDependOnThisProject(project.Id)
.Concat(project.Id);
.Concat(project.Id);
}

private static List<Project> GetOrderedProjectsToExamine(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.FindSymbols
{
internal partial class FindReferencesSearchEngine
{
/// <summary>
/// Symbol set used when <see cref="FindReferencesSearchOptions.UnidirectionalHierarchyCascade"/> is <see
/// langword="false"/>. This symbol set will cascade up *and* down the inheritance hierarchy for all symbols we
/// are searching for. This is the symbol set used for features like 'Rename', where all cascaded symbols must
/// be updated in order to keep the code compiling.
/// </summary>
private sealed class BidirectionalSymbolSet : SymbolSet
{
/// <summary>
/// When we're cascading in both direction, we can just keep all symbols in a single set. We'll always be
/// examining all of them to go in both up and down directions in every project we process. Any time we
/// add a new symbol to it we'll continue to cascade in both directions looking for more.
/// </summary>
private readonly HashSet<ISymbol> _allSymbols = new();

public BidirectionalSymbolSet(FindReferencesSearchEngine engine, HashSet<ISymbol> initialSymbols, HashSet<ISymbol> upSymbols)
: base(engine)
{
_allSymbols.AddRange(initialSymbols);
_allSymbols.AddRange(upSymbols);
}

public override ImmutableArray<ISymbol> GetAllSymbols()
=> _allSymbols.ToImmutableArray();

public override async Task InheritanceCascadeAsync(Project project, CancellationToken cancellationToken)
{
// Start searching using the current set of symbols built up so far.
var workQueue = new Stack<ISymbol>();
workQueue.Push(_allSymbols);

var projects = ImmutableHashSet.Create(project);

while (workQueue.Count > 0)
{
var current = workQueue.Pop();

// For each symbol we're examining try to walk both up and down from it to see if we discover any
// new symbols in this project. As long as we keep finding symbols, we'll keep searching from them
// in both directions.
await AddDownSymbolsAsync(this.Engine, current, _allSymbols, workQueue, projects, cancellationToken).ConfigureAwait(false);
await AddUpSymbolsAsync(this.Engine, current, _allSymbols, workQueue, projects, cancellationToken).ConfigureAwait(false);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.CodeAnalysis.FindSymbols
{
internal partial class FindReferencesSearchEngine
{
/// <summary>
/// A symbol set used when the find refs caller does not want cascading. This is a trivial impl that basically
/// just wraps the initial symbol provided and doesn't need to do anything beyond that.
/// </summary>
private sealed class NonCascadingSymbolSet : SymbolSet
{
private readonly ImmutableArray<ISymbol> _symbols;

public NonCascadingSymbolSet(FindReferencesSearchEngine engine, ISymbol searchSymbol) : base(engine)
{
_symbols = ImmutableArray.Create(searchSymbol);
}

public override ImmutableArray<ISymbol> GetAllSymbols()
=> _symbols;

public override Task InheritanceCascadeAsync(Project project, CancellationToken cancellationToken)
{
// Nothing to do here. We're in a non-cascading scenario, so even as we encounter a new project we
// don't have to figure out what new symbols may be found.
return Task.CompletedTask;
}
}
}
}
Loading

0 comments on commit debd668

Please sign in to comment.