Added RazorParserFeatureFlags and added support for minimized bool tag

helper bound attributes
 - Fixes #1678, #431
This commit is contained in:
Ajay Bhargav Baaskaran 2017-09-14 19:19:20 -07:00 committed by N. Taylor Mullen
parent edcf6857d1
commit bd8e9ecc31
31 changed files with 704 additions and 58 deletions

View File

@ -21,10 +21,14 @@ namespace Microsoft.AspNetCore.Razor.Language
public bool IsIndexerStringProperty { get; protected set; }
public bool IsIndexerBooleanProperty { get; protected set; }
public bool IsEnum { get; protected set; }
public bool IsStringProperty { get; protected set; }
public bool IsBooleanProperty { get; protected set; }
public string Name { get; protected set; }
public string IndexerNamePrefix { get; protected set; }

View File

@ -27,5 +27,27 @@ namespace Microsoft.AspNetCore.Razor.Language
return string.Equals(attribute.Kind, TagHelperConventions.DefaultKind, StringComparison.Ordinal);
}
internal static bool ExpectsStringValue(this BoundAttributeDescriptor attribute, string name)
{
if (attribute.IsStringProperty)
{
return true;
}
var isIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(name, attribute);
return isIndexerNameMatch && attribute.IsIndexerStringProperty;
}
internal static bool ExpectsBooleanValue(this BoundAttributeDescriptor attribute, string name)
{
if (attribute.IsBooleanProperty)
{
return true;
}
var isIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(name, attribute);
return isIndexerNameMatch && attribute.IsIndexerBooleanProperty;
}
}
}

View File

@ -35,6 +35,9 @@ namespace Microsoft.AspNetCore.Razor.Language
IsIndexerStringProperty = indexerTypeName == typeof(string).FullName || indexerTypeName == "string";
IsStringProperty = typeName == typeof(string).FullName || typeName == "string";
IsIndexerBooleanProperty = indexerTypeName == typeof(bool).FullName || indexerTypeName == "bool";
IsBooleanProperty = typeName == typeof(bool).FullName || typeName == "bool";
}
}
}

View File

@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Razor.Language
var imports = codeDocument.GetImportSyntaxTrees();
if (imports != null)
{
var importsVisitor = new ImportsVisitor(document, builder, namespaces);
var importsVisitor = new ImportsVisitor(document, builder, namespaces, syntaxTree.Options.FeatureFlags);
for (var j = 0; j < imports.Count; j++)
{
@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Razor.Language
}
var tagHelperPrefix = tagHelperContext?.Prefix;
var visitor = new MainSourceVisitor(document, builder, namespaces, tagHelperPrefix)
var visitor = new MainSourceVisitor(document, builder, namespaces, tagHelperPrefix, syntaxTree.Options.FeatureFlags)
{
FilePath = syntaxTree.Source.FilePath,
};
@ -151,12 +151,14 @@ namespace Microsoft.AspNetCore.Razor.Language
protected readonly IntermediateNodeBuilder _builder;
protected readonly DocumentIntermediateNode _document;
protected readonly Dictionary<string, SourceSpan?> _namespaces;
protected readonly RazorParserFeatureFlags _featureFlags;
public LoweringVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary<string, SourceSpan?> namespaces)
public LoweringVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary<string, SourceSpan?> namespaces, RazorParserFeatureFlags featureFlags)
{
_document = document;
_builder = builder;
_namespaces = namespaces;
_featureFlags = featureFlags;
}
public string FilePath { get; set; }
@ -331,8 +333,7 @@ namespace Microsoft.AspNetCore.Razor.Language
protected SourceSpan? BuildSourceSpanFromNode(SyntaxTreeNode node)
{
var location = node.Start;
if (location == SourceLocation.Undefined)
if (node == null || node.Start == SourceLocation.Undefined)
{
return null;
}
@ -351,8 +352,8 @@ namespace Microsoft.AspNetCore.Razor.Language
{
private readonly string _tagHelperPrefix;
public MainSourceVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary<string, SourceSpan?> namespaces, string tagHelperPrefix)
: base(document, builder, namespaces)
public MainSourceVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary<string, SourceSpan?> namespaces, string tagHelperPrefix, RazorParserFeatureFlags featureFlags)
: base(document, builder, namespaces, featureFlags)
{
_tagHelperPrefix = tagHelperPrefix;
}
@ -670,10 +671,11 @@ namespace Microsoft.AspNetCore.Razor.Language
if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attribute.Name))
{
if (attributeValueNode == null)
var isMinimizedAttribute = attributeValueNode == null;
if (isMinimizedAttribute && !_featureFlags.AllowMinimizedBooleanTagHelperAttributes)
{
// Minimized attributes are not valid for bound attributes. TagHelperBlockRewriter has already
// logged an error if it was a bound attribute; so we can skip.
// Minimized attributes are not valid for non-boolean bound attributes. TagHelperBlockRewriter
// has already logged an error if it was a non-boolean bound attribute; so we can skip.
continue;
}
@ -684,6 +686,14 @@ namespace Microsoft.AspNetCore.Razor.Language
return TagHelperMatchingConventions.CanSatisfyBoundAttribute(attribute.Name, a);
});
var expectsBooleanValue = associatedAttributeDescriptor.ExpectsBooleanValue(attribute.Name);
if (isMinimizedAttribute && !expectsBooleanValue)
{
// We do not allow minimized non-boolean bound attributes.
continue;
}
var setTagHelperProperty = new TagHelperPropertyIntermediateNode()
{
AttributeName = attribute.Name,
@ -695,7 +705,7 @@ namespace Microsoft.AspNetCore.Razor.Language
};
_builder.Push(setTagHelperProperty);
attributeValueNode.Accept(this);
attributeValueNode?.Accept(this);
_builder.Pop();
}
}
@ -720,8 +730,8 @@ namespace Microsoft.AspNetCore.Razor.Language
private class ImportsVisitor : LoweringVisitor
{
public ImportsVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary<string, SourceSpan?> namespaces)
: base(document, new ImportBuilder(builder), namespaces)
public ImportsVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, Dictionary<string, SourceSpan?> namespaces, RazorParserFeatureFlags featureFlags)
: base(document, new ImportBuilder(builder), namespaces, featureFlags)
{
}

