Skip to content

Commit 2899b38

Browse files
Handle LINQ enumeration exceptions within try-catch analysis (#306)
* Handle LINQ enumeration exceptions within try-catch analysis * Update tests and CHANGELOG.md
1 parent 6b1ae1a commit 2899b38

5 files changed

Lines changed: 77 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
## Unreleased
1010

11+
### Fixed
12+
13+
- PR [#306](https://github.com/marinasundstrom/CheckedExceptions/pull/306) Account for LINQ pipeline exceptions when analyzing foreach statements inside try-catch blocks
14+
1115
## [2.5.0] - 2025-09-05
1216

1317
### Added

CheckedExceptions.Tests/CheckedExceptionsAnalyzerTests.Linq.cs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,45 @@ await Verifier.VerifyAnalyzerAsync(test, setup: o =>
158158
o.ExpectedDiagnostics.AddRange(expected, expected2, expected3, expected4);
159159
o.DisabledDiagnostics.Remove(CheckedExceptionsAnalyzer.DiagnosticIdRedundantExceptionDeclaration);
160160
}, executable: true);
161-
}
162-
163-
[Fact]
164-
public async Task PassDelegateByVariable()
165-
{
161+
}
162+
163+
[Fact]
164+
public async Task ForEachCaughtByCatchAll()
165+
{
166+
var test = /* lang=c#-test */ """
167+
#nullable enable
168+
using System;
169+
using System.Collections.Generic;
170+
using System.Linq;
171+
172+
IEnumerable<int> items = [];
173+
var query = items.Where(x => int.Parse("10") == x);
174+
try
175+
{
176+
foreach (var item in query) { }
177+
}
178+
catch
179+
{
180+
}
181+
""";
182+
183+
var expected = Verifier.Diagnostic(CheckedExceptionsAnalyzer.DiagnosticIdImplicitlyDeclaredException)
184+
.WithArguments("FormatException")
185+
.WithSpan(7, 34, 7, 45);
186+
187+
var expected2 = Verifier.Diagnostic(CheckedExceptionsAnalyzer.DiagnosticIdImplicitlyDeclaredException)
188+
.WithArguments("OverflowException")
189+
.WithSpan(7, 34, 7, 45);
190+
191+
await Verifier.VerifyAnalyzerAsync(test, setup: o =>
192+
{
193+
o.ExpectedDiagnostics.AddRange(expected, expected2);
194+
}, executable: true);
195+
}
196+
197+
[Fact]
198+
public async Task PassDelegateByVariable()
199+
{
166200
var test = /* lang=c#-test */ """
167201
#nullable enable
168202
using System;

CheckedExceptions/CheckedExceptionsAnalyzer.Analysis.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,13 @@ private static void CollectExceptionsFromExpression(SyntaxNode expression, Compi
376376
}
377377
}
378378
}
379+
380+
// Capture exceptions from deferred enumerable pipelines directly referenced by the expression itself
381+
var exprOp = semanticModel.GetOperation(expression);
382+
if (exprOp is not null)
383+
{
384+
CollectEnumerationExceptions(exprOp, exceptions, compilation, semanticModel, settings, default);
385+
}
379386
}
380387

381388
private static HashSet<INamedTypeSymbol>? GetCaughtExceptions(SyntaxList<CatchClauseSyntax> catchClauses, SemanticModel semanticModel)

Test/CheckedExceptions.settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"defaultExceptionClassification": "NonStrict",
2+
"defaultExceptionClassification": "Strict",
33
"exceptions": {
44
"System.NotImplementedException": "Informational",
55
"System.IO.IOException": "Informational",

Test/LinqTest.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,41 @@ public void Test1()
2222
private static void ExplicitlyDeclaredThrows()
2323
{
2424
IEnumerable<int> items = [];
25-
var query = items.Where([Throws(typeof(FormatException), typeof(OverflowException))] (x) => x == int.Parse("10"));
25+
var query = items.Where((x) => x == int.Parse("10"));
2626
var x = query;
2727
var r = x.Select((z) => 2);
2828

2929
foreach (var n in query) { }
3030

31+
try
32+
{
33+
foreach (var n in query) { }
34+
}
35+
catch (OverflowException exc)
36+
{
37+
38+
}
39+
catch
40+
{
41+
42+
}
43+
3144
foreach (var item in r)
3245
{
3346

3447
}
48+
49+
try
50+
{
51+
foreach (var item in r)
52+
{
53+
54+
}
55+
}
56+
catch
57+
{
58+
59+
}
3560
}
3661

3762
private static void ImplicitlyDeclaredThrows()

0 commit comments

Comments
 (0)