Skip to content

Commit 52f0a4f

Browse files
authored
Merge pull request #538 from CommunityToolkit/dev/remove-nullability-attributes
Remove nullability attributes generator
2 parents 2a47127 + da68b2c commit 52f0a4f

File tree

7 files changed

+98
-137
lines changed

7 files changed

+98
-137
lines changed

src/CommunityToolkit.Mvvm.SourceGenerators/Attributes/NullabilityAttributesGenerator.cs

Lines changed: 0 additions & 77 deletions
This file was deleted.

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@
1313
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
1414
<LogicalName>INotifyPropertyChanged.cs</LogicalName>
1515
</EmbeddedResource>
16-
<EmbeddedResource Include="$(MSBuildThisFileDirectory)EmbeddedResources\NotNullAttribute.cs">
17-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
18-
<LogicalName>NotNullAttribute.cs</LogicalName>
19-
</EmbeddedResource>
20-
<EmbeddedResource Include="$(MSBuildThisFileDirectory)EmbeddedResources\NotNullIfNotNullAttribute.cs">
21-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
22-
<LogicalName>NotNullIfNotNullAttribute.cs</LogicalName>
23-
</EmbeddedResource>
2416
<EmbeddedResource Include="$(MSBuildThisFileDirectory)EmbeddedResources\ObservableObject.cs">
2517
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
2618
<LogicalName>ObservableObject.cs</LogicalName>
@@ -31,7 +23,6 @@
3123
</EmbeddedResource>
3224
</ItemGroup>
3325
<ItemGroup>
34-
<Compile Include="$(MSBuildThisFileDirectory)Attributes\NullabilityAttributesGenerator.cs" />
3526
<Compile Include="$(MSBuildThisFileDirectory)ComponentModel\INotifyPropertyChangedGenerator.cs" />
3627
<Compile Include="$(MSBuildThisFileDirectory)ComponentModel\Models\AttributeInfo.cs" />
3728
<Compile Include="$(MSBuildThisFileDirectory)ComponentModel\Models\INotifyPropertyChangedInfo.cs" />

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

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Linq;
99
using System.Reflection;
1010
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
11+
using CommunityToolkit.Mvvm.SourceGenerators.Helpers;
1112
using Microsoft.CodeAnalysis;
1213
using Microsoft.CodeAnalysis.CSharp;
1314
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -23,6 +24,18 @@ partial class TransitiveMembersGenerator<TInfo>
2324
/// </summary>
2425
internal static class Execute
2526
{
27+
/// <summary>
28+
/// Checks whether or not nullability attributes are currently available.
29+
/// </summary>
30+
/// <param name="compilation">The input <see cref="Compilation"/> instance.</param>
31+
/// <returns>Whether or not nullability attributes are currently available.</returns>
32+
public static bool IsNullabilitySupported(Compilation compilation)
33+
{
34+
return
35+
compilation.HasAccessibleTypeWithMetadataName("System.Diagnostics.CodeAnalysis.NotNullAttribute") &&
36+
compilation.HasAccessibleTypeWithMetadataName("System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute");
37+
}
38+
2639
/// <summary>
2740
/// Loads the source <see cref="ClassDeclarationSyntax"/> instance to get member declarations from.
2841
/// </summary>
@@ -68,8 +81,14 @@ public static void ProcessMemberDeclarations(
6881
AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(generatorType.Assembly.GetName().Version.ToString())))))))
6982
.WithLeadingTrivia(member.GetLeadingTrivia());
7083

