Skip to content

Commit 65e1767

Browse files
authored
Merge pull request #141 from CommunityToolkit/dev/observable-property-name-clash-diagnostics
Extend source generator diagnostics in several scenarios
2 parents 2372ea9 + 216d4ea commit 65e1767

19 files changed

+1127
-106
lines changed

CommunityToolkit.Mvvm.SourceGenerators/AnalyzerReleases.Unshipped.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,11 @@ MVVMTK0010 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error |
1818
MVVMTK0011 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
1919
MVVMTK0012 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
2020
MVVMTK0013 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
21+
MVVMTK0014 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
22+
MVVMTK0015 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
23+
MVVMTK0016 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
24+
MVVMTK0017 | CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator | Error | See https://aka.ms/mvvmtoolkit/error
25+
MVVMTK0018 | CommunityToolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Error | See https://aka.ms/mvvmtoolkit/error
26+
MVVMTK0019 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
27+
MVVMTK0020 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
28+
MVVMTK0021 | CommunityToolkit.Mvvm.SourceGenerators.ObservableRecipientGenerator | Error | See https://aka.ms/mvvmtoolkit/error

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public INotifyPropertyChangedGenerator()
3434
{
3535
static INotifyPropertyChangedInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
3636
{
37-
bool includeAdditionalHelperMethods = attributeData.GetNamedArgument<bool>("IncludeAdditionalHelperMethods", true);
37+
bool includeAdditionalHelperMethods = attributeData.GetNamedArgument("IncludeAdditionalHelperMethods", true);
3838

3939
return new(includeAdditionalHelperMethods);
4040
}
@@ -57,6 +57,17 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, INotifyP
5757
return false;
5858
}
5959

60+
// Check if the type uses [INotifyPropertyChanged] or [ObservableObject] already (in the type hierarchy too)
61+
if (typeSymbol.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") ||
62+
typeSymbol.InheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute"))
63+
{
64+
builder.Add(InvalidAttributeCombinationForINotifyPropertyChangedAttributeError, typeSymbol, typeSymbol);
65+
66+
diagnostics = builder.ToImmutable();
67+
68+
return false;
69+
}
70+
6071
diagnostics = builder.ToImmutable();
6172

6273
return true;

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableObjectGenerator.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, object?
6060
return false;
6161
}
6262

