Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Allow two symbols to be found at position in tuple (FAR, Highlighting) #24363

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5299,6 +5299,8 @@ public string ToMinimalDisplayString(SemanticModel semanticModel, int position,

public bool IsComImport => throw new NotImplementedException();

public ImmutableArray<string> TupleElementNames => throw new NotImplementedException();

#endregion
}

Expand Down
1 change: 1 addition & 0 deletions src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Microsoft.CodeAnalysis.Diagnostics.Telemetry.AnalyzerTelemetryInfo.OperationBloc
Microsoft.CodeAnalysis.ILocalSymbol.RefKind.get -> Microsoft.CodeAnalysis.RefKind
Microsoft.CodeAnalysis.IMethodSymbol.RefKind.get -> Microsoft.CodeAnalysis.RefKind
Microsoft.CodeAnalysis.IMethodSymbol.ReturnsByRefReadonly.get -> bool
Microsoft.CodeAnalysis.INamedTypeSymbol.TupleElementNames.get -> System.Collections.Immutable.ImmutableArray<string>
Microsoft.CodeAnalysis.IOperation
Microsoft.CodeAnalysis.IOperation.Accept(Microsoft.CodeAnalysis.Operations.OperationVisitor visitor) -> void
Microsoft.CodeAnalysis.IOperation.Accept<TArgument, TResult>(Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult> visitor, TArgument argument) -> TResult
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,5 +165,7 @@ public interface INamedTypeSymbol : ITypeSymbol
/// If this type is not a tuple, then returns default.
/// </summary>
ImmutableArray<IFieldSymbol> TupleElements { get; }

ImmutableArray<string> TupleElementNames { get; }
}
}
6 changes: 6 additions & 0 deletions src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
End Get
End Property

Private ReadOnly Property INamedTypeSymbol_TupleElementNames As ImmutableArray(Of String) Implements INamedTypeSymbol.TupleElementNames
Get
Return TupleElementNames
End Get
End Property

Private ReadOnly Property INamedTypeSymbol_TupleUnderlyingType As INamedTypeSymbol Implements INamedTypeSymbol.TupleUnderlyingType
Get
Return TupleUnderlyingType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ private async Task FindSymbolReferencesAsync(

await FindSymbolReferencesAsync(
context, symbolAndProject?.symbol, symbolAndProject?.project, cancellationToken).ConfigureAwait(false);

// PROTOTYPE
var secondary = symbolAndProject.Value.secondary;
if (secondary != null)
{
await FindSymbolReferencesAsync(
context, secondary, symbolAndProject?.project, cancellationToken).ConfigureAwait(false);
}
}

/// <summary>
Expand Down
15 changes: 10 additions & 5 deletions src/EditorFeatures/Core/FindUsages/FindUsagesHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,34 @@ public static string GetDisplayName(ISymbol symbol)
/// there may be symbol mapping involved (for example in Metadata-As-Source
/// scenarios).
/// </summary>
public static async Task<(ISymbol symbol, Project project)?> GetRelevantSymbolAndProjectAtPositionAsync(
public static async Task<(ISymbol symbol, ISymbol secondary, Project project)?> GetRelevantSymbolAndProjectAtPositionAsync(
Document document, int position, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken: cancellationToken).ConfigureAwait(false);
if (symbol == null)
var symbols = await SymbolFinder.FindSymbolExAtPositionAsync(document, position, cancellationToken: cancellationToken).ConfigureAwait(false);
if (symbols.primary == null)
{
return null;
}

if (symbols.secondary != null)
{
return (symbols.primary, symbols.secondary, document.Project);
}

// If this document is not in the primary workspace, we may want to search for results
// in a solution different from the one we started in. Use the starting workspace's
// ISymbolMappingService to get a context for searching in the proper solution.
var mappingService = document.Project.Solution.Workspace.Services.GetService<ISymbolMappingService>();

var mapping = await mappingService.MapSymbolAsync(document, symbol, cancellationToken).ConfigureAwait(false);
var mapping = await mappingService.MapSymbolAsync(document, symbols.primary, cancellationToken).ConfigureAwait(false);
if (mapping == null)
{
return null;
}

return (mapping.Symbol, mapping.Project);
return (mapping.Symbol, null, mapping.Project);
}

