Skip to content

Commit dcee064

Browse files
Update docs
1 parent 201b234 commit dcee064

File tree

4 files changed

+678
-189
lines changed

4 files changed

+678
-189
lines changed

README.md

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,29 @@ Register in `.csproj`:
189189

190190
The analyzer offers the following automated code fixes:
191191

192-
-**Add `[Throws]` declaration** – Adds a `[Throws(typeof(...))]` attribute to declare the exception.
193-
- 🧯 **Surround with try/catch** – Wraps the statement in a `try` block with a generated `catch`.
194-
- 🧯 **Add catch to existing try block** – Appends a new `catch` clause to a nearby `try` block.
195-
- 🧹 **Remove redundant catch clause** – Removes the catch clause for an undeclared exception type.
196-
- 🪛 **Suppress warning** – Adds `#pragma warning disable` or `[SuppressMessage]`.
192+
***Add exception declaration**
193+
Adds `[Throws(typeof(...))]` attribute to declare the exception, or appends exception to existing attribute.
194+
*(Fixes `THROW001`)*
195+
196+
* 🧯 **Surround with try/catch**
197+
Wraps the throwing site in a `try`/`catch` block.
198+
*(Fixes `THROW001`)*
199+
200+
***Add catch to existing try block**
201+
Appends a new `catch` clause to an existing `try`.
202+
*(Fixes `THROW001`)*
203+
204+
* 🧹 **Remove redundant catch clause**
205+
Removes a catch clause for an exception type that is never thrown.
206+
*(Fixes `THROW009`)*
207+
208+
* 🔧 **Add `[Throws]` declaration from base member**
209+
Ensures overridden or implemented members declare the same exceptions as their base/interface.
210+
*(Fixes `THROW007`)*
211+
212+
* 🔧 **Add `[Throws]` declaration from XML doc**
213+
Converts `<exception>` XML documentation into `[Throws]` attributes.
214+
*(Fixes `THROW011`)*
197215

198216
---
199217

docs/analyzer-specification.md

Lines changed: 155 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
# Analyzer Specification
22

3+
## Overview
4+
5+
The **CheckedExceptionsAnalyzer** operates in two complementary layers:
6+
7+
1. **Exception Handling Analysis (core, always on)**
8+
9+
* Detects thrown exceptions from `throw` statements, `[Throws]` annotations, and `<exception>` XML docs.
10+
* Ensures each exception is either **caught** in a surrounding `try/catch` or **declared** with `[Throws]`.
11+
* Produces diagnostics such as **unhandled exceptions** (`THROW001`) and **bad practices** (e.g. `THROW004` for `throw new Exception()`).
12+
13+
2. **Control Flow Analysis (optional)**
14+
15+
* Performs lightweight reachability analysis to refine diagnostics.
16+
* Detects **redundant catch clauses** and **unreachable code** (with IDE gray‑out support).
17+
* Improves accuracy by reporting only exceptions that are truly reachable in context.
18+
* This analysis can be **disabled** in configuration if only the basic handling checks are desired.
19+
20+
---
21+
322
## `Throws` attribute
423

524
The `[Throws]` attribute is the contract that tells that a method, property accessor, local function, or lambda expression might throw one or more specified exceptions.
@@ -8,7 +27,7 @@ Placing a `Throws` declaration on a declaration will mark the specified exceptio
827

928
---
1029

11-
## Unhandled exceptions
30+
## Unhandled exceptions (Core analysis)
1231

1332
The diagnostic for *Unhandled exception types* is **`THROW001`**.
1433

@@ -36,7 +55,7 @@ public void TestMethod()
3655

3756
---
3857

39-
## Handling exceptions
58+
## Handling exceptions (Core analysis)
4059

4160
### Exception declarations (`[Throws]`)
4261

@@ -125,10 +144,12 @@ public void TestMethod() => throw new InvalidOperationException();
125144

126145
---
127146

128-
## Redundant or invalid handling
147+
## Redundant or invalid handling (Control flow analysis)
129148

130149
### Redundant catch clauses
131150

151+
Detected only with control flow analysis:
152+
132153
* **Typed catch never matched****`THROW009`**
133154

134155
```c#
@@ -147,6 +168,8 @@ public void TestMethod() => throw new InvalidOperationException();
147168

148169
### Redundant exception declarations
149170

171+
Control flow analysis is also used to determine whether declarations are truly necessary:
172+
150173
* **Declared but never thrown****`THROW012`**
151174

152175
```c#
@@ -158,33 +181,31 @@ public void TestMethod() => throw new InvalidOperationException();
158181

159182
* **Already covered by base type****`THROW008`**
160183

161-
Flow analysis is used to determine whether declared exceptions are necessary based on actual code paths.
162-
163-
### Invalid placement
184+
### Invalid placement (Core analysis)
164185

165186
* **Throws on full property (instead of accessor)****`THROW010`**
166187

167188
---
168189

169-
## Bad practices with `Exception`
190+
## Bad practices with `Exception` (Core analysis)
170191

171192
* **Throwing `System.Exception` directly****`THROW004`**
172193
* **Declaring `[Throws(typeof(Exception))]`****`THROW003`**
173194

174195
---
175196

176-
## Inheritance hierarchies
197+
## Inheritance hierarchies (Core analysis)
177198

178199
The analyzer ensures consistency across inheritance and interface implementations:
179200

180201
* **Missing exceptions from base/interface****`THROW007`**
181202
* **Declaring incompatible exceptions****`THROW006`**
182203

183-
Redundant handling is also reported when more general types make specific ones unnecessary (**`THROW008`**).
204+
Redundant handling is also reported when more general types make specific ones unnecessary (**`THROW008`**, via control flow analysis).
184205

185206
---
186207

187-
## Interop: XML documentation support
208+
## Interop: XML documentation support (Core analysis)
188209

189210
Exceptions from XML documentation are outwardly treated as *declared* by the consumer. This provides compatibility with the .NET class library and third‑party code.
190211

@@ -215,7 +236,94 @@ Disabling removes XML doc interop, including .NET class library coverage.
215236

216237
---
217238

218-
## Ignored exceptions
239+
### Property heuristics
240+
241+
When `<exception>` documentation is applied to a **property**, the analyzer uses text‑based heuristics to decide which accessor receives the implied `[Throws]`:
242+
243+
* If the text mentions **“get”**, **“gets”**, **“getting”**, or **“retrieved”**`[Throws]` is applied to the **getter**.
244+
* If the text mentions **“set”**, **“sets”**, or **“setting”**`[Throws]` is applied to the **setter**.
245+
* If **no keywords are found**:
246+
247+
* If the property has only a **getter** or only a **setter**`[Throws]` applies to that accessor.
248+
* If the property has both → the analyzer **defaults to the getter**.
249+
250+
#### Examples
251+
252+
**Setter keyword**
253+
254+
```c#
255+
/// <exception cref="InvalidOperationException">
256+
/// Thrown if the value cannot be **set**.
257+
/// </exception>
258+
public int Value { get; set; }
259+
260+
// Analyzer interprets this as:
261+
// [Throws(typeof(InvalidOperationException))]
262+
// set { ... }
263+
```
264+
265+
**Getter keyword**
266+
267+
```c#
268+
/// <exception cref="InvalidOperationException">
269+
/// Thrown if the value cannot be **retrieved**.
270+
/// </exception>
271+
public int Value { get; set; }
272+
273+
// Analyzer interprets this as:
274+
// [Throws(typeof(InvalidOperationException))]
275+
// get { ... }
276+
```
277+
278+
**No keyword, defaults to getter**
279+
280+
```c#
281+
/// <exception cref="InvalidOperationException">
282+
/// Thrown if the property is in an invalid state.
283+
/// </exception>
284+
public int Value { get; set; }
285+
286+
// Analyzer interprets this as:
287+
// [Throws(typeof(InvalidOperationException))]
288+
// get { ... }
289+
```
290+
291+
---
292+
293+
#### Expression‑bodied properties
294+
295+
Expression‑bodied properties (`=>`) are treated as a single accessor:
296+
297+
* If it has only a `get``[Throws]` applies to the getter.
298+
* If it has only a `set` (rare, but possible with `init` or `set =>`) → `[Throws]` applies to the setter.
299+
300+
Examples:
301+
302+
```c#
303+
/// <exception cref="InvalidOperationException">
304+
/// Thrown when retrieving the value.
305+
/// </exception>
306+
public int Value => throw new InvalidOperationException();
307+
308+
// Analyzer interprets this as:
309+
// [Throws(typeof(InvalidOperationException))]
310+
// get => ...
311+
```
312+
313+
```c#
314+
/// <exception cref="InvalidOperationException">
315+
/// Thrown when attempting to set.
316+
/// </exception>
317+
public int Value { set => throw new InvalidOperationException(); }
318+
319+
// Analyzer interprets this as:
320+
// [Throws(typeof(InvalidOperationException))]
321+
// set => ...
322+
```
323+
324+
---
325+
326+
## Ignored exceptions (Core analysis)
219327

220328
You can configure ignored exception types in `CheckedExceptions.settings.json`.
221329

@@ -225,7 +333,7 @@ Ignored exceptions will not produce *unhandled* diagnostics, but are still repor
225333

226334
---
227335

228-
## Casts and conversions
336+
## Casts and conversions (Core analysis, refined by control flow)
229337

230338
The analyzer inspects explicit and implicit **cast syntax** and accounts for possible exceptions raised by the runtime:
231339

@@ -250,13 +358,13 @@ checked
250358
}
251359
```
252360

253-
Flow analysis considers these conversions part of the potential throw set for a method or block.
361+
Control flow analysis ensures these conversions are only reported when reachable on actual paths.
254362

255363
---
256364

257365
## Special cases
258366

259-
### Expression-bodied property declarations
367+
### Expressionbodied property declarations (Core analysis)
260368

261369
Normally, `[Throws]` belongs on accessors:
262370

@@ -276,3 +384,36 @@ public int TestProp => throw new InvalidOperationException();
276384
```
277385

278386
This is treated as valid when there is only a `get` accessor.
387+
388+
> ℹ️ When exceptions are inferred from **XML documentation**, the same [property heuristics](#property-heuristics) apply to expression‑bodied properties: if only a `get` is present, exceptions are mapped to the getter; if only a `set` is present, exceptions are mapped to the setter.
389+
390+
---
391+
392+
## Configuration
393+
394+
Certain features can be toggled in `CheckedExceptions.settings.json`.
395+
396+
### Disable XML documentation interop
397+
398+
```json
399+
{
400+
"disableXmlDocInterop": true
401+
}
402+
```
403+
404+
Disables XML doc interop, including .NET class library coverage.
405+
406+
### Disable control flow analysis
407+
408+
```json
409+
{
410+
"disableControlFlowAnalysis": true
411+
}
412+
```
413+
414+
Disables the optional flow‑sensitive analysis.
415+
When set, the analyzer will still report **unhandled exceptions** and enforce `[Throws]` contracts, but will no longer:
416+
417+
* Detect redundant catch clauses (`THROW009`, `THROW013`)
418+
* Report redundant exception declarations (`THROW012`, `THROW008`)
419+
* Highlight unreachable code (IDE gray‑out support)

0 commit comments

Comments
 (0)