Skip to content

Commit e094757

Browse files
committed
Skip generation for more invalid cases
1 parent fce5bf8 commit e094757

File tree

3 files changed

+51
-6
lines changed

3 files changed

+51
-6
lines changed

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

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ static bool IsCandidateField(SyntaxNode node, out TypeDeclarationSyntax? contain
7171
/// <param name="node">The <see cref="MemberDeclarationSyntax"/> instance to process.</param>
7272
/// <param name="semanticModel">The <see cref="SemanticModel"/> instance for the current run.</param>
7373
/// <returns>Whether <paramref name="node"/> is valid.</returns>
74-
public static bool IsCandidateValidForCompilation(SyntaxNode node, SemanticModel semanticModel)
74+
public static bool IsCandidateValidForCompilation(MemberDeclarationSyntax node, SemanticModel semanticModel)
7575
{
7676
// At least C# 8 is always required
7777
if (!semanticModel.Compilation.HasLanguageVersionAtLeastEqualTo(LanguageVersion.CSharp8))
@@ -90,6 +90,35 @@ public static bool IsCandidateValidForCompilation(SyntaxNode node, SemanticModel
9090
return true;
9191
}
9292

93+
/// <summary>
94+
/// Performs additional checks before running the core generation logic.
95+
/// </summary>
96+
/// <param name="memberSymbol">The input <see cref="ISymbol"/> instance to process.</param>
97+
/// <returns>Whether <paramref name="memberSymbol"/> is valid.</returns>
98+
public static bool IsCandidateSymbolValid(ISymbol memberSymbol)
99+
{
100+
#if ROSLYN_4_12_0_OR_GREATER
101+
// We only need additional checks for properties (Roslyn already validates things for fields in our scenarios)
102+
if (memberSymbol is IPropertySymbol propertySymbol)
103+
{
104+
// Ensure that the property declaration is a partial definition with no implementation
105+
if (propertySymbol is not { IsPartialDefinition: true, PartialImplementationPart: null })
106+
{
107+
return false;
108+
}
109+
110+
// Also ignore all properties that have an invalid declaration
111+
if (propertySymbol.ReturnsByRef || propertySymbol.ReturnsByRefReadonly || propertySymbol.Type.IsRefLikeType)
112+
{
113+
return false;
114+
}
115+
}
116+
#endif
117+
118+
// We assume all other cases are supported (other failure cases will be detected later)
119+
return true;
120+
}
121+
93122
/// <summary>
94123
/// Gets the candidate <see cref="MemberDeclarationSyntax"/> after the initial filtering.
95124
/// </summary>
@@ -140,13 +169,11 @@ public static bool TryGetInfo(
140169
return false;
141170
}
142171

143-
using ImmutableArrayBuilder<DiagnosticInfo> builder = ImmutableArrayBuilder<DiagnosticInfo>.Rent();
144-
145172
// Validate the target type
146173
if (!IsTargetTypeValid(memberSymbol, out bool shouldInvokeOnPropertyChanging))
147174
{
148175
propertyInfo = null;
149-
diagnostics = builder.ToImmutable();
176+
diagnostics = ImmutableArray<DiagnosticInfo>.Empty;
150177

151178
return false;
152179
}
@@ -168,7 +195,7 @@ public static bool TryGetInfo(
168195
if (fieldName == propertyName && memberSyntax.IsKind(SyntaxKind.FieldDeclaration))
169196
{
170197
propertyInfo = null;
171-
diagnostics = builder.ToImmutable();
198+
diagnostics = ImmutableArray<DiagnosticInfo>.Empty;
172199

173200
// If the generated property would collide, skip generating it entirely. This makes sure that
174201
// users only get the helpful diagnostic about the collision, and not the normal compiler error
@@ -182,7 +209,7 @@ public static bool TryGetInfo(
182209
if (IsGeneratedPropertyInvalid(propertyName, GetPropertyType(memberSymbol)))
183210
{
184211
propertyInfo = null;
185-
diagnostics = builder.ToImmutable();
212+
diagnostics = ImmutableArray<DiagnosticInfo>.Empty;
186213

187214
return false;
188215
}
@@ -232,6 +259,8 @@ public static bool TryGetInfo(
232259

233260
token.ThrowIfCancellationRequested();
234261

262+
using ImmutableArrayBuilder<DiagnosticInfo> builder = ImmutableArrayBuilder<DiagnosticInfo>.Rent();
263+
235264
// Gather attributes info
236265
foreach (AttributeData attributeData in memberSymbol.GetAttributes())
237266
{

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
3838
return default;
3939
}
4040

41+
// Validate the symbol as well before doing any work
42+
if (!Execute.IsCandidateSymbolValid(context.TargetSymbol))
43+
{
44+
return default;
45+
}
46+
47+
token.ThrowIfCancellationRequested();
48+
4149
// Get the hierarchy info for the target symbol, and try to gather the property info
4250
HierarchyInfo hierarchy = HierarchyInfo.From(context.TargetSymbol.ContainingType);
4351

src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/InvalidPropertyLevelObservablePropertyAttributeAnalyzer.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ internal static bool IsValidCandidateProperty(SyntaxNode node, out TypeDeclarati
9191
return false;
9292
}
9393

94+
// Static properties are not supported
95+
if (property.Modifiers.Any(SyntaxKind.StaticKeyword))
96+
{
97+
containingTypeNode = null;
98+
99+
return false;
100+
}
101+
94102
// The accessors must be a get and a set (with any accessibility)
95103
if (accessors[0].Kind() is not (SyntaxKind.GetAccessorDeclaration or SyntaxKind.SetAccessorDeclaration) ||
96104
accessors[1].Kind() is not (SyntaxKind.GetAccessorDeclaration or SyntaxKind.SetAccessorDeclaration))

0 commit comments

Comments
 (0)