Skip to content

Commit a0750a1

Browse files
authored
Add AOT reflection warnings for .NET 6+ methods (#4085)
<!-- Please be sure to read the [Contribute](https://github.com/reactiveui/reactiveui#contribute) section of the README --> **What kind of change does this PR introduce?** <!-- Bug fix, feature, docs update, ... --> update **What is the current behavior?** <!-- You can also link to an open issue here. --> Some AOT markup is applied to broadly **What is the new behavior?** <!-- If this is a feature change --> Added [RequiresDynamicCode] and [RequiresUnreferencedCode] attributes to methods and interfaces that use reflection and are not compatible with AOT environments in .NET 6 or greater. This change improves compatibility and provides clearer warnings for developers targeting AOT scenarios. **What might this PR break?** AOT requirements updated **Please check if the PR fulfills these requirements** - [ ] Tests for the changes have been added (for bug fixes / features) - [ ] Docs have been added / updated (for bug fixes / features) **Other information**:
1 parent 27fe0c4 commit a0750a1

16 files changed

+281
-69
lines changed

src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.DotNet8_0.verified.txt

Lines changed: 57 additions & 11 deletions
Large diffs are not rendered by default.

src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.DotNet9_0.verified.txt

Lines changed: 57 additions & 11 deletions
Large diffs are not rendered by default.

src/ReactiveUI/Expression/Reflection.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,10 @@ public static bool IsStatic(this PropertyInfo item) // TODO: Create Test
395395
return method.IsStatic;
396396
}
397397

398+
#if NET6_0_OR_GREATER
399+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
400+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
401+
#endif
398402
internal static IObservable<object> ViewModelWhenAnyValue<TView, TViewModel>(TViewModel? viewModel, TView view, Expression? expression)
399403
where TView : class, IViewFor
400404
where TViewModel : class =>

src/ReactiveUI/Interfaces/ISuspensionDriver.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,9 @@ public interface ISuspensionDriver
3737
/// Invalidates the application state (i.e. deletes it from disk).
3838
/// </summary>
3939
/// <returns>A completed observable.</returns>
40+
#if NET6_0_OR_GREATER
41+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
42+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
43+
#endif
4044
IObservable<Unit> InvalidateState();
4145
}

