Do not report MA0152 when the inner await suppresses the exceptions - #1484
Merged
Merged
Conversation
`await (await outer).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing)` only suppresses the exceptions of the inner task: the outer await is outside the configured awaitable, so the failures of the outer task still propagate. The code fix moved the options onto the unwrapped task, and `Unwrap()` folds the faults of the outer task into it, so `SuppressThrowing` swallowed those too. Both versions compile, so the transformation silently changed the behavior. There is no `Unwrap()` form that preserves the semantics, so the fix is in the analyzer: the diagnostic is no longer reported when the `ConfigureAwaitOptions` argument is not a constant, or is a constant that has the `SuppressThrowing` bit set. The value of the flag is read from the enum member, and the check is inert on the target frameworks that do not have `ConfigureAwaitOptions`.
…ion-suppression-612aa8 # Conflicts: # tests/Meziantou.Analyzer.Test/Rules/UseTaskUnwrapAnalyzerTests.cs
This was referenced Sep 12, 2026
This was referenced Sep 17, 2026
Merged
Open
Open
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
MA0152 suggested replacing a double
awaitwithUnwrap(), and its code fix changed the behavior when the inner task was awaited withConfigureAwaitOptions.SuppressThrowing:The outer
awaitis outside the configured awaitable, so only the exceptions of the inner task are suppressed and the failure ofouterstill propagates: the snippet above returnscaught. The code fix rewrote it toawait outer.Unwrap().ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing), and sinceUnwrap()folds the faults of the outer task into the unwrapped one,SuppressThrowingswallows those too: the fixed code returnssuccess. Both versions compile, so the exception was silently lost.I confirmed the difference by executing both forms:
Fix
There is no
Unwrap()form that preserves the semantics here, so this is fixed in the analyzer rather than in the fixer — reporting a diagnostic whose only code fix is unsound would be worse than not reporting it.UseTaskUnwrapAnalyzernow bails out of theConfigureAwaitbranch when the options may containSuppressThrowing:SuppressThrowingbit, soConfigureAwait(false)andConfigureAwait(ForceYielding | ContinueOnCapturedContext)are unaffected;The value of
SuppressThrowingis read from theConstantValueof the enum member instead of being hardcoded, and the whole check is inert on the target frameworks that do not haveConfigureAwaitOptions.Notes for reviewers
Task<Task<T>>case is covered by the same guard.Task<TResult>.ConfigureAwaitrejectsSuppressThrowingat run time anyway, so this only makes the two branches consistent.docs/Rules/MA0152.mddocuments the case that is not reported.dotnet run --project src/DocumentationGeneratorexits 0 afterwards.Tests
Four tests added to
UseTaskUnwrapAnalyzerTests: the plainSuppressThrowingcase, a combined-flags case, a non-constant-options case, and a positiveForceYielding | ContinueOnCapturedContextcase that still reports and fixes.I checked the new tests are meaningful by neutralizing the guard: 3 of the 11 fail without it, all 11 pass with it.
11 tests passed, 0 failed, 0 skipped, on every Roslyn version (4.8, 4.14, 5.0, 5.6, 5.9). The full solution builds with 0 warnings and 0 errors. The whole test suite was not run locally; CI covers it.