Skip to content

Commit f7a980e

Browse files
authored
Merge pull request #76 from CommunityToolkit/dev/trimming-support
Add trimming support
2 parents 4427b44 + 63abbb0 commit f7a980e

29 files changed

+700
-271
lines changed

CommunityToolkit.Common/CommunityToolkit.Common.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
<PackageTags>Incremental;Loading;Collection;IncrementalLoadingCollection;String;Array;Extensions;Helpers</PackageTags>
1515
</PropertyGroup>
1616

17-
<!-- .NET Standard 2.1 and .NET 6 already have [NotNullIfNotNull] and [NotNullWhen] -->
17+
<!-- .NET Standard 2.1 and .NET 6 already have [NotNullIfNotNull] and [NotNullWhen].
18+
Additionally, also enable trimming support on .NET 6. -->
1819
<PropertyGroup Condition="'$(TargetFramework)' == 'net6.0'">
1920
<DefineConstants>NETSTANDARD2_1_OR_GREATER</DefineConstants>
21+
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
22+
<IsTrimmable>true</IsTrimmable>
2023
</PropertyGroup>
2124

2225
</Project>

CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,12 @@
3434
</ItemGroup>
3535
</When>
3636

37+
<!-- Enable trimming support on .NET 6 -->
3738
<When Condition="'$(TargetFramework)' == 'net6.0'">
3839
<PropertyGroup>
3940
<DefineConstants>NETSTANDARD2_1_OR_GREATER</DefineConstants>
41+
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
42+
<IsTrimmable>true</IsTrimmable>
4043
</PropertyGroup>
4144
</When>
4245
</Choose>

CommunityToolkit.Diagnostics/Extensions/TypeExtensions.cs

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,7 @@ static string FormatDisplayString(Type type, int genericTypeOffset, ReadOnlySpan
128128
// If the type is nested, recursively format the hierarchy as well
129129
if (type.IsNested)
130130
{
131-
Type? openDeclaringType = type.DeclaringType!;
132-
Type[]? rootGenericArguments = typeArguments.Slice(0, typeArguments.Length - genericTypeOffset).ToArray();
133-
134-
// If the declaring type is generic, we need to reconstruct the closed type
135-
// manually, as the declaring type instance doesn't retain type information.
136-
if (rootGenericArguments.Length > 0)
137-
{
138-
Type? closedDeclaringType = openDeclaringType.GetGenericTypeDefinition().MakeGenericType(rootGenericArguments);
139-
140-
return $"{FormatDisplayString(closedDeclaringType, genericTypeOffset, typeArguments)}.{displayName}";
141-
}
142-
143-
return $"{FormatDisplayString(openDeclaringType, genericTypeOffset, typeArguments)}.{displayName}";
131+
return $"{FormatDisplayString(type.DeclaringType!, genericTypeOffset, typeArguments)}.{displayName}";
144132
}
145133

146134
return $"{type.Namespace}.{displayName}";
@@ -149,21 +137,21 @@ static string FormatDisplayString(Type type, int genericTypeOffset, ReadOnlySpan
149137
// Atomically get or build the display string for the current type.
150138
return DisplayNames.GetValue(type, t =>
151139
{
152-
// By-ref types are displayed as T&
153-
if (t.IsByRef)
140+
// By-ref types are displayed as T&
141+
if (t.IsByRef)
154142
{
155143
t = t.GetElementType()!;
156144

157145
return $"{FormatDisplayString(t, 0, t.GetGenericArguments())}&";
158146
}
159147

160-
// Pointer types are displayed as T*
161-
if (t.IsPointer)
148+
// Pointer types are displayed as T*
149+
if (t.IsPointer)
162150
{
163151
int depth = 0;
164152

165-
// Calculate the pointer indirection level
166-
while (t.IsPointer)
153+
// Calculate the pointer indirection level
154+
while (t.IsPointer)
167155
{
168156
depth++;
169157
t = t.GetElementType()!;
@@ -172,8 +160,8 @@ static string FormatDisplayString(Type type, int genericTypeOffset, ReadOnlySpan
172160
return $"{FormatDisplayString(t, 0, t.GetGenericArguments())}{new string('*', depth)}";
173161
}
174162

175-
// Standard path for concrete types
176-
return FormatDisplayString(t, 0, t.GetGenericArguments());
163+
// Standard path for concrete types
164+
return FormatDisplayString(t, 0, t.GetGenericArguments());
177165
});
178166
}
179167
}

CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@
4747

4848
<When Condition="'$(TargetFramework)' == 'net6.0'">
4949

50-
<!-- NETSTANDARD2_1_OR_GREATER: includes both .NET Standard 2.1, .NET Core 3.1 and .NET 6 -->
50+
<!-- NETSTANDARD2_1_OR_GREATER: includes both .NET Standard 2.1, .NET Core 3.1 and .NET 6.
51+
Additionally, also enable trimming support on .NET 6. -->
5152
<PropertyGroup>
5253
<DefineConstants>NETSTANDARD2_1_OR_GREATER</DefineConstants>
54+
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
55+
<IsTrimmable>true</IsTrimmable>
5356
</PropertyGroup>
5457
</When>
5558

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@
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.Generic;
65
using System.Collections.Immutable;
76
using System.Linq;
87
using CommunityToolkit.Mvvm.SourceGenerators.Diagnostics;
98
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
109
using CommunityToolkit.Mvvm.SourceGenerators.Input.Models;
1110
using Microsoft.CodeAnalysis;
12-
using Microsoft.CodeAnalysis.CSharp;
1311
using Microsoft.CodeAnalysis.CSharp.Syntax;
1412
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;
1513

@@ -30,11 +28,18 @@ public INotifyPropertyChangedGenerator()
3028
}
3129

3230
/// <inheritdoc/>
33-
protected override INotifyPropertyChangedInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
31+
protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, INotifyPropertyChangedInfo Info)> GetInfo(
32+
IncrementalGeneratorInitializationContext context,
33+
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source)
3434
{
35-
bool includeAdditionalHelperMethods = attributeData.GetNamedArgument<bool>("IncludeAdditionalHelperMethods", true);
35+
static INotifyPropertyChangedInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
36+
{
37+
bool includeAdditionalHelperMethods = attributeData.GetNamedArgument<bool>("IncludeAdditionalHelperMethods", true);
38+
39+
return new(includeAdditionalHelperMethods);
40+
}
3641

37-
return new(includeAdditionalHelperMethods);
42+
return source.Select(static (item, _) => (item.Symbol, GetInfo(item.Symbol, item.AttributeData)));
3843
}
3944

4045
/// <inheritdoc/>

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/ObservableRecipientInfo.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@ namespace CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
1111
/// <param name="HasExplicitConstructors">Whether or not the target type has explicit constructors.</param>
1212
/// <param name="IsAbstract">Whether or not the target type is abstract.</param>
1313
/// <param name="IsObservableValidator">Whether or not the target type inherits from <c>ObservableValidator</c>.</param>
14+
/// <param name="IsRequiresUnreferencedCodeAttributeAvailable">Whether or not the <c>RequiresUnreferencedCodeAttribute</c> type is available.</param>
15+
/// <param name="HasOnActivatedMethod">Whether the target type has a custom <c>OnActivated()</c> method.</param>
16+
/// <param name="HasOnDeactivatedMethod">Whether the target type has a custom <c>OnDeactivated()</c> method.</param>
1417
public sealed record ObservableRecipientInfo(
1518
string TypeName,
1619
bool HasExplicitConstructors,
1720
bool IsAbstract,
18-
bool IsObservableValidator);
21+
bool IsObservableValidator,
22+
bool IsRequiresUnreferencedCodeAttributeAvailable,
23+
bool HasOnActivatedMethod,
24+
bool HasOnDeactivatedMethod);

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableObjectGenerator.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ public ObservableObjectGenerator()
2828
}
2929

3030
/// <inheritdoc/>
31-
protected override object? GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
31+
protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, object? Info)> GetInfo(
32+
IncrementalGeneratorInitializationContext context,
33+
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source)
3234
{
33-
return null;
35+
return source.Select(static (item, _) => (item.Symbol, (object?)null));
3436
}
3537

3638
/// <inheritdoc/>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ public static MemberDeclarationSyntax GetSyntax(PropertyInfo propertyInfo)
349349
SyntaxKind.StringLiteralExpression,
350350
Literal("This type is not intended to be used directly by user code")))))))
351351
.AddMembers(names.Select(name => CreateFieldDeclaration(ArgsTypeName, name)).ToArray())))
352-
.NormalizeWhitespace(eol: "\n");
352+
.NormalizeWhitespace();
353353
}
354354

355355
/// <summary>

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs

Lines changed: 80 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,38 @@ public ObservableRecipientGenerator()
3030
}
3131

3232
/// <inheritdoc/>
33-
protected override ObservableRecipientInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
33+
protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, ObservableRecipientInfo Info)> GetInfo(
34+
IncrementalGeneratorInitializationContext context,
35+
IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source)
3436
{
35-
string typeName = typeSymbol.Name;
36-
bool hasExplicitConstructors = !(typeSymbol.InstanceConstructors.Length == 1 && typeSymbol.InstanceConstructors[0] is { Parameters.IsEmpty: true, IsImplicitlyDeclared: true });
37-
bool isAbstract = typeSymbol.IsAbstract;
38-
bool isObservableValidator = typeSymbol.InheritsFrom("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
39-
40-
return new(
41-
typeName,
42-
hasExplicitConstructors,
43-
isAbstract,
44-
isObservableValidator);
37+
static ObservableRecipientInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, bool isRequiresUnreferencedCodeAttributeAvailable)
38+
{
39+
string typeName = typeSymbol.Name;
40+
bool hasExplicitConstructors = !(typeSymbol.InstanceConstructors.Length == 1 && typeSymbol.InstanceConstructors[0] is { Parameters.IsEmpty: true, IsImplicitlyDeclared: true });
41+
bool isAbstract = typeSymbol.IsAbstract;
42+
bool isObservableValidator = typeSymbol.InheritsFrom("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator");
43+
bool hasOnActivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnActivated" });
44+
bool hasOnDeactivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnDeactivated" });
45+
46+
return new(
47+
typeName,
48+
hasExplicitConstructors,
49+
isAbstract,
50+
isObservableValidator,
51+
isRequiresUnreferencedCodeAttributeAvailable,
52+
hasOnActivatedMethod,
53+
hasOnDeactivatedMethod);
54+
}
55+
56+
// Check whether [RequiresUnreferencedCode] is available
57+
IncrementalValueProvider<bool> isRequiresUnreferencedCodeAttributeAvailable =
58+
context.CompilationProvider
59+
.Select(static (item, _) => item.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute") is { DeclaredAccessibility: Accessibility.Public });
60+
61+
return
62+
source
63+
.Combine(isRequiresUnreferencedCodeAttributeAvailable)
64+
.Select(static (item, _) => (item.Left.Symbol, GetInfo(item.Left.Symbol, item.Left.AttributeData, item.Right)));
4565
}
4666

4767
/// <inheritdoc/>
@@ -103,14 +123,61 @@ protected override ImmutableArray<MemberDeclarationSyntax> FilterDeclaredMembers
103123
}
104124
}
105125

126+
MemberDeclarationSyntax FixupFilteredMemberDeclaration(MemberDeclarationSyntax member)
127+
{
128+
// Make OnActivated partial if the type already has the method
129+
if (info.HasOnActivatedMethod &&
130+
member is MethodDeclarationSyntax { Identifier.ValueText: "OnActivated" } onActivatdMethod)
131+
{
132+
SyntaxNode attributeNode =
133+
member
134+
.DescendantNodes()
135+
.OfType<AttributeListSyntax>()
136+
.First(node => node.Attributes[0].Name is QualifiedNameSyntax { Right: IdentifierNameSyntax { Identifier.ValueText: "RequiresUnreferencedCode" } });
137+
138+
return
139+
onActivatdMethod
140+
.RemoveNode(attributeNode, SyntaxRemoveOptions.KeepExteriorTrivia)!
141+
.AddModifiers(Token(SyntaxKind.PartialKeyword))
142+
.WithBody(null)
143+
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
144+
}
145+
146+
// Make OnDeactivated partial if the type already has the method
147+
if (info.HasOnDeactivatedMethod &&
148+
member is MethodDeclarationSyntax { Identifier.ValueText: "OnDeactivated" } onDeactivatedMethod)
149+
{
150+
return
151+
onDeactivatedMethod
152+
.AddModifiers(Token(SyntaxKind.PartialKeyword))
153+
.WithBody(null)
154+
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
155+
}
156+
157+
// Remove [RequiresUnreferencedCode] if the attribute is not available
158+
if (!info.IsRequiresUnreferencedCodeAttributeAvailable &&
159+
member is PropertyDeclarationSyntax { Identifier.ValueText: "IsActive" } or MethodDeclarationSyntax { Identifier.ValueText: "OnActivated" })
160+
{
161+
SyntaxNode attributeNode =
162+
member
163+
.DescendantNodes()
164+
.OfType<AttributeListSyntax>()
165+
.First(node => node.Attributes[0].Name is QualifiedNameSyntax { Right: IdentifierNameSyntax { Identifier.ValueText: "RequiresUnreferencedCode" } });
166+
167+
return member.RemoveNode(attributeNode, SyntaxRemoveOptions.KeepExteriorTrivia)!;
168+
}
169+
170+
return member;
171+
}
172+
106173
// Skip the SetProperty overloads if the target type inherits from ObservableValidator, to avoid conflicts
107174
if (info.IsObservableValidator)
108175
{
109176
foreach (MemberDeclarationSyntax member in memberDeclarations.Where(static member => member is not ConstructorDeclarationSyntax))
110177
{
111178
if (member is not MethodDeclarationSyntax { Identifier.ValueText: "SetProperty" })
112179
{
113-
builder.Add(member);
180+
builder.Add(FixupFilteredMemberDeclaration(member));
114181
}
115182
}
116183

@@ -120,7 +187,7 @@ protected override ImmutableArray<MemberDeclarationSyntax> FilterDeclaredMembers
120187
// If the target type has at least one custom constructor, only generate methods
121188
foreach (MemberDeclarationSyntax member in memberDeclarations.Where(static member => member is not ConstructorDeclarationSyntax))
122189
{
123-
builder.Add(member);
190+
builder.Add(FixupFilteredMemberDeclaration(member));
124191
}
125192

126193
return builder.ToImmutable();

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.Execute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public static CompilationUnitSyntax GetSyntax()
150150
AttributeArgument(LiteralExpression(
151151
SyntaxKind.StringLiteralExpression,
152152
Literal("This type is not intended to be used directly by user code")))))))))
153-
.NormalizeWhitespace(eol: "\n");
153+
.NormalizeWhitespace();
154154
}
155155

156156
/// <summary>
@@ -229,7 +229,7 @@ public static CompilationUnitSyntax GetSyntax(ValidationInfo validationInfo)
229229
IdentifierName("obj")))))))
230230
.AddStatements(EnumerateValidationStatements(validationInfo).ToArray())),
231231
ReturnStatement(IdentifierName("ValidateAllProperties")))))))
232-
.NormalizeWhitespace(eol: "\n");
232+
.NormalizeWhitespace();
233233
}
234234

235235
/// <summary>

0 commit comments

Comments
 (0)