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); }
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); }
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); }
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); })); }
private static bool IsIdentityOrImplicitConversion( ExpressionSyntax expression, ITypeSymbol destinationType, SemanticModel semanticModel) { Conversion conversion = semanticModel.ClassifyConversion(expression, destinationType); return(conversion.IsIdentity || conversion.IsImplicit); }
/// <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)); }
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); }
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)); }
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); }
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)); }
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); }
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)); }
private static bool CanBeConvertedTo(ExpressionSyntax expression, ITypeSymbol type, SemanticModel model) { var conversion = model.ClassifyConversion(expression, type); return(conversion.Exists && (conversion.IsIdentity || conversion.IsImplicit)); }
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); }
/// <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)));
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); }
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);
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)); }
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); }
protected override bool CanBeConvertedTo(ExpressionSyntax expression, ITypeSymbol type, SemanticModel semanticModel) { var conversion = semanticModel.ClassifyConversion(expression, type); return(conversion.Exists && (conversion.IsIdentity || conversion.IsImplicit)); }
private bool IsArgumentExceptionType(TypeSyntax typeSyntax) { var conversion = _semanticModel.ClassifyConversion(typeSyntax, _argumentExceptionType); return(conversion.IsReference && conversion.IsImplicit); }
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); }
/// <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; }