From 5504374125290d419d000293b66a98a329d31bb6 Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Mon, 9 Apr 2018 13:21:43 -0700 Subject: [PATCH] Preserve directive node diagnostics in document node --- .../RazorPageDocumentClassifierPass.cs | 4 +- .../DirectiveRemovalOptimizationPass.cs | 6 +++ .../RazorPageDocumentClassifierPassTest.cs | 10 +++-- .../DirectiveRemovalOptimizationPassTest.cs | 41 +++++++++++++++++++ 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs index b7721addc1..daf8e0e606 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs @@ -116,9 +116,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions if (!PageDirective.TryGetPageDirective(leadingDirectiveDocumentNode, out var _)) { // The page directive is not the leading directive. Add an error. - // Note: Adding the error to the top-level document node because the directive node will be removed by a later optimization pass. - var originalDocumentNode = codeDocument.GetDocumentIntermediateNode(); - originalDocumentNode.Diagnostics.Add( + pageDirective.DirectiveNode.Diagnostics.Add( RazorExtensionsDiagnosticFactory.CreatePageDirective_MustExistAtTheTopOfFile(pageDirective.DirectiveNode.Source.Value)); } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/DirectiveRemovalOptimizationPass.cs b/src/Microsoft.AspNetCore.Razor.Language/DirectiveRemovalOptimizationPass.cs index 09dc4f788a..5b7e136d3b 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/DirectiveRemovalOptimizationPass.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/DirectiveRemovalOptimizationPass.cs @@ -17,6 +17,12 @@ namespace Microsoft.AspNetCore.Razor.Language foreach (var nodeReference in visitor.DirectiveNodes) { + // Lift the diagnostics in the directive node up to the document node. + for (var i = 0; i < nodeReference.Node.Diagnostics.Count; i++) + { + documentNode.Diagnostics.Add(nodeReference.Node.Diagnostics[i]); + } + nodeReference.Remove(); } } diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/RazorPageDocumentClassifierPassTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/RazorPageDocumentClassifierPassTest.cs index 526bf68a88..0ba9dd5178 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/RazorPageDocumentClassifierPassTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/RazorPageDocumentClassifierPassTest.cs @@ -68,8 +68,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions visitor.Visit(irDocument); // Assert - var documentNode = codeDocument.GetDocumentIntermediateNode(); - var diagnostic = Assert.Single(documentNode.Diagnostics); + var pageDirectives = irDocument.FindDirectiveReferences(PageDirective.Directive); + var directive = Assert.Single(pageDirectives); + var diagnostic = Assert.Single(directive.Node.Diagnostics); Assert.Equal(expectedDiagnostic, diagnostic); } @@ -97,8 +98,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions visitor.Visit(irDocument); // Assert - var documentNode = codeDocument.GetDocumentIntermediateNode(); - Assert.Empty(documentNode.Diagnostics); + var pageDirectives = irDocument.FindDirectiveReferences(PageDirective.Directive); + var directive = Assert.Single(pageDirectives); + Assert.Empty(directive.Node.Diagnostics); } [Fact] diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/DirectiveRemovalOptimizationPassTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/DirectiveRemovalOptimizationPassTest.cs index f8b415709a..cb8984f84e 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/DirectiveRemovalOptimizationPassTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/DirectiveRemovalOptimizationPassTest.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.Linq; using Microsoft.AspNetCore.Razor.Language.Intermediate; using Xunit; using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert; @@ -72,6 +73,46 @@ namespace Microsoft.AspNetCore.Razor.Language Assert.Empty(method.Children); } + [Fact] + public void Execute_DirectiveWithError_PreservesDiagnosticsAndRemovesDirectiveNodeFromDocument() + { + // Arrange + var content = "@custom \"Hello\""; + var expectedDiagnostic = RazorDiagnostic.Create(new RazorDiagnosticDescriptor("RZ9999", () => "Some diagnostic message.", RazorDiagnosticSeverity.Error), SourceSpan.Undefined); + var sourceDocument = TestRazorSourceDocument.Create(content); + var codeDocument = RazorCodeDocument.Create(sourceDocument); + var defaultEngine = RazorEngine.Create(b => + { + b.AddDirective(DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, d => d.AddStringToken())); + }); + var documentNode = Lower(codeDocument, defaultEngine); + + // Add the diagnostic to the directive node. + var directiveNode = documentNode.FindDescendantNodes().Single(); + directiveNode.Diagnostics.Add(expectedDiagnostic); + + var pass = new DirectiveRemovalOptimizationPass() + { + Engine = defaultEngine, + }; + + // Act + pass.Execute(codeDocument, documentNode); + + // Assert + var diagnostic = Assert.Single(documentNode.Diagnostics); + Assert.Equal(expectedDiagnostic, diagnostic); + + Children(documentNode, + node => Assert.IsType(node)); + var @namespace = documentNode.Children[0]; + Children(@namespace, + node => Assert.IsType(node)); + var @class = @namespace.Children[0]; + var method = SingleChild(@class); + Assert.Empty(method.Children); + } + private static DocumentIntermediateNode Lower(RazorCodeDocument codeDocument, RazorEngine engine) { for (var i = 0; i < engine.Phases.Count; i++)