[Xaml[C]] throw meaningful exception while trying to set the content … (#714)
authorStephane Delcroix <stephane@delcroix.org>
Thu, 2 Feb 2017 10:14:35 +0000 (11:14 +0100)
committerKangho Hur <kangho.hur@samsung.com>
Fri, 24 Mar 2017 04:18:32 +0000 (13:18 +0900)
* [Xaml[C]] throw meaningful exception while trying to set the content of a property without ContentPropertyAttribute

* fix test

Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
Xamarin.Forms.Xaml.UnitTests/Issues/Bz43694.xaml [new file with mode: 0644]
Xamarin.Forms.Xaml.UnitTests/Issues/Bz43694.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs

index 37dff29..235115d 100644 (file)
@@ -128,7 +128,7 @@ namespace Xamarin.Forms.Build.Tasks
                                // Collection element, implicit content, or implicit collection element.
                                string contentProperty;
                                var parentVar = Context.Variables[(IElementNode)parentNode];
-                               if (parentVar.VariableType.ImplementsInterface(Module.ImportReference(typeof (IEnumerable))))
+                               if (parentVar.VariableType.ImplementsInterface(Module.ImportReference(typeof (IEnumerable))) && parentVar.VariableType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).Any())
                                {
                                        var elementType = parentVar.VariableType;
                                        if (elementType.FullName != "Xamarin.Forms.ResourceDictionary" && elementType.Resolve().BaseType.FullName != "Xamarin.Forms.ResourceDictionary")
@@ -152,7 +152,8 @@ namespace Xamarin.Forms.Build.Tasks
                                        if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
                                                return;
                                        Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], name, node, Context, node));
-                               }
+                               } else
+                                       throw new XamlParseException($"Can not set the content of {((IElementNode)parentNode).XmlType.Name} as it doesn't have a ContentPropertyAttribute", node);
                        }
                        else if (IsCollectionItem(node, parentNode) && parentNode is ListNode)
                        {
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz43694.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz43694.xaml
new file mode 100644 (file)
index 0000000..9fcc0c0
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+       x:Class="Xamarin.Forms.Xaml.UnitTests.Bz43694">
+       <ContentPage.Resources>
+               <ResourceDictionary>
+                       <x:String x:Key="Dark">Dark</x:String>
+                       <x:String x:Key="Light">
+                               <OnPlatform x:TypeArguments="x:String">
+                                       <OnPlatform.iOS>OpenSans-Light</OnPlatform.iOS>
+                                       <OnPlatform.Android>OpenSans-Light</OnPlatform.Android>
+                                       <OnPlatform.WinPhone>/Assets/Fonts/OpenSans-Light.ttf#Open Sans</OnPlatform.WinPhone>
+                               </OnPlatform>
+                       </x:String>
+               </ResourceDictionary>
+       </ContentPage.Resources>
+       <Label Text="{StaticResource Light}" 
+               VerticalOptions="Center"
+               HorizontalOptions="Center"
+               x:Name="label"/>
+</ContentPage>
\ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz43694.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz43694.xaml.cs
new file mode 100644 (file)
index 0000000..62af1c0
--- /dev/null
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+using Xamarin.Forms;
+using Xamarin.Forms.Core.UnitTests;
+
+namespace Xamarin.Forms.Xaml.UnitTests
+{
+       [XamlCompilation(XamlCompilationOptions.Skip)]
+       public partial class Bz43694 : ContentPage
+       {
+               public Bz43694()
+               {
+                       InitializeComponent();
+               }
+
+               public Bz43694(bool useCompiledXaml)
+               {
+                       //this stub will be replaced at compile time
+               }
+
+               [TestFixture]
+               class Tests
+               {
+                       [SetUp]
+                       public void Setup()
+                       {
+                               Device.PlatformServices = new MockPlatformServices();
+                       }
+
+                       [TearDown]
+                       public void TearDown()
+                       {
+                               Device.PlatformServices = null;
+                       }
+
+                       [TestCase(true)]
+                       [TestCase(false)]
+                       public void xStaticWithOnPlatformChildInRD(bool useCompiledXaml)
+                       {
+                               if (useCompiledXaml)
+                                       Assert.Throws(new XamlParseExceptionConstraint(9, 6), () => MockCompiler.Compile(typeof(Bz43694)));
+                               else
+                                       Assert.Throws(new XamlParseExceptionConstraint(9, 6), () => new Bz43694(useCompiledXaml));
+                       }
+               }
+       }
+}
\ No newline at end of file
index 47ef9f6..3ea1d1c 100644 (file)
@@ -121,7 +121,7 @@ namespace Xamarin.Forms.Xaml
                        } else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) {
                                // Collection element, implicit content, or implicit collection element.
                                string contentProperty;
-                               if (typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(Context.Types [parentElement].GetTypeInfo())) {
+                               if (typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(Context.Types [parentElement].GetTypeInfo()) && Context.Types[parentElement].GetRuntimeMethods().Any(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) {
                                        var source = Values [parentNode];
                                        if (!(typeof(ResourceDictionary).IsAssignableFrom(Context.Types [parentElement]))) {
                                                var addMethod =
@@ -137,7 +137,8 @@ namespace Xamarin.Forms.Xaml
 
                                        var source = Values [parentNode];
                                        SetPropertyValue(source, name, value, Context.RootElement, node, Context, node);
-                               }
+                               } else
+                                       throw new XamlParseException($"Can not set the content of {((IElementNode)parentNode).XmlType.Name} as it doesn't have a ContentPropertyAttribute", node);
                        } else if (IsCollectionItem(node, parentNode) && parentNode is ListNode) {
                                var parentList = (ListNode)parentNode;
                                var source = Values [parentNode.Parent];