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

[Trimming] Use new feature switch definition attribute and enable analyzers in Controls.Core.csproj #21621

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Android.App;
Expand Down Expand Up @@ -104,7 +105,7 @@ public void SetStatusBarColor(AColor color)
Window.SetStatusBarColor(color);
}

static void RegisterHandler(Type target, Type handler, Type filter)
static void RegisterHandler(Type target, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type handler, Type filter)
{
Profile.FrameBegin();

Expand Down Expand Up @@ -459,7 +460,7 @@ void OnStateChanged()
}

// This is currently being used by the previewer please do not change or remove this
void RegisterHandlerForDefaultRenderer(Type target, Type handler, Type filter)
void RegisterHandlerForDefaultRenderer(Type target, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type handler, Type filter)
{
RegisterHandler(target, handler, filter);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Maui.Controls.Internals;

namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{
public static class NativeBindingExtensions
{
[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
public static void SetBinding(this global::Android.Views.View view, string propertyName, BindingBase binding, string updateSourceEventName = null)
{
PlatformBindingHelpers.SetBinding(view, propertyName, binding, updateSourceEventName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{
class NativeBindingService : INativeBindingService
{
[UnconditionalSuppressMessage("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)]
[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
public bool TrySetBinding(object target, string propertyName, BindingBase binding)
{
var view = target as AView;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,9 @@ Cell GetNewGroupHeaderCell(ITemplatedItemsList<Cell> group)
else
{
groupHeaderCell = new TextCell();
groupHeaderCell.SetBinding(TextCell.TextProperty, nameof(group.Name));
groupHeaderCell.SetBinding(
TextCell.TextProperty,
TypedBinding.ForSingleNestingLevel(nameof(group.Name), static (ITemplatedItemsList<Cell> g) => g.Name));
groupHeaderCell.BindingContext = group;
}

Expand Down
9 changes: 5 additions & 4 deletions src/Compatibility/Core/src/ExportRendererAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
using System;
using System.Diagnostics.CodeAnalysis;

namespace Microsoft.Maui.Controls.Compatibility
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class ExportRendererAttribute : HandlerAttribute
{
public ExportRendererAttribute(Type handler, Type target) : this(handler, target, null)
public ExportRendererAttribute(Type handler, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type target) : this(handler, target, null)
{
}

public ExportRendererAttribute(Type handler, Type target, Type[] supportedVisuals) : base(handler, target, supportedVisuals)
public ExportRendererAttribute(Type handler, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type target, Type[] supportedVisuals) : base(handler, target, supportedVisuals)
{
}
}

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class ExportCellAttribute : HandlerAttribute
{
public ExportCellAttribute(Type handler, Type target) : base(handler, target)
public ExportCellAttribute(Type handler, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type target) : base(handler, target)
{
}
}

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class ExportImageSourceHandlerAttribute : HandlerAttribute
{
public ExportImageSourceHandlerAttribute(Type handler, Type target) : base(handler, target)
public ExportImageSourceHandlerAttribute(Type handler, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type target) : base(handler, target)
{
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Hosting;

namespace Microsoft.Maui.Controls.Compatibility.Hosting
{
public static class MauiHandlersCollectionExtensions
{
public static IMauiHandlersCollection TryAddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection, Type controlType, Type rendererType)
public static IMauiHandlersCollection TryAddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection, Type controlType, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type rendererType)
{
Internals.Registrar.CheckIfRendererIsCompatibilityRenderer(rendererType);
Hosting.MauiAppBuilderExtensions.CheckForCompatibility();
Expand All @@ -21,7 +22,7 @@ public static IMauiHandlersCollection TryAddCompatibilityRenderer(this IMauiHand
return handlersCollection;
}

public static IMauiHandlersCollection AddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection, Type controlType, Type rendererType)
public static IMauiHandlersCollection AddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection, Type controlType, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type rendererType)
{
Internals.Registrar.CheckIfRendererIsCompatibilityRenderer(rendererType);
Hosting.MauiAppBuilderExtensions.CheckForCompatibility();
Expand All @@ -36,7 +37,7 @@ public static IMauiHandlersCollection AddCompatibilityRenderer(this IMauiHandler
return handlersCollection;
}

public static IMauiHandlersCollection AddCompatibilityRenderer<TControlType, TMauiType, TRenderer>(this IMauiHandlersCollection handlersCollection)
public static IMauiHandlersCollection AddCompatibilityRenderer<TControlType, TMauiType, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] TRenderer>(this IMauiHandlersCollection handlersCollection)
where TMauiType : IView
{
Internals.Registrar.CheckIfRendererIsCompatibilityRenderer(typeof(TRenderer));
Expand All @@ -51,7 +52,7 @@ public static IMauiHandlersCollection AddCompatibilityRenderer<TControlType, TMa
return handlersCollection;
}

public static IMauiHandlersCollection AddCompatibilityRenderer<TControlType, TRenderer>(this IMauiHandlersCollection handlersCollection)
public static IMauiHandlersCollection AddCompatibilityRenderer<TControlType, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] TRenderer>(this IMauiHandlersCollection handlersCollection)
where TControlType : IView
{
Internals.Registrar.CheckIfRendererIsCompatibilityRenderer(typeof(TRenderer));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Maui.Controls.Internals;
using NView = Tizen.NUI.BaseComponents.View;

namespace Microsoft.Maui.Controls.Compatibility.Platform.Tizen
{
public static class NativeBindingExtensions
{
[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
public static void SetBinding(this NView view, string propertyName, BindingBase binding, string updateSourceEventName = null)
{
PlatformBindingHelpers.SetBinding(view, propertyName, binding, updateSourceEventName);
Expand Down
2 changes: 1 addition & 1 deletion src/Compatibility/Core/src/Tizen/NativeBindingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Tizen
{
class NativeBindingService : INativeBindingService
{
[UnconditionalSuppressMessage("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)]
[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
public bool TrySetBinding(object target, string propertyName, BindingBase binding)
{
Hosting.MauiAppBuilderExtensions.CheckForCompatibility();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Maui.Controls.Internals;
using Microsoft.UI.Xaml;
using static System.String;
Expand All @@ -9,6 +10,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP
{
public static class NativeBindingExtensions
{
[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
public static void SetBinding(this FrameworkElement view, string propertyName, BindingBase bindingBase, string updateSourceEventName = null)
{
var binding = bindingBase as Binding;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP
{
public class NativeBindingService : INativeBindingService
{
[UnconditionalSuppressMessage("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)]
[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
public bool TrySetBinding(object target, string propertyName, BindingBase binding)
{
var view = target as FrameworkElement;
Expand Down
2 changes: 1 addition & 1 deletion src/Compatibility/Core/src/iOS/NativeBindingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS
[Preserve(AllMembers = true)]
class NativeBindingService : INativeBindingService
{
[UnconditionalSuppressMessage("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)]
[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
public bool TrySetBinding(object target, string propertyName, BindingBase binding)
{
Hosting.MauiAppBuilderExtensions.CheckForCompatibility();
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/src/Core/AppThemeBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ void Set()
target.SetDynamicResource(_targetProperty, dynamicResource.Key, specificity);
else
{
if (!BindingExpression.TryConvert(ref value, _targetProperty, _targetProperty.ReturnType, true))
if (!BindingExpressionHelper.TryConvert(ref value, _targetProperty, _targetProperty.ReturnType, true))
{
BindingDiagnostics.SendBindingFailure(this, null, target, _targetProperty, "AppThemeBinding", BindingExpression.CannotConvertTypeErrorMessage, value, _targetProperty.ReturnType);
return;
Expand Down
2 changes: 2 additions & 0 deletions src/Controls/src/Core/BindableObjectExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Maui.Graphics;

Expand Down Expand Up @@ -48,6 +49,7 @@ internal static void PropagateBindingContext<T>(this BindableObject self, IEnume
}

/// <include file="../../docs/Microsoft.Maui.Controls/BindableObjectExtensions.xml" path="//Member[@MemberName='SetBinding']/Docs/*" />
[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
public static void SetBinding(this BindableObject self, BindableProperty targetProperty, string path, BindingMode mode = BindingMode.Default, IValueConverter converter = null,
string stringFormat = null)
{
Expand Down
5 changes: 2 additions & 3 deletions src/Controls/src/Core/Binding.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;

namespace Microsoft.Maui.Controls
{
/// <include file="../../docs/Microsoft.Maui.Controls/Binding.xml" path="Type[@FullName='Microsoft.Maui.Controls.Binding']/Docs/*" />
[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
public sealed class Binding : BindingBase
{
public const string SelfPath = ".";
Expand Down Expand Up @@ -92,7 +91,7 @@ public object Source
}

/// <include file="../../docs/Microsoft.Maui.Controls/Binding.xml" path="//Member[@MemberName='DoNothing']/Docs/*" />
public static readonly object DoNothing = new object();
public static readonly object DoNothing = MultiBinding.DoNothing; // the instance was moved to MultiBinding because the Binding class is annotated with [RequiresUnreferencedCode]

/// <include file="../../docs/Microsoft.Maui.Controls/Binding.xml" path="//Member[@MemberName='UpdateSourceEventName']/Docs/*" />
[EditorBrowsable(EditorBrowsableState.Never)]
Expand Down
54 changes: 4 additions & 50 deletions src/Controls/src/Core/BindingExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
Expand All @@ -12,6 +13,7 @@

namespace Microsoft.Maui.Controls
{
[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
internal sealed class BindingExpression
{
internal const string PropertyNotFoundErrorMessage = "'{0}' property not found on '{1}', target property: '{2}.{3}'";
Expand Down Expand Up @@ -154,7 +156,7 @@ void ApplyCore(object sourceObject, BindableObject target, BindableProperty prop
else
value = Binding.FallbackValue ?? property.GetDefaultValue(target);

if (!TryConvert(ref value, property, property.ReturnType, true))
if (!BindingExpressionHelper.TryConvert(ref value, property, property.ReturnType, true))
{
BindingDiagnostics.SendBindingFailure(Binding, current, target, property, "Binding", CannotConvertTypeErrorMessage, value, property.ReturnType);
return;
Expand All @@ -166,7 +168,7 @@ void ApplyCore(object sourceObject, BindableObject target, BindableProperty prop
{
object value = Binding.GetTargetValue(target.GetValue(property), part.SetterType);

if (!TryConvert(ref value, property, part.SetterType, false))
if (!BindingExpressionHelper.TryConvert(ref value, property, part.SetterType, false))
{
BindingDiagnostics.SendBindingFailure(Binding, current, target, property, "Binding", CannotConvertTypeErrorMessage, value, part.SetterType);
return;
Expand Down Expand Up @@ -428,54 +430,6 @@ void SetupPart(TypeInfo sourceType, BindingExpressionPart part)
}
}

static readonly Type[] DecimalTypes = { typeof(float), typeof(decimal), typeof(double) };

internal static bool TryConvert(ref object value, BindableProperty targetProperty, Type convertTo, bool toTarget)
{
if (value == null)
return !convertTo.GetTypeInfo().IsValueType || Nullable.GetUnderlyingType(convertTo) != null;
try
{
if ((toTarget && targetProperty.TryConvert(ref value)) || (!toTarget && convertTo.IsInstanceOfType(value)))
return true;
}
catch (InvalidOperationException)
{ //that's what TypeConverters ususally throw
return false;
}

object original = value;
try
{
convertTo = Nullable.GetUnderlyingType(convertTo) ?? convertTo;

var stringValue = value as string ?? string.Empty;
// see: https://bugzilla.xamarin.com/show_bug.cgi?id=32871
// do not canonicalize "*.[.]"; "1." should not update bound BindableProperty
if (stringValue.EndsWith(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, StringComparison.Ordinal) && DecimalTypes.Contains(convertTo))
{
value = original;
return false;
}

// do not canonicalize "-0"; user will likely enter a period after "-0"
if (stringValue == "-0" && DecimalTypes.Contains(convertTo))
{
value = original;
return false;
}

value = Convert.ChangeType(value, convertTo, CultureInfo.CurrentCulture);

return true;
}
catch (Exception ex) when (ex is InvalidCastException || ex is FormatException || ex is InvalidOperationException || ex is OverflowException)
{
value = original;
return false;
}
}

// SubscribeToAncestryChanges, ClearAncestryChangeSubscriptions, FindAncestryIndex, and
// OnElementParentSet are used with RelativeSource ancestor-type bindings, to detect when
// there has been an ancestry change requiring re-applying the binding, and to minimize
Expand Down
58 changes: 58 additions & 0 deletions src/Controls/src/Core/BindingExpressionHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;

namespace Microsoft.Maui.Controls
{
internal static class BindingExpressionHelper
{
static readonly Type[] DecimalTypes = { typeof(float), typeof(decimal), typeof(double) };

internal static bool TryConvert(ref object value, BindableProperty targetProperty, Type convertTo, bool toTarget)
{
if (value == null)
return !convertTo.GetTypeInfo().IsValueType || Nullable.GetUnderlyingType(convertTo) != null;
try
{
if ((toTarget && targetProperty.TryConvert(ref value)) || (!toTarget && convertTo.IsInstanceOfType(value)))
return true;
}
catch (InvalidOperationException)
{ //that's what TypeConverters ususally throw
return false;
}

object original = value;
try
{
convertTo = Nullable.GetUnderlyingType(convertTo) ?? convertTo;

var stringValue = value as string ?? string.Empty;
// see: https://bugzilla.xamarin.com/show_bug.cgi?id=32871
// do not canonicalize "*.[.]"; "1." should not update bound BindableProperty
if (stringValue.EndsWith(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, StringComparison.Ordinal) && DecimalTypes.Contains(convertTo))
{
value = original;
return false;
}

// do not canonicalize "-0"; user will likely enter a period after "-0"
if (stringValue == "-0" && DecimalTypes.Contains(convertTo))
{
value = original;
return false;
}

value = Convert.ChangeType(value, convertTo, CultureInfo.CurrentCulture);

return true;
}
catch (Exception ex) when (ex is InvalidCastException || ex is FormatException || ex is InvalidOperationException || ex is OverflowException)
{
value = original;
return false;
}
}
}
}
Loading
Loading