From 441abbbdbc8b8a2718a15f52141226c3a8d8fa51 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Sat, 21 Jan 2023 23:13:38 -0800 Subject: [PATCH 1/6] Add fixers project and implement a fixer for FieldReferenceForObservablePropertyFieldAnalyzer This adds a fixer for MVVMTK0034, that simply swaps the field reference for what will be generated for the property. It leaves the qualification intact, basically just doing a text swap. Because it's very a very simple fixer, the built-in batch fixall provider works just fine and does not need special handling. While I implemented the fixer and added some tests, I did not actually set up all the msbuild goop for properly packaging this as part of the existing packages (as, frankly, the multi-targeting and msbuild setup in this repo is very complicated and I didn't feel like trying to grok exactly how to add it given the setup). The fixer assembly needs to be put next to the analyzer assemblies. This PR will be editable by maintainers, so feel free to implement this in-place in the PR itself. Fixes https://github.com/CommunityToolkit/dotnet/issues/577. --- dotnet Community Toolkit.sln | 22 ++ .../CommunityToolkit.Mvvm.Fixers.csproj | 15 + ...eferenceForObservablePropertyFieldFixer.cs | 70 +++++ ...ityToolkit.Mvvm.SourceGenerators.projitems | 1 + ...renceForObservablePropertyFieldAnalyzer.cs | 11 +- .../Diagnostics/DiagnosticDescriptors.cs | 4 +- .../InternalsVisibleTo.cs | 7 + ...ourceGenerators.Roslyn401.UnitTests.csproj | 3 + ...eferenceForObservablePropertyFieldFixer.cs | 275 ++++++++++++++++++ 9 files changed, 406 insertions(+), 2 deletions(-) create mode 100644 src/CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj create mode 100644 src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs create mode 100644 src/CommunityToolkit.Mvvm.SourceGenerators/InternalsVisibleTo.cs create mode 100644 tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs diff --git a/dotnet Community Toolkit.sln b/dotnet Community Toolkit.sln index 8e1b61745..0383ac335 100644 --- a/dotnet Community Toolkit.sln +++ b/dotnet Community Toolkit.sln @@ -81,6 +81,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.Exter EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.ExternalAssembly.Roslyn431", "tests\CommunityToolkit.Mvvm.ExternalAssembly.Roslyn431\CommunityToolkit.Mvvm.ExternalAssembly.Roslyn431.csproj", "{4FCD501C-1BB5-465C-AD19-356DAB6600C6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Mvvm.Fixers", "src\CommunityToolkit.Mvvm.Fixers\CommunityToolkit.Mvvm.Fixers.csproj", "{E79DCA2A-4C59-499F-85BD-F45215ED6B72}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -435,6 +437,26 @@ Global {4FCD501C-1BB5-465C-AD19-356DAB6600C6}.Release|x64.Build.0 = Release|Any CPU {4FCD501C-1BB5-465C-AD19-356DAB6600C6}.Release|x86.ActiveCfg = Release|Any CPU {4FCD501C-1BB5-465C-AD19-356DAB6600C6}.Release|x86.Build.0 = Release|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Debug|ARM.ActiveCfg = Debug|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Debug|ARM.Build.0 = Debug|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Debug|ARM64.Build.0 = Debug|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Debug|x64.ActiveCfg = Debug|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Debug|x64.Build.0 = Debug|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Debug|x86.ActiveCfg = Debug|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Debug|x86.Build.0 = Debug|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Release|Any CPU.Build.0 = Release|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Release|ARM.ActiveCfg = Release|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Release|ARM.Build.0 = Release|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Release|ARM64.ActiveCfg = Release|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Release|ARM64.Build.0 = Release|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Release|x64.ActiveCfg = Release|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Release|x64.Build.0 = Release|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Release|x86.ActiveCfg = Release|Any CPU + {E79DCA2A-4C59-499F-85BD-F45215ED6B72}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj b/src/CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj new file mode 100644 index 000000000..69e945207 --- /dev/null +++ b/src/CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs b/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs new file mode 100644 index 000000000..970436a3b --- /dev/null +++ b/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs @@ -0,0 +1,70 @@ +// 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.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.SourceGenerators; +using CommunityToolkit.Mvvm.SourceGenerators.Diagnostics; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace CommunityToolkit.Mvvm.Fixers; + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member +[ExportCodeFixProvider(LanguageNames.CSharp)] +public class FieldReferenceForObservablePropertyFieldFixer : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticDescriptors.FieldReferenceForObservablePropertyFieldId); + + public override FixAllProvider? GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + Diagnostic diagnostic = context.Diagnostics[0]; + Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = diagnostic.Location.SourceSpan; + + string? propertyName = diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.PropertyNameKey]; + string? fieldName = diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.FieldNameKey]; + + if (propertyName == null || fieldName == null) + { + return; + } + + SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + Debug.Assert(root != null); + + IdentifierNameSyntax fieldReference = root!.FindNode(diagnosticSpan).DescendantNodesAndSelf().OfType().FirstOrDefault(i => i.ToString() == fieldName); + + if (fieldReference == null) + { + return; + } + + context.RegisterCodeFix( + CodeAction.Create( + title: "Reference property", + createChangedDocument: c => UpdateReference(context.Document, fieldReference, propertyName, c), + equivalenceKey: "Reference property"), + diagnostic); + + } + + private async Task UpdateReference(Document document, IdentifierNameSyntax fieldReference, string propertyName, CancellationToken cancellationToken) + { + IdentifierNameSyntax propertyReference = SyntaxFactory.IdentifierName(propertyName); + SyntaxNode originalRoot = await fieldReference.SyntaxTree.GetRootAsync(cancellationToken); + SyntaxTree updatedTree = originalRoot.ReplaceNode(fieldReference, propertyReference).SyntaxTree; + return document.WithSyntaxRoot(await updatedTree.GetRootAsync(cancellationToken)); + } +} diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems b/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems index 405e84cfd..da44c1e47 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems @@ -65,6 +65,7 @@ + diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/FieldReferenceForObservablePropertyFieldAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/FieldReferenceForObservablePropertyFieldAnalyzer.cs index cda520e6f..2a9f75ee8 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/FieldReferenceForObservablePropertyFieldAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/FieldReferenceForObservablePropertyFieldAnalyzer.cs @@ -16,6 +16,9 @@ namespace CommunityToolkit.Mvvm.SourceGenerators; [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class FieldReferenceForObservablePropertyFieldAnalyzer : DiagnosticAnalyzer { + internal const string PropertyNameKey = "PropertyName"; + internal const string FieldNameKey = "FieldName"; + /// public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(FieldReferenceForObservablePropertyFieldWarning); @@ -59,7 +62,13 @@ public override void Initialize(AnalysisContext context) SymbolEqualityComparer.Default.Equals(attributeClass, attributeSymbol)) { // Emit a warning to redirect users to access the generated property instead - context.ReportDiagnostic(Diagnostic.Create(FieldReferenceForObservablePropertyFieldWarning, context.Operation.Syntax.GetLocation(), fieldSymbol)); + context.ReportDiagnostic(Diagnostic.Create( + FieldReferenceForObservablePropertyFieldWarning, + context.Operation.Syntax.GetLocation(), + ImmutableDictionary.Create() + .Add(PropertyNameKey, ObservablePropertyGenerator.Execute.GetGeneratedPropertyName(fieldSymbol)) + .Add(FieldNameKey, fieldSymbol.Name), + fieldSymbol)); return; } diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs index d3c4fd4d1..33031bdb7 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs @@ -543,6 +543,8 @@ internal static class DiagnosticDescriptors "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).", helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0033"); + public const string FieldReferenceForObservablePropertyFieldId = "MVVMTK0034"; + /// /// Gets a indicating when a field with [ObservableProperty] is being directly referenced. /// @@ -550,7 +552,7 @@ internal static class DiagnosticDescriptors /// /// public static readonly DiagnosticDescriptor FieldReferenceForObservablePropertyFieldWarning = new DiagnosticDescriptor( - id: "MVVMTK0034", + id: FieldReferenceForObservablePropertyFieldId, title: "Direct field reference to [ObservableProperty] backing field", messageFormat: "The field {0} is annotated with [ObservableProperty] and should not be directly referenced (use the generated property instead)", category: typeof(ObservablePropertyGenerator).FullName, diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/InternalsVisibleTo.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/InternalsVisibleTo.cs new file mode 100644 index 000000000..11619e949 --- /dev/null +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/InternalsVisibleTo.cs @@ -0,0 +1,7 @@ +// 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.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("CommunityToolkit.Mvvm.Fixers, PublicKey=002400000480000094000000060200000024000052534131000400000100010041753af735ae6140c9508567666c51c6ab929806adb0d210694b30ab142a060237bc741f9682e7d8d4310364b4bba4ee89cc9d3d5ce7e5583587e8ea44dca09977996582875e71fb54fa7b170798d853d5d8010b07219633bdb761d01ac924da44576d6180cdceae537973982bb461c541541d58417a3794e34f45e6f2d129e2")] diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj index 8bfbf73a2..98e8c9eb5 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj @@ -6,6 +6,8 @@ + + @@ -13,6 +15,7 @@ + diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs new file mode 100644 index 000000000..e60d71c14 --- /dev/null +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs @@ -0,0 +1,275 @@ +// 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.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Test = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixTest< + CommunityToolkit.Mvvm.SourceGenerators.FieldReferenceForObservablePropertyFieldAnalyzer, + CommunityToolkit.Mvvm.Fixers.FieldReferenceForObservablePropertyFieldFixer, + Microsoft.CodeAnalysis.Testing.Verifiers.MSTestVerifier>; +using VerifyCS = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier< + CommunityToolkit.Mvvm.SourceGenerators.FieldReferenceForObservablePropertyFieldAnalyzer, + CommunityToolkit.Mvvm.Fixers.FieldReferenceForObservablePropertyFieldFixer, + Microsoft.CodeAnalysis.Testing.Verifiers.MSTestVerifier>; + +namespace CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests; +[TestClass] +public class Test_FieldReferenceForObservablePropertyFieldFixer +{ + [TestMethod] + public async Task SimpleMemberAccess() + { + string original = """ + using CommunityToolkit.Mvvm.ComponentModel; + + class C : ObservableObject + { + [ObservableProperty] + private int i; + + void M() + { + _ = i; + i = 1; + } + } + """; + + string @fixed = """ + using CommunityToolkit.Mvvm.ComponentModel; + + class C : ObservableObject + { + [ObservableProperty] + private int i; + + void M() + { + _ = I; + I = 1; + } + } + """; + + Test test = new() + { + TestCode = original, + FixedCode = @fixed, + }; + + test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly); + test.ExpectedDiagnostics.AddRange(new[] + { + // /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) + VerifyCS.Diagnostic().WithSpan(10, 13, 10, 14).WithArguments("C.i"), + // /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) + VerifyCS.Diagnostic().WithSpan(11, 9, 11, 10).WithArguments("C.i") + }); + + test.FixedState.ExpectedDiagnostics.AddRange(new[] + { + // /0/Test0.cs(10,13): error CS0103: The name 'I' does not exist in the current context + DiagnosticResult.CompilerError("CS0103").WithSpan(10, 13, 10, 14).WithArguments("I"), + // /0/Test0.cs(11,9): error CS0103: The name 'I' does not exist in the current context + DiagnosticResult.CompilerError("CS0103").WithSpan(11, 9, 11, 10).WithArguments("I"), + }); + + await test.RunAsync(); + } + + [TestMethod] + public async Task QualifiedMemberAccess() + { + string original = """ + using CommunityToolkit.Mvvm.ComponentModel; + + class C : ObservableObject + { + [ObservableProperty] + private int i; + + void M() + { + _ = this.i; + this.i = 1; + } + } + """; + + string @fixed = """ + using CommunityToolkit.Mvvm.ComponentModel; + + class C : ObservableObject + { + [ObservableProperty] + private int i; + + void M() + { + _ = this.I; + this.I = 1; + } + } + """; + + Test test = new() + { + TestCode = original, + FixedCode = @fixed, + }; + + test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly); + test.ExpectedDiagnostics.AddRange(new[] + { + // /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) + VerifyCS.Diagnostic().WithSpan(10, 13, 10, 19).WithArguments("C.i"), + // /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) + VerifyCS.Diagnostic().WithSpan(11, 9, 11, 15).WithArguments("C.i"), + }); + + test.FixedState.ExpectedDiagnostics.AddRange(new[] + { + // /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?) + DiagnosticResult.CompilerError("CS1061").WithSpan(10, 18, 10, 19).WithArguments("C", "I"), + // /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?) + DiagnosticResult.CompilerError("CS1061").WithSpan(11, 14, 11, 15).WithArguments("C", "I"), + }); + + await test.RunAsync(); + } + + [TestMethod] + public async Task ExternalQualifiedMemberAccess() + { + string original = """ + using CommunityToolkit.Mvvm.ComponentModel; + + class C : ObservableObject + { + [ObservableProperty] + public int i; + } + + class D + { + void M() + { + var c = new C(); + c.i = 1; + _ = c.i; + } + } + """; + + string @fixed = """ + using CommunityToolkit.Mvvm.ComponentModel; + + class C : ObservableObject + { + [ObservableProperty] + public int i; + } + + class D + { + void M() + { + var c = new C(); + c.I = 1; + _ = c.I; + } + } + """; + + Test test = new() + { + TestCode = original, + FixedCode = @fixed, + }; + + test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly); + test.ExpectedDiagnostics.AddRange(new[] + { + // /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) + VerifyCS.Diagnostic().WithSpan(14, 9, 14, 12).WithArguments("C.i"), + // /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) + VerifyCS.Diagnostic().WithSpan(15, 13, 15, 16).WithArguments("C.i"), + }); + + test.FixedState.ExpectedDiagnostics.AddRange(new[] + { + // /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?) + DiagnosticResult.CompilerError("CS1061").WithSpan(14, 11, 14, 12).WithArguments("C", "I"), + // /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?) + DiagnosticResult.CompilerError("CS1061").WithSpan(15, 15, 15, 16).WithArguments("C", "I"), + }); + + await test.RunAsync(); + } + + [TestMethod] + public async Task ExternalConditionalMemberAccess() + { + string original = """ + using CommunityToolkit.Mvvm.ComponentModel; + + class C : ObservableObject + { + [ObservableProperty] + public int i; + } + + class D + { + void M() + { + var c = new C(); + _ = c?.i; + } + } + """; + + string @fixed = """ + using CommunityToolkit.Mvvm.ComponentModel; + + class C : ObservableObject + { + [ObservableProperty] + public int i; + } + + class D + { + void M() + { + var c = new C(); + _ = c?.I; + } + } + """; + + Test test = new() + { + TestCode = original, + FixedCode = @fixed, + }; + + test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly); + test.ExpectedDiagnostics.AddRange(new[] + { + // /0/Test0.cs(14,15): warning MVVMTK0034: The field C.i is annotated with [ObservableProperty] and should not be directly referenced (use the generated property instead) + VerifyCS.Diagnostic().WithSpan(14, 15, 14, 17).WithArguments("C.i"), + }); + + test.FixedState.ExpectedDiagnostics.AddRange(new[] + { + // /0/Test0.cs(14,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?) + DiagnosticResult.CompilerError("CS1061").WithSpan(14, 15, 14, 17).WithArguments("C", "I"), + }); + + await test.RunAsync(); + } +} From 78d37ab7008792ce636c12d3c302a061dd08743a Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Sun, 22 Jan 2023 15:11:53 -0800 Subject: [PATCH 2/6] Use specific reference assemblies to resolve test errors, mark fixer shared. --- .../FieldReferenceForObservablePropertyFieldFixer.cs | 5 +++-- .../Test_FieldReferenceForObservablePropertyFieldFixer.cs | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs b/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs index 970436a3b..b145c2755 100644 --- a/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs +++ b/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Composition; using System.Diagnostics; using System.Linq; using System.Threading; @@ -18,7 +19,7 @@ namespace CommunityToolkit.Mvvm.Fixers; #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member -[ExportCodeFixProvider(LanguageNames.CSharp)] +[ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class FieldReferenceForObservablePropertyFieldFixer : CodeFixProvider { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticDescriptors.FieldReferenceForObservablePropertyFieldId); @@ -60,7 +61,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) } - private async Task UpdateReference(Document document, IdentifierNameSyntax fieldReference, string propertyName, CancellationToken cancellationToken) + private static async Task UpdateReference(Document document, IdentifierNameSyntax fieldReference, string propertyName, CancellationToken cancellationToken) { IdentifierNameSyntax propertyReference = SyntaxFactory.IdentifierName(propertyName); SyntaxNode originalRoot = await fieldReference.SyntaxTree.GetRootAsync(cancellationToken); diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs index e60d71c14..ac6aef267 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs @@ -58,6 +58,7 @@ void M() { TestCode = original, FixedCode = @fixed, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60 }; test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly); @@ -119,6 +120,7 @@ void M() { TestCode = original, FixedCode = @fixed, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60 }; test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly); @@ -188,6 +190,7 @@ void M() { TestCode = original, FixedCode = @fixed, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60 }; test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly); @@ -255,6 +258,7 @@ void M() { TestCode = original, FixedCode = @fixed, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60 }; test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly); From b908b3fffd485779c9fa171e61d13259c66496ad Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 23 Jan 2023 00:22:42 +0100 Subject: [PATCH 3/6] Define [IVT] in .props file, don't hardcode public key --- .../CommunityToolkit.HighPerformance.csproj | 2 +- .../CommunityToolkit.Mvvm.SourceGenerators.projitems | 1 - .../CommunityToolkit.Mvvm.SourceGenerators.props | 5 +++++ .../InternalsVisibleTo.cs | 7 ------- 4 files changed, 6 insertions(+), 9 deletions(-) delete mode 100644 src/CommunityToolkit.Mvvm.SourceGenerators/InternalsVisibleTo.cs diff --git a/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj b/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj index 1639b330d..bfc0e6bbe 100644 --- a/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj +++ b/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj @@ -36,7 +36,7 @@ - + diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems b/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems index da44c1e47..405e84cfd 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems @@ -65,7 +65,6 @@ - diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.props b/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.props index c3130e015..30e6cdca3 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.props +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.props @@ -35,4 +35,9 @@ + + + + + \ No newline at end of file diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/InternalsVisibleTo.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/InternalsVisibleTo.cs deleted file mode 100644 index 11619e949..000000000 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/InternalsVisibleTo.cs +++ /dev/null @@ -1,7 +0,0 @@ -// 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.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("CommunityToolkit.Mvvm.Fixers, PublicKey=002400000480000094000000060200000024000052534131000400000100010041753af735ae6140c9508567666c51c6ab929806adb0d210694b30ab142a060237bc741f9682e7d8d4310364b4bba4ee89cc9d3d5ce7e5583587e8ea44dca09977996582875e71fb54fa7b170798d853d5d8010b07219633bdb761d01ac924da44576d6180cdceae537973982bb461c541541d58417a3794e34f45e6f2d129e2")] From 6ee8d7cff73b073ea22a31d85e6c38d8177ea0da Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 23 Jan 2023 00:37:15 +0100 Subject: [PATCH 4/6] Update code style to follow repo conventions --- ...eferenceForObservablePropertyFieldFixer.cs | 65 ++++++++++++------- ...renceForObservablePropertyFieldAnalyzer.cs | 13 +++- .../Diagnostics/DiagnosticDescriptors.cs | 7 +- ...eferenceForObservablePropertyFieldFixer.cs | 7 ++ 4 files changed, 62 insertions(+), 30 deletions(-) diff --git a/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs b/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs index b145c2755..7e96afcd2 100644 --- a/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs +++ b/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs @@ -4,8 +4,6 @@ using System.Collections.Immutable; using System.Composition; -using System.Diagnostics; -using System.Linq; using System.Threading; using System.Threading.Tasks; using CommunityToolkit.Mvvm.SourceGenerators; @@ -15,57 +13,74 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; namespace CommunityToolkit.Mvvm.Fixers; -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member -[ExportCodeFixProvider(LanguageNames.CSharp), Shared] -public class FieldReferenceForObservablePropertyFieldFixer : CodeFixProvider +/// +/// A code fixer that automatically updates references to fields with [ObservableProperty] to reference the generated property instead. +/// +[ExportCodeFixProvider(LanguageNames.CSharp)] +[Shared] +public sealed class FieldReferenceForObservablePropertyFieldFixer : CodeFixProvider { + /// public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticDescriptors.FieldReferenceForObservablePropertyFieldId); + /// public override FixAllProvider? GetFixAllProvider() { return WellKnownFixAllProviders.BatchFixer; } + /// public override async Task RegisterCodeFixesAsync(CodeFixContext context) { Diagnostic diagnostic = context.Diagnostics[0]; - Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = diagnostic.Location.SourceSpan; + TextSpan diagnosticSpan = diagnostic.Location.SourceSpan; - string? propertyName = diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.PropertyNameKey]; - string? fieldName = diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.FieldNameKey]; - - if (propertyName == null || fieldName == null) + // Retrieve the properties passed by the analyzer + if (diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.FieldNameKey] is not string fieldName || + diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.PropertyNameKey] is not string propertyName) { return; } SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - Debug.Assert(root != null); - - IdentifierNameSyntax fieldReference = root!.FindNode(diagnosticSpan).DescendantNodesAndSelf().OfType().FirstOrDefault(i => i.ToString() == fieldName); - if (fieldReference == null) + foreach (SyntaxNode syntaxNode in root!.FindNode(diagnosticSpan).DescendantNodesAndSelf()) { - return; - } - - context.RegisterCodeFix( - CodeAction.Create( - title: "Reference property", - createChangedDocument: c => UpdateReference(context.Document, fieldReference, propertyName, c), - equivalenceKey: "Reference property"), - diagnostic); + // Find the first descendant node from the source of the diagnostic that is an identifier with the target name + if (syntaxNode is IdentifierNameSyntax { Identifier.Text: string identifierName } identifierNameSyntax && + identifierName == fieldName) + { + // Register the code fix to update the field reference to use the generated property instead + context.RegisterCodeFix( + CodeAction.Create( + title: "Reference property", + createChangedDocument: token => UpdateReference(context.Document, identifierNameSyntax, propertyName, token), + equivalenceKey: "Reference property"), + diagnostic); + return; + } + } } + /// + /// Applies the code fix to a target identifier and returns an updated document. + /// + /// The original document being fixed. + /// The corresponding to the field reference to update. + /// The name of the generated property. + /// The cancellation token for the operation. + /// An updated document with the applied code fix, and being replaced with a property reference. private static async Task UpdateReference(Document document, IdentifierNameSyntax fieldReference, string propertyName, CancellationToken cancellationToken) { IdentifierNameSyntax propertyReference = SyntaxFactory.IdentifierName(propertyName); - SyntaxNode originalRoot = await fieldReference.SyntaxTree.GetRootAsync(cancellationToken); + SyntaxNode originalRoot = await fieldReference.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); SyntaxTree updatedTree = originalRoot.ReplaceNode(fieldReference, propertyReference).SyntaxTree; - return document.WithSyntaxRoot(await updatedTree.GetRootAsync(cancellationToken)); + + return document.WithSyntaxRoot(await updatedTree.GetRootAsync(cancellationToken).ConfigureAwait(false)); } } diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/FieldReferenceForObservablePropertyFieldAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/FieldReferenceForObservablePropertyFieldAnalyzer.cs index 2a9f75ee8..dd7885ed2 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/FieldReferenceForObservablePropertyFieldAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/FieldReferenceForObservablePropertyFieldAnalyzer.cs @@ -16,9 +16,16 @@ namespace CommunityToolkit.Mvvm.SourceGenerators; [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class FieldReferenceForObservablePropertyFieldAnalyzer : DiagnosticAnalyzer { - internal const string PropertyNameKey = "PropertyName"; + /// + /// The key for the name of the target field to update. + /// internal const string FieldNameKey = "FieldName"; + /// + /// The key for the name of the generated property to update a field reference to. + /// + internal const string PropertyNameKey = "PropertyName"; + /// public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(FieldReferenceForObservablePropertyFieldWarning); @@ -66,8 +73,8 @@ public override void Initialize(AnalysisContext context) FieldReferenceForObservablePropertyFieldWarning, context.Operation.Syntax.GetLocation(), ImmutableDictionary.Create() - .Add(PropertyNameKey, ObservablePropertyGenerator.Execute.GetGeneratedPropertyName(fieldSymbol)) - .Add(FieldNameKey, fieldSymbol.Name), + .Add(FieldNameKey, fieldSymbol.Name) + .Add(PropertyNameKey, ObservablePropertyGenerator.Execute.GetGeneratedPropertyName(fieldSymbol)), fieldSymbol)); return; diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs index 33031bdb7..05efb3e48 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs @@ -14,6 +14,11 @@ namespace CommunityToolkit.Mvvm.SourceGenerators.Diagnostics; /// internal static class DiagnosticDescriptors { + /// + /// The diagnostic id for . + /// + public const string FieldReferenceForObservablePropertyFieldId = "MVVMTK0034"; + /// /// Gets a indicating when a duplicate declaration of would happen. /// @@ -543,8 +548,6 @@ internal static class DiagnosticDescriptors "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).", helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0033"); - public const string FieldReferenceForObservablePropertyFieldId = "MVVMTK0034"; - /// /// Gets a indicating when a field with [ObservableProperty] is being directly referenced. /// diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs index ac6aef267..754ef7298 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs @@ -16,6 +16,7 @@ Microsoft.CodeAnalysis.Testing.Verifiers.MSTestVerifier>; namespace CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests; + [TestClass] public class Test_FieldReferenceForObservablePropertyFieldFixer { @@ -66,6 +67,7 @@ void M() { // /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) VerifyCS.Diagnostic().WithSpan(10, 13, 10, 14).WithArguments("C.i"), + // /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) VerifyCS.Diagnostic().WithSpan(11, 9, 11, 10).WithArguments("C.i") }); @@ -74,6 +76,7 @@ void M() { // /0/Test0.cs(10,13): error CS0103: The name 'I' does not exist in the current context DiagnosticResult.CompilerError("CS0103").WithSpan(10, 13, 10, 14).WithArguments("I"), + // /0/Test0.cs(11,9): error CS0103: The name 'I' does not exist in the current context DiagnosticResult.CompilerError("CS0103").WithSpan(11, 9, 11, 10).WithArguments("I"), }); @@ -128,6 +131,7 @@ void M() { // /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) VerifyCS.Diagnostic().WithSpan(10, 13, 10, 19).WithArguments("C.i"), + // /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) VerifyCS.Diagnostic().WithSpan(11, 9, 11, 15).WithArguments("C.i"), }); @@ -136,6 +140,7 @@ void M() { // /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?) DiagnosticResult.CompilerError("CS1061").WithSpan(10, 18, 10, 19).WithArguments("C", "I"), + // /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?) DiagnosticResult.CompilerError("CS1061").WithSpan(11, 14, 11, 15).WithArguments("C", "I"), }); @@ -198,6 +203,7 @@ void M() { // /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) VerifyCS.Diagnostic().WithSpan(14, 9, 14, 12).WithArguments("C.i"), + // /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) VerifyCS.Diagnostic().WithSpan(15, 13, 15, 16).WithArguments("C.i"), }); @@ -206,6 +212,7 @@ void M() { // /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?) DiagnosticResult.CompilerError("CS1061").WithSpan(14, 11, 14, 12).WithArguments("C", "I"), + // /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?) DiagnosticResult.CompilerError("CS1061").WithSpan(15, 15, 15, 16).WithArguments("C", "I"), }); From 1be2fa125dffd5b9c030ecafbc4c910bf0fe2927 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 23 Jan 2023 01:07:22 +0100 Subject: [PATCH 5/6] Pack code fixer project in the MVVM Toolkit package --- .../CommunityToolkit.Mvvm.Fixers.csproj | 1 + .../CommunityToolkit.Mvvm.csproj | 11 ++++++++++- ...t.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj b/src/CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj index 69e945207..a3b8e5376 100644 --- a/src/CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj +++ b/src/CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj @@ -2,6 +2,7 @@ netstandard2.0 + false diff --git a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj index 43adfb763..2e1eb269e 100644 --- a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj +++ b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj @@ -34,10 +34,11 @@ - + + @@ -73,9 +74,17 @@ + + \ No newline at end of file diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj index 98e8c9eb5..9d6fe897d 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj @@ -15,8 +15,8 @@ - + From e7a79c3c5bc2ea56b7789210ef5be8098b6b6840 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 23 Jan 2023 16:53:47 +0100 Subject: [PATCH 6/6] Rename code fixer project to CommunityToolkit.Mvvm.CodeFixers --- dotnet Community Toolkit.sln | 2 +- .../CommunityToolkit.Mvvm.CodeFixers.csproj} | 0 ...eferenceForObservablePropertyFieldFixer.cs | 4 +-- ...mmunityToolkit.Mvvm.SourceGenerators.props | 2 +- .../CommunityToolkit.Mvvm.csproj | 6 ++-- ...ourceGenerators.Roslyn401.UnitTests.csproj | 2 +- ...eferenceForObservablePropertyFieldFixer.cs | 32 +++++++++---------- 7 files changed, 24 insertions(+), 24 deletions(-) rename src/{CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj => CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.csproj} (100%) rename src/{CommunityToolkit.Mvvm.Fixers => CommunityToolkit.Mvvm.CodeFixers}/FieldReferenceForObservablePropertyFieldFixer.cs (96%) diff --git a/dotnet Community Toolkit.sln b/dotnet Community Toolkit.sln index 0383ac335..e8d004357 100644 --- a/dotnet Community Toolkit.sln +++ b/dotnet Community Toolkit.sln @@ -81,7 +81,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.Exter EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.ExternalAssembly.Roslyn431", "tests\CommunityToolkit.Mvvm.ExternalAssembly.Roslyn431\CommunityToolkit.Mvvm.ExternalAssembly.Roslyn431.csproj", "{4FCD501C-1BB5-465C-AD19-356DAB6600C6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Mvvm.Fixers", "src\CommunityToolkit.Mvvm.Fixers\CommunityToolkit.Mvvm.Fixers.csproj", "{E79DCA2A-4C59-499F-85BD-F45215ED6B72}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Mvvm.CodeFixers", "src\CommunityToolkit.Mvvm.CodeFixers\CommunityToolkit.Mvvm.CodeFixers.csproj", "{E79DCA2A-4C59-499F-85BD-F45215ED6B72}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj b/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.csproj similarity index 100% rename from src/CommunityToolkit.Mvvm.Fixers/CommunityToolkit.Mvvm.Fixers.csproj rename to src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.csproj diff --git a/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs b/src/CommunityToolkit.Mvvm.CodeFixers/FieldReferenceForObservablePropertyFieldFixer.cs similarity index 96% rename from src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs rename to src/CommunityToolkit.Mvvm.CodeFixers/FieldReferenceForObservablePropertyFieldFixer.cs index 7e96afcd2..5a3a186db 100644 --- a/src/CommunityToolkit.Mvvm.Fixers/FieldReferenceForObservablePropertyFieldFixer.cs +++ b/src/CommunityToolkit.Mvvm.CodeFixers/FieldReferenceForObservablePropertyFieldFixer.cs @@ -15,14 +15,14 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; -namespace CommunityToolkit.Mvvm.Fixers; +namespace CommunityToolkit.Mvvm.CodeFixers; /// /// A code fixer that automatically updates references to fields with [ObservableProperty] to reference the generated property instead. /// [ExportCodeFixProvider(LanguageNames.CSharp)] [Shared] -public sealed class FieldReferenceForObservablePropertyFieldFixer : CodeFixProvider +public sealed class FieldReferenceForObservablePropertyFieldCodeFixer : CodeFixProvider { /// public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticDescriptors.FieldReferenceForObservablePropertyFieldId); diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.props b/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.props index 30e6cdca3..ee2074322 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.props +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.props @@ -37,7 +37,7 @@ - + \ No newline at end of file diff --git a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj index 2e1eb269e..272f56c7f 100644 --- a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj +++ b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj @@ -38,7 +38,7 @@ - + @@ -83,8 +83,8 @@ --> - - + + \ No newline at end of file diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj index 9d6fe897d..947810b2e 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests.csproj @@ -16,7 +16,7 @@ - + diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs index 754ef7298..dec18e08a 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests/Test_FieldReferenceForObservablePropertyFieldFixer.cs @@ -6,19 +6,19 @@ using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.CodeAnalysis.Testing; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Test = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixTest< +using CSharpCodeFixTest = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixTest< CommunityToolkit.Mvvm.SourceGenerators.FieldReferenceForObservablePropertyFieldAnalyzer, - CommunityToolkit.Mvvm.Fixers.FieldReferenceForObservablePropertyFieldFixer, + CommunityToolkit.Mvvm.CodeFixers.FieldReferenceForObservablePropertyFieldCodeFixer, Microsoft.CodeAnalysis.Testing.Verifiers.MSTestVerifier>; -using VerifyCS = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier< +using CSharpCodeFixVerifier = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier< CommunityToolkit.Mvvm.SourceGenerators.FieldReferenceForObservablePropertyFieldAnalyzer, - CommunityToolkit.Mvvm.Fixers.FieldReferenceForObservablePropertyFieldFixer, + CommunityToolkit.Mvvm.CodeFixers.FieldReferenceForObservablePropertyFieldCodeFixer, Microsoft.CodeAnalysis.Testing.Verifiers.MSTestVerifier>; namespace CommunityToolkit.Mvvm.SourceGenerators.Roslyn401.UnitTests; [TestClass] -public class Test_FieldReferenceForObservablePropertyFieldFixer +public class Test_FieldReferenceForObservablePropertyFieldCodeFixer { [TestMethod] public async Task SimpleMemberAccess() @@ -55,7 +55,7 @@ void M() } """; - Test test = new() + CSharpCodeFixTest test = new() { TestCode = original, FixedCode = @fixed, @@ -66,10 +66,10 @@ void M() test.ExpectedDiagnostics.AddRange(new[] { // /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) - VerifyCS.Diagnostic().WithSpan(10, 13, 10, 14).WithArguments("C.i"), + CSharpCodeFixVerifier.Diagnostic().WithSpan(10, 13, 10, 14).WithArguments("C.i"), // /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) - VerifyCS.Diagnostic().WithSpan(11, 9, 11, 10).WithArguments("C.i") + CSharpCodeFixVerifier.Diagnostic().WithSpan(11, 9, 11, 10).WithArguments("C.i") }); test.FixedState.ExpectedDiagnostics.AddRange(new[] @@ -119,7 +119,7 @@ void M() } """; - Test test = new() + CSharpCodeFixTest test = new() { TestCode = original, FixedCode = @fixed, @@ -130,10 +130,10 @@ void M() test.ExpectedDiagnostics.AddRange(new[] { // /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) - VerifyCS.Diagnostic().WithSpan(10, 13, 10, 19).WithArguments("C.i"), + CSharpCodeFixVerifier.Diagnostic().WithSpan(10, 13, 10, 19).WithArguments("C.i"), // /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) - VerifyCS.Diagnostic().WithSpan(11, 9, 11, 15).WithArguments("C.i"), + CSharpCodeFixVerifier.Diagnostic().WithSpan(11, 9, 11, 15).WithArguments("C.i"), }); test.FixedState.ExpectedDiagnostics.AddRange(new[] @@ -191,7 +191,7 @@ void M() } """; - Test test = new() + CSharpCodeFixTest test = new() { TestCode = original, FixedCode = @fixed, @@ -202,10 +202,10 @@ void M() test.ExpectedDiagnostics.AddRange(new[] { // /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) - VerifyCS.Diagnostic().WithSpan(14, 9, 14, 12).WithArguments("C.i"), + CSharpCodeFixVerifier.Diagnostic().WithSpan(14, 9, 14, 12).WithArguments("C.i"), // /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) - VerifyCS.Diagnostic().WithSpan(15, 13, 15, 16).WithArguments("C.i"), + CSharpCodeFixVerifier.Diagnostic().WithSpan(15, 13, 15, 16).WithArguments("C.i"), }); test.FixedState.ExpectedDiagnostics.AddRange(new[] @@ -261,7 +261,7 @@ void M() } """; - Test test = new() + CSharpCodeFixTest test = new() { TestCode = original, FixedCode = @fixed, @@ -272,7 +272,7 @@ void M() test.ExpectedDiagnostics.AddRange(new[] { // /0/Test0.cs(14,15): warning MVVMTK0034: The field C.i is annotated with [ObservableProperty] and should not be directly referenced (use the generated property instead) - VerifyCS.Diagnostic().WithSpan(14, 15, 14, 17).WithArguments("C.i"), + CSharpCodeFixVerifier.Diagnostic().WithSpan(14, 15, 14, 17).WithArguments("C.i"), }); test.FixedState.ExpectedDiagnostics.AddRange(new[]