Skip to content

Commit 6ee8d7c

Browse files
committed
Update code style to follow repo conventions
1 parent b908b3f commit 6ee8d7c

File tree

4 files changed

+62
-30
lines changed

4 files changed

+62
-30
lines changed

src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
using System.Collections.Immutable;
66
using System.Composition;
7-
using System.Diagnostics;
8-
using System.Linq;
97
using System.Threading;
108
using System.Threading.Tasks;
119
using CommunityToolkit.Mvvm.SourceGenerators;
@@ -15,57 +13,74 @@
1513
using Microsoft.CodeAnalysis.CodeFixes;
1614
using Microsoft.CodeAnalysis.CSharp;
1715
using Microsoft.CodeAnalysis.CSharp.Syntax;
16+
using Microsoft.CodeAnalysis.Text;
1817

1918
namespace CommunityToolkit.Mvvm.Fixers;
2019

21-
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
22-
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
23-
public class FieldReferenceForObservablePropertyFieldFixer : CodeFixProvider
20+
/// <summary>
21+
/// A code fixer that automatically updates references to fields with <c>[ObservableProperty]</c> to reference the generated property instead.
22+
/// </summary>
23+
[ExportCodeFixProvider(LanguageNames.CSharp)]
24+
[Shared]
25+
public sealed class FieldReferenceForObservablePropertyFieldFixer : CodeFixProvider
2426
{
27+
/// <inheritdoc/>
2528
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticDescriptors.FieldReferenceForObservablePropertyFieldId);
2629

30+
/// <inheritdoc/>
2731
public override FixAllProvider? GetFixAllProvider()
2832
{
2933
return WellKnownFixAllProviders.BatchFixer;
3034
}
3135

36+
/// <inheritdoc/>
3237
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
3338
{
3439
Diagnostic diagnostic = context.Diagnostics[0];
35-
Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;
40+
TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;
3641

37-
string? propertyName = diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.PropertyNameKey];
38-
string? fieldName = diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.FieldNameKey];
39-
40-
if (propertyName == null || fieldName == null)
42+
// Retrieve the properties passed by the analyzer
43+
if (diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.FieldNameKey] is not string fieldName ||
44+
diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.PropertyNameKey] is not string propertyName)
4145
{
4246
return;
4347
}
4448

4549
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
46-
Debug.Assert(root != null);
47-
48-
IdentifierNameSyntax fieldReference = root!.FindNode(diagnosticSpan).DescendantNodesAndSelf().OfType<IdentifierNameSyntax>().FirstOrDefault(i => i.ToString() == fieldName);
4950

50-
if (fieldReference == null)
51+
foreach (SyntaxNode syntaxNode in root!.FindNode(diagnosticSpan).DescendantNodesAndSelf())
5152
{
52-
return;
53-
}
54-
55-
context.RegisterCodeFix(
56-
CodeAction.Create(
57-
title: "Reference property",
58-
createChangedDocument: c => UpdateReference(context.Document, fieldReference, propertyName, c),
59-
equivalenceKey: "Reference property"),
60-
diagnostic);
53+
// Find the first descendant node from the source of the diagnostic that is an identifier with the target name
54+
if (syntaxNode is IdentifierNameSyntax { Identifier.Text: string identifierName } identifierNameSyntax &&
55+
identifierName == fieldName)
56+
{
57+
// Register the code fix to update the field reference to use the generated property instead
58+
context.RegisterCodeFix(
59+
CodeAction.Create(
60+
title: "Reference property",
61+
createChangedDocument: token => UpdateReference(context.Document, identifierNameSyntax, propertyName, token),
62+
equivalenceKey: "Reference property"),
63+
diagnostic);
6164

65+
return;
66+
}
67+
}
6268
}
6369

