Skip to content

Commit 0a2e8ed

Browse files
authored
Merge pull request #606 from CommunityToolkit/dev/fix-add-source
Fix source generators processing nested/generic types
2 parents 63ba418 + 0ea77c3 commit 0a2e8ed

File tree

10 files changed

+134
-18
lines changed

10 files changed

+134
-18
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IncrementalGeneratorInitializationContextExtensions.cs" />
5656
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IncrementalValuesProviderExtensions.cs" />
5757
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ISymbolExtensions.cs" />
58+
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SourceProductionContextExtensions.cs" />
5859
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ITypeSymbolExtensions.cs" />
5960
<Compile Include="$(MSBuildThisFileDirectory)Extensions\MemberDeclarationSyntaxExtensions.cs" />
6061
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SyntaxNodeExtensions.cs" />

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using System.Collections.Immutable;
66
using System.Linq;
7-
using System.Text;
87
using CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
98
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
109
using CommunityToolkit.Mvvm.SourceGenerators.Helpers;
@@ -75,7 +74,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
7574
// Insert all members into the same partial type declaration
7675
CompilationUnitSyntax compilationUnit = item.Hierarchy.GetCompilationUnit(memberDeclarations);
7776

78-
context.AddSource($"{item.Hierarchy.FilenameHint}.g.cs", compilationUnit.GetText(Encoding.UTF8));
77+
context.AddSource($"{item.Hierarchy.FilenameHint}.g.cs", compilationUnit);
7978
});
8079

8180
// Gather all property changing names
@@ -92,7 +91,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
9291

9392
if (compilationUnit is not null)
9493
{
95-
context.AddSource("__KnownINotifyPropertyChangingArgs.g.cs", compilationUnit.GetText(Encoding.UTF8));
94+
context.AddSource("__KnownINotifyPropertyChangingArgs.g.cs", compilationUnit);
9695
}
9796
});
9897

@@ -110,7 +109,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
110109

111110
if (compilationUnit is not null)
112111
{
113-
context.AddSource("__KnownINotifyPropertyChangedArgs.g.cs", compilationUnit.GetText(Encoding.UTF8));
112+
context.AddSource("__KnownINotifyPropertyChangedArgs.g.cs", compilationUnit);
114113
}
115114
});
116115
}

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

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

55
using System.Linq;
6-
using System.Text;
76
using CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
87
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
98
using Microsoft.CodeAnalysis;
@@ -79,15 +78,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
7978
{
8079
CompilationUnitSyntax compilationUnit = Execute.GetSyntax(item);
8180

82-
context.AddSource("__ObservableValidatorExtensions.g.cs", compilationUnit.GetText(Encoding.UTF8));
81+
context.AddSource("__ObservableValidatorExtensions.g.cs", compilationUnit);
8382
});
8483

8584
// Generate the class with all validation methods
8685
context.RegisterImplementationSourceOutput(validationInfo, static (context, item) =>
8786
{
8887
CompilationUnitSyntax compilationUnit = Execute.GetSyntax(item);
8988

90-
context.AddSource($"{item.FilenameHint}.g.cs", compilationUnit.GetText(Encoding.UTF8));
89+
context.AddSource($"{item.FilenameHint}.g.cs", compilationUnit);
9190
});
9291
}
9392
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System;
66
using System.Collections.Immutable;
77
using System.Linq;
8-
using System.Text;
98
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
109
using CommunityToolkit.Mvvm.SourceGenerators.Models;
1110
using Microsoft.CodeAnalysis;
@@ -34,12 +33,12 @@ public abstract partial class TransitiveMembersGenerator<TInfo> : IIncrementalGe
3433
/// <summary>
3534
/// The sequence of member declarations for sealed types.
3635
/// </summary>
37-
private ImmutableArray<MemberDeclarationSyntax> sealedMemberDeclarations;
36+
private readonly ImmutableArray<MemberDeclarationSyntax> sealedMemberDeclarations;
3837

3938
/// <summary>
4039
/// The resulting sequence of member declarations for non sealed types.
4140
/// </summary>
42-
private ImmutableArray<MemberDeclarationSyntax> nonSealedMemberDeclarations;
41+
private readonly ImmutableArray<MemberDeclarationSyntax> nonSealedMemberDeclarations;
4342

4443
/// <summary>
4544
/// Initializes a new instance of the <see cref="TransitiveMembersGenerator{TInfo}"/> class.
@@ -108,7 +107,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
108107
ImmutableArray<MemberDeclarationSyntax> updatedMemberDeclarations = Execute.AdjustMemberDeclarationNullabilityAnnotations(filteredMemberDeclarations, item.MetadataInfo.IsNullabilitySupported);
109108
CompilationUnitSyntax compilationUnit = item.Hierarchy.GetCompilationUnit(updatedMemberDeclarations, this.classDeclaration.BaseList);
110109

111-
context.AddSource($"{item.Hierarchy.FilenameHint}.g.cs", compilationUnit.GetText(Encoding.UTF8));
110+
context.AddSource($"{item.Hierarchy.FilenameHint}.g.cs", compilationUnit);
112111
});
113112
}
114113

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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.Text;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.CSharp.Syntax;
8+
9+
namespace CommunityToolkit.Mvvm.SourceGenerators.Extensions;
10+
11+
/// <summary>
12+
/// Extension methods for the <see cref="SourceProductionContext"/> type.
13+
/// </summary>
14+
internal static class SourceProductionContextExtensions
15+
{
16+
/// <summary>
17+
/// Adds a new source file to a target <see cref="SourceProductionContext"/> instance.
18+
/// </summary>
19+
/// <param name="context">The input <see cref="SourceProductionContext"/> instance to use.</param>
20+
/// <param name="name">The name of the source file to add.</param>
21+
/// <param name="compilationUnit">The <see cref="CompilationUnitSyntax"/> instance representing the syntax tree to add.</param>
22+
public static void AddSource(this SourceProductionContext context, string name, CompilationUnitSyntax compilationUnit)
23+
{
24+
#if !ROSLYN_4_3_1_OR_GREATER
25+
// We're fine with the extra allocation in the few cases where adjusting the filename is necessary.
26+
// This will only ever be done when code generation is executed again anyway, which is a slow path.
27+
name = name.Replace('+', '.').Replace('`', '_');
28+
#endif
29+
30+
// Add the UTF8 text for the input compilation unit
31+
context.AddSource(name, compilationUnit.GetText(Encoding.UTF8));
32+
}
33+
}

src/CommunityToolkit.Mvvm.SourceGenerators/Input/RelayCommandGenerator.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using System.Collections.Immutable;
66
using System.Linq;
7-
using System.Text;
87
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
98
using CommunityToolkit.Mvvm.SourceGenerators.Input.Models;
109
using CommunityToolkit.Mvvm.SourceGenerators.Models;
@@ -61,7 +60,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
6160
ImmutableArray<MemberDeclarationSyntax> memberDeclarations = Execute.GetSyntax(item.Info.Value);
6261
CompilationUnitSyntax compilationUnit = item.Hierarchy.GetCompilationUnit(memberDeclarations);
6362

64-
context.AddSource($"{item.Hierarchy.FilenameHint}.{item.Info.Value.MethodName}.g.cs", compilationUnit.GetText(Encoding.UTF8));
63+
context.AddSource($"{item.Hierarchy.FilenameHint}.{item.Info.Value.MethodName}.g.cs", compilationUnit);
6564
});
6665
}
6766
}

src/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using System.Collections.Immutable;
66
using System.Linq;
7-
using System.Text;
87
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
98
using CommunityToolkit.Mvvm.SourceGenerators.Messaging.Models;
109
using Microsoft.CodeAnalysis;
@@ -84,15 +83,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
8483
{
8584
CompilationUnitSyntax compilationUnit = Execute.GetSyntax(item);
8685

87-
context.AddSource("__IMessengerExtensions.g.cs", compilationUnit.GetText(Encoding.UTF8));
86+
context.AddSource("__IMessengerExtensions.g.cs", compilationUnit);
8887
});
8988

9089
// Generate the class with all registration methods
9190
context.RegisterImplementationSourceOutput(recipientInfo, static (context, item) =>
9291
{
9392
CompilationUnitSyntax compilationUnit = Execute.GetSyntax(item);
9493

95-
context.AddSource($"{item.FilenameHint}.g.cs", compilationUnit.GetText(Encoding.UTF8));
94+
context.AddSource($"{item.FilenameHint}.g.cs", compilationUnit);
9695
});
9796
}
9897
}

tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.MSTest" Version="1.1.1" />
99
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.1" />
1010
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.MSTest" Version="1.1.1" />
11-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.4.0" />
11+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" />
1212
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
1313
<PackageReference Include="MSTest.TestAdapter" Version="3.0.1" />
1414
<PackageReference Include="MSTest.TestFramework" Version="3.0.1" />

tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn431.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn431.UnitTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks>
5+
<DefineConstants>$(DefineConstants);ROSLYN_4_3_1_OR_GREATER</DefineConstants>
56
</PropertyGroup>
67

78
<ItemGroup>

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

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,92 @@ public object? A
193193
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
194194
}
195195

196+
[TestMethod]
197+
public void ObservablePropertyWithinGenericAndNestedTypes()
198+
{
199+
string source = """
200+
using System.ComponentModel;
201+
using CommunityToolkit.Mvvm.ComponentModel;
202+
203+
#nullable enable
204+
205+
namespace MyApp;
206+
207+
partial class Foo
208+
{
209+
partial class MyViewModel<T> : ObservableObject
210+
{
211+
[ObservableProperty]
212+
private string? a;
213+
}
214+
}
215+
""";
216+
217+
string result = """
218+
// <auto-generated/>
219+
#pragma warning disable
220+
#nullable enable
221+
namespace MyApp
222+
{
223+
partial class Foo
224+
{
225+
partial class MyViewModel<T>
226+
{
227+
/// <inheritdoc cref="a"/>
228+
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")]
229+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
230+
public string? A
231+
{
232+
get => a;
233+
set
234+
{
235+
if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(a, value))
236+
{
237+
OnAChanging(value);
238+
OnAChanging(default, value);
239+
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.A);
240+
a = value;
241+
OnAChanged(value);
242+
OnAChanged(default, value);
243+
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.A);
244+
}
245+
}
246+
}
247+
248+
/// <summary>Executes the logic for when <see cref="A"/> is changing.</summary>
249+
/// <param name="value">The new property value being set.</param>
250+
/// <remarks>This method is invoked right before the value of <see cref="A"/> is changed.</remarks>
251+
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")]
252+
partial void OnAChanging(string? value);
253+
/// <summary>Executes the logic for when <see cref="A"/> is changing.</summary>
254+
/// <param name="oldValue">The previous property value that is being replaced.</param>
255+
/// <param name="newValue">The new property value being set.</param>
256+
/// <remarks>This method is invoked right before the value of <see cref="A"/> is changed.</remarks>
257+
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")]
258+
partial void OnAChanging(string? oldValue, string? newValue);
259+
/// <summary>Executes the logic for when <see cref="A"/> just changed.</summary>
260+
/// <param name="value">The new property value that was set.</param>
261+
/// <remarks>This method is invoked right after the value of <see cref="A"/> is changed.</remarks>
262+
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")]
263+
partial void OnAChanged(string? value);
264+
/// <summary>Executes the logic for when <see cref="A"/> just changed.</summary>
265+
/// <param name="oldValue">The previous property value that was replaced.</param>
266+
/// <param name="newValue">The new property value that was set.</param>
267+
/// <remarks>This method is invoked right after the value of <see cref="A"/> is changed.</remarks>
268+
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")]
269+
partial void OnAChanged(string? oldValue, string? newValue);
270+
}
271+
}
272+
}
273+
""";
274+
275+
#if ROSLYN_4_3_1_OR_GREATER
276+
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.Foo+MyViewModel`1.g.cs", result));
277+
#else
278+
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.Foo.MyViewModel_1.g.cs", result));
279+
#endif
280+
}
281+
196282
/// <summary>
197283
/// Generates the requested sources
198284
/// </summary>
@@ -212,7 +298,7 @@ from assembly in AppDomain.CurrentDomain.GetAssemblies()
212298
let reference = MetadataReference.CreateFromFile(assembly.Location)
213299
select reference;
214300

215-
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp11));
301+
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10));
216302

217303
// Create a syntax tree with the input source
218304
CSharpCompilation compilation = CSharpCompilation.Create(

0 commit comments

Comments
 (0)