Skip to content

Commit

Permalink
Merge pull request #1758 from riganti/attribute-initializer-fix
Browse files Browse the repository at this point in the history
Attribute value initializer of PropertyDirective changes
  • Loading branch information
exyi authored Jan 9, 2024
2 parents d376126 + 99be07b commit ce78339
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public interface IAbstractDirectiveAttributeReference
{
TypeReferenceBindingParserNode TypeSyntax { get; }
IdentifierNameBindingParserNode NameSyntax { get; }
LiteralExpressionBindingParserNode Initializer { get; }
BindingParserNode Initializer { get; }
ITypeDescriptor? Type { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public interface IAbstractTreeBuilder

IAbstractViewModuleDirective BuildViewModuleDirective(DothtmlDirectiveNode directiveNode, string modulePath, string resourceName);
IAbstractPropertyDeclarationDirective BuildPropertyDeclarationDirective(DothtmlDirectiveNode directive, TypeReferenceBindingParserNode typeSyntax, SimpleNameBindingParserNode nameSyntax, BindingParserNode? initializer, IList<IAbstractDirectiveAttributeReference> resolvedAttributes, BindingParserNode valueSyntaxRoot, ImmutableList<NamespaceImport> imports);
IAbstractDirectiveAttributeReference BuildPropertyDeclarationAttributeReference(DothtmlDirectiveNode directiveNode, IdentifierNameBindingParserNode propertyNameSyntax, TypeReferenceBindingParserNode typeSyntax, LiteralExpressionBindingParserNode initializer, ImmutableList<NamespaceImport> imports);
IAbstractDirectiveAttributeReference BuildPropertyDeclarationAttributeReference(DothtmlDirectiveNode directiveNode, IdentifierNameBindingParserNode propertyNameSyntax, TypeReferenceBindingParserNode typeSyntax, BindingParserNode initializer, ImmutableList<NamespaceImport> imports);
IAbstractPropertyBinding BuildPropertyBinding(IPropertyDescriptor property, IAbstractBinding binding, DothtmlAttributeNode? sourceAttributeNode);

IAbstractPropertyControl BuildPropertyControl(IPropertyDescriptor property, IAbstractControl? control, DothtmlElementNode? wrapperElementNode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private IEnumerable<object> InstantiateAttributes(DothtmlDirectiveNode dothtmlDi
(name, attributes) => {

var attributeType = (attributes.First().Type as ResolvedTypeDescriptor)?.Type;
var properties = attributes.Select(a => (name: a.NameSyntax.Name, value: a.Initializer.Value));
var properties = attributes.Select(a => (name: a.NameSyntax.Name, value: (a.Initializer as LiteralExpressionBindingParserNode)?.Value ?? ""));

return (attributeType, properties);
}).ToList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ public class ResolvedPropertyDirectiveAttributeReference : IAbstractDirectiveAtt
public TypeReferenceBindingParserNode TypeSyntax { get; }
public IdentifierNameBindingParserNode NameSyntax { get; }
public ITypeDescriptor? Type { get; set; }
public LiteralExpressionBindingParserNode Initializer { get; }
public BindingParserNode Initializer { get; }

public ResolvedPropertyDirectiveAttributeReference(
DirectiveCompilationService directiveService,
DothtmlDirectiveNode directiveNode,
TypeReferenceBindingParserNode typeReferenceBindingParserNode,
IdentifierNameBindingParserNode attributePropertyNameReference,
LiteralExpressionBindingParserNode initializer,
BindingParserNode initializer,
ImmutableList<NamespaceImport> imports)
{
DirectiveNode = directiveNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public IAbstractDirectiveAttributeReference BuildPropertyDeclarationAttributeRef
DothtmlDirectiveNode directiveNode,
IdentifierNameBindingParserNode propertyNameSyntax,
TypeReferenceBindingParserNode typeSyntax,
LiteralExpressionBindingParserNode initializer,
BindingParserNode initializer,
ImmutableList<NamespaceImport> imports)
{
return new ResolvedPropertyDirectiveAttributeReference(directiveService, directiveNode, typeSyntax, propertyNameSyntax, initializer, imports);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ private AttributeInfo GetAttributeInfo(BindingParserNode attributeReference, Dot
return new AttributeInfo(
typeRef,
new SimpleNameBindingParserNode("") { StartPosition = attributeReference.EndPosition },
new LiteralExpressionBindingParserNode("") { StartPosition = attributeReference.EndPosition });
new SimpleNameBindingParserNode("") { StartPosition = attributeReference.EndPosition });
}

var attributePropertyReference = assignment.FirstExpression as MemberAccessBindingParserNode;
var initializer = assignment.SecondExpression as LiteralExpressionBindingParserNode;
var initializer = assignment.SecondExpression;
var attributeTypeReference = attributePropertyReference?.TargetExpression;
var attributePropertyNameReference = attributePropertyReference?.MemberNameExpression;

Expand All @@ -93,10 +93,9 @@ private AttributeInfo GetAttributeInfo(BindingParserNode attributeReference, Dot
attributeTypeReference = attributeTypeReference ?? new SimpleNameBindingParserNode("");
attributePropertyNameReference = attributePropertyNameReference ?? new SimpleNameBindingParserNode("") { StartPosition = attributeTypeReference.EndPosition };
}
if (initializer == null)
if (assignment.SecondExpression is not LiteralExpressionBindingParserNode)
{
directiveNode.AddError($"Value for property {attributeTypeReference.ToDisplayString()} of attribute {attributePropertyNameReference.ToDisplayString()} is missing or not a constant.");
initializer = new LiteralExpressionBindingParserNode("") { StartPosition = attributePropertyNameReference.EndPosition };
}

var type = new ActualTypeReferenceBindingParserNode(attributeTypeReference);
Expand All @@ -120,7 +119,7 @@ protected override ImmutableList<DotvvmProperty> CreateArtefact(IReadOnlyList<IA
protected abstract bool HasPropertyType(IAbstractPropertyDeclarationDirective directive);
protected abstract DotvvmProperty TryCreateDotvvmPropertyFromDirective(IAbstractPropertyDeclarationDirective propertyDeclarationDirective);

private record AttributeInfo(ActualTypeReferenceBindingParserNode Type, IdentifierNameBindingParserNode Name, LiteralExpressionBindingParserNode Initializer);
private record AttributeInfo(ActualTypeReferenceBindingParserNode Type, IdentifierNameBindingParserNode Name, BindingParserNode Initializer);
}

public class ResolvedPropertyDeclarationDirectiveCompiler : PropertyDeclarationDirectiveCompiler
Expand Down
29 changes: 29 additions & 0 deletions src/Tests/AssertEx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DotVVM.Framework.Compilation.Parser.Binding.Parser;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DotVVM.Framework.Tests
{
public static class AssertEx
{
public static void BindingNode(BindingParserNode node, string expectedDisplayString, int start, int length, bool hasErrors = false)
{
Assert.AreEqual(expectedDisplayString, node.ToDisplayString(), $"Node {node.GetType().Name}: display string incorrect.");
Assert.AreEqual(start, node.StartPosition, $"Node {node.GetType().Name}: Start position incorrect.");
Assert.AreEqual(length, node.Length, $"Node {node.GetType().Name}: Length incorrect.");

if (hasErrors)
{
Assert.IsTrue(node.HasNodeErrors);
}
else
{
Assert.IsFalse(node.HasNodeErrors);
}
}
}
}
15 changes: 1 addition & 14 deletions src/Tests/Parser/Binding/BindingParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1490,20 +1490,7 @@ public void BindingParser_GenericMethodCall_MultipleGenericArguments()
}

private static void AssertNode(BindingParserNode node, string expectedDisplayString, int start, int length, bool hasErrors = false)
{
Assert.AreEqual(expectedDisplayString, node.ToDisplayString(), $"Node {node.GetType().Name}: display string incorrect.");
Assert.AreEqual(start, node.StartPosition, $"Node {node.GetType().Name}: Start position incorrect.");
Assert.AreEqual(length, node.Length, $"Node {node.GetType().Name}: Length incorrect.");

if (hasErrors)
{
Assert.IsTrue(node.HasNodeErrors);
}
else
{
Assert.IsFalse(node.HasNodeErrors);
}
}
=> AssertEx.BindingNode(node, expectedDisplayString, start, length, hasErrors);

private static string SkipWhitespaces(string str) => string.Join("", str.Where(c => !char.IsWhiteSpace(c)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ @viewModel object
Assert.AreEqual("MyProperty", property.NameSyntax.Name);

Assert.AreEqual(4, property.Attributes.Count);

AssertEx.BindingNode(property.Attributes[0].Initializer, "", 19, 0);
AssertEx.BindingNode(property.Attributes[1].Initializer, "", 28, 0);
AssertEx.BindingNode(property.Attributes[2].Initializer, "", 42, 1, true);
AssertEx.BindingNode(property.Attributes[3].Initializer, "t", 104, 2);
}

[TestMethod]
Expand All @@ -45,19 +50,37 @@ @viewModel object
}

[TestMethod]
public void ResolvedTree_PropertyDirectiveArrayInicializer_ResolvedCorrectly()
public void ResolvedTree_PropertyDirectiveInvalidArrayInicializer_ResolvedCorrectly()
{
var root = ParseSource(@$"
@viewModel object
@property string a=[, MarkupOptionsAttribute.Required = true");

var property = root.Directives["property"].SingleOrDefault() as IAbstractPropertyDeclarationDirective;

var nodes = property.InitializerSyntax.EnumerateChildNodes();
Assert.AreEqual("System.String", property.PropertyType.FullName);
Assert.AreEqual("a", property.NameSyntax.Name);

var nodes = property.InitializerSyntax.EnumerateChildNodes();
Assert.IsFalse(nodes.Any(n => n == property.InitializerSyntax));
}

[TestMethod]
public void ResolvedTree_PropertyDirectiveArrayInicializerAndAttributes_ResolvedCorrectly()
{
var root = ParseSource("""
@viewModel object
@property string[] MyProperty=["",""], MarkupOptionsAttribute.Required = true, MarkupOptionsAttribute.AllowBinding = false
""");

var property = root.Directives["property"].SingleOrDefault() as IAbstractPropertyDeclarationDirective;

Assert.AreEqual("System.String[]", property.PropertyType.FullName);
Assert.AreEqual("MyProperty", property.NameSyntax.Name);

Assert.AreEqual(2,nodes.Count());
Assert.AreEqual(2, property.Attributes.Count);
AssertEx.BindingNode(property.Attributes[0].Initializer, "True", 62, 5);
AssertEx.BindingNode(property.Attributes[1].Initializer, "False", 106, 6);
}
}
}

0 comments on commit ce78339

Please sign in to comment.