Change `RazorError`s to consistently provide error lengths.

- Updated `TagHelper` errors to no longer highlight the entire tag as an error, instead just the tag name is marked as an error. This is now consistent with nested tags in `@{ ... }` errors.
- Updated `RazorError` and corresponding error logging constructs to disallow creation without providing lengths.
- Updated `TagHelperDescriptorResolver` and related classes to properly determine assembly locations within directives. This allows for exact error locations in the `@addTagHelper` directive.

#386
This commit is contained in:
N. Taylor Mullen 2015-09-01 21:55:21 -07:00
parent 43fce8c927
commit ad5bfc5b66
15 changed files with 263 additions and 121 deletions

View File

@ -315,7 +315,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
if (string.IsNullOrWhiteSpace(name))
{
errorSink.OnError(SourceLocation.Zero, whitespaceError);
errorSink.OnError(SourceLocation.Zero, whitespaceError, length: 0);
validName = false;
}
@ -327,7 +327,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
InvalidNonWhitespaceNameCharacters.Contains(character))
{
var error = characterErrorBuilder(character);
errorSink.OnError(SourceLocation.Zero, error);
errorSink.OnError(SourceLocation.Zero, error, length: 0);
validName = false;
}
@ -379,7 +379,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
type.FullName,
property.Name,
typeof(HtmlAttributeNameAttribute).FullName,
nameof(HtmlAttributeNameAttribute.Name)));
nameof(HtmlAttributeNameAttribute.Name)),
length: 0);
continue;
}
@ -438,7 +439,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
SourceLocation.Zero,
Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty(
parentType.FullName,
attributeDescriptor.PropertyName));
attributeDescriptor.PropertyName),
length: 0);
return false;
}
@ -492,7 +494,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace(
parentType.FullName,
propertyName,
nameOrPrefix));
nameOrPrefix),
length: 0);
return false;
}
@ -508,7 +511,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
propertyName,
nameOrPrefix,
attributeNameOrPrefix,
DataDashPrefix));
DataDashPrefix),
length: 0);
return false;
}
@ -525,7 +529,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
propertyName,
nameOrPrefix,
attributeNameOrPrefix,
character));
character),
length: 0);
isValid = false;
}
@ -576,7 +581,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
property.Name,
nameof(HtmlAttributeNameAttribute),
nameof(HtmlAttributeNameAttribute.DictionaryAttributePrefix),
"IDictionary<string, TValue>"));
"IDictionary<string, TValue>"),
length: 0);
}
else if (attributeNameAttribute != null && !hasPublicSetter)
{
@ -589,7 +595,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
parentType.FullName,
property.Name,
nameof(HtmlAttributeNameAttribute),
"IDictionary<string, TValue>"));
"IDictionary<string, TValue>"),
length: 0);
}
return null;
@ -608,7 +615,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
property.Name,
nameof(HtmlAttributeNameAttribute),
nameof(HtmlAttributeNameAttribute.DictionaryAttributePrefix),
"IDictionary<string, TValue>"));
"IDictionary<string, TValue>"),
length: 0);
return null;
}

View File

