Skip to content

Commit 302b8a9

Browse files
authored
Merge pull request #564 from CommunityToolkit/dev/property-attribute-diagnostic
Emit diagnostic for unresolved property: attribute over [ObservableProperty] field
2 parents 3f38be0 + 9eb3a05 commit 302b8a9

File tree

4 files changed

+77
-2
lines changed

4 files changed

+77
-2
lines changed

src/CommunityToolkit.Mvvm.SourceGenerators/AnalyzerReleases.Shipped.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,4 @@ Rule ID | Category | Severity | Notes
4848
MVVMTK0032 | CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0032
4949
MVVMTK0033 | CommunityToolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0033
5050
MVVMTK0034 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0034
51+
MVVMTK0035 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0035

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,17 @@ public static bool TryGetInfo(
214214
// lack of IntelliSense when constructing attributes over the field, but this is the best we can do from this end anyway.
215215
SymbolInfo attributeSymbolInfo = semanticModel.GetSymbolInfo(attribute, token);
216216

217+
// Check if the attribute type can be resolved, and emit a diagnostic if that is not the case. This happens if eg. the
218+
// attribute type is spelled incorrectly, or if a user is missing the using directive for the attribute type being used.
217219
if ((attributeSymbolInfo.Symbol ?? attributeSymbolInfo.CandidateSymbols.SingleOrDefault()) is not ISymbol attributeSymbol ||
218220
(attributeSymbol as INamedTypeSymbol ?? attributeSymbol.ContainingType) is not INamedTypeSymbol attributeTypeSymbol)
219221
{
222+
builder.Add(
223+
InvalidPropertyTargetedAttributeOnObservablePropertyField,
224+
attribute,
225+
fieldSymbol,
226+
attribute.Name);
227+
220228
continue;
221229
}
222230

src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ internal static class DiagnosticDescriptors
541541
description:
542542
"Classes with no base types should prefer inheriting from ObservableObject instead of using attributes to generate INotifyPropertyChanged code, as that will " +
543543
"reduce the binary size of the application (the attributes are only meant to support cases where the annotated types are already inheriting from a different type).",
544-
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0032");
544+
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0033");
545545

546546
/// <summary>
547547
/// Gets a <see cref="DiagnosticDescriptor"/> indicating when a field with <c>[ObservableProperty]</c> is being directly referenced.
@@ -557,5 +557,21 @@ internal static class DiagnosticDescriptors
557557
defaultSeverity: DiagnosticSeverity.Warning,
558558
isEnabledByDefault: true,
559559
description: "Fields with [ObservableProperty] should not be directly referenced, and the generated properties should be used instead.",
560-
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0033");
560+
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0034");
561+
562+
/// <summary>
563+
/// Gets a <see cref="DiagnosticDescriptor"/> indicating when a field with <c>[ObservableProperty]</c> is using an invalid attribute targeting the property.
564+
/// <para>
565+
/// Format: <c>"The field {0} annotated with [ObservableProperty] is using attribute "{1}" which was not recognized as a valid type (are you missing a using directive?)"</c>.
566+
/// </para>
567+
/// </summary>
568+
public static readonly DiagnosticDescriptor InvalidPropertyTargetedAttributeOnObservablePropertyField = new DiagnosticDescriptor(
569+
id: "MVVMTK0035",
570+
title: "Invalid property targeted attribute type",
571+
messageFormat: "The field {0} annotated with [ObservableProperty] is using attribute \"{1}\" which was not recognized as a valid type (are you missing a using directive?)",
572+
category: typeof(ObservablePropertyGenerator).FullName,
573+
defaultSeverity: DiagnosticSeverity.Error,
574+
isEnabledByDefault: true,
575+
description: "All attributes targeting the generated property for a field annotated with [ObservableProperty] must correctly be resolved to valid types.",
576+
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0035");
561577
}

tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,56 @@ public MyViewModel()
16251625
await VerifyAnalyzerDiagnosticsAndSuccessfulGeneration<FieldReferenceForObservablePropertyFieldAnalyzer>(source, LanguageVersion.CSharp8);
16261626
}
16271627

1628+
// See https://github.com/CommunityToolkit/dotnet/issues/563
1629+
[TestMethod]
1630+
public void InvalidPropertyTargetedAttributeOnObservablePropertyField_MissingUsingDirective()
1631+
{
1632+
string source = """
1633+
using System;
1634+
using CommunityToolkit.Mvvm.ComponentModel;
1635+
1636+
namespace MyApp
1637+
{
1638+
public partial class MyViewModel : ObservableObject
1639+
{
1640+
[ObservableProperty]
1641+
[property: MyTest]
1642+
public int number;
1643+
}
1644+
}
1645+
1646+
namespace MyAttributes
1647+
{
1648+
[AttributeUsage(AttributeTargets.Property)]
1649+
public class MyTestAttribute : Attribute
1650+
{
1651+
}
1652+
}
1653+
""";
1654+
1655+
VerifyGeneratedDiagnostics<ObservablePropertyGenerator>(source, "MVVMTK0035");
1656+
}
1657+
1658+
[TestMethod]
1659+
public void InvalidPropertyTargetedAttributeOnObservablePropertyField_TypoInAttributeName()
1660+
{
1661+
string source = """
1662+
using CommunityToolkit.Mvvm.ComponentModel;
1663+
1664+
namespace MyApp
1665+
{
1666+
public partial class MyViewModel : ObservableObject
1667+
{
1668+
[ObservableProperty]
1669+
[property: Fbuifbweif]
1670+
public int number;
1671+
}
1672+
}
1673+
""";
1674+
1675+
VerifyGeneratedDiagnostics<ObservablePropertyGenerator>(source, "MVVMTK0035");
1676+
}
1677+
16281678
/// <summary>
16291679
/// Verifies the diagnostic errors for a given analyzer, and that all available source generators can run successfully with the input source (including subsequent compilation).
16301680
/// </summary>

0 commit comments

Comments
 (0)