63+
// Check if the type uses [INotifyPropertyChanged] or [ObservableObject] already (in the type hierarchy too)
64+
if (typeSymbol.InheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") ||
65+
typeSymbol.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute"))
66+
{
67+
builder.Add(InvalidAttributeCombinationForObservableObjectAttributeError, typeSymbol, typeSymbol);
68+
69+
diagnostics = builder.ToImmutable();
70+
71+
return false;
72+
}
73+
6374
diagnostics = builder.ToImmutable();
6475

6576
return true;

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs

Lines changed: 240 additions & 25 deletions
Large diffs are not rendered by default.

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using System.Linq;
88
using System.Text;
99
using CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
10-
using CommunityToolkit.Mvvm.SourceGenerators.Diagnostics;
1110
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
1211
using CommunityToolkit.Mvvm.SourceGenerators.Models;
1312
using Microsoft.CodeAnalysis;
@@ -38,20 +37,32 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
3837
// Filter the fields using [ObservableProperty]
3938
IncrementalValuesProvider<IFieldSymbol> fieldSymbolsWithAttribute =
4039
fieldSymbols
41-
.Where(static item => item.GetAttributes().Any(a => a.AttributeClass?.HasFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") == true));
40+
.Where(static item => item.HasAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute"));
41+
42+
// Get diagnostics for fields using [AlsoNotifyChangeFor] and [AlsoNotifyCanExecuteFor], but not [ObservableProperty]
43+
IncrementalValuesProvider<Diagnostic> fieldSymbolsWithOrphanedDependentAttributeWithErrors =
44+
fieldSymbols
45+
.Where(static item =>
46+
(item.HasAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.AlsoNotifyChangeForAttribute") ||
47+
item.HasAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.AlsoNotifyCanExecuteForAttribute")) &&
48+
!item.HasAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute"))
49+
.Select(static (item, _) => Execute.GetDiagnosticForFieldWithOrphanedDependentAttributes(item));
50+
51+
// Output the diagnostics
52+
context.ReportDiagnostics(fieldSymbolsWithOrphanedDependentAttributeWithErrors);
4253

4354
// Filter by language version
4455
context.FilterWithLanguageVersion(ref fieldSymbolsWithAttribute, LanguageVersion.CSharp8, UnsupportedCSharpLanguageVersionError);
4556

4657
// Gather info for all annotated fields
47-
IncrementalValuesProvider<(HierarchyInfo Hierarchy, Result<PropertyInfo> Info)> propertyInfoWithErrors =
58+
IncrementalValuesProvider<(HierarchyInfo Hierarchy, Result<PropertyInfo?> Info)> propertyInfoWithErrors =
4859
fieldSymbolsWithAttribute
4960
.Select(static (item, _) =>
5061
{
5162
HierarchyInfo hierarchy = HierarchyInfo.From(item.ContainingType);
52-
PropertyInfo propertyInfo = Execute.GetInfo(item, out ImmutableArray<Diagnostic> diagnostics);
63+
PropertyInfo? propertyInfo = Execute.TryGetInfo(item, out ImmutableArray<Diagnostic> diagnostics);
5364

54-
return (hierarchy, new Result<PropertyInfo>(propertyInfo, diagnostics));
65+
return (hierarchy, new Result<PropertyInfo?>(propertyInfo, diagnostics));
5566
});
5667

5768
// Output the diagnostics
@@ -60,7 +71,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
6071
// Get the filtered sequence to enable caching
6172
IncrementalValuesProvider<(HierarchyInfo Hierarchy, PropertyInfo Info)> propertyInfo =
6273
propertyInfoWithErrors
63-
.Select(static (item, _) => (item.Hierarchy, item.Info.Value))
74+
.Select(static (item, _) => (item.Hierarchy, Info: item.Info.Value))
75+
.Where(static item => item.Info is not null)!
6476
.WithComparers(HierarchyInfo.Comparer.Default, PropertyInfo.Comparer.Default);
6577

6678
// Split and group by containing type

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ static ObservableRecipientInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeDat
3939
string typeName = typeSymbol.Name;
4040
bool hasExplicitConstructors = !(typeSymbol.InstanceConstructors.Length == 1 && typeSymbol.InstanceConstructors[0] is { Parameters.IsEmpty: true, IsImplicitlyDeclared: true });
4141
bool isAbstract = typeSymbol.IsAbstract;
42-
bool isObservableValidator = typeSymbol.InheritsFrom("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
42+
bool isObservableValidator = typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
4343
bool hasOnActivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnActivated" });
4444
bool hasOnDeactivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnDeactivated" });
4545

@@ -70,7 +70,7 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, Observab
7070
ImmutableArray<Diagnostic>.Builder builder = ImmutableArray.CreateBuilder<Diagnostic>();
7171

7272
// Check if the type already inherits from ObservableRecipient
73-
if (typeSymbol.InheritsFrom("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient"))
73+
if (typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient"))
7474
{
7575
builder.Add(DuplicateObservableRecipientError, typeSymbol, typeSymbol);
7676

@@ -79,10 +79,20 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, Observab
7979
return false;
8080
}
8181

82+
// Check if the type already inherits [ObservableRecipient]
83+
if (typeSymbol.InheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipientAttribute"))
84+
{
85+
builder.Add(InvalidAttributeCombinationForObservableRecipientAttributeError, typeSymbol, typeSymbol);
86+
87+
diagnostics = builder.ToImmutable();
88+
89+
return false;
90+
}
91+
8292
// In order to use [ObservableRecipient], the target type needs to inherit from ObservableObject,
8393
// or be annotated with [ObservableObject] or [INotifyPropertyChanged] (with additional helpers).
84-
if (!typeSymbol.InheritsFrom("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObject") &&
85-
!typeSymbol.HasOrInheritsAttribute(static a => a.AttributeClass?.HasFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") == true) &&
94+
if (!typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObject") &&
95+
!typeSymbol.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") &&
8696
!typeSymbol.HasOrInheritsAttribute(static a =>
8797
a.AttributeClass?.HasFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute") == true &&
8898
!a.HasNamedArgument("IncludeAdditionalHelperMethods", false)))

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.Execute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ private static class Execute
2929
/// <returns>Whether <paramref name="typeSymbol"/> inherits from <c>ObservableValidator</c>.</returns>
3030
public static bool IsObservableValidator(INamedTypeSymbol typeSymbol)
3131
{
32-
return typeSymbol.InheritsFrom("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
32+
return typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
3333
}
3434

3535
/// <summary>
@@ -61,7 +61,7 @@ public static ValidationInfo GetInfo(INamedTypeSymbol typeSymbol)
6161
}
6262

6363
// Skip the current member if there are no validation attributes applied to it
64-
if (!attributes.Any(a => a.AttributeClass?.InheritsFrom(
64+
if (!attributes.Any(a => a.AttributeClass?.InheritsFromFullyQualifiedName(
6565
"global::System.ComponentModel.DataAnnotations.ValidationAttribute") == true))
6666
{
6767
continue;

0 commit comments

Comments
 (0)