View File

@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultRazorParserOptions : RazorParserOptions
{
public DefaultRazorParserOptions(DirectiveDescriptor[] directives, bool designTime, bool parseLeadingDirectives)
public DefaultRazorParserOptions(DirectiveDescriptor[] directives, bool designTime, bool parseLeadingDirectives, RazorLanguageVersion version)
{
if (directives == null)
{
@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.Razor.Language
Directives = directives;
DesignTime = designTime;
ParseLeadingDirectives = parseLeadingDirectives;
Version = version;
FeatureFlags = RazorParserFeatureFlags.Create(Version);
}
public override bool DesignTime { get; }
@ -25,5 +27,9 @@ namespace Microsoft.AspNetCore.Razor.Language
public override IReadOnlyCollection<DirectiveDescriptor> Directives { get; }
public override bool ParseLeadingDirectives { get; }
public override RazorLanguageVersion Version { get; }
internal override RazorParserFeatureFlags FeatureFlags { get; }
}
}

View File

@ -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.Linq;
@ -8,9 +9,10 @@ namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultRazorParserOptionsBuilder : RazorParserOptionsBuilder
{
public DefaultRazorParserOptionsBuilder(bool designTime)
public DefaultRazorParserOptionsBuilder(bool designTime, RazorLanguageVersion version)
{
DesignTime = designTime;
Version = version;
}
public override bool DesignTime { get; }
@ -19,9 +21,11 @@ namespace Microsoft.AspNetCore.Razor.Language
public override bool ParseLeadingDirectives { get; set; }
public override RazorLanguageVersion Version { get; }
public override RazorParserOptions Build()
{
return new DefaultRazorParserOptions(Directives.ToArray(), DesignTime, ParseLeadingDirectives);
return new DefaultRazorParserOptions(Directives.ToArray(), DesignTime, ParseLeadingDirectives, Version);
}
}
}

View File

@ -8,11 +8,13 @@ namespace Microsoft.AspNetCore.Razor.Language
internal class DefaultRazorParserOptionsFeature : RazorEngineFeatureBase, IRazorParserOptionsFeature
{
private readonly bool _designTime;
private readonly RazorLanguageVersion _version;
private IConfigureRazorParserOptionsFeature[] _configureOptions;
public DefaultRazorParserOptionsFeature(bool designTime)
public DefaultRazorParserOptionsFeature(bool designTime, RazorLanguageVersion version)
{
_designTime = designTime;
_version = version;
}
protected override void OnInitialized()
@ -22,7 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Language
public RazorParserOptions GetOptions()
{
var builder = new DefaultRazorParserOptionsBuilder(_designTime);
var builder = new DefaultRazorParserOptionsBuilder(_designTime, _version);
for (var i = 0; i < _configureOptions.Length; i++)
{
_configureOptions[i].Configure(builder);

View File

@ -53,7 +53,8 @@ namespace Microsoft.AspNetCore.Razor.Language
}
var errorSink = new ErrorSink();
var rewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, descriptors);
var rewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, descriptors, syntaxTree.Options.FeatureFlags);
var root = syntaxTree.Root;
root = rewriter.Rewrite(root, errorSink);

View File

@ -336,7 +336,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
}
// If we get there, this is the first time seeing this property so we need to evaluate the expression.
if (node.BoundAttribute.IsStringProperty || (node.IsIndexerNameMatch && node.BoundAttribute.IsIndexerStringProperty))
if (node.BoundAttribute.ExpectsStringValue(node.AttributeName))
{
if (DesignTime)
{
@ -407,7 +407,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
context.CodeWriter.WriteStartAssignment(GetPropertyAccessor(node));
}
RenderTagHelperAttributeInline(context, node, node.Source);
if (node.Children.Count == 0 &&
node.AttributeStructure == AttributeStructure.Minimized &&
node.BoundAttribute.ExpectsBooleanValue(node.AttributeName))
{
// If this is a minimized boolean attribute, set the value to true.
context.CodeWriter.Write("true");
}
else
{
RenderTagHelperAttributeInline(context, node, node.Source);
}
context.CodeWriter.WriteLine(";");
}
@ -429,7 +439,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
.Write(".");
}
RenderTagHelperAttributeInline(context, node, node.Source);
if (node.Children.Count == 0 &&
node.AttributeStructure == AttributeStructure.Minimized &&
node.BoundAttribute.ExpectsBooleanValue(node.AttributeName))
{
// If this is a minimized boolean attribute, set the value to true.
context.CodeWriter.Write("true");
}
else
{
RenderTagHelperAttributeInline(context, node, node.Source);
}
context.CodeWriter.WriteLine(";");
}

View File

@ -16,13 +16,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public static TagHelperBlockBuilder Rewrite(
string tagName,
bool validStructure,
RazorParserFeatureFlags featureFlags,
Block tag,
TagHelperBinding bindingResult,
ErrorSink errorSink)
{
// There will always be at least one child for the '<'.
var start = tag.Children.First().Start;
var attributes = GetTagAttributes(tagName, validStructure, tag, bindingResult, errorSink);
var attributes = GetTagAttributes(tagName, validStructure, tag, bindingResult, errorSink, featureFlags);
var tagMode = GetTagMode(tagName, tag, bindingResult, errorSink);
return new TagHelperBlockBuilder(tagName, tagMode, start, attributes, bindingResult);
@ -33,7 +34,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
bool validStructure,
Block tagBlock,
TagHelperBinding bindingResult,
ErrorSink errorSink)
ErrorSink errorSink,
RazorParserFeatureFlags featureFlags)
{
var attributes = new List<TagHelperAttributeNode>();
@ -61,10 +63,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
SourceLocation? errorLocation = null;
// Check if it's a bound attribute that is minimized or if it's a bound non-string attribute that
// is null or whitespace.
if ((result.IsBoundAttribute && result.AttributeValueNode == null) ||
(result.IsBoundNonStringAttribute &&
// Check if it's a non-boolean bound attribute that is minimized or if it's a bound
// non-string attribute that has null or whitespace content.
var isMinimized = result.AttributeValueNode == null;
var isValidMinimizedAttribute = featureFlags.AllowMinimizedBooleanTagHelperAttributes && result.IsBoundBooleanAttribute;
if ((isMinimized &&
result.IsBoundAttribute &&
!isValidMinimizedAttribute) ||
(!isMinimized &&
result.IsBoundNonStringAttribute &&
IsNullOrWhitespaceAttributeValue(result.AttributeValueNode)))
{
errorLocation = GetAttributeNameStartLocation(child);
@ -690,9 +697,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
var isBoundAttribute = firstBoundAttribute != null;
var isBoundNonStringAttribute = isBoundAttribute &&
!(firstBoundAttribute.IsStringProperty ||
(TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(name, firstBoundAttribute) && firstBoundAttribute.IsIndexerStringProperty));
var isBoundNonStringAttribute = isBoundAttribute && !firstBoundAttribute.ExpectsStringValue(name);
var isBoundBooleanAttribute = isBoundAttribute && firstBoundAttribute.ExpectsBooleanValue(name);
var isMissingDictionaryKey = isBoundAttribute &&
firstBoundAttribute.IndexerNamePrefix != null &&
name.Length == firstBoundAttribute.IndexerNamePrefix.Length;
@ -709,6 +715,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AttributeName = name,
IsBoundAttribute = isBoundAttribute,
IsBoundNonStringAttribute = isBoundNonStringAttribute,
IsBoundBooleanAttribute = isBoundBooleanAttribute,
IsMissingDictionaryKey = isMissingDictionaryKey,
IsDuplicateAttribute = isDuplicateAttribute
};
@ -766,6 +773,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public bool IsBoundNonStringAttribute { get; set; }
public bool IsBoundBooleanAttribute { get; set; }
public bool IsMissingDictionaryKey { get; set; }
public bool IsDuplicateAttribute { get; set; }

View File

@ -44,8 +44,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private readonly Stack<BlockBuilder> _blockStack;
private TagHelperBlockTracker _currentTagHelperTracker;
private BlockBuilder _currentBlock;
private RazorParserFeatureFlags _featureFlags;
public TagHelperParseTreeRewriter(string tagHelperPrefix, IEnumerable<TagHelperDescriptor> descriptors)
public TagHelperParseTreeRewriter(
string tagHelperPrefix,
IEnumerable<TagHelperDescriptor> descriptors,
RazorParserFeatureFlags featureFlags)
{
_tagHelperPrefix = tagHelperPrefix;
_tagHelperBinder = new TagHelperBinder(tagHelperPrefix, descriptors);
@ -53,6 +57,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
_blockStack = new Stack<BlockBuilder>();
_attributeValueBuilder = new StringBuilder();
_htmlAttributeTracker = new List<KeyValuePair<string, string>>();
_featureFlags = featureFlags;
}
private TagBlockTracker CurrentTracker => _trackerStack.Count > 0 ? _trackerStack.Peek() : null;
@ -225,6 +230,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var builder = TagHelperBlockRewriter.Rewrite(
tagName,
validTagStructure,
_featureFlags,
tagBlock,
tagHelperBinding,
errorSink);

View File

@ -682,6 +682,20 @@ namespace Microsoft.AspNetCore.Razor.Language
internal static string FormatTagHelperPrefixDirective_Description()
=> GetString("TagHelperPrefixDirective_Description");
/// <summary>
/// Provided value for razor language version is unsupported or invalid: '{0}'.
/// </summary>
internal static string InvalidRazorLanguageVersion
{
get => GetString("InvalidRazorLanguageVersion");
}
/// <summary>
/// Provided value for razor language version is unsupported or invalid: '{0}'.
/// </summary>
internal static string FormatInvalidRazorLanguageVersion(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidRazorLanguageVersion"), p0);
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Razor.Language
internal static void AddRuntimeDefaults(IRazorEngineBuilder builder)
{
// Configure options
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false));
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorParserOptions.LatestRazorLanguageVersion));
builder.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
// Intermediate Node Passes
@ -116,7 +116,7 @@ namespace Microsoft.AspNetCore.Razor.Language
internal static void AddDesignTimeDefaults(IRazorEngineBuilder builder)
{
// Configure options
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: true));
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: true, version: RazorParserOptions.LatestRazorLanguageVersion));
builder.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: true));
builder.Features.Add(new SuppressChecksumOptionsFeature());

View File

@ -0,0 +1,33 @@
// 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.
namespace Microsoft.AspNetCore.Razor.Language
{
public enum RazorLanguageVersion
{
Version1_0 = 1,
Version1_1 = 2,
Version2_0 = 3,
Version2_1 = 4,
}
internal static class RazorLanguageVersionExtensions
{
internal static bool IsValid(this RazorLanguageVersion version)
{
switch (version)
{
case RazorLanguageVersion.Version1_0:
case RazorLanguageVersion.Version1_1:
case RazorLanguageVersion.Version2_0:
case RazorLanguageVersion.Version2_1:
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,39 @@
// 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;
namespace Microsoft.AspNetCore.Razor.Language
{
internal abstract class RazorParserFeatureFlags
{
public static RazorParserFeatureFlags Create(RazorLanguageVersion version)
{
if (!version.IsValid())
{
throw new ArgumentException(Resources.FormatInvalidRazorLanguageVersion(version.ToString()));
}
var allowMinimizedBooleanTagHelperAttributes = false;
if (version == RazorLanguageVersion.Version2_1)
{
allowMinimizedBooleanTagHelperAttributes = true;
}
return new DefaultRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes);
}
public abstract bool AllowMinimizedBooleanTagHelperAttributes { get; }
private class DefaultRazorParserFeatureFlags : RazorParserFeatureFlags
{
public DefaultRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes)
{
AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes;
}
public override bool AllowMinimizedBooleanTagHelperAttributes { get; }
}
}
}

View File

@ -8,9 +8,15 @@ namespace Microsoft.AspNetCore.Razor.Language
{
public abstract class RazorParserOptions
{
internal static readonly RazorLanguageVersion LatestRazorLanguageVersion = RazorLanguageVersion.Version2_1;
public static RazorParserOptions CreateDefault()
{
return new DefaultRazorParserOptions(Array.Empty<DirectiveDescriptor>(), designTime: false, parseLeadingDirectives: false);
return new DefaultRazorParserOptions(
Array.Empty<DirectiveDescriptor>(),
designTime: false,
parseLeadingDirectives: false,
version: LatestRazorLanguageVersion);
}
public static RazorParserOptions Create(Action<RazorParserOptionsBuilder> configure)
@ -20,7 +26,7 @@ namespace Microsoft.AspNetCore.Razor.Language
throw new ArgumentNullException(nameof(configure));
}
var builder = new DefaultRazorParserOptionsBuilder(designTime: false);
var builder = new DefaultRazorParserOptionsBuilder(designTime: false, version: LatestRazorLanguageVersion);
configure(builder);
var options = builder.Build();
@ -34,7 +40,7 @@ namespace Microsoft.AspNetCore.Razor.Language
throw new ArgumentNullException(nameof(configure));
}
var builder = new DefaultRazorParserOptionsBuilder(designTime: true);
var builder = new DefaultRazorParserOptionsBuilder(designTime: true, version: LatestRazorLanguageVersion);
configure(builder);
var options = builder.Build();
@ -54,5 +60,9 @@ namespace Microsoft.AspNetCore.Razor.Language
/// In a future release this may be updated to include all leading directive content.
/// </remarks>
public abstract bool ParseLeadingDirectives { get; }
public virtual RazorLanguageVersion Version { get; } = LatestRazorLanguageVersion;
internal virtual RazorParserFeatureFlags FeatureFlags { get; }
}
}

View File

@ -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;
namespace Microsoft.AspNetCore.Razor.Language
@ -13,6 +14,8 @@ namespace Microsoft.AspNetCore.Razor.Language
public abstract bool ParseLeadingDirectives { get; set; }
public virtual RazorLanguageVersion Version { get; }
public abstract RazorParserOptions Build();
}
}

View File

@ -261,4 +261,7 @@
<data name="TagHelperPrefixDirective_Description" xml:space="preserve">
<value>Specify a prefix that is required in an element name for it to be included in Tag Helper processing.</value>
</data>
<data name="InvalidRazorLanguageVersion" xml:space="preserve">
<value>Provided value for razor language version is unsupported or invalid: '{0}'.</value>
</data>
</root>

View File

@ -95,5 +95,185 @@ namespace Microsoft.AspNetCore.Razor.Language
// Assert
Assert.False(isDefault);
}
[Fact]
public void ExpectsStringValue_ReturnsTrue_ForStringProperty()
{
// Arrange
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
tagHelperBuilder.TypeName("TestTagHelper");
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
builder
.Name("test")
.PropertyName("BoundProp")
.TypeName(typeof(string).FullName);
var descriptor = builder.Build();
// Act
var result = descriptor.ExpectsStringValue("test");
// Assert
Assert.True(result);
}
[Fact]
public void ExpectsStringValue_ReturnsFalse_ForNonStringProperty()
{
// Arrange
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
tagHelperBuilder.TypeName("TestTagHelper");
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
builder
.Name("test")
.PropertyName("BoundProp")
.TypeName(typeof(bool).FullName);
var descriptor = builder.Build();
// Act
var result = descriptor.ExpectsStringValue("test");
// Assert
Assert.False(result);
}
[Fact]
public void ExpectsStringValue_ReturnsTrue_StringIndexerAndNameMatch()
{
// Arrange
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
tagHelperBuilder.TypeName("TestTagHelper");
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
builder
.Name("test")
.PropertyName("BoundProp")
.TypeName("System.Collection.Generic.IDictionary<string, string>")
.AsDictionary("prefix-test-", typeof(string).FullName);
var descriptor = builder.Build();
// Act
var result = descriptor.ExpectsStringValue("prefix-test-key");
// Assert
Assert.True(result);
}
[Fact]
public void ExpectsStringValue_ReturnsFalse_StringIndexerAndNameMismatch()
{
// Arrange
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
tagHelperBuilder.TypeName("TestTagHelper");
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
builder
.Name("test")
.PropertyName("BoundProp")
.TypeName("System.Collection.Generic.IDictionary<string, string>")
.AsDictionary("prefix-test-", typeof(string).FullName);
var descriptor = builder.Build();
// Act
var result = descriptor.ExpectsStringValue("test");
// Assert
Assert.False(result);
}
[Fact]
public void ExpectsBooleanValue_ReturnsTrue_ForBooleanProperty()
{
// Arrange
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
tagHelperBuilder.TypeName("TestTagHelper");
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
builder
.Name("test")
.PropertyName("BoundProp")
.TypeName(typeof(bool).FullName);
var descriptor = builder.Build();
// Act
var result = descriptor.ExpectsBooleanValue("test");
// Assert
Assert.True(result);
}
[Fact]
public void ExpectsBooleanValue_ReturnsFalse_ForNonBooleanProperty()
{
// Arrange
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
tagHelperBuilder.TypeName("TestTagHelper");
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
builder
.Name("test")
.PropertyName("BoundProp")
.TypeName(typeof(int).FullName);
var descriptor = builder.Build();
// Act
var result = descriptor.ExpectsBooleanValue("test");
// Assert
Assert.False(result);
}
[Fact]
public void ExpectsBooleanValue_ReturnsTrue_BooleanIndexerAndNameMatch()
{
// Arrange
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
tagHelperBuilder.TypeName("TestTagHelper");
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
builder
.Name("test")
.PropertyName("BoundProp")
.TypeName("System.Collection.Generic.IDictionary<string, bool>")
.AsDictionary("prefix-test-", typeof(bool).FullName);
var descriptor = builder.Build();
// Act
var result = descriptor.ExpectsBooleanValue("prefix-test-key");
// Assert
Assert.True(result);
}
[Fact]
public void ExpectsBooleanValue_ReturnsFalse_BooleanIndexerAndNameMismatch()
{
// Arrange
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
tagHelperBuilder.TypeName("TestTagHelper");
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
builder
.Name("test")
.PropertyName("BoundProp")
.TypeName("System.Collection.Generic.IDictionary<string, bool>")
.AsDictionary("prefix-test-", typeof(bool).FullName);
var descriptor = builder.Build();
// Act
var result = descriptor.ExpectsBooleanValue("test");
// Assert
Assert.False(result);
}
}
}

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.Language
var engine = RazorEngine.CreateEmpty(builder =>
{
builder.Phases.Add(phase);
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false));
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorParserOptions.LatestRazorLanguageVersion));
});
var codeDocument = TestRazorCodeDocument.CreateEmpty();
@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Razor.Language
var engine = RazorEngine.CreateEmpty((builder) =>
{
builder.Phases.Add(phase);
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false));
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorParserOptions.LatestRazorLanguageVersion));
builder.Features.Add(new MyParserOptionsFeature());
});
@ -58,9 +58,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var engine = RazorEngine.CreateEmpty((builder) =>
{
builder.Phases.Add(phase);
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false));
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorParserOptions.LatestRazorLanguageVersion));
builder.Features.Add(new MyParserOptionsFeature());
});
var imports = new[]

