Skip to content

Commit

Permalink
Merge pull request #564 from CommunityToolkit/dev/property-attribute-…
Browse files Browse the repository at this point in the history
…diagnostic

Emit diagnostic for unresolved property: attribute over [ObservableProperty] field
  • Loading branch information
Sergio0694 authored Jan 16, 2023
2 parents 3f38be0 + 9eb3a05 commit 302b8a9
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ Rule ID | Category | Severity | Notes
MVVMTK0032 | CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0032
MVVMTK0033 | CommunityToolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0033
MVVMTK0034 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0034
MVVMTK0035 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0035
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,17 @@ public static bool TryGetInfo(
// lack of IntelliSense when constructing attributes over the field, but this is the best we can do from this end anyway.
SymbolInfo attributeSymbolInfo = semanticModel.GetSymbolInfo(attribute, token);

// Check if the attribute type can be resolved, and emit a diagnostic if that is not the case. This happens if eg. the
// attribute type is spelled incorrectly, or if a user is missing the using directive for the attribute type being used.
if ((attributeSymbolInfo.Symbol ?? attributeSymbolInfo.CandidateSymbols.SingleOrDefault()) is not ISymbol attributeSymbol ||
(attributeSymbol as INamedTypeSymbol ?? attributeSymbol.ContainingType) is not INamedTypeSymbol attributeTypeSymbol)
{
builder.Add(
InvalidPropertyTargetedAttributeOnObservablePropertyField,
attribute,
fieldSymbol,
attribute.Name);

continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ internal static class DiagnosticDescriptors
description:
"Classes with no base types should prefer inheriting from ObservableObject instead of using attributes to generate INotifyPropertyChanged code, as that will " +
"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).",
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0032");
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0033");

/// <summary>
/// Gets a <see cref="DiagnosticDescriptor"/> indicating when a field with <c>[ObservableProperty]</c> is being directly referenced.
Expand All @@ -557,5 +557,21 @@ internal static class DiagnosticDescriptors
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "Fields with [ObservableProperty] should not be directly referenced, and the generated properties should be used instead.",
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0033");
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0034");

/// <summary>
/// Gets a <see cref="DiagnosticDescriptor"/> indicating when a field with <c>[ObservableProperty]</c> is using an invalid attribute targeting the property.
/// <para>
/// 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>.
/// </para>
/// </summary>
public static readonly DiagnosticDescriptor InvalidPropertyTargetedAttributeOnObservablePropertyField = new DiagnosticDescriptor(
id: "MVVMTK0035",
title: "Invalid property targeted attribute type",
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?)",
category: typeof(ObservablePropertyGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "All attributes targeting the generated property for a field annotated with [ObservableProperty] must correctly be resolved to valid types.",
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0035");
}
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,56 @@ public MyViewModel()
await VerifyAnalyzerDiagnosticsAndSuccessfulGeneration<FieldReferenceForObservablePropertyFieldAnalyzer>(source, LanguageVersion.CSharp8);
}

// See https://github.com/CommunityToolkit/dotnet/issues/563
[TestMethod]
public void InvalidPropertyTargetedAttributeOnObservablePropertyField_MissingUsingDirective()
{
string source = """
using System;
using CommunityToolkit.Mvvm.ComponentModel;
namespace MyApp
{
public partial class MyViewModel : ObservableObject
{
[ObservableProperty]
[property: MyTest]
public int number;
}
}
namespace MyAttributes
{
[AttributeUsage(AttributeTargets.Property)]
public class MyTestAttribute : Attribute
{
}
}
""";

VerifyGeneratedDiagnostics<ObservablePropertyGenerator>(source, "MVVMTK0035");
}

[TestMethod]
public void InvalidPropertyTargetedAttributeOnObservablePropertyField_TypoInAttributeName()
{
string source = """
using CommunityToolkit.Mvvm.ComponentModel;
namespace MyApp
{
public partial class MyViewModel : ObservableObject
{
[ObservableProperty]
[property: Fbuifbweif]
public int number;
}
}
""";

VerifyGeneratedDiagnostics<ObservablePropertyGenerator>(source, "MVVMTK0035");
}

/// <summary>
/// Verifies the diagnostic errors for a given analyzer, and that all available source generators can run successfully with the input source (including subsequent compilation).
/// </summary>
Expand Down

0 comments on commit 302b8a9

Please sign in to comment.