Skip to content

Commit 0ed3e4d

Browse files
committed
Added [GeneratedCode] attribute to some generators, refactoring
1 parent 0a08615 commit 0ed3e4d

File tree

5 files changed

+72
-994
lines changed

5 files changed

+72
-994
lines changed

Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.cs

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.CodeDom.Compiler;
67
using System.Collections.Generic;
78
using System.Diagnostics.CodeAnalysis;
89
using System.Diagnostics.Contracts;
@@ -107,15 +108,19 @@ private void OnExecute(
107108
{
108109
ClassDeclarationSyntax sourceDeclaration = sourceSyntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().First();
109110
UsingDirectiveSyntax[] usingDirectives = sourceSyntaxTree.GetRoot().DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
111+
BaseListSyntax? baseListSyntax = BaseList(SeparatedList(
112+
sourceDeclaration.BaseList?.Types
113+
.OfType<SimpleBaseTypeSyntax>()
114+
.Select(static t => t.Type)
115+
.OfType<IdentifierNameSyntax>()
116+
.Where(static t => t.Identifier.ValueText.StartsWith("I"))
117+
.Select(static t => SimpleBaseType(t))
118+
.ToArray()
119+
?? Array.Empty<BaseTypeSyntax>()));
110120

111-
IEnumerable<MemberDeclarationSyntax> generatedMembers = FilterDeclaredMembers(context, attributeData, classDeclaration, classDeclarationSymbol, sourceDeclaration);
112-
113-
// If the target class is sealed, make protected members private and remove the virtual modifier
114-
if (classDeclarationSymbol.IsSealed)
121+
if (baseListSyntax.Types.Count == 0)
115122
{
116-
generatedMembers = generatedMembers.Select(static member => member
117-
.ReplaceModifier(SyntaxKind.ProtectedKeyword, SyntaxKind.PrivateKeyword)
118-
.RemoveModifier(SyntaxKind.VirtualKeyword));
123+
baseListSyntax = null;
119124
}
120125

121126
// Create the class declaration for the user type. This will produce a tree as follows:
@@ -127,8 +132,8 @@ private void OnExecute(
127132
var classDeclarationSyntax =
128133
ClassDeclaration(classDeclaration.Identifier.Text)
129134
.WithModifiers(classDeclaration.Modifiers)
130-
.WithBaseList(sourceDeclaration.BaseList)
131-
.AddMembers(generatedMembers.ToArray());
135+
.WithBaseList(baseListSyntax)
136+
.AddMembers(OnLoadDeclaredMembers(context, attributeData, classDeclaration, classDeclarationSymbol, sourceDeclaration).ToArray());
132137

133138
TypeDeclarationSyntax typeDeclarationSyntax = classDeclarationSyntax;
134139

@@ -152,14 +157,69 @@ private void OnExecute(
152157
CompilationUnit()
153158
.AddMembers(NamespaceDeclaration(IdentifierName(namespaceName))
154159
.AddMembers(typeDeclarationSyntax))
155-
.AddUsings(usingDirectives)
160+
.AddUsings(usingDirectives.ToArray())
156161
.NormalizeWhitespace()
157162
.ToFullString();
158163

159164
// Add the partial type
160165
context.AddSource($"[{typeof(TAttribute).Name}]_[{classDeclarationSymbol.GetFullMetadataNameForFileName()}].cs", SourceText.From(source, Encoding.UTF8));
161166
}
162167

168+
/// <summary>
169+
/// Loads the <see cref="MemberDeclarationSyntax"/> nodes to generate from the input parsed tree.
170+
/// </summary>
171+
/// <param name="context">The input <see cref="GeneratorExecutionContext"/> instance to use.</param>
172+
/// <param name="attributeData">The <see cref="AttributeData"/> for the current attribute being processed.</param>
173+
/// <param name="classDeclaration">The <see cref="ClassDeclarationSyntax"/> node to process.</param>
174+
/// <param name="classDeclarationSymbol">The <see cref="INamedTypeSymbol"/> for <paramref name="classDeclaration"/>.</param>
175+
/// <param name="sourceDeclaration">The parsed <see cref="ClassDeclarationSyntax"/> instance with the source nodes.</param>
176+
/// <returns>A sequence of <see cref="MemberDeclarationSyntax"/> nodes to emit in the generated file.</returns>
177+
private IEnumerable<MemberDeclarationSyntax> OnLoadDeclaredMembers(
178+
GeneratorExecutionContext context,
179+
AttributeData attributeData,
180+
ClassDeclarationSyntax classDeclaration,
181+
INamedTypeSymbol classDeclarationSymbol,
182+
ClassDeclarationSyntax sourceDeclaration)
183+
{
184+
IEnumerable<MemberDeclarationSyntax> generatedMembers = FilterDeclaredMembers(context, attributeData, classDeclaration, classDeclarationSymbol, sourceDeclaration);
185+
186+
// Add the attributes on each member
187+
return generatedMembers.Select(member =>
188+
{
189+
// [GeneratedCode] is always present
190+
member = member
191+
.WithoutLeadingTrivia()
192+
.AddAttributeLists(AttributeList(SingletonSeparatedList(
193+
Attribute(IdentifierName($"global::System.CodeDom.Compiler.GeneratedCode"))
194+
.AddArgumentListArguments(
195+
AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GetType().FullName))),
196+
AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GetType().Assembly.GetName().Version.ToString())))))))
197+
.WithLeadingTrivia(member.GetLeadingTrivia());
198+
199+
// [DebuggerNonUserCode] is not supported over interfaces, events or fields
200+
if (member.Kind() is not SyntaxKind.InterfaceDeclaration and not SyntaxKind.EventFieldDeclaration and not SyntaxKind.FieldDeclaration)
201+
{
202+
member = member.AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))));
203+
}
204+
205+
// [ExcludeFromCodeCoverage] is not supported on interfaces and fields
206+
if (member.Kind() is not SyntaxKind.InterfaceDeclaration and not SyntaxKind.FieldDeclaration)
207+
{
208+
member = member.AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")))));
209+
}
210+
211+
// If the target class is sealed, make protected members private and remove the virtual modifier
212+
if (classDeclarationSymbol.IsSealed)
213+
{
214+
return member
215+
.ReplaceModifier(SyntaxKind.ProtectedKeyword, SyntaxKind.PrivateKeyword)
216+
.RemoveModifier(SyntaxKind.VirtualKeyword);
217+
}
218+
219+
return member;
220+
});
221+
}
222+
163223
/// <summary>
164224
/// Validates a target type being processed.
165225
/// </summary>

Microsoft.Toolkit.Mvvm.SourceGenerators/EmbeddedResources/INotifyPropertyChanged.cs

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
using System;
88
using System.Collections.Generic;
99
using System.ComponentModel;
10-
using System.Diagnostics;
11-
using System.Diagnostics.CodeAnalysis;
1210
using System.Runtime.CompilerServices;
1311
using System.Threading.Tasks;
1412

@@ -20,15 +18,12 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
2018
public abstract class NotifyPropertyChanged : INotifyPropertyChanged
2119
{
2220
/// <inheritdoc cref="INotifyPropertyChanged.PropertyChanged"/>
23-
[ExcludeFromCodeCoverage]
2421
public event PropertyChangedEventHandler? PropertyChanged;
2522

2623
/// <summary>
2724
/// Raises the <see cref="PropertyChanged"/> event.
2825
/// </summary>
2926
/// <param name="e">The input <see cref="PropertyChangedEventArgs"/> instance.</param>
30-
[DebuggerNonUserCode]
31-
[ExcludeFromCodeCoverage]
3227
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
3328
{
3429
PropertyChanged?.Invoke(this, e);
@@ -38,8 +33,6 @@ protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
3833
/// Raises the <see cref="PropertyChanged"/> event.
3934
/// </summary>
4035
/// <param name="propertyName">(optional) The name of the property that changed.</param>
41-
[DebuggerNonUserCode]
42-
[ExcludeFromCodeCoverage]
4336
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
4437
{
4538
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
@@ -57,8 +50,6 @@ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
5750
/// <remarks>
5851
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
5952
/// </remarks>
60-
[DebuggerNonUserCode]
61-
[ExcludeFromCodeCoverage]
6253
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)
6354
{
6455
if (EqualityComparer<T>.Default.Equals(field, newValue))
@@ -84,8 +75,6 @@ protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string
8475
/// <param name="comparer">The <see cref="IEqualityComparer{T}"/> instance to use to compare the input values.</param>
8576
/// <param name="propertyName">(optional) The name of the property that changed.</param>
8677
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
87-
[DebuggerNonUserCode]
88-
[ExcludeFromCodeCoverage]
8978
protected bool SetProperty<T>(ref T field, T newValue, IEqualityComparer<T> comparer, [CallerMemberName] string? propertyName = null)
9079
{
9180
if (comparer.Equals(field, newValue))
@@ -121,8 +110,6 @@ protected bool SetProperty<T>(ref T field, T newValue, IEqualityComparer<T> comp
121110
/// <remarks>
122111
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
123112
/// </remarks>
124-
[DebuggerNonUserCode]
125-
[ExcludeFromCodeCoverage]
126113
protected bool SetProperty<T>(T oldValue, T newValue, Action<T> callback, [CallerMemberName] string? propertyName = null)
127114
{
128115
if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
@@ -149,8 +136,6 @@ protected bool SetProperty<T>(T oldValue, T newValue, Action<T> callback, [Calle
149136
/// <param name="callback">A callback to invoke to update the property value.</param>
150137
/// <param name="propertyName">(optional) The name of the property that changed.</param>
151138
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
152-
[DebuggerNonUserCode]
153-
[ExcludeFromCodeCoverage]
154139
protected bool SetProperty<T>(T oldValue, T newValue, IEqualityComparer<T> comparer, Action<T> callback, [CallerMemberName] string? propertyName = null)
155140
{
156141
if (comparer.Equals(oldValue, newValue))
@@ -217,8 +202,6 @@ protected bool SetProperty<T>(T oldValue, T newValue, IEqualityComparer<T> compa
217202
/// <remarks>
218203
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
219204
/// </remarks>
220-
[DebuggerNonUserCode]
221-
[ExcludeFromCodeCoverage]
222205
protected bool SetProperty<TModel, T>(T oldValue, T newValue, TModel model, Action<TModel, T> callback, [CallerMemberName] string? propertyName = null)
223206
where TModel : class
224207
{
@@ -250,8 +233,6 @@ protected bool SetProperty<TModel, T>(T oldValue, T newValue, TModel model, Acti
250233
/// <param name="callback">The callback to invoke to set the target property value, if a change has occurred.</param>
251234
/// <param name="propertyName">(optional) The name of the property that changed.</param>
252235
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
253-
[DebuggerNonUserCode]
254-
[ExcludeFromCodeCoverage]
255236
protected bool SetProperty<TModel, T>(T oldValue, T newValue, IEqualityComparer<T> comparer, TModel model, Action<TModel, T> callback, [CallerMemberName] string? propertyName = null)
256237
where TModel : class
257238
{
@@ -299,8 +280,6 @@ protected bool SetProperty<TModel, T>(T oldValue, T newValue, IEqualityComparer<
299280
/// <paramref name="taskNotifier"/> is different than the previous one, and it does not mean the new
300281
/// <see cref="Task"/> instance passed as argument is in any particular state.
301282
/// </remarks>
302-
[DebuggerNonUserCode]
303-
[ExcludeFromCodeCoverage]
304283
protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
305284
{
306285
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName);
@@ -321,8 +300,6 @@ protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier,
321300
/// <remarks>
322301
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
323302
/// </remarks>
324-
[DebuggerNonUserCode]
325-
[ExcludeFromCodeCoverage]
326303
protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, Action<Task?> callback, [CallerMemberName] string? propertyName = null)
327304
{
328305
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
@@ -361,8 +338,6 @@ protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier,
361338
/// <paramref name="taskNotifier"/> is different than the previous one, and it does not mean the new
362339
/// <see cref="Task{TResult}"/> instance passed as argument is in any particular state.
363340
/// </remarks>
364-
[DebuggerNonUserCode]
365-
[ExcludeFromCodeCoverage]
366341
protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, [CallerMemberName] string? propertyName = null)
367342
{
368343
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName);
@@ -384,8 +359,6 @@ protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNoti
384359
/// <remarks>
385360
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
386361
/// </remarks>
387-
[DebuggerNonUserCode]
388-
[ExcludeFromCodeCoverage]
389362
protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, Action<Task<T>?> callback, [CallerMemberName] string? propertyName = null)
390363
{
391364
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
@@ -400,8 +373,6 @@ protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNoti
400373
/// <param name="callback">A callback to invoke to update the property value.</param>
401374
/// <param name="propertyName">(optional) The name of the property that changed.</param>
402375
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
403-
[DebuggerNonUserCode]
404-
[ExcludeFromCodeCoverage]
405376
private bool SetPropertyAndNotifyOnCompletion<TTask>(ITaskNotifier<TTask> taskNotifier, TTask? newValue, Action<TTask?> callback, [CallerMemberName] string? propertyName = null)
406377
where TTask : Task
407378
{
@@ -456,16 +427,12 @@ private interface ITaskNotifier<TTask>
456427
/// <summary>
457428
/// Gets or sets the wrapped <typeparamref name="TTask"/> value.
458429
/// </summary>
459-
[DebuggerNonUserCode]
460-
[ExcludeFromCodeCoverage]
461430
TTask? Task { get; set; }
462431
}
463432

464433
/// <summary>
465434
/// A wrapping class that can hold a <see cref="Task"/> value.
466435
/// </summary>
467-
[DebuggerNonUserCode]
468-
[ExcludeFromCodeCoverage]
469436
protected sealed class TaskNotifier : ITaskNotifier<Task>
470437
{
471438
/// <summary>
@@ -498,8 +465,6 @@ internal TaskNotifier()
498465
/// A wrapping class that can hold a <see cref="Task{T}"/> value.
499466
/// </summary>
500467
/// <typeparam name="T">The type of value for the wrapped <see cref="Task{T}"/> instance.</typeparam>
501-
[DebuggerNonUserCode]
502-
[ExcludeFromCodeCoverage]
503468
protected sealed class TaskNotifier<T> : ITaskNotifier<Task<T>>
504469
{
505470
/// <summary>

0 commit comments

Comments
 (0)