From dad3018fd32f24ca85988bb101b31e9607057a22 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 27 Jul 2021 16:32:06 +0200 Subject: [PATCH] Added attributes generation if missing --- .../NullabilityAttributesGenerator.cs | 56 +++++++++++++++++++ .../INotifyPropertyChanged.cs | 13 +++-- ...osoft.Toolkit.Mvvm.SourceGenerators.csproj | 8 +++ 3 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NullabilityAttributesGenerator.cs diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NullabilityAttributesGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NullabilityAttributesGenerator.cs new file mode 100644 index 00000000..5566ed21 --- /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 72d14de5..3f9a4a7c 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 8e718f68..fa2804b3 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