From 088d3697390e42d686b73d921bdd40714149808f Mon Sep 17 00:00:00 2001 From: Stephane Delcroix Date: Tue, 14 Aug 2018 08:14:18 +0000 Subject: [PATCH 1/2] [XamlC] Throws on duplicate x:Key (#3542) Throws a XamlParseException on duplicate x:Key on RDs. This will find duplicate in xaml, but duplicates can still happen if the RD is modified in code. Those dupes are already detectd at runtime. - helps, but doesn't fixes, #3512 --- .../SetPropertiesVisitor.cs | 18 +++++-- .../Issues/Gh3512.xaml | 7 +++ .../Issues/Gh3512.xaml.cs | 48 +++++++++++++++++++ .../Xamarin.Forms.Xaml.UnitTests.csproj | 7 +++ 4 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 Xamarin.Forms.Xaml.UnitTests/Issues/Gh3512.xaml create mode 100644 Xamarin.Forms.Xaml.UnitTests/Issues/Gh3512.xaml.cs diff --git a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs index 18ba9151f..9891d2cf7 100644 --- a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs @@ -137,7 +137,7 @@ namespace Xamarin.Forms.Build.Tasks var parentVar = Context.Variables[(IElementNode)parentNode]; string contentProperty; - if (CanAddToResourceDictionary(parentVar.VariableType, node, node, Context)) { + if (CanAddToResourceDictionary(parentVar, parentVar.VariableType, node, node, Context)) { Context.IL.Emit(Ldloc, parentVar); Context.IL.Append(AddToResourceDictionary(node, node, Context)); } @@ -186,7 +186,7 @@ namespace Xamarin.Forms.Build.Tasks TypeReference propertyType; Context.IL.Append(GetPropertyValue(parent, parentList.XmlName, Context, node, out propertyType)); - if (CanAddToResourceDictionary(propertyType, node, node, Context)) { + if (CanAddToResourceDictionary(parent, propertyType, node, node, Context)) { Context.IL.Append(AddToResourceDictionary(node, node, Context)); return; } @@ -1220,15 +1220,23 @@ namespace Xamarin.Forms.Build.Tasks return true; } - static bool CanAddToResourceDictionary(TypeReference collectionType, IElementNode node, IXmlLineInfo lineInfo, ILContext context) + static Dictionary> resourceNamesInUse = new Dictionary>(); + static bool CanAddToResourceDictionary(VariableDefinition parent, TypeReference collectionType, IElementNode node, IXmlLineInfo lineInfo, ILContext context) { if ( collectionType.FullName != "Xamarin.Forms.ResourceDictionary" && collectionType.ResolveCached().BaseType?.FullName != "Xamarin.Forms.ResourceDictionary") return false; - if (node.Properties.ContainsKey(XmlName.xKey)) + if (node.Properties.ContainsKey(XmlName.xKey)) { + var key = (node.Properties[XmlName.xKey] as ValueNode).Value as string; + if (!resourceNamesInUse.TryGetValue(parent, out var names)) + resourceNamesInUse[parent] = (names = new List()); + if (names.Contains(key)) + throw new XamlParseException($"A resource with the key '{key}' is already present in the ResourceDictionary.", lineInfo); + names.Add(key); return true; + } //is there a RD.Add() overrides that accepts this ? var nodeTypeRef = context.Variables[node].VariableType; @@ -1251,7 +1259,7 @@ namespace Xamarin.Forms.Build.Tasks foreach (var instruction in GetPropertyValue(parent, propertyName, context, iXmlLineInfo, out propertyType)) yield return instruction; - if (CanAddToResourceDictionary(propertyType, elementNode, iXmlLineInfo, context)) { + if (CanAddToResourceDictionary(parent, propertyType, elementNode, iXmlLineInfo, context)) { foreach (var instruction in AddToResourceDictionary(elementNode, iXmlLineInfo, context)) yield return instruction; yield break; diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Gh3512.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Gh3512.xaml new file mode 100644 index 000000000..f4399702e --- /dev/null +++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Gh3512.xaml @@ -0,0 +1,7 @@ + + + +