src/ReactiveUI/Mixins/AutoPersistHelper.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ public static class AutoPersistHelper
2222
{
2323
private static readonly MemoizingMRUCache<Type, Dictionary<string, bool>> _persistablePropertiesCache = new(
2424
static (type, _) => type.GetTypeInfo().DeclaredProperties
25-
.Where(x => x.CustomAttributes.Any(y => typeof(DataMemberAttribute).GetTypeInfo().IsAssignableFrom(y.AttributeType.GetTypeInfo())))
26-
.ToDictionary(k => k.Name, _ => true),
25+
.Where(x => x.CustomAttributes.Any(y => typeof(DataMemberAttribute).GetTypeInfo().IsAssignableFrom(y.AttributeType.GetTypeInfo())))
26+
.ToDictionary(k => k.Name, _ => true),
2727
RxApp.SmallCacheLimit);
2828

2929
private static readonly MemoizingMRUCache<Type, bool> _dataContractCheckCache = new(
@@ -49,6 +49,10 @@ public static class AutoPersistHelper
4949
/// it is possible that it will never be saved.
5050
/// </param>
5151
/// <returns>A Disposable to disable automatic persistence.</returns>
52+
#if NET6_0_OR_GREATER
53+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
54+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
55+
#endif
5256
public static IDisposable AutoPersist<T>(this T @this, Func<T, IObservable<Unit>> doPersist, TimeSpan? interval = null)
5357
where T : IReactiveObject =>
5458
@this.AutoPersist(doPersist, Observable<Unit>.Never, interval);
@@ -76,6 +80,10 @@ public static IDisposable AutoPersist<T>(this T @this, Func<T, IObservable<Unit>
7680
/// it is possible that it will never be saved.
7781
/// </param>
7882
/// <returns>A Disposable to disable automatic persistence.</returns>
83+
#if NET6_0_OR_GREATER
84+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
85+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
86+
#endif
7987
public static IDisposable AutoPersist<T, TDontCare>(this T @this, Func<T, IObservable<Unit>> doPersist, IObservable<TDontCare> manualSaveSignal, TimeSpan? interval = null)
8088
where T : IReactiveObject
8189
{
@@ -127,6 +135,10 @@ public static IDisposable AutoPersist<T, TDontCare>(this T @this, Func<T, IObser
127135
/// it is possible that it will never be saved.
128136
/// </param>
129137
/// <returns>A Disposable to disable automatic persistence.</returns>
138+
#if NET6_0_OR_GREATER
139+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
140+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
141+
#endif
130142
public static IDisposable AutoPersistCollection<TItem>(this ObservableCollection<TItem> @this, Func<TItem, IObservable<Unit>> doPersist, TimeSpan? interval = null) // TODO: Create Test
131143
where TItem : IReactiveObject =>
132144
AutoPersistCollection(@this, doPersist, Observable<Unit>.Never, interval);
@@ -151,6 +163,10 @@ public static IDisposable AutoPersistCollection<TItem>(this ObservableCollection
151163
/// it is possible that it will never be saved.
152164
/// </param>
153165
/// <returns>A Disposable to disable automatic persistence.</returns>
166+
#if NET6_0_OR_GREATER
167+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
168+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
169+
#endif
154170
public static IDisposable AutoPersistCollection<TItem, TDontCare>(this ObservableCollection<TItem> @this, Func<TItem, IObservable<Unit>> doPersist, IObservable<TDontCare> manualSaveSignal, TimeSpan? interval = null)
155171
where TItem : IReactiveObject =>
156172
AutoPersistCollection<TItem, ObservableCollection<TItem>, TDontCare>(@this, doPersist, manualSaveSignal, interval);
@@ -175,6 +191,10 @@ public static IDisposable AutoPersistCollection<TItem, TDontCare>(this Observabl
175191
/// it is possible that it will never be saved.
176192
/// </param>
177193
/// <returns>A Disposable to disable automatic persistence.</returns>
194+
#if NET6_0_OR_GREATER
195+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
196+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
197+
#endif
178198
public static IDisposable AutoPersistCollection<TItem, TDontCare>(this ReadOnlyObservableCollection<TItem> @this, Func<TItem, IObservable<Unit>> doPersist, IObservable<TDontCare> manualSaveSignal, TimeSpan? interval = null) // TODO: Create Test
179199
where TItem : IReactiveObject =>
180200
AutoPersistCollection<TItem, ReadOnlyObservableCollection<TItem>, TDontCare>(@this, doPersist, manualSaveSignal, interval);
@@ -200,6 +220,10 @@ public static IDisposable AutoPersistCollection<TItem, TDontCare>(this ReadOnlyO
200220
/// it is possible that it will never be saved.
201221
/// </param>
202222
/// <returns>A Disposable to disable automatic persistence.</returns>
223+
#if NET6_0_OR_GREATER
224+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
225+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
226+
#endif
203227
public static IDisposable AutoPersistCollection<TItem, TCollection, TDontCare>(this TCollection @this, Func<TItem, IObservable<Unit>> doPersist, IObservable<TDontCare> manualSaveSignal, TimeSpan? interval = null) // TODO: Create Test
204228
where TItem : IReactiveObject
205229
where TCollection : INotifyCollectionChanged, IEnumerable<TItem>

src/ReactiveUI/Platforms/android/BundleSuspensionDriver.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ namespace ReactiveUI;
1111
/// <summary>
1212
/// Loads and saves state to persistent storage.
1313
/// </summary>
14-
#if NET6_0_OR_GREATER
15-
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
16-
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
17-
#endif
1814
public class BundleSuspensionDriver : ISuspensionDriver
1915
{
2016
/// <inheritdoc/>
@@ -71,6 +67,10 @@ public IObservable<Unit> SaveState(object state) // TODO: Create Test
7167
}
7268

7369
/// <inheritdoc/>
70+
#if NET6_0_OR_GREATER
71+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
72+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
73+
#endif
7474
public IObservable<Unit> InvalidateState() // TODO: Create Test
7575
{
7676
try

src/ReactiveUI/Platforms/android/ControlFetcherMixin.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ namespace ReactiveUI;
1717
/// Fragments via property names, similar to Butter Knife, as well as allows
1818
/// you to fetch controls manually.
1919
/// </summary>
20-
#if NET6_0_OR_GREATER
21-
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
22-
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
23-
#endif
2420
public static partial class ControlFetcherMixin
2521
{
2622
private static readonly ConcurrentDictionary<Assembly, Dictionary<string, int>> _controlIds = new();
@@ -47,6 +43,10 @@ public static partial class ControlFetcherMixin
4743
/// <param name="assembly">The assembly containing the user-defined view.</param>
4844
/// <param name="propertyName">The property.</param>
4945
/// <returns>The return view.</returns>
46+
#if NET6_0_OR_GREATER
47+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
48+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
49+
#endif
5050
public static View? GetControl(this View view, Assembly assembly, [CallerMemberName] string? propertyName = null) // TODO: Create Test
5151
=> GetCachedControl(propertyName, view, () => view.FindViewById(GetControlIdByName(assembly, propertyName)));
5252

@@ -55,6 +55,10 @@ public static partial class ControlFetcherMixin
5555
/// </summary>
5656
/// <param name="layoutHost">The layout view host.</param>
5757
/// <param name="resolveMembers">The resolve members.</param>
58+
#if NET6_0_OR_GREATER
59+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
60+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
61+
#endif
5862
public static void WireUpControls(this ILayoutViewHost layoutHost, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) // TODO: Create Test
5963
{
6064
ArgumentNullException.ThrowIfNull(layoutHost);
@@ -80,6 +84,10 @@ public static void WireUpControls(this ILayoutViewHost layoutHost, ResolveStrate
8084
/// </summary>
8185
/// <param name="view">The view.</param>
8286
/// <param name="resolveMembers">The resolve members.</param>
87+
#if NET6_0_OR_GREATER
88+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
89+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
90+
#endif
8391
public static void WireUpControls(this View view, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) // TODO: Create Test
8492
{
8593
ArgumentNullException.ThrowIfNull(view);
@@ -111,6 +119,10 @@ public static void WireUpControls(this View view, ResolveStrategy resolveMembers
111119
/// <param name="fragment">The fragment.</param>
112120
/// <param name="inflatedView">The inflated view.</param>
113121
/// <param name="resolveMembers">The resolve members.</param>
122+
#if NET6_0_OR_GREATER
123+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
124+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
125+
#endif
114126
public static void WireUpControls(this Fragment fragment, View inflatedView, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) // TODO: Create Test
115127
{
116128
ArgumentNullException.ThrowIfNull(fragment);
@@ -140,6 +152,10 @@ public static void WireUpControls(this Fragment fragment, View inflatedView, Res
140152
/// </summary>
141153
/// <param name="activity">The Activity.</param>
142154
/// <param name="resolveMembers">The resolve members.</param>
155+
#if NET6_0_OR_GREATER
156+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
157+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
158+
#endif
143159
public static void WireUpControls(this Activity activity, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) // TODO: Create Test
144160
{
145161
ArgumentNullException.ThrowIfNull(activity);

src/ReactiveUI/Platforms/android/PlatformRegistrations.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ namespace ReactiveUI;
99
/// Android platform registrations.
1010
/// </summary>
1111
/// <seealso cref="ReactiveUI.IWantsToRegisterStuff" />
12-
#if NET6_0_OR_GREATER
13-
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
14-
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
15-
#endif
1612
public class PlatformRegistrations : IWantsToRegisterStuff
1713
{
1814
/// <inheritdoc/>

src/ReactiveUI/Platforms/apple-common/AppSupportJsonSuspensionDriver.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ namespace ReactiveUI;
1212
/// <summary>
1313
/// Loads and saves state to persistent storage.
1414
/// </summary>
15-
#if NET6_0_OR_GREATER
16-
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
17-
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
18-
#endif
1915
public class AppSupportJsonSuspensionDriver : ISuspensionDriver
2016
{
2117
/// <inheritdoc/>
@@ -68,6 +64,10 @@ public IObservable<Unit> SaveState(object state)
6864
}
6965

7066
/// <inheritdoc/>
67+
#if NET6_0_OR_GREATER
68+
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
69+
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
70+
#endif
7171
public IObservable<Unit> InvalidateState()
7272
{
7373
try

src/ReactiveUI/Platforms/mac/PlatformRegistrations.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ namespace ReactiveUI;
99
/// Mac platform registrations.
1010
/// </summary>
1111
/// <seealso cref="ReactiveUI.IWantsToRegisterStuff" />
12-
#if NET6_0_OR_GREATER
13-
[RequiresDynamicCode("The method uses reflection and will not work in AOT environments.")]
14-
[RequiresUnreferencedCode("The method uses reflection and will not work in AOT environments.")]
15-
#endif
1612
public class PlatformRegistrations : IWantsToRegisterStuff
1713
{
1814
/// <inheritdoc/>

0 commit comments

Comments
 (0)