70+
/// <summary>
71+
/// Applies the code fix to a target identifier and returns an updated document.
72+
/// </summary>
73+
/// <param name="document">The original document being fixed.</param>
74+
/// <param name="fieldReference">The <see cref="IdentifierNameSyntax"/> corresponding to the field reference to update.</param>
75+
/// <param name="propertyName">The name of the generated property.</param>
76+
/// <param name="cancellationToken">The cancellation token for the operation.</param>
77+
/// <returns>An updated document with the applied code fix, and <paramref name="fieldReference"/> being replaced with a property reference.</returns>
6478
private static async Task<Document> UpdateReference(Document document, IdentifierNameSyntax fieldReference, string propertyName, CancellationToken cancellationToken)
6579
{
6680
IdentifierNameSyntax propertyReference = SyntaxFactory.IdentifierName(propertyName);
67-
SyntaxNode originalRoot = await fieldReference.SyntaxTree.GetRootAsync(cancellationToken);
81+
SyntaxNode originalRoot = await fieldReference.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
6882
SyntaxTree updatedTree = originalRoot.ReplaceNode(fieldReference, propertyReference).SyntaxTree;
69-
return document.WithSyntaxRoot(await updatedTree.GetRootAsync(cancellationToken));
83+
84+
return document.WithSyntaxRoot(await updatedTree.GetRootAsync(cancellationToken).ConfigureAwait(false));
7085
}
7186
}

src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/FieldReferenceForObservablePropertyFieldAnalyzer.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,16 @@ namespace CommunityToolkit.Mvvm.SourceGenerators;
1616
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1717
public sealed class FieldReferenceForObservablePropertyFieldAnalyzer : DiagnosticAnalyzer
1818
{
19-
internal const string PropertyNameKey = "PropertyName";
19+
/// <summary>
20+
/// The key for the name of the target field to update.
21+
/// </summary>
2022
internal const string FieldNameKey = "FieldName";
2123

24+
/// <summary>
25+
/// The key for the name of the generated property to update a field reference to.
26+
/// </summary>
27+
internal const string PropertyNameKey = "PropertyName";
28+
2229
/// <inheritdoc/>
2330
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(FieldReferenceForObservablePropertyFieldWarning);
2431

@@ -66,8 +73,8 @@ public override void Initialize(AnalysisContext context)
6673
FieldReferenceForObservablePropertyFieldWarning,
6774
context.Operation.Syntax.GetLocation(),
6875
ImmutableDictionary.Create<string, string?>()
69-
.Add(PropertyNameKey, ObservablePropertyGenerator.Execute.GetGeneratedPropertyName(fieldSymbol))
70-
.Add(FieldNameKey, fieldSymbol.Name),
76+
.Add(FieldNameKey, fieldSymbol.Name)
77+
.Add(PropertyNameKey, ObservablePropertyGenerator.Execute.GetGeneratedPropertyName(fieldSymbol)),
7178
fieldSymbol));
7279

7380
return;

