mirror of
https://github.com/CommunityToolkit/dotnet.git
synced 2026-10-26 01:53:02 +08:00
Added handling for modifiers in sealed classes
This commit is contained in:
+11
-1
@@ -108,6 +108,16 @@ namespace Microsoft.Toolkit.Mvvm.SourceGenerators
|
||||
ClassDeclarationSyntax sourceDeclaration = sourceSyntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().First();
|
||||
UsingDirectiveSyntax[] usingDirectives = sourceSyntaxTree.GetRoot().DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> generatedMembers = FilterDeclaredMembers(context, attributeData, classDeclaration, classDeclarationSymbol, sourceDeclaration);
|
||||
|
||||
// If the target class is sealed, make protected members private and remove the virtual modifier
|
||||
if (classDeclarationSymbol.IsSealed)
|
||||
{
|
||||
generatedMembers = generatedMembers.Select(static member => member
|
||||
.ReplaceModifier(SyntaxKind.ProtectedKeyword, SyntaxKind.PrivateKeyword)
|
||||
.RemoveModifier(SyntaxKind.VirtualKeyword));
|
||||
}
|
||||
|
||||
// Create the class declaration for the user type. This will produce a tree as follows:
|
||||
//
|
||||
// <MODIFIERS> <CLASS_NAME> : <BASE_TYPES>
|
||||
@@ -118,7 +128,7 @@ namespace Microsoft.Toolkit.Mvvm.SourceGenerators
|
||||
ClassDeclaration(classDeclaration.Identifier.Text)
|
||||
.WithModifiers(classDeclaration.Modifiers)
|
||||
.WithBaseList(sourceDeclaration.BaseList)
|
||||
.AddMembers(FilterDeclaredMembers(context, attributeData, classDeclaration, classDeclarationSymbol, sourceDeclaration).ToArray());
|
||||
.AddMembers(generatedMembers.ToArray());
|
||||
|
||||
TypeDeclarationSyntax typeDeclarationSyntax = classDeclarationSyntax;
|
||||
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
namespace Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for the <see cref="MemberDeclarationSyntax"/> type.
|
||||
/// </summary>
|
||||
internal static class MemberDeclarationSyntaxExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Replaces a specific modifier.
|
||||
/// </summary>
|
||||
/// <param name="memberDeclaration">The input <see cref="MemberDeclarationSyntax"/> instance.</param>
|
||||
/// <param name="oldKind">The target modifier kind to replace.</param>
|
||||
/// <param name="newKind">The new modifier kind to add or replace.</param>
|
||||
/// <returns>A <see cref="MemberDeclarationSyntax"/> instance with the target modifier.</returns>
|
||||
[Pure]
|
||||
public static MemberDeclarationSyntax ReplaceModifier(this MemberDeclarationSyntax memberDeclaration, SyntaxKind oldKind, SyntaxKind newKind)
|
||||
{
|
||||
int index = memberDeclaration.Modifiers.IndexOf(oldKind);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
return memberDeclaration.WithModifiers(memberDeclaration.Modifiers.Replace(memberDeclaration.Modifiers[index], Token(newKind)));
|
||||
}
|
||||
|
||||
return memberDeclaration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a specific modifier.
|
||||
/// </summary>
|
||||
/// <param name="memberDeclaration">The input <see cref="MemberDeclarationSyntax"/> instance.</param>
|
||||
/// <param name="kind">The modifier kind to remove.</param>
|
||||
/// <returns>A <see cref="MemberDeclarationSyntax"/> instance without the specified modifier.</returns>
|
||||
[Pure]
|
||||
public static MemberDeclarationSyntax RemoveModifier(this MemberDeclarationSyntax memberDeclaration, SyntaxKind kind)
|
||||
{
|
||||
int index = memberDeclaration.Modifiers.IndexOf(kind);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
return memberDeclaration.WithModifiers(memberDeclaration.Modifiers.RemoveAt(index));
|
||||
}
|
||||
|
||||
return memberDeclaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,45 @@ namespace UnitTests.Mvvm
|
||||
Assert.AreEqual(changed.Item2, 42);
|
||||
}
|
||||
|
||||
[TestCategory("Mvvm")]
|
||||
[TestMethod]
|
||||
public void Test_ObservableObjectAttribute_OnSealedClass_Events()
|
||||
{
|
||||
var model = new SampleModelSealed();
|
||||
|
||||
(PropertyChangingEventArgs, int) changing = default;
|
||||
(PropertyChangedEventArgs, int) changed = default;
|
||||
|
||||
model.PropertyChanging += (s, e) =>
|
||||
{
|
||||
Assert.IsNull(changing.Item1);
|
||||
Assert.IsNull(changed.Item1);
|
||||
Assert.AreSame(model, s);
|
||||
Assert.IsNotNull(s);
|
||||
Assert.IsNotNull(e);
|
||||
|
||||
changing = (e, model.Data);
|
||||
};
|
||||
|
||||
model.PropertyChanged += (s, e) =>
|
||||
{
|
||||
Assert.IsNotNull(changing.Item1);
|
||||
Assert.IsNull(changed.Item1);
|
||||
Assert.AreSame(model, s);
|
||||
Assert.IsNotNull(s);
|
||||
Assert.IsNotNull(e);
|
||||
|
||||
changed = (e, model.Data);
|
||||
};
|
||||
|
||||
model.Data = 42;
|
||||
|
||||
Assert.AreEqual(changing.Item1?.PropertyName, nameof(SampleModelSealed.Data));
|
||||
Assert.AreEqual(changing.Item2, 0);
|
||||
Assert.AreEqual(changed.Item1?.PropertyName, nameof(SampleModelSealed.Data));
|
||||
Assert.AreEqual(changed.Item2, 42);
|
||||
}
|
||||
|
||||
[ObservableObject]
|
||||
public partial class SampleModel
|
||||
{
|
||||
@@ -61,5 +100,17 @@ namespace UnitTests.Mvvm
|
||||
set => SetProperty(ref data, value);
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableObject]
|
||||
public sealed partial class SampleModelSealed
|
||||
{
|
||||
private int data;
|
||||
|
||||
public int Data
|
||||
{
|
||||
get => data;
|
||||
set => SetProperty(ref data, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user