View File

@ -46,6 +46,43 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
}
}
public static IEnumerable<TagHelperDescriptor> MinimizedBooleanTagHelperDescriptors
{
get
{
return new[]
{
CreateTagHelperDescriptor(
tagName: "span",
typeName: "SpanTagHelper",
assemblyName: "TestAssembly"),
CreateTagHelperDescriptor(
tagName: "div",
typeName: "DivTagHelper",
assemblyName: "TestAssembly"),
CreateTagHelperDescriptor(
tagName: "input",
typeName: "InputTagHelper",
assemblyName: "TestAssembly",
attributes: new Action<BoundAttributeDescriptorBuilder>[]
{
builder => builder
.Name("value")
.PropertyName("FooProp")
.TypeName("System.String"),
builder => builder
.Name("bound")
.PropertyName("BoundProp")
.TypeName("System.Boolean"),
builder => builder
.Name("age")
.PropertyName("AgeProp")
.TypeName("System.Int32"),
})
};
}
}
public static IEnumerable<TagHelperDescriptor> CssSelectorTagHelperDescriptors
{
get
@ -267,6 +304,22 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
.RequireAttributeDescriptor(attribute => attribute.Name("input-bound-required-string"))
.RequireAttributeDescriptor(attribute => attribute.Name("input-unbound-required")),
}),
CreateTagHelperDescriptor(
tagName: "div",
typeName: "DivTagHelper",
assemblyName: "TestAssembly",
attributes: new Action<BoundAttributeDescriptorBuilder>[]
{
builder => builder
.Name("boundbool")
.PropertyName("BoundBoolProp")
.TypeName(typeof(bool).FullName),
builder => builder
.Name("booldict")
.PropertyName("BoolDictProp")
.TypeName("System.Collections.Generic.IDictionary<string, bool>")
.AsDictionaryAttribute("booldict-prefix-", typeof(bool).FullName),
}),
};
}
}

View File

