Skip to content

Commit

Permalink
Allow parsing ref readonly parameters in cref
Browse files Browse the repository at this point in the history
  • Loading branch information
jjonescz committed Jul 19, 2023
1 parent 60de847 commit 308e1b2
Show file tree
Hide file tree
Showing 13 changed files with 340 additions and 47 deletions.
6 changes: 6 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,12 @@ private ImmutableArray<ParameterSymbol> BindCrefParameters(BaseCrefParameterList
foreach (CrefParameterSyntax parameter in parameterListSyntax.Parameters)
{
RefKind refKind = parameter.RefKindKeyword.Kind().GetRefKind();
Debug.Assert(parameter.ReadOnlyKeyword.IsKind(SyntaxKind.None) ||
(parameter.ReadOnlyKeyword.IsKind(SyntaxKind.ReadOnlyKeyword) && refKind == RefKind.Ref));
if (refKind == RefKind.Ref && parameter.ReadOnlyKeyword.IsKind(SyntaxKind.ReadOnlyKeyword))
{
refKind = RefKind.RefReadOnlyParameter;
}

Debug.Assert(parameterListSyntax.Parent is object);
TypeSymbol type = BindCrefParameterOrReturnType(parameter.Type, (MemberCrefSyntax)parameterListSyntax.Parent, diagnostics);
Expand Down
6 changes: 3 additions & 3 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 @@ -30195,62 +30195,81 @@ static CrefBracketedParameterListSyntax()

/// <summary>
/// An element of a BaseCrefParameterListSyntax.
/// Unlike a regular parameter, a cref parameter has only an optional ref or out keyword and a type -
/// Unlike a regular parameter, a cref parameter has only an optional ref, in, out keyword,
/// an optional readonly keyword, and a type -
/// there is no name and there are no attributes or other modifiers.
/// </summary>
internal sealed partial class CrefParameterSyntax : CSharpSyntaxNode
{
internal readonly SyntaxToken? refKindKeyword;
internal readonly SyntaxToken? readOnlyKeyword;
internal readonly TypeSyntax type;

internal CrefParameterSyntax(SyntaxKind kind, SyntaxToken? refKindKeyword, TypeSyntax type, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations)
internal CrefParameterSyntax(SyntaxKind kind, SyntaxToken? refKindKeyword, SyntaxToken? readOnlyKeyword, TypeSyntax type, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations)
: base(kind, diagnostics, annotations)
{
this.SlotCount = 2;
this.SlotCount = 3;
if (refKindKeyword != null)
{
this.AdjustFlagsAndWidth(refKindKeyword);
this.refKindKeyword = refKindKeyword;
}
if (readOnlyKeyword != null)
{
this.AdjustFlagsAndWidth(readOnlyKeyword);
this.readOnlyKeyword = readOnlyKeyword;
}
this.AdjustFlagsAndWidth(type);
this.type = type;
}

internal CrefParameterSyntax(SyntaxKind kind, SyntaxToken? refKindKeyword, TypeSyntax type, SyntaxFactoryContext context)
internal CrefParameterSyntax(SyntaxKind kind, SyntaxToken? refKindKeyword, SyntaxToken? readOnlyKeyword, TypeSyntax type, SyntaxFactoryContext context)
: base(kind)
{
this.SetFactoryContext(context);
this.SlotCount = 2;
this.SlotCount = 3;
if (refKindKeyword != null)
{
this.AdjustFlagsAndWidth(refKindKeyword);
this.refKindKeyword = refKindKeyword;
}
if (readOnlyKeyword != null)
{
this.AdjustFlagsAndWidth(readOnlyKeyword);
this.readOnlyKeyword = readOnlyKeyword;
}
this.AdjustFlagsAndWidth(type);
this.type = type;
}

internal CrefParameterSyntax(SyntaxKind kind, SyntaxToken? refKindKeyword, TypeSyntax type)
internal CrefParameterSyntax(SyntaxKind kind, SyntaxToken? refKindKeyword, SyntaxToken? readOnlyKeyword, TypeSyntax type)
: base(kind)
{
this.SlotCount = 2;
this.SlotCount = 3;
if (refKindKeyword != null)
{
this.AdjustFlagsAndWidth(refKindKeyword);
this.refKindKeyword = refKindKeyword;
}
if (readOnlyKeyword != null)
{
this.AdjustFlagsAndWidth(readOnlyKeyword);
this.readOnlyKeyword = readOnlyKeyword;
}
this.AdjustFlagsAndWidth(type);
this.type = type;
}

public SyntaxToken? RefKindKeyword => this.refKindKeyword;
public SyntaxToken? ReadOnlyKeyword => this.readOnlyKeyword;
public TypeSyntax Type => this.type;

internal override GreenNode? GetSlot(int index)
=> index switch
{
0 => this.refKindKeyword,
1 => this.type,
1 => this.readOnlyKeyword,
2 => this.type,
_ => null,
};

Expand All @@ -30259,11 +30278,11 @@ internal CrefParameterSyntax(SyntaxKind kind, SyntaxToken? refKindKeyword, TypeS
public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitCrefParameter(this);
public override TResult Accept<TResult>(CSharpSyntaxVisitor<TResult> visitor) => visitor.VisitCrefParameter(this);

public CrefParameterSyntax Update(SyntaxToken refKindKeyword, TypeSyntax type)
public CrefParameterSyntax Update(SyntaxToken refKindKeyword, SyntaxToken readOnlyKeyword, TypeSyntax type)
{
if (refKindKeyword != this.RefKindKeyword || type != this.Type)
if (refKindKeyword != this.RefKindKeyword || readOnlyKeyword != this.ReadOnlyKeyword || type != this.Type)
{
var newNode = SyntaxFactory.CrefParameter(refKindKeyword, type);
var newNode = SyntaxFactory.CrefParameter(refKindKeyword, readOnlyKeyword, type);
var diags = GetDiagnostics();
if (diags?.Length > 0)
newNode = newNode.WithDiagnosticsGreen(diags);
Expand All @@ -30277,21 +30296,27 @@ public CrefParameterSyntax Update(SyntaxToken refKindKeyword, TypeSyntax type)
}

internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics)
=> new CrefParameterSyntax(this.Kind, this.refKindKeyword, this.type, diagnostics, GetAnnotations());
=> new CrefParameterSyntax(this.Kind, this.refKindKeyword, this.readOnlyKeyword, this.type, diagnostics, GetAnnotations());

internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations)
=> new CrefParameterSyntax(this.Kind, this.refKindKeyword, this.type, GetDiagnostics(), annotations);
=> new CrefParameterSyntax(this.Kind, this.refKindKeyword, this.readOnlyKeyword, this.type, GetDiagnostics(), annotations);

internal CrefParameterSyntax(ObjectReader reader)
: base(reader)
{
this.SlotCount = 2;
this.SlotCount = 3;
var refKindKeyword = (SyntaxToken?)reader.ReadValue();
if (refKindKeyword != null)
{
AdjustFlagsAndWidth(refKindKeyword);
this.refKindKeyword = refKindKeyword;
}
var readOnlyKeyword = (SyntaxToken?)reader.ReadValue();
if (readOnlyKeyword != null)
{
AdjustFlagsAndWidth(readOnlyKeyword);
this.readOnlyKeyword = readOnlyKeyword;
}
var type = (TypeSyntax)reader.ReadValue();
AdjustFlagsAndWidth(type);
this.type = type;
Expand All @@ -30301,6 +30326,7 @@ internal override void WriteTo(ObjectWriter writer)
{
base.WriteTo(writer);
writer.WriteValue(this.refKindKeyword);
writer.WriteValue(this.readOnlyKeyword);
writer.WriteValue(this.type);
}

Expand Down Expand Up @@ -35985,7 +36011,7 @@ public override CSharpSyntaxNode VisitCrefBracketedParameterList(CrefBracketedPa
=> node.Update((SyntaxToken)Visit(node.OpenBracketToken), VisitList(node.Parameters), (SyntaxToken)Visit(node.CloseBracketToken));

public override CSharpSyntaxNode VisitCrefParameter(CrefParameterSyntax node)
=> node.Update((SyntaxToken)Visit(node.RefKindKeyword), (TypeSyntax)Visit(node.Type));
=> node.Update((SyntaxToken)Visit(node.RefKindKeyword), (SyntaxToken)Visit(node.ReadOnlyKeyword), (TypeSyntax)Visit(node.Type));

public override CSharpSyntaxNode VisitXmlElement(XmlElementSyntax node)
=> node.Update((XmlElementStartTagSyntax)Visit(node.StartTag), VisitList(node.Content), (XmlElementEndTagSyntax)Visit(node.EndTag));
Expand Down Expand Up @@ -40645,7 +40671,7 @@ public CrefBracketedParameterListSyntax CrefBracketedParameterList(SyntaxToken o
return result;
}

public CrefParameterSyntax CrefParameter(SyntaxToken? refKindKeyword, TypeSyntax type)
public CrefParameterSyntax CrefParameter(SyntaxToken? refKindKeyword, SyntaxToken? readOnlyKeyword, TypeSyntax type)
{
#if DEBUG
if (refKindKeyword != null)
Expand All @@ -40659,14 +40685,23 @@ public CrefParameterSyntax CrefParameter(SyntaxToken? refKindKeyword, TypeSyntax
default: throw new ArgumentException(nameof(refKindKeyword));
}
}
if (readOnlyKeyword != null)
{
switch (readOnlyKeyword.Kind)
{
case SyntaxKind.ReadOnlyKeyword:
case SyntaxKind.None: break;
default: throw new ArgumentException(nameof(readOnlyKeyword));
}
}
if (type == null) throw new ArgumentNullException(nameof(type));
#endif

int hash;
var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.CrefParameter, refKindKeyword, type, this.context, out hash);
var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.CrefParameter, refKindKeyword, readOnlyKeyword, type, this.context, out hash);
if (cached != null) return (CrefParameterSyntax)cached;

var result = new CrefParameterSyntax(SyntaxKind.CrefParameter, refKindKeyword, type, this.context);
var result = new CrefParameterSyntax(SyntaxKind.CrefParameter, refKindKeyword, readOnlyKeyword, type, this.context);
if (hash >= 0)
{
SyntaxNodeCache.AddNode(result, hash);
Expand Down Expand Up @@ -45852,7 +45887,7 @@ public static CrefBracketedParameterListSyntax CrefBracketedParameterList(Syntax
return result;
}

public static CrefParameterSyntax CrefParameter(SyntaxToken? refKindKeyword, TypeSyntax type)
public static CrefParameterSyntax CrefParameter(SyntaxToken? refKindKeyword, SyntaxToken? readOnlyKeyword, TypeSyntax type)
{
#if DEBUG
if (refKindKeyword != null)
Expand All @@ -45866,14 +45901,23 @@ public static CrefParameterSyntax CrefParameter(SyntaxToken? refKindKeyword, Typ
default: throw new ArgumentException(nameof(refKindKeyword));
}
}
if (readOnlyKeyword != null)
{
switch (readOnlyKeyword.Kind)
{
case SyntaxKind.ReadOnlyKeyword:
case SyntaxKind.None: break;
default: throw new ArgumentException(nameof(readOnlyKeyword));
}
}
if (type == null) throw new ArgumentNullException(nameof(type));
#endif

int hash;
var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.CrefParameter, refKindKeyword, type, out hash);
var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.CrefParameter, refKindKeyword, readOnlyKeyword, type, out hash);
if (cached != null) return (CrefParameterSyntax)cached;

var result = new CrefParameterSyntax(SyntaxKind.CrefParameter, refKindKeyword, type);
var result = new CrefParameterSyntax(SyntaxKind.CrefParameter, refKindKeyword, readOnlyKeyword, type);
if (hash >= 0)
{
SyntaxNodeCache.AddNode(result, hash);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2089,7 +2089,7 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor<SyntaxNode?>
=> node.Update(VisitToken(node.OpenBracketToken), VisitList(node.Parameters), VisitToken(node.CloseBracketToken));

public override SyntaxNode? VisitCrefParameter(CrefParameterSyntax node)
=> node.Update(VisitToken(node.RefKindKeyword), (TypeSyntax?)Visit(node.Type) ?? throw new ArgumentNullException("type"));
=> node.Update(VisitToken(node.RefKindKeyword), VisitToken(node.ReadOnlyKeyword), (TypeSyntax?)Visit(node.Type) ?? throw new ArgumentNullException("type"));

public override SyntaxNode? VisitXmlElement(XmlElementSyntax node)
=> node.Update((XmlElementStartTagSyntax?)Visit(node.StartTag) ?? throw new ArgumentNullException("startTag"), VisitList(node.Content), (XmlElementEndTagSyntax?)Visit(node.EndTag) ?? throw new ArgumentNullException("endTag"));
Expand Down Expand Up @@ -5860,7 +5860,7 @@ public static CrefBracketedParameterListSyntax CrefBracketedParameterList(Separa
=> SyntaxFactory.CrefBracketedParameterList(SyntaxFactory.Token(SyntaxKind.OpenBracketToken), parameters, SyntaxFactory.Token(SyntaxKind.CloseBracketToken));

/// <summary>Creates a new CrefParameterSyntax instance.</summary>
public static CrefParameterSyntax CrefParameter(SyntaxToken refKindKeyword, TypeSyntax type)
public static CrefParameterSyntax CrefParameter(SyntaxToken refKindKeyword, SyntaxToken readOnlyKeyword, TypeSyntax type)
{
switch (refKindKeyword.Kind())
{
Expand All @@ -5870,13 +5870,23 @@ public static CrefParameterSyntax CrefParameter(SyntaxToken refKindKeyword, Type
case SyntaxKind.None: break;
default: throw new ArgumentException(nameof(refKindKeyword));
}
switch (readOnlyKeyword.Kind())
{
case SyntaxKind.ReadOnlyKeyword:
case SyntaxKind.None: break;
default: throw new ArgumentException(nameof(readOnlyKeyword));
}
if (type == null) throw new ArgumentNullException(nameof(type));
return (CrefParameterSyntax)Syntax.InternalSyntax.SyntaxFactory.CrefParameter((Syntax.InternalSyntax.SyntaxToken?)refKindKeyword.Node, (Syntax.InternalSyntax.TypeSyntax)type.Green).CreateRed();
return (CrefParameterSyntax)Syntax.InternalSyntax.SyntaxFactory.CrefParameter((Syntax.InternalSyntax.SyntaxToken?)refKindKeyword.Node, (Syntax.InternalSyntax.SyntaxToken?)readOnlyKeyword.Node, (Syntax.InternalSyntax.TypeSyntax)type.Green).CreateRed();
}

/// <summary>Creates a new CrefParameterSyntax instance.</summary>
public static CrefParameterSyntax CrefParameter(SyntaxToken refKindKeyword, TypeSyntax type)
=> SyntaxFactory.CrefParameter(refKindKeyword, default, type);

/// <summary>Creates a new CrefParameterSyntax instance.</summary>
public static CrefParameterSyntax CrefParameter(TypeSyntax type)
=> SyntaxFactory.CrefParameter(default, type);
=> SyntaxFactory.CrefParameter(default, default, type);

/// <summary>Creates a new XmlElementSyntax instance.</summary>
public static XmlElementSyntax XmlElement(XmlElementStartTagSyntax startTag, SyntaxList<XmlNodeSyntax> content, XmlElementEndTagSyntax endTag)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14332,7 +14332,8 @@ public CrefBracketedParameterListSyntax Update(SyntaxToken openBracketToken, Sep

/// <summary>
/// An element of a BaseCrefParameterListSyntax.
/// Unlike a regular parameter, a cref parameter has only an optional ref or out keyword and a type -
/// Unlike a regular parameter, a cref parameter has only an optional ref, in, out keyword,
/// an optional readonly keyword, and a type -
/// there is no name and there are no attributes or other modifiers.
/// </summary>
/// <remarks>
Expand All @@ -14359,29 +14360,39 @@ public SyntaxToken RefKindKeyword
}
}

public TypeSyntax Type => GetRed(ref this.type, 1)!;
public SyntaxToken ReadOnlyKeyword
{
get
{
var slot = ((Syntax.InternalSyntax.CrefParameterSyntax)this.Green).readOnlyKeyword;
return slot != null ? new SyntaxToken(this, slot, GetChildPosition(1), GetChildIndex(1)) : default;
}
}

internal override SyntaxNode? GetNodeSlot(int index) => index == 1 ? GetRed(ref this.type, 1)! : null;
public TypeSyntax Type => GetRed(ref this.type, 2)!;

internal override SyntaxNode? GetCachedSlot(int index) => index == 1 ? this.type : null;
internal override SyntaxNode? GetNodeSlot(int index) => index == 2 ? GetRed(ref this.type, 2)! : null;

internal override SyntaxNode? GetCachedSlot(int index) => index == 2 ? this.type : null;

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

public CrefParameterSyntax Update(SyntaxToken refKindKeyword, TypeSyntax type)
public CrefParameterSyntax Update(SyntaxToken refKindKeyword, SyntaxToken readOnlyKeyword, TypeSyntax type)
{
if (refKindKeyword != this.RefKindKeyword || type != this.Type)
if (refKindKeyword != this.RefKindKeyword || readOnlyKeyword != this.ReadOnlyKeyword || type != this.Type)
{
var newNode = SyntaxFactory.CrefParameter(refKindKeyword, type);
var newNode = SyntaxFactory.CrefParameter(refKindKeyword, readOnlyKeyword, type);
var annotations = GetAnnotations();
return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode;
}

return this;
}

public CrefParameterSyntax WithRefKindKeyword(SyntaxToken refKindKeyword) => Update(refKindKeyword, this.Type);
public CrefParameterSyntax WithType(TypeSyntax type) => Update(this.RefKindKeyword, type);
public CrefParameterSyntax WithRefKindKeyword(SyntaxToken refKindKeyword) => Update(refKindKeyword, this.ReadOnlyKeyword, this.Type);
public CrefParameterSyntax WithReadOnlyKeyword(SyntaxToken readOnlyKeyword) => Update(this.RefKindKeyword, readOnlyKeyword, this.Type);
public CrefParameterSyntax WithType(TypeSyntax type) => Update(this.RefKindKeyword, this.ReadOnlyKeyword, type);
}

public abstract partial class XmlNodeSyntax : CSharpSyntaxNode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,7 @@ private bool IsPossibleCrefParameter()
/// Parse an element of a cref parameter list.
/// </summary>
/// <remarks>
/// "ref" and "out" work, but "params", "this", and "__arglist" don't.
/// "ref", "ref readonly", "in", "out" work, but "params", "this", and "__arglist" don't.
/// </remarks>
private CrefParameterSyntax ParseCrefParameter()
{
Expand All @@ -1262,8 +1262,14 @@ private CrefParameterSyntax ParseCrefParameter()
break;
}

SyntaxToken readOnlyOpt = null;
if (CurrentToken.Kind == SyntaxKind.ReadOnlyKeyword && refKindOpt is { Kind: SyntaxKind.RefKeyword })
{
readOnlyOpt = EatToken();
}

TypeSyntax type = ParseCrefType(typeArgumentsMustBeIdentifiers: false);
return SyntaxFactory.CrefParameter(refKindOpt, type);
return SyntaxFactory.CrefParameter(refKindKeyword: refKindOpt, readOnlyKeyword: readOnlyOpt, type);
}

/// <summary>
Expand Down
Loading

0 comments on commit 308e1b2

Please sign in to comment.