未验证 提交 944ec4f7 编写于 作者: C Charles Stoner 提交者: GitHub

Set nullability of foreach iteration variable (#26660)

上级 c112e519
......@@ -16,7 +16,7 @@ internal sealed class ForEachEnumeratorInfo
// Types identified by the algorithm in the spec (8.8.4).
public readonly TypeSymbol CollectionType;
// public readonly TypeSymbol EnumeratorType; // redundant - return type of GetEnumeratorMethod
public readonly TypeSymbol ElementType;
public readonly TypeSymbolWithAnnotations ElementType;
// Members required by the "pattern" based approach. Also populated for other approaches.
public readonly MethodSymbol GetEnumeratorMethod;
......@@ -37,7 +37,7 @@ internal sealed class ForEachEnumeratorInfo
private ForEachEnumeratorInfo(
TypeSymbol collectionType,
TypeSymbol elementType,
TypeSymbolWithAnnotations elementType,
MethodSymbol getEnumeratorMethod,
MethodSymbol currentPropertyGetter,
MethodSymbol moveNextMethod,
......@@ -69,7 +69,7 @@ internal sealed class ForEachEnumeratorInfo
internal struct Builder
{
public TypeSymbol CollectionType;
public TypeSymbol ElementType;
public TypeSymbolWithAnnotations ElementType;
public MethodSymbol GetEnumeratorMethod;
public MethodSymbol CurrentPropertyGetter;
......
......@@ -164,13 +164,13 @@ internal override BoundStatement BindForEachDeconstruction(DiagnosticBag diagnos
BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindValue(_syntax.Expression, diagnostics, BindValueKind.RValue);
ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder();
TypeSymbol inferredType;
TypeSymbolWithAnnotations inferredType;
bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType);
ExpressionSyntax variables = ((ForEachVariableStatementSyntax)_syntax).Variable;
// Tracking narrowest safe-to-escape scope by default, the proper val escape will be set when doing full binding of the foreach statement
var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, this.LocalScopeDepth, inferredType ?? CreateErrorType("var"));
var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, this.LocalScopeDepth, inferredType?.TypeSymbol ?? CreateErrorType("var"));
DeclarationExpressionSyntax declaration = null;
ExpressionSyntax expression = null;
......@@ -192,7 +192,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindValue(_syntax.Expression, diagnostics, BindValueKind.RValue);
ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder();
TypeSymbol inferredType;
TypeSymbolWithAnnotations inferredType;
bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType);
// These should only occur when special types are missing or malformed.
......@@ -226,18 +226,18 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
if (isVar)
{
iterationVariableType = inferredType ?? CreateErrorType("var");
declType = inferredType ?? TypeSymbolWithAnnotations.Create(CreateErrorType("var"), isNullableIfReferenceType: null);
}
else
{
Debug.Assert((object)declType != null);
iterationVariableType = declType.TypeSymbol;
}
iterationVariableType = declType.TypeSymbol;
boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType);
SourceLocalSymbol local = this.IterationVariable;
local.SetTypeSymbol(TypeSymbolWithAnnotations.Create(iterationVariableType));
local.SetTypeSymbol(declType);
local.SetValEscape(collectionEscape);
if (local.RefKind != RefKind.None)
......@@ -280,7 +280,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
case SyntaxKind.ForEachVariableStatement:
{
var node = (ForEachVariableStatementSyntax)_syntax;
iterationVariableType = inferredType ?? CreateErrorType("var");
iterationVariableType = inferredType?.TypeSymbol ?? CreateErrorType("var");
var variables = node.Variable;
if (variables.IsDeconstructionLeft())
......@@ -370,18 +370,18 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
// but it turns out that these are equivalent (when both are available).
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
Conversion elementConversion = this.Conversions.ClassifyConversionFromType(inferredType, iterationVariableType, ref useSiteDiagnostics, forCast: true);
Conversion elementConversion = this.Conversions.ClassifyConversionFromType(inferredType.TypeSymbol, iterationVariableType, ref useSiteDiagnostics, forCast: true);
if (!elementConversion.IsValid)
{
ImmutableArray<MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions;
if (originalUserDefinedConversions.Length > 1)
{
diagnostics.Add(ErrorCode.ERR_AmbigUDConv, foreachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType);
diagnostics.Add(ErrorCode.ERR_AmbigUDConv, foreachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType.TypeSymbol, iterationVariableType);
}
else
{
SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType);
SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType.TypeSymbol, iterationVariableType);
diagnostics.Add(ErrorCode.ERR_NoExplicitConv, foreachKeyword.GetLocation(), distinguisher.First, distinguisher.Second);
}
hasErrors = true;
......@@ -395,7 +395,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
// If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression)
// to the System.Collections.IEnumerable interface (§6.1.8).
builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics);
builder.CurrentConversion = this.Conversions.ClassifyConversionFromType(builder.CurrentPropertyGetter.ReturnType.TypeSymbol, builder.ElementType, ref useSiteDiagnostics);
builder.CurrentConversion = this.Conversions.ClassifyConversionFromType(builder.CurrentPropertyGetter.ReturnType.TypeSymbol, builder.ElementType.TypeSymbol, ref useSiteDiagnostics);
var getEnumeratorType = builder.GetEnumeratorMethod.ReturnType.TypeSymbol;
// we never convert struct enumerators to object - it is done only for null-checks.
......@@ -419,7 +419,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
Debug.Assert(builder.CollectionConversion.IsValid);
Debug.Assert(builder.CurrentConversion.IsValid ||
(builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) ||
(builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray()));
(builder.ElementType.IsNullableType() && builder.ElementType.TypeSymbol.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray()));
Debug.Assert(builder.EnumeratorConversion.IsValid ||
this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error ||
!useSiteDiagnostics.IsNullOrEmpty(),
......@@ -460,18 +460,18 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
hasErrors);
}
internal TypeSymbol InferCollectionElementType(DiagnosticBag diagnostics, ExpressionSyntax collectionSyntax)
internal TypeSymbolWithAnnotations InferCollectionElementType(DiagnosticBag diagnostics, ExpressionSyntax collectionSyntax)
{
// Use the right binder to avoid seeing iteration variable
BoundExpression collectionExpr = this.GetBinder(collectionSyntax).BindValue(collectionSyntax, diagnostics, BindValueKind.RValue);
ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder();
TypeSymbol inferredType;
TypeSymbolWithAnnotations inferredType;
GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType);
return inferredType;
}
private bool GetEnumeratorInfoAndInferCollectionElementType(ref ForEachEnumeratorInfo.Builder builder, ref BoundExpression collectionExpr, DiagnosticBag diagnostics, out TypeSymbol inferredType)
private bool GetEnumeratorInfoAndInferCollectionElementType(ref ForEachEnumeratorInfo.Builder builder, ref BoundExpression collectionExpr, DiagnosticBag diagnostics, out TypeSymbolWithAnnotations inferredType)
{
UnwrapCollectionExpressionIfNullable(ref collectionExpr, diagnostics);
......@@ -484,7 +484,7 @@ private bool GetEnumeratorInfoAndInferCollectionElementType(ref ForEachEnumerato
else if (collectionExpr.HasDynamicType())
{
// If the enumerator is dynamic, it yields dynamic values
inferredType = DynamicTypeSymbol.Instance;
inferredType = TypeSymbolWithAnnotations.Create(DynamicTypeSymbol.Instance, isNullableIfReferenceType: null);
}
else if (collectionExpr.Type.SpecialType == SpecialType.System_String && builder.CollectionType.SpecialType == SpecialType.System_Collections_IEnumerable)
{
......@@ -492,7 +492,7 @@ private bool GetEnumeratorInfoAndInferCollectionElementType(ref ForEachEnumerato
// over the string's Chars indexer. Therefore, we should infer "char", regardless of what the spec
// indicates the element type is. This actually matters in practice because the System.String in
// the portable library doesn't have a pattern GetEnumerator method or implement IEnumerable<char>.
inferredType = GetSpecialType(SpecialType.System_Char, diagnostics, collectionExpr.Syntax);
inferredType = TypeSymbolWithAnnotations.Create(GetSpecialType(SpecialType.System_Char, diagnostics, collectionExpr.Syntax), isNullableIfReferenceType: null);
}
else
{
......@@ -610,7 +610,7 @@ private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundE
if (SatisfiesForEachPattern(ref builder, diagnostics))
{
builder.ElementType = GetTypeOrReturnTypeWithAdjustedNullableAnnotations((PropertySymbol)builder.CurrentPropertyGetter.AssociatedSymbol).TypeSymbol;
builder.ElementType = GetTypeOrReturnTypeWithAdjustedNullableAnnotations((PropertySymbol)builder.CurrentPropertyGetter.AssociatedSymbol);
// NOTE: if IDisposable is not available at all, no diagnostics will be reported - we will just assume that
// the enumerator is not disposable. If it has IDisposable in its interface list, there will be a diagnostic there.
......@@ -660,7 +660,7 @@ private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundE
{
// If the type is generic, we have to search for the methods
Debug.Assert(collectionType.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T);
builder.ElementType = collectionType.TypeArgumentsNoUseSiteDiagnostics.Single().TypeSymbol;
builder.ElementType = collectionType.TypeArgumentsNoUseSiteDiagnostics.Single();
MethodSymbol getEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator, diagnostics, errorLocationSyntax);
if ((object)getEnumeratorMethod != null)
......@@ -682,11 +682,13 @@ private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundE
{
// Non-generic - use special members to avoid re-computing
Debug.Assert(collectionType.SpecialType == SpecialType.System_Collections_IEnumerable);
builder.ElementType = GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationSyntax);
builder.GetEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator, diagnostics, errorLocationSyntax);
builder.CurrentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__get_Current, diagnostics, errorLocationSyntax);
builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, errorLocationSyntax);
builder.ElementType = TypeSymbolWithAnnotations.Create(
GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationSyntax),
isNullableIfReferenceType: builder.CurrentPropertyGetter?.ReturnType.IsNullable);
Debug.Assert((object)builder.GetEnumeratorMethod == null ||
builder.GetEnumeratorMethod.ReturnType.SpecialType == SpecialType.System_Collections_IEnumerator);
......@@ -722,15 +724,17 @@ private ForEachEnumeratorInfo.Builder GetDefaultEnumeratorInfo(ForEachEnumerator
if (collectionExprType.IsDynamic())
{
builder.ElementType = ((_syntax as ForEachStatementSyntax)?.Type.IsVar == true) ?
(TypeSymbol)DynamicTypeSymbol.Instance :
GetSpecialType(SpecialType.System_Object, diagnostics, _syntax);
builder.ElementType = TypeSymbolWithAnnotations.Create(
((_syntax as ForEachStatementSyntax)?.Type.IsVar == true) ?
(TypeSymbol)DynamicTypeSymbol.Instance :
GetSpecialType(SpecialType.System_Object, diagnostics, _syntax),
isNullableIfReferenceType: null);
}
else
{
builder.ElementType = collectionExprType.SpecialType == SpecialType.System_String ?
GetSpecialType(SpecialType.System_Char, diagnostics, _syntax) :
((ArrayTypeSymbol)collectionExprType).ElementType.TypeSymbol;
TypeSymbolWithAnnotations.Create(GetSpecialType(SpecialType.System_Char, diagnostics, _syntax), isNullableIfReferenceType: null) :
((ArrayTypeSymbol)collectionExprType).ElementType;
}
// CONSIDER:
......
......@@ -840,7 +840,7 @@ public override ForEachStatementInfo GetForEachStatementInfo(CommonForEachStatem
enumeratorInfoOpt.MoveNextMethod,
(PropertySymbol)enumeratorInfoOpt.CurrentPropertyGetter.AssociatedSymbol,
enumeratorInfoOpt.NeedsDisposeMethod ? (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose) : null,
enumeratorInfoOpt.ElementType,
enumeratorInfoOpt.ElementType.TypeSymbol,
boundForEach.ElementConversion,
enumeratorInfoOpt.CurrentConversion);
}
......
......@@ -2749,7 +2749,19 @@ public override void VisitForEachIterationVariables(BoundForEachStatement node)
// declare and assign all iteration variables
foreach (var iterationVariable in node.IterationVariables)
{
// PROTOTYPE(NullableReferenceTypes): Mark as assigned.
int slot = GetOrCreateSlot(iterationVariable);
TypeSymbolWithAnnotations sourceType = node.EnumeratorInfoOpt?.ElementType;
bool? isNullableIfReferenceType = null;
if ((object)sourceType != null)
{
TypeSymbolWithAnnotations destinationType = iterationVariable.Type;
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
Conversion conversion = _conversions.ClassifyImplicitConversionFromType(sourceType.TypeSymbol, destinationType.TypeSymbol, ref useSiteDiagnostics);
// PROTOTYPE(NullableReferenceTypes): Report nullability mismatch if any.
TypeSymbolWithAnnotations result = InferResultNullability(operand: null, conversion, destinationType.TypeSymbol, sourceType);
isNullableIfReferenceType = result.IsNullable;
}
this.State[slot] = !isNullableIfReferenceType;
}
}
......
......@@ -106,7 +106,7 @@ private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement n
BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body);
TypeSymbol enumeratorType = enumeratorInfo.GetEnumeratorMethod.ReturnType.TypeSymbol;
TypeSymbol elementType = enumeratorInfo.ElementType;
TypeSymbol elementType = enumeratorInfo.ElementType.TypeSymbol;
// E e
LocalSymbol enumeratorVar = _factory.SynthesizedLocal(enumeratorType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachEnumerator);
......
......@@ -623,8 +623,7 @@ private sealed class ForEachLocalSymbol : SourceLocalSymbol
protected override TypeSymbolWithAnnotations InferTypeOfVarVariable(DiagnosticBag diagnostics)
{
var type = ForEachLoopBinder.InferCollectionElementType(diagnostics, _collection);
return type == null ? null : TypeSymbolWithAnnotations.Create(type);
return ForEachLoopBinder.InferCollectionElementType(diagnostics, _collection);
}
/// <summary>
......
......@@ -3049,7 +3049,7 @@ private static BoundForEachStatement GetBoundForEachStatement(string text, param
Assert.Null(statementInfo.DisposeMethod);
}
Assert.Equal(enumeratorInfo.ElementType, statementInfo.ElementType);
Assert.Equal(enumeratorInfo.ElementType.TypeSymbol, statementInfo.ElementType);
Assert.Equal(boundNode.ElementConversion, statementInfo.ElementConversion);
Assert.Equal(enumeratorInfo.CurrentConversion, statementInfo.CurrentConversion);
}
......
......@@ -19516,6 +19516,275 @@ public void ExplicitCast_StaticType()
Diagnostic(ErrorCode.ERR_ConvertToStaticClass, "(C?)y").WithArguments("C").WithLocation(4, 36));
}
[Fact]
public void ForEach_01()
{
var source =
@"class Enumerable
{
public Enumerator GetEnumerator() => new Enumerator();
}
class Enumerator
{
public object Current => throw null;
public bool MoveNext() => false;
}
class C
{
static void F(Enumerable e)
{
foreach (var x in e)
x.ToString();
foreach (string y in e)
y.ToString();
foreach (string? z in e)
z.ToString();
}
}";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
[Fact]
public void ForEach_02()
{
var source =
@"class Enumerable
{
public Enumerator GetEnumerator() => new Enumerator();
}
class Enumerator
{
public object? Current => throw null;
public bool MoveNext() => false;
}
class C
{
static void F(Enumerable e)
{
foreach (var x in e)
x.ToString();
foreach (object y in e)
y.ToString();
}
}";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (15,13): warning CS8602: Possible dereference of a null reference.
// x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(15, 13),
// (17,13): warning CS8602: Possible dereference of a null reference.
// y.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y").WithLocation(17, 13));
}
[Fact]
public void ForEach_03()
{
var source =
@"using System.Collections;
namespace System
{
public class Object
{
public string ToString() => throw null;
}
public abstract class ValueType { }
public struct Void { }
public struct Boolean { }
public class String { }
}
namespace System.Collections
{
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
object? Current { get; }
bool MoveNext();
}
}
class Enumerable : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() => throw null;
}
class C
{
static void F(Enumerable e)
{
foreach (var x in e)
x.ToString();
foreach (object y in e)
y.ToString();
}
static void G(IEnumerable e)
{
foreach (var z in e)
z.ToString();
foreach (object w in e)
w.ToString();
}
}";
var comp = CreateEmptyCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (34,13): warning CS8602: Possible dereference of a null reference.
// x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(34, 13),
// (36,13): warning CS8602: Possible dereference of a null reference.
// y.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y").WithLocation(36, 13),
// (41,13): warning CS8602: Possible dereference of a null reference.
// z.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "z").WithLocation(41, 13),
// (43,13): warning CS8602: Possible dereference of a null reference.
// w.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "w").WithLocation(43, 13));
}
// z.ToString() should warn if IEnumerator.Current is annotated as `object?`.
[Fact]
public void ForEach_04()
{
var source =
@"using System.Collections;
using System.Collections.Generic;
class C
{
static void F(IEnumerable<object?> cx, object?[] cy)
{
foreach (var x in cx)
x.ToString();
foreach (object? y in cy)
y.ToString();
foreach (object? z in (IEnumerable)cx)
z.ToString();
}
}";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (8,13): warning CS8602: Possible dereference of a null reference.
// x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(8, 13),
// (10,13): warning CS8602: Possible dereference of a null reference.
// y.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y").WithLocation(10, 13));
}
[Fact]
public void ForEach_05()
{
var source =
@"class C
{
static void F1(dynamic c)
{
foreach (var x in c)
x.ToString();
foreach (object? y in c)
y.ToString();
}
}";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
[Fact]
public void ForEach_06()
{
var source =
@"using System.Collections;
using System.Collections.Generic;
class C<T> : IEnumerable<T> where T : class
{
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null;
IEnumerator IEnumerable.GetEnumerator() => throw null;
}
class P
{
static void F<T>(C<T?> c) where T : class
{
foreach (var x in c)
x.ToString();
foreach (T? y in c)
y.ToString();
foreach (T z in c)
z.ToString();
foreach (object w in c)
w.ToString();
}
}";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (13,13): warning CS8602: Possible dereference of a null reference.
// x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(13, 13),
// (15,13): warning CS8602: Possible dereference of a null reference.
// y.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y").WithLocation(15, 13),
// (17,13): warning CS8602: Possible dereference of a null reference.
// z.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "z").WithLocation(17, 13),
// (19,13): warning CS8602: Possible dereference of a null reference.
// w.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "w").WithLocation(19, 13));
}
[Fact]
public void ForEach_07()
{
var source =
@"struct S<T> where T : class
{
public E<T> GetEnumerator() => new E<T>();
}
struct E<T> where T : class
{
public T Current => throw null;
public bool MoveNext() => false;
}
class P
{
static void F1<T>() where T : class
{
foreach (var x1 in new S<T>())
x1.ToString();
foreach (T y1 in new S<T>())
y1.ToString();
foreach (T? z1 in new S<T>())
z1.ToString();
foreach (object? w1 in new S<T>())
w1.ToString();
}
static void F2<T>() where T : class
{
foreach (var x2 in new S<T?>())
x2.ToString();
foreach (T y2 in new S<T?>())
y2.ToString();
foreach (T? z2 in new S<T?>())
z2.ToString();
foreach (object? w2 in new S<T?>())
w2.ToString();
}
}";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (26,13): warning CS8602: Possible dereference of a null reference.
// x2.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(26, 13),
// (28,13): warning CS8602: Possible dereference of a null reference.
// y2.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y2").WithLocation(28, 13),
// (30,13): warning CS8602: Possible dereference of a null reference.
// z2.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "z2").WithLocation(30, 13),
// (32,13): warning CS8602: Possible dereference of a null reference.
// w2.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "w2").WithLocation(32, 13));
}
[Fact]
public void UnconstrainedTypeParameter_MayBeNonNullable()
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册