Exemplo n.º 1
0
        private bool ConversionExists(ExpressionSyntax whenTrue, ExpressionSyntax whenFalse)
        {
            var whenTrueInfo  = SemanticModel.GetTypeInfo(whenTrue);
            var whenFalseInfo = SemanticModel.GetTypeInfo(whenFalse);

            return(whenTrueInfo.Type != null &&
                   whenFalseInfo.Type != null &&
                   SemanticModel.ClassifyConversion(whenFalse, whenTrueInfo.Type).Exists &&
                   SemanticModel.ClassifyConversion(whenTrue, whenFalseInfo.Type).Exists);
        }
 private static bool CanRefactor(
     SeparatedSyntaxList <ExpressionSyntax> expressions,
     ITypeSymbol keyType,
     ITypeSymbol valueType,
     SemanticModel semanticModel)
 {
     return(expressions.Count == 2 &&
            semanticModel.ClassifyConversion(expressions[0], keyType).IsImplicit &&
            semanticModel.ClassifyConversion(expressions[1], valueType).IsImplicit);
 }
Exemplo n.º 3
0
        internal static bool IsExplicitConversion(
            this SemanticModel semanticModel,
            ExpressionSyntax expression,
            ITypeSymbol destinationType,
            bool isExplicitInSource = false)
        {
            if (semanticModel == null)
            {
                throw new ArgumentNullException(nameof(semanticModel));
            }

            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }

            if (destinationType == null)
            {
                throw new ArgumentNullException(nameof(destinationType));
            }

            if (!destinationType.IsErrorType() &&
                !destinationType.IsVoid())
            {
                Conversion conversion = semanticModel.ClassifyConversion(
                    expression,
                    destinationType,
                    isExplicitInSource);

                return(conversion.IsExplicit);
            }

            return(false);
        }
        static bool IsDeclarationConstFriendly(LocalDeclarationStatementSyntax declaration, SemanticModel semanticModel)
        {
            // all variables could be const?
            foreach (var variable in declaration.Declaration.Variables)
            {
                if (variable.Initializer == null)
                {
                    return(false);
                }

                // is constant
                var constantValue   = semanticModel.GetConstantValue(variable.Initializer.Value);
                var valueIsConstant = constantValue.HasValue;
                if (!valueIsConstant)
                {
                    return(false);
                }

                // if reference type, value is null?
                var variableTypeName = declaration.Declaration.Type;
                var variableType     = semanticModel.GetTypeInfo(variableTypeName).ConvertedType;
                if (variableType.IsReferenceType && variableType.SpecialType != SpecialType.System_String && constantValue.Value != null)
                {
                    return(false);
                }

                // value can be converted to variable type?
                var conversion = semanticModel.ClassifyConversion(variable.Initializer.Value, variableType);
                if (!conversion.Exists || conversion.IsUserDefined)
                {
                    return(false);
                }
            }
            return(true);
        }
Exemplo n.º 5
0
        private static SyntaxKind GetReplacementKind(
            ReturnStatementSyntax returnStatement,
            ISymbol containingSymbol,
            SemanticModel semanticModel,
            CancellationToken cancellationToken)
        {
            SyntaxToken      returnKeyword = returnStatement.ReturnKeyword;
            ExpressionSyntax expression    = returnStatement.Expression;

            if (semanticModel.ContainsCompilerDiagnostic(CSharpErrorCodes.CannotImplicitlyConvertType, expression.Span, cancellationToken))
            {
                return(SyntaxKind.YieldReturnStatement);
            }
            else if (semanticModel.ContainsCompilerDiagnostic(CSharpErrorCodes.CannotReturnValueFromIterator, returnKeyword.Span, cancellationToken))
            {
                ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression, cancellationToken);

                containingSymbol = containingSymbol ?? semanticModel.GetEnclosingSymbol(returnStatement.SpanStart, cancellationToken);

                if (containingSymbol?.IsKind(SymbolKind.Method) == true)
                {
                    var methodSymbol = (IMethodSymbol)containingSymbol;

                    ITypeSymbol returnType = methodSymbol.ReturnType;

                    if (returnType.SpecialType == SpecialType.System_Collections_IEnumerable)
                    {
                        if (typeSymbol.IsIEnumerableOrConstructedFromIEnumerableOfT())
                        {
                            return(SyntaxKind.ForEachStatement);
                        }
                        else
                        {
                            return(SyntaxKind.YieldReturnStatement);
                        }
                    }
                    else if (returnType.IsNamedType())
                    {
                        var namedTypeSymbol = (INamedTypeSymbol)returnType;

                        if (namedTypeSymbol.ConstructedFrom.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T)
                        {
                            if (semanticModel
                                .ClassifyConversion(expression, namedTypeSymbol.TypeArguments[0])
                                .IsImplicit)
                            {
                                return(SyntaxKind.YieldReturnStatement);
                            }
                            else
                            {
                                return(SyntaxKind.ForEachStatement);
                            }
                        }
                    }
                }
            }

            return(SyntaxKind.None);
        }
Exemplo n.º 6
0
 private static bool AllTypesAreConvertible(SemanticModel semanticModel, ArrayCreationExpressionSyntax arrayCreation, IArrayTypeSymbol arrayType)
 {
     return(arrayCreation.Initializer.Initializers.All(initializer =>
     {
         var conversion = semanticModel.ClassifyConversion(initializer, arrayType.ElementType);
         return conversion.Exists && (conversion.IsIdentity || conversion.IsWidening);
     }));
 }
Exemplo n.º 7
0
        private static bool IsIdentityOrImplicitConversion(
            ExpressionSyntax expression,
            ITypeSymbol destinationType,
            SemanticModel semanticModel)
        {
            Conversion conversion = semanticModel.ClassifyConversion(expression, destinationType);

            return(conversion.IsIdentity ||
                   conversion.IsImplicit);
        }
Exemplo n.º 8
0
        /// <summary>
        ///   Checks whether <paramref name="expression" /> is implicitly converted to <see cref="Formula" />.
        ///   If so, replaces the implicit conversion by an invocation of the corresponding state expression factory method.
        /// </summary>
        private ExpressionSyntax ReplaceImplicitConversion(ExpressionSyntax expression)
        {
            if (SemanticModel.GetTypeInfo(expression).Type.IsDerivedFrom(_formulaType))
            {
                return((ExpressionSyntax)Visit(expression));
            }

            var conversion = SemanticModel.ClassifyConversion(expression, _formulaType);

            if (conversion.IsUserDefined && conversion.IsImplicit)
            {
                return(CreateInvocation((ExpressionSyntax)Visit(expression)));
            }

            return((ExpressionSyntax)Visit(expression));
        }
