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

Implement scoping and shadowing rules for extension parameter and type parameters #77704

Merged
merged 5 commits into from
Mar 21, 2025

Conversation

AlekseyTs
Copy link
Contributor

@AlekseyTs AlekseyTs commented Mar 20, 2025

See dotnet/csharplang#9229 (commit 9)

Relates to test plan #76130

@AlekseyTs AlekseyTs added Area-Compilers Feature - Extension Everything The extension everything feature labels Mar 20, 2025
@AlekseyTs AlekseyTs requested review from jjonescz and jcouv March 20, 2025 17:35
@AlekseyTs AlekseyTs requested a review from a team as a code owner March 20, 2025 17:35
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged Issues and PRs which have not yet been triaged by a lead label Mar 20, 2025
@jcouv jcouv self-assigned this Mar 20, 2025
<value>Type parameter '{0}' has the same name as an extension container type parameter</value>
</data>
<data name="ERR_LocalSameNameAsExtensionParameter" xml:space="preserve">
<value>'{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter</value>
Copy link
Member

@jcouv jcouv Mar 20, 2025

Choose a reason for hiding this comment

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

an extension parameter

Why "an extension parameter" as opposed to "the extension parameter"?
Also applies to other messages #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 used the following message as a template "'{0}': a parameter, local variable, or local function cannot have the same name as a method type parameter"

{
get
{
int local() => p;
Copy link
Member

@jcouv jcouv Mar 20, 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 for the happy case, where the extension parameter is used in a non-static local function or lambda, inside an instance extension member? #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.

Implementation_InstanceMethod_03_WithLocalFunction, for example?

else if (parameter.ContainingSymbol is NamedTypeSymbol { IsExtension: true } &&
(InParameterDefaultValue || InAttributeArgument ||
this.ContainingMember() is not { Kind: not SymbolKind.NamedType, IsStatic: false } || // We are not in an instance member
(object)this.ContainingMember().ContainingSymbol != parameter.ContainingSymbol) &&
Copy link
Member

@jcouv jcouv Mar 20, 2025

Choose a reason for hiding this comment

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

(object)this.ContainingMember().ContainingSymbol != parameter.ContainingSymbol

Is this condition necessary? #Resolved

Copy link
Contributor Author

@AlekseyTs AlekseyTs Mar 20, 2025

Choose a reason for hiding this comment

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

That is for references in nested types. IsInDeclaringTypeInstanceMember does the same. Declaring a nested type in extension is an error, but I prefer to be consistent

Copy link
Member

Choose a reason for hiding this comment

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

Consider adding a test for this

diagnostics.Add(ErrorCode.ERR_LocalSameNameAsTypeParam, GetLocation(p), name);
if (tp.ContainingSymbol is NamedTypeSymbol { IsExtension: true })
{
if (p.ContainingSymbol != (object)tp.ContainingSymbol)
Copy link
Member

@jcouv jcouv Mar 20, 2025

Choose a reason for hiding this comment

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

nit: consider leaving a comment (the error for naming the extension parameter after an extension type parameter is reported elsewhere) #Resolved

}

if (!pNames.Add(name))
{
// The parameter name '{0}' is a duplicate
diagnostics.Add(ErrorCode.ERR_DuplicateParamName, GetLocation(p), name);
if (parameters[0] is { ContainingSymbol: NamedTypeSymbol { IsExtension: true }, Name: var receiverName } && receiverName == name)
Copy link
Member

@jcouv jcouv Mar 21, 2025

Choose a reason for hiding this comment

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

&& receiverName == name

Consider adding a test to cover this part of the condition (extension(int p1) void M(int p2, int p2)) #Resolved


using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
Copy link
Member

@jcouv jcouv Mar 21, 2025

Choose a reason for hiding this comment

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

nit: unused using? #Resolved

{
Debug.Assert(result.IsClear);

if ((options & (LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly)) != 0)
Copy link
Member

@jcouv jcouv Mar 21, 2025

Choose a reason for hiding this comment

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

| LookupOptions.NamespacesOrTypesOnly

I didn't follow, what's the reasoning for checking some options early, but letting others be handled by CheckViability?
For comparison, InMethodBinder and WithLambdaParametersBinder only bail out early for NamespaceAliasesOnly. #Resolved

Copy link
Contributor Author

@AlekseyTs AlekseyTs Mar 21, 2025

Choose a reason for hiding this comment

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

I didn't follow, what's the reasoning for checking some options early, but letting others be handled by

Why do the work if we know that parameters do not satisfy the request. Especially, when we are binding types names in source, we want to avoid having to create symbols for members, etc. This helps with avoiding cycles. In terms of implementation, I used WithPrimaryConstructorParametersBinder as a template.

";
var comp = CreateCompilation(src);

comp.VerifyDiagnostics(
Copy link
Member

Choose a reason for hiding this comment

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

VerifyDiagnostics

nit: consider VerifyEmitDiagnostics over VerifyDiagnostics. Applies to other added tests too

// (5,14): error CS0102: The type 'Extensions.extension<T>(object)' already contains a definition for 'T'
// void T() { }
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "T").WithArguments("Extensions.extension<T>(object)", "T").WithLocation(5, 14));
comp.VerifyEmitDiagnostics();
Copy link
Member

@jcouv jcouv Mar 21, 2025

Choose a reason for hiding this comment

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

Consider adding a similar test for an accessor: extension<get_P>(...) int P => 0; #Resolved

_type = type;
}

internal override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo result, LookupOptions options, Binder originalBinder)
Copy link
Member

@jcouv jcouv Mar 21, 2025

Choose a reason for hiding this comment

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

We probably need some direct LookupSymbols tests to cover this method #Resolved

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 (iteration 4) with some minor questions/suggestions

@AlekseyTs
Copy link
Contributor Author

@jjonescz, @dotnet/roslyn-compiler For the second review.

<value>'{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter</value>
</data>
<data name="ERR_ValueParameterSameNameAsExtensionParameter" xml:space="preserve">
<value>'value': an automatically-generated parameter name conflicts with an extension parameter name</value>
Copy link
Member

@jjonescz jjonescz Mar 21, 2025

Choose a reason for hiding this comment

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

automatically-generated parameter

I think the language term is "implicit parameter". Same comment for ERR_ValueParameterSameNameAsExtensionTypeParameter. #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.

The terminology was "copied" from ERR_DuplicateGeneratedName

@AlekseyTs AlekseyTs merged commit 4482c72 into dotnet:features/extensions Mar 21, 2025
24 checks passed
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 untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants