Skip to content

Commit 939e208

Browse files
Fix S1144 FN: Unused local functions (#9071)
1 parent d0e7cf8 commit 939e208

File tree

9 files changed

+27
-20
lines changed

9 files changed

+27
-20
lines changed

analyzers/src/SonarAnalyzer.CSharp/Rules/UnusedPrivateMember.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,14 +320,19 @@ public CSharpRemovableSymbolWalker(Func<SyntaxTree, bool, SemanticModel> getSema
320320
this.containingTypeAccessibility = containingTypeAccessibility;
321321
}
322322

323-
// This override is needed because VisitRecordDeclaration is not available due to the Roslyn version.
323+
// This override is needed because VisitRecordDeclaration and LocalFunctionStatementSyntax are not available due to the Roslyn version.
324324
public override void Visit(SyntaxNode node)
325325
{
326326
if (node.IsAnyKind(SyntaxKindEx.RecordClassDeclaration, SyntaxKindEx.RecordStructDeclaration))
327327
{
328328
VisitBaseTypeDeclaration(node);
329329
}
330330

331+
if (node.IsKind(SyntaxKindEx.LocalFunctionStatement))
332+
{
333+
ConditionalStore((IMethodSymbol)GetDeclaredSymbol(node), IsRemovableMethod);
334+
}
335+
331336
base.Visit(node);
332337
}
333338

@@ -488,7 +493,7 @@ static bool IsPartial(TypeDeclarationSyntax typeDeclaration) =>
488493

489494
private static bool IsRemovableMethod(IMethodSymbol methodSymbol) =>
490495
IsRemovableMember(methodSymbol)
491-
&& (methodSymbol.MethodKind == MethodKind.Ordinary || methodSymbol.MethodKind == MethodKind.Constructor)
496+
&& (methodSymbol.MethodKind is MethodKind.Ordinary or MethodKind.Constructor or MethodKindEx.LocalFunction)
492497
&& !methodSymbol.IsMainMethod()
493498
&& (!methodSymbol.IsEventHandler() || !IsDeclaredInPartialClass(methodSymbol)) // Event handlers could be added in XAML and no method reference will be generated in the .g.cs file.
494499
&& !methodSymbol.IsSerializationConstructor()

analyzers/src/SonarAnalyzer.Common/Helpers/SymbolHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ public static string GetClassification(this ISymbol symbol) =>
315315
{ MethodKind: MethodKind.Destructor } => "destructor",
316316
{ MethodKind: MethodKind.PropertyGet } => "getter",
317317
{ MethodKind: MethodKind.PropertySet } => "setter",
318+
{ MethodKind: MethodKindEx.LocalFunction } => "local function",
318319
_ => "method",
319320
},
320321
INamedTypeSymbol namedTypeSymbol => namedTypeSymbol switch

analyzers/tests/SonarAnalyzer.Test/Helpers/SymbolHelperTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ public void GetClassification_Record(TypeKind typeKind, string expected)
398398
[DataRow(MethodKind.ExplicitInterfaceImplementation, "method")]
399399
[DataRow(MethodKind.FunctionPointerSignature, "method")]
400400
[DataRow(MethodKind.LambdaMethod, "method")]
401-
[DataRow(MethodKind.LocalFunction, "method")]
401+
[DataRow(MethodKind.LocalFunction, "local function")]
402402
[DataRow(MethodKind.Ordinary, "method")]
403403
[DataRow(MethodKind.PropertyGet, "getter")]
404404
[DataRow(MethodKind.PropertySet, "setter")]

analyzers/tests/SonarAnalyzer.Test/TestCases/UnusedPrivateMember.CSharp10.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ static void Quix() { }
7070
[Obsolete]
7171
static void ForCoverage() { }
7272

73-
static void NoAttribute() { }
73+
static void NoAttribute() { } // Noncompliant
7474
}
7575
}
7676

analyzers/tests/SonarAnalyzer.Test/TestCases/UnusedPrivateMember.CSharp7.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,6 @@ private enum Y
198198
}
199199
}
200200

201-
// https://github.com/SonarSource/sonar-dotnet/issues/6699
202-
public class Repro_6699
203-
{
204-
public void MethodUsingLocalMethod()
205-
{
206-
void LocalMethod() { } // FN
207-
}
208-
}
209-
210201
// https://github.com/SonarSource/sonar-dotnet/issues/6724
211202
public class Repro_6724
212203
{

analyzers/tests/SonarAnalyzer.Test/TestCases/UnusedPrivateMember.CSharp9.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static void Quix() { }
6969
[Obsolete]
7070
static void ForCoverage() { }
7171

72-
static void NoAttribute() { }
72+
static void NoAttribute() { } // Noncompliant
7373
}
7474
}
7575

@@ -158,7 +158,7 @@ public bool MethodWithLocalFunction()
158158
{
159159
return false;
160160

161-
bool PrintMembers(StringBuilder builder) => true; // FN - local functions are not handled
161+
bool PrintMembers(StringBuilder builder) => true; // Noncompliant
162162
}
163163
}
164164

analyzers/tests/SonarAnalyzer.Test/TestCases/UnusedPrivateMember.Fixed.Batch.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,13 @@ private interface MyInterface
9494
void Method();
9595
}
9696

97+
// https://github.com/SonarSource/sonar-dotnet/issues/6699
9798
public void MethodUsingLocalMethod()
9899
{
99-
void LocalMethod() // FN: local function is never used
100-
{
100+
LocalMethod();
101101

102+
void LocalMethod()
103+
{
102104
}
103105
}
104106
}

analyzers/tests/SonarAnalyzer.Test/TestCases/UnusedPrivateMember.Fixed.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,13 @@ private int MyProperty
7878
[My]
7979
private class Class1 { }
8080

81+
// https://github.com/SonarSource/sonar-dotnet/issues/6699
8182
public void MethodUsingLocalMethod()
8283
{
83-
void LocalMethod() // FN: local function is never used
84-
{
84+
LocalMethod();
8585

86+
void LocalMethod()
87+
{
8688
}
8789
}
8890
}

analyzers/tests/SonarAnalyzer.Test/TestCases/UnusedPrivateMember.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,17 @@ internal class Class4 : MyInterface // Noncompliant {{Remove the unused internal
144144
public void Method() { }
145145
}
146146

147+
// https://github.com/SonarSource/sonar-dotnet/issues/6699
147148
public void MethodUsingLocalMethod()
148149
{
149-
void LocalMethod() // FN: local function is never used
150+
LocalMethod();
151+
152+
void LocalMethod()
150153
{
154+
}
151155

156+
void UnusedLocalMethod() // Noncompliant {{Remove the unused private local function 'UnusedLocalMethod'.}}
157+
{
152158
}
153159
}
154160
}

0 commit comments

Comments
 (0)