Log errors if directives do not start at beginning of line.

- Updated tests to validate expectations.
- Added two additional tests to validate extensible directives and built-in directives get start at line verification.
This commit is contained in:
N. Taylor Mullen 2017-05-19 15:14:59 -07:00
parent 3b53f04518
commit 0688cd3ef7
5 changed files with 148 additions and 1 deletions

View File

@ -101,7 +101,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
foreach (var directive in directives)
{
_directiveParsers.Add(directive, handler);
_directiveParsers.Add(directive, () =>
{
EnsureDirectiveIsAtStartOfLine();
handler();
});
Keywords.Add(directive);
// These C# keywords are reserved for use in directives. It's an error to use them outside of
@ -1519,6 +1523,30 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
MapDirectives(RemoveTagHelperDirective, SyntaxConstants.CSharp.RemoveTagHelperKeyword);
}
private void EnsureDirectiveIsAtStartOfLine()
{
// 1 is the offset of the @ transition for the directive.
if (CurrentStart.CharacterIndex > 1)
{
var index = CurrentStart.AbsoluteIndex - 1;
var lineStart = CurrentStart.AbsoluteIndex - CurrentStart.CharacterIndex;
while (--index >= lineStart)
{
var @char = Context.SourceDocument[index];
if (!char.IsWhiteSpace(@char))
{
var currentDirective = CurrentSymbol.Content;
Context.ErrorSink.OnError(
CurrentStart,
Resources.FormatDirectiveMustAppearAtStartOfLine(currentDirective),
length: currentDirective.Length);
break;
}
}
}
}
private void HandleDirective(DirectiveDescriptor descriptor)
{
Context.Builder.CurrentBlock.Type = BlockKindInternal.Directive;

View File

@ -430,6 +430,20 @@ namespace Microsoft.AspNetCore.Razor.Language
internal static string FormatDiagnostic_CodeTarget_UnsupportedExtension(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("Diagnostic_CodeTarget_UnsupportedExtension"), p0, p1);
/// <summary>
/// The '{0}` directive must appear at the start of the line.
/// </summary>
internal static string DirectiveMustAppearAtStartOfLine
{
get => GetString("DirectiveMustAppearAtStartOfLine");
}
/// <summary>
/// The '{0}` directive must appear at the start of the line.
/// </summary>
internal static string FormatDirectiveMustAppearAtStartOfLine(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("DirectiveMustAppearAtStartOfLine"), p0);
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -207,4 +207,7 @@
<data name="Diagnostic_CodeTarget_UnsupportedExtension" xml:space="preserve">
<value>The document type '{0}' does not support the extension '{1}'.</value>
</data>
<data name="DirectiveMustAppearAtStartOfLine" xml:space="preserve">
<value>The '{0}` directive must appear at the start of the line.</value>
</data>
</root>

View File

@ -10,6 +10,104 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
public class CSharpDirectivesTest : CsHtmlCodeParserTestBase
{
[Fact]
public void ExtensibleDirectiveDoesNotErorrIfNotAtStartOfLineBecauseOfWhitespace()
{
// Arrange
var descriptor = DirectiveDescriptor.CreateDirective(
"custom",
DirectiveKind.SingleLine,
b => b.AddTypeToken());
// Act & Assert
ParseCodeBlockTest(Environment.NewLine + " @custom System.Text.Encoding.ASCIIEncoding",
new[] { descriptor },
new DirectiveBlock(
new DirectiveChunkGenerator(descriptor),
Factory.Code(Environment.NewLine + " ").AsStatement(),
Factory.CodeTransition(),
Factory.MetaCode("custom").Accepts(AcceptedCharactersInternal.None),
Factory.Span(SpanKindInternal.Code, " ", markup: false).Accepts(AcceptedCharactersInternal.WhiteSpace),
Factory.Span(SpanKindInternal.Code, "System.Text.Encoding.ASCIIEncoding", markup: false)
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
.Accepts(AcceptedCharactersInternal.NonWhiteSpace)));
}
[Fact]
public void BuiltInDirectiveDoesNotErorrIfNotAtStartOfLineBecauseOfWhitespace()
{
// Act & Assert
ParseCodeBlockTest(Environment.NewLine + " @addTagHelper \"*, Foo\"",
Enumerable.Empty<DirectiveDescriptor>(),
new DirectiveBlock(
Factory.Code(Environment.NewLine + " ").AsStatement(),
Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ")
.Accepts(AcceptedCharactersInternal.None),
Factory.Code("\"*, Foo\"")
.AsAddTagHelper("\"*, Foo\"")));
}
[Fact]
public void BuiltInDirectiveErorrsIfNotAtStartOfLine()
{
// Act & Assert
ParseCodeBlockTest("{ @addTagHelper \"*, Foo\"" + Environment.NewLine + "}",
Enumerable.Empty<DirectiveDescriptor>(),
new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
Factory.Code(" ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null, atEndOfSpan: false),
new DirectiveBlock(
Factory.CodeTransition(),
Factory.MetaCode(SyntaxConstants.CSharp.AddTagHelperKeyword + " ")
.Accepts(AcceptedCharactersInternal.None),
Factory.Code("\"*, Foo\"")
.AsAddTagHelper("\"*, Foo\"")),
Factory.Code(Environment.NewLine).AsStatement(),
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
new RazorError(
Resources.FormatDirectiveMustAppearAtStartOfLine("addTagHelper"),
new SourceLocation(4, 0, 4),
12));
}
[Fact]
public void ExtensibleDirectiveErorrsIfNotAtStartOfLine()
{
// Arrange
var descriptor = DirectiveDescriptor.CreateDirective(
"custom",
DirectiveKind.SingleLine,
b => b.AddTypeToken());
// Act & Assert
ParseCodeBlockTest(
"{ @custom System.Text.Encoding.ASCIIEncoding" + Environment.NewLine + "}",
new[] { descriptor },
new StatementBlock(
Factory.MetaCode("{").Accepts(AcceptedCharactersInternal.None),
Factory.Code(" ")
.AsStatement()
.AutoCompleteWith(autoCompleteString: null, atEndOfSpan: false),
new DirectiveBlock(
new DirectiveChunkGenerator(descriptor),
Factory.CodeTransition(),
Factory.MetaCode("custom").Accepts(AcceptedCharactersInternal.None),
Factory.Span(SpanKindInternal.Code, " ", markup: false).Accepts(AcceptedCharactersInternal.WhiteSpace),
Factory.Span(SpanKindInternal.Code, "System.Text.Encoding.ASCIIEncoding", markup: false)
.With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0]))
.Accepts(AcceptedCharactersInternal.NonWhiteSpace),
Factory.Span(SpanKindInternal.Markup, Environment.NewLine, markup: false).Accepts(AcceptedCharactersInternal.WhiteSpace)),
Factory.EmptyCSharp().AsStatement(),
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
new RazorError(
Resources.FormatDirectiveMustAppearAtStartOfLine("custom"),
new SourceLocation(4, 0, 4),
6));
}
[Fact]
public void DirectiveDescriptor_UnderstandsTypeTokens()
{

View File

@ -164,6 +164,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Factory.Markup(" ")),
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
Factory.EmptyHtml()),
new RazorError(
Resources.FormatDirectiveMustAppearAtStartOfLine("section"),
new SourceLocation(16, 0, 16),
7),
new RazorError(
LegacyResources.FormatParseError_Sections_Cannot_Be_Nested(LegacyResources.SectionExample_CS),
new SourceLocation(15, 0, 15),