src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ namespace CommunityToolkit.Mvvm.SourceGenerators.Diagnostics;
1414
/// </summary>
1515
internal static class DiagnosticDescriptors
1616
{
17+
/// <summary>
18+
/// The diagnostic id for <see cref="FieldReferenceForObservablePropertyFieldWarning"/>.
19+
/// </summary>
20+
public const string FieldReferenceForObservablePropertyFieldId = "MVVMTK0034";
21+
1722
/// <summary>
1823
/// Gets a <see cref="DiagnosticDescriptor"/> indicating when a duplicate declaration of <see cref="INotifyPropertyChanged"/> would happen.
1924
/// <para>
@@ -543,8 +548,6 @@ internal static class DiagnosticDescriptors
543548
"reduce the binary size of the application (the attributes are only meant to support cases where the annotated types are already inheriting from a different type).",
544549
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0033");
545550

546-
public const string FieldReferenceForObservablePropertyFieldId = "MVVMTK0034";
547-
548551
/// <summary>
549552
/// Gets a <see cref="DiagnosticDescriptor"/> indicating when a field with <c>[ObservableProperty]</c> is being directly referenced.
550553
/// <para>

tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
Microsoft.CodeAnalysis.Testing.Verifiers.MSTestVerifier>;
1717

1818
namespace CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests;
19+
1920
[TestClass]
2021
public class Test_FieldReferenceForObservablePropertyFieldFixer
2122
{
@@ -66,6 +67,7 @@ void M()
6667
{
6768
// /0/Test0.cs(10,13): warning MVVMTK0034: The field C.i is annotated with [ObservableProperty] and should not be directly referenced (use the generated property instead)
6869
VerifyCS.Diagnostic().WithSpan(10, 13, 10, 14).WithArguments("C.i"),
70+
6971
// /0/Test0.cs(11,9): warning MVVMTK0034: The field C.i is annotated with [ObservableProperty] and should not be directly referenced (use the generated property instead)
7072
VerifyCS.Diagnostic().WithSpan(11, 9, 11, 10).WithArguments("C.i")
7173
});
@@ -74,6 +76,7 @@ void M()
7476
{
7577
// /0/Test0.cs(10,13): error CS0103: The name 'I' does not exist in the current context
7678
DiagnosticResult.CompilerError("CS0103").WithSpan(10, 13, 10, 14).WithArguments("I"),
79+
7780
// /0/Test0.cs(11,9): error CS0103: The name 'I' does not exist in the current context
7881
DiagnosticResult.CompilerError("CS0103").WithSpan(11, 9, 11, 10).WithArguments("I"),
7982
});
@@ -128,6 +131,7 @@ void M()
128131
{
129132
// /0/Test0.cs(10,13): warning MVVMTK0034: The field C.i is annotated with [ObservableProperty] and should not be directly referenced (use the generated property instead)
130133
VerifyCS.Diagnostic().WithSpan(10, 13, 10, 19).WithArguments("C.i"),
134+
131135
// /0/Test0.cs(11,9): warning MVVMTK0034: The field C.i is annotated with [ObservableProperty] and should not be directly referenced (use the generated property instead)
132136
VerifyCS.Diagnostic().WithSpan(11, 9, 11, 15).WithArguments("C.i"),
133137
});
@@ -136,6 +140,7 @@ void M()
136140
{
137141
// /0/Test0.cs(10,18): error CS1061: 'C' does not contain a definition for 'I' and no accessible extension method 'I' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?)
138142
DiagnosticResult.CompilerError("CS1061").WithSpan(10, 18, 10, 19).WithArguments("C", "I"),
143+
139144
// /0/Test0.cs(11,14): error CS1061: 'C' does not contain a definition for 'I' and no accessible extension method 'I' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?)
140145
DiagnosticResult.CompilerError("CS1061").WithSpan(11, 14, 11, 15).WithArguments("C", "I"),
141146
});
@@ -198,6 +203,7 @@ void M()
198203
{
199204
// /0/Test0.cs(14,9): warning MVVMTK0034: The field C.i is annotated with [ObservableProperty] and should not be directly referenced (use the generated property instead)
200205
VerifyCS.Diagnostic().WithSpan(14, 9, 14, 12).WithArguments("C.i"),
206+
201207
// /0/Test0.cs(15,13): warning MVVMTK0034: The field C.i is annotated with [ObservableProperty] and should not be directly referenced (use the generated property instead)
202208
VerifyCS.Diagnostic().WithSpan(15, 13, 15, 16).WithArguments("C.i"),
203209
});
@@ -206,6 +212,7 @@ void M()
206212
{
207213
// /0/Test0.cs(14,11): error CS1061: 'C' does not contain a definition for 'I' and no accessible extension method 'I' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?)
208214
DiagnosticResult.CompilerError("CS1061").WithSpan(14, 11, 14, 12).WithArguments("C", "I"),
215+
209216
// /0/Test0.cs(15,15): error CS1061: 'C' does not contain a definition for 'I' and no accessible extension method 'I' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?)
210217
DiagnosticResult.CompilerError("CS1061").WithSpan(15, 15, 15, 16).WithArguments("C", "I"),
211218
});

0 commit comments

Comments
 (0)