From 70e469101ee89fd55b33171008a0c6136732c124 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 13 Nov 2024 18:42:37 -0800 Subject: [PATCH 1/2] Remove leftover analyzer test code --- ...CSharpAnalyzerWithLanguageVersionTest{TAnalyzer}.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Helpers/CSharpAnalyzerWithLanguageVersionTest{TAnalyzer}.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Helpers/CSharpAnalyzerWithLanguageVersionTest{TAnalyzer}.cs index c44dbc154..14a15ee60 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Helpers/CSharpAnalyzerWithLanguageVersionTest{TAnalyzer}.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Helpers/CSharpAnalyzerWithLanguageVersionTest{TAnalyzer}.cs @@ -63,16 +63,6 @@ public static Task VerifyAnalyzerAsync(string source, LanguageVersion languageVe #endif test.TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(ObservableObject).Assembly.Location)); - test.SolutionTransforms.Add((solution, projectId) => - solution.AddAnalyzerConfigDocument(DocumentId.CreateNewId(projectId), - "UseMarshalType.editorconfig", - SourceText.From(""" - is_global = true - build_property.LibraryImportGenerator_UseMarshalType = true - """, - Encoding.UTF8), - filePath: "/UseMarshalType.editorconfig")); - test.ExpectedDiagnostics.AddRange(expected); return test.RunAsync(CancellationToken.None); From 1f88b1ed21cb310d62f62d05e7afc630a8675c3a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 13 Nov 2024 18:46:21 -0800 Subject: [PATCH 2/2] Skip MVVMTK0041 when properties are not partial --- ...resCSharpLanguageVersionPreviewAnalyzer.cs | 18 +++++- .../Test_SourceGeneratorsDiagnostics.cs | 55 ++++++++++++++++++- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/RequiresCSharpLanguageVersionPreviewAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/RequiresCSharpLanguageVersionPreviewAnalyzer.cs index 301b0e3f4..f0b5793b2 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/RequiresCSharpLanguageVersionPreviewAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/RequiresCSharpLanguageVersionPreviewAnalyzer.cs @@ -7,6 +7,8 @@ using System.Collections.Immutable; using CommunityToolkit.Mvvm.SourceGenerators.Extensions; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; @@ -44,11 +46,25 @@ public override void Initialize(AnalysisContext context) context.RegisterSymbolAction(context => { // We only want to target partial property definitions (also include non-partial ones for diagnostics) - if (context.Symbol is not IPropertySymbol { PartialDefinitionPart: null }) + if (context.Symbol is not IPropertySymbol { PartialDefinitionPart: null } partialProperty) { return; } + // Make sure to skip the warning if the property is not actually partial + if (partialProperty.DeclaringSyntaxReferences is [var syntaxReference]) + { + // Make sure we can find the syntax node, and that it's a property declaration + if (syntaxReference.GetSyntax(context.CancellationToken) is PropertyDeclarationSyntax propertyDeclarationSyntax) + { + // If the property is not partial, ignore it, as we'll already have a warning from the other analyzer here + if (!propertyDeclarationSyntax.Modifiers.Any(SyntaxKind.PartialKeyword)) + { + return; + } + } + } + // If the property is using [ObservableProperty], emit the diagnostic if (context.Symbol.TryGetAttributeWithType(observablePropertySymbol, out AttributeData? observablePropertyAttribute)) { diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_SourceGeneratorsDiagnostics.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_SourceGeneratorsDiagnostics.cs index 2fc686df1..66c6eea28 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_SourceGeneratorsDiagnostics.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4110.UnitTests/Test_SourceGeneratorsDiagnostics.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.SourceGenerators.UnitTests.Helpers; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Testing; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace CommunityToolkit.Mvvm.SourceGenerators.UnitTests; @@ -12,7 +13,7 @@ namespace CommunityToolkit.Mvvm.SourceGenerators.UnitTests; partial class Test_SourceGeneratorsDiagnostics { [TestMethod] - public async Task RequireCSharpLanguageVersionPreviewAnalyzer_LanguageVersionIsNotPreview_Warns() + public async Task RequireCSharpLanguageVersionPreviewAnalyzer_LanguageVersionIsNotPreview_DoesnNotWarn() { const string source = """ using CommunityToolkit.Mvvm.ComponentModel; @@ -21,7 +22,7 @@ namespace MyApp { public partial class SampleViewModel : ObservableObject { - [{|MVVMTK0041:ObservableProperty|}] + [ObservableProperty] public string Name { get; set; } } } @@ -30,6 +31,32 @@ public partial class SampleViewModel : ObservableObject await VerifyAnalyzerDiagnosticsAndSuccessfulGeneration(source, LanguageVersion.CSharp12); } + [TestMethod] + public async Task RequireCSharpLanguageVersionPreviewAnalyzer_LanguageVersionIsNotPreview_Partial_Warns() + { + const string source = """ + using CommunityToolkit.Mvvm.ComponentModel; + + namespace MyApp + { + public partial class SampleViewModel : ObservableObject + { + [{|MVVMTK0041:ObservableProperty|}] + public partial string Name { get; set; } + } + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.CSharp12, + + // /0/Test0.cs(8,31): error CS8703: The modifier 'partial' is not valid for this item in C# 12.0. Please use language version 'preview' or greater. + DiagnosticResult.CompilerError("CS8703").WithSpan(8, 31, 8, 35).WithArguments("partial", "12.0", "preview"), + // /0/Test0.cs(8,31): error CS9248: Partial property 'SampleViewModel.Name' must have an implementation part. + DiagnosticResult.CompilerError("CS9248").WithSpan(8, 31, 8, 35).WithArguments("MyApp.SampleViewModel.Name")); + } + [TestMethod] public async Task RequireCSharpLanguageVersionPreviewAnalyzer_LanguageVersionIsPreview_DoesNotWarn() { @@ -49,6 +76,30 @@ public partial class SampleViewModel : ObservableObject await VerifyAnalyzerDiagnosticsAndSuccessfulGeneration(source, languageVersion: LanguageVersion.Preview); } + [TestMethod] + public async Task RequireCSharpLanguageVersionPreviewAnalyzer_LanguageVersionIsPreview_Partial_DoesNotWarn() + { + const string source = """ + using CommunityToolkit.Mvvm.ComponentModel; + + namespace MyApp + { + public partial class SampleViewModel : ObservableObject + { + [ObservableProperty] + public partial string Name { get; set; } + } + } + """; + + await CSharpAnalyzerWithLanguageVersionTest.VerifyAnalyzerAsync( + source, + LanguageVersion.Preview, + + // /0/Test0.cs(8,31): error CS9248: Partial property 'SampleViewModel.Name' must have an implementation part. + DiagnosticResult.CompilerError("CS9248").WithSpan(8, 31, 8, 35).WithArguments("MyApp.SampleViewModel.Name")); + } + [TestMethod] public async Task UseObservablePropertyOnPartialPropertyAnalyzer_LanguageVersionIsNotPreview_DoesNotWarn() {