Skip to content

Conversation

@ivanbasov
Copy link
Contributor

Fixes #38700

  1. Support Go to base in case with bases is in metadata.
  2. Removes interfaces implemented by hidden by new/Shadows members from the base hierarchy, if the interface is not implemented below the hide. For example:
interface I { void M(); }
class C : I { public virtual void M(); }
class D : C { public new void M(); }

here

I i = new D();
i.M(); 

calls C.M() not D.M(). Therefore, I.M() is not a base for D.M(). Fixed this.
3. Fixed a issue with overloads and interfaces.

@ivanbasov ivanbasov requested review from a team, JoeRobich, jasonmalinowski and olegtk October 4, 2019 01:07
@ivanbasov ivanbasov added this to the 16.4.P3 milestone Oct 4, 2019
@ivanbasov
Copy link
Contributor Author

ivanbasov commented Oct 4, 2019

@olegtk could you please review dcb65ae ?
Is it the right way to navigate from table entries? #Resolved


var found = false;

foreach (var baseSymbol in bases)
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Oct 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doc what this is doing. #Resolved

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

@CyrusNajmabadi CyrusNajmabadi Oct 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a fan of passing an omitted arg. #Resolved

var inlines = DefinitionBucket.DefinitionItem.DisplayParts
.ToInlines(_presenter.ClassificationFormatMap, _presenter.TypeMap);

var textBlock = inlines.ToTextBlock(_presenter.ClassificationFormatMap, wrap: false);
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Oct 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this copying code elsewhere? can we share? #Resolved

{
content = _entries[index];
return true;
}
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Oct 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

who calls into this with this value? #Resolved

return await ConvertToSymbolAndProjectIdsAsync(typesAndInterfaces.CastArray<ISymbol>(), project, cancellationToken).ConfigureAwait(false);
}
public static ImmutableArray<ISymbol> FindBaseTypesAndInterfaces(
INamedTypeSymbol type)
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Oct 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to prevous line. #Resolved

@ivanbasov
Copy link
Contributor Author

ivanbasov commented Oct 8, 2019

Thank you for the review, @CyrusNajmabadi ! Feedback addressed.

@jasonmalinowski and @JoeRobich , please take a look. #Resolved

Copy link
Member

@JoeRobich JoeRobich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ivanbasov ivanbasov merged commit 94afbe5 into dotnet:master Oct 10, 2019
@ivanbasov ivanbasov deleted the GoToBase branch October 10, 2019 22:03
Copy link
Member

@jasonmalinowski jasonmalinowski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ivanbasov I know I'm late to the party, but looking at the PR I'm wondering if there's actually a compiler bug around the "new"/hidden method case? Or are we misunderstanding something else here about the language behavior?

Next

Dim actualDefintionsWithoutSpans = context.GetDefinitions().
Where(Function(d) d.SourceSpans.IsDefaultOrEmpty).
Copy link
Member

@jasonmalinowski jasonmalinowski Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indenting is off here, and usually we always do a _ at the end of a previous line here so the . stays on the next line. #Resolved

var found = false;

// For each potential base, try to find its definition in sources.
// If found, add its' definitionItem to the context.
Copy link
Member

@jasonmalinowski jasonmalinowski Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"it's" (and everywhere else) #Resolved

var sourceDefinition = await SymbolFinder.FindSourceDefinitionAsync(
SymbolAndProjectId.Create(baseSymbol, projectId), solution, cancellationToken).ConfigureAwait(false);
if (sourceDefinition.Symbol != null &&
sourceDefinition.Symbol.Locations.Any(l => l.IsInSource))
Copy link
Member

@jasonmalinowski jasonmalinowski Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FindSourceDefinitionAsync should only be returning symbols from source in the first place... #Resolved

Public Async Function TestWithVirtualMethodHiddenAndInterfaceImplementedOnDerivedType() As Task
' We should not find a hidden method.
' We should not find hidden methods.
' We should not find methods of interfaces no implemented by the method symbol.
Copy link
Member

@jasonmalinowski jasonmalinowski Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"not"? #Resolved

metadataDefinitions = {}
End If

Assert.Equal(actualDefintionsWithoutSpans.Count, metadataDefinitions.Count)
Copy link
Member

@jasonmalinowski jasonmalinowski Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just do Assert.Equal -- there's an overload that compares to IEnumerables item by item, and if the count differs will also show you the actual contents which is a lot easier to debug than this. #Resolved

{
foreach (var member in type.GetMembers(symbol.Name))
{
var sourceMember = await SymbolFinder.FindSourceDefinitionAsync(
Copy link
Member

@jasonmalinowski jasonmalinowski Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this returns metadata symbols, do we expose this anywhere as a public API that this is a breaking change of? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn't change FindSourceDefinitionAsync. Only in case, where we cannot get sources by it; we go to metadata.


In reply to: 335223864 [](ancestors = 335223864)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Methods changed belong to internal classes and should not be involved in public APIs


In reply to: 335726193 [](ancestors = 335726193,335223864)

// We need to start from each base class for cases like N() Implements I.M()
// where N() can be hidden or overwritten in a nested class later on.
interfaceImplementations.AddRange(member.ExplicitOrImplicitInterfaceImplementations());
// We should add implementations only for overridden members but not for hidden ones.
Copy link
Member

@jasonmalinowski jasonmalinowski Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not understanding how this is working now: if you just have the simple case of:

interface I { void M(); }
class A : I { public void M(); }

M() isn't an override of anything, but it implements A -- is the SymbolFinder.IsOverride still being fired? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case, we will add the interface method above on line 22. Then, we will find that A is inherited from object. We will try to find "M" in the object but will not find it here and will not call SymbolFinder.IsOverride.


In reply to: 335224638 [](ancestors = 335224638)

// will call the method from B. We should find the base for B.M in this case.
// And if we change 'new' to 'override' in the original code and add 'virtual' where needed,
// we should find I.M as a base for B.M(). And the next line helps with this scenario.
results.AddRange(member.ExplicitOrImplicitInterfaceImplementations());
Copy link
Member

@jasonmalinowski jasonmalinowski Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried this is actually a bug in ExplicitOrImplicitInterfaceImplementations:

memberInInterface => symbol.Equals(containingType.FindImplementationForInterfaceMember(memberInInterface)));

It looks like this is finding B, seeing it implements I, and then asks I what the explicit implementation is in B -- is it returning A.M or nothing at all? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that Explicit in this context means VB implementations and C# ones like void I.M(). And implicit are all other.

Then, in the scenario
abstract class C : I { public abstract void |M| { } }
class D : C { public override void $$M() { } }
interface I { void |M|; }

In the line you mentioned
containingType.FindImplementationForInterfaceMember(memberInInterface))
is called for containingType = "D" and memberInInterface = "I.M()".

And it returns "C.M()" not "D.M()". Then it is rejected by symbol.Equals.


In reply to: 335224689 [](ancestors = 335224689)

{
private abstract class AbstractItemEntry : Entry
{
protected readonly StreamingFindUsagesPresenter _presenter;
Copy link
Member

@jasonmalinowski jasonmalinowski Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Protected members shouldn't be prefixed with _ -- make this a property. #Resolved

internal partial class StreamingFindUsagesPresenter
{
// Name of the key used to retireve the whole entry object.
internal const string SelfKeyName = "self";
Copy link
Member

@jasonmalinowski jasonmalinowski Oct 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a constant being shared with something else? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is just introduced and shared between FindUsagesTableControlEventProcessorProvider and StreamingFindUsagesPresenter


In reply to: 335225827 [](ancestors = 335225827)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Go to base" on override says "The symbol has no base"

5 participants