diff --git a/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs index a7f969758b..98672b3cef 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs @@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Razor.Runtime } /// - /// Cannot resolve TagHelper containing assembly '{0}'. + /// Cannot resolve TagHelper containing assembly '{0}'. Error: {1} /// internal static string TagHelperTypeResolver_CannotResolveTagHelperAssembly { @@ -39,11 +39,11 @@ namespace Microsoft.AspNet.Razor.Runtime } /// - /// Cannot resolve TagHelper containing assembly '{0}'. + /// Cannot resolve TagHelper containing assembly '{0}'. Error: {1} /// - internal static string FormatTagHelperTypeResolver_CannotResolveTagHelperAssembly(object p0) + internal static string FormatTagHelperTypeResolver_CannotResolveTagHelperAssembly(object p0, object p1) { - return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperTypeResolver_CannotResolveTagHelperAssembly"), p0); + return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperTypeResolver_CannotResolveTagHelperAssembly"), p0, p1); } /// @@ -110,6 +110,22 @@ namespace Microsoft.AspNet.Razor.Runtime return GetString("ArgumentCannotBeNullOrEmpty"); } + /// + /// Encountered an unexpected error when attempting to resolve tag helper directive '{0}' with value '{1}'. Error: {2} + /// + internal static string TagHelperDescriptorResolver_EncounteredUnexpectedError + { + get { return GetString("TagHelperDescriptorResolver_EncounteredUnexpectedError"); } + } + + /// + /// Encountered an unexpected error when attempting to resolve tag helper directive '{0}' with value '{1}'. Error: {2} + /// + internal static string FormatTagHelperDescriptorResolver_EncounteredUnexpectedError(object p0, object p1, object p2) + { + return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorResolver_EncounteredUnexpectedError"), p0, p1, p2); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNet.Razor.Runtime/Resources.resx b/src/Microsoft.AspNet.Razor.Runtime/Resources.resx index 46d21d10d4..d6222dec62 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/Resources.resx +++ b/src/Microsoft.AspNet.Razor.Runtime/Resources.resx @@ -123,7 +123,7 @@ "typeName, assemblyName" - Cannot resolve TagHelper containing assembly '{0}'. + Cannot resolve TagHelper containing assembly '{0}'. Error: {1} Tag helper directive assembly name cannot be null or empty. @@ -137,4 +137,7 @@ The value cannot be null or empty. + + Encountered an unexpected error when attempting to resolve tag helper directive '{0}' with value '{1}'. Error: {2} + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorResolver.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorResolver.cs index 0793ea7b92..64ce1e5085 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorResolver.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorResolver.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.TagHelpers; +using Microsoft.AspNet.Razor.Text; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { @@ -36,20 +38,42 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers foreach (var directiveDescriptor in context.DirectiveDescriptors) { - var lookupInfo = GetLookupInfo(directiveDescriptor); - - if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.RemoveTagHelper) + try { - resolvedDescriptors.RemoveWhere(descriptor => MatchesLookupInfo(descriptor, lookupInfo)); + var lookupInfo = GetLookupInfo(directiveDescriptor, context.ErrorSink); + + // Could not resolve the lookup info. + if (lookupInfo == null) + { + return Enumerable.Empty(); + } + + if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.RemoveTagHelper) + { + resolvedDescriptors.RemoveWhere(descriptor => MatchesLookupInfo(descriptor, lookupInfo)); + } + else if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.AddTagHelper) + { + var descriptors = ResolveDescriptorsInAssembly(lookupInfo.AssemblyName, + directiveDescriptor.Location, + context.ErrorSink); + + // Only use descriptors that match our lookup info + descriptors = descriptors.Where(descriptor => MatchesLookupInfo(descriptor, lookupInfo)); + + resolvedDescriptors.UnionWith(descriptors); + } } - else if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.AddTagHelper) + catch (Exception ex) { - var descriptors = ResolveDescriptorsInAssembly(lookupInfo.AssemblyName); + var directiveName = "@" + directiveDescriptor.DirectiveType.ToString().ToLowerInvariant(); - // Only use descriptors that match our lookup info - descriptors = descriptors.Where(descriptor => MatchesLookupInfo(descriptor, lookupInfo)); - - resolvedDescriptors.UnionWith(descriptors); + context.ErrorSink.OnError( + directiveDescriptor.Location, + Resources.FormatTagHelperDescriptorResolver_EncounteredUnexpectedError( + directiveName, + directiveDescriptor.LookupText, + ex.Message)); } } @@ -63,13 +87,18 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// /// The name of the assembly to resolve s from. /// + /// The of the directive. + /// Used to record errors found when resolving s + /// within the given . /// s for s from the given /// . // This is meant to be overridden by tooling to enable assembly level caching. - protected virtual IEnumerable ResolveDescriptorsInAssembly(string assemblyName) + protected virtual IEnumerable ResolveDescriptorsInAssembly(string assemblyName, + SourceLocation documentLocation, + ParserErrorSink errorSink) { // Resolve valid tag helper types from the assembly. - var tagHelperTypes = _typeResolver.Resolve(assemblyName); + var tagHelperTypes = _typeResolver.Resolve(assemblyName, documentLocation, errorSink); // Convert types to TagHelperDescriptors var descriptors = tagHelperTypes.SelectMany(TagHelperDescriptorFactory.CreateDescriptors); @@ -88,7 +117,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers string.Equals(descriptor.TypeName, lookupInfo.TypeName, StringComparison.Ordinal); } - private static LookupInfo GetLookupInfo(TagHelperDirectiveDescriptor directiveDescriptor) + private static LookupInfo GetLookupInfo(TagHelperDirectiveDescriptor directiveDescriptor, + ParserErrorSink errorSink) { var lookupText = directiveDescriptor.LookupText; var lookupStrings = lookupText?.Split(new[] { ',' }); @@ -100,9 +130,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers lookupStrings.Any(string.IsNullOrWhiteSpace) || (lookupStrings.Length != 1 && lookupStrings.Length != 2)) { - throw new ArgumentException( - Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperLookupText(lookupText), - nameof(lookupText)); + errorSink.OnError( + directiveDescriptor.Location, + Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperLookupText(lookupText)); + + return null; } // Grab the assembly name from the lookup text strings. Due to our supported lookupText formats it will diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs index 3a6897a01c..a88752ea37 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Text; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { @@ -27,18 +29,43 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// all valid s. /// /// The name of an to search. - /// An of valid s. - public IEnumerable Resolve(string name) + /// The of the associated + /// responsible for the current call. + /// + /// The used to record errors found when resolving + /// s. + /// An of valid s. + /// + public IEnumerable Resolve(string name, + SourceLocation documentLocation, + [NotNull] ParserErrorSink errorSink) { if (string.IsNullOrEmpty(name)) { - throw new ArgumentException( - Resources.TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull, - nameof(name)); + errorSink.OnError(documentLocation, + Resources.TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull); + + return Type.EmptyTypes; } var assemblyName = new AssemblyName(name); - var libraryTypes = GetLibraryDefinedTypes(assemblyName); + + IEnumerable libraryTypes; + try + { + libraryTypes = GetLibraryDefinedTypes(assemblyName); + } + catch (Exception ex) + { + errorSink.OnError( + documentLocation, + Resources.FormatTagHelperTypeResolver_CannotResolveTagHelperAssembly( + assemblyName.Name, + ex.Message)); + + return Type.EmptyTypes; + } + var validTagHelpers = libraryTypes.Where(IsTagHelper); // Convert from TypeInfo[] to Type[] @@ -48,17 +75,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Internal for testing, don't want to be loading assemblies during a test. internal virtual IEnumerable GetLibraryDefinedTypes(AssemblyName assemblyName) { - try - { - var assembly = Assembly.Load(assemblyName); + var assembly = Assembly.Load(assemblyName); - return assembly.DefinedTypes; - } - catch (Exception ex) - { - throw new InvalidOperationException( - Resources.FormatTagHelperTypeResolver_CannotResolveTagHelperAssembly(assemblyName.Name), ex); - } + return assembly.ExportedTypes.Select(type => type.GetTypeInfo()); } // Internal for testing. diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs index 543f257023..a47a0a436a 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs @@ -243,9 +243,8 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp // https://github.com/aspnet/Razor/issues/129 if (!isPlainTextValue) { - throw new InvalidOperationException( - RazorResources.FormatTagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols( - attributeDescriptor.PropertyName)); + _writer.WriteLine(";"); + return; } // We aren't a bufferable attribute which means we have no Razor code in our value. diff --git a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs index 02d2046303..4d2a21d1ec 100644 --- a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs +++ b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs @@ -172,7 +172,7 @@ namespace Microsoft.AspNet.Razor.Parser if (TagHelperDescriptorResolver != null) { - var descriptors = GetTagHelperDescriptors(rewritingContext.SyntaxTree); + var descriptors = GetTagHelperDescriptors(rewritingContext.SyntaxTree, rewritingContext.ErrorSink); var tagHelperProvider = new TagHelperDescriptorProvider(descriptors); var tagHelperParseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperProvider); @@ -203,10 +203,15 @@ namespace Microsoft.AspNet.Razor.Parser /// specified . /// /// The to scan for tag helper registrations in. - /// - protected virtual IEnumerable GetTagHelperDescriptors([NotNull] Block documentRoot) + /// Used to manage s encountered during the Razor parsing + /// phase. + /// s that are applicable to the + /// + protected virtual IEnumerable GetTagHelperDescriptors([NotNull] Block documentRoot, + [NotNull] ParserErrorSink errorSink) { - var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(TagHelperDescriptorResolver); + var addOrRemoveTagHelperSpanVisitor = + new AddOrRemoveTagHelperSpanVisitor(TagHelperDescriptorResolver, errorSink); return addOrRemoveTagHelperSpanVisitor.GetDescriptors(documentRoot); } diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/AddOrRemoveTagHelperSpanVisitor.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/AddOrRemoveTagHelperSpanVisitor.cs index d182f010e1..1efd57f2b8 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/AddOrRemoveTagHelperSpanVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/AddOrRemoveTagHelperSpanVisitor.cs @@ -15,12 +15,15 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers public class AddOrRemoveTagHelperSpanVisitor : ParserVisitor { private readonly ITagHelperDescriptorResolver _descriptorResolver; + private readonly ParserErrorSink _errorSink; private List _directiveDescriptors; - public AddOrRemoveTagHelperSpanVisitor([NotNull] ITagHelperDescriptorResolver descriptorResolver) + public AddOrRemoveTagHelperSpanVisitor([NotNull] ITagHelperDescriptorResolver descriptorResolver, + [NotNull] ParserErrorSink errorSink) { _descriptorResolver = descriptorResolver; + _errorSink = errorSink; } public IEnumerable GetDescriptors([NotNull] Block root) @@ -30,7 +33,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers // This will recurse through the syntax tree. VisitBlock(root); - var resolutionContext = GetTagHelperDescriptorResolutionContext(_directiveDescriptors); + var resolutionContext = GetTagHelperDescriptorResolutionContext(_directiveDescriptors, _errorSink); var descriptors = _descriptorResolver.Resolve(resolutionContext); return descriptors; @@ -38,9 +41,10 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers // Allows MVC a chance to override the TagHelperDescriptorResolutionContext protected virtual TagHelperDescriptorResolutionContext GetTagHelperDescriptorResolutionContext( - [NotNull] IEnumerable descriptors) + [NotNull] IEnumerable descriptors, + [NotNull] ParserErrorSink errorSink) { - return new TagHelperDescriptorResolutionContext(descriptors); + return new TagHelperDescriptorResolutionContext(descriptors, errorSink); } public override void VisitSpan(Span span) @@ -55,7 +59,9 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers TagHelperDirectiveType.RemoveTagHelper : TagHelperDirectiveType.AddTagHelper; - var directiveDescriptor = new TagHelperDirectiveDescriptor(codeGenerator.LookupText, directive); + var directiveDescriptor = new TagHelperDirectiveDescriptor(codeGenerator.LookupText, + span.Start, + directive); _directiveDescriptors.Add(directiveDescriptor); } diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorResolutionContext.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorResolutionContext.cs index aeb42ef875..a06d56a9f0 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorResolutionContext.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorResolutionContext.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.Collections.Generic; +using Microsoft.AspNet.Razor.Parser; namespace Microsoft.AspNet.Razor.TagHelpers { @@ -15,15 +16,23 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// /// s used to resolve /// s. + /// Used to aggregate s. public TagHelperDescriptorResolutionContext( - [NotNull] IEnumerable directiveDescriptors) + [NotNull] IEnumerable directiveDescriptors, + [NotNull] ParserErrorSink errorSink) { DirectiveDescriptors = new List(directiveDescriptors); + ErrorSink = errorSink; } /// /// s used to resolve s. /// public IList DirectiveDescriptors { get; private set; } + + /// + /// Used to aggregate s. + /// + public ParserErrorSink ErrorSink { get; private set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs index c16529f302..b508387b17 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.AspNet.Razor.Text; + namespace Microsoft.AspNet.Razor.TagHelpers { /// @@ -12,10 +14,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// Instantiates a new instance of . /// /// A used to find tag helper s. + /// The of the directive. /// The of this directive. - public TagHelperDirectiveDescriptor([NotNull] string lookupText, TagHelperDirectiveType directiveType) + public TagHelperDirectiveDescriptor([NotNull] string lookupText, + SourceLocation location, + TagHelperDirectiveType directiveType) { LookupText = lookupText; + Location = location; DirectiveType = directiveType; } @@ -28,5 +34,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// The of this directive. /// public TagHelperDirectiveType DirectiveType { get; private set; } + + /// + /// The of the directive. + /// + public SourceLocation Location { get; private set; } } } \ No newline at end of file