Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ public TestViewModel()
/// The partial property test.
/// </value>
[Reactive]
[field: JsonInclude]
public partial string? PartialPropertyTest { get; set; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ internal static void GetForwardedAttributes(
// Only look for attribute lists explicitly targeting the (generated) property. Roslyn will normally emit a
// CS0657 warning (invalid target), but that is automatically suppressed by a dedicated diagnostic suppressor
// that recognizes uses of this target specifically to support [ObservableAsProperty].
if (attributeList.Target?.Identifier is not SyntaxToken(SyntaxKind.PropertyKeyword))
if (attributeList.Target?.Identifier is not SyntaxToken(SyntaxKind.PropertyKeyword) && attributeList.Target?.Identifier is not SyntaxToken(SyntaxKind.FieldKeyword))
{
continue;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) 2024 .NET Foundation and Contributors. All rights reserved.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using ReactiveUI.SourceGenerators.Extensions;
using ReactiveUI.SourceGenerators.Helpers;
using static ReactiveUI.SourceGenerators.Diagnostics.SuppressionDescriptors;

namespace ReactiveUI.SourceGenerators.Diagnostics.Suppressions
{
/// <summary>
/// ReactiveCommand Attribute With Field Or Property Target Diagnostic Suppressor.
/// </summary>
/// <seealso cref="DiagnosticSuppressor" />
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ReactiveAttributeWithFieldTargetDiagnosticSuppressor : DiagnosticSuppressor
{
/// <inheritdoc/>
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(FieldOrPropertyAttributeListForReactiveProperty);

/// <inheritdoc/>
public override void ReportSuppressions(SuppressionAnalysisContext context)
{
foreach (var diagnostic in context.ReportedDiagnostics)
{
var syntaxNode = diagnostic.Location.SourceTree?.GetRoot(context.CancellationToken).FindNode(diagnostic.Location.SourceSpan);

// Check that the target is effectively [field:] or [property:] over a method declaration, which is the case we're looking for
if (syntaxNode is AttributeTargetSpecifierSyntax { Parent.Parent: PropertyDeclarationSyntax propertyDeclaration, Identifier: SyntaxToken(SyntaxKind.FieldKeyword) })
{
var semanticModel = context.GetSemanticModel(syntaxNode.SyntaxTree);

// Get the method symbol from the first variable declaration
ISymbol? declaredSymbol = semanticModel.GetDeclaredSymbol(propertyDeclaration, context.CancellationToken);

// Check if the method is using [Reactive], in which case we should suppress the warning
if (declaredSymbol is IPropertySymbol propertySymbol &&
semanticModel.Compilation.GetTypeByMetadataName(AttributeDefinitions.ReactiveAttributeType) is INamedTypeSymbol reactiveSymbol &&
propertySymbol.HasAttributeWithType(reactiveSymbol))
{
context.ReportSuppression(Suppression.Create(FieldOrPropertyAttributeListForReactiveProperty, diagnostic));
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,15 @@ public sealed partial class ReactiveGenerator
out var isReferenceTypeOrUnconstraindTypeParameter,
out var includeMemberNotNullOnSetAccessor);

ImmutableArray<string> forwardedAttributesString = [];
var propertyDeclaration = (PropertyDeclarationSyntax)context.TargetNode;

context.GetForwardedAttributes(
builder,
propertySymbol,
propertyDeclaration.AttributeLists,
token,
out var forwardedAttributesString);

token.ThrowIfCancellationRequested();

// Get the containing type info
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\OAPHMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ObservableAsPropertyAttributeWithFieldNeverReadDiagnosticSuppressor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ReactiveAttributeWithPropertyTargetDiagnosticSuppressor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ReactiveAttributeWithFieldTargetDiagnosticSuppressor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ReactiveCommandAttributeWithFieldOrPropertyTargetDiagnosticSuppressor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ReactiveCommandMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ReactiveFieldDoesNotNeedToBeReadOnlyDiagnosticSuppressor.cs" />
Expand Down
Loading