Skip to content

Commit debd668

Browse files
Merge pull request #54654 from CyrusNajmabadi/farOrder2
Update FAR engine to be able to work without having access to all projects at the same time.
2 parents 9087236 + 53896ea commit debd668

33 files changed

Lines changed: 716 additions & 707 deletions

src/Features/Core/Portable/ChangeSignature/DelegateInvokeMethodReferenceFinder.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,17 @@ internal class DelegateInvokeMethodReferenceFinder : AbstractReferenceFinder<IMe
3333
protected override bool CanFind(IMethodSymbol symbol)
3434
=> symbol.MethodKind == MethodKind.DelegateInvoke;
3535

36-
protected override async Task<ImmutableArray<(ISymbol symbol, FindReferencesCascadeDirection cascadeDirection)>> DetermineCascadedSymbolsAsync(
36+
protected override async Task<ImmutableArray<ISymbol>> DetermineCascadedSymbolsAsync(
3737
IMethodSymbol symbol,
3838
Solution solution,
39-
IImmutableSet<Project> projects,
4039
FindReferencesSearchOptions options,
41-
FindReferencesCascadeDirection cascadeDirection,
4240
CancellationToken cancellationToken)
4341
{
44-
using var _ = ArrayBuilder<(ISymbol symbol, FindReferencesCascadeDirection cascadeDirection)>.GetInstance(out var result);
42+
using var _ = ArrayBuilder<ISymbol>.GetInstance(out var result);
4543

4644
var beginInvoke = symbol.ContainingType.GetMembers(WellKnownMemberNames.DelegateBeginInvokeName).FirstOrDefault();
4745
if (beginInvoke != null)
48-
result.Add((beginInvoke, cascadeDirection));
46+
result.Add(beginInvoke);
4947

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

src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,33 @@ private static async Task<ImmutableArray<INamedTypeSymbol>> DescendInheritanceTr
8383
// are passed in. There is no need to check D as there's no way it could
8484
// contribute an intermediate type that affects A or C. We only need to check
8585
// A, B and C
86+
//
87+
// An exception to the above rule is if we're just searching a single project.
88+
// in that case there can be no intermediary projects that could add types.
89+
// So we can just limit ourselves to that single project.
8690

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

91-
// Now, based on the list of projects that could actually reference the type,
92-
// and the list of projects the caller wants to search, find the actual list of
93-
// projects we need to search through.
94-
//
95-
// This list of projects is properly topologically ordered. Because of this we
96-
// can just process them in order from first to last because we know no project
97-
// in this list could affect a prior project.
98-
var orderedProjectsToExamine = GetOrderedProjectsToExamine(
99-
solution, projects, projectsThatCouldReferenceType);
94+
if (projects.Count == 1)
95+
{
96+
orderedProjectsToExamine = projects.ToList();
97+
}
98+
else
99+
{
100+
var projectsThatCouldReferenceType = await GetProjectsThatCouldReferenceTypeAsync(
101+
type, solution, searchInMetadata, cancellationToken).ConfigureAwait(false);
102+
103+
// Now, based on the list of projects that could actually reference the type,
104+
// and the list of projects the caller wants to search, find the actual list of
105+
// projects we need to search through.
106+
//
107+
// This list of projects is properly topologically ordered. Because of this we
108+
// can just process them in order from first to last because we know no project
109+
// in this list could affect a prior project.
110+
orderedProjectsToExamine = GetOrderedProjectsToExamine(
111+
solution, projects, projectsThatCouldReferenceType);
112+
}
100113

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

284297
private static List<Project> GetOrderedProjectsToExamine(

src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesCascadeDirection.cs

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Generic;
6+
using System.Collections.Immutable;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
using Microsoft.CodeAnalysis.Shared.Extensions;
10+
using Roslyn.Utilities;
11+
12+
namespace Microsoft.CodeAnalysis.FindSymbols
13+
{
14+
internal partial class FindReferencesSearchEngine
15+
{
16+
/// <summary>
17+
/// Symbol set used when <see cref="FindReferencesSearchOptions.UnidirectionalHierarchyCascade"/> is <see
18+
/// langword="false"/>. This symbol set will cascade up *and* down the inheritance hierarchy for all symbols we
19+
/// are searching for. This is the symbol set used for features like 'Rename', where all cascaded symbols must
20+
/// be updated in order to keep the code compiling.
21+
/// </summary>
22+
private sealed class BidirectionalSymbolSet : SymbolSet
23+
{
24+
/// <summary>
25+
/// When we're cascading in both direction, we can just keep all symbols in a single set. We'll always be
26+
/// examining all of them to go in both up and down directions in every project we process. Any time we
27+
/// add a new symbol to it we'll continue to cascade in both directions looking for more.
28+
/// </summary>
29+
private readonly HashSet<ISymbol> _allSymbols = new();
30+
31+
public BidirectionalSymbolSet(FindReferencesSearchEngine engine, HashSet<ISymbol> initialSymbols, HashSet<ISymbol> upSymbols)
32+
: base(engine)
33+
{
34+
_allSymbols.AddRange(initialSymbols);
35+
_allSymbols.AddRange(upSymbols);
36+
}
37+
38+
public override ImmutableArray<ISymbol> GetAllSymbols()
39+
=> _allSymbols.ToImmutableArray();
40+
41+
public override async Task InheritanceCascadeAsync(Project project, CancellationToken cancellationToken)
42+
{
43+
// Start searching using the current set of symbols built up so far.
44+
var workQueue = new Stack<ISymbol>();
45+
workQueue.Push(_allSymbols);
46+
47+
var projects = ImmutableHashSet.Create(project);
48+
49+
while (workQueue.Count > 0)
50+
{
51+
var current = workQueue.Pop();
52+
53+
// For each symbol we're examining try to walk both up and down from it to see if we discover any
54+
// new symbols in this project. As long as we keep finding symbols, we'll keep searching from them
55+
// in both directions.
56+
await AddDownSymbolsAsync(this.Engine, current, _allSymbols, workQueue, projects, cancellationToken).ConfigureAwait(false);
57+
await AddUpSymbolsAsync(this.Engine, current, _allSymbols, workQueue, projects, cancellationToken).ConfigureAwait(false);
58+
}
59+
}
60+
}
61+
}
62+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Immutable;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
9+
namespace Microsoft.CodeAnalysis.FindSymbols
10+
{
11+
internal partial class FindReferencesSearchEngine
12+
{
13+
/// <summary>
14+
/// A symbol set used when the find refs caller does not want cascading. This is a trivial impl that basically
15+
/// just wraps the initial symbol provided and doesn't need to do anything beyond that.
16+
/// </summary>
17+
private sealed class NonCascadingSymbolSet : SymbolSet
18+
{
19+
private readonly ImmutableArray<ISymbol> _symbols;
20+
21+
public NonCascadingSymbolSet(FindReferencesSearchEngine engine, ISymbol searchSymbol) : base(engine)
22+
{
23+
_symbols = ImmutableArray.Create(searchSymbol);
24+
}
25+
26+
public override ImmutableArray<ISymbol> GetAllSymbols()
27+
=> _symbols;
28+
29+
public override Task InheritanceCascadeAsync(Project project, CancellationToken cancellationToken)
30+
{
31+
// Nothing to do here. We're in a non-cascading scenario, so even as we encounter a new project we
32+
// don't have to figure out what new symbols may be found.
33+
return Task.CompletedTask;
34+
}
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)