Skip to content

Commit 3c250da

Browse files
committed
Fix some analyzer bugs, add unit tests
1 parent a7c5130 commit 3c250da

File tree

4 files changed

+103
-1
lines changed

4 files changed

+103
-1
lines changed

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,13 @@ void HandleSetAccessor(IPropertySymbol propertySymbol, PropertyFlags propertyFla
438438
if (conversionOperation.Operand.Type is { TypeKind: TypeKind.Enum } operandType &&
439439
operandType.IsContainedInNamespace(WellKnownTypeNames.XamlNamespace(useWindowsUIXaml)))
440440
{
441-
fieldFlags.DefaultValue = null;
441+
// Before actually enabling the optimization, validate that the default value is actually
442+
// the same as the default value of the enum (ie. the value of its first declared field).
443+
if (operandType.TryGetDefaultValueForEnumType(out object? defaultValue) &&
444+
conversionOperation.Operand.ConstantValue.Value == defaultValue)
445+
{
446+
fieldFlags.DefaultValue = null;
447+
}
442448
}
443449
}
444450
else

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/Extensions/ITypeSymbolExtensions.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Diagnostics.CodeAnalysis;
67
using CommunityToolkit.GeneratedDependencyProperty.Helpers;
78
using Microsoft.CodeAnalysis;
89

@@ -23,6 +24,37 @@ public static bool IsDefaultValueNull(this ITypeSymbol symbol)
2324
return symbol is { IsValueType: false } or INamedTypeSymbol { IsGenericType: true, ConstructedFrom.SpecialType: SpecialType.System_Nullable_T };
2425
}
2526

