Skip to content

Commit d5f3a0c

Browse files
committed
Add 'InvalidPointerTypeObservablePropertyAttributeAnalyzer'
1 parent fc975d1 commit d5f3a0c

File tree

5 files changed

+91
-1
lines changed

5 files changed

+91
-1
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,4 @@ MVVMTK0051 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator
9696
MVVMTK0052 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0052
9797
MVVMTK0053 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0053
9898
MVVMTK0054 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0054
99+
MVVMTK0055 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0055

src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
<Compile Include="$(MSBuildThisFileDirectory)ComponentModel\TransitiveMembersGenerator.Execute.cs" />
4242
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\AsyncVoidReturningRelayCommandMethodAnalyzer.cs" />
4343
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\InvalidGeneratedPropertyObservablePropertyAttributeAnalyzer.cs" />
44+
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\InvalidPointerTypeObservablePropertyAttributeAnalyzer.cs" />
4445
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\InvalidPartialPropertyLevelObservablePropertyAttributeAnalyzer.cs" />
4546
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer.cs" />
4647
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs" />

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public static bool IsCandidateValidForCompilation(MemberDeclarationSyntax node,
9999
public static bool IsCandidateSymbolValid(ISymbol memberSymbol)
100100
{
101101
#if ROSLYN_4_12_0_OR_GREATER
102-
// We only need additional checks for properties (Roslyn already validates things for fields in our scenarios)
102+
// We only need these additional checks for properties (Roslyn already validates things for fields in our scenarios)
103103
if (memberSymbol is IPropertySymbol propertySymbol)
104104
{
105105
// Ensure that the property declaration is a partial definition with no implementation
@@ -116,6 +116,14 @@ public static bool IsCandidateSymbolValid(ISymbol memberSymbol)
116116
}
117117
#endif
118118

119+
// Pointer types are never allowed in either case
120+
if (memberSymbol is
121+
IPropertySymbol { Type.TypeKind: TypeKind.Pointer or TypeKind.FunctionPointer } or
122+
IFieldSymbol { Type.TypeKind: TypeKind.Pointer or TypeKind.FunctionPointer })
123+
{
124+
return false;
125+
}
126+
119127
// We assume all other cases are supported (other failure cases will be detected later)
120128
return true;
121129
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Immutable;
6+
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.Diagnostics;
9+
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;
10+
11+
namespace CommunityToolkit.Mvvm.SourceGenerators;
12+
13+
/// <summary>
14+
/// A diagnostic analyzer that generates an error whenever <c>[ObservableProperty]</c> is used with pointer types.
15+
/// </summary>
16+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
17+
public sealed class InvalidPointerTypeObservablePropertyAttributeAnalyzer : DiagnosticAnalyzer
18+
{
19+
/// <inheritdoc/>
20+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(InvalidObservablePropertyDeclarationReturnsPointerLikeType);
21+
22+
/// <inheritdoc/>
23+
public override void Initialize(AnalysisContext context)
24+
{
25+
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
26+
context.EnableConcurrentExecution();
27+
28+
context.RegisterCompilationStartAction(static context =>
29+
{
30+
// Get the [ObservableProperty] symbol
31+
if (context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") is not INamedTypeSymbol observablePropertySymbol)
32+
{
33+
return;
34+
}
35+
36+
context.RegisterSymbolAction(context =>
37+
{
38+
// Ensure that we have a valid target symbol to analyze
39+
if (context.Symbol is not (IFieldSymbol or IPropertySymbol))
40+
{
41+
return;
42+
}
43+
44+
// If the property is not using [ObservableProperty], there's nothing to do
45+
if (!context.Symbol.TryGetAttributeWithType(observablePropertySymbol, out AttributeData? observablePropertyAttribute))
46+
{
47+
return;
48+
}
49+
50+
// Emit a diagnostic if the type is a pointer type
51+
if (context.Symbol is
52+
IPropertySymbol { Type.TypeKind: TypeKind.Pointer or TypeKind.FunctionPointer } or
53+
IFieldSymbol { Type.TypeKind: TypeKind.Pointer or TypeKind.FunctionPointer })
54+
{
55+
context.ReportDiagnostic(Diagnostic.Create(
56+
InvalidObservablePropertyDeclarationReturnsPointerLikeType,
57+
observablePropertyAttribute.GetLocation(),
58+
context.Symbol.ContainingType,
59+
context.Symbol.Name));
60+
}
61+
}, SymbolKind.Field, SymbolKind.Property);
62+
});
63+
}
64+
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,4 +907,20 @@ internal static class DiagnosticDescriptors
907907
isEnabledByDefault: true,
908908
description: "A property using [ObservableProperty] returns a byref-like value ([ObservableProperty] must be used on properties of a non byref-like type).",
909909
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0054");
910+
911+
/// <summary>
912+
/// Gets a <see cref="DiagnosticDescriptor"/> for when <c>[ObservableProperty]</c> is used on a property that returns a pointer type.
913+
/// <para>
914+
/// Format: <c>"The property {0}.{1} returns a pointer or function pointer value ([ObservableProperty] must be used on properties of a non pointer-like type)"</c>.
915+
/// </para>
916+
/// </summary>
917+
public static readonly DiagnosticDescriptor InvalidObservablePropertyDeclarationReturnsPointerLikeType = new DiagnosticDescriptor(
918+
id: "MVVMTK0055",
919+
title: "Using [ObservableProperty] on a property that returns pointer-like",
920+
messageFormat: """The property {0}.{1} returns a pointer or function pointer value ([ObservableProperty] must be used on properties of a non pointer-like type)""",
921+
category: typeof(ObservablePropertyGenerator).FullName,
922+
defaultSeverity: DiagnosticSeverity.Error,
923+
isEnabledByDefault: true,
924+
description: "A property using [ObservableProperty] returns a pointer-like value ([ObservableProperty] must be used on properties of a non pointer-like type).",
925+
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0055");
910926
}

0 commit comments

Comments
 (0)