Skip to content

Commit 1eb2caf

Browse files
authored
Merge pull request #6304 from mavasani/FixThrowConversion
Handle conversion operation as operands of IThrowOperation
2 parents f67947a + 3b84a3d commit 1eb2caf

File tree

6 files changed

+46
-18
lines changed

6 files changed

+46
-18
lines changed

src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/QualityGuidelines/RethrowToPreserveStackDetails.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public override void Initialize(AnalysisContext context)
4040
{
4141
var throwOperation = (IThrowOperation)context.Operation;
4242

43-
if (throwOperation.Exception is not ILocalReferenceOperation localReference)
43+
if (throwOperation.GetThrownException() is not ILocalReferenceOperation localReference)
4444
{
4545
return;
4646
}

src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public bool IsSimpleAffirmativeCheck(IConditionalOperation conditional, [NotNull
139139
if (conditional.Condition is IPropertyReferenceOperation propertyReference &&
140140
SymbolEqualityComparer.Default.Equals(propertyReference.Property, IsCancellationRequestedProperty) &&
141141
whenTrueUnwrapped is IThrowOperation @throw &&
142-
@throw.Exception is IObjectCreationOperation objectCreation &&
142+
@throw.GetThrownException() is IObjectCreationOperation objectCreation &&
143143
IsDefaultOrTokenOperationCanceledExceptionCtor(objectCreation.Constructor))
144144
{
145145
isCancellationRequestedPropertyReference = propertyReference;
@@ -171,7 +171,7 @@ public bool IsNegatedCheckWithThrowingElseClause(IConditionalOperation condition
171171
unary.Operand is IPropertyReferenceOperation propertyReference &&
172172
SymbolEqualityComparer.Default.Equals(propertyReference.Property, IsCancellationRequestedProperty) &&
173173
whenFalseUnwrapped is IThrowOperation @throw &&
174-
@throw.Exception is IObjectCreationOperation objectCreation &&
174+
@throw.GetThrownException() is IObjectCreationOperation objectCreation &&
175175
IsDefaultOrTokenOperationCanceledExceptionCtor(objectCreation.Constructor))
176176
{
177177
isCancellationRequestedPropertyReference = propertyReference;

src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseExceptionThrowHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ aooreThrowIfGreaterThan is not null || aooreThrowIfGreaterThanOrEqual is not nul
146146
// any exceptions where a meaningful message may have been provided. This is an attempt to reduce
147147
// false positives, at the expense of potentially more false negatives in cases where a non-valuable
148148
// error message was used.
149-
if (throwOperation.Exception.WalkDownConversion() is not IObjectCreationOperation objectCreationOperation ||
149+
if (throwOperation.GetThrownException() is not IObjectCreationOperation objectCreationOperation ||
150150
HasPossiblyMeaningfulAdditionalArguments(objectCreationOperation))
151151
{
152152
return;

src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/QualityGuidelines/RethrowToPreserveStackDetailsTests.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,14 @@ public void M()
138138
");
139139
}
140140

141-
[Fact]
142-
public async Task CA2200_DiagnosticForThrowCaughtExceptionAsync()
141+
[Theory]
142+
[InlineData(CodeAnalysis.CSharp.LanguageVersion.CSharp7)]
143+
[InlineData(CodeAnalysis.CSharp.LanguageVersion.CSharp10)]
144+
public async Task CA2200_DiagnosticForThrowCaughtExceptionAsync(Microsoft.CodeAnalysis.CSharp.LanguageVersion languageVersion)
143145
{
144-
await VerifyCS.VerifyAnalyzerAsync(@"
146+
await new VerifyCS.Test
147+
{
148+
TestCode = @"
145149
using System;
146150
147151
class Program
@@ -162,8 +166,9 @@ void ThrowException()
162166
{
163167
throw new ArithmeticException();
164168
}
165-
}
166-
");
169+
}",
170+
LanguageVersion = languageVersion,
171+
}.RunAsync();
167172

168173
await VerifyVB.VerifyAnalyzerAsync(@"
169174
Imports System

src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests
1919
{
2020
public class UseCancellationTokenThrowIfCancellationRequestedTests
2121
{
22+
private static IEnumerable<string> LanguageVersionsToTest_CS
23+
{
24+
get
25+
{
26+
yield return CodeAnalysis.CSharp.LanguageVersion.CSharp7.ToString();
27+
yield return CodeAnalysis.CSharp.LanguageVersion.CSharp10.ToString();
28+
}
29+
}
30+
2231
private static IEnumerable<string> OperationCanceledExceptionCtors
2332
{
2433
get
@@ -46,27 +55,29 @@ static IEnumerable<string> ConditionalFormatStrings()
4655
}}";
4756
}
4857

49-
return CartesianProduct(OperationCanceledExceptionCtors, ConditionalFormatStrings());
58+
return CartesianProduct(OperationCanceledExceptionCtors, ConditionalFormatStrings(), LanguageVersionsToTest_CS);
5059
}
5160
}
5261

5362
[Theory]
5463
[MemberData(nameof(Data_SimpleAffirmativeCheck_ReportedAndFixed_CS))]
55-
public Task SimpleAffirmativeCheck_ReportedAndFixed_CSAsync(string operationCanceledExceptionCtor, string simpleConditionalFormatString)
64+
public Task SimpleAffirmativeCheck_ReportedAndFixed_CSAsync(string operationCanceledExceptionCtor, string simpleConditionalFormatString, string languageVersion)
5665
{
5766
string testStatements = Markup(
5867
FormatInvariant(
5968
simpleConditionalFormatString,
6069
@"token.IsCancellationRequested",
6170
$@"throw new {operationCanceledExceptionCtor};"), 0);
6271
string fixedStatements = @"token.ThrowIfCancellationRequested();";
72+
var parsedVersion = (CodeAnalysis.CSharp.LanguageVersion)Enum.Parse(typeof(CodeAnalysis.CSharp.LanguageVersion), languageVersion);
6373

6474
var test = new VerifyCS.Test
6575
{
6676
TestCode = CS.CreateBlock(testStatements),
6777
FixedCode = CS.CreateBlock(fixedStatements),
6878
ExpectedDiagnostics = { CS.DiagnosticAt(0) },
69-
ReferenceAssemblies = ReferenceAssemblies.Net.Net50
79+
ReferenceAssemblies = ReferenceAssemblies.Net.Net50,
80+
LanguageVersion = parsedVersion,
7081
};
7182
return test.RunAsync();
7283
}
@@ -149,7 +160,7 @@ static IEnumerable<string> ConditionalFormatStrings()
149160
{2}";
150161
}
151162

152-
return CartesianProduct(OperationCanceledExceptionCtors, ConditionalFormatStrings());
163+
return CartesianProduct(OperationCanceledExceptionCtors, ConditionalFormatStrings(), LanguageVersionsToTest_CS);
153164
}
154165
}
155166

@@ -290,8 +301,10 @@ public void M()
290301

291302
[Theory]
292303
[MemberData(nameof(Data_NegatedCheckWithElse_ReportedAndFixed_CS))]
293-
public Task NegatedCheckWithElse_ReportedAndFixed_CSAsync(string operationCanceledExceptionCtor, string conditionalFormatString)
304+
public Task NegatedCheckWithElse_ReportedAndFixed_CSAsync(string operationCanceledExceptionCtor, string conditionalFormatString, string languageVersion)
294305
{
306+
var parsedVersion = (CodeAnalysis.CSharp.LanguageVersion)Enum.Parse(typeof(CodeAnalysis.CSharp.LanguageVersion), languageVersion);
307+
295308
const string members = @"
296309
private CancellationToken token;
297310
private void DoSomething() { }";
@@ -311,7 +324,8 @@ public Task NegatedCheckWithElse_ReportedAndFixed_CSAsync(string operationCancel
311324
TestCode = CS.CreateBlock(testStatements, members),
312325
FixedCode = CS.CreateBlock(fixedStatements, members),
313326
ExpectedDiagnostics = { CS.DiagnosticAt(0) },
314-
ReferenceAssemblies = ReferenceAssemblies.Net.Net50
327+
ReferenceAssemblies = ReferenceAssemblies.Net.Net50,
328+
LanguageVersion = parsedVersion,
315329
};
316330
return test.RunAsync();
317331
}
@@ -676,6 +690,11 @@ private static IEnumerable<object[]> CartesianProduct(IEnumerable<object> left,
676690
return left.SelectMany(x => right.Select(y => new[] { x, y }));
677691
}
678692

693+
private static IEnumerable<object[]> CartesianProduct(IEnumerable<object> first, IEnumerable<object> second, IEnumerable<object> third)
694+
{
695+
return first.SelectMany(x => second.SelectMany(y => third.Select(z => new[] { x, y, z })));
696+
}
697+
679698
private static string FormatInvariant(string format, params object[] args) => string.Format(System.Globalization.CultureInfo.InvariantCulture, format, args);
680699
#endregion
681700
}

src/Utilities/Compiler/Extensions/IOperationExtensions.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -741,20 +741,24 @@ public static IOperation WalkDownConversion(this IOperation operation, Func<ICon
741741
return operation;
742742
}
743743

744-
public static ITypeSymbol? GetThrownExceptionType(this IThrowOperation operation)
744+
public static IOperation? GetThrownException(this IThrowOperation operation)
745745
{
746746
var thrownObject = operation.Exception;
747747

748748
// Starting C# 8.0, C# compiler wraps the thrown operation within an implicit conversion to System.Exception type.
749+
// We also want to walk down explicit conversions such as "throw (Exception)new ArgumentNullException())".
749750
if (thrownObject is IConversionOperation conversion &&
750-
conversion.IsImplicit)
751+
conversion.Conversion.Exists)
751752
{
752753
thrownObject = conversion.Operand;
753754
}
754755

755-
return thrownObject?.Type;
756+
return thrownObject;
756757
}
757758

759+
public static ITypeSymbol? GetThrownExceptionType(this IThrowOperation operation)
760+
=> operation.GetThrownException()?.Type;
761+
758762
/// <summary>
759763
/// Determines if the one of the invocation's arguments' values is an argument of the specified type, and if so, find
760764
/// the first one.

0 commit comments

Comments
 (0)