11048 lines
442 KiB
C#
11048 lines
442 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
using Microsoft.CodeAnalysis.CSharp.Symbols;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Microsoft.CodeAnalysis.Collections;
|
|
using Microsoft.CodeAnalysis.PooledObjects;
|
|
using Microsoft.CodeAnalysis.Symbols;
|
|
using Microsoft.CodeAnalysis.Text;
|
|
using Roslyn.Utilities;
|
|
|
|
namespace Microsoft.CodeAnalysis.CSharp;
|
|
|
|
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
|
|
internal sealed class NullableWalker : LocalDataFlowPass<NullableWalker.LocalState, NullableWalker.LocalFunctionState>
|
|
{
|
|
internal sealed class NullableAnalysisData
|
|
{
|
|
internal readonly int MaxRecursionDepth;
|
|
|
|
internal readonly ConcurrentDictionary<object, Data> Data;
|
|
|
|
internal NullableAnalysisData(int maxRecursionDepth = -1)
|
|
{
|
|
MaxRecursionDepth = maxRecursionDepth;
|
|
Data = new ConcurrentDictionary<object, Data>();
|
|
}
|
|
}
|
|
|
|
internal sealed class VariableState
|
|
{
|
|
internal readonly VariablesSnapshot Variables;
|
|
|
|
internal readonly LocalStateSnapshot VariableNullableStates;
|
|
|
|
internal VariableState(VariablesSnapshot variables, LocalStateSnapshot variableNullableStates)
|
|
{
|
|
Variables = variables;
|
|
VariableNullableStates = variableNullableStates;
|
|
}
|
|
}
|
|
|
|
internal readonly struct Data
|
|
{
|
|
internal readonly int TrackedEntries;
|
|
|
|
internal readonly bool RequiredAnalysis;
|
|
|
|
internal Data(int trackedEntries, bool requiredAnalysis)
|
|
{
|
|
TrackedEntries = trackedEntries;
|
|
RequiredAnalysis = requiredAnalysis;
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
|
|
private readonly struct VisitResult
|
|
{
|
|
public readonly TypeWithState RValueType;
|
|
|
|
public readonly TypeWithAnnotations LValueType;
|
|
|
|
public VisitResult(TypeWithState rValueType, TypeWithAnnotations lValueType)
|
|
{
|
|
RValueType = rValueType;
|
|
LValueType = lValueType;
|
|
}
|
|
|
|
public VisitResult(TypeSymbol? type, NullableAnnotation annotation, NullableFlowState state)
|
|
{
|
|
RValueType = TypeWithState.Create(type, state);
|
|
LValueType = TypeWithAnnotations.Create(type, annotation);
|
|
}
|
|
|
|
internal string GetDebuggerDisplay()
|
|
{
|
|
return "{LValue: " + LValueType.GetDebuggerDisplay() + ", RValue: " + RValueType.GetDebuggerDisplay() + "}";
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("{VisitResult.GetDebuggerDisplay(), nq}")]
|
|
private readonly struct VisitArgumentResult
|
|
{
|
|
public readonly VisitResult VisitResult;
|
|
|
|
public readonly Optional<LocalState> StateForLambda;
|
|
|
|
public TypeWithState RValueType => VisitResult.RValueType;
|
|
|
|
public TypeWithAnnotations LValueType => VisitResult.LValueType;
|
|
|
|
public VisitArgumentResult(VisitResult visitResult, Optional<LocalState> stateForLambda)
|
|
{
|
|
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
|
|
VisitResult = visitResult;
|
|
StateForLambda = stateForLambda;
|
|
}
|
|
}
|
|
|
|
private enum AssignmentKind
|
|
{
|
|
Assignment,
|
|
Return,
|
|
Argument,
|
|
ForEachIterationVariable
|
|
}
|
|
|
|
private readonly struct CompareExchangeInfo(ImmutableArray<BoundExpression> arguments, ImmutableArray<VisitArgumentResult> results, ImmutableArray<int> argsToParamsOpt)
|
|
{
|
|
public readonly ImmutableArray<BoundExpression> Arguments = arguments;
|
|
|
|
public readonly ImmutableArray<VisitArgumentResult> Results = results;
|
|
|
|
public readonly ImmutableArray<int> ArgsToParamsOpt = argsToParamsOpt;
|
|
|
|
public bool IsDefault
|
|
{
|
|
get
|
|
{
|
|
if (!Arguments.IsDefault)
|
|
{
|
|
return Results.IsDefault;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private delegate(MethodSymbol? method, bool returnNotNull) ArgumentsCompletionDelegate(ImmutableArray<VisitArgumentResult> argumentResults, ImmutableArray<ParameterSymbol> parametersOpt, MethodSymbol? method);
|
|
|
|
private sealed class MethodInferenceExtensions : MethodTypeInferrer.Extensions
|
|
{
|
|
private readonly NullableWalker _walker;
|
|
|
|
internal MethodInferenceExtensions(NullableWalker walker)
|
|
{
|
|
_walker = walker;
|
|
}
|
|
|
|
internal override TypeWithAnnotations GetTypeWithAnnotations(BoundExpression expr)
|
|
{
|
|
return TypeWithAnnotations.Create(expr.GetTypeOrFunctionType(), GetNullableAnnotation(expr));
|
|
}
|
|
|
|
private static NullableAnnotation GetNullableAnnotation(BoundExpression expr)
|
|
{
|
|
switch (expr.Kind)
|
|
{
|
|
case BoundKind.DefaultLiteral:
|
|
case BoundKind.DefaultExpression:
|
|
case BoundKind.Literal:
|
|
if (!(expr.ConstantValueOpt == (ConstantValue)null) && expr.ConstantValueOpt.IsNull && !expr.IsSuppressed)
|
|
{
|
|
return NullableAnnotation.Annotated;
|
|
}
|
|
return NullableAnnotation.NotAnnotated;
|
|
case BoundKind.ExpressionWithNullability:
|
|
return ((BoundExpressionWithNullability)expr).NullableAnnotation;
|
|
case BoundKind.MethodGroup:
|
|
case BoundKind.UnconvertedObjectCreationExpression:
|
|
case BoundKind.UnconvertedCollectionExpression:
|
|
case BoundKind.ConvertedTupleLiteral:
|
|
case BoundKind.UnboundLambda:
|
|
return NullableAnnotation.NotAnnotated;
|
|
default:
|
|
return NullableAnnotation.Oblivious;
|
|
}
|
|
}
|
|
|
|
internal override TypeWithAnnotations GetMethodGroupResultType(BoundMethodGroup group, MethodSymbol method)
|
|
{
|
|
if (_walker.TryGetMethodGroupReceiverNullability(group.ReceiverOpt, out var type) && !method.IsStatic)
|
|
{
|
|
method = (MethodSymbol)AsMemberOfType(type.Type, method);
|
|
}
|
|
return method.ReturnTypeWithAnnotations;
|
|
}
|
|
}
|
|
|
|
private readonly struct DeconstructionVariable
|
|
{
|
|
internal readonly BoundExpression Expression;
|
|
|
|
internal readonly TypeWithAnnotations Type;
|
|
|
|
internal readonly ArrayBuilder<DeconstructionVariable>? NestedVariables;
|
|
|
|
internal DeconstructionVariable(BoundExpression expression, TypeWithAnnotations type)
|
|
{
|
|
Expression = expression;
|
|
Type = type;
|
|
NestedVariables = null;
|
|
}
|
|
|
|
internal DeconstructionVariable(BoundExpression expression, ArrayBuilder<DeconstructionVariable> nestedVariables)
|
|
{
|
|
Expression = expression;
|
|
Type = default(TypeWithAnnotations);
|
|
NestedVariables = nestedVariables;
|
|
}
|
|
}
|
|
|
|
internal sealed class LocalStateSnapshot
|
|
{
|
|
internal readonly int Id;
|
|
|
|
internal readonly LocalStateSnapshot? Container;
|
|
|
|
internal readonly BitVector State;
|
|
|
|
internal LocalStateSnapshot(int id, LocalStateSnapshot? container, BitVector state)
|
|
{
|
|
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
|
|
Id = id;
|
|
Container = container;
|
|
State = state;
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
|
|
internal struct LocalState : ILocalDataFlowState, ILocalState
|
|
{
|
|
private sealed class Boxed
|
|
{
|
|
internal LocalState Value;
|
|
|
|
internal Boxed(LocalState value)
|
|
{
|
|
Value = value;
|
|
}
|
|
}
|
|
|
|
internal readonly int Id;
|
|
|
|
private readonly Boxed? _container;
|
|
|
|
private BitVector _state;
|
|
|
|
public bool Reachable => ((BitVector)(ref _state))[0];
|
|
|
|
public bool NormalizeToBottom => false;
|
|
|
|
private int Capacity => ((BitVector)(ref _state)).Capacity / 2;
|
|
|
|
public NullableFlowState this[int slot]
|
|
{
|
|
get
|
|
{
|
|
var (id, index) = Variables.DeconstructSlot(slot);
|
|
return GetValue(id, index);
|
|
}
|
|
set
|
|
{
|
|
var (id, index) = Variables.DeconstructSlot(slot);
|
|
SetValue(id, index, value);
|
|
}
|
|
}
|
|
|
|
private LocalState(int id, Boxed? container, BitVector state)
|
|
{
|
|
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
|
|
Id = id;
|
|
_container = container;
|
|
_state = state;
|
|
}
|
|
|
|
internal static LocalState Create(LocalStateSnapshot snapshot)
|
|
{
|
|
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
|
|
Boxed container = ((snapshot.Container == null) ? null : new Boxed(Create(snapshot.Container)));
|
|
int id = snapshot.Id;
|
|
BitVector state = snapshot.State;
|
|
return new LocalState(id, container, ((BitVector)(ref state)).Clone());
|
|
}
|
|
|
|
internal LocalStateSnapshot CreateSnapshot()
|
|
{
|
|
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
|
|
return new LocalStateSnapshot(Id, _container?.Value.CreateSnapshot(), ((BitVector)(ref _state)).Clone());
|
|
}
|
|
|
|
public static LocalState ReachableState(Variables variables)
|
|
{
|
|
return CreateReachableOrUnreachableState(variables, reachable: true);
|
|
}
|
|
|
|
public static LocalState UnreachableState(Variables variables)
|
|
{
|
|
return CreateReachableOrUnreachableState(variables, reachable: false);
|
|
}
|
|
|
|
public static LocalState ReachableStateWithNotNulls(Variables variables)
|
|
{
|
|
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
|
|
Boxed container = ((variables.Container == null) ? null : new Boxed(ReachableStateWithNotNulls(variables.Container)));
|
|
int nextAvailableIndex = variables.NextAvailableIndex;
|
|
return new LocalState(variables.Id, container, createBitVectorWithNotNulls(nextAvailableIndex, reachable: true));
|
|
static BitVector createBitVectorWithNotNulls(int capacity, bool reachable)
|
|
{
|
|
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
|
|
BitVector result = BitVector.Create(capacity * 2);
|
|
((BitVector)(ref result))[0] = reachable;
|
|
for (int i = 1; i < capacity; i++)
|
|
{
|
|
int num = i * 2;
|
|
((BitVector)(ref result))[num] = true;
|
|
((BitVector)(ref result))[num + 1] = true;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private static LocalState CreateReachableOrUnreachableState(Variables variables, bool reachable)
|
|
{
|
|
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
|
|
Boxed container = ((variables.Container == null) ? null : new Boxed(CreateReachableOrUnreachableState(variables.Container, reachable)));
|
|
return new LocalState(variables.Id, container, CreateBitVector(reachable));
|
|
}
|
|
|
|
public LocalState CreateNestedMethodState(Variables variables)
|
|
{
|
|
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
|
|
return new LocalState(variables.Id, new Boxed(this), CreateBitVector(reachable: true));
|
|
}
|
|
|
|
private static BitVector CreateBitVector(bool reachable)
|
|
{
|
|
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
|
|
BitVector result = BitVector.Create(2);
|
|
((BitVector)(ref result))[0] = reachable;
|
|
return result;
|
|
}
|
|
|
|
private void EnsureCapacity(int capacity)
|
|
{
|
|
((BitVector)(ref _state)).EnsureCapacity(capacity * 2);
|
|
}
|
|
|
|
public bool HasVariable(int slot)
|
|
{
|
|
if (slot <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
var (id, index) = Variables.DeconstructSlot(slot);
|
|
return hasVariableCore(ref this, id, index);
|
|
static bool hasVariableCore(ref LocalState state, int num, int index2)
|
|
{
|
|
if (state.Id > num)
|
|
{
|
|
return hasVariableCore(ref state._container.Value, num, index2);
|
|
}
|
|
return state.Id == num;
|
|
}
|
|
}
|
|
|
|
public void NormalizeIfNeeded(int slot, NullableWalker walker, Variables variables, bool useNotNullsAsDefault = false)
|
|
{
|
|
if (!hasValue(ref this, slot))
|
|
{
|
|
Normalize(walker, variables, useNotNullsAsDefault);
|
|
}
|
|
static bool hasValue(ref LocalState state, int num)
|
|
{
|
|
if (num <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
var (id, index) = Variables.DeconstructSlot(num);
|
|
return hasValueCore(ref state, id, index);
|
|
}
|
|
static bool hasValueCore(ref LocalState state, int id, int index)
|
|
{
|
|
if (state.Id != id)
|
|
{
|
|
return hasValueCore(ref state._container.Value, id, index);
|
|
}
|
|
return index < state.Capacity;
|
|
}
|
|
}
|
|
|
|
public void Normalize(NullableWalker walker, Variables variables, bool useNotNullsAsDefault = false)
|
|
{
|
|
if (Id != variables.Id)
|
|
{
|
|
Normalize(walker, variables.Container, useNotNullsAsDefault);
|
|
return;
|
|
}
|
|
_container?.Value.Normalize(walker, variables.Container, useNotNullsAsDefault);
|
|
int capacity = Capacity;
|
|
EnsureCapacity(variables.NextAvailableIndex);
|
|
Populate(walker, capacity, useNotNullsAsDefault);
|
|
}
|
|
|
|
public void PopulateAll(NullableWalker walker)
|
|
{
|
|
_container?.Value.PopulateAll(walker);
|
|
Populate(walker, 1, useNotNullsAsDefault: false);
|
|
}
|
|
|
|
private void Populate(NullableWalker walker, int start, bool useNotNullsAsDefault)
|
|
{
|
|
int capacity = Capacity;
|
|
for (int i = start; i < capacity; i++)
|
|
{
|
|
int slot = Variables.ConstructSlot(Id, i);
|
|
SetValue(Id, i, (!useNotNullsAsDefault) ? walker.GetDefaultState(ref this, slot) : NullableFlowState.NotNull);
|
|
}
|
|
}
|
|
|
|
private NullableFlowState GetValue(int id, int index)
|
|
{
|
|
if (Id != id)
|
|
{
|
|
return _container.Value.GetValue(id, index);
|
|
}
|
|
return GetValue(index);
|
|
}
|
|
|
|
private NullableFlowState GetValue(int index)
|
|
{
|
|
if (!Reachable)
|
|
{
|
|
return NullableFlowState.NotNull;
|
|
}
|
|
index *= 2;
|
|
bool num = ((BitVector)(ref _state))[index];
|
|
bool flag = ((BitVector)(ref _state))[index + 1];
|
|
if (!num)
|
|
{
|
|
if (!flag)
|
|
{
|
|
return NullableFlowState.NotNull;
|
|
}
|
|
return NullableFlowState.MaybeDefault;
|
|
}
|
|
if (!flag)
|
|
{
|
|
return NullableFlowState.MaybeNull;
|
|
}
|
|
return NullableFlowState.NotNull;
|
|
}
|
|
|
|
private void SetValue(int id, int index, NullableFlowState value)
|
|
{
|
|
if (Id != id)
|
|
{
|
|
_container.Value.SetValue(id, index, value);
|
|
}
|
|
else
|
|
{
|
|
SetValue(index, value);
|
|
}
|
|
}
|
|
|
|
private void SetValue(int index, NullableFlowState value)
|
|
{
|
|
if (Reachable)
|
|
{
|
|
index *= 2;
|
|
ref BitVector state = ref _state;
|
|
int num = index;
|
|
ref BitVector state2 = ref _state;
|
|
int num2 = index + 1;
|
|
(bool, bool) tuple = value switch
|
|
{
|
|
NullableFlowState.MaybeNull => (true, false),
|
|
NullableFlowState.MaybeDefault => (false, true),
|
|
NullableFlowState.NotNull => (true, true),
|
|
_ => throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs", 11855),
|
|
};
|
|
((BitVector)(ref state))[num] = tuple.Item1;
|
|
((BitVector)(ref state2))[num2] = tuple.Item2;
|
|
}
|
|
}
|
|
|
|
internal void ForEach<TArg>(Action<int, TArg> action, TArg arg)
|
|
{
|
|
_container?.Value.ForEach(action, arg);
|
|
for (int i = 1; i < Capacity; i++)
|
|
{
|
|
action(Variables.ConstructSlot(Id, i), arg);
|
|
}
|
|
}
|
|
|
|
internal LocalState GetStateForVariables(int id)
|
|
{
|
|
LocalState result = this;
|
|
while (result.Id != id)
|
|
{
|
|
result = result._container.Value;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public LocalState Clone()
|
|
{
|
|
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
|
|
Boxed container = ((_container == null) ? null : new Boxed(_container.Value.Clone()));
|
|
return new LocalState(Id, container, ((BitVector)(ref _state)).Clone());
|
|
}
|
|
|
|
public bool Join(in LocalState other)
|
|
{
|
|
bool flag = false;
|
|
if (_container != null && _container.Value.Join(in other._container.Value))
|
|
{
|
|
flag = true;
|
|
}
|
|
bool reachable = Reachable;
|
|
bool flag2 = reachable | other.Reachable;
|
|
((BitVector)(ref _state))[0] = flag2;
|
|
flag = flag || reachable != flag2;
|
|
for (int i = 1; i < Capacity; i++)
|
|
{
|
|
NullableFlowState nullableFlowState = (reachable ? GetValue(i) : NullableFlowState.NotNull);
|
|
NullableFlowState nullableFlowState2 = nullableFlowState.Join(other.GetValue(i));
|
|
SetValue(i, nullableFlowState2);
|
|
flag = flag || nullableFlowState != nullableFlowState2;
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
public bool Meet(in LocalState other)
|
|
{
|
|
bool flag = false;
|
|
if (_container != null && _container.Value.Meet(in other._container.Value))
|
|
{
|
|
flag = true;
|
|
}
|
|
bool reachable = Reachable;
|
|
bool flag2 = reachable & other.Reachable;
|
|
((BitVector)(ref _state))[0] = flag2;
|
|
flag = flag || reachable != flag2;
|
|
for (int i = 1; i < Capacity; i++)
|
|
{
|
|
NullableFlowState value = GetValue(i);
|
|
NullableFlowState nullableFlowState = value.Meet(other.GetValue(i));
|
|
SetValue(i, nullableFlowState);
|
|
flag = flag || value != nullableFlowState;
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
internal string GetDebuggerDisplay()
|
|
{
|
|
PooledStringBuilder instance = PooledStringBuilder.GetInstance();
|
|
StringBuilder builder = instance.Builder;
|
|
builder.Append(" ");
|
|
for (int num = Math.Min(Capacity, 8) - 1; num >= 0; num--)
|
|
{
|
|
NullableFlowState value = GetValue(num);
|
|
bool flag = ((value == NullableFlowState.MaybeNull || value == NullableFlowState.MaybeDefault) ? true : false);
|
|
bool flag2 = flag;
|
|
builder.Append(flag2 ? '?' : '!');
|
|
}
|
|
return instance.ToStringAndFree();
|
|
}
|
|
|
|
internal string Dump(Variables variables)
|
|
{
|
|
if (!Reachable)
|
|
{
|
|
return "unreachable";
|
|
}
|
|
if (Id != variables.Id)
|
|
{
|
|
return "invalid";
|
|
}
|
|
PooledStringBuilder instance = PooledStringBuilder.GetInstance();
|
|
Dump(PooledStringBuilder.op_Implicit(instance), variables);
|
|
return instance.ToStringAndFree();
|
|
}
|
|
|
|
private void Dump(StringBuilder builder, Variables variables)
|
|
{
|
|
_container?.Value.Dump(builder, variables.Container);
|
|
for (int i = 1; i < Capacity; i++)
|
|
{
|
|
string text = getName(Variables.ConstructSlot(Id, i));
|
|
if (text != null)
|
|
{
|
|
builder.Append(text);
|
|
builder.Append(GetValue(Id, i) switch
|
|
{
|
|
NullableFlowState.MaybeNull => "?",
|
|
NullableFlowState.MaybeDefault => "??",
|
|
_ => "!",
|
|
});
|
|
}
|
|
}
|
|
string? getName(int slot)
|
|
{
|
|
VariableIdentifier variableIdentifier = variables[slot];
|
|
string name = variableIdentifier.Symbol.Name;
|
|
int containingSlot = variableIdentifier.ContainingSlot;
|
|
if (containingSlot <= 0)
|
|
{
|
|
return name;
|
|
}
|
|
return getName(containingSlot) + "." + name;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed class LocalFunctionState : AbstractLocalFunctionState
|
|
{
|
|
public LocalState StartingState;
|
|
|
|
public LocalFunctionState(LocalState unreachableState)
|
|
: base(unreachableState.Clone(), unreachableState.Clone())
|
|
{
|
|
StartingState = unreachableState;
|
|
}
|
|
}
|
|
|
|
private sealed class NullabilityInfoTypeComparer : IEqualityComparer<(NullabilityInfo info, TypeSymbol? type)>
|
|
{
|
|
public static readonly NullabilityInfoTypeComparer Instance = new NullabilityInfoTypeComparer();
|
|
|
|
public bool Equals((NullabilityInfo info, TypeSymbol? type) x, (NullabilityInfo info, TypeSymbol? type) y)
|
|
{
|
|
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
|
|
if (((NullabilityInfo)(ref x.info)).Equals(y.info))
|
|
{
|
|
return SymbolEqualityComparer.ConsiderEverything.Equals(x.type, y.type);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public int GetHashCode((NullabilityInfo info, TypeSymbol? type) obj)
|
|
{
|
|
return obj.GetHashCode();
|
|
}
|
|
}
|
|
|
|
private sealed class ExpressionAndSymbolEqualityComparer : IEqualityComparer<(BoundNode? expr, Symbol symbol)>
|
|
{
|
|
internal static readonly ExpressionAndSymbolEqualityComparer Instance = new ExpressionAndSymbolEqualityComparer();
|
|
|
|
private ExpressionAndSymbolEqualityComparer()
|
|
{
|
|
}
|
|
|
|
public bool Equals((BoundNode? expr, Symbol symbol) x, (BoundNode? expr, Symbol symbol) y)
|
|
{
|
|
if (x.expr == y.expr)
|
|
{
|
|
return (object)x.symbol == y.symbol;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public int GetHashCode((BoundNode? expr, Symbol symbol) obj)
|
|
{
|
|
return Hash.Combine<BoundNode>(obj.expr, obj.symbol.GetHashCode());
|
|
}
|
|
}
|
|
|
|
private sealed class PlaceholderLocal : LocalSymbol
|
|
{
|
|
private readonly Symbol _containingSymbol;
|
|
|
|
private readonly TypeWithAnnotations _type;
|
|
|
|
private readonly object _identifier;
|
|
|
|
internal override SyntaxNode ScopeDesignatorOpt => null;
|
|
|
|
public override Symbol ContainingSymbol => _containingSymbol;
|
|
|
|
public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences => ImmutableArray<SyntaxReference>.Empty;
|
|
|
|
public override ImmutableArray<Location> Locations => ImmutableArray<Location>.Empty;
|
|
|
|
public override TypeWithAnnotations TypeWithAnnotations => _type;
|
|
|
|
internal override LocalDeclarationKind DeclarationKind => LocalDeclarationKind.None;
|
|
|
|
internal override SyntaxToken IdentifierToken
|
|
{
|
|
get
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs", 54);
|
|
}
|
|
}
|
|
|
|
internal override bool IsCompilerGenerated => true;
|
|
|
|
internal override bool IsImportedFromMetadata => false;
|
|
|
|
internal override bool IsPinned => false;
|
|
|
|
internal override bool IsKnownToReferToTempIfReferenceType => false;
|
|
|
|
public override RefKind RefKind => (RefKind)0;
|
|
|
|
internal override SynthesizedLocalKind SynthesizedKind
|
|
{
|
|
get
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs", 60);
|
|
}
|
|
}
|
|
|
|
internal override bool HasSourceLocation => false;
|
|
|
|
internal override ScopedKind Scope => (ScopedKind)0;
|
|
|
|
public PlaceholderLocal(Symbol containingSymbol, object identifier, TypeWithAnnotations type)
|
|
{
|
|
_containingSymbol = containingSymbol;
|
|
_type = type;
|
|
_identifier = identifier;
|
|
}
|
|
|
|
public override bool Equals(Symbol obj, TypeCompareKind compareKind)
|
|
{
|
|
if ((object)this == obj)
|
|
{
|
|
return true;
|
|
}
|
|
if (obj is PlaceholderLocal placeholderLocal)
|
|
{
|
|
return _identifier.Equals(placeholderLocal._identifier);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return _identifier.GetHashCode();
|
|
}
|
|
|
|
internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, BindingDiagnosticBag diagnostics = null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
internal override ImmutableBindingDiagnostic<AssemblySymbol> GetConstantValueDiagnostics(BoundExpression boundInitValue)
|
|
{
|
|
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
|
|
return ImmutableBindingDiagnostic<AssemblySymbol>.Empty;
|
|
}
|
|
|
|
internal override SyntaxNode GetDeclaratorSyntax()
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs", 63);
|
|
}
|
|
|
|
internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax)
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs", 72);
|
|
}
|
|
}
|
|
|
|
internal sealed class SnapshotManager
|
|
{
|
|
internal sealed class Builder
|
|
{
|
|
private readonly ImmutableDictionary<(BoundNode?, Symbol), Symbol>.Builder _updatedSymbolMap = ImmutableDictionary.CreateBuilder(ExpressionAndSymbolEqualityComparer.Instance, SymbolEqualityComparer.ConsiderEverything);
|
|
|
|
private readonly ArrayBuilder<SharedWalkerState> _walkerStates = ArrayBuilder<SharedWalkerState>.GetInstance();
|
|
|
|
private readonly SortedDictionary<int, Snapshot> _incrementalSnapshots = new SortedDictionary<int, Snapshot>();
|
|
|
|
private readonly PooledDictionary<Symbol, int> _symbolToSlot = PooledDictionary<Symbol, int>.GetInstance();
|
|
|
|
private int _currentWalkerSlot = -1;
|
|
|
|
internal SnapshotManager ToManagerAndFree()
|
|
{
|
|
_symbolToSlot.Free();
|
|
ImmutableArray<(int, Snapshot)> incrementalSnapshots = EnumerableExtensions.SelectAsArray<KeyValuePair<int, Snapshot>, (int, Snapshot)>((IReadOnlyCollection<KeyValuePair<int, Snapshot>>)_incrementalSnapshots, (Func<KeyValuePair<int, Snapshot>, (int, Snapshot)>)((KeyValuePair<int, Snapshot> kvp) => (kvp.Key, kvp.Value)));
|
|
ImmutableDictionary<(BoundNode, Symbol), Symbol> updatedSymbolsMap = _updatedSymbolMap.ToImmutable();
|
|
return new SnapshotManager(_walkerStates.ToImmutableAndFree(), incrementalSnapshots, updatedSymbolsMap);
|
|
}
|
|
|
|
internal int EnterNewWalker(Symbol symbol)
|
|
{
|
|
int currentWalkerSlot = _currentWalkerSlot;
|
|
if (((Dictionary<Symbol, int>)(object)_symbolToSlot).TryGetValue(symbol, out int value))
|
|
{
|
|
_currentWalkerSlot = value;
|
|
return currentWalkerSlot;
|
|
}
|
|
_currentWalkerSlot = ((Dictionary<Symbol, int>)(object)_symbolToSlot).Count;
|
|
((Dictionary<Symbol, int>)(object)_symbolToSlot).Add(symbol, _currentWalkerSlot);
|
|
return currentWalkerSlot;
|
|
}
|
|
|
|
internal void ExitWalker(SharedWalkerState stableState, int previousSlot)
|
|
{
|
|
_walkerStates.SetItem(_currentWalkerSlot, stableState);
|
|
_currentWalkerSlot = previousSlot;
|
|
}
|
|
|
|
internal void TakeIncrementalSnapshot(BoundNode? node, LocalState currentState)
|
|
{
|
|
if (node != null && !node.WasCompilerGenerated)
|
|
{
|
|
_incrementalSnapshots[node.Syntax.SpanStart] = new Snapshot(currentState.CreateSnapshot(), _currentWalkerSlot);
|
|
}
|
|
}
|
|
|
|
internal void SetUpdatedSymbol(BoundNode node, Symbol originalSymbol, Symbol updatedSymbol)
|
|
{
|
|
_updatedSymbolMap[GetKey(node, originalSymbol)] = updatedSymbol;
|
|
}
|
|
|
|
internal void RemoveSymbolIfPresent(BoundNode node, Symbol symbol)
|
|
{
|
|
_updatedSymbolMap.Remove(GetKey(node, symbol));
|
|
}
|
|
|
|
private static (BoundNode?, Symbol) GetKey(BoundNode node, Symbol symbol)
|
|
{
|
|
if (node is BoundLambda && symbol is LambdaSymbol)
|
|
{
|
|
return (null, symbol);
|
|
}
|
|
return (node, symbol);
|
|
}
|
|
}
|
|
|
|
private readonly ImmutableArray<SharedWalkerState> _walkerSharedStates;
|
|
|
|
private readonly ImmutableArray<(int position, Snapshot snapshot)> _incrementalSnapshots;
|
|
|
|
private readonly ImmutableDictionary<(BoundNode?, Symbol), Symbol> _updatedSymbolsMap;
|
|
|
|
private static readonly Func<(int position, Snapshot snapshot), int, int> BinarySearchComparer = ((int position, Snapshot snapshot) current, int target) => current.position.CompareTo(target);
|
|
|
|
private SnapshotManager(ImmutableArray<SharedWalkerState> walkerSharedStates, ImmutableArray<(int position, Snapshot snapshot)> incrementalSnapshots, ImmutableDictionary<(BoundNode?, Symbol), Symbol> updatedSymbolsMap)
|
|
{
|
|
_walkerSharedStates = walkerSharedStates;
|
|
_incrementalSnapshots = incrementalSnapshots;
|
|
_updatedSymbolsMap = updatedSymbolsMap;
|
|
}
|
|
|
|
internal (VariablesSnapshot, LocalStateSnapshot) GetSnapshot(int position)
|
|
{
|
|
Snapshot snapshotForPosition = GetSnapshotForPosition(position);
|
|
return (_walkerSharedStates[snapshotForPosition.SharedStateIndex].Variables, snapshotForPosition.VariableState);
|
|
}
|
|
|
|
internal TypeWithAnnotations? GetUpdatedTypeForLocalSymbol(SourceLocalSymbol symbol)
|
|
{
|
|
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
|
|
SyntaxToken identifierToken = symbol.IdentifierToken;
|
|
Snapshot snapshotForPosition = GetSnapshotForPosition(((SyntaxToken)(ref identifierToken)).SpanStart);
|
|
if (_walkerSharedStates[snapshotForPosition.SharedStateIndex].Variables.TryGetType(symbol, out var type))
|
|
{
|
|
return type;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal NamedTypeSymbol? GetUpdatedDelegateTypeForLambda(LambdaSymbol lambda)
|
|
{
|
|
if (_updatedSymbolsMap.TryGetValue((null, lambda), out Symbol value))
|
|
{
|
|
return (NamedTypeSymbol)value;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal bool TryGetUpdatedSymbol(BoundNode node, Symbol symbol, [NotNullWhen(true)] out Symbol? updatedSymbol)
|
|
{
|
|
return _updatedSymbolsMap.TryGetValue((node, symbol), out updatedSymbol);
|
|
}
|
|
|
|
private Snapshot GetSnapshotForPosition(int position)
|
|
{
|
|
int num = ImmutableArrayExtensions.BinarySearch<(int, Snapshot), int>(_incrementalSnapshots, position, BinarySearchComparer);
|
|
if (num < 0)
|
|
{
|
|
num = ~num - 1;
|
|
if (num < 0)
|
|
{
|
|
num = 0;
|
|
}
|
|
}
|
|
return _incrementalSnapshots[num].snapshot;
|
|
}
|
|
}
|
|
|
|
internal readonly struct SharedWalkerState
|
|
{
|
|
internal readonly VariablesSnapshot Variables;
|
|
|
|
internal SharedWalkerState(VariablesSnapshot variables)
|
|
{
|
|
Variables = variables;
|
|
}
|
|
}
|
|
|
|
private readonly struct Snapshot
|
|
{
|
|
internal readonly LocalStateSnapshot VariableState;
|
|
|
|
internal readonly int SharedStateIndex;
|
|
|
|
internal Snapshot(LocalStateSnapshot variableState, int sharedStateIndex)
|
|
{
|
|
VariableState = variableState;
|
|
SharedStateIndex = sharedStateIndex;
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
|
|
internal sealed class VariablesSnapshot
|
|
{
|
|
internal readonly int Id;
|
|
|
|
internal readonly VariablesSnapshot? Container;
|
|
|
|
internal readonly Symbol? Symbol;
|
|
|
|
internal readonly ImmutableArray<KeyValuePair<VariableIdentifier, int>> VariableSlot;
|
|
|
|
internal readonly ImmutableDictionary<Symbol, TypeWithAnnotations> VariableTypes;
|
|
|
|
internal VariablesSnapshot(int id, VariablesSnapshot? container, Symbol? symbol, ImmutableArray<KeyValuePair<VariableIdentifier, int>> variableSlot, ImmutableDictionary<Symbol, TypeWithAnnotations> variableTypes)
|
|
{
|
|
Id = id;
|
|
Container = container;
|
|
Symbol = symbol;
|
|
VariableSlot = variableSlot;
|
|
VariableTypes = variableTypes;
|
|
}
|
|
|
|
internal bool TryGetType(Symbol symbol, out TypeWithAnnotations type)
|
|
{
|
|
return VariableTypes.TryGetValue(symbol, out type);
|
|
}
|
|
|
|
private string GetDebuggerDisplay()
|
|
{
|
|
object arg = ((object)Symbol) ?? ((object)"<null>");
|
|
return $"Id={Id}, Symbol={arg}, Count={VariableSlot.Length}";
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
|
|
internal sealed class Variables
|
|
{
|
|
private const int MaxSlotDepth = 5;
|
|
|
|
private const int IdOffset = 16;
|
|
|
|
private const int IdMask = 32767;
|
|
|
|
private const int IndexMask = 65535;
|
|
|
|
internal readonly int Id;
|
|
|
|
internal readonly Variables? Container;
|
|
|
|
internal readonly Symbol? Symbol;
|
|
|
|
private readonly PooledDictionary<VariableIdentifier, int> _variableSlot = PooledDictionary<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int>.GetInstance();
|
|
|
|
private readonly PooledDictionary<Symbol, TypeWithAnnotations> _variableTypes = SpecializedSymbolCollections.GetPooledSymbolDictionaryInstance<Symbol, TypeWithAnnotations>();
|
|
|
|
private readonly ArrayBuilder<VariableIdentifier> _variableBySlot = ArrayBuilder<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier>.GetInstance(1, default(LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier));
|
|
|
|
internal VariableIdentifier this[int slot]
|
|
{
|
|
get
|
|
{
|
|
var (id, num) = DeconstructSlot(slot);
|
|
return GetVariablesForId(id)._variableBySlot[num];
|
|
}
|
|
}
|
|
|
|
internal int NextAvailableIndex => _variableBySlot.Count;
|
|
|
|
internal static Variables Create(Symbol? symbol)
|
|
{
|
|
return new Variables(0, null, symbol);
|
|
}
|
|
|
|
internal static Variables Create(VariablesSnapshot snapshot)
|
|
{
|
|
Variables container = ((snapshot.Container == null) ? null : Create(snapshot.Container));
|
|
Variables variables = new Variables(snapshot.Id, container, snapshot.Symbol);
|
|
variables.Populate(snapshot);
|
|
return variables;
|
|
}
|
|
|
|
private int GetNextId()
|
|
{
|
|
return Id + 1;
|
|
}
|
|
|
|
private void Populate(VariablesSnapshot snapshot)
|
|
{
|
|
_variableBySlot.AddMany(default(LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier), snapshot.VariableSlot.Length);
|
|
ImmutableArray<KeyValuePair<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int>>.Enumerator enumerator = snapshot.VariableSlot.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
KeyValuePair<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int> current = enumerator.Current;
|
|
LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier key = current.Key;
|
|
int value = current.Value;
|
|
((Dictionary<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int>)(object)_variableSlot).Add(key, value);
|
|
_variableBySlot[value] = key;
|
|
}
|
|
foreach (KeyValuePair<Symbol, TypeWithAnnotations> variableType in snapshot.VariableTypes)
|
|
{
|
|
((Dictionary<Symbol, TypeWithAnnotations>)(object)_variableTypes).Add(variableType.Key, variableType.Value);
|
|
}
|
|
}
|
|
|
|
private Variables(int id, Variables? container, Symbol? symbol)
|
|
{
|
|
Id = id;
|
|
Container = container;
|
|
Symbol = symbol;
|
|
}
|
|
|
|
internal void Free()
|
|
{
|
|
Container?.Free();
|
|
_variableBySlot.Free();
|
|
_variableTypes.Free();
|
|
_variableSlot.Free();
|
|
}
|
|
|
|
internal VariablesSnapshot CreateSnapshot()
|
|
{
|
|
return new VariablesSnapshot(Id, Container?.CreateSnapshot(), Symbol, ImmutableArray.CreateRange((IEnumerable<KeyValuePair<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int>>)_variableSlot), ImmutableDictionary.CreateRange((IEnumerable<KeyValuePair<Symbol, TypeWithAnnotations>>)_variableTypes));
|
|
}
|
|
|
|
internal Variables CreateNestedMethodScope(MethodSymbol method)
|
|
{
|
|
return new Variables(GetNextId(), this, method);
|
|
}
|
|
|
|
internal int RootSlot(int slot)
|
|
{
|
|
while (true)
|
|
{
|
|
int containingSlot = this[slot].ContainingSlot;
|
|
if (containingSlot == 0)
|
|
{
|
|
break;
|
|
}
|
|
slot = containingSlot;
|
|
}
|
|
return slot;
|
|
}
|
|
|
|
internal bool TryGetValue(VariableIdentifier identifier, out int slot)
|
|
{
|
|
return GetVariablesForVariable(identifier).TryGetValueInternal(identifier, out slot);
|
|
}
|
|
|
|
private bool TryGetValueInternal(VariableIdentifier identifier, out int slot)
|
|
{
|
|
if (((Dictionary<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int>)(object)_variableSlot).TryGetValue(identifier, out int value))
|
|
{
|
|
slot = ConstructSlot(Id, value);
|
|
return true;
|
|
}
|
|
slot = -1;
|
|
return false;
|
|
}
|
|
|
|
internal int Add(VariableIdentifier identifier)
|
|
{
|
|
return GetVariablesForVariable(identifier).AddInternal(identifier);
|
|
}
|
|
|
|
private int AddInternal(VariableIdentifier identifier)
|
|
{
|
|
if (getSlotDepth(identifier.ContainingSlot) >= 5)
|
|
{
|
|
return -1;
|
|
}
|
|
int nextAvailableIndex = NextAvailableIndex;
|
|
if (nextAvailableIndex > 65535)
|
|
{
|
|
return -1;
|
|
}
|
|
((Dictionary<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int>)(object)_variableSlot).Add(identifier, nextAvailableIndex);
|
|
_variableBySlot.Add(identifier);
|
|
return ConstructSlot(Id, nextAvailableIndex);
|
|
int getSlotDepth(int slot)
|
|
{
|
|
int num = 0;
|
|
while (slot > 0)
|
|
{
|
|
num++;
|
|
int item = DeconstructSlot(slot).Index;
|
|
slot = _variableBySlot[item].ContainingSlot;
|
|
}
|
|
return num;
|
|
}
|
|
}
|
|
|
|
internal bool TryGetType(Symbol symbol, out TypeWithAnnotations type)
|
|
{
|
|
return ((Dictionary<Symbol, TypeWithAnnotations>)(object)GetVariablesContainingSymbol(symbol)._variableTypes).TryGetValue(symbol, out type);
|
|
}
|
|
|
|
internal void SetType(Symbol symbol, TypeWithAnnotations type)
|
|
{
|
|
((Dictionary<Symbol, TypeWithAnnotations>)(object)GetVariablesContainingSymbol(symbol)._variableTypes)[symbol] = type;
|
|
}
|
|
|
|
internal int GetTotalVariableCount()
|
|
{
|
|
return (Container?.GetTotalVariableCount() ?? 0) + ((Dictionary<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int>)(object)_variableSlot).Count;
|
|
}
|
|
|
|
internal void GetMembers(ArrayBuilder<(VariableIdentifier, int)> builder, int containingSlot)
|
|
{
|
|
(int Id, int Index) tuple = DeconstructSlot(containingSlot);
|
|
int item = tuple.Id;
|
|
int item2 = tuple.Index;
|
|
ArrayBuilder<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier> variableBySlot = GetVariablesForId(item)._variableBySlot;
|
|
for (item2++; item2 < variableBySlot.Count; item2++)
|
|
{
|
|
LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier item3 = variableBySlot[item2];
|
|
if (item3.ContainingSlot == containingSlot)
|
|
{
|
|
builder.Add((item3, ConstructSlot(item, item2)));
|
|
}
|
|
}
|
|
}
|
|
|
|
private Variables GetVariablesForVariable(VariableIdentifier identifier)
|
|
{
|
|
int containingSlot = identifier.ContainingSlot;
|
|
if (containingSlot > 0)
|
|
{
|
|
return GetVariablesForId(DeconstructSlot(containingSlot).Id);
|
|
}
|
|
return GetVariablesContainingSymbol(identifier.Symbol);
|
|
}
|
|
|
|
private Variables GetVariablesContainingSymbol(Symbol symbol)
|
|
{
|
|
if ((symbol is LocalSymbol || symbol is ParameterSymbol) && symbol.ContainingSymbol is MethodSymbol method)
|
|
{
|
|
Variables variablesForMethodScope = GetVariablesForMethodScope(method);
|
|
if (variablesForMethodScope != null)
|
|
{
|
|
return variablesForMethodScope;
|
|
}
|
|
}
|
|
return GetRootScope();
|
|
}
|
|
|
|
internal Variables GetRootScope()
|
|
{
|
|
Variables variables = this;
|
|
while (true)
|
|
{
|
|
Variables container = variables.Container;
|
|
if (container == null)
|
|
{
|
|
break;
|
|
}
|
|
variables = container;
|
|
}
|
|
return variables;
|
|
}
|
|
|
|
private Variables? GetVariablesForId(int id)
|
|
{
|
|
Variables variables = this;
|
|
do
|
|
{
|
|
if (variables.Id == id)
|
|
{
|
|
return variables;
|
|
}
|
|
variables = variables.Container;
|
|
}
|
|
while (variables != null);
|
|
return null;
|
|
}
|
|
|
|
internal Variables? GetVariablesForMethodScope(MethodSymbol method)
|
|
{
|
|
method = method.PartialImplementationPart ?? method;
|
|
Variables variables = this;
|
|
do
|
|
{
|
|
if ((object)method == variables.Symbol)
|
|
{
|
|
return variables;
|
|
}
|
|
variables = variables.Container;
|
|
}
|
|
while (variables != null);
|
|
return null;
|
|
}
|
|
|
|
internal static int ConstructSlot(int id, int index)
|
|
{
|
|
if (index >= 0)
|
|
{
|
|
return (id << 16) | index;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
internal static (int Id, int Index) DeconstructSlot(int slot)
|
|
{
|
|
if (slot >= 0)
|
|
{
|
|
return (Id: (slot >> 16) & 0x7FFF, Index: slot & 0xFFFF);
|
|
}
|
|
return (Id: 0, Index: slot);
|
|
}
|
|
|
|
private string GetDebuggerDisplay()
|
|
{
|
|
object arg = ((object)Symbol) ?? ((object)"<null>");
|
|
return $"Id={Id}, Symbol={arg}, Count={((Dictionary<LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int>)(object)_variableSlot).Count}";
|
|
}
|
|
}
|
|
|
|
private struct PossiblyConditionalState
|
|
{
|
|
public LocalState State;
|
|
|
|
public LocalState StateWhenTrue;
|
|
|
|
public LocalState StateWhenFalse;
|
|
|
|
public bool IsConditionalState;
|
|
|
|
public PossiblyConditionalState(LocalState stateWhenTrue, LocalState stateWhenFalse)
|
|
{
|
|
StateWhenTrue = stateWhenTrue.Clone();
|
|
StateWhenFalse = stateWhenFalse.Clone();
|
|
IsConditionalState = true;
|
|
State = default(LocalState);
|
|
}
|
|
|
|
public PossiblyConditionalState(LocalState state)
|
|
{
|
|
StateWhenTrue = (StateWhenFalse = default(LocalState));
|
|
IsConditionalState = false;
|
|
State = state.Clone();
|
|
}
|
|
|
|
public static PossiblyConditionalState Create(NullableWalker nullableWalker)
|
|
{
|
|
if (!nullableWalker.IsConditionalState)
|
|
{
|
|
return new PossiblyConditionalState(nullableWalker.State);
|
|
}
|
|
return new PossiblyConditionalState(nullableWalker.StateWhenTrue, nullableWalker.StateWhenFalse);
|
|
}
|
|
|
|
public PossiblyConditionalState Clone()
|
|
{
|
|
if (!IsConditionalState)
|
|
{
|
|
return new PossiblyConditionalState(State);
|
|
}
|
|
return new PossiblyConditionalState(StateWhenTrue, StateWhenFalse);
|
|
}
|
|
}
|
|
|
|
private Variables _variables;
|
|
|
|
private readonly Binder _binder;
|
|
|
|
private readonly Conversions _conversions;
|
|
|
|
private readonly bool _useConstructorExitWarnings;
|
|
|
|
private bool _useDelegateInvokeParameterTypes;
|
|
|
|
private bool _useDelegateInvokeReturnType;
|
|
|
|
private MethodSymbol? _delegateInvokeMethod;
|
|
|
|
private ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>? _returnTypesOpt;
|
|
|
|
private static readonly TypeWithState _invalidType = TypeWithState.Create(new UnsupportedMetadataTypeSymbol(), NullableFlowState.NotNull);
|
|
|
|
private readonly ImmutableDictionary<BoundExpression, (NullabilityInfo Info, TypeSymbol? Type)>.Builder? _analyzedNullabilityMapOpt;
|
|
|
|
private readonly SnapshotManager.Builder? _snapshotBuilderOpt;
|
|
|
|
private bool _disableNullabilityAnalysis;
|
|
|
|
private PooledDictionary<BoundExpression, TypeWithState>? _methodGroupReceiverMapOpt;
|
|
|
|
private PooledDictionary<BoundValuePlaceholderBase, (BoundExpression? Replacement, VisitResult Result)>? _resultForPlaceholdersOpt;
|
|
|
|
private PooledDictionary<MethodSymbol, Variables>? _nestedFunctionVariables;
|
|
|
|
private PooledDictionary<BoundExpression, Func<TypeWithAnnotations, TypeWithState>>? _targetTypedAnalysisCompletionOpt;
|
|
|
|
private readonly bool _isSpeculative;
|
|
|
|
private readonly bool _hasInitialState;
|
|
|
|
private readonly MethodSymbol? _baseOrThisInitializer;
|
|
|
|
private VisitResult _visitResult;
|
|
|
|
private VisitResult _currentConditionalReceiverVisitResult;
|
|
|
|
private PooledDictionary<object, PlaceholderLocal>? _placeholderLocalsOpt;
|
|
|
|
private bool _disableDiagnostics;
|
|
|
|
private bool _expressionIsRead = true;
|
|
|
|
private int _lastConditionalAccessSlot = -1;
|
|
|
|
private PooledDictionary<BoundExpression, Func<TypeWithAnnotations, TypeWithState>> TargetTypedAnalysisCompletion => _targetTypedAnalysisCompletionOpt ?? (_targetTypedAnalysisCompletionOpt = PooledDictionary<BoundExpression, Func<TypeWithAnnotations, TypeWithState>>.GetInstance());
|
|
|
|
private TypeWithState ResultType => _visitResult.RValueType;
|
|
|
|
private TypeWithAnnotations LvalueResultType => _visitResult.LValueType;
|
|
|
|
private bool IsAnalyzingAttribute => methodMainNode.Kind == BoundKind.Attribute;
|
|
|
|
public sealed override bool AwaitUsingAndForeachAddsPendingBranch => true;
|
|
|
|
private void SetResultType(BoundExpression? expression, TypeWithState type, bool updateAnalyzedNullability = true)
|
|
{
|
|
SetResult(expression, type, type.ToTypeWithAnnotations(compilation), updateAnalyzedNullability);
|
|
}
|
|
|
|
private void SetAnalyzedNullability(BoundExpression? expression, TypeWithState type)
|
|
{
|
|
SetAnalyzedNullability(expression, type, type.ToTypeWithAnnotations(compilation));
|
|
}
|
|
|
|
private void UseRvalueOnly(BoundExpression? expression)
|
|
{
|
|
SetResult(expression, ResultType, ResultType.ToTypeWithAnnotations(compilation), updateAnalyzedNullability: true, false);
|
|
}
|
|
|
|
private void SetLvalueResultType(BoundExpression? expression, TypeWithAnnotations type)
|
|
{
|
|
SetResult(expression, type.ToTypeWithState(), type);
|
|
}
|
|
|
|
private void UseLvalueOnly(BoundExpression? expression)
|
|
{
|
|
SetResult(expression, LvalueResultType.ToTypeWithState(), LvalueResultType, updateAnalyzedNullability: true, true);
|
|
}
|
|
|
|
private void SetInvalidResult()
|
|
{
|
|
SetResult(null, _invalidType, _invalidType.ToTypeWithAnnotations(compilation), updateAnalyzedNullability: false);
|
|
}
|
|
|
|
private void SetResult(BoundExpression? expression, TypeWithState resultType, TypeWithAnnotations lvalueType, bool updateAnalyzedNullability = true, bool? isLvalue = null)
|
|
{
|
|
_visitResult = new VisitResult(resultType, lvalueType);
|
|
if (updateAnalyzedNullability)
|
|
{
|
|
SetAnalyzedNullability(expression, _visitResult, isLvalue);
|
|
}
|
|
}
|
|
|
|
private void SetAnalyzedNullability(BoundExpression? expression, TypeWithState resultType, TypeWithAnnotations lvalueType, bool? isLvalue = null)
|
|
{
|
|
SetAnalyzedNullability(expression, new VisitResult(resultType, lvalueType), isLvalue);
|
|
}
|
|
|
|
private bool ShouldMakeNotNullRvalue(BoundExpression node)
|
|
{
|
|
if (!node.IsSuppressed && !node.HasAnyErrors)
|
|
{
|
|
return !IsReachable();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void SetAnalyzedNullability(BoundExpression? expr, VisitResult result, bool? isLvalue = null)
|
|
{
|
|
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
|
|
if (expr != null && !_disableNullabilityAnalysis && _analyzedNullabilityMapOpt != null)
|
|
{
|
|
ImmutableDictionary<BoundExpression, (NullabilityInfo Info, TypeSymbol? Type)>.Builder? analyzedNullabilityMapOpt = _analyzedNullabilityMapOpt;
|
|
NullabilityInfo item = new NullabilityInfo(result.LValueType.ToPublicAnnotation(), result.RValueType.State.ToPublicFlowState());
|
|
TypeSymbol? type = expr.Type;
|
|
analyzedNullabilityMapOpt[expr] = (item, ((object)type != null && type.Equals(result.RValueType.Type, (TypeCompareKind)63)) ? result.RValueType.Type : expr.Type);
|
|
}
|
|
}
|
|
|
|
protected override void Free()
|
|
{
|
|
_nestedFunctionVariables?.Free();
|
|
_resultForPlaceholdersOpt?.Free();
|
|
_methodGroupReceiverMapOpt?.Free();
|
|
_placeholderLocalsOpt?.Free();
|
|
_variables.Free();
|
|
_targetTypedAnalysisCompletionOpt?.Free();
|
|
base.Free();
|
|
}
|
|
|
|
private NullableWalker(CSharpCompilation compilation, Symbol? symbol, bool useConstructorExitWarnings, bool useDelegateInvokeParameterTypes, bool useDelegateInvokeReturnType, MethodSymbol? delegateInvokeMethodOpt, BoundNode node, Binder binder, Conversions conversions, Variables? variables, MethodSymbol? baseOrThisInitializer, ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>? returnTypesOpt, ImmutableDictionary<BoundExpression, (NullabilityInfo, TypeSymbol?)>.Builder? analyzedNullabilityMapOpt, SnapshotManager.Builder? snapshotBuilderOpt, bool isSpeculative = false)
|
|
: base(compilation, symbol, node, EmptyStructTypeCache.CreatePrecise(), true)
|
|
{
|
|
_variables = variables ?? Variables.Create(symbol);
|
|
_binder = binder;
|
|
_conversions = conversions.WithNullability(includeNullability: true);
|
|
_useConstructorExitWarnings = useConstructorExitWarnings;
|
|
_useDelegateInvokeParameterTypes = useDelegateInvokeParameterTypes;
|
|
_useDelegateInvokeReturnType = useDelegateInvokeReturnType;
|
|
_delegateInvokeMethod = delegateInvokeMethodOpt;
|
|
_analyzedNullabilityMapOpt = analyzedNullabilityMapOpt;
|
|
_returnTypesOpt = returnTypesOpt;
|
|
_snapshotBuilderOpt = snapshotBuilderOpt;
|
|
_isSpeculative = isSpeculative;
|
|
_hasInitialState = variables != null;
|
|
_baseOrThisInitializer = baseOrThisInitializer;
|
|
}
|
|
|
|
public string GetDebuggerDisplay()
|
|
{
|
|
if (IsConditionalState)
|
|
{
|
|
return "{" + GetType().Name + " WhenTrue:" + Dump(StateWhenTrue) + " WhenFalse:" + Dump(StateWhenFalse) + "}";
|
|
}
|
|
return "{" + GetType().Name + " " + Dump(State) + "}";
|
|
}
|
|
|
|
protected override void EnsureSufficientExecutionStack(int recursionDepth)
|
|
{
|
|
if (recursionDepth > 20 && compilation.TestOnlyCompilationData is NullableAnalysisData { MaxRecursionDepth: var maxRecursionDepth } && maxRecursionDepth > 0 && recursionDepth > maxRecursionDepth)
|
|
{
|
|
throw new InsufficientExecutionStackException();
|
|
}
|
|
base.EnsureSufficientExecutionStack(recursionDepth);
|
|
}
|
|
|
|
protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
protected override bool TryGetVariable(VariableIdentifier identifier, out int slot)
|
|
{
|
|
return _variables.TryGetValue(identifier, out slot);
|
|
}
|
|
|
|
protected override int AddVariable(VariableIdentifier identifier)
|
|
{
|
|
return _variables.Add(identifier);
|
|
}
|
|
|
|
[Conditional("DEBUG")]
|
|
private void AssertNoPlaceholderReplacements()
|
|
{
|
|
_ = _resultForPlaceholdersOpt;
|
|
}
|
|
|
|
private void AddPlaceholderReplacement(BoundValuePlaceholderBase placeholder, BoundExpression? expression, VisitResult result)
|
|
{
|
|
if (_resultForPlaceholdersOpt == null)
|
|
{
|
|
_resultForPlaceholdersOpt = PooledDictionary<BoundValuePlaceholderBase, (BoundExpression, VisitResult)>.GetInstance();
|
|
}
|
|
((Dictionary<BoundValuePlaceholderBase, (BoundExpression, VisitResult)>)(object)_resultForPlaceholdersOpt).Add(placeholder, (expression, result));
|
|
}
|
|
|
|
private void RemovePlaceholderReplacement(BoundValuePlaceholderBase placeholder)
|
|
{
|
|
((Dictionary<BoundValuePlaceholderBase, (BoundExpression, VisitResult)>)(object)_resultForPlaceholdersOpt).Remove(placeholder);
|
|
}
|
|
|
|
[Conditional("DEBUG")]
|
|
private static void AssertPlaceholderAllowedWithoutRegistration(BoundValuePlaceholderBase placeholder)
|
|
{
|
|
switch (placeholder.Kind)
|
|
{
|
|
case BoundKind.DeconstructValuePlaceholder:
|
|
case BoundKind.AwaitableValuePlaceholder:
|
|
case BoundKind.ObjectOrCollectionValuePlaceholder:
|
|
case BoundKind.ImplicitIndexerValuePlaceholder:
|
|
case BoundKind.InterpolatedStringHandlerPlaceholder:
|
|
case BoundKind.InterpolatedStringArgumentPlaceholder:
|
|
return;
|
|
}
|
|
throw ExceptionUtilities.UnexpectedValue((object)placeholder.Kind);
|
|
}
|
|
|
|
protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
|
|
{
|
|
if (_returnTypesOpt != null)
|
|
{
|
|
_returnTypesOpt.Clear();
|
|
}
|
|
base.Diagnostics.Clear();
|
|
regionPlace = RegionPlace.Before;
|
|
if (!_isSpeculative)
|
|
{
|
|
ParameterSymbol methodThisParameter = base.MethodThisParameter;
|
|
EnterParameters();
|
|
if ((object)methodThisParameter != null)
|
|
{
|
|
EnterParameter(methodThisParameter, methodThisParameter.TypeWithAnnotations);
|
|
}
|
|
makeNotNullMembersMaybeNull();
|
|
_snapshotBuilderOpt?.TakeIncrementalSnapshot(methodMainNode, State);
|
|
}
|
|
ImmutableArray<AbstractFlowPass<LocalState, LocalFunctionState>.PendingBranch> result = base.Scan(ref badRegion);
|
|
MethodSymbol obj = _symbol as MethodSymbol;
|
|
if ((object)obj == null || !obj.IsConstructor() || _useConstructorExitWarnings)
|
|
{
|
|
EnforceDoesNotReturn(null);
|
|
enforceMemberNotNull(null, State);
|
|
EnforceParameterNotNullOnExit(null, State);
|
|
ImmutableArray<AbstractFlowPass<LocalState, LocalFunctionState>.PendingBranch>.Enumerator enumerator = result.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
AbstractFlowPass<LocalState, LocalFunctionState>.PendingBranch current = enumerator.Current;
|
|
enforceMemberNotNull(current.Branch.Syntax, current.State);
|
|
if (current.Branch is BoundReturnStatement boundReturnStatement)
|
|
{
|
|
EnforceParameterNotNullOnExit(boundReturnStatement.Syntax, current.State);
|
|
EnforceNotNullWhenForPendingReturn(current, boundReturnStatement);
|
|
enforceMemberNotNullWhenForPendingReturn(current, boundReturnStatement);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
void checkMemberStateOnConstructorExit(MethodSymbol constructor, Symbol member, LocalState state, int thisSlot, Location? exitLocation, ImmutableArray<string> membersWithStateEnforcedByRequiredMembers, bool forcePropertyAnalysis)
|
|
{
|
|
//IL_0155: Unknown result type (might be due to invalid IL or missing references)
|
|
bool flag = !constructor.RequiresInstanceReceiver();
|
|
if (member.IsStatic == flag && (!LocalDataFlowPass<LocalState, LocalFunctionState>.HasInitializer(member) || !constructor.IncludeFieldInitializersInBody()))
|
|
{
|
|
FieldSymbol fieldSymbol2;
|
|
Symbol symbol;
|
|
TypeWithAnnotations typeWithAnnotations;
|
|
if (!(member is FieldSymbol fieldSymbol))
|
|
{
|
|
if (!(member is EventSymbol eventSymbol))
|
|
{
|
|
if (!(member is PropertySymbol propertySymbol) || !forcePropertyAnalysis)
|
|
{
|
|
return;
|
|
}
|
|
typeWithAnnotations = propertySymbol.TypeWithAnnotations;
|
|
fieldSymbol2 = null;
|
|
symbol = propertySymbol;
|
|
}
|
|
else
|
|
{
|
|
typeWithAnnotations = eventSymbol.TypeWithAnnotations;
|
|
fieldSymbol2 = eventSymbol.AssociatedField;
|
|
symbol = eventSymbol;
|
|
if ((object)fieldSymbol2 == null)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
typeWithAnnotations = fieldSymbol.TypeWithAnnotations;
|
|
fieldSymbol2 = fieldSymbol;
|
|
symbol = (Symbol)(((object)(fieldSymbol.AssociatedSymbol as PropertySymbol)) ?? ((object)fieldSymbol));
|
|
}
|
|
if (((object)fieldSymbol2 == null || !fieldSymbol2.IsConst) && !typeWithAnnotations.Type.IsValueType && !typeWithAnnotations.Type.IsErrorType() && ((!symbol.IsRequired() && !membersWithStateEnforcedByRequiredMembers.Contains(symbol.Name)) || !constructor.ShouldCheckRequiredMembers()))
|
|
{
|
|
FlowAnalysisAnnotations flowAnalysisAnnotations = symbol.GetFlowAnalysisAnnotations();
|
|
if ((flowAnalysisAnnotations & FlowAnalysisAnnotations.AllowNull) == 0)
|
|
{
|
|
typeWithAnnotations = ApplyUnconditionalAnnotations(typeWithAnnotations, flowAnalysisAnnotations);
|
|
if (typeWithAnnotations.NullableAnnotation.IsNotAnnotated())
|
|
{
|
|
int orCreateSlot = GetOrCreateSlot(symbol, thisSlot);
|
|
if (orCreateSlot >= 0)
|
|
{
|
|
NullableFlowState state2 = GetState(ref state, orCreateSlot);
|
|
NullableFlowState nullableFlowState = ((!typeWithAnnotations.Type.IsPossiblyNullableReferenceTypeTypeParameter() || (flowAnalysisAnnotations & FlowAnalysisAnnotations.NotNull) != FlowAnalysisAnnotations.None) ? NullableFlowState.MaybeNull : NullableFlowState.MaybeDefault);
|
|
if ((int)state2 >= (int)nullableFlowState)
|
|
{
|
|
CSDiagnosticInfo info = new CSDiagnosticInfo(ErrorCode.WRN_UninitializedNonNullableField, new object[2]
|
|
{
|
|
symbol.Kind.Localize(),
|
|
symbol.Name
|
|
}, ImmutableArray<Symbol>.Empty, symbol.Locations);
|
|
base.Diagnostics.Add((DiagnosticInfo)(object)info, exitLocation ?? symbol.GetFirstLocationOrNone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void enforceMemberNotNull(SyntaxNode? syntaxOpt, LocalState state)
|
|
{
|
|
//IL_0152: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0157: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_015b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0160: Unknown result type (might be due to invalid IL or missing references)
|
|
if (state.Reachable)
|
|
{
|
|
MethodSymbol methodSymbol = _symbol as MethodSymbol;
|
|
if ((object)methodSymbol != null)
|
|
{
|
|
if (methodSymbol.IsConstructor())
|
|
{
|
|
int thisSlot = 0;
|
|
if (methodSymbol.RequiresInstanceReceiver)
|
|
{
|
|
methodSymbol.TryGetThisParameter(out var thisParameter);
|
|
thisSlot = GetOrCreateSlot(thisParameter);
|
|
}
|
|
Location exitLocation = (methodSymbol.DeclaringSyntaxReferences.IsEmpty ? null : methodSymbol.TryGetFirstLocation());
|
|
bool flag = methodSymbol.ShouldCheckRequiredMembers();
|
|
ImmutableArray<string> membersWithStateEnforcedByRequiredMembers = (flag ? ImmutableArrayExtensions.SelectManyAsArray<Symbol, string>(methodSymbol.ContainingType.GetMembersUnordered(), (Func<Symbol, bool>)((Symbol symbol2) => symbol2 is PropertySymbol propertySymbol && propertySymbol.IsRequired), (Func<Symbol, IEnumerable<string>>)delegate(Symbol symbol2)
|
|
{
|
|
PropertySymbol propertySymbol = (PropertySymbol)symbol2;
|
|
return propertySymbol.SetMethod?.NotNullMembers ?? propertySymbol.NotNullMembers;
|
|
}) : ImmutableArray<string>.Empty);
|
|
PooledHashSet<Symbol> instance = PooledHashSet<Symbol>.GetInstance();
|
|
ImmutableArray<Symbol>.Enumerator enumerator2 = methodSymbol.ContainingType.GetMembersUnordered().GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
Symbol current2 = enumerator2.Current;
|
|
bool forcePropertyAnalysis = !flag && !(current2 is SourcePropertySymbolBase { BackingField: not null }) && current2.IsRequired();
|
|
checkMemberStateOnConstructorExit(methodSymbol, current2, state, thisSlot, exitLocation, membersWithStateEnforcedByRequiredMembers, forcePropertyAnalysis);
|
|
}
|
|
MethodSymbol? baseOrThisInitializer = GetBaseOrThisInitializer();
|
|
if ((object)baseOrThisInitializer != null && baseOrThisInitializer.ShouldCheckRequiredMembers() && !flag)
|
|
{
|
|
NamedTypeSymbol baseTypeNoUseSiteDiagnostics = methodSymbol.ContainingType.BaseTypeNoUseSiteDiagnostics;
|
|
if ((object)baseTypeNoUseSiteDiagnostics != null)
|
|
{
|
|
Enumerator<string, Symbol> enumerator3 = baseTypeNoUseSiteDiagnostics.AllRequiredMembers.GetEnumerator();
|
|
try
|
|
{
|
|
string text = default(string);
|
|
Symbol symbol = default(Symbol);
|
|
while (enumerator3.MoveNext())
|
|
{
|
|
KeyValuePairUtil.Deconstruct<string, Symbol>(enumerator3.Current, ref text, ref symbol);
|
|
Symbol member = symbol;
|
|
checkMemberStateOnConstructorExit(methodSymbol, member, state, thisSlot, exitLocation, ImmutableArray<string>.Empty, forcePropertyAnalysis: true);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
((IDisposable)enumerator3/*cast due to constrained. prefix*/).Dispose();
|
|
}
|
|
}
|
|
}
|
|
instance.Free();
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
ImmutableArray<string>.Enumerator enumerator4 = methodSymbol.NotNullMembers.GetEnumerator();
|
|
while (enumerator4.MoveNext())
|
|
{
|
|
string current3 = enumerator4.Current;
|
|
enforceMemberNotNullOnMember(syntaxOpt, state, methodSymbol, current3);
|
|
}
|
|
methodSymbol = methodSymbol.OverriddenMethod;
|
|
}
|
|
while (methodSymbol != null);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void enforceMemberNotNullOnMember(SyntaxNode? syntaxOpt, LocalState state, MethodSymbol method, string memberName)
|
|
{
|
|
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
|
|
ImmutableArray<Symbol>.Enumerator enumerator2 = method.ContainingType.GetMembers(memberName).GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
Symbol current2 = enumerator2.Current;
|
|
if (memberHasBadState(current2, state))
|
|
{
|
|
DiagnosticBag diagnostics = base.Diagnostics;
|
|
object obj2 = ((syntaxOpt != null) ? syntaxOpt.GetLocation() : null);
|
|
if (obj2 == null)
|
|
{
|
|
SyntaxToken lastToken = methodMainNode.Syntax.GetLastToken(false, false, false, false);
|
|
obj2 = ((SyntaxToken)(ref lastToken)).GetLocation();
|
|
}
|
|
diagnostics.Add(ErrorCode.WRN_MemberNotNull, (Location)obj2, current2.Name);
|
|
}
|
|
}
|
|
}
|
|
void enforceMemberNotNullWhen(SyntaxNode? syntaxOpt, bool sense, LocalState state)
|
|
{
|
|
if (_symbol is MethodSymbol methodSymbol)
|
|
{
|
|
ImmutableArray<string>.Enumerator enumerator2 = (sense ? methodSymbol.NotNullWhenTrueMembers : methodSymbol.NotNullWhenFalseMembers).GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
string current2 = enumerator2.Current;
|
|
ImmutableArray<Symbol>.Enumerator enumerator3 = methodSymbol.ContainingType.GetMembers(current2).GetEnumerator();
|
|
while (enumerator3.MoveNext())
|
|
{
|
|
Symbol current3 = enumerator3.Current;
|
|
reportMemberIfBadConditionalState(syntaxOpt, sense, current3, state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void enforceMemberNotNullWhenForPendingReturn(PendingBranch pendingReturn, BoundReturnStatement returnStatement)
|
|
{
|
|
if (pendingReturn.IsConditionalState)
|
|
{
|
|
BoundExpression expressionOpt = returnStatement.ExpressionOpt;
|
|
if (expressionOpt != null)
|
|
{
|
|
ConstantValue constantValueOpt = expressionOpt.ConstantValueOpt;
|
|
if (constantValueOpt != null && constantValueOpt.IsBoolean)
|
|
{
|
|
bool booleanValue = constantValueOpt.BooleanValue;
|
|
enforceMemberNotNullWhen(returnStatement.Syntax, booleanValue, pendingReturn.State);
|
|
return;
|
|
}
|
|
}
|
|
if (pendingReturn.StateWhenTrue.Reachable && pendingReturn.StateWhenFalse.Reachable && _symbol is MethodSymbol { NotNullWhenTrueMembers: var notNullWhenTrueMembers } methodSymbol)
|
|
{
|
|
ImmutableArray<string>.Enumerator enumerator2 = notNullWhenTrueMembers.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
string current2 = enumerator2.Current;
|
|
enforceMemberNotNullWhenIfAffected(returnStatement.Syntax, sense: true, methodSymbol.ContainingType.GetMembers(current2), pendingReturn.StateWhenTrue, pendingReturn.StateWhenFalse);
|
|
}
|
|
enumerator2 = methodSymbol.NotNullWhenFalseMembers.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
string current3 = enumerator2.Current;
|
|
enforceMemberNotNullWhenIfAffected(returnStatement.Syntax, sense: false, methodSymbol.ContainingType.GetMembers(current3), pendingReturn.StateWhenFalse, pendingReturn.StateWhenTrue);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BoundExpression expressionOpt = returnStatement.ExpressionOpt;
|
|
if (expressionOpt != null)
|
|
{
|
|
ConstantValue constantValueOpt = expressionOpt.ConstantValueOpt;
|
|
if (constantValueOpt != null && constantValueOpt.IsBoolean)
|
|
{
|
|
bool booleanValue2 = constantValueOpt.BooleanValue;
|
|
enforceMemberNotNullWhen(returnStatement.Syntax, booleanValue2, pendingReturn.State);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void enforceMemberNotNullWhenIfAffected(SyntaxNode? syntaxOpt, bool sense, ImmutableArray<Symbol> members, LocalState state, LocalState otherState)
|
|
{
|
|
ImmutableArray<Symbol>.Enumerator enumerator2 = members.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
Symbol current2 = enumerator2.Current;
|
|
if (memberHasBadState(current2, state) != memberHasBadState(current2, otherState))
|
|
{
|
|
reportMemberIfBadConditionalState(syntaxOpt, sense, current2, state);
|
|
}
|
|
}
|
|
}
|
|
static IEnumerable<Symbol> getAllMembersToBeDefaulted(Symbol requiredMember)
|
|
{
|
|
if (requiredMember is FieldSymbol)
|
|
{
|
|
yield return requiredMember;
|
|
}
|
|
else
|
|
{
|
|
PropertySymbol property = (PropertySymbol)requiredMember;
|
|
yield return getFieldSymbolToBeInitialized(property);
|
|
ImmutableArray<string>.Enumerator enumerator2 = (property.SetMethod?.NotNullMembers ?? property.NotNullMembers).GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
string current2 = enumerator2.Current;
|
|
ImmutableArray<Symbol>.Enumerator enumerator3 = property.ContainingType.GetMembers(current2).GetEnumerator();
|
|
while (enumerator3.MoveNext())
|
|
{
|
|
Symbol current3 = enumerator3.Current;
|
|
yield return getFieldSymbolToBeInitialized(current3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static ImmutableArray<Symbol> getAllTypeAndRequiredMembers(TypeSymbol containingType)
|
|
{
|
|
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
|
|
ImmutableArray<Symbol> membersUnordered = containingType.GetMembersUnordered();
|
|
ImmutableSegmentedDictionary<string, Symbol> val = containingType.BaseTypeNoUseSiteDiagnostics?.AllRequiredMembers ?? ImmutableSegmentedDictionary<string, Symbol>.Empty;
|
|
if (val.IsEmpty)
|
|
{
|
|
return membersUnordered;
|
|
}
|
|
ArrayBuilder<Symbol> instance = ArrayBuilder<Symbol>.GetInstance(membersUnordered.Length + val.Count);
|
|
instance.AddRange(membersUnordered);
|
|
Enumerator<string, Symbol> enumerator2 = val.GetEnumerator();
|
|
try
|
|
{
|
|
string text = default(string);
|
|
Symbol symbol = default(Symbol);
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
KeyValuePairUtil.Deconstruct<string, Symbol>(enumerator2.Current, ref text, ref symbol);
|
|
Symbol requiredMember = symbol;
|
|
instance.AddRange(getAllMembersToBeDefaulted(requiredMember));
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
((IDisposable)enumerator2/*cast due to constrained. prefix*/).Dispose();
|
|
}
|
|
return instance.ToImmutableAndFree();
|
|
}
|
|
static Symbol getFieldSymbolToBeInitialized(Symbol requiredMember)
|
|
{
|
|
if (!(requiredMember is SourcePropertySymbol { IsAutoPropertyWithGetAccessor: not false } sourcePropertySymbol))
|
|
{
|
|
return requiredMember;
|
|
}
|
|
return sourcePropertySymbol.BackingField;
|
|
}
|
|
int getSlotForFieldOrPropertyOrEvent(Symbol member)
|
|
{
|
|
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0007: Invalid comparison between Unknown and I4
|
|
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0011: Invalid comparison between Unknown and I4
|
|
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_001a: Invalid comparison between Unknown and I4
|
|
if ((int)member.Kind != 6 && (int)member.Kind != 15 && (int)member.Kind != 5)
|
|
{
|
|
return -1;
|
|
}
|
|
int num = 0;
|
|
if (!member.IsStatic)
|
|
{
|
|
if ((object)base.MethodThisParameter == null)
|
|
{
|
|
return -1;
|
|
}
|
|
num = GetOrCreateSlot(base.MethodThisParameter);
|
|
if (num < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
return GetOrCreateSlot(member, num);
|
|
}
|
|
void makeMemberMaybeNull(MethodSymbol method, string memberName)
|
|
{
|
|
ImmutableArray<Symbol>.Enumerator enumerator2 = method.ContainingType.GetMembers(memberName).GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
Symbol current2 = enumerator2.Current;
|
|
int num = getSlotForFieldOrPropertyOrEvent(current2);
|
|
if (num > 0)
|
|
{
|
|
SetState(ref State, num, NullableFlowState.MaybeNull);
|
|
}
|
|
}
|
|
}
|
|
void makeMembersMaybeNull(MethodSymbol method, ImmutableArray<string> members)
|
|
{
|
|
ImmutableArray<string>.Enumerator enumerator2 = members.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
string current2 = enumerator2.Current;
|
|
makeMemberMaybeNull(method, current2);
|
|
}
|
|
}
|
|
void makeNotNullMembersMaybeNull()
|
|
{
|
|
Symbol symbol = _symbol;
|
|
MethodSymbol method = symbol as MethodSymbol;
|
|
if ((object)method != null)
|
|
{
|
|
if (method.IsConstructor())
|
|
{
|
|
ImmutableArray<Symbol>.Enumerator enumerator2 = getMembersNeedingDefaultInitialState().GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
Symbol current2 = enumerator2.Current;
|
|
if (current2.IsStatic == method.IsStatic)
|
|
{
|
|
Symbol symbol2 = current2;
|
|
if (current2 is PropertySymbol propertySymbol)
|
|
{
|
|
if (!propertySymbol.IsRequired)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if (current2 is FieldSymbol fieldSymbol)
|
|
{
|
|
if (fieldSymbol.OriginalDefinition is SynthesizedPrimaryConstructorParameterBackingFieldSymbol || fieldSymbol.IsConst)
|
|
{
|
|
continue;
|
|
}
|
|
if (fieldSymbol.AssociatedSymbol is PropertySymbol propertySymbol2)
|
|
{
|
|
if (IsPropertyOutputMoreStrictThanInput(propertySymbol2))
|
|
{
|
|
continue;
|
|
}
|
|
symbol2 = propertySymbol2;
|
|
}
|
|
}
|
|
int num = getSlotForFieldOrPropertyOrEvent(symbol2);
|
|
if (num > 0)
|
|
{
|
|
TypeWithAnnotations typeOrReturnType = symbol2.GetTypeOrReturnType();
|
|
if (!typeOrReturnType.NullableAnnotation.IsOblivious())
|
|
{
|
|
SetState(ref State, num, (!typeOrReturnType.Type.IsPossiblyNullableReferenceTypeTypeParameter()) ? NullableFlowState.MaybeNull : NullableFlowState.MaybeDefault);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
makeMembersMaybeNull(method, method.NotNullMembers);
|
|
makeMembersMaybeNull(method, method.NotNullWhenTrueMembers);
|
|
makeMembersMaybeNull(method, method.NotNullWhenFalseMembers);
|
|
method = method.OverriddenMethod;
|
|
}
|
|
while (method != null);
|
|
}
|
|
}
|
|
ImmutableArray<Symbol> getMembersNeedingDefaultInitialState()
|
|
{
|
|
if (_hasInitialState)
|
|
{
|
|
return ImmutableArray<Symbol>.Empty;
|
|
}
|
|
bool includeCurrentTypeRequiredMembers = true;
|
|
bool flag = true;
|
|
bool flag2 = false;
|
|
if (method is SourceMemberMethodSymbol { SyntaxNode: ConstructorDeclarationSyntax syntaxNode })
|
|
{
|
|
ConstructorInitializerSyntax initializer = syntaxNode.Initializer;
|
|
if (initializer != null)
|
|
{
|
|
int rawKind = ((SyntaxNode)initializer).RawKind;
|
|
flag = GetBaseOrThisInitializer()?.ShouldCheckRequiredMembers() ?? true;
|
|
switch (rawKind)
|
|
{
|
|
case 8890:
|
|
flag2 = true;
|
|
includeCurrentTypeRequiredMembers = flag;
|
|
break;
|
|
case 8889:
|
|
includeCurrentTypeRequiredMembers = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!flag2 && (!method.ContainingType.IsValueType || method.IsStatic || compilation.IsFeatureEnabled(MessageID.IDS_FeatureAutoDefaultStructs)))
|
|
{
|
|
return membersToBeInitialized(method.ContainingType, includeAllMembers: true, includeCurrentTypeRequiredMembers, flag);
|
|
}
|
|
return membersToBeInitialized(method.ContainingType, method.IncludeFieldInitializersInBody(), includeCurrentTypeRequiredMembers, flag);
|
|
}
|
|
}
|
|
bool memberHasBadState(Symbol member, LocalState state)
|
|
{
|
|
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0023: Expected I4, but got Unknown
|
|
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0026: Invalid comparison between Unknown and I4
|
|
SymbolKind kind = member.Kind;
|
|
switch (kind - 5)
|
|
{
|
|
default:
|
|
if ((int)kind != 15)
|
|
{
|
|
break;
|
|
}
|
|
goto case 1;
|
|
case 1:
|
|
{
|
|
int num = getSlotForFieldOrPropertyOrEvent(member);
|
|
if (num > 0)
|
|
{
|
|
return !GetState(ref state, num).IsNotNull();
|
|
}
|
|
return false;
|
|
}
|
|
case 0:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
static ImmutableArray<Symbol> membersToBeInitialized(NamedTypeSymbol containingType, bool includeAllMembers, bool includeCurrentTypeRequiredMembers, bool includeBaseRequiredMembers)
|
|
{
|
|
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
|
|
if (!includeAllMembers)
|
|
{
|
|
if (includeCurrentTypeRequiredMembers)
|
|
{
|
|
if (!includeBaseRequiredMembers)
|
|
{
|
|
return ImmutableArrayExtensions.SelectManyAsArray<Symbol, Symbol>(containingType.GetMembersUnordered(), (Func<Symbol, bool>)SymbolExtensions.IsRequired, (Func<Symbol, IEnumerable<Symbol>>)getAllMembersToBeDefaulted);
|
|
}
|
|
return EnumerableExtensions.SelectManyAsArray<KeyValuePair<string, Symbol>, Symbol>((IReadOnlyCollection<KeyValuePair<string, Symbol>>)(object)containingType.AllRequiredMembers, (Func<KeyValuePair<string, Symbol>, IEnumerable<Symbol>>)((KeyValuePair<string, Symbol> kvp) => getAllMembersToBeDefaulted(kvp.Value)));
|
|
}
|
|
if (!includeBaseRequiredMembers)
|
|
{
|
|
return ImmutableArray<Symbol>.Empty;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!includeBaseRequiredMembers)
|
|
{
|
|
return ImmutableArrayExtensions.SelectAsArray<Symbol, Symbol>(containingType.GetMembersUnordered(), (Func<Symbol, Symbol>)getFieldSymbolToBeInitialized);
|
|
}
|
|
if (includeCurrentTypeRequiredMembers)
|
|
{
|
|
return getAllTypeAndRequiredMembers(containingType);
|
|
}
|
|
}
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs", 996);
|
|
}
|
|
void reportMemberIfBadConditionalState(SyntaxNode? syntaxOpt, bool sense, Symbol member, LocalState state)
|
|
{
|
|
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
|
|
if (memberHasBadState(member, state))
|
|
{
|
|
DiagnosticBag diagnostics = base.Diagnostics;
|
|
object obj2 = ((syntaxOpt != null) ? syntaxOpt.GetLocation() : null);
|
|
if (obj2 == null)
|
|
{
|
|
SyntaxToken lastToken = methodMainNode.Syntax.GetLastToken(false, false, false, false);
|
|
obj2 = ((SyntaxToken)(ref lastToken)).GetLocation();
|
|
}
|
|
diagnostics.Add(ErrorCode.WRN_MemberNotNullWhen, (Location)obj2, member.Name, sense ? "true" : "false");
|
|
}
|
|
}
|
|
}
|
|
|
|
private MethodSymbol? GetBaseOrThisInitializer()
|
|
{
|
|
return _baseOrThisInitializer ?? GetConstructorThisOrBaseSymbol(methodMainNode);
|
|
}
|
|
|
|
private void EnforceNotNullWhenForPendingReturn(PendingBranch pendingReturn, BoundReturnStatement returnStatement)
|
|
{
|
|
ImmutableArray<ParameterSymbol> methodParameters = base.MethodParameters;
|
|
if (methodParameters.IsEmpty)
|
|
{
|
|
return;
|
|
}
|
|
BoundExpression expressionOpt;
|
|
if (pendingReturn.IsConditionalState)
|
|
{
|
|
expressionOpt = returnStatement.ExpressionOpt;
|
|
if (expressionOpt != null)
|
|
{
|
|
ConstantValue constantValueOpt = expressionOpt.ConstantValueOpt;
|
|
if (constantValueOpt != null && constantValueOpt.IsBoolean)
|
|
{
|
|
bool booleanValue = constantValueOpt.BooleanValue;
|
|
EnforceParameterNotNullWhenOnExit(returnStatement.Syntax, methodParameters, booleanValue, pendingReturn.State);
|
|
return;
|
|
}
|
|
}
|
|
if (!pendingReturn.StateWhenTrue.Reachable || !pendingReturn.StateWhenFalse.Reachable)
|
|
{
|
|
return;
|
|
}
|
|
ImmutableArray<ParameterSymbol>.Enumerator enumerator = methodParameters.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
ParameterSymbol current = enumerator.Current;
|
|
int orCreateSlot = GetOrCreateSlot(current);
|
|
if (orCreateSlot > 0 && GetState(ref pendingReturn.StateWhenTrue, orCreateSlot) != GetState(ref pendingReturn.StateWhenFalse, orCreateSlot))
|
|
{
|
|
ReportParameterIfBadConditionalState(returnStatement.Syntax, current, sense: true, pendingReturn.StateWhenTrue);
|
|
ReportParameterIfBadConditionalState(returnStatement.Syntax, current, sense: false, pendingReturn.StateWhenFalse);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
expressionOpt = returnStatement.ExpressionOpt;
|
|
if (expressionOpt != null)
|
|
{
|
|
ConstantValue constantValueOpt = expressionOpt.ConstantValueOpt;
|
|
if (constantValueOpt != null && constantValueOpt.IsBoolean)
|
|
{
|
|
bool booleanValue2 = constantValueOpt.BooleanValue;
|
|
EnforceParameterNotNullWhenOnExit(returnStatement.Syntax, methodParameters, booleanValue2, pendingReturn.State);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EnforceParameterNotNullOnExit(SyntaxNode? syntaxOpt, LocalState state)
|
|
{
|
|
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
|
|
if (!state.Reachable)
|
|
{
|
|
return;
|
|
}
|
|
ImmutableArray<ParameterSymbol>.Enumerator enumerator = base.MethodParameters.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
ParameterSymbol current = enumerator.Current;
|
|
int orCreateSlot = GetOrCreateSlot(current);
|
|
if (orCreateSlot <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
bool num = (current.FlowAnalysisAnnotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull;
|
|
NullableFlowState state2 = GetState(ref state, orCreateSlot);
|
|
if (num && state2.MayBeNull())
|
|
{
|
|
SyntaxToken val;
|
|
Location location;
|
|
if (syntaxOpt is BlockSyntax blockSyntax)
|
|
{
|
|
val = blockSyntax.CloseBraceToken;
|
|
location = ((SyntaxToken)(ref val)).GetLocation();
|
|
}
|
|
else
|
|
{
|
|
object obj = ((syntaxOpt != null) ? syntaxOpt.GetLocation() : null);
|
|
if (obj == null)
|
|
{
|
|
val = methodMainNode.Syntax.GetLastToken(false, false, false, false);
|
|
obj = ((SyntaxToken)(ref val)).GetLocation();
|
|
}
|
|
location = (Location)obj;
|
|
}
|
|
base.Diagnostics.Add(ErrorCode.WRN_ParameterDisallowsNull, location, current.Name);
|
|
}
|
|
else
|
|
{
|
|
EnforceNotNullIfNotNull(syntaxOpt, state, base.MethodParameters, current.NotNullIfParameterNotNull, state2, current);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EnforceParameterNotNullWhenOnExit(SyntaxNode syntax, ImmutableArray<ParameterSymbol> parameters, bool sense, LocalState stateWhen)
|
|
{
|
|
if (stateWhen.Reachable)
|
|
{
|
|
ImmutableArray<ParameterSymbol>.Enumerator enumerator = parameters.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
ParameterSymbol current = enumerator.Current;
|
|
ReportParameterIfBadConditionalState(syntax, current, sense, stateWhen);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ReportParameterIfBadConditionalState(SyntaxNode syntax, ParameterSymbol parameter, bool sense, LocalState stateWhen)
|
|
{
|
|
if (parameterHasBadConditionalState(parameter, sense, stateWhen))
|
|
{
|
|
base.Diagnostics.Add(ErrorCode.WRN_ParameterConditionallyDisallowsNull, syntax.Location, parameter.Name, sense ? "true" : "false");
|
|
}
|
|
bool parameterHasBadConditionalState(ParameterSymbol parameterSymbol, bool flag, LocalState state2)
|
|
{
|
|
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0009: Invalid comparison between Unknown and I4
|
|
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_000d: Invalid comparison between Unknown and I4
|
|
RefKind refKind = parameterSymbol.RefKind;
|
|
if ((int)refKind != 2 && (int)refKind != 1)
|
|
{
|
|
return false;
|
|
}
|
|
int orCreateSlot = GetOrCreateSlot(parameterSymbol);
|
|
if (orCreateSlot > 0)
|
|
{
|
|
NullableFlowState state = GetState(ref state2, orCreateSlot);
|
|
FlowAnalysisAnnotations flowAnalysisAnnotations = parameterSymbol.FlowAnalysisAnnotations;
|
|
if (flag)
|
|
{
|
|
bool num = (flowAnalysisAnnotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNullWhenTrue;
|
|
bool flag2 = (flowAnalysisAnnotations & FlowAnalysisAnnotations.MaybeNull) == FlowAnalysisAnnotations.MaybeNullWhenFalse;
|
|
if (!num || !state.MayBeNull())
|
|
{
|
|
if (flag2)
|
|
{
|
|
return ShouldReportNullableAssignment(parameterSymbol.TypeWithAnnotations, state);
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
bool num2 = (flowAnalysisAnnotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNullWhenFalse;
|
|
bool flag3 = (flowAnalysisAnnotations & FlowAnalysisAnnotations.MaybeNull) == FlowAnalysisAnnotations.MaybeNullWhenTrue;
|
|
if (!num2 || !state.MayBeNull())
|
|
{
|
|
if (flag3)
|
|
{
|
|
return ShouldReportNullableAssignment(parameterSymbol.TypeWithAnnotations, state);
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void EnforceNotNullIfNotNull(SyntaxNode? syntaxOpt, LocalState state, ImmutableArray<ParameterSymbol> parameters, ImmutableHashSet<string> inputParamNames, NullableFlowState outputState, ParameterSymbol? outputParam)
|
|
{
|
|
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
|
|
if (inputParamNames.IsEmpty || outputState.IsNotNull())
|
|
{
|
|
return;
|
|
}
|
|
ImmutableArray<ParameterSymbol>.Enumerator enumerator = parameters.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
ParameterSymbol current = enumerator.Current;
|
|
if (!inputParamNames.Contains(current.Name))
|
|
{
|
|
continue;
|
|
}
|
|
int orCreateSlot = GetOrCreateSlot(current);
|
|
if (orCreateSlot > 0 && GetState(ref state, orCreateSlot).IsNotNull())
|
|
{
|
|
object obj = ((syntaxOpt != null) ? syntaxOpt.GetLocation() : null);
|
|
if (obj == null)
|
|
{
|
|
SyntaxToken lastToken = methodMainNode.Syntax.GetLastToken(false, false, false, false);
|
|
obj = ((SyntaxToken)(ref lastToken)).GetLocation();
|
|
}
|
|
Location location = (Location)obj;
|
|
if ((object)outputParam != null)
|
|
{
|
|
base.Diagnostics.Add(ErrorCode.WRN_ParameterNotNullIfNotNull, location, outputParam.Name, current.Name);
|
|
}
|
|
else if (CurrentSymbol is MethodSymbol { IsAsync: false })
|
|
{
|
|
base.Diagnostics.Add(ErrorCode.WRN_ReturnNotNullIfNotNull, location, current.Name);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EnforceDoesNotReturn(SyntaxNode? syntaxOpt)
|
|
{
|
|
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
|
|
if (CurrentSymbol is MethodSymbol methodSymbol && (methodSymbol.FlowAnalysisAnnotations & FlowAnalysisAnnotations.DoesNotReturn) == FlowAnalysisAnnotations.DoesNotReturn && IsReachable())
|
|
{
|
|
object obj = ((syntaxOpt != null) ? syntaxOpt.GetLocation() : null);
|
|
if (obj == null)
|
|
{
|
|
SyntaxToken lastToken = methodMainNode.Syntax.GetLastToken(false, false, false, false);
|
|
obj = ((SyntaxToken)(ref lastToken)).GetLocation();
|
|
}
|
|
ReportDiagnostic(ErrorCode.WRN_ShouldNotReturn, (Location)obj);
|
|
}
|
|
}
|
|
|
|
internal static void AnalyzeIfNeeded(CSharpCompilation compilation, MethodSymbol method, BoundNode node, DiagnosticBag diagnostics, bool useConstructorExitWarnings, VariableState? initialNullableState, bool getFinalNullableState, MethodSymbol? baseOrThisInitializer, out VariableState? finalNullableState)
|
|
{
|
|
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_002e: Expected O, but got Unknown
|
|
if (!HasRequiredLanguageVersion(compilation) || !compilation.IsNullableAnalysisEnabledIn(method))
|
|
{
|
|
if (compilation.IsNullableAnalysisEnabledAlways)
|
|
{
|
|
Analyze(compilation, method, node, new DiagnosticBag(), useConstructorExitWarnings: false, null, getFinalNullableState: false, baseOrThisInitializer, out VariableState _, requiresAnalysis: false);
|
|
}
|
|
finalNullableState = null;
|
|
}
|
|
else
|
|
{
|
|
Analyze(compilation, method, node, diagnostics, useConstructorExitWarnings, initialNullableState, getFinalNullableState, baseOrThisInitializer, out finalNullableState);
|
|
}
|
|
}
|
|
|
|
private static void Analyze(CSharpCompilation compilation, MethodSymbol method, BoundNode node, DiagnosticBag diagnostics, bool useConstructorExitWarnings, VariableState? initialNullableState, bool getFinalNullableState, MethodSymbol? baseOrThisInitializer, out VariableState? finalNullableState, bool requiresAnalysis = true)
|
|
{
|
|
if (method.IsImplicitlyDeclared && !method.IsImplicitConstructor && !method.IsScriptInitializer)
|
|
{
|
|
finalNullableState = null;
|
|
return;
|
|
}
|
|
Binder binder = ((method is SynthesizedSimpleProgramEntryPointSymbol synthesizedSimpleProgramEntryPointSymbol) ? synthesizedSimpleProgramEntryPointSymbol.GetBodyBinder(ignoreAccessibility: false) : compilation.GetBinderFactory(node.SyntaxTree).GetBinder(node.Syntax));
|
|
Conversions conversions = binder.Conversions;
|
|
Analyze(compilation, method, node, binder, conversions, diagnostics, useConstructorExitWarnings, useDelegateInvokeParameterTypes: false, useDelegateInvokeReturnType: false, null, initialNullableState, baseOrThisInitializer, null, null, null, getFinalNullableState, out finalNullableState, requiresAnalysis);
|
|
}
|
|
|
|
internal static VariableState? GetAfterInitializersState(CSharpCompilation compilation, Symbol? symbol, BoundNode constructorBody)
|
|
{
|
|
if (symbol is MethodSymbol methodSymbol && methodSymbol.IncludeFieldInitializersInBody() && methodSymbol.ContainingType is SourceMemberContainerTypeSymbol sourceMemberContainerTypeSymbol)
|
|
{
|
|
Binder.ProcessedFieldInitializers processedInitializers = default(Binder.ProcessedFieldInitializers);
|
|
Binder.BindFieldInitializers(compilation, null, methodSymbol.IsStatic ? sourceMemberContainerTypeSymbol.StaticInitializers : sourceMemberContainerTypeSymbol.InstanceInitializers, BindingDiagnosticBag.Discarded, ref processedInitializers);
|
|
return GetAfterInitializersState(compilation, methodSymbol, InitializerRewriter.RewriteConstructor(processedInitializers.BoundInitializers, methodSymbol), constructorBody, BindingDiagnosticBag.Discarded);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal static VariableState? GetAfterInitializersState(CSharpCompilation compilation, MethodSymbol method, BoundNode nodeToAnalyze, BoundNode? constructorBody, BindingDiagnosticBag diagnostics)
|
|
{
|
|
DiagnosticBag val;
|
|
bool flag;
|
|
if (((BindingDiagnosticBag)diagnostics).DiagnosticBag == null)
|
|
{
|
|
diagnostics = BindingDiagnosticBag.Discarded;
|
|
val = DiagnosticBag.GetInstance();
|
|
flag = true;
|
|
}
|
|
else
|
|
{
|
|
val = ((BindingDiagnosticBag)diagnostics).DiagnosticBag;
|
|
flag = false;
|
|
}
|
|
MethodSymbol constructorThisOrBaseSymbol = GetConstructorThisOrBaseSymbol(constructorBody);
|
|
AnalyzeIfNeeded(compilation, method, nodeToAnalyze, val, useConstructorExitWarnings: false, null, getFinalNullableState: true, constructorThisOrBaseSymbol, out VariableState finalNullableState);
|
|
if (flag)
|
|
{
|
|
val.Free();
|
|
}
|
|
return finalNullableState;
|
|
}
|
|
|
|
private static MethodSymbol? GetConstructorThisOrBaseSymbol(BoundNode? constructorBody)
|
|
{
|
|
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0039: Invalid comparison between Unknown and I4
|
|
if (constructorBody is BoundConstructorMethodBody { Initializer: BoundExpressionStatement { Expression: BoundCall expression } })
|
|
{
|
|
MethodSymbol method = expression.Method;
|
|
if ((object)method != null && (int)method.MethodKind == 1)
|
|
{
|
|
return method;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal static void AnalyzeWithoutRewrite(CSharpCompilation compilation, Symbol? symbol, BoundNode node, Binder binder, DiagnosticBag diagnostics, bool createSnapshots)
|
|
{
|
|
AnalyzeWithSemanticInfo(compilation, symbol, node, binder, GetAfterInitializersState(compilation, symbol, node), diagnostics, createSnapshots, requiresAnalysis: false);
|
|
}
|
|
|
|
internal static BoundNode AnalyzeAndRewrite(CSharpCompilation compilation, Symbol? symbol, BoundNode node, Binder binder, VariableState? initialState, DiagnosticBag diagnostics, bool createSnapshots, out SnapshotManager? snapshotManager, ref ImmutableDictionary<Symbol, Symbol>? remappedSymbols)
|
|
{
|
|
(SnapshotManager, ImmutableDictionary<BoundExpression, (NullabilityInfo, TypeSymbol)>) tuple = AnalyzeWithSemanticInfo(compilation, symbol, node, binder, initialState, diagnostics, createSnapshots, requiresAnalysis: true);
|
|
(snapshotManager, _) = tuple;
|
|
return Rewrite(tuple.Item2, snapshotManager, node, ref remappedSymbols);
|
|
}
|
|
|
|
private static (SnapshotManager?, ImmutableDictionary<BoundExpression, (NullabilityInfo, TypeSymbol?)>) AnalyzeWithSemanticInfo(CSharpCompilation compilation, Symbol? symbol, BoundNode node, Binder binder, VariableState? initialState, DiagnosticBag diagnostics, bool createSnapshots, bool requiresAnalysis)
|
|
{
|
|
ImmutableDictionary<BoundExpression, (NullabilityInfo, TypeSymbol)>.Builder builder = ImmutableDictionary.CreateBuilder(EqualityComparer<BoundExpression>.Default, NullabilityInfoTypeComparer.Instance);
|
|
SnapshotManager.Builder builder2 = ((createSnapshots && symbol != null) ? new SnapshotManager.Builder() : null);
|
|
Analyze(compilation, symbol, node, binder, binder.Conversions, diagnostics, useConstructorExitWarnings: true, useDelegateInvokeParameterTypes: false, useDelegateInvokeReturnType: false, null, initialState, null, builder, builder2, null, getFinalNullableState: false, out VariableState _, requiresAnalysis);
|
|
ImmutableDictionary<BoundExpression, (NullabilityInfo, TypeSymbol)> item = builder.ToImmutable();
|
|
return (builder2?.ToManagerAndFree(), item);
|
|
}
|
|
|
|
internal static BoundNode AnalyzeAndRewriteSpeculation(int position, BoundNode node, Binder binder, SnapshotManager originalSnapshots, out SnapshotManager newSnapshots, ref ImmutableDictionary<Symbol, Symbol>? remappedSymbols)
|
|
{
|
|
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
|
|
ImmutableDictionary<BoundExpression, (NullabilityInfo, TypeSymbol)>.Builder builder = ImmutableDictionary.CreateBuilder(EqualityComparer<BoundExpression>.Default, NullabilityInfoTypeComparer.Instance);
|
|
SnapshotManager.Builder builder2 = new SnapshotManager.Builder();
|
|
(VariablesSnapshot, LocalStateSnapshot) snapshot = originalSnapshots.GetSnapshot(position);
|
|
VariablesSnapshot item = snapshot.Item1;
|
|
LocalStateSnapshot item2 = snapshot.Item2;
|
|
Symbol symbol = item.Symbol;
|
|
NullableWalker nullableWalker = new NullableWalker(binder.Compilation, symbol, useConstructorExitWarnings: false, useDelegateInvokeParameterTypes: false, useDelegateInvokeReturnType: false, null, node, binder, binder.Conversions, Variables.Create(item), null, null, builder, builder2, isSpeculative: true);
|
|
try
|
|
{
|
|
Analyze(nullableWalker, symbol, null, Optional<LocalState>.op_Implicit(LocalState.Create(item2)), builder2);
|
|
}
|
|
finally
|
|
{
|
|
nullableWalker.Free();
|
|
}
|
|
ImmutableDictionary<BoundExpression, (NullabilityInfo, TypeSymbol)> updatedNullabilities = builder.ToImmutable();
|
|
newSnapshots = builder2.ToManagerAndFree();
|
|
return Rewrite(updatedNullabilities, newSnapshots, node, ref remappedSymbols);
|
|
}
|
|
|
|
private static BoundNode Rewrite(ImmutableDictionary<BoundExpression, (NullabilityInfo, TypeSymbol?)> updatedNullabilities, SnapshotManager? snapshotManager, BoundNode node, ref ImmutableDictionary<Symbol, Symbol>? remappedSymbols)
|
|
{
|
|
ImmutableDictionary<Symbol, Symbol>.Builder builder = ImmutableDictionary.CreateBuilder(SymbolEqualityComparer.ConsiderEverything, SymbolEqualityComparer.ConsiderEverything);
|
|
if (remappedSymbols != null)
|
|
{
|
|
builder.AddRange(remappedSymbols);
|
|
}
|
|
BoundNode result = new NullabilityRewriter(updatedNullabilities, snapshotManager, builder).Visit(node);
|
|
remappedSymbols = builder.ToImmutable();
|
|
return result;
|
|
}
|
|
|
|
private static bool HasRequiredLanguageVersion(CSharpCompilation compilation)
|
|
{
|
|
return compilation.LanguageVersion >= MessageID.IDS_FeatureNullableReferenceTypes.RequiredVersion();
|
|
}
|
|
|
|
internal static bool NeedsAnalysis(CSharpCompilation compilation, SyntaxNode syntaxNode)
|
|
{
|
|
if (HasRequiredLanguageVersion(compilation))
|
|
{
|
|
if (!compilation.IsNullableAnalysisEnabledIn(syntaxNode))
|
|
{
|
|
return compilation.IsNullableAnalysisEnabledAlways;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal static void AnalyzeIfNeeded(Binder binder, BoundNode node, SyntaxNode syntax, DiagnosticBag diagnostics)
|
|
{
|
|
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_002a: Expected O, but got Unknown
|
|
bool requiresAnalysis = true;
|
|
CSharpCompilation cSharpCompilation = binder.Compilation;
|
|
if (!HasRequiredLanguageVersion(cSharpCompilation) || !cSharpCompilation.IsNullableAnalysisEnabledIn(syntax))
|
|
{
|
|
if (!cSharpCompilation.IsNullableAnalysisEnabledAlways)
|
|
{
|
|
return;
|
|
}
|
|
diagnostics = new DiagnosticBag();
|
|
requiresAnalysis = false;
|
|
}
|
|
Analyze(cSharpCompilation, null, node, binder, binder.Conversions, diagnostics, useConstructorExitWarnings: false, useDelegateInvokeParameterTypes: false, useDelegateInvokeReturnType: false, null, null, null, null, null, null, getFinalNullableState: false, out VariableState _, requiresAnalysis);
|
|
}
|
|
|
|
internal static void Analyze(CSharpCompilation compilation, BoundLambda lambda, Conversions conversions, DiagnosticBag diagnostics, MethodSymbol? delegateInvokeMethodOpt, VariableState initialState, ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>? returnTypesOpt)
|
|
{
|
|
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
|
|
LambdaSymbol symbol = lambda.Symbol;
|
|
Variables variables = Variables.Create(initialState.Variables).CreateNestedMethodScope(symbol);
|
|
UseDelegateInvokeParameterAndReturnTypes(lambda, delegateInvokeMethodOpt, out var useDelegateInvokeParameterTypes, out var useDelegateInvokeReturnType);
|
|
NullableWalker nullableWalker = new NullableWalker(compilation, symbol, useConstructorExitWarnings: false, useDelegateInvokeParameterTypes, useDelegateInvokeReturnType, delegateInvokeMethodOpt, lambda.Body, lambda.Binder, conversions, variables, null, returnTypesOpt, null, null);
|
|
try
|
|
{
|
|
LocalState localState = LocalState.Create(initialState.VariableNullableStates).CreateNestedMethodState(variables);
|
|
Analyze(nullableWalker, symbol, diagnostics, Optional<LocalState>.op_Implicit(localState), null);
|
|
}
|
|
finally
|
|
{
|
|
nullableWalker.Free();
|
|
}
|
|
}
|
|
|
|
private static void Analyze(CSharpCompilation compilation, Symbol? symbol, BoundNode node, Binder binder, Conversions conversions, DiagnosticBag diagnostics, bool useConstructorExitWarnings, bool useDelegateInvokeParameterTypes, bool useDelegateInvokeReturnType, MethodSymbol? delegateInvokeMethodOpt, VariableState? initialState, MethodSymbol? baseOrThisInitializer, ImmutableDictionary<BoundExpression, (NullabilityInfo, TypeSymbol?)>.Builder? analyzedNullabilityMapOpt, SnapshotManager.Builder? snapshotBuilderOpt, ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>? returnTypesOpt, bool getFinalNullableState, out VariableState? finalNullableState, bool requiresAnalysis = true)
|
|
{
|
|
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
|
|
NullableWalker nullableWalker = new NullableWalker(compilation, symbol, useConstructorExitWarnings, useDelegateInvokeParameterTypes, useDelegateInvokeReturnType, delegateInvokeMethodOpt, node, binder, conversions, (initialState == null) ? null : Variables.Create(initialState.Variables), baseOrThisInitializer, returnTypesOpt, analyzedNullabilityMapOpt, snapshotBuilderOpt);
|
|
finalNullableState = null;
|
|
try
|
|
{
|
|
Analyze(nullableWalker, symbol, diagnostics, (initialState == null) ? default(Optional<LocalState>) : Optional<LocalState>.op_Implicit(LocalState.Create(initialState.VariableNullableStates)), snapshotBuilderOpt, requiresAnalysis);
|
|
if (getFinalNullableState)
|
|
{
|
|
finalNullableState = GetVariableState(nullableWalker._variables, nullableWalker.State);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
nullableWalker.Free();
|
|
}
|
|
}
|
|
|
|
private static void Analyze(NullableWalker walker, Symbol? symbol, DiagnosticBag? diagnostics, Optional<LocalState> initialState, SnapshotManager.Builder? snapshotBuilderOpt, bool requiresAnalysis = true)
|
|
{
|
|
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
|
|
int previousSlot = snapshotBuilderOpt?.EnterNewWalker(symbol) ?? (-1);
|
|
try
|
|
{
|
|
bool badRegion = false;
|
|
walker.Analyze(ref badRegion, initialState);
|
|
if (diagnostics != null)
|
|
{
|
|
diagnostics.AddRange(walker.Diagnostics);
|
|
}
|
|
}
|
|
catch (CancelledByStackGuardException ex) when (diagnostics != null)
|
|
{
|
|
ex.AddAnError(diagnostics);
|
|
}
|
|
finally
|
|
{
|
|
snapshotBuilderOpt?.ExitWalker(walker.SaveSharedState(), previousSlot);
|
|
}
|
|
walker.RecordNullableAnalysisData(symbol, requiresAnalysis);
|
|
}
|
|
|
|
private void RecordNullableAnalysisData(Symbol? symbol, bool requiredAnalysis)
|
|
{
|
|
if (!(compilation.TestOnlyCompilationData is NullableAnalysisData nullableAnalysisData))
|
|
{
|
|
return;
|
|
}
|
|
ConcurrentDictionary<object, Data> data = nullableAnalysisData.Data;
|
|
if (data != null)
|
|
{
|
|
object key = ((object)symbol) ?? ((object)methodMainNode.Syntax);
|
|
if (!data.TryGetValue(key, out var _))
|
|
{
|
|
data.TryAdd(key, new Data(_variables.GetTotalVariableCount(), requiredAnalysis));
|
|
}
|
|
}
|
|
}
|
|
|
|
private SharedWalkerState SaveSharedState()
|
|
{
|
|
return new SharedWalkerState(_variables.CreateSnapshot());
|
|
}
|
|
|
|
private void TakeIncrementalSnapshot(BoundNode? node)
|
|
{
|
|
_snapshotBuilderOpt?.TakeIncrementalSnapshot(node, State);
|
|
}
|
|
|
|
private void SetUpdatedSymbol(BoundNode node, Symbol originalSymbol, Symbol updatedSymbol)
|
|
{
|
|
if (_snapshotBuilderOpt == null)
|
|
{
|
|
return;
|
|
}
|
|
bool flag = false;
|
|
if (node is BoundLambda boundLambda && originalSymbol is LambdaSymbol l && updatedSymbol is NamedTypeSymbol n)
|
|
{
|
|
if (!AreLambdaAndNewDelegateSimilar(l, n))
|
|
{
|
|
return;
|
|
}
|
|
flag = updatedSymbol.Equals(boundLambda.Type.GetDelegateType(), (TypeCompareKind)0);
|
|
}
|
|
if (flag || Symbol.Equals(originalSymbol, updatedSymbol, (TypeCompareKind)0))
|
|
{
|
|
_snapshotBuilderOpt.RemoveSymbolIfPresent(node, originalSymbol);
|
|
}
|
|
else
|
|
{
|
|
_snapshotBuilderOpt.SetUpdatedSymbol(node, originalSymbol, updatedSymbol);
|
|
}
|
|
}
|
|
|
|
private NullableFlowState GetState(ref LocalState state, int slot)
|
|
{
|
|
if (!state.Reachable)
|
|
{
|
|
return NullableFlowState.NotNull;
|
|
}
|
|
NormalizeIfNeeded(ref state, slot, useNotNullsAsDefault: false);
|
|
return state[slot];
|
|
}
|
|
|
|
private void SetState(ref LocalState state, int slot, NullableFlowState value, bool useNotNullsAsDefault = false)
|
|
{
|
|
if (state.Reachable)
|
|
{
|
|
NormalizeIfNeeded(ref state, slot, useNotNullsAsDefault);
|
|
state[slot] = value;
|
|
}
|
|
}
|
|
|
|
private void NormalizeIfNeeded(ref LocalState state, int slot, bool useNotNullsAsDefault)
|
|
{
|
|
state.NormalizeIfNeeded(slot, this, _variables, useNotNullsAsDefault);
|
|
}
|
|
|
|
protected override void Normalize(ref LocalState state)
|
|
{
|
|
if (state.Reachable)
|
|
{
|
|
state.Normalize(this, _variables);
|
|
}
|
|
}
|
|
|
|
private NullableFlowState GetDefaultState(ref LocalState state, int slot)
|
|
{
|
|
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003f: Expected I4, but got Unknown
|
|
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0042: Invalid comparison between Unknown and I4
|
|
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0047: Invalid comparison between Unknown and I4
|
|
if (!state.Reachable)
|
|
{
|
|
return NullableFlowState.NotNull;
|
|
}
|
|
Symbol symbol = _variables[slot].Symbol;
|
|
SymbolKind kind = symbol.Kind;
|
|
switch (kind - 4)
|
|
{
|
|
default:
|
|
{
|
|
if ((int)kind != 13)
|
|
{
|
|
if ((int)kind != 15)
|
|
{
|
|
break;
|
|
}
|
|
goto case 1;
|
|
}
|
|
ParameterSymbol parameterSymbol = (ParameterSymbol)symbol;
|
|
if (!_variables.TryGetType(parameterSymbol, out var type2))
|
|
{
|
|
type2 = parameterSymbol.TypeWithAnnotations;
|
|
}
|
|
return GetParameterState(type2, parameterSymbol.FlowAnalysisAnnotations).State;
|
|
}
|
|
case 4:
|
|
{
|
|
LocalSymbol localSymbol = (LocalSymbol)symbol;
|
|
if (!_variables.TryGetType(localSymbol, out var type))
|
|
{
|
|
type = localSymbol.TypeWithAnnotations;
|
|
}
|
|
return type.ToTypeWithState().State;
|
|
}
|
|
case 1:
|
|
case 2:
|
|
return GetDefaultState(symbol);
|
|
case 0:
|
|
return NullableFlowState.NotNull;
|
|
case 3:
|
|
break;
|
|
}
|
|
throw ExceptionUtilities.UnexpectedValue((object)symbol.Kind);
|
|
}
|
|
|
|
protected override bool TryGetReceiverAndMember(BoundExpression expr, out BoundExpression? receiver, [NotNullWhen(true)] out Symbol? member)
|
|
{
|
|
receiver = null;
|
|
member = null;
|
|
switch (expr.Kind)
|
|
{
|
|
case BoundKind.FieldAccess:
|
|
{
|
|
BoundFieldAccess boundFieldAccess = (BoundFieldAccess)expr;
|
|
FieldSymbol fieldSymbol = (FieldSymbol)(member = boundFieldAccess.FieldSymbol);
|
|
if (fieldSymbol.IsFixedSizeBuffer)
|
|
{
|
|
return false;
|
|
}
|
|
if (fieldSymbol.IsStatic)
|
|
{
|
|
return true;
|
|
}
|
|
receiver = boundFieldAccess.ReceiverOpt;
|
|
break;
|
|
}
|
|
case BoundKind.EventAccess:
|
|
{
|
|
BoundEventAccess boundEventAccess = (BoundEventAccess)expr;
|
|
if ((member = boundEventAccess.EventSymbol).IsStatic)
|
|
{
|
|
return true;
|
|
}
|
|
receiver = boundEventAccess.ReceiverOpt;
|
|
break;
|
|
}
|
|
case BoundKind.PropertyAccess:
|
|
{
|
|
BoundPropertyAccess boundPropertyAccess = (BoundPropertyAccess)expr;
|
|
if ((member = boundPropertyAccess.PropertySymbol).IsStatic)
|
|
{
|
|
return true;
|
|
}
|
|
receiver = boundPropertyAccess.ReceiverOpt;
|
|
break;
|
|
}
|
|
}
|
|
if ((object)member != null && receiver != null && receiver.Kind != BoundKind.TypeExpression)
|
|
{
|
|
return (object)receiver.Type != null;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected override int MakeSlot(BoundExpression node)
|
|
{
|
|
return makeSlot(node);
|
|
int getPlaceholderSlot(BoundExpression expr)
|
|
{
|
|
if (_placeholderLocalsOpt != null && ((Dictionary<object, PlaceholderLocal>)(object)_placeholderLocalsOpt).TryGetValue((object)expr, out PlaceholderLocal value))
|
|
{
|
|
return GetOrCreateSlot(value);
|
|
}
|
|
return -1;
|
|
}
|
|
static MethodSymbol? getTopLevelMethod(MethodSymbol? method)
|
|
{
|
|
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0011: Invalid comparison between Unknown and I4
|
|
while ((object)method != null)
|
|
{
|
|
Symbol containingSymbol = method.ContainingSymbol;
|
|
if ((int)containingSymbol.Kind == 11)
|
|
{
|
|
return method;
|
|
}
|
|
method = containingSymbol as MethodSymbol;
|
|
}
|
|
return null;
|
|
}
|
|
int makeSlot(BoundExpression boundExpression)
|
|
{
|
|
BoundConversion boundConversion;
|
|
switch (boundExpression.Kind)
|
|
{
|
|
case BoundKind.ThisReference:
|
|
case BoundKind.BaseReference:
|
|
{
|
|
ParameterSymbol parameterSymbol = getTopLevelMethod(_symbol as MethodSymbol)?.ThisParameter;
|
|
if ((object)parameterSymbol == null)
|
|
{
|
|
return -1;
|
|
}
|
|
return GetOrCreateSlot(parameterSymbol);
|
|
}
|
|
case BoundKind.Conversion:
|
|
{
|
|
int num2 = getPlaceholderSlot(boundExpression);
|
|
if (num2 > 0)
|
|
{
|
|
return num2;
|
|
}
|
|
boundConversion = (BoundConversion)boundExpression;
|
|
ConversionKind kind = boundConversion.Conversion.Kind;
|
|
if (kind <= ConversionKind.Boxing)
|
|
{
|
|
if (kind == ConversionKind.Identity || kind == ConversionKind.ImplicitTupleLiteral || kind - 12 <= ConversionKind.NoConversion)
|
|
{
|
|
goto IL_01a1;
|
|
}
|
|
}
|
|
else if (kind <= ConversionKind.ConditionalExpression)
|
|
{
|
|
if (kind != ConversionKind.ExplicitNullable)
|
|
{
|
|
if (kind - 35 <= ConversionKind.NoConversion)
|
|
{
|
|
goto IL_017b;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BoundExpression operand = boundConversion.Operand;
|
|
TypeSymbol type = operand.Type;
|
|
TypeSymbol type2 = boundConversion.Type;
|
|
if (AreNullableAndUnderlyingTypes(type, type2, out var _))
|
|
{
|
|
int num3 = MakeSlot(operand);
|
|
Symbol valueProperty;
|
|
if (num3 >= 0)
|
|
{
|
|
return GetNullableOfTValueSlot(type, num3, out valueProperty);
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (kind == ConversionKind.DefaultLiteral)
|
|
{
|
|
goto IL_01a1;
|
|
}
|
|
if (kind == ConversionKind.ObjectCreation)
|
|
{
|
|
goto IL_017b;
|
|
}
|
|
}
|
|
goto IL_01de;
|
|
}
|
|
case BoundKind.DefaultLiteral:
|
|
case BoundKind.DefaultExpression:
|
|
case BoundKind.ObjectCreationExpression:
|
|
case BoundKind.TupleLiteral:
|
|
case BoundKind.ConvertedTupleLiteral:
|
|
case BoundKind.DynamicObjectCreationExpression:
|
|
case BoundKind.AnonymousObjectCreationExpression:
|
|
case BoundKind.NewT:
|
|
return getPlaceholderSlot(boundExpression);
|
|
case BoundKind.ConditionalAccess:
|
|
return getPlaceholderSlot(boundExpression);
|
|
case BoundKind.ConditionalReceiver:
|
|
return _lastConditionalAccessSlot;
|
|
default:
|
|
{
|
|
int num = getPlaceholderSlot(boundExpression);
|
|
if (num <= 0)
|
|
{
|
|
return base.MakeSlot(boundExpression);
|
|
}
|
|
return num;
|
|
}
|
|
IL_01a1:
|
|
return MakeSlot(boundConversion.Operand);
|
|
IL_017b:
|
|
if (IsTargetTypedExpression(boundConversion.Operand) && TypeSymbol.Equals(boundConversion.Type, boundConversion.Operand.Type, (TypeCompareKind)8))
|
|
{
|
|
goto IL_01a1;
|
|
}
|
|
goto IL_01de;
|
|
IL_01de:
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override int GetOrCreateSlot(Symbol symbol, int containingSlot = 0, bool forceSlotEvenIfEmpty = false, bool createIfMissing = true)
|
|
{
|
|
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0092: Invalid comparison between Unknown and I4
|
|
if (containingSlot > 0 && !IsSlotMember(containingSlot, symbol))
|
|
{
|
|
return -1;
|
|
}
|
|
if (symbol is ParameterSymbol key && symbol.ContainingSymbol is SynthesizedPrimaryConstructor synthesizedPrimaryConstructor && synthesizedPrimaryConstructor.GetCapturedParameters().TryGetValue(key, out FieldSymbol value))
|
|
{
|
|
MethodSymbol methodSymbol = _symbol as MethodSymbol;
|
|
while (true)
|
|
{
|
|
MethodKind? val = methodSymbol?.MethodKind;
|
|
bool flag;
|
|
if (val.HasValue)
|
|
{
|
|
MethodKind valueOrDefault = val.GetValueOrDefault();
|
|
if ((int)valueOrDefault == 0 || (int)valueOrDefault == 17)
|
|
{
|
|
flag = true;
|
|
goto IL_009c;
|
|
}
|
|
}
|
|
flag = false;
|
|
goto IL_009c;
|
|
IL_009c:
|
|
if (!flag)
|
|
{
|
|
break;
|
|
}
|
|
methodSymbol = methodSymbol.ContainingSymbol as MethodSymbol;
|
|
}
|
|
if ((object)methodSymbol != null && methodSymbol.TryGetThisParameter(out var thisParameter) && (object)thisParameter?.ContainingSymbol.ContainingSymbol == synthesizedPrimaryConstructor.ContainingSymbol)
|
|
{
|
|
int orCreateSlot = GetOrCreateSlot(thisParameter);
|
|
if (orCreateSlot >= 0)
|
|
{
|
|
symbol = value;
|
|
containingSlot = orCreateSlot;
|
|
}
|
|
}
|
|
}
|
|
return base.GetOrCreateSlot(symbol, containingSlot, forceSlotEvenIfEmpty, createIfMissing);
|
|
}
|
|
|
|
private void VisitAndUnsplitAll<T>(ImmutableArray<T> nodes) where T : BoundNode
|
|
{
|
|
if (!nodes.IsDefault)
|
|
{
|
|
ImmutableArray<T>.Enumerator enumerator = nodes.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
T current = enumerator.Current;
|
|
Visit(current);
|
|
Unsplit();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void VisitWithoutDiagnostics(BoundNode? node)
|
|
{
|
|
bool disableDiagnostics = _disableDiagnostics;
|
|
_disableDiagnostics = true;
|
|
Visit(node);
|
|
_disableDiagnostics = disableDiagnostics;
|
|
}
|
|
|
|
protected override void VisitRvalue(BoundExpression? node, bool isKnownToBeAnLvalue = false)
|
|
{
|
|
Visit(node);
|
|
VisitRvalueEpilogue(node);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
private void VisitRvalueEpilogue(BoundExpression? node)
|
|
{
|
|
Unsplit();
|
|
UseRvalueOnly(node);
|
|
}
|
|
|
|
private TypeWithState VisitRvalueWithState(BoundExpression? node)
|
|
{
|
|
VisitRvalue(node);
|
|
return ResultType;
|
|
}
|
|
|
|
private TypeWithAnnotations VisitLvalueWithAnnotations(BoundExpression node)
|
|
{
|
|
VisitLValue(node);
|
|
Unsplit();
|
|
return LvalueResultType;
|
|
}
|
|
|
|
private static object GetTypeAsDiagnosticArgument(TypeSymbol? typeOpt)
|
|
{
|
|
return ((object)typeOpt) ?? ((object)"<null>");
|
|
}
|
|
|
|
private static object GetParameterAsDiagnosticArgument(ParameterSymbol? parameterOpt)
|
|
{
|
|
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_000f: Expected O, but got Unknown
|
|
if ((object)parameterOpt != null)
|
|
{
|
|
return (object)new FormattedSymbol((ISymbolInternal)(object)parameterOpt, SymbolDisplayFormat.ShortFormat);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
private static object GetContainingSymbolAsDiagnosticArgument(ParameterSymbol? parameterOpt)
|
|
{
|
|
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_001c: Expected O, but got Unknown
|
|
Symbol symbol = parameterOpt?.ContainingSymbol;
|
|
if ((object)symbol != null)
|
|
{
|
|
return (object)new FormattedSymbol((ISymbolInternal)(object)symbol, SymbolDisplayFormat.MinimallyQualifiedFormat);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
private static bool ShouldReportNullableAssignment(TypeWithAnnotations type, NullableFlowState state)
|
|
{
|
|
if (!type.HasType || type.Type.IsValueType)
|
|
{
|
|
return false;
|
|
}
|
|
NullableAnnotation nullableAnnotation = type.NullableAnnotation;
|
|
if (nullableAnnotation - 1 <= NullableAnnotation.Oblivious)
|
|
{
|
|
return false;
|
|
}
|
|
switch (state)
|
|
{
|
|
case NullableFlowState.NotNull:
|
|
return false;
|
|
case NullableFlowState.MaybeNull:
|
|
if (type.Type.IsTypeParameterDisallowingAnnotationInCSharp8() && (!(type.Type is TypeParameterSymbol { IsNotNullable: var isNotNullable }) || !(isNotNullable ?? false)))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void ReportNullableAssignmentIfNecessary(BoundExpression? value, TypeWithAnnotations targetType, TypeWithState valueType, bool useLegacyWarnings, AssignmentKind assignmentKind = AssignmentKind.Assignment, ParameterSymbol? parameterOpt = null, Location? location = null)
|
|
{
|
|
if ((targetType.HasType && !targetType.Type.Equals(valueType.Type, (TypeCompareKind)63)) || value == null || (value.WasCompilerGenerated && assignmentKind == AssignmentKind.Argument && value.Kind != BoundKind.InterpolatedStringArgumentPlaceholder) || !ShouldReportNullableAssignment(targetType, valueType.State))
|
|
{
|
|
return;
|
|
}
|
|
if (location == null)
|
|
{
|
|
location = value.Syntax.GetLocation();
|
|
}
|
|
if (SkipReferenceConversions(value).IsSuppressed)
|
|
{
|
|
return;
|
|
}
|
|
ConstantValue? constantValueOpt = value.ConstantValueOpt;
|
|
if (constantValueOpt != null && constantValueOpt.IsNull && !useLegacyWarnings)
|
|
{
|
|
ReportDiagnostic((assignmentKind == AssignmentKind.Return) ? ErrorCode.WRN_NullReferenceReturn : ErrorCode.WRN_NullAsNonNullable, location);
|
|
}
|
|
else if (assignmentKind == AssignmentKind.Argument)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_NullReferenceArgument, location, GetParameterAsDiagnosticArgument(parameterOpt), GetContainingSymbolAsDiagnosticArgument(parameterOpt));
|
|
LearnFromNonNullTest(value, ref State);
|
|
}
|
|
else if (useLegacyWarnings)
|
|
{
|
|
if (!isMaybeDefaultValue(valueType) || allowUnconstrainedTypeParameterAnnotations(compilation))
|
|
{
|
|
ReportNonSafetyDiagnostic(location);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReportDiagnostic((assignmentKind == AssignmentKind.Return) ? ErrorCode.WRN_NullReferenceReturn : ErrorCode.WRN_NullReferenceAssignment, location);
|
|
}
|
|
static bool allowUnconstrainedTypeParameterAnnotations(CSharpCompilation compilation)
|
|
{
|
|
return MessageID.IDS_FeatureDefaultTypeParameterConstraint.RequiredVersion() <= compilation.LanguageVersion;
|
|
}
|
|
static bool isMaybeDefaultValue(TypeWithState typeWithState)
|
|
{
|
|
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0014: Invalid comparison between Unknown and I4
|
|
TypeSymbol? type = typeWithState.Type;
|
|
if ((object)type != null && (int)type.TypeKind == 11)
|
|
{
|
|
return typeWithState.State == NullableFlowState.MaybeDefault;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal static bool AreParameterAnnotationsCompatible(RefKind refKind, TypeWithAnnotations overriddenType, FlowAnalysisAnnotations overriddenAnnotations, TypeWithAnnotations overridingType, FlowAnalysisAnnotations overridingAnnotations, bool forRef = false)
|
|
{
|
|
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0023: Invalid comparison between Unknown and I4
|
|
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_006f: Invalid comparison between Unknown and I4
|
|
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00ee: Invalid comparison between Unknown and I4
|
|
if ((int)refKind == 1)
|
|
{
|
|
if (AreParameterAnnotationsCompatible((RefKind)0, overriddenType, overriddenAnnotations, overridingType, overridingAnnotations, forRef: true))
|
|
{
|
|
return AreParameterAnnotationsCompatible((RefKind)2, overriddenType, overriddenAnnotations, overridingType, overridingAnnotations);
|
|
}
|
|
return false;
|
|
}
|
|
if (((int)refKind == 0 || refKind - 3 <= 1) ? true : false)
|
|
{
|
|
if (isBadAssignment(GetParameterState(overriddenType, overriddenAnnotations), overridingType, overridingAnnotations))
|
|
{
|
|
return false;
|
|
}
|
|
bool flag = (overridingAnnotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull;
|
|
if ((overriddenAnnotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull && !flag && !forRef)
|
|
{
|
|
return false;
|
|
}
|
|
bool flag2 = (overridingAnnotations & FlowAnalysisAnnotations.MaybeNull) == FlowAnalysisAnnotations.MaybeNull;
|
|
if ((overriddenAnnotations & FlowAnalysisAnnotations.MaybeNull) == FlowAnalysisAnnotations.MaybeNull && !flag2 && !forRef)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if ((int)refKind == 2 && (!canAssignOutputValueWhen(sense: true) || !canAssignOutputValueWhen(sense: false)))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
bool canAssignOutputValueWhen(bool sense)
|
|
{
|
|
if (isBadAssignment(ApplyUnconditionalAnnotations(overridingType.ToTypeWithState(), makeUnconditionalAnnotation(overridingAnnotations, sense)), destinationAnnotations: ToInwardAnnotations(makeUnconditionalAnnotation(overriddenAnnotations, sense)), destinationType: overriddenType))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
static bool isBadAssignment(TypeWithState valueState, TypeWithAnnotations destinationType, FlowAnalysisAnnotations destinationAnnotations)
|
|
{
|
|
if (ShouldReportNullableAssignment(ApplyLValueAnnotations(destinationType, destinationAnnotations), valueState.State))
|
|
{
|
|
return true;
|
|
}
|
|
if (IsDisallowedNullAssignment(valueState, destinationAnnotations))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
static FlowAnalysisAnnotations makeUnconditionalAnnotation(FlowAnalysisAnnotations annotations, bool sense)
|
|
{
|
|
if (sense)
|
|
{
|
|
return makeUnconditionalAnnotationCore(makeUnconditionalAnnotationCore(annotations, FlowAnalysisAnnotations.NotNullWhenTrue, FlowAnalysisAnnotations.NotNull), FlowAnalysisAnnotations.MaybeNullWhenTrue, FlowAnalysisAnnotations.MaybeNull);
|
|
}
|
|
return makeUnconditionalAnnotationCore(makeUnconditionalAnnotationCore(annotations, FlowAnalysisAnnotations.NotNullWhenFalse, FlowAnalysisAnnotations.NotNull), FlowAnalysisAnnotations.MaybeNullWhenFalse, FlowAnalysisAnnotations.MaybeNull);
|
|
}
|
|
static FlowAnalysisAnnotations makeUnconditionalAnnotationCore(FlowAnalysisAnnotations annotations, FlowAnalysisAnnotations conditionalAnnotation, FlowAnalysisAnnotations replacementAnnotation)
|
|
{
|
|
if ((annotations & conditionalAnnotation) != FlowAnalysisAnnotations.None)
|
|
{
|
|
return annotations | replacementAnnotation;
|
|
}
|
|
return annotations & ~replacementAnnotation;
|
|
}
|
|
}
|
|
|
|
private static bool IsDefaultValue(BoundExpression expr)
|
|
{
|
|
switch (expr.Kind)
|
|
{
|
|
case BoundKind.Conversion:
|
|
{
|
|
BoundConversion boundConversion = (BoundConversion)expr;
|
|
ConversionKind kind = boundConversion.Conversion.Kind;
|
|
if (kind == ConversionKind.DefaultLiteral || kind == ConversionKind.NullLiteral)
|
|
{
|
|
return IsDefaultValue(boundConversion.Operand);
|
|
}
|
|
return false;
|
|
}
|
|
case BoundKind.DefaultLiteral:
|
|
case BoundKind.DefaultExpression:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void ReportNullabilityMismatchInAssignment(SyntaxNode syntaxNode, object sourceType, object destinationType)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, syntaxNode, sourceType, destinationType);
|
|
}
|
|
|
|
private void ReportNullabilityMismatchInAssignment(Location location, object sourceType, object destinationType)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, location, sourceType, destinationType);
|
|
}
|
|
|
|
private void TrackNullableStateForAssignment(BoundExpression? valueOpt, TypeWithAnnotations targetType, int targetSlot, TypeWithState valueType, int valueSlot = -1)
|
|
{
|
|
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0069: Invalid comparison between Unknown and I4
|
|
if (!State.Reachable || !targetType.HasType || targetSlot <= 0 || targetSlot == valueSlot)
|
|
{
|
|
return;
|
|
}
|
|
NullableFlowState state = valueType.State;
|
|
SetStateAndTrackForFinally(ref State, targetSlot, state);
|
|
InheritDefaultState(targetType.Type, targetSlot);
|
|
if (!areEquivalentTypes(targetType, valueType))
|
|
{
|
|
return;
|
|
}
|
|
if (targetType.Type.IsReferenceType || (int)targetType.TypeKind == 11 || targetType.IsNullableType())
|
|
{
|
|
if (valueSlot > 0)
|
|
{
|
|
InheritNullableStateOfTrackableType(targetSlot, valueSlot, targetSlot);
|
|
}
|
|
}
|
|
else if (EmptyStructTypeCache.IsTrackableStructType(targetType.Type))
|
|
{
|
|
InheritNullableStateOfTrackableStruct(targetType.Type, targetSlot, valueSlot, valueOpt != null && IsDefaultValue(valueOpt), targetSlot);
|
|
}
|
|
static bool areEquivalentTypes(TypeWithAnnotations target, TypeWithState assignedValue)
|
|
{
|
|
return target.Type.Equals(assignedValue.Type, (TypeCompareKind)63);
|
|
}
|
|
}
|
|
|
|
private void ReportNonSafetyDiagnostic(Location location)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, location);
|
|
}
|
|
|
|
private void ReportDiagnostic(ErrorCode errorCode, SyntaxNode syntaxNode, params object[] arguments)
|
|
{
|
|
ReportDiagnostic(errorCode, syntaxNode.GetLocation(), arguments);
|
|
}
|
|
|
|
private void ReportDiagnostic(ErrorCode errorCode, Location location, params object[] arguments)
|
|
{
|
|
if (IsReachable() && !_disableDiagnostics)
|
|
{
|
|
base.Diagnostics.Add(errorCode, location, arguments);
|
|
}
|
|
}
|
|
|
|
private void InheritNullableStateOfTrackableStruct(TypeSymbol targetType, int targetSlot, int valueSlot, bool isDefaultValue, int skipSlot = -1)
|
|
{
|
|
if (skipSlot < 0)
|
|
{
|
|
skipSlot = targetSlot;
|
|
}
|
|
if (!isDefaultValue && valueSlot > 0)
|
|
{
|
|
InheritNullableStateOfTrackableType(targetSlot, valueSlot, skipSlot);
|
|
return;
|
|
}
|
|
foreach (FieldSymbol structInstanceField in _emptyStructTypeCache.GetStructInstanceFields(targetType))
|
|
{
|
|
InheritNullableStateOfMember(targetSlot, valueSlot, structInstanceField, isDefaultValue, skipSlot);
|
|
}
|
|
}
|
|
|
|
private bool IsSlotMember(int slot, Symbol possibleMember)
|
|
{
|
|
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
|
|
TypeSymbol containingType = possibleMember.ContainingType;
|
|
TypeSymbol source = NominalSlotType(slot);
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
Conversions conversions = _conversions.WithNullability(includeNullability: false);
|
|
if (!conversions.HasIdentityOrImplicitReferenceConversion(source, containingType, ref useSiteInfo))
|
|
{
|
|
return conversions.HasBoxingConversion(source, containingType, ref useSiteInfo);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void InheritNullableStateOfMember(int targetContainerSlot, int valueContainerSlot, Symbol member, bool isDefaultValue, int skipSlot)
|
|
{
|
|
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0029: Invalid comparison between Unknown and I4
|
|
if (!IsSlotMember(targetContainerSlot, member))
|
|
{
|
|
return;
|
|
}
|
|
TypeWithAnnotations typeOrReturnType = member.GetTypeOrReturnType();
|
|
if (typeOrReturnType.Type.IsReferenceType || (int)typeOrReturnType.TypeKind == 11 || typeOrReturnType.IsNullableType())
|
|
{
|
|
int orCreateSlot = GetOrCreateSlot(member, targetContainerSlot);
|
|
if (orCreateSlot <= 0)
|
|
{
|
|
return;
|
|
}
|
|
NullableFlowState newState = (isDefaultValue ? NullableFlowState.MaybeNull : typeOrReturnType.ToTypeWithState().State);
|
|
int num = -1;
|
|
if (valueContainerSlot > 0)
|
|
{
|
|
num = VariableSlot(member, valueContainerSlot);
|
|
if (num == skipSlot)
|
|
{
|
|
return;
|
|
}
|
|
newState = ((num > 0) ? GetState(ref State, num) : NullableFlowState.NotNull);
|
|
}
|
|
SetStateAndTrackForFinally(ref State, orCreateSlot, newState);
|
|
if (num > 0)
|
|
{
|
|
InheritNullableStateOfTrackableType(orCreateSlot, num, skipSlot);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!EmptyStructTypeCache.IsTrackableStructType(typeOrReturnType.Type))
|
|
{
|
|
return;
|
|
}
|
|
int orCreateSlot2 = GetOrCreateSlot(member, targetContainerSlot);
|
|
if (orCreateSlot2 > 0)
|
|
{
|
|
int num2 = ((valueContainerSlot > 0) ? GetOrCreateSlot(member, valueContainerSlot) : (-1));
|
|
if (num2 != skipSlot)
|
|
{
|
|
InheritNullableStateOfTrackableStruct(typeOrReturnType.Type, orCreateSlot2, num2, isDefaultValue, skipSlot);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private TypeSymbol NominalSlotType(int slot)
|
|
{
|
|
return _variables[slot].Symbol.GetTypeOrReturnType().Type;
|
|
}
|
|
|
|
private void SetStateAndTrackForFinally(ref LocalState state, int slot, NullableFlowState newState)
|
|
{
|
|
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
|
|
SetState(ref state, slot, newState);
|
|
if (newState != NullableFlowState.NotNull && NonMonotonicState.HasValue)
|
|
{
|
|
LocalState state2 = NonMonotonicState.Value;
|
|
if (state2.HasVariable(slot))
|
|
{
|
|
SetState(ref state2, slot, newState.Join(GetState(ref state2, slot)), useNotNullsAsDefault: true);
|
|
NonMonotonicState = Optional<LocalState>.op_Implicit(state2);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void JoinTryBlockState(ref LocalState self, ref LocalState other)
|
|
{
|
|
LocalState other2 = other.GetStateForVariables(self.Id);
|
|
Join(ref self, ref other2);
|
|
}
|
|
|
|
private void InheritDefaultState(TypeSymbol targetType, int targetSlot)
|
|
{
|
|
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
|
|
ArrayBuilder<(LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int)> instance = ArrayBuilder<(LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int)>.GetInstance();
|
|
_variables.GetMembers(instance, targetSlot);
|
|
Enumerator<(LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int)> enumerator = instance.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
(LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int) current = enumerator.Current;
|
|
LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier item = current.Item1;
|
|
int item2 = current.Item2;
|
|
Symbol symbol = AsMemberOfType(targetType, item.Symbol);
|
|
SetStateAndTrackForFinally(ref State, item2, GetDefaultState(symbol));
|
|
InheritDefaultState(symbol.GetTypeOrReturnType().Type, item2);
|
|
}
|
|
instance.Free();
|
|
}
|
|
|
|
private NullableFlowState GetDefaultState(Symbol symbol)
|
|
{
|
|
return ApplyUnconditionalAnnotations(symbol.GetTypeOrReturnType().ToTypeWithState(), GetRValueAnnotations(symbol)).State;
|
|
}
|
|
|
|
private void InheritNullableStateOfTrackableType(int targetSlot, int valueSlot, int skipSlot)
|
|
{
|
|
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
|
|
ArrayBuilder<(LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int)> instance = ArrayBuilder<(LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int)>.GetInstance();
|
|
_variables.GetMembers(instance, valueSlot);
|
|
Enumerator<(LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier, int)> enumerator = instance.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
LocalDataFlowPass<LocalState, LocalFunctionState>.VariableIdentifier item = enumerator.Current.Item1;
|
|
Symbol symbol = item.Symbol;
|
|
InheritNullableStateOfMember(targetSlot, valueSlot, symbol, isDefaultValue: false, skipSlot);
|
|
}
|
|
instance.Free();
|
|
}
|
|
|
|
protected override LocalState TopState()
|
|
{
|
|
LocalState result = LocalState.ReachableState(_variables);
|
|
result.PopulateAll(this);
|
|
return result;
|
|
}
|
|
|
|
protected override LocalState UnreachableState()
|
|
{
|
|
return LocalState.UnreachableState(_variables);
|
|
}
|
|
|
|
protected override LocalState ReachableBottomState()
|
|
{
|
|
return LocalState.ReachableStateWithNotNulls(_variables);
|
|
}
|
|
|
|
private void EnterParameters()
|
|
{
|
|
if (!(CurrentSymbol is MethodSymbol methodSymbol))
|
|
{
|
|
return;
|
|
}
|
|
if (methodSymbol is SynthesizedPrimaryConstructor)
|
|
{
|
|
if (_hasInitialState)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else if (methodSymbol.IsConstructor() && !_hasInitialState)
|
|
{
|
|
return;
|
|
}
|
|
MethodSymbol methodSymbol2 = methodSymbol.PartialDefinitionPart ?? methodSymbol;
|
|
ImmutableArray<ParameterSymbol> parameters = methodSymbol2.Parameters;
|
|
ImmutableArray<ParameterSymbol> parameters2 = (_useDelegateInvokeParameterTypes ? _delegateInvokeMethod : methodSymbol2).Parameters;
|
|
LocalState other = State.Clone();
|
|
for (int i = 0; i < parameters.Length; i++)
|
|
{
|
|
ParameterSymbol parameterSymbol = parameters[i];
|
|
TypeWithAnnotations parameterType = ((i >= parameters2.Length) ? parameterSymbol.TypeWithAnnotations : parameters2[i].TypeWithAnnotations);
|
|
EnterParameter(parameterSymbol, parameterType);
|
|
}
|
|
Join(ref State, ref other);
|
|
}
|
|
|
|
private void EnterParameter(ParameterSymbol parameter, TypeWithAnnotations parameterType)
|
|
{
|
|
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0014: Invalid comparison between Unknown and I4
|
|
_variables.SetType(parameter, parameterType);
|
|
if ((int)parameter.RefKind == 2)
|
|
{
|
|
return;
|
|
}
|
|
int orCreateSlot = GetOrCreateSlot(parameter);
|
|
if (orCreateSlot > 0)
|
|
{
|
|
NullableFlowState state = GetParameterState(parameterType, parameter.FlowAnalysisAnnotations).State;
|
|
SetState(ref State, orCreateSlot, state);
|
|
if (EmptyStructTypeCache.IsTrackableStructType(parameterType.Type))
|
|
{
|
|
TypeSymbol type = parameterType.Type;
|
|
ConstantValue? explicitDefaultConstantValue = parameter.ExplicitDefaultConstantValue;
|
|
InheritNullableStateOfTrackableStruct(type, orCreateSlot, -1, explicitDefaultConstantValue != null && explicitDefaultConstantValue.IsNull);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitParameterEqualsValue(BoundParameterEqualsValue equalsValue)
|
|
{
|
|
ParameterSymbol parameter = equalsValue.Parameter;
|
|
FlowAnalysisAnnotations parameterAnnotations = GetParameterAnnotations(parameter);
|
|
TypeWithAnnotations targetTypeOpt = ApplyLValueAnnotations(parameter.TypeWithAnnotations, parameterAnnotations);
|
|
TypeWithState state = VisitOptionalImplicitConversion(equalsValue.Value, targetTypeOpt, useLegacyWarnings: false, trackMembers: false, AssignmentKind.Assignment);
|
|
Unsplit();
|
|
CheckDisallowedNullAssignment(state, parameterAnnotations, equalsValue.Value.Syntax);
|
|
return null;
|
|
}
|
|
|
|
internal static TypeWithState GetParameterState(TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations)
|
|
{
|
|
if ((parameterAnnotations & FlowAnalysisAnnotations.AllowNull) != FlowAnalysisAnnotations.None)
|
|
{
|
|
return TypeWithState.Create(parameterType.Type, NullableFlowState.MaybeDefault);
|
|
}
|
|
if ((parameterAnnotations & FlowAnalysisAnnotations.DisallowNull) != FlowAnalysisAnnotations.None)
|
|
{
|
|
return TypeWithState.Create(parameterType.Type, NullableFlowState.NotNull);
|
|
}
|
|
return parameterType.ToTypeWithState();
|
|
}
|
|
|
|
public sealed override BoundNode? VisitReturnStatement(BoundReturnStatement node)
|
|
{
|
|
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0074: Invalid comparison between Unknown and I4
|
|
BoundExpression expressionOpt = node.ExpressionOpt;
|
|
if (expressionOpt == null)
|
|
{
|
|
EnforceDoesNotReturn(node.Syntax);
|
|
base.PendingBranches.Add(new AbstractFlowPass<LocalState, LocalFunctionState>.PendingBranch(node, State, null));
|
|
SetUnreachable();
|
|
return null;
|
|
}
|
|
if (_returnTypesOpt == null && TryGetReturnType(out var type, out var annotations))
|
|
{
|
|
if ((int)node.RefKind == 0 && (int)type.Type.SpecialType == 7)
|
|
{
|
|
Visit(expressionOpt);
|
|
}
|
|
else
|
|
{
|
|
TypeWithState state = (((int)node.RefKind != 0) ? VisitRefExpression(expressionOpt, type) : VisitOptionalImplicitConversion(expressionOpt, type, useLegacyWarnings: false, trackMembers: false, AssignmentKind.Return));
|
|
CheckDisallowedNullAssignment(state, ToInwardAnnotations(annotations), node.Syntax, expressionOpt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TypeWithState typeWithState = VisitRvalueWithState(expressionOpt);
|
|
if (_returnTypesOpt != null)
|
|
{
|
|
_returnTypesOpt.Add((node, typeWithState.ToTypeWithAnnotations(compilation)));
|
|
}
|
|
}
|
|
EnforceDoesNotReturn(node.Syntax);
|
|
if (IsConditionalState)
|
|
{
|
|
LocalState self = StateWhenTrue.Clone();
|
|
Join(ref self, ref StateWhenFalse);
|
|
base.PendingBranches.Add(new AbstractFlowPass<LocalState, LocalFunctionState>.PendingBranch(node, self, null, IsConditionalState, StateWhenTrue, StateWhenFalse));
|
|
}
|
|
else
|
|
{
|
|
base.PendingBranches.Add(new AbstractFlowPass<LocalState, LocalFunctionState>.PendingBranch(node, State, null));
|
|
}
|
|
Unsplit();
|
|
if (CurrentSymbol is MethodSymbol methodSymbol)
|
|
{
|
|
EnforceNotNullIfNotNull(node.Syntax, State, methodSymbol.Parameters, methodSymbol.ReturnNotNullIfParameterNotNull, ResultType.State, null);
|
|
}
|
|
SetUnreachable();
|
|
return null;
|
|
}
|
|
|
|
private TypeWithState VisitRefExpression(BoundExpression expr, TypeWithAnnotations destinationType)
|
|
{
|
|
Visit(expr);
|
|
TypeWithState resultType = ResultType;
|
|
if (!expr.IsSuppressed && RemoveConversion(expr, includeExplicitConversions: false).expression.Kind != BoundKind.ThrowExpression)
|
|
{
|
|
TypeWithAnnotations lvalueResultType = LvalueResultType;
|
|
if (IsNullabilityMismatch(lvalueResultType, destinationType))
|
|
{
|
|
ReportNullabilityMismatchInAssignment(expr.Syntax, lvalueResultType, destinationType);
|
|
}
|
|
else
|
|
{
|
|
ReportNullableAssignmentIfNecessary(expr, destinationType, resultType, useLegacyWarnings: false);
|
|
}
|
|
}
|
|
return resultType;
|
|
}
|
|
|
|
private bool TryGetReturnType(out TypeWithAnnotations type, out FlowAnalysisAnnotations annotations)
|
|
{
|
|
if (!(CurrentSymbol is MethodSymbol methodSymbol))
|
|
{
|
|
type = default(TypeWithAnnotations);
|
|
annotations = FlowAnalysisAnnotations.None;
|
|
return false;
|
|
}
|
|
TypeWithAnnotations returnTypeWithAnnotations = (_useDelegateInvokeReturnType ? _delegateInvokeMethod : methodSymbol).ReturnTypeWithAnnotations;
|
|
if (returnTypeWithAnnotations.IsVoidType())
|
|
{
|
|
type = default(TypeWithAnnotations);
|
|
annotations = FlowAnalysisAnnotations.None;
|
|
return false;
|
|
}
|
|
if (!methodSymbol.IsAsync)
|
|
{
|
|
annotations = methodSymbol.ReturnTypeFlowAnalysisAnnotations;
|
|
type = ApplyUnconditionalAnnotations(returnTypeWithAnnotations, annotations);
|
|
return true;
|
|
}
|
|
if (methodSymbol.IsAsyncEffectivelyReturningGenericTask(compilation))
|
|
{
|
|
type = ((NamedTypeSymbol)returnTypeWithAnnotations.Type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.Single();
|
|
annotations = FlowAnalysisAnnotations.None;
|
|
return true;
|
|
}
|
|
type = default(TypeWithAnnotations);
|
|
annotations = FlowAnalysisAnnotations.None;
|
|
return false;
|
|
}
|
|
|
|
public override BoundNode? VisitLocal(BoundLocal node)
|
|
{
|
|
LocalSymbol localSymbol = node.LocalSymbol;
|
|
if (localSymbol is SourceLocalSymbol { IsVar: not false })
|
|
{
|
|
SyntaxNode forbiddenZone = localSymbol.ForbiddenZone;
|
|
if (forbiddenZone != null && forbiddenZone.Contains(node.Syntax))
|
|
{
|
|
SetResultType(node, TypeWithState.ForType(node.Type));
|
|
return null;
|
|
}
|
|
}
|
|
int orCreateSlot = GetOrCreateSlot(localSymbol);
|
|
TypeWithAnnotations lvalueType = GetDeclaredLocalResult(localSymbol);
|
|
if (!node.Type.Equals(lvalueType.Type, (TypeCompareKind)14))
|
|
{
|
|
lvalueType = TypeWithAnnotations.Create(node.Type, lvalueType.NullableAnnotation);
|
|
}
|
|
SetResult(node, GetAdjustedResult(lvalueType.ToTypeWithState(), orCreateSlot), lvalueType);
|
|
SplitIfBooleanConstant(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitBlock(BoundBlock node)
|
|
{
|
|
DeclareLocals(node.Locals);
|
|
VisitStatementsWithLocalFunctions(node);
|
|
return null;
|
|
}
|
|
|
|
private void VisitStatementsWithLocalFunctions(BoundBlock block)
|
|
{
|
|
if (!TrackingRegions && !block.LocalFunctions.IsDefaultOrEmpty)
|
|
{
|
|
ImmutableArray<BoundStatement>.Enumerator enumerator = block.Statements.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundStatement current = enumerator.Current;
|
|
if (current.Kind != BoundKind.LocalFunctionStatement)
|
|
{
|
|
VisitStatement(current);
|
|
}
|
|
}
|
|
enumerator = block.Statements.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
if (enumerator.Current is BoundLocalFunctionStatement node)
|
|
{
|
|
TakeIncrementalSnapshot(node);
|
|
VisitLocalFunctionStatement(node);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImmutableArray<BoundStatement>.Enumerator enumerator = block.Statements.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundStatement current2 = enumerator.Current;
|
|
VisitStatement(current2);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
|
|
{
|
|
LocalFunctionSymbol localFunc = node.Symbol;
|
|
LocalFunctionState orCreateLocalFuncUsages = GetOrCreateLocalFuncUsages(localFunc);
|
|
LocalState state = TopState();
|
|
LocalState startingState = orCreateLocalFuncUsages.StartingState;
|
|
startingState.ForEach(delegate(int slot, Variables variables)
|
|
{
|
|
if (Symbol.IsCaptured(variables[variables.RootSlot(slot)].Symbol, localFunc))
|
|
{
|
|
SetState(ref state, slot, GetState(ref startingState, slot));
|
|
}
|
|
}, _variables);
|
|
orCreateLocalFuncUsages.Visited = true;
|
|
AnalyzeLocalFunctionOrLambda(node, localFunc, state, null, useDelegateInvokeParameterTypes: false, useDelegateInvokeReturnType: false);
|
|
SetInvalidResult();
|
|
return null;
|
|
}
|
|
|
|
private Variables GetOrCreateNestedFunctionVariables(Variables container, MethodSymbol lambdaOrLocalFunction)
|
|
{
|
|
if (_nestedFunctionVariables == null)
|
|
{
|
|
_nestedFunctionVariables = PooledDictionary<MethodSymbol, Variables>.GetInstance();
|
|
}
|
|
if (!((Dictionary<MethodSymbol, Variables>)(object)_nestedFunctionVariables).TryGetValue(lambdaOrLocalFunction, out Variables value))
|
|
{
|
|
value = container.CreateNestedMethodScope(lambdaOrLocalFunction);
|
|
((Dictionary<MethodSymbol, Variables>)(object)_nestedFunctionVariables).Add(lambdaOrLocalFunction, value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
private void AnalyzeLocalFunctionOrLambda(IBoundLambdaOrFunction lambdaOrFunction, MethodSymbol lambdaOrFunctionSymbol, LocalState state, MethodSymbol? delegateInvokeMethod, bool useDelegateInvokeParameterTypes, bool useDelegateInvokeReturnType)
|
|
{
|
|
Symbol symbol = _symbol;
|
|
_symbol = lambdaOrFunctionSymbol;
|
|
Symbol currentSymbol = CurrentSymbol;
|
|
CurrentSymbol = lambdaOrFunctionSymbol;
|
|
MethodSymbol delegateInvokeMethod2 = _delegateInvokeMethod;
|
|
_delegateInvokeMethod = delegateInvokeMethod;
|
|
bool useDelegateInvokeParameterTypes2 = _useDelegateInvokeParameterTypes;
|
|
_useDelegateInvokeParameterTypes = useDelegateInvokeParameterTypes;
|
|
bool useDelegateInvokeReturnType2 = _useDelegateInvokeReturnType;
|
|
_useDelegateInvokeReturnType = useDelegateInvokeReturnType;
|
|
ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)> returnTypesOpt = _returnTypesOpt;
|
|
_returnTypesOpt = null;
|
|
LocalState state2 = State;
|
|
_variables = GetOrCreateNestedFunctionVariables(_variables, lambdaOrFunctionSymbol);
|
|
State = state.CreateNestedMethodState(_variables);
|
|
int previousSlot = _snapshotBuilderOpt?.EnterNewWalker(lambdaOrFunctionSymbol) ?? (-1);
|
|
try
|
|
{
|
|
AbstractFlowPass<LocalState, LocalFunctionState>.SavedPending oldPending = SavePending();
|
|
EnterParameters();
|
|
AbstractFlowPass<LocalState, LocalFunctionState>.SavedPending oldPending2 = SavePending();
|
|
if (lambdaOrFunctionSymbol.IsIterator)
|
|
{
|
|
base.PendingBranches.Add(new AbstractFlowPass<LocalState, LocalFunctionState>.PendingBranch(null, State, null));
|
|
}
|
|
VisitAlways(lambdaOrFunction.Body);
|
|
EnforceDoesNotReturn(null);
|
|
EnforceParameterNotNullOnExit(null, State);
|
|
RestorePending(oldPending2);
|
|
ImmutableArray<AbstractFlowPass<LocalState, LocalFunctionState>.PendingBranch>.Enumerator enumerator = RemoveReturns().GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
AbstractFlowPass<LocalState, LocalFunctionState>.PendingBranch current = enumerator.Current;
|
|
if (current.Branch is BoundReturnStatement boundReturnStatement)
|
|
{
|
|
EnforceParameterNotNullOnExit(boundReturnStatement.Syntax, current.State);
|
|
EnforceNotNullWhenForPendingReturn(current, boundReturnStatement);
|
|
}
|
|
}
|
|
RestorePending(oldPending);
|
|
}
|
|
finally
|
|
{
|
|
_snapshotBuilderOpt?.ExitWalker(SaveSharedState(), previousSlot);
|
|
}
|
|
_variables = _variables.Container;
|
|
State = state2;
|
|
_returnTypesOpt = returnTypesOpt;
|
|
_useDelegateInvokeReturnType = useDelegateInvokeReturnType2;
|
|
_useDelegateInvokeParameterTypes = useDelegateInvokeParameterTypes2;
|
|
_delegateInvokeMethod = delegateInvokeMethod2;
|
|
CurrentSymbol = currentSymbol;
|
|
_symbol = symbol;
|
|
}
|
|
|
|
protected override void VisitLocalFunctionUse(LocalFunctionSymbol symbol, LocalFunctionState localFunctionState, SyntaxNode syntax, bool isCall)
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs", 3172);
|
|
}
|
|
|
|
private void VisitLocalFunctionUse(LocalFunctionSymbol symbol)
|
|
{
|
|
LocalFunctionState orCreateLocalFuncUsages = GetOrCreateLocalFuncUsages(symbol);
|
|
LocalState other = State.GetStateForVariables(orCreateLocalFuncUsages.StartingState.Id);
|
|
if (Join(ref orCreateLocalFuncUsages.StartingState, ref other) && orCreateLocalFuncUsages.Visited)
|
|
{
|
|
stateChangedAfterUse = true;
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitDoStatement(BoundDoStatement node)
|
|
{
|
|
DeclareLocals(node.Locals);
|
|
return base.VisitDoStatement(node);
|
|
}
|
|
|
|
public override BoundNode? VisitWhileStatement(BoundWhileStatement node)
|
|
{
|
|
DeclareLocals(node.Locals);
|
|
return base.VisitWhileStatement(node);
|
|
}
|
|
|
|
public override BoundNode? VisitWithExpression(BoundWithExpression withExpr)
|
|
{
|
|
BoundExpression receiver = withExpr.Receiver;
|
|
VisitRvalue(receiver);
|
|
CheckPossibleNullReceiver(receiver);
|
|
TypeWithAnnotations typeWithAnnotations = ResultType.ToTypeWithAnnotations(compilation);
|
|
TypeWithState typeWithState = ApplyUnconditionalAnnotations(typeWithAnnotations.ToTypeWithState(), GetRValueAnnotations(withExpr.CloneMethod));
|
|
int orCreatePlaceholderSlot = GetOrCreatePlaceholderSlot(withExpr);
|
|
TrackNullableStateForAssignment(receiver, typeWithAnnotations, orCreatePlaceholderSlot, typeWithState, MakeSlot(receiver));
|
|
SetResult(withExpr, typeWithState, typeWithAnnotations);
|
|
VisitObjectCreationInitializer(orCreatePlaceholderSlot, typeWithAnnotations.Type, withExpr.InitializerExpression, delayCompletionForType: false);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitForStatement(BoundForStatement node)
|
|
{
|
|
DeclareLocals(node.OuterLocals);
|
|
DeclareLocals(node.InnerLocals);
|
|
return base.VisitForStatement(node);
|
|
}
|
|
|
|
public override BoundNode? VisitForEachStatement(BoundForEachStatement node)
|
|
{
|
|
DeclareLocals(node.IterationVariables);
|
|
return base.VisitForEachStatement(node);
|
|
}
|
|
|
|
public override BoundNode? VisitUsingStatement(BoundUsingStatement node)
|
|
{
|
|
DeclareLocals(node.Locals);
|
|
Visit(node.AwaitOpt);
|
|
return base.VisitUsingStatement(node);
|
|
}
|
|
|
|
public override BoundNode? VisitUsingLocalDeclarations(BoundUsingLocalDeclarations node)
|
|
{
|
|
Visit(node.AwaitOpt);
|
|
return base.VisitUsingLocalDeclarations(node);
|
|
}
|
|
|
|
public override BoundNode? VisitFixedStatement(BoundFixedStatement node)
|
|
{
|
|
DeclareLocals(node.Locals);
|
|
return base.VisitFixedStatement(node);
|
|
}
|
|
|
|
public override BoundNode? VisitConstructorMethodBody(BoundConstructorMethodBody node)
|
|
{
|
|
DeclareLocals(node.Locals);
|
|
return base.VisitConstructorMethodBody(node);
|
|
}
|
|
|
|
private void DeclareLocal(LocalSymbol local)
|
|
{
|
|
if (local.DeclarationKind != LocalDeclarationKind.None)
|
|
{
|
|
int orCreateSlot = GetOrCreateSlot(local);
|
|
if (orCreateSlot > 0)
|
|
{
|
|
SetState(ref State, orCreateSlot, GetDefaultState(ref State, orCreateSlot));
|
|
InheritDefaultState(GetDeclaredLocalResult(local).Type, orCreateSlot);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DeclareLocals(ImmutableArray<LocalSymbol> locals)
|
|
{
|
|
ImmutableArray<LocalSymbol>.Enumerator enumerator = locals.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
LocalSymbol current = enumerator.Current;
|
|
DeclareLocal(current);
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitLocalDeclaration(BoundLocalDeclaration node)
|
|
{
|
|
LocalSymbol localSymbol = node.LocalSymbol;
|
|
int orCreateSlot = GetOrCreateSlot(localSymbol);
|
|
bool disableDiagnostics = _disableDiagnostics;
|
|
_disableDiagnostics = true;
|
|
LocalState state = State;
|
|
VisitAndUnsplitAll(node.ArgumentsOpt);
|
|
_disableDiagnostics = disableDiagnostics;
|
|
SetState(state);
|
|
if (node.DeclaredTypeOpt != null)
|
|
{
|
|
VisitTypeExpression(node.DeclaredTypeOpt);
|
|
}
|
|
BoundExpression initializerOpt = node.InitializerOpt;
|
|
if (initializerOpt == null)
|
|
{
|
|
return null;
|
|
}
|
|
TypeWithAnnotations typeWithAnnotations = localSymbol.TypeWithAnnotations;
|
|
bool inferredType = node.InferredType;
|
|
TypeWithState valueType;
|
|
if (localSymbol.IsRef)
|
|
{
|
|
valueType = VisitRefExpression(initializerOpt, typeWithAnnotations);
|
|
}
|
|
else
|
|
{
|
|
valueType = VisitOptionalImplicitConversion(initializerOpt, inferredType ? default(TypeWithAnnotations) : typeWithAnnotations, useLegacyWarnings: true, trackMembers: true, AssignmentKind.Assignment);
|
|
Unsplit();
|
|
}
|
|
if (inferredType)
|
|
{
|
|
if (valueType.HasNullType)
|
|
{
|
|
valueType = typeWithAnnotations.ToTypeWithState();
|
|
}
|
|
typeWithAnnotations = valueType.ToAnnotatedTypeWithAnnotations(compilation);
|
|
_variables.SetType(localSymbol, typeWithAnnotations);
|
|
if (node.DeclaredTypeOpt != null)
|
|
{
|
|
SetAnalyzedNullability(node.DeclaredTypeOpt, new VisitResult(typeWithAnnotations.ToTypeWithState(), typeWithAnnotations), true);
|
|
}
|
|
}
|
|
TrackNullableStateForAssignment(initializerOpt, typeWithAnnotations, orCreateSlot, valueType, MakeSlot(initializerOpt));
|
|
return null;
|
|
}
|
|
|
|
protected override BoundExpression? VisitExpressionWithoutStackGuard(BoundExpression node)
|
|
{
|
|
SetInvalidResult();
|
|
base.VisitExpressionWithoutStackGuard(node);
|
|
VisitExpressionWithoutStackGuardEpilogue(node);
|
|
return null;
|
|
}
|
|
|
|
private void VisitExpressionWithoutStackGuardEpilogue(BoundExpression node)
|
|
{
|
|
TypeWithState resultType = ResultType;
|
|
if (ShouldMakeNotNullRvalue(node))
|
|
{
|
|
TypeWithState resultType2 = resultType.WithNotNullState();
|
|
SetResult(node, resultType2, LvalueResultType);
|
|
}
|
|
}
|
|
|
|
private static bool AreLambdaAndNewDelegateSimilar(LambdaSymbol l, NamedTypeSymbol n)
|
|
{
|
|
MethodSymbol delegateInvokeMethod = n.DelegateInvokeMethod;
|
|
if (delegateInvokeMethod.Parameters.SequenceEqual(l.Parameters, (ParameterSymbol p1, ParameterSymbol p2) => p1.Type.Equals(p2.Type, (TypeCompareKind)28)))
|
|
{
|
|
return delegateInvokeMethod.ReturnType.Equals(l.ReturnType, (TypeCompareKind)28);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public override BoundNode? Visit(BoundNode? node)
|
|
{
|
|
return Visit(node, expressionIsRead: true);
|
|
}
|
|
|
|
private BoundNode VisitLValue(BoundNode node)
|
|
{
|
|
return Visit(node, expressionIsRead: false);
|
|
}
|
|
|
|
private BoundNode Visit(BoundNode? node, bool expressionIsRead)
|
|
{
|
|
bool expressionIsRead2 = _expressionIsRead;
|
|
_expressionIsRead = expressionIsRead;
|
|
TakeIncrementalSnapshot(node);
|
|
BoundNode result = base.Visit(node);
|
|
_expressionIsRead = expressionIsRead2;
|
|
return result;
|
|
}
|
|
|
|
protected override void VisitStatement(BoundStatement statement)
|
|
{
|
|
SetInvalidResult();
|
|
base.VisitStatement(statement);
|
|
SetInvalidResult();
|
|
}
|
|
|
|
public override BoundNode? VisitObjectCreationExpression(BoundObjectCreationExpression node)
|
|
{
|
|
VisitObjectCreationExpressionBase(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitUnconvertedObjectCreationExpression(BoundUnconvertedObjectCreationExpression node)
|
|
{
|
|
SetResultType(node, TypeWithState.Create(null, NullableFlowState.NotNull));
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitCollectionExpression(BoundCollectionExpression node)
|
|
{
|
|
GetOrCreatePlaceholderSlot(node);
|
|
NullableFlowState defaultState = NullableFlowState.NotNull;
|
|
if (ConversionsBase.GetCollectionExpressionTypeKind(compilation, node.Type, out var elementType) == CollectionExpressionTypeKind.CollectionBuilder)
|
|
{
|
|
MethodSymbol collectionBuilderMethod = node.CollectionBuilderMethod;
|
|
if ((object)collectionBuilderMethod != null)
|
|
{
|
|
elementType = ((NamedTypeSymbol)collectionBuilderMethod.Parameters[0].Type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0];
|
|
FlowAnalysisAnnotations flowAnalysisAnnotations = collectionBuilderMethod.GetFlowAnalysisAnnotations();
|
|
defaultState = ApplyUnconditionalAnnotations(collectionBuilderMethod.ReturnTypeWithAnnotations, flowAnalysisAnnotations).ToTypeWithState().State;
|
|
}
|
|
}
|
|
ImmutableArray<BoundExpression>.Enumerator enumerator = node.Elements.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current = enumerator.Current;
|
|
if (!(current is BoundCollectionElementInitializer boundCollectionElementInitializer))
|
|
{
|
|
if (current is BoundCollectionExpressionSpreadElement node2)
|
|
{
|
|
Visit(node2);
|
|
}
|
|
else
|
|
{
|
|
VisitOptionalImplicitConversion(current, elementType, useLegacyWarnings: false, trackMembers: false, AssignmentKind.Assignment);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NamedTypeSymbol containingType = boundCollectionElementInitializer.AddMethod.ContainingType;
|
|
VisitCollectionElementInitializer(boundCollectionElementInitializer, containingType, delayCompletionForType: false);
|
|
}
|
|
}
|
|
SetResultType(node, TypeWithState.Create(node.Type, defaultState));
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitUnconvertedCollectionExpression(BoundUnconvertedCollectionExpression node)
|
|
{
|
|
ImmutableArray<BoundExpression>.Enumerator enumerator = node.Elements.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current = enumerator.Current;
|
|
VisitRvalue(current);
|
|
}
|
|
SetResultType(node, TypeWithState.Create(node.Type, NullableFlowState.NotNull));
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitCollectionExpressionSpreadElement(BoundCollectionExpressionSpreadElement node)
|
|
{
|
|
base.VisitCollectionExpressionSpreadElement(node);
|
|
SetResultType(node, default(TypeWithState));
|
|
return null;
|
|
}
|
|
|
|
private void VisitObjectCreationExpressionBase(BoundObjectCreationExpressionBase node)
|
|
{
|
|
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
|
|
bool wasTargetTyped = node.WasTargetTyped;
|
|
MethodSymbol methodSymbol = getConstructor(node, node.Type);
|
|
ImmutableArray<BoundExpression> arguments = node.Arguments;
|
|
(MethodSymbol? method, ImmutableArray<VisitArgumentResult> results, bool returnNotNull, ArgumentsCompletionDelegate? completion) tuple = VisitArguments(node, arguments, node.ArgumentRefKindsOpt, methodSymbol?.Parameters ?? default(ImmutableArray<ParameterSymbol>), node.ArgsToParamsOpt, node.DefaultArguments, node.Expanded, invokedAsExtensionMethod: false, methodSymbol, wasTargetTyped);
|
|
ImmutableArray<VisitArgumentResult> item = tuple.results;
|
|
ArgumentsCompletionDelegate item2 = tuple.completion;
|
|
TypeSymbol type = node.Type;
|
|
(int slot, NullableFlowState resultState, Func<TypeSymbol, MethodSymbol?, int>? completion) tuple2 = inferInitialObjectState(node, type, methodSymbol, arguments, item, wasTargetTyped);
|
|
int item3 = tuple2.slot;
|
|
NullableFlowState item4 = tuple2.resultState;
|
|
Func<TypeSymbol, MethodSymbol, int> item5 = tuple2.completion;
|
|
Action<int, TypeSymbol> initializerCompletion = null;
|
|
BoundObjectInitializerExpressionBase initializerExpressionOpt = node.InitializerExpressionOpt;
|
|
if (initializerExpressionOpt != null)
|
|
{
|
|
initializerCompletion = VisitObjectCreationInitializer(item3, type, initializerExpressionOpt, wasTargetTyped);
|
|
}
|
|
TypeWithState type2 = setAnalyzedNullability(node, type, item, item2, item5, initializerCompletion, item4, wasTargetTyped);
|
|
SetResultType(node, type2, updateAnalyzedNullability: false);
|
|
static MethodSymbol? getConstructor(BoundObjectCreationExpressionBase boundObjectCreationExpressionBase, TypeSymbol type3)
|
|
{
|
|
MethodSymbol methodSymbol2 = boundObjectCreationExpressionBase.Constructor;
|
|
if ((object)methodSymbol2 != null && !type3.IsInterfaceType())
|
|
{
|
|
methodSymbol2 = (MethodSymbol)AsMemberOfType(type3, methodSymbol2);
|
|
}
|
|
return methodSymbol2;
|
|
}
|
|
(int slot, NullableFlowState resultState, Func<TypeSymbol, MethodSymbol?, int>? completion) inferInitialObjectState(BoundExpression boundExpression, TypeSymbol typeSymbol, MethodSymbol? constructor, ImmutableArray<BoundExpression> immutableArray, ImmutableArray<VisitArgumentResult> argumentResults, bool isTargetTyped)
|
|
{
|
|
if (isTargetTyped)
|
|
{
|
|
return (slot: -1, resultState: NullableFlowState.NotNull, completion: inferInitialObjectStateAsContinuation(boundExpression, immutableArray, argumentResults));
|
|
}
|
|
ImmutableArray<TypeWithState> types = ImmutableArrayExtensions.SelectAsArray<VisitArgumentResult, TypeWithState>(argumentResults, (Func<VisitArgumentResult, TypeWithState>)((VisitArgumentResult ar) => ar.RValueType));
|
|
int num = -1;
|
|
NullableFlowState nullableFlowState = NullableFlowState.NotNull;
|
|
if ((object)typeSymbol != null)
|
|
{
|
|
num = GetOrCreatePlaceholderSlot(boundExpression);
|
|
if (num > 0)
|
|
{
|
|
bool flag = constructor?.IsDefaultValueTypeConstructor() ?? false;
|
|
if (EmptyStructTypeCache.IsTrackableStructType(typeSymbol))
|
|
{
|
|
NamedTypeSymbol namedTypeSymbol = constructor?.ContainingType;
|
|
if ((object)namedTypeSymbol != null && namedTypeSymbol.IsTupleType && !flag)
|
|
{
|
|
TrackNullableStateOfTupleElements(num, namedTypeSymbol, immutableArray, types, ((BoundObjectCreationExpression)boundExpression).ArgsToParamsOpt, useRestField: true);
|
|
}
|
|
else
|
|
{
|
|
InheritNullableStateOfTrackableStruct(typeSymbol, num, -1, flag);
|
|
}
|
|
}
|
|
else if (typeSymbol.IsNullableType())
|
|
{
|
|
TypeWithAnnotations underlyingTypeWithAnnotations;
|
|
if (flag)
|
|
{
|
|
nullableFlowState = NullableFlowState.MaybeNull;
|
|
}
|
|
else if ((object)constructor != null && constructor.ParameterCount == 1 && AreNullableAndUnderlyingTypes(typeSymbol, constructor.ParameterTypesWithAnnotations[0].Type, out underlyingTypeWithAnnotations))
|
|
{
|
|
BoundExpression boundExpression2 = immutableArray[0];
|
|
int num2 = MakeSlot(boundExpression2);
|
|
if (num2 > 0)
|
|
{
|
|
TrackNullableStateOfNullableValue(num, typeSymbol, boundExpression2, underlyingTypeWithAnnotations.ToTypeWithState(), num2);
|
|
}
|
|
}
|
|
}
|
|
SetState(ref State, num, nullableFlowState);
|
|
}
|
|
}
|
|
return (slot: num, resultState: nullableFlowState, completion: null);
|
|
}
|
|
Func<TypeSymbol, MethodSymbol?, int> inferInitialObjectStateAsContinuation(BoundExpression node2, ImmutableArray<BoundExpression> arguments2, ImmutableArray<VisitArgumentResult> argumentResults)
|
|
{
|
|
return (TypeSymbol type3, MethodSymbol? constructor) => inferInitialObjectState(node2, type3, constructor, arguments2, argumentResults, isTargetTyped: false).slot;
|
|
}
|
|
TypeWithState setAnalyzedNullability(BoundObjectCreationExpressionBase boundObjectCreationExpressionBase, TypeSymbol? type3, ImmutableArray<VisitArgumentResult> argumentResults, ArgumentsCompletionDelegate? argumentsCompletion, Func<TypeSymbol, MethodSymbol?, int>? initialStateInferenceCompletion, Action<int, TypeSymbol>? initializerCompletion2, NullableFlowState resultState, bool isTargetTyped)
|
|
{
|
|
TypeWithState typeWithState = TypeWithState.Create(type3, resultState);
|
|
if (isTargetTyped)
|
|
{
|
|
setAnalyzedNullabilityAsContinuation(boundObjectCreationExpressionBase, argumentResults, argumentsCompletion, initialStateInferenceCompletion, initializerCompletion2, resultState);
|
|
}
|
|
else
|
|
{
|
|
SetAnalyzedNullability(boundObjectCreationExpressionBase, typeWithState);
|
|
}
|
|
return typeWithState;
|
|
}
|
|
void setAnalyzedNullabilityAsContinuation(BoundObjectCreationExpressionBase boundObjectCreationExpressionBase, ImmutableArray<VisitArgumentResult> argumentResults, ArgumentsCompletionDelegate argumentsCompletion, Func<TypeSymbol, MethodSymbol?, int> initialStateInferenceCompletion, Action<int, TypeSymbol>? action, NullableFlowState resultState)
|
|
{
|
|
((Dictionary<BoundExpression, Func<TypeWithAnnotations, TypeWithState>>)(object)TargetTypedAnalysisCompletion)[(BoundExpression)boundObjectCreationExpressionBase] = delegate(TypeWithAnnotations resultTypeWithAnnotations)
|
|
{
|
|
_ = boundObjectCreationExpressionBase.Arguments;
|
|
TypeSymbol type3 = resultTypeWithAnnotations.Type;
|
|
MethodSymbol methodSymbol2 = getConstructor(boundObjectCreationExpressionBase, type3);
|
|
argumentsCompletion(argumentResults, methodSymbol2?.Parameters ?? default(ImmutableArray<ParameterSymbol>), methodSymbol2);
|
|
int arg = initialStateInferenceCompletion(type3, methodSymbol2);
|
|
action?.Invoke(arg, type3);
|
|
return setAnalyzedNullability(boundObjectCreationExpressionBase, type3, argumentResults, null, null, null, resultState, isTargetTyped: false);
|
|
};
|
|
}
|
|
}
|
|
|
|
private Action<int, TypeSymbol>? VisitObjectCreationInitializer(int containingSlot, TypeSymbol containingType, BoundObjectInitializerExpressionBase node, bool delayCompletionForType)
|
|
{
|
|
Action<int, TypeSymbol> action = null;
|
|
TakeIncrementalSnapshot(node);
|
|
if (!(node is BoundObjectInitializerExpression boundObjectInitializerExpression))
|
|
{
|
|
if (node is BoundCollectionInitializerExpression boundCollectionInitializerExpression)
|
|
{
|
|
ImmutableArray<BoundExpression>.Enumerator enumerator = boundCollectionInitializerExpression.Initializers.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current = enumerator.Current;
|
|
if (current.Kind == BoundKind.CollectionElementInitializer)
|
|
{
|
|
action = (Action<int, TypeSymbol>)Delegate.Combine(action, VisitCollectionElementInitializer((BoundCollectionElementInitializer)current, containingType, delayCompletionForType));
|
|
}
|
|
else
|
|
{
|
|
VisitRvalue(current);
|
|
}
|
|
}
|
|
SetNotNullResult(boundCollectionInitializerExpression.Placeholder);
|
|
}
|
|
else
|
|
{
|
|
ExceptionUtilities.UnexpectedValue((object)node.Kind);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImmutableArray<BoundExpression>.Enumerator enumerator = boundObjectInitializerExpression.Initializers.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current2 = enumerator.Current;
|
|
if (current2.Kind == BoundKind.AssignmentOperator)
|
|
{
|
|
action = (Action<int, TypeSymbol>)Delegate.Combine(action, VisitObjectElementInitializer(containingSlot, containingType, (BoundAssignmentOperator)current2, delayCompletionForType));
|
|
}
|
|
else
|
|
{
|
|
VisitRvalue(current2);
|
|
}
|
|
}
|
|
SetNotNullResult(boundObjectInitializerExpression.Placeholder);
|
|
}
|
|
return action;
|
|
}
|
|
|
|
private Action<int, TypeSymbol>? VisitObjectElementInitializer(int containingSlot, TypeSymbol containingType, BoundAssignmentOperator node, bool delayCompletionForType)
|
|
{
|
|
TakeIncrementalSnapshot(node);
|
|
BoundExpression left = node.Left;
|
|
if (left.Kind == BoundKind.ObjectInitializerMember)
|
|
{
|
|
TakeIncrementalSnapshot(left);
|
|
return visitMemberInitializer(containingSlot, containingType, node, delayCompletionForType);
|
|
}
|
|
VisitRvalue(node);
|
|
return null;
|
|
Action<int, Symbol>? completeNestedInitializerAnalysis(Symbol symbol, BoundObjectInitializerExpressionBase initializer, int slot, Action<int, TypeSymbol>? nestedCompletion, bool flag)
|
|
{
|
|
if (flag)
|
|
{
|
|
return completeNestedInitializerAnalysisAsContinuation(initializer, nestedCompletion);
|
|
}
|
|
if (slot >= 0 && !initializer.Initializers.IsEmpty && !initializer.Type.IsValueType && GetState(ref State, slot).MayBeNull())
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_NullReferenceInitializer, initializer.Syntax, symbol);
|
|
}
|
|
return null;
|
|
}
|
|
Action<int, Symbol>? completeNestedInitializerAnalysisAsContinuation(BoundObjectInitializerExpressionBase initializer, Action<int, TypeSymbol>? nestedCompletion)
|
|
{
|
|
return delegate(int containingSlot2, Symbol symbol)
|
|
{
|
|
int num = getOrCreateSlot(containingSlot2, symbol);
|
|
completeNestedInitializerAnalysis(symbol, initializer, num, null, delayCompletionForType: false);
|
|
nestedCompletion?.Invoke(num, symbol.GetTypeOrReturnType().Type);
|
|
};
|
|
}
|
|
int getOrCreateSlot(int num, Symbol symbol)
|
|
{
|
|
if (num >= 0 && IsSlotMember(num, symbol))
|
|
{
|
|
return GetOrCreateSlot(symbol, num);
|
|
}
|
|
return -1;
|
|
}
|
|
static Symbol? getTargetMember(TypeSymbol type, BoundObjectInitializerMember objectInitializer)
|
|
{
|
|
Symbol symbol = objectInitializer.MemberSymbol;
|
|
if (symbol != null)
|
|
{
|
|
symbol = AsMemberOfType(type, symbol);
|
|
}
|
|
return symbol;
|
|
}
|
|
Action<int, TypeSymbol>? setAnalyzedNullability(BoundAssignmentOperator boundAssignmentOperator, ImmutableArray<VisitArgumentResult> argumentResults, ArgumentsCompletionDelegate? argumentsCompletion, Action<int, Symbol>? initializationCompletion, bool flag)
|
|
{
|
|
if (flag)
|
|
{
|
|
return setAnalyzedNullabilityAsContinuation(boundAssignmentOperator, argumentResults, argumentsCompletion, initializationCompletion);
|
|
}
|
|
BoundObjectInitializerMember boundObjectInitializerMember = (BoundObjectInitializerMember)boundAssignmentOperator.Left;
|
|
VisitResult result = new VisitResult(boundObjectInitializerMember.Type, NullableAnnotation.NotAnnotated, NullableFlowState.NotNull);
|
|
SetAnalyzedNullability(boundObjectInitializerMember, result);
|
|
SetAnalyzedNullability(boundAssignmentOperator, result);
|
|
return null;
|
|
}
|
|
Action<int, TypeSymbol>? setAnalyzedNullabilityAsContinuation(BoundAssignmentOperator boundAssignmentOperator, ImmutableArray<VisitArgumentResult> argumentResults, ArgumentsCompletionDelegate? argumentsCompletion, Action<int, Symbol>? initializationCompletion)
|
|
{
|
|
return delegate(int arg, TypeSymbol containingType2)
|
|
{
|
|
Symbol symbol = getTargetMember(containingType2, (BoundObjectInitializerMember)boundAssignmentOperator.Left);
|
|
argumentsCompletion?.Invoke(argumentResults, ((PropertySymbol)symbol)?.Parameters ?? default(ImmutableArray<ParameterSymbol>), null);
|
|
initializationCompletion?.Invoke(arg, symbol);
|
|
setAnalyzedNullability(boundAssignmentOperator, argumentResults, null, null, delayCompletionForType: false);
|
|
};
|
|
}
|
|
Action<int, Symbol>? visitMemberAssignment(BoundAssignmentOperator boundAssignmentOperator, int containingSlot2, Symbol symbol, bool flag, Func<TypeWithAnnotations, TypeWithState>? conversionCompletion = null)
|
|
{
|
|
if (!flag && conversionCompletion == null)
|
|
{
|
|
TakeIncrementalSnapshot(boundAssignmentOperator.Right);
|
|
}
|
|
TypeWithAnnotations typeWithAnnotations = ApplyLValueAnnotations(symbol.GetTypeOrReturnType(), GetObjectInitializerMemberLValueAnnotations(symbol));
|
|
TypeWithState valueType;
|
|
if (conversionCompletion == null)
|
|
{
|
|
(valueType, conversionCompletion) = VisitOptionalImplicitConversion(boundAssignmentOperator.Right, typeWithAnnotations, useLegacyWarnings: false, trackMembers: true, AssignmentKind.Assignment, flag);
|
|
}
|
|
else
|
|
{
|
|
TypeWithState typeWithState = conversionCompletion(typeWithAnnotations);
|
|
conversionCompletion = null;
|
|
valueType = typeWithState;
|
|
}
|
|
Unsplit();
|
|
if (flag)
|
|
{
|
|
return visitMemberAssignmentAsContinuation(boundAssignmentOperator, conversionCompletion);
|
|
}
|
|
int targetSlot = getOrCreateSlot(containingSlot2, symbol);
|
|
TrackNullableStateForAssignment(boundAssignmentOperator.Right, typeWithAnnotations, targetSlot, valueType, MakeSlot(boundAssignmentOperator.Right));
|
|
return null;
|
|
}
|
|
Action<int, Symbol>? visitMemberAssignmentAsContinuation(BoundAssignmentOperator node2, Func<TypeWithAnnotations, TypeWithState> conversionCompletion)
|
|
{
|
|
return delegate(int containingSlot2, Symbol symbol)
|
|
{
|
|
visitMemberAssignment(node2, containingSlot2, symbol, delayCompletionForType: false, conversionCompletion);
|
|
};
|
|
}
|
|
Action<int, TypeSymbol>? visitMemberInitializer(int containingSlot2, TypeSymbol containingType2, BoundAssignmentOperator boundAssignmentOperator, bool flag)
|
|
{
|
|
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
|
|
BoundObjectInitializerMember boundObjectInitializerMember = (BoundObjectInitializerMember)boundAssignmentOperator.Left;
|
|
Symbol symbol = getTargetMember(containingType2, boundObjectInitializerMember);
|
|
ImmutableArray<VisitArgumentResult> argumentResults = default(ImmutableArray<VisitArgumentResult>);
|
|
ArgumentsCompletionDelegate argumentsCompletion = null;
|
|
if (!boundObjectInitializerMember.Arguments.IsDefaultOrEmpty)
|
|
{
|
|
(MethodSymbol? method, ImmutableArray<VisitArgumentResult> results, bool returnNotNull, ArgumentsCompletionDelegate? completion) tuple = VisitArguments(boundObjectInitializerMember, boundObjectInitializerMember.Arguments, boundObjectInitializerMember.ArgumentRefKindsOpt, ((PropertySymbol)symbol)?.Parameters ?? default(ImmutableArray<ParameterSymbol>), boundObjectInitializerMember.ArgsToParamsOpt, boundObjectInitializerMember.DefaultArguments, boundObjectInitializerMember.Expanded, invokedAsExtensionMethod: false, null, flag);
|
|
argumentResults = tuple.results;
|
|
argumentsCompletion = tuple.completion;
|
|
}
|
|
Action<int, Symbol> initializationCompletion = null;
|
|
if ((object)symbol != null)
|
|
{
|
|
if (boundAssignmentOperator.Right is BoundObjectInitializerExpressionBase initializer)
|
|
{
|
|
initializationCompletion = visitNestedInitializer(containingSlot2, containingType2, symbol, initializer, flag);
|
|
}
|
|
else
|
|
{
|
|
TakeIncrementalSnapshot(boundAssignmentOperator.Right);
|
|
initializationCompletion = visitMemberAssignment(boundAssignmentOperator, containingSlot2, symbol, flag);
|
|
}
|
|
}
|
|
return setAnalyzedNullability(boundAssignmentOperator, argumentResults, argumentsCompletion, initializationCompletion, flag);
|
|
}
|
|
Action<int, Symbol>? visitNestedInitializer(int containingSlot2, TypeSymbol typeSymbol, Symbol symbol, BoundObjectInitializerExpressionBase initializer, bool delayCompletionForType2)
|
|
{
|
|
int num = getOrCreateSlot(containingSlot2, symbol);
|
|
Action<int, TypeSymbol> nestedCompletion = VisitObjectCreationInitializer(num, symbol.GetTypeOrReturnType().Type, initializer, delayCompletionForType2);
|
|
return completeNestedInitializerAnalysis(symbol, initializer, num, nestedCompletion, delayCompletionForType2);
|
|
}
|
|
}
|
|
|
|
[Obsolete("Use VisitCollectionElementInitializer(BoundCollectionElementInitializer node, TypeSymbol containingType, bool delayCompletionForType) instead.", true)]
|
|
private new void VisitCollectionElementInitializer(BoundCollectionElementInitializer node)
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs", 3985);
|
|
}
|
|
|
|
private Action<int, TypeSymbol>? VisitCollectionElementInitializer(BoundCollectionElementInitializer node, TypeSymbol containingType, bool delayCompletionForType)
|
|
{
|
|
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
|
|
ImmutableArray<VisitArgumentResult> argumentResults = default(ImmutableArray<VisitArgumentResult>);
|
|
MethodSymbol methodSymbol = addMethodAsMemberOfContainingType(node, containingType, ref argumentResults);
|
|
MethodSymbol reinferredMethod;
|
|
ArgumentsCompletionDelegate visitArgumentsCompletion;
|
|
(reinferredMethod, argumentResults, _, visitArgumentsCompletion) = VisitArguments(node, node.Arguments, default(ImmutableArray<RefKind>), methodSymbol.Parameters, node.ArgsToParamsOpt, node.DefaultArguments, node.Expanded, node.InvokedAsExtensionMethod, methodSymbol, delayCompletionForType);
|
|
return setUpdatedSymbol(node, containingType, reinferredMethod, argumentResults, visitArgumentsCompletion, delayCompletionForType);
|
|
static MethodSymbol addMethodAsMemberOfContainingType(BoundCollectionElementInitializer boundCollectionElementInitializer, TypeSymbol typeSymbol, ref ImmutableArray<VisitArgumentResult> reference)
|
|
{
|
|
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
|
|
MethodSymbol methodSymbol2 = boundCollectionElementInitializer.AddMethod;
|
|
if (boundCollectionElementInitializer.InvokedAsExtensionMethod)
|
|
{
|
|
if (!reference.IsDefault)
|
|
{
|
|
VisitArgumentResult visitArgumentResult = reference[0];
|
|
ArrayBuilder<VisitArgumentResult> instance = ArrayBuilder<VisitArgumentResult>.GetInstance(reference.Length);
|
|
instance.Add(new VisitArgumentResult(new VisitResult(TypeWithState.Create(typeSymbol, visitArgumentResult.RValueType.State), visitArgumentResult.LValueType.WithType(typeSymbol)), visitArgumentResult.StateForLambda));
|
|
instance.AddRange(reference, 1, reference.Length - 1);
|
|
reference = instance.ToImmutableAndFree();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
methodSymbol2 = (MethodSymbol)AsMemberOfType(typeSymbol, methodSymbol2);
|
|
}
|
|
return methodSymbol2;
|
|
}
|
|
Action<int, TypeSymbol>? setUpdatedSymbol(BoundCollectionElementInitializer boundCollectionElementInitializer, TypeSymbol typeSymbol, MethodSymbol? updatedSymbol, ImmutableArray<VisitArgumentResult> argumentResults2, ArgumentsCompletionDelegate? visitArgumentsCompletion2, bool flag)
|
|
{
|
|
if (flag)
|
|
{
|
|
return setUpdatedSymbolAsContinuation(boundCollectionElementInitializer, argumentResults2, visitArgumentsCompletion2);
|
|
}
|
|
if (boundCollectionElementInitializer.ImplicitReceiverOpt != null)
|
|
{
|
|
SetAnalyzedNullability(boundCollectionElementInitializer.ImplicitReceiverOpt, new VisitResult(boundCollectionElementInitializer.ImplicitReceiverOpt.Type, NullableAnnotation.NotAnnotated, NullableFlowState.NotNull));
|
|
}
|
|
SetUnknownResultNullability(boundCollectionElementInitializer);
|
|
SetUpdatedSymbol(boundCollectionElementInitializer, boundCollectionElementInitializer.AddMethod, updatedSymbol);
|
|
return null;
|
|
}
|
|
Action<int, TypeSymbol>? setUpdatedSymbolAsContinuation(BoundCollectionElementInitializer node2, ImmutableArray<VisitArgumentResult> argumentResults2, ArgumentsCompletionDelegate argumentsCompletionDelegate)
|
|
{
|
|
return delegate(int containingSlot, TypeSymbol containingType2)
|
|
{
|
|
MethodSymbol methodSymbol2 = addMethodAsMemberOfContainingType(node2, containingType2, ref argumentResults2);
|
|
setUpdatedSymbol(node2, containingType2, argumentsCompletionDelegate(argumentResults2, methodSymbol2.Parameters, methodSymbol2).method, argumentResults2, null, delayCompletionForType: false);
|
|
};
|
|
}
|
|
}
|
|
|
|
private void SetNotNullResult(BoundExpression node)
|
|
{
|
|
SetResultType(node, TypeWithState.Create(node.Type, NullableFlowState.NotNull));
|
|
}
|
|
|
|
protected override bool IsEmptyStructType(TypeSymbol type)
|
|
{
|
|
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0008: Invalid comparison between Unknown and I4
|
|
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
|
|
if ((int)type.TypeKind != 10)
|
|
{
|
|
return false;
|
|
}
|
|
if (!_emptyStructTypeCache.IsEmptyStructType(type))
|
|
{
|
|
return false;
|
|
}
|
|
if ((int)type.SpecialType != 0)
|
|
{
|
|
return true;
|
|
}
|
|
ImmutableArray<Symbol> membersUnordered = ((NamedTypeSymbol)type).GetMembersUnordered();
|
|
if (membersUnordered.Any((Symbol m) => (int)m.Kind == 6))
|
|
{
|
|
return true;
|
|
}
|
|
if (membersUnordered.Any((Symbol m) => (int)m.Kind == 15))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private int GetOrCreatePlaceholderSlot(BoundExpression node)
|
|
{
|
|
if (IsEmptyStructType(node.Type))
|
|
{
|
|
return -1;
|
|
}
|
|
return GetOrCreatePlaceholderSlot(node, TypeWithAnnotations.Create(node.Type, NullableAnnotation.NotAnnotated));
|
|
}
|
|
|
|
private int GetOrCreatePlaceholderSlot(object identifier, TypeWithAnnotations type)
|
|
{
|
|
if (_placeholderLocalsOpt == null)
|
|
{
|
|
_placeholderLocalsOpt = PooledDictionary<object, PlaceholderLocal>.GetInstance();
|
|
}
|
|
if (!((Dictionary<object, PlaceholderLocal>)(object)_placeholderLocalsOpt).TryGetValue(identifier, out PlaceholderLocal value))
|
|
{
|
|
value = new PlaceholderLocal(CurrentSymbol, identifier, type);
|
|
((Dictionary<object, PlaceholderLocal>)(object)_placeholderLocalsOpt).Add(identifier, value);
|
|
}
|
|
return GetOrCreateSlot(value, 0, forceSlotEvenIfEmpty: true);
|
|
}
|
|
|
|
public override BoundNode? VisitAnonymousObjectCreationExpression(BoundAnonymousObjectCreationExpression node)
|
|
{
|
|
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00c0: Invalid comparison between Unknown and I4
|
|
NamedTypeSymbol type = (NamedTypeSymbol)node.Type;
|
|
ImmutableArray<BoundExpression> arguments = node.Arguments;
|
|
ImmutableArray<TypeWithState> immutableArray = ImmutableArrayExtensions.SelectAsArray<BoundExpression, NullableWalker, TypeWithState>(arguments, (Func<BoundExpression, NullableWalker, TypeWithState>)((BoundExpression arg, NullableWalker self) => self.VisitRvalueWithState(arg)), this);
|
|
ImmutableArray<TypeWithAnnotations> immutableArray2 = ImmutableArrayExtensions.SelectAsArray<TypeWithState, TypeWithAnnotations>(immutableArray, (Func<TypeWithState, TypeWithAnnotations>)((TypeWithState arg) => arg.ToTypeWithAnnotations(compilation)));
|
|
if (immutableArray2.All((TypeWithAnnotations argType) => argType.HasType))
|
|
{
|
|
type = AnonymousTypeManager.ConstructAnonymousTypeSymbol(type, immutableArray2);
|
|
int orCreatePlaceholderSlot = GetOrCreatePlaceholderSlot(node);
|
|
int currentDeclarationIndex = 0;
|
|
for (int num = 0; num < arguments.Length; num++)
|
|
{
|
|
BoundExpression boundExpression = arguments[num];
|
|
TypeWithState typeWithState = immutableArray[num];
|
|
PropertySymbol anonymousTypeProperty = AnonymousTypeManager.GetAnonymousTypeProperty(type, num);
|
|
if ((int)anonymousTypeProperty.Type.SpecialType != 6)
|
|
{
|
|
int orCreateSlot = GetOrCreateSlot(anonymousTypeProperty, orCreatePlaceholderSlot);
|
|
TrackNullableStateForAssignment(boundExpression, anonymousTypeProperty.TypeWithAnnotations, orCreateSlot, typeWithState, MakeSlot(boundExpression));
|
|
BoundAnonymousPropertyDeclaration boundAnonymousPropertyDeclaration = getDeclaration(node, anonymousTypeProperty, ref currentDeclarationIndex);
|
|
if (boundAnonymousPropertyDeclaration != null)
|
|
{
|
|
TakeIncrementalSnapshot(boundAnonymousPropertyDeclaration);
|
|
SetAnalyzedNullability(boundAnonymousPropertyDeclaration, new VisitResult(typeWithState, anonymousTypeProperty.TypeWithAnnotations));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SetResultType(node, TypeWithState.Create(type, NullableFlowState.NotNull));
|
|
return null;
|
|
static BoundAnonymousPropertyDeclaration? getDeclaration(BoundAnonymousObjectCreationExpression boundAnonymousObjectCreationExpression, PropertySymbol currentProperty, ref int reference)
|
|
{
|
|
if (reference >= boundAnonymousObjectCreationExpression.Declarations.Length)
|
|
{
|
|
return null;
|
|
}
|
|
BoundAnonymousPropertyDeclaration boundAnonymousPropertyDeclaration2 = boundAnonymousObjectCreationExpression.Declarations[reference];
|
|
if (boundAnonymousPropertyDeclaration2.Property.MemberIndexOpt == currentProperty.MemberIndexOpt)
|
|
{
|
|
reference++;
|
|
return boundAnonymousPropertyDeclaration2;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitArrayCreation(BoundArrayCreation node)
|
|
{
|
|
ImmutableArray<BoundExpression>.Enumerator enumerator = node.Bounds.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current = enumerator.Current;
|
|
VisitRvalue(current);
|
|
}
|
|
BoundArrayInitialization initializerOpt = node.InitializerOpt;
|
|
if (initializerOpt == null)
|
|
{
|
|
SetResultType(node, TypeWithState.Create(node.Type, NullableFlowState.NotNull));
|
|
return null;
|
|
}
|
|
TypeSymbol type = VisitArrayInitialization(node.Type, initializerOpt, node.HasErrors);
|
|
SetResultType(node, TypeWithState.Create(type, NullableFlowState.NotNull));
|
|
return null;
|
|
}
|
|
|
|
private TypeSymbol VisitArrayInitialization(TypeSymbol type, BoundArrayInitialization initialization, bool hasErrors)
|
|
{
|
|
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0179: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_017e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0203: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0209: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0276: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_027c: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_032a: Unknown result type (might be due to invalid IL or missing references)
|
|
TakeIncrementalSnapshot(initialization);
|
|
ArrayBuilder<BoundExpression> instance = ArrayBuilder<BoundExpression>.GetInstance(initialization.Initializers.Length);
|
|
GetArrayElements(initialization, instance);
|
|
int count = instance.Count;
|
|
TypeWithAnnotations typeWithAnnotations;
|
|
if (!(type is ArrayTypeSymbol arrayTypeSymbol))
|
|
{
|
|
if (!(type is PointerTypeSymbol pointerTypeSymbol))
|
|
{
|
|
if (!(type is NamedTypeSymbol namedType))
|
|
{
|
|
throw ExceptionUtilities.UnexpectedValue((object)type.TypeKind);
|
|
}
|
|
typeWithAnnotations = getSpanElementType(namedType);
|
|
}
|
|
else
|
|
{
|
|
typeWithAnnotations = pointerTypeSymbol.PointedAtTypeWithAnnotations;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
typeWithAnnotations = arrayTypeSymbol.ElementTypeWithAnnotations;
|
|
}
|
|
TypeWithAnnotations targetTypeOpt = typeWithAnnotations;
|
|
TypeSymbol result = type;
|
|
if (!initialization.IsInferred)
|
|
{
|
|
Enumerator<BoundExpression> enumerator = instance.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current = enumerator.Current;
|
|
VisitOptionalImplicitConversion(current, targetTypeOpt, useLegacyWarnings: false, trackMembers: false, AssignmentKind.Assignment);
|
|
Unsplit();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ArrayBuilder<BoundExpression> instance2 = ArrayBuilder<BoundExpression>.GetInstance(count);
|
|
ArrayBuilder<Conversion> instance3 = ArrayBuilder<Conversion>.GetInstance(count);
|
|
ArrayBuilder<TypeWithState> instance4 = ArrayBuilder<TypeWithState>.GetInstance(count);
|
|
ArrayBuilder<BoundExpression> instance5 = ArrayBuilder<BoundExpression>.GetInstance(count);
|
|
Enumerator<BoundExpression> enumerator = instance.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current2 = enumerator.Current;
|
|
var (boundExpression, conversion) = RemoveConversion(current2, includeExplicitConversions: false);
|
|
instance2.Add(boundExpression);
|
|
instance3.Add(conversion);
|
|
SnapshotWalkerThroughConversionGroup(current2, boundExpression);
|
|
TypeWithState typeWithState = VisitRvalueWithState(boundExpression);
|
|
instance4.Add(typeWithState);
|
|
if (!IsTargetTypedExpression(boundExpression))
|
|
{
|
|
instance5.Add(CreatePlaceholderIfNecessary(boundExpression, typeWithState.ToTypeWithAnnotations(compilation)));
|
|
}
|
|
}
|
|
ImmutableArray<BoundExpression> exprs = instance5.ToImmutableAndFree();
|
|
TypeSymbol typeSymbol = null;
|
|
if (!hasErrors)
|
|
{
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
typeSymbol = BestTypeInferrer.InferBestType(exprs, _conversions, ref useSiteInfo, out var _);
|
|
}
|
|
TypeWithAnnotations typeWithAnnotations2 = (((object)typeSymbol == null) ? targetTypeOpt.SetUnknownNullabilityForReferenceTypes() : TypeWithAnnotations.Create(typeSymbol));
|
|
if ((object)typeSymbol != null)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
BoundExpression boundExpression2 = instance2[i];
|
|
BoundConversion conversionIfApplicable = GetConversionIfApplicable(instance[i], boundExpression2);
|
|
instance4[i] = VisitConversion(conversionIfApplicable, boundExpression2, instance3[i], typeWithAnnotations2, instance4[i], checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, null, reportTopLevelWarnings: false);
|
|
}
|
|
NullableFlowState nullableState = BestTypeInferrer.GetNullableState(instance4);
|
|
typeWithAnnotations2 = TypeWithState.Create(typeWithAnnotations2.Type, nullableState).ToTypeWithAnnotations(compilation);
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
VisitConversion(null, instance2[j], Conversion.Identity, typeWithAnnotations2, instance4[j], checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, null, reportTopLevelWarnings: true, reportRemainingWarnings: false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int k = 0; k < count; k++)
|
|
{
|
|
TrackAnalyzedNullabilityThroughConversionGroup(typeWithAnnotations2.ToTypeWithState(), instance[k] as BoundConversion, instance2[k]);
|
|
}
|
|
}
|
|
instance2.Free();
|
|
instance3.Free();
|
|
instance4.Free();
|
|
TypeSymbol typeSymbol2;
|
|
if (!(type is ArrayTypeSymbol arrayTypeSymbol2))
|
|
{
|
|
if (!(type is PointerTypeSymbol pointerTypeSymbol2))
|
|
{
|
|
if (!(type is NamedTypeSymbol namedType2))
|
|
{
|
|
throw ExceptionUtilities.UnexpectedValue((object)type.TypeKind);
|
|
}
|
|
typeSymbol2 = setSpanElementType(namedType2, typeWithAnnotations2);
|
|
}
|
|
else
|
|
{
|
|
typeSymbol2 = pointerTypeSymbol2.WithPointedAtType(typeWithAnnotations2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
typeSymbol2 = arrayTypeSymbol2.WithElementType(typeWithAnnotations2);
|
|
}
|
|
result = typeSymbol2;
|
|
}
|
|
instance.Free();
|
|
return result;
|
|
static TypeWithAnnotations getSpanElementType(NamedTypeSymbol namedTypeSymbol)
|
|
{
|
|
return namedTypeSymbol.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0];
|
|
}
|
|
static TypeSymbol setSpanElementType(NamedTypeSymbol namedTypeSymbol, TypeWithAnnotations elementType)
|
|
{
|
|
return namedTypeSymbol.OriginalDefinition.Construct(ImmutableArray.Create(elementType));
|
|
}
|
|
}
|
|
|
|
private static bool IsTargetTypedExpression(BoundExpression node)
|
|
{
|
|
if (node is BoundConditionalOperator boundConditionalOperator)
|
|
{
|
|
if (boundConditionalOperator.WasTargetTyped)
|
|
{
|
|
goto IL_004e;
|
|
}
|
|
}
|
|
else if (node is BoundConvertedSwitchExpression boundConvertedSwitchExpression)
|
|
{
|
|
if (boundConvertedSwitchExpression.WasTargetTyped)
|
|
{
|
|
goto IL_004e;
|
|
}
|
|
}
|
|
else if (node is BoundObjectCreationExpressionBase boundObjectCreationExpressionBase)
|
|
{
|
|
if (boundObjectCreationExpressionBase.WasTargetTyped)
|
|
{
|
|
goto IL_004e;
|
|
}
|
|
}
|
|
else if (node is BoundDelegateCreationExpression { WasTargetTyped: not false })
|
|
{
|
|
goto IL_004e;
|
|
}
|
|
return false;
|
|
IL_004e:
|
|
return true;
|
|
}
|
|
|
|
internal static TypeWithAnnotations BestTypeForLambdaReturns(ArrayBuilder<(BoundExpression expr, TypeWithAnnotations resultType, bool isChecked)> returns, Binder binder, BoundNode node, Conversions conversions, out bool inferredFromFunctionType)
|
|
{
|
|
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_010b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0111: Unknown result type (might be due to invalid IL or missing references)
|
|
NullableWalker nullableWalker = new NullableWalker(binder.Compilation, null, useConstructorExitWarnings: false, useDelegateInvokeParameterTypes: false, useDelegateInvokeReturnType: false, null, node, binder, conversions, null, null, null, null, null);
|
|
int count = returns.Count;
|
|
ArrayBuilder<TypeWithAnnotations> instance = ArrayBuilder<TypeWithAnnotations>.GetInstance(count);
|
|
ArrayBuilder<BoundExpression> instance2 = ArrayBuilder<BoundExpression>.GetInstance(count);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var (expr, typeWithAnnotations, _) = returns[i];
|
|
instance.Add(typeWithAnnotations);
|
|
instance2.Add(CreatePlaceholderIfNecessary(expr, typeWithAnnotations));
|
|
}
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
ImmutableArray<BoundExpression> exprs = instance2.ToImmutableAndFree();
|
|
TypeSymbol typeSymbol = BestTypeInferrer.InferBestType(exprs, nullableWalker._conversions, ref useSiteInfo, out inferredFromFunctionType);
|
|
TypeWithAnnotations result;
|
|
if ((object)typeSymbol != null)
|
|
{
|
|
TypeWithAnnotations targetTypeWithNullability = TypeWithAnnotations.Create(typeSymbol);
|
|
Conversions conversions2 = nullableWalker._conversions.WithNullability(includeNullability: false);
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
BoundExpression boundExpression = exprs[j];
|
|
Conversion conversion = conversions2.ClassifyConversionFromExpression(boundExpression, typeSymbol, returns[j].Item3, ref useSiteInfo);
|
|
instance[j] = nullableWalker.VisitConversion(null, boundExpression, conversion, targetTypeWithNullability, instance[j].ToTypeWithState(), checkConversion: false, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Return, null, reportTopLevelWarnings: false, reportRemainingWarnings: false).ToTypeWithAnnotations(binder.Compilation);
|
|
}
|
|
result = TypeWithAnnotations.Create(typeSymbol, BestTypeInferrer.GetNullableAnnotation(instance));
|
|
}
|
|
else
|
|
{
|
|
result = default(TypeWithAnnotations);
|
|
}
|
|
instance.Free();
|
|
nullableWalker.Free();
|
|
return result;
|
|
}
|
|
|
|
private static void GetArrayElements(BoundArrayInitialization node, ArrayBuilder<BoundExpression> builder)
|
|
{
|
|
ImmutableArray<BoundExpression>.Enumerator enumerator = node.Initializers.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current = enumerator.Current;
|
|
if (current.Kind == BoundKind.ArrayInitialization)
|
|
{
|
|
GetArrayElements((BoundArrayInitialization)current, builder);
|
|
}
|
|
else
|
|
{
|
|
builder.Add(current);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitArrayAccess(BoundArrayAccess node)
|
|
{
|
|
Visit(node.Expression);
|
|
CheckPossibleNullReceiver(node.Expression);
|
|
ArrayTypeSymbol arrayTypeSymbol = ResultType.Type as ArrayTypeSymbol;
|
|
ImmutableArray<BoundExpression>.Enumerator enumerator = node.Indices.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current = enumerator.Current;
|
|
VisitRvalue(current);
|
|
}
|
|
TypeWithAnnotations type = ((node.Indices.Length != 1 || !TypeSymbol.Equals(node.Indices[0].Type, compilation.GetWellKnownType((WellKnownType)285), (TypeCompareKind)0)) ? (arrayTypeSymbol?.ElementTypeWithAnnotations ?? default(TypeWithAnnotations)) : TypeWithAnnotations.Create(arrayTypeSymbol));
|
|
SetLvalueResultType(node, type);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitInlineArrayAccess(BoundInlineArrayAccess node)
|
|
{
|
|
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0036: Invalid comparison between Unknown and I4
|
|
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003e: Invalid comparison between Unknown and I4
|
|
TypeSymbol? type = VisitRvalueWithState(node.Expression).Type;
|
|
VisitRvalue(node.Argument);
|
|
TypeWithAnnotations typeWithAnnotations = type.TryGetInlineArrayElementField().TypeWithAnnotations;
|
|
WellKnownMember getItemOrSliceHelper = node.GetItemOrSliceHelper;
|
|
if (((int)getItemOrSliceHelper == 402 || (int)getItemOrSliceHelper == 408) ? true : false)
|
|
{
|
|
typeWithAnnotations = TypeWithAnnotations.Create(((NamedTypeSymbol)node.Type).OriginalDefinition.Construct(ImmutableArray.Create(typeWithAnnotations)));
|
|
}
|
|
SetResult(node, typeWithAnnotations.ToTypeWithState(), typeWithAnnotations);
|
|
return null;
|
|
}
|
|
|
|
private TypeWithState InferResultNullability(BinaryOperatorKind operatorKind, MethodSymbol? methodOpt, TypeSymbol resultType, TypeWithState leftType, TypeWithState rightType)
|
|
{
|
|
NullableFlowState defaultState = NullableFlowState.NotNull;
|
|
if (operatorKind.IsUserDefined())
|
|
{
|
|
if ((object)methodOpt != null && methodOpt.ParameterCount == 2)
|
|
{
|
|
if (operatorKind.IsLifted() && !operatorKind.IsComparison())
|
|
{
|
|
return GetLiftedReturnType(methodOpt.ReturnTypeWithAnnotations, leftType.State.Join(rightType.State));
|
|
}
|
|
TypeWithState result = GetReturnTypeWithState(methodOpt);
|
|
if ((leftType.IsNotNull && methodOpt.ReturnNotNullIfParameterNotNull.Contains(methodOpt.Parameters[0].Name)) || (rightType.IsNotNull && methodOpt.ReturnNotNullIfParameterNotNull.Contains(methodOpt.Parameters[1].Name)))
|
|
{
|
|
result = result.WithNotNullState();
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
else if (!operatorKind.IsDynamic() && !resultType.IsValueType)
|
|
{
|
|
defaultState = (operatorKind.Operator() | operatorKind.OperandTypes()) switch
|
|
{
|
|
BinaryOperatorKind.DelegateCombination => leftType.State.Meet(rightType.State),
|
|
BinaryOperatorKind.DelegateRemoval => NullableFlowState.MaybeNull,
|
|
_ => NullableFlowState.NotNull,
|
|
};
|
|
}
|
|
if (operatorKind.IsLifted() && !operatorKind.IsComparison())
|
|
{
|
|
defaultState = leftType.State.Join(rightType.State);
|
|
}
|
|
return TypeWithState.Create(resultType, defaultState);
|
|
}
|
|
|
|
protected override void VisitBinaryOperatorChildren(ArrayBuilder<BoundBinaryOperator> stack)
|
|
{
|
|
BoundBinaryOperator binary = ArrayBuilderExtensions.Pop<BoundBinaryOperator>(stack);
|
|
(BoundExpression, Conversion) tuple = RemoveConversion(binary.Left, includeExplicitConversions: false);
|
|
BoundExpression leftOperand = tuple.Item1;
|
|
Conversion leftConversion = tuple.Item2;
|
|
PossiblyConditionalState stateWhenNotNull;
|
|
bool flag = VisitPossibleConditionalAccess(leftOperand, out stateWhenNotNull) && AbstractFlowPass<LocalState, LocalFunctionState>.CanPropagateStateWhenNotNull(leftConversion);
|
|
if (flag)
|
|
{
|
|
BinaryOperatorKind binaryOperatorKind = binary.OperatorKind.Operator();
|
|
bool flag2 = ((binaryOperatorKind == BinaryOperatorKind.Equal || binaryOperatorKind == BinaryOperatorKind.NotEqual) ? true : false);
|
|
flag = flag2;
|
|
}
|
|
if (flag)
|
|
{
|
|
TypeWithState resultType = ResultType;
|
|
(BoundExpression expression, Conversion conversion) tuple2 = RemoveConversion(binary.Right, includeExplicitConversions: false);
|
|
BoundExpression item = tuple2.expression;
|
|
Conversion item2 = tuple2.conversion;
|
|
bool disableDiagnostics = _disableDiagnostics;
|
|
_disableDiagnostics = true;
|
|
LocalState state = State;
|
|
SetState(getUnconditionalStateWhenNotNull(item, stateWhenNotNull));
|
|
VisitRvalue(item);
|
|
LocalState state2 = State;
|
|
_disableDiagnostics = disableDiagnostics;
|
|
SetState(state);
|
|
TypeWithState typeWithState = VisitRvalueWithState(item);
|
|
ReinferBinaryOperatorAndSetResult(leftOperand, leftConversion, resultType, item, item2, typeWithState, binary);
|
|
if (isKnownNullOrNotNull(item, typeWithState))
|
|
{
|
|
ConstantValue? constantValueOpt = item.ConstantValueOpt;
|
|
bool flag3 = constantValueOpt != null && constantValueOpt.IsNull;
|
|
SetConditionalState((flag3 == isEquals(binary)) ? (whenTrue: State, whenFalse: state2) : (whenTrue: state2, whenFalse: State));
|
|
}
|
|
if (stack.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
leftOperand = binary;
|
|
leftConversion = Conversion.Identity;
|
|
binary = ArrayBuilderExtensions.Pop<BoundBinaryOperator>(stack);
|
|
}
|
|
while (true)
|
|
{
|
|
if (!learnFromConditionalAccessOrBoolConstant())
|
|
{
|
|
Unsplit();
|
|
UseRvalueOnly(leftOperand);
|
|
AfterLeftChildHasBeenVisited(leftOperand, leftConversion, binary);
|
|
}
|
|
if (stack.Count != 0)
|
|
{
|
|
leftOperand = binary;
|
|
leftConversion = Conversion.Identity;
|
|
binary = ArrayBuilderExtensions.Pop<BoundBinaryOperator>(stack);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
LocalState getUnconditionalStateWhenNotNull(BoundExpression otherOperand, PossiblyConditionalState conditionalStateWhenNotNull)
|
|
{
|
|
LocalState self;
|
|
if (!conditionalStateWhenNotNull.IsConditionalState)
|
|
{
|
|
self = conditionalStateWhenNotNull.State;
|
|
}
|
|
else
|
|
{
|
|
if (isEquals(binary))
|
|
{
|
|
ConstantValue constantValueOpt2 = otherOperand.ConstantValueOpt;
|
|
if (constantValueOpt2 != null && constantValueOpt2.IsBoolean)
|
|
{
|
|
self = (constantValueOpt2.BooleanValue ? conditionalStateWhenNotNull.StateWhenTrue : conditionalStateWhenNotNull.StateWhenFalse);
|
|
goto IL_0062;
|
|
}
|
|
}
|
|
self = conditionalStateWhenNotNull.StateWhenTrue;
|
|
Join(ref self, ref conditionalStateWhenNotNull.StateWhenFalse);
|
|
}
|
|
goto IL_0062;
|
|
IL_0062:
|
|
return self;
|
|
}
|
|
static bool isEquals(BoundBinaryOperator boundBinaryOperator)
|
|
{
|
|
return boundBinaryOperator.OperatorKind.Operator() == BinaryOperatorKind.Equal;
|
|
}
|
|
static bool isKnownNullOrNotNull(BoundExpression expr, TypeWithState typeWithState2)
|
|
{
|
|
if (!typeWithState2.State.IsNotNull())
|
|
{
|
|
return expr.ConstantValueOpt != null;
|
|
}
|
|
return true;
|
|
}
|
|
bool learnFromConditionalAccessOrBoolConstant()
|
|
{
|
|
BinaryOperatorKind binaryOperatorKind2 = binary.OperatorKind.Operator();
|
|
if ((binaryOperatorKind2 != BinaryOperatorKind.Equal && binaryOperatorKind2 != BinaryOperatorKind.NotEqual) || 1 == 0)
|
|
{
|
|
return false;
|
|
}
|
|
TypeWithState resultType2 = ResultType;
|
|
var (boundExpression, conversion) = RemoveConversion(binary.Right, includeExplicitConversions: false);
|
|
if (isKnownNullOrNotNull(leftOperand, resultType2) && AbstractFlowPass<LocalState, LocalFunctionState>.CanPropagateStateWhenNotNull(conversion) && TryVisitConditionalAccess(boundExpression, out var stateWhenNotNull2))
|
|
{
|
|
ReinferBinaryOperatorAndSetResult(leftOperand, leftConversion, resultType2, boundExpression, conversion, ResultType, binary);
|
|
LocalState localState = getUnconditionalStateWhenNotNull(leftOperand, stateWhenNotNull2);
|
|
ConstantValue? constantValueOpt2 = leftOperand.ConstantValueOpt;
|
|
bool flag4 = constantValueOpt2 != null && constantValueOpt2.IsNull;
|
|
SetConditionalState((flag4 == isEquals(binary)) ? (whenTrue: State, whenFalse: localState) : (whenTrue: localState, whenFalse: State));
|
|
return true;
|
|
}
|
|
if (binary.OperatorKind.IsUserDefined())
|
|
{
|
|
return false;
|
|
}
|
|
if (IsConditionalState)
|
|
{
|
|
ConstantValue constantValueOpt3 = binary.Right.ConstantValueOpt;
|
|
if (constantValueOpt3 != null && constantValueOpt3.IsBoolean)
|
|
{
|
|
LocalState localState2 = StateWhenTrue.Clone();
|
|
LocalState localState3 = StateWhenFalse.Clone();
|
|
LocalState localState4 = localState2;
|
|
Unsplit();
|
|
Visit(binary.Right);
|
|
UseRvalueOnly(binary.Right);
|
|
SetConditionalState((isEquals(binary) == constantValueOpt3.BooleanValue) ? (whenTrue: localState4, whenFalse: localState3) : (whenTrue: localState3, whenFalse: localState4));
|
|
goto IL_0226;
|
|
}
|
|
}
|
|
ConstantValue constantValueOpt4 = binary.Left.ConstantValueOpt;
|
|
if (constantValueOpt4 == null || !constantValueOpt4.IsBoolean)
|
|
{
|
|
return false;
|
|
}
|
|
Unsplit();
|
|
Visit(binary.Right);
|
|
UseRvalueOnly(binary.Right);
|
|
if (IsConditionalState && isEquals(binary) != constantValueOpt4.BooleanValue)
|
|
{
|
|
SetConditionalState(StateWhenFalse, StateWhenTrue);
|
|
}
|
|
goto IL_0226;
|
|
IL_0226:
|
|
SetResult(binary, TypeWithState.ForType(binary.Type), TypeWithAnnotations.Create(binary.Type));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private void ReinferBinaryOperatorAndSetResult(BoundExpression leftOperand, Conversion leftConversion, TypeWithState leftType, BoundExpression rightOperand, Conversion rightConversion, TypeWithState rightType, BoundBinaryOperator binary)
|
|
{
|
|
MethodSymbol methodSymbol = binary.Method;
|
|
bool isLifted;
|
|
if (binary.OperatorKind.IsUserDefined() && (object)methodSymbol != null && methodSymbol.ParameterCount == 2)
|
|
{
|
|
TypeSymbol containingType = methodSymbol.ContainingType;
|
|
isLifted = binary.OperatorKind.IsLifted();
|
|
TypeWithState nullableUnderlyingTypeIfNecessary = GetNullableUnderlyingTypeIfNecessary(isLifted, leftType);
|
|
TypeWithState nullableUnderlyingTypeIfNecessary2 = GetNullableUnderlyingTypeIfNecessary(isLifted, rightType);
|
|
methodSymbol = (MethodSymbol)AsMemberOfType(getTypeIfContainingType(containingType, nullableUnderlyingTypeIfNecessary.Type, leftOperand) ?? getTypeIfContainingType(containingType, nullableUnderlyingTypeIfNecessary2.Type, rightOperand) ?? containingType, methodSymbol);
|
|
ImmutableArray<ParameterSymbol> parameters = methodSymbol.Parameters;
|
|
visitOperandConversionAndPostConditions(binary.Left, leftOperand, leftConversion, parameters[0], nullableUnderlyingTypeIfNecessary);
|
|
visitOperandConversionAndPostConditions(binary.Right, rightOperand, rightConversion, parameters[1], nullableUnderlyingTypeIfNecessary2);
|
|
SetUpdatedSymbol(binary, binary.Method, methodSymbol);
|
|
}
|
|
else
|
|
{
|
|
visitOperandConversion(binary.Left, leftOperand, leftConversion, leftType);
|
|
visitOperandConversion(binary.Right, rightOperand, rightConversion, rightType);
|
|
}
|
|
bool flag = binary.OperatorKind.IsLifted();
|
|
if (flag)
|
|
{
|
|
bool flag2;
|
|
switch (binary.OperatorKind.Operator())
|
|
{
|
|
case BinaryOperatorKind.GreaterThan:
|
|
case BinaryOperatorKind.LessThan:
|
|
case BinaryOperatorKind.GreaterThanOrEqual:
|
|
case BinaryOperatorKind.LessThanOrEqual:
|
|
flag2 = true;
|
|
break;
|
|
default:
|
|
flag2 = false;
|
|
break;
|
|
}
|
|
flag = flag2;
|
|
}
|
|
if (flag)
|
|
{
|
|
SplitAndLearnFromNonNullTest(binary.Left, whenTrue: true);
|
|
SplitAndLearnFromNonNullTest(binary.Right, whenTrue: true);
|
|
}
|
|
TypeWithState resultType = InferResultNullability(binary.OperatorKind, methodSymbol, binary.Type, leftType, rightType);
|
|
SetResult(binary, resultType, resultType.ToTypeWithAnnotations(compilation));
|
|
TypeSymbol? getTypeIfContainingType(TypeSymbol baseType, TypeSymbol? derivedType, BoundExpression operand)
|
|
{
|
|
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
|
|
if ((object)derivedType == null || IsTargetTypedExpression(operand))
|
|
{
|
|
return null;
|
|
}
|
|
derivedType = derivedType.StrippedType();
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
Conversion conversion = _conversions.ClassifyBuiltInConversion(derivedType, baseType, isChecked: false, ref useSiteInfo);
|
|
if (conversion.Exists && !conversion.IsExplicit)
|
|
{
|
|
return derivedType;
|
|
}
|
|
return null;
|
|
}
|
|
void visitOperandConversion(BoundExpression expr, BoundExpression operand, Conversion conversion, TypeWithState operandType)
|
|
{
|
|
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
|
|
if ((object)expr.Type != null)
|
|
{
|
|
VisitConversion(expr as BoundConversion, operand, conversion, TypeWithAnnotations.Create(expr.Type), operandType, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Argument);
|
|
}
|
|
}
|
|
void visitOperandConversionAndPostConditions(BoundExpression expr, BoundExpression operand, Conversion conversion, ParameterSymbol parameter, TypeWithState operandType)
|
|
{
|
|
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
|
|
FlowAnalysisAnnotations parameterAnnotations = GetParameterAnnotations(parameter);
|
|
TypeWithAnnotations typeWithAnnotations = ApplyLValueAnnotations(parameter.TypeWithAnnotations, parameterAnnotations);
|
|
if (isLifted && typeWithAnnotations.Type.IsNonNullableValueType())
|
|
{
|
|
typeWithAnnotations = TypeWithAnnotations.Create(MakeNullableOf(typeWithAnnotations));
|
|
}
|
|
TypeWithState state = VisitConversion(expr as BoundConversion, operand, conversion, typeWithAnnotations, operandType, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Argument, parameter);
|
|
CheckDisallowedNullAssignment(state, parameterAnnotations, expr.Syntax, operand);
|
|
LearnFromPostConditions(operand, parameterAnnotations);
|
|
}
|
|
}
|
|
|
|
private void AfterLeftChildHasBeenVisited(BoundExpression leftOperand, Conversion leftConversion, BoundBinaryOperator binary)
|
|
{
|
|
TypeWithState resultType = ResultType;
|
|
var (boundExpression, rightConversion) = RemoveConversion(binary.Right, includeExplicitConversions: false);
|
|
VisitRvalue(boundExpression);
|
|
TypeWithState resultType2 = ResultType;
|
|
ReinferBinaryOperatorAndSetResult(leftOperand, leftConversion, resultType, boundExpression, rightConversion, resultType2, binary);
|
|
BinaryOperatorKind binaryOperatorKind = binary.OperatorKind.Operator();
|
|
if (binaryOperatorKind == BinaryOperatorKind.Equal || binaryOperatorKind == BinaryOperatorKind.NotEqual)
|
|
{
|
|
BoundExpression boundExpression2 = null;
|
|
ConstantValue? constantValueOpt = binary.Right.ConstantValueOpt;
|
|
if (constantValueOpt != null && constantValueOpt.IsNull)
|
|
{
|
|
boundExpression2 = binary.Left;
|
|
}
|
|
else
|
|
{
|
|
ConstantValue? constantValueOpt2 = binary.Left.ConstantValueOpt;
|
|
if (constantValueOpt2 != null && constantValueOpt2.IsNull)
|
|
{
|
|
boundExpression2 = binary.Right;
|
|
}
|
|
}
|
|
if (boundExpression2 != null)
|
|
{
|
|
bool flag = binaryOperatorKind != BinaryOperatorKind.Equal;
|
|
SplitAndLearnFromNonNullTest(boundExpression2, flag);
|
|
LearnFromNullTest(boundExpression2, ref flag ? ref StateWhenFalse : ref StateWhenTrue);
|
|
return;
|
|
}
|
|
}
|
|
BoundExpression boundExpression3 = null;
|
|
if (resultType.IsNotNull && resultType2.MayBeNull)
|
|
{
|
|
boundExpression3 = binary.Right;
|
|
}
|
|
else if (resultType2.IsNotNull && resultType.MayBeNull)
|
|
{
|
|
boundExpression3 = binary.Left;
|
|
}
|
|
if (boundExpression3 != null)
|
|
{
|
|
switch (binaryOperatorKind)
|
|
{
|
|
case BinaryOperatorKind.Equal:
|
|
case BinaryOperatorKind.GreaterThan:
|
|
case BinaryOperatorKind.LessThan:
|
|
case BinaryOperatorKind.GreaterThanOrEqual:
|
|
case BinaryOperatorKind.LessThanOrEqual:
|
|
boundExpression3 = SkipReferenceConversions(boundExpression3);
|
|
SplitAndLearnFromNonNullTest(boundExpression3, whenTrue: true);
|
|
break;
|
|
case BinaryOperatorKind.NotEqual:
|
|
boundExpression3 = SkipReferenceConversions(boundExpression3);
|
|
SplitAndLearnFromNonNullTest(boundExpression3, whenTrue: false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SplitAndLearnFromNonNullTest(BoundExpression operandComparedToNonNull, bool whenTrue)
|
|
{
|
|
ArrayBuilder<int> instance = ArrayBuilder<int>.GetInstance();
|
|
GetSlotsToMarkAsNotNullable(operandComparedToNonNull, instance);
|
|
if (instance.Count != 0)
|
|
{
|
|
Split();
|
|
MarkSlotsAsNotNull(instance, ref whenTrue ? ref StateWhenTrue : ref StateWhenFalse);
|
|
}
|
|
instance.Free();
|
|
}
|
|
|
|
protected override bool VisitInterpolatedStringHandlerParts(BoundInterpolatedStringBase node, bool usesBoolReturns, bool firstPartIsConditional, ref LocalState shortCircuitState)
|
|
{
|
|
bool result = base.VisitInterpolatedStringHandlerParts(node, usesBoolReturns, firstPartIsConditional, ref shortCircuitState);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
protected override void VisitInterpolatedStringBinaryOperatorNode(BoundBinaryOperator node)
|
|
{
|
|
SetNotNullResult(node);
|
|
}
|
|
|
|
private void GetSlotsToMarkAsNotNullable(BoundExpression operand, ArrayBuilder<int> slotBuilder)
|
|
{
|
|
int lastConditionalAccessSlot = _lastConditionalAccessSlot;
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
switch (operand.Kind)
|
|
{
|
|
case BoundKind.Conversion:
|
|
operand = ((BoundConversion)operand).Operand;
|
|
break;
|
|
case BoundKind.AsOperator:
|
|
operand = ((BoundAsOperator)operand).Operand;
|
|
break;
|
|
case BoundKind.ConditionalAccess:
|
|
{
|
|
BoundConditionalAccess boundConditionalAccess = (BoundConditionalAccess)operand;
|
|
GetSlotsToMarkAsNotNullable(boundConditionalAccess.Receiver, slotBuilder);
|
|
int num = MakeSlot(boundConditionalAccess.Receiver);
|
|
if (num > 0)
|
|
{
|
|
TypeSymbol type = boundConditionalAccess.Receiver.Type;
|
|
if (type.IsNullableType())
|
|
{
|
|
num = GetNullableOfTValueSlot(type, num, out Symbol _);
|
|
}
|
|
}
|
|
if (num > 0)
|
|
{
|
|
_lastConditionalAccessSlot = num;
|
|
operand = boundConditionalAccess.AccessExpression;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
{
|
|
int num = MakeSlot(operand);
|
|
if (num > 0 && PossiblyNullableType(operand.Type))
|
|
{
|
|
slotBuilder.Add(num);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_lastConditionalAccessSlot = lastConditionalAccessSlot;
|
|
}
|
|
}
|
|
|
|
private static bool PossiblyNullableType([NotNullWhen(true)] TypeSymbol? operandType)
|
|
{
|
|
return operandType?.CanContainNull() ?? false;
|
|
}
|
|
|
|
private void MarkSlotsAsNotNull(ArrayBuilder<int> slots, ref LocalState stateToUpdate)
|
|
{
|
|
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
|
|
Enumerator<int> enumerator = slots.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
int current = enumerator.Current;
|
|
SetState(ref stateToUpdate, current, NullableFlowState.NotNull);
|
|
}
|
|
}
|
|
|
|
private void LearnFromNonNullTest(BoundExpression expression, ref LocalState state)
|
|
{
|
|
if (expression is BoundValuePlaceholderBase key)
|
|
{
|
|
if (_resultForPlaceholdersOpt == null || !((Dictionary<BoundValuePlaceholderBase, (BoundExpression, VisitResult)>)(object)_resultForPlaceholdersOpt).TryGetValue(key, out (BoundExpression, VisitResult) value) || value.Item1 == null)
|
|
{
|
|
return;
|
|
}
|
|
(expression, _) = value;
|
|
}
|
|
ArrayBuilder<int> instance = ArrayBuilder<int>.GetInstance();
|
|
GetSlotsToMarkAsNotNullable(expression, instance);
|
|
MarkSlotsAsNotNull(instance, ref state);
|
|
instance.Free();
|
|
}
|
|
|
|
private void LearnFromNonNullTest(int slot, ref LocalState state)
|
|
{
|
|
SetState(ref state, slot, NullableFlowState.NotNull);
|
|
}
|
|
|
|
private void LearnFromNullTest(BoundExpression expression, ref LocalState state)
|
|
{
|
|
if (!(expression.ConstantValueOpt != (ConstantValue)null))
|
|
{
|
|
BoundExpression item = RemoveConversion(expression, includeExplicitConversions: true).expression;
|
|
int slot = MakeSlot(item);
|
|
LearnFromNullTest(slot, item.Type, ref state, markDependentSlotsNotNull: false);
|
|
}
|
|
}
|
|
|
|
private void LearnFromNullTest(int slot, TypeSymbol? expressionType, ref LocalState state, bool markDependentSlotsNotNull)
|
|
{
|
|
if (slot > 0 && PossiblyNullableType(expressionType))
|
|
{
|
|
if (GetState(ref state, slot) == NullableFlowState.NotNull)
|
|
{
|
|
SetState(ref state, slot, NullableFlowState.MaybeNull);
|
|
}
|
|
if (markDependentSlotsNotNull)
|
|
{
|
|
MarkDependentSlotsNotNull(slot, expressionType, ref state);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void MarkDependentSlotsNotNull(int slot, TypeSymbol expressionType, ref LocalState state, int depth = 2)
|
|
{
|
|
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0053: Invalid comparison between Unknown and I4
|
|
if (depth <= 0)
|
|
{
|
|
return;
|
|
}
|
|
foreach (Symbol member in getMembers(expressionType))
|
|
{
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
NamedTypeSymbol namedTypeSymbol = _symbol?.ContainingType;
|
|
if ((member is PropertySymbol { IsIndexedProperty: false } || (int)member.Kind == 6) && member.RequiresInstanceReceiver() && ((object)namedTypeSymbol == null || AccessCheck.IsSymbolAccessible(member, namedTypeSymbol, ref useSiteInfo)))
|
|
{
|
|
int orCreateSlot = GetOrCreateSlot(member, slot, forceSlotEvenIfEmpty: true, createIfMissing: false);
|
|
if (orCreateSlot > 0)
|
|
{
|
|
SetState(ref state, orCreateSlot, NullableFlowState.NotNull);
|
|
MarkDependentSlotsNotNull(orCreateSlot, member.GetTypeOrReturnType().Type, ref state, depth - 1);
|
|
}
|
|
}
|
|
}
|
|
static NamedTypeSymbol effectiveBase(TypeSymbol type)
|
|
{
|
|
if (!(type is TypeParameterSymbol { EffectiveBaseClassNoUseSiteDiagnostics: var effectiveBaseClassNoUseSiteDiagnostics }))
|
|
{
|
|
return type.BaseTypeNoUseSiteDiagnostics;
|
|
}
|
|
return effectiveBaseClassNoUseSiteDiagnostics;
|
|
}
|
|
static IEnumerable<Symbol> getMembers(TypeSymbol type)
|
|
{
|
|
ImmutableArray<Symbol>.Enumerator enumerator2 = type.GetMembers().GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
yield return enumerator2.Current;
|
|
}
|
|
NamedTypeSymbol baseType = effectiveBase(type);
|
|
while ((object)baseType != null)
|
|
{
|
|
enumerator2 = baseType.GetMembers().GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
yield return enumerator2.Current;
|
|
}
|
|
baseType = baseType.BaseTypeNoUseSiteDiagnostics;
|
|
}
|
|
ImmutableArray<NamedTypeSymbol>.Enumerator enumerator3 = inheritedInterfaces(type).GetEnumerator();
|
|
while (enumerator3.MoveNext())
|
|
{
|
|
NamedTypeSymbol current2 = enumerator3.Current;
|
|
enumerator2 = current2.GetMembers().GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
yield return enumerator2.Current;
|
|
}
|
|
}
|
|
}
|
|
static ImmutableArray<NamedTypeSymbol> inheritedInterfaces(TypeSymbol type)
|
|
{
|
|
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0014: Invalid comparison between Unknown and I4
|
|
if (!(type is TypeParameterSymbol { AllEffectiveInterfacesNoUseSiteDiagnostics: var allEffectiveInterfacesNoUseSiteDiagnostics }))
|
|
{
|
|
if ((object)type != null && (int)type.TypeKind == 7)
|
|
{
|
|
return type.AllInterfacesNoUseSiteDiagnostics;
|
|
}
|
|
return ImmutableArray<NamedTypeSymbol>.Empty;
|
|
}
|
|
return allEffectiveInterfacesNoUseSiteDiagnostics;
|
|
}
|
|
}
|
|
|
|
private static BoundExpression SkipReferenceConversions(BoundExpression possiblyConversion)
|
|
{
|
|
while (possiblyConversion.Kind == BoundKind.Conversion)
|
|
{
|
|
BoundConversion boundConversion = (BoundConversion)possiblyConversion;
|
|
ConversionKind conversionKind = boundConversion.ConversionKind;
|
|
if (conversionKind == ConversionKind.ImplicitReference || conversionKind == ConversionKind.ExplicitReference)
|
|
{
|
|
possiblyConversion = boundConversion.Operand;
|
|
continue;
|
|
}
|
|
return possiblyConversion;
|
|
}
|
|
return possiblyConversion;
|
|
}
|
|
|
|
public override BoundNode? VisitNullCoalescingAssignmentOperator(BoundNullCoalescingAssignmentOperator node)
|
|
{
|
|
BoundExpression leftOperand = node.LeftOperand;
|
|
BoundExpression rightOperand = node.RightOperand;
|
|
int num = MakeSlot(leftOperand);
|
|
TypeWithAnnotations typeWithAnnotations = VisitLvalueWithAnnotations(leftOperand);
|
|
LocalState state = State.Clone();
|
|
LearnFromNonNullTest(leftOperand, ref state);
|
|
LearnFromNullTest(leftOperand, ref State);
|
|
if (node.IsNullableValueTypeAssignment)
|
|
{
|
|
if (num > 0)
|
|
{
|
|
SetState(ref State, num, NullableFlowState.NotNull);
|
|
num = GetNullableOfTValueSlot(typeWithAnnotations.Type, num, out Symbol _);
|
|
}
|
|
typeWithAnnotations = TypeWithAnnotations.Create(node.Type, NullableAnnotation.NotAnnotated);
|
|
}
|
|
TypeWithState valueType = VisitOptionalImplicitConversion(rightOperand, typeWithAnnotations, UseLegacyWarnings(leftOperand), trackMembers: false, AssignmentKind.Assignment);
|
|
TrackNullableStateForAssignment(rightOperand, typeWithAnnotations, num, valueType, MakeSlot(rightOperand));
|
|
Join(ref State, ref state);
|
|
TypeWithState type = TypeWithState.Create(typeWithAnnotations.Type, valueType.State);
|
|
SetResultType(node, type);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitNullCoalescingOperator(BoundNullCoalescingOperator node)
|
|
{
|
|
BoundExpression leftOperand = node.LeftOperand;
|
|
BoundExpression rightOperand = node.RightOperand;
|
|
if (AbstractFlowPass<LocalState, LocalFunctionState>.IsConstantNull(leftOperand))
|
|
{
|
|
VisitRvalue(leftOperand);
|
|
Visit(rightOperand);
|
|
TypeWithState resultType = ResultType;
|
|
SetResultType(node, TypeWithState.Create(node.Type, resultType.State));
|
|
return null;
|
|
}
|
|
VisitPossibleConditionalAccess(leftOperand, out var stateWhenNotNull);
|
|
TypeWithState resultType2 = ResultType;
|
|
Unsplit();
|
|
LearnFromNullTest(leftOperand, ref State);
|
|
if (leftOperand.ConstantValueOpt != (ConstantValue)null)
|
|
{
|
|
SetUnreachable();
|
|
}
|
|
Visit(rightOperand);
|
|
TypeWithState resultType3 = ResultType;
|
|
Join(ref stateWhenNotNull);
|
|
TypeSymbol type = resultType2.Type;
|
|
TypeSymbol type2 = resultType3.Type;
|
|
var (type3, b) = node.OperatorResultKind switch
|
|
{
|
|
BoundNullCoalescingOperatorResultKind.NoCommonType => (node.Type, NullableFlowState.NotNull),
|
|
BoundNullCoalescingOperatorResultKind.LeftType => getLeftResultType(type, type2),
|
|
BoundNullCoalescingOperatorResultKind.LeftUnwrappedType => getLeftResultType(type.StrippedType(), type2),
|
|
BoundNullCoalescingOperatorResultKind.RightType => getResultStateWithRightType(type, type2),
|
|
BoundNullCoalescingOperatorResultKind.LeftUnwrappedRightType => getResultStateWithRightType(type.StrippedType(), type2),
|
|
BoundNullCoalescingOperatorResultKind.RightDynamicType => (type2, NullableFlowState.NotNull),
|
|
_ => throw ExceptionUtilities.UnexpectedValue((object)node.OperatorResultKind),
|
|
};
|
|
SetResultType(node, TypeWithState.Create(type3, resultType3.State.Join(b)));
|
|
return null;
|
|
(TypeSymbol ResultType, NullableFlowState LeftState) getLeftResultType(TypeSymbol leftType, TypeSymbol rightType)
|
|
{
|
|
BoundConversion obj = node.RightOperand as BoundConversion;
|
|
if ((obj == null || obj.ExplicitCastInCode) && GenerateConversionForConditionalOperator(node.LeftOperand, leftType, rightType, reportMismatch: false, node.Checked).Exists)
|
|
{
|
|
return (ResultType: rightType, LeftState: NullableFlowState.NotNull);
|
|
}
|
|
Conversion conversion = GenerateConversionForConditionalOperator(node.RightOperand, rightType, leftType, reportMismatch: true, node.Checked);
|
|
return (ResultType: leftType, LeftState: NullableFlowState.NotNull);
|
|
}
|
|
(TypeSymbol ResultType, NullableFlowState LeftState) getResultStateWithRightType(TypeSymbol leftType, TypeSymbol rightType)
|
|
{
|
|
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
|
|
Conversion conversion = GenerateConversionForConditionalOperator(node.LeftOperand, leftType, rightType, reportMismatch: true, node.Checked);
|
|
if (conversion.IsUserDefined)
|
|
{
|
|
TypeWithState typeWithState = VisitConversion(null, node.LeftOperand, conversion, TypeWithAnnotations.Create(rightType), TypeWithState.Create(leftType, NullableFlowState.NotNull), checkConversion: false, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, null, reportTopLevelWarnings: false, reportRemainingWarnings: false);
|
|
return (ResultType: typeWithState.Type, LeftState: typeWithState.State);
|
|
}
|
|
return (ResultType: rightType, LeftState: NullableFlowState.NotNull);
|
|
}
|
|
}
|
|
|
|
private bool TryVisitConditionalAccess(BoundExpression node, out PossiblyConditionalState stateWhenNotNull)
|
|
{
|
|
//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
|
|
var (boundExpression, conversion) = RemoveConversion(node, includeExplicitConversions: true);
|
|
if (!(boundExpression is BoundConditionalAccess boundConditionalAccess) || !AbstractFlowPass<LocalState, LocalFunctionState>.CanPropagateStateWhenNotNull(conversion))
|
|
{
|
|
stateWhenNotNull = default(PossiblyConditionalState);
|
|
return false;
|
|
}
|
|
Unsplit();
|
|
VisitConditionalAccess(boundConditionalAccess, out stateWhenNotNull);
|
|
if (node is BoundConversion boundConversion)
|
|
{
|
|
TypeWithState resultType = ResultType;
|
|
TypeWithAnnotations typeWithAnnotations = boundConversion.ConversionGroupOpt?.ExplicitType ?? default(TypeWithAnnotations);
|
|
bool hasType = typeWithAnnotations.HasType;
|
|
TypeWithAnnotations targetTypeWithNullability = (hasType ? typeWithAnnotations : TypeWithAnnotations.Create(boundConversion.Type));
|
|
TypeWithState type = VisitConversion(boundConversion, boundConditionalAccess, conversion, targetTypeWithNullability, resultType, checkConversion: true, hasType, useLegacyWarnings: true, AssignmentKind.Assignment);
|
|
SetResultType(boundConversion, type);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool VisitPossibleConditionalAccess(BoundExpression node, out PossiblyConditionalState stateWhenNotNull)
|
|
{
|
|
if (TryVisitConditionalAccess(node, out stateWhenNotNull))
|
|
{
|
|
return true;
|
|
}
|
|
Visit(node);
|
|
stateWhenNotNull = PossiblyConditionalState.Create(this);
|
|
node = RemoveConversion(node, includeExplicitConversions: true).expression;
|
|
int num = MakeSlot(node);
|
|
if (num > -1)
|
|
{
|
|
if (IsConditionalState)
|
|
{
|
|
LearnFromNonNullTest(num, ref stateWhenNotNull.StateWhenTrue);
|
|
LearnFromNonNullTest(num, ref stateWhenNotNull.StateWhenFalse);
|
|
}
|
|
else
|
|
{
|
|
LearnFromNonNullTest(num, ref stateWhenNotNull.State);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void VisitConditionalAccess(BoundConditionalAccess node, out PossiblyConditionalState stateWhenNotNull)
|
|
{
|
|
BoundExpression receiver = node.Receiver;
|
|
VisitPossibleConditionalAccess(receiver, out stateWhenNotNull);
|
|
Unsplit();
|
|
_currentConditionalReceiverVisitResult = _visitResult;
|
|
int lastConditionalAccessSlot = _lastConditionalAccessSlot;
|
|
ConstantValue constantValueOpt = receiver.ConstantValueOpt;
|
|
if (constantValueOpt != null && !constantValueOpt.IsNull)
|
|
{
|
|
VisitPossibleConditionalAccess(node.AccessExpression, out stateWhenNotNull);
|
|
}
|
|
else
|
|
{
|
|
LocalState state = State.Clone();
|
|
if (AbstractFlowPass<LocalState, LocalFunctionState>.IsConstantNull(receiver))
|
|
{
|
|
SetUnreachable();
|
|
_lastConditionalAccessSlot = -1;
|
|
}
|
|
else
|
|
{
|
|
LearnFromNullTest(receiver, ref state);
|
|
makeAndAdjustReceiverSlot(receiver);
|
|
SetPossiblyConditionalState(in stateWhenNotNull);
|
|
}
|
|
BoundExpression accessExpression;
|
|
for (accessExpression = node.AccessExpression; accessExpression is BoundConditionalAccess boundConditionalAccess; accessExpression = boundConditionalAccess.AccessExpression)
|
|
{
|
|
VisitRvalue(boundConditionalAccess.Receiver);
|
|
_currentConditionalReceiverVisitResult = _visitResult;
|
|
makeAndAdjustReceiverSlot(boundConditionalAccess.Receiver);
|
|
Join(ref state, ref State);
|
|
}
|
|
Visit(accessExpression);
|
|
for (accessExpression = node.AccessExpression; accessExpression is BoundConditionalAccess boundConditionalAccess2; accessExpression = boundConditionalAccess2.AccessExpression)
|
|
{
|
|
SetAnalyzedNullability(boundConditionalAccess2, _visitResult);
|
|
}
|
|
int num = MakeSlot(accessExpression);
|
|
if (num > -1)
|
|
{
|
|
if (IsConditionalState)
|
|
{
|
|
LearnFromNonNullTest(num, ref StateWhenTrue);
|
|
LearnFromNonNullTest(num, ref StateWhenFalse);
|
|
}
|
|
else
|
|
{
|
|
LearnFromNonNullTest(num, ref State);
|
|
}
|
|
}
|
|
stateWhenNotNull = PossiblyConditionalState.Create(this);
|
|
Unsplit();
|
|
Join(ref State, ref state);
|
|
}
|
|
TypeWithAnnotations lvalueResultType = LvalueResultType;
|
|
TypeSymbol type = lvalueResultType.Type;
|
|
TypeSymbol type2 = node.Type;
|
|
TypeSymbol type3 = ((type2.IsVoidType() || type2.IsErrorType()) ? type2 : ((type2.IsNullableType() && !type.IsNullableType()) ? MakeNullableOf(lvalueResultType) : type));
|
|
SetResultType(node, TypeWithState.Create(type3, NullableFlowState.MaybeDefault));
|
|
_currentConditionalReceiverVisitResult = default(VisitResult);
|
|
_lastConditionalAccessSlot = lastConditionalAccessSlot;
|
|
void makeAndAdjustReceiverSlot(BoundExpression boundExpression)
|
|
{
|
|
int num2 = MakeSlot(boundExpression);
|
|
if (num2 > -1)
|
|
{
|
|
LearnFromNonNullTest(num2, ref State);
|
|
}
|
|
if (num2 > 0)
|
|
{
|
|
TypeSymbol? type4 = boundExpression.Type;
|
|
if ((object)type4 != null && type4.IsNullableType())
|
|
{
|
|
num2 = GetNullableOfTValueSlot(boundExpression.Type, num2, out Symbol _);
|
|
}
|
|
}
|
|
_lastConditionalAccessSlot = num2;
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitConditionalAccess(BoundConditionalAccess node)
|
|
{
|
|
VisitConditionalAccess(node, out var _);
|
|
return null;
|
|
}
|
|
|
|
protected override BoundNode? VisitConditionalOperatorCore(BoundExpression node, bool isRef, BoundExpression condition, BoundExpression originalConsequence, BoundExpression originalAlternative)
|
|
{
|
|
//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
|
|
VisitCondition(condition);
|
|
LocalState stateWhenTrue = StateWhenTrue;
|
|
LocalState stateWhenFalse = StateWhenFalse;
|
|
TypeWithState item2;
|
|
TypeWithState typeWithState;
|
|
if (isRef)
|
|
{
|
|
(TypeWithAnnotations LValueType, TypeWithState RValueType) tuple = visitConditionalRefOperand(stateWhenTrue, originalConsequence);
|
|
TypeWithAnnotations item = tuple.LValueType;
|
|
item2 = tuple.RValueType;
|
|
stateWhenTrue = State;
|
|
TypeWithAnnotations typeWithAnnotations;
|
|
(typeWithAnnotations, typeWithState) = visitConditionalRefOperand(stateWhenFalse, originalAlternative);
|
|
Join(ref State, ref stateWhenTrue);
|
|
TypeSymbol typeSymbol = node.Type?.SetUnknownNullabilityForReferenceTypes();
|
|
if (IsNullabilityMismatch(item, typeWithAnnotations))
|
|
{
|
|
ReportNullabilityMismatchInAssignment(node.Syntax, item, typeWithAnnotations);
|
|
}
|
|
else if (!node.HasErrors)
|
|
{
|
|
typeSymbol = item2.Type.MergeEquivalentTypes(typeWithState.Type, (VarianceKind)0);
|
|
}
|
|
NullableAnnotation nullableAnnotation = item.NullableAnnotation.EnsureCompatible(typeWithAnnotations.NullableAnnotation);
|
|
NullableFlowState defaultState = item2.State.Join(typeWithState.State);
|
|
SetResult(node, TypeWithState.Create(typeSymbol, defaultState), TypeWithAnnotations.Create(typeSymbol, nullableAnnotation));
|
|
return null;
|
|
}
|
|
(BoundExpression, Conversion, TypeWithState) tuple3 = visitConditionalOperand(stateWhenTrue, originalConsequence);
|
|
BoundExpression item3 = tuple3.Item1;
|
|
Conversion item4 = tuple3.Item2;
|
|
item2 = tuple3.Item3;
|
|
PossiblyConditionalState conditionalState = PossiblyConditionalState.Create(this);
|
|
stateWhenTrue = CloneAndUnsplit(ref conditionalState);
|
|
bool reachable = stateWhenTrue.Reachable;
|
|
(BoundExpression, Conversion, TypeWithState) tuple4 = visitConditionalOperand(stateWhenFalse, originalAlternative);
|
|
BoundExpression item5 = tuple4.Item1;
|
|
Conversion item6 = tuple4.Item2;
|
|
typeWithState = tuple4.Item3;
|
|
PossiblyConditionalState conditionalState2 = PossiblyConditionalState.Create(this);
|
|
stateWhenFalse = CloneAndUnsplit(ref conditionalState2);
|
|
bool reachable2 = stateWhenFalse.Reachable;
|
|
SetPossiblyConditionalState(in conditionalState);
|
|
Join(ref conditionalState2);
|
|
bool flag = node is BoundConditionalOperator boundConditionalOperator && boundConditionalOperator.WasTargetTyped;
|
|
TypeSymbol typeSymbol2;
|
|
if (node.HasErrors || flag)
|
|
{
|
|
typeSymbol2 = null;
|
|
}
|
|
else
|
|
{
|
|
BoundExpression expr = CreatePlaceholderIfNecessary(item3, item2.ToTypeWithAnnotations(compilation));
|
|
BoundExpression expr2 = CreatePlaceholderIfNecessary(item5, typeWithState.ToTypeWithAnnotations(compilation));
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
typeSymbol2 = BestTypeInferrer.InferBestTypeForConditionalOperator(expr, expr2, _conversions, out var _, ref useSiteInfo);
|
|
}
|
|
if ((object)typeSymbol2 == null)
|
|
{
|
|
typeSymbol2 = node.Type?.SetUnknownNullabilityForReferenceTypes();
|
|
}
|
|
TypeWithAnnotations resultTypeWithAnnotations;
|
|
if ((object)typeSymbol2 == null)
|
|
{
|
|
if (!flag)
|
|
{
|
|
SetResultType(node, TypeWithState.Create(typeSymbol2, NullableFlowState.NotNull));
|
|
return null;
|
|
}
|
|
resultTypeWithAnnotations = default(TypeWithAnnotations);
|
|
}
|
|
else
|
|
{
|
|
resultTypeWithAnnotations = TypeWithAnnotations.Create(typeSymbol2);
|
|
}
|
|
TypeWithState type = convertArms(node, originalConsequence, originalAlternative, stateWhenTrue, stateWhenFalse, item2, typeWithState, item3, item4, reachable, item5, item6, reachable2, resultTypeWithAnnotations, flag);
|
|
SetResultType(node, type, updateAnalyzedNullability: false);
|
|
return null;
|
|
void addConvertArmsAsCompletion(BoundExpression boundExpression, BoundExpression originalConsequence2, BoundExpression originalAlternative2, LocalState consequenceState, LocalState alternativeState, TypeWithState consequenceRValue, TypeWithState alternativeRValue, BoundExpression consequence, Conversion consequenceConversion, bool consequenceEndReachable, BoundExpression alternative, Conversion alternativeConversion, bool alternativeEndReachable)
|
|
{
|
|
((Dictionary<BoundExpression, Func<TypeWithAnnotations, TypeWithState>>)(object)TargetTypedAnalysisCompletion)[boundExpression] = (TypeWithAnnotations resultTypeWithAnnotations2) => convertArms(boundExpression, originalConsequence2, originalAlternative2, consequenceState, alternativeState, consequenceRValue, alternativeRValue, consequence, consequenceConversion, consequenceEndReachable, alternative, alternativeConversion, alternativeEndReachable, resultTypeWithAnnotations2, wasTargetTyped: false);
|
|
}
|
|
TypeWithState convertArms(BoundExpression boundExpression3, BoundExpression boundExpression, BoundExpression boundExpression2, LocalState consequenceState, LocalState alternativeState, TypeWithState consequenceRValue, TypeWithState alternativeRValue, BoundExpression consequence, Conversion consequenceConversion, bool consequenceEndReachable, BoundExpression alternative, Conversion alternativeConversion, bool alternativeEndReachable, TypeWithAnnotations targetType, bool wasTargetTyped)
|
|
{
|
|
NullableFlowState defaultState2;
|
|
if (!wasTargetTyped)
|
|
{
|
|
TypeWithState typeWithState2 = ConvertConditionalOperandOrSwitchExpressionArmResult(boundExpression, consequence, consequenceConversion, targetType, consequenceRValue, consequenceState, consequenceEndReachable);
|
|
TypeWithState typeWithState3 = ConvertConditionalOperandOrSwitchExpressionArmResult(boundExpression2, alternative, alternativeConversion, targetType, alternativeRValue, alternativeState, alternativeEndReachable);
|
|
defaultState2 = typeWithState2.State.Join(typeWithState3.State);
|
|
TypeWithState typeWithState4 = TypeWithState.Create(targetType.Type, defaultState2);
|
|
SetAnalyzedNullability(boundExpression3, typeWithState4);
|
|
return typeWithState4;
|
|
}
|
|
addConvertArmsAsCompletion(boundExpression3, boundExpression, boundExpression2, consequenceState, alternativeState, consequenceRValue, alternativeRValue, consequence, consequenceConversion, consequenceEndReachable, alternative, alternativeConversion, alternativeEndReachable);
|
|
defaultState2 = consequenceRValue.State.Join(alternativeRValue.State);
|
|
return TypeWithState.Create(targetType.Type, defaultState2);
|
|
}
|
|
(BoundExpression, Conversion, TypeWithState) visitConditionalOperand(LocalState state, BoundExpression operand)
|
|
{
|
|
SetState(state);
|
|
var (boundExpression, item7) = RemoveConversion(operand, includeExplicitConversions: false);
|
|
SnapshotWalkerThroughConversionGroup(operand, boundExpression);
|
|
Visit(boundExpression);
|
|
return (boundExpression, item7, ResultType);
|
|
}
|
|
(TypeWithAnnotations LValueType, TypeWithState RValueType) visitConditionalRefOperand(LocalState state, BoundExpression operand)
|
|
{
|
|
SetState(state);
|
|
return (LValueType: VisitLvalueWithAnnotations(operand), RValueType: ResultType);
|
|
}
|
|
}
|
|
|
|
private TypeWithState ConvertConditionalOperandOrSwitchExpressionArmResult(BoundExpression node, BoundExpression operand, Conversion conversion, TypeWithAnnotations targetType, TypeWithState operandType, LocalState state, bool isReachable)
|
|
{
|
|
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
|
|
PossiblyConditionalState conditionalState = PossiblyConditionalState.Create(this);
|
|
SetState(state);
|
|
bool disableDiagnostics = _disableDiagnostics;
|
|
if (!isReachable)
|
|
{
|
|
_disableDiagnostics = true;
|
|
}
|
|
TypeWithState result = VisitConversion(GetConversionIfApplicable(node, operand), operand, conversion, targetType, operandType, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, null, reportTopLevelWarnings: false);
|
|
if (!isReachable)
|
|
{
|
|
result = default(TypeWithState);
|
|
_disableDiagnostics = disableDiagnostics;
|
|
}
|
|
SetPossiblyConditionalState(in conditionalState);
|
|
return result;
|
|
}
|
|
|
|
private bool IsReachable()
|
|
{
|
|
if (!IsConditionalState)
|
|
{
|
|
return State.Reachable;
|
|
}
|
|
if (!StateWhenTrue.Reachable)
|
|
{
|
|
return StateWhenFalse.Reachable;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static BoundExpression CreatePlaceholderIfNecessary(BoundExpression expr, TypeWithAnnotations type)
|
|
{
|
|
if (type.HasType)
|
|
{
|
|
return new BoundExpressionWithNullability(expr.Syntax, expr, type.NullableAnnotation, type.Type);
|
|
}
|
|
return expr;
|
|
}
|
|
|
|
public override BoundNode? VisitConditionalReceiver(BoundConditionalReceiver node)
|
|
{
|
|
TypeSymbol typeSymbol = _currentConditionalReceiverVisitResult.RValueType.Type;
|
|
if ((object)typeSymbol != null && typeSymbol.IsNullableType())
|
|
{
|
|
typeSymbol = typeSymbol.GetNullableUnderlyingType();
|
|
}
|
|
SetResultType(node, TypeWithState.Create(typeSymbol, NullableFlowState.NotNull));
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitCall(BoundCall node)
|
|
{
|
|
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
|
|
if (tryGetReceiver(node, out var receiver))
|
|
{
|
|
ArrayBuilder<BoundCall> instance = ArrayBuilder<BoundCall>.GetInstance();
|
|
ArrayBuilderExtensions.Push<BoundCall>(instance, node);
|
|
node = receiver;
|
|
bool expressionIsRead = _expressionIsRead;
|
|
_expressionIsRead = true;
|
|
BoundCall receiver2;
|
|
while (tryGetReceiver(node, out receiver2))
|
|
{
|
|
TakeIncrementalSnapshot(node);
|
|
ArrayBuilderExtensions.Push<BoundCall>(instance, node);
|
|
node = receiver2;
|
|
}
|
|
TakeIncrementalSnapshot(node);
|
|
TypeWithState receiverType = visitAndCheckReceiver(node);
|
|
VisitArgumentResult? firstArgumentResult = null;
|
|
while (true)
|
|
{
|
|
ReinferMethodAndVisitArguments(node, receiverType, firstArgumentResult);
|
|
receiver = node;
|
|
if (!ArrayBuilderExtensions.TryPop<BoundCall>(instance, ref node))
|
|
{
|
|
break;
|
|
}
|
|
VisitExpressionWithoutStackGuardEpilogue(receiver);
|
|
if (node.ReceiverOpt != null)
|
|
{
|
|
VisitRvalueEpilogue(receiver);
|
|
receiverType = ResultType;
|
|
CheckCallReceiver(receiver, receiverType, node.Method);
|
|
firstArgumentResult = null;
|
|
}
|
|
else
|
|
{
|
|
RefKind refKind = AbstractFlowPass<LocalState, LocalFunctionState>.GetRefKind(node.ArgumentRefKindsOpt, 0);
|
|
FlowAnalysisAnnotations item = GetCorrespondingParameter(0, node.Method.Parameters, node.ArgsToParamsOpt, node.Expanded).Annotations;
|
|
firstArgumentResult = VisitArgumentEvaluateEpilogue(receiver, default(Optional<LocalState>), refKind, item);
|
|
receiverType = default(TypeWithState);
|
|
}
|
|
}
|
|
_expressionIsRead = expressionIsRead;
|
|
instance.Free();
|
|
}
|
|
else
|
|
{
|
|
TypeWithState receiverType2 = visitAndCheckReceiver(node);
|
|
ReinferMethodAndVisitArguments(node, receiverType2);
|
|
}
|
|
return null;
|
|
bool tryGetReceiver(BoundCall boundCall2, [MaybeNullWhen(false)] out BoundCall reference)
|
|
{
|
|
if (boundCall2.ReceiverOpt is BoundCall boundCall)
|
|
{
|
|
reference = boundCall;
|
|
return true;
|
|
}
|
|
if (boundCall2.InvokedAsExtensionMethod)
|
|
{
|
|
ImmutableArray<BoundExpression> arguments = boundCall2.Arguments;
|
|
if (arguments.Length >= 1 && arguments[0] is BoundCall boundCall3 && !VisitArgumentEvaluateNeedsCloningState(boundCall3))
|
|
{
|
|
reference = boundCall3;
|
|
return true;
|
|
}
|
|
}
|
|
reference = null;
|
|
return false;
|
|
}
|
|
TypeWithState visitAndCheckReceiver(BoundCall boundCall)
|
|
{
|
|
TypeWithState typeWithState = default(TypeWithState);
|
|
BoundExpression receiverOpt = boundCall.ReceiverOpt;
|
|
if (receiverOpt != null)
|
|
{
|
|
typeWithState = VisitRvalueWithState(receiverOpt);
|
|
CheckCallReceiver(receiverOpt, typeWithState, boundCall.Method);
|
|
}
|
|
return typeWithState;
|
|
}
|
|
}
|
|
|
|
private void ReinferMethodAndVisitArguments(BoundCall node, TypeWithState receiverType, VisitArgumentResult? firstArgumentResult = null)
|
|
{
|
|
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
|
|
MethodSymbol methodSymbol = node.Method;
|
|
ImmutableArray<RefKind> argumentRefKindsOpt = node.ArgumentRefKindsOpt;
|
|
if (!receiverType.HasNullType)
|
|
{
|
|
methodSymbol = (MethodSymbol)AsMemberOfType(receiverType.Type, methodSymbol);
|
|
}
|
|
ImmutableArray<VisitArgumentResult> results;
|
|
bool flag;
|
|
(methodSymbol, results, flag) = VisitArguments(node, node.Arguments, argumentRefKindsOpt, methodSymbol.Parameters, node.ArgsToParamsOpt, node.DefaultArguments, node.Expanded, node.InvokedAsExtensionMethod, methodSymbol, firstArgumentResult);
|
|
ApplyMemberPostConditions(node.ReceiverOpt, methodSymbol);
|
|
LearnFromEqualsMethod(methodSymbol, node, receiverType, results);
|
|
TypeWithState resultType = GetReturnTypeWithState(methodSymbol);
|
|
if (flag)
|
|
{
|
|
resultType = resultType.WithNotNullState();
|
|
}
|
|
SetResult(node, resultType, methodSymbol.ReturnTypeWithAnnotations);
|
|
SetUpdatedSymbol(node, node.Method, methodSymbol);
|
|
}
|
|
|
|
private void LearnFromEqualsMethod(MethodSymbol method, BoundCall node, TypeWithState receiverType, ImmutableArray<VisitArgumentResult> results)
|
|
{
|
|
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0030: Invalid comparison between Unknown and I4
|
|
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003e: Invalid comparison between Unknown and I4
|
|
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
|
|
int parameterCount = method.ParameterCount;
|
|
ImmutableArray<BoundExpression> arguments = node.Arguments;
|
|
if (node.HasErrors || (parameterCount != 1 && parameterCount != 2) || parameterCount != arguments.Length || (int)method.MethodKind != 10 || (int)method.ReturnType.SpecialType != 7 || (method.Name != SpecialMembers.GetDescriptor((SpecialMember)98).Name && method.Name != SpecialMembers.GetDescriptor((SpecialMember)101).Name && !anyOverriddenMethodHasExplicitImplementation(method)))
|
|
{
|
|
return;
|
|
}
|
|
if (method.Equals(compilation.GetSpecialTypeMember((SpecialMember)99)) || method.Equals(compilation.GetSpecialTypeMember((SpecialMember)101)) || isWellKnownEqualityMethodOrImplementation(compilation, method, receiverType.Type, (WellKnownMember)56))
|
|
{
|
|
learnFromEqualsMethodArguments(arguments[0], results[0].RValueType, arguments[1], results[1].RValueType);
|
|
return;
|
|
}
|
|
bool flag = method.GetLeastOverriddenMethod(null).Equals(compilation.GetSpecialTypeMember((SpecialMember)98));
|
|
BoundExpression receiverOpt = node.ReceiverOpt;
|
|
if (receiverOpt != null && (flag || isWellKnownEqualityMethodOrImplementation(compilation, method, receiverType.Type, (WellKnownMember)55)))
|
|
{
|
|
learnFromEqualsMethodArguments(receiverOpt, receiverType, arguments[0], results[0].RValueType);
|
|
}
|
|
static bool anyOverriddenMethodHasExplicitImplementation(MethodSymbol methodSymbol2)
|
|
{
|
|
MethodSymbol methodSymbol = methodSymbol2;
|
|
while ((object)methodSymbol != null)
|
|
{
|
|
if (methodSymbol.IsExplicitInterfaceImplementation)
|
|
{
|
|
return true;
|
|
}
|
|
methodSymbol = methodSymbol.OverriddenMethod;
|
|
}
|
|
return false;
|
|
}
|
|
static bool isWellKnownEqualityMethodOrImplementation(CSharpCompilation compilation, MethodSymbol overriddenMethod, TypeSymbol? typeSymbol, WellKnownMember wellKnownMember)
|
|
{
|
|
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
|
MethodSymbol methodSymbol = (MethodSymbol)compilation.GetWellKnownTypeMember(wellKnownMember);
|
|
if ((object)methodSymbol == null || (object)typeSymbol == null)
|
|
{
|
|
return false;
|
|
}
|
|
NamedTypeSymbol containingType = methodSymbol.ContainingType;
|
|
TypeWithAnnotations typeWithAnnotations = overriddenMethod.Parameters[0].TypeWithAnnotations;
|
|
NamedTypeSymbol newOwner = containingType.Construct(ImmutableArray.Create(typeWithAnnotations));
|
|
MethodSymbol methodSymbol2 = methodSymbol.AsMember(newOwner);
|
|
if (methodSymbol2.Equals(overriddenMethod))
|
|
{
|
|
return true;
|
|
}
|
|
TypeSymbol typeSymbol2 = typeSymbol;
|
|
while ((object)typeSymbol2 != null && (object)overriddenMethod != null)
|
|
{
|
|
Symbol symbol = typeSymbol2.FindImplementationForInterfaceMember(methodSymbol2);
|
|
if ((object)symbol == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (symbol.ContainingType.IsInterface)
|
|
{
|
|
return false;
|
|
}
|
|
MethodSymbol methodSymbol3 = overriddenMethod;
|
|
while ((object)methodSymbol3 != null)
|
|
{
|
|
if (methodSymbol3.Equals(symbol))
|
|
{
|
|
return true;
|
|
}
|
|
methodSymbol3 = methodSymbol3.OverriddenMethod;
|
|
}
|
|
while (!typeSymbol2.Equals(symbol.ContainingType) && (object)overriddenMethod != null)
|
|
{
|
|
if (typeSymbol2.Equals(overriddenMethod.ContainingType))
|
|
{
|
|
overriddenMethod = overriddenMethod.OverriddenMethod;
|
|
}
|
|
typeSymbol2 = typeSymbol2.BaseTypeNoUseSiteDiagnostics;
|
|
}
|
|
if ((object)overriddenMethod != null && typeSymbol2.Equals(overriddenMethod.ContainingType))
|
|
{
|
|
overriddenMethod = overriddenMethod.OverriddenMethod;
|
|
}
|
|
typeSymbol2 = typeSymbol2.BaseTypeNoUseSiteDiagnostics;
|
|
}
|
|
return false;
|
|
}
|
|
void learnFromEqualsMethodArguments(BoundExpression left, TypeWithState leftType, BoundExpression right, TypeWithState rightType)
|
|
{
|
|
ConstantValue? constantValueOpt = left.ConstantValueOpt;
|
|
if (constantValueOpt != null && constantValueOpt.IsNull)
|
|
{
|
|
Split();
|
|
LearnFromNullTest(right, ref StateWhenTrue);
|
|
LearnFromNonNullTest(right, ref StateWhenFalse);
|
|
}
|
|
else
|
|
{
|
|
ConstantValue? constantValueOpt2 = right.ConstantValueOpt;
|
|
if (constantValueOpt2 != null && constantValueOpt2.IsNull)
|
|
{
|
|
Split();
|
|
LearnFromNullTest(left, ref StateWhenTrue);
|
|
LearnFromNonNullTest(left, ref StateWhenFalse);
|
|
}
|
|
else if (leftType.MayBeNull && rightType.IsNotNull)
|
|
{
|
|
Split();
|
|
LearnFromNonNullTest(left, ref StateWhenTrue);
|
|
}
|
|
else if (rightType.MayBeNull && leftType.IsNotNull)
|
|
{
|
|
Split();
|
|
LearnFromNonNullTest(right, ref StateWhenTrue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool IsCompareExchangeMethod(MethodSymbol? method)
|
|
{
|
|
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
|
|
if ((object)method == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (!method.Equals(compilation.GetWellKnownTypeMember((WellKnownMember)140), SymbolEqualityComparer.ConsiderEverything.CompareKind))
|
|
{
|
|
return method.OriginalDefinition.Equals(compilation.GetWellKnownTypeMember((WellKnownMember)141), SymbolEqualityComparer.ConsiderEverything.CompareKind);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private NullableFlowState LearnFromCompareExchangeMethod(in CompareExchangeInfo compareExchangeInfo)
|
|
{
|
|
if (compareExchangeInfo.Arguments.Length != 3)
|
|
{
|
|
return NullableFlowState.NotNull;
|
|
}
|
|
ImmutableArray<int> argsToParamsOpt = compareExchangeInfo.ArgsToParamsOpt;
|
|
int index;
|
|
int index2;
|
|
int index3;
|
|
if (!argsToParamsOpt.IsDefault)
|
|
{
|
|
int num = argsToParamsOpt.IndexOf(2);
|
|
int num2 = argsToParamsOpt.IndexOf(1);
|
|
int num3 = argsToParamsOpt.IndexOf(0);
|
|
index = num3;
|
|
index2 = num2;
|
|
index3 = num;
|
|
}
|
|
else
|
|
{
|
|
index3 = 2;
|
|
index2 = 1;
|
|
index = 0;
|
|
}
|
|
BoundExpression boundExpression = compareExchangeInfo.Arguments[index3];
|
|
NullableFlowState nullableFlowState = compareExchangeInfo.Results[index2].RValueType.State;
|
|
ConstantValue? constantValueOpt = boundExpression.ConstantValueOpt;
|
|
if (constantValueOpt == null || !constantValueOpt.IsNull)
|
|
{
|
|
NullableFlowState state = compareExchangeInfo.Results[index].RValueType.State;
|
|
nullableFlowState = nullableFlowState.Join(state);
|
|
}
|
|
return nullableFlowState;
|
|
}
|
|
|
|
private void CheckCallReceiver(BoundExpression? receiverOpt, TypeWithState receiverType, MethodSymbol method)
|
|
{
|
|
bool checkNullableValueType = false;
|
|
TypeSymbol type = receiverType.Type;
|
|
if (method.RequiresInstanceReceiver && (object)type != null && type.IsNullableType() && method.ContainingType.IsReferenceType)
|
|
{
|
|
checkNullableValueType = true;
|
|
}
|
|
else if (method.OriginalDefinition == compilation.GetSpecialTypeMember((SpecialMember)115))
|
|
{
|
|
checkNullableValueType = true;
|
|
}
|
|
CheckPossibleNullReceiver(receiverOpt, receiverType, checkNullableValueType);
|
|
}
|
|
|
|
private TypeWithState GetReturnTypeWithState(MethodSymbol method)
|
|
{
|
|
return TypeWithState.Create(method.ReturnTypeWithAnnotations, GetRValueAnnotations(method));
|
|
}
|
|
|
|
private FlowAnalysisAnnotations GetRValueAnnotations(Symbol? symbol)
|
|
{
|
|
if (IsAnalyzingAttribute)
|
|
{
|
|
return FlowAnalysisAnnotations.None;
|
|
}
|
|
return symbol.GetFlowAnalysisAnnotations() & (FlowAnalysisAnnotations.MaybeNull | FlowAnalysisAnnotations.NotNull);
|
|
}
|
|
|
|
private FlowAnalysisAnnotations GetParameterAnnotations(ParameterSymbol parameter)
|
|
{
|
|
if (!IsAnalyzingAttribute)
|
|
{
|
|
return parameter.FlowAnalysisAnnotations;
|
|
}
|
|
return FlowAnalysisAnnotations.None;
|
|
}
|
|
|
|
private static TypeWithAnnotations ApplyLValueAnnotations(TypeWithAnnotations declaredType, FlowAnalysisAnnotations flowAnalysisAnnotations)
|
|
{
|
|
if ((flowAnalysisAnnotations & FlowAnalysisAnnotations.DisallowNull) == FlowAnalysisAnnotations.DisallowNull)
|
|
{
|
|
return declaredType.AsNotAnnotated();
|
|
}
|
|
if ((flowAnalysisAnnotations & FlowAnalysisAnnotations.AllowNull) == FlowAnalysisAnnotations.AllowNull)
|
|
{
|
|
return declaredType.AsAnnotated();
|
|
}
|
|
return declaredType;
|
|
}
|
|
|
|
private static TypeWithState ApplyUnconditionalAnnotations(TypeWithState typeWithState, FlowAnalysisAnnotations annotations)
|
|
{
|
|
if ((annotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull)
|
|
{
|
|
return TypeWithState.Create(typeWithState.Type, NullableFlowState.NotNull);
|
|
}
|
|
if ((annotations & FlowAnalysisAnnotations.MaybeNull) == FlowAnalysisAnnotations.MaybeNull)
|
|
{
|
|
return TypeWithState.Create(typeWithState.Type, NullableFlowState.MaybeDefault);
|
|
}
|
|
return typeWithState;
|
|
}
|
|
|
|
private static TypeWithAnnotations ApplyUnconditionalAnnotations(TypeWithAnnotations declaredType, FlowAnalysisAnnotations annotations)
|
|
{
|
|
if ((annotations & FlowAnalysisAnnotations.MaybeNull) == FlowAnalysisAnnotations.MaybeNull)
|
|
{
|
|
return declaredType.AsAnnotated();
|
|
}
|
|
if ((annotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull)
|
|
{
|
|
return declaredType.AsNotAnnotated();
|
|
}
|
|
return declaredType;
|
|
}
|
|
|
|
private static bool HasImplicitTypeArguments(BoundNode node)
|
|
{
|
|
if (node is BoundCollectionElementInitializer boundCollectionElementInitializer)
|
|
{
|
|
MethodSymbol addMethod = boundCollectionElementInitializer.AddMethod;
|
|
if ((object)addMethod != null && !addMethod.TypeArgumentsWithAnnotations.IsEmpty)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
if (node is BoundForEachStatement boundForEachStatement)
|
|
{
|
|
ForEachEnumeratorInfo enumeratorInfoOpt = boundForEachStatement.EnumeratorInfoOpt;
|
|
if (enumeratorInfoOpt != null)
|
|
{
|
|
MethodArgumentInfo getEnumeratorInfo = enumeratorInfoOpt.GetEnumeratorInfo;
|
|
if ((object)getEnumeratorInfo != null)
|
|
{
|
|
MethodSymbol addMethod = getEnumeratorInfo.Method;
|
|
if ((object)addMethod != null && !addMethod.TypeArgumentsWithAnnotations.IsEmpty)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SyntaxNode syntax = node.Syntax;
|
|
if (syntax.Kind() != SyntaxKind.InvocationExpression)
|
|
{
|
|
return false;
|
|
}
|
|
return HasImplicitTypeArguments((SyntaxNode)(object)((InvocationExpressionSyntax)(object)syntax).Expression);
|
|
}
|
|
|
|
private static bool HasImplicitTypeArguments(SyntaxNode syntax)
|
|
{
|
|
NameSyntax nameSyntax = Binder.GetNameSyntax(syntax, out var _);
|
|
if (nameSyntax == null)
|
|
{
|
|
return false;
|
|
}
|
|
nameSyntax = nameSyntax.GetUnqualifiedName();
|
|
return nameSyntax.Kind() != SyntaxKind.GenericName;
|
|
}
|
|
|
|
protected override void VisitArguments(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, MethodSymbol method)
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs", 6316);
|
|
}
|
|
|
|
private (MethodSymbol? method, ImmutableArray<VisitArgumentResult> results, bool returnNotNull) VisitArguments(BoundExpression node, ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, MethodSymbol? method, ImmutableArray<int> argsToParamsOpt, BitVector defaultArguments, bool expanded, bool invokedAsExtensionMethod)
|
|
{
|
|
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
|
|
return VisitArguments(node, arguments, refKindsOpt, method?.Parameters ?? default(ImmutableArray<ParameterSymbol>), argsToParamsOpt, defaultArguments, expanded, invokedAsExtensionMethod, method);
|
|
}
|
|
|
|
private ImmutableArray<VisitArgumentResult> VisitArguments(BoundExpression node, ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, PropertySymbol? property, ImmutableArray<int> argsToParamsOpt, BitVector defaultArguments, bool expanded)
|
|
{
|
|
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
|
|
return VisitArguments(node, arguments, refKindsOpt, property?.Parameters ?? default(ImmutableArray<ParameterSymbol>), argsToParamsOpt, defaultArguments, expanded, invokedAsExtensionMethod: false).results;
|
|
}
|
|
|
|
private (MethodSymbol? method, ImmutableArray<VisitArgumentResult> results, bool returnNotNull) VisitArguments(BoundNode node, ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, ImmutableArray<ParameterSymbol> parametersOpt, ImmutableArray<int> argsToParamsOpt, BitVector defaultArguments, bool expanded, bool invokedAsExtensionMethod, MethodSymbol? method = null, VisitArgumentResult? firstArgumentResult = null)
|
|
{
|
|
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
|
|
(MethodSymbol, ImmutableArray<VisitArgumentResult>, bool, ArgumentsCompletionDelegate) tuple = VisitArguments(node, arguments, refKindsOpt, parametersOpt, argsToParamsOpt, defaultArguments, expanded, invokedAsExtensionMethod, method, delayCompletionForTargetMember: false, firstArgumentResult);
|
|
return (method: tuple.Item1, results: tuple.Item2, returnNotNull: tuple.Item3);
|
|
}
|
|
|
|
private (MethodSymbol? method, ImmutableArray<VisitArgumentResult> results, bool returnNotNull, ArgumentsCompletionDelegate? completion) VisitArguments(BoundNode node, ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, ImmutableArray<ParameterSymbol> parametersOpt, ImmutableArray<int> argsToParamsOpt, BitVector defaultArguments, bool expanded, bool invokedAsExtensionMethod, MethodSymbol? method, bool delayCompletionForTargetMember, VisitArgumentResult? firstArgumentResult = null)
|
|
{
|
|
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
|
|
(ImmutableArray<BoundExpression> arguments, ImmutableArray<Conversion> conversions) tuple = RemoveArgumentConversions(arguments, refKindsOpt);
|
|
ImmutableArray<BoundExpression> item = tuple.arguments;
|
|
ImmutableArray<Conversion> item2 = tuple.conversions;
|
|
ImmutableArray<VisitArgumentResult> results = VisitArgumentsEvaluate(item, refKindsOpt, GetParametersAnnotations(arguments, parametersOpt, argsToParamsOpt, expanded), defaultArguments, firstArgumentResult);
|
|
return visitArguments(node, arguments, item, item2, results, refKindsOpt, parametersOpt, argsToParamsOpt, defaultArguments, expanded, invokedAsExtensionMethod, method, delayCompletionForTargetMember);
|
|
(MethodSymbol? method, ImmutableArray<VisitArgumentResult> results, bool returnNotNull, ArgumentsCompletionDelegate? completion) visitArguments(BoundNode boundNode, ImmutableArray<BoundExpression> arguments2, ImmutableArray<BoundExpression> argumentsNoConversions, ImmutableArray<Conversion> conversions, ImmutableArray<VisitArgumentResult> immutableArray, ImmutableArray<RefKind> immutableArray2, ImmutableArray<ParameterSymbol> parameters, ImmutableArray<int> argsToParamsOpt2, BitVector defaultArguments2, bool expanded2, bool flag2, MethodSymbol? methodSymbol, bool flag)
|
|
{
|
|
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0323: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
|
|
bool item3 = false;
|
|
if (flag)
|
|
{
|
|
return (method: methodSymbol, results: immutableArray, returnNotNull: item3, completion: visitArgumentsAsContinuation(boundNode, arguments2, argumentsNoConversions, conversions, immutableArray2, argsToParamsOpt2, defaultArguments2, expanded2, flag2));
|
|
}
|
|
if ((object)methodSymbol != null && methodSymbol.IsGenericMethod)
|
|
{
|
|
if (HasImplicitTypeArguments(boundNode))
|
|
{
|
|
methodSymbol = InferMethodTypeArguments(methodSymbol, GetArgumentsForMethodTypeInference(immutableArray, argumentsNoConversions), immutableArray2, argsToParamsOpt2, expanded2);
|
|
parameters = methodSymbol.Parameters;
|
|
}
|
|
if (ConstraintsHelper.RequiresChecking(methodSymbol))
|
|
{
|
|
SyntaxNode syntax = boundNode.Syntax;
|
|
SyntaxNode syntax2;
|
|
if (syntax is InvocationExpressionSyntax invocationExpressionSyntax)
|
|
{
|
|
ExpressionSyntax expression = invocationExpressionSyntax.Expression;
|
|
syntax2 = (SyntaxNode)(object)expression;
|
|
}
|
|
else if (syntax is ForEachStatementSyntax forEachStatementSyntax)
|
|
{
|
|
ExpressionSyntax expression2 = forEachStatementSyntax.Expression;
|
|
syntax2 = (SyntaxNode)(object)expression2;
|
|
}
|
|
else
|
|
{
|
|
syntax2 = syntax;
|
|
}
|
|
CheckMethodConstraints(syntax2, methodSymbol);
|
|
}
|
|
}
|
|
ArrayBuilder<ParameterSymbol> val = ((!IsAnalyzingAttribute && !parameters.IsDefault && parameters.Any((ParameterSymbol p) => !p.NotNullIfParameterNotNull.IsEmpty)) ? ArrayBuilder<ParameterSymbol>.GetInstance() : null);
|
|
ArrayBuilder<VisitResult> instance = ArrayBuilder<VisitResult>.GetInstance(immutableArray.Length);
|
|
if (!parameters.IsDefault)
|
|
{
|
|
ImmutableHashSet<string> immutableHashSet = (IsAnalyzingAttribute ? null : methodSymbol?.ReturnNotNullIfParameterNotNull);
|
|
for (int num = 0; num < immutableArray.Length; num++)
|
|
{
|
|
BoundExpression boundExpression = argumentsNoConversions[num];
|
|
BoundExpression conversionOpt = ((num < arguments2.Length) ? arguments2[num] : boundExpression);
|
|
var (parameterSymbol, parameterType, parameterAnnotations, flag3) = GetCorrespondingParameter(num, parameters, argsToParamsOpt2, expanded2);
|
|
if ((object)parameterSymbol != null)
|
|
{
|
|
bool disableDiagnostics = _disableDiagnostics;
|
|
_disableDiagnostics |= boundNode.HasErrors || ((BitVector)(ref defaultArguments2))[num];
|
|
VisitArgumentConversionAndInboundAssignmentsAndPreConditions(GetConversionIfApplicable(conversionOpt, boundExpression), boundExpression, (conversions.IsDefault || num >= conversions.Length) ? Conversion.Identity : conversions[num], AbstractFlowPass<LocalState, LocalFunctionState>.GetRefKind(immutableArray2, num), parameterSymbol, parameterType, parameterAnnotations, immutableArray[num], instance, flag2 && num == 0);
|
|
_disableDiagnostics = disableDiagnostics;
|
|
if (immutableArray[num].RValueType.IsNotNull || flag3)
|
|
{
|
|
val?.Add(parameterSymbol);
|
|
if (immutableHashSet != null && immutableHashSet.Contains(parameterSymbol.Name))
|
|
{
|
|
item3 = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
instance.Free();
|
|
if (boundNode is BoundCall boundCall)
|
|
{
|
|
MethodSymbol method2 = boundCall.Method;
|
|
if ((object)method2 != null && method2.OriginalDefinition is LocalFunctionSymbol symbol)
|
|
{
|
|
VisitLocalFunctionUse(symbol);
|
|
}
|
|
}
|
|
if (!boundNode.HasErrors && !parameters.IsDefault)
|
|
{
|
|
CompareExchangeInfo compareExchangeInfo = (IsCompareExchangeMethod(methodSymbol) ? new CompareExchangeInfo(arguments2, immutableArray, argsToParamsOpt2) : default(CompareExchangeInfo));
|
|
for (int num2 = 0; num2 < arguments2.Length; num2++)
|
|
{
|
|
var (parameterSymbol2, parameterType2, parameterAnnotations2, _) = GetCorrespondingParameter(num2, parameters, argsToParamsOpt2, expanded2);
|
|
if ((object)parameterSymbol2 != null)
|
|
{
|
|
VisitArgumentOutboundAssignmentsAndPostConditions(arguments2[num2], AbstractFlowPass<LocalState, LocalFunctionState>.GetRefKind(immutableArray2, num2), parameterSymbol2, parameterType2, parameterAnnotations2, immutableArray[num2], val, (!compareExchangeInfo.IsDefault && parameterSymbol2.Ordinal == 0) ? compareExchangeInfo : default(CompareExchangeInfo));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int num3 = 0; num3 < arguments2.Length; num3++)
|
|
{
|
|
BoundExpression boundExpression2 = arguments2[num3];
|
|
VisitArgumentResult visitArgumentResult = immutableArray[num3];
|
|
BoundExpression convertedNode = argumentsNoConversions[num3];
|
|
TrackAnalyzedNullabilityThroughConversionGroup(TypeWithState.Create(boundExpression2.Type, visitArgumentResult.RValueType.State), boundExpression2 as BoundConversion, convertedNode);
|
|
}
|
|
}
|
|
if (!IsAnalyzingAttribute && (object)methodSymbol != null && (methodSymbol.FlowAnalysisAnnotations & FlowAnalysisAnnotations.DoesNotReturn) == FlowAnalysisAnnotations.DoesNotReturn)
|
|
{
|
|
SetUnreachable();
|
|
}
|
|
val?.Free();
|
|
return (method: methodSymbol, results: immutableArray, returnNotNull: item3, completion: null);
|
|
}
|
|
ArgumentsCompletionDelegate visitArgumentsAsContinuation(BoundNode node2, ImmutableArray<BoundExpression> arguments2, ImmutableArray<BoundExpression> argumentsNoConversions, ImmutableArray<Conversion> conversions, ImmutableArray<RefKind> refKindsOpt2, ImmutableArray<int> argsToParamsOpt2, BitVector defaultArguments2, bool expanded2, bool invokedAsExtensionMethod2)
|
|
{
|
|
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
|
|
return delegate(ImmutableArray<VisitArgumentResult> results2, ImmutableArray<ParameterSymbol> parametersOpt2, MethodSymbol? method2)
|
|
{
|
|
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
|
|
(MethodSymbol, ImmutableArray<VisitArgumentResult>, bool, ArgumentsCompletionDelegate) tuple2 = visitArguments(node2, arguments2, argumentsNoConversions, conversions, results2, refKindsOpt2, parametersOpt2, argsToParamsOpt2, defaultArguments2, expanded2, invokedAsExtensionMethod2, method2, delayCompletionForTargetMember: false);
|
|
return (method: tuple2.Item1, returnNotNull: tuple2.Item3);
|
|
};
|
|
}
|
|
}
|
|
|
|
private void ApplyMemberPostConditions(BoundExpression? receiverOpt, MethodSymbol? method)
|
|
{
|
|
if ((object)method != null)
|
|
{
|
|
int num = ((!method.IsStatic) ? ((receiverOpt == null) ? (-1) : MakeSlot(receiverOpt)) : 0);
|
|
if (num >= 0)
|
|
{
|
|
ApplyMemberPostConditions(num, method);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ApplyMemberPostConditions(int receiverSlot, MethodSymbol method)
|
|
{
|
|
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_005f: Invalid comparison between Unknown and I4
|
|
do
|
|
{
|
|
NamedTypeSymbol containingType = method.ContainingType;
|
|
ImmutableArray<string> notNullMembers = method.NotNullMembers;
|
|
ImmutableArray<string> notNullWhenTrueMembers = method.NotNullWhenTrueMembers;
|
|
ImmutableArray<string> notNullWhenFalseMembers = method.NotNullWhenFalseMembers;
|
|
if (IsConditionalState)
|
|
{
|
|
applyMemberPostConditions(receiverSlot, containingType, notNullMembers, ref StateWhenTrue);
|
|
applyMemberPostConditions(receiverSlot, containingType, notNullMembers, ref StateWhenFalse);
|
|
}
|
|
else
|
|
{
|
|
applyMemberPostConditions(receiverSlot, containingType, notNullMembers, ref State);
|
|
}
|
|
if ((int)method.ReturnType.SpecialType == 7 && (!notNullWhenTrueMembers.IsEmpty || !notNullWhenFalseMembers.IsEmpty))
|
|
{
|
|
Split();
|
|
applyMemberPostConditions(receiverSlot, containingType, notNullWhenTrueMembers, ref StateWhenTrue);
|
|
applyMemberPostConditions(receiverSlot, containingType, notNullWhenFalseMembers, ref StateWhenFalse);
|
|
}
|
|
method = method.OverriddenMethod;
|
|
}
|
|
while (method != null);
|
|
void applyMemberPostConditions(int receiverSlot2, TypeSymbol type, ImmutableArray<string> members, ref LocalState state)
|
|
{
|
|
if (!members.IsEmpty)
|
|
{
|
|
ImmutableArray<string>.Enumerator enumerator = members.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
string current = enumerator.Current;
|
|
markMembersAsNotNull(receiverSlot2, type, current, ref state);
|
|
}
|
|
}
|
|
}
|
|
void markMembersAsNotNull(int containingSlot, TypeSymbol type, string memberName, ref LocalState state)
|
|
{
|
|
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_004a: Expected I4, but got Unknown
|
|
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_004e: Invalid comparison between Unknown and I4
|
|
ImmutableArray<Symbol>.Enumerator enumerator = type.GetMembers(memberName).GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
Symbol current = enumerator.Current;
|
|
if (current.IsStatic)
|
|
{
|
|
containingSlot = 0;
|
|
}
|
|
SymbolKind kind = current.Kind;
|
|
switch (kind - 5)
|
|
{
|
|
default:
|
|
if ((int)kind != 15)
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
case 1:
|
|
break;
|
|
case 0:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
continue;
|
|
}
|
|
int orCreateSlot = GetOrCreateSlot(current, containingSlot);
|
|
if (orCreateSlot > 0)
|
|
{
|
|
SetState(ref state, orCreateSlot, NullableFlowState.NotNull);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private ImmutableArray<VisitArgumentResult> VisitArgumentsEvaluate(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, ImmutableArray<FlowAnalysisAnnotations> parameterAnnotationsOpt, BitVector defaultArguments, VisitArgumentResult? firstArgumentResult = null)
|
|
{
|
|
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
|
|
int length = arguments.Length;
|
|
if (length == 0 && parameterAnnotationsOpt.IsDefaultOrEmpty)
|
|
{
|
|
return ImmutableArray<VisitArgumentResult>.Empty;
|
|
}
|
|
ArrayBuilder<VisitArgumentResult> instance = ArrayBuilder<VisitArgumentResult>.GetInstance(length);
|
|
bool disableDiagnostics = _disableDiagnostics;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
_disableDiagnostics = ((BitVector)(ref defaultArguments))[i] || disableDiagnostics;
|
|
if (i == 0 && firstArgumentResult.HasValue)
|
|
{
|
|
VisitArgumentResult valueOrDefault = firstArgumentResult.GetValueOrDefault();
|
|
instance.Add(valueOrDefault);
|
|
}
|
|
else
|
|
{
|
|
instance.Add(VisitArgumentEvaluate(arguments[i], AbstractFlowPass<LocalState, LocalFunctionState>.GetRefKind(refKindsOpt, i), (!parameterAnnotationsOpt.IsDefault) ? parameterAnnotationsOpt[i] : FlowAnalysisAnnotations.None));
|
|
}
|
|
}
|
|
_disableDiagnostics = disableDiagnostics;
|
|
SetInvalidResult();
|
|
return instance.ToImmutableAndFree();
|
|
}
|
|
|
|
private ImmutableArray<FlowAnalysisAnnotations> GetParametersAnnotations(ImmutableArray<BoundExpression> arguments, ImmutableArray<ParameterSymbol> parametersOpt, ImmutableArray<int> argsToParamsOpt, bool expanded)
|
|
{
|
|
ImmutableArray<FlowAnalysisAnnotations> result = default(ImmutableArray<FlowAnalysisAnnotations>);
|
|
if (!parametersOpt.IsDefault)
|
|
{
|
|
return ImmutableArrayExtensions.SelectAsArray<BoundExpression, (NullableWalker, ImmutableArray<ParameterSymbol>, ImmutableArray<int>, bool), FlowAnalysisAnnotations>(arguments, (Func<BoundExpression, int, (NullableWalker, ImmutableArray<ParameterSymbol>, ImmutableArray<int>, bool), FlowAnalysisAnnotations>)((BoundExpression argument, int i, (NullableWalker self, ImmutableArray<ParameterSymbol> parametersOpt, ImmutableArray<int> argsToParamsOpt, bool expanded) arg) => arg.self.GetCorrespondingParameter(i, arg.parametersOpt, arg.argsToParamsOpt, arg.expanded).Annotations), (this, parametersOpt, argsToParamsOpt, expanded));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private VisitArgumentResult VisitArgumentEvaluate(BoundExpression argument, RefKind refKind, FlowAnalysisAnnotations annotations)
|
|
{
|
|
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
|
|
Optional<LocalState> savedState = (VisitArgumentEvaluateNeedsCloningState(argument) ? Optional<LocalState>.op_Implicit(State.Clone()) : default(Optional<LocalState>));
|
|
Visit(argument);
|
|
return VisitArgumentEvaluateEpilogue(argument, savedState, refKind, annotations);
|
|
}
|
|
|
|
private bool VisitArgumentEvaluateNeedsCloningState(BoundExpression argument)
|
|
{
|
|
return argument.Kind == BoundKind.Lambda;
|
|
}
|
|
|
|
private VisitArgumentResult VisitArgumentEvaluateEpilogue(BoundExpression argument, Optional<LocalState> savedState, RefKind refKind, FlowAnalysisAnnotations annotations)
|
|
{
|
|
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0016: Expected I4, but got Unknown
|
|
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
|
|
switch ((int)refKind)
|
|
{
|
|
case 1:
|
|
Unsplit();
|
|
break;
|
|
case 0:
|
|
case 3:
|
|
switch (annotations & FlowAnalysisAnnotations.DoesNotReturn)
|
|
{
|
|
case FlowAnalysisAnnotations.DoesNotReturnIfTrue:
|
|
if (IsConditionalState)
|
|
{
|
|
SetState(StateWhenFalse);
|
|
}
|
|
break;
|
|
case FlowAnalysisAnnotations.DoesNotReturnIfFalse:
|
|
if (IsConditionalState)
|
|
{
|
|
SetState(StateWhenTrue);
|
|
}
|
|
break;
|
|
default:
|
|
VisitRvalueEpilogue(argument);
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
Unsplit();
|
|
UseLvalueOnly(argument);
|
|
break;
|
|
default:
|
|
throw ExceptionUtilities.UnexpectedValue((object)refKind);
|
|
}
|
|
return new VisitArgumentResult(_visitResult, savedState);
|
|
}
|
|
|
|
private void VisitArgumentConversionAndInboundAssignmentsAndPreConditions(BoundConversion? conversionOpt, BoundExpression argumentNoConversion, Conversion conversion, RefKind refKind, ParameterSymbol parameter, TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations, VisitArgumentResult result, ArrayBuilder<VisitResult>? conversionResultsBuilder, bool extensionMethodThisArgument)
|
|
{
|
|
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_001f: Expected I4, but got Unknown
|
|
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0173: Unknown result type (might be due to invalid IL or missing references)
|
|
TypeWithState rValueType = result.RValueType;
|
|
switch ((int)refKind)
|
|
{
|
|
case 0:
|
|
case 3:
|
|
{
|
|
if (conversion.Kind == ConversionKind.ImplicitUserDefined)
|
|
{
|
|
TypeSymbol type = rValueType.Type;
|
|
conversion = GenerateConversion(_conversions, argumentNoConversion, type, parameterType.Type, fromExplicitCast: false, extensionMethodThisArgument: false, conversionOpt?.Checked ?? false);
|
|
if (!conversion.Exists && !argumentNoConversion.IsSuppressed)
|
|
{
|
|
ReportNullabilityMismatchInArgument(argumentNoConversion.Syntax, type, parameter, parameterType.Type, forOutput: false);
|
|
}
|
|
}
|
|
TypeWithState typeWithState = VisitConversion(conversionOpt, argumentNoConversion, conversion, ApplyLValueAnnotations(parameterType, parameterAnnotations), rValueType, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Argument, parameter, reportTopLevelWarnings: true, reportRemainingWarnings: true, extensionMethodThisArgument, result.StateForLambda, trackMembers: false, null, conversionResultsBuilder);
|
|
if (CheckDisallowedNullAssignment(typeWithState, parameterAnnotations, argumentNoConversion.Syntax))
|
|
{
|
|
LearnFromNonNullTest(argumentNoConversion, ref State);
|
|
}
|
|
SetResultType(argumentNoConversion, typeWithState, updateAnalyzedNullability: false);
|
|
conversionResultsBuilder?.Add(_visitResult);
|
|
break;
|
|
}
|
|
case 1:
|
|
if (!argumentNoConversion.IsSuppressed)
|
|
{
|
|
TypeWithAnnotations lValueType = result.LValueType;
|
|
if (IsNullabilityMismatch(lValueType.Type, parameterType.Type))
|
|
{
|
|
ReportNullabilityMismatchInRefArgument(argumentNoConversion, lValueType.Type, parameter, parameterType.Type);
|
|
}
|
|
else
|
|
{
|
|
ReportNullableAssignmentIfNecessary(argumentNoConversion, ApplyLValueAnnotations(parameterType, parameterAnnotations), rValueType, useLegacyWarnings: false);
|
|
CheckDisallowedNullAssignment(rValueType, parameterAnnotations, argumentNoConversion.Syntax);
|
|
}
|
|
}
|
|
conversionResultsBuilder?.Add(result.VisitResult);
|
|
break;
|
|
case 2:
|
|
conversionResultsBuilder?.Add(result.VisitResult);
|
|
break;
|
|
default:
|
|
throw ExceptionUtilities.UnexpectedValue((object)refKind);
|
|
}
|
|
}
|
|
|
|
private bool CheckDisallowedNullAssignment(TypeWithState state, FlowAnalysisAnnotations annotations, SyntaxNode node, BoundExpression? boundValueOpt = null)
|
|
{
|
|
if (boundValueOpt != null && boundValueOpt.WasCompilerGenerated)
|
|
{
|
|
return false;
|
|
}
|
|
if (IsDisallowedNullAssignment(state, annotations))
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, node.Location);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool IsDisallowedNullAssignment(TypeWithState valueState, FlowAnalysisAnnotations targetAnnotations)
|
|
{
|
|
if ((targetAnnotations & FlowAnalysisAnnotations.DisallowNull) != FlowAnalysisAnnotations.None && hasNoNonNullableCounterpart(valueState.Type))
|
|
{
|
|
return valueState.MayBeNull;
|
|
}
|
|
return false;
|
|
static bool hasNoNonNullableCounterpart(TypeSymbol? type)
|
|
{
|
|
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_000d: Invalid comparison between Unknown and I4
|
|
if ((object)type == null)
|
|
{
|
|
return false;
|
|
}
|
|
if ((int)type.Kind != 17 || type.IsReferenceType)
|
|
{
|
|
return type.IsNullableTypeOrTypeParameter();
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private void VisitArgumentOutboundAssignmentsAndPostConditions(BoundExpression argument, RefKind refKind, ParameterSymbol parameter, TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations, VisitArgumentResult result, ArrayBuilder<ParameterSymbol>? notNullParametersOpt, CompareExchangeInfo compareExchangeInfoOpt)
|
|
{
|
|
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0016: Expected I4, but got Unknown
|
|
//IL_0213: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
|
|
switch ((int)refKind)
|
|
{
|
|
case 0:
|
|
case 3:
|
|
LearnFromPostConditions(argument, parameterAnnotations);
|
|
break;
|
|
case 1:
|
|
{
|
|
parameterAnnotations = notNullBasedOnParameters(parameterAnnotations, notNullParametersOpt, parameter);
|
|
TypeWithState typeWithState = TypeWithState.Create(parameterType, parameterAnnotations);
|
|
if (!compareExchangeInfoOpt.IsDefault)
|
|
{
|
|
NullableFlowState defaultState = LearnFromCompareExchangeMethod(in compareExchangeInfoOpt);
|
|
typeWithState = TypeWithState.Create(parameterType.Type, defaultState);
|
|
}
|
|
BoundParameter boundParameter2 = new BoundParameter(argument.Syntax, parameter);
|
|
TypeWithAnnotations lValueType2 = result.LValueType;
|
|
trackNullableStateForAssignment(boundParameter2, lValueType2, MakeSlot(argument), typeWithState, argument.IsSuppressed, parameterAnnotations);
|
|
if (!argument.IsSuppressed)
|
|
{
|
|
FlowAnalysisAnnotations lValueAnnotations2 = GetLValueAnnotations(argument);
|
|
ReportNullableAssignmentIfNecessary(boundParameter2, ApplyLValueAnnotations(lValueType2, lValueAnnotations2), applyPostConditionsUnconditionally(typeWithState, parameterAnnotations), UseLegacyWarnings(argument));
|
|
}
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
parameterAnnotations = notNullBasedOnParameters(parameterAnnotations, notNullParametersOpt, parameter);
|
|
TypeWithState rightState = TypeWithState.Create(parameterType, parameterAnnotations);
|
|
TypeWithState valueType = applyPostConditionsUnconditionally(rightState, parameterAnnotations);
|
|
TypeWithAnnotations lValueType = result.LValueType;
|
|
FlowAnalysisAnnotations lValueAnnotations = GetLValueAnnotations(argument);
|
|
TypeWithAnnotations typeWithAnnotations = ApplyLValueAnnotations(lValueType, lValueAnnotations);
|
|
if (argument is BoundLocal { DeclarationKind: BoundLocalDeclarationKind.WithInferredType } boundLocal)
|
|
{
|
|
TypeWithAnnotations typeWithAnnotations2 = valueType.ToAnnotatedTypeWithAnnotations(compilation);
|
|
_variables.SetType(boundLocal.LocalSymbol, typeWithAnnotations2);
|
|
typeWithAnnotations = typeWithAnnotations2;
|
|
}
|
|
else if (argument is BoundDiscardExpression { IsInferred: not false } boundDiscardExpression)
|
|
{
|
|
SetAnalyzedNullability(boundDiscardExpression, new VisitResult(rightState, rightState.ToTypeWithAnnotations(compilation)), true);
|
|
}
|
|
BoundParameter boundParameter = new BoundParameter(argument.Syntax, parameter);
|
|
CheckDisallowedNullAssignment(rightState, lValueAnnotations, argument.Syntax);
|
|
AdjustSetValue(argument, ref rightState);
|
|
trackNullableStateForAssignment(boundParameter, typeWithAnnotations, MakeSlot(argument), rightState, argument.IsSuppressed, parameterAnnotations);
|
|
if (!argument.IsSuppressed)
|
|
{
|
|
ReportNullableAssignmentIfNecessary(boundParameter, typeWithAnnotations, valueType, UseLegacyWarnings(argument));
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
if (!_conversions.HasIdentityOrImplicitReferenceConversion(parameterType.Type, typeWithAnnotations.Type, ref useSiteInfo))
|
|
{
|
|
ReportNullabilityMismatchInArgument(argument.Syntax, typeWithAnnotations.Type, parameter, parameterType.Type, forOutput: true);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
throw ExceptionUtilities.UnexpectedValue((object)refKind);
|
|
}
|
|
static TypeWithState applyPostConditionsUnconditionally(TypeWithState result2, FlowAnalysisAnnotations annotations)
|
|
{
|
|
if ((annotations & FlowAnalysisAnnotations.MaybeNull) != FlowAnalysisAnnotations.None)
|
|
{
|
|
return TypeWithState.Create(result2.Type, NullableFlowState.MaybeDefault);
|
|
}
|
|
if ((annotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull)
|
|
{
|
|
return TypeWithState.Create(result2.Type, NullableFlowState.NotNull);
|
|
}
|
|
return result2;
|
|
}
|
|
static TypeWithState applyPostConditionsWhenFalse(TypeWithState result2, FlowAnalysisAnnotations annotations)
|
|
{
|
|
bool flag = (annotations & FlowAnalysisAnnotations.NotNullWhenFalse) != 0;
|
|
bool flag2 = (annotations & FlowAnalysisAnnotations.MaybeNullWhenTrue) != 0;
|
|
if ((annotations & FlowAnalysisAnnotations.MaybeNullWhenFalse) != FlowAnalysisAnnotations.None && !(flag2 && flag))
|
|
{
|
|
return TypeWithState.Create(result2.Type, NullableFlowState.MaybeDefault);
|
|
}
|
|
if (flag)
|
|
{
|
|
return TypeWithState.Create(result2.Type, NullableFlowState.NotNull);
|
|
}
|
|
return result2;
|
|
}
|
|
static TypeWithState applyPostConditionsWhenTrue(TypeWithState result2, FlowAnalysisAnnotations annotations)
|
|
{
|
|
bool flag = (annotations & FlowAnalysisAnnotations.NotNullWhenTrue) != 0;
|
|
bool num = (annotations & FlowAnalysisAnnotations.MaybeNullWhenTrue) != 0;
|
|
bool flag2 = (annotations & FlowAnalysisAnnotations.MaybeNullWhenFalse) != 0;
|
|
if (num && !(flag2 && flag))
|
|
{
|
|
return TypeWithState.Create(result2.Type, NullableFlowState.MaybeDefault);
|
|
}
|
|
if (flag)
|
|
{
|
|
return TypeWithState.Create(result2.Type, NullableFlowState.NotNull);
|
|
}
|
|
return result2;
|
|
}
|
|
static bool hasConditionalPostCondition(FlowAnalysisAnnotations annotations)
|
|
{
|
|
if (!(((annotations & FlowAnalysisAnnotations.MaybeNullWhenTrue) != 0) ^ ((annotations & FlowAnalysisAnnotations.MaybeNullWhenFalse) != 0)))
|
|
{
|
|
return ((annotations & FlowAnalysisAnnotations.NotNullWhenTrue) != 0) ^ ((annotations & FlowAnalysisAnnotations.NotNullWhenFalse) != 0);
|
|
}
|
|
return true;
|
|
}
|
|
FlowAnalysisAnnotations notNullBasedOnParameters(FlowAnalysisAnnotations result2, ArrayBuilder<ParameterSymbol>? val, ParameterSymbol parameterSymbol)
|
|
{
|
|
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
|
|
if (!IsAnalyzingAttribute && val != null)
|
|
{
|
|
ImmutableHashSet<string> notNullIfParameterNotNull = parameterSymbol.NotNullIfParameterNotNull;
|
|
if (!notNullIfParameterNotNull.IsEmpty)
|
|
{
|
|
Enumerator<ParameterSymbol> enumerator = val.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
ParameterSymbol current = enumerator.Current;
|
|
if (notNullIfParameterNotNull.Contains(current.Name))
|
|
{
|
|
return FlowAnalysisAnnotations.NotNull;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result2;
|
|
}
|
|
void trackNullableStateForAssignment(BoundExpression parameterValue, TypeWithAnnotations targetType, int targetSlot, TypeWithState parameterWithState, bool isSuppressed, FlowAnalysisAnnotations annotations)
|
|
{
|
|
if (!IsConditionalState && !hasConditionalPostCondition(annotations))
|
|
{
|
|
TrackNullableStateForAssignment(parameterValue, targetType, targetSlot, parameterWithState.WithSuppression(isSuppressed));
|
|
}
|
|
else
|
|
{
|
|
Split();
|
|
LocalState state = StateWhenFalse.Clone();
|
|
SetState(StateWhenTrue);
|
|
TrackNullableStateForAssignment(parameterValue, targetType, targetSlot, applyPostConditionsWhenTrue(parameterWithState, annotations).WithSuppression(isSuppressed));
|
|
LocalState whenTrue = State.Clone();
|
|
SetState(state);
|
|
TrackNullableStateForAssignment(parameterValue, targetType, targetSlot, applyPostConditionsWhenFalse(parameterWithState, annotations).WithSuppression(isSuppressed));
|
|
SetConditionalState(whenTrue, State);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LearnFromPostConditions(BoundExpression argument, FlowAnalysisAnnotations parameterAnnotations)
|
|
{
|
|
bool flag = (parameterAnnotations & FlowAnalysisAnnotations.NotNullWhenTrue) != 0;
|
|
bool flag2 = (parameterAnnotations & FlowAnalysisAnnotations.NotNullWhenFalse) != 0;
|
|
bool flag3 = (parameterAnnotations & FlowAnalysisAnnotations.MaybeNullWhenTrue) != 0;
|
|
bool flag4 = (parameterAnnotations & FlowAnalysisAnnotations.MaybeNullWhenFalse) != 0;
|
|
if (flag3 && flag4 && !IsConditionalState && !(flag && flag2))
|
|
{
|
|
LearnFromNullTest(argument, ref State);
|
|
}
|
|
else if (flag && flag2 && !IsConditionalState && !(flag3 || flag4))
|
|
{
|
|
LearnFromNonNullTest(argument, ref State);
|
|
}
|
|
else if (flag || flag2 || flag3 || flag4)
|
|
{
|
|
Split();
|
|
if (flag)
|
|
{
|
|
LearnFromNonNullTest(argument, ref StateWhenTrue);
|
|
}
|
|
if (flag2)
|
|
{
|
|
LearnFromNonNullTest(argument, ref StateWhenFalse);
|
|
}
|
|
if (flag3)
|
|
{
|
|
LearnFromNullTest(argument, ref StateWhenTrue);
|
|
}
|
|
if (flag4)
|
|
{
|
|
LearnFromNullTest(argument, ref StateWhenFalse);
|
|
}
|
|
}
|
|
}
|
|
|
|
private (ImmutableArray<BoundExpression> arguments, ImmutableArray<Conversion> conversions) RemoveArgumentConversions(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt)
|
|
{
|
|
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
|
|
int length = arguments.Length;
|
|
ImmutableArray<Conversion> item = default(ImmutableArray<Conversion>);
|
|
if (length > 0)
|
|
{
|
|
ArrayBuilder<BoundExpression> instance = ArrayBuilder<BoundExpression>.GetInstance(length);
|
|
ArrayBuilder<Conversion> instance2 = ArrayBuilder<Conversion>.GetInstance(length);
|
|
bool flag = false;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
RefKind refKind = AbstractFlowPass<LocalState, LocalFunctionState>.GetRefKind(refKindsOpt, i);
|
|
BoundExpression boundExpression = arguments[i];
|
|
Conversion conversion = Conversion.Identity;
|
|
if ((int)refKind == 0)
|
|
{
|
|
BoundExpression boundExpression2 = boundExpression;
|
|
(boundExpression, conversion) = RemoveConversion(boundExpression, includeExplicitConversions: false);
|
|
if (boundExpression != boundExpression2)
|
|
{
|
|
SnapshotWalkerThroughConversionGroup(boundExpression2, boundExpression);
|
|
flag = true;
|
|
}
|
|
}
|
|
instance.Add(boundExpression);
|
|
instance2.Add(conversion);
|
|
}
|
|
if (flag)
|
|
{
|
|
arguments = instance.ToImmutable();
|
|
item = instance2.ToImmutable();
|
|
}
|
|
instance.Free();
|
|
instance2.Free();
|
|
}
|
|
return (arguments: arguments, conversions: item);
|
|
}
|
|
|
|
private static VariableState GetVariableState(Variables variables, LocalState localState)
|
|
{
|
|
return new VariableState(variables.CreateSnapshot(), localState.CreateSnapshot());
|
|
}
|
|
|
|
private (ParameterSymbol? Parameter, TypeWithAnnotations Type, FlowAnalysisAnnotations Annotations, bool isExpandedParamsArgument) GetCorrespondingParameter(int argumentOrdinal, ImmutableArray<ParameterSymbol> parametersOpt, ImmutableArray<int> argsToParamsOpt, bool expanded)
|
|
{
|
|
if (parametersOpt.IsDefault)
|
|
{
|
|
return default((ParameterSymbol, TypeWithAnnotations, FlowAnalysisAnnotations, bool));
|
|
}
|
|
ParameterSymbol correspondingParameter = Binder.GetCorrespondingParameter(argumentOrdinal, parametersOpt, argsToParamsOpt, expanded);
|
|
if ((object)correspondingParameter == null)
|
|
{
|
|
return default((ParameterSymbol, TypeWithAnnotations, FlowAnalysisAnnotations, bool));
|
|
}
|
|
TypeWithAnnotations typeWithAnnotations = correspondingParameter.TypeWithAnnotations;
|
|
if (expanded && correspondingParameter.Ordinal == parametersOpt.Length - 1 && typeWithAnnotations.IsSZArray())
|
|
{
|
|
typeWithAnnotations = ((ArrayTypeSymbol)typeWithAnnotations.Type).ElementTypeWithAnnotations;
|
|
return (Parameter: correspondingParameter, Type: typeWithAnnotations, Annotations: FlowAnalysisAnnotations.None, isExpandedParamsArgument: true);
|
|
}
|
|
return (Parameter: correspondingParameter, Type: typeWithAnnotations, Annotations: GetParameterAnnotations(correspondingParameter), isExpandedParamsArgument: false);
|
|
}
|
|
|
|
private MethodSymbol InferMethodTypeArguments(MethodSymbol method, ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> argumentRefKindsOpt, ImmutableArray<int> argsToParamsOpt, bool expanded)
|
|
{
|
|
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
|
|
MethodSymbol constructedFrom = method.ConstructedFrom;
|
|
ArrayBuilder<RefKind> instance = ArrayBuilder<RefKind>.GetInstance();
|
|
if (argumentRefKindsOpt != null)
|
|
{
|
|
instance.AddRange(argumentRefKindsOpt);
|
|
}
|
|
OverloadResolution.GetEffectiveParameterTypes(constructedFrom, arguments.Length, argsToParamsOpt, instance, isMethodGroupConversion: false, allowRefOmittedArguments: true, _binder, expanded, out var parameterTypes, out var parameterRefKinds);
|
|
instance.Free();
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
MethodTypeInferenceResult methodTypeInferenceResult = MethodTypeInferrer.Infer(_binder, _conversions, constructedFrom.TypeParameters, constructedFrom.ContainingType, parameterTypes, parameterRefKinds, arguments, ref useSiteInfo, new MethodInferenceExtensions(this));
|
|
if (!methodTypeInferenceResult.Success)
|
|
{
|
|
return method;
|
|
}
|
|
return constructedFrom.Construct(methodTypeInferenceResult.InferredTypeArguments);
|
|
}
|
|
|
|
private ImmutableArray<BoundExpression> GetArgumentsForMethodTypeInference(ImmutableArray<VisitArgumentResult> argumentResults, ImmutableArray<BoundExpression> arguments)
|
|
{
|
|
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
|
|
int length = argumentResults.Length;
|
|
ArrayBuilder<BoundExpression> instance = ArrayBuilder<BoundExpression>.GetInstance(length);
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
VisitArgumentResult visitArgumentResult = argumentResults[i];
|
|
Optional<LocalState> stateForLambda = visitArgumentResult.StateForLambda;
|
|
TypeWithAnnotations argumentType = visitArgumentResult.RValueType.ToTypeWithAnnotations(compilation);
|
|
instance.Add(getArgumentForMethodTypeInference(arguments[i], argumentType, stateForLambda));
|
|
}
|
|
return instance.ToImmutableAndFree();
|
|
BoundExpression getArgumentForMethodTypeInference(BoundExpression argument, TypeWithAnnotations typeWithAnnotations, Optional<LocalState> lambdaState)
|
|
{
|
|
if (argument.Kind == BoundKind.Lambda)
|
|
{
|
|
return getUnboundLambda((BoundLambda)argument, GetVariableState(_variables, lambdaState.Value));
|
|
}
|
|
if (!typeWithAnnotations.HasType)
|
|
{
|
|
return argument;
|
|
}
|
|
if (argument is BoundLocal { DeclarationKind: BoundLocalDeclarationKind.WithInferredType } || IsTargetTypedExpression(argument))
|
|
{
|
|
return new BoundExpressionWithNullability(argument.Syntax, argument, NullableAnnotation.Oblivious, null);
|
|
}
|
|
return new BoundExpressionWithNullability(argument.Syntax, argument, typeWithAnnotations.NullableAnnotation, typeWithAnnotations.Type);
|
|
}
|
|
static UnboundLambda getUnboundLambda(BoundLambda expr, VariableState variableState)
|
|
{
|
|
return expr.UnboundLambda.WithNullableState(variableState);
|
|
}
|
|
}
|
|
|
|
private void CheckMethodConstraints(SyntaxNode syntax, MethodSymbol method)
|
|
{
|
|
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
|
|
if (_disableDiagnostics)
|
|
{
|
|
return;
|
|
}
|
|
ArrayBuilder<TypeParameterDiagnosticInfo> instance = ArrayBuilder<TypeParameterDiagnosticInfo>.GetInstance();
|
|
ArrayBuilder<TypeParameterDiagnosticInfo> instance2 = ArrayBuilder<TypeParameterDiagnosticInfo>.GetInstance();
|
|
ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null;
|
|
ConstraintsHelper.CheckMethodConstraints(method, new ConstraintsHelper.CheckConstraintsArgs(compilation, _conversions, includeNullability: true, NoLocation.Singleton, null, CompoundUseSiteInfo<AssemblySymbol>.Discarded), instance, instance2, ref useSiteDiagnosticsBuilder);
|
|
Enumerator<TypeParameterDiagnosticInfo> enumerator = instance2.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
TypeParameterDiagnosticInfo current = enumerator.Current;
|
|
if (current.UseSiteInfo.DiagnosticInfo != null)
|
|
{
|
|
base.Diagnostics.Add(current.UseSiteInfo.DiagnosticInfo, syntax.Location);
|
|
}
|
|
}
|
|
useSiteDiagnosticsBuilder?.Free();
|
|
instance2.Free();
|
|
instance.Free();
|
|
}
|
|
|
|
private static (BoundExpression expression, Conversion conversion) RemoveConversion(BoundExpression expr, bool includeExplicitConversions)
|
|
{
|
|
ConversionGroup conversionGroup = null;
|
|
while (expr.Kind == BoundKind.Conversion)
|
|
{
|
|
BoundConversion boundConversion = (BoundConversion)expr;
|
|
if (conversionGroup != boundConversion.ConversionGroupOpt && conversionGroup != null)
|
|
{
|
|
break;
|
|
}
|
|
conversionGroup = boundConversion.ConversionGroupOpt;
|
|
if (!includeExplicitConversions && conversionGroup != null && conversionGroup.IsExplicitConversion)
|
|
{
|
|
return (expression: expr, conversion: Conversion.Identity);
|
|
}
|
|
expr = boundConversion.Operand;
|
|
if (conversionGroup == null)
|
|
{
|
|
return (expression: expr, conversion: boundConversion.Conversion);
|
|
}
|
|
}
|
|
return (expression: expr, conversion: conversionGroup?.Conversion ?? Conversion.Identity);
|
|
}
|
|
|
|
private Conversion GenerateConversionForConditionalOperator(BoundExpression sourceExpression, TypeSymbol? sourceType, TypeSymbol destinationType, bool reportMismatch, bool isChecked)
|
|
{
|
|
Conversion result = GenerateConversion(_conversions, sourceExpression, sourceType, destinationType, fromExplicitCast: false, extensionMethodThisArgument: false, isChecked);
|
|
if (!result.Exists && reportMismatch && !sourceExpression.IsSuppressed)
|
|
{
|
|
ReportNullabilityMismatchInAssignment(sourceExpression.Syntax, GetTypeAsDiagnosticArgument(sourceType), destinationType);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private Conversion GenerateConversion(Conversions conversions, BoundExpression? sourceExpression, TypeSymbol? sourceType, TypeSymbol destinationType, bool fromExplicitCast, bool extensionMethodThisArgument, bool isChecked)
|
|
{
|
|
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
bool flag = (object)sourceType == null || UseExpressionForConversion(sourceExpression);
|
|
if (extensionMethodThisArgument)
|
|
{
|
|
return conversions.ClassifyImplicitExtensionMethodThisArgConversion(flag ? sourceExpression : null, sourceType, destinationType, ref useSiteInfo);
|
|
}
|
|
if (!flag)
|
|
{
|
|
if (!fromExplicitCast)
|
|
{
|
|
return conversions.ClassifyImplicitConversionFromType(sourceType, destinationType, ref useSiteInfo);
|
|
}
|
|
return conversions.ClassifyConversionFromType(sourceType, destinationType, isChecked, ref useSiteInfo, forCast: true);
|
|
}
|
|
if (!fromExplicitCast)
|
|
{
|
|
return conversions.ClassifyImplicitConversionFromExpression(sourceExpression, destinationType, ref useSiteInfo);
|
|
}
|
|
return conversions.ClassifyConversionFromExpression(sourceExpression, destinationType, isChecked, ref useSiteInfo, forCast: true);
|
|
}
|
|
|
|
private bool UseExpressionForConversion([NotNullWhen(true)] BoundExpression? value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
return false;
|
|
}
|
|
if ((object)value.Type == null || value.Type.IsDynamic() || value.ConstantValueOpt != (ConstantValue)null)
|
|
{
|
|
return true;
|
|
}
|
|
if (value.Kind == BoundKind.InterpolatedString)
|
|
{
|
|
return true;
|
|
}
|
|
if (!_binder.InAttributeArgument && !_binder.InParameterDefaultValue && value.Type.HasInlineArrayAttribute(out var _) && (object)value.Type.TryGetInlineArrayElementField() != null)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private TypeWithState GetAdjustedResult(TypeWithState type, int slot)
|
|
{
|
|
if (slot > 0)
|
|
{
|
|
NullableFlowState state = GetState(ref State, slot);
|
|
return TypeWithState.Create(type.Type, state);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
private static Symbol AsMemberOfType(TypeSymbol? type, Symbol symbol)
|
|
{
|
|
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003b: Invalid comparison between Unknown and I4
|
|
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_004f: Invalid comparison between Unknown and I4
|
|
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00b4: Invalid comparison between Unknown and I4
|
|
NamedTypeSymbol namedTypeSymbol = type as NamedTypeSymbol;
|
|
if ((object)namedTypeSymbol == null || namedTypeSymbol.IsErrorType() || symbol is ErrorMethodSymbol)
|
|
{
|
|
return symbol;
|
|
}
|
|
if ((int)symbol.Kind == 9 && (int)((MethodSymbol)symbol).MethodKind == 17)
|
|
{
|
|
return symbol;
|
|
}
|
|
if ((symbol is TupleElementFieldSymbol || symbol is TupleErrorFieldSymbol) ? true : false)
|
|
{
|
|
return symbol.SymbolAsMember(namedTypeSymbol);
|
|
}
|
|
NamedTypeSymbol symbolContainer = symbol.ContainingType;
|
|
if (symbolContainer.IsAnonymousType)
|
|
{
|
|
int? num = (((int)symbol.Kind == 15) ? symbol.MemberIndexOpt : ((int?)null));
|
|
if (!num.HasValue)
|
|
{
|
|
return symbol;
|
|
}
|
|
return AnonymousTypeManager.GetAnonymousTypeProperty(namedTypeSymbol, num.GetValueOrDefault());
|
|
}
|
|
if (!symbolContainer.IsGenericType)
|
|
{
|
|
return symbol;
|
|
}
|
|
if (!namedTypeSymbol.IsGenericType)
|
|
{
|
|
return symbol;
|
|
}
|
|
if (symbolContainer.IsInterface)
|
|
{
|
|
if (tryAsMemberOfSingleType(namedTypeSymbol, out var result))
|
|
{
|
|
return result;
|
|
}
|
|
ImmutableArray<NamedTypeSymbol>.Enumerator enumerator = namedTypeSymbol.AllInterfacesNoUseSiteDiagnostics.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
if (tryAsMemberOfSingleType(enumerator.Current, out result))
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (tryAsMemberOfSingleType(namedTypeSymbol, out var result2))
|
|
{
|
|
return result2;
|
|
}
|
|
namedTypeSymbol = namedTypeSymbol.BaseTypeNoUseSiteDiagnostics;
|
|
}
|
|
while ((object)namedTypeSymbol != null);
|
|
}
|
|
return symbol;
|
|
bool tryAsMemberOfSingleType(NamedTypeSymbol singleType, [NotNullWhen(true)] out Symbol? reference)
|
|
{
|
|
if (!singleType.Equals(symbolContainer, (TypeCompareKind)63))
|
|
{
|
|
reference = null;
|
|
return false;
|
|
}
|
|
Symbol originalDefinition = symbol.OriginalDefinition;
|
|
reference = originalDefinition.SymbolAsMember(singleType);
|
|
if (reference is MethodSymbol { IsGenericMethod: not false } methodSymbol)
|
|
{
|
|
reference = methodSymbol.Construct(((MethodSymbol)symbol).TypeArgumentsWithAnnotations);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitConversion(BoundConversion node)
|
|
{
|
|
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
|
|
TypeWithAnnotations typeWithAnnotations = node.ConversionGroupOpt?.ExplicitType ?? default(TypeWithAnnotations);
|
|
bool hasType = typeWithAnnotations.HasType;
|
|
TypeWithAnnotations targetTypeWithNullability = (hasType ? typeWithAnnotations : TypeWithAnnotations.Create(node.Type));
|
|
var (boundExpression, conversion) = RemoveConversion(node, includeExplicitConversions: true);
|
|
SnapshotWalkerThroughConversionGroup(node, boundExpression);
|
|
TypeWithState operandType = VisitRvalueWithState(boundExpression);
|
|
SetResultType(node, VisitConversion(node, boundExpression, conversion, targetTypeWithNullability, operandType, checkConversion: true, hasType, hasType, AssignmentKind.Assignment, null, hasType, reportRemainingWarnings: true, extensionMethodThisArgument: false, default(Optional<LocalState>), trackMembers: true));
|
|
return null;
|
|
}
|
|
|
|
private TypeWithState VisitOptionalImplicitConversion(BoundExpression expr, TypeWithAnnotations targetTypeOpt, bool useLegacyWarnings, bool trackMembers, AssignmentKind assignmentKind)
|
|
{
|
|
if (!targetTypeOpt.HasType)
|
|
{
|
|
return VisitRvalueWithState(expr);
|
|
}
|
|
return VisitOptionalImplicitConversion(expr, targetTypeOpt, useLegacyWarnings, trackMembers, assignmentKind, delayCompletionForTargetType: false).resultType;
|
|
}
|
|
|
|
private (TypeWithState resultType, Func<TypeWithAnnotations, TypeWithState>? completion) VisitOptionalImplicitConversion(BoundExpression expr, TypeWithAnnotations targetTypeOpt, bool useLegacyWarnings, bool trackMembers, AssignmentKind assignmentKind, bool delayCompletionForTargetType)
|
|
{
|
|
var (boundExpression, conversion) = RemoveConversion(expr, includeExplicitConversions: false);
|
|
SnapshotWalkerThroughConversionGroup(expr, boundExpression);
|
|
TypeWithState operandType = VisitRvalueWithState(boundExpression);
|
|
return visitConversion(expr, targetTypeOpt, useLegacyWarnings, trackMembers, assignmentKind, boundExpression, conversion, operandType, delayCompletionForTargetType);
|
|
(TypeWithState resultType, Func<TypeWithAnnotations, TypeWithState>? completion) visitConversion(BoundExpression boundExpression2, TypeWithAnnotations typeWithAnnotations, bool useLegacyWarnings2, bool flag2, AssignmentKind assignmentKind2, BoundExpression operand, Conversion conversion2, TypeWithState operandType2, bool flag)
|
|
{
|
|
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
|
|
if (flag)
|
|
{
|
|
return (resultType: TypeWithState.Create(typeWithAnnotations), completion: visitConversionAsContinuation(boundExpression2, useLegacyWarnings2, flag2, assignmentKind2, operand, conversion2, operandType2));
|
|
}
|
|
bool reportRemainingWarnings = !conversion2.IsExplicit;
|
|
BoundConversion? conversionIfApplicable = GetConversionIfApplicable(boundExpression2, operand);
|
|
Conversion conversion3 = conversion2;
|
|
bool trackMembers2 = flag2;
|
|
return (resultType: VisitConversion(conversionIfApplicable, operand, conversion3, typeWithAnnotations, operandType2, checkConversion: true, fromExplicitCast: false, useLegacyWarnings2, assignmentKind2, null, reportTopLevelWarnings: true, reportRemainingWarnings, extensionMethodThisArgument: false, default(Optional<LocalState>), trackMembers2), completion: null);
|
|
}
|
|
Func<TypeWithAnnotations, TypeWithState> visitConversionAsContinuation(BoundExpression expr2, bool useLegacyWarnings2, bool trackMembers2, AssignmentKind assignmentKind2, BoundExpression operand, Conversion conversion2, TypeWithState operandType2)
|
|
{
|
|
return (TypeWithAnnotations targetTypeOpt2) => visitConversion(expr2, targetTypeOpt2, useLegacyWarnings2, trackMembers2, assignmentKind2, operand, conversion2, operandType2, delayCompletionForTargetType: false).resultType;
|
|
}
|
|
}
|
|
|
|
private static bool AreNullableAndUnderlyingTypes([NotNullWhen(true)] TypeSymbol? nullableTypeOpt, [NotNullWhen(true)] TypeSymbol? underlyingTypeOpt, out TypeWithAnnotations underlyingTypeWithAnnotations)
|
|
{
|
|
if ((object)nullableTypeOpt != null && nullableTypeOpt.IsNullableType() && (object)underlyingTypeOpt != null && !underlyingTypeOpt.IsNullableType())
|
|
{
|
|
TypeWithAnnotations nullableUnderlyingTypeWithAnnotations = nullableTypeOpt.GetNullableUnderlyingTypeWithAnnotations();
|
|
if (nullableUnderlyingTypeWithAnnotations.Type.Equals(underlyingTypeOpt, (TypeCompareKind)63))
|
|
{
|
|
underlyingTypeWithAnnotations = nullableUnderlyingTypeWithAnnotations;
|
|
return true;
|
|
}
|
|
}
|
|
underlyingTypeWithAnnotations = default(TypeWithAnnotations);
|
|
return false;
|
|
}
|
|
|
|
public override BoundNode? VisitTupleLiteral(BoundTupleLiteral node)
|
|
{
|
|
VisitTupleExpression(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitConvertedTupleLiteral(BoundConvertedTupleLiteral node)
|
|
{
|
|
LocalState state = State.Clone();
|
|
VisitWithoutDiagnostics(node.SourceTuple);
|
|
SetState(state);
|
|
VisitTupleExpression(node);
|
|
return null;
|
|
}
|
|
|
|
private void VisitTupleExpression(BoundTupleExpression node)
|
|
{
|
|
ImmutableArray<BoundExpression> arguments = node.Arguments;
|
|
ImmutableArray<TypeWithState> immutableArray = ImmutableArrayExtensions.SelectAsArray<BoundExpression, NullableWalker, TypeWithState>(arguments, (Func<BoundExpression, NullableWalker, TypeWithState>)((BoundExpression a, NullableWalker w) => w.VisitRvalueWithState(a)), this);
|
|
ImmutableArray<TypeWithAnnotations> newElementTypes = ImmutableArrayExtensions.SelectAsArray<TypeWithState, TypeWithAnnotations>(immutableArray, (Func<TypeWithState, TypeWithAnnotations>)((TypeWithState a) => a.ToTypeWithAnnotations(compilation)));
|
|
NamedTypeSymbol namedTypeSymbol = (NamedTypeSymbol)node.Type;
|
|
if ((object)namedTypeSymbol == null)
|
|
{
|
|
SetResultType(node, TypeWithState.Create(null, NullableFlowState.NotNull));
|
|
return;
|
|
}
|
|
int orCreatePlaceholderSlot = GetOrCreatePlaceholderSlot(node);
|
|
if (orCreatePlaceholderSlot > 0)
|
|
{
|
|
SetState(ref State, orCreatePlaceholderSlot, NullableFlowState.NotNull);
|
|
TrackNullableStateOfTupleElements(orCreatePlaceholderSlot, namedTypeSymbol, arguments, immutableArray, default(ImmutableArray<int>), useRestField: false);
|
|
}
|
|
namedTypeSymbol = namedTypeSymbol.WithElementTypes(newElementTypes);
|
|
if (!_disableDiagnostics)
|
|
{
|
|
ImmutableArray<Location> elementLocations = ImmutableArrayExtensions.SelectAsArray<FieldSymbol, Location, Location>(namedTypeSymbol.TupleElements, (Func<FieldSymbol, Location, Location>)((FieldSymbol element, Location location) => element.TryGetFirstLocation() ?? location), node.Syntax.Location);
|
|
BindingDiagnosticBag instance = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false);
|
|
namedTypeSymbol.CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(compilation, _conversions, includeNullability: true, node.Syntax.Location, null), node.Syntax, elementLocations, instance);
|
|
base.Diagnostics.AddRange(((BindingDiagnosticBag)instance).DiagnosticBag);
|
|
((BindingDiagnosticBag<AssemblySymbol>)(object)instance).Free();
|
|
}
|
|
SetResultType(node, TypeWithState.Create(namedTypeSymbol, NullableFlowState.NotNull));
|
|
}
|
|
|
|
private void TrackNullableStateOfTupleElements(int slot, NamedTypeSymbol tupleType, ImmutableArray<BoundExpression> values, ImmutableArray<TypeWithState> types, ImmutableArray<int> argsToParamsOpt, bool useRestField)
|
|
{
|
|
if (slot > 0)
|
|
{
|
|
ImmutableArray<FieldSymbol> tupleElements = tupleType.TupleElements;
|
|
int num = values.Length;
|
|
if (useRestField)
|
|
{
|
|
num = Math.Min(num, 7);
|
|
}
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
int index = getArgumentOrdinalFromParameterOrdinal(i);
|
|
trackState(values[index], tupleElements[i], types[index]);
|
|
}
|
|
if (useRestField && values.Length == 8 && tupleType.GetMembers("Rest").FirstOrDefault() is FieldSymbol field)
|
|
{
|
|
int index2 = getArgumentOrdinalFromParameterOrdinal(7);
|
|
trackState(values[index2], field, types[index2]);
|
|
}
|
|
}
|
|
int getArgumentOrdinalFromParameterOrdinal(int parameterOrdinal)
|
|
{
|
|
if (!argsToParamsOpt.IsDefault)
|
|
{
|
|
return argsToParamsOpt.IndexOf(parameterOrdinal);
|
|
}
|
|
return parameterOrdinal;
|
|
}
|
|
void trackState(BoundExpression value, FieldSymbol fieldSymbol, TypeWithState valueType)
|
|
{
|
|
int orCreateSlot = GetOrCreateSlot(fieldSymbol, slot);
|
|
TrackNullableStateForAssignment(value, fieldSymbol.TypeWithAnnotations, orCreateSlot, valueType, MakeSlot(value));
|
|
}
|
|
}
|
|
|
|
private void TrackNullableStateOfNullableValue(int containingSlot, TypeSymbol containingType, BoundExpression? value, TypeWithState valueType, int valueSlot)
|
|
{
|
|
Symbol valueProperty;
|
|
int nullableOfTValueSlot = GetNullableOfTValueSlot(containingType, containingSlot, out valueProperty);
|
|
if (nullableOfTValueSlot > 0)
|
|
{
|
|
TrackNullableStateForAssignment(value, valueProperty.GetTypeOrReturnType(), nullableOfTValueSlot, valueType, valueSlot);
|
|
}
|
|
}
|
|
|
|
private void TrackNullableStateOfTupleConversion(BoundConversion? conversionOpt, BoundExpression convertedNode, Conversion conversion, TypeSymbol targetType, TypeSymbol operandType, int slot, int valueSlot, AssignmentKind assignmentKind, ParameterSymbol? parameterOpt, bool reportWarnings)
|
|
{
|
|
if (operandType is NamedTypeSymbol { IsTupleType: not false } namedTypeSymbol)
|
|
{
|
|
ImmutableArray<Conversion> underlyingConversions = conversion.UnderlyingConversions;
|
|
ImmutableArray<FieldSymbol> tupleElements = ((NamedTypeSymbol)targetType).TupleElements;
|
|
ImmutableArray<FieldSymbol> tupleElements2 = namedTypeSymbol.TupleElements;
|
|
int length = tupleElements2.Length;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
trackConvertedValue(tupleElements[i], underlyingConversions[i], tupleElements2[i]);
|
|
}
|
|
}
|
|
void trackConvertedValue(FieldSymbol targetField, Conversion conversion2, FieldSymbol valueField)
|
|
{
|
|
switch (conversion2.Kind)
|
|
{
|
|
case ConversionKind.Identity:
|
|
case ConversionKind.NullLiteral:
|
|
case ConversionKind.ImplicitReference:
|
|
case ConversionKind.Boxing:
|
|
case ConversionKind.ExplicitReference:
|
|
case ConversionKind.Unboxing:
|
|
case ConversionKind.DefaultLiteral:
|
|
InheritNullableStateOfMember(slot, valueSlot, valueField, isDefaultValue: false, slot);
|
|
break;
|
|
case ConversionKind.ImplicitTupleLiteral:
|
|
case ConversionKind.ImplicitTuple:
|
|
case ConversionKind.ExplicitTupleLiteral:
|
|
case ConversionKind.ExplicitTuple:
|
|
{
|
|
int orCreateSlot4 = GetOrCreateSlot(targetField, slot);
|
|
if (orCreateSlot4 > 0)
|
|
{
|
|
SetState(ref State, orCreateSlot4, NullableFlowState.NotNull);
|
|
int orCreateSlot5 = GetOrCreateSlot(valueField, valueSlot);
|
|
if (orCreateSlot5 > 0)
|
|
{
|
|
TrackNullableStateOfTupleConversion(conversionOpt, convertedNode, conversion2, targetField.Type, valueField.Type, orCreateSlot4, orCreateSlot5, assignmentKind, parameterOpt, reportWarnings);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ConversionKind.ImplicitNullable:
|
|
case ConversionKind.ExplicitNullable:
|
|
{
|
|
if (AreNullableAndUnderlyingTypes(targetField.Type, valueField.Type, out var _))
|
|
{
|
|
int orCreateSlot2 = GetOrCreateSlot(targetField, slot);
|
|
if (orCreateSlot2 > 0)
|
|
{
|
|
SetState(ref State, orCreateSlot2, NullableFlowState.NotNull);
|
|
int orCreateSlot3 = GetOrCreateSlot(valueField, valueSlot);
|
|
if (orCreateSlot3 > 0)
|
|
{
|
|
TrackNullableStateOfNullableValue(orCreateSlot2, targetField.Type, null, valueField.TypeWithAnnotations.ToTypeWithState(), orCreateSlot3);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ConversionKind.ImplicitUserDefined:
|
|
case ConversionKind.ExplicitUserDefined:
|
|
{
|
|
TypeWithState typeWithState = VisitUserDefinedConversion(conversionOpt, convertedNode, conversion2, targetField.TypeWithAnnotations, valueField.TypeWithAnnotations.ToTypeWithState(), useLegacyWarnings: false, assignmentKind, parameterOpt, reportWarnings, reportWarnings, (conversionOpt ?? convertedNode).Syntax.GetLocation());
|
|
int orCreateSlot = GetOrCreateSlot(targetField, slot);
|
|
if (orCreateSlot > 0)
|
|
{
|
|
SetState(ref State, orCreateSlot, typeWithState.State);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitTupleBinaryOperator(BoundTupleBinaryOperator node)
|
|
{
|
|
base.VisitTupleBinaryOperator(node);
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
private void ReportNullabilityMismatchWithTargetDelegate(Location location, TypeSymbol targetType, MethodSymbol targetInvokeMethod, MethodSymbol sourceInvokeMethod, bool invokedAsExtensionMethod)
|
|
{
|
|
BindingDiagnosticBag instance = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false);
|
|
SourceMemberContainerTypeSymbol.CheckValidNullableMethodOverride(compilation, targetInvokeMethod, sourceInvokeMethod, instance, reportBadDelegateReturn, reportBadDelegateParameter, (targetType, location), invokedAsExtensionMethod);
|
|
base.Diagnostics.AddRange(((BindingDiagnosticBag)instance).DiagnosticBag);
|
|
((BindingDiagnosticBag<AssemblySymbol>)(object)instance).Free();
|
|
void reportBadDelegateParameter(BindingDiagnosticBag bag, MethodSymbol methodSymbol, MethodSymbol methodSymbol2, ParameterSymbol parameter, bool topLevel, (TypeSymbol targetType, Location location) arg)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, arg.location, GetParameterAsDiagnosticArgument(parameter), GetContainingSymbolAsDiagnosticArgument(parameter), arg.targetType);
|
|
}
|
|
void reportBadDelegateReturn(BindingDiagnosticBag bag, MethodSymbol methodSymbol2, MethodSymbol methodSymbol, bool topLevel, (TypeSymbol targetType, Location location) arg)
|
|
{
|
|
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0021: Expected O, but got Unknown
|
|
ReportDiagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, arg.location, (object)new FormattedSymbol((ISymbolInternal)(object)methodSymbol, SymbolDisplayFormat.MinimallyQualifiedFormat), arg.targetType);
|
|
}
|
|
}
|
|
|
|
private void ReportNullabilityMismatchWithTargetDelegate(Location location, NamedTypeSymbol delegateType, BoundLambda lambda)
|
|
{
|
|
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
|
|
MethodSymbol delegateInvokeMethod = delegateType.DelegateInvokeMethod;
|
|
LambdaSymbol symbol = lambda.Symbol;
|
|
UnboundLambda unboundLambda = lambda.UnboundLambda;
|
|
if ((object)delegateInvokeMethod != null && delegateInvokeMethod.ParameterCount == symbol.ParameterCount)
|
|
{
|
|
if (lambda.Syntax is LambdaExpressionSyntax lambdaExpressionSyntax)
|
|
{
|
|
int spanStart = ((SyntaxNode)lambdaExpressionSyntax).SpanStart;
|
|
SyntaxTree syntaxTree = lambdaExpressionSyntax.SyntaxTree;
|
|
SyntaxToken arrowToken = lambdaExpressionSyntax.ArrowToken;
|
|
TextSpan span = ((SyntaxToken)(ref arrowToken)).Span;
|
|
location = Location.Create(syntaxTree, new TextSpan(spanStart, ((TextSpan)(ref span)).End - spanStart));
|
|
}
|
|
BindingDiagnosticBag instance = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false);
|
|
if (SourceMemberContainerTypeSymbol.CheckValidNullableMethodOverride<Location>(compilation, delegateInvokeMethod, symbol, instance, reportBadDelegateReturn, reportBadDelegateParameter, location))
|
|
{
|
|
base.Diagnostics.AddRange(((BindingDiagnosticBag)instance).DiagnosticBag);
|
|
((BindingDiagnosticBag<AssemblySymbol>)(object)instance).Free();
|
|
}
|
|
else
|
|
{
|
|
SourceMemberContainerTypeSymbol.CheckValidNullableMethodOverride<Location>(compilation, symbol, delegateInvokeMethod, instance, reportBadDelegateReturn, reportBadDelegateParameter, location);
|
|
base.Diagnostics.AddRange(((BindingDiagnosticBag)instance).DiagnosticBag);
|
|
((BindingDiagnosticBag<AssemblySymbol>)(object)instance).Free();
|
|
}
|
|
}
|
|
void reportBadDelegateParameter(BindingDiagnosticBag bag, MethodSymbol sourceInvokeMethod, MethodSymbol targetInvokeMethod, ParameterSymbol parameterSymbol, bool topLevel, Location location2)
|
|
{
|
|
if (unboundLambda.HasSignature)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, location2, unboundLambda.ParameterName(parameterSymbol.Ordinal), unboundLambda.MessageID.Localize(), delegateType);
|
|
}
|
|
}
|
|
void reportBadDelegateReturn(BindingDiagnosticBag bag, MethodSymbol targetInvokeMethod, MethodSymbol sourceInvokeMethod, bool topLevel, Location location2)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, location2, unboundLambda.MessageID.Localize(), delegateType);
|
|
}
|
|
}
|
|
|
|
private static BoundConversion? GetConversionIfApplicable(BoundExpression? conversionOpt, BoundExpression convertedNode)
|
|
{
|
|
if (conversionOpt != convertedNode)
|
|
{
|
|
return (BoundConversion)conversionOpt;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private TypeWithState VisitConversion(BoundConversion? conversionOpt, BoundExpression conversionOperand, Conversion conversion, TypeWithAnnotations targetTypeWithNullability, TypeWithState operandType, bool checkConversion, bool fromExplicitCast, bool useLegacyWarnings, AssignmentKind assignmentKind, ParameterSymbol? parameterOpt = null, bool reportTopLevelWarnings = true, bool reportRemainingWarnings = true, bool extensionMethodThisArgument = false, Optional<LocalState> stateForLambda = default(Optional<LocalState>), bool trackMembers = false, Location? diagnosticLocation = null, ArrayBuilder<VisitResult>? previousArgumentConversionResults = null)
|
|
{
|
|
//IL_024b: Unknown result type (might be due to invalid IL or missing references)
|
|
if (IsTargetTypedExpression(conversionOperand) && ((Dictionary<BoundExpression, Func<TypeWithAnnotations, TypeWithState>>)(object)TargetTypedAnalysisCompletion).TryGetValue(conversionOperand, out Func<TypeWithAnnotations, TypeWithState> value))
|
|
{
|
|
((Dictionary<BoundExpression, Func<TypeWithAnnotations, TypeWithState>>)(object)TargetTypedAnalysisCompletion).Remove(conversionOperand);
|
|
if (conversionOperand is BoundObjectCreationExpressionBase && targetTypeWithNullability.IsNullableType())
|
|
{
|
|
operandType = value(targetTypeWithNullability.Type.GetNullableUnderlyingTypeWithAnnotations());
|
|
conversion = Conversion.MakeNullableConversion(ConversionKind.ImplicitNullable, Conversion.Identity);
|
|
}
|
|
else
|
|
{
|
|
operandType = value(targetTypeWithNullability);
|
|
}
|
|
}
|
|
NullableFlowState resultState = NullableFlowState.NotNull;
|
|
bool flag = true;
|
|
bool isSuppressed = false;
|
|
if (conversionOperand.IsSuppressed)
|
|
{
|
|
reportTopLevelWarnings = false;
|
|
reportRemainingWarnings = false;
|
|
isSuppressed = true;
|
|
}
|
|
TypeSymbol type = targetTypeWithNullability.Type;
|
|
switch (conversion.Kind)
|
|
{
|
|
case ConversionKind.MethodGroup:
|
|
{
|
|
BoundMethodGroup boundMethodGroup = conversionOperand as BoundMethodGroup;
|
|
(MethodSymbol invokeSignature, ImmutableArray<ParameterSymbol>) tuple = getDelegateOrFunctionPointerInfo(type);
|
|
MethodSymbol item = tuple.invokeSignature;
|
|
ImmutableArray<ParameterSymbol> item2 = tuple.Item2;
|
|
MethodSymbol methodSymbol = conversion.Method;
|
|
if (boundMethodGroup != null)
|
|
{
|
|
if (methodSymbol?.OriginalDefinition is LocalFunctionSymbol symbol)
|
|
{
|
|
VisitLocalFunctionUse(symbol);
|
|
}
|
|
methodSymbol = CheckMethodGroupReceiverNullability(boundMethodGroup, item2, methodSymbol, conversion.IsExtensionMethod);
|
|
}
|
|
if (reportRemainingWarnings && item != null)
|
|
{
|
|
ReportNullabilityMismatchWithTargetDelegate(getDiagnosticLocation(), type, item, methodSymbol, conversion.IsExtensionMethod);
|
|
}
|
|
resultState = NullableFlowState.NotNull;
|
|
break;
|
|
}
|
|
case ConversionKind.AnonymousFunction:
|
|
if (conversionOperand is BoundLambda boundLambda)
|
|
{
|
|
NamedTypeSymbol delegateType = type.GetDelegateType();
|
|
VisitLambda(boundLambda, delegateType, stateForLambda);
|
|
if (reportRemainingWarnings && (object)delegateType != null)
|
|
{
|
|
ReportNullabilityMismatchWithTargetDelegate(getDiagnosticLocation(), delegateType, boundLambda);
|
|
}
|
|
TrackAnalyzedNullabilityThroughConversionGroup(targetTypeWithNullability.ToTypeWithState(), conversionOpt, conversionOperand);
|
|
return TypeWithState.Create(type, NullableFlowState.NotNull);
|
|
}
|
|
break;
|
|
case ConversionKind.FunctionType:
|
|
resultState = NullableFlowState.NotNull;
|
|
break;
|
|
case ConversionKind.InterpolatedString:
|
|
resultState = NullableFlowState.NotNull;
|
|
break;
|
|
case ConversionKind.InterpolatedStringHandler:
|
|
visitInterpolatedStringHandlerConstructor();
|
|
resultState = NullableFlowState.NotNull;
|
|
break;
|
|
case ConversionKind.SwitchExpression:
|
|
case ConversionKind.ConditionalExpression:
|
|
case ConversionKind.ObjectCreation:
|
|
case ConversionKind.CollectionExpression:
|
|
resultState = getConversionResultState(operandType);
|
|
break;
|
|
case ConversionKind.ImplicitUserDefined:
|
|
case ConversionKind.ExplicitUserDefined:
|
|
return VisitUserDefinedConversion(conversionOpt, conversionOperand, conversion, targetTypeWithNullability, operandType, useLegacyWarnings, assignmentKind, parameterOpt, reportTopLevelWarnings, reportRemainingWarnings, getDiagnosticLocation());
|
|
case ConversionKind.ImplicitDynamic:
|
|
case ConversionKind.ExplicitDynamic:
|
|
resultState = getConversionResultState(operandType);
|
|
break;
|
|
case ConversionKind.Boxing:
|
|
resultState = getBoxingConversionResultState(targetTypeWithNullability, operandType);
|
|
break;
|
|
case ConversionKind.Unboxing:
|
|
if (type.IsNonNullableValueType())
|
|
{
|
|
if (!operandType.IsNotNull && reportRemainingWarnings)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_UnboxPossibleNull, getDiagnosticLocation());
|
|
}
|
|
LearnFromNonNullTest(conversionOperand, ref State);
|
|
}
|
|
else
|
|
{
|
|
resultState = getUnboxingConversionResultState(operandType);
|
|
}
|
|
break;
|
|
case ConversionKind.ImplicitThrow:
|
|
resultState = NullableFlowState.NotNull;
|
|
break;
|
|
case ConversionKind.NoConversion:
|
|
resultState = getConversionResultState(operandType);
|
|
break;
|
|
case ConversionKind.NullLiteral:
|
|
case ConversionKind.DefaultLiteral:
|
|
checkConversion = false;
|
|
goto case ConversionKind.Identity;
|
|
case ConversionKind.Identity:
|
|
{
|
|
if (useLegacyWarnings && conversionOperand is BoundConversion boundConversion && !boundConversion.ConversionKind.IsUserDefinedConversion())
|
|
{
|
|
TypeWithAnnotations? typeWithAnnotations = boundConversion.ConversionGroupOpt?.ExplicitType;
|
|
if (typeWithAnnotations.HasValue && typeWithAnnotations.GetValueOrDefault().Equals(targetTypeWithNullability, (TypeCompareKind)0))
|
|
{
|
|
TrackAnalyzedNullabilityThroughConversionGroup(calculateResultType(targetTypeWithNullability, fromExplicitCast, operandType.State, isSuppressed, type), conversionOpt, conversionOperand);
|
|
return operandType;
|
|
}
|
|
}
|
|
TypeSymbol? type3 = operandType.Type;
|
|
if (((object)type3 == null || !type3.IsTupleType) && conversionOperand.Kind != BoundKind.TupleLiteral)
|
|
{
|
|
goto case ConversionKind.ImplicitReference;
|
|
}
|
|
goto case ConversionKind.ImplicitTupleLiteral;
|
|
}
|
|
case ConversionKind.ImplicitReference:
|
|
case ConversionKind.ExplicitReference:
|
|
if (checkConversion)
|
|
{
|
|
conversion = GenerateConversion(_conversions, conversionOperand, operandType.Type, type, fromExplicitCast, extensionMethodThisArgument, conversionOpt?.Checked ?? false);
|
|
flag = conversion.Exists;
|
|
}
|
|
resultState = (conversion.IsReference ? getReferenceConversionResultState(targetTypeWithNullability, operandType) : operandType.State);
|
|
break;
|
|
case ConversionKind.ImplicitNullable:
|
|
{
|
|
if (trackMembers && AreNullableAndUnderlyingTypes(type, operandType.Type, out var underlyingTypeWithAnnotations))
|
|
{
|
|
int num2 = MakeSlot(conversionOperand);
|
|
if (num2 > 0)
|
|
{
|
|
int orCreatePlaceholderSlot2 = GetOrCreatePlaceholderSlot(conversionOpt);
|
|
TrackNullableStateOfNullableValue(orCreatePlaceholderSlot2, type, conversionOperand, underlyingTypeWithAnnotations.ToTypeWithState(), num2);
|
|
}
|
|
}
|
|
if (checkConversion)
|
|
{
|
|
conversion = GenerateConversion(_conversions, conversionOperand, operandType.Type, type, fromExplicitCast, extensionMethodThisArgument, conversionOpt?.Checked ?? false);
|
|
flag = conversion.Exists;
|
|
}
|
|
resultState = operandType.State;
|
|
break;
|
|
}
|
|
case ConversionKind.ExplicitNullable:
|
|
{
|
|
TypeSymbol? type2 = operandType.Type;
|
|
if ((object)type2 != null && type2.IsNullableType() && !type.IsNullableType())
|
|
{
|
|
if (reportTopLevelWarnings && operandType.MayBeNull)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, getDiagnosticLocation());
|
|
}
|
|
if (conversionOperand != null)
|
|
{
|
|
LearnFromNonNullTest(conversionOperand, ref State);
|
|
}
|
|
}
|
|
goto case ConversionKind.ImplicitNullable;
|
|
}
|
|
case ConversionKind.ImplicitTupleLiteral:
|
|
case ConversionKind.ImplicitTuple:
|
|
case ConversionKind.ExplicitTupleLiteral:
|
|
case ConversionKind.ExplicitTuple:
|
|
if (trackMembers)
|
|
{
|
|
ConversionKind kind = conversion.Kind;
|
|
if (kind == ConversionKind.ImplicitTuple || kind == ConversionKind.ExplicitTuple)
|
|
{
|
|
int num = MakeSlot(conversionOperand);
|
|
if (num > 0)
|
|
{
|
|
int orCreatePlaceholderSlot = GetOrCreatePlaceholderSlot(conversionOpt);
|
|
if (orCreatePlaceholderSlot > 0)
|
|
{
|
|
TrackNullableStateOfTupleConversion(conversionOpt, conversionOperand, conversion, type, operandType.Type, orCreatePlaceholderSlot, num, assignmentKind, parameterOpt, reportRemainingWarnings);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (checkConversion && !type.IsErrorType())
|
|
{
|
|
conversion = GenerateConversion(_conversions, conversionOperand, operandType.Type, type, fromExplicitCast, extensionMethodThisArgument, conversionOpt?.Checked ?? false);
|
|
flag = conversion.Exists;
|
|
}
|
|
resultState = NullableFlowState.NotNull;
|
|
break;
|
|
case ConversionKind.InlineArray:
|
|
if (checkConversion)
|
|
{
|
|
conversion = GenerateConversion(_conversions, conversionOperand, operandType.Type, type, fromExplicitCast, extensionMethodThisArgument, conversionOpt?.Checked ?? false);
|
|
flag = conversion.Exists;
|
|
}
|
|
break;
|
|
}
|
|
TypeWithState typeWithState = calculateResultType(targetTypeWithNullability, fromExplicitCast, resultState, isSuppressed, type);
|
|
if (!conversionOperand.HasErrors && !type.IsErrorType())
|
|
{
|
|
if (reportTopLevelWarnings)
|
|
{
|
|
ReportNullableAssignmentIfNecessary(conversionOperand, targetTypeWithNullability, typeWithState, useLegacyWarnings, assignmentKind, parameterOpt, getDiagnosticLocation());
|
|
}
|
|
if (reportRemainingWarnings && !flag)
|
|
{
|
|
if (assignmentKind == AssignmentKind.Argument)
|
|
{
|
|
ReportNullabilityMismatchInArgument(getDiagnosticLocation(), operandType.Type, parameterOpt, type, forOutput: false);
|
|
}
|
|
else
|
|
{
|
|
ReportNullabilityMismatchInAssignment(getDiagnosticLocation(), GetTypeAsDiagnosticArgument(operandType.Type), type);
|
|
}
|
|
}
|
|
}
|
|
TrackAnalyzedNullabilityThroughConversionGroup(typeWithState, conversionOpt, conversionOperand);
|
|
return typeWithState;
|
|
static TypeWithState calculateResultType(TypeWithAnnotations typeWithAnnotations2, bool flag3, NullableFlowState defaultState, bool flag2, TypeSymbol targetType)
|
|
{
|
|
if (flag2)
|
|
{
|
|
defaultState = NullableFlowState.NotNull;
|
|
}
|
|
else if (flag3 && typeWithAnnotations2.NullableAnnotation.IsAnnotated() && !targetType.IsNullableType())
|
|
{
|
|
defaultState = (((object)targetType == null || !targetType.IsTypeParameterDisallowingAnnotationInCSharp8()) ? NullableFlowState.MaybeNull : NullableFlowState.MaybeDefault);
|
|
}
|
|
return TypeWithState.Create(targetType, defaultState);
|
|
}
|
|
static bool dependsOnTypeParameter(TypeParameterSymbol typeParameter1, TypeParameterSymbol typeParameter2, NullableAnnotation typeParameter1Annotation, out NullableAnnotation annotation)
|
|
{
|
|
if (typeParameter1.Equals(typeParameter2, (TypeCompareKind)63))
|
|
{
|
|
annotation = typeParameter1Annotation;
|
|
return true;
|
|
}
|
|
bool flag2 = false;
|
|
NullableAnnotation a = NullableAnnotation.Annotated;
|
|
ImmutableArray<TypeWithAnnotations>.Enumerator enumerator = typeParameter1.ConstraintTypesNoUseSiteDiagnostics.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
TypeWithAnnotations current = enumerator.Current;
|
|
if (current.Type is TypeParameterSymbol typeParameter3 && dependsOnTypeParameter(typeParameter3, typeParameter2, current.NullableAnnotation, out var annotation2))
|
|
{
|
|
flag2 = true;
|
|
a = a.Meet(annotation2);
|
|
}
|
|
}
|
|
if (flag2)
|
|
{
|
|
annotation = a.Join(typeParameter1Annotation);
|
|
return true;
|
|
}
|
|
annotation = NullableAnnotation.NotAnnotated;
|
|
return false;
|
|
}
|
|
static NullableFlowState getBoxingConversionResultState(TypeWithAnnotations targetType, TypeWithState typeWithState2)
|
|
{
|
|
NullableFlowState state = typeWithState2.State;
|
|
if (state == NullableFlowState.MaybeNull)
|
|
{
|
|
TypeSymbol type4 = typeWithState2.Type;
|
|
if ((object)type4 == null || !type4.IsTypeParameterDisallowingAnnotationInCSharp8())
|
|
{
|
|
return NullableFlowState.MaybeDefault;
|
|
}
|
|
if (targetType.NullableAnnotation.IsNotAnnotated() && type4 is TypeParameterSymbol typeParameter && targetType.Type is TypeParameterSymbol typeParameter2 && dependsOnTypeParameter(typeParameter, typeParameter2, NullableAnnotation.NotAnnotated, out var annotation))
|
|
{
|
|
if (annotation != NullableAnnotation.Annotated)
|
|
{
|
|
return NullableFlowState.MaybeNull;
|
|
}
|
|
return NullableFlowState.MaybeDefault;
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
static NullableFlowState getConversionResultState(TypeWithState typeWithState2)
|
|
{
|
|
NullableFlowState state = typeWithState2.State;
|
|
if (state == NullableFlowState.MaybeNull)
|
|
{
|
|
return NullableFlowState.MaybeDefault;
|
|
}
|
|
return state;
|
|
}
|
|
static (MethodSymbol invokeSignature, ImmutableArray<ParameterSymbol>) getDelegateOrFunctionPointerInfo(TypeSymbol targetType)
|
|
{
|
|
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0013: Invalid comparison between Unknown and I4
|
|
if (targetType is NamedTypeSymbol namedTypeSymbol)
|
|
{
|
|
if ((int)targetType.TypeKind == 3)
|
|
{
|
|
MethodSymbol delegateInvokeMethod = namedTypeSymbol.DelegateInvokeMethod;
|
|
if ((object)delegateInvokeMethod != null)
|
|
{
|
|
ImmutableArray<ParameterSymbol> parameters = delegateInvokeMethod.Parameters;
|
|
return (invokeSignature: delegateInvokeMethod, parameters);
|
|
}
|
|
}
|
|
}
|
|
else if (targetType is FunctionPointerTypeSymbol functionPointerTypeSymbol)
|
|
{
|
|
FunctionPointerMethodSymbol signature = functionPointerTypeSymbol.Signature;
|
|
if ((object)signature != null)
|
|
{
|
|
ImmutableArray<ParameterSymbol> parameters2 = signature.Parameters;
|
|
return (invokeSignature: signature, parameters2);
|
|
}
|
|
}
|
|
return (invokeSignature: null, ImmutableArray<ParameterSymbol>.Empty);
|
|
}
|
|
Location getDiagnosticLocation()
|
|
{
|
|
if (diagnosticLocation == null)
|
|
{
|
|
diagnosticLocation = (conversionOpt ?? conversionOperand).Syntax.GetLocation();
|
|
}
|
|
return diagnosticLocation;
|
|
}
|
|
static NullableFlowState getReferenceConversionResultState(TypeWithAnnotations targetType, TypeWithState typeWithState2)
|
|
{
|
|
NullableFlowState state = typeWithState2.State;
|
|
switch (state)
|
|
{
|
|
case NullableFlowState.MaybeNull:
|
|
{
|
|
TypeSymbol type5 = targetType.Type;
|
|
if ((object)type5 != null && type5.IsTypeParameterDisallowingAnnotationInCSharp8())
|
|
{
|
|
TypeSymbol type6 = typeWithState2.Type;
|
|
if ((object)type6 == null || !type6.IsTypeParameterDisallowingAnnotationInCSharp8())
|
|
{
|
|
return NullableFlowState.MaybeDefault;
|
|
}
|
|
if (targetType.NullableAnnotation.IsNotAnnotated() && type6 is TypeParameterSymbol typeParameter && dependsOnTypeParameter(typeParameter, (TypeParameterSymbol)targetType.Type, NullableAnnotation.NotAnnotated, out var annotation))
|
|
{
|
|
if (annotation != NullableAnnotation.Annotated)
|
|
{
|
|
return NullableFlowState.MaybeNull;
|
|
}
|
|
return NullableFlowState.MaybeDefault;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case NullableFlowState.MaybeDefault:
|
|
{
|
|
TypeSymbol type4 = targetType.Type;
|
|
if ((object)type4 != null && !type4.IsTypeParameterDisallowingAnnotationInCSharp8())
|
|
{
|
|
return NullableFlowState.MaybeNull;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
static NullableFlowState getUnboxingConversionResultState(TypeWithState typeWithState2)
|
|
{
|
|
NullableFlowState state = typeWithState2.State;
|
|
if (state == NullableFlowState.MaybeNull)
|
|
{
|
|
return NullableFlowState.MaybeDefault;
|
|
}
|
|
return state;
|
|
}
|
|
void visitHandlerConstruction(InterpolatedStringHandlerData handlerData)
|
|
{
|
|
VisitRvalue(handlerData.Construction);
|
|
}
|
|
void visitInterpolatedStringHandlerConstructor()
|
|
{
|
|
InterpolatedStringHandlerData interpolatedStringHandlerData = conversionOperand.GetInterpolatedStringHandlerData(throwOnMissing: false);
|
|
if (!interpolatedStringHandlerData.IsDefault)
|
|
{
|
|
if (previousArgumentConversionResults == null)
|
|
{
|
|
visitHandlerConstruction(interpolatedStringHandlerData);
|
|
}
|
|
else
|
|
{
|
|
bool flag2 = false;
|
|
ImmutableArray<BoundInterpolatedStringArgumentPlaceholder>.Enumerator enumerator = interpolatedStringHandlerData.ArgumentPlaceholders.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundInterpolatedStringArgumentPlaceholder current = enumerator.Current;
|
|
int argumentIndex = current.ArgumentIndex;
|
|
if ((uint)(argumentIndex - -3) > 2u && previousArgumentConversionResults.Count > current.ArgumentIndex)
|
|
{
|
|
AddPlaceholderReplacement(current, null, previousArgumentConversionResults[current.ArgumentIndex]);
|
|
flag2 = true;
|
|
}
|
|
}
|
|
visitHandlerConstruction(interpolatedStringHandlerData);
|
|
if (flag2)
|
|
{
|
|
enumerator = interpolatedStringHandlerData.ArgumentPlaceholders.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundInterpolatedStringArgumentPlaceholder current2 = enumerator.Current;
|
|
if (current2.ArgumentIndex < previousArgumentConversionResults.Count && current2.ArgumentIndex >= 0)
|
|
{
|
|
RemovePlaceholderReplacement(current2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private TypeWithState VisitUserDefinedConversion(BoundConversion? conversionOpt, BoundExpression conversionOperand, Conversion conversion, TypeWithAnnotations targetTypeWithNullability, TypeWithState operandType, bool useLegacyWarnings, AssignmentKind assignmentKind, ParameterSymbol? parameterOpt, bool reportTopLevelWarnings, bool reportRemainingWarnings, Location diagnosticLocation)
|
|
{
|
|
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
|
|
TypeSymbol type = targetTypeWithNullability.Type;
|
|
if (!conversion.IsValid)
|
|
{
|
|
TypeWithState typeWithState = TypeWithState.Create(type, NullableFlowState.NotNull);
|
|
TrackAnalyzedNullabilityThroughConversionGroup(typeWithState, conversionOpt, conversionOperand);
|
|
return typeWithState;
|
|
}
|
|
operandType = VisitConversion(conversionOpt, conversionOperand, conversion.UserDefinedFromConversion, TypeWithAnnotations.Create(conversion.BestUserDefinedConversionAnalysis.FromType), operandType, checkConversion: true, fromExplicitCast: false, useLegacyWarnings, assignmentKind, parameterOpt, reportTopLevelWarnings, reportRemainingWarnings, extensionMethodThisArgument: false, default(Optional<LocalState>), trackMembers: false, diagnosticLocation);
|
|
MethodSymbol method = conversion.Method;
|
|
ParameterSymbol parameterSymbol = method.Parameters[0];
|
|
FlowAnalysisAnnotations parameterAnnotations = GetParameterAnnotations(parameterSymbol);
|
|
TypeWithAnnotations targetType = ApplyLValueAnnotations(parameterSymbol.TypeWithAnnotations, parameterAnnotations);
|
|
TypeWithState typeWithState2 = default(TypeWithState);
|
|
bool flag = false;
|
|
if (operandType.Type.IsNullableType() && !targetType.IsNullableType())
|
|
{
|
|
TypeWithAnnotations nullableUnderlyingTypeWithAnnotations = operandType.Type.GetNullableUnderlyingTypeWithAnnotations();
|
|
typeWithState2 = nullableUnderlyingTypeWithAnnotations.ToTypeWithState();
|
|
flag = targetType.Equals(nullableUnderlyingTypeWithAnnotations, (TypeCompareKind)63);
|
|
}
|
|
NullableFlowState state = operandType.State;
|
|
Location location = conversionOperand.Syntax.GetLocation();
|
|
ClassifyAndVisitConversion(conversionOperand, targetType, flag ? typeWithState2 : operandType, useLegacyWarnings, AssignmentKind.Argument, parameterSymbol, reportRemainingWarnings, fromExplicitCast: false, location);
|
|
if (!flag && CheckDisallowedNullAssignment(operandType, parameterAnnotations, conversionOperand.Syntax))
|
|
{
|
|
LearnFromNonNullTest(conversionOperand, ref State);
|
|
}
|
|
TypeWithAnnotations returnTypeWithAnnotations = method.ReturnTypeWithAnnotations;
|
|
operandType = GetLiftedReturnTypeIfNecessary(flag, returnTypeWithAnnotations, state);
|
|
if (!flag || state.IsNotNull())
|
|
{
|
|
operandType = ((!state.IsNotNull() || !method.ReturnNotNullIfParameterNotNull.Contains(parameterSymbol.Name)) ? ApplyUnconditionalAnnotations(operandType, GetRValueAnnotations(method)) : operandType.WithNotNullState());
|
|
}
|
|
operandType = ClassifyAndVisitConversion(conversionOperand, TypeWithAnnotations.Create(conversion.BestUserDefinedConversionAnalysis.ToType), operandType, useLegacyWarnings, assignmentKind, parameterOpt, reportRemainingWarnings, fromExplicitCast: false, location);
|
|
operandType = ClassifyAndVisitConversion(conversionOpt ?? conversionOperand, targetTypeWithNullability, operandType, useLegacyWarnings, assignmentKind, parameterOpt, reportRemainingWarnings, conversionOpt?.ExplicitCastInCode ?? false, diagnosticLocation);
|
|
LearnFromPostConditions(conversionOperand, parameterAnnotations);
|
|
TrackAnalyzedNullabilityThroughConversionGroup(operandType, conversionOpt, conversionOperand);
|
|
return operandType;
|
|
}
|
|
|
|
private void SnapshotWalkerThroughConversionGroup(BoundExpression conversionExpression, BoundExpression convertedNode)
|
|
{
|
|
if (_snapshotBuilderOpt != null)
|
|
{
|
|
BoundConversion boundConversion = conversionExpression as BoundConversion;
|
|
_ = boundConversion?.ConversionGroupOpt;
|
|
while (boundConversion != null && boundConversion != convertedNode && boundConversion.Syntax.SpanStart != convertedNode.Syntax.SpanStart)
|
|
{
|
|
TakeIncrementalSnapshot(boundConversion);
|
|
boundConversion = boundConversion.Operand as BoundConversion;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void TrackAnalyzedNullabilityThroughConversionGroup(TypeWithState resultType, BoundConversion? conversionOpt, BoundExpression convertedNode)
|
|
{
|
|
VisitResult visitResult = new VisitResult(resultType, resultType.ToTypeWithAnnotations(compilation));
|
|
_ = conversionOpt?.ConversionGroupOpt;
|
|
while (conversionOpt != null && conversionOpt != convertedNode)
|
|
{
|
|
visitResult = withType(visitResult, conversionOpt.Type);
|
|
SetAnalyzedNullability(conversionOpt, visitResult);
|
|
conversionOpt = conversionOpt.Operand as BoundConversion;
|
|
}
|
|
static VisitResult withType(VisitResult visitResult2, TypeSymbol newType)
|
|
{
|
|
return new VisitResult(TypeWithState.Create(newType, visitResult2.RValueType.State), TypeWithAnnotations.Create(newType, visitResult2.LValueType.NullableAnnotation));
|
|
}
|
|
}
|
|
|
|
private TypeWithState GetLiftedReturnType(TypeWithAnnotations returnType, NullableFlowState operandState)
|
|
{
|
|
TypeSymbol type = (returnType.Type.IsNonNullableValueType() ? MakeNullableOf(returnType) : returnType.Type);
|
|
NullableFlowState defaultState = returnType.ToTypeWithState().State.Join(operandState);
|
|
return TypeWithState.Create(type, defaultState);
|
|
}
|
|
|
|
private static TypeWithState GetNullableUnderlyingTypeIfNecessary(bool isLifted, TypeWithState typeWithState)
|
|
{
|
|
if (isLifted)
|
|
{
|
|
TypeSymbol type = typeWithState.Type;
|
|
if ((object)type != null && type.IsNullableType())
|
|
{
|
|
return type.GetNullableUnderlyingTypeWithAnnotations().ToTypeWithState();
|
|
}
|
|
}
|
|
return typeWithState;
|
|
}
|
|
|
|
private TypeWithState GetLiftedReturnTypeIfNecessary(bool isLifted, TypeWithAnnotations returnType, NullableFlowState operandState)
|
|
{
|
|
if (!isLifted)
|
|
{
|
|
return returnType.ToTypeWithState();
|
|
}
|
|
return GetLiftedReturnType(returnType, operandState);
|
|
}
|
|
|
|
private TypeSymbol MakeNullableOf(TypeWithAnnotations underlying)
|
|
{
|
|
return compilation.GetSpecialType((SpecialType)32).Construct(ImmutableArray.Create(underlying));
|
|
}
|
|
|
|
private TypeWithState ClassifyAndVisitConversion(BoundExpression node, TypeWithAnnotations targetType, TypeWithState operandType, bool useLegacyWarnings, AssignmentKind assignmentKind, ParameterSymbol? parameterOpt, bool reportWarnings, bool fromExplicitCast, Location diagnosticLocation)
|
|
{
|
|
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
Conversion conversion = _conversions.ClassifyStandardConversion(operandType.Type, targetType.Type, ref useSiteInfo);
|
|
if (reportWarnings && !conversion.Exists)
|
|
{
|
|
if (assignmentKind == AssignmentKind.Argument)
|
|
{
|
|
ReportNullabilityMismatchInArgument(diagnosticLocation, operandType.Type, parameterOpt, targetType.Type, forOutput: false);
|
|
}
|
|
else
|
|
{
|
|
ReportNullabilityMismatchInAssignment(diagnosticLocation, operandType.Type, targetType.Type);
|
|
}
|
|
}
|
|
return VisitConversion(null, node, conversion, targetType, operandType, checkConversion: false, fromExplicitCast, useLegacyWarnings, assignmentKind, parameterOpt, reportWarnings, !fromExplicitCast && reportWarnings, extensionMethodThisArgument: false, default(Optional<LocalState>), trackMembers: false, diagnosticLocation);
|
|
}
|
|
|
|
public override BoundNode? VisitDelegateCreationExpression(BoundDelegateCreationExpression node)
|
|
{
|
|
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_008a: Invalid comparison between Unknown and I4
|
|
if (node.MethodOpt?.OriginalDefinition is LocalFunctionSymbol symbol)
|
|
{
|
|
VisitLocalFunctionUse(symbol);
|
|
}
|
|
NamedTypeSymbol delegateType = (NamedTypeSymbol)node.Type;
|
|
BoundExpression argument = node.Argument;
|
|
Action<NamedTypeSymbol> analysisCompletion;
|
|
if (!(argument is BoundMethodGroup boundMethodGroup))
|
|
{
|
|
if (!(argument is BoundLambda lambda))
|
|
{
|
|
if (argument != null)
|
|
{
|
|
TypeSymbol type = argument.Type;
|
|
if ((object)type != null && (int)type.TypeKind == 3)
|
|
{
|
|
analysisCompletion = visitDelegateArgument(delegateType, argument, node.WasTargetTyped);
|
|
goto IL_00ad;
|
|
}
|
|
}
|
|
VisitRvalue(node.Argument);
|
|
analysisCompletion = null;
|
|
}
|
|
else
|
|
{
|
|
analysisCompletion = visitLambdaArgument(delegateType, lambda, node.WasTargetTyped);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
analysisCompletion = visitMethodGroupArgument(node, delegateType, boundMethodGroup);
|
|
}
|
|
goto IL_00ad;
|
|
IL_00ad:
|
|
TypeWithState type2 = setAnalyzedNullability(node, delegateType, analysisCompletion, node.WasTargetTyped);
|
|
SetResultType(node, type2, updateAnalyzedNullability: false);
|
|
return null;
|
|
Action<NamedTypeSymbol>? analyzeDelegateConversion(NamedTypeSymbol namedTypeSymbol, BoundExpression arg, bool isTargetTyped)
|
|
{
|
|
if (isTargetTyped)
|
|
{
|
|
return analyzeDelegateConversionAsContinuation(arg);
|
|
}
|
|
TypeSymbol type3 = arg.Type;
|
|
if (!arg.IsSuppressed)
|
|
{
|
|
MethodSymbol delegateInvokeMethod = namedTypeSymbol.DelegateInvokeMethod;
|
|
if ((object)delegateInvokeMethod != null)
|
|
{
|
|
MethodSymbol methodSymbol = type3.DelegateInvokeMethod();
|
|
if ((object)methodSymbol != null)
|
|
{
|
|
ReportNullabilityMismatchWithTargetDelegate(arg.Syntax.Location, namedTypeSymbol, delegateInvokeMethod, methodSymbol, invokedAsExtensionMethod: false);
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
Action<NamedTypeSymbol> analyzeDelegateConversionAsContinuation(BoundExpression arg)
|
|
{
|
|
return delegate(NamedTypeSymbol delegateType2)
|
|
{
|
|
analyzeDelegateConversion(delegateType2, arg, isTargetTyped: false);
|
|
};
|
|
}
|
|
Action<NamedTypeSymbol>? analyzeLambdaConversion(NamedTypeSymbol namedTypeSymbol, BoundLambda boundLambda, bool isTargetTyped)
|
|
{
|
|
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
|
|
if (isTargetTyped)
|
|
{
|
|
return analyzeLambdaConversionAsContinuation(boundLambda);
|
|
}
|
|
VisitLambda(boundLambda, namedTypeSymbol);
|
|
if (!boundLambda.IsSuppressed)
|
|
{
|
|
ReportNullabilityMismatchWithTargetDelegate(boundLambda.Symbol.DiagnosticLocation, namedTypeSymbol, boundLambda);
|
|
}
|
|
return null;
|
|
}
|
|
Action<NamedTypeSymbol> analyzeLambdaConversionAsContinuation(BoundLambda lambda2)
|
|
{
|
|
return delegate(NamedTypeSymbol delegateType2)
|
|
{
|
|
analyzeLambdaConversion(delegateType2, lambda2, isTargetTyped: false);
|
|
};
|
|
}
|
|
Action<NamedTypeSymbol>? analyzeMethodGroupConversion(BoundDelegateCreationExpression boundDelegateCreationExpression, NamedTypeSymbol namedTypeSymbol, BoundMethodGroup group, bool isTargetTyped)
|
|
{
|
|
if (isTargetTyped)
|
|
{
|
|
return analyzeMethodGroupConversionAsContinuation(boundDelegateCreationExpression, group);
|
|
}
|
|
MethodSymbol methodOpt = boundDelegateCreationExpression.MethodOpt;
|
|
if ((object)methodOpt != null)
|
|
{
|
|
MethodSymbol delegateInvokeMethod = namedTypeSymbol.DelegateInvokeMethod;
|
|
if ((object)delegateInvokeMethod != null)
|
|
{
|
|
methodOpt = CheckMethodGroupReceiverNullability(group, delegateInvokeMethod.Parameters, methodOpt, boundDelegateCreationExpression.IsExtensionMethod);
|
|
if (!group.IsSuppressed)
|
|
{
|
|
ReportNullabilityMismatchWithTargetDelegate(group.Syntax.Location, namedTypeSymbol, delegateInvokeMethod, methodOpt, boundDelegateCreationExpression.IsExtensionMethod);
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
Action<NamedTypeSymbol>? analyzeMethodGroupConversionAsContinuation(BoundDelegateCreationExpression node2, BoundMethodGroup group)
|
|
{
|
|
return delegate(NamedTypeSymbol delegateType2)
|
|
{
|
|
analyzeMethodGroupConversion(node2, delegateType2, group, isTargetTyped: false);
|
|
};
|
|
}
|
|
TypeWithState setAnalyzedNullability(BoundDelegateCreationExpression boundDelegateCreationExpression, NamedTypeSymbol type3, Action<NamedTypeSymbol>? analysisCompletion2, bool isTargetTyped)
|
|
{
|
|
TypeWithState typeWithState = TypeWithState.Create(type3, NullableFlowState.NotNull);
|
|
if (isTargetTyped)
|
|
{
|
|
setAnalyzedNullabilityAsContinuation(boundDelegateCreationExpression, analysisCompletion2);
|
|
}
|
|
else
|
|
{
|
|
SetAnalyzedNullability(boundDelegateCreationExpression, typeWithState);
|
|
}
|
|
return typeWithState;
|
|
}
|
|
void setAnalyzedNullabilityAsContinuation(BoundDelegateCreationExpression boundDelegateCreationExpression, Action<NamedTypeSymbol>? action)
|
|
{
|
|
((Dictionary<BoundExpression, Func<TypeWithAnnotations, TypeWithState>>)(object)TargetTypedAnalysisCompletion)[(BoundExpression)boundDelegateCreationExpression] = delegate(TypeWithAnnotations resultTypeWithAnnotations)
|
|
{
|
|
NamedTypeSymbol namedTypeSymbol = (NamedTypeSymbol)resultTypeWithAnnotations.Type;
|
|
action?.Invoke(namedTypeSymbol);
|
|
return setAnalyzedNullability(boundDelegateCreationExpression, namedTypeSymbol, null, isTargetTyped: false);
|
|
};
|
|
}
|
|
Action<NamedTypeSymbol>? visitDelegateArgument(NamedTypeSymbol delegateType2, BoundExpression arg, bool isTargetTyped)
|
|
{
|
|
TypeWithAnnotations targetType = TypeWithAnnotations.Create(arg.Type, NullableAnnotation.NotAnnotated);
|
|
TypeWithState valueType = VisitRvalueWithState(arg);
|
|
ReportNullableAssignmentIfNecessary(arg, targetType, valueType, useLegacyWarnings: false);
|
|
LearnFromNonNullTest(arg, ref State);
|
|
return analyzeDelegateConversion(delegateType2, arg, isTargetTyped);
|
|
}
|
|
Action<NamedTypeSymbol>? visitLambdaArgument(NamedTypeSymbol delegateType2, BoundLambda boundLambda, bool isTargetTyped)
|
|
{
|
|
SetNotNullResult(boundLambda);
|
|
return analyzeLambdaConversion(delegateType2, boundLambda, isTargetTyped);
|
|
}
|
|
Action<NamedTypeSymbol>? visitMethodGroupArgument(BoundDelegateCreationExpression boundDelegateCreationExpression, NamedTypeSymbol delegateType2, BoundMethodGroup group)
|
|
{
|
|
VisitMethodGroup(group);
|
|
SetAnalyzedNullability(group, default(TypeWithState));
|
|
return analyzeMethodGroupConversion(boundDelegateCreationExpression, delegateType2, group, boundDelegateCreationExpression.WasTargetTyped);
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitMethodGroup(BoundMethodGroup node)
|
|
{
|
|
BoundExpression receiverOpt = node.ReceiverOpt;
|
|
if (receiverOpt != null)
|
|
{
|
|
VisitRvalue(receiverOpt);
|
|
SetMethodGroupReceiverNullability(receiverOpt, ResultType);
|
|
}
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
private bool TryGetMethodGroupReceiverNullability([NotNullWhen(true)] BoundExpression? receiverOpt, out TypeWithState type)
|
|
{
|
|
if (receiverOpt != null && _methodGroupReceiverMapOpt != null && ((Dictionary<BoundExpression, TypeWithState>)(object)_methodGroupReceiverMapOpt).TryGetValue(receiverOpt, out type))
|
|
{
|
|
return true;
|
|
}
|
|
type = default(TypeWithState);
|
|
return false;
|
|
}
|
|
|
|
private void SetMethodGroupReceiverNullability(BoundExpression receiver, TypeWithState type)
|
|
{
|
|
if (_methodGroupReceiverMapOpt == null)
|
|
{
|
|
_methodGroupReceiverMapOpt = PooledDictionary<BoundExpression, TypeWithState>.GetInstance();
|
|
}
|
|
((Dictionary<BoundExpression, TypeWithState>)(object)_methodGroupReceiverMapOpt)[receiver] = type;
|
|
}
|
|
|
|
private MethodSymbol CheckMethodGroupReceiverNullability(BoundMethodGroup group, ImmutableArray<ParameterSymbol> parameters, MethodSymbol method, bool invokedAsExtensionMethod)
|
|
{
|
|
BoundExpression receiverOpt = group.ReceiverOpt;
|
|
if (TryGetMethodGroupReceiverNullability(receiverOpt, out var type))
|
|
{
|
|
SyntaxNode syntax = group.Syntax;
|
|
if (!invokedAsExtensionMethod)
|
|
{
|
|
method = (MethodSymbol)AsMemberOfType(type.Type, method);
|
|
}
|
|
if (method.IsGenericMethod && HasImplicitTypeArguments(group.Syntax))
|
|
{
|
|
ArrayBuilder<BoundExpression> instance = ArrayBuilder<BoundExpression>.GetInstance();
|
|
if (invokedAsExtensionMethod)
|
|
{
|
|
instance.Add(CreatePlaceholderIfNecessary(receiverOpt, type.ToTypeWithAnnotations(compilation)));
|
|
}
|
|
ImmutableArray<ParameterSymbol>.Enumerator enumerator = parameters.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
ParameterSymbol current = enumerator.Current;
|
|
TypeWithAnnotations typeWithAnnotations = current.TypeWithAnnotations;
|
|
instance.Add((BoundExpression)new BoundExpressionWithNullability(syntax, new BoundParameter(syntax, current), typeWithAnnotations.NullableAnnotation, typeWithAnnotations.Type));
|
|
}
|
|
method = InferMethodTypeArguments(method, instance.ToImmutableAndFree(), default(ImmutableArray<RefKind>), default(ImmutableArray<int>), expanded: false);
|
|
}
|
|
if (invokedAsExtensionMethod)
|
|
{
|
|
CheckExtensionMethodThisNullability(receiverOpt, Conversion.Identity, method.Parameters[0], type);
|
|
}
|
|
else
|
|
{
|
|
CheckPossibleNullReceiver(receiverOpt, type, checkNullableValueType: false);
|
|
}
|
|
if (ConstraintsHelper.RequiresChecking(method))
|
|
{
|
|
CheckMethodConstraints(syntax, method);
|
|
}
|
|
}
|
|
return method;
|
|
}
|
|
|
|
public override BoundNode? VisitLambda(BoundLambda node)
|
|
{
|
|
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
|
|
if (!node.InAnonymousFunctionConversion)
|
|
{
|
|
VisitLambda(node, null);
|
|
}
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
private void VisitLambda(BoundLambda node, NamedTypeSymbol? delegateTypeOpt, Optional<LocalState> initialState = default(Optional<LocalState>))
|
|
{
|
|
MethodSymbol delegateInvokeMethod = delegateTypeOpt?.DelegateInvokeMethod;
|
|
UseDelegateInvokeParameterAndReturnTypes(node, delegateInvokeMethod, out var useDelegateInvokeParameterTypes, out var useDelegateInvokeReturnType);
|
|
if (useDelegateInvokeParameterTypes && _snapshotBuilderOpt != null)
|
|
{
|
|
SetUpdatedSymbol(node, node.Symbol, delegateTypeOpt);
|
|
}
|
|
AnalyzeLocalFunctionOrLambda(node, node.Symbol, initialState.HasValue ? initialState.Value : State.Clone(), delegateInvokeMethod, useDelegateInvokeParameterTypes, useDelegateInvokeReturnType);
|
|
}
|
|
|
|
private static void UseDelegateInvokeParameterAndReturnTypes(BoundLambda lambda, MethodSymbol? delegateInvokeMethod, out bool useDelegateInvokeParameterTypes, out bool useDelegateInvokeReturnType)
|
|
{
|
|
if ((object)delegateInvokeMethod == null)
|
|
{
|
|
useDelegateInvokeParameterTypes = false;
|
|
useDelegateInvokeReturnType = false;
|
|
}
|
|
else
|
|
{
|
|
UnboundLambda unboundLambda = lambda.UnboundLambda;
|
|
useDelegateInvokeParameterTypes = !unboundLambda.HasExplicitlyTypedParameterList;
|
|
useDelegateInvokeReturnType = !unboundLambda.HasExplicitReturnType(out var _, out var _);
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitUnboundLambda(UnboundLambda node)
|
|
{
|
|
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
|
|
BoundLambda node2 = node.BindForErrorRecovery();
|
|
VisitLambda(node2, null);
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitThisReference(BoundThisReference node)
|
|
{
|
|
VisitThisOrBaseReference(node);
|
|
return null;
|
|
}
|
|
|
|
private void VisitThisOrBaseReference(BoundExpression node)
|
|
{
|
|
TypeWithState resultType = TypeWithState.Create(node.Type, NullableFlowState.NotNull);
|
|
TypeWithAnnotations lvalueType = TypeWithAnnotations.Create(node.Type, NullableAnnotation.NotAnnotated);
|
|
SetResult(node, resultType, lvalueType);
|
|
}
|
|
|
|
public override BoundNode? VisitParameter(BoundParameter node)
|
|
{
|
|
ParameterSymbol parameterSymbol = node.ParameterSymbol;
|
|
int orCreateSlot = GetOrCreateSlot(parameterSymbol);
|
|
TypeWithAnnotations declaredParameterResult = GetDeclaredParameterResult(parameterSymbol);
|
|
TypeWithState parameterState = GetParameterState(declaredParameterResult, parameterSymbol.FlowAnalysisAnnotations);
|
|
SetResult(node, GetAdjustedResult(parameterState, orCreateSlot), declaredParameterResult);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitAssignmentOperator(BoundAssignmentOperator node)
|
|
{
|
|
BoundExpression boundExpression = node.Left;
|
|
if (boundExpression is BoundFieldAccess { ExpressionSymbol: FieldSymbol { AssociatedSymbol: var associatedSymbol } } boundFieldAccess)
|
|
{
|
|
if (!(associatedSymbol is PropertySymbol propertySymbol))
|
|
{
|
|
if (associatedSymbol is EventSymbol eventSymbol)
|
|
{
|
|
BoundFieldAccess boundFieldAccess2 = boundFieldAccess;
|
|
boundExpression = new BoundEventAccess(boundFieldAccess2.Syntax, boundFieldAccess2.ReceiverOpt, eventSymbol, isUsableAsField: true, LookupResultKind.Viable, eventSymbol.Type, boundFieldAccess2.HasErrors);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
boundExpression = new BoundPropertyAccess(boundFieldAccess.Syntax, boundFieldAccess.ReceiverOpt, (ThreeState)0, propertySymbol, LookupResultKind.Viable, propertySymbol.Type, boundFieldAccess.HasErrors);
|
|
}
|
|
}
|
|
BoundExpression right = node.Right;
|
|
VisitLValue(boundExpression);
|
|
Unsplit();
|
|
FlowAnalysisAnnotations lValueAnnotations = GetLValueAnnotations(boundExpression);
|
|
TypeWithAnnotations typeWithAnnotations = ApplyLValueAnnotations(LvalueResultType, lValueAnnotations);
|
|
if (boundExpression.Kind == BoundKind.EventAccess && ((BoundEventAccess)boundExpression).EventSymbol.IsWindowsRuntimeEvent)
|
|
{
|
|
VisitRvalue(right);
|
|
SetNotNullResult(node);
|
|
}
|
|
else
|
|
{
|
|
TypeWithState rightState;
|
|
if (!node.IsRef)
|
|
{
|
|
bool flag = boundExpression is BoundDiscardExpression;
|
|
rightState = VisitOptionalImplicitConversion(right, flag ? default(TypeWithAnnotations) : typeWithAnnotations, UseLegacyWarnings(boundExpression), trackMembers: true, AssignmentKind.Assignment);
|
|
Unsplit();
|
|
}
|
|
else
|
|
{
|
|
rightState = VisitRefExpression(right, typeWithAnnotations);
|
|
}
|
|
CheckDisallowedNullAssignment(rightState, lValueAnnotations, right.Syntax);
|
|
AdjustSetValue(boundExpression, ref rightState);
|
|
TrackNullableStateForAssignment(right, typeWithAnnotations, MakeSlot(boundExpression), rightState, MakeSlot(right));
|
|
if (boundExpression is BoundDiscardExpression)
|
|
{
|
|
TypeWithAnnotations lvalueType = rightState.ToTypeWithAnnotations(compilation);
|
|
SetResult(boundExpression, rightState, lvalueType, updateAnalyzedNullability: true, true);
|
|
SetResult(node, rightState, lvalueType);
|
|
}
|
|
else
|
|
{
|
|
SetResult(node, TypeWithState.Create(typeWithAnnotations.Type, rightState.State), typeWithAnnotations);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private bool IsPropertyOutputMoreStrictThanInput(PropertySymbol property)
|
|
{
|
|
TypeWithAnnotations typeWithAnnotations = property.TypeWithAnnotations;
|
|
FlowAnalysisAnnotations flowAnalysisAnnotations = ((!IsAnalyzingAttribute) ? property.GetFlowAnalysisAnnotations() : FlowAnalysisAnnotations.None);
|
|
TypeWithAnnotations typeWithAnnotations2 = ApplyLValueAnnotations(typeWithAnnotations, flowAnalysisAnnotations);
|
|
if (typeWithAnnotations2.NullableAnnotation.IsOblivious() || !typeWithAnnotations2.CanBeAssignedNull)
|
|
{
|
|
return false;
|
|
}
|
|
return ApplyUnconditionalAnnotations(typeWithAnnotations.ToTypeWithState(), flowAnalysisAnnotations).IsNotNull;
|
|
}
|
|
|
|
private void AdjustSetValue(BoundExpression left, ref TypeWithState rightState)
|
|
{
|
|
PropertySymbol propertySymbol = ((left is BoundPropertyAccess boundPropertyAccess) ? boundPropertyAccess.PropertySymbol : ((!(left is BoundIndexerAccess boundIndexerAccess)) ? null : boundIndexerAccess.Indexer));
|
|
PropertySymbol propertySymbol2 = propertySymbol;
|
|
if ((object)propertySymbol2 != null && IsPropertyOutputMoreStrictThanInput(propertySymbol2))
|
|
{
|
|
rightState = rightState.WithNotNullState();
|
|
}
|
|
}
|
|
|
|
private FlowAnalysisAnnotations GetLValueAnnotations(BoundExpression expr)
|
|
{
|
|
if (IsAnalyzingAttribute)
|
|
{
|
|
return FlowAnalysisAnnotations.None;
|
|
}
|
|
FlowAnalysisAnnotations flowAnalysisAnnotations;
|
|
if (!(expr is BoundPropertyAccess boundPropertyAccess))
|
|
{
|
|
if (!(expr is BoundIndexerAccess boundIndexerAccess))
|
|
{
|
|
if (!(expr is BoundFieldAccess boundFieldAccess))
|
|
{
|
|
if (expr is BoundParameter boundParameter)
|
|
{
|
|
ParameterSymbol parameterSymbol = boundParameter.ParameterSymbol;
|
|
if ((object)parameterSymbol != null)
|
|
{
|
|
flowAnalysisAnnotations = ToInwardAnnotations(GetParameterAnnotations(parameterSymbol) & ~FlowAnalysisAnnotations.NotNull);
|
|
goto IL_0084;
|
|
}
|
|
}
|
|
flowAnalysisAnnotations = FlowAnalysisAnnotations.None;
|
|
}
|
|
else
|
|
{
|
|
flowAnalysisAnnotations = GetFieldAnnotations(boundFieldAccess.FieldSymbol);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flowAnalysisAnnotations = boundIndexerAccess.Indexer.GetFlowAnalysisAnnotations();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flowAnalysisAnnotations = boundPropertyAccess.PropertySymbol.GetFlowAnalysisAnnotations();
|
|
}
|
|
goto IL_0084;
|
|
IL_0084:
|
|
return flowAnalysisAnnotations & (FlowAnalysisAnnotations.AllowNull | FlowAnalysisAnnotations.DisallowNull);
|
|
}
|
|
|
|
private static FlowAnalysisAnnotations GetFieldAnnotations(FieldSymbol field)
|
|
{
|
|
if (!(field.AssociatedSymbol is PropertySymbol property))
|
|
{
|
|
return field.FlowAnalysisAnnotations;
|
|
}
|
|
return property.GetFlowAnalysisAnnotations();
|
|
}
|
|
|
|
private FlowAnalysisAnnotations GetObjectInitializerMemberLValueAnnotations(Symbol memberSymbol)
|
|
{
|
|
if (IsAnalyzingAttribute)
|
|
{
|
|
return FlowAnalysisAnnotations.None;
|
|
}
|
|
FlowAnalysisAnnotations flowAnalysisAnnotations = ((memberSymbol is PropertySymbol property) ? property.GetFlowAnalysisAnnotations() : ((memberSymbol is FieldSymbol field) ? GetFieldAnnotations(field) : FlowAnalysisAnnotations.None));
|
|
return flowAnalysisAnnotations & (FlowAnalysisAnnotations.AllowNull | FlowAnalysisAnnotations.DisallowNull);
|
|
}
|
|
|
|
private static FlowAnalysisAnnotations ToInwardAnnotations(FlowAnalysisAnnotations outwardAnnotations)
|
|
{
|
|
FlowAnalysisAnnotations flowAnalysisAnnotations = FlowAnalysisAnnotations.None;
|
|
if ((outwardAnnotations & FlowAnalysisAnnotations.MaybeNull) != FlowAnalysisAnnotations.None)
|
|
{
|
|
flowAnalysisAnnotations |= FlowAnalysisAnnotations.AllowNull;
|
|
}
|
|
if ((outwardAnnotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull)
|
|
{
|
|
flowAnalysisAnnotations |= FlowAnalysisAnnotations.DisallowNull;
|
|
}
|
|
return flowAnalysisAnnotations;
|
|
}
|
|
|
|
private static bool UseLegacyWarnings(BoundExpression expr)
|
|
{
|
|
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0020: Invalid comparison between Unknown and I4
|
|
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0042: Invalid comparison between Unknown and I4
|
|
if (expr is BoundLocal boundLocal)
|
|
{
|
|
LocalSymbol localSymbol = boundLocal.LocalSymbol;
|
|
if ((object)localSymbol != null && (int)localSymbol.RefKind == 0)
|
|
{
|
|
goto IL_0061;
|
|
}
|
|
}
|
|
else if (expr is BoundParameter boundParameter)
|
|
{
|
|
ParameterSymbol parameterSymbol = boundParameter.ParameterSymbol;
|
|
if ((object)parameterSymbol != null && (int)parameterSymbol.RefKind == 0 && (!(parameterSymbol.ContainingSymbol is SynthesizedPrimaryConstructor synthesizedPrimaryConstructor) || !synthesizedPrimaryConstructor.GetCapturedParameters().ContainsKey(parameterSymbol)))
|
|
{
|
|
goto IL_0061;
|
|
}
|
|
}
|
|
return false;
|
|
IL_0061:
|
|
return true;
|
|
}
|
|
|
|
public override BoundNode? VisitDeconstructionAssignmentOperator(BoundDeconstructionAssignmentOperator node)
|
|
{
|
|
return VisitDeconstructionAssignmentOperator(node, null);
|
|
}
|
|
|
|
private BoundNode? VisitDeconstructionAssignmentOperator(BoundDeconstructionAssignmentOperator node, TypeWithState? rightResultOpt)
|
|
{
|
|
bool disableNullabilityAnalysis = _disableNullabilityAnalysis;
|
|
_disableNullabilityAnalysis = true;
|
|
BoundTupleExpression left = node.Left;
|
|
BoundConversion right = node.Right;
|
|
ArrayBuilder<DeconstructionVariable> deconstructionAssignmentVariables = GetDeconstructionAssignmentVariables(left);
|
|
if (node.HasErrors)
|
|
{
|
|
VisitRvalue(right.Operand);
|
|
}
|
|
else
|
|
{
|
|
VisitDeconstructionArguments(deconstructionAssignmentVariables, right.Conversion, right.Operand, rightResultOpt);
|
|
}
|
|
ArrayBuilderExtensions.FreeAll<DeconstructionVariable>(deconstructionAssignmentVariables, (Func<DeconstructionVariable, ArrayBuilder<DeconstructionVariable>>)((DeconstructionVariable v) => v.NestedVariables));
|
|
SetNotNullResult(node);
|
|
_disableNullabilityAnalysis = disableNullabilityAnalysis;
|
|
return null;
|
|
}
|
|
|
|
private void VisitDeconstructionArguments(ArrayBuilder<DeconstructionVariable> variables, Conversion conversion, BoundExpression right, TypeWithState? rightResultOpt = null)
|
|
{
|
|
if (!conversion.DeconstructionInfo.IsDefault)
|
|
{
|
|
VisitDeconstructMethodArguments(variables, conversion, right, rightResultOpt);
|
|
}
|
|
else
|
|
{
|
|
VisitTupleDeconstructionArguments(variables, conversion.DeconstructConversionInfo, right, rightResultOpt);
|
|
}
|
|
}
|
|
|
|
private void VisitDeconstructMethodArguments(ArrayBuilder<DeconstructionVariable> variables, Conversion conversion, BoundExpression right, TypeWithState? rightResultOpt)
|
|
{
|
|
//IL_0238: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0268: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_026e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
|
|
VisitRvalue(right);
|
|
if (rightResultOpt.HasValue)
|
|
{
|
|
SetResultType(right, rightResultOpt.Value);
|
|
}
|
|
TypeWithState resultType = ResultType;
|
|
BoundCall boundCall = conversion.DeconstructionInfo.Invocation as BoundCall;
|
|
MethodSymbol methodSymbol = boundCall?.Method;
|
|
if ((object)methodSymbol == null)
|
|
{
|
|
return;
|
|
}
|
|
int count = variables.Count;
|
|
if (!boundCall.InvokedAsExtensionMethod)
|
|
{
|
|
CheckPossibleNullReceiver(right);
|
|
if (methodSymbol.OriginalDefinition != methodSymbol)
|
|
{
|
|
methodSymbol = (MethodSymbol)AsMemberOfType(resultType.Type, methodSymbol);
|
|
}
|
|
}
|
|
else if (methodSymbol.IsGenericMethod)
|
|
{
|
|
ArrayBuilder<BoundExpression> instance = ArrayBuilder<BoundExpression>.GetInstance(count + 1);
|
|
instance.Add(CreatePlaceholderIfNecessary(right, resultType.ToTypeWithAnnotations(compilation)));
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
instance.Add((BoundExpression)new BoundExpressionWithNullability(variables[i].Expression.Syntax, variables[i].Expression, NullableAnnotation.Oblivious, conversion.DeconstructionInfo.OutputPlaceholders[i].Type));
|
|
}
|
|
methodSymbol = InferMethodTypeArguments(methodSymbol, instance.ToImmutableAndFree(), boundCall.ArgumentRefKindsOpt, boundCall.ArgsToParamsOpt, boundCall.Expanded);
|
|
if (ConstraintsHelper.RequiresChecking(methodSymbol))
|
|
{
|
|
CheckMethodConstraints(boundCall.Syntax, methodSymbol);
|
|
}
|
|
}
|
|
ImmutableArray<ParameterSymbol> parameters = methodSymbol.Parameters;
|
|
int num = (boundCall.InvokedAsExtensionMethod ? 1 : 0);
|
|
if (boundCall.InvokedAsExtensionMethod)
|
|
{
|
|
Conversion item = RemoveConversion(boundCall.Arguments[0], includeExplicitConversions: false).conversion;
|
|
CheckExtensionMethodThisNullability(right, item, methodSymbol.Parameters[0], resultType);
|
|
}
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
DeconstructionVariable deconstructionVariable = variables[j];
|
|
ParameterSymbol parameterSymbol = parameters[j + num];
|
|
(BoundValuePlaceholder? placeholder, BoundExpression? conversion) tuple = conversion.DeconstructConversionInfo[j];
|
|
Conversion conversion2 = BoundNode.GetConversion(placeholder: tuple.placeholder, conversion: tuple.conversion);
|
|
ArrayBuilder<DeconstructionVariable> nestedVariables = deconstructionVariable.NestedVariables;
|
|
if (nestedVariables != null)
|
|
{
|
|
BoundExpression right2 = CreatePlaceholderIfNecessary(boundCall.Arguments[j + num], parameterSymbol.TypeWithAnnotations);
|
|
VisitDeconstructionArguments(nestedVariables, conversion2, right2);
|
|
}
|
|
else
|
|
{
|
|
VisitArgumentConversionAndInboundAssignmentsAndPreConditions(null, deconstructionVariable.Expression, conversion2, parameterSymbol.RefKind, parameterSymbol, parameterSymbol.TypeWithAnnotations, GetParameterAnnotations(parameterSymbol), new VisitArgumentResult(new VisitResult(deconstructionVariable.Type.ToTypeWithState(), deconstructionVariable.Type), default(Optional<LocalState>)), null, extensionMethodThisArgument: false);
|
|
}
|
|
}
|
|
for (int k = 0; k < count; k++)
|
|
{
|
|
DeconstructionVariable deconstructionVariable2 = variables[k];
|
|
ParameterSymbol parameterSymbol2 = parameters[k + num];
|
|
if (deconstructionVariable2.NestedVariables == null)
|
|
{
|
|
VisitArgumentOutboundAssignmentsAndPostConditions(deconstructionVariable2.Expression, parameterSymbol2.RefKind, parameterSymbol2, parameterSymbol2.TypeWithAnnotations, GetRValueAnnotations(parameterSymbol2), new VisitArgumentResult(new VisitResult(deconstructionVariable2.Type.ToTypeWithState(), deconstructionVariable2.Type), default(Optional<LocalState>)), null, default(CompareExchangeInfo));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void VisitTupleDeconstructionArguments(ArrayBuilder<DeconstructionVariable> variables, ImmutableArray<(BoundValuePlaceholder? placeholder, BoundExpression? conversion)> deconstructConversionInfo, BoundExpression right, TypeWithState? rightResultOpt)
|
|
{
|
|
//IL_0129: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
|
|
int count = variables.Count;
|
|
ImmutableArray<BoundExpression> deconstructionRightParts = GetDeconstructionRightParts(right, rightResultOpt);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
DeconstructionVariable deconstructionVariable = variables[i];
|
|
(BoundValuePlaceholder? placeholder, BoundExpression? conversion) tuple = deconstructConversionInfo[i];
|
|
Conversion conversion = BoundNode.GetConversion(placeholder: tuple.placeholder, conversion: tuple.conversion);
|
|
BoundExpression boundExpression = deconstructionRightParts[i];
|
|
ArrayBuilder<DeconstructionVariable> nestedVariables = deconstructionVariable.NestedVariables;
|
|
if (nestedVariables != null)
|
|
{
|
|
VisitDeconstructionArguments(nestedVariables, conversion, boundExpression);
|
|
continue;
|
|
}
|
|
TypeWithAnnotations type = deconstructionVariable.Type;
|
|
FlowAnalysisAnnotations lValueAnnotations = GetLValueAnnotations(deconstructionVariable.Expression);
|
|
type = ApplyLValueAnnotations(type, lValueAnnotations);
|
|
TypeWithState rightState;
|
|
TypeWithState operandType;
|
|
int valueSlot;
|
|
if (conversion.IsIdentity)
|
|
{
|
|
if (deconstructionVariable.Expression is BoundLocal { DeclarationKind: BoundLocalDeclarationKind.WithInferredType } boundLocal)
|
|
{
|
|
rightState = (operandType = VisitRvalueWithState(boundExpression));
|
|
_variables.SetType(boundLocal.LocalSymbol, operandType.ToAnnotatedTypeWithAnnotations(compilation));
|
|
}
|
|
else
|
|
{
|
|
operandType = default(TypeWithState);
|
|
rightState = VisitOptionalImplicitConversion(boundExpression, type, useLegacyWarnings: true, trackMembers: true, AssignmentKind.Assignment);
|
|
Unsplit();
|
|
}
|
|
valueSlot = MakeSlot(boundExpression);
|
|
}
|
|
else
|
|
{
|
|
operandType = VisitRvalueWithState(boundExpression);
|
|
rightState = VisitConversion(null, boundExpression, conversion, type, operandType, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: true, AssignmentKind.Assignment);
|
|
valueSlot = -1;
|
|
}
|
|
CheckDisallowedNullAssignment(rightState, lValueAnnotations, right.Syntax);
|
|
int num = MakeSlot(deconstructionVariable.Expression);
|
|
AdjustSetValue(deconstructionVariable.Expression, ref rightState);
|
|
TrackNullableStateForAssignment(boundExpression, type, num, rightState, valueSlot);
|
|
if (num > 0 && conversion.Kind == ConversionKind.ImplicitNullable && AreNullableAndUnderlyingTypes(type.Type, operandType.Type, out var underlyingTypeWithAnnotations))
|
|
{
|
|
valueSlot = MakeSlot(boundExpression);
|
|
if (valueSlot > 0)
|
|
{
|
|
TypeWithState valueType = TypeWithState.Create(underlyingTypeWithAnnotations.Type, NullableFlowState.NotNull);
|
|
TrackNullableStateOfNullableValue(num, type.Type, boundExpression, valueType, valueSlot);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private ArrayBuilder<DeconstructionVariable> GetDeconstructionAssignmentVariables(BoundTupleExpression tuple)
|
|
{
|
|
ImmutableArray<BoundExpression> arguments = tuple.Arguments;
|
|
ArrayBuilder<DeconstructionVariable> instance = ArrayBuilder<DeconstructionVariable>.GetInstance(arguments.Length);
|
|
ImmutableArray<BoundExpression>.Enumerator enumerator = arguments.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current = enumerator.Current;
|
|
instance.Add(getDeconstructionAssignmentVariable(current));
|
|
}
|
|
return instance;
|
|
DeconstructionVariable getDeconstructionAssignmentVariable(BoundExpression expr)
|
|
{
|
|
BoundKind kind = expr.Kind;
|
|
if (kind - 168 <= BoundKind.PropertyEqualsValue)
|
|
{
|
|
return new DeconstructionVariable(expr, GetDeconstructionAssignmentVariables((BoundTupleExpression)expr));
|
|
}
|
|
VisitLValue(expr);
|
|
return new DeconstructionVariable(expr, LvalueResultType);
|
|
}
|
|
}
|
|
|
|
private ImmutableArray<BoundExpression> GetDeconstructionRightParts(BoundExpression expr, TypeWithState? rightResultOpt)
|
|
{
|
|
switch (expr.Kind)
|
|
{
|
|
case BoundKind.TupleLiteral:
|
|
case BoundKind.ConvertedTupleLiteral:
|
|
return ((BoundTupleExpression)expr).Arguments;
|
|
case BoundKind.Conversion:
|
|
{
|
|
BoundConversion boundConversion = (BoundConversion)expr;
|
|
ConversionKind conversionKind = boundConversion.ConversionKind;
|
|
if (conversionKind == ConversionKind.Identity || conversionKind == ConversionKind.ImplicitTupleLiteral)
|
|
{
|
|
return GetDeconstructionRightParts(boundConversion.Operand, null);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (rightResultOpt.HasValue)
|
|
{
|
|
expr = CreatePlaceholderIfNecessary(expr, rightResultOpt.GetValueOrDefault().ToTypeWithAnnotations(compilation));
|
|
}
|
|
if (expr.Type is NamedTypeSymbol { IsTupleType: not false } namedTypeSymbol)
|
|
{
|
|
return ImmutableArrayExtensions.SelectAsArray<FieldSymbol, BoundExpression, BoundExpression>(namedTypeSymbol.TupleElements, (Func<FieldSymbol, BoundExpression, BoundExpression>)((FieldSymbol f, BoundExpression e) => new BoundFieldAccess(e.Syntax, e, f, null)), expr);
|
|
}
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs", 9797);
|
|
}
|
|
|
|
public override BoundNode? VisitIncrementOperator(BoundIncrementOperator node)
|
|
{
|
|
//IL_010f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0115: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0172: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0178: Unknown result type (might be due to invalid IL or missing references)
|
|
TypeWithState typeWithState = VisitRvalueWithState(node.Operand);
|
|
TypeWithAnnotations lvalueResultType = LvalueResultType;
|
|
bool flag = false;
|
|
object obj;
|
|
if (State.Reachable)
|
|
{
|
|
if (node.OperatorKind.IsUserDefined())
|
|
{
|
|
MethodSymbol? methodOpt = node.MethodOpt;
|
|
if ((object)methodOpt != null && methodOpt.ParameterCount == 1)
|
|
{
|
|
obj = node.MethodOpt;
|
|
goto IL_0053;
|
|
}
|
|
}
|
|
obj = null;
|
|
goto IL_0053;
|
|
}
|
|
goto IL_01d5;
|
|
IL_01d5:
|
|
if (!flag)
|
|
{
|
|
SetNotNullResult(node);
|
|
}
|
|
return null;
|
|
IL_0053:
|
|
MethodSymbol methodSymbol = (MethodSymbol)obj;
|
|
AssignmentKind assignmentKind = AssignmentKind.Assignment;
|
|
ParameterSymbol parameterOpt = null;
|
|
TypeWithAnnotations targetTypeWithNullability;
|
|
if (node.OperandConversion is BoundConversion { Conversion: { IsUserDefined: not false } conversion })
|
|
{
|
|
MethodSymbol? method = conversion.Method;
|
|
if ((object)method != null && method.ParameterCount == 1)
|
|
{
|
|
targetTypeWithNullability = conversion.Method.ReturnTypeWithAnnotations;
|
|
goto IL_00de;
|
|
}
|
|
}
|
|
if ((object)methodSymbol != null)
|
|
{
|
|
targetTypeWithNullability = methodSymbol.Parameters[0].TypeWithAnnotations;
|
|
assignmentKind = AssignmentKind.Argument;
|
|
parameterOpt = methodSymbol.Parameters[0];
|
|
}
|
|
else
|
|
{
|
|
targetTypeWithNullability = default(TypeWithAnnotations);
|
|
}
|
|
goto IL_00de;
|
|
IL_00de:
|
|
TypeWithState typeWithState2 = ((!targetTypeWithNullability.HasType) ? typeWithState : VisitConversion(null, node.Operand, BoundNode.GetConversion(node.OperandConversion, node.OperandPlaceholder), targetTypeWithNullability, typeWithState, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, assignmentKind, parameterOpt));
|
|
TypeWithState operandType = methodSymbol?.ReturnTypeWithAnnotations.ToTypeWithState() ?? typeWithState2;
|
|
TypeWithAnnotations targetTypeWithNullability2 = typeWithState.ToTypeWithAnnotations(compilation);
|
|
operandType = VisitConversion(null, node, BoundNode.GetConversion(node.ResultConversion, node.ResultPlaceholder), targetTypeWithNullability2, operandType, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment);
|
|
if (!node.HasErrors)
|
|
{
|
|
UnaryOperatorKind unaryOperatorKind = node.OperatorKind.Operator();
|
|
TypeWithState type = ((unaryOperatorKind == UnaryOperatorKind.PrefixIncrement || unaryOperatorKind == UnaryOperatorKind.PrefixDecrement) ? operandType : typeWithState);
|
|
SetResultType(node, type);
|
|
flag = true;
|
|
TrackNullableStateForAssignment(node, lvalueResultType, MakeSlot(node.Operand), operandType);
|
|
}
|
|
goto IL_01d5;
|
|
}
|
|
|
|
public override BoundNode? VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
|
|
{
|
|
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0128: Unknown result type (might be due to invalid IL or missing references)
|
|
BoundExpression left = node.Left;
|
|
BoundExpression right = node.Right;
|
|
Visit(left);
|
|
TypeWithAnnotations typeWithAnnotations = LvalueResultType;
|
|
TypeWithState resultType = ResultType;
|
|
TypeWithState adjustedResult = GetAdjustedResult(resultType, MakeSlot(node.Left));
|
|
adjustedResult = (((object)node.Operator.LeftType == null) ? default(TypeWithState) : VisitConversion(null, node.Left, BoundNode.GetConversion(node.LeftConversion, node.LeftPlaceholder), TypeWithAnnotations.Create(node.Operator.LeftType), adjustedResult, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, null, reportTopLevelWarnings: false));
|
|
TypeWithState rightType = VisitRvalueWithState(right);
|
|
TypeWithState operandType;
|
|
if ((object)node.Operator.ReturnType != null)
|
|
{
|
|
if (node.Operator.Kind.IsUserDefined() && (object)node.Operator.Method != null && node.Operator.Method.ParameterCount == 2)
|
|
{
|
|
MethodSymbol method = node.Operator.Method;
|
|
VisitArguments(node, ImmutableArray.Create(node.Left, right), method.ParameterRefKinds, method.Parameters, default(ImmutableArray<int>), default(BitVector), expanded: true, invokedAsExtensionMethod: false, method);
|
|
}
|
|
operandType = InferResultNullability(node.Operator.Kind, node.Operator.Method, node.Operator.ReturnType, adjustedResult, rightType);
|
|
FlowAnalysisAnnotations lValueAnnotations = GetLValueAnnotations(node.Left);
|
|
typeWithAnnotations = ApplyLValueAnnotations(typeWithAnnotations, lValueAnnotations);
|
|
operandType = VisitConversion(null, node, BoundNode.GetConversion(node.FinalConversion, node.FinalPlaceholder), typeWithAnnotations, operandType, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment);
|
|
CheckDisallowedNullAssignment(operandType, lValueAnnotations, node.Syntax);
|
|
}
|
|
else
|
|
{
|
|
operandType = TypeWithState.Create(node.Type, NullableFlowState.NotNull);
|
|
}
|
|
AdjustSetValue(left, ref operandType);
|
|
TrackNullableStateForAssignment(node, typeWithAnnotations, MakeSlot(node.Left), operandType);
|
|
SetResultType(node, operandType);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitFixedLocalCollectionInitializer(BoundFixedLocalCollectionInitializer node)
|
|
{
|
|
BoundExpression boundExpression = node.Expression;
|
|
if (boundExpression.Kind == BoundKind.AddressOfOperator)
|
|
{
|
|
boundExpression = ((BoundAddressOfOperator)boundExpression).Operand;
|
|
}
|
|
VisitRvalue(boundExpression);
|
|
if (node.Expression.Kind == BoundKind.AddressOfOperator)
|
|
{
|
|
SetResultType(node.Expression, TypeWithState.Create(node.Expression.Type, ResultType.State));
|
|
}
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitAddressOfOperator(BoundAddressOfOperator node)
|
|
{
|
|
Visit(node.Operand);
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
private void ReportArgumentWarnings(BoundExpression argument, TypeWithState argumentType, ParameterSymbol parameter)
|
|
{
|
|
TypeWithAnnotations typeWithAnnotations = parameter.TypeWithAnnotations;
|
|
ReportNullableAssignmentIfNecessary(argument, typeWithAnnotations, argumentType, useLegacyWarnings: false, AssignmentKind.Argument, parameter);
|
|
TypeSymbol type = argumentType.Type;
|
|
if ((object)type != null && IsNullabilityMismatch(typeWithAnnotations.Type, type))
|
|
{
|
|
ReportNullabilityMismatchInArgument(argument.Syntax, type, parameter, typeWithAnnotations.Type, forOutput: false);
|
|
}
|
|
}
|
|
|
|
private void ReportNullabilityMismatchInRefArgument(BoundExpression argument, TypeSymbol argumentType, ParameterSymbol parameter, TypeSymbol parameterType)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, argument.Syntax, argumentType, parameterType, GetParameterAsDiagnosticArgument(parameter), GetContainingSymbolAsDiagnosticArgument(parameter));
|
|
}
|
|
|
|
private void ReportNullabilityMismatchInArgument(SyntaxNode argument, TypeSymbol argumentType, ParameterSymbol parameter, TypeSymbol parameterType, bool forOutput)
|
|
{
|
|
ReportNullabilityMismatchInArgument(argument.GetLocation(), argumentType, parameter, parameterType, forOutput);
|
|
}
|
|
|
|
private void ReportNullabilityMismatchInArgument(Location argumentLocation, TypeSymbol argumentType, ParameterSymbol? parameterOpt, TypeSymbol parameterType, bool forOutput)
|
|
{
|
|
ReportDiagnostic(forOutput ? ErrorCode.WRN_NullabilityMismatchInArgumentForOutput : ErrorCode.WRN_NullabilityMismatchInArgument, argumentLocation, argumentType, ((object)parameterOpt != null && parameterOpt.Type.IsNonNullableValueType() && parameterType.IsNullableType()) ? parameterOpt.Type : parameterType, GetParameterAsDiagnosticArgument(parameterOpt), GetContainingSymbolAsDiagnosticArgument(parameterOpt));
|
|
}
|
|
|
|
private TypeWithAnnotations GetDeclaredLocalResult(LocalSymbol local)
|
|
{
|
|
if (!_variables.TryGetType(local, out var type))
|
|
{
|
|
return local.TypeWithAnnotations;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter)
|
|
{
|
|
if (!_variables.TryGetType(parameter, out var type))
|
|
{
|
|
return parameter.TypeWithAnnotations;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
public override BoundNode? VisitBaseReference(BoundBaseReference node)
|
|
{
|
|
VisitThisOrBaseReference(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitFieldAccess(BoundFieldAccess node)
|
|
{
|
|
Symbol updatedSymbol = VisitMemberAccess(node, node.ReceiverOpt, node.FieldSymbol);
|
|
SplitIfBooleanConstant(node);
|
|
SetUpdatedSymbol(node, node.FieldSymbol, updatedSymbol);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitPropertyAccess(BoundPropertyAccess node)
|
|
{
|
|
PropertySymbol propertySymbol = node.PropertySymbol;
|
|
Symbol updatedSymbol = VisitMemberAccess(node, node.ReceiverOpt, propertySymbol);
|
|
if (!IsAnalyzingAttribute)
|
|
{
|
|
if (_expressionIsRead)
|
|
{
|
|
ApplyMemberPostConditions(node.ReceiverOpt, propertySymbol.GetMethod);
|
|
}
|
|
else
|
|
{
|
|
ApplyMemberPostConditions(node.ReceiverOpt, propertySymbol.SetMethod);
|
|
}
|
|
}
|
|
SetUpdatedSymbol(node, propertySymbol, updatedSymbol);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitIndexerAccess(BoundIndexerAccess node)
|
|
{
|
|
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
|
|
BoundExpression receiverOpt = node.ReceiverOpt;
|
|
TypeSymbol type = VisitRvalueWithState(receiverOpt).Type;
|
|
CheckPossibleNullReceiver(receiverOpt);
|
|
PropertySymbol propertySymbol = node.Indexer;
|
|
if ((object)type != null)
|
|
{
|
|
propertySymbol = (PropertySymbol)AsMemberOfType(type, propertySymbol);
|
|
}
|
|
VisitArguments(node, node.Arguments, node.ArgumentRefKindsOpt, propertySymbol, node.ArgsToParamsOpt, node.DefaultArguments, node.Expanded);
|
|
TypeWithState resultType = ApplyUnconditionalAnnotations(propertySymbol.TypeWithAnnotations.ToTypeWithState(), GetRValueAnnotations(propertySymbol));
|
|
SetResult(node, resultType, propertySymbol.TypeWithAnnotations);
|
|
SetUpdatedSymbol(node, node.Indexer, propertySymbol);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node)
|
|
{
|
|
VisitRvalue(node.Receiver);
|
|
VisitResult visitResult = _visitResult;
|
|
VisitRvalue(node.Argument);
|
|
AddPlaceholderReplacement(node.ReceiverPlaceholder, node.Receiver, visitResult);
|
|
VisitRvalue(node.IndexerOrSliceAccess);
|
|
RemovePlaceholderReplacement(node.ReceiverPlaceholder);
|
|
SetResult(node, ResultType, LvalueResultType);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitImplicitIndexerValuePlaceholder(BoundImplicitIndexerValuePlaceholder node)
|
|
{
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node)
|
|
{
|
|
VisitPlaceholderWithReplacement(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitEventAccess(BoundEventAccess node)
|
|
{
|
|
Symbol updatedSymbol = VisitMemberAccess(node, node.ReceiverOpt, node.EventSymbol);
|
|
SetUpdatedSymbol(node, node.EventSymbol, updatedSymbol);
|
|
return null;
|
|
}
|
|
|
|
private Symbol VisitMemberAccess(BoundExpression node, BoundExpression? receiverOpt, Symbol member)
|
|
{
|
|
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
|
|
TypeWithState typeWithState = ((receiverOpt != null) ? VisitRvalueWithState(receiverOpt) : default(TypeWithState));
|
|
SpecialMember? val = null;
|
|
if (member.RequiresInstanceReceiver())
|
|
{
|
|
member = AsMemberOfType(typeWithState.Type, member);
|
|
val = GetNullableOfTMember(member);
|
|
bool flag = val != (SpecialMember?)115;
|
|
CheckPossibleNullReceiver(receiverOpt, !flag);
|
|
}
|
|
TypeWithAnnotations typeOrReturnType = member.GetTypeOrReturnType();
|
|
FlowAnalysisAnnotations rValueAnnotations = GetRValueAnnotations(member);
|
|
TypeWithState resultType = ApplyUnconditionalAnnotations(typeOrReturnType.ToTypeWithState(), rValueAnnotations);
|
|
if (PossiblyNullableType(resultType.Type))
|
|
{
|
|
int num = MakeMemberSlot(receiverOpt, member);
|
|
if (num > 0)
|
|
{
|
|
NullableFlowState state = GetState(ref State, num);
|
|
resultType = TypeWithState.Create(resultType.Type, state);
|
|
}
|
|
}
|
|
if (val == (SpecialMember?)116 && receiverOpt != null)
|
|
{
|
|
int num2 = MakeSlot(receiverOpt);
|
|
if (num2 > 0)
|
|
{
|
|
Split();
|
|
SetState(ref StateWhenTrue, num2, NullableFlowState.NotNull);
|
|
}
|
|
}
|
|
SetResult(node, resultType, typeOrReturnType);
|
|
return member;
|
|
}
|
|
|
|
private SpecialMember? GetNullableOfTMember(Symbol member)
|
|
{
|
|
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0008: Invalid comparison between Unknown and I4
|
|
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_002b: Invalid comparison between Unknown and I4
|
|
if ((int)member.Kind == 15)
|
|
{
|
|
MethodSymbol getMethod = ((PropertySymbol)member.OriginalDefinition).GetMethod;
|
|
if ((object)getMethod != null && (int)getMethod.ContainingType.SpecialType == 32)
|
|
{
|
|
if (getMethod == compilation.GetSpecialTypeMember((SpecialMember)115))
|
|
{
|
|
return (SpecialMember)115;
|
|
}
|
|
if (getMethod == compilation.GetSpecialTypeMember((SpecialMember)116))
|
|
{
|
|
return (SpecialMember)116;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private int GetNullableOfTValueSlot(TypeSymbol containingType, int containingSlot, out Symbol? valueProperty, bool forceSlotEvenIfEmpty = false)
|
|
{
|
|
valueProperty = ((MethodSymbol)compilation.GetSpecialTypeMember((SpecialMember)115))?.AsMember((NamedTypeSymbol)containingType)?.AssociatedSymbol;
|
|
if ((object)valueProperty != null)
|
|
{
|
|
return GetOrCreateSlot(valueProperty, containingSlot, forceSlotEvenIfEmpty);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
protected unsafe override void VisitForEachExpression(BoundForEachStatement node)
|
|
{
|
|
//IL_0200: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0206: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_012e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0135: Invalid comparison between Unknown and I4
|
|
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_016e: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0175: Invalid comparison between Unknown and I4
|
|
//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_02b0: Invalid comparison between Unknown and I4
|
|
//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0300: Unknown result type (might be due to invalid IL or missing references)
|
|
if (node.Expression.Kind != BoundKind.Conversion)
|
|
{
|
|
VisitRvalue(node.Expression);
|
|
Visit(node.AwaitOpt);
|
|
return;
|
|
}
|
|
var (boundExpression, conversion) = RemoveConversion(node.Expression, includeExplicitConversions: false);
|
|
SnapshotWalkerThroughConversionGroup(node.Expression, boundExpression);
|
|
TypeWithState operandType = VisitRvalueWithState(boundExpression);
|
|
TypeSymbol type = operandType.Type;
|
|
SetAnalyzedNullability(boundExpression, _visitResult);
|
|
MethodSymbol methodSymbol = null;
|
|
MethodArgumentInfo methodArgumentInfo = node.EnumeratorInfoOpt?.GetEnumeratorInfo;
|
|
TypeWithAnnotations targetTypeWithNullability;
|
|
MethodSymbol method;
|
|
if ((object)methodArgumentInfo != null)
|
|
{
|
|
method = methodArgumentInfo.Method;
|
|
if ((object)method != null && method.IsExtensionMethod)
|
|
{
|
|
ImmutableArray<ParameterSymbol> parameters = method.Parameters;
|
|
(MethodSymbol? method, ImmutableArray<VisitArgumentResult> results, bool returnNotNull) tuple2 = VisitArguments(node, methodArgumentInfo.Arguments, default(ImmutableArray<RefKind>), parameters, methodArgumentInfo.ArgsToParamsOpt, methodArgumentInfo.DefaultArguments, expanded: false, invokedAsExtensionMethod: true, methodArgumentInfo.Method);
|
|
MethodSymbol item = tuple2.method;
|
|
ImmutableArray<VisitArgumentResult> item2 = tuple2.results;
|
|
targetTypeWithNullability = item2[0].LValueType;
|
|
methodSymbol = item;
|
|
goto IL_01e4;
|
|
}
|
|
}
|
|
if (conversion.IsIdentity || (conversion.Kind == ConversionKind.ExplicitReference && (int)type.SpecialType == 20))
|
|
{
|
|
targetTypeWithNullability = operandType.ToTypeWithAnnotations(compilation);
|
|
}
|
|
else
|
|
{
|
|
if (!conversion.IsImplicit)
|
|
{
|
|
return;
|
|
}
|
|
bool isAsync = node.AwaitOpt != null;
|
|
if ((int)node.Expression.Type.SpecialType == 24)
|
|
{
|
|
targetTypeWithNullability = TypeWithAnnotations.Create(node.Expression.Type);
|
|
}
|
|
else
|
|
{
|
|
if (!Binder.IsIEnumerableT(node.Expression.Type.OriginalDefinition, isAsync, compilation))
|
|
{
|
|
return;
|
|
}
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
targetTypeWithNullability = TypeWithAnnotations.Create(Binder.GetIEnumerableOfT(type, isAsync, compilation, ref useSiteInfo, out var _));
|
|
}
|
|
}
|
|
goto IL_01e4;
|
|
IL_032f:
|
|
TypeSymbol type2;
|
|
methodSymbol = (MethodSymbol)AsMemberOfType(type2, node.EnumeratorInfoOpt.GetEnumeratorInfo.Method);
|
|
goto IL_034d;
|
|
IL_01e4:
|
|
TypeWithState rValueType = VisitConversion(GetConversionIfApplicable(node.Expression, boundExpression), boundExpression, conversion, targetTypeWithNullability, operandType, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment);
|
|
method = node.EnumeratorInfoOpt?.GetEnumeratorInfo.Method;
|
|
bool flag = ((object)method == null || !method.IsExtensionMethod) && CheckPossibleNullReceiver(boundExpression);
|
|
SetAnalyzedNullability(node.Expression, new VisitResult(rValueType, rValueType.ToTypeWithAnnotations(compilation)));
|
|
TypeWithState type3;
|
|
ForEachEnumeratorInfo enumeratorInfoOpt;
|
|
if (node.EnumeratorInfoOpt == null)
|
|
{
|
|
type3 = default(TypeWithState);
|
|
}
|
|
else if (type is ArrayTypeSymbol { ElementTypeWithAnnotations: var elementTypeWithAnnotations })
|
|
{
|
|
type3 = elementTypeWithAnnotations.ToTypeWithState();
|
|
}
|
|
else
|
|
{
|
|
if ((int)type.SpecialType != 20)
|
|
{
|
|
if ((object)methodSymbol == null)
|
|
{
|
|
enumeratorInfoOpt = node.EnumeratorInfoOpt;
|
|
if (enumeratorInfoOpt != null)
|
|
{
|
|
WellKnownType inlineArraySpanType = enumeratorInfoOpt.InlineArraySpanType;
|
|
if ((int)inlineArraySpanType != 0)
|
|
{
|
|
type2 = compilation.GetWellKnownType(inlineArraySpanType).Construct(ImmutableArray.Create(rValueType.Type.TryGetInlineArrayElementField().TypeWithAnnotations));
|
|
goto IL_032f;
|
|
}
|
|
}
|
|
type2 = rValueType.Type;
|
|
goto IL_032f;
|
|
}
|
|
goto IL_034d;
|
|
}
|
|
type3 = TypeWithAnnotations.Create(node.EnumeratorInfoOpt.ElementType, NullableAnnotation.NotAnnotated).ToTypeWithState();
|
|
}
|
|
goto IL_04e9;
|
|
IL_03a3:
|
|
TypeWithState returnTypeWithState;
|
|
MethodSymbol methodSymbol2 = (MethodSymbol)AsMemberOfType(((TypeWithState*)(&returnTypeWithState))->Type, node.EnumeratorInfoOpt.CurrentPropertyGetter);
|
|
type3 = ApplyUnconditionalAnnotations(methodSymbol2.ReturnTypeWithAnnotations.ToTypeWithState(), methodSymbol2.ReturnTypeFlowAnalysisAnnotations);
|
|
BoundAwaitableInfo awaitOpt = node.AwaitOpt;
|
|
if (awaitOpt != null)
|
|
{
|
|
BoundAwaitableValuePlaceholder awaitableInstancePlaceholder = awaitOpt.AwaitableInstancePlaceholder;
|
|
if (awaitableInstancePlaceholder != null)
|
|
{
|
|
MethodSymbol methodSymbol3 = (MethodSymbol)AsMemberOfType(methodSymbol.ReturnType, node.EnumeratorInfoOpt.MoveNextInfo.Method);
|
|
VisitResult result = new VisitResult(GetReturnTypeWithState(methodSymbol3), methodSymbol3.ReturnTypeWithAnnotations);
|
|
AddPlaceholderReplacement(awaitableInstancePlaceholder, awaitableInstancePlaceholder, result);
|
|
Visit(awaitOpt);
|
|
RemovePlaceholderReplacement(awaitableInstancePlaceholder);
|
|
}
|
|
}
|
|
enumeratorInfoOpt = node.EnumeratorInfoOpt;
|
|
if (enumeratorInfoOpt != null && enumeratorInfoOpt.NeedsDisposal)
|
|
{
|
|
BoundAwaitableInfo disposeAwaitableInfo = enumeratorInfoOpt.DisposeAwaitableInfo;
|
|
if (disposeAwaitableInfo != null)
|
|
{
|
|
BoundAwaitableValuePlaceholder awaitableInstancePlaceholder2 = disposeAwaitableInfo.AwaitableInstancePlaceholder;
|
|
bool flag2 = false;
|
|
MethodArgumentInfo patternDisposeInfo = node.EnumeratorInfoOpt.PatternDisposeInfo;
|
|
if ((object)patternDisposeInfo != null)
|
|
{
|
|
MethodSymbol method2 = patternDisposeInfo.Method;
|
|
MethodSymbol methodSymbol4 = (MethodSymbol)AsMemberOfType(methodSymbol.ReturnType, method2);
|
|
VisitResult result2 = new VisitResult(GetReturnTypeWithState(methodSymbol4), methodSymbol4.ReturnTypeWithAnnotations);
|
|
AddPlaceholderReplacement(awaitableInstancePlaceholder2, awaitableInstancePlaceholder2, result2);
|
|
flag2 = true;
|
|
}
|
|
Visit(disposeAwaitableInfo);
|
|
if (flag2)
|
|
{
|
|
RemovePlaceholderReplacement(awaitableInstancePlaceholder2);
|
|
}
|
|
}
|
|
}
|
|
goto IL_04e9;
|
|
IL_04e9:
|
|
SetResultType(null, type3);
|
|
return;
|
|
IL_034d:
|
|
returnTypeWithState = GetReturnTypeWithState(methodSymbol);
|
|
if (returnTypeWithState.State != NullableFlowState.NotNull && !flag)
|
|
{
|
|
if (node.Expression is BoundConversion boundConversion)
|
|
{
|
|
BoundExpression operand = boundConversion.Operand;
|
|
if (operand != null && operand.IsSuppressed)
|
|
{
|
|
goto IL_03a3;
|
|
}
|
|
}
|
|
ReportDiagnostic(ErrorCode.WRN_NullReferenceReceiver, boundExpression.Syntax.GetLocation());
|
|
}
|
|
goto IL_03a3;
|
|
}
|
|
|
|
public override void VisitForEachIterationVariables(BoundForEachStatement node)
|
|
{
|
|
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0192: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0197: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
|
|
TypeWithState typeWithState = ((node.EnumeratorInfoOpt == null) ? default(TypeWithState) : ResultType);
|
|
TypeWithAnnotations typeWithAnnotations = typeWithState.ToTypeWithAnnotations(compilation);
|
|
SyntaxNode syntax = node.Syntax;
|
|
Location location;
|
|
if (!(syntax is ForEachStatementSyntax { Identifier: var identifier }))
|
|
{
|
|
if (!(syntax is ForEachVariableStatementSyntax forEachVariableStatementSyntax))
|
|
{
|
|
throw ExceptionUtilities.UnexpectedValue((object)node.Syntax);
|
|
}
|
|
location = forEachVariableStatementSyntax.Variable.GetLocation();
|
|
}
|
|
else
|
|
{
|
|
location = ((SyntaxToken)(ref identifier)).GetLocation();
|
|
}
|
|
Location val = location;
|
|
if (node.DeconstructionOpt != null)
|
|
{
|
|
BoundDeconstructionAssignmentOperator deconstructionAssignment = node.DeconstructionOpt.DeconstructionAssignment;
|
|
VisitDeconstructionAssignmentOperator(deconstructionAssignment, typeWithState.HasNullType ? ((TypeWithState?)null) : new TypeWithState?(typeWithState));
|
|
Visit(node.IterationVariableType);
|
|
return;
|
|
}
|
|
Visit(node.IterationVariableType);
|
|
ImmutableArray<LocalSymbol>.Enumerator enumerator = node.IterationVariables.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
LocalSymbol current = enumerator.Current;
|
|
NullableFlowState value = NullableFlowState.NotNull;
|
|
if (!typeWithState.HasNullType)
|
|
{
|
|
TypeWithAnnotations typeWithAnnotations2 = current.TypeWithAnnotations;
|
|
TypeWithState typeWithState2 = typeWithState;
|
|
TypeWithState rValueType = typeWithState;
|
|
if (current.IsRef)
|
|
{
|
|
if (IsNullabilityMismatch(typeWithAnnotations, typeWithAnnotations2))
|
|
{
|
|
ForEachStatementSyntax forEachStatementSyntax2 = (ForEachStatementSyntax)(object)node.Syntax;
|
|
ReportNullabilityMismatchInAssignment((SyntaxNode)(object)forEachStatementSyntax2.Type, typeWithAnnotations, typeWithAnnotations2);
|
|
}
|
|
}
|
|
else if (current is SourceLocalSymbol { IsVar: not false })
|
|
{
|
|
typeWithAnnotations2 = typeWithState.ToAnnotatedTypeWithAnnotations(compilation);
|
|
_variables.SetType(current, typeWithAnnotations2);
|
|
rValueType = typeWithAnnotations2.ToTypeWithState();
|
|
}
|
|
else
|
|
{
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
Conversion conversion = BoundNode.GetConversion(node.ElementConversion, node.ElementPlaceholder);
|
|
if (conversion.Kind == ConversionKind.NoConversion)
|
|
{
|
|
conversion = _conversions.ClassifyImplicitConversionFromType(typeWithAnnotations.Type, typeWithAnnotations2.Type, ref useSiteInfo);
|
|
}
|
|
BoundTypeExpression iterationVariableType = node.IterationVariableType;
|
|
Conversion conversion2 = conversion;
|
|
TypeWithAnnotations targetTypeWithNullability = typeWithAnnotations2;
|
|
TypeWithState operandType = typeWithState;
|
|
bool fromExplicitCast = !conversion.IsImplicit;
|
|
location = val;
|
|
typeWithState2 = VisitConversion(null, iterationVariableType, conversion2, targetTypeWithNullability, operandType, checkConversion: true, fromExplicitCast, useLegacyWarnings: true, AssignmentKind.ForEachIterationVariable, null, reportTopLevelWarnings: true, reportRemainingWarnings: true, extensionMethodThisArgument: false, default(Optional<LocalState>), trackMembers: false, location);
|
|
}
|
|
SetAnalyzedNullability(node.IterationVariableType, new VisitResult(rValueType, typeWithAnnotations2), true);
|
|
value = typeWithState2.State;
|
|
}
|
|
int orCreateSlot = GetOrCreateSlot(current);
|
|
if (orCreateSlot > 0)
|
|
{
|
|
SetState(ref State, orCreateSlot, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitFromEndIndexExpression(BoundFromEndIndexExpression node)
|
|
{
|
|
BoundNode result = base.VisitFromEndIndexExpression(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitObjectInitializerMember(BoundObjectInitializerMember node)
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs", 10541);
|
|
}
|
|
|
|
public override BoundNode? VisitDynamicObjectInitializerMember(BoundDynamicObjectInitializerMember node)
|
|
{
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitBadExpression(BoundBadExpression node)
|
|
{
|
|
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
|
|
ImmutableArray<BoundExpression>.Enumerator enumerator = node.ChildBoundNodes.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundExpression current = enumerator.Current;
|
|
if (current is BoundLambda node2)
|
|
{
|
|
TakeIncrementalSnapshot(node2);
|
|
VisitLambda(node2, null);
|
|
VisitRvalueEpilogue(node2);
|
|
}
|
|
else
|
|
{
|
|
VisitRvalue(current);
|
|
}
|
|
}
|
|
TypeWithAnnotations type = TypeWithAnnotations.Create(node.Type);
|
|
SetLvalueResultType(node, type);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitTypeExpression(BoundTypeExpression node)
|
|
{
|
|
BoundNode result = base.VisitTypeExpression(node);
|
|
if (node.BoundContainingTypeOpt != null)
|
|
{
|
|
VisitTypeExpression(node.BoundContainingTypeOpt);
|
|
}
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitTypeOrValueExpression(BoundTypeOrValueExpression node)
|
|
{
|
|
BoundNode result = base.VisitTypeOrValueExpression(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitUnaryOperator(BoundUnaryOperator node)
|
|
{
|
|
//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
|
|
TypeWithState type;
|
|
switch (node.OperatorKind)
|
|
{
|
|
case UnaryOperatorKind.BoolLogicalNegation:
|
|
Visit(node.Operand);
|
|
if (IsConditionalState)
|
|
{
|
|
SetConditionalState(StateWhenFalse, StateWhenTrue);
|
|
}
|
|
type = adjustForLifting(ResultType);
|
|
break;
|
|
case UnaryOperatorKind.DynamicTrue:
|
|
Visit(node.Operand);
|
|
type = adjustForLifting(ResultType);
|
|
break;
|
|
case UnaryOperatorKind.DynamicLogicalNegation:
|
|
Visit(node.Operand);
|
|
if (IsConditionalState)
|
|
{
|
|
SetConditionalState(StateWhenFalse, StateWhenTrue);
|
|
}
|
|
type = adjustForLifting(ResultType);
|
|
break;
|
|
default:
|
|
if (node.OperatorKind.IsUserDefined())
|
|
{
|
|
MethodSymbol methodOpt = node.MethodOpt;
|
|
if ((object)methodOpt != null && methodOpt.ParameterCount == 1)
|
|
{
|
|
var (boundExpression, conversion) = RemoveConversion(node.Operand, includeExplicitConversions: false);
|
|
VisitRvalue(boundExpression);
|
|
TypeWithState resultType = ResultType;
|
|
bool isLifted = node.OperatorKind.IsLifted();
|
|
TypeWithState nullableUnderlyingTypeIfNecessary = GetNullableUnderlyingTypeIfNecessary(isLifted, resultType);
|
|
methodOpt = (MethodSymbol)AsMemberOfType(nullableUnderlyingTypeIfNecessary.Type.StrippedType(), methodOpt);
|
|
ParameterSymbol parameterSymbol = methodOpt.Parameters[0];
|
|
VisitConversion(node.Operand as BoundConversion, boundExpression, conversion, parameterSymbol.TypeWithAnnotations, nullableUnderlyingTypeIfNecessary, checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Argument, parameterSymbol);
|
|
type = GetLiftedReturnTypeIfNecessary(isLifted, methodOpt.ReturnTypeWithAnnotations, resultType.State);
|
|
SetUpdatedSymbol(node, node.MethodOpt, methodOpt);
|
|
break;
|
|
}
|
|
}
|
|
VisitRvalue(node.Operand);
|
|
type = adjustForLifting(ResultType);
|
|
break;
|
|
}
|
|
SetResultType(node, type);
|
|
return null;
|
|
TypeWithState adjustForLifting(TypeWithState argumentResult)
|
|
{
|
|
return TypeWithState.Create(node.Type, node.OperatorKind.IsLifted() ? argumentResult.State : NullableFlowState.NotNull);
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitPointerIndirectionOperator(BoundPointerIndirectionOperator node)
|
|
{
|
|
BoundNode result = base.VisitPointerIndirectionOperator(node);
|
|
TypeWithAnnotations type = TypeWithAnnotations.Create(node.Type);
|
|
SetLvalueResultType(node, type);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitPointerElementAccess(BoundPointerElementAccess node)
|
|
{
|
|
BoundNode result = base.VisitPointerElementAccess(node);
|
|
TypeWithAnnotations type = TypeWithAnnotations.Create(node.Type);
|
|
SetLvalueResultType(node, type);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitRefTypeOperator(BoundRefTypeOperator node)
|
|
{
|
|
VisitRvalue(node.Operand);
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitMakeRefOperator(BoundMakeRefOperator node)
|
|
{
|
|
BoundNode result = base.VisitMakeRefOperator(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitRefValueOperator(BoundRefValueOperator node)
|
|
{
|
|
BoundNode result = base.VisitRefValueOperator(node);
|
|
TypeWithAnnotations type = TypeWithAnnotations.Create(node.Type, node.NullableAnnotation);
|
|
SetLvalueResultType(node, type);
|
|
return result;
|
|
}
|
|
|
|
private TypeWithState InferResultNullability(BoundUserDefinedConditionalLogicalOperator node)
|
|
{
|
|
if (node.OperatorKind.IsLifted())
|
|
{
|
|
return TypeWithState.Create(node.Type, NullableFlowState.NotNull);
|
|
}
|
|
if ((object)node.LogicalOperator != null && node.LogicalOperator.ParameterCount == 2)
|
|
{
|
|
return GetReturnTypeWithState(node.LogicalOperator);
|
|
}
|
|
return default(TypeWithState);
|
|
}
|
|
|
|
protected override void AfterLeftChildOfBinaryLogicalOperatorHasBeenVisited(BoundExpression node, BoundExpression right, bool isAnd, bool isBool, ref LocalState leftTrue, ref LocalState leftFalse)
|
|
{
|
|
TypeWithState resultType = ResultType;
|
|
MethodSymbol methodSymbol = null;
|
|
MethodSymbol methodSymbol2 = null;
|
|
BoundExpression argument = null;
|
|
switch (node.Kind)
|
|
{
|
|
case BoundKind.UserDefinedConditionalLogicalOperator:
|
|
{
|
|
BoundUserDefinedConditionalLogicalOperator boundUserDefinedConditionalLogicalOperator = (BoundUserDefinedConditionalLogicalOperator)node;
|
|
if (boundUserDefinedConditionalLogicalOperator.LogicalOperator != null && boundUserDefinedConditionalLogicalOperator.LogicalOperator.ParameterCount == 2)
|
|
{
|
|
methodSymbol = boundUserDefinedConditionalLogicalOperator.LogicalOperator;
|
|
argument = boundUserDefinedConditionalLogicalOperator.Left;
|
|
methodSymbol2 = (isAnd ? boundUserDefinedConditionalLogicalOperator.FalseOperator : boundUserDefinedConditionalLogicalOperator.TrueOperator);
|
|
if ((object)methodSymbol2 != null && methodSymbol2.ParameterCount != 1)
|
|
{
|
|
methodSymbol2 = null;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
throw ExceptionUtilities.UnexpectedValue((object)node.Kind);
|
|
case BoundKind.BinaryOperator:
|
|
break;
|
|
}
|
|
if ((object)methodSymbol2 != null)
|
|
{
|
|
ReportArgumentWarnings(argument, resultType, methodSymbol2.Parameters[0]);
|
|
}
|
|
if ((object)methodSymbol != null)
|
|
{
|
|
ReportArgumentWarnings(argument, resultType, methodSymbol.Parameters[0]);
|
|
}
|
|
Visit(right);
|
|
TypeWithState resultType2 = ResultType;
|
|
SetResultType(node, InferResultNullabilityOfBinaryLogicalOperator(node, resultType, resultType2));
|
|
if ((object)methodSymbol != null)
|
|
{
|
|
ReportArgumentWarnings(right, resultType2, methodSymbol.Parameters[1]);
|
|
}
|
|
AfterRightChildOfBinaryLogicalOperatorHasBeenVisited(right, isAnd, isBool, ref leftTrue, ref leftFalse);
|
|
}
|
|
|
|
private TypeWithState InferResultNullabilityOfBinaryLogicalOperator(BoundExpression node, TypeWithState leftType, TypeWithState rightType)
|
|
{
|
|
if (!(node is BoundBinaryOperator boundBinaryOperator))
|
|
{
|
|
if (node is BoundUserDefinedConditionalLogicalOperator node2)
|
|
{
|
|
return InferResultNullability(node2);
|
|
}
|
|
throw ExceptionUtilities.UnexpectedValue((object)node);
|
|
}
|
|
return InferResultNullability(boundBinaryOperator.OperatorKind, boundBinaryOperator.Method, boundBinaryOperator.Type, leftType, rightType);
|
|
}
|
|
|
|
public override BoundNode? VisitAwaitExpression(BoundAwaitExpression node)
|
|
{
|
|
BoundNode result = base.VisitAwaitExpression(node);
|
|
BoundAwaitableInfo awaitableInfo = node.AwaitableInfo;
|
|
BoundAwaitableValuePlaceholder awaitableInstancePlaceholder = awaitableInfo.AwaitableInstancePlaceholder;
|
|
AddPlaceholderReplacement(awaitableInstancePlaceholder, node.Expression, _visitResult);
|
|
Visit(awaitableInfo);
|
|
RemovePlaceholderReplacement(awaitableInstancePlaceholder);
|
|
if (node.Type.IsValueType || node.HasErrors || (object)awaitableInfo.GetResult == null)
|
|
{
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
MethodSymbol getResult = awaitableInfo.GetResult;
|
|
MethodSymbol methodSymbol = ((_visitResult.RValueType.Type is NamedTypeSymbol newOwner) ? getResult.OriginalDefinition.AsMember(newOwner) : getResult);
|
|
SetResultType(node, methodSymbol.ReturnTypeWithAnnotations.ToTypeWithState());
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitTypeOfOperator(BoundTypeOfOperator node)
|
|
{
|
|
BoundNode result = base.VisitTypeOfOperator(node);
|
|
SetResultType(node, TypeWithState.Create(node.Type, NullableFlowState.NotNull));
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitMethodInfo(BoundMethodInfo node)
|
|
{
|
|
BoundNode result = base.VisitMethodInfo(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitFieldInfo(BoundFieldInfo node)
|
|
{
|
|
BoundNode result = base.VisitFieldInfo(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitDefaultLiteral(BoundDefaultLiteral node)
|
|
{
|
|
BoundNode result = base.VisitDefaultLiteral(node);
|
|
SetResultType(node, TypeWithState.Create(node.Type, NullableFlowState.MaybeDefault));
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitDefaultExpression(BoundDefaultExpression node)
|
|
{
|
|
BoundNode result = base.VisitDefaultExpression(node);
|
|
TypeSymbol type = node.Type;
|
|
if (EmptyStructTypeCache.IsTrackableStructType(type))
|
|
{
|
|
int orCreatePlaceholderSlot = GetOrCreatePlaceholderSlot(node);
|
|
if (orCreatePlaceholderSlot > 0)
|
|
{
|
|
SetState(ref State, orCreatePlaceholderSlot, NullableFlowState.NotNull);
|
|
InheritNullableStateOfTrackableStruct(type, orCreatePlaceholderSlot, -1, isDefaultValue: true);
|
|
}
|
|
}
|
|
SetResultType(node, TypeWithState.ForType(type));
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitIsOperator(BoundIsOperator node)
|
|
{
|
|
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0066: Invalid comparison between Unknown and I4
|
|
BoundExpression operand = node.Operand;
|
|
BoundTypeExpression targetType = node.TargetType;
|
|
VisitPossibleConditionalAccess(operand, out var stateWhenNotNull);
|
|
Unsplit();
|
|
LocalState self;
|
|
if (!stateWhenNotNull.IsConditionalState)
|
|
{
|
|
self = stateWhenNotNull.State;
|
|
}
|
|
else
|
|
{
|
|
self = stateWhenNotNull.StateWhenTrue;
|
|
Join(ref self, ref stateWhenNotNull.StateWhenFalse);
|
|
}
|
|
SetConditionalState(self, State);
|
|
TypeSymbol type = targetType.Type;
|
|
if ((object)type != null && (int)type.SpecialType == 1)
|
|
{
|
|
LearnFromNullTest(operand, ref StateWhenFalse);
|
|
}
|
|
VisitTypeExpression(targetType);
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitAsOperator(BoundAsOperator node)
|
|
{
|
|
TypeWithState typeWithState = VisitRvalueWithState(node.Operand);
|
|
NullableFlowState defaultState = NullableFlowState.NotNull;
|
|
TypeSymbol type = node.Type;
|
|
if (type.CanContainNull())
|
|
{
|
|
ConversionKind kind = BoundNode.GetConversion(node.OperandConversion, node.OperandPlaceholder).Kind;
|
|
defaultState = ((kind != ConversionKind.Identity && kind != ConversionKind.ImplicitNullable && kind - 12 > ConversionKind.NoConversion) ? NullableFlowState.MaybeDefault : typeWithState.State);
|
|
}
|
|
VisitTypeExpression(node.TargetType);
|
|
SetResultType(node, TypeWithState.Create(type, defaultState));
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitSizeOfOperator(BoundSizeOfOperator node)
|
|
{
|
|
BoundNode result = base.VisitSizeOfOperator(node);
|
|
VisitTypeExpression(node.SourceType);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitArgList(BoundArgList node)
|
|
{
|
|
BoundNode result = base.VisitArgList(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitArgListOperator(BoundArgListOperator node)
|
|
{
|
|
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
|
|
VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt, default(ImmutableArray<FlowAnalysisAnnotations>), default(BitVector));
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitLiteral(BoundLiteral node)
|
|
{
|
|
BoundNode result = base.VisitLiteral(node);
|
|
TypeSymbol? type = node.Type;
|
|
TypeSymbol? type2 = node.Type;
|
|
int defaultState;
|
|
if ((object)type2 == null || type2.CanContainNull())
|
|
{
|
|
ConstantValue? constantValueOpt = node.ConstantValueOpt;
|
|
if (constantValueOpt != null && constantValueOpt.IsNull)
|
|
{
|
|
defaultState = 3;
|
|
goto IL_003b;
|
|
}
|
|
}
|
|
defaultState = 0;
|
|
goto IL_003b;
|
|
IL_003b:
|
|
SetResultType(node, TypeWithState.Create(type, (NullableFlowState)defaultState));
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitUtf8String(BoundUtf8String node)
|
|
{
|
|
BoundNode result = base.VisitUtf8String(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitPreviousSubmissionReference(BoundPreviousSubmissionReference node)
|
|
{
|
|
BoundNode result = base.VisitPreviousSubmissionReference(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitHostObjectMemberReference(BoundHostObjectMemberReference node)
|
|
{
|
|
BoundNode result = base.VisitHostObjectMemberReference(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitPseudoVariable(BoundPseudoVariable node)
|
|
{
|
|
BoundNode? result = base.VisitPseudoVariable(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitRangeExpression(BoundRangeExpression node)
|
|
{
|
|
BoundNode result = base.VisitRangeExpression(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitRangeVariable(BoundRangeVariable node)
|
|
{
|
|
VisitWithoutDiagnostics(node.Value);
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitLabel(BoundLabel node)
|
|
{
|
|
BoundNode? result = base.VisitLabel(node);
|
|
SetUnknownResultNullability(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitDynamicMemberAccess(BoundDynamicMemberAccess node)
|
|
{
|
|
BoundExpression receiver = node.Receiver;
|
|
VisitRvalue(receiver);
|
|
CheckPossibleNullReceiver(receiver);
|
|
TypeWithAnnotations type = TypeWithAnnotations.Create(node.Type);
|
|
SetLvalueResultType(node, type);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitDynamicInvocation(BoundDynamicInvocation node)
|
|
{
|
|
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
|
|
BoundExpression expression = node.Expression;
|
|
VisitRvalue(expression);
|
|
BoundExpression receiverOpt = (expression as BoundMethodGroup)?.ReceiverOpt;
|
|
if (TryGetMethodGroupReceiverNullability(receiverOpt, out var type))
|
|
{
|
|
CheckPossibleNullReceiver(receiverOpt, type, checkNullableValueType: false);
|
|
}
|
|
VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt, default(ImmutableArray<FlowAnalysisAnnotations>), default(BitVector));
|
|
TypeWithAnnotations type2 = TypeWithAnnotations.Create(node.Type);
|
|
SetLvalueResultType(node, type2);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitEventAssignmentOperator(BoundEventAssignmentOperator node)
|
|
{
|
|
BoundExpression receiverOpt = node.ReceiverOpt;
|
|
VisitRvalue(receiverOpt);
|
|
EventSymbol eventSymbol = node.Event;
|
|
if (!eventSymbol.IsStatic)
|
|
{
|
|
eventSymbol = (EventSymbol)AsMemberOfType(ResultType.Type, eventSymbol);
|
|
CheckPossibleNullReceiver(receiverOpt);
|
|
SetUpdatedSymbol(node, node.Event, eventSymbol);
|
|
}
|
|
VisitRvalue(node.Argument);
|
|
ConstantValue? constantValueOpt = node.Argument.ConstantValueOpt;
|
|
if (constantValueOpt == null || !constantValueOpt.IsNull)
|
|
{
|
|
int num = MakeMemberSlot(receiverOpt, eventSymbol);
|
|
if (num > 0)
|
|
{
|
|
SetState(ref State, num, (!node.IsAddition) ? NullableFlowState.MaybeNull : GetState(ref State, num).Meet(ResultType.State));
|
|
}
|
|
}
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitDynamicObjectCreationExpression(BoundDynamicObjectCreationExpression node)
|
|
{
|
|
VisitObjectCreationExpressionBase(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitObjectInitializerExpression(BoundObjectInitializerExpression node)
|
|
{
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitCollectionInitializerExpression(BoundCollectionInitializerExpression node)
|
|
{
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitDynamicCollectionElementInitializer(BoundDynamicCollectionElementInitializer node)
|
|
{
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitImplicitReceiver(BoundImplicitReceiver node)
|
|
{
|
|
BoundNode result = base.VisitImplicitReceiver(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitAnonymousPropertyDeclaration(BoundAnonymousPropertyDeclaration node)
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs", 11118);
|
|
}
|
|
|
|
public override BoundNode? VisitNoPiaObjectCreationExpression(BoundNoPiaObjectCreationExpression node)
|
|
{
|
|
VisitObjectCreationExpressionBase(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitNewT(BoundNewT node)
|
|
{
|
|
VisitObjectCreationExpressionBase(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitArrayInitialization(BoundArrayInitialization node)
|
|
{
|
|
BoundNode result = base.VisitArrayInitialization(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
private void SetUnknownResultNullability(BoundExpression expression)
|
|
{
|
|
SetResultType(expression, TypeWithState.Create(expression.Type, NullableFlowState.NotNull));
|
|
}
|
|
|
|
public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node)
|
|
{
|
|
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
|
|
BoundExpression receiver = node.Receiver;
|
|
VisitRvalue(receiver);
|
|
CheckPossibleNullReceiver(receiver);
|
|
VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt, default(ImmutableArray<FlowAnalysisAnnotations>), default(BitVector));
|
|
TypeWithAnnotations type = TypeWithAnnotations.Create(node.Type);
|
|
SetLvalueResultType(node, type);
|
|
return null;
|
|
}
|
|
|
|
private bool CheckPossibleNullReceiver(BoundExpression? receiverOpt, bool checkNullableValueType = false)
|
|
{
|
|
return CheckPossibleNullReceiver(receiverOpt, ResultType, checkNullableValueType);
|
|
}
|
|
|
|
private bool CheckPossibleNullReceiver(BoundExpression? receiverOpt, TypeWithState resultType, bool checkNullableValueType)
|
|
{
|
|
bool reportedDiagnostic = false;
|
|
if (receiverOpt != null && State.Reachable)
|
|
{
|
|
TypeSymbol type = resultType.Type;
|
|
if ((object)type == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (!ReportPossibleNullReceiverIfNeeded(type, resultType.State, checkNullableValueType, receiverOpt.Syntax, out reportedDiagnostic))
|
|
{
|
|
return reportedDiagnostic;
|
|
}
|
|
LearnFromNonNullTest(receiverOpt, ref State);
|
|
}
|
|
return reportedDiagnostic;
|
|
}
|
|
|
|
private bool ReportPossibleNullReceiverIfNeeded(TypeSymbol type, NullableFlowState state, bool checkNullableValueType, SyntaxNode syntax, out bool reportedDiagnostic)
|
|
{
|
|
reportedDiagnostic = false;
|
|
if (state.MayBeNull())
|
|
{
|
|
bool isValueType = type.IsValueType;
|
|
if (isValueType && (!checkNullableValueType || !type.IsNullableTypeOrTypeParameter() || type.GetNullableUnderlyingType().IsErrorType()))
|
|
{
|
|
return false;
|
|
}
|
|
ReportDiagnostic(isValueType ? ErrorCode.WRN_NullableValueTypeMayBeNull : ErrorCode.WRN_NullReferenceReceiver, syntax);
|
|
reportedDiagnostic = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void CheckExtensionMethodThisNullability(BoundExpression expr, Conversion conversion, ParameterSymbol parameter, TypeWithState result)
|
|
{
|
|
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
|
|
VisitArgumentConversionAndInboundAssignmentsAndPreConditions(null, expr, conversion, parameter.RefKind, parameter, parameter.TypeWithAnnotations, GetParameterAnnotations(parameter), new VisitArgumentResult(new VisitResult(result, result.ToTypeWithAnnotations(compilation)), default(Optional<LocalState>)), null, extensionMethodThisArgument: true);
|
|
}
|
|
|
|
private static bool IsNullabilityMismatch(TypeWithAnnotations type1, TypeWithAnnotations type2)
|
|
{
|
|
if (type1.Equals(type2, (TypeCompareKind)63))
|
|
{
|
|
return !type1.Equals(type2, (TypeCompareKind)55);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool IsNullabilityMismatch(TypeSymbol type1, TypeSymbol type2)
|
|
{
|
|
if (type1.Equals(type2, (TypeCompareKind)63))
|
|
{
|
|
return !type1.Equals(type2, (TypeCompareKind)55);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public override BoundNode? VisitQueryClause(BoundQueryClause node)
|
|
{
|
|
BoundNode result = base.VisitQueryClause(node);
|
|
SetNotNullResult(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitNameOfOperator(BoundNameOfOperator node)
|
|
{
|
|
BoundNode result = base.VisitNameOfOperator(node);
|
|
SetResultType(node, TypeWithState.Create(node.Type, NullableFlowState.NotNull));
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitNamespaceExpression(BoundNamespaceExpression node)
|
|
{
|
|
BoundNode result = base.VisitNamespaceExpression(node);
|
|
SetUnknownResultNullability(node);
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitUnconvertedInterpolatedString(BoundUnconvertedInterpolatedString node)
|
|
{
|
|
BoundNode result = base.VisitUnconvertedInterpolatedString(node);
|
|
SetResultType(node, TypeWithState.Create(node.Type, NullableFlowState.NotNull));
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode? VisitStringInsert(BoundStringInsert node)
|
|
{
|
|
BoundNode result = base.VisitStringInsert(node);
|
|
SetUnknownResultNullability(node);
|
|
return result;
|
|
}
|
|
|
|
protected override void VisitInterpolatedStringHandlerConstructor(BoundExpression? constructor)
|
|
{
|
|
}
|
|
|
|
public override BoundNode? VisitInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder node)
|
|
{
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node)
|
|
{
|
|
VisitPlaceholderWithReplacement(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitStackAllocArrayCreation(BoundStackAllocArrayCreation node)
|
|
{
|
|
return VisitStackAllocArrayCreationBase(node);
|
|
}
|
|
|
|
public override BoundNode? VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node)
|
|
{
|
|
return VisitStackAllocArrayCreationBase(node);
|
|
}
|
|
|
|
private BoundNode? VisitStackAllocArrayCreationBase(BoundStackAllocArrayCreationBase node)
|
|
{
|
|
VisitRvalue(node.Count);
|
|
BoundArrayInitialization initializerOpt = node.InitializerOpt;
|
|
if (initializerOpt == null)
|
|
{
|
|
SetResultType(node, TypeWithState.Create(node.Type, NullableFlowState.NotNull));
|
|
return null;
|
|
}
|
|
TypeSymbol type = VisitArrayInitialization(node.Type, initializerOpt, node.HasErrors);
|
|
SetResultType(node, TypeWithState.Create(type, NullableFlowState.NotNull));
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitDiscardExpression(BoundDiscardExpression node)
|
|
{
|
|
TypeWithAnnotations lvalueType = TypeWithAnnotations.Create(node.Type, node.IsInferred ? NullableAnnotation.Annotated : node.NullableAnnotation);
|
|
TypeWithState resultType = TypeWithState.ForType(node.Type);
|
|
SetResult(node, resultType, lvalueType);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitThrowExpression(BoundThrowExpression node)
|
|
{
|
|
VisitThrow(node.Expression);
|
|
SetResultType(node, default(TypeWithState));
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitThrowStatement(BoundThrowStatement node)
|
|
{
|
|
VisitThrow(node.ExpressionOpt);
|
|
return null;
|
|
}
|
|
|
|
private void VisitThrow(BoundExpression? expr)
|
|
{
|
|
if (expr != null && VisitRvalueWithState(expr).MayBeNull)
|
|
{
|
|
ReportDiagnostic(ErrorCode.WRN_ThrowPossibleNull, expr.Syntax);
|
|
}
|
|
SetUnreachable();
|
|
}
|
|
|
|
public override BoundNode? VisitYieldReturnStatement(BoundYieldReturnStatement node)
|
|
{
|
|
BoundExpression expression = node.Expression;
|
|
if (expression == null)
|
|
{
|
|
return null;
|
|
}
|
|
MethodSymbol methodSymbol = (MethodSymbol)CurrentSymbol;
|
|
TypeWithAnnotations iteratorElementTypeFromReturnType = InMethodBinder.GetIteratorElementTypeFromReturnType(compilation, (RefKind)0, methodSymbol.ReturnType, null, null);
|
|
VisitOptionalImplicitConversion(expression, iteratorElementTypeFromReturnType, useLegacyWarnings: false, trackMembers: false, AssignmentKind.Return);
|
|
Unsplit();
|
|
return null;
|
|
}
|
|
|
|
protected override void VisitCatchBlock(BoundCatchBlock node, ref LocalState finallyState)
|
|
{
|
|
TakeIncrementalSnapshot(node);
|
|
if (node.Locals.Length > 0)
|
|
{
|
|
LocalSymbol localSymbol = node.Locals[0];
|
|
if (localSymbol.DeclarationKind == LocalDeclarationKind.CatchVariable)
|
|
{
|
|
int orCreateSlot = GetOrCreateSlot(localSymbol);
|
|
if (orCreateSlot > 0)
|
|
{
|
|
SetState(ref State, orCreateSlot, NullableFlowState.NotNull);
|
|
}
|
|
}
|
|
}
|
|
if (node.ExceptionSourceOpt != null)
|
|
{
|
|
VisitWithoutDiagnostics(node.ExceptionSourceOpt);
|
|
}
|
|
base.VisitCatchBlock(node, ref finallyState);
|
|
}
|
|
|
|
public override BoundNode? VisitLockStatement(BoundLockStatement node)
|
|
{
|
|
VisitRvalue(node.Argument);
|
|
CheckPossibleNullReceiver(node.Argument);
|
|
VisitStatement(node.Body);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitAttribute(BoundAttribute node)
|
|
{
|
|
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
|
|
VisitArguments(node, node.ConstructorArguments, ImmutableArray<RefKind>.Empty, node.Constructor, node.ConstructorArgumentsToParamsOpt, default(BitVector), node.ConstructorExpanded, invokedAsExtensionMethod: false);
|
|
ImmutableArray<BoundAssignmentOperator>.Enumerator enumerator = node.NamedArguments.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundAssignmentOperator current = enumerator.Current;
|
|
Visit(current);
|
|
}
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitExpressionWithNullability(BoundExpressionWithNullability node)
|
|
{
|
|
TypeWithAnnotations lvalueType = TypeWithAnnotations.Create(node.Type, node.NullableAnnotation);
|
|
SetResult(node.Expression, lvalueType.ToTypeWithState(), lvalueType);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitDeconstructValuePlaceholder(BoundDeconstructValuePlaceholder node)
|
|
{
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node)
|
|
{
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node)
|
|
{
|
|
VisitPlaceholderWithReplacement(node);
|
|
return null;
|
|
}
|
|
|
|
private void VisitPlaceholderWithReplacement(BoundValuePlaceholderBase node)
|
|
{
|
|
if (_resultForPlaceholdersOpt != null && ((Dictionary<BoundValuePlaceholderBase, (BoundExpression, VisitResult)>)(object)_resultForPlaceholdersOpt).TryGetValue(node, out (BoundExpression, VisitResult) value))
|
|
{
|
|
VisitResult item = value.Item2;
|
|
SetResult(node, item.RValueType, item.LValueType);
|
|
}
|
|
else
|
|
{
|
|
SetNotNullResult(node);
|
|
}
|
|
}
|
|
|
|
public override BoundNode? VisitAwaitableInfo(BoundAwaitableInfo node)
|
|
{
|
|
Visit(node.AwaitableInstancePlaceholder);
|
|
Visit(node.GetAwaiter);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode? VisitFunctionPointerInvocation(BoundFunctionPointerInvocation node)
|
|
{
|
|
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
|
|
Visit(node.InvokedExpression);
|
|
VisitArguments(node, node.Arguments, node.ArgumentRefKindsOpt, node.FunctionPointer.Signature, default(ImmutableArray<int>), default(BitVector), expanded: false, invokedAsExtensionMethod: false);
|
|
TypeWithAnnotations returnTypeWithAnnotations = node.FunctionPointer.Signature.ReturnTypeWithAnnotations;
|
|
SetResult(node, returnTypeWithAnnotations.ToTypeWithState(), returnTypeWithAnnotations);
|
|
return null;
|
|
}
|
|
|
|
protected override string Dump(LocalState state)
|
|
{
|
|
return state.Dump(_variables);
|
|
}
|
|
|
|
protected override bool Meet(ref LocalState self, ref LocalState other)
|
|
{
|
|
if (!self.Reachable)
|
|
{
|
|
return false;
|
|
}
|
|
if (!other.Reachable)
|
|
{
|
|
self = other.Clone();
|
|
return true;
|
|
}
|
|
Normalize(ref self);
|
|
Normalize(ref other);
|
|
return self.Meet(in other);
|
|
}
|
|
|
|
protected override bool Join(ref LocalState self, ref LocalState other)
|
|
{
|
|
if (!other.Reachable)
|
|
{
|
|
return false;
|
|
}
|
|
if (!self.Reachable)
|
|
{
|
|
self = other.Clone();
|
|
return true;
|
|
}
|
|
Normalize(ref self);
|
|
Normalize(ref other);
|
|
return self.Join(in other);
|
|
}
|
|
|
|
private void Join(ref PossiblyConditionalState other)
|
|
{
|
|
bool isConditionalState = other.IsConditionalState;
|
|
if (isConditionalState)
|
|
{
|
|
Split();
|
|
}
|
|
if (IsConditionalState)
|
|
{
|
|
Join(ref StateWhenTrue, ref isConditionalState ? ref other.StateWhenTrue : ref other.State);
|
|
Join(ref StateWhenFalse, ref isConditionalState ? ref other.StateWhenFalse : ref other.State);
|
|
}
|
|
else
|
|
{
|
|
Join(ref State, ref other.State);
|
|
}
|
|
}
|
|
|
|
private LocalState CloneAndUnsplit(ref PossiblyConditionalState conditionalState)
|
|
{
|
|
if (!conditionalState.IsConditionalState)
|
|
{
|
|
return conditionalState.State.Clone();
|
|
}
|
|
LocalState self = conditionalState.StateWhenTrue.Clone();
|
|
Join(ref self, ref conditionalState.StateWhenFalse);
|
|
return self;
|
|
}
|
|
|
|
private void SetPossiblyConditionalState(in PossiblyConditionalState conditionalState)
|
|
{
|
|
if (!conditionalState.IsConditionalState)
|
|
{
|
|
SetState(conditionalState.State);
|
|
}
|
|
else
|
|
{
|
|
SetConditionalState(conditionalState.StateWhenTrue, conditionalState.StateWhenFalse);
|
|
}
|
|
}
|
|
|
|
protected override LocalFunctionState CreateLocalFunctionState(LocalFunctionSymbol symbol)
|
|
{
|
|
return new LocalFunctionState(LocalState.UnreachableState(((symbol.ContainingSymbol is MethodSymbol method) ? _variables.GetVariablesForMethodScope(method) : null) ?? _variables.GetRootScope()));
|
|
}
|
|
|
|
private void LearnFromAnyNullPatterns(BoundExpression expression, BoundPattern pattern)
|
|
{
|
|
int inputSlot = MakeSlot(expression);
|
|
LearnFromAnyNullPatterns(inputSlot, expression.Type, pattern);
|
|
}
|
|
|
|
private void VisitForRewriting(BoundNode node)
|
|
{
|
|
LocalState state = State;
|
|
VisitWithoutDiagnostics(node);
|
|
SetState(state);
|
|
}
|
|
|
|
public override BoundNode VisitPositionalSubpattern(BoundPositionalSubpattern node)
|
|
{
|
|
Visit(node.Pattern);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitPropertySubpattern(BoundPropertySubpattern node)
|
|
{
|
|
Visit(node.Pattern);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitRecursivePattern(BoundRecursivePattern node)
|
|
{
|
|
Visit(node.DeclaredType);
|
|
VisitAndUnsplitAll(node.Deconstruction);
|
|
VisitAndUnsplitAll(node.Properties);
|
|
Visit(node.VariableAccess);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitConstantPattern(BoundConstantPattern node)
|
|
{
|
|
VisitRvalue(node.Value);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitDeclarationPattern(BoundDeclarationPattern node)
|
|
{
|
|
Visit(node.VariableAccess);
|
|
Visit(node.DeclaredType);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitDiscardPattern(BoundDiscardPattern node)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitSlicePattern(BoundSlicePattern node)
|
|
{
|
|
Visit(node.Pattern);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitListPattern(BoundListPattern node)
|
|
{
|
|
VisitAndUnsplitAll(node.Subpatterns);
|
|
Visit(node.VariableAccess);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitTypePattern(BoundTypePattern node)
|
|
{
|
|
Visit(node.DeclaredType);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitRelationalPattern(BoundRelationalPattern node)
|
|
{
|
|
Visit(node.Value);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitNegatedPattern(BoundNegatedPattern node)
|
|
{
|
|
Visit(node.Negated);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitBinaryPattern(BoundBinaryPattern node)
|
|
{
|
|
Visit(node.Left);
|
|
Visit(node.Right);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitITuplePattern(BoundITuplePattern node)
|
|
{
|
|
VisitAndUnsplitAll(node.Subpatterns);
|
|
return null;
|
|
}
|
|
|
|
private void LearnFromAnyNullPatterns(int inputSlot, TypeSymbol inputType, BoundPattern pattern)
|
|
{
|
|
if (inputSlot <= 0)
|
|
{
|
|
return;
|
|
}
|
|
VisitForRewriting(pattern);
|
|
if (!(pattern is BoundConstantPattern boundConstantPattern))
|
|
{
|
|
if (pattern is BoundDeclarationPattern || pattern is BoundDiscardPattern || pattern is BoundITuplePattern || pattern is BoundRelationalPattern || pattern is BoundSlicePattern || pattern is BoundListPattern)
|
|
{
|
|
return;
|
|
}
|
|
if (!(pattern is BoundTypePattern boundTypePattern))
|
|
{
|
|
if (!(pattern is BoundRecursivePattern boundRecursivePattern))
|
|
{
|
|
if (!(pattern is BoundNegatedPattern boundNegatedPattern))
|
|
{
|
|
if (!(pattern is BoundBinaryPattern boundBinaryPattern))
|
|
{
|
|
throw ExceptionUtilities.UnexpectedValue((object)pattern);
|
|
}
|
|
LearnFromAnyNullPatterns(inputSlot, inputType, boundBinaryPattern.Left);
|
|
LearnFromAnyNullPatterns(inputSlot, inputType, boundBinaryPattern.Right);
|
|
}
|
|
else
|
|
{
|
|
LearnFromAnyNullPatterns(inputSlot, inputType, boundNegatedPattern.Negated);
|
|
}
|
|
return;
|
|
}
|
|
if (boundRecursivePattern.IsExplicitNotNullTest)
|
|
{
|
|
LearnFromNullTest(inputSlot, inputType, ref State, markDependentSlotsNotNull: false);
|
|
}
|
|
if ((object)boundRecursivePattern.DeconstructMethod == null && !boundRecursivePattern.Deconstruction.IsDefault)
|
|
{
|
|
ImmutableArray<FieldSymbol> tupleElements = inputType.TupleElements;
|
|
int i = 0;
|
|
for (int num = Math.Min(boundRecursivePattern.Deconstruction.Length, (!tupleElements.IsDefault) ? tupleElements.Length : 0); i < num; i++)
|
|
{
|
|
BoundSubpattern boundSubpattern = boundRecursivePattern.Deconstruction[i];
|
|
FieldSymbol fieldSymbol = tupleElements[i];
|
|
LearnFromAnyNullPatterns(GetOrCreateSlot(fieldSymbol, inputSlot), fieldSymbol.Type, boundSubpattern.Pattern);
|
|
}
|
|
}
|
|
if (boundRecursivePattern.Properties.IsDefault)
|
|
{
|
|
return;
|
|
}
|
|
ImmutableArray<BoundPropertySubpattern>.Enumerator enumerator = boundRecursivePattern.Properties.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundPropertySubpattern current = enumerator.Current;
|
|
BoundPropertySubpatternMember member = current.Member;
|
|
if (member != null)
|
|
{
|
|
LearnFromAnyNullPatterns(getExtendedPropertySlot(member, inputSlot), member.Type, current.Pattern);
|
|
}
|
|
}
|
|
}
|
|
else if (boundTypePattern.IsExplicitNotNullTest)
|
|
{
|
|
LearnFromNullTest(inputSlot, inputType, ref State, markDependentSlotsNotNull: false);
|
|
}
|
|
}
|
|
else if (boundConstantPattern.Value.ConstantValueOpt == ConstantValue.Null)
|
|
{
|
|
LearnFromNullTest(inputSlot, inputType, ref State, markDependentSlotsNotNull: false);
|
|
}
|
|
int getExtendedPropertySlot(BoundPropertySubpatternMember boundPropertySubpatternMember, int num2)
|
|
{
|
|
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0035: Invalid comparison between Unknown and I4
|
|
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_003a: Invalid comparison between Unknown and I4
|
|
if ((object)boundPropertySubpatternMember.Symbol == null)
|
|
{
|
|
return -1;
|
|
}
|
|
if (boundPropertySubpatternMember.Receiver != null)
|
|
{
|
|
num2 = getExtendedPropertySlot(boundPropertySubpatternMember.Receiver, num2);
|
|
}
|
|
if (num2 < 0)
|
|
{
|
|
return num2;
|
|
}
|
|
SymbolKind kind = boundPropertySubpatternMember.Symbol.Kind;
|
|
if (((int)kind != 6 && (int)kind != 15) || 1 == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
return GetOrCreateSlot(boundPropertySubpatternMember.Symbol, num2);
|
|
}
|
|
}
|
|
|
|
protected override LocalState VisitSwitchStatementDispatch(BoundSwitchStatement node)
|
|
{
|
|
int slotForSwitchInputValue = GetSlotForSwitchInputValue(node.Expression);
|
|
ImmutableArray<BoundSwitchSection>.Enumerator enumerator;
|
|
if (slotForSwitchInputValue > 0)
|
|
{
|
|
TypeSymbol type = node.Expression.Type;
|
|
enumerator = node.SwitchSections.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
ImmutableArray<BoundSwitchLabel>.Enumerator enumerator2 = enumerator.Current.SwitchLabels.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
BoundSwitchLabel current = enumerator2.Current;
|
|
LearnFromAnyNullPatterns(slotForSwitchInputValue, type, current.Pattern);
|
|
}
|
|
}
|
|
}
|
|
DeclareLocals(node.InnerLocals);
|
|
enumerator = node.SwitchSections.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundSwitchSection current2 = enumerator.Current;
|
|
DeclareLocals(current2.Locals);
|
|
}
|
|
Visit(node.Expression);
|
|
TypeWithState resultType = ResultType;
|
|
PooledDictionary<LabelSymbol, (LocalState, bool)> val = LearnFromDecisionDag(node.Syntax, node.ReachabilityDecisionDag, node.Expression, resultType, null);
|
|
enumerator = node.SwitchSections.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
ImmutableArray<BoundSwitchLabel>.Enumerator enumerator2 = enumerator.Current.SwitchLabels.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
BoundSwitchLabel current3 = enumerator2.Current;
|
|
SetState((((Dictionary<LabelSymbol, (LocalState, bool)>)(object)val).TryGetValue(current3.Label, out (LocalState, bool) value) ? value : (UnreachableState(), false)).Item1);
|
|
base.PendingBranches.Add(new AbstractFlowPass<LocalState, LocalFunctionState>.PendingBranch(current3, State, current3.Label));
|
|
}
|
|
}
|
|
(LocalState, bool) value2;
|
|
LocalState result = (((Dictionary<LabelSymbol, (LocalState, bool)>)(object)val).TryGetValue((LabelSymbol)node.BreakLabel, out value2) ? value2.Item1 : UnreachableState());
|
|
val.Free();
|
|
return result;
|
|
}
|
|
|
|
protected override void VisitSwitchSection(BoundSwitchSection node, bool isLastSection)
|
|
{
|
|
TakeIncrementalSnapshot(node);
|
|
SetState(UnreachableState());
|
|
ImmutableArray<BoundSwitchLabel>.Enumerator enumerator = node.SwitchLabels.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundSwitchLabel current = enumerator.Current;
|
|
TakeIncrementalSnapshot(current);
|
|
VisitForRewriting(current.Pattern);
|
|
if (!State.Reachable && current.WhenClause != null)
|
|
{
|
|
VisitForRewriting(current.WhenClause);
|
|
}
|
|
VisitLabel(current.Label, node);
|
|
}
|
|
VisitStatementList(node);
|
|
}
|
|
|
|
private PooledDictionary<LabelSymbol, (LocalState state, bool believedReachable)> LearnFromDecisionDag(SyntaxNode node, BoundDecisionDag decisionDag, BoundExpression expression, TypeWithState expressionTypeWithState, PossiblyConditionalState? stateWhenNotNullOpt)
|
|
{
|
|
//IL_033f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0344: Unknown result type (might be due to invalid IL or missing references)
|
|
BoundDagTemp boundDagTemp = BoundDagTemp.ForOriginalInput(expression);
|
|
int originalInputSlot = MakeSlot(expression);
|
|
TypeWithAnnotations typeWithAnnotations = expressionTypeWithState.ToTypeWithAnnotations(compilation);
|
|
if (originalInputSlot <= 0)
|
|
{
|
|
originalInputSlot = makeDagTempSlot(typeWithAnnotations, boundDagTemp);
|
|
if (!IsConditionalState)
|
|
{
|
|
TrackNullableStateForAssignment(null, typeWithAnnotations, originalInputSlot, expressionTypeWithState);
|
|
}
|
|
}
|
|
ImmutableArray<int> immutableArray = ((expression is BoundTupleExpression boundTupleExpression) ? ImmutableArrayExtensions.SelectAsArray<BoundExpression, NullableWalker, int>(boundTupleExpression.Arguments, (Func<BoundExpression, NullableWalker, int>)((BoundExpression a, NullableWalker w) => w.GetSlotForSwitchInputValue(a)), this) : default(ImmutableArray<int>));
|
|
PooledDictionary<int, BoundExpression> originalInputMap = PooledDictionary<int, BoundExpression>.GetInstance();
|
|
((Dictionary<int, BoundExpression>)(object)originalInputMap).Add(originalInputSlot, expression);
|
|
PooledDictionary<BoundDagTemp, (int slot, TypeSymbol type)> tempMap = PooledDictionary<BoundDagTemp, (int, TypeSymbol)>.GetInstance();
|
|
((Dictionary<BoundDagTemp, (int, TypeSymbol)>)(object)tempMap).Add(boundDagTemp, (originalInputSlot, expressionTypeWithState.Type));
|
|
PooledDictionary<BoundDecisionDagNode, (PossiblyConditionalState state, bool believedReachable)> nodeStateMap = PooledDictionary<BoundDecisionDagNode, (PossiblyConditionalState, bool)>.GetInstance();
|
|
((Dictionary<BoundDecisionDagNode, (PossiblyConditionalState, bool)>)(object)nodeStateMap).Add(decisionDag.RootNode, (PossiblyConditionalState.Create(this), true));
|
|
PooledDictionary<LabelSymbol, (LocalState, bool)> instance = PooledDictionary<LabelSymbol, (LocalState, bool)>.GetInstance();
|
|
ImmutableArray<BoundDecisionDagNode>.Enumerator enumerator = decisionDag.TopologicallySortedNodes.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundDecisionDagNode current = enumerator.Current;
|
|
((Dictionary<BoundDecisionDagNode, (PossiblyConditionalState, bool)>)(object)nodeStateMap).TryGetValue(current, out (PossiblyConditionalState, bool) value);
|
|
var (possiblyConditionalState, flag) = value;
|
|
if (possiblyConditionalState.IsConditionalState)
|
|
{
|
|
SetConditionalState(possiblyConditionalState.StateWhenTrue, possiblyConditionalState.StateWhenFalse);
|
|
}
|
|
else
|
|
{
|
|
SetState(possiblyConditionalState.State);
|
|
}
|
|
if (!(current is BoundEvaluationDecisionDagNode boundEvaluationDecisionDagNode))
|
|
{
|
|
if (!(current is BoundTestDecisionDagNode boundTestDecisionDagNode))
|
|
{
|
|
if (!(current is BoundLeafDecisionDagNode boundLeafDecisionDagNode))
|
|
{
|
|
if (!(current is BoundWhenDecisionDagNode boundWhenDecisionDagNode))
|
|
{
|
|
throw ExceptionUtilities.UnexpectedValue((object)current.Kind);
|
|
}
|
|
Unsplit();
|
|
ImmutableArray<BoundPatternBinding>.Enumerator enumerator2 = boundWhenDecisionDagNode.Bindings.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
BoundPatternBinding current2 = enumerator2.Current;
|
|
BoundExpression variableAccess = current2.VariableAccess;
|
|
BoundDagTemp tempContainingValue = current2.TempContainingValue;
|
|
if (!((Dictionary<BoundDagTemp, (int, TypeSymbol)>)(object)tempMap).TryGetValue(tempContainingValue, out (int, TypeSymbol) value2))
|
|
{
|
|
continue;
|
|
}
|
|
(int, TypeSymbol) tuple2 = value2;
|
|
int item = tuple2.Item1;
|
|
TypeSymbol item2 = tuple2.Item2;
|
|
NullableFlowState state = GetState(ref State, item);
|
|
if (variableAccess is BoundLocal { LocalSymbol: SourceLocalSymbol localSymbol } boundLocal)
|
|
{
|
|
TypeWithAnnotations typeWithAnnotations2 = TypeWithState.Create(item2, state).ToTypeWithAnnotations(compilation, boundLocal.DeclarationKind == BoundLocalDeclarationKind.WithInferredType);
|
|
if (_variables.TryGetType(localSymbol, out var type))
|
|
{
|
|
typeWithAnnotations2 = TypeWithAnnotations.Create(typeWithAnnotations2.Type, type.NullableAnnotation.Join(typeWithAnnotations2.NullableAnnotation));
|
|
}
|
|
_variables.SetType(localSymbol, typeWithAnnotations2);
|
|
int orCreateSlot = GetOrCreateSlot(localSymbol, 0, forceSlotEvenIfEmpty: true);
|
|
if (orCreateSlot > 0)
|
|
{
|
|
TrackNullableStateForAssignment(null, typeWithAnnotations2, orCreateSlot, TypeWithState.Create(item2, state), item);
|
|
}
|
|
}
|
|
}
|
|
if (boundWhenDecisionDagNode.WhenExpression != null && boundWhenDecisionDagNode.WhenExpression.ConstantValueOpt != ConstantValue.True)
|
|
{
|
|
VisitCondition(boundWhenDecisionDagNode.WhenExpression);
|
|
gotoNode(boundWhenDecisionDagNode.WhenTrue, StateWhenTrue, flag);
|
|
gotoNode(boundWhenDecisionDagNode.WhenFalse, StateWhenFalse, flag);
|
|
}
|
|
else
|
|
{
|
|
gotoNode(boundWhenDecisionDagNode.WhenTrue, State, flag);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Unsplit();
|
|
((Dictionary<LabelSymbol, (LocalState, bool)>)(object)instance).Add(boundLeafDecisionDagNode.Label, (State, flag));
|
|
}
|
|
continue;
|
|
}
|
|
BoundDagTest test = boundTestDecisionDagNode.Test;
|
|
((Dictionary<BoundDagTemp, (int, TypeSymbol)>)(object)tempMap).TryGetValue(test.Input, out (int, TypeSymbol) value3);
|
|
var (num, expressionType) = value3;
|
|
Split();
|
|
if (!(test is BoundDagTypeTest))
|
|
{
|
|
if (!(test is BoundDagNonNullTest boundDagNonNullTest))
|
|
{
|
|
if (!(test is BoundDagExplicitNullTest))
|
|
{
|
|
if (!(test is BoundDagValueTest boundDagValueTest))
|
|
{
|
|
if (test is BoundDagRelationalTest)
|
|
{
|
|
if (num > 0)
|
|
{
|
|
learnFromNonNullTest(num, ref StateWhenTrue);
|
|
}
|
|
gotoNode(boundTestDecisionDagNode.WhenTrue, StateWhenTrue, flag);
|
|
gotoNode(boundTestDecisionDagNode.WhenFalse, StateWhenFalse, flag);
|
|
continue;
|
|
}
|
|
throw ExceptionUtilities.UnexpectedValue((object)test.Kind);
|
|
}
|
|
if (stateWhenNotNullOpt.HasValue)
|
|
{
|
|
PossiblyConditionalState conditionalState = stateWhenNotNullOpt.GetValueOrDefault();
|
|
BoundDagEvaluation source = boundDagValueTest.Input.Source;
|
|
if (source is BoundDagTypeEvaluation)
|
|
{
|
|
BoundDagTemp input = source.Input;
|
|
if (input != null && input.IsOriginalInput)
|
|
{
|
|
SetPossiblyConditionalState(in conditionalState);
|
|
Split();
|
|
goto IL_0884;
|
|
}
|
|
}
|
|
}
|
|
if (num > 0)
|
|
{
|
|
learnFromNonNullTest(num, ref StateWhenTrue);
|
|
}
|
|
goto IL_0884;
|
|
}
|
|
if (num > 0)
|
|
{
|
|
LearnFromNullTest(num, expressionType, ref StateWhenTrue, markDependentSlotsNotNull: true);
|
|
learnFromNonNullTest(num, ref StateWhenFalse);
|
|
}
|
|
gotoNode(boundTestDecisionDagNode.WhenTrue, StateWhenTrue, flag);
|
|
gotoNode(boundTestDecisionDagNode.WhenFalse, StateWhenFalse, flag);
|
|
continue;
|
|
}
|
|
bool flag2 = GetState(ref StateWhenTrue, num).MayBeNull();
|
|
if (num > 0)
|
|
{
|
|
MarkDependentSlotsNotNull(num, expressionType, ref StateWhenFalse);
|
|
if (boundDagNonNullTest.IsExplicitTest)
|
|
{
|
|
LearnFromNullTest(num, expressionType, ref StateWhenFalse, markDependentSlotsNotNull: false);
|
|
}
|
|
learnFromNonNullTest(num, ref StateWhenTrue);
|
|
}
|
|
gotoNode(boundTestDecisionDagNode.WhenTrue, StateWhenTrue, flag);
|
|
gotoNode(boundTestDecisionDagNode.WhenFalse, StateWhenFalse, flag && flag2);
|
|
continue;
|
|
}
|
|
if (num > 0)
|
|
{
|
|
learnFromNonNullTest(num, ref StateWhenTrue);
|
|
}
|
|
gotoNode(boundTestDecisionDagNode.WhenTrue, StateWhenTrue, flag);
|
|
gotoNode(boundTestDecisionDagNode.WhenFalse, StateWhenFalse, flag);
|
|
continue;
|
|
}
|
|
BoundDagEvaluation evaluation = boundEvaluationDecisionDagNode.Evaluation;
|
|
if (!((Dictionary<BoundDagTemp, (int, TypeSymbol)>)(object)tempMap).TryGetValue(evaluation.Input, out (int, TypeSymbol) value4))
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs", 399);
|
|
}
|
|
var (num2, typeSymbol) = value4;
|
|
BoundDagTemp boundDagTemp2;
|
|
int num3;
|
|
if (!(evaluation is BoundDagDeconstructEvaluation boundDagDeconstructEvaluation))
|
|
{
|
|
if (evaluation is BoundDagTypeEvaluation boundDagTypeEvaluation)
|
|
{
|
|
boundDagTemp2 = new BoundDagTemp(boundDagTypeEvaluation.Syntax, boundDagTypeEvaluation.Type, boundDagTypeEvaluation);
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
ConversionKind kind = _conversions.WithNullability(includeNullability: false).ClassifyConversionFromType(typeSymbol, boundDagTypeEvaluation.Type, isChecked: false, ref useSiteInfo).Kind;
|
|
if (kind != ConversionKind.Identity && kind != ConversionKind.ImplicitReference)
|
|
{
|
|
if (kind == ConversionKind.ExplicitNullable && AreNullableAndUnderlyingTypes(typeSymbol, boundDagTypeEvaluation.Type, out var _))
|
|
{
|
|
num3 = GetNullableOfTValueSlot(typeSymbol, num2, out Symbol _, forceSlotEvenIfEmpty: true);
|
|
if (num3 >= 0)
|
|
{
|
|
goto IL_03d1;
|
|
}
|
|
}
|
|
num3 = makeDagTempSlot(TypeWithAnnotations.Create(boundDagTypeEvaluation.Type, NullableAnnotation.NotAnnotated), boundDagTemp2);
|
|
}
|
|
else
|
|
{
|
|
num3 = num2;
|
|
}
|
|
goto IL_03d1;
|
|
}
|
|
if (!(evaluation is BoundDagFieldEvaluation boundDagFieldEvaluation))
|
|
{
|
|
if (!(evaluation is BoundDagPropertyEvaluation boundDagPropertyEvaluation))
|
|
{
|
|
if (!(evaluation is BoundDagIndexEvaluation boundDagIndexEvaluation))
|
|
{
|
|
if (!(evaluation is BoundDagIndexerEvaluation boundDagIndexerEvaluation))
|
|
{
|
|
if (!(evaluation is BoundDagSliceEvaluation boundDagSliceEvaluation))
|
|
{
|
|
if (!(evaluation is BoundDagAssignmentEvaluation))
|
|
{
|
|
throw ExceptionUtilities.UnexpectedValue((object)boundEvaluationDecisionDagNode.Evaluation.Kind);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TypeWithAnnotations type2 = getIndexerOutputType(typeSymbol, boundDagSliceEvaluation.IndexerAccess, isSlice: true);
|
|
BoundDagTemp boundDagTemp3 = new BoundDagTemp(boundDagSliceEvaluation.Syntax, type2.Type, boundDagSliceEvaluation);
|
|
int slot = makeDagTempSlot(type2, boundDagTemp3);
|
|
addToTempMap(boundDagTemp3, slot, type2.Type);
|
|
SetState(ref State, slot, NullableFlowState.NotNull);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TypeWithAnnotations typeWithAnnotations3 = getIndexerOutputType(typeSymbol, boundDagIndexerEvaluation.IndexerAccess, isSlice: false);
|
|
BoundDagTemp boundDagTemp4 = new BoundDagTemp(boundDagIndexerEvaluation.Syntax, typeWithAnnotations3.Type, boundDagIndexerEvaluation);
|
|
int num4 = makeDagTempSlot(typeWithAnnotations3, boundDagTemp4);
|
|
TrackNullableStateForAssignment(null, typeWithAnnotations3, num4, typeWithAnnotations3.ToTypeWithState());
|
|
addToTempMap(boundDagTemp4, num4, typeWithAnnotations3.Type);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
addTemp(boundDagIndexEvaluation, boundDagIndexEvaluation.Property.Type);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PropertySymbol propertySymbol = (PropertySymbol)AsMemberOfType(typeSymbol, boundDagPropertyEvaluation.Property);
|
|
TypeWithAnnotations typeWithAnnotations4 = propertySymbol.TypeWithAnnotations;
|
|
BoundDagTemp boundDagTemp5 = new BoundDagTemp(boundDagPropertyEvaluation.Syntax, typeWithAnnotations4.Type, boundDagPropertyEvaluation);
|
|
int num5 = GetOrCreateSlot(propertySymbol, num2, forceSlotEvenIfEmpty: true);
|
|
if (num5 <= 0)
|
|
{
|
|
num5 = makeDagTempSlot(typeWithAnnotations4, boundDagTemp5);
|
|
}
|
|
addToTempMap(boundDagTemp5, num5, typeWithAnnotations4.Type);
|
|
if ((object)propertySymbol.GetMethod != null)
|
|
{
|
|
ApplyMemberPostConditions(num2, propertySymbol.GetMethod);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FieldSymbol fieldSymbol = (FieldSymbol)AsMemberOfType(typeSymbol, boundDagFieldEvaluation.Field);
|
|
TypeWithAnnotations typeWithAnnotations5 = fieldSymbol.TypeWithAnnotations;
|
|
BoundDagTemp boundDagTemp6 = new BoundDagTemp(boundDagFieldEvaluation.Syntax, typeWithAnnotations5.Type, boundDagFieldEvaluation);
|
|
int num6 = -1;
|
|
FieldSymbol fieldSymbol2 = ((boundDagFieldEvaluation.Input.IsOriginalInput && !immutableArray.IsDefault) ? fieldSymbol : null);
|
|
if ((object)fieldSymbol2 != null)
|
|
{
|
|
num6 = immutableArray[fieldSymbol2.TupleElementIndex];
|
|
}
|
|
if (num6 <= 0)
|
|
{
|
|
num6 = GetOrCreateSlot(fieldSymbol, num2, forceSlotEvenIfEmpty: true);
|
|
if ((object)fieldSymbol2 != null && num6 > 0 && !((Dictionary<int, BoundExpression>)(object)originalInputMap).ContainsKey(num6))
|
|
{
|
|
((Dictionary<int, BoundExpression>)(object)originalInputMap).Add(num6, ((BoundTupleExpression)expression).Arguments[fieldSymbol2.TupleElementIndex]);
|
|
}
|
|
}
|
|
if (num6 <= 0)
|
|
{
|
|
num6 = makeDagTempSlot(typeWithAnnotations5, boundDagTemp6);
|
|
}
|
|
addToTempMap(boundDagTemp6, num6, typeWithAnnotations5.Type);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MethodSymbol deconstructMethod = boundDagDeconstructEvaluation.DeconstructMethod;
|
|
int num7 = ((!deconstructMethod.RequiresInstanceReceiver) ? 1 : 0);
|
|
for (int num8 = 0; num8 < deconstructMethod.ParameterCount - num7; num8++)
|
|
{
|
|
TypeWithAnnotations typeWithAnnotations6 = deconstructMethod.Parameters[num8 + num7].TypeWithAnnotations;
|
|
BoundDagTemp boundDagTemp7 = new BoundDagTemp(boundDagDeconstructEvaluation.Syntax, typeWithAnnotations6.Type, boundDagDeconstructEvaluation, num8);
|
|
int slot2 = makeDagTempSlot(typeWithAnnotations6, boundDagTemp7);
|
|
addToTempMap(boundDagTemp7, slot2, typeWithAnnotations6.Type);
|
|
}
|
|
}
|
|
goto IL_065c;
|
|
IL_0884:
|
|
bool flag3 = boundDagValueTest.Value == ConstantValue.False;
|
|
gotoNode(boundTestDecisionDagNode.WhenTrue, flag3 ? StateWhenFalse : StateWhenTrue, flag);
|
|
gotoNode(boundTestDecisionDagNode.WhenFalse, flag3 ? StateWhenTrue : StateWhenFalse, flag);
|
|
continue;
|
|
IL_03d1:
|
|
Unsplit();
|
|
SetState(ref State, num3, NullableFlowState.NotNull);
|
|
addToTempMap(boundDagTemp2, num3, boundDagTypeEvaluation.Type);
|
|
goto IL_065c;
|
|
IL_065c:
|
|
gotoNodeWithCurrentState(boundEvaluationDecisionDagNode.Next, flag);
|
|
}
|
|
SetUnreachable();
|
|
originalInputMap.Free();
|
|
tempMap.Free();
|
|
nodeStateMap.Free();
|
|
return instance;
|
|
void addTemp(BoundDagEvaluation e, TypeSymbol t, int index = 0)
|
|
{
|
|
TypeWithAnnotations type3 = TypeWithAnnotations.Create(t, NullableAnnotation.Annotated);
|
|
BoundDagTemp boundDagTemp8 = new BoundDagTemp(e.Syntax, type3.Type, e, index);
|
|
int slot3 = makeDagTempSlot(type3, boundDagTemp8);
|
|
addToTempMap(boundDagTemp8, slot3, type3.Type);
|
|
}
|
|
void addToTempMap(BoundDagTemp output, int item3, TypeSymbol item4)
|
|
{
|
|
if (!((Dictionary<BoundDagTemp, (int, TypeSymbol)>)(object)tempMap).TryGetValue(output, out (int, TypeSymbol) _))
|
|
{
|
|
((Dictionary<BoundDagTemp, (int, TypeSymbol)>)(object)tempMap).Add(output, (item3, item4));
|
|
}
|
|
}
|
|
static TypeWithAnnotations getIndexerOutputType(TypeSymbol inputType, BoundExpression e, bool isSlice)
|
|
{
|
|
if (e is BoundIndexerAccess boundIndexerAccess)
|
|
{
|
|
return AsMemberOfType(inputType, boundIndexerAccess.Indexer).GetTypeOrReturnType();
|
|
}
|
|
if (e is BoundCall boundCall)
|
|
{
|
|
return AsMemberOfType(inputType, boundCall.Method).GetTypeOrReturnType();
|
|
}
|
|
if (e is BoundArrayAccess)
|
|
{
|
|
return isSlice ? TypeWithAnnotations.Create(isNullableEnabled: true, inputType) : ((ArrayTypeSymbol)inputType).ElementTypeWithAnnotations;
|
|
}
|
|
if (!(e is BoundImplicitIndexerAccess boundImplicitIndexerAccess))
|
|
{
|
|
throw ExceptionUtilities.UnexpectedValue((object)e.Kind);
|
|
}
|
|
return getIndexerOutputType(inputType, boundImplicitIndexerAccess.IndexerOrSliceAccess, isSlice);
|
|
}
|
|
void gotoNode(BoundDecisionDagNode key, LocalState other, bool believedReachable)
|
|
{
|
|
PossiblyConditionalState item3;
|
|
if (((Dictionary<BoundDecisionDagNode, (PossiblyConditionalState, bool)>)(object)nodeStateMap).TryGetValue(key, out (PossiblyConditionalState, bool) value5))
|
|
{
|
|
(item3, _) = value5;
|
|
if (item3.IsConditionalState)
|
|
{
|
|
Join(ref item3.StateWhenTrue, ref other);
|
|
Join(ref item3.StateWhenFalse, ref other);
|
|
}
|
|
else
|
|
{
|
|
Join(ref item3.State, ref other);
|
|
}
|
|
believedReachable |= value5.Item2;
|
|
}
|
|
else
|
|
{
|
|
item3 = new PossiblyConditionalState(other);
|
|
}
|
|
((Dictionary<BoundDecisionDagNode, (PossiblyConditionalState, bool)>)(object)nodeStateMap)[key] = (item3, believedReachable);
|
|
}
|
|
void gotoNodeWithCurrentState(BoundDecisionDagNode key, bool believedReachable)
|
|
{
|
|
if (((Dictionary<BoundDecisionDagNode, (PossiblyConditionalState, bool)>)(object)nodeStateMap).TryGetValue(key, out (PossiblyConditionalState, bool) value5))
|
|
{
|
|
bool isConditionalState = IsConditionalState;
|
|
bool isConditionalState2 = value5.Item1.IsConditionalState;
|
|
if (isConditionalState)
|
|
{
|
|
if (isConditionalState2)
|
|
{
|
|
Join(ref StateWhenTrue, ref value5.Item1.StateWhenTrue);
|
|
Join(ref StateWhenFalse, ref value5.Item1.StateWhenFalse);
|
|
}
|
|
else
|
|
{
|
|
Join(ref StateWhenTrue, ref value5.Item1.State);
|
|
Join(ref StateWhenFalse, ref value5.Item1.State);
|
|
}
|
|
}
|
|
else if (isConditionalState2)
|
|
{
|
|
Split();
|
|
Join(ref StateWhenTrue, ref value5.Item1.StateWhenTrue);
|
|
Join(ref StateWhenFalse, ref value5.Item1.StateWhenFalse);
|
|
}
|
|
else
|
|
{
|
|
Join(ref State, ref value5.Item1.State);
|
|
}
|
|
believedReachable |= value5.Item2;
|
|
}
|
|
((Dictionary<BoundDecisionDagNode, (PossiblyConditionalState, bool)>)(object)nodeStateMap)[key] = (PossiblyConditionalState.Create(this), believedReachable);
|
|
}
|
|
void learnFromNonNullTest(int inputSlot, ref LocalState reference)
|
|
{
|
|
if (stateWhenNotNullOpt.HasValue)
|
|
{
|
|
PossiblyConditionalState conditionalState2 = stateWhenNotNullOpt.GetValueOrDefault();
|
|
if (inputSlot == originalInputSlot)
|
|
{
|
|
reference = CloneAndUnsplit(ref conditionalState2);
|
|
}
|
|
}
|
|
LearnFromNonNullTest(inputSlot, ref reference);
|
|
if (((Dictionary<int, BoundExpression>)(object)originalInputMap).TryGetValue(inputSlot, out BoundExpression value5))
|
|
{
|
|
LearnFromNonNullTest(value5, ref reference);
|
|
}
|
|
}
|
|
int makeDagTempSlot(TypeWithAnnotations type3, BoundDagTemp temp)
|
|
{
|
|
object identifier = (node, temp);
|
|
return GetOrCreatePlaceholderSlot(identifier, type3);
|
|
}
|
|
}
|
|
|
|
public override BoundNode VisitConvertedSwitchExpression(BoundConvertedSwitchExpression node)
|
|
{
|
|
bool inferType = !node.WasTargetTyped;
|
|
VisitSwitchExpressionCore(node, inferType);
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitUnconvertedSwitchExpression(BoundUnconvertedSwitchExpression node)
|
|
{
|
|
VisitSwitchExpressionCore(node, inferType: true);
|
|
return null;
|
|
}
|
|
|
|
private void VisitSwitchExpressionCore(BoundSwitchExpression node, bool inferType)
|
|
{
|
|
//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0192: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0197: Unknown result type (might be due to invalid IL or missing references)
|
|
int slotForSwitchInputValue = GetSlotForSwitchInputValue(node.Expression);
|
|
ImmutableArray<BoundSwitchExpressionArm>.Enumerator enumerator;
|
|
if (slotForSwitchInputValue > 0)
|
|
{
|
|
TypeSymbol type = node.Expression.Type;
|
|
enumerator = node.SwitchArms.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundSwitchExpressionArm current = enumerator.Current;
|
|
LearnFromAnyNullPatterns(slotForSwitchInputValue, type, current.Pattern);
|
|
}
|
|
}
|
|
Visit(node.Expression);
|
|
TypeWithState resultType = ResultType;
|
|
PooledDictionary<LabelSymbol, (LocalState, bool)> val = LearnFromDecisionDag(node.Syntax, node.ReachabilityDecisionDag, node.Expression, resultType, null);
|
|
LocalState self = UnreachableState();
|
|
bool unnamedEnumValue;
|
|
if (!node.ReportedNotExhaustive && node.DefaultLabel != null && ((Dictionary<LabelSymbol, (LocalState, bool)>)(object)val).TryGetValue(node.DefaultLabel, out (LocalState, bool) value) && value.Item2)
|
|
{
|
|
SetState(value.Item1);
|
|
ImmutableArray<BoundDecisionDagNode> topologicallySortedNodes = node.ReachabilityDecisionDag.TopologicallySortedNodes;
|
|
BoundDecisionDagNode targetNode = topologicallySortedNodes.Where((BoundDecisionDagNode n) => n is BoundLeafDecisionDagNode boundLeafDecisionDagNode && boundLeafDecisionDagNode.Label == node.DefaultLabel).First();
|
|
bool requiresFalseWhenClause;
|
|
string text = PatternExplainer.SamplePatternForPathToDagNode(BoundDagTemp.ForOriginalInput(node.Expression), topologicallySortedNodes, targetNode, nullPaths: true, out requiresFalseWhenClause, out unnamedEnumValue);
|
|
ErrorCode errorCode = (requiresFalseWhenClause ? ErrorCode.WRN_SwitchExpressionNotExhaustiveForNullWithWhen : ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull);
|
|
SyntaxToken switchKeyword = ((SwitchExpressionSyntax)(object)node.Syntax).SwitchKeyword;
|
|
ReportDiagnostic(errorCode, ((SyntaxToken)(ref switchKeyword)).GetLocation(), text);
|
|
}
|
|
int length = node.SwitchArms.Length;
|
|
ArrayBuilder<Conversion> instance = ArrayBuilder<Conversion>.GetInstance(length);
|
|
ArrayBuilder<TypeWithState> instance2 = ArrayBuilder<TypeWithState>.GetInstance(length);
|
|
ArrayBuilder<BoundExpression> instance3 = ArrayBuilder<BoundExpression>.GetInstance(length);
|
|
ArrayBuilder<BoundExpression> instance4 = ArrayBuilder<BoundExpression>.GetInstance(length);
|
|
enumerator = node.SwitchArms.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
BoundSwitchExpressionArm current2 = enumerator.Current;
|
|
SetState(getStateForArm(current2, val));
|
|
TakeIncrementalSnapshot(current2);
|
|
VisitForRewriting(current2.Pattern);
|
|
if (!State.Reachable && current2.WhenClause != null)
|
|
{
|
|
VisitForRewriting(current2.WhenClause);
|
|
}
|
|
var (boundExpression, conversion) = RemoveConversion(current2.Value, includeExplicitConversions: false);
|
|
SnapshotWalkerThroughConversionGroup(current2.Value, boundExpression);
|
|
instance3.Add(boundExpression);
|
|
instance.Add(conversion);
|
|
TypeWithState typeWithState = VisitRvalueWithState(boundExpression);
|
|
instance2.Add(typeWithState);
|
|
Join(ref self, ref State);
|
|
instance4.Add(CreatePlaceholderIfNecessary(boundExpression, typeWithState.ToTypeWithAnnotations(compilation)));
|
|
}
|
|
SetState(self);
|
|
ImmutableArray<BoundExpression> exprs = instance4.ToImmutableAndFree();
|
|
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
|
|
TypeSymbol typeSymbol = (inferType ? BestTypeInferrer.InferBestType(exprs, _conversions, ref useSiteInfo, out unnamedEnumValue) : null) ?? node.Type?.SetUnknownNullabilityForReferenceTypes();
|
|
TypeWithAnnotations typeWithAnnotations = TypeWithAnnotations.Create(typeSymbol);
|
|
if (inferType && (object)typeSymbol == null)
|
|
{
|
|
NullableFlowState defaultState = NullableFlowState.NotNull;
|
|
TypeWithState resultType2 = TypeWithState.Create(typeSymbol, defaultState);
|
|
instance.Free();
|
|
instance2.Free();
|
|
instance3.Free();
|
|
val.Free();
|
|
SetResult(node, resultType2, typeWithAnnotations);
|
|
}
|
|
else
|
|
{
|
|
TypeWithState resultType2 = convertArms(node, val, instance, instance2, instance3, typeWithAnnotations, !inferType);
|
|
SetResult(node, resultType2, typeWithAnnotations, updateAnalyzedNullability: false);
|
|
}
|
|
void addConvertArmsAsCompletion(BoundSwitchExpression boundSwitchExpression, PooledDictionary<LabelSymbol, (LocalState state, bool believedReachable)> labelStateMap, ArrayBuilder<Conversion> conversions, ArrayBuilder<TypeWithState> resultTypes, ArrayBuilder<BoundExpression> expressions)
|
|
{
|
|
((Dictionary<BoundExpression, Func<TypeWithAnnotations, TypeWithState>>)(object)TargetTypedAnalysisCompletion)[(BoundExpression)boundSwitchExpression] = (TypeWithAnnotations inferredTypeWithAnnotations) => convertArms(boundSwitchExpression, labelStateMap, conversions, resultTypes, expressions, inferredTypeWithAnnotations, isTargetTyped: false);
|
|
}
|
|
TypeWithState convertArms(BoundSwitchExpression boundSwitchExpression, PooledDictionary<LabelSymbol, (LocalState state, bool believedReachable)> labelStateMap, ArrayBuilder<Conversion> conversions, ArrayBuilder<TypeWithState> resultTypes, ArrayBuilder<BoundExpression> expressions, TypeWithAnnotations inferredTypeWithAnnotations, bool isTargetTyped)
|
|
{
|
|
if (!isTargetTyped)
|
|
{
|
|
int length2 = boundSwitchExpression.SwitchArms.Length;
|
|
for (int i = 0; i < length2; i++)
|
|
{
|
|
BoundExpression operand = expressions[i];
|
|
BoundSwitchExpressionArm boundSwitchExpressionArm = boundSwitchExpression.SwitchArms[i];
|
|
LocalState state = getStateForArm(boundSwitchExpressionArm, labelStateMap);
|
|
resultTypes[i] = ConvertConditionalOperandOrSwitchExpressionArmResult(boundSwitchExpressionArm.Value, operand, conversions[i], inferredTypeWithAnnotations, resultTypes[i], state, state.Reachable);
|
|
}
|
|
}
|
|
NullableFlowState nullableState = BestTypeInferrer.GetNullableState(resultTypes);
|
|
if (!isTargetTyped)
|
|
{
|
|
conversions.Free();
|
|
resultTypes.Free();
|
|
expressions.Free();
|
|
labelStateMap.Free();
|
|
}
|
|
else
|
|
{
|
|
addConvertArmsAsCompletion(boundSwitchExpression, labelStateMap, conversions, resultTypes, expressions);
|
|
}
|
|
TypeWithState typeWithState2 = TypeWithState.Create(inferredTypeWithAnnotations.Type, nullableState);
|
|
if (!isTargetTyped)
|
|
{
|
|
SetAnalyzedNullability(boundSwitchExpression, typeWithState2);
|
|
}
|
|
return typeWithState2;
|
|
}
|
|
LocalState getStateForArm(BoundSwitchExpressionArm arm, PooledDictionary<LabelSymbol, (LocalState state, bool believedReachable)> labelStateMap)
|
|
{
|
|
if (arm.Pattern.HasErrors || !((Dictionary<LabelSymbol, (LocalState, bool)>)(object)labelStateMap).TryGetValue(arm.Label, out (LocalState, bool) value2))
|
|
{
|
|
return UnreachableState();
|
|
}
|
|
return value2.Item1;
|
|
}
|
|
}
|
|
|
|
private int GetSlotForSwitchInputValue(BoundExpression node)
|
|
{
|
|
if (!node.IsSuppressed)
|
|
{
|
|
return MakeSlot(node);
|
|
}
|
|
return GetOrCreatePlaceholderSlot(node);
|
|
}
|
|
|
|
public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node)
|
|
{
|
|
LearnFromAnyNullPatterns(node.Expression, node.Pattern);
|
|
VisitForRewriting(node.Pattern);
|
|
PossiblyConditionalState stateWhenNotNull;
|
|
bool flag = VisitPossibleConditionalAccess(node.Expression, out stateWhenNotNull);
|
|
TypeWithState resultType = ResultType;
|
|
PooledDictionary<LabelSymbol, (LocalState state, bool believedReachable)> obj = LearnFromDecisionDag(node.Syntax, node.ReachabilityDecisionDag, node.Expression, resultType, flag ? new PossiblyConditionalState?(stateWhenNotNull) : ((PossiblyConditionalState?)null));
|
|
(LocalState, bool) value;
|
|
LocalState whenTrue = (((Dictionary<LabelSymbol, (LocalState, bool)>)(object)obj).TryGetValue(node.IsNegated ? node.WhenFalseLabel : node.WhenTrueLabel, out value) ? value.Item1 : UnreachableState());
|
|
(LocalState, bool) value2;
|
|
LocalState whenFalse = (((Dictionary<LabelSymbol, (LocalState, bool)>)(object)obj).TryGetValue(node.IsNegated ? node.WhenTrueLabel : node.WhenFalseLabel, out value2) ? value2.Item1 : UnreachableState());
|
|
obj.Free();
|
|
SetConditionalState(whenTrue, whenFalse);
|
|
SetNotNullResult(node);
|
|
return null;
|
|
}
|
|
}
|