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 @@ -10,6 +10,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
class NativeBindingService : INativeBindingService
{
[UnconditionalSuppressMessage("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)]
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmerConstants.NativeBindingService)]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not very happy we have these UnconditionalSuppressMessage here. I think in this case it might be worth putting [RequiresUnreferencedCode] on the INativeBindingService.TrySetBinding(object, string, BindingBase) and all the classes that implement it.

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
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;

/// <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;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using static System.String;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Graphics;
using System.Diagnostics.CodeAnalysis;

#if __MOBILE__
using ObjCRuntime;
using UIKit;
Expand Down Expand Up @@ -48,6 +50,7 @@ public static SizeRequest GetSizeRequest(this UIView self, double widthConstrain
return new SizeRequest(request, minimum);
}

[RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)]
public static void SetBinding(this UIView view, string propertyName, BindingBase bindingBase,
string updateSourceEventName = null)
{
Expand Down
8 changes: 8 additions & 0 deletions src/Controls/src/Core/Controls.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@
<Description>.NET Multi-platform App UI (.NET MAUI) is a cross-platform framework for creating native mobile and desktop apps with C# and XAML. This package only contains the core C# and XAML objects used by .NET MAUI. Please install the Microsoft.Maui.Controls package to start using .NET MAUI.</Description>
</PropertyGroup>

<PropertyGroup Condition="!$(TargetFramework.StartsWith('netstandard')) and $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) != 'tizen'">
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
<EnableSingleFileAnalyzer>true</EnableSingleFileAnalyzer>
<!-- TODO: Remove once XamlTypeInfo.g.cs generates trimming-friendly code -->
<NoWarn Condition="$(TargetFramework.Contains('-windows'))">$(NoWarn);IL2059</NoWarn>
</PropertyGroup>

<Import Project="$(MauiSrcDirectory)MultiTargeting.targets" />

<ItemGroup>
Expand Down
Loading
Loading