From 16deedf3a02ce18876aebd675dbd9865573b4345 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 8 Nov 2024 15:32:26 -0800 Subject: [PATCH 1/3] Add 'WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer' --- .../AnalyzerReleases.Shipped.md | 2 + ...ityToolkit.Mvvm.SourceGenerators.projitems | 1 + ...ValidatorValidateAllPropertiesGenerator.cs | 2 +- ...NotifyPropertyChangedAttributesAnalyzer.cs | 89 +++++++++++++++++++ ...leCustomPropertyWithBasesMemberAnalyzer.cs | 2 +- ...indableCustomPropertyCompatibleAnalyzer.cs | 2 +- .../Diagnostics/DiagnosticDescriptors.cs | 32 +++++++ 7 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer.cs diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/AnalyzerReleases.Shipped.md b/src/CommunityToolkit.Mvvm.SourceGenerators/AnalyzerReleases.Shipped.md index 6b2b3441d..e5a0fecb0 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/AnalyzerReleases.Shipped.md +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/AnalyzerReleases.Shipped.md @@ -90,3 +90,5 @@ MVVMTK0045 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator MVVMTK0046 | CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0046 MVVMTK0047 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0047 MVVMTK0048 | CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0048 +MVVMTK0049 | CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0049 +MVVMTK0050 | CommunityToolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0050 diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems b/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems index 11c513634..cc4351664 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems @@ -41,6 +41,7 @@ + diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs index e3a00ae0a..4ec9bc6f0 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs @@ -12,7 +12,7 @@ namespace CommunityToolkit.Mvvm.SourceGenerators; /// -/// A source generator for message registration without relying on compiled LINQ expressions. +/// A source generator for property validation without relying on compiled LINQ expressions. /// [Generator(LanguageNames.CSharp)] public sealed partial class ObservableValidatorValidateAllPropertiesGenerator : IIncrementalGenerator diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer.cs new file mode 100644 index 000000000..b96c58443 --- /dev/null +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer.cs @@ -0,0 +1,89 @@ +// 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.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using CommunityToolkit.Mvvm.SourceGenerators.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; + +namespace CommunityToolkit.Mvvm.SourceGenerators; + +/// +/// A diagnostic analyzer that generates a warning when [ObservableObject] and [INotifyPropertyChanged] are used on a class in WinRT scenarios. +/// +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer : DiagnosticAnalyzer +{ + /// + /// The mapping of target attributes that will trigger the analyzer. + /// + private static readonly ImmutableDictionary GeneratorAttributeNamesToFullyQualifiedNamesMap = ImmutableDictionary.CreateRange(new[] + { + new KeyValuePair("ObservableObjectAttribute", "CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute"), + new KeyValuePair("INotifyPropertyChangedAttribute", "CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute"), + }); + + /// + /// The mapping of diagnostics for each target attribute. + /// + private static readonly ImmutableDictionary GeneratorAttributeNamesToDiagnosticsMap = ImmutableDictionary.CreateRange(new[] + { + new KeyValuePair("ObservableObjectAttribute", WinRTUsingObservableObjectAttribute), + new KeyValuePair("INotifyPropertyChangedAttribute", WinRTUsingINotifyPropertyChangedAttribute), + }); + + /// + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( + WinRTUsingObservableObjectAttribute, + WinRTUsingINotifyPropertyChangedAttribute); + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(static context => + { + // This analyzer is only enabled when CsWinRT is in AOT mode + if (!context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.IsCsWinRTAotOptimizerEnabled(context.Compilation)) + { + return; + } + + // Try to get all necessary type symbols + if (!context.Compilation.TryBuildNamedTypeSymbolMap(GeneratorAttributeNamesToFullyQualifiedNamesMap, out ImmutableDictionary? typeSymbols)) + { + return; + } + + context.RegisterSymbolAction(context => + { + // We're looking for class declarations that don't have any base type (same as the other analyzer for non-WinRT scenarios), but inverted for base types. + // That is, we only want to warn in cases where the other analyzer would not warn. Otherwise, warnings from that one are already more than sufficient. + if (context.Symbol is not INamedTypeSymbol { TypeKind: TypeKind.Class, IsRecord: false, IsStatic: false, IsImplicitlyDeclared: false, BaseType.SpecialType: not SpecialType.System_Object } classSymbol) + { + return; + } + + foreach (AttributeData attribute in context.Symbol.GetAttributes()) + { + // Warn if either attribute is used, as it's not compatible with AOT + if (attribute.AttributeClass is { Name: string attributeName } attributeClass && + typeSymbols.TryGetValue(attributeName, out INamedTypeSymbol? attributeSymbol) && + SymbolEqualityComparer.Default.Equals(attributeClass, attributeSymbol)) + { + context.ReportDiagnostic(Diagnostic.Create( + GeneratorAttributeNamesToDiagnosticsMap[attributeClass.Name], + context.Symbol.Locations.FirstOrDefault(), + context.Symbol)); + } + } + }, SymbolKind.NamedType); + }); + } +} diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs index af21f5d49..2f3c8bab2 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs @@ -38,7 +38,7 @@ public override void Initialize(AnalysisContext context) return; } - // Get the symbol for [ObservableProperty], [RelayCommand] and [GeneratedBindableCustomProperty] + // Get the symbols for [ObservableProperty], [RelayCommand] and [GeneratedBindableCustomProperty] if (context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") is not INamedTypeSymbol observablePropertySymbol || context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.Input.RelayCommandAttribute") is not INamedTypeSymbol relayCommandSymbol || context.Compilation.GetTypeByMetadataName("WinRT.GeneratedBindableCustomPropertyAttribute") is not INamedTypeSymbol generatedBindableCustomPropertySymbol) diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs index 13dd28e73..a7fe9f5b9 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs @@ -33,7 +33,7 @@ public override void Initialize(AnalysisContext context) return; } - // Get the symbol for [RelayCommand] and [GeneratedBindableCustomProperty] + // Get the symbols for [RelayCommand] and [GeneratedBindableCustomProperty] if (context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.Input.RelayCommandAttribute") is not INamedTypeSymbol relayCommandSymbol || context.Compilation.GetTypeByMetadataName("WinRT.GeneratedBindableCustomPropertyAttribute") is not INamedTypeSymbol generatedBindableCustomPropertySymbol) { diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs index 13e4d7e65..cfa7b9f4f 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs @@ -812,4 +812,36 @@ internal static class DiagnosticDescriptors isEnabledByDefault: true, description: "Using [GeneratedBindableCustomProperty] on types that also use [RelayCommand] on any inherited methods is not supported, and a manually declared command property should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated property that is produced by the MVVM Toolkit generator).", helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0048"); + + /// + /// Gets a indicating when [INotifyPropertyChanged] is used on a type in WinRT scenarios. + /// + /// Format: "The type {0} is using the [INotifyPropertyChanged] attribute, with is not AOT compatible in WinRT scenarios (such as UWP XAML and WinUI 3 apps), and it should derive from ObservableObject or manually implement INotifyPropertyChanged instead (as it allows the CsWinRT generators to correctly produce the necessary WinRT marshalling code)". + /// + /// + public static readonly DiagnosticDescriptor WinRTUsingINotifyPropertyChangedAttribute = new DiagnosticDescriptor( + id: "MVVMTK0049", + title: "Using [INotifyPropertyChanged] is not AOT compatible for WinRT", + messageFormat: "The type {0} is using the [INotifyPropertyChanged] attribute, with is not AOT compatible in WinRT scenarios (such as UWP XAML and WinUI 3 apps), and it should derive from ObservableObject or manually implement INotifyPropertyChanged instead (as it allows the CsWinRT generators to correctly produce the necessary WinRT marshalling code)", + category: typeof(INotifyPropertyChangedGenerator).FullName, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Using the [INotifyPropertyChanged] attribute on types is not AOT compatible in WinRT scenarios (such as UWP XAML and WinUI 3 apps), and they should derive from ObservableObject or manually implement INotifyPropertyChanged instead (as it allows the CsWinRT generators to correctly produce the necessary WinRT marshalling code).", + helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0049"); + + /// + /// Gets a indicating when [ObservableObject] is used on a type in WinRT scenarios. + /// + /// Format: "The type {0} is using the [ObservableObject] attribute, with is not AOT compatible in WinRT scenarios (such as UWP XAML and WinUI 3 apps), and it should derive from ObservableObject instead (as it allows the CsWinRT generators to correctly produce the necessary WinRT marshalling code)". + /// + /// + public static readonly DiagnosticDescriptor WinRTUsingObservableObjectAttribute = new DiagnosticDescriptor( + id: "MVVMTK0050", + title: "Using [ObservableObject] is not AOT compatible for WinRT", + messageFormat: "The type {0} is using the [ObservableObject] attribute, with is not AOT compatible in WinRT scenarios (such as UWP XAML and WinUI 3 apps), and it should derive from ObservableObject instead (as it allows the CsWinRT generators to correctly produce the necessary WinRT marshalling code)", + category: typeof(ObservableObjectGenerator).FullName, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Using the [ObservableObject] attribute on types is not AOT compatible in WinRT scenarios (such as UWP XAML and WinUI 3 apps), and they should derive from ObservableObject instead (as it allows the CsWinRT generators to correctly produce the necessary WinRT marshalling code).", + helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0050"); } From a74423d1b39d1f1b662f2335a66d4c928c2dfa65 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 8 Nov 2024 17:29:37 -0800 Subject: [PATCH 2/3] Run WinRT '[RelayCommand]' tests for all Roslyn-s --- .../Test_SourceGeneratorsDiagnostics.cs | 83 ------------------ .../Test_SourceGeneratorsDiagnostics.cs | 85 +++++++++++++++++++ 2 files changed, 85 insertions(+), 83 deletions(-) diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_SourceGeneratorsDiagnostics.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_SourceGeneratorsDiagnostics.cs index d7fdfc933..ec33b277c 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_SourceGeneratorsDiagnostics.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_SourceGeneratorsDiagnostics.cs @@ -503,89 +503,6 @@ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( - source, - LanguageVersion.CSharp12, - editorconfig: []); - } - - [TestMethod] - public async Task WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer_TargetingWindows_DoesNotWarn() - { - const string source = """ - using CommunityToolkit.Mvvm.ComponentModel; - using CommunityToolkit.Mvvm.Input; - - namespace MyApp - { - public partial class SampleViewModel : ObservableObject - { - [RelayCommand] - private void DoStuff() - { - } - } - } - """; - - await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( - source, - LanguageVersion.CSharp12, - editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]); - } - - [TestMethod] - public async Task WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer_TargetingWindows_Bindable_Warns() - { - const string source = """ - using System; - using CommunityToolkit.Mvvm.ComponentModel; - using CommunityToolkit.Mvvm.Input; - using WinRT; - - namespace MyApp - { - [GeneratedBindableCustomProperty] - public partial class SampleViewModel : ObservableObject - { - [{|MVVMTK0046:RelayCommand|}] - private void DoStuff() - { - } - } - } - - namespace WinRT - { - public class GeneratedBindableCustomPropertyAttribute : Attribute; - } - """; - - await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( - source, - LanguageVersion.CSharp12, - editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]); - } - [TestMethod] public async Task WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer_NotTargetingWindows_DoesNotWarn() { diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs index bef6d9c35..6a03c55bc 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs @@ -1999,6 +1999,91 @@ internal static class IsExternalInit await VerifyAnalyzerDiagnosticsAndSuccessfulGeneration(source, LanguageVersion.CSharp9); } + [TestMethod] + public async Task WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer_NotTargetingWindows_DoesNotWarn() + { + const string source = """ + using CommunityToolkit.Mvvm.ComponentModel; + using CommunityToolkit.Mvvm.Input; + + namespace MyApp + { + public partial class SampleViewModel : ObservableObject + { + [RelayCommand] + private void DoStuff() + { + } + } + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp10, + editorconfig: []); + } + + [TestMethod] + public async Task WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer_TargetingWindows_DoesNotWarn() + { + const string source = """ + using CommunityToolkit.Mvvm.ComponentModel; + using CommunityToolkit.Mvvm.Input; + + namespace MyApp + { + public partial class SampleViewModel : ObservableObject + { + [RelayCommand] + private void DoStuff() + { + } + } + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp10, + editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]); + } + + [TestMethod] + public async Task WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer_TargetingWindows_Bindable_Warns() + { + const string source = """ + using System; + using CommunityToolkit.Mvvm.ComponentModel; + using CommunityToolkit.Mvvm.Input; + using WinRT; + + namespace MyApp + { + [GeneratedBindableCustomProperty] + public partial class SampleViewModel : ObservableObject + { + [{|MVVMTK0046:RelayCommand|}] + private void DoStuff() + { + } + } + } + + namespace WinRT + { + public class GeneratedBindableCustomPropertyAttribute : Attribute + { + } + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp10, + editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]); + } + /// /// Verifies the diagnostic errors for a given analyzer, and that all available source generators can run successfully with the input source (including subsequent compilation). /// From ae66c28c6c223f822a37677b9dda39bf90115655 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 8 Nov 2024 17:55:27 -0800 Subject: [PATCH 3/3] Add unit tests for new WinRT analyzer --- .../Test_SourceGeneratorsDiagnostics.cs | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs index 6a03c55bc..d9169c00c 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs @@ -2084,6 +2084,148 @@ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp10, + editorconfig: []); + } + + [TestMethod] + public async Task WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer_TargetingWindows_NoCsWinRTAotOptimizer_DoesNotWarn() + { + const string source = """ + using System; + using CommunityToolkit.Mvvm.ComponentModel; + + namespace MyApp; + + [ObservableObject] + public partial class SampleViewModel : BaseType + { + } + + public class BaseType + { + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp10, + editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]); + } + + [TestMethod] + public async Task WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer_TargetingWindows_NoBaseType_ObservableObject_DoesNotWarn() + { + const string source = """ + using System; + using CommunityToolkit.Mvvm.ComponentModel; + + namespace MyApp; + + [ObservableObject] + public partial class SampleViewModel + { + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp10, + editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true), ("CsWinRTAotOptimizerEnabled", "auto")]); + } + + [TestMethod] + public async Task WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer_TargetingWindows_NoBaseType_INotifyPropertyChanged_DoesNotWarn() + { + const string source = """ + using System; + using CommunityToolkit.Mvvm.ComponentModel; + + namespace MyApp; + + [INotifyPropertyChanged] + public partial class SampleViewModel + { + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp10, + editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true), ("CsWinRTAotOptimizerEnabled", "auto")]); + } + + [TestMethod] + public async Task WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer_TargetingWindows_BaseType_ObservableObject_Warns() + { + const string source = """ + using System; + using CommunityToolkit.Mvvm.ComponentModel; + + namespace MyApp; + + [ObservableObject] + public partial class {|MVVMTK0050:SampleViewModel|} : BaseType + { + } + + public class BaseType + { + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp10, + editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true), ("CsWinRTAotOptimizerEnabled", "auto")]); + } + + [TestMethod] + public async Task WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer_TargetingWindows_BaseType_INotifyPropertyChanged_Warns() + { + const string source = """ + using System; + using CommunityToolkit.Mvvm.ComponentModel; + + namespace MyApp; + + [INotifyPropertyChanged] + public partial class {|MVVMTK0049:SampleViewModel|} : BaseType + { + } + + public class BaseType + { + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp10, + editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true), ("CsWinRTAotOptimizerEnabled", "auto")]); + } + /// /// Verifies the diagnostic errors for a given analyzer, and that all available source generators can run successfully with the input source (including subsequent compilation). ///