diff --git a/dotnet Community Toolkit.sln b/dotnet Community Toolkit.sln
index 8a94f3c65..6d56ee910 100644
--- a/dotnet Community Toolkit.sln
+++ b/dotnet Community Toolkit.sln
@@ -81,12 +81,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.Exter
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.ExternalAssembly.Roslyn4031", "tests\CommunityToolkit.Mvvm.ExternalAssembly.Roslyn4031\CommunityToolkit.Mvvm.ExternalAssembly.Roslyn4031.csproj", "{4FCD501C-1BB5-465C-AD19-356DAB6600C6}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.CodeFixers", "src\CommunityToolkit.Mvvm.CodeFixers\CommunityToolkit.Mvvm.CodeFixers.csproj", "{E79DCA2A-4C59-499F-85BD-F45215ED6B72}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.CodeFixers.Roslyn4001", "src\CommunityToolkit.Mvvm.CodeFixers.Roslyn4001\CommunityToolkit.Mvvm.CodeFixers.Roslyn4001.csproj", "{E79DCA2A-4C59-499F-85BD-F45215ED6B72}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110", "src\CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110\CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.csproj", "{FCC13AD5-CEB8-4CC1-8250-89B616D126F2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests", "tests\CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests\CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests.csproj", "{C342302D-A263-42D6-B8EE-01DEF8192690}"
EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CommunityToolkit.Mvvm.CodeFixers", "src\CommunityToolkit.Mvvm.CodeFixers\CommunityToolkit.Mvvm.CodeFixers.shproj", "{A2EBDA90-B720-430D-83F5-C6BCC355232C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Mvvm.CodeFixers.Roslyn4110", "src\CommunityToolkit.Mvvm.CodeFixers.Roslyn4110\CommunityToolkit.Mvvm.CodeFixers.Roslyn4110.csproj", "{98572004-D29A-486E-8053-6D409557CE44}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -501,6 +505,26 @@ Global
{C342302D-A263-42D6-B8EE-01DEF8192690}.Release|x64.Build.0 = Release|Any CPU
{C342302D-A263-42D6-B8EE-01DEF8192690}.Release|x86.ActiveCfg = Release|Any CPU
{C342302D-A263-42D6-B8EE-01DEF8192690}.Release|x86.Build.0 = Release|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Debug|ARM.Build.0 = Debug|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Debug|x64.Build.0 = Debug|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Debug|x86.Build.0 = Debug|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Release|Any CPU.Build.0 = Release|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Release|ARM.ActiveCfg = Release|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Release|ARM.Build.0 = Release|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Release|ARM64.Build.0 = Release|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Release|x64.ActiveCfg = Release|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Release|x64.Build.0 = Release|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Release|x86.ActiveCfg = Release|Any CPU
+ {98572004-D29A-486E-8053-6D409557CE44}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -532,11 +556,14 @@ Global
tests\CommunityToolkit.Mvvm.ExternalAssembly\CommunityToolkit.Mvvm.ExternalAssembly.projitems*{4fcd501c-1bb5-465c-ad19-356dab6600c6}*SharedItemsImports = 5
tests\CommunityToolkit.Mvvm.UnitTests\CommunityToolkit.Mvvm.UnitTests.projitems*{5b44f7f1-dca2-4776-924e-a266f7bbf753}*SharedItemsImports = 5
src\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.projitems*{5e7f1212-a54b-40ca-98c5-1ff5cd1a1638}*SharedItemsImports = 13
+ src\CommunityToolkit.Mvvm.CodeFixers\CommunityToolkit.Mvvm.CodeFixers.projitems*{98572004-d29a-486e-8053-6d409557ce44}*SharedItemsImports = 5
+ src\CommunityToolkit.Mvvm.CodeFixers\CommunityToolkit.Mvvm.CodeFixers.projitems*{a2ebda90-b720-430d-83f5-c6bcc355232c}*SharedItemsImports = 13
tests\CommunityToolkit.Mvvm.UnitTests\CommunityToolkit.Mvvm.UnitTests.projitems*{ad9c3223-8e37-4fd4-a0d4-a45119551d3a}*SharedItemsImports = 5
tests\CommunityToolkit.Mvvm.UnitTests\CommunityToolkit.Mvvm.UnitTests.projitems*{b8dcd82e-b53b-4249-ad4e-f9b99acb9334}*SharedItemsImports = 13
tests\CommunityToolkit.Mvvm.SourceGenerators.UnitTests\CommunityToolkit.Mvvm.SourceGenerators.UnitTests.projitems*{c342302d-a263-42d6-b8ee-01def8192690}*SharedItemsImports = 5
src\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.projitems*{df455c40-b18e-4890-8758-7cccb5ca7052}*SharedItemsImports = 5
src\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.projitems*{e24d1146-5ad8-498f-a518-4890d8bf4937}*SharedItemsImports = 5
+ src\CommunityToolkit.Mvvm.CodeFixers\CommunityToolkit.Mvvm.CodeFixers.projitems*{e79dca2a-4c59-499f-85bd-f45215ed6b72}*SharedItemsImports = 5
tests\CommunityToolkit.Mvvm.ExternalAssembly\CommunityToolkit.Mvvm.ExternalAssembly.projitems*{e827a9cd-405f-43e4-84c7-68cc7e845cdc}*SharedItemsImports = 13
tests\CommunityToolkit.Mvvm.ExternalAssembly\CommunityToolkit.Mvvm.ExternalAssembly.projitems*{ecfe93aa-4b98-4292-b3fa-9430d513b4f9}*SharedItemsImports = 5
tests\CommunityToolkit.Mvvm.SourceGenerators.UnitTests\CommunityToolkit.Mvvm.SourceGenerators.UnitTests.projitems*{f3799252-7a66-4533-89d8-b3c312052d95}*SharedItemsImports = 5
diff --git a/src/CommunityToolkit.Mvvm.CodeFixers.Roslyn4001/CommunityToolkit.Mvvm.CodeFixers.Roslyn4001.csproj b/src/CommunityToolkit.Mvvm.CodeFixers.Roslyn4001/CommunityToolkit.Mvvm.CodeFixers.Roslyn4001.csproj
new file mode 100644
index 000000000..9f443712d
--- /dev/null
+++ b/src/CommunityToolkit.Mvvm.CodeFixers.Roslyn4001/CommunityToolkit.Mvvm.CodeFixers.Roslyn4001.csproj
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/CommunityToolkit.Mvvm.CodeFixers.Roslyn4110/CommunityToolkit.Mvvm.CodeFixers.Roslyn4110.csproj b/src/CommunityToolkit.Mvvm.CodeFixers.Roslyn4110/CommunityToolkit.Mvvm.CodeFixers.Roslyn4110.csproj
new file mode 100644
index 000000000..9f443712d
--- /dev/null
+++ b/src/CommunityToolkit.Mvvm.CodeFixers.Roslyn4110/CommunityToolkit.Mvvm.CodeFixers.Roslyn4110.csproj
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.csproj b/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.csproj
deleted file mode 100644
index 01020c625..000000000
--- a/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.csproj
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- netstandard2.0
- false
- true
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.projitems b/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.projitems
new file mode 100644
index 000000000..40339da45
--- /dev/null
+++ b/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.projitems
@@ -0,0 +1,20 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ a2ebda90-b720-430d-83f5-c6bcc355232c
+
+
+ CommunityToolkit.Mvvm.CodeFixers
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.props b/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.props
new file mode 100644
index 000000000..04381ec56
--- /dev/null
+++ b/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.props
@@ -0,0 +1,37 @@
+
+
+
+ netstandard2.0
+ false
+ true
+
+
+
+
+
+
+ $(MSBuildProjectName.Substring(0, $([MSBuild]::Subtract($(MSBuildProjectName.Length), 11))))
+
+
+ $(MSBuildProjectName.Substring($([MSBuild]::Subtract($(MSBuildProjectName.Length), 10))))
+
+
+ $(MSBuildProjectName.Substring($([MSBuild]::Subtract($(MSBuildProjectName.Length), 4)), 1))
+ $(MSBuildProjectName.Substring($([MSBuild]::Subtract($(MSBuildProjectName.Length), 3)), 2))
+ $(MSBuildProjectName.Substring($([MSBuild]::Subtract($(MSBuildProjectName.Length), 1)), 1))
+ $(MvvmToolkitSourceGeneratorRoslynMajorVersion).$(MvvmToolkitSourceGeneratorRoslynMinorVersion).$(MvvmToolkitSourceGeneratorRoslynPatchVersion)
+
+
+ $(DefineConstants);ROSLYN_4_3_1_OR_GREATER
+ $(DefineConstants);ROSLYN_4_11_0_OR_GREATER
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.shproj b/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.shproj
new file mode 100644
index 000000000..f36e53832
--- /dev/null
+++ b/src/CommunityToolkit.Mvvm.CodeFixers/CommunityToolkit.Mvvm.CodeFixers.shproj
@@ -0,0 +1,13 @@
+
+
+
+ a2ebda90-b720-430d-83f5-c6bcc355232c
+ 14.0
+
+
+
+
+
+
+
+
diff --git a/src/CommunityToolkit.Mvvm.CodeFixers/UsePartialPropertyForObservablePropertyCodeFixer.cs b/src/CommunityToolkit.Mvvm.CodeFixers/UsePartialPropertyForObservablePropertyCodeFixer.cs
index f688fa8f8..f6e2684e1 100644
--- a/src/CommunityToolkit.Mvvm.CodeFixers/UsePartialPropertyForObservablePropertyCodeFixer.cs
+++ b/src/CommunityToolkit.Mvvm.CodeFixers/UsePartialPropertyForObservablePropertyCodeFixer.cs
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#if ROSLYN_4_11_0_OR_GREATER
+
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
@@ -229,7 +231,7 @@ private static async Task ConvertToPartialProperty(
// Create the following property declaration:
//
//
- // public partial
+ //
// {
//
// get;
@@ -239,7 +241,7 @@ private static async Task ConvertToPartialProperty(
// }
PropertyDeclarationSyntax propertyDeclaration =
PropertyDeclaration(fieldDeclaration.Declaration.Type, Identifier(propertyName))
- .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.PartialKeyword))
+ .WithModifiers(GetPropertyModifiers(fieldDeclaration))
.AddAttributeLists(propertyAttributes.ToArray())
.WithAdditionalAnnotations(Formatter.Annotation)
.AddAccessorListAccessors(
@@ -260,7 +262,7 @@ private static async Task ConvertToPartialProperty(
// Create an editor to perform all mutations. This allows to keep track of multiple
// replacements for nodes on the same original tree, which otherwise wouldn't work.
- SyntaxEditor editor = new(root, document.Project.Solution.Workspace);
+ SyntaxEditor editor = new(root, document.Project.Solution.Workspace.Services);
editor.ReplaceNode(fieldDeclaration, propertyDeclaration);
@@ -291,4 +293,25 @@ private static async Task ConvertToPartialProperty(
return document.WithSyntaxRoot(editor.GetChangedRoot());
}
+
+ ///
+ /// Gets all modifiers that need to be added to a generated property.
+ ///
+ /// The for the field being updated.
+ /// The list of necessary modifiers for .
+ private static SyntaxTokenList GetPropertyModifiers(FieldDeclarationSyntax fieldDeclaration)
+ {
+ SyntaxTokenList propertyModifiers = TokenList(Token(SyntaxKind.PublicKeyword));
+
+ // Add the 'required' modifier if the field also had it
+ if (fieldDeclaration.Modifiers.Any(SyntaxKind.RequiredKeyword))
+ {
+ propertyModifiers = propertyModifiers.Add(Token(SyntaxKind.RequiredKeyword));
+ }
+
+ // Always add 'partial' last
+ return propertyModifiers.Add(Token(SyntaxKind.PartialKeyword));
+ }
}
+
+#endif
diff --git a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj
index f0246d204..44b02cfa8 100644
--- a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj
+++ b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj
@@ -69,7 +69,8 @@
-
+
+
@@ -120,9 +121,9 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4001.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4001.UnitTests.csproj b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4001.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4001.UnitTests.csproj
index e66b04fda..21b341ca9 100644
--- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4001.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4001.UnitTests.csproj
+++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4001.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4001.UnitTests.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests.csproj b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests.csproj
index 15077e2aa..83566c52d 100644
--- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests.csproj
+++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_UsePartialPropertyForObservablePropertyCodeFixer.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_UsePartialPropertyForObservablePropertyCodeFixer.cs
index 6ecdabb23..932c8096f 100644
--- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_UsePartialPropertyForObservablePropertyCodeFixer.cs
+++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_UsePartialPropertyForObservablePropertyCodeFixer.cs
@@ -650,4 +650,51 @@ partial class C : ObservableObject
await test.RunAsync();
}
+
+ // See https://github.com/CommunityToolkit/dotnet/issues/971
+ [TestMethod]
+ public async Task SimpleField_WithNoReferences_WithRequiredModifier()
+ {
+ string original = """
+ using CommunityToolkit.Mvvm.ComponentModel;
+
+ partial class C : ObservableObject
+ {
+ [ObservableProperty]
+ internal required string foo;
+ }
+ """;
+
+ string @fixed = """
+ using CommunityToolkit.Mvvm.ComponentModel;
+
+ partial class C : ObservableObject
+ {
+ [ObservableProperty]
+ public required partial string Foo { 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(5,6): info MVVMTK0042: The field C.C.foo using [ObservableProperty] can be converted to a partial property instead, which is recommended (doing so improves the developer experience and allows other generators and analyzers to correctly see the generated property as well)
+ CSharpCodeFixVerifier.Diagnostic().WithSpan(5, 6, 5, 24).WithArguments("C", "C.foo"),
+ });
+
+ test.FixedState.ExpectedDiagnostics.AddRange(new[]
+ {
+ // /0/Test0.cs(6,36): error CS9248: Partial property 'C.Foo' must have an implementation part.
+ DiagnosticResult.CompilerError("CS9248").WithSpan(6, 36, 6, 39).WithArguments("C.Foo"),
+ });
+
+ await test.RunAsync();
+ }
}