27+
/// <summary>
28+
/// Tries to get the default value of a given enum type.
29+
/// </summary>
30+
/// <param name="symbol">The input <see cref="ITypeSymbol"/> instance to check.</param>
31+
/// <param name="value">The resulting default value for <paramref name="symbol"/>, if it was an enum type.</param>
32+
/// <returns>Whether <paramref name="value"/> was retrieved successfully.</returns>
33+
public static bool TryGetDefaultValueForEnumType(this ITypeSymbol symbol, [NotNullWhen(true)] out object? value)
34+
{
35+
if (symbol.TypeKind is not TypeKind.Enum)
36+
{
37+
value = default;
38+
39+
return false;
40+
}
41+
42+
// The default value of the enum is the value of its first constant field
43+
foreach (ISymbol memberSymbol in symbol.GetMembers())
44+
{
45+
if (memberSymbol is IFieldSymbol { IsConst: true, ConstantValue: object defaultValue })
46+
{
47+
value = defaultValue;
48+
49+
return true;
50+
}
51+
}
52+
53+
value = default;
54+
55+
return false;
56+
}
57+
2658
/// <summary>
2759
/// Checks whether or not a given type symbol has a specified fully qualified metadata name.
2860
/// </summary>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,7 @@ public string? Name
13821382
[TestMethod]
13831383
[DataRow("global::System.TimeSpan", "global::System.TimeSpan", "global::System.TimeSpan.FromSeconds(1)")]
13841384
[DataRow("global::System.TimeSpan?", "global::System.TimeSpan?", "global::System.TimeSpan.FromSeconds(1)")]
1385+
[DataRow("global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility.Collapsed")]
13851386
public async Task UseGeneratedDependencyPropertyOnManualPropertyAnalyzer_ValidProperty_ExplicitDefaultValue_DoesNotWarn(
13861387
string dependencyPropertyType,
13871388
string propertyType,
@@ -1502,6 +1503,7 @@ public class MyClass { }
15021503
[DataRow("global::Windows.Foundation.Rect", "global::Windows.Foundation.Rect", "default(global::Windows.Foundation.Rect)")]
15031504
[DataRow("global::Windows.Foundation.Size", "global::Windows.Foundation.Size", "default(global::Windows.Foundation.Size)")]
15041505
[DataRow("global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility", "default(global::Windows.UI.Xaml.Visibility)")]
1506+
[DataRow("global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility.Visible")]
15051507
[DataRow("global::System.TimeSpan", "global::System.TimeSpan", "default(System.TimeSpan)")]
15061508
[DataRow("global::System.DateTimeOffset", "global::System.DateTimeOffset", "default(global::System.DateTimeOffset)")]
15071509
[DataRow("global::System.DateTimeOffset?", "global::System.DateTimeOffset?", "null")]

components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.Tests/Test_UseGeneratedDependencyPropertyOnManualPropertyCodeFixer.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Microsoft.CodeAnalysis.CSharp;
99
using Microsoft.CodeAnalysis.Testing;
1010
using Microsoft.VisualStudio.TestTools.UnitTesting;
11+
using Windows.Foundation;
1112
using Windows.UI.ViewManagement;
1213
using Windows.UI.Xaml;
1314
using CSharpCodeFixTest = CommunityToolkit.GeneratedDependencyProperty.Tests.Helpers.CSharpCodeFixTest<
@@ -25,7 +26,41 @@ public class Test_UseGeneratedDependencyPropertyOnManualPropertyCodeFixer
2526
[DataRow("object", "object")]
2627
[DataRow("object", "object?")]
2728
[DataRow("int", "int")]
29+
[DataRow("byte", "byte")]
30+
[DataRow("sbyte", "sbyte")]
31+
[DataRow("short", "short")]
32+
[DataRow("ushort", "ushort")]
33+
[DataRow("uint", "uint")]
34+
[DataRow("long", "long")]
35+
[DataRow("ulong", "ulong")]
36+
[DataRow("char", "char")]
37+
[DataRow("float", "float")]
38+
[DataRow("double", "double")]
39+
[DataRow("global::System.Numerics.Matrix3x2", "global::System.Numerics.Matrix3x2")]
40+
[DataRow("global::System.Numerics.Matrix4x4", "global::System.Numerics.Matrix4x4")]
41+
[DataRow("global::System.Numerics.Plane", "global::System.Numerics.Plane")]
42+
[DataRow("global::System.Numerics.Quaternion", "global::System.Numerics.Quaternion")]
43+
[DataRow("global::System.Numerics.Vector2", "global::System.Numerics.Vector2")]
44+
[DataRow("global::System.Numerics.Vector3", "global::System.Numerics.Vector3")]
45+
[DataRow("global::System.Numerics.Vector4", "global::System.Numerics.Vector4")]
46+
[DataRow("global::Windows.Foundation.Point", "global::Windows.Foundation.Point")]
47+
[DataRow("global::Windows.Foundation.Rect", "global::Windows.Foundation.Rect")]
48+
[DataRow("global::Windows.Foundation.Size", "global::Windows.Foundation.Size")]
49+
[DataRow("global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility")]
2850
[DataRow("int?", "int?")]
51+
[DataRow("byte?", "byte?")]
52+
[DataRow("char?", "char?")]
53+
[DataRow("long?", "long?")]
54+
[DataRow("float?", "float?")]
55+
[DataRow("double?", "double?")]
56+
[DataRow("global::System.DateTimeOffset?", "global::System.DateTimeOffset?")]
57+
[DataRow("global::System.TimeSpan?", "global::System.TimeSpan?")]
58+
[DataRow("global::System.Guid?", "global::System.Guid?")]
59+
[DataRow("global::System.Collections.Generic.KeyValuePair<int, float>?", "global::System.Collections.Generic.KeyValuePair<int, float>?")]
60+
[DataRow("global::MyApp.MyStruct", "global::MyApp.MyStruct")]
61+
[DataRow("global::MyApp.MyStruct?", "global::MyApp.MyStruct?")]
62+
[DataRow("global::MyApp.MyEnum", "global::MyApp.MyEnum")]
63+
[DataRow("global::MyApp.MyEnum?", "global::MyApp.MyEnum?")]
2964
public async Task SimpleProperty(string dependencyPropertyType, string propertyType)
3065
{
3166
string original = $$"""
@@ -48,6 +83,9 @@ public class MyControl : Control
4883
set => SetValue(NameProperty, value);
4984
}
5085
}
86+
87+
public struct MyStruct { public string X { get; set; } }
88+
public enum MyEnum { A, B, C }
5189
""";
5290

5391
string @fixed = $$"""
@@ -62,6 +100,9 @@ public partial class MyControl : Control
62100
[GeneratedDependencyProperty]
63101
public partial {{propertyType}} {|CS9248:Name|} { get; set; }
64102
}
103+
104+
public struct MyStruct { public string X { get; set; } }
105+
public enum MyEnum { A, B, C }
65106
""";
66107

67108
CSharpCodeFixTest test = new(LanguageVersion.Preview)
@@ -71,6 +112,7 @@ public partial class MyControl : Control
71112
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
72113
TestState = { AdditionalReferences =
73114
{
115+
MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
74116
MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
75117
MetadataReference.CreateFromFile(typeof(DependencyProperty).Assembly.Location),
76118
MetadataReference.CreateFromFile(typeof(GeneratedDependencyPropertyAttribute).Assembly.Location)
@@ -93,6 +135,23 @@ public partial class MyControl : Control
93135
[DataRow("int?", "int?", "default(int?)")]
94136
[DataRow("int?", "int?", "null")]
95137
[DataRow("System.TimeSpan", "System.TimeSpan", "default(System.TimeSpan)")]
138+
[DataRow("global::System.Numerics.Matrix3x2", "global::System.Numerics.Matrix3x2", "default(global::System.Numerics.Matrix3x2)")]
139+
[DataRow("global::System.Numerics.Matrix4x4", "global::System.Numerics.Matrix4x4", "default(global::System.Numerics.Matrix4x4)")]
140+
[DataRow("global::System.Numerics.Plane", "global::System.Numerics.Plane", "default(global::System.Numerics.Plane)")]
141+
[DataRow("global::System.Numerics.Quaternion", "global::System.Numerics.Quaternion", "default(global::System.Numerics.Quaternion)")]
142+
[DataRow("global::System.Numerics.Vector2", "global::System.Numerics.Vector2", "default(global::System.Numerics.Vector2)")]
143+
[DataRow("global::System.Numerics.Vector3", "global::System.Numerics.Vector3", "default(global::System.Numerics.Vector3)")]
144+
[DataRow("global::System.Numerics.Vector4", "global::System.Numerics.Vector4", "default(global::System.Numerics.Vector4)")]
145+
[DataRow("global::Windows.Foundation.Point", "global::Windows.Foundation.Point", "default(global::Windows.Foundation.Point)")]
146+
[DataRow("global::Windows.Foundation.Rect", "global::Windows.Foundation.Rect", "default(global::Windows.Foundation.Rect)")]
147+
[DataRow("global::Windows.Foundation.Size", "global::Windows.Foundation.Size", "default(global::Windows.Foundation.Size)")]
148+
[DataRow("global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility", "default(global::Windows.UI.Xaml.Visibility)")]
149+
[DataRow("global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility.Visible")]
150+
[DataRow("global::System.DateTimeOffset?", "global::System.DateTimeOffset?", "default(global::System.DateTimeOffset?)")]
151+
[DataRow("global::System.DateTimeOffset?", "global::System.DateTimeOffset?", "null")]
152+
[DataRow("global::System.TimeSpan?", "global::System.TimeSpan?", "null")]
153+
[DataRow("global::System.Guid?", "global::System.Guid?", "null")]
154+
[DataRow("global::System.Collections.Generic.KeyValuePair<int, float>?", "global::System.Collections.Generic.KeyValuePair<int, float>?", "null")]
96155
public async Task SimpleProperty_WithExplicitValue_DefaultValue(
97156
string dependencyPropertyType,
98157
string propertyType,
@@ -145,6 +204,7 @@ public partial class MyControl : Control
145204
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
146205
TestState = { AdditionalReferences =
147206
{
207+
MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
148208
MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
149209
MetadataReference.CreateFromFile(typeof(DependencyProperty).Assembly.Location),
150210
MetadataReference.CreateFromFile(typeof(GeneratedDependencyPropertyAttribute).Assembly.Location)
@@ -160,6 +220,7 @@ public partial class MyControl : Control
160220
[DataRow("int", "int", "42")]
161221
[DataRow("int?", "int?", "0")]
162222
[DataRow("int?", "int?", "42")]
223+
[DataRow("global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility", "global::Windows.UI.Xaml.Visibility.Collapsed")]
163224
public async Task SimpleProperty_WithExplicitValue_NotDefault(
164225
string dependencyPropertyType,
165226
string propertyType,
@@ -212,6 +273,7 @@ public partial class MyControl : Control
212273
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
213274
TestState = { AdditionalReferences =
214275
{
276+
MetadataReference.CreateFromFile(typeof(Point).Assembly.Location),
215277
MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location),
216278
MetadataReference.CreateFromFile(typeof(DependencyProperty).Assembly.Location),
217279
MetadataReference.CreateFromFile(typeof(GeneratedDependencyPropertyAttribute).Assembly.Location)

0 commit comments

Comments
 (0)