From ea2872fd2a889cd8d4a8802c2401874f8b9f7a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Wed, 6 Mar 2019 15:18:37 +0100 Subject: [PATCH] Fix S1144: False Positive when private member is only used in a generated file --- .../Rules/UnusedPrivateMember.cs | 13 +++++++++---- .../Rules/UnusedPrivateMemberTest.cs | 13 +++++++++++++ .../UnusedPrivateMember.CalledFromGenerated.cs | 15 +++++++++++++++ .../TestCases/UnusedPrivateMember.Generated.cs | 14 ++++++++++++++ 4 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/UnusedPrivateMember.CalledFromGenerated.cs create mode 100644 sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/UnusedPrivateMember.Generated.cs diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/UnusedPrivateMember.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/UnusedPrivateMember.cs index 19593ffe156..b4417469019 100644 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/UnusedPrivateMember.cs +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Rules/UnusedPrivateMember.cs @@ -85,7 +85,7 @@ protected override void Initialize(SonarAnalysisContext context) // Collect symbols of private members that could potentially be removed var removableSymbolsCollector = new CSharpRemovableSymbolWalker(c.Compilation.GetSemanticModel); - if (!VisitDeclaringReferences(namedType, removableSymbolsCollector, c.Compilation)) + if (!VisitDeclaringReferences(namedType, removableSymbolsCollector, c.Compilation, includeGeneratedFile: false)) { return; } @@ -100,7 +100,7 @@ protected override void Initialize(SonarAnalysisContext context) c.Compilation.GetSemanticModel, removableSymbolsCollector.PrivateSymbols.Select(s => s.Name).ToHashSet()); - if (!VisitDeclaringReferences(namedType, usageCollector, c.Compilation)) + if (!VisitDeclaringReferences(namedType, usageCollector, c.Compilation, includeGeneratedFile: true)) { return; } @@ -286,9 +286,14 @@ private static string GetMemberType(ISymbol symbol) } } - private static bool VisitDeclaringReferences(INamedTypeSymbol namedType, CSharpSyntaxWalker visitor, Compilation compilation) + private static bool VisitDeclaringReferences(INamedTypeSymbol namedType, CSharpSyntaxWalker visitor, Compilation compilation, + bool includeGeneratedFile) { - foreach (var reference in namedType.DeclaringSyntaxReferences.Where(r => !IsGenerated(r))) + var syntaxReferencesToVisit = includeGeneratedFile + ? namedType.DeclaringSyntaxReferences + : namedType.DeclaringSyntaxReferences.Where(r => !IsGenerated(r)); + + foreach (var reference in syntaxReferencesToVisit) { if (!visitor.SafeVisit(reference.GetSyntax())) { diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/UnusedPrivateMemberTest.cs b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/UnusedPrivateMemberTest.cs index 1bdf624b6d1..2dd748a4f0c 100644 --- a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/UnusedPrivateMemberTest.cs +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Rules/UnusedPrivateMemberTest.cs @@ -190,5 +190,18 @@ public void UnusedPrivateMember_CodeFix() new CS.UnusedPrivateMember(), new CS.UnusedPrivateMemberCodeFixProvider()); } + + [TestMethod] + [TestCategory("Rule")] + public void UnusedPrivateMember_UsedInGeneratedFile() + { + Verifier.VerifyAnalyzer( + new[] + { + @"TestCases\UnusedPrivateMember.CalledFromGenerated.cs", + @"TestCases\UnusedPrivateMember.Generated.cs" + }, + new CS.UnusedPrivateMember()); + } } } diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/UnusedPrivateMember.CalledFromGenerated.cs b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/UnusedPrivateMember.CalledFromGenerated.cs new file mode 100644 index 00000000000..9dd17999920 --- /dev/null +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/UnusedPrivateMember.CalledFromGenerated.cs @@ -0,0 +1,15 @@ +using System; + +namespace Tests.Diagnostics +{ + public partial class MyClass + { + private void PrivateMethodOnlyCalledFromGenerated() + { + } + + internal void InternalMethodOnlyCalledFromGenerated() + { + } + } +} diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/UnusedPrivateMember.Generated.cs b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/UnusedPrivateMember.Generated.cs new file mode 100644 index 00000000000..fea3a692cdf --- /dev/null +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/TestCases/UnusedPrivateMember.Generated.cs @@ -0,0 +1,14 @@ +// +using System; + +namespace Tests.Diagnostics +{ + public partial class MyClass + { + public void Foo() + { + PrivateMethodOnlyCalledFromGenerated(); + InternalMethodOnlyCalledFromGenerated(); + } + } +}