diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs
index 2ba5d60c76a3e..d65a510d172c2 100644
--- a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs
+++ b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs
@@ -1503,6 +1503,36 @@ public C(int [|i|])
}");
}
+ [WorkItem(56317, "https://github.com/dotnet/roslyn/issues/56317")]
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedParameters)]
+ public async Task NotImplementedException_NoDiagnostic4()
+ {
+ await TestDiagnosticMissingAsync(
+@"using System;
+
+class C
+{
+ private int Goo(int [|i|])
+ => throw new NotImplementedException();
+}");
+ }
+
+ [WorkItem(56317, "https://github.com/dotnet/roslyn/issues/56317")]
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedParameters)]
+ public async Task NotImplementedException_NoDiagnostic5()
+ {
+ await TestDiagnosticMissingAsync(
+@"using System;
+
+class C
+{
+ private int Goo(int [|i|])
+ {
+ throw new NotImplementedException();
+ }
+}");
+ }
+
[WorkItem(41236, "https://github.com/dotnet/roslyn/issues/41236")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedParameters)]
public async Task NotImplementedException_MultipleStatements1()
diff --git a/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs
index f110f9e90fdb9..54e1f0deb591e 100644
--- a/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs
+++ b/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs
@@ -152,9 +152,18 @@ static bool IsSingleThrowNotImplementedOperation(IOperation firstBlock)
if (firstOp == null)
return false;
- // unwrap: { throw new NYI(); }
if (firstOp is IExpressionStatementOperation expressionStatement)
+ {
+ // unwrap: { throw new NYI(); }
firstOp = expressionStatement.Operation;
+ }
+ else if (firstOp is IReturnOperation returnOperation)
+ {
+ // unwrap: 'int M(int p) => throw new NYI();'
+ // For this case, the throw operation is wrapped within a conversion operation to 'int',
+ // which in turn is wrapped within a return operation.
+ firstOp = returnOperation.ReturnedValue.WalkDownConversion();
+ }
// => throw new NotImplementedOperation(...)
return IsThrowNotImplementedOperation(notImplementedExceptionType, firstOp);
@@ -177,7 +186,7 @@ static bool IsSingleThrowNotImplementedOperation(IOperation firstBlock)
return firstOp;
}
- static bool IsThrowNotImplementedOperation(INamedTypeSymbol notImplementedExceptionType, IOperation operation)
+ static bool IsThrowNotImplementedOperation(INamedTypeSymbol notImplementedExceptionType, IOperation? operation)
=> operation is IThrowOperation throwOperation &&
UnwrapImplicitConversion(throwOperation.Exception) is IObjectCreationOperation objectCreation &&
notImplementedExceptionType.Equals(objectCreation.Type);
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/OperationExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/OperationExtensions.cs
index 3bde34e3df8b5..b9027c0239b53 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/OperationExtensions.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/OperationExtensions.cs
@@ -370,5 +370,20 @@ public static bool IsNumericLiteral(this IOperation operation)
public static bool IsNullLiteral(this IOperation operand)
=> operand is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } };
+
+ ///
+ /// Walks down consecutive conversion operations until an operand is reached that isn't a conversion operation.
+ ///
+ /// The starting operation.
+ /// The inner non conversion operation or the starting operation if it wasn't a conversion operation.
+ public static IOperation? WalkDownConversion(this IOperation? operation)
+ {
+ while (operation is IConversionOperation conversionOperation)
+ {
+ operation = conversionOperation.Operand;
+ }
+
+ return operation;
+ }
}
}