Skip to content

Commit 0b8ee5a

Browse files
committed
Add SymbolInfoExtensions extensions, minor code tweaks
1 parent ee0587e commit 0b8ee5a

File tree

3 files changed

+43
-6
lines changed

3 files changed

+43
-6
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
<Compile Include="$(MSBuildThisFileDirectory)Extensions\INamedTypeSymbolExtensions.cs" />
5555
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IncrementalGeneratorInitializationContextExtensions.cs" />
5656
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IncrementalValuesProviderExtensions.cs" />
57+
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SymbolInfoExtensions.cs" />
5758
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ISymbolExtensions.cs" />
5859
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SourceProductionContextExtensions.cs" />
5960
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ITypeSymbolExtensions.cs" />

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,7 @@ public static bool TryGetInfo(
214214
// There is no need to validate anything here: the attribute will be forwarded as is, and then Roslyn will validate on the
215215
// generated property. Users will get the same validation they'd have had directly over the field. The only drawback is the
216216
// lack of IntelliSense when constructing attributes over the field, but this is the best we can do from this end anyway.
217-
SymbolInfo attributeSymbolInfo = semanticModel.GetSymbolInfo(attribute, token);
218-
219-
// Check if the attribute type can be resolved, and emit a diagnostic if that is not the case. This happens if eg. the
220-
// attribute type is spelled incorrectly, or if a user is missing the using directive for the attribute type being used.
221-
if ((attributeSymbolInfo.Symbol ?? attributeSymbolInfo.CandidateSymbols.SingleOrDefault()) is not ISymbol attributeSymbol ||
222-
(attributeSymbol as INamedTypeSymbol ?? attributeSymbol.ContainingType) is not INamedTypeSymbol attributeTypeSymbol)
217+
if (!semanticModel.GetSymbolInfo(attribute, token).TryGetAttributeTypeSymbol(out INamedTypeSymbol? attributeTypeSymbol))
223218
{
224219
builder.Add(
225220
InvalidPropertyTargetedAttributeOnObservablePropertyField,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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.Diagnostics.CodeAnalysis;
6+
using System.Linq;
7+
using Microsoft.CodeAnalysis;
8+
9+
namespace CommunityToolkit.Mvvm.SourceGenerators.Extensions;
10+
11+
/// <summary>
12+
/// Extension methods for the <see cref="SymbolInfo"/> type.
13+
/// </summary>
14+
internal static class SymbolInfoExtensions
15+
{
16+
/// <summary>
17+
/// Tries to get the resolved attribute type symbol from a given <see cref="SymbolInfo"/> value.
18+
/// </summary>
19+
/// <param name="symbolInfo">The <see cref="SymbolInfo"/> value to check.</param>
20+
/// <param name="typeSymbol">The resulting attribute type symbol, if correctly resolved.</param>
21+
/// <returns>Whether <paramref name="symbolInfo"/> is resolved to a symbol.</returns>
22+
/// <remarks>
23+
/// This can be used to ensure users haven't eg. spelled names incorrecty or missed a using directive. Normally, code would just
24+
/// not compile if that was the case, but that doesn't apply for attributes using invalid targets. In that case, Roslyn will ignore
25+
/// any errors, meaning the generator has to validate the type symbols are correctly resolved on its own.
26+
/// </remarks>
27+
public static bool TryGetAttributeTypeSymbol(this SymbolInfo symbolInfo, [NotNullWhen(true)] out INamedTypeSymbol? typeSymbol)
28+
{
29+
if ((symbolInfo.Symbol ?? symbolInfo.CandidateSymbols.SingleOrDefault()) is not ISymbol attributeSymbol ||
30+
(attributeSymbol as INamedTypeSymbol ?? attributeSymbol.ContainingType) is not INamedTypeSymbol resultingSymbol)
31+
{
32+
typeSymbol = null;
33+
34+
return false;
35+
}
36+
37+
typeSymbol = resultingSymbol;
38+
39+
return true;
40+
}
41+
}

0 commit comments

Comments
 (0)