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

Improve the signature display when a tuple appears many times within it. #56025

Merged

Conversation

CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Aug 31, 2021

Fixes #29949
Fixes #53061
Fixes #33609

Similar to what we do with anonymous types, we'll now show tuples like so (when they appear at least two times in a signature).

Before:

image

After:

image

@@ -517,6 +517,12 @@ private void AddTupleTypeName(INamedTypeSymbol symbol)
{
Debug.Assert(symbol.IsTupleType);

if (this.format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.DoNotExpandTupleType))
{
builder.Add(CreatePart(SymbolDisplayPartKind.StructName, symbol, "<tuple>"));
Copy link
Member Author

Choose a reason for hiding this comment

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

this is similar to what we do with anonymous types. With anon-types, the compiler just makes a single part, allowing the IDE control of how to build the final display string. For tuples it's fine (by default) to expand it as (...), but we need a way to have that not happen in order to use the new display logic.

@@ -534,7 +540,7 @@ private void AddTupleTypeName(INamedTypeSymbol symbol)
if (element.IsExplicitlyNamedTupleElement)
{
AddSpace();
builder.Add(CreatePart(SymbolDisplayPartKind.FieldName, symbol, element.Name));
builder.Add(CreatePart(SymbolDisplayPartKind.FieldName, element, element.Name));
Copy link
Member Author

Choose a reason for hiding this comment

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

this was a bug before (we were adding in the tuple again for each tuple element). This caused problems with the IDE code as we'd then try to expand these references out as well.

/// Insert a tuple into the display parts as a single part instead of multiple parts (similar
/// to how anonymous types are inserted0.
/// </summary>
DoNotExpandTupleType = 1 << 9,
Copy link
Member Author

Choose a reason for hiding this comment

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

open to discussing a good name here. We use the term 'expand' in other places (like 'ExpandNullable'). Maybe DoNotExpandTuples?

{
$$M(default);
}",
MainDescription(@"void C.M((int x, string y) t)"),
Copy link
Member Author

Choose a reason for hiding this comment

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

in the single use case, we keep the signature as before.

[ExportLanguageService(typeof(IAnonymousTypeDisplayService), LanguageNames.CSharp), Shared]
internal class CSharpAnonymousTypeDisplayService : AbstractAnonymousTypeDisplayService
[ExportLanguageService(typeof(IStructuralTypeDisplayService), LanguageNames.CSharp), Shared]
internal class CSharpStructuralTypeDisplayService : AbstractStructuralTypeDisplayService
Copy link
Member Author

Choose a reason for hiding this comment

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

most of the code changes are a strictly mechanical change of 'AnonymousType' => 'StructuralType'. I'll mark which parts of the code are actually changed.

@@ -1,176 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
Copy link
Member Author

Choose a reason for hiding this comment

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

this type was not deleted. but enough changed happened to it that github considers it a rewrite.

orderAndCount.count++;
_namedTypes[symbol] = orderAndCount;
return;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

this is new logic.

else
{
typeParts.AddRange(GetTypeParts(structuralType, semanticModel, position));
}
Copy link
Member Author

Choose a reason for hiding this comment

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

this part is new.


private static ImmutableArray<INamedTypeSymbol> GetTransitiveStructuralTypeReferences(
ImmutableArray<INamedTypeSymbol> structuralTypes)
{
Copy link
Member Author

Choose a reason for hiding this comment

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

this method has been rewritten.

IDictionary<INamedTypeSymbol, string> structuralTypeToName,
SemanticModel semanticModel,
int position)
{
Copy link
Member Author

Choose a reason for hiding this comment

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

this method was rewritten.


restart:
Copy link
Member Author

Choose a reason for hiding this comment

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

this loop moved into StructuralTypeDisplayInfo.ReplaceStructuralTypes

@davidwengier
Copy link
Contributor

I was expecting this to be a new section on the tool tip. Did it get too long with a new section, or do you just consider them the same? And if so, how much is that your compiler-dev brain at work? :)

"Structural types" doesn't have much meaning to me, though I'm not exactly against it, as it I can work out what it means. Mainly just surprised, especially if adding delegate info is going to give us a new section here anyway.

Copy link
Contributor

@davidwengier davidwengier left a comment

Choose a reason for hiding this comment

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

Another random thought, is it worth using 't for tuples to distinguish them?

{
if (namedType.IsTupleType && count == 1)
{
// Ignore tuples only referenced once. We'll keep them inline. If the tuple shows up multiple times,
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this should only apply if there isn't otherwise going to be a "Structural Types" section? eg, what happens in this case:

public (int a, int b) Goo((int a, int b), (string x, string y);

You're going to end up with a "Structural Types" section that doesn't list out all of the structural types.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's an interesting point. I could revise to make it that once we have at least one duplicated tuple that all tuples then are broken out... That seems reasonable to me.

@CyrusNajmabadi
Copy link
Member Author

Did it get too long with a new section

I just didn't think this warranted a new section. To me each section is very distinct in it's purpose. If we added a new section it would effectively be done the exact same as the existing anonymous type section.

Because of that, I just washed one section that showed you all the type expansions in a single place.

@CyrusNajmabadi
Copy link
Member Author

especially if adding delegate info is going to give us a new section here anyway.

I would definitely put delegates into this section, and I had that in mind when writing this :-)

@CyrusNajmabadi
Copy link
Member Author

Structural types" doesn't have much meaning to me, though I'm not exactly against it,

You're right. I think that can just simplify to Types: and remain just as clear.

@CyrusNajmabadi
Copy link
Member Author

Another random thought, is it worth using 't for tuples to distinguish them?

Possibly. Are you thinking 't1, 't2, etc ?

@davidwengier
Copy link
Contributor

Another random thought, is it worth using 't for tuples to distinguish them?

Possibly. Are you thinking 't1, 't2, etc ?

Nevermind, ignore me. I always assumed the "a" in 'a stood for "anonymous".. just tried with two and it turns out its just alphabetical.. this is fine :)

@333fred
Copy link
Member

333fred commented Aug 31, 2021

@CyrusNajmabadi I don't think this requires a full session, but can you please send an email to the api design alias with the proposal?

@CyrusNajmabadi
Copy link
Member Author

On it.

@akhera99
Copy link
Member

Fixes: #29949 #53061 #33609

@akhera99 akhera99 linked an issue Aug 31, 2021 that may be closed by this pull request
@CyrusNajmabadi
Copy link
Member Author

@333fred renamed to CollapseTupleTypes

@CyrusNajmabadi
Copy link
Member Author

@dotnet/roslyn-compiler for small public API change to enhance SymbolDisplay.

@CyrusNajmabadi
Copy link
Member Author

@dotnet/roslyn-compiler @333fred for eyes on the SymbolDisplay change.

@CyrusNajmabadi CyrusNajmabadi requested review from a team as code owners September 1, 2021 22:01
@CyrusNajmabadi CyrusNajmabadi changed the base branch from main to release/dev17.1-preview1 September 1, 2021 22:02
@CyrusNajmabadi
Copy link
Member Author

Rebasing to 17.1

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

Symbol display change LGTM

@CyrusNajmabadi
Copy link
Member Author

@jcouv can you ptal?

/// Insert a tuple into the display parts as a single part instead of multiple parts (similar
/// to how anonymous types are inserted).
/// </summary>
CollapseTupleTypes = 1 << 9,
Copy link
Member

Choose a reason for hiding this comment

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

CollapseTupleTypes

I'm curious why special-case tuples and anonymous types. Wouldn't similar behavior be useful for other type arguments?

Copy link
Member Author

Choose a reason for hiding this comment

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

Primarily because they are structural types that require a lot more than a name to reference. For example, when you have a nominal type, it doesn't matter how many members that type has, you just reference it by name. Hit with a structural type you have to include that structure in somehow.

If we have more cases like that in the future, I'd expect we go down a similar route.

Copy link
Member

Choose a reason for hiding this comment

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

when you have a nominal type, it doesn't matter how many members that type has, you just reference it by name

But the nominal type could be arbitrarily complicated, for instance Dictionary<string, object>.

Copy link
Member Author

Choose a reason for hiding this comment

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

Potentially. We haven't had that arise as significant enough to need attention. If we did though, I'd prefer that as a separate flag.

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

Compiler change LGTM Thanks (iteration 34). I take Fred's sign-off as evidence of sufficient public API discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
6 participants