@ -81,7 +81,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
var descriptors = ResolveDescriptorsInAssembly(
lookupInfo.AssemblyName,
directiveDescriptor.Location,
lookupInfo.AssemblyNameLocation,
context.ErrorSink);
// Only use descriptors that match our lookup info
@ -101,7 +101,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
Resources.FormatTagHelperDescriptorResolver_EncounteredUnexpectedError(
"@" + directiveName,
directiveDescriptor.DirectiveText,
ex.Message));
ex.Message),
GetErrorLength(directiveDescriptor.DirectiveText));
}
}
@ -183,7 +184,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
context.ErrorSink.OnError(
directive.Location,
Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperDirective(
SyntaxConstants.CSharp.TagHelperPrefixKeyword));
SyntaxConstants.CSharp.TagHelperPrefixKeyword),
GetErrorLength(directive.DirectiveText));
}
}
@ -213,7 +215,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperPrefixValue(
SyntaxConstants.CSharp.TagHelperPrefixKeyword,
character,
prefix));
prefix),
prefix.Length);
return false;
}
@ -241,8 +244,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
return regex.IsMatch(descriptor.TypeName);
}
private static LookupInfo GetLookupInfo(TagHelperDirectiveDescriptor directiveDescriptor,
ErrorSink errorSink)
private static LookupInfo GetLookupInfo(
TagHelperDirectiveDescriptor directiveDescriptor,
ErrorSink errorSink)
{
var lookupText = directiveDescriptor.DirectiveText;
var lookupStrings = lookupText?.Split(new[] { ',' });
@ -256,23 +260,42 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
errorSink.OnError(
directiveDescriptor.Location,
Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperLookupText(lookupText));
Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperLookupText(lookupText),
GetErrorLength(lookupText));
return null;
}
var trimmedAssemblyName = lookupStrings[1].Trim();
// + 1 is for the comma separator in the lookup text.
var assemblyNameIndex = lookupStrings[0].Length + 1 + lookupStrings[1].IndexOf(trimmedAssemblyName);
var assemblyNamePrefix = directiveDescriptor.DirectiveText.Substring(0, assemblyNameIndex);
var assemblyNameLocation = SourceLocation.Advance(directiveDescriptor.Location, assemblyNamePrefix);
return new LookupInfo
{
TypePattern = lookupStrings[0].Trim(),
AssemblyName = lookupStrings[1].Trim()
AssemblyName = trimmedAssemblyName,
AssemblyNameLocation = assemblyNameLocation,
};
}
private static int GetErrorLength(string directiveText)
{
var nonNullLength = directiveText == null ? 1 : directiveText.Length;
var normalizeEmptyStringLength = Math.Max(nonNullLength, 1);
return normalizeEmptyStringLength;
}
private class LookupInfo
{
public string AssemblyName { get; set; }
public string TypePattern { get; set; }
public SourceLocation AssemblyNameLocation { get; set; }
}
}
}

View File

@ -35,14 +35,18 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// <see cref="ITagHelper"/> <see cref="Type"/>s.</param>
/// <returns>An <see cref="IEnumerable{Type}"/> of valid <see cref="ITagHelper"/> <see cref="Type"/>s.
/// </returns>
public IEnumerable<Type> Resolve(string name,
SourceLocation documentLocation,
[NotNull] ErrorSink errorSink)
public IEnumerable<Type> Resolve(
string name,
SourceLocation documentLocation,
[NotNull] ErrorSink errorSink)
{
if (string.IsNullOrEmpty(name))
{
errorSink.OnError(documentLocation,
Resources.TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull);
var errorLength = name == null ? 1 : Math.Max(name.Length, 1);
errorSink.OnError(
documentLocation,
Resources.TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull,
errorLength);
return Type.EmptyTypes;
}
@ -60,7 +64,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
documentLocation,
Resources.FormatTagHelperTypeResolver_CannotResolveTagHelperAssembly(
assemblyName.Name,
ex.Message));
ex.Message),
name.Length);
return Type.EmptyTypes;
}

View File

@ -40,16 +40,6 @@ namespace Microsoft.AspNet.Razor
_errors.Add(error);
}
/// <summary>
/// Creates and tracks a new <see cref="RazorError"/>.
/// </summary>
/// <param name="location"><see cref="SourceLocation"/> of the error.</param>
/// <param name="message">A message describing the error.</param>
public void OnError(SourceLocation location, string message)
{
OnError(location, message, RazorError.DefaultErrorLength);
}
/// <summary>
/// Creates and tracks a new <see cref="RazorError"/>.
/// </summary>

View File

@ -102,7 +102,10 @@ namespace Microsoft.AspNet.Razor.Parser
if (!errorReported)
{
errorReported = true;
Context.OnError(errorLocation, RazorResources.ParseError_MissingOpenBraceAfterSection);
Context.OnError(
errorLocation,
RazorResources.ParseError_MissingOpenBraceAfterSection,
length: 1 /* { */);
}
PutCurrentBack();
@ -133,9 +136,10 @@ namespace Microsoft.AspNet.Razor.Parser
if (!Optional(CSharpSymbolType.RightBrace))
{
editHandler.AutoCompleteString = "}";
Context.OnError(CurrentLocation,
RazorResources.FormatParseError_Expected_X(
Language.GetSample(CSharpSymbolType.RightBrace)));
Context.OnError(
CurrentLocation,
RazorResources.FormatParseError_Expected_X(Language.GetSample(CSharpSymbolType.RightBrace)),
length: 1 /* } */);
}
else
{
@ -160,8 +164,10 @@ namespace Microsoft.AspNet.Razor.Parser
if (!At(CSharpSymbolType.LeftBrace))
{
Context.OnError(CurrentLocation,
RazorResources.FormatParseError_Expected_X(Language.GetSample(CSharpSymbolType.LeftBrace)));
Context.OnError(
CurrentLocation,
RazorResources.FormatParseError_Expected_X(Language.GetSample(CSharpSymbolType.LeftBrace)),
length: 1 /* { */);
CompleteBlock();
Output(SpanKind.MetaCode);
return;
@ -186,7 +192,10 @@ namespace Microsoft.AspNet.Razor.Parser
if (!At(CSharpSymbolType.RightBrace))
{
editHandler.AutoCompleteString = "}";
Context.OnError(blockStart, RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF(block.Name, "}", "{"));
Context.OnError(
blockStart,
RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF(block.Name, "}", "{"),
length: 1 /* } */);
CompleteBlock();
Output(SpanKind.Code);
}

View File

@ -29,7 +29,10 @@ namespace Microsoft.AspNet.Razor.Parser
protected virtual void ReservedDirective(bool topLevel)
{
Context.OnError(CurrentLocation, RazorResources.FormatParseError_ReservedWord(CurrentSymbol.Content));
Context.OnError(
CurrentLocation,
RazorResources.FormatParseError_ReservedWord(CurrentSymbol.Content),
CurrentSymbol.Content.Length);
AcceptAndMoveNext();
Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None;
Span.ChunkGenerator = SpanChunkGenerator.Null;
@ -109,7 +112,10 @@ namespace Microsoft.AspNet.Razor.Parser
// using Identifier ==> Using Declaration
if (!topLevel)
{
Context.OnError(block.Start, RazorResources.ParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock);
Context.OnError(
block.Start,
RazorResources.ParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock,
block.Name.Length);
StandardStatement();
}
else
@ -327,10 +333,12 @@ namespace Microsoft.AspNet.Razor.Parser
// Check for "{" to make sure we're at a block
if (!At(CSharpSymbolType.LeftBrace))
{
Context.OnError(CurrentLocation,
RazorResources.FormatParseError_SingleLine_ControlFlowStatements_Not_Allowed(
Language.GetSample(CSharpSymbolType.LeftBrace),
CurrentSymbol.Content));
Context.OnError(
CurrentLocation,
RazorResources.FormatParseError_SingleLine_ControlFlowStatements_Not_Allowed(
Language.GetSample(CSharpSymbolType.LeftBrace),
CurrentSymbol.Content),
CurrentSymbol.Content.Length);
}
// Parse the statement and then we're done
@ -480,7 +488,10 @@ namespace Microsoft.AspNet.Razor.Parser
{
if (type == CSharpSymbolType.Transition && !isSingleLineMarkup)
{
Context.OnError(loc, RazorResources.ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start);
Context.OnError(
loc,
RazorResources.ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start,
length: 1 /* @ */);
}
// Markup block
@ -560,7 +571,10 @@ namespace Microsoft.AspNet.Razor.Parser
// Throw errors as necessary, but continue parsing
if (At(CSharpSymbolType.LeftBrace))
{
Context.OnError(CurrentLocation, RazorResources.ParseError_Unexpected_Nested_CodeBlock);
Context.OnError(
CurrentLocation,
RazorResources.ParseError_Unexpected_Nested_CodeBlock,
length: 1 /* { */);
}
// @( or @foo - Nested expression, parse a child block
@ -649,7 +663,10 @@ namespace Microsoft.AspNet.Razor.Parser
if (EndOfFile)
{
Context.OnError(block.Start, RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF(block.Name, '}', '{'));
Context.OnError(
block.Start,
RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF(block.Name, '}', '{'),
length: 1 /* { OR } */);
}
else if (acceptTerminatingBrace)
{

View File

@ -256,15 +256,25 @@ namespace Microsoft.AspNet.Razor.Parser
};
if (At(CSharpSymbolType.WhiteSpace) || At(CSharpSymbolType.NewLine))
{
Context.OnError(CurrentLocation, RazorResources.ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS);
Context.OnError(
CurrentLocation,
RazorResources.ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS,
CurrentSymbol.Content.Length);
}
else if (EndOfFile)
{
Context.OnError(CurrentLocation, RazorResources.ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock);
Context.OnError(
CurrentLocation,
RazorResources.ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock,
length: 1 /* end of file */);
}
else
{
Context.OnError(CurrentLocation, RazorResources.FormatParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS(CurrentSymbol.Content));
Context.OnError(
CurrentLocation,
RazorResources.FormatParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS(
CurrentSymbol.Content),
CurrentSymbol.Content.Length);
}
}
finally
@ -529,7 +539,10 @@ namespace Microsoft.AspNet.Razor.Parser
if (!success)
{
AcceptUntil(CSharpSymbolType.LessThan);
Context.OnError(block.Start, RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF(block.Name, ")", "("));
Context.OnError(
block.Start,
RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF(block.Name, ")", "("),
length: 1 /* ( */);
}
// If necessary, put an empty-content marker symbol here
@ -556,7 +569,10 @@ namespace Microsoft.AspNet.Razor.Parser
{
if (Context.IsWithin(BlockType.Template))
{
Context.OnError(CurrentLocation, RazorResources.ParseError_InlineMarkup_Blocks_Cannot_Be_Nested);
Context.OnError(
CurrentLocation,
RazorResources.ParseError_InlineMarkup_Blocks_Cannot_Be_Nested,
length: 1 /* @ */);
}
Output(SpanKind.Code);
using (Context.StartBlock(BlockType.Template))

View File

@ -15,6 +15,8 @@ namespace Microsoft.AspNet.Razor.Parser
{
public partial class HtmlMarkupParser
{
private const string ScriptTagName = "script";
private SourceLocation _lastTagStart = SourceLocation.Zero;
private HtmlSymbol _bufferedOpenAngle;
@ -62,7 +64,10 @@ namespace Microsoft.AspNet.Razor.Parser
}
else
{
Context.OnError(CurrentSymbol.Start, RazorResources.ParseError_MarkupBlock_Must_Start_With_Tag);
Context.OnError(
CurrentSymbol.Start,
RazorResources.ParseError_MarkupBlock_Must_Start_With_Tag,
CurrentSymbol.Content.Length);
}
Output(SpanKind.Markup);
}
@ -221,7 +226,10 @@ namespace Microsoft.AspNet.Razor.Parser
}
if (tags.Count == 0)
{
Context.OnError(CurrentLocation, RazorResources.ParseError_OuterTagMissingName);
Context.OnError(
CurrentLocation,
RazorResources.ParseError_OuterTagMissingName,
length: 1 /* end of file */);
}
return false;
}
@ -346,11 +354,10 @@ namespace Microsoft.AspNet.Razor.Parser
private bool EndTextTag(HtmlSymbol solidus, IDisposable tagBlockWrapper)
{
var start = _bufferedOpenAngle.Start;
Accept(_bufferedOpenAngle);
Accept(solidus);
var textLocation = CurrentLocation;
Assert(HtmlSymbolType.Text);
AcceptAndMoveNext();
@ -358,7 +365,10 @@ namespace Microsoft.AspNet.Razor.Parser
if (!seenCloseAngle)
{
Context.OnError(start, RazorResources.ParseError_TextTagCannotContainAttributes);
Context.OnError(
textLocation,
RazorResources.ParseError_TextTagCannotContainAttributes,
length: 4 /* text */);
Span.EditHandler.AcceptedCharacters = AcceptedCharacters.Any;
RecoverTextTag();
@ -751,6 +761,7 @@ namespace Microsoft.AspNet.Razor.Parser
Span.ChunkGenerator = SpanChunkGenerator.Null;
Accept(_bufferedOpenAngle);
var textLocation = CurrentLocation;
Assert(HtmlSymbolType.Text);
AcceptAndMoveNext();
@ -771,7 +782,10 @@ namespace Microsoft.AspNet.Razor.Parser
{
Context.Source.Position = bookmark;
NextToken();
Context.OnError(tag.Item2, RazorResources.ParseError_TextTagCannotContainAttributes);
Context.OnError(
textLocation,
RazorResources.ParseError_TextTagCannotContainAttributes,
length: 4 /* text */);
RecoverTextTag();
}
@ -821,7 +835,10 @@ namespace Microsoft.AspNet.Razor.Parser
var seenClose = Optional(HtmlSymbolType.CloseAngle);
if (!seenClose)
{
Context.OnError(tag.Item2, RazorResources.FormatParseError_UnfinishedTag(tag.Item1.Content));
Context.OnError(
SourceLocation.Advance(tag.Item2, "<"),
RazorResources.FormatParseError_UnfinishedTag(tag.Item1.Content),
Math.Max(tag.Item1.Content.Length, 1));
}
else
{
@ -883,7 +900,7 @@ namespace Microsoft.AspNet.Razor.Parser
Context.Source.Position = bookmark;
NextToken();
}
else if (string.Equals(tagName, "script", StringComparison.OrdinalIgnoreCase))
else if (string.Equals(tagName, ScriptTagName, StringComparison.OrdinalIgnoreCase))
{
CompleteTagBlockWithSpan(tagBlockWrapper, AcceptedCharacters.None, SpanKind.Markup);
@ -916,7 +933,8 @@ namespace Microsoft.AspNet.Razor.Parser
var solidus = CurrentSymbol;
NextToken(); // Skip over '/', current should be text
if (At(HtmlSymbolType.Text) && string.Equals(CurrentSymbol.Content, "script", StringComparison.OrdinalIgnoreCase))
if (At(HtmlSymbolType.Text) &&
string.Equals(CurrentSymbol.Content, ScriptTagName, StringComparison.OrdinalIgnoreCase))
{
seenEndScript = true;
}
@ -944,7 +962,10 @@ namespace Microsoft.AspNet.Razor.Parser
SkipToAndParseCode(HtmlSymbolType.CloseAngle);
if (!Optional(HtmlSymbolType.CloseAngle))
{
Context.OnError(tagStart, RazorResources.FormatParseError_UnfinishedTag("script"));
Context.OnError(
SourceLocation.Advance(tagStart, "</"),
RazorResources.FormatParseError_UnfinishedTag(ScriptTagName),
ScriptTagName.Length);
}
Output(SpanKind.Markup);
}
@ -999,11 +1020,17 @@ namespace Microsoft.AspNet.Razor.Parser
}
if (currentTag != null)
{
Context.OnError(currentTag.Item2, RazorResources.FormatParseError_MissingEndTag(currentTag.Item1.Content));
Context.OnError(
SourceLocation.Advance(currentTag.Item2, "<"),
RazorResources.FormatParseError_MissingEndTag(currentTag.Item1.Content),
currentTag.Item1.Content.Length);
}
else
{
Context.OnError(tagStart, RazorResources.FormatParseError_UnexpectedEndTag(tagName));
Context.OnError(
SourceLocation.Advance(tagStart, "</"),
RazorResources.FormatParseError_UnexpectedEndTag(tagName),
tagName.Length);
}
return false;
}
@ -1018,7 +1045,10 @@ namespace Microsoft.AspNet.Razor.Parser
tags.Pop();
}
var tag = tags.Pop();
Context.OnError(tag.Item2, RazorResources.FormatParseError_MissingEndTag(tag.Item1.Content));
Context.OnError(
SourceLocation.Advance(tag.Item2, "<"),
RazorResources.FormatParseError_MissingEndTag(tag.Item1.Content),
tag.Item1.Content.Length);
}
else if (complete)
{

View File

@ -200,14 +200,6 @@ namespace Microsoft.AspNet.Razor.Parser
_errorSink.OnError(error);
}
public void OnError(SourceLocation location, string message)
{
EnusreNotTerminated();
AssertOnOwnerTask();
_errorSink.OnError(location, message);
}
public void OnError(SourceLocation location, string message, int length)
{
EnusreNotTerminated();
@ -216,12 +208,12 @@ namespace Microsoft.AspNet.Razor.Parser
_errorSink.OnError(location, message, length);
}
public void OnError(SourceLocation location, string message, params object[] args)
public void OnError(SourceLocation location, string message, int length, params object[] args)
{
EnusreNotTerminated();
AssertOnOwnerTask();
OnError(location, string.Format(CultureInfo.CurrentCulture, message, args));
OnError(location, string.Format(CultureInfo.CurrentCulture, message, args), length);
}
public ParserResults CompleteParse()

View File

@ -296,8 +296,10 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
if (childSpan == null || childSpan.Kind != SpanKind.Markup)
{
errorSink.OnError(block.Children.First().Start,
RazorResources.FormatTagHelpers_CannotHaveCSharpInTagDeclaration(tagName));
errorSink.OnError(
block.Start,
RazorResources.FormatTagHelpers_CannotHaveCSharpInTagDeclaration(tagName),
block.Length);
return null;
}
@ -316,7 +318,10 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
if (name == null)
{
errorSink.OnError(childSpan.Start, RazorResources.FormatTagHelpers_AttributesMustHaveAName(tagName));
errorSink.OnError(
childSpan.Start,
RazorResources.FormatTagHelpers_AttributesMustHaveAName(tagName),
childSpan.Length);
return null;
}

View File

@ -67,11 +67,12 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
chunkGenerator.RemoveTagHelperDescriptors ?
TagHelperDirectiveType.RemoveTagHelper :
TagHelperDirectiveType.AddTagHelper;
var textLocation = GetSubTextSourceLocation(span, chunkGenerator.LookupText);
var directiveDescriptor = new TagHelperDirectiveDescriptor
{
DirectiveText = chunkGenerator.LookupText,
Location = span.Start,
Location = textLocation,
DirectiveType = directive
};
@ -80,16 +81,26 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
else if (span.ChunkGenerator is TagHelperPrefixDirectiveChunkGenerator)
{
var chunkGenerator = (TagHelperPrefixDirectiveChunkGenerator)span.ChunkGenerator;
var textLocation = GetSubTextSourceLocation(span, chunkGenerator.Prefix);
var directiveDescriptor = new TagHelperDirectiveDescriptor
{
DirectiveText = chunkGenerator.Prefix,
Location = span.Start,
Location = textLocation,
DirectiveType = TagHelperDirectiveType.TagHelperPrefix
};
_directiveDescriptors.Add(directiveDescriptor);
}
}
private static SourceLocation GetSubTextSourceLocation(Span span, string text)
{
var startOffset = span.Content.IndexOf(text);
var offsetContent = span.Content.Substring(0, startOffset);
var offsetTextLocation = SourceLocation.Advance(span.Start, offsetContent);
return offsetTextLocation;
}
}
}

