diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NullabilityAttributesGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NullabilityAttributesGenerator.cs
new file mode 100644
index 00000000000..5566ed21b59
--- /dev/null
+++ b/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NullabilityAttributesGenerator.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Microsoft.Toolkit.Mvvm.SourceGenerators
+{
+ ///
+ /// A source generator for necessary nullability attributes.
+ ///
+ [Generator]
+ public sealed class NullabilityAttributesGenerator : ISourceGenerator
+ {
+ ///
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ ///
+ public void Execute(GeneratorExecutionContext context)
+ {
+ AddSourceCodeIfTypeIsNotPresent(context, "System.Diagnostics.CodeAnalysis.NotNullAttribute");
+ AddSourceCodeIfTypeIsNotPresent(context, "System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute");
+ }
+
+ ///
+ /// Adds the source for a given attribute type if it's not present already in the compilation.
+ ///
+ private void AddSourceCodeIfTypeIsNotPresent(GeneratorExecutionContext context, string typeFullName)
+ {
+ if (context.Compilation.GetTypeByMetadataName(typeFullName) is not null)
+ {
+ return;
+ }
+
+ string
+ typeName = typeFullName.Split('.').Last(),
+ filename = $"Microsoft.Toolkit.Mvvm.SourceGenerators.EmbeddedResources.{typeName}.cs";
+
+ Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(filename);
+ StreamReader reader = new(stream);
+
+ string
+ originalSource = reader.ReadToEnd(),
+ outputSource = originalSource.Replace("NETSTANDARD2_0", "true");
+
+ context.AddSource($"{typeFullName}.cs", SourceText.From(outputSource, Encoding.UTF8));
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/EmbeddedResources/INotifyPropertyChanged.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/EmbeddedResources/INotifyPropertyChanged.cs
index 72d14de5394..3f9a4a7c8e7 100644
--- a/Microsoft.Toolkit.Mvvm.SourceGenerators/EmbeddedResources/INotifyPropertyChanged.cs
+++ b/Microsoft.Toolkit.Mvvm.SourceGenerators/EmbeddedResources/INotifyPropertyChanged.cs
@@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
@@ -50,7 +51,7 @@ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
///
/// The event is not raised if the current and new value for the target property are the same.
///
- protected bool SetProperty(ref T field, T newValue, [CallerMemberName] string? propertyName = null)
+ protected bool SetProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer.Default.Equals(field, newValue))
{
@@ -75,7 +76,7 @@ protected bool SetProperty(ref T field, T newValue, [CallerMemberName] string
/// The instance to use to compare the input values.
/// (optional) The name of the property that changed.
/// if the property was changed, otherwise.
- protected bool SetProperty(ref T field, T newValue, IEqualityComparer comparer, [CallerMemberName] string? propertyName = null)
+ protected bool SetProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer comparer, [CallerMemberName] string? propertyName = null)
{
if (comparer.Equals(field, newValue))
{
@@ -280,7 +281,7 @@ protected bool SetProperty(T oldValue, T newValue, IEqualityComparer<
/// is different than the previous one, and it does not mean the new
/// instance passed as argument is in any particular state.
///
- protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
+ protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
{
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName);
}
@@ -300,7 +301,7 @@ protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier,
///
/// The event is not raised if the current and new value for the target property are the same.
///
- protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, Action callback, [CallerMemberName] string? propertyName = null)
+ protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, Action callback, [CallerMemberName] string? propertyName = null)
{
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
}
@@ -338,7 +339,7 @@ protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier,
/// is different than the previous one, and it does not mean the new
/// instance passed as argument is in any particular state.
///
- protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
+ protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
{
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName);
}
@@ -359,7 +360,7 @@ protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNoti
///
/// The event is not raised if the current and new value for the target property are the same.
///
- protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, Action?> callback, [CallerMemberName] string? propertyName = null)
+ protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, Action?> callback, [CallerMemberName] string? propertyName = null)
{
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
}
diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Microsoft.Toolkit.Mvvm.SourceGenerators.csproj b/Microsoft.Toolkit.Mvvm.SourceGenerators/Microsoft.Toolkit.Mvvm.SourceGenerators.csproj
index 8e718f68287..fa2804b3282 100644
--- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Microsoft.Toolkit.Mvvm.SourceGenerators.csproj
+++ b/Microsoft.Toolkit.Mvvm.SourceGenerators/Microsoft.Toolkit.Mvvm.SourceGenerators.csproj
@@ -9,6 +9,8 @@
+
+
@@ -17,6 +19,12 @@
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest