From da32faade9371e20ab86783fdab2c52524e12d16 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Tue, 15 Apr 2025 16:22:47 -0700 Subject: [PATCH 1/2] Delete dead code --- .../CSharpRenameRewriterLanguageService.cs | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs index 54803eb64cc8a..169f9f962f57a 100644 --- a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs +++ b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs @@ -923,34 +923,6 @@ renamedSymbol.ContainingSymbol is IMethodSymbol methodSymbol && } } - private static async Task GetVBPropertyFromAccessorOrAnOverrideAsync(ISymbol symbol, Solution solution, CancellationToken cancellationToken) - { - try - { - if (symbol.IsPropertyAccessor()) - { - var property = ((IMethodSymbol)symbol).AssociatedSymbol!; - - return property.Language == LanguageNames.VisualBasic ? property : null; - } - - if (symbol.IsOverride && symbol.GetOverriddenMember() != null) - { - var originalSourceSymbol = SymbolFinder.FindSourceDefinition(symbol.GetOverriddenMember(), solution, cancellationToken); - if (originalSourceSymbol != null) - { - return await GetVBPropertyFromAccessorOrAnOverrideAsync(originalSourceSymbol, solution, cancellationToken).ConfigureAwait(false); - } - } - - return null; - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - } - private static void AddSymbolSourceSpans( ArrayBuilder conflicts, IEnumerable symbols, IDictionary reverseMappedLocations) From 39e3a54c8fe6795b6ddec1135444728026876309 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Mon, 14 Apr 2025 15:01:18 -0700 Subject: [PATCH 2/2] Ensure FindSourceDefinitionAsync works in frozen compilation cases FindSourceDefinitionAsync was assuming that in the cross-language case, if you have a symbol from another project, that the compilation must always be available for that project -- for example, that calling TryGetCompilation on the project must always work. That's not the case if we're not generating skeleton references all the time. The fix to make that method work reliably again is simply to call GetCompilationAsync(). This results in a cascade of making more things async though: although our public entrypoint for FindSourceDefinitionAsync was Task-returning, it wasn't async and called into a non-async helper. So this removes those internal helpers and makes everything async again. Fixes https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2443981 Fixes https://developercommunity.visualstudio.com/t/Go-to-definition-stopped-working-after-u/10796494 --- ...bstractGenerateConstructorService.State.cs | 22 ++-- ...AbstractGenerateEnumMemberService.State.cs | 9 +- .../AbstractGenerateEnumMemberService.cs | 2 +- ...enerateParameterizedMemberService.State.cs | 2 +- .../AbstractGenerateVariableService.State.cs | 11 +- .../AbstractGenerateVariableService.cs | 2 +- .../AbstractUnsealClassCodeFixProvider.cs | 4 +- .../Core.Wpf/Peek/PeekableItemFactory.cs | 4 +- ...eTrackingTaggerProvider.TrackingSession.cs | 12 +-- ...ypeFinderTests.cs => SymbolFinderTests.cs} | 86 +++++++++++++++ .../CSharpChangeSignatureService.cs | 2 +- .../AbstractChangeSignatureService.cs | 2 +- .../DelegateInvokeMethodReferenceFinder.cs | 2 +- .../AbstractGenerateTypeService.State.cs | 13 +-- .../AbstractGenerateTypeService.cs | 2 +- .../GoToBase/AbstractGoToBaseService.cs | 4 +- .../GoToDefinitionFeatureHelpers.cs | 6 +- ...bstractInheritanceMarginService_Helpers.cs | 100 ++++++++++-------- .../AbstractNavigableItemsService.cs | 8 +- .../VisualBasicChangeSignatureService.vb | 4 +- .../GenerateEventCodeFixProvider.vb | 8 +- .../Core/Def/Progression/GraphBuilder.cs | 2 +- .../CSharpRenameRewriterLanguageService.cs | 4 +- .../FindReferencesSearchEngine.SymbolSet.cs | 12 +-- .../Core/Portable/FindSymbols/SymbolFinder.cs | 5 +- .../FindSymbols/SymbolFinder_Callers.cs | 2 +- .../FindSymbols/SymbolFinder_Hierarchy.cs | 12 +-- .../ConflictResolver.Session.cs | 7 +- .../Rename/ConflictEngine/ConflictResolver.cs | 14 ++- .../Core/Portable/Rename/RenameUtilities.cs | 8 +- .../Extensions/SyntaxGeneratorExtensions.cs | 3 +- .../Core/SymbolFinder/SymbolFinderInternal.cs | 15 ++- 32 files changed, 246 insertions(+), 143 deletions(-) rename src/EditorFeatures/Test/SymbolFinder/{DependentTypeFinderTests.cs => SymbolFinderTests.cs} (88%) diff --git a/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs b/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs index e59af87ddedd5..3c7d215fba9f5 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs @@ -90,17 +90,17 @@ private async Task TryInitializeAsync( { if (_service.IsConstructorInitializerGeneration(_document, node, cancellationToken)) { - if (!TryInitializeConstructorInitializerGeneration(node, cancellationToken)) + if (!(await TryInitializeConstructorInitializerGenerationAsync(node, cancellationToken).ConfigureAwait(false))) return false; } else if (_service.IsSimpleNameGeneration(_document, node, cancellationToken)) { - if (!TryInitializeSimpleNameGeneration(node, cancellationToken)) + if (!(await TryInitializeSimpleNameGenerationAsync(node, cancellationToken).ConfigureAwait(false))) return false; } else if (_service.IsImplicitObjectCreation(_document, node, cancellationToken)) { - if (!TryInitializeImplicitObjectCreation(node, cancellationToken)) + if (!(await TryInitializeImplicitObjectCreationAsync(node, cancellationToken).ConfigureAwait(false))) return false; } else @@ -296,7 +296,7 @@ private static ITypeSymbol FixType(ITypeSymbol typeSymbol, SemanticModel semanti .RemoveUnnamedErrorTypes(compilation); } - private bool TryInitializeConstructorInitializerGeneration( + private async ValueTask TryInitializeConstructorInitializerGenerationAsync( SyntaxNode constructorInitializer, CancellationToken cancellationToken) { if (_service.TryInitializeConstructorInitializerGeneration( @@ -309,13 +309,13 @@ private bool TryInitializeConstructorInitializerGeneration( var semanticInfo = _document.SemanticModel.GetSymbolInfo(constructorInitializer, cancellationToken); if (semanticInfo.Symbol == null) - return TryDetermineTypeToGenerateIn(typeToGenerateIn, cancellationToken); + return await TryDetermineTypeToGenerateInAsync(typeToGenerateIn, cancellationToken).ConfigureAwait(false); } return false; } - private bool TryInitializeImplicitObjectCreation(SyntaxNode implicitObjectCreation, CancellationToken cancellationToken) + private async ValueTask TryInitializeImplicitObjectCreationAsync(SyntaxNode implicitObjectCreation, CancellationToken cancellationToken) { if (_service.TryInitializeImplicitObjectCreation( _document, implicitObjectCreation, cancellationToken, @@ -326,13 +326,13 @@ private bool TryInitializeImplicitObjectCreation(SyntaxNode implicitObjectCreati var semanticInfo = _document.SemanticModel.GetSymbolInfo(implicitObjectCreation, cancellationToken); if (semanticInfo.Symbol == null) - return TryDetermineTypeToGenerateIn(typeToGenerateIn, cancellationToken); + return await TryDetermineTypeToGenerateInAsync(typeToGenerateIn, cancellationToken).ConfigureAwait(false); } return false; } - private bool TryInitializeSimpleNameGeneration( + private async ValueTask TryInitializeSimpleNameGenerationAsync( SyntaxNode simpleName, CancellationToken cancellationToken) { @@ -359,7 +359,7 @@ private bool TryInitializeSimpleNameGeneration( cancellationToken.ThrowIfCancellationRequested(); - return TryDetermineTypeToGenerateIn(typeToGenerateIn, cancellationToken); + return await TryDetermineTypeToGenerateInAsync(typeToGenerateIn, cancellationToken).ConfigureAwait(false); } private static bool IsValidAttributeParameterType(ITypeSymbol type) @@ -397,10 +397,10 @@ private static bool IsValidAttributeParameterType(ITypeSymbol type) } } - private bool TryDetermineTypeToGenerateIn( + private async ValueTask TryDetermineTypeToGenerateInAsync( INamedTypeSymbol original, CancellationToken cancellationToken) { - var definition = SymbolFinderInternal.FindSourceDefinition(original, _document.Project.Solution, cancellationToken); + var definition = await SymbolFinderInternal.FindSourceDefinitionAsync(original, _document.Project.Solution, cancellationToken).ConfigureAwait(false); TypeToGenerateIn = definition as INamedTypeSymbol; return TypeToGenerateIn?.TypeKind is (TypeKind?)TypeKind.Class or (TypeKind?)TypeKind.Struct; diff --git a/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.State.cs b/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.State.cs index fde000094f0d2..555c2362af311 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.State.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.State.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.LanguageService; @@ -23,17 +24,17 @@ private sealed partial class State public TSimpleNameSyntax SimpleName { get; private set; } = null!; public TExpressionSyntax SimpleNameOrMemberAccessExpression { get; private set; } = null!; - public static State? Generate( + public static async Task GenerateAsync( TService service, SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken) { var state = new State(); - return state.TryInitialize(service, document, node, cancellationToken) ? state : null; + return await state.TryInitializeAsync(service, document, node, cancellationToken).ConfigureAwait(false) ? state : null; } - private bool TryInitialize( + private async ValueTask TryInitializeAsync( TService service, SemanticDocument document, SyntaxNode node, @@ -63,7 +64,7 @@ private bool TryInitialize( } cancellationToken.ThrowIfCancellationRequested(); - var sourceType = SymbolFinderInternal.FindSourceDefinition(TypeToGenerateIn, document.Project.Solution, cancellationToken) as INamedTypeSymbol; + var sourceType = (await SymbolFinderInternal.FindSourceDefinitionAsync(TypeToGenerateIn, document.Project.Solution, cancellationToken).ConfigureAwait(false)) as INamedTypeSymbol; if (!ValidateTypeToGenerateIn(sourceType, true, EnumType)) return false; diff --git a/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.cs b/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.cs index 9330bb1151c89..bddcffe56e29e 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.cs @@ -27,7 +27,7 @@ public async Task> GenerateEnumMemberAsync(Document d using (Logger.LogBlock(FunctionId.Refactoring_GenerateMember_GenerateEnumMember, cancellationToken)) { var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); - var state = State.Generate((TService)this, semanticDocument, node, cancellationToken); + var state = await State.GenerateAsync((TService)this, semanticDocument, node, cancellationToken).ConfigureAwait(false); if (state == null) { return []; diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs index dd390823cc3fa..a166774d68938 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs @@ -58,7 +58,7 @@ public Location Location protected async Task TryFinishInitializingStateAsync(TService service, SemanticDocument document, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - TypeToGenerateIn = SymbolFinderInternal.FindSourceDefinition(TypeToGenerateIn, document.Project.Solution, cancellationToken) as INamedTypeSymbol; + TypeToGenerateIn = await SymbolFinderInternal.FindSourceDefinitionAsync(TypeToGenerateIn, document.Project.Solution, cancellationToken).ConfigureAwait(false) as INamedTypeSymbol; if (TypeToGenerateIn.IsErrorType()) { return false; diff --git a/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.State.cs b/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.State.cs index 4bef0b1c90b76..518f28a42145b 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.State.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.State.cs @@ -8,6 +8,7 @@ using System.Collections.Immutable; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.FindSymbols; @@ -69,14 +70,14 @@ private State( _document = document; } - public static State Generate( + public static async ValueTask GenerateAsync( TService service, SemanticDocument document, SyntaxNode interfaceNode, CancellationToken cancellationToken) { var state = new State(service, document); - return state.TryInitialize(interfaceNode, cancellationToken) ? state : null; + return await state.TryInitializeAsync(interfaceNode, cancellationToken).ConfigureAwait(false) ? state : null; } public Accessibility DetermineMaximalAccessibility() @@ -102,7 +103,7 @@ public Accessibility DetermineMaximalAccessibility() return accessibility; } - private bool TryInitialize( + private async ValueTask TryInitializeAsync( SyntaxNode node, CancellationToken cancellationToken) { if (_service.IsIdentifierNameGeneration(node)) @@ -148,8 +149,8 @@ private bool TryInitialize( return false; } - TypeToGenerateIn = SymbolFinderInternal.FindSourceDefinition( - TypeToGenerateIn, _document.Project.Solution, cancellationToken) as INamedTypeSymbol; + TypeToGenerateIn = await SymbolFinder.FindSourceDefinitionAsync( + TypeToGenerateIn, _document.Project.Solution, cancellationToken).ConfigureAwait(false) as INamedTypeSymbol; if (!ValidateTypeToGenerateIn(TypeToGenerateIn, IsStatic, ClassInterfaceModuleStructTypes)) { diff --git a/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.cs b/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.cs index 98516ad5b2a24..cca19be1a2ca2 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.cs @@ -38,7 +38,7 @@ public async Task> GenerateVariableAsync( { var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); - var state = State.Generate((TService)this, semanticDocument, node, cancellationToken); + var state = await State.GenerateAsync((TService)this, semanticDocument, node, cancellationToken).ConfigureAwait(false); if (state == null) return []; diff --git a/src/Analyzers/Core/CodeFixes/UnsealClass/AbstractUnsealClassCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UnsealClass/AbstractUnsealClassCodeFixProvider.cs index 38f722d3a79c0..31f8bbfe218af 100644 --- a/src/Analyzers/Core/CodeFixes/UnsealClass/AbstractUnsealClassCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UnsealClass/AbstractUnsealClassCodeFixProvider.cs @@ -39,8 +39,8 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) if (semanticModel.GetSymbolInfo(node, cancellationToken).Symbol is INamedTypeSymbol type && type.TypeKind == TypeKind.Class && type.IsSealed && !type.IsStatic) { - var definition = SymbolFinderInternal.FindSourceDefinition( - type, document.Project.Solution, cancellationToken); + var definition = await SymbolFinderInternal.FindSourceDefinitionAsync( + type, document.Project.Solution, cancellationToken).ConfigureAwait(false); if (definition is not null && definition.DeclaringSyntaxReferences.Length > 0) { var title = string.Format(TitleFormat, type.Name); diff --git a/src/EditorFeatures/Core.Wpf/Peek/PeekableItemFactory.cs b/src/EditorFeatures/Core.Wpf/Peek/PeekableItemFactory.cs index b634b292f166a..b26062e8216f6 100644 --- a/src/EditorFeatures/Core.Wpf/Peek/PeekableItemFactory.cs +++ b/src/EditorFeatures/Core.Wpf/Peek/PeekableItemFactory.cs @@ -59,8 +59,8 @@ public async Task> GetPeekableItemsAsync( throw new ArgumentNullException(nameof(peekResultFactory)); var solution = project.Solution; - symbol = SymbolFinder.FindSourceDefinition(symbol, solution, cancellationToken) ?? symbol; - symbol = GoToDefinitionFeatureHelpers.TryGetPreferredSymbol(solution, symbol, cancellationToken); + symbol = await SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).ConfigureAwait(false) ?? symbol; + symbol = await GoToDefinitionFeatureHelpers.TryGetPreferredSymbolAsync(solution, symbol, cancellationToken).ConfigureAwait(false); if (symbol is null) return []; diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs index 018ff51ee2b6a..dd45532611da6 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs @@ -197,7 +197,7 @@ private async Task DetermineIfRenamableIdentifierAsync(Sn // This is a reference from a nameof expression. Allow the rename but set the RenameOverloads option ForceRenameOverloads = true; - return DetermineIfRenamableSymbols(renameSymbolInfo.Symbols, document); + return await DetermineIfRenamableSymbolsAsync(renameSymbolInfo.Symbols, document).ConfigureAwait(false); } else { @@ -208,7 +208,7 @@ private async Task DetermineIfRenamableIdentifierAsync(Sn return TriggerIdentifierKind.NotRenamable; } - return DetermineIfRenamableSymbol(renameSymbolInfo.Symbols.Single(), document, token); + return await DetermineIfRenamableSymbolAsync(renameSymbolInfo.Symbols.Single(), document, token).ConfigureAwait(false); } } } @@ -216,12 +216,12 @@ private async Task DetermineIfRenamableIdentifierAsync(Sn return TriggerIdentifierKind.NotRenamable; } - private TriggerIdentifierKind DetermineIfRenamableSymbols(IEnumerable symbols, Document document) + private async ValueTask DetermineIfRenamableSymbolsAsync(IEnumerable symbols, Document document) { foreach (var symbol in symbols) { // Get the source symbol if possible - var sourceSymbol = SymbolFinder.FindSourceDefinition(symbol, document.Project.Solution, _cancellationToken) ?? symbol; + var sourceSymbol = await SymbolFinder.FindSourceDefinitionAsync(symbol, document.Project.Solution, _cancellationToken).ConfigureAwait(false) ?? symbol; if (!sourceSymbol.IsFromSource()) { @@ -232,10 +232,10 @@ private TriggerIdentifierKind DetermineIfRenamableSymbols(IEnumerable s return TriggerIdentifierKind.RenamableReference; } - private TriggerIdentifierKind DetermineIfRenamableSymbol(ISymbol symbol, Document document, SyntaxToken token) + private async ValueTask DetermineIfRenamableSymbolAsync(ISymbol symbol, Document document, SyntaxToken token) { // Get the source symbol if possible - var sourceSymbol = SymbolFinder.FindSourceDefinition(symbol, document.Project.Solution, _cancellationToken) ?? symbol; + var sourceSymbol = await SymbolFinder.FindSourceDefinitionAsync(symbol, document.Project.Solution, _cancellationToken).ConfigureAwait(false) ?? symbol; if (sourceSymbol is IFieldSymbol { ContainingType.IsTupleType: true, IsImplicitlyDeclared: true }) { diff --git a/src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs b/src/EditorFeatures/Test/SymbolFinder/SymbolFinderTests.cs similarity index 88% rename from src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs rename to src/EditorFeatures/Test/SymbolFinder/SymbolFinderTests.cs index 8446b550bb854..211b55143f489 100644 --- a/src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs +++ b/src/EditorFeatures/Test/SymbolFinder/SymbolFinderTests.cs @@ -796,4 +796,90 @@ public interface I // verify that we don't crash here. var implementedMembers = await SymbolFinder.FindImplementedInterfaceMembersArrayAsync(namespaceSymbol, solution, CancellationToken.None); } + + [Theory, CombinatorialData] + public async Task FindSourceDefinition_CrossAssembly(TestHost host) + { + using var workspace = CreateWorkspace(host); + var solution = workspace.CurrentSolution; + + // Create a source assembly with a class in Visual Basic + solution = AddProjectWithMetadataReferences(solution, "ReferencedProject", LanguageNames.VisualBasic, @" +Namespace N + Public Class ReferencedClass + End Class +End Namespace +", Net40.References.mscorlib); + + var referencedProjectId = solution.Projects.Single(p => p.Name == "ReferencedProject").Id; + + // Create a project that uses the class from the other assembly in C# + solution = AddProjectWithMetadataReferences(solution, "SourceProject", LanguageNames.CSharp, "", Net40.References.mscorlib, referencedProjectId); + + // Get the symbol for ReferencedClass from the using project's compilation + var sourceCompilation = await solution.Projects.Single(p => p.Name == "SourceProject").GetCompilationAsync(); + var classInSource = sourceCompilation.GetTypeByMetadataName("N.ReferencedClass"); + + // It should be a metadata symbol (from assembly reference) + Assert.True(classInSource.Locations.Any(loc => loc.IsInMetadata)); + + // Find the source definition + var sourceDefinition = await SymbolFinder.FindSourceDefinitionAsync(classInSource, solution, CancellationToken.None); + + // Verify we found the source definition + Assert.NotNull(sourceDefinition); + Assert.True(sourceDefinition.Locations.Any(loc => loc.IsInSource)); + + // Verify it comes from the VB project + var document = solution.GetDocument(sourceDefinition.Locations.First(loc => loc.IsInSource).SourceTree); + Assert.Equal(referencedProjectId, document.Project.Id); + } + + [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2443981")] + public async Task FindSourceDefinition_CrossAssembly_WithFrozen(TestHost host) + { + using var workspace = CreateWorkspace(host); + var solution = workspace.CurrentSolution; + + var code = @" +Namespace N + Public Class ReferencedClass + Public Sub M() + Dim x As Integer = 0 + End Sub + End Class +End Namespace +"; + + // Create a source assembly with a class in Visual Basic + solution = AddProjectWithMetadataReferences(solution, "ReferencedProject", LanguageNames.VisualBasic, code, Net40.References.mscorlib); + + var referencedProjectId = solution.Projects.Single(p => p.Name == "ReferencedProject").Id; + + // Create a project that uses the class from the other assembly in C# + solution = AddProjectWithMetadataReferences(solution, "SourceProject", LanguageNames.CSharp, "", Net40.References.mscorlib, referencedProjectId); + + // Fetch the C# compilation to ensure we produce all compilations, and then make a change to the VB project and freeze it. + // At this point we won't create more skeleton references for the VB reference, but we also won't have an up-to-date compilation. + await solution.Projects.Single(p => p.Name == "SourceProject").GetCompilationAsync(); + solution = solution.GetProject(referencedProjectId).Documents.Single().WithText(SourceText.From(code.Replace('0', '1'))) + .Project.Solution.WithFrozenPartialCompilations(CancellationToken.None); + + var sourceCompilation = await solution.Projects.Single(p => p.Name == "SourceProject").GetCompilationAsync(); + var classInSource = sourceCompilation.GetTypeByMetadataName("N.ReferencedClass"); + + // It should be a metadata symbol (from assembly reference) + Assert.True(classInSource.Locations.Any(loc => loc.IsInMetadata)); + + // Find the source definition + var sourceDefinition = await SymbolFinder.FindSourceDefinitionAsync(classInSource, solution, CancellationToken.None); + + // Verify we found the source definition + Assert.NotNull(sourceDefinition); + Assert.True(sourceDefinition.Locations.Any(loc => loc.IsInSource)); + + // Verify it comes from the VB project + var document = solution.GetDocument(sourceDefinition.Locations.First(loc => loc.IsInSource).SourceTree); + Assert.Equal(referencedProjectId, document.Project.Id); + } } diff --git a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs index 3e8088d3424a5..c3f2f98829936 100644 --- a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs +++ b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs @@ -890,7 +890,7 @@ public override async Task> DetermineCascadedSymbolsFrom if (convertedType != null) { - convertedType = SymbolFinder.FindSourceDefinition(convertedType, document.Project.Solution, cancellationToken) + convertedType = await SymbolFinder.FindSourceDefinitionAsync(convertedType, document.Project.Solution, cancellationToken).ConfigureAwait(false) ?? convertedType; } diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs index fdf9f9d1f0618..df6336c849c8e 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs @@ -107,7 +107,7 @@ internal async Task GetChangeSignatureContextAsy document, position, restrictToDeclarations, cancellationToken).ConfigureAwait(false); // Cross-language symbols will show as metadata, so map it to source if possible. - symbol = SymbolFinder.FindSourceDefinition(symbol, document.Project.Solution, cancellationToken) ?? symbol; + symbol = await SymbolFinder.FindSourceDefinitionAsync(symbol, document.Project.Solution, cancellationToken).ConfigureAwait(false) ?? symbol; if (symbol == null) return new CannotChangeSignatureAnalyzedContext(ChangeSignatureFailureKind.IncorrectKind); diff --git a/src/Features/Core/Portable/ChangeSignature/DelegateInvokeMethodReferenceFinder.cs b/src/Features/Core/Portable/ChangeSignature/DelegateInvokeMethodReferenceFinder.cs index ceefcf6a65088..832571f7667e0 100644 --- a/src/Features/Core/Portable/ChangeSignature/DelegateInvokeMethodReferenceFinder.cs +++ b/src/Features/Core/Portable/ChangeSignature/DelegateInvokeMethodReferenceFinder.cs @@ -109,7 +109,7 @@ protected override void FindReferencesInDocument( var convertedType = (ISymbol?)state.SemanticModel.GetTypeInfo(node, cancellationToken).ConvertedType; if (convertedType != null) { - convertedType = SymbolFinder.FindSourceDefinition(convertedType, state.Solution, cancellationToken) ?? convertedType; + convertedType = SymbolFinder.FindSourceDefinitionAsync(convertedType, state.Solution, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken) ?? convertedType; } if (convertedType == methodSymbol.ContainingType) diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.State.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.State.cs index 3c105c0483368..83030c79f685b 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.State.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.State.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.FindSymbols; @@ -64,17 +65,17 @@ protected sealed class State private State(Compilation compilation) => Compilation = compilation; - public static State? Generate( + public static async ValueTask GenerateAsync( TService service, SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken) { var state = new State(document.SemanticModel.Compilation); - return state.TryInitialize(service, document, node, cancellationToken) ? state : (State?)null; + return await state.TryInitializeAsync(service, document, node, cancellationToken).ConfigureAwait(false) ? state : (State?)null; } - private bool TryInitialize( + private async ValueTask TryInitializeAsync( TService service, SemanticDocument semanticDocument, SyntaxNode node, @@ -160,7 +161,7 @@ CandidateReason.NotReferencable or } } - DetermineNamespaceOrTypeToGenerateIn(service, semanticDocument, cancellationToken); + await DetermineNamespaceOrTypeToGenerateInAsync(service, semanticDocument, cancellationToken).ConfigureAwait(false); // Now, try to infer a possible base type for this new class/interface. InferBaseType(service, semanticDocument, cancellationToken); @@ -274,7 +275,7 @@ private bool GenerateInterface(TService service) return service.IsInInterfaceList(NameOrMemberAccessExpression); } - private void DetermineNamespaceOrTypeToGenerateIn( + private async ValueTask DetermineNamespaceOrTypeToGenerateInAsync( TService service, SemanticDocument document, CancellationToken cancellationToken) @@ -291,7 +292,7 @@ private void DetermineNamespaceOrTypeToGenerateIn( } else { - var symbol = SymbolFinder.FindSourceDefinition(TypeToGenerateInOpt, document.Project.Solution, cancellationToken); + var symbol = await SymbolFinder.FindSourceDefinitionAsync(TypeToGenerateInOpt, document.Project.Solution, cancellationToken).ConfigureAwait(false); if (symbol == null || !symbol.IsKind(SymbolKind.NamedType) || !symbol.Locations.Any(static loc => loc.IsInSource)) diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.cs index 13ecc170cc013..0ebac83ad0f28 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.cs @@ -71,7 +71,7 @@ public async Task> GenerateTypeAsync( { var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); - var state = State.Generate((TService)this, semanticDocument, node, cancellationToken); + var state = await State.GenerateAsync((TService)this, semanticDocument, node, cancellationToken).ConfigureAwait(false); if (state != null) { var actions = GetActions(semanticDocument, node, state, cancellationToken); diff --git a/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs b/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs index fd9c3e46de556..734402b53aad1 100644 --- a/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs +++ b/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs @@ -61,8 +61,8 @@ await context.SetSearchTitleAsync( // If not found but the symbol is from metadata, create it's definition item from metadata and add to the context. foreach (var baseSymbol in bases) { - var sourceDefinition = SymbolFinder.FindSourceDefinition( - baseSymbol, solution, cancellationToken); + var sourceDefinition = await SymbolFinder.FindSourceDefinitionAsync( + baseSymbol, solution, cancellationToken).ConfigureAwait(false); if (sourceDefinition != null) { var definitionItem = await sourceDefinition.ToClassifiedDefinitionItemAsync( diff --git a/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs b/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs index cd1f8c77540e6..a472b4ef95d42 100644 --- a/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs +++ b/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.GoToDefinition; internal static class GoToDefinitionFeatureHelpers { - public static ISymbol? TryGetPreferredSymbol( + public static async ValueTask TryGetPreferredSymbolAsync( Solution solution, ISymbol? symbol, CancellationToken cancellationToken) { if (symbol is null) @@ -43,7 +43,7 @@ internal static class GoToDefinitionFeatureHelpers symbol = alias.Target; } - var definition = SymbolFinder.FindSourceDefinition(symbol, solution, cancellationToken); + var definition = await SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); symbol = definition ?? symbol; @@ -66,7 +66,7 @@ public static async Task> GetDefinitionsAsync( bool thirdPartyNavigationAllowed, CancellationToken cancellationToken) { - symbol = TryGetPreferredSymbol(solution, symbol, cancellationToken); + symbol = await TryGetPreferredSymbolAsync(solution, symbol, cancellationToken).ConfigureAwait(false); if (symbol is null) return []; diff --git a/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs b/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs index a726c078f159a..6cb7cdce24f2a 100644 --- a/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs +++ b/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs @@ -314,25 +314,25 @@ private static async ValueTask AddInheritanceMemberItemsForNamedTypeAsync( { if (memberSymbol.TypeKind == TypeKind.Interface) { - var item = CreateInheritanceMemberItemForInterface( + var item = await CreateInheritanceMemberItemForInterfaceAsync( solution, memberSymbol, lineNumber, baseSymbols: baseSymbols.CastArray(), derivedTypesSymbols: derivedSymbols.CastArray(), - cancellationToken); + cancellationToken).ConfigureAwait(false); builder.AddIfNotNull(item); } else { Debug.Assert(memberSymbol.TypeKind is TypeKind.Class or TypeKind.Struct); - var item = CreateInheritanceItemForClassAndStructure( + var item = await CreateInheritanceItemForClassAndStructureAsync( solution, memberSymbol, lineNumber, baseSymbols: baseSymbols.CastArray(), derivedTypesSymbols: derivedSymbols.CastArray(), - cancellationToken); + cancellationToken).ConfigureAwait(false); builder.AddIfNotNull(item); } } @@ -357,12 +357,12 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( if (implementingSymbols.Any()) { - var item = CreateInheritanceMemberItemForInterfaceMember( + var item = await CreateInheritanceMemberItemForInterfaceMemberAsync( solution, memberSymbol, lineNumber, implementingMembers: implementingSymbols, - cancellationToken); + cancellationToken).ConfigureAwait(false); builder.AddIfNotNull(item); } } @@ -384,19 +384,19 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( if (overridingSymbols.Any() || overriddenSymbols.Any() || implementedSymbols.Any()) { - var item = CreateInheritanceMemberItemForClassOrStructMember(solution, + var item = await CreateInheritanceMemberItemForClassOrStructMemberAsync(solution, memberSymbol, lineNumber, implementedMembers: implementedSymbols, overridingMembers: overridingSymbols, overriddenMembers: overriddenSymbols, - cancellationToken); + cancellationToken).ConfigureAwait(false); builder.AddIfNotNull(item); } } } - private static InheritanceMarginItem? CreateInheritanceMemberItemForInterface( + private static async ValueTask CreateInheritanceMemberItemForInterfaceAsync( Solution solution, INamedTypeSymbol interfaceSymbol, int lineNumber, @@ -404,23 +404,25 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( ImmutableArray derivedTypesSymbols, CancellationToken cancellationToken) { - var baseSymbolItems = baseSymbols + var baseSymbolItems = await baseSymbols .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArray(static (symbol, tuple) => CreateInheritanceItem( - tuple.solution, + .SelectAsArrayAsync(static (symbol, solution, cancellationToken) => CreateInheritanceItemAsync( + solution, symbol, InheritanceRelationship.InheritedInterface, - tuple.cancellationToken), (solution, cancellationToken)); + cancellationToken), solution, cancellationToken) + .ConfigureAwait(false); - var derivedTypeItems = derivedTypesSymbols + var derivedTypeItems = await derivedTypesSymbols .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArray(static (symbol, tuple) => CreateInheritanceItem( - tuple.solution, + .SelectAsArrayAsync(static (symbol, solution, cancellationToken) => CreateInheritanceItemAsync( + solution, symbol, InheritanceRelationship.ImplementingType, - tuple.cancellationToken), (solution, cancellationToken)); + cancellationToken), solution, cancellationToken) + .ConfigureAwait(false); var nonNullBaseSymbolItems = GetNonNullTargetItems(baseSymbolItems); var nonNullDerivedTypeItems = GetNonNullTargetItems(derivedTypeItems); @@ -433,21 +435,22 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( nonNullBaseSymbolItems.Concat(nonNullDerivedTypeItems)); } - private static InheritanceMarginItem? CreateInheritanceMemberItemForInterfaceMember( + private static async ValueTask CreateInheritanceMemberItemForInterfaceMemberAsync( Solution solution, ISymbol memberSymbol, int lineNumber, ImmutableArray implementingMembers, CancellationToken cancellationToken) { - var implementedMemberItems = implementingMembers + var implementedMemberItems = await implementingMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArray(static (symbol, tuple) => CreateInheritanceItem( - tuple.solution, + .SelectAsArrayAsync(static (symbol, solution, cancellationToken) => CreateInheritanceItemAsync( + solution, symbol, InheritanceRelationship.ImplementingMember, - tuple.cancellationToken), (solution, cancellationToken)); + cancellationToken), solution, cancellationToken) + .ConfigureAwait(false); var nonNullImplementedMemberItems = GetNonNullTargetItems(implementedMemberItems); return InheritanceMarginItem.CreateOrdered( @@ -458,7 +461,7 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( nonNullImplementedMemberItems); } - private static InheritanceMarginItem? CreateInheritanceItemForClassAndStructure( + private static async ValueTask CreateInheritanceItemForClassAndStructureAsync( Solution solution, INamedTypeSymbol memberSymbol, int lineNumber, @@ -468,23 +471,25 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( { // If the target is an interface, it would be shown as 'Inherited interface', // and if it is an class/struct, it whould be shown as 'Base Type' - var baseSymbolItems = baseSymbols + var baseSymbolItems = await baseSymbols .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArray(static (symbol, tuple) => CreateInheritanceItem( - tuple.solution, + .SelectAsArrayAsync(static (symbol, solution, cancellationToken) => CreateInheritanceItemAsync( + solution, symbol, symbol.IsInterfaceType() ? InheritanceRelationship.ImplementedInterface : InheritanceRelationship.BaseType, - tuple.cancellationToken), (solution, cancellationToken)); + cancellationToken), solution, cancellationToken) + .ConfigureAwait(false); - var derivedTypeItems = derivedTypesSymbols + var derivedTypeItems = await derivedTypesSymbols .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArray(static (symbol, tuple) => CreateInheritanceItem( - tuple.solution, + .SelectAsArrayAsync(static (symbol, solution, cancellationToken) => CreateInheritanceItemAsync( + solution, symbol, InheritanceRelationship.DerivedType, - tuple.cancellationToken), (solution, cancellationToken)); + cancellationToken), solution, cancellationToken) + .ConfigureAwait(false); var nonNullBaseSymbolItems = GetNonNullTargetItems(baseSymbolItems); var nonNullDerivedTypeItems = GetNonNullTargetItems(derivedTypeItems); @@ -497,7 +502,7 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( nonNullBaseSymbolItems.Concat(nonNullDerivedTypeItems)); } - private static InheritanceMarginItem? CreateInheritanceMemberItemForClassOrStructMember( + private static async ValueTask CreateInheritanceMemberItemForClassOrStructMemberAsync( Solution solution, ISymbol memberSymbol, int lineNumber, @@ -506,32 +511,35 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( ImmutableArray overriddenMembers, CancellationToken cancellationToken) { - var implementedMemberItems = implementedMembers + var implementedMemberItems = await implementedMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArray(static (symbol, tuple) => CreateInheritanceItem( - tuple.solution, + .SelectAsArrayAsync(static (symbol, solution, cancellationToken) => CreateInheritanceItemAsync( + solution, symbol, InheritanceRelationship.ImplementedMember, - tuple.cancellationToken), (solution, cancellationToken)); + cancellationToken), solution, cancellationToken) + .ConfigureAwait(false); - var overriddenMemberItems = overriddenMembers + var overriddenMemberItems = await overriddenMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArray(static (symbol, tuple) => CreateInheritanceItem( - tuple.solution, + .SelectAsArrayAsync(static (symbol, solution, cancellationToken) => CreateInheritanceItemAsync( + solution, symbol, InheritanceRelationship.OverriddenMember, - tuple.cancellationToken), (solution, cancellationToken)); + cancellationToken), solution, cancellationToken) + .ConfigureAwait(false); - var overridingMemberItems = overridingMembers + var overridingMemberItems = await overridingMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArray(static (symbol, tuple) => CreateInheritanceItem( - tuple.solution, + .SelectAsArrayAsync(static (symbol, solution, cancellationToken) => CreateInheritanceItemAsync( + solution, symbol, InheritanceRelationship.OverridingMember, - tuple.cancellationToken), (solution, cancellationToken)); + cancellationToken), solution, cancellationToken) + .ConfigureAwait(false); var nonNullImplementedMemberItems = GetNonNullTargetItems(implementedMemberItems); var nonNullOverriddenMemberItems = GetNonNullTargetItems(overriddenMemberItems); @@ -545,13 +553,13 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( nonNullImplementedMemberItems.Concat(nonNullOverriddenMemberItems, nonNullOverridingMemberItems)); } - private static InheritanceTargetItem? CreateInheritanceItem( + private static async ValueTask CreateInheritanceItemAsync( Solution solution, ISymbol targetSymbol, InheritanceRelationship inheritanceRelationship, CancellationToken cancellationToken) { - var symbolInSource = SymbolFinder.FindSourceDefinition(targetSymbol, solution, cancellationToken); + var symbolInSource = await SymbolFinder.FindSourceDefinitionAsync(targetSymbol, solution, cancellationToken).ConfigureAwait(false); targetSymbol = symbolInSource ?? targetSymbol; // Right now the targets are not shown in a classified way. diff --git a/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs b/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs index 864e44e2c2ca3..67c5cfd8bbbc8 100644 --- a/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs +++ b/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs @@ -48,8 +48,8 @@ await GetSymbolAsync(document.WithFrozenPartialSemantics(cancellationToken)).Con var solution = project.Solution; - symbol = SymbolFinder.FindSourceDefinition(symbol, solution, cancellationToken) ?? symbol; - symbol = GoToDefinitionFeatureHelpers.TryGetPreferredSymbol(solution, symbol, cancellationToken); + symbol = await SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).ConfigureAwait(false) ?? symbol; + symbol = await GoToDefinitionFeatureHelpers.TryGetPreferredSymbolAsync(solution, symbol, cancellationToken).ConfigureAwait(false); if (symbol is null or IErrorTypeSymbol) return null; @@ -61,8 +61,8 @@ await GetSymbolAsync(document.WithFrozenPartialSemantics(cancellationToken)).Con if (typeSymbol is null) return null; - typeSymbol = SymbolFinder.FindSourceDefinition(typeSymbol, solution, cancellationToken) ?? typeSymbol; - typeSymbol = GoToDefinitionFeatureHelpers.TryGetPreferredSymbol(solution, typeSymbol, cancellationToken); + typeSymbol = await SymbolFinder.FindSourceDefinitionAsync(typeSymbol, solution, cancellationToken).ConfigureAwait(false) ?? typeSymbol; + typeSymbol = await GoToDefinitionFeatureHelpers.TryGetPreferredSymbolAsync(solution, typeSymbol, cancellationToken).ConfigureAwait(false); if (typeSymbol is null or IErrorTypeSymbol) return null; diff --git a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb index fdbc52e418eaa..76d9f7d6778df 100644 --- a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb +++ b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb @@ -702,7 +702,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeSignature End If If convertedType IsNot Nothing Then - convertedType = If(SymbolFinder.FindSourceDefinition(convertedType, document.Project.Solution, cancellationToken), convertedType) + convertedType = If(Await SymbolFinder.FindSourceDefinitionAsync(convertedType, document.Project.Solution, cancellationToken).ConfigureAwait(False), convertedType) End If If Equals(convertedType, symbol.ContainingType) Then @@ -721,7 +721,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeSignature End If If nodeType IsNot Nothing Then - nodeType = If(SymbolFinder.FindSourceDefinition(nodeType, document.Project.Solution, cancellationToken), nodeType) + nodeType = If(Await SymbolFinder.FindSourceDefinitionAsync(nodeType, document.Project.Solution, cancellationToken).ConfigureAwait(False), nodeType) End If If Equals(nodeType, symbol.ContainingType) Then diff --git a/src/Features/VisualBasic/Portable/CodeFixes/GenerateEvent/GenerateEventCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/GenerateEvent/GenerateEventCodeFixProvider.vb index 458b0d27fea5a..6d1792578115f 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/GenerateEvent/GenerateEventCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/GenerateEvent/GenerateEventCodeFixProvider.vb @@ -105,7 +105,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent End If ' Target type may be in other project so we need to find its source definition - Dim sourceDefinition = SymbolFinder.FindSourceDefinition(targetType, document.Project.Solution, cancellationToken) + Dim sourceDefinition = Await SymbolFinder.FindSourceDefinitionAsync(targetType, document.Project.Solution, cancellationToken).ConfigureAwait(False) targetType = TryCast(sourceDefinition, INamedTypeSymbol) @@ -256,7 +256,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent Return Nothing End If - Dim targetType = TryCast(SymbolFinder.FindSourceDefinition(semanticModel.GetSymbolInfo(node.Left, cancellationToken).Symbol, document.Project.Solution, cancellationToken), INamedTypeSymbol) + Dim targetType = TryCast(Await SymbolFinder.FindSourceDefinitionAsync(semanticModel.GetSymbolInfo(node.Left, cancellationToken).Symbol, document.Project.Solution, cancellationToken).ConfigureAwait(False), INamedTypeSymbol) If targetType Is Nothing OrElse (targetType.TypeKind <> TypeKind.Interface AndAlso targetType.TypeKind <> TypeKind.Class) Then Return Nothing End If @@ -347,11 +347,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent Return Nothing End If - targetType = TryCast(SymbolFinder.FindSourceDefinition(withEventsProperty.Type, document.Project.Solution, cancellationToken), INamedTypeSymbol) + targetType = TryCast(Await SymbolFinder.FindSourceDefinitionAsync(withEventsProperty.Type, document.Project.Solution, cancellationToken).ConfigureAwait(False), INamedTypeSymbol) End If - targetType = TryCast(SymbolFinder.FindSourceDefinition(targetType, document.Project.Solution, cancellationToken), INamedTypeSymbol) + targetType = TryCast(Await SymbolFinder.FindSourceDefinitionAsync(targetType, document.Project.Solution, cancellationToken).ConfigureAwait(False), INamedTypeSymbol) If targetType Is Nothing OrElse Not (targetType.TypeKind = TypeKind.Class OrElse targetType.TypeKind = TypeKind.Interface) OrElse targetType.IsAnonymousType Then diff --git a/src/VisualStudio/Core/Def/Progression/GraphBuilder.cs b/src/VisualStudio/Core/Def/Progression/GraphBuilder.cs index 5caae9a75ac52..6cf11e279f1f0 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphBuilder.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphBuilder.cs @@ -210,7 +210,7 @@ public async Task AddNodeAsync( // We may need to look up source code within this solution if (preferredLocation == null && symbol.Locations.Any(static loc => loc.IsInMetadata)) { - var newSymbol = SymbolFinder.FindSourceDefinition(symbol, contextProject.Solution, cancellationToken); + var newSymbol = await SymbolFinder.FindSourceDefinitionAsync(symbol, contextProject.Solution, cancellationToken).ConfigureAwait(false); if (newSymbol != null) preferredLocation = newSymbol.Locations.Where(loc => loc.IsInSource).FirstOrDefault(); } diff --git a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs index 169f9f962f57a..043057b57b8a5 100644 --- a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs +++ b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs @@ -358,7 +358,9 @@ private SyntaxToken RenameAndAnnotate(SyntaxToken token, SyntaxToken newToken, b symbol = symbol.ContainingSymbol; } - var sourceDefinition = SymbolFinder.FindSourceDefinition(symbol, _solution, _cancellationToken); + // We cannot make this containing method async since it's being used in a rewriter. FindSourceDefinitionAsync will only yield in cross-language cases + // when the compilation is not already available, so this is expected to not really cause any significant blocking. + var sourceDefinition = SymbolFinder.FindSourceDefinitionAsync(symbol, _solution, _cancellationToken).WaitAndGetResult_CanCallOnBackground(_cancellationToken); symbol = sourceDefinition ?? symbol; if (symbol is INamedTypeSymbol namedTypeSymbol) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.SymbolSet.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.SymbolSet.cs index d5458c1696ae9..467cecbd9e1e5 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.SymbolSet.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.SymbolSet.cs @@ -65,7 +65,7 @@ public static async Task CreateAsync( var options = engine._options; // Start by mapping the initial symbol to the appropriate source symbol in originating project if possible. - var searchSymbols = MapToAppropriateSymbols(solution, symbols, cancellationToken); + var searchSymbols = await MapToAppropriateSymbolsAsync(solution, symbols, cancellationToken).ConfigureAwait(false); // If the caller doesn't want any cascading then just return an appropriate set that will just point at // only the search symbol and won't cascade to any related symbols, linked symbols, or inheritance @@ -89,17 +89,17 @@ public static async Task CreateAsync( : new BidirectionalSymbolSet(engine, initialSymbols, upSymbols, includeImplementationsThroughDerivedTypes); } - private static MetadataUnifyingSymbolHashSet MapToAppropriateSymbols( + private static async ValueTask MapToAppropriateSymbolsAsync( Solution solution, MetadataUnifyingSymbolHashSet symbols, CancellationToken cancellationToken) { var result = new MetadataUnifyingSymbolHashSet(); foreach (var symbol in symbols) - result.AddIfNotNull(TryMapToAppropriateSymbol(solution, symbol, cancellationToken)); + result.AddIfNotNull(await TryMapToAppropriateSymbolAsync(solution, symbol, cancellationToken).ConfigureAwait(false)); return result; } - private static ISymbol? TryMapToAppropriateSymbol( + private static async ValueTask TryMapToAppropriateSymbolAsync( Solution solution, ISymbol symbol, CancellationToken cancellationToken) { // Never search for an alias. Always search for it's target. Note: if the caller was @@ -132,7 +132,7 @@ private static MetadataUnifyingSymbolHashSet MapToAppropriateSymbols( // source definition as the 'truth' of a symbol versus seeing it projected into dependent cross language // projects as a metadata symbol. If there is no source symbol, then continue to just use the metadata // symbol as the one to be looking for. - var sourceSymbol = SymbolFinder.FindSourceDefinition(searchSymbol, solution, cancellationToken); + var sourceSymbol = await SymbolFinder.FindSourceDefinitionAsync(searchSymbol, solution, cancellationToken).ConfigureAwait(false); return sourceSymbol ?? searchSymbol; } @@ -209,7 +209,7 @@ protected static async Task AddCascadedAndLinkedSymbolsToAsync( async Task TryMapAndAddLinkedSymbolsAsync(ISymbol symbol) { - var mapped = TryMapToAppropriateSymbol(solution, symbol, cancellationToken); + var mapped = await TryMapToAppropriateSymbolAsync(solution, symbol, cancellationToken).ConfigureAwait(false); if (mapped is null) return null; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs index 7bff87642a8b2..585cacaefab95 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs @@ -137,10 +137,7 @@ public static async Task FindSymbolAtPositionAsync( /// Returns null if no such symbol can be found in the specified solution. /// public static Task FindSourceDefinitionAsync(ISymbol? symbol, Solution solution, CancellationToken cancellationToken = default) - => Task.FromResult(SymbolFinderInternal.FindSourceDefinition(symbol, solution, cancellationToken)); - - internal static ISymbol? FindSourceDefinition(ISymbol? symbol, Solution solution, CancellationToken cancellationToken) - => SymbolFinderInternal.FindSourceDefinition(symbol, solution, cancellationToken); + => SymbolFinderInternal.FindSourceDefinitionAsync(symbol, solution, cancellationToken).AsTask(); /// /// Finds symbols in the given compilation that are similar to the specified symbol. diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Callers.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Callers.cs index 5d70854ba3423..6bc36c7403560 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Callers.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Callers.cs @@ -38,7 +38,7 @@ public static async Task> FindCallersAsync( throw new System.ArgumentNullException(nameof(solution)); symbol = symbol.OriginalDefinition; - var foundSymbol = FindSourceDefinition(symbol, solution, cancellationToken); + var foundSymbol = await FindSourceDefinitionAsync(symbol, solution, cancellationToken).ConfigureAwait(false); symbol = foundSymbol ?? symbol; var references = await FindCallReferencesAsync(solution, symbol, documents, cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs index 43c5714c3ea5d..a53b463b88878 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs @@ -51,19 +51,19 @@ internal static async Task> FindOverridesArrayAsync( // First try finding exact overrides. If that fails to find anything, look for overrides that loosely // match due to errors. - FindOverrides(allowLooseMatch: false); + await FindOverridesAsync(allowLooseMatch: false).ConfigureAwait(false); if (results.Count == 0) - FindOverrides(allowLooseMatch: true); + await FindOverridesAsync(allowLooseMatch: true).ConfigureAwait(false); return results.ToImmutableAndClear(); - void FindOverrides(bool allowLooseMatch) + async ValueTask FindOverridesAsync(bool allowLooseMatch) { foreach (var type in derivedTypes) { foreach (var m in type.GetMembers(symbol.Name)) { - var sourceMember = FindSourceDefinition(m, solution, cancellationToken); + var sourceMember = await FindSourceDefinitionAsync(m, solution, cancellationToken).ConfigureAwait(false); var bestMember = sourceMember ?? m; if (IsOverride(solution, bestMember, symbol, allowLooseMatch)) @@ -166,7 +166,7 @@ internal static async Task> FindImplementedInterfaceMemb { foreach (var interfaceMember in interfaceType.GetMembers(symbol.Name)) { - var sourceMethod = FindSourceDefinition(interfaceMember, solution, cancellationToken); + var sourceMethod = await FindSourceDefinitionAsync(interfaceMember, solution, cancellationToken).ConfigureAwait(false); var bestMethod = sourceMethod ?? interfaceMember; var implementations = type.FindImplementationsForInterfaceMember(bestMethod, solution, cancellationToken); @@ -373,7 +373,7 @@ internal static async Task> FindMemberImplementationsArr var implementations = t.FindImplementationsForInterfaceMember(symbol, solution, cancellationToken); foreach (var implementation in implementations) { - var sourceDef = FindSourceDefinition(implementation, solution, cancellationToken); + var sourceDef = await FindSourceDefinitionAsync(implementation, solution, cancellationToken).ConfigureAwait(false); var bestDef = sourceDef ?? implementation; if (IsAccessible(bestDef)) results.Add(bestDef.OriginalDefinition); diff --git a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs index 8d8f373ce19be..bd3a117b5cc92 100644 --- a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs +++ b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs @@ -534,11 +534,12 @@ private async Task CheckForConflictAsync( hasConflict = true; - var newLocations = newReferencedSymbols - .Select(symbol => GetSymbolLocation(solution, symbol, _cancellationToken)) + var newLocations = (await newReferencedSymbols + .SelectAsArrayAsync(static (symbol, solution, cancellationToken) => GetSymbolLocationAsync(solution, symbol, cancellationToken), solution, _cancellationToken).ConfigureAwait(false)) .WhereNotNull() .Where(loc => loc.IsInSource) .ToArray(); + foreach (var originalReference in conflictAnnotation.RenameDeclarationLocationReferences.Where(loc => loc.IsSourceLocation)) { var adjustedStartPosition = conflictResolution.GetAdjustedTokenStartingPosition(originalReference.TextSpan.Start, originalReference.DocumentId); @@ -579,7 +580,7 @@ private async Task CheckForConflictAsync( break; } - var newLocation = GetSymbolLocation(solution, symbol, _cancellationToken); + var newLocation = await GetSymbolLocationAsync(solution, symbol, _cancellationToken).ConfigureAwait(false); if (newLocation != null && conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsSourceLocation) { // location was in source before, but not after rename diff --git a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs index 103368e0802a2..7d4ab01b204a6 100644 --- a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs +++ b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs @@ -359,12 +359,18 @@ public static RenameDeclarationLocationReference[] CreateDeclarationLocationAnno if (overriddenSymbol != null) { - overriddenSymbol = SymbolFinder.FindSourceDefinition(overriddenSymbol, solution, cancellationToken); + // Unfortunately we cannot easily make CreateDeclarationLocationAnnotations async, as it's used in the rewriter that is rewriting trees. + // The asynchrony in GetSymbolLocationAsync comes from SymbolFinder.FindSourceDefinitionAsync() which will only be async in the cross-language case, and only once + // when the compilation wasn't already available. + overriddenSymbol = SymbolFinder.FindSourceDefinitionAsync(overriddenSymbol, solution, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); overriddenFromMetadata = overriddenSymbol == null || overriddenSymbol.Locations.All(loc => loc.IsInMetadata); } } - var location = GetSymbolLocation(solution, symbol, cancellationToken); + // Unfortunately we cannot easily make CreateDeclarationLocationAnnotations async, as it's used in the rewriter that is rewriting trees. + // The asynchrony in GetSymbolLocationAsync comes from SymbolFinder.FindSourceDefinitionAsync() which will only be async in the cross-language case, and only once + // when the compilation wasn't already available. + var location = GetSymbolLocationAsync(solution, symbol, cancellationToken).AsTask().WaitAndGetResult_CanCallOnBackground(cancellationToken); if (location != null && location.IsInSource) { renameDeclarationLocations[symbolIndex] = new RenameDeclarationLocationReference(solution.GetDocumentId(location.SourceTree), location.SourceSpan, overriddenFromMetadata, locations.Length); @@ -397,11 +403,11 @@ private static string GetString(ISymbol symbol) /// /// Gives the First Location for a given Symbol by ordering the locations using DocumentId first and Location starting position second /// - private static Location? GetSymbolLocation(Solution solution, ISymbol symbol, CancellationToken cancellationToken) + private static async ValueTask GetSymbolLocationAsync(Solution solution, ISymbol symbol, CancellationToken cancellationToken) { var locations = symbol.Locations; - var originalsourcesymbol = SymbolFinder.FindSourceDefinition(symbol, solution, cancellationToken); + var originalsourcesymbol = await SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).ConfigureAwait(false); if (originalsourcesymbol != null) locations = originalsourcesymbol.Locations; diff --git a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs index 471b1941a4742..b1eabd87a360c 100644 --- a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs +++ b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs @@ -215,8 +215,8 @@ public static IEnumerable GetOverloadedSymbols(ISymbol symbol) if (symbol.IsOverride && symbol.GetOverriddenMember() != null) { - var originalSourceSymbol = SymbolFinder.FindSourceDefinition( - symbol.GetOverriddenMember(), solution, cancellationToken); + var originalSourceSymbol = await SymbolFinder.FindSourceDefinitionAsync( + symbol.GetOverriddenMember(), solution, cancellationToken).ConfigureAwait(false); if (originalSourceSymbol != null) return await TryGetPropertyFromAccessorOrAnOverrideAsync(originalSourceSymbol, solution, cancellationToken).ConfigureAwait(false); @@ -319,8 +319,8 @@ public static async Task FindDefinitionSymbolAsync( Contract.ThrowIfNull(solution); // Make sure we're on the original source definition if we can be - var foundSymbol = SymbolFinder.FindSourceDefinition( - symbol, solution, cancellationToken); + var foundSymbol = await SymbolFinder.FindSourceDefinitionAsync( + symbol, solution, cancellationToken).ConfigureAwait(false); var bestSymbol = foundSymbol ?? symbol; symbol = bestSymbol; diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions.cs index d2345bb39a186..977f2a94ec94f 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions.cs @@ -77,7 +77,8 @@ public static async Task OverridePropertyAsync( { // Call accessors directly if C# overriding VB if (document.Project.Language == LanguageNames.CSharp - && SymbolFinder.FindSourceDefinition(overriddenProperty, document.Project.Solution, cancellationToken) is { Language: LanguageNames.VisualBasic }) + && (await SymbolFinder.FindSourceDefinitionAsync(overriddenProperty, document.Project.Solution, cancellationToken).ConfigureAwait(false)) + is { Language: LanguageNames.VisualBasic }) { var getName = overriddenProperty.GetMethod?.Name; var setName = overriddenProperty.SetMethod?.Name; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/SymbolFinder/SymbolFinderInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/SymbolFinder/SymbolFinderInternal.cs index d16e37c51fde3..c6cee45a9fc67 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/SymbolFinder/SymbolFinderInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/SymbolFinder/SymbolFinderInternal.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -14,7 +15,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols; internal static class SymbolFinderInternal { /// - internal static ISymbol? FindSourceDefinition( + internal static ValueTask FindSourceDefinitionAsync( ISymbol? symbol, Solution solution, CancellationToken cancellationToken) { if (symbol != null) @@ -31,14 +32,14 @@ internal static class SymbolFinderInternal case SymbolKind.Property: case SymbolKind.TypeParameter: case SymbolKind.Namespace: - return FindSourceDefinitionWorker(symbol, solution, cancellationToken); + return FindSourceDefinitionWorkerAsync(symbol, solution, cancellationToken); } } - return null; + return new ValueTask(result: null); } - private static ISymbol? FindSourceDefinitionWorker( + private static async ValueTask FindSourceDefinitionWorkerAsync( ISymbol symbol, Solution solution, CancellationToken cancellationToken) @@ -75,11 +76,9 @@ internal static class SymbolFinderInternal var project = solution.GetProject(symbol.ContainingAssembly, cancellationToken); - // Note: if the assembly came from a particular project, then we should be able to get the compilation without - // building it. That's because once we create the compilation, we'll hold onto it for the lifetime of the - // project, to avoid unnecessary recomputation. - if (project?.TryGetCompilation(out var projectCompilation) is true) + if (project is not null) { + var projectCompilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); var symbolId = symbol.GetSymbolKey(cancellationToken); var result = symbolId.Resolve(projectCompilation, ignoreAssemblyKey: true, cancellationToken: cancellationToken);