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

Parse field as a contextual keyword #73947

Merged
merged 39 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
9afc7e1
Field and value as contextual keywords
cston Jun 5, 2024
62bdd75
Fix bootstrap build
cston Jun 11, 2024
538db7e
Fix tests
cston Jun 12, 2024
55da141
Additional test
cston Jun 13, 2024
96b23b5
Merge remote-tracking branch 'upstream/main' into field-value-keywords
cston Jun 13, 2024
dee9526
Fix tests
cston Jun 13, 2024
4d227d3
Do not treat field as keyword in property initializer
cston Jun 24, 2024
0ede824
Address feedback
cston Jun 24, 2024
5f40ed7
Merge remote-tracking branch 'upstream/main' into field-value-keywords
cston Jun 25, 2024
afcbacb
Address feedback
cston Jun 25, 2024
4919f39
Remove event support
cston Jun 25, 2024
b300536
Merge remote-tracking branch 'upstream/main' into field-value-keywords
cston Jun 27, 2024
7924bbe
Remove error for contextual keyword as identifier
cston Jun 27, 2024
05c024d
Revert unnecessary changes
cston Jun 27, 2024
0ea409b
Restore test
cston Jun 27, 2024
b957b4c
Fix tests
cston Jul 1, 2024
6f61596
More tests
cston Jul 1, 2024
0461361
Update public API
cston Jul 2, 2024
94d3381
Merge remote-tracking branch 'upstream/main' into field-value-keywords
cston Jul 2, 2024
6626a89
Report diagnostic when value keyword does not bind to parameter
cston Jul 2, 2024
412382a
Fix bootstrap build
cston Jul 2, 2024
2de28ec
Fix test
cston Jul 2, 2024
ef1b6fa
Do not parse 'value' as contextual keyword
cston Jul 16, 2024
fd03d65
Misc.
cston Jul 17, 2024
4a01d5a
initonly
cston Jul 18, 2024
f0d6151
Attributes
cston Jul 19, 2024
117fe3b
Add incremental parsing test
cston Jul 19, 2024
6cfd56a
Fix test
cston Jul 19, 2024
50c45b2
Merge remote-tracking branch 'upstream/main' into field-value-keywords
cston Jul 19, 2024
33543b1
Update tests
cston Jul 19, 2024
1c55beb
More tests
cston Jul 19, 2024
f165c4f
More tests
cston Jul 29, 2024
2b068b2
Simplify FieldExpression case
cston Jul 30, 2024
7ed1c3d
Address feedback
cston Jul 30, 2024
219d868
Traverse green nodes
cston Aug 1, 2024
b4717dc
Simplify names
cston Aug 1, 2024
74f6f88
Update tests
cston Aug 1, 2024
247c82c
Address feedback
cston Aug 1, 2024
1858006
Add PROTOTYPE comments
cston Aug 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,8 @@ BoundExpression bindExpressionInternal(ExpressionSyntax node, BindingDiagnosticB
return BindThis((ThisExpressionSyntax)node, diagnostics);
case SyntaxKind.BaseExpression:
return BindBase((BaseExpressionSyntax)node, diagnostics);
case SyntaxKind.FieldExpression:
return BindFieldExpression((FieldExpressionSyntax)node, diagnostics);
case SyntaxKind.InvocationExpression:
return BindInvocationExpression((InvocationExpressionSyntax)node, diagnostics);
case SyntaxKind.ArrayInitializerExpression:
Expand Down Expand Up @@ -1433,6 +1435,42 @@ private BoundExpression BindSizeOf(SizeOfExpressionSyntax node, BindingDiagnosti
this.GetSpecialType(SpecialType.System_Int32, diagnostics, node), hasErrors);
}

