Skip to content

Commit 52aec6f

Browse files
authored
Merge pull request #1025 from Youssef1313/patch-1
Support nested blocks in semi-auto property analyzer, add tests
2 parents eb25f10 + b3c0266 commit 52aec6f

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,16 @@ public override void Initialize(AnalysisContext context)
148148
// We expect a top-level block operation, that immediately returns an expression
149149
if (context.OperationBlocks is not [IBlockOperation { Operations: [IReturnOperation returnOperation] }])
150150
{
151-
return;
151+
// Special case for nested blocks (e.g. for code generated by some other tooling)
152+
if (context.OperationBlocks is [IBlockOperation { Operations: [IBlockOperation { Operations: [IReturnOperation nestedReturnOperation] }] }])
153+
{
154+
returnOperation = nestedReturnOperation;
155+
}
156+
else
157+
{
158+
// Invalid 'get' accessor structure, we have to stop here
159+
return;
160+
}
152161
}
153162

154163
// Next, we expect the return to produce a field reference

tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4120.UnitTests/Test_UseObservablePropertyOnSemiAutoPropertyCodeFixer.cs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,123 @@ public partial class SampleViewModel : ObservableObject
7474
await test.RunAsync();
7575
}
7676

77+
[TestMethod]
78+
public async Task SimplePropertyWithBlockAccessorSyntax()
79+
{
80+
string original = """
81+
using CommunityToolkit.Mvvm.ComponentModel;
82+
83+
namespace MyApp;
84+
85+
public class SampleViewModel : ObservableObject
86+
{
87+
public string Name
88+
{
89+
get
90+
{
91+
return field;
92+
}
93+
set
94+
{
95+
SetProperty(ref field, value);
96+
}
97+
}
98+
}
99+
""";
100+
101+
string @fixed = """
102+
using CommunityToolkit.Mvvm.ComponentModel;
103+
104+
namespace MyApp;
105+
106+
public partial class SampleViewModel : ObservableObject
107+
{
108+
[ObservableProperty]
109+
public partial string Name { get; set; }
110+
}
111+
""";
112+
113+
CSharpCodeFixTest test = new(LanguageVersion.Preview)
114+
{
115+
TestCode = original,
116+
FixedCode = @fixed,
117+
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
118+
};
119+
120+
test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly);
121+
test.ExpectedDiagnostics.AddRange(new[]
122+
{
123+
// /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)
124+
CSharpCodeFixVerifier.Diagnostic().WithSpan(7, 19, 7, 23).WithArguments("MyApp.SampleViewModel", "Name"),
125+
});
126+
127+
test.FixedState.ExpectedDiagnostics.AddRange(new[]
128+
{
129+
// /0/Test0.cs(8,27): error CS9248: Partial property 'SampleViewModel.Name' must have an implementation part.
130+
DiagnosticResult.CompilerError("CS9248").WithSpan(8, 27, 8, 31).WithArguments("MyApp.SampleViewModel.Name"),
131+
});
132+
133+
await test.RunAsync();
134+
}
135+
136+
[TestMethod]
137+
public async Task SimplePropertyWithNestedBlockSyntax()
138+
{
139+
string original = """
140+
using CommunityToolkit.Mvvm.ComponentModel;
141+
142+
namespace MyApp;
143+
144+
public class SampleViewModel : ObservableObject
145+
{
146+
public string Name
147+
{
148+
get
149+
{
150+
{
151+
return field;
152+
}
153+
}
154+
set => SetProperty(ref field, value);
155+
}
156+
}
157+
""";
158+
159+
string @fixed = """
160+
using CommunityToolkit.Mvvm.ComponentModel;
161+
162+
namespace MyApp;
163+
164+
public partial class SampleViewModel : ObservableObject
165+
{
166+
[ObservableProperty]
167+
public partial string Name { get; set; }
168+
}
169+
""";
170+
171+
CSharpCodeFixTest test = new(LanguageVersion.Preview)
172+
{
173+
TestCode = original,
174+
FixedCode = @fixed,
175+
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
176+
};
177+
178+
test.TestState.AdditionalReferences.Add(typeof(ObservableObject).Assembly);
179+
test.ExpectedDiagnostics.AddRange(new[]
180+
{
181+
// /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)
182+
CSharpCodeFixVerifier.Diagnostic().WithSpan(7, 19, 7, 23).WithArguments("MyApp.SampleViewModel", "Name"),
183+
});
184+
185+
test.FixedState.ExpectedDiagnostics.AddRange(new[]
186+
{
187+
// /0/Test0.cs(8,27): error CS9248: Partial property 'SampleViewModel.Name' must have an implementation part.
188+
DiagnosticResult.CompilerError("CS9248").WithSpan(8, 27, 8, 31).WithArguments("MyApp.SampleViewModel.Name"),
189+
});
190+
191+
await test.RunAsync();
192+
}
193+
77194
[TestMethod]
78195
public async Task SimpleProperty_WithSemicolonTokenGetAccessor()
79196
{

0 commit comments

Comments
 (0)