diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.resx b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.resx
index 7a28e7bc11..016d32c0c5 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.resx
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.resx
@@ -117,6 +117,15 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ The C# attribute that will be applied to the current class.
+
+
+ Attribute
+
+
+ Specifies the C# attribute that will be applied to the current class.
+
Binds the provided expression to the '{0}' property and a change event delegate to the '{1}' property of the component.
@@ -151,10 +160,10 @@
Sets the '{0}' attribute to the provided string or delegate value. A delegate value should be of type '{1}'.
- Declares an interface implementation for the current document.
+ Declares an interface implementation for the current class.
- The interface type implemented by the current document.
+ The interface type implemented by the current class.
TypeName
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DirectiveDescriptorBuilderExtensions.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DirectiveDescriptorBuilderExtensions.cs
index 1744475609..e8257bba7f 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DirectiveDescriptorBuilderExtensions.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DirectiveDescriptorBuilderExtensions.cs
@@ -95,6 +95,28 @@ namespace Microsoft.AspNetCore.Razor.Language
return builder;
}
+ public static IDirectiveDescriptorBuilder AddAttributeToken(this IDirectiveDescriptorBuilder builder)
+ {
+ return AddAttributeToken(builder, name: null, description: null);
+ }
+
+ public static IDirectiveDescriptorBuilder AddAttributeToken(this IDirectiveDescriptorBuilder builder, string name, string description)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ builder.Tokens.Add(
+ DirectiveTokenDescriptor.CreateToken(
+ DirectiveTokenKind.Attribute,
+ optional: false,
+ name: name,
+ description: description));
+
+ return builder;
+ }
+
public static IDirectiveDescriptorBuilder AddOptionalMemberToken(this IDirectiveDescriptorBuilder builder)
{
return AddOptionalMemberToken(builder, name: null, description: null);
@@ -182,5 +204,27 @@ namespace Microsoft.AspNetCore.Razor.Language
return builder;
}
+
+ public static IDirectiveDescriptorBuilder AddOptionalAttributeToken(this IDirectiveDescriptorBuilder builder)
+ {
+ return AddOptionalAttributeToken(builder, name: null, description: null);
+ }
+
+ public static IDirectiveDescriptorBuilder AddOptionalAttributeToken(this IDirectiveDescriptorBuilder builder, string name, string description)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ builder.Tokens.Add(
+ DirectiveTokenDescriptor.CreateToken(
+ DirectiveTokenKind.Attribute,
+ optional: true,
+ name: name,
+ description: description));
+
+ return builder;
+ }
}
}
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DirectiveTokenKind.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DirectiveTokenKind.cs
index 4ee292c083..916a57c3ca 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DirectiveTokenKind.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DirectiveTokenKind.cs
@@ -9,5 +9,6 @@ namespace Microsoft.AspNetCore.Razor.Language
Namespace,
Member,
String,
+ Attribute,
}
}
\ No newline at end of file
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/AttributeDirective.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/AttributeDirective.cs
new file mode 100644
index 0000000000..6a2347f38e
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/AttributeDirective.cs
@@ -0,0 +1,31 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.AspNetCore.Razor.Language.Extensions
+{
+ internal static class AttributeDirective
+ {
+ public static readonly DirectiveDescriptor Directive = DirectiveDescriptor.CreateDirective(
+ "attribute",
+ DirectiveKind.SingleLine,
+ builder =>
+ {
+ builder.AddAttributeToken(ComponentResources.AttributeDirective_AttributeToken_Name, ComponentResources.AttributeDirective_AttributeToken_Description);
+ builder.Usage = DirectiveUsage.FileScopedMultipleOccurring;
+ builder.Description = ComponentResources.AttributeDirective_Description;
+ });
+
+ public static void Register(RazorProjectEngineBuilder builder)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ builder.AddDirective(Directive, FileKinds.Legacy, FileKinds.Component, FileKinds.ComponentImport);
+ builder.Features.Add(new AttributeDirectivePass());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/AttributeDirectivePass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/AttributeDirectivePass.cs
new file mode 100644
index 0000000000..0a442cce42
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/AttributeDirectivePass.cs
@@ -0,0 +1,43 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using Microsoft.AspNetCore.Razor.Language.Intermediate;
+
+namespace Microsoft.AspNetCore.Razor.Language.Extensions
+{
+ internal class AttributeDirectivePass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
+ {
+ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
+ {
+ var @namespace = documentNode.FindPrimaryNamespace();
+ var @class = documentNode.FindPrimaryClass();
+ if (@namespace == null || @class == null)
+ {
+ return;
+ }
+
+ var classIndex = @namespace.Children.IndexOf(@class);
+ foreach (var attribute in documentNode.FindDirectiveReferences(AttributeDirective.Directive))
+ {
+ var token = ((DirectiveIntermediateNode)attribute.Node).Tokens.FirstOrDefault();
+ if (token != null)
+ {
+ var node = new CSharpCodeIntermediateNode
+ {
+ Source = token.Source
+ };
+
+ node.Children.Add(new IntermediateToken()
+ {
+ Content = token.Content,
+ Source = token.Source,
+ Kind = TokenKind.CSharp,
+ });
+
+ @namespace.Children.Insert(classIndex++, node);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/DesignTimeDirectiveTargetExtension.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/DesignTimeDirectiveTargetExtension.cs
index 3823ab17e7..fb2594b138 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/DesignTimeDirectiveTargetExtension.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Extensions/DesignTimeDirectiveTargetExtension.cs
@@ -44,6 +44,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
return;
}
+ if (tokenKind == DirectiveTokenKind.Attribute)
+ {
+ // We don't need to do anything special here.
+ // We let the Roslyn take care of providing syntax errors for C# attributes.
+ return;
+ }
+
// Wrap the directive token in a lambda to isolate variable names.
context.CodeWriter
.Write("((")
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/BalancingModes.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/BalancingModes.cs
index a5adf88a2a..898eb9133a 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/BalancingModes.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/BalancingModes.cs
@@ -12,6 +12,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
BacktrackOnFailure = 1,
NoErrorOnFailure = 2,
AllowCommentsAndTemplates = 4,
- AllowEmbeddedTransitions = 8
+ AllowEmbeddedTransitions = 8,
+ StopAtEndOfLine = 16,
}
}
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/CSharpCodeParser.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/CSharpCodeParser.cs
index 3390e570c4..b6fb7bfe6b 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/CSharpCodeParser.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/CSharpCodeParser.cs
@@ -1259,7 +1259,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
if (tokenDescriptor.Kind == DirectiveTokenKind.Member ||
tokenDescriptor.Kind == DirectiveTokenKind.Namespace ||
- tokenDescriptor.Kind == DirectiveTokenKind.Type)
+ tokenDescriptor.Kind == DirectiveTokenKind.Type ||
+ tokenDescriptor.Kind == DirectiveTokenKind.Attribute)
{
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
SpanContext.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Whitespace;
@@ -1351,6 +1352,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
builder.Add(BuildDirective());
return;
}
+ break;
+ case DirectiveTokenKind.Attribute:
+ if (At(SyntaxKind.LeftBracket))
+ {
+ if (Balance(directiveBuilder, BalancingModes.NoErrorOnFailure))
+ {
+ TryAccept(SyntaxKind.RightBracket);
+ }
+ }
+ else
+ {
+ Context.ErrorSink.OnError(
+ RazorDiagnosticFactory.CreateParsing_DirectiveExpectsCSharpAttribute(
+ new SourceSpan(CurrentStart, CurrentToken.Content.Length), descriptor.Directive));
+ builder.Add(BuildDirective());
+ return;
+ }
+
break;
}
@@ -2426,7 +2445,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
var startPosition = CurrentStart.AbsoluteIndex;
var nesting = 1;
- if (!EndOfFile)
+ var stopAtEndOfLine = (mode & BalancingModes.StopAtEndOfLine) == BalancingModes.StopAtEndOfLine;
+ if (!EndOfFile &&
+ !(stopAtEndOfLine && At(SyntaxKind.NewLine)))
{
var tokens = new List();
do
@@ -2455,7 +2476,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
tokens.Add(CurrentToken);
}
}
- while (nesting > 0 && NextToken());
+ while (nesting > 0 && NextToken() && !(stopAtEndOfLine && At(SyntaxKind.NewLine)));
if (nesting > 0)
{
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnosticFactory.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnosticFactory.cs
index e174c45c23..e1e1c9d621 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnosticFactory.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnosticFactory.cs
@@ -416,6 +416,16 @@ namespace Microsoft.AspNetCore.Razor.Language
{
return RazorDiagnostic.Create(Parsing_InvalidTagHelperLookupText, location, lookupText);
}
+
+ internal static readonly RazorDiagnosticDescriptor Parsing_DirectiveExpectsCSharpAttribute =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}1037",
+ () => Resources.DirectiveExpectsCSharpAttribute,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateParsing_DirectiveExpectsCSharpAttribute(SourceSpan location, string directiveName)
+ {
+ return RazorDiagnostic.Create(Parsing_DirectiveExpectsCSharpAttribute, location, directiveName);
+ }
#endregion
#region Semantic Errors
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectEngine.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectEngine.cs
index a150dc7020..32d2b68874 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectEngine.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectEngine.cs
@@ -118,6 +118,7 @@ namespace Microsoft.AspNetCore.Razor.Language
ImplementsDirective.Register(builder);
InheritsDirective.Register(builder);
NamespaceDirective.Register(builder);
+ AttributeDirective.Register(builder);
AddComponentFeatures(builder);
}
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Resources.resx b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Resources.resx
index d2eb94338e..b50fe38e29 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Resources.resx
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Resources.resx
@@ -547,4 +547,7 @@
Invalid tag helper bound attribute parameter '{0}'. Tag helpers cannot bind to HTML attribute parameters with a null or empty name.
+
+ The '{0}' directive expects a C# attribute.
+
\ No newline at end of file
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/DirectiveDescriptorBuilderExtensionsTest.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/DirectiveDescriptorBuilderExtensionsTest.cs
index ffcb4f8ba9..6e78ea1eec 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/DirectiveDescriptorBuilderExtensionsTest.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/DirectiveDescriptorBuilderExtensionsTest.cs
@@ -118,5 +118,33 @@ namespace Microsoft.AspNetCore.Razor.Language
Assert.Equal("Name", token.Name);
Assert.Equal("Description", token.Description);
}
+
+ [Fact]
+ public void AddAttributeToken_AddsToken()
+ {
+ // Arrange & Act
+ var descriptor = DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddAttributeToken("Name", "Description"));
+
+ // Assert
+ var token = Assert.Single(descriptor.Tokens);
+ Assert.Equal(DirectiveTokenKind.Attribute, token.Kind);
+ Assert.False(token.Optional);
+ Assert.Equal("Name", token.Name);
+ Assert.Equal("Description", token.Description);
+ }
+
+ [Fact]
+ public void AddOptionalAttributeToken_AddsToken()
+ {
+ // Arrange & Act
+ var descriptor = DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddOptionalAttributeToken());
+
+ // Assert
+ var token = Assert.Single(descriptor.Tokens);
+ Assert.Equal(DirectiveTokenKind.Attribute, token.Kind);
+ Assert.True(token.Optional);
+ Assert.Null(token.Name);
+ Assert.Null(token.Description);
+ }
}
}
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/CodeGenerationIntegrationTest.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/CodeGenerationIntegrationTest.cs
index 924079b1d9..22133be03f 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/CodeGenerationIntegrationTest.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/CodeGenerationIntegrationTest.cs
@@ -488,6 +488,12 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
RunTimeTest();
}
+ [Fact]
+ public void AttributeDirective_Runtime()
+ {
+ RunTimeTest();
+ }
+
#endregion
#region DesignTime
@@ -967,6 +973,12 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
DesignTimeTest();
}
+ [Fact]
+ public void AttributeDirective_DesignTime()
+ {
+ DesignTimeTest();
+ }
+
#endregion
private void DesignTimeTest()
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/Legacy/RazorDirectivesTest.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/Legacy/RazorDirectivesTest.cs
index 291331f915..946fea9b51 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/Legacy/RazorDirectivesTest.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/Legacy/RazorDirectivesTest.cs
@@ -906,5 +906,73 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
ParseDocumentTest("@{ class }");
}
+
+ [Fact]
+ public void DirectiveDescriptor_UnderstandsAttributeTokens()
+ {
+ // Arrange
+ var descriptor = DirectiveDescriptor.CreateDirective(
+ "custom",
+ DirectiveKind.SingleLine,
+ b => b.AddAttributeToken());
+
+ // Act & Assert
+ ParseDocumentTest(@"
+@custom [Serializable]
+@custom [DllImport(""user32.dll"", SetLastError=false, ExactSpelling=false)]
+",
+ new[] { descriptor });
+ }
+
+ [Fact]
+ public void DirectiveDescriptor_AttributeToken_BalancesBrackets()
+ {
+ // Arrange
+ var descriptor = DirectiveDescriptor.CreateDirective(
+ "custom",
+ DirectiveKind.SingleLine,
+ b => b.AddAttributeToken());
+
+ // Act & Assert
+ ParseDocumentTest(@"
+@custom [SomeCustom(new int[] { 1, 2, 3 }
+",
+ new[] { descriptor });
+ }
+
+ [Fact]
+ public void DirectiveDescriptor_MultilineAttributeToken_BalancesBrackets()
+ {
+ // Arrange
+ var descriptor = DirectiveDescriptor.CreateDirective(
+ "custom",
+ DirectiveKind.SingleLine,
+ b => b.AddAttributeToken());
+
+ // Act & Assert
+ ParseDocumentTest(@"
+@custom [SomeCustom(new int[]
+ {
+ 1,
+ 2,
+ 3
+ }]
+",
+ new[] { descriptor });
+ }
+
+ [Fact]
+ public void DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket()
+ {
+ // Arrange
+ var descriptor = DirectiveDescriptor.CreateDirective(
+ "custom",
+ DirectiveKind.SingleLine,
+ b => b.AddAttributeToken());
+
+ // Act & Assert
+ ParseDocumentTest("@custom Serializable]",
+ new[] { descriptor });
+ }
}
}
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/RazorProjectEngineTest.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/RazorProjectEngineTest.cs
index 96072a8cf1..76e7686fa1 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/RazorProjectEngineTest.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/RazorProjectEngineTest.cs
@@ -45,6 +45,7 @@ namespace Microsoft.AspNetCore.Razor.Language
var features = engine.EngineFeatures.OrderBy(f => f.GetType().Name).ToArray();
Assert.Collection(
features,
+ feature => Assert.IsType(feature),
feature => Assert.IsType(feature),
feature => Assert.IsType(feature),
feature => Assert.IsType(feature),
@@ -91,7 +92,8 @@ namespace Microsoft.AspNetCore.Razor.Language
directive => Assert.Same(FunctionsDirective.Directive, directive),
directive => Assert.Same(ImplementsDirective.Directive, directive),
directive => Assert.Same(InheritsDirective.Directive, directive),
- directive => Assert.Same(NamespaceDirective.Directive, directive));
+ directive => Assert.Same(NamespaceDirective.Directive, directive),
+ directive => Assert.Same(AttributeDirective.Directive, directive));
}
private static void AssertDefaultTargetExtensions(RazorProjectEngine engine)
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml
new file mode 100644
index 0000000000..638e221534
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml
@@ -0,0 +1,6 @@
+@attribute [System.Runtime.InteropServices.DllImport("user32.dll")]
+@attribute [assembly: AssemblyTitleAttribute("Some assembly")]
+@attribute [DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
+@attribute [Conditional("DEBUG"), Conditional("TEST1")]
+
+Hello World
\ No newline at end of file
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_DesignTime.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_DesignTime.codegen.cs
new file mode 100644
index 0000000000..a9a63af516
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_DesignTime.codegen.cs
@@ -0,0 +1,50 @@
+//
+#pragma warning disable 1591
+namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
+{
+ #line hidden
+#nullable restore
+#line 1 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml"
+ [System.Runtime.InteropServices.DllImport("user32.dll")]
+
+#line default
+#line hidden
+#nullable disable
+#nullable restore
+#line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml"
+ [assembly: AssemblyTitleAttribute("Some assembly")]
+
+#line default
+#line hidden
+#nullable disable
+#nullable restore
+#line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml"
+ [DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
+
+#line default
+#line hidden
+#nullable disable
+#nullable restore
+#line 4 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml"
+ [Conditional("DEBUG"), Conditional("TEST1")]
+
+#line default
+#line hidden
+#nullable disable
+ public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_AttributeDirective_DesignTime
+ {
+ #pragma warning disable 219
+ private void __RazorDirectiveTokenHelpers__() {
+ }
+ #pragma warning restore 219
+ #pragma warning disable 0414
+ private static System.Object __o = null;
+ #pragma warning restore 0414
+ #pragma warning disable 1998
+ public async System.Threading.Tasks.Task ExecuteAsync()
+ {
+ }
+ #pragma warning restore 1998
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_DesignTime.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_DesignTime.ir.txt
new file mode 100644
index 0000000000..143b299051
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_DesignTime.ir.txt
@@ -0,0 +1,25 @@
+Document -
+ NamespaceDeclaration - - Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
+ CSharpCode - (11:0,11 [56] AttributeDirective.cshtml)
+ IntermediateToken - (11:0,11 [56] AttributeDirective.cshtml) - CSharp - [System.Runtime.InteropServices.DllImport("user32.dll")]
+ CSharpCode - (80:1,11 [51] AttributeDirective.cshtml)
+ IntermediateToken - (80:1,11 [51] AttributeDirective.cshtml) - CSharp - [assembly: AssemblyTitleAttribute("Some assembly")]
+ CSharpCode - (144:2,11 [66] AttributeDirective.cshtml)
+ IntermediateToken - (144:2,11 [66] AttributeDirective.cshtml) - CSharp - [DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
+ CSharpCode - (223:3,11 [44] AttributeDirective.cshtml)
+ IntermediateToken - (223:3,11 [44] AttributeDirective.cshtml) - CSharp - [Conditional("DEBUG"), Conditional("TEST1")]
+ ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_AttributeDirective_DesignTime - -
+ DesignTimeDirective -
+ DirectiveToken - (11:0,11 [56] AttributeDirective.cshtml) - [System.Runtime.InteropServices.DllImport("user32.dll")]
+ DirectiveToken - (80:1,11 [51] AttributeDirective.cshtml) - [assembly: AssemblyTitleAttribute("Some assembly")]
+ DirectiveToken - (144:2,11 [66] AttributeDirective.cshtml) - [DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
+ DirectiveToken - (223:3,11 [44] AttributeDirective.cshtml) - [Conditional("DEBUG"), Conditional("TEST1")]
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning disable 0414
+ CSharpCode -
+ IntermediateToken - - CSharp - private static System.Object __o = null;
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning restore 0414
+ MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync
+ HtmlContent - (269:4,0 [13] AttributeDirective.cshtml)
+ IntermediateToken - (269:4,0 [13] AttributeDirective.cshtml) - Html - \nHello World
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_DesignTime.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_DesignTime.mappings.txt
new file mode 100644
index 0000000000..c76e0fc08c
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_DesignTime.mappings.txt
@@ -0,0 +1,20 @@
+Source Location: (11:0,11 [56] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml)
+|[System.Runtime.InteropServices.DllImport("user32.dll")]|
+Generated Location: (271:7,11 [56] )
+|[System.Runtime.InteropServices.DllImport("user32.dll")]|
+
+Source Location: (80:1,11 [51] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml)
+|[assembly: AssemblyTitleAttribute("Some assembly")]|
+Generated Location: (503:14,11 [51] )
+|[assembly: AssemblyTitleAttribute("Some assembly")]|
+
+Source Location: (144:2,11 [66] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml)
+|[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]|
+Generated Location: (730:21,11 [66] )
+|[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]|
+
+Source Location: (223:3,11 [44] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml)
+|[Conditional("DEBUG"), Conditional("TEST1")]|
+Generated Location: (972:28,11 [44] )
+|[Conditional("DEBUG"), Conditional("TEST1")]|
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_Runtime.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_Runtime.codegen.cs
new file mode 100644
index 0000000000..ec883320dd
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_Runtime.codegen.cs
@@ -0,0 +1,47 @@
+#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "796e795e99f31ccb6c596e30f8540f5de179132a"
+//
+#pragma warning disable 1591
+[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_AttributeDirective_Runtime), @"default", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml")]
+namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
+{
+ #line hidden
+#nullable restore
+#line 1 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml"
+ [System.Runtime.InteropServices.DllImport("user32.dll")]
+
+#line default
+#line hidden
+#nullable disable
+#nullable restore
+#line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml"
+ [assembly: AssemblyTitleAttribute("Some assembly")]
+
+#line default
+#line hidden
+#nullable disable
+#nullable restore
+#line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml"
+ [DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
+
+#line default
+#line hidden
+#nullable disable
+#nullable restore
+#line 4 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml"
+ [Conditional("DEBUG"), Conditional("TEST1")]
+
+#line default
+#line hidden
+#nullable disable
+ [global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"796e795e99f31ccb6c596e30f8540f5de179132a", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective.cshtml")]
+ public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_AttributeDirective_Runtime
+ {
+ #pragma warning disable 1998
+ public async System.Threading.Tasks.Task ExecuteAsync()
+ {
+ WriteLiteral("\r\nHello World");
+ }
+ #pragma warning restore 1998
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_Runtime.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_Runtime.ir.txt
new file mode 100644
index 0000000000..98f79e9671
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/AttributeDirective_Runtime.ir.txt
@@ -0,0 +1,16 @@
+Document -
+ RazorCompiledItemAttribute -
+ NamespaceDeclaration - - Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
+ CSharpCode - (11:0,11 [56] AttributeDirective.cshtml)
+ IntermediateToken - (11:0,11 [56] AttributeDirective.cshtml) - CSharp - [System.Runtime.InteropServices.DllImport("user32.dll")]
+ CSharpCode - (80:1,11 [51] AttributeDirective.cshtml)
+ IntermediateToken - (80:1,11 [51] AttributeDirective.cshtml) - CSharp - [assembly: AssemblyTitleAttribute("Some assembly")]
+ CSharpCode - (144:2,11 [66] AttributeDirective.cshtml)
+ IntermediateToken - (144:2,11 [66] AttributeDirective.cshtml) - CSharp - [DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
+ CSharpCode - (223:3,11 [44] AttributeDirective.cshtml)
+ IntermediateToken - (223:3,11 [44] AttributeDirective.cshtml) - CSharp - [Conditional("DEBUG"), Conditional("TEST1")]
+ RazorSourceChecksumAttribute -
+ ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_AttributeDirective_Runtime - -
+ MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync
+ HtmlContent - (269:4,0 [13] AttributeDirective.cshtml)
+ IntermediateToken - (269:4,0 [13] AttributeDirective.cshtml) - Html - \nHello World
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.cspans.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.cspans.txt
new file mode 100644
index 0000000000..a9da33c82f
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.cspans.txt
@@ -0,0 +1,6 @@
+Markup span at (0:0,0 [2] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [45] )
+Transition span at (2:1,0 [1] ) (Accepts:None) - Parent: Directive block at (2:1,0 [43] )
+MetaCode span at (3:1,1 [6] ) (Accepts:None) - Parent: Directive block at (2:1,0 [43] )
+Code span at (9:1,7 [1] ) (Accepts:Whitespace) - Parent: Directive block at (2:1,0 [43] )
+Code span at (10:1,8 [35] ) (Accepts:NonWhitespace) - Parent: Directive block at (2:1,0 [43] )
+Markup span at (45:2,0 [0] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [45] )
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.stree.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.stree.txt
new file mode 100644
index 0000000000..1bd3280f63
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.stree.txt
@@ -0,0 +1,38 @@
+RazorDocument - [0..45)::45 - [LF@custom [SomeCustom(new int[] { 1, 2, 3 }LF]
+ MarkupBlock - [0..45)::45
+ MarkupTextLiteral - [0..2)::2 - [LF] - Gen - SpanEditHandler;Accepts:Any
+ NewLine;[LF];
+ CSharpCodeBlock - [2..45)::43
+ RazorDirective - [2..45)::43 - Directive:{custom;SingleLine;Unrestricted}
+ CSharpTransition - [2..3)::1 - Gen - SpanEditHandler;Accepts:None
+ Transition;[@];
+ RazorDirectiveBody - [3..45)::42
+ RazorMetaCode - [3..9)::6 - Gen - SpanEditHandler;Accepts:None
+ Identifier;[custom];
+ CSharpCodeBlock - [9..45)::36
+ CSharpStatementLiteral - [9..10)::1 - [ ] - Gen - SpanEditHandler;Accepts:Whitespace
+ Whitespace;[ ];
+ CSharpStatementLiteral - [10..45)::35 - [[SomeCustom(new int[] { 1, 2, 3 }LF] - Gen - DirectiveTokenEditHandler;Accepts:NonWhitespace
+ LeftBracket;[[];
+ Identifier;[SomeCustom];
+ LeftParenthesis;[(];
+ Keyword;[new];
+ Whitespace;[ ];
+ Keyword;[int];
+ LeftBracket;[[];
+ RightBracket;[]];
+ Whitespace;[ ];
+ LeftBrace;[{];
+ Whitespace;[ ];
+ IntegerLiteral;[1];
+ Comma;[,];
+ Whitespace;[ ];
+ IntegerLiteral;[2];
+ Comma;[,];
+ Whitespace;[ ];
+ IntegerLiteral;[3];
+ Whitespace;[ ];
+ RightBrace;[}];
+ NewLine;[LF];
+ MarkupTextLiteral - [45..45)::0 - [] - Gen - SpanEditHandler;Accepts:Any
+ Marker;[];
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket.cspans.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket.cspans.txt
new file mode 100644
index 0000000000..96e5261815
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket.cspans.txt
@@ -0,0 +1,5 @@
+Markup span at (0:0,0 [0] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [21] )
+Transition span at (0:0,0 [1] ) (Accepts:None) - Parent: Directive block at (0:0,0 [8] )
+MetaCode span at (1:0,1 [6] ) (Accepts:None) - Parent: Directive block at (0:0,0 [8] )
+Code span at (7:0,7 [1] ) (Accepts:Whitespace) - Parent: Directive block at (0:0,0 [8] )
+Markup span at (8:0,8 [13] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [21] )
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket.diag.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket.diag.txt
new file mode 100644
index 0000000000..46532eb8d0
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket.diag.txt
@@ -0,0 +1 @@
+(1,9): Error RZ1037: The 'custom' directive expects a C# attribute.
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket.stree.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket.stree.txt
new file mode 100644
index 0000000000..381265396e
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket.stree.txt
@@ -0,0 +1,17 @@
+RazorDocument - [0..21)::21 - [@custom Serializable]]
+ MarkupBlock - [0..21)::21
+ MarkupTextLiteral - [0..0)::0 - [] - Gen - SpanEditHandler;Accepts:Any
+ Marker;[];
+ CSharpCodeBlock - [0..8)::8
+ RazorDirective - [0..8)::8 - Directive:{custom;SingleLine;Unrestricted} [RZ1037(8:0,8 [12] )]
+ CSharpTransition - [0..1)::1 - Gen - SpanEditHandler;Accepts:None
+ Transition;[@];
+ RazorDirectiveBody - [1..8)::7
+ RazorMetaCode - [1..7)::6 - Gen - SpanEditHandler;Accepts:None
+ Identifier;[custom];
+ CSharpCodeBlock - [7..8)::1
+ CSharpStatementLiteral - [7..8)::1 - [ ] - Gen - SpanEditHandler;Accepts:Whitespace
+ Whitespace;[ ];
+ MarkupTextLiteral - [8..21)::13 - [Serializable]] - Gen - SpanEditHandler;Accepts:Any
+ Text;[Serializable];
+ RightBracket;[]];
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_MultilineAttributeToken_BalancesBrackets.cspans.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_MultilineAttributeToken_BalancesBrackets.cspans.txt
new file mode 100644
index 0000000000..af411e2d6a
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_MultilineAttributeToken_BalancesBrackets.cspans.txt
@@ -0,0 +1,7 @@
+Markup span at (0:0,0 [2] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [83] )
+Transition span at (2:1,0 [1] ) (Accepts:None) - Parent: Directive block at (2:1,0 [81] )
+MetaCode span at (3:1,1 [6] ) (Accepts:None) - Parent: Directive block at (2:1,0 [81] )
+Code span at (9:1,7 [1] ) (Accepts:Whitespace) - Parent: Directive block at (2:1,0 [81] )
+Code span at (10:1,8 [71] ) (Accepts:NonWhitespace) - Parent: Directive block at (2:1,0 [81] )
+Markup span at (81:6,6 [2] ) (Accepts:Whitespace) - Parent: Directive block at (2:1,0 [81] )
+Markup span at (83:7,0 [0] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [83] )
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_MultilineAttributeToken_BalancesBrackets.stree.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_MultilineAttributeToken_BalancesBrackets.stree.txt
new file mode 100644
index 0000000000..e2b43fc967
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_MultilineAttributeToken_BalancesBrackets.stree.txt
@@ -0,0 +1,45 @@
+RazorDocument - [0..83)::83 - [LF@custom [SomeCustom(new int[]LF {LF 1,LF 2,LF 3LF }]LF]
+ MarkupBlock - [0..83)::83
+ MarkupTextLiteral - [0..2)::2 - [LF] - Gen - SpanEditHandler;Accepts:Any
+ NewLine;[LF];
+ CSharpCodeBlock - [2..83)::81
+ RazorDirective - [2..83)::81 - Directive:{custom;SingleLine;Unrestricted}
+ CSharpTransition - [2..3)::1 - Gen - SpanEditHandler;Accepts:None
+ Transition;[@];
+ RazorDirectiveBody - [3..83)::80
+ RazorMetaCode - [3..9)::6 - Gen - SpanEditHandler;Accepts:None
+ Identifier;[custom];
+ CSharpCodeBlock - [9..83)::74
+ CSharpStatementLiteral - [9..10)::1 - [ ] - Gen - SpanEditHandler;Accepts:Whitespace
+ Whitespace;[ ];
+ CSharpStatementLiteral - [10..81)::71 - [[SomeCustom(new int[]LF {LF 1,LF 2,LF 3LF }]] - Gen - DirectiveTokenEditHandler;Accepts:NonWhitespace
+ LeftBracket;[[];
+ Identifier;[SomeCustom];
+ LeftParenthesis;[(];
+ Keyword;[new];
+ Whitespace;[ ];
+ Keyword;[int];
+ LeftBracket;[[];
+ RightBracket;[]];
+ NewLine;[LF];
+ Whitespace;[ ];
+ LeftBrace;[{];
+ NewLine;[LF];
+ Whitespace;[ ];
+ IntegerLiteral;[1];
+ Comma;[,];
+ NewLine;[LF];
+ Whitespace;[ ];
+ IntegerLiteral;[2];
+ Comma;[,];
+ NewLine;[LF];
+ Whitespace;[ ];
+ IntegerLiteral;[3];
+ NewLine;[LF];
+ Whitespace;[ ];
+ RightBrace;[}];
+ RightBracket;[]];
+ MarkupEphemeralTextLiteral - [81..83)::2 - [LF] - Gen - SpanEditHandler;Accepts:Whitespace
+ NewLine;[LF];
+ MarkupTextLiteral - [83..83)::0 - [] - Gen - SpanEditHandler;Accepts:Any
+ Marker;[];
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_UnderstandsAttributeTokens.cspans.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_UnderstandsAttributeTokens.cspans.txt
new file mode 100644
index 0000000000..7911e989b0
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_UnderstandsAttributeTokens.cspans.txt
@@ -0,0 +1,13 @@
+Markup span at (0:0,0 [2] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [102] )
+Transition span at (2:1,0 [1] ) (Accepts:None) - Parent: Directive block at (2:1,0 [24] )
+MetaCode span at (3:1,1 [6] ) (Accepts:None) - Parent: Directive block at (2:1,0 [24] )
+Code span at (9:1,7 [1] ) (Accepts:Whitespace) - Parent: Directive block at (2:1,0 [24] )
+Code span at (10:1,8 [14] ) (Accepts:NonWhitespace) - Parent: Directive block at (2:1,0 [24] )
+Markup span at (24:1,22 [2] ) (Accepts:Whitespace) - Parent: Directive block at (2:1,0 [24] )
+Markup span at (26:2,0 [0] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [102] )
+Transition span at (26:2,0 [1] ) (Accepts:None) - Parent: Directive block at (26:2,0 [76] )
+MetaCode span at (27:2,1 [6] ) (Accepts:None) - Parent: Directive block at (26:2,0 [76] )
+Code span at (33:2,7 [1] ) (Accepts:Whitespace) - Parent: Directive block at (26:2,0 [76] )
+Code span at (34:2,8 [66] ) (Accepts:NonWhitespace) - Parent: Directive block at (26:2,0 [76] )
+Markup span at (100:2,74 [2] ) (Accepts:Whitespace) - Parent: Directive block at (26:2,0 [76] )
+Markup span at (102:3,0 [0] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [102] )
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_UnderstandsAttributeTokens.stree.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_UnderstandsAttributeTokens.stree.txt
new file mode 100644
index 0000000000..8ae8662dd6
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_UnderstandsAttributeTokens.stree.txt
@@ -0,0 +1,53 @@
+RazorDocument - [0..102)::102 - [LF@custom [Serializable]LF@custom [DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]LF]
+ MarkupBlock - [0..102)::102
+ MarkupTextLiteral - [0..2)::2 - [LF] - Gen - SpanEditHandler;Accepts:Any
+ NewLine;[LF];
+ CSharpCodeBlock - [2..26)::24
+ RazorDirective - [2..26)::24 - Directive:{custom;SingleLine;Unrestricted}
+ CSharpTransition - [2..3)::1 - Gen - SpanEditHandler;Accepts:None
+ Transition;[@];
+ RazorDirectiveBody - [3..26)::23
+ RazorMetaCode - [3..9)::6 - Gen - SpanEditHandler;Accepts:None
+ Identifier;[custom];
+ CSharpCodeBlock - [9..26)::17
+ CSharpStatementLiteral - [9..10)::1 - [ ] - Gen - SpanEditHandler;Accepts:Whitespace
+ Whitespace;[ ];
+ CSharpStatementLiteral - [10..24)::14 - [[Serializable]] - Gen - DirectiveTokenEditHandler;Accepts:NonWhitespace
+ LeftBracket;[[];
+ Identifier;[Serializable];
+ RightBracket;[]];
+ MarkupEphemeralTextLiteral - [24..26)::2 - [LF] - Gen - SpanEditHandler;Accepts:Whitespace
+ NewLine;[LF];
+ MarkupTextLiteral - [26..26)::0 - [] - Gen - SpanEditHandler;Accepts:Any
+ Marker;[];
+ CSharpCodeBlock - [26..102)::76
+ RazorDirective - [26..102)::76 - Directive:{custom;SingleLine;Unrestricted}
+ CSharpTransition - [26..27)::1 - Gen - SpanEditHandler;Accepts:None
+ Transition;[@];
+ RazorDirectiveBody - [27..102)::75
+ RazorMetaCode - [27..33)::6 - Gen - SpanEditHandler;Accepts:None
+ Identifier;[custom];
+ CSharpCodeBlock - [33..102)::69
+ CSharpStatementLiteral - [33..34)::1 - [ ] - Gen - SpanEditHandler;Accepts:Whitespace
+ Whitespace;[ ];
+ CSharpStatementLiteral - [34..100)::66 - [[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]] - Gen - DirectiveTokenEditHandler;Accepts:NonWhitespace
+ LeftBracket;[[];
+ Identifier;[DllImport];
+ LeftParenthesis;[(];
+ StringLiteral;["user32.dll"];
+ Comma;[,];
+ Whitespace;[ ];
+ Identifier;[SetLastError];
+ Assign;[=];
+ Keyword;[false];
+ Comma;[,];
+ Whitespace;[ ];
+ Identifier;[ExactSpelling];
+ Assign;[=];
+ Keyword;[false];
+ RightParenthesis;[)];
+ RightBracket;[]];
+ MarkupEphemeralTextLiteral - [100..102)::2 - [LF] - Gen - SpanEditHandler;Accepts:Whitespace
+ NewLine;[LF];
+ MarkupTextLiteral - [102..102)::0 - [] - Gen - SpanEditHandler;Accepts:Any
+ Marker;[];