Exemplo n.º 9
0
        public static MyInfo GetTypeInfo2(this SemanticModel model, ExpressionSyntax expression)
        {
            var typeInfo = ModelExtensions.GetTypeInfo(model, expression);

            if (typeInfo.ConvertedType == null)
            {
                Debug.Write("");
            }
            var g = new MyInfo
            {
                Conversion1 = typeInfo.ConvertedType != null
                ? (Conversion?)model.ClassifyConversion(expression, typeInfo.ConvertedType)
                : null,
                Conversion2 = model.GetConversion(expression),
                TypeInfo    = typeInfo
            };

            return(g);
        }
Exemplo n.º 10
0
        private static bool MightBeConvertibleToInt(ExpressionSyntax expression, SemanticModel semanticModel, out ITypeSymbol type)
        {
            type = semanticModel.GetTypeInfo(expression).Type;
            if (type is IErrorTypeSymbol)
            {
                return(true);
            }

            var intType = semanticModel.Compilation.GetTypeByMetadataName("System.Int32");

            if (intType == null)
            {
                return(false);
            }

            var conversion = semanticModel.ClassifyConversion(expression, intType);

            return(conversion.Exists && (conversion.IsIdentity || conversion.IsImplicit));
        }
Exemplo n.º 11
0
        internal static bool IsImplicitConversion(
            this SemanticModel semanticModel,
            ExpressionSyntax expression,
            ITypeSymbol destinationType,
            bool isExplicitInSource = false)
        {
            if (!destinationType.IsErrorType() &&
                !destinationType.IsVoid())
            {
                Conversion conversion = semanticModel.ClassifyConversion(
                    expression,
                    destinationType,
                    isExplicitInSource);

                return(conversion.IsImplicit);
            }

            return(false);
        }
Exemplo n.º 12
0
        private static bool ArgumentsMatchParameters(ArgumentListSyntax argumentList, List <INamedTypeSymbol> argumentTypes,
                                                     IMethodSymbol possibleOtherMethod, SemanticModel semanticModel)
        {
            var methodParameterLookup = new CSharpMethodParameterLookup(argumentList, possibleOtherMethod);

            var matchedParameters = new List <IParameterSymbol>();

            for (var i = 0; i < argumentList.Arguments.Count; i++)
            {
                var argument     = argumentList.Arguments[i];
                var argumentType = argumentTypes[i];
                if (!methodParameterLookup.TryGetSymbol(argument, out var parameter))
                {
                    return(false);
                }

                if (argumentType == null)
                {
                    if (!parameter.Type.IsReferenceType)
                    {
                        return(false);
                    }
                }
                else
                {
                    var conversion = semanticModel.ClassifyConversion(argument.Expression, parameter.Type);
                    if (!conversion.IsImplicit)
                    {
                        return(false);
                    }
                }

                matchedParameters.Add(parameter);
            }

            var nonMatchedParameters = possibleOtherMethod.Parameters.Except(matchedParameters);

            return(nonMatchedParameters.All(p => p.HasExplicitDefaultValue));
        }
Exemplo n.º 13
0
        internal static bool IsImplicitConversion(
            this SemanticModel semanticModel,
            ExpressionSyntax expression,
            ITypeSymbol destinationType,
            bool isExplicitInSource = false)
        {
            if (semanticModel == null)
            {
                throw new ArgumentNullException(nameof(semanticModel));
            }

            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }

            if (destinationType == null)
            {
                throw new ArgumentNullException(nameof(destinationType));
            }

            if (destinationType.Kind == SymbolKind.ErrorType)
            {
                return(false);
            }

            if (destinationType.SpecialType == SpecialType.System_Void)
            {
                return(false);
            }

            Conversion conversion = semanticModel.ClassifyConversion(
                expression,
                destinationType,
                isExplicitInSource);

            return(conversion.IsImplicit);
        }
Exemplo n.º 14
0
        private static void ChangeTypeAccordingToExpression(
            RefactoringContext context,
            VariableDeclarationSyntax variableDeclaration,
            ITypeSymbol typeSymbol,
            SemanticModel semanticModel)
        {
            foreach (VariableDeclaratorSyntax variableDeclarator in variableDeclaration.Variables)
            {
                ExpressionSyntax value = variableDeclarator.Initializer?.Value;

                if (value == null)
                {
                    return;
                }

                Conversion conversion = semanticModel.ClassifyConversion(value, typeSymbol);

                if (conversion.IsIdentity)
                {
                    return;
                }

                if (!conversion.IsImplicit)
                {
                    return;
                }
            }

            ITypeSymbol newTypeSymbol = semanticModel.GetTypeSymbol(variableDeclaration.Variables[0].Initializer.Value, context.CancellationToken);

            if (newTypeSymbol == null)
            {
                return;
            }

            context.RegisterRefactoring(CodeActionFactory.ChangeType(context.Document, variableDeclaration.Type, newTypeSymbol, semanticModel, equivalenceKey: RefactoringIdentifiers.ChangeTypeAccordingToExpression));
        }
Exemplo n.º 15
0
        private static bool CanBeConvertedTo(ExpressionSyntax expression, ITypeSymbol type, SemanticModel model)
        {
            var conversion = model.ClassifyConversion(expression, type);

            return(conversion.Exists && (conversion.IsIdentity || conversion.IsImplicit));
        }
Exemplo n.º 16
0
        private static bool CanBeMadeConst(LocalDeclarationStatementSyntax localDeclaration, SemanticModel semanticModel)
        {
            // already const?
            if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword))
            {
                return(false);
            }

            // Ensure that all variables in the local declaration have initializers that
            // are assigned with constant values.
            foreach (var variable in localDeclaration.Declaration.Variables)
            {
                var initializer = variable.Initializer;
                if (initializer == null)
                {
                    return(false);
                }

                var constantValue = semanticModel.GetConstantValue(initializer.Value);
                if (!constantValue.HasValue)
                {
                    return(false);
                }

                var variableTypeName = localDeclaration.Declaration.Type;
                var variableType     = semanticModel.GetTypeInfo(variableTypeName).ConvertedType;

                // Ensure that the initializer value can be converted to the type of the
                // local declaration without a user-defined conversion.
                var conversion = semanticModel.ClassifyConversion(initializer.Value, variableType);
                if (!conversion.Exists || conversion.IsUserDefined)
                {
                    return(false);
                }

                // Special cases:
                //   * If the constant value is a string, the type of the local declaration
                //     must be System.String.
                //   * If the constant value is null, the type of the local declaration must
                //     be a reference type.
                if (constantValue.Value is string)
                {
                    if (variableType.SpecialType != SpecialType.System_String)
                    {
                        return(false);
                    }
                }
                else if (variableType.IsReferenceType && constantValue.Value != null)
                {
                    return(false);
                }
            }

            // Perform data flow analysis on the local declaration.
            var dataFlowAnalysis = semanticModel.AnalyzeDataFlow(localDeclaration);

            // Retrieve the local symbol for each variable in the local declaration
            // and ensure that it is not written outside of the data flow analysis region.
            foreach (var variable in localDeclaration.Declaration.Variables)
            {
                var variableSymbol = semanticModel.GetDeclaredSymbol(variable);
                if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
                {
                    return(false);
                }
            }

            return(true);
        }
Exemplo n.º 17
0
        /// <summary>
        /// This method finds all posible const value
        /// </summary>
        /// <param name="root"><see cref="SyntaxNode"/> in which diagnostics are searched</param>
        /// <param name="semanticModel"><see cref="semanticModel"/> which is used to flow analysis</param>
        /// <returns>Diagnostics with possible const values</returns>
        public static IEnumerable <DiagnosticHelper> FindPossibleConstVariables(this SyntaxNode root,
                                                                                SemanticModel semanticModel)
        => root.DescendantNodes()
        .OfType <LocalDeclarationStatementSyntax>()
        .Where(declaration => !declaration.Modifiers.Any(SyntaxKind.ConstKeyword))
        .Where(declaration => {
            // Retrieve detailed information about the declared type of the local declaration
            TypeSyntax variableTypeName = declaration.Declaration.Type;
            ITypeSymbol variableType    = semanticModel.GetTypeInfo(variableTypeName).ConvertedType;

            // Perform data flow analysis on the local declaration.
            DataFlowAnalysis dataFlowAnalysis = semanticModel.AnalyzeDataFlow(declaration);

            // Retrieve the local symbol for each variable in the local declaration
            foreach (VariableDeclaratorSyntax variable in declaration.Declaration.Variables)
            {
                // Ensure that variable has initializers that are assigned with constant values.
                EqualsValueClauseSyntax initializer = variable.Initializer;
                if (initializer == null)
                {
                    return(false);
                }

                var constantValue = semanticModel.GetConstantValue(initializer.Value);
                if (!constantValue.HasValue)
                {
                    return(false);
                }

                // Ensure that the initializer value can be converted to the type of the
                // local declaration without a user-defined conversion.
                Conversion conversion = semanticModel.ClassifyConversion(initializer.Value, variableType);
                if (!conversion.Exists || conversion.IsUserDefined)
                {
                    return(false);
                }

                // Special cases:
                //   * If the constant value is a string, the type of the local declaration must be System.String.
                //   * If the constant value is null, the type of the local declaration must be a reference type.
                if (constantValue.Value is string)
                {
                    if (variableType.SpecialType != SpecialType.System_String)
                    {
                        return(false);
                    }
                }
                else if (variableType.IsReferenceType && constantValue.Value != null)
                {
                    return(false);
                }

                // Ensure that variable is not written outside of the data flow analysis region.
                ISymbol variableSymbol = semanticModel.GetDeclaredSymbol(variable);
                if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
                {
                    return(false);
                }
            }

            // Finally, it can be const
            return(true);
        })
        .Select(declaration => DiagnosticHelper.Create(declaration, "Variable can be const",
                                                       new AddConstModifier(semanticModel)));
Exemplo n.º 18
0
 protected override Conversion ClassifyConversion(SemanticModel model, ExpressionSyntax expression, ITypeSymbol targetType) =>
 model.ClassifyConversion(expression, targetType);
        public static bool IsUnnecessaryCast(this CastExpressionSyntax cast, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var speculationAnalyzer = new SpeculationAnalyzer(cast,
                                                              cast.Expression, semanticModel, cancellationToken,
                                                              skipVerificationForReplacedNode: true, failOnOverloadResolutionFailuresInOriginalCode: true);

            // First, check to see if the node ultimately parenting this cast has any
            // syntax errors. If so, we bail.
            if (speculationAnalyzer.SemanticRootOfOriginalExpression.ContainsDiagnostics)
            {
                return(false);
            }

            var castTypeInfo = semanticModel.GetTypeInfo(cast, cancellationToken);
            var castType     = castTypeInfo.Type;

            // Case:
            // 1 . Console.WriteLine(await (dynamic)task); Any Dynamic Cast will not be removed.
            if (castType == null || castType.Kind == SymbolKind.DynamicType || castType.IsErrorType())
            {
                return(false);
            }

            var expressionTypeInfo = semanticModel.GetTypeInfo(cast.Expression, cancellationToken);
            var expressionType     = expressionTypeInfo.Type;

            // We do not remove any cast on
            // 1. Dynamic Expressions
            // 2. If there is any other argument which is dynamic
            // 3. Dynamic Invocation
            if ((expressionType != null &&
                 (expressionType.IsErrorType() ||
                  expressionType.Kind == SymbolKind.DynamicType)) ||
                IsDynamicInvocation(cast, semanticModel, cancellationToken))
            {
                return(false);
            }

            if (PointerCastDefinitelyCantBeRemoved(cast))
            {
                return(false);
            }

            if (CastPassedToParamsArrayDefinitelyCantBeRemoved(cast, castType, semanticModel, cancellationToken))
            {
                return(false);
            }

            if (speculationAnalyzer.ReplacementChangesSemantics())
            {
                return(false);
            }

            var expressionToCastType = semanticModel.ClassifyConversion(cast.SpanStart, cast.Expression, castType, isExplicitInSource: true);

            bool parentIsOrAsExpression;
            var  outerType = GetOuterCastType(cast, semanticModel, out parentIsOrAsExpression) ?? castTypeInfo.ConvertedType;

            // Simple case: If the conversion from the inner expression to the cast type is identity,
            // the cast can be removed.
            if (expressionToCastType.IsIdentity)
            {
                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      (object)x == "Hi!"
                ExpressionSyntax other;
                if (IsRequiredCastForReferenceEqualityComparison(outerType, cast, semanticModel, out other))
                {
                    var otherToOuterType = semanticModel.ClassifyConversion(other, outerType);
                    if (otherToOuterType.IsImplicit && otherToOuterType.IsReference)
                    {
                        return(false);
                    }
                }

                return(true);
            }

            if (parentIsOrAsExpression)
            {
                // Note: speculationAnalyzer.ReplacementChangesSemantics() ensures that the parenting is or as expression are not broken.
                // Here we just need to ensure that the original cast expression doesn't invoke a user defined operator.
                return(!expressionToCastType.IsUserDefined);
            }

            if (outerType != null)
            {
                var castToOuterType       = semanticModel.ClassifyConversion(cast.SpanStart, cast, outerType);
                var expressionToOuterType = GetSpeculatedExpressionToOuterTypeConversion(speculationAnalyzer.ReplacedExpression, speculationAnalyzer, cancellationToken);

                // CONSIDER: Anonymous function conversions cannot be compared from different semantic models as lambda symbol comparison requires syntax tree equality. Should this be a compiler bug?
                // For now, just revert back to computing expressionToOuterType using the original semantic model.
                if (expressionToOuterType.IsAnonymousFunction)
                {
                    expressionToOuterType = semanticModel.ClassifyConversion(cast.SpanStart, cast.Expression, outerType);
                }

                // If there is an user-defined conversion from the expression to the cast type or the cast
                // to the outer type, we need to make sure that the same user-defined conversion will be
                // called if the cast is removed.
                if (castToOuterType.IsUserDefined || expressionToCastType.IsUserDefined)
                {
                    return(!expressionToOuterType.IsExplicit &&
                           (HaveSameUserDefinedConversion(expressionToCastType, expressionToOuterType) ||
                            HaveSameUserDefinedConversion(castToOuterType, expressionToOuterType)) &&
                           UserDefinedConversionIsAllowed(cast, semanticModel));
                }
                else if (expressionToOuterType.IsUserDefined)
                {
                    return(false);
                }

                if (expressionToCastType.IsExplicit &&
                    expressionToOuterType.IsExplicit)
                {
                    return(false);
                }

                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      x == (object)"Hi!"
                ExpressionSyntax other;
                if (expressionToCastType.IsImplicit && expressionToCastType.IsReference &&
                    castToOuterType.IsIdentity &&
                    IsRequiredCastForReferenceEqualityComparison(outerType, cast, semanticModel, out other))
                {
                    return(false);
                }

                // If the conversion from the expression to the cast type is implicit numeric or constant
                // and the conversion from the expression to the outer type is identity, we'll go ahead
                // and remove the cast.
                if (expressionToOuterType.IsIdentity &&
                    expressionToCastType.IsImplicit &&
                    (expressionToCastType.IsNumeric || expressionToCastType.IsConstantExpression))
                {
                    return(true);
                }

                if (!castToOuterType.IsBoxing &&
                    castToOuterType == expressionToOuterType)
                {
                    if (castToOuterType.IsNullable)
                    {
                        // Even though both the nullable conversions (castToOuterType and expressionToOuterType) are equal, we can guarantee no data loss only if there is an
                        // implicit conversion from expression type to cast type and expression type is non-nullable. For example, consider the cast removal "(float?)" for below:

                        // Console.WriteLine((int)(float?)(int?)2147483647); // Prints -2147483648

                        // castToOuterType:         ExplicitNullable
                        // expressionToOuterType:   ExplicitNullable
                        // expressionToCastType:    ImplicitNullable

                        // We should not remove the cast to "float?".
                        // However, cast to "int?" is unnecessary and should be removable.
                        return(expressionToCastType.IsImplicit && ((ITypeSymbol)expressionType.OriginalDefinition).SpecialType != SpecialType.System_Nullable_T);
                    }
                    else if (expressionToCastType.IsImplicit && expressionToCastType.IsNumeric && !castToOuterType.IsIdentity)
                    {
                        // Some implicit numeric conversions can cause loss of precision and must not be removed.
                        return(!IsRequiredImplicitNumericConversion(expressionType, castType));
                    }

                    return(true);
                }

                if (castToOuterType.IsIdentity &&
                    !expressionToCastType.IsUnboxing &&
                    expressionToCastType == expressionToOuterType)
                {
                    return(true);
                }

                // Special case: It's possible to have useless casts inside delegate creation expressions.
                // For example: new Func<string, bool>((Predicate<object>)(y => true)).
                if (IsInDelegateCreationExpression(cast, semanticModel))
                {
                    if (expressionToCastType.IsAnonymousFunction && expressionToOuterType.IsAnonymousFunction)
                    {
                        return(!speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(cast.Expression, speculationAnalyzer.ReplacedExpression));
                    }

                    if (expressionToCastType.IsMethodGroup && expressionToOuterType.IsMethodGroup)
                    {
                        return(true);
                    }
                }

                // Case :
                // 1. IList<object> y = (IList<dynamic>)new List<object>()
                if (expressionToCastType.IsExplicit && castToOuterType.IsExplicit && expressionToOuterType.IsImplicit)
                {
                    return(true);
                }

                // Case :
                // 2. object y = (ValueType)1;
                if (expressionToCastType.IsBoxing && expressionToOuterType.IsBoxing && castToOuterType.IsImplicit)
                {
                    return(true);
                }

                // Case :
                // 3. object y = (NullableValueType)null;
                if ((!castToOuterType.IsBoxing || expressionToCastType.IsNullLiteral) &&
                    castToOuterType.IsImplicit &&
                    expressionToCastType.IsImplicit &&
                    expressionToOuterType.IsImplicit)
                {
                    if (expressionToOuterType.IsAnonymousFunction)
                    {
                        return(expressionToCastType.IsAnonymousFunction &&
                               !speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(cast.Expression, speculationAnalyzer.ReplacedExpression));
                    }

                    return(true);
                }

                // case :
                // 4. baseType x;
                //    baseType y = (DerivedType)x;
                if (expressionToOuterType.IsIdentity &&
                    castToOuterType.IsImplicit &&
                    castToOuterType.IsReference)
                {
                    return(true);
                }
            }

            return(false);
        }
