Skip to content

Fix S4456 FP: rule incorrectly triggers for async iterator methods returning IAsyncEnumerable #9787

@denisbredikhin

Description

@denisbredikhin

Description

Rule S4456 ("Parameter validation in yield methods") is incorrectly raised for async iterator methods returning IAsyncEnumerable<T> because the C# language does not allow applying the compliant fix recommended by the rule.

S4456 suggests splitting the method into:

  • an outer method performing parameter validation
  • an inner iterator method containing the yield

This pattern is not expressible for async iterators.
In C#, an async method that returns IAsyncEnumerable<T> must contain a yield return or yield break.
If such a method tries to return the result of another iterator, the compiler produces an error.

Therefore, when S4456 is raised on an async IAsyncEnumerable<T> method, the developer has no possible compliant fix, because C# prohibits the required refactoring.

Expected behavior

Rule S4456 should not be raised for methods returning:

async IAsyncEnumerable<T>

because the recommended fix cannot be implemented due to C# language restrictions.

Reproducer

Code that triggers S4456

public async IAsyncEnumerable<long> GetValuesAsync(int count)
{
    if (count <= 0)
        throw new ArgumentOutOfRangeException(nameof(count));

    await Task.Delay(10);
    yield return 1;
}

Sonar reports S4456 because parameter validation happens inside an iterator method.


Attempt to apply the compliant pattern (fails to compile)

public async IAsyncEnumerable<long> GetValuesAsync(int count)
{
    if (count <= 0)
        throw new ArgumentOutOfRangeException(nameof(count));

    return Iterator(count); 
    // ❌ Compiler error:
    // Cannot return a value from an iterator. Use the yield return statement to return a value.
}

private async IAsyncEnumerable<long> Iterator(int count)
{
    await Task.Delay(10);
    yield return 1;
}

This demonstrates that the compliant solution required by rule S4456 is not possible for async iterators.

Product and Version

SonarQube Cloud

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions