dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.01k stars 4.03k forks source link

Consider new warning for unobserved async-iterator call #33939

Open akarpov89 opened 5 years ago

akarpov89 commented 5 years ago

Version Used: 3.0.19.12206 (ec366687)

Steps to Reproduce: Compile the following code:

using System.Collections.Generic;
using System.Threading.Tasks;

class C
{
  async IAsyncEnumerable<int> ProduceAsync()
  {
    await Task.CompletedTask;
    yield return 42;
  }

  void Consume()
  {
    ProduceAsync(); // currently emits WRN_UnobservedAwaitableExpression
  }
}

Expected Behavior:

Emit new warning with text like "Because result of asynchronous iterator call is not being iterated, execution of the current method continues before the call is completed. Consider using the 'await foreach' over the result of the call." For async-iterators returning IAsyncEnumerator<T> the second sentence may be omitted. Or maybe you'll come up with better wording which will work for both cases :)

Actual Behavior:

The warning "CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call" is emitted. But it's somewhat misleading.

austindrenski commented 10 months ago

FYI this still happens today, and while it doesn't produce bad advice in most cases, there are some trivial examples where it would be nice if we were able to configure/suppress this specifically for async-iterators separate from normal other awaitable types.

One such example occurs when using a common pattern for guard expressions/statements in multi-targeted code bases:

{
  "sdk": {
    "version": "8.0.100"
  }
}
[DebuggerStepThrough]
static class Check
{
    [return: NotNull]
    public static T NotNull<T>([NotNull] T? value, [CallerArgumentExpression(nameof(value))] string? name = null)
    {
#if NET8_0_OR_GREATER
        ArgumentNullException.ThrowIfNull(value, name);
#else
        if (value is null)
            throw new ArgumentNullException(name);
#endif

        return value;
    }
}
Check.NotNull(default(IEnumerable<object>));

// CS4014: Because this call is not awaited, execution of the current method continues before the call is completed
Check.NotNull(default(IAsyncEnumerable<object>));