View File

@ -208,12 +208,12 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
{
// End tag TagHelper that states it shouldn't have an end tag.
context.ErrorSink.OnError(
tagBlock.Start,
SourceLocation.Advance(tagBlock.Start, "</"),
RazorResources.FormatTagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag(
tagName,
invalidDescriptor.TypeName,
invalidDescriptor.TagStructure),
tagBlock.Length);
tagName.Length);
return false;
}
@ -233,8 +233,9 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
// Could not recover, the end tag helper has no corresponding start tag, create
// an error based on the current childBlock.
context.ErrorSink.OnError(
tagBlock.Start,
RazorResources.FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper(tagName));
SourceLocation.Advance(tagBlock.Start, "</"),
RazorResources.FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper(tagName),
tagName.Length);
return false;
}
@ -333,8 +334,9 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
tagName,
tracker.Builder.TagName,
allowedChildrenString);
var errorStart = GetTagDeclarationErrorStart(tagBlock);
errorSink.OnError(tagBlock.Start, errorMessage, tagBlock.Length);
errorSink.OnError(errorStart, errorMessage, tagName.Length);
}
private static void ValidateDescriptors(
@ -373,9 +375,12 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
// We assume an invalid syntax until we verify that the tag meets all of our "valid syntax" criteria.
if (IsPartialTag(tag))
{
var errorStart = GetTagDeclarationErrorStart(tag);
context.ErrorSink.OnError(
tag.Start,
RazorResources.FormatTagHelpersParseTreeRewriter_MissingCloseAngle(tagName));
errorStart,
RazorResources.FormatTagHelpersParseTreeRewriter_MissingCloseAngle(tagName),
tagName.Length);
return false;
}
@ -383,6 +388,13 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
return true;
}
private static SourceLocation GetTagDeclarationErrorStart(Block tagBlock)
{
var advanceBy = IsEndTag(tagBlock) ? "</" : "<";
return SourceLocation.Advance(tagBlock.Start, advanceBy);
}
private static bool IsPartialTag(Block tagBlock)
{
// No need to validate the tag end because in order to be a tag block it must start with '<'.
@ -501,9 +513,10 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
var malformedTagHelper = _trackerStack.Peek().Builder;
context.ErrorSink.OnError(
malformedTagHelper.Start,
SourceLocation.Advance(malformedTagHelper.Start, "<"),
RazorResources.FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper(
malformedTagHelper.TagName));
malformedTagHelper.TagName),
malformedTagHelper.TagName.Length);
BuildCurrentlyTrackedTagHelperBlock(endTag: null);
}

