| File: Lowering\Extensions.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// 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. #nullable disable using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp { internal static partial class OperatorKindExtensions { public static RefKind RefKinds(this ImmutableArray<RefKind> ArgumentRefKinds, int index) { if (!ArgumentRefKinds.IsDefault && index < ArgumentRefKinds.Length) { return ArgumentRefKinds[index]; } else { return RefKind.None; } } } internal static partial class BoundExpressionExtensions { public static bool NullableAlwaysHasValue(this BoundExpression expr) { Debug.Assert(expr != null); if ((object)expr.Type == null) { return false; } if (expr.Type.IsDynamic()) { return false; } if (!expr.Type.IsNullableType()) { return true; } // new int?(123) always has a value: if (expr.Kind == BoundKind.ObjectCreationExpression) { var creation = (BoundObjectCreationExpression)expr; return creation.Constructor.ParameterCount != 0; } else if (expr.Kind == BoundKind.Conversion) { var conversion = (BoundConversion)expr; switch (conversion.ConversionKind) { case ConversionKind.ImplicitNullable: case ConversionKind.ExplicitNullable: // A conversion from X? to Y? will be non-null if the operand is non-null, // so simply recurse. return conversion.Operand.NullableAlwaysHasValue(); case ConversionKind.ImplicitEnumeration: // The C# specification categorizes conversion from literal zero to nullable enum as // an Implicit Enumeration Conversion. return conversion.Operand.NullableAlwaysHasValue(); } } return false; } public static bool NullableNeverHasValue(this BoundExpression expr) { Debug.Assert(expr != null); if ((object)expr.Type == null && expr.ConstantValueOpt == ConstantValue.Null) { return true; } if ((object)expr.Type == null || !expr.Type.IsNullableType()) { return false; } // "default(int?)" and "default" never have a value. if (expr is BoundDefaultLiteral || expr is BoundDefaultExpression) { return true; } // "new int?()" never has a value, but "new int?(x)" always does. if (expr.Kind == BoundKind.ObjectCreationExpression) { var creation = (BoundObjectCreationExpression)expr; return creation.Constructor.ParameterCount == 0; } if (expr.Kind == BoundKind.Conversion) { var conversion = (BoundConversion)expr; switch (conversion.ConversionKind) { case ConversionKind.NullLiteral: // Any null literal conversion is a conversion from the literal null to // a nullable value type; obviously it never has a value. return true; case ConversionKind.DefaultLiteral: // Any default literal to a nullable value type never has a value. return true; case ConversionKind.ImplicitNullable: case ConversionKind.ExplicitNullable: // A conversion from X? to Y? will be null if the operand is null, // so simply recurse. return conversion.Operand.NullableNeverHasValue(); } } // UNDONE: We could be more sophisticated here. For example, most lifted operators that have // UNDONE: a known-to-be-null operand are also known to be null. return false; } public static bool IsNullableNonBoolean(this BoundExpression expr) { Debug.Assert(expr != null); if (expr.Type.IsNullableType() && expr.Type.GetNullableUnderlyingType().SpecialType != SpecialType.System_Boolean) return true; return false; } } }