@ -118,11 +118,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var descriptors = new[]
{
TagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
.TagMatchingRuleDescriptor(rule =>
.TagMatchingRuleDescriptor(rule =>
rule
.RequireTagName("*")
.RequireAttributeDescriptor(attribute => attribute.Name("bound")))
.BoundAttributeDescriptor(attribute =>
.BoundAttributeDescriptor(attribute =>
attribute
.Name("[item]")
.PropertyName("ListItems")
@ -222,7 +222,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var descriptors = new TagHelperDescriptor[]
{
TagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
.TagMatchingRuleDescriptor(rule =>
.TagMatchingRuleDescriptor(rule =>
rule
.RequireTagName("input")
.RequireTagStructure(TagStructure.WithoutEndTag))
@ -320,7 +320,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var descriptors = new TagHelperDescriptor[]
{
TagHelperDescriptorBuilder.Create("InputTagHelper1", "SomeAssembly")
.TagMatchingRuleDescriptor(rule =>
.TagMatchingRuleDescriptor(rule =>
rule
.RequireTagName("input")
.RequireTagStructure(structure1))
@ -2251,7 +2251,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
TagHelperDescriptorBuilder.Create("mythTagHelper", "SomeAssembly")
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("myth"))
.BoundAttributeDescriptor(attribute =>
.BoundAttributeDescriptor(attribute =>
attribute
.Name("bound")
.PropertyName("Bound")
@ -3901,7 +3901,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var descriptors = new TagHelperDescriptor[]
{
TagHelperDescriptorBuilder.Create("InputTagHelper1", "SomeAssembly")
.TagMatchingRuleDescriptor(rule =>
.TagMatchingRuleDescriptor(rule =>
rule
.RequireTagName("input")
.RequireAttributeDescriptor(attribute => attribute.Name("unbound-required")))
@ -3959,5 +3959,107 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Act & Assert
EvaluateData(descriptors, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
}
[Fact]
public void Rewrite_UnderstandsMinimizedBooleanBoundAttributes()
{
// Arrange
var documentContent = "<input boundbool boundbooldict-key />";
var descriptors = new TagHelperDescriptor[]
{
TagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
.TagMatchingRuleDescriptor(rule =>
rule
.RequireTagName("input"))
.BoundAttributeDescriptor(attribute =>
attribute
.Name("boundbool")
.PropertyName("BoundBoolProp")
.TypeName(typeof(bool).FullName))
.BoundAttributeDescriptor(attribute =>
attribute
.Name("boundbooldict")
.PropertyName("BoundBoolDictProp")
.TypeName("System.Collections.Generic.IDictionary<string, bool>")
.AsDictionary("boundbooldict-", typeof(bool).FullName))
.Build(),
};
var expectedOutput = new MarkupBlock(
new MarkupTagHelperBlock(
"input",
TagMode.SelfClosing,
attributes: new List<TagHelperAttributeNode>()
{
new TagHelperAttributeNode("boundbool", null, AttributeStructure.Minimized),
new TagHelperAttributeNode("boundbooldict-key", null, AttributeStructure.Minimized),
}));
// Act & Assert
EvaluateData(descriptors, documentContent, expectedOutput, new RazorError[] { });
}
[Fact]
public void Rewrite_FeatureDisabled_AddsErrorForMinimizedBooleanBoundAttributes()
{
// Arrange
var documentContent = "<input boundbool boundbooldict-key />";
var descriptors = new TagHelperDescriptor[]
{
TagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
.TagMatchingRuleDescriptor(rule =>
rule
.RequireTagName("input"))
.BoundAttributeDescriptor(attribute =>
attribute
.Name("boundbool")
.PropertyName("BoundBoolProp")
.TypeName(typeof(bool).FullName))
.BoundAttributeDescriptor(attribute =>
attribute
.Name("boundbooldict")
.PropertyName("BoundBoolDictProp")
.TypeName("System.Collections.Generic.IDictionary<string, bool>")
.AsDictionary("boundbooldict-", typeof(bool).FullName))
.Build(),
};
var featureFlags = new TestRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes: false);
var expectedOutput = new MarkupBlock(
new MarkupTagHelperBlock(
"input",
TagMode.SelfClosing,
attributes: new List<TagHelperAttributeNode>()
{
new TagHelperAttributeNode("boundbool", null, AttributeStructure.Minimized),
new TagHelperAttributeNode("boundbooldict-key", null, AttributeStructure.Minimized),
}));
var expectedErrors = new[]
{
new RazorError(
"Attribute 'boundbool' on tag helper element 'input' requires a value. Tag helper bound attributes of type 'System.Boolean' cannot be empty or contain only whitespace.",
new SourceLocation(7, 0, 7),
length: 9),
new RazorError(
"Attribute 'boundbooldict-key' on tag helper element 'input' requires a value. Tag helper bound attributes of type 'System.Boolean' cannot be empty or contain only whitespace.",
new SourceLocation(17, 0, 17),
length: 17),
};
// Act & Assert
EvaluateData(descriptors, documentContent, expectedOutput, expectedErrors, featureFlags: featureFlags);
}
private class TestRazorParserFeatureFlags : RazorParserFeatureFlags
{
public TestRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes)
{
AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes;
}
public override bool AllowMinimizedBooleanTagHelperAttributes { get; }
}
}
}

View File

@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var errorSink = new ErrorSink();
var parseResult = ParseDocument(documentContent);
var document = parseResult.Root;
var parseTreeRewriter = new TagHelperParseTreeRewriter(null, Enumerable.Empty<TagHelperDescriptor>());
var parseTreeRewriter = new TagHelperParseTreeRewriter(null, Enumerable.Empty<TagHelperDescriptor>(), parseResult.Options.FeatureFlags);
// Assert - Guard
var rootBlock = Assert.IsType<Block>(document);

View File

@ -51,11 +51,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
string documentContent,
MarkupBlock expectedOutput,
IEnumerable<RazorError> expectedErrors,
string tagHelperPrefix = null)
string tagHelperPrefix = null,
RazorParserFeatureFlags featureFlags = null)
{
var syntaxTree = ParseDocument(documentContent);
var errorSink = new ErrorSink();
var parseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, descriptors);
var parseTreeRewriter = new TagHelperParseTreeRewriter(
tagHelperPrefix,
descriptors,
featureFlags ?? syntaxTree.Options.FeatureFlags);
var actualTree = parseTreeRewriter.Rewrite(syntaxTree.Root, errorSink);
var allErrors = syntaxTree.Diagnostics.Concat(errorSink.Errors.Select(error => RazorDiagnostic.Create(error)));

View File

@ -0,0 +1,41 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.Language
{
public class RazorParserFeatureFlagsTest
{
[Fact]
public void Create_LatestVersion_AllowsMinimizedBooleanTagHelperAttributes()
{
// Arrange & Act
var context = RazorParserFeatureFlags.Create(RazorLanguageVersion.Version2_1);
// Assert
Assert.True(context.AllowMinimizedBooleanTagHelperAttributes);
}
[Fact]
public void Create_OlderVersion_DoesNotAllowMinimizedBooleanTagHelperAttributes()
{
// Arrange & Act
var context = RazorParserFeatureFlags.Create(RazorLanguageVersion.Version1_1);
// Assert
Assert.False(context.AllowMinimizedBooleanTagHelperAttributes);
}
[Fact]
public void Create_UnknownVersion_Throws()
{
// Arrange, Act & Assert
var exception = Assert.Throws<ArgumentException>(
() => RazorParserFeatureFlags.Create(0));
Assert.Equal("Provided value for razor language version is unsupported or invalid: '0'.", exception.Message);
}
}
}

View File

@ -15,4 +15,6 @@
input-unbound-required="hello2"
catchall-unbound-required
input-bound-required-string="world" />
<div boundbool booldict-prefix-key></div>
<div>Tag helper with unmatched bound boolean attributes.</div>
</p>

View File

@ -7,6 +7,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
{
private global::TestNamespace.CatchAllTagHelper __TestNamespace_CatchAllTagHelper;
private global::TestNamespace.InputTagHelper __TestNamespace_InputTagHelper;
private global::DivTagHelper __DivTagHelper;
#pragma warning disable 219
private void __RazorDirectiveTokenHelpers__() {
((System.Action)(() => {
@ -32,6 +33,10 @@ global::System.Object __typeHelper = "*, TestAssembly";
__TestNamespace_InputTagHelper = CreateTagHelper<global::TestNamespace.InputTagHelper>();
__TestNamespace_CatchAllTagHelper = CreateTagHelper<global::TestNamespace.CatchAllTagHelper>();
__TestNamespace_InputTagHelper.BoundRequiredString = "world";
__DivTagHelper = CreateTagHelper<global::DivTagHelper>();
__DivTagHelper.BoundBoolProp = true;
__DivTagHelper.BoolDictProp["key"] = true;
__DivTagHelper = CreateTagHelper<global::DivTagHelper>();
__TestNamespace_CatchAllTagHelper = CreateTagHelper<global::TestNamespace.CatchAllTagHelper>();
}
#pragma warning restore 1998

View File

@ -4,6 +4,7 @@ Document -
DefaultTagHelperRuntime -
FieldDeclaration - - private - global::TestNamespace.CatchAllTagHelper - __TestNamespace_CatchAllTagHelper
FieldDeclaration - - private - global::TestNamespace.InputTagHelper - __TestNamespace_InputTagHelper
FieldDeclaration - - private - global::DivTagHelper - __DivTagHelper
DesignTimeDirective -
DirectiveToken - (14:0,14 [17] MinimizedTagHelpers.cshtml) - "*, TestAssembly"
CSharpCode -
@ -15,7 +16,7 @@ Document -
MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (31:0,31 [4] MinimizedTagHelpers.cshtml)
IntermediateToken - (31:0,31 [4] MinimizedTagHelpers.cshtml) - Html - \n\n
TagHelper - (35:2,0 [647] MinimizedTagHelpers.cshtml) - p - TagMode.StartTagAndEndTag
TagHelper - (35:2,0 [762] MinimizedTagHelpers.cshtml) - p - TagMode.StartTagAndEndTag
DefaultTagHelperBody -
HtmlContent - (64:2,29 [34] MinimizedTagHelpers.cshtml)
IntermediateToken - (64:2,29 [6] MinimizedTagHelpers.cshtml) - Html - \n
@ -84,8 +85,24 @@ Document -
HtmlContent - (667:16,40 [5] MinimizedTagHelpers.cshtml)
IntermediateToken - (667:16,40 [5] MinimizedTagHelpers.cshtml) - Html - world
DefaultTagHelperExecute -
HtmlContent - (676:16,49 [2] MinimizedTagHelpers.cshtml)
IntermediateToken - (676:16,49 [2] MinimizedTagHelpers.cshtml) - Html - \n
HtmlContent - (676:16,49 [6] MinimizedTagHelpers.cshtml)
IntermediateToken - (676:16,49 [6] MinimizedTagHelpers.cshtml) - Html - \n
TagHelper - (682:17,4 [41] MinimizedTagHelpers.cshtml) - div - TagMode.StartTagAndEndTag
DefaultTagHelperBody -
DefaultTagHelperCreate - - DivTagHelper
DefaultTagHelperProperty - - boundbool - bool DivTagHelper.BoundBoolProp - HtmlAttributeValueStyle.Minimized
DefaultTagHelperProperty - - booldict-prefix-key - System.Collections.Generic.IDictionary<string, bool> DivTagHelper.BoolDictProp - HtmlAttributeValueStyle.Minimized
DefaultTagHelperExecute -
HtmlContent - (723:17,45 [6] MinimizedTagHelpers.cshtml)
IntermediateToken - (723:17,45 [6] MinimizedTagHelpers.cshtml) - Html - \n
TagHelper - (729:18,4 [62] MinimizedTagHelpers.cshtml) - div - TagMode.StartTagAndEndTag
DefaultTagHelperBody -
HtmlContent - (734:18,9 [51] MinimizedTagHelpers.cshtml)
IntermediateToken - (734:18,9 [51] MinimizedTagHelpers.cshtml) - Html - Tag helper with unmatched bound boolean attributes.
DefaultTagHelperCreate - - DivTagHelper
DefaultTagHelperExecute -
HtmlContent - (791:18,66 [2] MinimizedTagHelpers.cshtml)
IntermediateToken - (791:18,66 [2] MinimizedTagHelpers.cshtml) - Html - \n
DefaultTagHelperCreate - - TestNamespace.CatchAllTagHelper
DefaultTagHelperHtmlAttribute - - catchall-unbound-required - HtmlAttributeValueStyle.Minimized
DefaultTagHelperExecute -

View File

@ -1,5 +1,5 @@
Source Location: (14:0,14 [17] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MinimizedTagHelpers.cshtml)
|"*, TestAssembly"|
Generated Location: (603:12,37 [17] )
Generated Location: (657:13,37 [17] )
|"*, TestAssembly"|

View File

@ -1,4 +1,4 @@
#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MinimizedTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "cc93b363a32adf077cc406265e403db466e4ae7d"
#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MinimizedTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "ab8c9a5af38d07138a55ae82942b4a97fe3c9025"
// <auto-generated/>
#pragma warning disable 1591
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
@ -33,6 +33,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
}
private global::TestNamespace.CatchAllTagHelper __TestNamespace_CatchAllTagHelper;
private global::TestNamespace.InputTagHelper __TestNamespace_InputTagHelper;
private global::DivTagHelper __DivTagHelper;
#pragma warning disable 1998
public async System.Threading.Tasks.Task ExecuteAsync()
{
@ -128,6 +129,41 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
}
Write(__tagHelperExecutionContext.Output);
__tagHelperExecutionContext = __tagHelperScopeManager.End();
WriteLiteral("\r\n ");
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("div", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.StartTagAndEndTag, "test", async() => {
}
);
__DivTagHelper = CreateTagHelper<global::DivTagHelper>();
__tagHelperExecutionContext.Add(__DivTagHelper);
__DivTagHelper.BoundBoolProp = true;
__tagHelperExecutionContext.AddTagHelperAttribute("boundbool", __DivTagHelper.BoundBoolProp, global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.Minimized);
if (__DivTagHelper.BoolDictProp == null)
{
throw new InvalidOperationException(InvalidTagHelperIndexerAssignment("booldict-prefix-key", "DivTagHelper", "BoolDictProp"));
}
__DivTagHelper.BoolDictProp["key"] = true;
__tagHelperExecutionContext.AddTagHelperAttribute("booldict-prefix-key", __DivTagHelper.BoolDictProp["key"], global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.Minimized);
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Write(__tagHelperExecutionContext.Output);
__tagHelperExecutionContext = __tagHelperScopeManager.End();
WriteLiteral("\r\n ");
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("div", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.StartTagAndEndTag, "test", async() => {
WriteLiteral("Tag helper with unmatched bound boolean attributes.");
}
);
__DivTagHelper = CreateTagHelper<global::DivTagHelper>();
__tagHelperExecutionContext.Add(__DivTagHelper);
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Write(__tagHelperExecutionContext.Output);
__tagHelperExecutionContext = __tagHelperScopeManager.End();
WriteLiteral("\r\n");
}
);

View File

@ -11,10 +11,11 @@ Document -
DefaultTagHelperRuntime -
FieldDeclaration - - private - global::TestNamespace.CatchAllTagHelper - __TestNamespace_CatchAllTagHelper
FieldDeclaration - - private - global::TestNamespace.InputTagHelper - __TestNamespace_InputTagHelper
FieldDeclaration - - private - global::DivTagHelper - __DivTagHelper
MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (33:1,0 [2] MinimizedTagHelpers.cshtml)
IntermediateToken - (33:1,0 [2] MinimizedTagHelpers.cshtml) - Html - \n
TagHelper - (35:2,0 [647] MinimizedTagHelpers.cshtml) - p - TagMode.StartTagAndEndTag
TagHelper - (35:2,0 [762] MinimizedTagHelpers.cshtml) - p - TagMode.StartTagAndEndTag
DefaultTagHelperBody -
HtmlContent - (64:2,29 [34] MinimizedTagHelpers.cshtml)
IntermediateToken - (64:2,29 [6] MinimizedTagHelpers.cshtml) - Html - \n
@ -63,8 +64,24 @@ Document -
DefaultTagHelperHtmlAttribute - - catchall-unbound-required - HtmlAttributeValueStyle.Minimized
PreallocatedTagHelperProperty - (667:16,40 [5] MinimizedTagHelpers.cshtml) - __tagHelperAttribute_6 - input-bound-required-string - BoundRequiredString
DefaultTagHelperExecute -
HtmlContent - (676:16,49 [2] MinimizedTagHelpers.cshtml)
IntermediateToken - (676:16,49 [2] MinimizedTagHelpers.cshtml) - Html - \n
HtmlContent - (676:16,49 [6] MinimizedTagHelpers.cshtml)
IntermediateToken - (676:16,49 [6] MinimizedTagHelpers.cshtml) - Html - \n
TagHelper - (682:17,4 [41] MinimizedTagHelpers.cshtml) - div - TagMode.StartTagAndEndTag
DefaultTagHelperBody -
DefaultTagHelperCreate - - DivTagHelper
DefaultTagHelperProperty - - boundbool - bool DivTagHelper.BoundBoolProp - HtmlAttributeValueStyle.Minimized
DefaultTagHelperProperty - - booldict-prefix-key - System.Collections.Generic.IDictionary<string, bool> DivTagHelper.BoolDictProp - HtmlAttributeValueStyle.Minimized
DefaultTagHelperExecute -
HtmlContent - (723:17,45 [6] MinimizedTagHelpers.cshtml)
IntermediateToken - (723:17,45 [6] MinimizedTagHelpers.cshtml) - Html - \n
TagHelper - (729:18,4 [62] MinimizedTagHelpers.cshtml) - div - TagMode.StartTagAndEndTag
DefaultTagHelperBody -
HtmlContent - (734:18,9 [51] MinimizedTagHelpers.cshtml)
IntermediateToken - (734:18,9 [51] MinimizedTagHelpers.cshtml) - Html - Tag helper with unmatched bound boolean attributes.
DefaultTagHelperCreate - - DivTagHelper
DefaultTagHelperExecute -
HtmlContent - (791:18,66 [2] MinimizedTagHelpers.cshtml)
IntermediateToken - (791:18,66 [2] MinimizedTagHelpers.cshtml) - Html - \n
DefaultTagHelperCreate - - TestNamespace.CatchAllTagHelper
DefaultTagHelperHtmlAttribute - - catchall-unbound-required - HtmlAttributeValueStyle.Minimized
DefaultTagHelperExecute -