private BoundExpression BindFieldExpression(FieldExpressionSyntax node, BindingDiagnosticBag diagnostics)
{
Debug.Assert(ContainingType is { });
SynthesizedBackingFieldSymbolBase? field = null;

ReportFieldContextualKeywordConflict(node, node.Token, diagnostics);

switch (ContainingMember())
{
case SynthesizedBackingFieldSymbolBase backingField:
field = backingField;
break;
case MethodSymbol { AssociatedSymbol: SourcePropertySymbol property }:
field = property.BackingField;
break;
default:
{
Debug.Assert((this.Flags & BinderFlags.InContextualAttributeBinder) != 0);
var contextualAttributeBinder = TryGetContextualAttributeBinder(this);
if (contextualAttributeBinder is { AttributeTarget: MethodSymbol { AssociatedSymbol: SourcePropertySymbol property } })
{
field = property.BackingField;
}
break;
}
}

if (field is null)
{
throw ExceptionUtilities.UnexpectedValue(ContainingMember());
}

var implicitReceiver = field.IsStatic ? null : ThisReference(node, field.ContainingType, wasCompilerGenerated: true);
return new BoundFieldAccess(node, implicitReceiver, field, constantValueOpt: null);
}

/// <returns>true if managed type-related errors were found, otherwise false.</returns>
internal static bool CheckManagedAddr(CSharpCompilation compilation, TypeSymbol type, Location location, BindingDiagnosticBag diagnostics, bool errorForManaged = false)
{
Expand Down Expand Up @@ -1744,10 +1782,16 @@ internal void ReportFieldContextualKeywordConflictIfAny(SyntaxNode syntax, Synta
if (name == "field" &&
ContainingMember() is MethodSymbol { MethodKind: MethodKind.PropertyGet or MethodKind.PropertySet, AssociatedSymbol: PropertySymbol { IsIndexer: false } })
{
var requiredVersion = MessageID.IDS_FeatureFieldKeyword.RequiredVersion();
diagnostics.Add(ErrorCode.INF_IdentifierConflictWithContextualKeyword, syntax, name, requiredVersion.ToDisplayString());
ReportFieldContextualKeywordConflict(syntax, identifier, diagnostics);
}
}

private static void ReportFieldContextualKeywordConflict(SyntaxNode syntax, SyntaxToken identifier, BindingDiagnosticBag diagnostics)
{
string name = identifier.Text;
var requiredVersion = MessageID.IDS_FeatureFieldKeyword.RequiredVersion();
diagnostics.Add(ErrorCode.INF_IdentifierConflictWithContextualKeyword, syntax, name, requiredVersion.ToDisplayString());
Copy link
Member Author

@cston cston Jul 29, 2024

Choose a reason for hiding this comment

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

ErrorCode.INF_IdentifierConflictWithContextualKeyword

Should be reported in LanguageParser.ParseTermWithoutPostfix rather than here. #Resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this an open issue that we are tracking resolving in a future PR?

Copy link
Member Author

Choose a reason for hiding this comment

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

I was assuming the comment is the tracking. (It might be useful to move the diagnostic to the parser, but it doesn't seem necessary, so I didn't think it required an issue.)

Copy link
Contributor

Choose a reason for hiding this comment

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

It's fine to not have an issue, but in that case I would expect a prototype comment, is there one present here that I missed?

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 we refine the diagnostic reporting to only report when field would not bind to a distinct symbol, reporting will need to stay in the Binder. Given that, I'll leave the code as is, with no PROTOTYPE comment for now.

}
#nullable disable

private void LookupIdentifier(LookupResult lookupResult, SimpleNameSyntax node, bool invoked, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -3201,6 +3201,71 @@ internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations)
=> new LiteralExpressionSyntax(this.Kind, this.token, GetDiagnostics(), annotations);
}

/// <summary>Class which represents the syntax node for a field expression.</summary>
internal sealed partial class FieldExpressionSyntax : ExpressionSyntax
{
internal readonly SyntaxToken token;

internal FieldExpressionSyntax(SyntaxKind kind, SyntaxToken token, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations)
: base(kind, diagnostics, annotations)
{
this.SlotCount = 1;
this.AdjustFlagsAndWidth(token);
this.token = token;
}

internal FieldExpressionSyntax(SyntaxKind kind, SyntaxToken token, SyntaxFactoryContext context)
: base(kind)
{
this.SetFactoryContext(context);
this.SlotCount = 1;
this.AdjustFlagsAndWidth(token);
this.token = token;
}

internal FieldExpressionSyntax(SyntaxKind kind, SyntaxToken token)
: base(kind)
{
this.SlotCount = 1;
this.AdjustFlagsAndWidth(token);
this.token = token;
}

/// <summary>SyntaxToken representing the field keyword.</summary>
public SyntaxToken Token => this.token;

internal override GreenNode? GetSlot(int index)
=> index == 0 ? this.token : null;

internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.FieldExpressionSyntax(this, parent, position);

public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitFieldExpression(this);
public override TResult Accept<TResult>(CSharpSyntaxVisitor<TResult> visitor) => visitor.VisitFieldExpression(this);

public FieldExpressionSyntax Update(SyntaxToken token)
{
if (token != this.Token)
{
var newNode = SyntaxFactory.FieldExpression(token);
var diags = GetDiagnostics();
if (diags?.Length > 0)
newNode = newNode.WithDiagnosticsGreen(diags);
var annotations = GetAnnotations();
if (annotations?.Length > 0)
newNode = newNode.WithAnnotationsGreen(annotations);
return newNode;
}

return this;
}

internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics)
=> new FieldExpressionSyntax(this.Kind, this.token, diagnostics, GetAnnotations());

internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations)
=> new FieldExpressionSyntax(this.Kind, this.token, GetDiagnostics(), annotations);
}

/// <summary>Class which represents the syntax node for MakeRef expression.</summary>
internal sealed partial class MakeRefExpressionSyntax : ExpressionSyntax
{
Expand Down Expand Up @@ -26456,6 +26521,7 @@ internal partial class CSharpSyntaxVisitor<TResult>
public virtual TResult VisitThisExpression(ThisExpressionSyntax node) => this.DefaultVisit(node);
public virtual TResult VisitBaseExpression(BaseExpressionSyntax node) => this.DefaultVisit(node);
public virtual TResult VisitLiteralExpression(LiteralExpressionSyntax node) => this.DefaultVisit(node);
public virtual TResult VisitFieldExpression(FieldExpressionSyntax node) => this.DefaultVisit(node);
public virtual TResult VisitMakeRefExpression(MakeRefExpressionSyntax node) => this.DefaultVisit(node);
public virtual TResult VisitRefTypeExpression(RefTypeExpressionSyntax node) => this.DefaultVisit(node);
public virtual TResult VisitRefValueExpression(RefValueExpressionSyntax node) => this.DefaultVisit(node);
Expand Down Expand Up @@ -26703,6 +26769,7 @@ internal partial class CSharpSyntaxVisitor
public virtual void VisitThisExpression(ThisExpressionSyntax node) => this.DefaultVisit(node);
public virtual void VisitBaseExpression(BaseExpressionSyntax node) => this.DefaultVisit(node);
public virtual void VisitLiteralExpression(LiteralExpressionSyntax node) => this.DefaultVisit(node);
public virtual void VisitFieldExpression(FieldExpressionSyntax node) => this.DefaultVisit(node);
public virtual void VisitMakeRefExpression(MakeRefExpressionSyntax node) => this.DefaultVisit(node);
public virtual void VisitRefTypeExpression(RefTypeExpressionSyntax node) => this.DefaultVisit(node);
public virtual void VisitRefValueExpression(RefValueExpressionSyntax node) => this.DefaultVisit(node);
Expand Down Expand Up @@ -27024,6 +27091,9 @@ public override CSharpSyntaxNode VisitBaseExpression(BaseExpressionSyntax node)
public override CSharpSyntaxNode VisitLiteralExpression(LiteralExpressionSyntax node)
=> node.Update((SyntaxToken)Visit(node.Token));

public override CSharpSyntaxNode VisitFieldExpression(FieldExpressionSyntax node)
=> node.Update((SyntaxToken)Visit(node.Token));

public override CSharpSyntaxNode VisitMakeRefExpression(MakeRefExpressionSyntax node)
=> node.Update((SyntaxToken)Visit(node.Keyword), (SyntaxToken)Visit(node.OpenParenToken), (ExpressionSyntax)Visit(node.Expression), (SyntaxToken)Visit(node.CloseParenToken));

Expand Down Expand Up @@ -28620,6 +28690,26 @@ public LiteralExpressionSyntax LiteralExpression(SyntaxKind kind, SyntaxToken to
return result;
}

public FieldExpressionSyntax FieldExpression(SyntaxToken token)
{
#if DEBUG
if (token == null) throw new ArgumentNullException(nameof(token));
if (token.Kind != SyntaxKind.FieldKeyword) throw new ArgumentException(nameof(token));
#endif

int hash;
var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.FieldExpression, token, this.context, out hash);
if (cached != null) return (FieldExpressionSyntax)cached;

var result = new FieldExpressionSyntax(SyntaxKind.FieldExpression, token, this.context);
if (hash >= 0)
{
SyntaxNodeCache.AddNode(result, hash);
}

return result;
}

public MakeRefExpressionSyntax MakeRefExpression(SyntaxToken keyword, SyntaxToken openParenToken, ExpressionSyntax expression, SyntaxToken closeParenToken)
{
#if DEBUG
Expand Down Expand Up @@ -33868,6 +33958,26 @@ public static LiteralExpressionSyntax LiteralExpression(SyntaxKind kind, SyntaxT
return result;
}

public static FieldExpressionSyntax FieldExpression(SyntaxToken token)
{
#if DEBUG
if (token == null) throw new ArgumentNullException(nameof(token));
if (token.Kind != SyntaxKind.FieldKeyword) throw new ArgumentException(nameof(token));
#endif

int hash;
var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.FieldExpression, token, out hash);
if (cached != null) return (FieldExpressionSyntax)cached;

var result = new FieldExpressionSyntax(SyntaxKind.FieldExpression, token);
if (hash >= 0)
{
SyntaxNodeCache.AddNode(result, hash);
}

return result;
}

public static MakeRefExpressionSyntax MakeRefExpression(SyntaxToken keyword, SyntaxToken openParenToken, ExpressionSyntax expression, SyntaxToken closeParenToken)
{
#if DEBUG
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ public partial class CSharpSyntaxVisitor<TResult>
/// <summary>Called when the visitor visits a LiteralExpressionSyntax node.</summary>
public virtual TResult? VisitLiteralExpression(LiteralExpressionSyntax node) => this.DefaultVisit(node);

/// <summary>Called when the visitor visits a FieldExpressionSyntax node.</summary>
public virtual TResult? VisitFieldExpression(FieldExpressionSyntax node) => this.DefaultVisit(node);

/// <summary>Called when the visitor visits a MakeRefExpressionSyntax node.</summary>
public virtual TResult? VisitMakeRefExpression(MakeRefExpressionSyntax node) => this.DefaultVisit(node);

Expand Down Expand Up @@ -858,6 +861,9 @@ public partial class CSharpSyntaxVisitor
/// <summary>Called when the visitor visits a LiteralExpressionSyntax node.</summary>
public virtual void VisitLiteralExpression(LiteralExpressionSyntax node) => this.DefaultVisit(node);

/// <summary>Called when the visitor visits a FieldExpressionSyntax node.</summary>
public virtual void VisitFieldExpression(FieldExpressionSyntax node) => this.DefaultVisit(node);

/// <summary>Called when the visitor visits a MakeRefExpressionSyntax node.</summary>
public virtual void VisitMakeRefExpression(MakeRefExpressionSyntax node) => this.DefaultVisit(node);

Expand Down Expand Up @@ -1590,6 +1596,9 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor<SyntaxNode?>
public override SyntaxNode? VisitLiteralExpression(LiteralExpressionSyntax node)
=> node.Update(VisitToken(node.Token));

public override SyntaxNode? VisitFieldExpression(FieldExpressionSyntax node)
=> node.Update(VisitToken(node.Token));

public override SyntaxNode? VisitMakeRefExpression(MakeRefExpressionSyntax node)
=> node.Update(VisitToken(node.Keyword), VisitToken(node.OpenParenToken), (ExpressionSyntax?)Visit(node.Expression) ?? throw new ArgumentNullException("expression"), VisitToken(node.CloseParenToken));

Expand Down Expand Up @@ -2936,6 +2945,17 @@ public static LiteralExpressionSyntax LiteralExpression(SyntaxKind kind, SyntaxT
return (LiteralExpressionSyntax)Syntax.InternalSyntax.SyntaxFactory.LiteralExpression(kind, (Syntax.InternalSyntax.SyntaxToken)token.Node!).CreateRed();
}

/// <summary>Creates a new FieldExpressionSyntax instance.</summary>
public static FieldExpressionSyntax FieldExpression(SyntaxToken token)
{
if (token.Kind() != SyntaxKind.FieldKeyword) throw new ArgumentException(nameof(token));
return (FieldExpressionSyntax)Syntax.InternalSyntax.SyntaxFactory.FieldExpression((Syntax.InternalSyntax.SyntaxToken)token.Node!).CreateRed();
}

/// <summary>Creates a new FieldExpressionSyntax instance.</summary>
public static FieldExpressionSyntax FieldExpression()
=> SyntaxFactory.FieldExpression(SyntaxFactory.Token(SyntaxKind.FieldKeyword));

/// <summary>Creates a new MakeRefExpressionSyntax instance.</summary>
public static MakeRefExpressionSyntax MakeRefExpression(SyntaxToken keyword, SyntaxToken openParenToken, ExpressionSyntax expression, SyntaxToken closeParenToken)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2032,6 +2032,46 @@ public LiteralExpressionSyntax Update(SyntaxToken token)
public LiteralExpressionSyntax WithToken(SyntaxToken token) => Update(token);
}

