Skip to content

Commit da68b2c

Browse files
committed
Strip nullability attributes from parameters when not available
1 parent 8cfd646 commit da68b2c

File tree

2 files changed

+78
-2
lines changed

2 files changed

+78
-2
lines changed

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

Lines changed: 75 additions & 0 deletions
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>
@@ -101,5 +114,67 @@ public static void ProcessMemberDeclarations(
101114

102115
nonSealedMemberDeclarations = annotatedMemberDeclarations;
103116
}
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+
}
104179
}
105180
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
8585
}
8686

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

9090
return new Result<(HierarchyInfo, MetadataInfo?, TInfo?)>((hierarchy, metadataInfo, info), diagnostics);
9191
})
@@ -105,7 +105,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
105105
{
106106
ImmutableArray<MemberDeclarationSyntax> sourceMemberDeclarations = item.MetadataInfo.IsSealed ? this.sealedMemberDeclarations : this.nonSealedMemberDeclarations;
107107
ImmutableArray<MemberDeclarationSyntax> filteredMemberDeclarations = FilterDeclaredMembers(item.Info, sourceMemberDeclarations);
108-
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);
109110

110111
context.AddSource($"{item.Hierarchy.FilenameHint}.g.cs", compilationUnit.GetText(Encoding.UTF8));
111112
});

0 commit comments

Comments
 (0)