Skip to content

CMTK0003 misses async assertions inside rethrowing catches #166

Closed

Description

@CaffeinatedCoder

Problem

CMTK0003 treats every catch surrounding an awaited assertion in an async lambda as if it observed the failure. A catch that unconditionally rethrows, cannot catch the assertion exception, or has an always-false filter does not observe it; the outer dropped task remains the only place that failure is visible.

Reproduced at release tip 9878a6cb7500dbf7d3e1a199c3b9446d7e5cc278 with the real analyzer and Testing assemblies through Roslyn Compilation.WithAnalyzers, without compiler errors:

public static void Check(Task<Option<int>> pending)
{
    Task.WhenAll(new[] { pending }.Select(async item =>
    {
        try { await item.ShouldBeNone(); }
        catch { throw; }
    }));
}

No CMTK0003 is produced. Replacing the catch with catch (OperationCanceledException) { } or catch (Exception) when (false) { } also gives no diagnostic. All three leave a TypeKitAssertionException unobserved when the pending value is Some. A runtime check confirmed the rethrowing lambda faults its outer WhenAll task with TypeKitAssertionException; dropping that task lets the test pass.

Controls: a directly awaited assertion in a lambda without a catch (including a try/finally) is diagnosed when the outer WhenAll task is dropped. A catch that actually swallows the failure is correctly silent.

Cause

IgnoredOutcomeAnalyzer.EnclosingAsyncFunction immediately returns null for any ITryOperation with a nonempty Catches list, without considering whether those catches handle the failure or rethrow it. HandsOn therefore loses the assertion's failure path before it reaches the async lambda and task combinator.

Expected

Do not treat a rethrow-only catch or a catch that cannot handle the assertion's failure as observing it. Extend the async-lambda axis of DroppedTaskShapes with no catch, try/finally, swallowing catch, rethrow/log-and-rethrow, unrelated exception type and filtered catch controls. Keep awaited/held/returned/explicit-discard uses silent where appropriate.

Found in the second pre-release review, in the async-lambda reach added with #157/#164. Decide and fix before the tag under decision 119.

Activity

  1. added this to the 2.3.0 milestone on Oct 29, 2026
  2. added
    bugSomething isn't working
    silentCompiles or answers and does less than it appears to
    severity/mediumInconsistency or failure in a narrow scenario
    package/analyzersAffects CodoMetis.TypeKit.Analyzers
    package/testingAffects CodoMetis.TypeKit.Testing
    on Oct 29, 2026
  3. CaffeinatedCoder commented on Oct 29, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Fixed in #172 (into release/2.3.0, commit f636a06). A catch now stops the failure only when the await is in its try block (not a handler or finally), its type is TypeKitAssertionException or a base of it (resolved in the Testing assembly; a look-alike of that name elsewhere doesn't count), and its handler can complete by Roslyn control flow (end point reachable, or a return/break leaves it). catch { throw; }, log-and-throw;, throw new …, catch (OperationCanceledException) and when (false) (handler unreachable) now report. Decision: a filter that is no constant (when (Expected(e))) counts as keeping the failure, since an Error reports only a failure lost whatever the filter answers; written in design/analyzers.md and the analyzer README. DroppedTaskShapes gained every catch kind and the silent controls (catch all, catch the assertion's exception, true/opaque filter, conditional rethrow, return, a later clause, nested tries).

  4. CaffeinatedCoder commented on Oct 29, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Fixed by #172, merged into release/2.3.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/option-resultOption, Result and their extensionsbugSomething isn't workingpackage/analyzersAffects CodoMetis.TypeKit.Analyzerspackage/testingAffects CodoMetis.TypeKit.Testingseverity/mediumInconsistency or failure in a narrow scenariosilentCompiles or answers and does less than it appears to

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions