Skip to content

Commit

Permalink
Merge pull request #39232 from dotnet/merges/master-to-features/stati…
Browse files Browse the repository at this point in the history
…c-lambdas

Merge master to features/static-lambdas
  • Loading branch information
dibarbet authored Oct 11, 2019
2 parents e78682a + 23ca0ff commit 9f64936
Show file tree
Hide file tree
Showing 22 changed files with 481 additions and 196 deletions.
57 changes: 42 additions & 15 deletions src/EditorFeatures/Core/GoToBase/AbstractGoToBaseService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// 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.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.FindSymbols;
Expand All @@ -12,34 +13,60 @@ internal abstract partial class AbstractGoToBaseService : IGoToBaseService
public async Task FindBasesAsync(Document document, int position, IFindUsagesContext context)
{
var cancellationToken = context.CancellationToken;
var tuple = await FindBaseHelpers.FindBasesAsync(document, position, cancellationToken).ConfigureAwait(false);
if (tuple == null)
var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);

if (symbolAndProject == default)
{
await context.ReportMessageAsync(
EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret).ConfigureAwait(false);
return;
}

var (symbol, implementations, message) = tuple.Value;

if (message != null)
{
await context.ReportMessageAsync(message).ConfigureAwait(false);
return;
}
var symbol = symbolAndProject.Value.symbol;
var bases = FindBaseHelpers.FindBases(
symbol, symbolAndProject.Value.project, cancellationToken);

await context.SetSearchTitleAsync(
string.Format(EditorFeaturesResources._0_bases,
FindUsagesHelpers.GetDisplayName(symbol))).ConfigureAwait(false);

var solution = document.Project.Solution;
var project = document.Project;
var solution = project.Solution;
var projectId = project.Id;

var found = false;

// For each potential base, try to find its definition in sources.
// If found, add its' definitionItem to the context.
// If not found but the symbol is from metadata, create its' definition item from metadata and add to the context.
foreach (var baseSymbol in bases)
{
var sourceDefinition = await SymbolFinder.FindSourceDefinitionAsync(
SymbolAndProjectId.Create(baseSymbol, projectId), solution, cancellationToken).ConfigureAwait(false);
if (sourceDefinition.Symbol != null &&
sourceDefinition.Symbol.Locations.Any(l => l.IsInSource))
{
var definitionItem = await sourceDefinition.Symbol.ToClassifiedDefinitionItemAsync(
solution.GetProject(sourceDefinition.ProjectId), includeHiddenLocations: false,
FindReferencesSearchOptions.Default, cancellationToken: cancellationToken)
.ConfigureAwait(false);
await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false);
found = true;
}
else if (baseSymbol.Locations.Any(l => l.IsInMetadata))
{
var definitionItem = baseSymbol.ToNonClassifiedDefinitionItem(
project, includeHiddenLocations: true);
await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false);
found = true;
}
}

foreach (var implementation in implementations)
if (!found)
{
var definitionItem = await implementation.Symbol.ToClassifiedDefinitionItemAsync(
solution.GetProject(implementation.ProjectId), includeHiddenLocations: false,
FindReferencesSearchOptions.Default, cancellationToken: cancellationToken).ConfigureAwait(false);
await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false);
await context.ReportMessageAsync(EditorFeaturesResources.The_symbol_has_no_base)
.ConfigureAwait(false);
}
}
}
Expand Down
41 changes: 11 additions & 30 deletions src/EditorFeatures/Core/GoToBase/FindBaseHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,52 +1,33 @@
// 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.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindSymbols.FindReferences;

namespace Microsoft.CodeAnalysis.Editor.GoToBase
{
internal static class FindBaseHelpers
{
public static async Task<(ISymbol symbol, ImmutableArray<SymbolAndProjectId> implementations, string message)?> FindBasesAsync(Document document, int position, CancellationToken cancellationToken)
{
var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndProject == null)
{
return null;
}

var symbol = symbolAndProject.Value.symbol;
var project = symbolAndProject.Value.project;

var bases = await FindBasesWorkerAsync(symbol, project, cancellationToken).ConfigureAwait(false);
var filteredSymbols = bases.WhereAsArray(s => s.Symbol.Locations.Any(l => l.IsInSource));

return filteredSymbols.Length == 0
? (symbol, filteredSymbols, EditorFeaturesResources.The_symbol_has_no_base)
: (symbol, filteredSymbols, null);
}

private static async Task<ImmutableArray<SymbolAndProjectId>> FindBasesWorkerAsync(
public static ImmutableArray<ISymbol> FindBases(
ISymbol symbol, Project project, CancellationToken cancellationToken)
{
if (symbol is INamedTypeSymbol namedTypeSymbol &&
(namedTypeSymbol.TypeKind == TypeKind.Class || namedTypeSymbol.TypeKind == TypeKind.Interface || namedTypeSymbol.TypeKind == TypeKind.Struct))
(namedTypeSymbol.TypeKind == TypeKind.Class ||
namedTypeSymbol.TypeKind == TypeKind.Interface ||
namedTypeSymbol.TypeKind == TypeKind.Struct))
{
return await BaseTypeFinder.FindBaseTypesAndInterfacesAsync(namedTypeSymbol, project, cancellationToken).ConfigureAwait(false);
return BaseTypeFinder.FindBaseTypesAndInterfaces(namedTypeSymbol);
}
else if (symbol.Kind == SymbolKind.Property || symbol.Kind == SymbolKind.Method || symbol.Kind == SymbolKind.Event)
else if (symbol.Kind == SymbolKind.Property ||
symbol.Kind == SymbolKind.Method ||
symbol.Kind == SymbolKind.Event)
{
return await BaseTypeFinder.FindOverriddenAndImplementedMembersAsync(symbol, project, cancellationToken).ConfigureAwait(false);
return BaseTypeFinder.FindOverriddenAndImplementedMembers(
symbol, project, cancellationToken);
}
else
{
return ImmutableArray.Create<SymbolAndProjectId>();
return ImmutableArray<ISymbol>.Empty;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public AsyncCompletionData.CommitResult TryCommit(
return new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.None);
}

if (!Helpers.TryGetInitialTriggerLocation(session, out var triggerLocation))
if (!Helpers.TryGetInitialTriggerLocation(item, out var triggerLocation))
{
// Need the trigger snapshot to calculate the span when the commit changes to be applied.
// They should always be available from VS. Just to be defensive, if it's not found here, Roslyn should not make a commit.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncComplet
internal class CompletionSource : ForegroundThreadAffinitizedObject, IAsyncExpandingCompletionSource
{
internal const string RoslynItem = nameof(RoslynItem);
internal const string TriggerLocation = nameof(TriggerLocation);
internal const string CompletionListSpan = nameof(CompletionListSpan);
internal const string InsertionText = nameof(InsertionText);
internal const string HasSuggestionItemOptions = nameof(HasSuggestionItemOptions);
Expand Down Expand Up @@ -284,7 +285,7 @@ private bool TryInvokeSnippetCompletion(
foreach (var roslynItem in completionList.Items)
{
cancellationToken.ThrowIfCancellationRequested();
var item = Convert(document, roslynItem, filterSet);
var item = Convert(document, roslynItem, filterSet, triggerLocation);
itemsBuilder.Add(item);
}

Expand Down Expand Up @@ -408,7 +409,8 @@ public VSCompletionItemData(
private VSCompletionItem Convert(
Document document,
RoslynCompletionItem roslynItem,
FilterSet filterSet)
FilterSet filterSet,
SnapshotPoint triggerLocation)
{
VSCompletionItemData itemData;

Expand Down Expand Up @@ -462,6 +464,7 @@ private VSCompletionItem Convert(
attributeIcons: itemData.AttributeIcons);

item.Properties.AddProperty(RoslynItem, roslynItem);
item.Properties.AddProperty(TriggerLocation, triggerLocation);

return item;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ internal static bool TryGetInitialTriggerLocation(EditorAsyncCompletion.IAsyncCo
return false;
}

internal static bool TryGetInitialTriggerLocation(VSCompletionItem item, out SnapshotPoint initialTriggerLocation)
{
if (item.Properties.TryGetProperty(CompletionSource.TriggerLocation, out initialTriggerLocation))
{
return true;
}

initialTriggerLocation = default;
return false;
}

// This is a temporarily method to support preference of IntelliCode items comparing to non-IntelliCode items.
// We expect that Editor will introduce this support and we will get rid of relying on the "★" then.
internal static bool IsPreferredItem(this RoslynCompletionItem completionItem)
Expand Down
61 changes: 44 additions & 17 deletions src/EditorFeatures/Test2/GoToBase/CSharpGoToBaseTests.vb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToBase
<[UseExportProvider]>
Public Class CSharpGoToBaseTests
Inherits GoToBaseTestsBase
Private Overloads Async Function TestAsync(source As String, Optional shouldSucceed As Boolean = True) As Task
Await TestAsync(source, LanguageNames.CSharp, shouldSucceed)
Private Overloads Async Function TestAsync(source As String, Optional shouldSucceed As Boolean = True,
Optional metadataDefinitions As String() = Nothing) As Task
Await TestAsync(source, LanguageNames.CSharp, shouldSucceed, metadataDefinitions)
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Expand All @@ -17,7 +18,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToBase

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Public Async Function TestWithSingleClass() As Task
Await TestAsync("class $$C { }")
Await TestAsync("class $$C { }", metadataDefinitions:={"mscorlib:Object"})
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Expand All @@ -29,15 +30,15 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToBase

class $$D : C
{
}")
}", metadataDefinitions:={"mscorlib:Object"})
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Public Async Function TestWithAbstractClassFromInterface() As Task
Await TestAsync(
"interface [|I|] { }
abstract class [|C|] : I { }
class $$D : C { }")
class $$D : C { }", metadataDefinitions:={"mscorlib:Object"})
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Expand All @@ -46,7 +47,7 @@ class $$D : C { }")
"class [|D|] { }
sealed class $$C : D
{
}")
}", metadataDefinitions:={"mscorlib:Object"})
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Expand All @@ -66,22 +67,22 @@ sealed class $$C : D

class $$D : C
{
}")
}", metadataDefinitions:={"mscorlib:Object"})
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Public Async Function TestWithSingleClassImplementation() As Task
Await TestAsync(
"class $$C : I { }
interface [|I|] { }")
interface [|I|] { }", metadataDefinitions:={"mscorlib:Object"})
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Public Async Function TestWithTwoClassImplementations() As Task
Await TestAsync(
"class $$C : I { }
class D : I { }
interface [|I|] { }")
interface [|I|] { }", metadataDefinitions:={"mscorlib:Object"})
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Expand All @@ -96,7 +97,7 @@ interface [|I2|] : I { }
interface I1 : I { }
interface [|I|] : J1, J2 { }
interface [|J1|] { }
interface [|J2|] { }")
interface [|J2|] { }", metadataDefinitions:={"mscorlib:Object"})
End Function

#End Region
Expand All @@ -108,14 +109,14 @@ interface [|J2|] { }")
Await TestAsync(
"struct $$C
{
}")
}", metadataDefinitions:={"mscorlib:Object", "mscorlib:ValueType"})
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Public Async Function TestWithSingleStructImplementation() As Task
Await TestAsync(
"struct $$C : I { }
interface [|I|] { }")
interface [|I|] { }", metadataDefinitions:={"mscorlib:Object", "mscorlib:ValueType"})
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Expand All @@ -126,7 +127,7 @@ interface [|I2|] : I { }
interface I1 : I { }
interface [|I|] : J1, J2 { }
interface [|J1|] { }
interface [|J2|] { }")
interface [|J2|] { }", metadataDefinitions:={"mscorlib:Object", "mscorlib:ValueType"})
End Function

#End Region
Expand Down Expand Up @@ -283,11 +284,12 @@ interface I { void [|M|](); }")

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Public Async Function TestWithVirtualMethodHiddenWithInterfaceOnBaseClass() As Task
' We should not find a hidden method.
' We should not find hidden methods
' and methods in interfaces if hidden below but the nested class does not implement the interface.
Await TestAsync(
"class C : I { public virtual void M() { } }
class D : C { public new void $$M() { } }
interface I { void [|M|](); }")
interface I { void M(); }")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Expand Down Expand Up @@ -317,7 +319,8 @@ interface I { void [|M|](); }")

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Public Async Function TestWithVirtualMethodHiddenAndInterfaceImplementedOnDerivedType() As Task
' We should not find a hidden method.
' We should not find hidden methods
' but should find methods in interfaces if hidden below but the nested class implements the interface.
Await TestAsync(
"class C : I { public virtual void M() { } }
class D : C, I { public new void $$M() { } }
Expand All @@ -328,7 +331,7 @@ interface I { void [|M|](); }")
Public Async Function TestWithAbstractMethodImplementation() As Task
Await TestAsync(
"abstract class C : I { public abstract void [|M|]() { } }
class D : C { public override void $$M() { } }}
class D : C { public override void $$M() { } }
interface I { void [|M|](); }")
End Function

Expand Down Expand Up @@ -386,6 +389,30 @@ sealed class C2 : A {
}")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Public Async Function TestWithOverloadsOverrdiesAndInterfaceImplementation_01() As Task
Await TestAsync(
"abstract class C : I { public virtual void [|M|]() { } public virtual void M(int i) { }}
class D : C { public override void $$M() { } public override void M(int i) { }}
interface I { void [|M|](); void M(int i};")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Public Async Function TestWithOverloadsOverrdiesAndInterfaceImplementation_02() As Task
Await TestAsync(
"abstract class C : I { public virtual void M() { } public virtual void [|M|](int i) { }}
class D : C { public override void M() { } public override void $$M(int i) { }}
interface I { void M(); void [|M|](int i};")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.GoToBase)>
Public Async Function TestOverrideOfMethodFromMetadata() As Task
Await TestAsync(
"using System;
class C { public override string $$ToString() { return base.ToString(); } }
", metadataDefinitions:={"mscorlib:Object.ToString"})
End Function

#End Region

#Region "Properties and Events"
Expand Down
Loading

0 comments on commit 9f64936

Please sign in to comment.