Skip to content

Commit e152803

Browse files
committed
Add diagnostic and test for pointer types
1 parent 1b40caf commit e152803

File tree

5 files changed

+76
-6
lines changed

5 files changed

+76
-6
lines changed

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/AnalyzerReleases.Shipped.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ WCTDP0008 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenera
1818
WCTDP0009 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenerator | Warning |
1919
WCTDP0010 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenerator | Warning |
2020
WCTDP0011 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenerator | Warning |
21+
WCTDP0012 | CommunityToolkit.GeneratedDependencyPropertyDependencyPropertyGenerator | Error |

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.Execute.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,15 @@ public static bool IsCandidateSymbolValid(IPropertySymbol propertySymbol, bool u
115115
return false;
116116
}
117117

118-
// Also ignore all properties that have an invalid declaration
119-
if (propertySymbol.IsStatic || propertySymbol.ReturnsByRef || propertySymbol.ReturnsByRefReadonly || propertySymbol.Type.IsRefLikeType)
118+
// Also ignore all properties returning a byref-like value. We don't need to also
119+
// check for ref values here, as that's already validated by the syntax filter.
120+
if (propertySymbol.Type.IsRefLikeType)
121+
{
122+
return false;
123+
}
124+
125+
// Pointer types are never allowed
126+
if (propertySymbol.Type.TypeKind is TypeKind.Pointer or TypeKind.FunctionPointer)
120127
{
121128
return false;
122129
}

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Diagnostics/Analyzers/InvalidPropertySymbolDeclarationAnalyzer.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ public sealed class InvalidPropertySymbolDeclarationAnalyzer : DiagnosticAnalyze
2222
[
2323
InvalidPropertyDeclarationIsNotIncompletePartialDefinition,
2424
InvalidPropertyDeclarationReturnsByRef,
25-
InvalidPropertyDeclarationReturnsRefLikeType
25+
InvalidPropertyDeclarationReturnsRefLikeType,
26+
InvalidPropertyDeclarationReturnsPointerType
2627
];
2728

2829
/// <inheritdoc/>
@@ -86,15 +87,22 @@ public override void Initialize(AnalysisContext context)
8687
attributeData.GetLocation(),
8788
propertySymbol));
8889
}
89-
90-
// Emit an error if the property type is a ref struct
91-
if (propertySymbol.Type.IsRefLikeType)
90+
else if (propertySymbol.Type.IsRefLikeType)
9291
{
92+
// Emit an error if the property type is a ref struct
9393
context.ReportDiagnostic(Diagnostic.Create(
9494
InvalidPropertyDeclarationReturnsRefLikeType,
9595
attributeData.GetLocation(),
9696
propertySymbol));
9797
}
98+
else if (propertySymbol.Type.TypeKind is TypeKind.Pointer or TypeKind.FunctionPointer)
99+
{
100+
// Emit a diagnostic if the type is a pointer type
101+
context.ReportDiagnostic(Diagnostic.Create(
102+
InvalidPropertyDeclarationReturnsPointerType,
103+
attributeData.GetLocation(),
104+
propertySymbol));
105+
}
98106
}, SymbolKind.Property);
99107
});
100108
}

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,20 @@ internal static class DiagnosticDescriptors
153153
isEnabledByDefault: true,
154154
description: "Properties annotated with [GeneratedDependencyProperty] and setting 'DefaultValue' should do so with an expression of a type comparible with the property type. Alternatively, the 'Get(ref object)' method should be implemented to handle the type mismatch.",
155155
helpLinkUri: "https://aka.ms/toolkit/labs/windows");
156+
157+
/// <summary>
158+
/// Gets a <see cref="DiagnosticDescriptor"/> for when <c>[ObservableProperty]</c> is used on a property that returns a pointer type.
159+
/// <para>
160+
/// 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>.
161+
/// </para>
162+
/// </summary>
163+
public static readonly DiagnosticDescriptor InvalidPropertyDeclarationReturnsPointerType = new(
164+
id: "WCTDP0012",
165+
title: "Using [GeneratedDependencyProperty] on a property that returns pointer type",
166+
messageFormat: """The property '{0}' cannot be used to generate a dependency property, as it returns a pointer value ([GeneratedDependencyProperty] must be used on properties returning a non pointer value)""",
167+
category: typeof(DependencyPropertyGenerator).FullName,
168+
defaultSeverity: DiagnosticSeverity.Error,
169+
isEnabledByDefault: true,
170+
description: "Properties annotated with [GeneratedDependencyProperty] must not return a pointer value (only reference types and non byref-like types are supported).",
171+
helpLinkUri: "https://aka.ms/toolkit/labs/windows");
156172
}

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.Tests/Test_Analyzers.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,44 @@ public partial class MyControl : Control
339339
await CSharpAnalyzerTest<InvalidPropertySymbolDeclarationAnalyzer>.VerifyAnalyzerAsync(source, LanguageVersion.CSharp13);
340340
}
341341

342+
[TestMethod]
343+
public async Task InvalidPropertySymbolDeclarationAnalyzer_ReturnsPointerType_Warns()
344+
{
345+
const string source = """
346+
using CommunityToolkit.WinUI;
347+
using Windows.UI.Xaml.Controls;
348+
349+
namespace MyApp;
350+
351+
public unsafe partial class MyControl : Control
352+
{
353+
[{|WCTDP0012:GeneratedDependencyProperty|}]
354+
public partial int* {|CS9248:Name|} { get; set; }
355+
}
356+
""";
357+
358+
await CSharpAnalyzerTest<InvalidPropertySymbolDeclarationAnalyzer>.VerifyAnalyzerAsync(source, LanguageVersion.CSharp13);
359+
}
360+
361+
[TestMethod]
362+
public async Task InvalidPropertySymbolDeclarationAnalyzer_ReturnsFunctionPointerType_Warns()
363+
{
364+
const string source = """
365+
using CommunityToolkit.WinUI;
366+
using Windows.UI.Xaml.Controls;
367+
368+
namespace MyApp;
369+
370+
public unsafe partial class MyControl : Control
371+
{
372+
[{|WCTDP0012:GeneratedDependencyProperty|}]
373+
public partial delegate* unmanaged[Stdcall]<int, void> {|CS9248:Name|} { get; set; }
374+
}
375+
""";
376+
377+
await CSharpAnalyzerTest<InvalidPropertySymbolDeclarationAnalyzer>.VerifyAnalyzerAsync(source, LanguageVersion.CSharp13);
378+
}
379+
342380
[TestMethod]
343381
public async Task InvalidPropertyContainingTypeDeclarationAnalyzer_NoAttribute_DoesNotWarn()
344382
{

0 commit comments

Comments
 (0)