Capture exceptions when trying to parse files and return parse errors.
#808
This commit is contained in:
parent
23a6604b9a
commit
82c9c40709
|
|
@ -1562,6 +1562,22 @@ namespace Microsoft.AspNetCore.Razor
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_IncompleteQuotesAroundDirective"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A fatal exception occurred when trying to parse '{0}':{1}{2}
|
||||
/// </summary>
|
||||
internal static string FatalException
|
||||
{
|
||||
get { return GetString("FatalException"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A fatal exception occurred when trying to parse '{0}':{1}{2}
|
||||
/// </summary>
|
||||
internal static string FormatFatalException(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("FatalException"), p0, p1, p2);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -428,4 +428,7 @@ Instead, wrap the contents of the block in "{{}}":
|
|||
<data name="ParseError_IncompleteQuotesAroundDirective" xml:space="preserve">
|
||||
<value>Optional quote around the directive '{0}' is missing the corresponding opening or closing quote.</value>
|
||||
</data>
|
||||
<data name="FatalException" xml:space="preserve">
|
||||
<value>A fatal exception occurred when trying to parse '{0}':{1}{2}</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -2,14 +2,19 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Razor.Chunks;
|
||||
using Microsoft.AspNetCore.Razor.Chunks.Generators;
|
||||
using Microsoft.AspNetCore.Razor.CodeGenerators;
|
||||
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
|
||||
using Microsoft.AspNetCore.Razor.Parser;
|
||||
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNetCore.Razor.Text;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor
|
||||
|
|
@ -363,26 +368,50 @@ namespace Microsoft.AspNetCore.Razor
|
|||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
className = (className ?? Host.DefaultClassName) ?? DefaultClassName;
|
||||
rootNamespace = (rootNamespace ?? Host.DefaultNamespace) ?? DefaultNamespace;
|
||||
try
|
||||
{
|
||||
className = (className ?? Host.DefaultClassName) ?? DefaultClassName;
|
||||
rootNamespace = (rootNamespace ?? Host.DefaultNamespace) ?? DefaultNamespace;
|
||||
|
||||
// Run the parser
|
||||
var parser = CreateParser(sourceFileName);
|
||||
Debug.Assert(parser != null);
|
||||
var results = parser.Parse(input);
|
||||
// Run the parser
|
||||
var parser = CreateParser(sourceFileName);
|
||||
Debug.Assert(parser != null);
|
||||
var results = parser.Parse(input);
|
||||
|
||||
// Generate code
|
||||
var chunkGenerator = CreateChunkGenerator(className, rootNamespace, sourceFileName);
|
||||
chunkGenerator.DesignTimeMode = Host.DesignTimeMode;
|
||||
chunkGenerator.Visit(results);
|
||||
// Generate code
|
||||
var chunkGenerator = CreateChunkGenerator(className, rootNamespace, sourceFileName);
|
||||
chunkGenerator.DesignTimeMode = Host.DesignTimeMode;
|
||||
chunkGenerator.Visit(results);
|
||||
|
||||
var codeGeneratorContext = new CodeGeneratorContext(chunkGenerator.Context, results.ErrorSink);
|
||||
codeGeneratorContext.Checksum = checksum;
|
||||
var codeGenerator = CreateCodeGenerator(codeGeneratorContext);
|
||||
var codeGeneratorResult = codeGenerator.Generate();
|
||||
var codeGeneratorContext = new CodeGeneratorContext(chunkGenerator.Context, results.ErrorSink);
|
||||
codeGeneratorContext.Checksum = checksum;
|
||||
var codeGenerator = CreateCodeGenerator(codeGeneratorContext);
|
||||
var codeGeneratorResult = codeGenerator.Generate();
|
||||
|
||||
// Collect results and return
|
||||
return new GeneratorResults(results, codeGeneratorResult, codeGeneratorContext.ChunkTreeBuilder.Root);
|
||||
// Collect results and return
|
||||
return new GeneratorResults(results, codeGeneratorResult, codeGeneratorContext.ChunkTreeBuilder.Root);
|
||||
}
|
||||
// During runtime we want code generation explosions to flow up into the calling code. At design time
|
||||
// we want to capture these exceptions to prevent IDEs from crashing.
|
||||
catch (Exception ex) when (Host.DesignTimeMode)
|
||||
{
|
||||
var errorSink = new ErrorSink();
|
||||
errorSink.OnError(
|
||||
SourceLocation.Undefined,
|
||||
RazorResources.FormatFatalException(sourceFileName, Environment.NewLine, ex.Message),
|
||||
length: -1);
|
||||
var emptyBlock = new BlockBuilder();
|
||||
emptyBlock.Type = default(BlockType);
|
||||
|
||||
return new GeneratorResults(
|
||||
document: emptyBlock.Build(),
|
||||
tagHelperDescriptors: Enumerable.Empty<TagHelperDescriptor>(),
|
||||
errorSink: errorSink,
|
||||
codeGeneratorResult: new CodeGeneratorResult(
|
||||
code: string.Empty,
|
||||
designTimeLineMappings: new List<LineMapping>()),
|
||||
chunkTree: new ChunkTree());
|
||||
}
|
||||
}
|
||||
|
||||
protected internal virtual RazorChunkGenerator CreateChunkGenerator(
|
||||
|
|
|
|||
|
|
@ -1,6 +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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
|
@ -18,6 +19,53 @@ namespace Microsoft.AspNetCore.Razor
|
|||
{
|
||||
public class RazorTemplateEngineTest
|
||||
{
|
||||
[Fact]
|
||||
public void InvalidRazorEngineHostReturnsParseErrorsAtDesignTime()
|
||||
{
|
||||
// Arrange
|
||||
var host = new InvalidRazorEngineHost(new CSharpRazorCodeLanguage())
|
||||
{
|
||||
DesignTimeMode = true
|
||||
};
|
||||
var razorEngine = new RazorTemplateEngine(host);
|
||||
var input = new StringTextBuffer("<div>Hello @(\"World\")</div>");
|
||||
var exception = new InvalidOperationException("Hello World");
|
||||
var expectedError = RazorResources.FormatFatalException("test", Environment.NewLine, exception.Message);
|
||||
|
||||
// Act
|
||||
var result = razorEngine.GenerateCode(input, className: null, rootNamespace: null, sourceFileName: "test");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.Document.Children);
|
||||
Assert.Empty(result.ChunkTree.Children);
|
||||
Assert.Empty(result.DesignTimeLineMappings);
|
||||
Assert.Empty(result.GeneratedCode);
|
||||
|
||||
var error = Assert.Single(result.ParserErrors);
|
||||
Assert.Equal(expectedError, error.Message, StringComparer.Ordinal);
|
||||
Assert.Equal(SourceLocation.Undefined, error.Location);
|
||||
Assert.Equal(-1, error.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidRazorEngineHostThrowsAtRuntime()
|
||||
{
|
||||
// Arrange
|
||||
var host = new InvalidRazorEngineHost(new CSharpRazorCodeLanguage())
|
||||
{
|
||||
DesignTimeMode = false
|
||||
};
|
||||
var razorEngine = new RazorTemplateEngine(host);
|
||||
var input = new StringTextBuffer("<div>Hello @(\"World\")</div>");
|
||||
|
||||
// Act
|
||||
var thrownException = Assert.Throws<InvalidOperationException>(() =>
|
||||
razorEngine.GenerateCode(input, className: null, rootNamespace: null, sourceFileName: "test"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Hello World", thrownException.Message, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConstructorInitializesHost()
|
||||
{
|
||||
|
|
@ -339,5 +387,23 @@ namespace Microsoft.AspNetCore.Razor
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class InvalidRazorEngineHost : RazorEngineHost
|
||||
{
|
||||
public InvalidRazorEngineHost(RazorCodeLanguage codeLanguage) : base(codeLanguage)
|
||||
{
|
||||
}
|
||||
|
||||
public override string DefaultClassName
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new InvalidOperationException("Hello World");
|
||||
}
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue