diff --git a/src/Microsoft.Unity.Analyzers.Tests/SerializeFieldSuppressorTests.cs b/src/Microsoft.Unity.Analyzers.Tests/SerializeFieldSuppressorTests.cs index a76d1db..e908d2c 100644 --- a/src/Microsoft.Unity.Analyzers.Tests/SerializeFieldSuppressorTests.cs +++ b/src/Microsoft.Unity.Analyzers.Tests/SerializeFieldSuppressorTests.cs @@ -110,6 +110,27 @@ class Camera : MonoBehaviour [SerializeField] string someField = ""default""; } +"; + var context = AnalyzerVerificationContext.Default + .WithAnalyzerOption("dotnet_style_readonly_field", "false"); + + var suppressor = ExpectSuppressor(SerializeFieldSuppressor.UnusedRule) + .WithLocation(7, 12); + + await VerifyCSharpDiagnosticAsync(context, test, suppressor); + } + + [Fact] + public async Task PrivateFieldWithCreateAttributeUnusedSuppressed() + { + const string test = @" +using UnityEngine; + +class Camera : MonoBehaviour +{ + [Unity.Properties.CreateProperty] + string someField = ""default""; +} "; var context = AnalyzerVerificationContext.Default .WithAnalyzerOption("dotnet_style_readonly_field", "false"); diff --git a/src/Microsoft.Unity.Analyzers/SerializeFieldSuppressor.cs b/src/Microsoft.Unity.Analyzers/SerializeFieldSuppressor.cs index 0cf67fe..a8e370f 100644 --- a/src/Microsoft.Unity.Analyzers/SerializeFieldSuppressor.cs +++ b/src/Microsoft.Unity.Analyzers/SerializeFieldSuppressor.cs @@ -3,12 +3,14 @@ * Licensed under the MIT License. See LICENSE in the project root for license information. *-------------------------------------------------------------------------------------------*/ +using System; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.Unity.Analyzers.Resources; +using Unity.Properties; using UnityEngine; namespace Microsoft.Unity.Analyzers; @@ -36,6 +38,8 @@ public class SerializeFieldSuppressor : DiagnosticSuppressor suppressedDiagnosticId: "CS0649", justification: Strings.NeverAssignedSerializeFieldSuppressorJustification); + private static readonly Type[] _suppressableAttributeTypes = [typeof(SerializeField), typeof(SerializeReference), typeof(CreatePropertyAttribute)]; + public override ImmutableArray SupportedSuppressions => ImmutableArray.Create(ReadonlyRule, UnusedRule, UnusedCodeQualityRule, NeverAssignedRule); public override void ReportSuppressions(SuppressionAnalysisContext context) @@ -46,12 +50,22 @@ public override void ReportSuppressions(SuppressionAnalysisContext context) } } + private static bool IsSuppressableAttribute(INamedTypeSymbol? symbol, Type type) + { + return symbol != null && symbol.Matches(type); + } + + private static bool IsSuppressableAttribute(INamedTypeSymbol? symbol) + { + return _suppressableAttributeTypes.Any(type => IsSuppressableAttribute(symbol, type)); + } + private static bool IsSuppressable(IFieldSymbol fieldSymbol) { - if (fieldSymbol.GetAttributes().Any(a => a.AttributeClass != null && (a.AttributeClass.Matches(typeof(SerializeField)) || a.AttributeClass.Matches(typeof(SerializeReference))))) + if (fieldSymbol.GetAttributes().Any(a => IsSuppressableAttribute(a.AttributeClass))) return true; - if (fieldSymbol.DeclaredAccessibility == Accessibility.Public && fieldSymbol.ContainingType.Extends(typeof(Object))) + if (fieldSymbol.DeclaredAccessibility == Accessibility.Public && fieldSymbol.ContainingType.Extends(typeof(UnityEngine.Object))) return true; return false; diff --git a/src/Microsoft.Unity.Analyzers/UnityStubs.cs b/src/Microsoft.Unity.Analyzers/UnityStubs.cs index fc05dba..7616693 100644 --- a/src/Microsoft.Unity.Analyzers/UnityStubs.cs +++ b/src/Microsoft.Unity.Analyzers/UnityStubs.cs @@ -796,6 +796,11 @@ struct TransformAccess } } +namespace Unity.Properties +{ + class CreatePropertyAttribute : System.Attribute { } +} + namespace JetBrains.Annotations { class UsedImplicitlyAttribute : Attribute { }