diff --git a/ChangeLog.md b/ChangeLog.md index 0042734593..04625479eb 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix analyzer [RCS1124](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1124) ([PR](https://github.com/dotnet/roslynator/pull/1279)) - Fix analyzer [RCS0058](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0058) ([PR](https://github.com/dotnet/roslynator/pull/1281)) +- Fix analyzer [RCS1163](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1163) ([PR](https://github.com/dotnet/roslynator/pull/1280)) ## [4.6.4] - 2023-11-24 diff --git a/src/Analyzers/CSharp/Analysis/UnusedParameter/UnusedParameterAnalyzer.cs b/src/Analyzers/CSharp/Analysis/UnusedParameter/UnusedParameterAnalyzer.cs index 4419054879..b8e4ac1d07 100644 --- a/src/Analyzers/CSharp/Analysis/UnusedParameter/UnusedParameterAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/UnusedParameter/UnusedParameterAnalyzer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -14,6 +15,14 @@ namespace Roslynator.CSharp.Analysis.UnusedParameter; [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class UnusedParameterAnalyzer : BaseDiagnosticAnalyzer { + private static readonly MetadataNameSet _attributes = new(new[] + { + MetadataName.Parse("System.Runtime.Serialization.OnSerializedAttribute"), + MetadataName.Parse("System.Runtime.Serialization.OnDeserializedAttribute"), + MetadataName.Parse("System.Runtime.Serialization.OnSerializingAttribute"), + MetadataName.Parse("System.Runtime.Serialization.OnDeserializingAttribute"), + }); + private static ImmutableArray _supportedDiagnostics; public override ImmutableArray SupportedDiagnostics @@ -134,7 +143,7 @@ private static void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context) walker.SetValues(context.SemanticModel, context.CancellationToken); - FindUnusedNodes(parameterInfo, walker); + FindUnusedNodes(parameterInfo, walker, methodSymbol); if (walker.Nodes.Count > 0 && !MethodReferencedAsMethodGroupWalker.IsReferencedAsMethodGroup(methodDeclaration, methodSymbol, context.SemanticModel, context.CancellationToken)) @@ -331,19 +340,21 @@ private static void Analyze(SyntaxNodeAnalysisContext context, in ParameterInfo } } - private static void FindUnusedNodes(in ParameterInfo parameterInfo, UnusedParameterWalker walker) + private static void FindUnusedNodes(in ParameterInfo parameterInfo, UnusedParameterWalker walker, IMethodSymbol methodSymbol = null) { if (parameterInfo.Parameter is not null && !IsArgListOrDiscard(parameterInfo.Parameter)) { - walker.AddParameter(parameterInfo.Parameter); + AddParameter(walker, parameterInfo.Parameter, 0, methodSymbol); } else { - foreach (ParameterSyntax parameter in parameterInfo.Parameters) + for (int i = 0; i < parameterInfo.Parameters.Count; i++) { + ParameterSyntax parameter = parameterInfo.Parameters[i]; + if (!IsArgListOrDiscard(parameter)) - walker.AddParameter(parameter); + AddParameter(walker, parameter, i, methodSymbol); } } @@ -357,6 +368,26 @@ private static void FindUnusedNodes(in ParameterInfo parameterInfo, UnusedParame walker.Visit(parameterInfo.Node); } + private static void AddParameter(UnusedParameterWalker walker, ParameterSyntax parameter, int index, IMethodSymbol methodSymbol) + { + if (methodSymbol is not null) + { + if (index >= methodSymbol.Parameters.Length) + { + Debug.Fail($"{index} {methodSymbol.Parameters.Length}"); + return; + } + + if (methodSymbol.Parameters[index].Type.HasMetadataName(MetadataNames.System_Runtime_Serialization_StreamingContext) + && _attributes.ContainsAny(methodSymbol.GetAttributes())) + { + return; + } + } + + walker.AddParameter(parameter); + } + private static bool IsArgListOrDiscard(ParameterSyntax parameter) { return parameter.Identifier.IsKind(SyntaxKind.ArgListKeyword) diff --git a/src/Workspaces.Core/MetadataNameSet.cs b/src/Core/MetadataNameSet.cs similarity index 79% rename from src/Workspaces.Core/MetadataNameSet.cs rename to src/Core/MetadataNameSet.cs index 58238bbb44..3395905a93 100644 --- a/src/Workspaces.Core/MetadataNameSet.cs +++ b/src/Core/MetadataNameSet.cs @@ -44,4 +44,18 @@ public bool Contains(ISymbol symbol) return false; } + + public bool ContainsAny(ImmutableArray attributes) + { + foreach (AttributeData attributeData in attributes) + { + if (attributeData.AttributeClass is not null + && Contains(attributeData.AttributeClass)) + { + return true; + } + } + + return false; + } } diff --git a/src/Tests/Analyzers.Tests/RCS1163UnusedParameterTests.cs b/src/Tests/Analyzers.Tests/RCS1163UnusedParameterTests.cs index bd698d7ac6..f85aecd673 100644 --- a/src/Tests/Analyzers.Tests/RCS1163UnusedParameterTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1163UnusedParameterTests.cs @@ -234,6 +234,41 @@ namespace System.Windows public class DependencyPropertyChangedEventArgs { } +} + "); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UnusedParameter)] + public async Task TestNoDiagnostic_StreamingContextAttributes() + { + await VerifyNoDiagnosticAsync(@" +using System; +using System.Runtime.Serialization; + +class C +{ + [Obsolete] + [OnSerialized] + void M1(string p, StreamingContext context) + { + var x = p; + } + + [OnDeserialized] + void M2(StreamingContext context, string p) + { + var x = p; + } + + [OnSerializing] + void M3(StreamingContext context) + { + } + + [OnDeserializing] + void M4(StreamingContext context) + { + } } "); }