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

View File

@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Razor.Language
{ {
internal class DefaultRazorParserOptions : RazorParserOptions 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) if (directives == null)
{ {
@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.Razor.Language
Directives = directives; Directives = directives;
DesignTime = designTime; DesignTime = designTime;
ParseLeadingDirectives = parseLeadingDirectives; ParseLeadingDirectives = parseLeadingDirectives;
Version = version;
FeatureFlags = RazorParserFeatureFlags.Create(Version);
} }
public override bool DesignTime { get; } public override bool DesignTime { get; }
@ -25,5 +27,9 @@ namespace Microsoft.AspNetCore.Razor.Language
public override IReadOnlyCollection<DirectiveDescriptor> Directives { get; } public override IReadOnlyCollection<DirectiveDescriptor> Directives { get; }
public override bool ParseLeadingDirectives { 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. // 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. // 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.Collections.Generic;
using System.Linq; using System.Linq;
@ -8,9 +9,10 @@ namespace Microsoft.AspNetCore.Razor.Language
{ {
internal class DefaultRazorParserOptionsBuilder : RazorParserOptionsBuilder internal class DefaultRazorParserOptionsBuilder : RazorParserOptionsBuilder
{ {
public DefaultRazorParserOptionsBuilder(bool designTime) public DefaultRazorParserOptionsBuilder(bool designTime, RazorLanguageVersion version)
{ {
DesignTime = designTime; DesignTime = designTime;
Version = version;
} }
public override bool DesignTime { get; } public override bool DesignTime { get; }
@ -19,9 +21,11 @@ namespace Microsoft.AspNetCore.Razor.Language
public override bool ParseLeadingDirectives { get; set; } public override bool ParseLeadingDirectives { get; set; }
public override RazorLanguageVersion Version { get; }
public override RazorParserOptions Build() 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 internal class DefaultRazorParserOptionsFeature : RazorEngineFeatureBase, IRazorParserOptionsFeature
{ {
private readonly bool _designTime; private readonly bool _designTime;
private readonly RazorLanguageVersion _version;
private IConfigureRazorParserOptionsFeature[] _configureOptions; private IConfigureRazorParserOptionsFeature[] _configureOptions;
public DefaultRazorParserOptionsFeature(bool designTime) public DefaultRazorParserOptionsFeature(bool designTime, RazorLanguageVersion version)
{ {
_designTime = designTime; _designTime = designTime;
_version = version;
} }
protected override void OnInitialized() protected override void OnInitialized()
@ -22,7 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Language
public RazorParserOptions GetOptions() public RazorParserOptions GetOptions()
{ {
var builder = new DefaultRazorParserOptionsBuilder(_designTime); var builder = new DefaultRazorParserOptionsBuilder(_designTime, _version);
for (var i = 0; i < _configureOptions.Length; i++) for (var i = 0; i < _configureOptions.Length; i++)
{ {
_configureOptions[i].Configure(builder); _configureOptions[i].Configure(builder);

View File

@ -53,7 +53,8 @@ namespace Microsoft.AspNetCore.Razor.Language
} }
var errorSink = new ErrorSink(); var errorSink = new ErrorSink();
var rewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, descriptors); var rewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, descriptors, syntaxTree.Options.FeatureFlags);
var root = syntaxTree.Root; var root = syntaxTree.Root;
root = rewriter.Rewrite(root, errorSink); 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 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) if (DesignTime)
{ {
@ -407,7 +407,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
context.CodeWriter.WriteStartAssignment(GetPropertyAccessor(node)); 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(";"); context.CodeWriter.WriteLine(";");
} }
@ -429,7 +439,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
.Write("."); .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(";"); context.CodeWriter.WriteLine(";");
} }

View File

@ -16,13 +16,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public static TagHelperBlockBuilder Rewrite( public static TagHelperBlockBuilder Rewrite(
string tagName, string tagName,
bool validStructure, bool validStructure,
RazorParserFeatureFlags featureFlags,
Block tag, Block tag,
TagHelperBinding bindingResult, TagHelperBinding bindingResult,
ErrorSink errorSink) ErrorSink errorSink)
{ {
// There will always be at least one child for the '<'. // There will always be at least one child for the '<'.
var start = tag.Children.First().Start; 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); var tagMode = GetTagMode(tagName, tag, bindingResult, errorSink);
return new TagHelperBlockBuilder(tagName, tagMode, start, attributes, bindingResult); return new TagHelperBlockBuilder(tagName, tagMode, start, attributes, bindingResult);
@ -33,7 +34,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
bool validStructure, bool validStructure,
Block tagBlock, Block tagBlock,
TagHelperBinding bindingResult, TagHelperBinding bindingResult,
ErrorSink errorSink) ErrorSink errorSink,
RazorParserFeatureFlags featureFlags)
{ {
var attributes = new List<TagHelperAttributeNode>(); var attributes = new List<TagHelperAttributeNode>();
@ -61,10 +63,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{ {
SourceLocation? errorLocation = null; SourceLocation? errorLocation = null;
// Check if it's a bound attribute that is minimized or if it's a bound non-string attribute that // Check if it's a non-boolean bound attribute that is minimized or if it's a bound
// is null or whitespace. // non-string attribute that has null or whitespace content.
if ((result.IsBoundAttribute && result.AttributeValueNode == null) || var isMinimized = result.AttributeValueNode == null;
(result.IsBoundNonStringAttribute && var isValidMinimizedAttribute = featureFlags.AllowMinimizedBooleanTagHelperAttributes && result.IsBoundBooleanAttribute;
if ((isMinimized &&
result.IsBoundAttribute &&
!isValidMinimizedAttribute) ||
(!isMinimized &&
result.IsBoundNonStringAttribute &&
IsNullOrWhitespaceAttributeValue(result.AttributeValueNode))) IsNullOrWhitespaceAttributeValue(result.AttributeValueNode)))
{ {
errorLocation = GetAttributeNameStartLocation(child); errorLocation = GetAttributeNameStartLocation(child);
@ -690,9 +697,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{ {
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors); var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
var isBoundAttribute = firstBoundAttribute != null; var isBoundAttribute = firstBoundAttribute != null;
var isBoundNonStringAttribute = isBoundAttribute && var isBoundNonStringAttribute = isBoundAttribute && !firstBoundAttribute.ExpectsStringValue(name);
!(firstBoundAttribute.IsStringProperty || var isBoundBooleanAttribute = isBoundAttribute && firstBoundAttribute.ExpectsBooleanValue(name);
(TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(name, firstBoundAttribute) && firstBoundAttribute.IsIndexerStringProperty));
var isMissingDictionaryKey = isBoundAttribute && var isMissingDictionaryKey = isBoundAttribute &&
firstBoundAttribute.IndexerNamePrefix != null && firstBoundAttribute.IndexerNamePrefix != null &&
name.Length == firstBoundAttribute.IndexerNamePrefix.Length; name.Length == firstBoundAttribute.IndexerNamePrefix.Length;
@ -709,6 +715,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
AttributeName = name, AttributeName = name,
IsBoundAttribute = isBoundAttribute, IsBoundAttribute = isBoundAttribute,
IsBoundNonStringAttribute = isBoundNonStringAttribute, IsBoundNonStringAttribute = isBoundNonStringAttribute,
IsBoundBooleanAttribute = isBoundBooleanAttribute,
IsMissingDictionaryKey = isMissingDictionaryKey, IsMissingDictionaryKey = isMissingDictionaryKey,
IsDuplicateAttribute = isDuplicateAttribute IsDuplicateAttribute = isDuplicateAttribute
}; };
@ -766,6 +773,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public bool IsBoundNonStringAttribute { get; set; } public bool IsBoundNonStringAttribute { get; set; }
public bool IsBoundBooleanAttribute { get; set; }
public bool IsMissingDictionaryKey { get; set; } public bool IsMissingDictionaryKey { get; set; }
public bool IsDuplicateAttribute { 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 readonly Stack<BlockBuilder> _blockStack;
private TagHelperBlockTracker _currentTagHelperTracker; private TagHelperBlockTracker _currentTagHelperTracker;
private BlockBuilder _currentBlock; private BlockBuilder _currentBlock;
private RazorParserFeatureFlags _featureFlags;
public TagHelperParseTreeRewriter(string tagHelperPrefix, IEnumerable<TagHelperDescriptor> descriptors) public TagHelperParseTreeRewriter(
string tagHelperPrefix,
IEnumerable<TagHelperDescriptor> descriptors,
RazorParserFeatureFlags featureFlags)
{ {
_tagHelperPrefix = tagHelperPrefix; _tagHelperPrefix = tagHelperPrefix;
_tagHelperBinder = new TagHelperBinder(tagHelperPrefix, descriptors); _tagHelperBinder = new TagHelperBinder(tagHelperPrefix, descriptors);
@ -53,6 +57,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
_blockStack = new Stack<BlockBuilder>(); _blockStack = new Stack<BlockBuilder>();
_attributeValueBuilder = new StringBuilder(); _attributeValueBuilder = new StringBuilder();
_htmlAttributeTracker = new List<KeyValuePair<string, string>>(); _htmlAttributeTracker = new List<KeyValuePair<string, string>>();
_featureFlags = featureFlags;
} }
private TagBlockTracker CurrentTracker => _trackerStack.Count > 0 ? _trackerStack.Peek() : null; private TagBlockTracker CurrentTracker => _trackerStack.Count > 0 ? _trackerStack.Peek() : null;
@ -225,6 +230,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var builder = TagHelperBlockRewriter.Rewrite( var builder = TagHelperBlockRewriter.Rewrite(
tagName, tagName,
validTagStructure, validTagStructure,
_featureFlags,
tagBlock, tagBlock,
tagHelperBinding, tagHelperBinding,
errorSink); errorSink);

View File

@ -682,6 +682,20 @@ namespace Microsoft.AspNetCore.Razor.Language
internal static string FormatTagHelperPrefixDirective_Description() internal static string FormatTagHelperPrefixDirective_Description()
=> GetString("TagHelperPrefixDirective_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) private static string GetString(string name, params string[] formatterNames)
{ {
var value = _resourceManager.GetString(name); var value = _resourceManager.GetString(name);

View File

@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Razor.Language
internal static void AddRuntimeDefaults(IRazorEngineBuilder builder) internal static void AddRuntimeDefaults(IRazorEngineBuilder builder)
{ {
// Configure options // 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)); builder.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
// Intermediate Node Passes // Intermediate Node Passes
@ -116,7 +116,7 @@ namespace Microsoft.AspNetCore.Razor.Language
internal static void AddDesignTimeDefaults(IRazorEngineBuilder builder) internal static void AddDesignTimeDefaults(IRazorEngineBuilder builder)
{ {
// Configure options // 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 DefaultRazorCodeGenerationOptionsFeature(designTime: true));
builder.Features.Add(new SuppressChecksumOptionsFeature()); 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 public abstract class RazorParserOptions
{ {
internal static readonly RazorLanguageVersion LatestRazorLanguageVersion = RazorLanguageVersion.Version2_1;
public static RazorParserOptions CreateDefault() 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) public static RazorParserOptions Create(Action<RazorParserOptionsBuilder> configure)
@ -20,7 +26,7 @@ namespace Microsoft.AspNetCore.Razor.Language
throw new ArgumentNullException(nameof(configure)); throw new ArgumentNullException(nameof(configure));
} }
var builder = new DefaultRazorParserOptionsBuilder(designTime: false); var builder = new DefaultRazorParserOptionsBuilder(designTime: false, version: LatestRazorLanguageVersion);
configure(builder); configure(builder);
var options = builder.Build(); var options = builder.Build();
@ -34,7 +40,7 @@ namespace Microsoft.AspNetCore.Razor.Language
throw new ArgumentNullException(nameof(configure)); throw new ArgumentNullException(nameof(configure));
} }
var builder = new DefaultRazorParserOptionsBuilder(designTime: true); var builder = new DefaultRazorParserOptionsBuilder(designTime: true, version: LatestRazorLanguageVersion);
configure(builder); configure(builder);
var options = builder.Build(); 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. /// In a future release this may be updated to include all leading directive content.
/// </remarks> /// </remarks>
public abstract bool ParseLeadingDirectives { get; } 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. // 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. // 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.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language namespace Microsoft.AspNetCore.Razor.Language
@ -13,6 +14,8 @@ namespace Microsoft.AspNetCore.Razor.Language
public abstract bool ParseLeadingDirectives { get; set; } public abstract bool ParseLeadingDirectives { get; set; }
public virtual RazorLanguageVersion Version { get; }
public abstract RazorParserOptions Build(); public abstract RazorParserOptions Build();
} }
} }

View File

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

View File

@ -95,5 +95,185 @@ namespace Microsoft.AspNetCore.Razor.Language
// Assert // Assert
Assert.False(isDefault); 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 => var engine = RazorEngine.CreateEmpty(builder =>
{ {
builder.Phases.Add(phase); 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(); var codeDocument = TestRazorCodeDocument.CreateEmpty();
@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Razor.Language
var engine = RazorEngine.CreateEmpty((builder) => var engine = RazorEngine.CreateEmpty((builder) =>
{ {
builder.Phases.Add(phase); 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()); builder.Features.Add(new MyParserOptionsFeature());
}); });
@ -58,9 +58,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var engine = RazorEngine.CreateEmpty((builder) => var engine = RazorEngine.CreateEmpty((builder) =>
{ {
builder.Phases.Add(phase); 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()); builder.Features.Add(new MyParserOptionsFeature());
}); });
var imports = new[] 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 public static IEnumerable<TagHelperDescriptor> CssSelectorTagHelperDescriptors
{ {
get get
@ -267,6 +304,22 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
.RequireAttributeDescriptor(attribute => attribute.Name("input-bound-required-string")) .RequireAttributeDescriptor(attribute => attribute.Name("input-bound-required-string"))
.RequireAttributeDescriptor(attribute => attribute.Name("input-unbound-required")), .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

@ -3959,5 +3959,107 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Act & Assert // Act & Assert
EvaluateData(descriptors, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors); 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 errorSink = new ErrorSink();
var parseResult = ParseDocument(documentContent); var parseResult = ParseDocument(documentContent);
var document = parseResult.Root; 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 // Assert - Guard
var rootBlock = Assert.IsType<Block>(document); var rootBlock = Assert.IsType<Block>(document);

View File

@ -51,11 +51,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
string documentContent, string documentContent,
MarkupBlock expectedOutput, MarkupBlock expectedOutput,
IEnumerable<RazorError> expectedErrors, IEnumerable<RazorError> expectedErrors,
string tagHelperPrefix = null) string tagHelperPrefix = null,
RazorParserFeatureFlags featureFlags = null)
{ {
var syntaxTree = ParseDocument(documentContent); var syntaxTree = ParseDocument(documentContent);
var errorSink = new ErrorSink(); 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 actualTree = parseTreeRewriter.Rewrite(syntaxTree.Root, errorSink);
var allErrors = syntaxTree.Diagnostics.Concat(errorSink.Errors.Select(error => RazorDiagnostic.Create(error))); 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" input-unbound-required="hello2"
catchall-unbound-required catchall-unbound-required
input-bound-required-string="world" /> input-bound-required-string="world" />
<div boundbool booldict-prefix-key></div>
<div>Tag helper with unmatched bound boolean attributes.</div>
</p> </p>

View File

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

View File

@ -4,6 +4,7 @@ Document -
DefaultTagHelperRuntime - DefaultTagHelperRuntime -
FieldDeclaration - - private - global::TestNamespace.CatchAllTagHelper - __TestNamespace_CatchAllTagHelper FieldDeclaration - - private - global::TestNamespace.CatchAllTagHelper - __TestNamespace_CatchAllTagHelper
FieldDeclaration - - private - global::TestNamespace.InputTagHelper - __TestNamespace_InputTagHelper FieldDeclaration - - private - global::TestNamespace.InputTagHelper - __TestNamespace_InputTagHelper
FieldDeclaration - - private - global::DivTagHelper - __DivTagHelper
DesignTimeDirective - DesignTimeDirective -
DirectiveToken - (14:0,14 [17] MinimizedTagHelpers.cshtml) - "*, TestAssembly" DirectiveToken - (14:0,14 [17] MinimizedTagHelpers.cshtml) - "*, TestAssembly"
CSharpCode - CSharpCode -
@ -15,7 +16,7 @@ Document -
MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (31:0,31 [4] MinimizedTagHelpers.cshtml) HtmlContent - (31:0,31 [4] MinimizedTagHelpers.cshtml)
IntermediateToken - (31:0,31 [4] MinimizedTagHelpers.cshtml) - Html - \n\n 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 - DefaultTagHelperBody -
HtmlContent - (64:2,29 [34] MinimizedTagHelpers.cshtml) HtmlContent - (64:2,29 [34] MinimizedTagHelpers.cshtml)
IntermediateToken - (64:2,29 [6] MinimizedTagHelpers.cshtml) - Html - \n IntermediateToken - (64:2,29 [6] MinimizedTagHelpers.cshtml) - Html - \n
@ -84,8 +85,24 @@ Document -
HtmlContent - (667:16,40 [5] MinimizedTagHelpers.cshtml) HtmlContent - (667:16,40 [5] MinimizedTagHelpers.cshtml)
IntermediateToken - (667:16,40 [5] MinimizedTagHelpers.cshtml) - Html - world IntermediateToken - (667:16,40 [5] MinimizedTagHelpers.cshtml) - Html - world
DefaultTagHelperExecute - DefaultTagHelperExecute -
HtmlContent - (676:16,49 [2] MinimizedTagHelpers.cshtml) HtmlContent - (676:16,49 [6] MinimizedTagHelpers.cshtml)
IntermediateToken - (676:16,49 [2] MinimizedTagHelpers.cshtml) - Html - \n 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 DefaultTagHelperCreate - - TestNamespace.CatchAllTagHelper
DefaultTagHelperHtmlAttribute - - catchall-unbound-required - HtmlAttributeValueStyle.Minimized DefaultTagHelperHtmlAttribute - - catchall-unbound-required - HtmlAttributeValueStyle.Minimized
DefaultTagHelperExecute - DefaultTagHelperExecute -

View File

@ -1,5 +1,5 @@
Source Location: (14:0,14 [17] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MinimizedTagHelpers.cshtml) Source Location: (14:0,14 [17] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/MinimizedTagHelpers.cshtml)
|"*, TestAssembly"| |"*, TestAssembly"|
Generated Location: (603:12,37 [17] ) Generated Location: (657:13,37 [17] )
|"*, TestAssembly"| |"*, 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/> // <auto-generated/>
#pragma warning disable 1591 #pragma warning disable 1591
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles 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.CatchAllTagHelper __TestNamespace_CatchAllTagHelper;
private global::TestNamespace.InputTagHelper __TestNamespace_InputTagHelper; private global::TestNamespace.InputTagHelper __TestNamespace_InputTagHelper;
private global::DivTagHelper __DivTagHelper;
#pragma warning disable 1998 #pragma warning disable 1998
public async System.Threading.Tasks.Task ExecuteAsync() public async System.Threading.Tasks.Task ExecuteAsync()
{ {
@ -128,6 +129,41 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles
} }
Write(__tagHelperExecutionContext.Output); Write(__tagHelperExecutionContext.Output);
__tagHelperExecutionContext = __tagHelperScopeManager.End(); __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"); WriteLiteral("\r\n");
} }
); );

View File

@ -11,10 +11,11 @@ Document -
DefaultTagHelperRuntime - DefaultTagHelperRuntime -
FieldDeclaration - - private - global::TestNamespace.CatchAllTagHelper - __TestNamespace_CatchAllTagHelper FieldDeclaration - - private - global::TestNamespace.CatchAllTagHelper - __TestNamespace_CatchAllTagHelper
FieldDeclaration - - private - global::TestNamespace.InputTagHelper - __TestNamespace_InputTagHelper FieldDeclaration - - private - global::TestNamespace.InputTagHelper - __TestNamespace_InputTagHelper
FieldDeclaration - - private - global::DivTagHelper - __DivTagHelper
MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (33:1,0 [2] MinimizedTagHelpers.cshtml) HtmlContent - (33:1,0 [2] MinimizedTagHelpers.cshtml)
IntermediateToken - (33:1,0 [2] MinimizedTagHelpers.cshtml) - Html - \n 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 - DefaultTagHelperBody -
HtmlContent - (64:2,29 [34] MinimizedTagHelpers.cshtml) HtmlContent - (64:2,29 [34] MinimizedTagHelpers.cshtml)
IntermediateToken - (64:2,29 [6] MinimizedTagHelpers.cshtml) - Html - \n IntermediateToken - (64:2,29 [6] MinimizedTagHelpers.cshtml) - Html - \n
@ -63,8 +64,24 @@ Document -
DefaultTagHelperHtmlAttribute - - catchall-unbound-required - HtmlAttributeValueStyle.Minimized DefaultTagHelperHtmlAttribute - - catchall-unbound-required - HtmlAttributeValueStyle.Minimized
PreallocatedTagHelperProperty - (667:16,40 [5] MinimizedTagHelpers.cshtml) - __tagHelperAttribute_6 - input-bound-required-string - BoundRequiredString PreallocatedTagHelperProperty - (667:16,40 [5] MinimizedTagHelpers.cshtml) - __tagHelperAttribute_6 - input-bound-required-string - BoundRequiredString
DefaultTagHelperExecute - DefaultTagHelperExecute -
HtmlContent - (676:16,49 [2] MinimizedTagHelpers.cshtml) HtmlContent - (676:16,49 [6] MinimizedTagHelpers.cshtml)
IntermediateToken - (676:16,49 [2] MinimizedTagHelpers.cshtml) - Html - \n 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 DefaultTagHelperCreate - - TestNamespace.CatchAllTagHelper
DefaultTagHelperHtmlAttribute - - catchall-unbound-required - HtmlAttributeValueStyle.Minimized DefaultTagHelperHtmlAttribute - - catchall-unbound-required - HtmlAttributeValueStyle.Minimized
DefaultTagHelperExecute - DefaultTagHelperExecute -