Exemplo n.º 20
0
        internal static bool?TryFindFirstMisMatch(ImmutableArray <IParameterSymbol> parameters, ImmutableArray <ExpressionSyntax> values, SemanticModel semanticModel, CancellationToken cancellationToken, out ExpressionSyntax?expression)
        {
            if (parameters.IsEmpty &&
                !values.IsEmpty)
            {
                expression = null;
                return(true);
            }

            if (parameters.TryFirst(x => x.RefKind == RefKind.Ref || x.RefKind == RefKind.Out, out _))
            {
                expression = null;
                return(null);
            }

            if (values.Length != parameters.Length)
            {
                if (parameters.TryLast(out var last) &&
                    !last.IsParams)
                {
                    expression = null;
                    return(true);
                }

                if (values.Length < parameters.Length - 1)
                {
                    expression = null;
                    return(true);
                }
            }

            IParameterSymbol?lastParameter = null;

            for (var i = 0; i < values.Length; i++)
            {
                expression = values[i];
                if (lastParameter is null ||
                    !lastParameter.IsParams)
                {
                    if (!parameters.TryElementAt(i, out lastParameter))
                    {
                        return(true);
                    }
                }

                if (lastParameter.IsOptional &&
                    semanticModel.TryGetSymbol(values[i], cancellationToken, out IFieldSymbol? field) &&
                    field == KnownSymbol.Missing.Value)
                {
                    continue;
                }

                if (IsNull(values[i]))
                {
                    if (lastParameter.IsParams)
                    {
                        return(true);
                    }

                    continue;
                }

                var conversion = semanticModel.ClassifyConversion(values[i], lastParameter.Type);
                if (!conversion.Exists)
                {
                    if (lastParameter.Type is IArrayTypeSymbol arrayType &&
                        semanticModel.ClassifyConversion(values[i], arrayType.ElementType).IsIdentity)
                    {
                        continue;
                    }

                    return(true);
                }

                if (conversion.IsIdentity || conversion.IsImplicit)
                {
                    if (values[i] is MemberAccessExpressionSyntax memberAccess &&
                        memberAccess.Name.Identifier.ValueText == "Value" &&
                        semanticModel.TryGetSymbol(values[i], cancellationToken, out field) &&
                        field == KnownSymbol.Missing.Value)
                    {
                        return(true);
                    }

                    continue;
                }

                if (conversion.IsExplicit &&
                    conversion.IsReference)
                {
                    continue;
                }

                return(true);
            }

            expression = null;
            return(false);
Exemplo n.º 21
0
        private static void AssertInfoForDeclarationExpressionSyntax(SemanticModel model, DeclarationExpressionSyntax decl)
        {
            var symbolInfo = model.GetSymbolInfo(decl);
            Assert.Null(symbolInfo.Symbol);
            Assert.Empty(symbolInfo.CandidateSymbols);
            Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
            Assert.Equal(symbolInfo, ((CSharpSemanticModel)model).GetSymbolInfo(decl));

            var typeInfo = model.GetTypeInfo(decl);
            Assert.Null(typeInfo.Type);
            Assert.Null(typeInfo.ConvertedType);
            Assert.Equal(typeInfo, ((CSharpSemanticModel)model).GetTypeInfo(decl));

            var conversion = model.ClassifyConversion(decl, model.Compilation.ObjectType, false);
            Assert.False(conversion.Exists);
            Assert.Equal(conversion, model.ClassifyConversion(decl, model.Compilation.ObjectType, true));
            Assert.Equal(conversion, ((CSharpSemanticModel)model).ClassifyConversion(decl, model.Compilation.ObjectType, false));
            Assert.Equal(conversion, ((CSharpSemanticModel)model).ClassifyConversion(decl, model.Compilation.ObjectType, true));
            Assert.Equal(conversion, model.ClassifyConversion(decl.Position, decl, model.Compilation.ObjectType, false));
            Assert.Equal(conversion, model.ClassifyConversion(decl.Position, decl, model.Compilation.ObjectType, true));
            Assert.Equal(conversion, ((CSharpSemanticModel)model).ClassifyConversion(decl.Position, decl, model.Compilation.ObjectType, false));
            Assert.Equal(conversion, ((CSharpSemanticModel)model).ClassifyConversion(decl.Position, decl, model.Compilation.ObjectType, true));

            Assert.Null(model.GetDeclaredSymbol(decl));
        }
Exemplo n.º 22
0
        private void ConversionTestHelper(SemanticModel semanticModel, ExpressionSyntax expr, ITypeSymbol expsym, ConversionKind expkind)
        {
            var info = semanticModel.GetTypeInfo(expr);
            Assert.NotNull(info);
            Assert.NotNull(info.ConvertedType);

            // NOT expect NoConversion
            Conversion act1 = semanticModel.ClassifyConversion(expr, expsym);
            Assert.Equal(expkind, act1.Kind);
            ValidateConversion(act1, expkind);
        }
 public override bool CanImplicitlyConvert(SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol targetType)
 {
     return(syntax is ExpressionSyntax expressionSyntax &&
            semanticModel.ClassifyConversion(expressionSyntax, targetType).IsImplicit);
 }
Exemplo n.º 24
0
        protected override bool CanBeConvertedTo(ExpressionSyntax expression, ITypeSymbol type, SemanticModel semanticModel)
        {
            var conversion = semanticModel.ClassifyConversion(expression, type);

            return(conversion.Exists && (conversion.IsIdentity || conversion.IsImplicit));
        }
Exemplo n.º 25
0
        private bool IsArgumentExceptionType(TypeSyntax typeSyntax)
        {
            var conversion = _semanticModel.ClassifyConversion(typeSyntax, _argumentExceptionType);

            return(conversion.IsReference && conversion.IsImplicit);
        }
Exemplo n.º 26
0
        private static bool IsUnnecessaryCast(
            ExpressionSyntax castNode, ExpressionSyntax castedExpressionNode,
            SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var speculationAnalyzer = new SpeculationAnalyzer(castNode,
                                                              castedExpressionNode, semanticModel, cancellationToken,
                                                              skipVerificationForReplacedNode: true, failOnOverloadResolutionFailuresInOriginalCode: true);

            // First, check to see if the node ultimately parenting this cast has any
            // syntax errors. If so, we bail.
            if (speculationAnalyzer.SemanticRootOfOriginalExpression.ContainsDiagnostics)
            {
                return(false);
            }

            var castTypeInfo = semanticModel.GetTypeInfo(castNode, cancellationToken);
            var castType     = castTypeInfo.Type;

            // Case:
            // 1 . Console.WriteLine(await (dynamic)task); Any Dynamic Cast will not be removed.
            if (castType == null || castType.Kind == SymbolKind.DynamicType || castType.IsErrorType())
            {
                return(false);
            }

            var expressionTypeInfo = semanticModel.GetTypeInfo(castedExpressionNode, cancellationToken);
            var expressionType     = expressionTypeInfo.Type;

            if (EnumCastDefinitelyCantBeRemoved(castNode, expressionType, castType))
            {
                return(false);
            }

            // We do not remove any cast on
            // 1. Dynamic Expressions
            // 2. If there is any other argument which is dynamic
            // 3. Dynamic Invocation
            // 4. Assignment to dynamic
            if ((expressionType != null &&
                 (expressionType.IsErrorType() ||
                  expressionType.Kind == SymbolKind.DynamicType)) ||
                IsDynamicInvocation(castNode, semanticModel, cancellationToken) ||
                IsDynamicAssignment(castNode, semanticModel, cancellationToken))
            {
                return(false);
            }

            if (PointerCastDefinitelyCantBeRemoved(castNode, castedExpressionNode))
            {
                return(false);
            }

            if (CastPassedToParamsArrayDefinitelyCantBeRemoved(castNode, castType, semanticModel, cancellationToken))
            {
                return(false);
            }

            // A casts to object can always be removed from an expression inside of an interpolation, since it'll be converted to object
            // in order to call string.Format(...) anyway.
            if (castType?.SpecialType == SpecialType.System_Object &&
                castNode.WalkUpParentheses().IsParentKind(SyntaxKind.Interpolation))
            {
                return(true);
            }

            if (speculationAnalyzer.ReplacementChangesSemantics())
            {
                return(false);
            }

            var expressionToCastType = semanticModel.ClassifyConversion(castNode.SpanStart, castedExpressionNode, castType, isExplicitInSource: true);
            var outerType            = GetOuterCastType(castNode, semanticModel, out var parentIsOrAsExpression) ?? castTypeInfo.ConvertedType;

            // Simple case: If the conversion from the inner expression to the cast type is identity,
            // the cast can be removed.
            if (expressionToCastType.IsIdentity)
            {
                // Simple case: Is this an identity cast to another cast? If so, we're safe to remove it.
                if (castedExpressionNode.WalkDownParentheses().IsKind(SyntaxKind.CastExpression))
                {
                    return(true);
                }

                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      (object)x == "Hi!"
                if (IsRequiredCastForReferenceEqualityComparison(outerType, castNode, semanticModel, out var other))
                {
                    var otherToOuterType = semanticModel.ClassifyConversion(other, outerType);
                    if (otherToOuterType.IsImplicit && otherToOuterType.IsReference)
                    {
                        return(false);
                    }
                }

                if (SameSizedFloatingPointCastMustBePreserved(
                        semanticModel, castNode, castedExpressionNode,
                        expressionType, castType, cancellationToken))
                {
                    return(false);
                }

                return(true);
            }

            Debug.Assert(!expressionToCastType.IsIdentity);
            if (expressionToCastType.IsExplicit)
            {
                // Explicit reference conversions can cause an exception or data loss, hence can never be removed.
                if (expressionToCastType.IsReference)
                {
                    return(false);
                }

                // Unboxing conversions can cause a null ref exception, hence can never be removed.
                if (expressionToCastType.IsUnboxing)
                {
                    return(false);
                }

                // Don't remove any explicit numeric casts.
                // https://github.com/dotnet/roslyn/issues/2987 tracks improving on this conservative approach.
                if (expressionToCastType.IsNumeric)
                {
                    return(false);
                }
            }

            if (expressionToCastType.IsPointer || expressionToCastType.IsIntPtr)
            {
                // Don't remove any non-identity pointer or IntPtr conversions.
                // https://github.com/dotnet/roslyn/issues/2987 tracks improving on this conservative approach.
                return(expressionType != null && expressionType.Equals(outerType));
            }

            if (expressionToCastType.IsInterpolatedString)
            {
                // interpolation casts are necessary to preserve semantics if our destination type is not itself
                // FormattableString or some interface of FormattableString.

                return(castType.Equals(castTypeInfo.ConvertedType) ||
                       ImmutableArray <ITypeSymbol> .CastUp(castType.AllInterfaces).Contains(castTypeInfo.ConvertedType));
            }

            if (castedExpressionNode.WalkDownParentheses().IsKind(SyntaxKind.DefaultLiteralExpression) &&
                !castType.Equals(outerType) &&
                outerType.IsNullable())
            {
                // We have a cast like `(T?)(X)default`. We can't remove the inner cast as it effects what value
                // 'default' means in this context.
                return(false);
            }

            if (parentIsOrAsExpression)
            {
                // Note: speculationAnalyzer.ReplacementChangesSemantics() ensures that the parenting is or as expression are not broken.
                // Here we just need to ensure that the original cast expression doesn't invoke a user defined operator.
                return(!expressionToCastType.IsUserDefined);
            }

            if (outerType != null)
            {
                var castToOuterType       = semanticModel.ClassifyConversion(castNode.SpanStart, castNode, outerType);
                var expressionToOuterType = GetSpeculatedExpressionToOuterTypeConversion(speculationAnalyzer.ReplacedExpression, speculationAnalyzer, cancellationToken);

                // if the conversion to the outer type doesn't exist, then we shouldn't offer, except for anonymous functions which can't be reasoned about the same way (see below)
                if (!expressionToOuterType.Exists && !expressionToOuterType.IsAnonymousFunction)
                {
                    return(false);
                }

                // CONSIDER: Anonymous function conversions cannot be compared from different semantic models as lambda symbol comparison requires syntax tree equality. Should this be a compiler bug?
                // For now, just revert back to computing expressionToOuterType using the original semantic model.
                if (expressionToOuterType.IsAnonymousFunction)
                {
                    expressionToOuterType = semanticModel.ClassifyConversion(castNode.SpanStart, castedExpressionNode, outerType);
                }

                // If there is an user-defined conversion from the expression to the cast type or the cast
                // to the outer type, we need to make sure that the same user-defined conversion will be
                // called if the cast is removed.
                if (castToOuterType.IsUserDefined || expressionToCastType.IsUserDefined)
                {
                    return(!expressionToOuterType.IsExplicit &&
                           (HaveSameUserDefinedConversion(expressionToCastType, expressionToOuterType) ||
                            HaveSameUserDefinedConversion(castToOuterType, expressionToOuterType)) &&
                           UserDefinedConversionIsAllowed(castNode, semanticModel));
                }
                else if (expressionToOuterType.IsUserDefined)
                {
                    return(false);
                }

                if (expressionToCastType.IsExplicit &&
                    expressionToOuterType.IsExplicit)
                {
                    return(false);
                }
                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      x == (object)"Hi!"
                if (expressionToCastType.IsImplicit && expressionToCastType.IsReference &&
                    castToOuterType.IsIdentity &&
                    IsRequiredCastForReferenceEqualityComparison(outerType, castNode, semanticModel, out var other))
                {
                    return(false);
                }

                // If the conversion from the expression to the cast type is implicit numeric or constant
                // and the conversion from the expression to the outer type is identity, we'll go ahead
                // and remove the cast.
                if (expressionToOuterType.IsIdentity &&
                    expressionToCastType.IsImplicit &&
                    (expressionToCastType.IsNumeric || expressionToCastType.IsConstantExpression))
                {
                    // Some implicit numeric conversions can cause loss of precision and must not be removed.
                    return(!IsRequiredImplicitNumericConversion(expressionType, castType));
                }

                if (!castToOuterType.IsBoxing &&
                    castToOuterType == expressionToOuterType)
                {
                    if (castToOuterType.IsNullable)
                    {
                        // Even though both the nullable conversions (castToOuterType and expressionToOuterType) are equal, we can guarantee no data loss only if there is an
                        // implicit conversion from expression type to cast type and expression type is non-nullable. For example, consider the cast removal "(float?)" for below:

                        // Console.WriteLine((int)(float?)(int?)2147483647); // Prints -2147483648

                        // castToOuterType:         ExplicitNullable
                        // expressionToOuterType:   ExplicitNullable
                        // expressionToCastType:    ImplicitNullable

                        // We should not remove the cast to "float?".
                        // However, cast to "int?" is unnecessary and should be removable.
                        return(expressionToCastType.IsImplicit && !expressionType.IsNullable());
                    }
                    else if (expressionToCastType.IsImplicit && expressionToCastType.IsNumeric && !castToOuterType.IsIdentity)
                    {
                        // Some implicit numeric conversions can cause loss of precision and must not be removed.
                        return(!IsRequiredImplicitNumericConversion(expressionType, castType));
                    }

                    return(true);
                }

                if (castToOuterType.IsIdentity &&
                    !expressionToCastType.IsUnboxing &&
                    expressionToCastType == expressionToOuterType)
                {
                    return(true);
                }

                // Special case: It's possible to have useless casts inside delegate creation expressions.
                // For example: new Func<string, bool>((Predicate<object>)(y => true)).
                if (IsInDelegateCreationExpression(castNode, semanticModel))
                {
                    if (expressionToCastType.IsAnonymousFunction && expressionToOuterType.IsAnonymousFunction)
                    {
                        return(!speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(castedExpressionNode, speculationAnalyzer.ReplacedExpression));
                    }

                    if (expressionToCastType.IsMethodGroup && expressionToOuterType.IsMethodGroup)
                    {
                        return(true);
                    }
                }

                // Case :
                // 1. IList<object> y = (IList<dynamic>)new List<object>()
                if (expressionToCastType.IsExplicit && castToOuterType.IsExplicit && expressionToOuterType.IsImplicit)
                {
                    // If both expressionToCastType and castToOuterType are numeric, then this is a required cast as one of the conversions leads to loss of precision.
                    // Cast removal can change program behavior.
                    return(!(expressionToCastType.IsNumeric && castToOuterType.IsNumeric));
                }

                // Case :
                // 2. object y = (ValueType)1;
                if (expressionToCastType.IsBoxing && expressionToOuterType.IsBoxing && castToOuterType.IsImplicit)
                {
                    return(true);
                }

                // Case :
                // 3. object y = (NullableValueType)null;
                if ((!castToOuterType.IsBoxing || expressionToCastType.IsNullLiteral) &&
                    castToOuterType.IsImplicit &&
                    expressionToCastType.IsImplicit &&
                    expressionToOuterType.IsImplicit)
                {
                    if (expressionToOuterType.IsAnonymousFunction)
                    {
                        return(expressionToCastType.IsAnonymousFunction &&
                               !speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(castedExpressionNode, speculationAnalyzer.ReplacedExpression));
                    }

                    return(true);
                }
            }

            return(false);
        }
Exemplo n.º 27
0
        /// <summary>
        /// 
        /// </summary>
        /// <param name="binding"></param>
        /// <param name="expr"></param>
        /// <param name="ept1">expr -> TypeInParent</param>
        /// <param name="ept2">Type(expr) -> TypeInParent</param>
        private void ConversionTestHelper(SemanticModel semanticModel, ExpressionSyntax expr, ConversionKind ept1, ConversionKind ept2)
        {
            var info = semanticModel.GetTypeInfo(expr);
            Assert.NotNull(info);
            Assert.NotNull(info.ConvertedType);
            var conv = semanticModel.GetConversion(expr);

            // NOT expect NoConversion
            Conversion act1 = semanticModel.ClassifyConversion(expr, (TypeSymbol)info.ConvertedType);
            Assert.Equal(ept1, act1.Kind);
            ValidateConversion(act1, ept1);
            ValidateConversion(act1, conv.Kind);

            if (ept2 == ConversionKind.NoConversion)
            {
                Assert.Null(info.Type);
            }
            else
            {
                Assert.NotNull(info.Type);
                var act2 = semanticModel.Compilation.ClassifyConversion(info.Type, info.ConvertedType);
                Assert.Equal(ept2, act2.Kind);
                ValidateConversion(act2, ept2);
            }
        }
        public static bool IsUnnecessaryCast(this CastExpressionSyntax cast, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var speculationAnalyzer = new SpeculationAnalyzer(cast,
                cast.Expression, semanticModel, cancellationToken,
                skipVerificationForReplacedNode: true, failOnOverloadResolutionFailuresInOriginalCode: true);

            // First, check to see if the node ultimately parenting this cast has any
            // syntax errors. If so, we bail.
            if (speculationAnalyzer.SemanticRootOfOriginalExpression.ContainsDiagnostics)
            {
                return false;
            }

            var castTypeInfo = semanticModel.GetTypeInfo(cast, cancellationToken);
            var castType = castTypeInfo.Type;

            // Case:
            // 1 . Console.WriteLine(await (dynamic)task); Any Dynamic Cast will not be removed.
            if (castType == null || castType.Kind == SymbolKind.DynamicType || castType.IsErrorType())
            {
                return false;
            }

            var expressionTypeInfo = semanticModel.GetTypeInfo(cast.Expression, cancellationToken);
            var expressionType = expressionTypeInfo.Type;

            // We do not remove any cast on 
            // 1. Dynamic Expressions
            // 2. If there is any other argument which is dynamic
            // 3. Dynamic Invocation
            if ((expressionType != null &&
                (expressionType.IsErrorType() ||
                 expressionType.Kind == SymbolKind.DynamicType)) ||
                IsDynamicInvocation(cast, semanticModel, cancellationToken))
            {
                return false;
            }

            if (PointerCastDefinitelyCantBeRemoved(cast))
            {
                return false;
            }

            if (CastPassedToParamsArrayDefinitelyCantBeRemoved(cast, castType, semanticModel, cancellationToken))
            {
                return false;
            }

            if (speculationAnalyzer.ReplacementChangesSemantics())
            {
                return false;
            }

            var expressionToCastType = semanticModel.ClassifyConversion(cast.SpanStart, cast.Expression, castType, isExplicitInSource: true);

            bool parentIsOrAsExpression;
            var outerType = GetOuterCastType(cast, semanticModel, out parentIsOrAsExpression) ?? castTypeInfo.ConvertedType;

            // Simple case: If the conversion from the inner expression to the cast type is identity,
            // the cast can be removed.
            if (expressionToCastType.IsIdentity)
            {
                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      (object)x == "Hi!"
                ExpressionSyntax other;
                if (IsRequiredCastForReferenceEqualityComparison(outerType, cast, semanticModel, out other))
                {
                    var otherToOuterType = semanticModel.ClassifyConversion(other, outerType);
                    if (otherToOuterType.IsImplicit && otherToOuterType.IsReference)
                    {
                        return false;
                    }
                }

                return true;
            }
            else if (expressionToCastType.IsExplicit && expressionToCastType.IsReference)
            {
                // Explicit reference conversions can cause an exception or data loss, hence can never be removed.
                return false;
            }
            else if (expressionToCastType.IsExplicit && expressionToCastType.IsNumeric && IsInExplicitCheckedOrUncheckedContext(cast))
            {
                // Don't remove any explicit numeric casts in explicit checked/unchecked context.
                // https://github.com/dotnet/roslyn/issues/2987 tracks improving on this conservative approach.
                return false;
            }
            else if (expressionToCastType.IsPointer)
            {
                // Don't remove any non-identity pointer conversions.
                // https://github.com/dotnet/roslyn/issues/2987 tracks improving on this conservative approach.
                return expressionType != null && expressionType.Equals(outerType);
            }

            if (parentIsOrAsExpression)
            {
                // Note: speculationAnalyzer.ReplacementChangesSemantics() ensures that the parenting is or as expression are not broken.
                // Here we just need to ensure that the original cast expression doesn't invoke a user defined operator.
                return !expressionToCastType.IsUserDefined;
            }

            if (outerType != null)
            {
                var castToOuterType = semanticModel.ClassifyConversion(cast.SpanStart, cast, outerType);
                var expressionToOuterType = GetSpeculatedExpressionToOuterTypeConversion(speculationAnalyzer.ReplacedExpression, speculationAnalyzer, cancellationToken);

                // CONSIDER: Anonymous function conversions cannot be compared from different semantic models as lambda symbol comparison requires syntax tree equality. Should this be a compiler bug?
                // For now, just revert back to computing expressionToOuterType using the original semantic model.
                if (expressionToOuterType.IsAnonymousFunction)
                {
                    expressionToOuterType = semanticModel.ClassifyConversion(cast.SpanStart, cast.Expression, outerType);
                }

                // If there is an user-defined conversion from the expression to the cast type or the cast
                // to the outer type, we need to make sure that the same user-defined conversion will be 
                // called if the cast is removed.
                if (castToOuterType.IsUserDefined || expressionToCastType.IsUserDefined)
                {
                    return !expressionToOuterType.IsExplicit &&
                        (HaveSameUserDefinedConversion(expressionToCastType, expressionToOuterType) ||
                         HaveSameUserDefinedConversion(castToOuterType, expressionToOuterType)) &&
                         UserDefinedConversionIsAllowed(cast, semanticModel);
                }
                else if (expressionToOuterType.IsUserDefined)
                {
                    return false;
                }

                if (expressionToCastType.IsExplicit &&
                    expressionToOuterType.IsExplicit)
                {
                    return false;
                }

                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      x == (object)"Hi!"
                ExpressionSyntax other;
                if (expressionToCastType.IsImplicit && expressionToCastType.IsReference &&
                    castToOuterType.IsIdentity &&
                    IsRequiredCastForReferenceEqualityComparison(outerType, cast, semanticModel, out other))
                {
                    return false;
                }

                // If the conversion from the expression to the cast type is implicit numeric or constant
                // and the conversion from the expression to the outer type is identity, we'll go ahead
                // and remove the cast.
                if (expressionToOuterType.IsIdentity &&
                    expressionToCastType.IsImplicit &&
                    (expressionToCastType.IsNumeric || expressionToCastType.IsConstantExpression))
                {
                    return true;
                }

                if (!castToOuterType.IsBoxing &&
                    castToOuterType == expressionToOuterType)
                {
                    if (castToOuterType.IsNullable)
                    {
                        // Even though both the nullable conversions (castToOuterType and expressionToOuterType) are equal, we can guarantee no data loss only if there is an
                        // implicit conversion from expression type to cast type and expression type is non-nullable. For example, consider the cast removal "(float?)" for below:

                        // Console.WriteLine((int)(float?)(int?)2147483647); // Prints -2147483648

                        // castToOuterType:         ExplicitNullable
                        // expressionToOuterType:   ExplicitNullable
                        // expressionToCastType:    ImplicitNullable

                        // We should not remove the cast to "float?".
                        // However, cast to "int?" is unnecessary and should be removable.
                        return expressionToCastType.IsImplicit && !((ITypeSymbol)expressionType).IsNullable();
                    }
                    else if (expressionToCastType.IsImplicit && expressionToCastType.IsNumeric && !castToOuterType.IsIdentity)
                    {
                        // Some implicit numeric conversions can cause loss of precision and must not be removed.
                        return !IsRequiredImplicitNumericConversion(expressionType, castType);
                    }

                    return true;
                }

                if (castToOuterType.IsIdentity &&
                    !expressionToCastType.IsUnboxing &&
                    expressionToCastType == expressionToOuterType)
                {
                    return true;
                }

                // Special case: It's possible to have useless casts inside delegate creation expressions.
                // For example: new Func<string, bool>((Predicate<object>)(y => true)).
                if (IsInDelegateCreationExpression(cast, semanticModel))
                {
                    if (expressionToCastType.IsAnonymousFunction && expressionToOuterType.IsAnonymousFunction)
                    {
                        return !speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(cast.Expression, speculationAnalyzer.ReplacedExpression);
                    }

                    if (expressionToCastType.IsMethodGroup && expressionToOuterType.IsMethodGroup)
                    {
                        return true;
                    }
                }

                // Case :
                // 1. IList<object> y = (IList<dynamic>)new List<object>()
                if (expressionToCastType.IsExplicit && castToOuterType.IsExplicit && expressionToOuterType.IsImplicit)
                {
                    // If both expressionToCastType and castToOuterType are numeric, then this is a required cast as one of the conversions leads to loss of precision.
                    // Cast removal can change program behavior.                    
                    return !(expressionToCastType.IsNumeric && castToOuterType.IsNumeric);
                }

                // Case :
                // 2. object y = (ValueType)1;
                if (expressionToCastType.IsBoxing && expressionToOuterType.IsBoxing && castToOuterType.IsImplicit)
                {
                    return true;
                }

                // Case :
                // 3. object y = (NullableValueType)null;
                if ((!castToOuterType.IsBoxing || expressionToCastType.IsNullLiteral) &&
                    castToOuterType.IsImplicit &&
                    expressionToCastType.IsImplicit &&
                    expressionToOuterType.IsImplicit)
                {
                    if (expressionToOuterType.IsAnonymousFunction)
                    {
                        return expressionToCastType.IsAnonymousFunction &&
                            !speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(cast.Expression, speculationAnalyzer.ReplacedExpression);
                    }

                    return true;
                }
            }

            return false;
        }