// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace CommunityToolkit.Mvvm.SourceGenerators.UnitTests;
[TestClass]
public class Test_SourceGeneratorsCodegen
{
[TestMethod]
public void ObservablePropertyWithNonNullableReferenceType_EmitsMemberNotNullAttribute()
{
string source = """
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
{
[ObservableProperty]
private string name;
}
""";
#if NET6_0_OR_GREATER
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public string Name
{
get => name;
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("name")]
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(name, value))
{
OnNameChanging(value);
OnNameChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Name);
name = value;
OnNameChanged(value);
OnNameChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Name);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanging(string value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanging(string? oldValue, string newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanged(string value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanged(string? oldValue, string newValue);
}
}
""";
#else
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public string Name
{
get => name;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(name, value))
{
OnNameChanging(value);
OnNameChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Name);
name = value;
OnNameChanged(value);
OnNameChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Name);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanging(string value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanging(string? oldValue, string newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanged(string value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanged(string? oldValue, string newValue);
}
}
""";
#endif
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
}
[TestMethod]
public void ObservablePropertyWithNonNullableValueType_DoesNotEmitMemberNotNullAttribute()
{
string source = """
using System;
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
{
[ObservableProperty]
private Guid id;
}
""";
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public global::System.Guid Id
{
get => id;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(id, value))
{
OnIdChanging(value);
OnIdChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Id);
id = value;
OnIdChanged(value);
OnIdChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Id);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnIdChanging(global::System.Guid value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnIdChanging(global::System.Guid oldValue, global::System.Guid newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnIdChanged(global::System.Guid value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnIdChanged(global::System.Guid oldValue, global::System.Guid newValue);
}
}
""";
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
}
[TestMethod]
public void ObservablePropertyWithNonNullableUnconstrainedGenericType_EmitsMemberNotNullAttribute()
{
string source = """
using System;
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
{
[ObservableProperty]
private T content;
}
""";
#if NET6_0_OR_GREATER
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public T Content
{
get => content;
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("content")]
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(content, value))
{
OnContentChanging(value);
OnContentChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
content = value;
OnContentChanged(value);
OnContentChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? oldValue, T newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? oldValue, T newValue);
}
}
""";
#else
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public T Content
{
get => content;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(content, value))
{
OnContentChanging(value);
OnContentChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
content = value;
OnContentChanged(value);
OnContentChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? oldValue, T newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? oldValue, T newValue);
}
}
""";
#endif
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
// Also test with C# 8 to double check that using a nullable unconstrained type parameter doesn't cause issues, but just a (suppressed) warning
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, LanguageVersion.CSharp8, ("MyApp.MyViewModel`1.g.cs", result));
}
[TestMethod]
public void ObservablePropertyWithNonNullableConstrainedNotNullGenericType_EmitsMemberNotNullAttribute()
{
string source = """
using System;
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
where T : notnull
{
[ObservableProperty]
private T content;
}
""";
#if NET6_0_OR_GREATER
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public T Content
{
get => content;
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("content")]
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(content, value))
{
OnContentChanging(value);
OnContentChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
content = value;
OnContentChanged(value);
OnContentChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? oldValue, T newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? oldValue, T newValue);
}
}
""";
#else
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public T Content
{
get => content;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(content, value))
{
OnContentChanging(value);
OnContentChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
content = value;
OnContentChanged(value);
OnContentChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? oldValue, T newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? oldValue, T newValue);
}
}
""";
#endif
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
}
[TestMethod]
public void ObservablePropertyWithNonNullableConstrainedReferenceTypeGenericType_EmitsMemberNotNullAttribute()
{
string source = """
using System;
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
where T : class
{
[ObservableProperty]
private T content;
}
""";
#if NET6_0_OR_GREATER
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public T Content
{
get => content;
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("content")]
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(content, value))
{
OnContentChanging(value);
OnContentChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
content = value;
OnContentChanged(value);
OnContentChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? oldValue, T newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? oldValue, T newValue);
}
}
""";
#else
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public T Content
{
get => content;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(content, value))
{
OnContentChanging(value);
OnContentChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
content = value;
OnContentChanged(value);
OnContentChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? oldValue, T newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? oldValue, T newValue);
}
}
""";
#endif
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
}
[TestMethod]
public void ObservablePropertyWithNonNullableConstrainedValueTypeGenericType_DoesNotEmitMemberNotNullAttribute()
{
string source = """
using System;
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
where T : struct
{
[ObservableProperty]
private T content;
}
""";
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public T Content
{
get => content;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(content, value))
{
OnContentChanging(value);
OnContentChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
content = value;
OnContentChanged(value);
OnContentChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T oldValue, T newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T oldValue, T newValue);
}
}
""";
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
}
[TestMethod]
public void ObservablePropertyWithNullableReferenceType_DoesNotEmitMemberNotNullAttribute()
{
string source = """
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
{
[ObservableProperty]
private string? name;
}
""";
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public string? Name
{
get => name;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(name, value))
{
OnNameChanging(value);
OnNameChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Name);
name = value;
OnNameChanged(value);
OnNameChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Name);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanging(string? value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanging(string? oldValue, string? newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanged(string? value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnNameChanged(string? oldValue, string? newValue);
}
}
""";
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
}
[TestMethod]
public void ObservablePropertyWithNullableValueType_DoesNotEmitMemberNotNullAttribute()
{
string source = """
using System;
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
{
[ObservableProperty]
private Guid? id;
}
""";
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public global::System.Guid? Id
{
get => id;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(id, value))
{
OnIdChanging(value);
OnIdChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Id);
id = value;
OnIdChanged(value);
OnIdChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Id);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnIdChanging(global::System.Guid? value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnIdChanging(global::System.Guid? oldValue, global::System.Guid? newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnIdChanged(global::System.Guid? value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnIdChanged(global::System.Guid? oldValue, global::System.Guid? newValue);
}
}
""";
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
}
[TestMethod]
public void ObservablePropertyWithNullableUnconstrainedGenericType_DoesNotEmitMemberNotNullAttribute()
{
string source = """
using System;
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
{
[ObservableProperty]
private T? content;
}
""";
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public T? Content
{
get => content;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(content, value))
{
OnContentChanging(value);
OnContentChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
content = value;
OnContentChanged(value);
OnContentChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? oldValue, T? newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? oldValue, T? newValue);
}
}
""";
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
}
[TestMethod]
public void ObservablePropertyWithNullableConstrainedReferenceTypeGenericType_DoesNotEmitMemberNotNullAttribute()
{
string source = """
using System;
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
where T : class
{
[ObservableProperty]
private T? content;
}
""";
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public T? Content
{
get => content;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(content, value))
{
OnContentChanging(value);
OnContentChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
content = value;
OnContentChanged(value);
OnContentChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? oldValue, T? newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? oldValue, T? newValue);
}
}
""";
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
}
[TestMethod]
public void ObservablePropertyWithNullableConstrainedValueTypeGenericType_DoesNotEmitMemberNotNullAttribute()
{
string source = """
using System;
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
where T : struct
{
[ObservableProperty]
private T? content;
}
""";
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public T? Content
{
get => content;
set
{
if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(content, value))
{
OnContentChanging(value);
OnContentChanging(default, value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
content = value;
OnContentChanged(value);
OnContentChanged(default, value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
}
}
}
/// Executes the logic for when is changing.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? value);
/// Executes the logic for when is changing.
/// The previous property value that is being replaced.
/// The new property value being set.
/// This method is invoked right before the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanging(T? oldValue, T? newValue);
/// Executes the logic for when just changed.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? value);
/// Executes the logic for when just changed.
/// The previous property value that was replaced.
/// The new property value that was set.
/// This method is invoked right after the value of is changed.
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
partial void OnContentChanged(T? oldValue, T? newValue);
}
}
""";
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
}
// See https://github.com/CommunityToolkit/dotnet/issues/601
[TestMethod]
public void ObservablePropertyWithForwardedAttributesWithNumberLiterals_PreservesType()
{
string source = """
using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
#nullable enable
namespace MyApp;
partial class MyViewModel : ObservableObject
{
const double MyDouble = 3.14;
const float MyFloat = 3.14f;
[ObservableProperty]
[property: DefaultValue(0.0)]
[property: DefaultValue(1.24)]
[property: DefaultValue(0.0f)]
[property: DefaultValue(0.0f)]
[property: DefaultValue(MyDouble)]
[property: DefaultValue(MyFloat)]
private object? a;
}
public class DefaultValueAttribute : Attribute
{
public DefaultValueAttribute(object value)
{
}
}
""";
string result = """
//
#pragma warning disable
#nullable enable
namespace MyApp
{
///
partial class MyViewModel
{
///
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[global::MyApp.DefaultValueAttribute(0D)]
[global::MyApp.DefaultValueAttribute(1.24D)]
[global::MyApp.DefaultValueAttribute(0F)]
[global::MyApp.DefaultValueAttribute(0F)]
[global::MyApp.DefaultValueAttribute(3.14D)]
[global::MyApp.DefaultValueAttribute(3.14F)]
public object? A
{
get => a;
set
{
if (!global::System.Collections.Generic.EqualityComparer