Skip to content

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Sep 7, 2025

  • update metadata name for grouping type (proposed design change)
  • add ExtensionGroupingName/ExtensionMarkerName APIs
  • update docID APIs
  • update symbol display to match

Proposed API design: #80163
Address part of #78957 (extension public API follow-ups)

Based on feedback from API review, I'm moving the API from ITypeSymbol down into INamedTypeSymbol.

Relates to test plan #76130

Filled #80165 for crash in VB test

Note: this was merged into SDK .NET 10 RC2.

@jcouv jcouv self-assigned this Sep 7, 2025
@jcouv jcouv added Area-Compilers Feature - Extension Everything The extension everything feature labels Sep 7, 2025
@dotnet-policy-service dotnet-policy-service bot added VSCode Needs API Review Needs to be reviewed by the API review council labels Sep 7, 2025
@jcouv jcouv force-pushed the extensions-docid branch 2 times, most recently from fe339a9 to c7f70d0 Compare September 7, 2025 07:04
ExtensionGroupingName/ExtensionMarkerName APIs
@jcouv jcouv marked this pull request as ready for review September 7, 2025 10:47
@jcouv jcouv requested review from a team as code owners September 7, 2025 10:47
@jcouv jcouv mentioned this pull request Sep 8, 2025
13 tasks
@jcouv jcouv requested review from jjonescz and AlekseyTs September 8, 2025 20:23
@jcouv
Copy link
Member Author

jcouv commented Sep 9, 2025

Moved APIs down into INamedTypeSymbol per API review

if (symbol.IsExtension)
{
_builder.Append('.');
_builder.Append(symbol.ExtensionMarkerName);
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 10, 2025

Choose a reason for hiding this comment

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

_builder.Append(symbol.ExtensionMarkerName);

Should type parameters be appended? #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

If we are supposed to reflect metadata name here, we should be good, because ExtensionMarkerName is it.

{
if (symbol.IsExtension)
{
return false;
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 10, 2025

Choose a reason for hiding this comment

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

return false;

It is not obvious why we are doing this. Consider adding a comment #Closed

}
}

GetMatchingExtensions(container, memberName, results); // Note: the arity is already in the content-based extension grouping name
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 10, 2025

Choose a reason for hiding this comment

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

// Note: the arity is already in the content-based extension grouping name

I do not think we can rely on this for imported symbols, other tools are free to generate names differently. #Closed

}
}

GetMatchingExtensions(container, memberName, results);
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 10, 2025

Choose a reason for hiding this comment

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

GetMatchingExtensions(container, memberName, results);

It looks like we are missing an arity check here as well #Closed

ImmutableArray<INamedTypeSymbol> unnamedNamedTypes = container.GetTypeMembers("");
foreach (var namedType in unnamedNamedTypes)
{
if (namedType.IsExtension && namedType.ExtensionGroupingName == memberName)
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 10, 2025

Choose a reason for hiding this comment

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

ExtensionGroupingName

Do we ever need to match marker name too? #Closed

Copy link
Member Author

@jcouv jcouv Sep 10, 2025

Choose a reason for hiding this comment

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

I don't think so Added above. We need to match E.GroupingType.MatchingType back to the extension


using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 10, 2025

Choose a reason for hiding this comment

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

Is the added using necessary? #Closed

continue;
}

var sourceNamedType = (SourceNamedTypeSymbol)type;
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 10, 2025

Choose a reason for hiding this comment

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

var sourceNamedType = (SourceNamedTypeSymbol)type;

My recommendation is to revert changes in this file. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

The thread got marked as resolved, but it looks like changes were not reverted.

Copy link
Member Author

Choose a reason for hiding this comment

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

The code changed below due to moving APIs down to NamedTypeSymbol

Copy link
Contributor

Choose a reason for hiding this comment

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

The code changed below due to moving APIs down to NamedTypeSymbol

SourceNamedTypeSymbol is a NamedTypeSymbol. I think changes in this function are not needed

Copy link
Member Author

Choose a reason for hiding this comment

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

Then we need to add assertions for nullability

Copy link
Contributor

Choose a reason for hiding this comment

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

Then we need to add assertions for nullability

I have no objection to that. I do object to rewriting code without an intent to change behavior.

if (symbol.TypeParameters.Length > 0)
if (symbol.TypeParameters.Length > 0 && !symbol.IsExtension)
{
_builder.Append('`');
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 10, 2025

Choose a reason for hiding this comment

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

builder.Append('`');

Are we supposed to add this only when MangleName property is true? Perhaps instead of having this if we should use MetadataName instead of Name above? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Using MetadataName here would be equivalent to using ExtensionMarkerName, but we want ExtensionGroupingName here:

  • for a member, we'll display Container.ExtensionGroupingType.Member
  • for the extension itself, we'll display Container.ExtensionGroupingType.ExtensionMarkerType (the last segment is added by caller who initiates visit on an extension)

@jcouv jcouv marked this pull request as ready for review September 15, 2025 18:36
}

