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:
parent
3b53f04518
commit
0688cd3ef7
|
|
@ -101,7 +101,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
foreach (var directive in directives)
|
foreach (var directive in directives)
|
||||||
{
|
{
|
||||||
_directiveParsers.Add(directive, handler);
|
_directiveParsers.Add(directive, () =>
|
||||||
|
{
|
||||||
|
EnsureDirectiveIsAtStartOfLine();
|
||||||
|
handler();
|
||||||
|
});
|
||||||
Keywords.Add(directive);
|
Keywords.Add(directive);
|
||||||
|
|
||||||
// These C# keywords are reserved for use in directives. It's an error to use them outside of
|
// 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);
|
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)
|
private void HandleDirective(DirectiveDescriptor descriptor)
|
||||||
{
|
{
|
||||||
Context.Builder.CurrentBlock.Type = BlockKindInternal.Directive;
|
Context.Builder.CurrentBlock.Type = BlockKindInternal.Directive;
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,20 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
internal static string FormatDiagnostic_CodeTarget_UnsupportedExtension(object p0, object p1)
|
internal static string FormatDiagnostic_CodeTarget_UnsupportedExtension(object p0, object p1)
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("Diagnostic_CodeTarget_UnsupportedExtension"), p0, 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)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -207,4 +207,7 @@
|
||||||
<data name="Diagnostic_CodeTarget_UnsupportedExtension" xml:space="preserve">
|
<data name="Diagnostic_CodeTarget_UnsupportedExtension" xml:space="preserve">
|
||||||
<value>The document type '{0}' does not support the extension '{1}'.</value>
|
<value>The document type '{0}' does not support the extension '{1}'.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DirectiveMustAppearAtStartOfLine" xml:space="preserve">
|
||||||
|
<value>The '{0}` directive must appear at the start of the line.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -10,6 +10,104 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
public class CSharpDirectivesTest : CsHtmlCodeParserTestBase
|
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]
|
[Fact]
|
||||||
public void DirectiveDescriptor_UnderstandsTypeTokens()
|
public void DirectiveDescriptor_UnderstandsTypeTokens()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
Factory.Markup(" ")),
|
Factory.Markup(" ")),
|
||||||
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)),
|
||||||
Factory.EmptyHtml()),
|
Factory.EmptyHtml()),
|
||||||
|
new RazorError(
|
||||||
|
Resources.FormatDirectiveMustAppearAtStartOfLine("section"),
|
||||||
|
new SourceLocation(16, 0, 16),
|
||||||
|
7),
|
||||||
new RazorError(
|
new RazorError(
|
||||||
LegacyResources.FormatParseError_Sections_Cannot_Be_Nested(LegacyResources.SectionExample_CS),
|
LegacyResources.FormatParseError_Sections_Cannot_Be_Nested(LegacyResources.SectionExample_CS),
|
||||||
new SourceLocation(15, 0, 15),
|
new SourceLocation(15, 0, 15),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue