Skip to content

Commit 766a0ba

Browse files
authored
Merge pull request #1000 from CommunityToolkit/dev/compilation-end-diagnostics
New info diagnostic for WinRT AOT compatibility when C# is not 'preview'
2 parents 60ff03e + b50a83a commit 766a0ba

File tree

4 files changed

+72
-2
lines changed

4 files changed

+72
-2
lines changed

src/CommunityToolkit.Mvvm.SourceGenerators/AnalyzerReleases.Shipped.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,4 @@ MVVMTK0047 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator
9292
MVVMTK0048 | CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0048
9393
MVVMTK0049 | CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0049
9494
MVVMTK0050 | CommunityToolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0050
95+
MVVMTK0051 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Info | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0050

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
using System.Collections.Immutable;
88
using System.Linq;
9+
using System.Threading;
910
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
1011
using Microsoft.CodeAnalysis;
1112
using Microsoft.CodeAnalysis.Diagnostics;
@@ -20,7 +21,9 @@ namespace CommunityToolkit.Mvvm.SourceGenerators;
2021
public sealed class WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer : DiagnosticAnalyzer
2122
{
2223
/// <inheritdoc/>
23-
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(WinRTObservablePropertyOnFieldsIsNotAotCompatible);
24+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
25+
WinRTObservablePropertyOnFieldsIsNotAotCompatible,
26+
WinRTObservablePropertyOnFieldsIsNotAotCompatibleCompilationEndInfo);
2427

2528
/// <inheritdoc/>
2629
public override void Initialize(AnalysisContext context)
@@ -42,6 +45,9 @@ public override void Initialize(AnalysisContext context)
4245
return;
4346
}
4447

48+
// Track whether we produced any diagnostics, for the compilation end scenario
49+
AttributeData? firstObservablePropertyAttribute = null;
50+
4551
context.RegisterSymbolAction(context =>
4652
{
4753
// Ensure we do have a valid field
@@ -51,7 +57,7 @@ public override void Initialize(AnalysisContext context)
5157
}
5258

5359
// Emit a diagnostic if the field is using the [ObservableProperty] attribute
54-
if (fieldSymbol.HasAttributeWithType(observablePropertySymbol))
60+
if (fieldSymbol.TryGetAttributeWithType(observablePropertySymbol, out AttributeData? observablePropertyAttribute))
5561
{
5662
context.ReportDiagnostic(Diagnostic.Create(
5763
WinRTObservablePropertyOnFieldsIsNotAotCompatible,
@@ -61,8 +67,32 @@ public override void Initialize(AnalysisContext context)
6167
.Add(FieldReferenceForObservablePropertyFieldAnalyzer.PropertyNameKey, ObservablePropertyGenerator.Execute.GetGeneratedPropertyName(fieldSymbol)),
6268
fieldSymbol.ContainingType,
6369
fieldSymbol.Name));
70+
71+
// Notify that we did produce at least one diagnostic. Note: callbacks can run in parallel, so the order
72+
// is not guaranteed. As such, there's no point in using an interlocked compare exchange operation here,
73+
// since we couldn't rely on the value being written actually being the "first" occurrence anyway.
74+
// So we can just do a normal volatile read for better performance.
75+
Volatile.Write(ref firstObservablePropertyAttribute, observablePropertyAttribute);
6476
}
6577
}, SymbolKind.Field);
78+
79+
// If C# preview is already in use, we can stop here. The last diagnostic is only needed when partial properties
80+
// cannot be used, to inform developers that they'll need to bump the language version to enable the code fixer.
81+
if (context.Compilation.IsLanguageVersionPreview())
82+
{
83+
return;
84+
}
85+
86+
context.RegisterCompilationEndAction(context =>
87+
{
88+
// If we have produced at least one diagnostic, also emit the info message
89+
if (Volatile.Read(ref firstObservablePropertyAttribute) is { } observablePropertyAttribute)
90+
{
91+
context.ReportDiagnostic(Diagnostic.Create(
92+
WinRTObservablePropertyOnFieldsIsNotAotCompatibleCompilationEndInfo,
93+
observablePropertyAttribute.GetLocation()));
94+
}
95+
});
6696
});
6797
}
6898
}