View File

@ -71,10 +71,12 @@ namespace Microsoft.AspNet.Razor.Parser
AcceptAndMoveNext();
if (EndOfFile && ((mode & BalancingModes.NoErrorOnFailure) != BalancingModes.NoErrorOnFailure))
{
Context.OnError(start,
RazorResources.FormatParseError_Expected_CloseBracket_Before_EOF(
Language.GetSample(left),
Language.GetSample(right)));
Context.OnError(
start,
RazorResources.FormatParseError_Expected_CloseBracket_Before_EOF(
Language.GetSample(left),
Language.GetSample(right)),
length: 1 /* { OR } */);
}
return Balance(mode, left, right, start);
@ -119,10 +121,12 @@ namespace Microsoft.AspNet.Razor.Parser
{
if ((mode & BalancingModes.NoErrorOnFailure) != BalancingModes.NoErrorOnFailure)
{
Context.OnError(start,
RazorResources.FormatParseError_Expected_CloseBracket_Before_EOF(
Language.GetSample(left),
Language.GetSample(right)));
Context.OnError(
start,
RazorResources.FormatParseError_Expected_CloseBracket_Before_EOF(
Language.GetSample(left),
Language.GetSample(right)),
length: 1 /* { OR } */);
}
if ((mode & BalancingModes.BacktrackOnFailure) == BalancingModes.BacktrackOnFailure)
{
@ -355,9 +359,17 @@ namespace Microsoft.AspNet.Razor.Parser
error = RazorResources.FormatErrorComponent_Character(CurrentSymbol.Content);
}
Context.OnError(
CurrentLocation,
errorBase(error));
int errorLength;
if (CurrentSymbol == null || CurrentSymbol.Content == null)
{
errorLength = 1;
}
else
{
errorLength = Math.Max(CurrentSymbol.Content.Length, 1);
}
Context.OnError(CurrentLocation, errorBase(error), errorLength);
}
return found;
}
@ -521,7 +533,10 @@ namespace Microsoft.AspNet.Razor.Parser
if (!Optional(KnownSymbolType.CommentStar))
{
errorReported = true;
Context.OnError(start, RazorResources.ParseError_RazorComment_Not_Terminated);
Context.OnError(
start,
RazorResources.ParseError_RazorComment_Not_Terminated,
length: 2 /* @* */);
}
else
{
@ -533,7 +548,10 @@ namespace Microsoft.AspNet.Razor.Parser
if (!errorReported)
{
errorReported = true;
Context.OnError(start, RazorResources.ParseError_RazorComment_Not_Terminated);
Context.OnError(
start,
RazorResources.ParseError_RazorComment_Not_Terminated,
length: 2 /* @* */);
}
}
else

