diff --git a/src/Framework/Framework/Compilation/Directives/PropertyDeclarationDirectiveCompiler.cs b/src/Framework/Framework/Compilation/Directives/PropertyDeclarationDirectiveCompiler.cs index 2d554c2950..e8d0398c3a 100644 --- a/src/Framework/Framework/Compilation/Directives/PropertyDeclarationDirectiveCompiler.cs +++ b/src/Framework/Framework/Compilation/Directives/PropertyDeclarationDirectiveCompiler.cs @@ -112,6 +112,19 @@ protected override ImmutableList CreateArtefact(IReadOnlyList directive.NameSyntax.Name, + directive => directive, + (name, directiveOfSameName) => { + if (directiveOfSameName.Count() > 1) + { + foreach (var sameNameDirective in directiveOfSameName.Skip(1)) + { + sameNameDirective.DothtmlNode?.AddError("Property with the same name is already defined."); + }; + } + return directiveOfSameName.First(); + }) .Select(TryCreateDotvvmPropertyFromDirective) .ToImmutableList(); } diff --git a/src/Samples/Common/DotVVM.Samples.Common.csproj b/src/Samples/Common/DotVVM.Samples.Common.csproj index 09852a5a42..216f55da77 100644 --- a/src/Samples/Common/DotVVM.Samples.Common.csproj +++ b/src/Samples/Common/DotVVM.Samples.Common.csproj @@ -74,6 +74,10 @@ + + + + diff --git a/src/Samples/Common/ViewModels/Errors/MarkupControlPropertiesSameNameWithBase.cs b/src/Samples/Common/ViewModels/Errors/MarkupControlPropertiesSameNameWithBase.cs new file mode 100644 index 0000000000..f6ca158159 --- /dev/null +++ b/src/Samples/Common/ViewModels/Errors/MarkupControlPropertiesSameNameWithBase.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using DotVVM.Framework.Binding; +using DotVVM.Framework.Controls; + +namespace DotVVM.Samples.Common.ViewModels.Errors +{ + public class MarkupControlPropertiesSameNameWithBase : DotvvmMarkupControl + { + public string MyProperty + { + get { return (string)GetValue(MyPropertyProperty); } + set { SetValue(MyPropertyProperty, value); } + } + public static readonly DotvvmProperty MyPropertyProperty + = DotvvmProperty.Register(c => c.MyProperty, null); + + } +} diff --git a/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameName.dotcontrol b/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameName.dotcontrol new file mode 100644 index 0000000000..8bdc7da22b --- /dev/null +++ b/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameName.dotcontrol @@ -0,0 +1,6 @@ +@viewModel object + +@property int MyProperty +@property bool MyProperty + +{{value: _control.MyProperty}} diff --git a/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameName.dothtml b/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameName.dothtml new file mode 100644 index 0000000000..9e607e0c51 --- /dev/null +++ b/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameName.dothtml @@ -0,0 +1,15 @@ +@viewModel object + + + + + + + + + + + + + + diff --git a/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameNameWithBase.dotcontrol b/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameNameWithBase.dotcontrol new file mode 100644 index 0000000000..54b691d9f0 --- /dev/null +++ b/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameNameWithBase.dotcontrol @@ -0,0 +1,6 @@ +@viewModel object +@baseType DotVVM.Samples.Common.ViewModels.Errors.MarkupControlPropertiesSameNameWithBase + +@property bool MyProperty + +{{value: _control.MyProperty}} diff --git a/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameNameWithBase.dothtml b/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameNameWithBase.dothtml new file mode 100644 index 0000000000..2d9c4dc864 --- /dev/null +++ b/src/Samples/Common/Views/Errors/MarkupControlPropertiesSameNameWithBase.dothtml @@ -0,0 +1,15 @@ +@viewModel object + + + + + + + + + + + + + + diff --git a/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs b/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs index fe2388d721..1801de683b 100644 --- a/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs +++ b/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs @@ -179,6 +179,8 @@ public partial class SamplesRouteUrls public const string Errors_InvalidViewModel = "Errors/InvalidViewModel"; public const string Errors_MalformedBinding = "Errors/MalformedBinding"; public const string Errors_MarkupControlInvalidViewModel = "Errors/MarkupControlInvalidViewModel"; + public const string Errors_MarkupControlPropertiesSameName = "Errors/MarkupControlPropertiesSameName"; + public const string Errors_MarkupControlPropertiesSameNameWithBase = "Errors/MarkupControlPropertiesSameNameWithBase"; public const string Errors_MasterPageRequiresDifferentViewModel = "Errors/MasterPageRequiresDifferentViewModel"; public const string Errors_MissingRequiredProperty = "Errors/MissingRequiredProperty"; public const string Errors_MissingRequiredProperty2 = "Errors/MissingRequiredProperty2"; diff --git a/src/Samples/Tests/Tests/ErrorsTests.cs b/src/Samples/Tests/Tests/ErrorsTests.cs index a3fe56d41e..a3afdd2bc3 100644 --- a/src/Samples/Tests/Tests/ErrorsTests.cs +++ b/src/Samples/Tests/Tests/ErrorsTests.cs @@ -487,6 +487,17 @@ public void Error_ExceptionInLifecycle() }); } + [Fact] + public void Error_MarkupControlPropertiesSameName() + { + RunInAllBrowsers(browser => { + browser.NavigateToUrl(SamplesRouteUrls.Errors_MarkupControlPropertiesSameName); + + AssertUI.InnerText(browser.First(".exceptionMessage"), s => s.Contains("already defined")); + AssertUI.InnerText(browser.First(".errorUnderline"), s => s.Contains("@property bool MyProperty")); + }); + } + public ErrorsTests(ITestOutputHelper output) : base(output) { } diff --git a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/PropertyDirectiveTests.cs b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/PropertyDirectiveTests.cs index 1128d1f66e..c74bf67c16 100644 --- a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/PropertyDirectiveTests.cs +++ b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/PropertyDirectiveTests.cs @@ -1,8 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using DotVVM.Framework.Tests.Runtime.ControlTree.DefaultControlTreeResolver; using System.Linq; using DotVVM.Framework.Compilation.ControlTree; -using DotVVM.Framework.Controls; namespace DotVVM.Framework.Tests.Runtime.ControlTree { @@ -82,5 +80,33 @@ @viewModel object AssertEx.BindingNode(property.Attributes[0].Initializer, "True", 62, 5); AssertEx.BindingNode(property.Attributes[1].Initializer, "False", 106, 6); } + + [TestMethod] + public void ResolvedTree_PropertyDirectiveSameName_ErrorReported() + { + var root = ParseSource(""" + @viewModel object + @property int MyProperty + @property bool MyProperty + @property string MyProperty + + {{value: _control.MyProperty}} +""", "control.dotcontrol"); + + var firstProperty = root.Directives["property"][0] as IAbstractPropertyDeclarationDirective; + var secondProperty = root.Directives["property"][1] as IAbstractPropertyDeclarationDirective; + var thirdProperty = root.Directives["property"][2] as IAbstractPropertyDeclarationDirective; + + var property = root.Metadata.FindProperty("MyProperty"); + + //First property wins + Assert.AreEqual(property.PropertyType, typeof(int)); + + Assert.IsFalse(firstProperty.DothtmlNode.HasNodeErrors); + Assert.IsTrue(secondProperty.DothtmlNode.HasNodeErrors); + Assert.IsTrue(thirdProperty.DothtmlNode.HasNodeErrors); + + + } } }