Skip to content

Commit c70e973

Browse files
committed
Use type symbols for lookup in new analyzers
1 parent b7f121c commit c70e973

File tree

4 files changed

+74
-13
lines changed

4 files changed

+74
-13
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ public override void Initialize(AnalysisContext context)
3535
}
3636

3737
// Only inspect classes that are using [NotifyDataErrorInfo]
38-
if (!classSymbol.HasAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.NotifyDataErrorInfoAttribute"))
38+
if (context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.NotifyDataErrorInfoAttribute") is not INamedTypeSymbol notifyDataErrorInfoAttributeSymbol ||
39+
!classSymbol.HasAttributeWithType(notifyDataErrorInfoAttributeSymbol))
3940
{
4041
return;
4142
}
4243

4344
// If the containing type is not valid, emit a diagnostic
44-
if (!classSymbol.InheritsFromFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableValidator"))
45+
if (context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableValidator") is not INamedTypeSymbol observableValidatorSymbol ||
46+
!classSymbol.InheritsFromType(observableValidatorSymbol))
4547
{
4648
context.ReportDiagnostic(Diagnostic.Create(
4749
InvalidTypeForNotifyDataErrorInfoError,

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,17 @@ public override void Initialize(AnalysisContext context)
3535
}
3636

3737
// Only inspect classes that are using [NotifyPropertyChangedRecipients]
38-
if (!classSymbol.HasAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.NotifyPropertyChangedRecipientsAttribute"))
38+
if (context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.NotifyPropertyChangedRecipientsAttribute") is not INamedTypeSymbol notifyPropertyChangedRecipientsAttributeSymbol ||
39+
!classSymbol.HasAttributeWithType(notifyPropertyChangedRecipientsAttributeSymbol))
3940
{
4041
return;
4142
}
4243

4344
// If the containing type is not valid, emit a diagnostic
44-
if (!classSymbol.InheritsFromFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient") &&
45-
!classSymbol.HasOrInheritsAttributeWithFullyQualifiedMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableRecipientAttribute"))
45+
if (context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient") is INamedTypeSymbol observableRecipientSymbol &&
46+
context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservableRecipientAttribute") is INamedTypeSymbol observableRecipientAttributeSymbol &&
47+
!classSymbol.InheritsFromType(observableRecipientSymbol) &&
48+
!classSymbol.HasOrInheritsAttributeWithType(observableRecipientAttributeSymbol))
4649
{
4750
context.ReportDiagnostic(Diagnostic.Create(
4851
InvalidTypeForNotifyPropertyChangedRecipientsError,

src/CommunityToolkit.Mvvm.SourceGenerators/Extensions/ISymbolExtensions.cs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using System.Collections.Immutable;
65
#if !ROSLYN_4_3_1_OR_GREATER
76
using System.Diagnostics.CodeAnalysis;
87
#endif
@@ -54,9 +53,7 @@ public static bool HasFullyQualifiedName(this ISymbol symbol, string name)
5453
/// <returns>Whether or not <paramref name="symbol"/> has an attribute with the specified name.</returns>
5554
public static bool HasAttributeWithFullyQualifiedMetadataName(this ISymbol symbol, string name)
5655
{
57-
ImmutableArray<AttributeData> attributes = symbol.GetAttributes();
58-
59-
foreach (AttributeData attribute in attributes)
56+
foreach (AttributeData attribute in symbol.GetAttributes())
6057
{
6158
if (attribute.AttributeClass?.HasFullyQualifiedMetadataName(name) == true)
6259
{
@@ -67,6 +64,25 @@ public static bool HasAttributeWithFullyQualifiedMetadataName(this ISymbol symbo
6764
return false;
6865
}
6966

67+
/// <summary>
68+
/// Checks whether or not a given symbol has an attribute with the specified fully qualified metadata name.
69+
/// </summary>
70+
/// <param name="symbol">The input <see cref="ISymbol"/> instance to check.</param>
71+
/// <param name="typeSymbol">The <see cref="ITypeSymbol"/> instance for the attribute type to look for.</param>
72+
/// <returns>Whether or not <paramref name="symbol"/> has an attribute with the specified type.</returns>
73+
public static bool HasAttributeWithType(this ISymbol symbol, ITypeSymbol typeSymbol)
74+
{
75+
foreach (AttributeData attribute in symbol.GetAttributes())
76+
{
77+
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, typeSymbol))
78+
{
79+
return true;
80+
}
81+
}
82+
83+
return false;
84+
}
85+
7086
#if !ROSLYN_4_3_1_OR_GREATER
7187
/// <summary>
7288
/// Tries to get an attribute with the specified fully qualified metadata name.
@@ -77,9 +93,7 @@ public static bool HasAttributeWithFullyQualifiedMetadataName(this ISymbol symbo
7793
/// <returns>Whether or not <paramref name="symbol"/> has an attribute with the specified name.</returns>
7894
public static bool TryGetAttributeWithFullyQualifiedMetadataName(this ISymbol symbol, string name, [NotNullWhen(true)] out AttributeData? attributeData)
7995
{
80-
ImmutableArray<AttributeData> attributes = symbol.GetAttributes();
81-
82-
foreach (AttributeData attribute in attributes)
96+
foreach (AttributeData attribute in symbol.GetAttributes())
8397
{
8498
if (attribute.AttributeClass?.HasFullyQualifiedMetadataName(name) == true)
8599
{

src/CommunityToolkit.Mvvm.SourceGenerators/Extensions/ITypeSymbolExtensions.cs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public static bool InheritsFromFullyQualifiedMetadataName(this ITypeSymbol typeS
4343
{
4444
INamedTypeSymbol? baseType = typeSymbol.BaseType;
4545

46-
while (baseType != null)
46+
while (baseType is not null)
4747
{
4848
if (baseType.HasFullyQualifiedMetadataName(name))
4949
{
@@ -56,6 +56,29 @@ public static bool InheritsFromFullyQualifiedMetadataName(this ITypeSymbol typeS
5656
return false;
5757
}
5858

59+
/// <summary>
60+
/// Checks whether or not a given <see cref="ITypeSymbol"/> inherits from a specified type.
61+
/// </summary>
62+
/// <param name="typeSymbol">The target <see cref="ITypeSymbol"/> instance to check.</param>
63+
/// <param name="baseTypeSymbol">The <see cref="ITypeSymbol"/> instane to check for inheritance from.</param>
64+
/// <returns>Whether or not <paramref name="typeSymbol"/> inherits from <paramref name="baseTypeSymbol"/>.</returns>
65+
public static bool InheritsFromType(this ITypeSymbol typeSymbol, ITypeSymbol baseTypeSymbol)
66+
{
67+
INamedTypeSymbol? currentBaseTypeSymbol = typeSymbol.BaseType;
68+
69+
while (currentBaseTypeSymbol is not null)
70+
{
71+
if (SymbolEqualityComparer.Default.Equals(currentBaseTypeSymbol, baseTypeSymbol))
72+
{
73+
return true;
74+
}
75+
76+
currentBaseTypeSymbol = currentBaseTypeSymbol.BaseType;
77+
}
78+
79+
return false;
80+
}
81+
5982
/// <summary>
6083
/// Checks whether or not a given <see cref="ITypeSymbol"/> implements an interface with a specified name.
6184
/// </summary>
@@ -113,6 +136,25 @@ public static bool HasOrInheritsAttributeWithFullyQualifiedMetadataName(this ITy
113136
return false;
114137
}
115138

139+
/// <summary>
140+
/// Checks whether or not a given <see cref="ITypeSymbol"/> has or inherits a specified attribute.
141+
/// </summary>
142+
/// <param name="typeSymbol">The target <see cref="ITypeSymbol"/> instance to check.</param>
143+
/// <param name="baseTypeSymbol">The <see cref="ITypeSymbol"/> instane to check for inheritance from.</param>
144+
/// <returns>Whether or not <paramref name="typeSymbol"/> has or inherits an attribute of type <paramref name="baseTypeSymbol"/>.</returns>
145+
public static bool HasOrInheritsAttributeWithType(this ITypeSymbol typeSymbol, ITypeSymbol baseTypeSymbol)
146+
{
147+
for (ITypeSymbol? currentType = typeSymbol; currentType is not null; currentType = currentType.BaseType)
148+
{
149+
if (currentType.HasAttributeWithType(baseTypeSymbol))
150+
{
151+
return true;
152+
}
153+
}
154+
155+
return false;
156+
}
157+
116158
/// <summary>
117159
/// Checks whether or not a given <see cref="ITypeSymbol"/> inherits a specified attribute.
118160
/// If the type has no base type, this method will automatically handle that and return <see langword="false"/>.

0 commit comments

Comments
 (0)