diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/PageDirective.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/PageDirective.cs index 6c58b9b8e0..2044117e25 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/PageDirective.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/PageDirective.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.AspNetCore.Razor.Language; @@ -20,13 +21,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions builder.Usage = DirectiveUsage.FileScopedSinglyOccurring; }); - private PageDirective(string routeTemplate) + private PageDirective(string routeTemplate, IntermediateNode directiveNode) { RouteTemplate = routeTemplate; + DirectiveNode = directiveNode; } public string RouteTemplate { get; } + public IntermediateNode DirectiveNode { get; } + public static IRazorEngineBuilder Register(IRazorEngineBuilder builder) { builder.AddDirective(Directive); @@ -41,25 +45,27 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions visitor.Visit(documentNode.Children[i]); } - if (visitor.DirectiveNode == null) + if (visitor.DirectiveTokens == null) { pageDirective = null; return false; } - var tokens = visitor.DirectiveNode.Tokens.ToList(); + var tokens = visitor.DirectiveTokens.ToList(); string routeTemplate = null; if (tokens.Count > 0) { routeTemplate = TrimQuotes(tokens[0].Content); } - pageDirective = new PageDirective(routeTemplate); + pageDirective = new PageDirective(routeTemplate, visitor.DirectiveNode); return true; } private static string TrimQuotes(string content) { + // Tokens aren't captured if they're malformed. Therefore, this method will + // always be called with a valid token content. Debug.Assert(content.Length >= 2); Debug.Assert(content.StartsWith("\"", StringComparison.Ordinal)); Debug.Assert(content.EndsWith("\"", StringComparison.Ordinal)); @@ -69,13 +75,25 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions private class Visitor : IntermediateNodeWalker { - public DirectiveIntermediateNode DirectiveNode { get; private set; } + public IntermediateNode DirectiveNode { get; private set; } + + public IEnumerable DirectiveTokens { get; private set; } public override void VisitDirective(DirectiveIntermediateNode node) { if (node.Descriptor == Directive) { DirectiveNode = node; + DirectiveTokens = node.Tokens; + } + } + + public override void VisitMalformedDirective(MalformedDirectiveIntermediateNode node) + { + if (DirectiveTokens == null && node.Descriptor == Directive) + { + DirectiveNode = node; + DirectiveTokens = node.Tokens; } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs index 90ea67329e..6e4131dd54 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs @@ -1,7 +1,7 @@ // 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; +using System.Diagnostics; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Intermediate; @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode) { - return PageDirective.TryGetPageDirective(documentNode, out var directive); + return PageDirective.TryGetPageDirective(documentNode, out var pageDirective); } protected override void OnDocumentStructureCreated( @@ -49,26 +49,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions private void EnsureValidPageDirective(RazorCodeDocument codeDocument) { var document = codeDocument.GetDocumentIntermediateNode(); - var visitor = new Visitor(); - visitor.VisitDocument(document); + PageDirective.TryGetPageDirective(document, out var pageDirective); - if (visitor.DirectiveNode.IsImported()) + Debug.Assert(pageDirective != null); + + if (pageDirective.DirectiveNode.IsImported()) { - visitor.DirectiveNode.Diagnostics.Add( - RazorExtensionsDiagnosticFactory.CreatePageDirective_CannotBeImported(visitor.DirectiveNode.Source.Value)); - } - } - - private class Visitor : IntermediateNodeWalker - { - public DirectiveIntermediateNode DirectiveNode { get; private set; } - - public override void VisitDirective(DirectiveIntermediateNode node) - { - if (node.Descriptor == PageDirective.Directive) - { - DirectiveNode = node; - } + pageDirective.DirectiveNode.Diagnostics.Add( + RazorExtensionsDiagnosticFactory.CreatePageDirective_CannotBeImported(pageDirective.DirectiveNode.Source.Value)); } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/PageDirectiveTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/PageDirectiveTest.cs index 19c27dca79..1c6e065e14 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/PageDirectiveTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/PageDirectiveTest.cs @@ -10,6 +10,25 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions { public class PageDirectiveTest { + [Fact] + public void TryGetPageDirective_ReturnsTrue_IfPageIsMalformed() + { + // Arrange + var content = "@page \"some-route-template\" Invalid"; + var sourceDocument = RazorSourceDocument.Create(content, "file"); + var codeDocument = RazorCodeDocument.Create(sourceDocument); + var engine = CreateEngine(); + var irDocument = CreateIRDocument(engine, codeDocument); + + // Act + var result = PageDirective.TryGetPageDirective(irDocument, out var pageDirective); + + // Assert + Assert.True(result); + Assert.Equal("some-route-template", pageDirective.RouteTemplate); + Assert.NotNull(pageDirective.DirectiveNode); + } + [Fact] public void TryGetPageDirective_ReturnsTrue_IfPageIsImported() { @@ -48,7 +67,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions } [Fact] - public void TryGetPageDirective_ReturnsFalse_IfPageDoesStartWithDirective() + public void TryGetPageDirective_ReturnsTrue_IfPageDoesStartWithDirective() { // Arrange var content = "Hello @page"; @@ -61,8 +80,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions var result = PageDirective.TryGetPageDirective(irDocument, out var pageDirective); // Assert - Assert.False(result); - Assert.Null(pageDirective); + Assert.True(result); + Assert.Null(pageDirective.RouteTemplate); + Assert.NotNull(pageDirective.DirectiveNode); } [Fact] diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_DesignTime.codegen.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_DesignTime.codegen.cs index 01801dc206..8c60cab876 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_DesignTime.codegen.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_DesignTime.codegen.cs @@ -1,6 +1,6 @@ // #pragma warning disable 1591 -[assembly:global::Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorViewAttribute(null, typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml))] +[assembly:global::Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.RazorPageAttribute(null, typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml), null)] namespace AspNetCore { #line hidden @@ -12,7 +12,7 @@ namespace AspNetCore using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; - public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage + public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml : global::Microsoft.AspNetCore.Mvc.RazorPages.Page { #pragma warning disable 219 private void __RazorDirectiveTokenHelpers__() { @@ -35,7 +35,9 @@ namespace AspNetCore [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; } [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] - public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html { get; private set; } + public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html { get; private set; } + public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary ViewData => (global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary)PageContext?.ViewData; + public TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml Model => ViewData.Model; } } #pragma warning restore 1591 diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_DesignTime.ir.txt b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_DesignTime.ir.txt index ab654bd13c..8a72021fa5 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_DesignTime.ir.txt +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_DesignTime.ir.txt @@ -1,6 +1,6 @@ Document - CSharpCode - - IntermediateToken - - CSharp - [assembly:global::Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorViewAttribute(null, typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml))] + IntermediateToken - - CSharp - [assembly:global::Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.RazorPageAttribute(null, typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml), null)] NamespaceDeclaration - - AspNetCore UsingDirective - - TModel = global::System.Object UsingDirective - (1:0,1 [12] ) - System @@ -10,7 +10,7 @@ Document - UsingDirective - (102:4,1 [30] ) - Microsoft.AspNetCore.Mvc UsingDirective - (135:5,1 [40] ) - Microsoft.AspNetCore.Mvc.Rendering UsingDirective - (178:6,1 [43] ) - Microsoft.AspNetCore.Mvc.ViewFeatures - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml - global::Microsoft.AspNetCore.Mvc.Razor.RazorPage - + ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml - global::Microsoft.AspNetCore.Mvc.RazorPages.Page - DesignTimeDirective - DirectiveToken - (231:7,8 [62] ) - global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper DirectiveToken - (294:7,71 [4] ) - Html @@ -47,3 +47,7 @@ Document - Inject - Inject - Inject - + CSharpCode - + IntermediateToken - - CSharp - public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary ViewData => (global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary)PageContext?.ViewData; + CSharpCode - + IntermediateToken - - CSharp - public TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml Model => ViewData.Model; diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_Runtime.codegen.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_Runtime.codegen.cs index 5e96321408..1ce4676fc9 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_Runtime.codegen.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_Runtime.codegen.cs @@ -1,7 +1,7 @@ #pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "5a9ff8440150c6746e4a8ba63bc633ea84930405" // #pragma warning disable 1591 -[assembly:global::Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorViewAttribute(null, typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml))] +[assembly:global::Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.RazorPageAttribute(null, typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml), null)] namespace AspNetCore { #line hidden @@ -12,7 +12,7 @@ namespace AspNetCore using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; - public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage + public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml : global::Microsoft.AspNetCore.Mvc.RazorPages.Page { #pragma warning disable 1998 public async override global::System.Threading.Tasks.Task ExecuteAsync() @@ -31,7 +31,9 @@ namespace AspNetCore [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; } [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] - public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html { get; private set; } + public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html { get; private set; } + public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary ViewData => (global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary)PageContext?.ViewData; + public TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml Model => ViewData.Model; } } #pragma warning restore 1591 diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_Runtime.ir.txt b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_Runtime.ir.txt index 4ffdea43ac..8acaa03a0d 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_Runtime.ir.txt +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MalformedPageDirective_Runtime.ir.txt @@ -1,6 +1,6 @@ Document - CSharpCode - - IntermediateToken - - CSharp - [assembly:global::Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorViewAttribute(null, typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml))] + IntermediateToken - - CSharp - [assembly:global::Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.RazorPageAttribute(null, typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml), null)] NamespaceDeclaration - - AspNetCore UsingDirective - (1:0,1 [14] ) - System UsingDirective - (16:1,1 [34] ) - System.Collections.Generic @@ -9,7 +9,7 @@ Document - UsingDirective - (102:4,1 [32] ) - Microsoft.AspNetCore.Mvc UsingDirective - (135:5,1 [42] ) - Microsoft.AspNetCore.Mvc.Rendering UsingDirective - (178:6,1 [45] ) - Microsoft.AspNetCore.Mvc.ViewFeatures - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml - global::Microsoft.AspNetCore.Mvc.Razor.RazorPage - + ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml - global::Microsoft.AspNetCore.Mvc.RazorPages.Page - MethodDeclaration - - public async override - global::System.Threading.Tasks.Task - ExecuteAsync MalformedDirective - (0:0,0 [6] MalformedPageDirective.cshtml) - page CSharpCode - @@ -30,3 +30,7 @@ Document - Inject - Inject - Inject - + CSharpCode - + IntermediateToken - - CSharp - public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary ViewData => (global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary)PageContext?.ViewData; + CSharpCode - + IntermediateToken - - CSharp - public TestFiles_IntegrationTests_CodeGenerationIntegrationTest_MalformedPageDirective_cshtml Model => ViewData.Model;