From ee5e84a75d1b7fcedf98cf0634382117441341bc Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 27 Jan 2026 16:16:22 -0800 Subject: [PATCH 1/2] Update analyzers for C# 14.0 language version checks Replaced preview language version checks with explicit C# 14.0 or greater checks using HasLanguageVersionAtLeastEqualTo when available. This ensures analyzers correctly handle new language versioning and maintain compatibility with Roslyn 5.0.0 or greater. --- .../UseObservablePropertyOnPartialPropertyAnalyzer.cs | 9 ++++++++- .../UseObservablePropertyOnSemiAutoPropertyAnalyzer.cs | 4 ++++ ...servablePropertyOnFieldsIsNotAotCompatibleAnalyzer.cs | 9 ++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UseObservablePropertyOnPartialPropertyAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UseObservablePropertyOnPartialPropertyAnalyzer.cs index bbda90e1b..f89cc4010 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UseObservablePropertyOnPartialPropertyAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UseObservablePropertyOnPartialPropertyAnalyzer.cs @@ -8,6 +8,9 @@ using System.Linq; using CommunityToolkit.Mvvm.SourceGenerators.Extensions; using Microsoft.CodeAnalysis; +#if ROSLYN_5_0_0_OR_GREATER +using Microsoft.CodeAnalysis.CSharp; +#endif using Microsoft.CodeAnalysis.Diagnostics; using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; @@ -30,9 +33,13 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(static context => { - // Using [ObservableProperty] on partial properties is only supported when using C# preview. + // Using [ObservableProperty] on partial properties is only supported without C# 14.0 or above. // As such, if that is not the case, return immediately, as no diagnostic should be produced. +#if ROSLYN_5_0_0_OR_GREATER + if (!context.Compilation.HasLanguageVersionAtLeastEqualTo(LanguageVersion.CSharp14)) +#else if (!context.Compilation.IsLanguageVersionPreview()) +#endif { return; } diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UseObservablePropertyOnSemiAutoPropertyAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UseObservablePropertyOnSemiAutoPropertyAnalyzer.cs index bcdacd2a8..e10640627 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UseObservablePropertyOnSemiAutoPropertyAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UseObservablePropertyOnSemiAutoPropertyAnalyzer.cs @@ -55,7 +55,11 @@ public override void Initialize(AnalysisContext context) { // Using [ObservableProperty] on partial properties is only supported when using C# preview. // As such, if that is not the case, return immediately, as no diagnostic should be produced. +#if ROSLYN_5_0_0_OR_GREATER + if (!context.Compilation.HasLanguageVersionAtLeastEqualTo(LanguageVersion.CSharp14)) +#else if (!context.Compilation.IsLanguageVersionPreview()) +#endif { return; } diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer.cs index 1a925e7a3..e825fd1f8 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer.cs @@ -11,6 +11,9 @@ using System.Threading; using CommunityToolkit.Mvvm.SourceGenerators.Extensions; using Microsoft.CodeAnalysis; +#if ROSLYN_5_0_0_OR_GREATER +using Microsoft.CodeAnalysis.CSharp; +#endif using Microsoft.CodeAnalysis.Diagnostics; using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; @@ -78,9 +81,13 @@ public override void Initialize(AnalysisContext context) } }, SymbolKind.Field); - // If C# preview is already in use, we can stop here. The last diagnostic is only needed when partial properties + // If C# is version 14.0 or above, we can stop here. The last diagnostic is only needed when partial properties // cannot be used, to inform developers that they'll need to bump the language version to enable the code fixer. +#if ROSLYN_5_0_0_OR_GREATER + if (context.Compilation.HasLanguageVersionAtLeastEqualTo(LanguageVersion.CSharp14)) +#else if (context.Compilation.IsLanguageVersionPreview()) +#endif { return; } From 4222078b2d8a98e4042337c120255917e49370d6 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 27 Jan 2026 16:23:21 -0800 Subject: [PATCH 2/2] Add analyzer tests for C# 14 language version Introduces new unit tests under the ROSLYN_5_0_0_OR_GREATER conditional to verify analyzer diagnostics and code generation behavior when using C# 14. These tests ensure correct warnings are produced for observable properties and WinRT compatibility in this language version. --- .../Test_SourceGeneratorsDiagnostics.cs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4120.UnitTests/Test_SourceGeneratorsDiagnostics.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4120.UnitTests/Test_SourceGeneratorsDiagnostics.cs index 28af166f7..12c0d1332 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4120.UnitTests/Test_SourceGeneratorsDiagnostics.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4120.UnitTests/Test_SourceGeneratorsDiagnostics.cs @@ -188,6 +188,27 @@ public partial class SampleViewModel : ObservableObject await VerifyAnalyzerDiagnosticsAndSuccessfulGeneration(source, LanguageVersion.Preview); } +#if ROSLYN_5_0_0_OR_GREATER + [TestMethod] + public async Task UseObservablePropertyOnPartialPropertyAnalyzer_LanguageVersionIsCSharp14_Warns() + { + const string source = """ + using CommunityToolkit.Mvvm.ComponentModel; + + namespace MyApp + { + public partial class SampleViewModel : ObservableObject + { + [ObservableProperty] + private string {|MVVMTK0042:name|}; + } + } + """; + + await VerifyAnalyzerDiagnosticsAndSuccessfulGeneration(source, LanguageVersion.CSharp14); + } +#endif + [TestMethod] public async Task UseObservablePropertyOnPartialPropertyAnalyzer_LanguageVersionIsPreview_OnPartialProperty_DoesNotWarn() { @@ -503,6 +524,30 @@ await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp14, + editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true), ("CsWinRTAotOptimizerEnabled", "auto")]); + } +#endif + [TestMethod] public async Task WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer_TargetingWindows_CsWinRTAotOptimizerEnabled_Auto_NotCSharpPreview_Warns_WithCompilationWarning() { @@ -1471,6 +1516,29 @@ public partial class SampleViewModel : ObservableObject await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, LanguageVersion.Preview); } +#if ROSLYN_5_0_0_OR_GREATER + [TestMethod] + public async Task UseObservablePropertyOnSemiAutoPropertyAnalyzer_ValidProperty_LanguageVersionIsCSharp14_Warns() + { + const string source = """ + using CommunityToolkit.Mvvm.ComponentModel; + + namespace MyApp; + + public partial class SampleViewModel : ObservableObject + { + public string {|MVVMTK0056:Name|} + { + get => field; + set => SetProperty(ref field, value); + } + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync(source, LanguageVersion.CSharp14); + } +#endif + [TestMethod] public async Task UseObservablePropertyOnSemiAutoPropertyAnalyzer_ValidProperty_WithModifiers_Warns() {