@@ -767,4 +767,232 @@ public void M()
767767
768768 await test . RunAsync ( ) ;
769769 }
770+
771+ [ TestMethod ]
772+ public async Task SimpleFields_WithMultipleAttributes_SingleProperty ( )
773+ {
774+ string original = """
775+ using System;
776+ using CommunityToolkit.Mvvm.ComponentModel;
777+
778+ public partial class Class1 : ObservableObject
779+ {
780+ [ObservableProperty, NotifyPropertyChangedFor("Age")] private string name = String.Empty;
781+ }
782+ """ ;
783+
784+ string @fixed = """
785+ using System;
786+ using CommunityToolkit.Mvvm.ComponentModel;
787+
788+ public partial class Class1 : ObservableObject
789+ {
790+ [ObservableProperty, NotifyPropertyChangedFor("Age")]
791+ public partial string Name { get; set; } = String.Empty;
792+ }
793+ """ ;
794+
795+ CSharpCodeFixTest test = new ( LanguageVersion . Preview )
796+ {
797+ TestCode = original ,
798+ FixedCode = @fixed ,
799+ ReferenceAssemblies = ReferenceAssemblies . Net . Net80 ,
800+ } ;
801+
802+ test . TestState . AdditionalReferences . Add ( typeof ( ObservableObject ) . Assembly ) ;
803+ test . ExpectedDiagnostics . AddRange ( new [ ]
804+ {
805+ // /0/Test0.cs(6,74): info MVVMTK0042: The field Class1.name 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)
806+ CSharpCodeFixVerifier . Diagnostic ( ) . WithSpan ( 6 , 74 , 6 , 78 ) . WithArguments ( "Class1" , "name" ) ,
807+ } ) ;
808+
809+ test . FixedState . ExpectedDiagnostics . AddRange ( new [ ]
810+ {
811+ // /0/Test0.cs(7,27): error CS8050: Only auto-implemented properties, or properties that use the 'field' keyword, can have initializers.
812+ DiagnosticResult . CompilerError ( "CS8050" ) . WithSpan ( 7 , 27 , 7 , 31 ) ,
813+
814+ // /0/Test0.cs(7,27): error CS9248: Partial property 'Class1.Name' must have an implementation part.
815+ DiagnosticResult . CompilerError ( "CS9248" ) . WithSpan ( 7 , 27 , 7 , 31 ) . WithArguments ( "Class1.Name" ) ,
816+ } ) ;
817+
818+ await test . RunAsync ( ) ;
819+ }
820+
821+ // See https://github.com/CommunityToolkit/dotnet/issues/1007
822+ [ TestMethod ]
823+ public async Task SimpleFields_WithMultipleAttributes_WithNoBlankLines ( )
824+ {
825+ string original = """
826+ using System;
827+ using CommunityToolkit.Mvvm.ComponentModel;
828+
829+ public partial class Class1 : ObservableObject
830+ {
831+ [ObservableProperty, NotifyPropertyChangedFor("Age")] private string name = String.Empty;
832+ [ObservableProperty] private int age;
833+ }
834+ """ ;
835+
836+ string @fixed = """
837+ using System;
838+ using CommunityToolkit.Mvvm.ComponentModel;
839+
840+ public partial class Class1 : ObservableObject
841+ {
842+ [ObservableProperty, NotifyPropertyChangedFor("Age")]
843+ public partial string Name { get; set; } = String.Empty;
844+
845+ [ObservableProperty]
846+ public partial int Age { get; set; }
847+ }
848+ """ ;
849+
850+ CSharpCodeFixTest test = new ( LanguageVersion . Preview )
851+ {
852+ TestCode = original ,
853+ FixedCode = @fixed ,
854+ ReferenceAssemblies = ReferenceAssemblies . Net . Net80 ,
855+ } ;
856+
857+ test . TestState . AdditionalReferences . Add ( typeof ( ObservableObject ) . Assembly ) ;
858+ test . ExpectedDiagnostics . AddRange ( new [ ]
859+ {
860+ // /0/Test0.cs(6,74): info MVVMTK0042: The field Class1.name 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)
861+ CSharpCodeFixVerifier . Diagnostic ( ) . WithSpan ( 6 , 74 , 6 , 78 ) . WithArguments ( "Class1" , "name" ) ,
862+
863+ // /0/Test0.cs(7,38): info MVVMTK0042: The field Class1.age 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)
864+ CSharpCodeFixVerifier . Diagnostic ( ) . WithSpan ( 7 , 38 , 7 , 41 ) . WithArguments ( "Class1" , "age" ) ,
865+ } ) ;
866+
867+ test . FixedState . ExpectedDiagnostics . AddRange ( new [ ]
868+ {
869+ // /0/Test0.cs(7,27): error CS8050: Only auto-implemented properties, or properties that use the 'field' keyword, can have initializers.
870+ DiagnosticResult . CompilerError ( "CS8050" ) . WithSpan ( 7 , 27 , 7 , 31 ) ,
871+
872+ // /0/Test0.cs(7,27): error CS9248: Partial property 'Class1.Name' must have an implementation part.
873+ DiagnosticResult . CompilerError ( "CS9248" ) . WithSpan ( 7 , 27 , 7 , 31 ) . WithArguments ( "Class1.Name" ) ,
874+
875+ // /0/Test0.cs(10,24): error CS9248: Partial property 'Class1.Age' must have an implementation part.
876+ DiagnosticResult . CompilerError ( "CS9248" ) . WithSpan ( 10 , 24 , 10 , 27 ) . WithArguments ( "Class1.Age" ) ,
877+ } ) ;
878+
879+ await test . RunAsync ( ) ;
880+ }
881+
882+ [ TestMethod ]
883+ public async Task SimpleFields_WithMultipleAttributes_WithMixedBuckets_1 ( )
884+ {
885+ string original = """
886+ using System;
887+ using System.ComponentModel.DataAnnotations;
888+ using CommunityToolkit.Mvvm.ComponentModel;
889+
890+ public partial class Class1 : ObservableObject
891+ {
892+ // Leading trivia
893+ [ObservableProperty, NotifyPropertyChangedFor("A"), Display]
894+ [NotifyPropertyChangedFor("B")]
895+ private string _name;
896+ }
897+ """ ;
898+
899+ string @fixed = """
900+ using System;
901+ using System.ComponentModel.DataAnnotations;
902+ using CommunityToolkit.Mvvm.ComponentModel;
903+
904+ public partial class Class1 : ObservableObject
905+ {
906+ // Leading trivia
907+ [ObservableProperty, NotifyPropertyChangedFor("A"), Display]
908+ [NotifyPropertyChangedFor("B")]
909+ public partial string Name { get; set; }
910+ }
911+ """ ;
912+
913+ CSharpCodeFixTest test = new ( LanguageVersion . Preview )
914+ {
915+ TestCode = original ,
916+ FixedCode = @fixed ,
917+ ReferenceAssemblies = ReferenceAssemblies . Net . Net80 ,
918+ } ;
919+
920+ test . TestState . AdditionalReferences . Add ( typeof ( ObservableObject ) . Assembly ) ;
921+ test . ExpectedDiagnostics . AddRange ( new [ ]
922+ {
923+ // /0/Test0.cs(10,20): info MVVMTK0042: The field Class1._name 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)
924+ CSharpCodeFixVerifier . Diagnostic ( ) . WithSpan ( 10 , 20 , 10 , 25 ) . WithArguments ( "Class1" , "_name" ) ,
925+ } ) ;
926+
927+ test . FixedState . ExpectedDiagnostics . AddRange ( new [ ]
928+ {
929+ // /0/Test0.cs(10,27): error CS9248: Partial property 'Class1.Name' must have an implementation part.
930+ DiagnosticResult . CompilerError ( "CS9248" ) . WithSpan ( 10 , 27 , 10 , 31 ) . WithArguments ( "Class1.Name" ) ,
931+ } ) ;
932+
933+ await test . RunAsync ( ) ;
934+ }
935+
936+ [ TestMethod ]
937+ public async Task SimpleFields_WithMultipleAttributes_WithMixedBuckets_2 ( )
938+ {
939+ string original = """
940+ using System;
941+ using System.ComponentModel.DataAnnotations;
942+ using CommunityToolkit.Mvvm.ComponentModel;
943+
944+ public partial class Class1 : ObservableObject
945+ {
946+ // Leading trivia
947+ [NotifyPropertyChangedFor("B")]
948+ [ObservableProperty, NotifyPropertyChangedFor("A"), Display, Test]
949+ [NotifyPropertyChangedFor("C")]
950+ [property: UIHint("name"), Test]
951+ private string name;
952+ }
953+
954+ public class TestAttribute : Attribute;
955+ """ ;
956+
957+ string @fixed = """
958+ using System;
959+ using System.ComponentModel.DataAnnotations;
960+ using CommunityToolkit.Mvvm.ComponentModel;
961+
962+ public partial class Class1 : ObservableObject
963+ {
964+ // Leading trivia
965+ [NotifyPropertyChangedFor("B")]
966+ [ObservableProperty, NotifyPropertyChangedFor("A"), Display]
967+ [field: Test]
968+ [NotifyPropertyChangedFor("C")]
969+ [UIHint("name"), Test]
970+ public partial string Name { get; set; }
971+ }
972+
973+ public class TestAttribute : Attribute;
974+ """ ;
975+
976+ CSharpCodeFixTest test = new ( LanguageVersion . Preview )
977+ {
978+ TestCode = original ,
979+ FixedCode = @fixed ,
980+ ReferenceAssemblies = ReferenceAssemblies . Net . Net80 ,
981+ } ;
982+
983+ test . TestState . AdditionalReferences . Add ( typeof ( ObservableObject ) . Assembly ) ;
984+ test . ExpectedDiagnostics . AddRange ( new [ ]
985+ {
986+ // /0/Test0.cs(12,20): info MVVMTK0042: The field Class1.name 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)
987+ CSharpCodeFixVerifier . Diagnostic ( ) . WithSpan ( 12 , 20 , 12 , 24 ) . WithArguments ( "Class1" , "name" ) ,
988+ } ) ;
989+
990+ test . FixedState . ExpectedDiagnostics . AddRange ( new [ ]
991+ {
992+ // /0/Test0.cs(13,27): error CS9248: Partial property 'Class1.Name' must have an implementation part.
993+ DiagnosticResult . CompilerError ( "CS9248" ) . WithSpan ( 13 , 27 , 13 , 31 ) . WithArguments ( "Class1.Name" ) ,
994+ } ) ;
995+
996+ await test . RunAsync ( ) ;
997+ }
770998}
0 commit comments