Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Comments

Add 'add extension completion' support for modern extensions.#81239

Merged
CyrusNajmabadi merged 44 commits intodotnet:mainfrom
CyrusNajmabadi:extensionMemberCompletion
Nov 19, 2025
Merged

Add 'add extension completion' support for modern extensions.#81239
CyrusNajmabadi merged 44 commits intodotnet:mainfrom
CyrusNajmabadi:extensionMemberCompletion

Conversation

@CyrusNajmabadi
Copy link
Contributor

@CyrusNajmabadi CyrusNajmabadi commented Nov 14, 2025

Fixes #80561

Relates to test plan #76130

@CyrusNajmabadi CyrusNajmabadi marked this pull request as ready for review November 14, 2025 14:24
@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner November 14, 2025 14:24
// This is the implementation at Editor layer to provide a CancellationToken
// for the workqueue used for background cache refresh.
[ExportWorkspaceServiceFactory(typeof(IImportCompletionCacheService<ExtensionMethodImportCompletionCacheEntry, object>), ServiceLayer.Editor), Shared]
[ExportWorkspaceServiceFactory(typeof(IImportCompletionCacheService<ExtensionMemberImportCompletionCacheEntry, object>), ServiceLayer.Editor), Shared]
Copy link
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Nov 14, 2025

Choose a reason for hiding this comment

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

a lot of the PR is just renaming 'method' to 'member' (and loosening IMethodSymbol to ISymbol). I will call out the interesting changes beyond that.

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 also recommend viewing with whitespace off.

}

// has to be `public static "<Extension>$"(parameter)` (with exactly one parameter). note:
// method.GetParameters() doesn't seem to work here for static methods (perhaps because the name
Copy link
Member

@jcouv jcouv Nov 15, 2025

Choose a reason for hiding this comment

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

method.GetParameters()

This seems odd. The compiler definitely does method.GetParameters() on the marker method. It should work for parameters with blank names in metadata. #Closed

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 def fails for me. Feel free to debug. Do you use MetadataReader? or some other system?

Copy link
Member

Choose a reason for hiding this comment

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

Looking at PEMethodSymbol we don't use MethodDefinition.GetParameters() we use DecodeMethodSignature as you do below.

That said, when I add var x = method.GetParameters(); here, none of the tests fail.
When I step through one test (TestPredefinedType_ModernExtensionProperty_GenericType) and inspect x it looks correct for the marker method.
Can you share some details of how things fail for you?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See #81257

Specifically 2a4e232

This causes TestPredefinedType_ModernExtensionMethod_Static to fail with the assert i added of: Debug.Assert(method.GetParameters().Count == 1);

Copy link
Member

Choose a reason for hiding this comment

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

Thanks. I can repro. GetParameters() returns a collection of 0 items.
Copilot gave some a plausible explanation:

In ECMA-335 metadata the Param table entries (what method.GetParameters() enumerates) are optional. A parameter only gets a row if it needs metadata: a name, attributes, marshaling info, default value, custom modifiers, etc. Synthetic methods (like your $ marker) can have parameters in the signature blob but no Param rows. The signature decoder (SignatureDecoder.DecodeMethodSignature) reads the raw signature (method.Signature) and will still show the parameter type, so signature.ParameterTypes.Length == 1 is correct while method.GetParameters().Count == 0.

I checked out sections "II.22.33 Param" and "II.15.4 Defining methods" but I couldn't spot where this falls out of... It does make sense that there would be no Param row if there's no information to store.

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 it's because you have no name. So there's no interesting parameter info to read out from the param table.

{
receiverTypeSymbol = container;
return true;
return (container, isStatic: false);
Copy link
Member

@jcouv jcouv Nov 15, 2025

Choose a reason for hiding this comment

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

Do we have a test showing the effect of isStatic: false here? #Closed

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'm fine not caring about hitting all scenarios. we can drive narrower cases based on reports if people are hitting this.

{
try
{
// Enable if, during debugging, you want to be able to easily debug into the PE reference processing
Copy link
Member

Choose a reason for hiding this comment

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

nit: FWIW, in the compiler, instead of ifdef we just avoid the parallelism when debugging.
See MethodCompiler checking ConcurrentBuild; our unittests set ConcurrentBuild to false when debugger is attached.
See CSharpTestBase.CreateCompilationCore (line 1836)

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'll look into that.

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.

Done with review pass (commit 39)

// }
//
// Note: to keep things simple, we don't check actual attributes. We just check enough to give us
// confidence that we found the right thing. (So same modifiers, has or does not have a special name,
Copy link
Member

Choose a reason for hiding this comment

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

has or does not have a special name,

Is this comment accurate? It looks like the if below makes a special name required.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True!

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.

LGTM Thanks (commit 44). I'm assuming the feedback on using well-known member name and conditioning parallelism on presence of debugger are for later

@CyrusNajmabadi
Copy link
Contributor Author

Yup. both of those are non-blocking. thnx.

@CyrusNajmabadi CyrusNajmabadi merged commit e1008e6 into dotnet:main Nov 19, 2025
26 checks passed
@CyrusNajmabadi CyrusNajmabadi deleted the extensionMemberCompletion branch November 19, 2025 18:15
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Nov 19, 2025
@davidwengier davidwengier modified the milestones: Next, 18.3 Jan 6, 2026
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.

Extension members not suggested by IntelliSense when "Show items from unimported namespaces" is enabled

4 participants