src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,4 +844,21 @@ internal static class DiagnosticDescriptors
844844
isEnabledByDefault: true,
845845
description: "Using the [ObservableObject] attribute on types is not AOT compatible in WinRT scenarios (such as UWP XAML and WinUI 3 apps), and they should derive from ObservableObject instead (as it allows the CsWinRT generators to correctly produce the necessary WinRT marshalling code).",
846846
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0050");
847+
848+
/// <summary>
849+
/// Gets a <see cref="DiagnosticDescriptor"/> for when <c>[ObservableProperty]</c> is used on a field in WinRT scenarios.
850+
/// <para>
851+
/// Format: <c>"This project produced one or more 'MVVMTK0045' warnings due to [ObservableProperty] being used on fields, which is not AOT compatible in WinRT scenarios, but it can't enable partial properties and the associated code fixer because 'LangVersion' is not set to 'preview' (setting 'LangVersion=preview' is required to use [ObservableProperty] on partial properties and address these warnings)"</c>.
852+
/// </para>
853+
/// </summary>
854+
public static readonly DiagnosticDescriptor WinRTObservablePropertyOnFieldsIsNotAotCompatibleCompilationEndInfo = new(
855+
id: "MVVMTK0051",
856+
title: "Using [ObservableProperty] with WinRT and AOT requires 'LangVersion=preview'",
857+
messageFormat: """This project produced one or more 'MVVMTK0045' warnings due to [ObservableProperty] being used on fields, which is not AOT compatible in WinRT scenarios, but it can't enable partial properties and the associated code fixer because 'LangVersion' is not set to 'preview' (setting 'LangVersion=preview' is required to use [ObservableProperty] on partial properties and address these warnings)""",
858+
category: typeof(ObservablePropertyGenerator).FullName,
859+
defaultSeverity: DiagnosticSeverity.Info,
860+
isEnabledByDefault: true,
861+
description: "Project producing one or more 'MVVMTK0045' warnings due to [ObservableProperty] being used on fields, which is not AOT compatible in WinRT scenarios, should set 'LangVersion' to 'preview' to enable partial properties and the associated code fixer because (setting 'LangVersion=preview' is required to use [ObservableProperty] on partial properties and address these warnings).",
862+
helpLinkUri: "https://aka.ms/mvvmtoolkit/errors/mvvmtk0051",
863+
customTags: WellKnownDiagnosticTags.CompilationEnd);
847864
}

tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_SourceGeneratorsDiagnostics.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,28 @@ await CSharpAnalyzerWithLanguageVersionTest<WinRTObservablePropertyOnFieldsIsNot
434434
editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true), ("CsWinRTAotOptimizerEnabled", "auto")]);
435435
}
436436

437+
[TestMethod]
438+
public async Task WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer_TargetingWindows_CsWinRTAotOptimizerEnabled_Auto_NotCSharpPreview_Warns_WithCompilationWarning()
439+
{
440+
const string source = """
441+
using CommunityToolkit.Mvvm.ComponentModel;
442+
443+
namespace MyApp
444+
{
445+
public partial class SampleViewModel : ObservableObject
446+
{
447+
[{|MVVMTK0051:ObservableProperty|}]
448+
private string {|MVVMTK0045:name|};
449+
}
450+
}
451+
""";
452+
453+
await CSharpAnalyzerWithLanguageVersionTest<WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer>.VerifyAnalyzerAsync(
454+
source,
455+
LanguageVersion.CSharp12,
456+
editorconfig: [("_MvvmToolkitIsUsingWindowsRuntimePack", true), ("CsWinRTAotOptimizerEnabled", "auto")]);
457+
}
458+
437459
[TestMethod]
438460
public async Task WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer_TargetingWindows_CsWinRTAotOptimizerEnabled_True_NoXaml_Level1_DoesNotWarn()
439461
{

0 commit comments

Comments
 (0)