Skip to content

Make MVC API analyzers detect undocumented status codes in conditional returns#66775

Open
KitKeen wants to merge 4 commits into
dotnet:mainfrom
KitKeen:feat/api-analyzer-conditional-returns
Open

Make MVC API analyzers detect undocumented status codes in conditional returns#66775
KitKeen wants to merge 4 commits into
dotnet:mainfrom
KitKeen:feat/api-analyzer-conditional-returns

Conversation

@KitKeen
Copy link
Copy Markdown

@KitKeen KitKeen commented May 21, 2026

Fixes #33105.

Background

API1000/API1001 already trigger on plain returns:

public ActionResult<object> OopsUndocumentedIf(int id)
{
    if (id == 0)
    {
        return NotFound(); // API1000 ✅
    }

    return Ok(new object());
}

…but the equivalent ternary form silently passes:

public ActionResult<object> OopsUndocumentedConditional(int id)
{
    return id == 0 ? NotFound() : Ok(new object()); // no diagnostic ❌
}

@pranavkm previously confirmed in the issue thread that the IOperation rewrite in #34020 did not add conditional handling and that a PR extending it would be welcome.

Change

ActualApiResponseMetadataFactory.InspectReturnOperation is taught to recurse into both branches of an IConditionalOperation (the operation that represents the C# ternary cond ? a : b). Each branch produces its own ActualApiResponseMetadata entry, mirroring the existing handling of ISwitchExpressionOperation.

if (returnedValue is IConditionalOperation { WhenFalse: { } whenFalse } conditional)
{
    var metadata = new List<ActualApiResponseMetadata?>();
    metadata.AddRange(InspectReturnOperation(symbolCache, returnOperation, overrideReturnedValue: conditional.WhenTrue));
    metadata.AddRange(InspectReturnOperation(symbolCache, returnOperation, overrideReturnedValue: whenFalse));
    return metadata.ToArray();
}

To support recursion without piggy-backing on the ISwitchExpressionArmOperation parameter that previously did this job, InspectReturnOperation gains an IOperation? overrideReturnedValue parameter. It is null for the existing call sites, so their behavior is unchanged.

The WhenFalse: { } whenFalse guard makes the new branch a no-op when the conditional is an if statement (where WhenFalse can be null) — only ternary expressions used as a return value reach this code.

Nested conditionals fall out of the recursion naturally — a x ? a : (y ? b : c) return produces three entries, one per leaf.

Tests

  • ActualApiResponseMetadataFactoryTest: two new unit tests — InspectReturnExpression_ReadsBothBranchesOfConditional and InspectReturnExpression_ReadsNestedConditional — backed by a new fixture file TestFiles/.../InspectReturnExpressionTestsForConditionalExpression.cs that mirrors the existing switch fixture.
  • ApiConventionAnalyzerIntegrationTest: new end-to-end test DiagnosticsAreReturned_ForActionsReturnedFromConditionalExpression that proves API1000 now fires on the ternary form shown in the issue.

Out of scope

Issue #33091 (expression-bodied methods) is referenced by the issue body but tracked separately. Expression-bodied methods already flow through GetReturnStatements via the implicit IReturnOperation Roslyn emits, so the new conditional handling will also kick in for => cond ? a : b bodies as a side effect, but adding a dedicated test for that case would require a different syntax-lookup helper and is left for #33091.

@KitKeen KitKeen requested a review from a team as a code owner May 21, 2026 12:18
@github-actions github-actions Bot added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label May 21, 2026
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label May 21, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Thanks for your PR, @KitKeen. Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

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

Labels

area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Analyzers API1000 and API1001 should trigger on conditional returns

1 participant