84+
// [DebuggerNonUserCode] is not supported on interfaces, fields and event
85+
if (member.Kind() is not (SyntaxKind.InterfaceDeclaration or SyntaxKind.FieldDeclaration or SyntaxKind.EventFieldDeclaration))
86+
{
87+
member = member.AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))));
88+
}
89+
7190
// [ExcludeFromCodeCoverage] is not supported on interfaces and fields
72-
if (member.Kind() is not SyntaxKind.InterfaceDeclaration and not SyntaxKind.FieldDeclaration)
91+
if (member.Kind() is not (SyntaxKind.InterfaceDeclaration or SyntaxKind.FieldDeclaration))
7392
{
7493
member = member.AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")))));
7594
}
@@ -95,5 +114,67 @@ public static void ProcessMemberDeclarations(
95114

96115
nonSealedMemberDeclarations = annotatedMemberDeclarations;
97116
}
117+
118+
/// <summary>
119+
/// Adjusts the nullability annotations for generated members, dropping attributes if needed.
120+
/// </summary>
121+
/// <param name="memberDeclarations">The input sequence of member declarations to generate.</param>
122+
/// <param name="isNullabilitySupported">Whether nullability attributes are supported.</param>
123+
/// <returns>The updated collection of member declarations to generate.</returns>
124+
public static ImmutableArray<MemberDeclarationSyntax> AdjustMemberDeclarationNullabilityAnnotations(
125+
ImmutableArray<MemberDeclarationSyntax> memberDeclarations,
126+
bool isNullabilitySupported)
127+
{
128+
// If nullability attributes are supported, there is nothing else to do
129+
if (isNullabilitySupported)
130+
{
131+
return memberDeclarations;
132+
}
133+
134+
using ImmutableArrayBuilder<MemberDeclarationSyntax> builder = ImmutableArrayBuilder<MemberDeclarationSyntax>.Rent();
135+
136+
NullabilityAdjustmentSyntaxRewriter syntaxRewriter = new();
137+
138+
// Iterate over all members and adjust the method declarations, if needed
139+
foreach (MemberDeclarationSyntax memberDeclaration in memberDeclarations)
140+
{
141+
if (memberDeclaration is MethodDeclarationSyntax methodDeclaration)
142+
{
143+
builder.Add((MethodDeclarationSyntax)syntaxRewriter.Visit(methodDeclaration));
144+
}
145+
else
146+
{
147+
builder.Add(memberDeclaration);
148+
}
149+
}
150+
151+
return builder.ToImmutable();
152+
}
153+
154+
/// <summary>
155+
/// A custom syntax rewriter that removes nullability attributes from method parameters.
156+
/// </summary>
157+
private sealed class NullabilityAdjustmentSyntaxRewriter : CSharpSyntaxRewriter
158+
{
159+
/// <inheritdoc/>
160+
public override SyntaxNode? VisitParameter(ParameterSyntax node)
161+
{
162+
SyntaxNode? updatedNode = base.VisitParameter(node);
163+
164+
// If the node is a parameter node with a single attribute being either [NotNull] or [NotNullIfNotNull], drop it.
165+
// This expression will match all parameters with the following format:
166+
//
167+
// ([global::<NAMESPACE>.<ATTRIBUTE_NAME>] <TYPE> <PARAMETER_NAME>)
168+
//
169+
// Where <ATTRIBUTE_NAME> is either "NotNull" or "NotNullIfNotNull". This relies on parameters following this structure
170+
// for nullability annotations, but that is fine in this context given the only source files are the embedded ones.
171+
if (updatedNode is ParameterSyntax { AttributeLists: [{ Attributes: [{ Name: QualifiedNameSyntax { Right.Identifier.Text: "NotNull" or "NotNullIfNotNull"} }] }] } parameterNode)
172+
{
173+
return parameterNode.WithAttributeLists(default);
174+
}
175+
176+
return updatedNode;
177+
}
178+
}
98179
}
99180
}

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

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private protected TransitiveMembersGenerator(string fullyQualifiedAttributeMetad
6161
public void Initialize(IncrementalGeneratorInitializationContext context)
6262
{
6363
// Gather all generation info, and any diagnostics
64-
IncrementalValuesProvider<Result<(HierarchyInfo Hierarchy, bool IsSealed, TInfo? Info)>> generationInfoWithErrors =
64+
IncrementalValuesProvider<Result<(HierarchyInfo Hierarchy, MetadataInfo? MetadataInfo, TInfo? Info)>> generationInfoWithErrors =
6565
context.SyntaxProvider
6666
.ForAttributeWithMetadataName(
6767
this.fullyQualifiedAttributeMetadataName,
@@ -81,30 +81,32 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
8181
// If there are any diagnostics, there's no need to compute the hierarchy info at all, just return them
8282
if (diagnostics.Length > 0)
8383
{
84-
return new Result<(HierarchyInfo, bool, TInfo?)>(default, diagnostics);
84+
return new Result<(HierarchyInfo, MetadataInfo?, TInfo?)>(default, diagnostics);
8585
}
8686

8787
HierarchyInfo hierarchy = HierarchyInfo.From(typeSymbol);
88+
MetadataInfo metadataInfo = new(typeSymbol.IsSealed, Execute.IsNullabilitySupported(context.SemanticModel.Compilation));
8889

89-
return new Result<(HierarchyInfo, bool, TInfo?)>((hierarchy, typeSymbol.IsSealed, info), diagnostics);
90+
return new Result<(HierarchyInfo, MetadataInfo?, TInfo?)>((hierarchy, metadataInfo, info), diagnostics);
9091
})
9192
.Where(static item => item is not null)!;
9293

9394
// Emit the diagnostic, if needed
9495
context.ReportDiagnostics(generationInfoWithErrors.Select(static (item, _) => item.Errors));
9596

9697
// Get the filtered sequence to enable caching
97-
IncrementalValuesProvider<(HierarchyInfo Hierarchy, bool IsSealed, TInfo Info)> generationInfo =
98+
IncrementalValuesProvider<(HierarchyInfo Hierarchy, MetadataInfo MetadataInfo, TInfo Info)> generationInfo =
9899
generationInfoWithErrors
99100
.Where(static item => item.Errors.IsEmpty)
100101
.Select(static (item, _) => item.Value)!;
101102

102103
// Generate the required members
103104
context.RegisterSourceOutput(generationInfo, (context, item) =>
104105
{
105-
ImmutableArray<MemberDeclarationSyntax> sourceMemberDeclarations = item.IsSealed ? this.sealedMemberDeclarations : this.nonSealedMemberDeclarations;
106+
ImmutableArray<MemberDeclarationSyntax> sourceMemberDeclarations = item.MetadataInfo.IsSealed ? this.sealedMemberDeclarations : this.nonSealedMemberDeclarations;
106107
ImmutableArray<MemberDeclarationSyntax> filteredMemberDeclarations = FilterDeclaredMembers(item.Info, sourceMemberDeclarations);
107-
CompilationUnitSyntax compilationUnit = item.Hierarchy.GetCompilationUnit(filteredMemberDeclarations, this.classDeclaration.BaseList);
108+
ImmutableArray<MemberDeclarationSyntax> updatedMemberDeclarations = Execute.AdjustMemberDeclarationNullabilityAnnotations(filteredMemberDeclarations, item.MetadataInfo.IsNullabilitySupported);
109+
CompilationUnitSyntax compilationUnit = item.Hierarchy.GetCompilationUnit(updatedMemberDeclarations, this.classDeclaration.BaseList);
108110

109111
context.AddSource($"{item.Hierarchy.FilenameHint}.g.cs", compilationUnit.GetText(Encoding.UTF8));
110112
});
@@ -128,4 +130,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
128130
/// <param name="memberDeclarations">The input sequence of <see cref="MemberDeclarationSyntax"/> instances to generate.</param>
129131
/// <returns>A sequence of <see cref="MemberDeclarationSyntax"/> nodes to emit in the generated file.</returns>
130132
protected abstract ImmutableArray<MemberDeclarationSyntax> FilterDeclaredMembers(TInfo info, ImmutableArray<MemberDeclarationSyntax> memberDeclarations);
131-
}
133+
134+
/// <summary>
135+
/// A small record for metadata info on types to generate.
136+
/// </summary>
137+
/// <param name="IsSealed">Whether the target type is sealed.</param>
138+
/// <param name="IsNullabilitySupported">Whether nullability attributes are supported.</param>
139+
private sealed record MetadataInfo(bool IsSealed, bool IsNullabilitySupported);
140+
}

src/CommunityToolkit.Mvvm.SourceGenerators/EmbeddedResources/NotNullAttribute.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/CommunityToolkit.Mvvm.SourceGenerators/EmbeddedResources/NotNullIfNotNullAttribute.cs

Lines changed: 0 additions & 27 deletions
This file was deleted.

tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsDiagnostics.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1639,7 +1639,6 @@ private static async Task VerifyAnalyzerDiagnosticsAndSuccessfulGeneration<TAnal
16391639
IIncrementalGenerator[] generators =
16401640
{
16411641
new IMessengerRegisterAllGenerator(),
1642-
new NullabilityAttributesGenerator(),
16431642
new ObservableObjectGenerator(),
16441643
new INotifyPropertyChangedGenerator(),
16451644
new ObservablePropertyGenerator(),

0 commit comments

Comments
 (0)