View File

@ -11,18 +11,16 @@ namespace Microsoft.AspNet.Razor
{
internal const int DefaultErrorLength = 1;
/// <summary>
/// Used only for deserialization.
/// </summary>
public RazorError()
: this(message: string.Empty, location: SourceLocation.Undefined)
: this(message: string.Empty, location: SourceLocation.Undefined, length: -1)
{
}
public RazorError(string message, SourceLocation location)
: this(message, location, DefaultErrorLength)
{
}
public RazorError(string message, int absoluteIndex, int lineIndex, int columnIndex)
: this(message, new SourceLocation(absoluteIndex, lineIndex, columnIndex))
public RazorError(string message, int absoluteIndex, int lineIndex, int columnIndex, int length)
: this(message, new SourceLocation(absoluteIndex, lineIndex, columnIndex), length)
{
}
@ -33,11 +31,6 @@ namespace Microsoft.AspNet.Razor
Length = length;
}
public RazorError(string message, int absoluteIndex, int lineIndex, int columnIndex, int length)
: this(message, new SourceLocation(absoluteIndex, lineIndex, columnIndex), length)
{
}
/// <summary>
/// Gets (or sets) the message describing the error.
/// </summary>

View File

@ -264,7 +264,11 @@ namespace Microsoft.AspNet.Razor.Tokenizer
}
else if (EndOfFile)
{
CurrentErrors.Add(new RazorError(RazorResources.ParseError_Unterminated_String_Literal, CurrentStart));
CurrentErrors.Add(
new RazorError(
RazorResources.ParseError_Unterminated_String_Literal,
CurrentStart,
length: 1 /* end of file */));
}
return Transition(EndSymbol(CSharpSymbolType.StringLiteral), Data);
}
@ -285,7 +289,11 @@ namespace Microsoft.AspNet.Razor.Tokenizer
}
else if (EndOfFile || ParserHelpers.IsNewLine(CurrentCharacter))
{
CurrentErrors.Add(new RazorError(RazorResources.ParseError_Unterminated_String_Literal, CurrentStart));
CurrentErrors.Add(
new RazorError(
RazorResources.ParseError_Unterminated_String_Literal,
CurrentStart,
length: 1 /* " */));
}
else
{
@ -300,7 +308,11 @@ namespace Microsoft.AspNet.Razor.Tokenizer
TakeUntil(c => c == '*');
if (EndOfFile)
{
CurrentErrors.Add(new RazorError(RazorResources.ParseError_BlockComment_Not_Terminated, CurrentStart));
CurrentErrors.Add(
new RazorError(
RazorResources.ParseError_BlockComment_Not_Terminated,
CurrentStart,
length: 1 /* end of file */));
return Transition(EndSymbol(CSharpSymbolType.Comment), Data);
}
if (CurrentCharacter == '*')