|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
/// <summary>
/// A type and its corresponding flow state resulting from evaluating an rvalue expression.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal readonly struct TypeWithState
{
public readonly TypeSymbol? Type;
public readonly NullableFlowState State;
[MemberNotNullWhen(false, nameof(Type))]
public bool HasNullType => Type is null;
public bool MayBeNull => State == NullableFlowState.MaybeNull;
public bool IsNotNull => State == NullableFlowState.NotNull;
public static TypeWithState ForType(TypeSymbol? type)
{
return Create(type, NullableFlowState.MaybeDefault);
}
public static TypeWithState Create(TypeSymbol? type, NullableFlowState defaultState)
{
if (defaultState == NullableFlowState.MaybeDefault &&
(type is null || type.IsTypeParameterDisallowingAnnotationInCSharp8()))
{
Debug.Assert(type?.IsNullableTypeOrTypeParameter() != true);
return new TypeWithState(type, defaultState);
}
var state = defaultState != NullableFlowState.NotNull && type?.CanContainNull() != false ? NullableFlowState.MaybeNull : NullableFlowState.NotNull;
return new TypeWithState(type, state);
}
public static TypeWithState Create(TypeWithAnnotations typeWithAnnotations, FlowAnalysisAnnotations annotations = FlowAnalysisAnnotations.None)
{
var type = typeWithAnnotations.Type;
Debug.Assert((object)type != null);
NullableFlowState state;
if (type.CanContainNull())
{
if ((annotations & FlowAnalysisAnnotations.MaybeNull) == FlowAnalysisAnnotations.MaybeNull)
{
state = NullableFlowState.MaybeDefault;
}
else if ((annotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull)
{
state = NullableFlowState.NotNull;
}
else
{
return typeWithAnnotations.ToTypeWithState();
}
}
else
{
state = NullableFlowState.NotNull;
}
return Create(type, state);
}
private TypeWithState(TypeSymbol? type, NullableFlowState state)
{
Debug.Assert(state == NullableFlowState.NotNull || type?.CanContainNull() != false);
Debug.Assert(state != NullableFlowState.MaybeDefault || type is null || type.IsTypeParameterDisallowingAnnotationInCSharp8());
Type = type;
State = state;
}
public string GetDebuggerDisplay() => $"{{Type:{Type?.GetDebuggerDisplay()}, State:{State}{"}"}";
public override string ToString() => GetDebuggerDisplay();
public TypeWithState WithNotNullState() => new TypeWithState(Type, NullableFlowState.NotNull);
public TypeWithState WithSuppression(bool suppress) => suppress ? new TypeWithState(Type, NullableFlowState.NotNull) : this;
public TypeWithAnnotations ToTypeWithAnnotations(CSharpCompilation compilation, bool asAnnotatedType = false)
{
if (Type?.IsTypeParameterDisallowingAnnotationInCSharp8() == true)
{
var type = TypeWithAnnotations.Create(Type, NullableAnnotation.NotAnnotated);
return (State == NullableFlowState.MaybeDefault || asAnnotatedType) ? type.SetIsAnnotated(compilation) : type;
}
NullableAnnotation annotation = asAnnotatedType ?
(Type?.IsValueType == true ? NullableAnnotation.NotAnnotated : NullableAnnotation.Annotated) :
(State.IsNotNull() || Type?.CanContainNull() == false ? NullableAnnotation.NotAnnotated : NullableAnnotation.Annotated);
return TypeWithAnnotations.Create(this.Type, annotation);
}
public TypeWithAnnotations ToAnnotatedTypeWithAnnotations(CSharpCompilation compilation) =>
ToTypeWithAnnotations(compilation, asAnnotatedType: true);
}
}
|