private static void GetMatchingTypes(List<INamespaceOrTypeSymbol> containers, string memberName, int arity, List<ISymbol> results)
private static void GetMatchingTypes(List<INamespaceOrTypeSymbol> containers, string memberName, int arity, bool isTerminal, List<ISymbol> results)
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 15, 2025

Choose a reason for hiding this comment

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

isTerminal

It would be good to explain meaning of this parameter in a comment #Closed

for (int i = 0, n = containers.Count; i < n; i++)
{
GetMatchingTypes(containers[i], memberName, arity, results);
GetMatchingTypes(containers[i], memberName, arity, isTerminal: isTerminal, results);
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 15, 2025

Choose a reason for hiding this comment

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

isTerminal

Should we pass false for all, but the last item? #Closed

&& container is INamedTypeSymbol { IsExtension: true } extension
&& extension.ExtensionMarkerName == memberName)
{
results.Add(extension);
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 15, 2025

Choose a reason for hiding this comment

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

results.Add(extension)

Where did we check the arity? #Closed


private static void GetMatchingExtensions(INamespaceOrTypeSymbol container, string memberName, int arity, List<ISymbol> results)
{
ImmutableArray<INamedTypeSymbol> unnamedNamedTypes = container.GetTypeMembers("");
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 15, 2025

Choose a reason for hiding this comment

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

ImmutableArray unnamedNamedTypes = container.GetTypeMembers("");

We probably should not attempt to look for an extension in a namespace #Closed

Friend Sub AddExtensionMarkerName(extension As INamedTypeSymbol)
Debug.Assert(extension.IsExtension)
AddNestedTypeSeparator()
Builder.Add(CreatePart(SymbolDisplayPartKind.ClassName, extension, extension.ExtensionMarkerName, True))
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 15, 2025

Choose a reason for hiding this comment

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

True

It is not obvious why this is the right value. #Closed

@jcouv jcouv requested a review from jjonescz September 15, 2025 22:10
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Sep 15, 2025

Are we testing that VB and C# produce the same doc ids for the same entities? I think we need to verify/observe that the issues outlined in dotnet/csharplang#9682 are not present. #Closed

&& container is INamedTypeSymbol { IsExtension: true } extension
&& extension.ExtensionMarkerName == memberName)
{
results.Add(extension);
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 15, 2025

Choose a reason for hiding this comment

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

results.Add(extension);

Are we testing this code path? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, each of the new tests does

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Sep 15, 2025

Done with review pass (commit 8) #Closed

@jcouv jcouv requested a review from AlekseyTs September 16, 2025 00:55
}
}

internal void AddExtensionMarkerName(INamedTypeSymbol extension)
Copy link
Member

@jjonescz jjonescz Sep 16, 2025

Choose a reason for hiding this comment

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

This seems to be used only once. Consider inlining or at least marking private. #ByDesign

Copy link
Member Author

Choose a reason for hiding this comment

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

If this is marked as private, then the caller can't call it.
If it's inlined, then we can't use Builder which is protected.

End If
End Sub

Friend Sub AddExtensionMarkerName(extension As INamedTypeSymbol)
Copy link
Member

@jjonescz jjonescz Sep 16, 2025

Choose a reason for hiding this comment

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

Same comment here. #ByDesign

var comp2 = CreateCompilation("", references: [comp.EmitToImageReference()]);
validate(comp2);

var vbComp = CreateVisualBasicCompilation("", referencedAssemblies: [comp.EmitToImageReference()]);
Copy link
Contributor

Choose a reason for hiding this comment

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

comp.EmitToImageReference()

Consider capturing in a local and reusing in two places that are doing the same work now.

var comp2 = CreateCompilation("", references: [comp.EmitToImageReference()]);
validate(comp2);

var vbComp = CreateVisualBasicCompilation("", referencedAssemblies: [comp.EmitToImageReference()]);
Copy link
Contributor

Choose a reason for hiding this comment

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

comp.EmitToImageReference()

Similar comment to reuse result of emit rather than doing it twice.

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (commit 12)

@jcouv jcouv merged commit aa0d5d8 into dotnet:main Sep 17, 2025
28 checks passed
@jcouv jcouv deleted the extensions-docid branch September 17, 2025 07:14
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Sep 17, 2025
@akhera99 akhera99 modified the milestones: Next, 18.0 P1, 18.0 P2 Sep 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Feature - Extension Everything The extension everything feature Needs API Review Needs to be reviewed by the API review council VSCode
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants