Skip to content

Commit

Permalink
Allow @@ sequences. Fixes dotnet/sdk#42730 for now. When we move to…
Browse files Browse the repository at this point in the history
… the new lexer, these will once again be disallowed; attempting to use runtime compilation will necessitate using the native lexer.
  • Loading branch information
333fred committed Aug 15, 2024
1 parent 1e6a7ed commit 543817c
Show file tree
Hide file tree
Showing 19 changed files with 119 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10528,10 +10528,7 @@ public void AtAtHandled()
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(1,28): error CS0103: The name 'Html' does not exist in the current context
// var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div");
Diagnostic(ErrorCode.ERR_NameNotInContext, "@Html").WithArguments("Html").WithLocation(1, 28),
// x:\dir\subdir\Test\TestComponent.cshtml(1,70): error CS9008: Sequence of '@' characters is not allowed. A verbatim string or identifier can only have one '@' character and a raw string cannot have any.
// var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div");
Diagnostic(ErrorCode.ERR_IllegalAtSequence, "@@").WithLocation(1, 70));
Diagnostic(ErrorCode.ERR_NameNotInContext, "@Html").WithArguments("Html").WithLocation(1, 28));
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.
{
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div");
var validationMessage = @Html.ValidationMessage("test", "", new {

#line default
#line hidden
#nullable disable
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
@class = "invalid-feedback" }, "div");

#line default
#line hidden
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@
CSharpCode -
IntermediateToken - - CSharp - #pragma warning restore 0414
MethodDeclaration - - protected override - void - BuildRenderTree
CSharpCode - (2:0,2 [107] x:\dir\subdir\Test\TestComponent.cshtml)
LazyIntermediateToken - (2:0,2 [107] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div");
CSharpCode - (2:0,2 [67] x:\dir\subdir\Test\TestComponent.cshtml)
LazyIntermediateToken - (2:0,2 [67] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - var validationMessage = @Html.ValidationMessage("test", "", new {
CSharpCode - (70:0,70 [39] x:\dir\subdir\Test\TestComponent.cshtml)
LazyIntermediateToken - (70:0,70 [39] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - @class = "invalid-feedback" }, "div");
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
Source Location: (2:0,2 [107] x:\dir\subdir\Test\TestComponent.cshtml)
| var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div"); |
Generated Location: (987:28,2 [107] )
| var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div"); |
Source Location: (2:0,2 [67] x:\dir\subdir\Test\TestComponent.cshtml)
| var validationMessage = @Html.ValidationMessage("test", "", new { |
Generated Location: (987:28,2 [67] )
| var validationMessage = @Html.ValidationMessage("test", "", new { |

Source Location: (70:0,70 [39] x:\dir\subdir\Test\TestComponent.cshtml)
|@class = "invalid-feedback" }, "div"); |
Generated Location: (1246:35,70 [39] )
|@class = "invalid-feedback" }, "div"); |

Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@ public partial class TestComponent : global::Microsoft.AspNetCore.Components.Com
protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
{
#nullable restore
#line (1,3)-(1,110) "x:\dir\subdir\Test\TestComponent.cshtml"
var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div");
#line (1,3)-(1,70) "x:\dir\subdir\Test\TestComponent.cshtml"
var validationMessage = @Html.ValidationMessage("test", "", new {

#line default
#line hidden
#nullable disable

#nullable restore
#line (1,71)-(1,110) "x:\dir\subdir\Test\TestComponent.cshtml"
@class = "invalid-feedback" }, "div");

#line default
#line hidden
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@
UsingDirective - (136:5,1 [47] ) - global::Microsoft.AspNetCore.Components
ClassDeclaration - - public partial - TestComponent - global::Microsoft.AspNetCore.Components.ComponentBase -
MethodDeclaration - - protected override - void - BuildRenderTree
CSharpCode - (2:0,2 [107] x:\dir\subdir\Test\TestComponent.cshtml)
LazyIntermediateToken - (2:0,2 [107] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div");
CSharpCode - (2:0,2 [67] x:\dir\subdir\Test\TestComponent.cshtml)
LazyIntermediateToken - (2:0,2 [67] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - var validationMessage = @Html.ValidationMessage("test", "", new {
CSharpCode - (70:0,70 [39] x:\dir\subdir\Test\TestComponent.cshtml)
LazyIntermediateToken - (70:0,70 [39] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - @class = "invalid-feedback" }, "div");
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
Source Location: (2:0,2 [107] x:\dir\subdir\Test\TestComponent.cshtml)
| var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div"); |
Generated Location: (736:21,0 [107] )
| var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div"); |
Source Location: (2:0,2 [67] x:\dir\subdir\Test\TestComponent.cshtml)
| var validationMessage = @Html.ValidationMessage("test", "", new { |
Generated Location: (735:21,0 [67] )
| var validationMessage = @Html.ValidationMessage("test", "", new { |

Source Location: (70:0,70 [39] x:\dir\subdir\Test\TestComponent.cshtml)
|@class = "invalid-feedback" }, "div"); |
Generated Location: (939:29,0 [39] )
|@class = "invalid-feedback" }, "div"); |

Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@
LeftParenthesis;[(];
CSharpCodeBlock - [38..45)::7
CSharpExpressionLiteral - [38..45)::7 - [@string] - Gen<Expr>
Transition;[@];
Keyword;[string];
Identifier;[@string];
RazorMetaCode - [45..46)::1 - Gen<None>
RightParenthesis;[)];
MarkupTextLiteral - [46..46)::0 - [] - Gen<Markup>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
Whitespace;[ ];
Identifier;[var];
Whitespace;[ ];
Identifier;[@];
Transition;[@];
Identifier;[@class];
Whitespace;[ ];
Assign;[=];
Expand All @@ -30,7 +30,7 @@
Whitespace;[ ];
Assign;[=];
Whitespace;[ ];
Identifier;[@];
Transition;[@];
Identifier;[@class];
Semicolon;[;];
NewLine;[LF];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@
LeftParenthesis;[(];
CSharpCodeBlock - [74..81)::7
CSharpExpressionLiteral - [74..81)::7 - [@string] - Gen<Expr>
Transition;[@];
Keyword;[string];
Identifier;[@string];
RazorMetaCode - [81..82)::1 - Gen<None>
RightParenthesis;[)];
MarkupTextLiteral - [82..82)::0 - [] - Gen<Markup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@
LeftParenthesis;[(];
CSharpCodeBlock - [75..82)::7
CSharpExpressionLiteral - [75..82)::7 - [@string] - Gen<Expr>
Transition;[@];
Keyword;[string];
Identifier;[@string];
RazorMetaCode - [82..83)::1 - Gen<None>
RightParenthesis;[)];
MarkupTextLiteral - [83..83)::0 - [] - Gen<Markup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@
LeftParenthesis;[(];
CSharpCodeBlock - [115..122)::7
CSharpExpressionLiteral - [115..122)::7 - [@string] - Gen<Expr>
Transition;[@];
Keyword;[string];
Identifier;[@string];
RazorMetaCode - [122..123)::1 - Gen<None>
RightParenthesis;[)];
MarkupTextLiteral - [123..123)::0 - [] - Gen<Markup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@
LeftParenthesis;[(];
CSharpCodeBlock - [117..124)::7
CSharpExpressionLiteral - [117..124)::7 - [@string] - Gen<Expr>
Transition;[@];
Keyword;[string];
Identifier;[@string];
RazorMetaCode - [124..125)::1 - Gen<None>
RightParenthesis;[)];
MarkupTextLiteral - [125..125)::0 - [] - Gen<Markup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Code span at (0:0,0 [1] ) - Parent: Statement block at (0:0,0 [118] )
Transition span at (1:0,1 [1] ) - Parent: Statement block at (1:0,1 [117] )
MetaCode span at (2:0,2 [1] ) - Parent: Statement block at (1:0,1 [117] )
Code span at (3:0,3 [114] ) - Parent: Statement block at (1:0,1 [117] )
Code span at (3:0,3 [74] ) - Parent: Statement block at (1:0,1 [117] )
Code span at (77:0,77 [1] ) - Parent: Statement block at (1:0,1 [117] )
Code span at (78:0,78 [39] ) - Parent: Statement block at (1:0,1 [117] )
MetaCode span at (117:0,117 [1] ) - Parent: Statement block at (1:0,1 [117] )
Markup span at (118:0,118 [0] ) - Parent: Markup block at (0:0,0 [118] )
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
RazorMetaCode - [2..3)::1 - Gen<None>
LeftBrace;[{];
CSharpCodeBlock - [3..117)::114
CSharpStatementLiteral - [3..117)::114 - [ var validationMessage = @Html.ValidationMessage(Model.Binding, "", new { @@class = "invalid-feedback" }, "div"); ] - Gen<Stmt>
CSharpStatementLiteral - [3..77)::74 - [ var validationMessage = @Html.ValidationMessage(Model.Binding, "", new { ] - Gen<Stmt>
Whitespace;[ ];
Identifier;[var];
Whitespace;[ ];
Expand All @@ -36,9 +36,10 @@
Whitespace;[ ];
LeftBrace;[{];
Whitespace;[ ];
CSharpEphemeralTextLiteral - [77..78)::1 - [@] - Gen<Stmt>
Transition;[@];
Transition;[@];
Keyword;[class];
CSharpStatementLiteral - [78..117)::39 - [@class = "invalid-feedback" }, "div"); ] - Gen<Stmt>
Identifier;[@class];
Whitespace;[ ];
Assign;[=];
Whitespace;[ ];
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
Transition;[@];
CSharpStatementLiteral - [12..28)::16 - [@@@class.Foo() }] - Gen<Stmt>
Transition;[@];
Identifier;[@];
Transition;[@];
Identifier;[@class];
Dot;[.];
Identifier;[Foo];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -977,23 +977,28 @@ not SyntaxKind.LeftBracket and
case SyntaxKind.Keyword:
// We want to stitch together `@text`.
Accept(in read);
Accept(NextAsEscapedIdentifier());
continue;

var transition = CurrentToken;
NextToken();
var identifier = CurrentToken;
NextToken();
Debug.Assert(transition.Kind == SyntaxKind.Transition);
Debug.Assert(identifier.Kind is SyntaxKind.Identifier or SyntaxKind.Keyword);
// We special case @@identifier because the old compiler behavior was to simply accept it and treat it as if it was @identifier. While
// this isn't legal, the runtime compiler doesn't handle @identifier correctly. We'll continue to accept this for now, but will potentially
// break it in the future when we move to the roslyn lexer and the runtime/compiletime split is much greater.
case SyntaxKind.Transition:
if (Lookahead(2) is not { Kind: SyntaxKind.Identifier or SyntaxKind.Keyword })
{
goto default;
}

var finalIdentifier = SyntaxFactory.Token(SyntaxKind.Identifier, $"{transition.Content}{identifier.Content}");
Accept(finalIdentifier);
Accept(in read);
AcceptAndMoveNext();
Accept(NextAsEscapedIdentifier());
continue;

default:
// Accept a broken identifier `@` and mark an error
Accept(in read);

transition = CurrentToken;
var transition = CurrentToken;

Debug.Assert(transition.Kind == SyntaxKind.Transition);

Expand All @@ -1002,7 +1007,7 @@ not SyntaxKind.LeftBracket and
new SourceSpan(CurrentStart, contentLength: 1 /* @ */)));

NextToken();
finalIdentifier = SyntaxFactory.Token(SyntaxKind.Identifier, transition.Content);
var finalIdentifier = SyntaxFactory.Token(SyntaxKind.Identifier, transition.Content);
Accept(finalIdentifier);
continue;
}
Expand Down Expand Up @@ -2639,7 +2644,7 @@ private void ParseReservedDirective(SyntaxListBuilder<RazorSyntaxNode> builder,
}

protected void CompleteBlock()
{
{
AcceptMarkerTokenIfNecessary();
CaptureWhitespaceToEndOfLine();
}
Expand Down Expand Up @@ -2822,8 +2827,7 @@ private bool Balance(SyntaxListBuilder<RazorSyntaxNode> builder, BalancingModes
do
{
if (IsAtEmbeddedTransition(
(mode & BalancingModes.AllowCommentsAndTemplates) == BalancingModes.AllowCommentsAndTemplates,
(mode & BalancingModes.AllowEmbeddedTransitions) == BalancingModes.AllowEmbeddedTransitions))
(mode & BalancingModes.AllowCommentsAndTemplates) == BalancingModes.AllowCommentsAndTemplates))
{
Accept(in tokens);
tokens.Clear();
Expand All @@ -2832,6 +2836,31 @@ private bool Balance(SyntaxListBuilder<RazorSyntaxNode> builder, BalancingModes
// Reset backtracking since we've already outputted some spans.
startPosition = CurrentStart.AbsoluteIndex;
}

if (At(SyntaxKind.Transition))
{
// We special case @@identifier because the old compiler behavior was to simply accept it and treat it as if it was @identifier. While
// this isn't legal, the runtime compiler doesn't handle @identifier correctly. We'll continue to accept this for now, but will potentially
// break it in the future when we move to the roslyn lexer and the runtime/compiletime split is much greater.
if (NextIs(SyntaxKind.Transition) && Lookahead(2) is { Kind: SyntaxKind.Identifier or SyntaxKind.Keyword })
{
Accept(in tokens);
tokens.Clear();
builder.Add(OutputTokensAsStatementLiteral());
AcceptAndMoveNext();
builder.Add(OutputTokensAsEphemeralLiteral());

// Reset backtracking since we've already outputted some spans.
startPosition = CurrentStart.AbsoluteIndex;
continue;
}
else if (NextIs(SyntaxKind.Keyword, SyntaxKind.Identifier))
{
tokens.Add(NextAsEscapedIdentifier());
continue;
}
}

if (At(left))
{
nesting++;
Expand All @@ -2840,12 +2869,14 @@ private bool Balance(SyntaxListBuilder<RazorSyntaxNode> builder, BalancingModes
{
nesting--;
}

if (nesting > 0)
{
tokens.Add(CurrentToken);
NextToken();
}
}
while (nesting > 0 && NextToken() && !(stopAtEndOfLine && At(SyntaxKind.NewLine)));
while (nesting > 0 && EnsureCurrent() && !(stopAtEndOfLine && At(SyntaxKind.NewLine)));

if (nesting > 0)
{
Expand Down Expand Up @@ -2876,9 +2907,8 @@ private bool Balance(SyntaxListBuilder<RazorSyntaxNode> builder, BalancingModes
return nesting == 0;
}

private bool IsAtEmbeddedTransition(bool allowTemplatesAndComments, bool allowTransitions)
private bool IsAtEmbeddedTransition(bool allowTemplatesAndComments)
{
// No embedded transitions in C#, so ignore that param
return allowTemplatesAndComments
&& ((Language.IsTransition(CurrentToken)
&& NextIs(SyntaxKind.LessThan, SyntaxKind.Colon, SyntaxKind.DoubleColon))
Expand Down Expand Up @@ -2910,6 +2940,19 @@ private void ParseEmbeddedTransition(in SyntaxListBuilder<RazorSyntaxNode> build
}
}

private SyntaxToken NextAsEscapedIdentifier()
{
Debug.Assert(CurrentToken.Kind == SyntaxKind.Transition);
var transition = CurrentToken;
NextToken();
Debug.Assert(CurrentToken.Kind is SyntaxKind.Identifier or SyntaxKind.Keyword);
var identifier = CurrentToken;
NextToken();

var finalIdentifier = SyntaxFactory.Token(SyntaxKind.Identifier, $"{transition.Content}{identifier.Content}");
return finalIdentifier;
}

[Conditional("DEBUG")]
internal void Assert(CSharpKeyword expectedKeyword)
{
Expand Down

0 comments on commit 543817c

Please sign in to comment.