diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs
index 7e2aa7e57..0c4861ec4 100644
--- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs
+++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs
@@ -60,25 +60,35 @@ public override void Initialize(AnalysisContext context)
return;
}
- // Warn on all [ObservableProperty] fields
+ // Warn on all [ObservableProperty] fields that would be included
foreach (IFieldSymbol fieldSymbol in FindObservablePropertyFields(typeSymbol, observablePropertySymbol))
{
- context.ReportDiagnostic(Diagnostic.Create(
- WinRTGeneratedBindableCustomPropertyWithBaseObservablePropertyOnField,
- typeSymbol.GetLocationFromAttributeDataOrDefault(generatedBindableCustomPropertyAttribute),
- typeSymbol,
- fieldSymbol.ContainingType,
- fieldSymbol.Name));
+ string propertyName = ObservablePropertyGenerator.Execute.GetGeneratedPropertyName(fieldSymbol);
+
+ if (WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.DoesGeneratedBindableCustomPropertyAttributeIncludePropertyName(generatedBindableCustomPropertyAttribute, propertyName))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ WinRTGeneratedBindableCustomPropertyWithBaseObservablePropertyOnField,
+ typeSymbol.GetLocationFromAttributeDataOrDefault(generatedBindableCustomPropertyAttribute),
+ typeSymbol,
+ fieldSymbol.ContainingType,
+ fieldSymbol.Name));
+ }
}
- // Warn on all [RelayCommand] methods
+ // Warn on all [RelayCommand] methods that would be included
foreach (IMethodSymbol methodSymbol in FindRelayCommandMethods(typeSymbol, relayCommandSymbol))
{
- context.ReportDiagnostic(Diagnostic.Create(
- WinRTGeneratedBindableCustomPropertyWithBaseRelayCommand,
- typeSymbol.GetLocationFromAttributeDataOrDefault(generatedBindableCustomPropertyAttribute),
- typeSymbol,
- methodSymbol));
+ (_, string propertyName) = RelayCommandGenerator.Execute.GetGeneratedFieldAndPropertyNames(methodSymbol);
+
+ if (WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.DoesGeneratedBindableCustomPropertyAttributeIncludePropertyName(generatedBindableCustomPropertyAttribute, propertyName))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ WinRTGeneratedBindableCustomPropertyWithBaseRelayCommand,
+ typeSymbol.GetLocationFromAttributeDataOrDefault(generatedBindableCustomPropertyAttribute),
+ typeSymbol,
+ methodSymbol));
+ }
}
}, SymbolKind.NamedType);
});
diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs
index 7cbcf136a..10029b84b 100644
--- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs
+++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs
@@ -54,9 +54,17 @@ public override void Initialize(AnalysisContext context)
return;
}
- // If the containing type is using [GeneratedBindableCustomProperty], emit a warning
- if (typeSymbol.HasAttributeWithType(generatedBindableCustomPropertySymbol))
+ // If the containing type is not using [GeneratedBindableCustomProperty], we can also skip it
+ if (!typeSymbol.TryGetAttributeWithType(generatedBindableCustomPropertySymbol, out AttributeData? generatedBindableCustomPropertyAttribute))
{
+ return;
+ }
+
+ (_, string propertyName) = RelayCommandGenerator.Execute.GetGeneratedFieldAndPropertyNames(methodSymbol);
+
+ if (DoesGeneratedBindableCustomPropertyAttributeIncludePropertyName(generatedBindableCustomPropertyAttribute, propertyName))
+ {
+ // Actually warn if the generated command would've been included by the generator
context.ReportDiagnostic(Diagnostic.Create(
WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatible,
methodSymbol.GetLocationFromAttributeDataOrDefault(relayCommandAttribute),
@@ -65,4 +73,32 @@ public override void Initialize(AnalysisContext context)
}, SymbolKind.Method);
});
}
+
+ ///
+ /// Checks whether a generated property with a given name would be included by the [GeneratedBindableCustomProperty] generator.
+ ///
+ /// The input value for the [GeneratedBindableCustomProperty] attribute.
+ /// The target generated property name to check.
+ /// Whether would be included by the [GeneratedBindableCustomProperty] generator.
+ internal static bool DoesGeneratedBindableCustomPropertyAttributeIncludePropertyName(AttributeData attributeData, string propertyName)
+ {
+ // Make sure we have a valid list of property names to explicitly include.
+ // If that is not the case, we consider all properties as included by default.
+ if (attributeData.ConstructorArguments is not [{ IsNull: false, Kind: TypedConstantKind.Array, Type: IArrayTypeSymbol { ElementType.SpecialType: SpecialType.System_String }, Values: var names }, ..])
+ {
+ return true;
+ }
+
+ // Simply match the input collection of target property names
+ foreach (TypedConstant propertyValue in names)
+ {
+ if (propertyValue is { IsNull: false, Type.SpecialType: SpecialType.System_String, Value: string targetName } && targetName == propertyName)
+ {
+ return true;
+ }
+ }
+
+ // No matches, we can consider the property as not included
+ return false;
+ }
}
diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
index 2de37f901..5196d5d89 100644
--- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
+++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
@@ -766,49 +766,49 @@ internal static class DiagnosticDescriptors
///
/// Gets a for when [RelayCommand] is used on a method in types where [GeneratedBindableCustomProperty] is used.
///
- /// Format: "The method {0} using [RelayCommand] within a type also using [GeneratedBindableCustomProperty], which is not supported, and a manually declared command property should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated command property that is produced by the MVVM Toolkit generator)".
+ /// Format: "The method {0} using [RelayCommand] within a type also using [GeneratedBindableCustomProperty] and including the generated property, which is not supported, and a manually declared command property should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated command property that is produced by the MVVM Toolkit generator)".
///
///
public static readonly DiagnosticDescriptor WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatible = new DiagnosticDescriptor(
id: "MVVMTK0046",
title: "Using [RelayCommand] is not compatible with [GeneratedBindableCustomProperty]",
- messageFormat: """The method {0} using [RelayCommand] within a type also using [GeneratedBindableCustomProperty], which is not supported, and a manually declared command property should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated command property that is produced by the MVVM Toolkit generator)""",
+ messageFormat: """The method {0} using [RelayCommand] within a type also using [GeneratedBindableCustomProperty] and including the generated property, which is not supported, and a manually declared command property should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated command property that is produced by the MVVM Toolkit generator)""",
category: typeof(RelayCommandGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
- description: "Using [RelayCommand] on methods within a type also using [GeneratedBindableCustomProperty] is not supported, and a manually declared command property should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated command property that is produced by the MVVM Toolkit generator).",
+ description: "Using [RelayCommand] on methods within a type also using [GeneratedBindableCustomProperty] and including the generated property is not supported, and a manually declared command property should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated command property that is produced by the MVVM Toolkit generator).",
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0046");
///
/// Gets a for when [GeneratedBindableCustomProperty] is used on a type that also uses [ObservableProperty] on any declared or inherited fields.
///
- /// Format: "The type {0} using [GeneratedBindableCustomProperty] is also using [ObservableProperty] on its declared (or inherited) field {1}.{2}: combining the two generators is not supported, and partial properties should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated property that is produced by the MVVM Toolkit generator)".
+ /// Format: "The type {0} using [GeneratedBindableCustomProperty] is also using [ObservableProperty] on its declared (or inherited) field {1}.{2}, and including the generated property: combining the two generators is not supported, and partial properties should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated property that is produced by the MVVM Toolkit generator)".
///
///
public static readonly DiagnosticDescriptor WinRTGeneratedBindableCustomPropertyWithBaseObservablePropertyOnField = new DiagnosticDescriptor(
id: "MVVMTK0047",
title: "Using [GeneratedBindableCustomProperty] is not compatible with [ObservableProperty] on fields",
- messageFormat: """The type {0} using [GeneratedBindableCustomProperty] is also using [ObservableProperty] on its declared (or inherited) field {1}.{2}: combining the two generators is not supported, and partial properties should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated property that is produced by the MVVM Toolkit generator)""",
+ messageFormat: """The type {0} using [GeneratedBindableCustomProperty] is also using [ObservableProperty] on its declared (or inherited) field {1}.{2}, and including the generated property: combining the two generators is not supported, and partial properties should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated property that is produced by the MVVM Toolkit generator)""",
category: typeof(ObservablePropertyGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
- description: "Using [GeneratedBindableCustomProperty] on types that also use [ObservableProperty] on any declared (or inherited) fields is not supported, and partial properties should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated property that is produced by the MVVM Toolkit generator).",
+ description: "Using [GeneratedBindableCustomProperty] on types that also use [ObservableProperty] on any declared (or inherited) fields and including the generated property is not supported, and partial properties 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/mvvmtk0047");
///
/// Gets a for when [GeneratedBindableCustomProperty] is used on a type that also uses [RelayCommand] on any declared or inherited methods.
///
- /// Format: "The type {0} using [GeneratedBindableCustomProperty] is also using [RelayCommand] on its inherited method {1}: combining the two generators 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)".
+ /// Format: "The type {0} using [GeneratedBindableCustomProperty] is also using [RelayCommand] on its inherited method {1} and including the generated property: combining the two generators 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)".
///
///
public static readonly DiagnosticDescriptor WinRTGeneratedBindableCustomPropertyWithBaseRelayCommand = new DiagnosticDescriptor(
id: "MVVMTK0048",
title: "Using [GeneratedBindableCustomProperty] is not compatible with [RelayCommand]",
- messageFormat: """The type {0} using [GeneratedBindableCustomProperty] is also using [RelayCommand] on its inherited method {1}: combining the two generators 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)""",
+ messageFormat: """The type {0} using [GeneratedBindableCustomProperty] is also using [RelayCommand] on its inherited method {1} and including the generated property: combining the two generators 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)""",
category: typeof(RelayCommandGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Warning,
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).",
+ description: "Using [GeneratedBindableCustomProperty] on types that also use [RelayCommand] on any inherited methods and including the generated property 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");
///
diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4120.UnitTests/Test_SourceGeneratorsDiagnostics.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4120.UnitTests/Test_SourceGeneratorsDiagnostics.cs
index 91c35db10..07ac71412 100644
--- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4120.UnitTests/Test_SourceGeneratorsDiagnostics.cs
+++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4120.UnitTests/Test_SourceGeneratorsDiagnostics.cs
@@ -747,6 +747,119 @@ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(
+ source,
+ LanguageVersion.CSharp12,
+ editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]);
+ }
+
+ [TestMethod]
+ public async Task WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer_TargetingWindows_BaseType_Field_NotIncluded_DoesNotWarn()
+ {
+ const string source = """
+ using System;
+ using CommunityToolkit.Mvvm.ComponentModel;
+ using WinRT;
+
+ namespace MyApp
+ {
+ [GeneratedBindableCustomProperty(["OtherName"], [])]
+ public partial class SampleViewModel : BaseViewModel
+ {
+ }
+
+ public partial class BaseViewModel : ObservableObject
+ {
+ [ObservableProperty]
+ private string name;
+ }
+ }
+
+ namespace WinRT
+ {
+ public class GeneratedBindableCustomPropertyAttribute(string[] a, string[] b) : Attribute
+ {
+ }
+ }
+ """;
+
+ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(
+ source,
+ LanguageVersion.CSharp12,
+ editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]);
+ }
+
+ [TestMethod]
+ public async Task WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer_TargetingWindows_BaseType_Method_NotIncluded_DoesNotWarn()
+ {
+ const string source = """
+ using System;
+ using CommunityToolkit.Mvvm.ComponentModel;
+ using CommunityToolkit.Mvvm.Input;
+ using WinRT;
+
+ namespace MyApp
+ {
+ [GeneratedBindableCustomProperty(["OtherMethod"], [])]
+ public partial class SampleViewModel : BaseViewModel
+ {
+ }
+
+ public partial class BaseViewModel : ObservableObject
+ {
+ [RelayCommand]
+ private void DoStuff()
+ {
+ }
+ }
+ }
+
+ namespace WinRT
+ {
+ public class GeneratedBindableCustomPropertyAttribute(string[] a, string[] b) : Attribute
+ {
+ }
+ }
+ """;
+
+ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(
+ source,
+ LanguageVersion.CSharp12,
+ editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]);
+ }
+
[TestMethod]
public async Task WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer_TargetingWindows_BaseType_Field_Warns()
{
@@ -781,6 +894,42 @@ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(
+ source,
+ LanguageVersion.CSharp12,
+ editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]);
+ }
+
[TestMethod]
public async Task WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer_TargetingWindows_BaseType_Method_Warns()
{
@@ -818,6 +967,45 @@ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(
+ source,
+ LanguageVersion.CSharp12,
+ editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]);
+ }
+
[TestMethod]
public async Task InvalidPartialPropertyLevelObservablePropertyAttributeAnalyzer_OnValidProperty_DoesNotWarn()
{
diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs
index 8030231aa..cb866377d 100644
--- a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs
+++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs
@@ -2049,6 +2049,111 @@ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(
+ source,
+ LanguageVersion.CSharp12,
+ editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]);
+ }
+
+ [TestMethod]
+ public async Task WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer_TargetingWindows_NotIncluded2_DoesNotWarn()
+ {
+ const string source = """
+ using System;
+ using CommunityToolkit.Mvvm.ComponentModel;
+ using CommunityToolkit.Mvvm.Input;
+ using WinRT;
+
+ namespace MyApp
+ {
+ [GeneratedBindableCustomProperty(["OtherCommand"], [])]
+ public partial class SampleViewModel : ObservableObject
+ {
+ [RelayCommand]
+ private void DoStuff()
+ {
+ }
+ }
+ }
+
+ namespace WinRT
+ {
+ public class GeneratedBindableCustomPropertyAttribute(string[] a, string[] b) : Attribute
+ {
+ }
+ }
+ """;
+
+ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(
+ source,
+ LanguageVersion.CSharp12,
+ editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]);
+ }
+
+ [TestMethod]
+ public async Task WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer_TargetingWindows_NotIncluded3_DoesNotWarn()
+ {
+ const string source = """
+ using System;
+ using CommunityToolkit.Mvvm.ComponentModel;
+ using CommunityToolkit.Mvvm.Input;
+ using WinRT;
+
+ namespace MyApp
+ {
+ [GeneratedBindableCustomProperty(["OtherCommand"], ["DoStuffCommand"])]
+ public partial class SampleViewModel : ObservableObject
+ {
+ [RelayCommand]
+ private void DoStuff()
+ {
+ }
+ }
+ }
+
+ namespace WinRT
+ {
+ public class GeneratedBindableCustomPropertyAttribute(string[] a, string[] b) : Attribute
+ {
+ }
+ }
+ """;
+
+ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(
+ source,
+ LanguageVersion.CSharp12,
+ editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]);
+ }
+
[TestMethod]
public async Task WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer_TargetingWindows_Bindable_Warns()
{
@@ -2084,6 +2189,76 @@ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(
+ source,
+ LanguageVersion.CSharp12,
+ editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]);
+ }
+
+ [TestMethod]
+ public async Task WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer_TargetingWindows_Bindable_Included2_Warns()
+ {
+ const string source = """
+ using System;
+ using CommunityToolkit.Mvvm.ComponentModel;
+ using CommunityToolkit.Mvvm.Input;
+ using WinRT;
+
+ namespace MyApp
+ {
+ [GeneratedBindableCustomProperty(["Blah", "", "DoStuffCommand"], [])]
+ public partial class SampleViewModel : ObservableObject
+ {
+ [RelayCommand]
+ private void {|MVVMTK0046:DoStuff|}()
+ {
+ }
+ }
+ }
+
+ namespace WinRT
+ {
+ public class GeneratedBindableCustomPropertyAttribute(string[] a, string[] b) : Attribute
+ {
+ }
+ }
+ """;
+
+ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(
+ source,
+ LanguageVersion.CSharp12,
+ editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true)]);
+ }
+
[TestMethod]
public async Task WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer_NotTargetingWindows_DoesNotWarn()
{