public static async Task<(ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, string message)?> FindImplementationsAsync(Document document, int position, CancellationToken cancellationToken)
Expand Down
110 changes: 107 additions & 3 deletions src/EditorFeatures/Test2/FindReferences/FindReferencesTests.Tuples.vb
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ namespace System
{
static void Main(string[] args)
{
var x = ({|Definition:Alice|}:1, Bob: 2);
var y = (Alice:1, Bob: 2);
var x = ([|{|Definition:Alice|}|]:1, Bob: 2);
var y = ([|Alice|]:1, Bob: 2); // PROTOTYPE confirm if this is desirable

var z = x.$$[|Item1|];
}
Expand Down Expand Up @@ -278,7 +278,7 @@ namespace System
var x = ($$[|{|Definition:Alice|}|]:1, Bob: 2);
var y = ([|Alice|]:1, Bob: 2);

var z = x.Item1;
var z = x.[|Item1|];
}
}

Expand Down Expand Up @@ -346,5 +346,109 @@ namespace System
</Workspace>
Await TestAPIAndFeature(input)
End Function

<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
<WorkItem(20115, "https://github.com/dotnet/roslyn/issues/20115")>
Public Async Function TestDualViewOnTupleElement() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document><![CDATA[
class Program
{
static void Main(int {|Definition:Alice|})
{
var x = ({|Definition:[|Ali$$ce|]|}, Bob: 2);
var y = ([|Alice|]:1, Bob: 2);

var z = x.[|Alice|];
var z2 = x.[|Item1|];
}
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function

<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
<WorkItem(20115, "https://github.com/dotnet/roslyn/issues/20115")>
Public Async Function TestDualViewOnTupleElement2() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document><![CDATA[
class Program
{
static void Main(int {|Definition:Al$$ice|})
{
var x = ([|Alice|], Bob: 2);
var y = (Alice:1, Bob: 2);

var z = x.Alice;
var z2 = x.Item1;
}
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function

<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
<WorkItem(20115, "https://github.com/dotnet/roslyn/issues/20115")>
Public Async Function TestDualViewOnTupleElement3() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document><![CDATA[
class Program
{
static void Main(int Alice)
{
var x = ({|Definition:Alice|}, Bob: 2);
var y = ([|Alice|]:1, Bob: 2);

var z = x.[|Al$$ice|];
var z2 = x.[|Item1|];
}
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function

<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
<WorkItem(20115, "https://github.com/dotnet/roslyn/issues/20115")>
Public Async Function TestDualViewOnTupleElement4() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document><![CDATA[
class Program
{
static void Main(int Alice)
{
var x = ({|Definition:Alice|}, Bob: 2);
var y = ([|Alice|]:1, Bob: 2);

var z = x.[|Alice|];
var z2 = x.[|It$$em1|];
}
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
End Class
End Namespace
58 changes: 44 additions & 14 deletions src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,17 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
Dim document = workspace.CurrentSolution.GetDocument(cursorDocument.Id)
Assert.NotNull(document)

Dim symbol = Await SymbolFinder.FindSymbolAtPositionAsync(document, cursorPosition)
Dim symbols = Await SymbolFinder.FindSymbolExAtPositionAsync(document, cursorPosition)
Dim result = SpecializedCollections.EmptyEnumerable(Of ReferencedSymbol)()
If symbol IsNot Nothing Then
If symbols.primary IsNot Nothing Then

Dim scope = If(searchSingleFileOnly, ImmutableHashSet.Create(Of Document)(document), Nothing)

result = result.Concat(Await SymbolFinder.FindReferencesAsync(symbol, document.Project.Solution, progress:=Nothing, documents:=scope))
result = result.Concat(Await SymbolFinder.FindReferencesAsync(symbols.primary, document.Project.Solution, progress:=Nothing, documents:=scope))

If symbols.secondary IsNot Nothing Then
result = result.Concat(Await SymbolFinder.FindReferencesAsync(symbols.secondary, document.Project.Solution, progress:=Nothing, documents:=scope))
End If
End If

Dim actualDefinitions =
Expand All @@ -208,7 +212,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
Dim actual = actualDefinitions(GetFilePathAndProjectLabel(workspace, doc)).Order()

If Not TextSpansMatch(expected, actual) Then
Assert.True(False, PrintSpans(expected, actual, workspace.CurrentSolution.GetDocument(doc.Id), "{|Definition:", "|}"))
Assert.True(False, PrintSpans(expected, actual, "Definition", workspace.CurrentSolution.GetDocument(doc.Id)))
End If
Next

Expand All @@ -230,13 +234,26 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
Dim actualSpans = actualReferences(GetFilePathAndProjectLabel(workspace, doc)).Order()

AssertEx.Equal(expectedSpans, actualSpans,
message:=PrintSpans(expectedSpans, actualSpans, workspace.CurrentSolution.GetDocument(doc.Id), "[|", "|]", messageOnly:=True))
message:=PrintSpans(expectedSpans, actualSpans, name:=Nothing, workspace.CurrentSolution.GetDocument(doc.Id), messageOnly:=True))
Next
Next
End Using
End Function

Private Shared Function PrintSpans(expected As IOrderedEnumerable(Of TextSpan), actual As IOrderedEnumerable(Of TextSpan), doc As Document, prefix As String, suffix As String, Optional messageOnly As Boolean = False) As String
Public Shared Function PrintSpans(expected As IEnumerable(Of TextSpan),
actual As IEnumerable(Of TextSpan),
name As String, doc As Document, Optional messageOnly As Boolean = False) As String
Return PrintSpans(expected.Select(Function(e) (name, e)), actual.Select(Function(a) (name, a)), doc, messageOnly)
End Function

Public Shared Function PrintSpans(expected As IEnumerable(Of (name As String, span As TextSpan)),
actual As IEnumerable(Of (name As String, span As TextSpan)),
doc As Document, Optional messageOnly As Boolean = False) As String

expected = expected.OrderBy(Function(e) e.span)
actual = actual.OrderBy(Function(a) a.span)


Debug.Assert(expected IsNot Nothing)
Debug.Assert(actual IsNot Nothing)

Expand All @@ -245,20 +262,33 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences

builder.AppendLine()
If Not messageOnly Then
builder.AppendLine($"Expected: {String.Join(", ", expected.Select(Function(e) e.ToString()))}")
builder.AppendLine($"Actual: {String.Join(", ", actual.Select(Function(a) a.ToString()))}")
builder.AppendLine($"Expected: {String.Join(", ", expected.Select(Function(e) e.name + ":" + e.span.ToString()))}")
builder.AppendLine($"Actual: {String.Join(", ", actual.Select(Function(a) a.name + ":" + a.span.ToString()))}")
End If

Dim text As SourceText = Nothing
doc.TryGetText(text)
Dim position = 0

For Each span In actual
builder.Append(text.GetSubText(New TextSpan(position, span.Start - position)))
builder.Append(prefix)
builder.Append(text.GetSubText(span))
builder.Append(suffix)
position = span.End
Dim actualGrouped = actual.GroupBy(Function(a) a.span)
For Each group In actualGrouped
builder.Append(text.GetSubText(New TextSpan(position, group.Key.Start - position)))

For Each name In group.Select(Function(g) g.name)
builder.Append(If(name Is Nothing, "[|", "{|"))
If name IsNot Nothing Then
builder.Append(name)
builder.Append(":")
End If
Next

builder.Append(text.GetSubText(group.Key))

For Each name In group.Select(Function(g) g.name)
builder.Append(If(name Is Nothing, "|]", "|}"))
Next

position = group.Key.End
Next
builder.Append(text.GetSubText(New TextSpan(position, text.Length - position)))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Imports Microsoft.CodeAnalysis.Notification
Imports Microsoft.CodeAnalysis.Remote
Imports Microsoft.CodeAnalysis.Shared.TestHooks
Imports Microsoft.CodeAnalysis.Test.Utilities.RemoteHost
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.VisualStudio.Text
Imports Roslyn.Utilities

Expand Down Expand Up @@ -50,19 +51,22 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting
Order By tag.Span.Start
Let spanType = If(tag.Tag.Type = DefinitionHighlightTag.TagId, "Definition",
If(tag.Tag.Type = WrittenReferenceHighlightTag.TagId, "WrittenReference", "Reference"))
Select spanType + ":" + tag.Span.Span.ToTextSpan().ToString()
Select (name:=spanType, Span:=tag.Span.Span.ToTextSpan())

Dim expectedTags As New List(Of String)
Dim expectedTags As New List(Of (name As String, span As TextSpan))

For Each hostDocument In workspace.Documents
For Each nameAndSpans In hostDocument.AnnotatedSpans
For Each span In nameAndSpans.Value
expectedTags.Add(nameAndSpans.Key + ":" + span.ToString())
expectedTags.Add((nameAndSpans.Key, span))
Next
Next
Next

AssertEx.Equal(expectedTags, producedTags)
AssertEx.Equal(expectedTags, producedTags,
message:=FindReferences.FindReferencesTests.PrintSpans(expectedTags,
producedTags,
document, messageOnly:=True))
End Using
End Function
End Class
Expand Down
Loading