未验证 提交 ff8e1c75 编写于 作者: J Julien Couvreur 提交者: GitHub

Nullable analysis of yield return (#31072)

上级 c4598d72
......@@ -176,6 +176,11 @@ internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, D
}
private TypeSymbol GetIteratorElementTypeFromReturnType(RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
{
return GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, errorLocationNode, diagnostics).TypeSymbol;
}
internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(CSharpCompilation compilation, RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
{
if (refKind == RefKind.None && returnType.Kind == SymbolKind.NamedType)
{
......@@ -183,20 +188,25 @@ private TypeSymbol GetIteratorElementTypeFromReturnType(RefKind refKind, TypeSym
{
case SpecialType.System_Collections_IEnumerable:
case SpecialType.System_Collections_IEnumerator:
return GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationNode);
var objectType = compilation.GetSpecialType(SpecialType.System_Object);
if (diagnostics != null)
{
ReportUseSiteDiagnostics(objectType, diagnostics, errorLocationNode);
}
return TypeSymbolWithAnnotations.Create(objectType);
case SpecialType.System_Collections_Generic_IEnumerable_T:
case SpecialType.System_Collections_Generic_IEnumerator_T:
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0].TypeSymbol;
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0];
}
if (returnType.OriginalDefinition == Compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T))
if (returnType.OriginalDefinition == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T))
{
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0].TypeSymbol;
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0];
}
}
return null;
return default;
}
internal override void LookupSymbolsInSingleBinder(
......
......@@ -4592,6 +4592,19 @@ public override BoundNode VisitThrowExpression(BoundThrowExpression node)
return result;
}
public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement node)
{
BoundExpression expr = node.Expression;
if (expr == null)
{
return null;
}
var method = (MethodSymbol)_member;
TypeSymbolWithAnnotations elementType = InMethodBinder.GetIteratorElementTypeFromReturnType(compilation, RefKind.None, method.ReturnType.TypeSymbol, errorLocationNode: null, diagnostics: null);
VisitOptionalImplicitConversion(expr, elementType, useLegacyWarnings: false, AssignmentKind.Return);
return null;
}
#endregion Visitors
protected override string Dump(LocalState state)
......
......@@ -14,6 +14,7 @@
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics
{
[CompilerTrait(CompilerFeature.NullableReferenceTypes)]
public class NullableReferenceTypesTests : CSharpTestBase
{
[Fact]
......@@ -10300,6 +10301,29 @@ class CL1<T>
);
}
[Fact]
public void ReturningValues_BadValue()
{
CSharpCompilation c = CreateCompilation(new[] { @"
class C
{
string M()
{
return bad;
}
}
" }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (6,16): error CS0103: The name 'bad' does not exist in the current context
// return bad;
Diagnostic(ErrorCode.ERR_NameNotInContext, "bad").WithArguments("bad").WithLocation(6, 16),
// (6,16): warning CS8619: Nullability of reference types in value of type '?' doesn't match target type 'string'.
// return bad;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "bad").WithArguments("?", "string").WithLocation(6, 16)
);
}
[Fact]
public void IdentityConversion_Return_01()
{
......@@ -22634,6 +22658,8 @@ public void UnboundLambda_02()
static void F(object? x)
{
var z = y => y ?? x.ToString();
System.Func<object?, object> z2 = y => y ?? x.ToString();
System.Func<object?, object> z3 = y => null;
}
}";
var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
......@@ -22643,7 +22669,13 @@ static void F(object? x)
Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "z = y => y ?? x.ToString()").WithArguments("lambda expression").WithLocation(5, 13),
// (5,27): warning CS8602: Possible dereference of a null reference.
// var z = y => y ?? x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(5, 27));
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(5, 27),
// (6,53): warning CS8602: Possible dereference of a null reference.
// System.Func<object?, object> z2 = y => y ?? x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(6, 53),
// (7,48): warning CS8603: Possible null reference return.
// System.Func<object?, object> z3 = y => null;
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(7, 48));
}
/// <summary>
......@@ -28249,6 +28281,421 @@ class CL1 {}
);
}
[Fact]
public void ReturningValues_IEnumerableT()
{
var source = @"
public class C
{
System.Collections.Generic.IEnumerable<string> M()
{
return null; // 1
}
public System.Collections.Generic.IEnumerable<string>? M2()
{
return null;
}
System.Collections.Generic.IEnumerable<string> M3() => null; // 2
System.Collections.Generic.IEnumerable<string>? M4() => null;
}";
var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (6,16): warning CS8603: Possible null reference return.
// return null; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(6, 16),
// (12,60): warning CS8603: Possible null reference return.
// System.Collections.Generic.IEnumerable<string> M3() => null; // 2
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(12, 60)
);
var source2 = @"
class D
{
void M(C c)
{
c.M2() /*T:System.Collections.Generic.IEnumerable<string!>?*/ ;
}
}
";
var comp2 = CreateCompilation(source2, references: new[] { comp.EmitToImageReference() });
comp2.VerifyTypes();
}
[Fact]
public void Yield_IEnumerableT()
{
var source = @"
public class C
{
public System.Collections.Generic.IEnumerable<string> M()
{
yield return null; // 1
yield return """";
yield return null; // 2
yield break;
}
public System.Collections.Generic.IEnumerable<string?> M2()
{
yield return null;
}
}";
var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (6,22): warning CS8603: Possible null reference return.
// yield return null; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(6, 22),
// (8,22): warning CS8603: Possible null reference return.
// yield return null; // 2
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(8, 22)
);
var source2 = @"
class D
{
void M(C c)
{
c.M() /*T:System.Collections.Generic.IEnumerable<string>!*/ ;
c.M2() /*T:System.Collections.Generic.IEnumerable<string?>!*/ ;
}
}
";
var comp2 = CreateCompilation(source2, references: new[] { comp.EmitToImageReference() });
comp2.VerifyTypes();
}
[Fact]
public void Yield_IEnumerableT_LocalFunction()
{
var source = @"
class C
{
void Method()
{
_ = M();
_ = M2();
System.Collections.Generic.IEnumerable<string> M()
{
yield return null; // 1
yield return """";
yield return null; // 2
yield break;
}
System.Collections.Generic.IEnumerable<string?> M2()
{
yield return null;
}
}
}";
var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (11,26): warning CS8603: Possible null reference return.
// yield return null; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(11, 26),
// (13,26): warning CS8603: Possible null reference return.
// yield return null; // 2
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(13, 26)
);
}
[Fact]
public void Yield_IEnumerableT_GenericT()
{
var source = @"
class C
{
System.Collections.Generic.IEnumerable<T> M<T>()
{
yield return default; // 1
}
System.Collections.Generic.IEnumerable<T> M1<T>() where T : class
{
yield return default; // 2
}
System.Collections.Generic.IEnumerable<T> M2<T>() where T : class?
{
yield return default; // 3
}
System.Collections.Generic.IEnumerable<T?> M3<T>() where T : class
{
yield return default;
}
}";
CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics(
// (6,22): warning CS8603: Possible null reference return.
// yield return default; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(6, 22),
// (10,22): warning CS8603: Possible null reference return.
// yield return default; // 2
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(10, 22),
// (14,22): warning CS8603: Possible null reference return.
// yield return default; // 3
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(14, 22)
);
}
[Fact]
public void Yield_IEnumerableT_ErrorValue()
{
var source = @"
class C
{
System.Collections.Generic.IEnumerable<string> M()
{
yield return bad;
}
}";
CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics(
// (6,22): error CS0103: The name 'bad' does not exist in the current context
// yield return bad;
Diagnostic(ErrorCode.ERR_NameNotInContext, "bad").WithArguments("bad").WithLocation(6, 22),
// (6,22): warning CS8619: Nullability of reference types in value of type '?' doesn't match target type 'string'.
// yield return bad;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "bad").WithArguments("?", "string").WithLocation(6, 22)
);
}
[Fact]
public void Yield_IEnumerableT_ErrorValue2()
{
var source = @"
static class C
{
static System.Collections.Generic.IEnumerable<object> M(object? x)
{
yield return (C)x;
}
static System.Collections.Generic.IEnumerable<object?> M(object? y)
{
yield return (C?)y;
}
}";
CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics(
// (6,22): error CS0716: Cannot convert to static type 'C'
// yield return (C)x;
Diagnostic(ErrorCode.ERR_ConvertToStaticClass, "(C)x").WithArguments("C").WithLocation(6, 22),
// (6,22): warning CS8600: Converting null literal or possible null value to non-nullable type.
// yield return (C)x;
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(C)x").WithLocation(6, 22),
// (6,22): warning CS8603: Possible null reference return.
// yield return (C)x;
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "(C)x").WithLocation(6, 22),
// (8,60): error CS0111: Type 'C' already defines a member called 'M' with the same parameter types
// static System.Collections.Generic.IEnumerable<object?> M(object? y)
Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M").WithArguments("M", "C").WithLocation(8, 60),
// (10,22): error CS0716: Cannot convert to static type 'C'
// yield return (C?)y;
Diagnostic(ErrorCode.ERR_ConvertToStaticClass, "(C?)y").WithArguments("C").WithLocation(10, 22)
);
}
[Fact]
public void Yield_IEnumerableT_NoValue()
{
var source = @"
class C
{
System.Collections.Generic.IEnumerable<string> M()
{
yield return;
}
}";
CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics(
// (6,9): warning CS8619: Nullability of reference types in value of type '?' doesn't match target type 'string'.
// yield return;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "yield return;").WithArguments("?", "string").WithLocation(6, 9),
// (6,15): error CS1627: Expression expected after yield return
// yield return;
Diagnostic(ErrorCode.ERR_EmptyYield, "return").WithLocation(6, 15)
);
}
[Fact]
public void Yield_IEnumeratorT()
{
var source = @"
class C
{
System.Collections.Generic.IEnumerator<string> M()
{
yield return null; // 1
yield return """";
}
System.Collections.Generic.IEnumerator<string?> M2()
{
yield return null;
}
}";
CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics(
// (6,22): warning CS8603: Possible null reference return.
// yield return null; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(6, 22)
);
}
[Fact]
public void Yield_IEnumeratorT_LocalFunction()
{
var source = @"
class C
{
void Method()
{
_ = M();
_ = M2();
System.Collections.Generic.IEnumerator<string> M()
{
yield return null; // 1
yield return """";
}
System.Collections.Generic.IEnumerator<string?> M2()
{
yield return null;
}
}
}";
CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics(
// (11,26): warning CS8603: Possible null reference return.
// yield return null; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(11, 26)
);
}
[Fact]
public void Yield_IEnumerable()
{
var source = @"
class C
{
System.Collections.IEnumerable M()
{
yield return null;
}
}";
CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics();
}
[Fact]
public void Yield_IEnumerator()
{
var source = @"
class C
{
System.Collections.IEnumerator M()
{
yield return null;
yield return null;
}
}";
CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics();
}
[Fact, CompilerTrait(CompilerFeature.AsyncStreams)]
public void Yield_IAsyncEnumerable()
{
var source = @"
using System.Collections.Generic;
using System.Threading.Tasks;
class C
{
public static async IAsyncEnumerable<string> M()
{
yield return null; // 1
yield return null; // 2
await Task.Delay(1);
yield break;
}
public static async IAsyncEnumerable<string?> M2()
{
yield return null;
yield return null;
await Task.Delay(1);
}
void Method()
{
_ = local();
_ = local2();
async IAsyncEnumerable<string> local()
{
yield return null; // 3
await Task.Delay(1);
yield break;
}
async IAsyncEnumerable<string?> local2()
{
yield return null;
await Task.Delay(1);
}
}
}";
CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: WithNonNullTypesTrue()).VerifyDiagnostics(
// (8,22): warning CS8603: Possible null reference return.
// yield return null; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(8, 22),
// (9,22): warning CS8603: Possible null reference return.
// yield return null; // 2
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(9, 22),
// (26,26): warning CS8603: Possible null reference return.
// yield return null; // 3
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(26, 26)
);
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/31057"), CompilerTrait(CompilerFeature.AsyncStreams)]
public void Yield_IAsyncEnumerator()
{
var source = @"
using System.Collections.Generic;
using System.Threading.Tasks;
class C
{
async IAsyncEnumerator<string> M()
{
yield return null; // 1
yield return null; // 2
await Task.Delay(1);
yield break;
}
async IAsyncEnumerator<string?> M2()
{
yield return null;
yield return null;
await Task.Delay(1);
}
void Method()
{
_ = local();
_ = local2();
async IAsyncEnumerator<string> local()
{
yield return null; // 3
await Task.Delay(1);
}
async IAsyncEnumerator<string?> local2()
{
yield return null;
await Task.Delay(1);
yield break;
}
}
}";
CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: WithNonNullTypesTrue()).VerifyDiagnostics(
// (8,22): warning CS8603: Possible null reference return.
// yield return null; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(8, 22),
// (9,22): warning CS8603: Possible null reference return.
// yield return null; // 2
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(9, 22),
// (26,26): warning CS8603: Possible null reference return.
// yield return null; // 3
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(26, 26)
);
}
[Fact]
public void Await_01()
{
......@@ -108,6 +108,241 @@ public class AssertsFalseAttribute : Attribute
public AssertsFalseAttribute () { }
}
}
";
protected const string AsyncStreamsTypes = @"
namespace System.Collections.Generic
{
public interface IAsyncEnumerable<out T>
{
IAsyncEnumerator<T> GetAsyncEnumerator();
}
public interface IAsyncEnumerator<out T> : System.IAsyncDisposable
{
System.Threading.Tasks.ValueTask<bool> MoveNextAsync();
T Current { get; }
}
}
namespace System
{
public interface IAsyncDisposable
{
System.Threading.Tasks.ValueTask DisposeAsync();
}
}
#nullable disable
namespace System.Runtime.CompilerServices
{
public interface IStrongBox<T>
{
ref T Value { get; }
}
}
namespace System.Threading.Tasks
{
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks.Sources;
public struct ManualResetValueTaskSourceLogic<TResult>
{
private static readonly Action<object> s_sentinel = new Action<object>(s => throw new InvalidOperationException());
private readonly IStrongBox<ManualResetValueTaskSourceLogic<TResult>> _parent;
private Action<object> _continuation;
private object _continuationState;
private object _capturedContext;
private ExecutionContext _executionContext;
private bool _completed;
private TResult _result;
private ExceptionDispatchInfo _error;
private short _version;
public ManualResetValueTaskSourceLogic(IStrongBox<ManualResetValueTaskSourceLogic<TResult>> parent)
{
_parent = parent ?? throw new ArgumentNullException(nameof(parent));
_continuation = null;
_continuationState = null;
_capturedContext = null;
_executionContext = null;
_completed = false;
_result = default;
_error = null;
_version = 0;
}
public short Version => _version;
private void ValidateToken(short token)
{
if (token != _version)
{
throw new InvalidOperationException();
}
}
public ValueTaskSourceStatus GetStatus(short token)
{
ValidateToken(token);
return
!_completed ? ValueTaskSourceStatus.Pending :
_error == null ? ValueTaskSourceStatus.Succeeded :
_error.SourceException is OperationCanceledException ? ValueTaskSourceStatus.Canceled :
ValueTaskSourceStatus.Faulted;
}
public TResult GetResult(short token)
{
ValidateToken(token);
if (!_completed)
{
throw new InvalidOperationException();
}
_error?.Throw();
return _result;
}
public void Reset()
{
_version++;
_completed = false;
_continuation = null;
_continuationState = null;
_result = default;
_error = null;
_executionContext = null;
_capturedContext = null;
}
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
{
if (continuation == null)
{
throw new ArgumentNullException(nameof(continuation));
}
ValidateToken(token);
if ((flags & ValueTaskSourceOnCompletedFlags.FlowExecutionContext) != 0)
{
_executionContext = ExecutionContext.Capture();
}
if ((flags & ValueTaskSourceOnCompletedFlags.UseSchedulingContext) != 0)
{
SynchronizationContext sc = SynchronizationContext.Current;
if (sc != null && sc.GetType() != typeof(SynchronizationContext))
{
_capturedContext = sc;
}
else
{
TaskScheduler ts = TaskScheduler.Current;
if (ts != TaskScheduler.Default)
{
_capturedContext = ts;
}
}
}
_continuationState = state;
if (Interlocked.CompareExchange(ref _continuation, continuation, null) != null)
{
_executionContext = null;
object cc = _capturedContext;
_capturedContext = null;
switch (cc)
{
case null:
Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
break;
case SynchronizationContext sc:
sc.Post(s =>
{
var tuple = (Tuple<Action<object>, object>)s;
tuple.Item1(tuple.Item2);
}, Tuple.Create(continuation, state));
break;
case TaskScheduler ts:
Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, ts);
break;
}
}
}
public void SetResult(TResult result)
{
_result = result;
SignalCompletion();
}
public void SetException(Exception error)
{
_error = ExceptionDispatchInfo.Capture(error);
SignalCompletion();
}
private void SignalCompletion()
{
if (_completed)
{
throw new InvalidOperationException();
}
_completed = true;
if (Interlocked.CompareExchange(ref _continuation, s_sentinel, null) != null)
{
if (_executionContext != null)
{
ExecutionContext.Run(
_executionContext,
s => ((IStrongBox<ManualResetValueTaskSourceLogic<TResult>>)s).Value.InvokeContinuation(),
_parent ?? throw new InvalidOperationException());
}
else
{
InvokeContinuation();
}
}
}
private void InvokeContinuation()
{
object cc = _capturedContext;
_capturedContext = null;
switch (cc)
{
case null:
_continuation(_continuationState);
break;
case SynchronizationContext sc:
sc.Post(s =>
{
ref ManualResetValueTaskSourceLogic<TResult> logicRef = ref ((IStrongBox<ManualResetValueTaskSourceLogic<TResult>>)s).Value;
logicRef._continuation(logicRef._continuationState);
}, _parent ?? throw new InvalidOperationException());
break;
case TaskScheduler ts:
Task.Factory.StartNew(_continuation, _continuationState, CancellationToken.None, TaskCreationOptions.DenyChildAttach, ts);
break;
}
}
}
}
";
protected static CSharpCompilationOptions WithNonNullTypesTrue(CSharpCompilationOptions options = null)
......
......@@ -33,5 +33,6 @@ public enum CompilerFeature
StackAllocInitializer,
NullCoalescingAssignment,
AsyncStreams,
NullableReferenceTypes,
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册