Skip to content

Commit

Permalink
Fix [RelayCommand] attribute over methods with no attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio0694 committed Mar 9, 2023
1 parent 11c06e9 commit c1b5d73
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Extensions\INamedTypeSymbolExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IncrementalGeneratorInitializationContextExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IncrementalValuesProviderExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\MethodDeclarationSyntaxExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SymbolInfoExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ISymbolExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SourceProductionContextExtensions.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace CommunityToolkit.Mvvm.SourceGenerators.Extensions;

/// <summary>
/// Extension methods for the <see cref="MethodDeclarationSyntax"/> type.
/// </summary>
internal static class MethodDeclarationSyntaxExtensions
{
/// <summary>
/// Checks whether a given <see cref="MethodDeclarationSyntax"/> has or could potentially have any attribute lists.
/// </summary>
/// <param name="methodDeclaration">The input <see cref="MethodDeclarationSyntax"/> to check.</param>
/// <returns>Whether <paramref name="methodDeclaration"/> has or potentially has any attribute lists.</returns>
public static bool HasOrPotentiallyHasAttributeLists(this MethodDeclarationSyntax methodDeclaration)
{
// If the declaration has any attribute lists, there's nothing left to do
if (methodDeclaration.AttributeLists.Count > 0)
{
return true;
}

// If there are no attributes, check whether the method declaration has the partial keyword. If it
// does, there could potentially be attribute lists on the other partial definition/implementation.
foreach (SyntaxToken modifier in methodDeclaration.Modifiers)
{
if (modifier.IsKind(SyntaxKind.PartialKeyword))
{
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
context.SyntaxProvider
.ForAttributeWithMetadataName(
"CommunityToolkit.Mvvm.Input.RelayCommandAttribute",
static (node, _) => node is MethodDeclarationSyntax { Parent: ClassDeclarationSyntax, AttributeLists.Count: > 0 },
static (node, _) => node is MethodDeclarationSyntax { Parent: ClassDeclarationSyntax } methodDeclaration && methodDeclaration.HasOrPotentiallyHasAttributeLists(),
static (context, token) =>
{
if (!context.SemanticModel.Compilation.HasLanguageVersionAtLeastEqualTo(LanguageVersion.CSharp8))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,76 @@ partial class MyViewModel
VerifyGenerateSources(source, new[] { new RelayCommandGenerator() }, ("MyApp.MyViewModel.Test.g.cs", result));
}

// See https://github.com/CommunityToolkit/dotnet/issues/632
[TestMethod]
public void RelayCommandMethodWithPartialDeclarations_TriggersCorrectly()
{
string source = """
using CommunityToolkit.Mvvm.Input;

#nullable enable

namespace MyApp;

partial class MyViewModel
{
[RelayCommand]
private partial void Test1()
{
}

private partial void Test1();

private partial void Test2()
{
}

[RelayCommand]
private partial void Test2();
}
""";

string result1 = """
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace MyApp
{
partial class MyViewModel
{
/// <summary>The backing field for <see cref="Test1Command"/>.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.1.0.0")]
private global::CommunityToolkit.Mvvm.Input.RelayCommand? test1Command;
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="Test1"/>.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.1.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public global::CommunityToolkit.Mvvm.Input.IRelayCommand Test1Command => test1Command ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test1));
}
}
""";

string result2 = """
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace MyApp
{
partial class MyViewModel
{
/// <summary>The backing field for <see cref="Test2Command"/>.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.1.0.0")]
private global::CommunityToolkit.Mvvm.Input.RelayCommand? test2Command;
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="Test2"/>.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.1.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public global::CommunityToolkit.Mvvm.Input.IRelayCommand Test2Command => test2Command ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test2));
}
}
""";

VerifyGenerateSources(source, new[] { new RelayCommandGenerator() }, ("MyApp.MyViewModel.Test1.g.cs", result1), ("MyApp.MyViewModel.Test2.g.cs", result2));
}

// See https://github.com/CommunityToolkit/dotnet/issues/632
[TestMethod]
public void RelayCommandMethodWithForwardedAttributesOverPartialDeclarations_MergesAttributes()
Expand Down

0 comments on commit c1b5d73

Please sign in to comment.