提交 23c52c34 编写于 作者: C Charles Stoner 提交者: Jared Parsons

Track nullability of static fields (#33121)

* Track nullability of static fields

* PR feedback

* Fix tests

* PR feedback

* Misc.
上级 75552774
......@@ -1231,7 +1231,7 @@ private bool FieldsAllSet(int containingSlot, LocalState state)
Debug.Assert(containingSlot != -1);
Debug.Assert(!state.IsAssigned(containingSlot));
VariableIdentifier variable = variableBySlot[containingSlot];
NamedTypeSymbol structType = (NamedTypeSymbol)VariableType(variable.Symbol).TypeSymbol;
TypeSymbol structType = VariableType(variable.Symbol).TypeSymbol;
foreach (var field in _emptyStructTypeCache.GetStructInstanceFields(structType))
{
if (_emptyStructTypeCache.IsEmptyStructType(field.Type.TypeSymbol)) continue;
......
......@@ -114,8 +114,11 @@ private bool CheckStructInstanceFields(ConsList<NamedTypeSymbol> typesWithMember
// unless necessary.
foreach (var member in type.OriginalDefinition.GetMembersUnordered())
{
var field = GetActualInstanceField(member, type);
if (member.IsStatic)
{
continue;
}
var field = GetActualField(member, type);
if ((object)field != null)
{
var actualFieldType = field.Type.TypeSymbol;
......@@ -141,17 +144,20 @@ public IEnumerable<FieldSymbol> GetStructInstanceFields(TypeSymbol type)
return SpecializedCollections.EmptyEnumerable<FieldSymbol>();
}
return GetStructInstanceFields(nts);
return GetStructFields(nts, includeStatic: false);
}
public IEnumerable<FieldSymbol> GetStructInstanceFields(NamedTypeSymbol type)
public IEnumerable<FieldSymbol> GetStructFields(NamedTypeSymbol type, bool includeStatic)
{
// PERF: we get members of the OriginalDefinition to not create substituted members/types
// unless necessary.
foreach (var member in type.OriginalDefinition.GetMembersUnordered())
{
var field = GetActualInstanceField(member, type);
if (!includeStatic && member.IsStatic)
{
continue;
}
var field = GetActualField(member, type);
if ((object)field != null)
{
yield return field;
......@@ -159,29 +165,27 @@ public IEnumerable<FieldSymbol> GetStructInstanceFields(NamedTypeSymbol type)
}
}
private FieldSymbol GetActualInstanceField(Symbol member, NamedTypeSymbol type)
private FieldSymbol GetActualField(Symbol member, NamedTypeSymbol type)
{
if (!member.IsStatic)
switch (member.Kind)
{
switch (member.Kind)
{
case SymbolKind.Field:
var field = (FieldSymbol)member;
// Do not report virtual tuple fields.
// They are additional aliases to the fields of the underlying struct or nested extensions.
// and as such are already accounted for via the nonvirtual fields.
if (field.IsVirtualTupleField)
{
return null;
}
case SymbolKind.Field:
var field = (FieldSymbol)member;
// Do not report virtual tuple fields.
// They are additional aliases to the fields of the underlying struct or nested extensions.
// and as such are already accounted for via the nonvirtual fields.
if (field.IsVirtualTupleField)
{
return null;
}
return (field.IsFixedSizeBuffer || ShouldIgnoreStructField(field, field.Type.TypeSymbol)) ? null : field.AsMember(type);
return (field.IsFixedSizeBuffer || ShouldIgnoreStructField(field, field.Type.TypeSymbol)) ? null : field.AsMember(type);
case SymbolKind.Event:
EventSymbol eventSymbol = (EventSymbol)member;
return (!eventSymbol.HasAssociatedField || ShouldIgnoreStructField(eventSymbol, eventSymbol.Type.TypeSymbol)) ? null : eventSymbol.AssociatedField.AsMember(type);
}
case SymbolKind.Event:
var eventSymbol = (EventSymbol)member;
return (!eventSymbol.HasAssociatedField || ShouldIgnoreStructField(eventSymbol, eventSymbol.Type.TypeSymbol)) ? null : eventSymbol.AssociatedField.AsMember(type);
}
return null;
}
......
......@@ -91,7 +91,7 @@ protected int VariableSlot(Symbol symbol, int containingSlot = 0)
/// <summary>
/// Force a variable to have a slot. Returns -1 if the variable has an empty struct type.
/// </summary>
protected int GetOrCreateSlot(Symbol symbol, int containingSlot = 0)
protected virtual int GetOrCreateSlot(Symbol symbol, int containingSlot = 0)
{
if (symbol.Kind == SymbolKind.RangeVariable) return -1;
......@@ -217,8 +217,8 @@ protected virtual int MakeSlot(BoundExpression node)
case BoundKind.PropertyAccess:
if (TryGetReceiverAndMember(node, out BoundExpression receiver, out Symbol member))
{
int containingSlot = MakeSlot(receiver);
return (containingSlot == -1) ? -1 : GetOrCreateSlot(member, containingSlot);
Debug.Assert((receiver is null) == member.IsStatic);
return MakeMemberSlot(receiver, member);
}
break;
case BoundKind.AssignmentOperator:
......@@ -227,6 +227,24 @@ protected virtual int MakeSlot(BoundExpression node)
return -1;
}
protected int MakeMemberSlot(BoundExpression receiverOpt, Symbol member)
{
int containingSlot = -1;
if (!member.IsStatic)
{
if (receiverOpt is null)
{
return -1;
}
containingSlot = MakeSlot(receiverOpt);
if (containingSlot < 0)
{
return -1;
}
}
return GetOrCreateSlot(member, containingSlot);
}
protected static TypeSymbolWithAnnotations VariableType(Symbol s)
{
switch (s.Kind)
......
......@@ -328,11 +328,15 @@ protected override bool TryGetReceiverAndMember(BoundExpression expr, out BoundE
{
var fieldAccess = (BoundFieldAccess)expr;
var fieldSymbol = fieldAccess.FieldSymbol;
if (fieldSymbol.IsStatic || fieldSymbol.IsFixedSizeBuffer)
member = fieldSymbol;
if (fieldSymbol.IsFixedSizeBuffer)
{
return false;
}
member = fieldSymbol;
if (fieldSymbol.IsStatic)
{
return true;
}
receiver = fieldAccess.ReceiverOpt;
break;
}
......@@ -340,12 +344,12 @@ protected override bool TryGetReceiverAndMember(BoundExpression expr, out BoundE
{
var eventAccess = (BoundEventAccess)expr;
var eventSymbol = eventAccess.EventSymbol;
// https://github.com/dotnet/roslyn/issues/29901 Use AssociatedField for field-like events?
member = eventSymbol;
if (eventSymbol.IsStatic)
{
return false;
return true;
}
// https://github.com/dotnet/roslyn/issues/29901 Use AssociatedField for field-like events?
member = eventSymbol;
receiver = eventAccess.ReceiverOpt;
break;
}
......@@ -353,16 +357,22 @@ protected override bool TryGetReceiverAndMember(BoundExpression expr, out BoundE
{
var propAccess = (BoundPropertyAccess)expr;
var propSymbol = propAccess.PropertySymbol;
if (propSymbol.IsStatic)
member = GetBackingFieldIfStructProperty(propSymbol);
if (member is null)
{
return false;
}
member = GetBackingFieldIfStructProperty(propSymbol);
if (propSymbol.IsStatic)
{
return true;
}
receiver = propAccess.ReceiverOpt;
break;
}
}
Debug.Assert(member?.IsStatic != true);
return (object)member != null &&
(object)receiver != null &&
receiver.Kind != BoundKind.TypeExpression &&
......@@ -383,7 +393,7 @@ private Symbol GetBackingFieldIfStructProperty(Symbol symbol)
// https://github.com/dotnet/roslyn/issues/29619 Relying on field name
// will not work for properties declared in other languages.
var fieldName = GeneratedNames.MakeBackingFieldName(property.Name);
return _emptyStructTypeCache.GetStructInstanceFields(containingType).FirstOrDefault(f => f.Name == fieldName);
return _emptyStructTypeCache.GetStructFields(containingType, includeStatic: symbol.IsStatic).FirstOrDefault(f => f.Name == fieldName);
}
}
return symbol;
......@@ -391,7 +401,7 @@ private Symbol GetBackingFieldIfStructProperty(Symbol symbol)
// https://github.com/dotnet/roslyn/issues/29619 Temporary, until we're using
// properties on structs directly.
private new int GetOrCreateSlot(Symbol symbol, int containingSlot = 0)
protected override int GetOrCreateSlot(Symbol symbol, int containingSlot = 0)
{
symbol = GetBackingFieldIfStructProperty(symbol);
if ((object)symbol == null)
......@@ -4717,8 +4727,7 @@ private void VisitMemberAccess(BoundExpression node, BoundExpression receiverOpt
// accumulate so far.
if (!resultType.IsValueType || resultType.IsNullableType())
{
int containingSlot = getReceiverSlot();
int slot = (containingSlot < 0) ? -1 : GetOrCreateSlot(member, containingSlot);
int slot = MakeMemberSlot(receiverOpt, member);
if (slot > 0 && slot < this.State.Capacity)
{
var annotation = this.State[slot];
......@@ -4732,7 +4741,7 @@ private void VisitMemberAccess(BoundExpression node, BoundExpression receiverOpt
Debug.Assert(!IsConditionalState);
if (nullableOfTMember == SpecialMember.System_Nullable_T_get_HasValue)
{
int containingSlot = getReceiverSlot();
int containingSlot = (receiverOpt is null) ? -1 : MakeSlot(receiverOpt);
if (containingSlot > 0)
{
// https://github.com/dotnet/roslyn/issues/31516: Report HDN_NullCheckIsProbablyAlwaysTrue/False
......@@ -4745,8 +4754,6 @@ private void VisitMemberAccess(BoundExpression node, BoundExpression receiverOpt
_resultType = resultType;
}
int getReceiverSlot() => (receiverOpt is null) ? -1 : MakeSlot(receiverOpt);
}
private static SpecialMember? GetNullableOfTMember(CSharpCompilation compilation, Symbol member)
......
......@@ -78937,5 +78937,331 @@ static void F(C? c)
// c.F = c.F; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c").WithLocation(10, 9));
}
[Fact]
[WorkItem(26651, "https://github.com/dotnet/roslyn/issues/26651")]
public void StaticMember_01()
{
var source =
@"class Program
{
static readonly string? F = null;
static readonly int? G = null;
static void Main()
{
if (F != null)
F.ToString();
if (G != null)
_ = G.Value;
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics();
}
[Fact]
[WorkItem(26651, "https://github.com/dotnet/roslyn/issues/26651")]
public void StaticMember_02()
{
var source =
@"#pragma warning disable 0649
class C<T>
{
static T F;
static void M()
{
if (F == null) return;
F.ToString();
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics();
}
[Fact]
[WorkItem(26651, "https://github.com/dotnet/roslyn/issues/26651")]
public void StaticMember_03()
{
var source =
@"#pragma warning disable 0649
class C<T>
{
internal static T F;
}
class Program
{
static void F1<T1>()
{
C<T1>.F.ToString(); // 1
C<T1>.F.ToString();
}
static void F2<T2>() where T2 : class
{
C<T2?>.F.ToString(); // 2
C<T2>.F.ToString();
}
static void F3<T3>() where T3 : class
{
C<T3>.F.ToString();
C<T3?>.F.ToString();
}
static void F4<T4>() where T4 : struct
{
_ = C<T4?>.F.Value; // 3
_ = C<T4?>.F.Value;
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (10,9): warning CS8602: Possible dereference of a null reference.
// C<T1>.F.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "C<T1>.F").WithLocation(10, 9),
// (15,9): warning CS8602: Possible dereference of a null reference.
// C<T2?>.F.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "C<T2?>.F").WithLocation(15, 9),
// (25,13): warning CS8629: Nullable value type may be null.
// _ = C<T4?>.F.Value; // 3
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "C<T4?>.F.Value").WithLocation(25, 13));
}
[Fact]
[WorkItem(26651, "https://github.com/dotnet/roslyn/issues/26651")]
public void StaticMember_04()
{
var source =
@"#pragma warning disable 0649
class C<T>
{
internal static T F;
}
class Program
{
static void F1<T1>()
{
C<T1>.F = default; // 1
C<T1>.F.ToString(); // 2
}
static void F2<T2>() where T2 : class, new()
{
C<T2?>.F = new T2();
C<T2?>.F.ToString();
}
static void F3<T3>() where T3 : class, new()
{
C<T3>.F = new T3();
C<T3>.F.ToString();
}
static void F4<T4>() where T4 : class
{
C<T4>.F = null; // 3
C<T4>.F.ToString(); // 4
}
static void F5<T5>() where T5 : struct
{
C<T5?>.F = default(T5);
_ = C<T5?>.F.Value;
}
static void F6<T6>() where T6 : new()
{
C<T6>.F = new T6();
C<T6>.F.ToString();
}
static void F7()
{
C<string>.F = null; // 5
_ = C<string>.F.Length; // 6
}
static void F8()
{
C<int?>.F = 3;
_ = C<int?>.F.Value;
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (10,19): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.
// C<T1>.F = default; // 1
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "default").WithLocation(10, 19),
// (11,9): warning CS8602: Possible dereference of a null reference.
// C<T1>.F.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "C<T1>.F").WithLocation(11, 9),
// (25,19): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.
// C<T4>.F = null; // 3
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(25, 19),
// (26,9): warning CS8602: Possible dereference of a null reference.
// C<T4>.F.ToString(); // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "C<T4>.F").WithLocation(26, 9),
// (40,23): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.
// C<string>.F = null; // 5
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(40, 23),
// (41,13): warning CS8602: Possible dereference of a null reference.
// _ = C<string>.F.Length; // 6
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "C<string>.F").WithLocation(41, 13));
}
[Fact]
[WorkItem(26651, "https://github.com/dotnet/roslyn/issues/26651")]
public void StaticMember_05()
{
var source =
@"class C<T>
{
internal static T P
{
get => throw null;
set { }
}
}
class Program
{
static void F1<T1>()
{
C<T1>.P = default; // 1
C<T1>.P.ToString(); // 2
}
static void F2<T2>() where T2 : class, new()
{
C<T2?>.P = new T2();
C<T2?>.P.ToString();
}
static void F3<T3>() where T3 : class, new()
{
C<T3>.P = new T3();
C<T3>.P.ToString();
}
static void F4<T4>() where T4 : class
{
C<T4>.P = null; // 3
C<T4>.P.ToString(); // 4
}
static void F5<T5>() where T5 : struct
{
C<T5?>.P = default(T5);
_ = C<T5?>.P.Value;
}
static void F6<T6>() where T6 : new()
{
C<T6>.P = new T6();
C<T6>.P.ToString();
}
static void F7()
{
C<string>.P = null; // 5
_ = C<string>.P.Length; // 6
}
static void F8()
{
C<int?>.P = 3;
_ = C<int?>.P.Value;
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (13,19): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.
// C<T1>.P = default; // 1
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "default").WithLocation(13, 19),
// (14,9): warning CS8602: Possible dereference of a null reference.
// C<T1>.P.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "C<T1>.P").WithLocation(14, 9),
// (28,19): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.
// C<T4>.P = null; // 3
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(28, 19),
// (29,9): warning CS8602: Possible dereference of a null reference.
// C<T4>.P.ToString(); // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "C<T4>.P").WithLocation(29, 9),
// (43,23): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.
// C<string>.P = null; // 5
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(43, 23),
// (44,13): warning CS8602: Possible dereference of a null reference.
// _ = C<string>.P.Length; // 6
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "C<string>.P").WithLocation(44, 13));
}
[Fact]
[WorkItem(26651, "https://github.com/dotnet/roslyn/issues/26651")]
public void StaticMember_06()
{
var source =
@"#pragma warning disable 0649
struct S<T>
{
internal static T F;
}
class Program
{
static void F1<T1>()
{
S<T1>.F = default; // 1
S<T1>.F.ToString(); // 2
}
static void F2<T2>() where T2 : class, new()
{
S<T2?>.F = new T2();
S<T2?>.F.ToString();
}
static void F3<T3>() where T3 : class
{
S<T3>.F = null; // 3
S<T3>.F.ToString(); // 4
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (10,19): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.
// S<T1>.F = default; // 1
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "default").WithLocation(10, 19),
// (11,9): warning CS8602: Possible dereference of a null reference.
// S<T1>.F.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "S<T1>.F").WithLocation(11, 9),
// (20,19): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.
// S<T3>.F = null; // 3
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(20, 19),
// (21,9): warning CS8602: Possible dereference of a null reference.
// S<T3>.F.ToString(); // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "S<T3>.F").WithLocation(21, 9));
}
[Fact]
[WorkItem(26651, "https://github.com/dotnet/roslyn/issues/26651")]
public void StaticMember_07()
{
var source =
@"struct S<T>
{
internal static T P { get; set; }
}
class Program
{
static void F1<T1>()
{
S<T1>.P = default; // 1
S<T1>.P.ToString(); // 2
}
static void F2<T2>() where T2 : class, new()
{
S<T2?>.P = new T2();
S<T2?>.P.ToString();
}
static void F3<T3>() where T3 : class
{
S<T3>.P = null; // 3
S<T3>.P.ToString(); // 4
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (9,19): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.
// S<T1>.P = default; // 1
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "default").WithLocation(9, 19),
// (10,9): warning CS8602: Possible dereference of a null reference.
// S<T1>.P.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "S<T1>.P").WithLocation(10, 9),
// (19,19): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.
// S<T3>.P = null; // 3
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(19, 19),
// (20,9): warning CS8602: Possible dereference of a null reference.
// S<T3>.P.ToString(); // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "S<T3>.P").WithLocation(20, 9));
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册