/// <summary>Class which represents the syntax node for a field expression.</summary>
/// <remarks>
/// <para>This node is associated with the following syntax kinds:</para>
/// <list type="bullet">
/// <item><description><see cref="SyntaxKind.FieldExpression"/></description></item>
/// </list>
/// </remarks>
public sealed partial class FieldExpressionSyntax : ExpressionSyntax
{

internal FieldExpressionSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position)
: base(green, parent, position)
{
}

/// <summary>SyntaxToken representing the field keyword.</summary>
public SyntaxToken Token => new SyntaxToken(this, ((InternalSyntax.FieldExpressionSyntax)this.Green).token, Position, 0);

internal override SyntaxNode? GetNodeSlot(int index) => null;

internal override SyntaxNode? GetCachedSlot(int index) => null;

public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitFieldExpression(this);
public override TResult? Accept<TResult>(CSharpSyntaxVisitor<TResult> visitor) where TResult : default => visitor.VisitFieldExpression(this);

public FieldExpressionSyntax Update(SyntaxToken token)
{
if (token != this.Token)
{
var newNode = SyntaxFactory.FieldExpression(token);
var annotations = GetAnnotations();
return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode;
}

return this;
}

public FieldExpressionSyntax WithToken(SyntaxToken token) => Update(token);
}

/// <summary>Class which represents the syntax node for MakeRef expression.</summary>
/// <remarks>
/// <para>This node is associated with the following syntax kinds:</para>
Expand Down
Loading