Skip to content

Commit 5309963

Browse files
authored
Merge pull request #63 from CommunityToolkit/dev/incremental-generators
Switch source generators to incremental generators
2 parents 359615a + 53614e7 commit 5309963

File tree

56 files changed

+4238
-2319
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+4238
-2319
lines changed

CommunityToolkit.Mvvm.SourceGenerators/AnalyzerReleases.Unshipped.md

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,13 @@ Rule ID | Category | Severity | Notes
77
--------|----------|----------|-------
88
MVVMTK0001 | CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator | Error | See https://aka.ms/mvvmtoolkit/error
99
MVVMTK0002 | CommunityToolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Error | See https://aka.ms/mvvmtoolkit/error
10-
MVVMTK0003 | CommunityToolkit.Mvvm.SourceGenerators.ObservableRecipientGenerator | Error | See https://aka.ms/mvvmtoolkit/error
11-
MVVMTK0004 | CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator | Error | See https://aka.ms/mvvmtoolkit/error
12-
MVVMTK0005 | CommunityToolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Error | See https://aka.ms/mvvmtoolkit/error
13-
MVVMTK0006 | CommunityToolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Error | See https://aka.ms/mvvmtoolkit/error
14-
MVVMTK0007 | CommunityToolkit.Mvvm.SourceGenerators.ObservableRecipientGenerator | Error | See https://aka.ms/mvvmtoolkit/error
15-
MVVMTK0008 | CommunityToolkit.Mvvm.SourceGenerators.ObservableRecipientGenerator | Error | See https://aka.ms/mvvmtoolkit/error
16-
MVVMTK0009 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
17-
MVVMTK0010 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
18-
MVVMTK0011 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
10+
MVVMTK0003 | CommunityToolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Error | See https://aka.ms/mvvmtoolkit/error
11+
MVVMTK0004 | CommunityToolkit.Mvvm.SourceGenerators.ObservableRecipientGenerator | Error | See https://aka.ms/mvvmtoolkit/error
12+
MVVMTK0005 | CommunityToolkit.Mvvm.SourceGenerators.ObservableRecipientGenerator | Error | See https://aka.ms/mvvmtoolkit/error
13+
MVVMTK0006 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
14+
MVVMTK0007 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
15+
MVVMTK0008 | Microsoft.CodeAnalysis.CSharp.CSharpParseOptions | Error | See https://aka.ms/mvvmtoolkit/error
16+
MVVMTK0009 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
17+
MVVMTK0010 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
18+
MVVMTK0011 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
1919
MVVMTK0012 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
20-
MVVMTK0013 | Microsoft.CodeAnalysis.CSharp.CSharpParseOptions | Error | See https://aka.ms/mvvmtoolkit/error
21-
MVVMTK0014 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
22-
MVVMTK0015 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
23-
MVVMTK0016 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error
24-
MVVMTK0017 | CommunityToolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error

CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
</ItemGroup>
3333

3434
<ItemGroup>
35-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.11.0" PrivateAssets="all" Pack="false" />
35+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" Pack="false" />
3636
</ItemGroup>
3737

3838
<ItemGroup>

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,74 +3,71 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections.Generic;
6-
using System.Diagnostics.CodeAnalysis;
6+
using System.Collections.Immutable;
77
using System.Linq;
8+
using CommunityToolkit.Mvvm.SourceGenerators.Diagnostics;
9+
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
10+
using CommunityToolkit.Mvvm.SourceGenerators.Input.Models;
811
using Microsoft.CodeAnalysis;
12+
using Microsoft.CodeAnalysis.CSharp;
913
using Microsoft.CodeAnalysis.CSharp.Syntax;
10-
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
1114
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;
1215

1316
namespace CommunityToolkit.Mvvm.SourceGenerators;
1417

1518
/// <summary>
1619
/// A source generator for the <c>INotifyPropertyChangedAttribute</c> type.
1720
/// </summary>
18-
[Generator]
19-
public sealed class INotifyPropertyChangedGenerator : TransitiveMembersGenerator
21+
[Generator(LanguageNames.CSharp)]
22+
public sealed class INotifyPropertyChangedGenerator : TransitiveMembersGenerator<INotifyPropertyChangedInfo>
2023
{
2124
/// <summary>
2225
/// Initializes a new instance of the <see cref="INotifyPropertyChangedGenerator"/> class.
2326
/// </summary>
2427
public INotifyPropertyChangedGenerator()
25-
: base("CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute")
28+
: base("global::CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute")
2629
{
2730
}
2831

2932
/// <inheritdoc/>
30-
protected override DiagnosticDescriptor TargetTypeErrorDescriptor => INotifyPropertyChangedGeneratorError;
33+
protected override INotifyPropertyChangedInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData)
34+
{
35+
bool includeAdditionalHelperMethods = attributeData.GetNamedArgument<bool>("IncludeAdditionalHelperMethods", true);
36+
37+
return new(includeAdditionalHelperMethods);
38+
}
3139

3240
/// <inheritdoc/>
33-
protected override bool ValidateTargetType(
34-
GeneratorExecutionContext context,
35-
AttributeData attributeData,
36-
ClassDeclarationSyntax classDeclaration,
37-
INamedTypeSymbol classDeclarationSymbol,
38-
[NotNullWhen(false)] out DiagnosticDescriptor? descriptor)
41+
protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, INotifyPropertyChangedInfo info, out ImmutableArray<Diagnostic> diagnostics)
3942
{
40-
INamedTypeSymbol iNotifyPropertyChangedSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged")!;
43+
ImmutableArray<Diagnostic>.Builder builder = ImmutableArray.CreateBuilder<Diagnostic>();
4144

4245
// Check if the type already implements INotifyPropertyChanged
43-
if (classDeclarationSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, iNotifyPropertyChangedSymbol)))
46+
if (typeSymbol.AllInterfaces.Any(i => i.HasFullyQualifiedName("global::System.ComponentModel.INotifyPropertyChanged")))
4447
{
45-
descriptor = DuplicateINotifyPropertyChangedInterfaceForINotifyPropertyChangedAttributeError;
48+
builder.Add(DuplicateINotifyPropertyChangedInterfaceForINotifyPropertyChangedAttributeError, typeSymbol, typeSymbol);
49+
50+
diagnostics = builder.ToImmutable();
4651

4752
return false;
4853
}
4954

50-
descriptor = null;
55+
diagnostics = builder.ToImmutable();
5156

5257
return true;
5358
}
5459

5560
/// <inheritdoc/>
56-
protected override IEnumerable<MemberDeclarationSyntax> FilterDeclaredMembers(
57-
GeneratorExecutionContext context,
58-
AttributeData attributeData,
59-
ClassDeclarationSyntax classDeclaration,
60-
INamedTypeSymbol classDeclarationSymbol,
61-
ClassDeclarationSyntax sourceDeclaration)
61+
protected override ImmutableArray<MemberDeclarationSyntax> FilterDeclaredMembers(INotifyPropertyChangedInfo info, ImmutableArray<MemberDeclarationSyntax> memberDeclarations)
6262
{
6363
// If requested, only include the event and the basic methods to raise it, but not the additional helpers
64-
if (attributeData.HasNamedArgument("IncludeAdditionalHelperMethods", false))
64+
if (!info.IncludeAdditionalHelperMethods)
6565
{
66-
return sourceDeclaration.Members.Where(static member =>
67-
{
68-
return member
69-
is EventFieldDeclarationSyntax
70-
or MethodDeclarationSyntax { Identifier: { ValueText: "OnPropertyChanged" } };
71-
});
66+
return memberDeclarations.Where(static member => member
67+
is EventFieldDeclarationSyntax
68+
or MethodDeclarationSyntax { Identifier.ValueText: "OnPropertyChanged" }).ToImmutableArray();
7269
}
7370

74-
return sourceDeclaration.Members;
71+
return memberDeclarations;
7572
}
7673
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Linq;
9+
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
10+
using CommunityToolkit.Mvvm.SourceGenerators.Helpers;
11+
using Microsoft.CodeAnalysis;
12+
using Microsoft.CodeAnalysis.CSharp.Syntax;
13+
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
14+
15+
namespace CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
16+
17+
/// <summary>
18+
/// A model representing an attribute declaration.
19+
/// </summary>
20+
internal sealed record AttributeInfo(
21+
string TypeName,
22+
ImmutableArray<TypedConstantInfo> ConstructorArgumentInfo,
23+
ImmutableArray<(string Name, TypedConstantInfo Value)> NamedArgumentInfo)
24+
{
25+
/// <summary>
26+
/// Creates a new <see cref="AttributeInfo"/> instance from a given <see cref="AttributeData"/> value.
27+
/// </summary>
28+
/// <param name="attributeData">The input <see cref="AttributeData"/> value.</param>
29+
/// <returns>A <see cref="AttributeInfo"/> instance representing <paramref name="attributeData"/>.</returns>
30+
public static AttributeInfo From(AttributeData attributeData)
31+
{
32+
string typeName = attributeData.AttributeClass!.GetFullyQualifiedName();
33+
34+
// Get the constructor arguments
35+
ImmutableArray<TypedConstantInfo> constructorArguments =
36+
attributeData.ConstructorArguments
37+
.Select(TypedConstantInfo.From)
38+
.ToImmutableArray();
39+
40+
// Get the named arguments
41+
ImmutableArray<(string, TypedConstantInfo)>.Builder namedArguments = ImmutableArray.CreateBuilder<(string, TypedConstantInfo)>();
42+
43+
foreach (KeyValuePair<string, TypedConstant> arg in attributeData.NamedArguments)
44+
{
45+
namedArguments.Add((arg.Key, TypedConstantInfo.From(arg.Value)));
46+
}
47+
48+
return new(
49+
typeName,
50+
constructorArguments,
51+
namedArguments.ToImmutable());
52+
}
53+
54+
/// <summary>
55+
/// Gets an <see cref="AttributeSyntax"/> instance representing the current value.
56+
/// </summary>
57+
/// <returns>The <see cref="ExpressionSyntax"/> instance representing the current value.</returns>
58+
public AttributeSyntax GetSyntax()
59+
{
60+
// Gather the constructor arguments
61+
IEnumerable<AttributeArgumentSyntax> arguments =
62+
ConstructorArgumentInfo
63+
.Select(static arg => AttributeArgument(arg.GetSyntax()));
64+
65+
// Gather the named arguments
66+
IEnumerable<AttributeArgumentSyntax> namedArguments =
67+
NamedArgumentInfo.Select(static arg =>
68+
AttributeArgument(arg.Value.GetSyntax())
69+
.WithNameEquals(NameEquals(IdentifierName(arg.Name))));
70+
71+
return Attribute(IdentifierName(TypeName), AttributeArgumentList(SeparatedList(arguments.Concat(namedArguments))));
72+
73+
}
74+
75+
/// <summary>
76+
/// An <see cref="IEqualityComparer{T}"/> implementation for <see cref="AttributeInfo"/>.
77+
/// </summary>
78+
public sealed class Comparer : Comparer<AttributeInfo, Comparer>
79+
{
80+
/// <inheritdoc/>
81+
protected override void AddToHashCode(ref HashCode hashCode, AttributeInfo obj)
82+
{
83+
hashCode.Add(obj.TypeName);
84+
hashCode.AddRange(obj.ConstructorArgumentInfo, TypedConstantInfo.Comparer.Default);
85+
86+
foreach ((string key, TypedConstantInfo value) in obj.NamedArgumentInfo)
87+
{
88+
hashCode.Add(key);
89+
hashCode.Add(value, TypedConstantInfo.Comparer.Default);
90+
}
91+
}
92+
93+
/// <inheritdoc/>
94+
protected override bool AreEqual(AttributeInfo x, AttributeInfo y)
95+
{
96+
if (x.TypeName != y.TypeName ||
97+
!x.ConstructorArgumentInfo.SequenceEqual(y.ConstructorArgumentInfo, TypedConstantInfo.Comparer.Default) ||
98+
x.NamedArgumentInfo.Length != y.NamedArgumentInfo.Length)
99+
{
100+
return false;
101+
}
102+
103+
for (int i = 0; i < x.NamedArgumentInfo.Length; i++)
104+
{
105+
(string Name, TypedConstantInfo Value) left = x.NamedArgumentInfo[i];
106+
(string Name, TypedConstantInfo Value) right = y.NamedArgumentInfo[i];
107+
108+
if (left.Name != right.Name ||
109+
!TypedConstantInfo.Comparer.Default.Equals(left.Value, right.Value))
110+
{
111+
return false;
112+
}
113+
}
114+
115+
return true;
116+
}
117+
}
118+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace CommunityToolkit.Mvvm.SourceGenerators.Input.Models;
6+
7+
/// <summary>
8+
/// A model with gathered info on a given <c>INotifyPropertyChangedAttribute</c> instance.
9+
/// </summary>
10+
/// <param name="IncludeAdditionalHelperMethods">Whether to also generate helper methods in the target type.</param>
11+
public sealed record INotifyPropertyChangedInfo(bool IncludeAdditionalHelperMethods);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
6+
7+
/// <summary>
8+
/// A model with gathered info on a given <c>ObservableRecipientAttribute</c> instance.
9+
/// </summary>
10+
/// <param name="TypeName">The type name of the type being annotated.</param>
11+
/// <param name="HasExplicitConstructors">Whether or not the target type has explicit constructors.</param>
12+
/// <param name="IsAbstract">Whether or not the target type is abstract.</param>
13+
/// <param name="IsObservableValidator">Whether or not the target type inherits from <c>ObservableValidator</c>.</param>
14+
public sealed record ObservableRecipientInfo(
15+
string TypeName,
16+
bool HasExplicitConstructors,
17+
bool IsAbstract,
18+
bool IsObservableValidator);
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Linq;
9+
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
10+
using CommunityToolkit.Mvvm.SourceGenerators.Helpers;
11+
12+
namespace CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
13+
14+
/// <summary>
15+
/// A model representing an generated property
16+
/// </summary>
17+
/// <param name="TypeName">The type name for the generated property.</param>
18+
/// <param name="IsNullableReferenceType">Whether or not the property is of a nullable reference type.</param>
19+
/// <param name="FieldName">The field name.</param>
20+
/// <param name="PropertyName">The generated property name.</param>
21+
/// <param name="PropertyChangingNames">The sequence of property changing properties to notify.</param>
22+
/// <param name="PropertyChangedNames">The sequence of property changed properties to notify.</param>
23+
/// <param name="NotifiedCommandNames">The sequence of commands to notify.</param>
24+
/// <param name="ValidationAttributes">The sequence of validation attributes for the generated property.</param>
25+
internal sealed record PropertyInfo(
26+
string TypeName,
27+
bool IsNullableReferenceType,
28+
string FieldName,
29+
string PropertyName,
30+
ImmutableArray<string> PropertyChangingNames,
31+
ImmutableArray<string> PropertyChangedNames,
32+
ImmutableArray<string> NotifiedCommandNames,
33+
ImmutableArray<AttributeInfo> ValidationAttributes)
34+
{
35+
/// <summary>
36+
/// An <see cref="IEqualityComparer{T}"/> implementation for <see cref="PropertyInfo"/>.
37+
/// </summary>
38+
public sealed class Comparer : Comparer<PropertyInfo, Comparer>
39+
{
40+
/// <inheritdoc/>
41+
protected override void AddToHashCode(ref HashCode hashCode, PropertyInfo obj)
42+
{
43+
hashCode.Add(obj.TypeName);
44+
hashCode.Add(obj.IsNullableReferenceType);
45+
hashCode.Add(obj.FieldName);
46+
hashCode.Add(obj.PropertyName);
47+
hashCode.AddRange(obj.PropertyChangingNames);
48+
hashCode.AddRange(obj.PropertyChangedNames);
49+
hashCode.AddRange(obj.NotifiedCommandNames);
50+
hashCode.AddRange(obj.ValidationAttributes, AttributeInfo.Comparer.Default);
51+
}
52+
53+
/// <inheritdoc/>
54+
protected override bool AreEqual(PropertyInfo x, PropertyInfo y)
55+
{
56+
return
57+
x.TypeName == y.TypeName &&
58+
x.IsNullableReferenceType == y.IsNullableReferenceType &&
59+
x.FieldName == y.FieldName &&
60+
x.PropertyName == y.PropertyName &&
61+
x.PropertyChangingNames.SequenceEqual(y.PropertyChangingNames) &&
62+
x.PropertyChangedNames.SequenceEqual(y.PropertyChangedNames) &&
63+
x.NotifiedCommandNames.SequenceEqual(y.NotifiedCommandNames) &&
64+
x.ValidationAttributes.SequenceEqual(y.ValidationAttributes, AttributeInfo.Comparer.Default);
65+
}
66+
}
67+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using CommunityToolkit.Mvvm.SourceGenerators.Helpers;
8+
9+
namespace CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
10+
11+
/// <inheritdoc/>
12+
partial record TypedConstantInfo
13+
{
14+
/// <summary>
15+
/// An <see cref="IEqualityComparer{T}"/> implementation for <see cref="TypedConstantInfo"/>.
16+
/// </summary>
17+
public sealed class Comparer : Comparer<TypedConstantInfo, Comparer>
18+
{
19+
/// <inheritdoc/>
20+
protected override void AddToHashCode(ref HashCode hashCode, TypedConstantInfo obj)
21+
{
22+
obj.AddToHashCode(ref hashCode);
23+
}
24+
25+
/// <inheritdoc/>
26+
protected override bool AreEqual(TypedConstantInfo x, TypedConstantInfo y)
27+
{
28+
return x.IsEqualTo(y);
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)