-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Handle capturing of primary constructor parameters within queries and in type-or-value scenarios #66254
Handle capturing of primary constructor parameters within queries and in type-or-value scenarios #66254
Changes from 4 commits
ee9a0c3
f3042a1
68025bd
608c702
a102b6c
808952a
4956b96
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1498,35 +1498,22 @@ private BoundExpression BindIdentifier( | |
// It's possible that the argument list is malformed; if so, do not attempt to bind it; | ||
// just use the null array. | ||
|
||
int arity = node.Arity; | ||
bool hasTypeArguments = arity > 0; | ||
bool hasTypeArguments = node.Arity > 0; | ||
|
||
SeparatedSyntaxList<TypeSyntax> typeArgumentList = node.Kind() == SyntaxKind.GenericName | ||
? ((GenericNameSyntax)node).TypeArgumentList.Arguments | ||
: default(SeparatedSyntaxList<TypeSyntax>); | ||
|
||
Debug.Assert(arity == typeArgumentList.Count); | ||
Debug.Assert(node.Arity == typeArgumentList.Count); | ||
|
||
var typeArgumentsWithAnnotations = hasTypeArguments ? | ||
BindTypeArguments(typeArgumentList, diagnostics) : | ||
default(ImmutableArray<TypeWithAnnotations>); | ||
|
||
var lookupResult = LookupResult.GetInstance(); | ||
LookupOptions options = LookupOptions.AllMethodsOnArityZero; | ||
if (invoked) | ||
{ | ||
options |= LookupOptions.MustBeInvocableIfMember; | ||
} | ||
|
||
if (!IsInMethodBody && !IsInsideNameof) | ||
{ | ||
Debug.Assert((options & LookupOptions.NamespacesOrTypesOnly) == 0); | ||
options |= LookupOptions.MustNotBeMethodTypeParameter; | ||
} | ||
|
||
var name = node.Identifier.ValueText; | ||
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); | ||
this.LookupSymbolsWithFallback(lookupResult, name, arity: arity, useSiteInfo: ref useSiteInfo, options: options); | ||
this.LookupIdentifier(lookupResult, node, invoked, ref useSiteInfo); | ||
diagnostics.Add(node, useSiteInfo); | ||
|
||
if (lookupResult.Kind != LookupResultKind.Empty) | ||
|
@@ -1657,6 +1644,23 @@ void adjustIdentifierMapIfAny(SimpleNameSyntax node, bool invoked) | |
#endif | ||
} | ||
|
||
private void LookupIdentifier(LookupResult lookupResult, SimpleNameSyntax node, bool invoked, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) | ||
{ | ||
LookupOptions options = LookupOptions.AllMethodsOnArityZero; | ||
if (invoked) | ||
{ | ||
options |= LookupOptions.MustBeInvocableIfMember; | ||
} | ||
|
||
if (!IsInMethodBody && !IsInsideNameof) | ||
{ | ||
Debug.Assert((options & LookupOptions.NamespacesOrTypesOnly) == 0); | ||
options |= LookupOptions.MustNotBeMethodTypeParameter; | ||
} | ||
|
||
this.LookupSymbolsWithFallback(lookupResult, node.Identifier.ValueText, arity: node.Arity, useSiteInfo: ref useSiteInfo, options: options); | ||
} | ||
|
||
/// <summary> | ||
/// Is this is an _ identifier in a context where discards are allowed? | ||
/// </summary> | ||
|
@@ -1919,11 +1923,8 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Bind | |
var primaryCtor = parameter.ContainingSymbol as SynthesizedPrimaryConstructor; | ||
|
||
if (primaryCtor is not null && | ||
(InParameterDefaultValue || | ||
InAttributeArgument || | ||
this.ContainingMember() is not { Kind: not SymbolKind.NamedType, IsStatic: false } contaningMember || // We are not in an instance member | ||
(object)contaningMember.ContainingSymbol != primaryCtor.ContainingSymbol || // The member doesn't belong to our type, i.e. from nested type | ||
((object)contaningMember != primaryCtor && contaningMember is MethodSymbol { MethodKind: MethodKind.Constructor })) && // We are in a non-primary instance constructor | ||
(!IsInDeclaringTypeInstanceMember(primaryCtor) || | ||
(this.ContainingMember() is MethodSymbol { MethodKind: MethodKind.Constructor } contaningMember && (object)contaningMember != primaryCtor)) && // We are in a non-primary instance constructor | ||
!IsInsideNameof) | ||
{ | ||
Error(diagnostics, ErrorCode.ERR_InvalidPrimaryConstructorParameterReference, node, parameter); | ||
|
@@ -2034,6 +2035,14 @@ bool isUsedBeforeDeclaration(SimpleNameSyntax node, LocalSymbol localSymbol) | |
} | ||
} | ||
|
||
private bool IsInDeclaringTypeInstanceMember(SynthesizedPrimaryConstructor primaryCtor) | ||
{ | ||
return !(InParameterDefaultValue || | ||
InAttributeArgument || | ||
this.ContainingMember() is not { Kind: not SymbolKind.NamedType, IsStatic: false } contaningMember || // We are not in an instance member | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
(object)contaningMember.ContainingSymbol != primaryCtor.ContainingSymbol); // The member doesn't belong to our type, i.e. from nested type | ||
} | ||
|
||
private bool ReportSimpleProgramLocalReferencedOutsideOfTopLevelStatement(SimpleNameSyntax node, Symbol symbol, BindingDiagnosticBag diagnostics) | ||
{ | ||
if (symbol.ContainingSymbol is SynthesizedSimpleProgramEntryPointSymbol && | ||
|
@@ -6288,6 +6297,7 @@ private BoundExpression BindMemberAccess( | |
BindingDiagnosticBag diagnostics) | ||
{ | ||
Debug.Assert(node != null); | ||
Debug.Assert(invoked == SyntaxFacts.IsInvoked(node)); | ||
|
||
BoundExpression boundLeft; | ||
|
||
|
@@ -6348,6 +6358,10 @@ private BoundExpression BindLeftOfPotentialColorColorMemberAccess(ExpressionSynt | |
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private BoundExpression BindLeftIdentifierOfPotentialColorColorMemberAccess(IdentifierNameSyntax left, BindingDiagnosticBag diagnostics) | ||
{ | ||
Debug.Assert((left.Parent is MemberAccessExpressionSyntax { RawKind: (int)SyntaxKind.SimpleMemberAccessExpression } memberAccess && memberAccess.Expression == left) || | ||
(left.Parent is QualifiedNameSyntax qualifiedName && qualifiedName.Left == left) || | ||
(left.Parent is FromClauseSyntax { Parent: QueryExpressionSyntax query } fromClause && query.FromClause == fromClause && fromClause.Expression == left)); | ||
|
||
// SPEC: 7.6.4.1 Identical simple names and type names | ||
// SPEC: In a member access of the form E.I, if E is a single identifier, and if the meaning of E as | ||
// SPEC: a simple-name (spec 7.6.2) is a constant, field, property, local variable, or parameter with the | ||
|
@@ -6390,12 +6404,17 @@ private BoundExpression BindLeftIdentifierOfPotentialColorColorMemberAccess(Iden | |
var boundType = BindNamespaceOrType(left, typeDiagnostics); | ||
if (TypeSymbol.Equals(boundType.Type, leftType, TypeCompareKind.AllIgnoreOptions)) | ||
{ | ||
Debug.Assert(!leftType.IsDynamic()); | ||
Debug.Assert(IsPotentialColorColorReceiver(left, leftType)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It is verifying that we can rely on |
||
|
||
// NOTE: ReplaceTypeOrValueReceiver will call CheckValue explicitly. | ||
boundValue = BindToNaturalType(boundValue, valueDiagnostics); | ||
return new BoundTypeOrValueExpression(left, | ||
new BoundTypeOrValueData(leftSymbol, boundValue, valueDiagnostics, boundType, typeDiagnostics), leftType); | ||
} | ||
} | ||
|
||
Debug.Assert(!IsPotentialColorColorReceiver(left, leftType)); | ||
break; | ||
|
||
// case SymbolKind.Event: //SPEC: 7.6.4.1 (a.k.a. Color Color) doesn't cover events | ||
|
@@ -6408,6 +6427,14 @@ private BoundExpression BindLeftIdentifierOfPotentialColorColorMemberAccess(Iden | |
return boundValue; | ||
} | ||
|
||
private bool IsPotentialColorColorReceiver(IdentifierNameSyntax id, TypeSymbol type) | ||
{ | ||
string name = id.Identifier.ValueText; | ||
|
||
return (type.Name == name || IsUsingAliasInScope(name)) && | ||
TypeSymbol.Equals(BindNamespaceOrType(id, BindingDiagnosticBag.Discarded).Type, type, TypeCompareKind.AllIgnoreOptions); | ||
} | ||
|
||
// returns true if name matches a using alias in scope | ||
// NOTE: when true is returned, the corresponding using is also marked as "used" | ||
private bool IsUsingAliasInScope(string name) | ||
|
@@ -6476,13 +6503,25 @@ private BoundExpression BindDynamicMemberAccess( | |
hasErrors: hasErrors); | ||
} | ||
|
||
#if DEBUG | ||
/// <summary> | ||
/// Bind the RHS of a member access expression, given the bound LHS. | ||
/// It is assumed that CheckValue has not been called on the LHS. | ||
/// </summary> | ||
/// <remarks> | ||
/// If new checks are added to this method, they will also need to be added to | ||
/// <see cref="MakeQueryInvocation(CSharpSyntaxNode, BoundExpression, string, TypeSyntax, TypeWithAnnotations, BindingDiagnosticBag, string)"/>. | ||
/// </remarks> | ||
#else | ||
/// <summary> | ||
/// Bind the RHS of a member access expression, given the bound LHS. | ||
/// It is assumed that CheckValue has not been called on the LHS. | ||
/// </summary> | ||
/// <remarks> | ||
/// If new checks are added to this method, they will also need to be added to <see cref="MakeQueryInvocation(CSharpSyntaxNode, BoundExpression, string, TypeSyntax, TypeWithAnnotations, BindingDiagnosticBag)"/>. | ||
/// If new checks are added to this method, they will also need to be added to | ||
/// <see cref="MakeQueryInvocation(CSharpSyntaxNode, BoundExpression, string, TypeSyntax, TypeWithAnnotations, BindingDiagnosticBag)"/>. | ||
/// </remarks> | ||
#endif | ||
private BoundExpression BindMemberAccessWithBoundLeft( | ||
ExpressionSyntax node, | ||
BoundExpression boundLeft, | ||
|
@@ -6820,11 +6859,6 @@ private BoundExpression BindInstanceMemberAccess( | |
{ | ||
Debug.Assert(rightArity == (typeArgumentsWithAnnotations.IsDefault ? 0 : typeArgumentsWithAnnotations.Length)); | ||
var leftType = boundLeft.Type; | ||
LookupOptions options = LookupOptions.AllMethodsOnArityZero; | ||
if (invoked) | ||
{ | ||
options |= LookupOptions.MustBeInvocableIfMember; | ||
} | ||
|
||
var lookupResult = LookupResult.GetInstance(); | ||
try | ||
|
@@ -6836,13 +6870,8 @@ private BoundExpression BindInstanceMemberAccess( | |
// UNDONE: Classify E as prop access, indexer access, variable or value | ||
|
||
bool leftIsBaseReference = boundLeft.Kind == BoundKind.BaseReference; | ||
if (leftIsBaseReference) | ||
{ | ||
options |= LookupOptions.UseBaseReferenceAccessibility; | ||
} | ||
|
||
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); | ||
this.LookupMembersWithFallback(lookupResult, leftType, rightName, rightArity, ref useSiteInfo, basesBeingResolved: null, options: options); | ||
this.LookupInstanceMember(lookupResult, leftType, leftIsBaseReference, rightName, rightArity, invoked, ref useSiteInfo); | ||
diagnostics.Add(right, useSiteInfo); | ||
|
||
// SPEC: Otherwise, an attempt is made to process E.I as an extension method invocation. | ||
|
@@ -6862,6 +6891,13 @@ private BoundExpression BindInstanceMemberAccess( | |
|
||
if (searchExtensionMethodsIfNecessary) | ||
{ | ||
BoundExpression colorColorValueReceiver = GetValueExpressionIfTypeOrValueReceiver(boundLeft); | ||
|
||
if (IsPossiblyCapturingPrimaryConstructorParameterReference(colorColorValueReceiver, out _)) | ||
{ | ||
boundLeft = ReplaceTypeOrValueReceiver(boundLeft, useType: false, diagnostics); | ||
} | ||
|
||
var boundMethodGroup = new BoundMethodGroup( | ||
node, | ||
typeArgumentsWithAnnotations, | ||
|
@@ -6889,6 +6925,22 @@ private BoundExpression BindInstanceMemberAccess( | |
} | ||
} | ||
|
||
private void LookupInstanceMember(LookupResult lookupResult, TypeSymbol leftType, bool leftIsBaseReference, string rightName, int rightArity, bool invoked, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) | ||
{ | ||
LookupOptions options = LookupOptions.AllMethodsOnArityZero; | ||
if (invoked) | ||
{ | ||
options |= LookupOptions.MustBeInvocableIfMember; | ||
} | ||
|
||
if (leftIsBaseReference) | ||
{ | ||
options |= LookupOptions.UseBaseReferenceAccessibility; | ||
} | ||
|
||
this.LookupMembersWithFallback(lookupResult, leftType, rightName, rightArity, ref useSiteInfo, basesBeingResolved: null, options: options); | ||
} | ||
|
||
private void BindMemberAccessReportError(BoundMethodGroup node, BindingDiagnosticBag diagnostics) | ||
{ | ||
var nameSyntax = node.NameSyntax; | ||
|
@@ -7322,21 +7374,18 @@ private void PopulateExtensionMethodsFromSingleBinder( | |
BindingDiagnosticBag diagnostics) | ||
{ | ||
int arity; | ||
LookupOptions options; | ||
if (typeArgumentsWithAnnotations.IsDefault) | ||
{ | ||
arity = 0; | ||
options = LookupOptions.AllMethodsOnArityZero; | ||
} | ||
else | ||
{ | ||
arity = typeArgumentsWithAnnotations.Length; | ||
options = LookupOptions.Default; | ||
} | ||
|
||
var lookupResult = LookupResult.GetInstance(); | ||
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); | ||
this.LookupExtensionMethodsInSingleBinder(scope, lookupResult, rightName, arity, options, ref useSiteInfo); | ||
this.LookupExtensionMethods(lookupResult, scope, rightName, arity, ref useSiteInfo); | ||
diagnostics.Add(node, useSiteInfo); | ||
|
||
if (lookupResult.IsMultiViable) | ||
|
@@ -7354,6 +7403,21 @@ private void PopulateExtensionMethodsFromSingleBinder( | |
lookupResult.Free(); | ||
} | ||
|
||
private void LookupExtensionMethods(LookupResult lookupResult, ExtensionMethodScope scope, string rightName, int arity, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) | ||
{ | ||
LookupOptions options; | ||
if (arity == 0) | ||
{ | ||
options = LookupOptions.AllMethodsOnArityZero; | ||
} | ||
else | ||
{ | ||
options = LookupOptions.Default; | ||
} | ||
|
||
this.LookupExtensionMethodsInSingleBinder(scope, lookupResult, rightName, arity, options, ref useSiteInfo); | ||
} | ||
|
||
protected BoundExpression BindFieldAccess( | ||
SyntaxNode node, | ||
BoundExpression receiver, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1564,6 +1564,27 @@ private BoundExpression ReplaceTypeOrValueReceiver(BoundExpression receiver, boo | |
} | ||
} | ||
|
||
private BoundExpression GetValueExpressionIfTypeOrValueReceiver(BoundExpression receiver) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
if ((object)receiver == null) | ||
{ | ||
return null; | ||
} | ||
|
||
switch (receiver) | ||
{ | ||
case BoundTypeOrValueExpression typeOrValueExpression: | ||
return typeOrValueExpression.Data.ValueExpression; | ||
|
||
case BoundQueryClause queryClause: | ||
// a query clause may wrap a TypeOrValueExpression. | ||
return GetValueExpressionIfTypeOrValueReceiver(queryClause.Value); | ||
|
||
default: | ||
return null; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Return the delegate type if this expression represents a delegate. | ||
/// </summary> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo. #Closed