Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add support for 'get;' accessors too
  • Loading branch information
Sergio0694 committed Dec 11, 2024
commit df714eaa6c194b5f2b8a46713541939d6321b0ce
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Linq;
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
Expand Down Expand Up @@ -201,6 +202,35 @@ public override void Initialize(AnalysisContext context)
}
});

// We also need to track getters which have no body, and we need syntax for that
context.RegisterSyntaxNodeAction(context =>
{
// Let's just make sure we do have a property symbol
if (context.ContainingSymbol is not IPropertySymbol { GetMethod: not null } propertySymbol)
{
return;
}

// Lookup the property to get its flags
if (!propertyMap.TryGetValue(propertySymbol, out bool[]? validFlags))
{
return;
}

// We expect two accessors, skip if otherwise (the setter will be validated by the other callback)
if (context.Node is not PropertyDeclarationSyntax { AccessorList.Accessors: [{ } firstAccessor, { } secondAccessor] })
{
return;
}

// Check that either of them is a semicolon token 'get;' accessor (it can be in either position)
if (firstAccessor.IsKind(SyntaxKind.GetAccessorDeclaration) && firstAccessor.SemicolonToken.IsKind(SyntaxKind.SemicolonToken) ||
secondAccessor.IsKind(SyntaxKind.GetAccessorDeclaration) && secondAccessor.SemicolonToken.IsKind(SyntaxKind.SemicolonToken))
{
validFlags[0] = true;
}
}, SyntaxKind.PropertyDeclaration);

// Finally, we can consume this information when we finish processing the symbol
context.RegisterSymbolEndAction(context =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,59 @@ public partial class SampleViewModel : ObservableObject
await test.RunAsync();
}

[TestMethod]
public async Task SimpleProperty_WithSemicolonTokenGetAccessor()
{
string original = """
using CommunityToolkit.Mvvm.ComponentModel;

namespace MyApp;

public class SampleViewModel : ObservableObject
{
public string Name
{
get;
set => SetProperty(ref field, value);
}
}
""";

string @fixed = """
using CommunityToolkit.Mvvm.ComponentModel;

namespace MyApp;

public partial class SampleViewModel : ObservableObject
{
[ObservableProperty]
public partial string Name { get; set; }
}
""";

CSharpCodeFixTest test = new(LanguageVersion.Preview)
{
TestCode = original,
FixedCode = @fixed,
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
};

test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly);
test.ExpectedDiagnostics.AddRange(new[]
{
// /0/Test0.cs(7,19): info MVVMTK0056: The semi-auto property MyApp.SampleViewModel.Name can be converted to a partial property using [ObservableProperty], which is recommended (doing so makes the code less verbose and results in more optimized code)
CSharpCodeFixVerifier.Diagnostic().WithSpan(7, 19, 7, 23).WithArguments("MyApp.SampleViewModel", "Name"),
});

test.FixedState.ExpectedDiagnostics.AddRange(new[]
{
// /0/Test0.cs(8,27): error CS9248: Partial property 'SampleViewModel.Name' must have an implementation part.
DiagnosticResult.CompilerError("CS9248").WithSpan(8, 27, 8, 31).WithArguments("MyApp.SampleViewModel.Name"),
});

await test.RunAsync();
}

[TestMethod]
public async Task SimpleProperty_WithLeadingTrivia()
{
Expand Down