Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attribute value initializer of PropertyDirective changes #1758

Merged
merged 6 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
}
Loading