Example #1
0
        internal static bool TryGetReturnType(ClassDeclarationSyntax classDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken, out ITypeSymbol returnType)
        {
            returnType = null;
            if (classDeclaration.TryFindMethod("ProvideValue", out var convertMethod) &&
                convertMethod.ReturnType is PredefinedTypeSyntax predefinedType &&
                predefinedType.Keyword.ValueText == "object" &&
                convertMethod.ParameterList != null &&
                convertMethod.ParameterList.Parameters.Count == 1)
            {
                using (var walker = ReturnValueWalker.Borrow(convertMethod))
                {
                    using (var returnTypes = PooledSet <ITypeSymbol> .Borrow())
                    {
                        returnTypes.UnionWith(walker.ReturnValues.Select(x => semanticModel.GetTypeInfoSafe(x, cancellationToken).Type));
                        return(returnTypes.TrySingle(out returnType));
                    }
                }
            }

            return(false);
        }
Example #2
0
        internal static bool TryGetConversionTypes(ClassDeclarationSyntax classDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken, out ITypeSymbol sourceType, out ITypeSymbol targetType)
        {
            sourceType = null;
            targetType = null;
            if (classDeclaration.TryFindMethod("Convert", out var convertMethod) &&
                convertMethod.ReturnType is PredefinedTypeSyntax returnType &&
                returnType.Keyword.ValueText == "object" &&
                convertMethod.ParameterList != null &&
                convertMethod.ParameterList.Parameters.Count == 4 &&
                convertMethod.ParameterList.Parameters.TryFirst(out var valueParameter))
            {
                using (var returnValues = ReturnValueWalker.Borrow(convertMethod))
                {
                    using (var returnTypes = PooledSet <ITypeSymbol> .Borrow())
                    {
                        foreach (var returnValue in returnValues.ReturnValues)
                        {
                            AddReturnType(returnTypes, returnValue);
                        }

                        return(returnTypes.TrySingle(out targetType) &&
                               ConversionWalker.TryGetCommonBase(
                                   convertMethod,
                                   semanticModel.GetDeclaredSymbolSafe(valueParameter, cancellationToken),
                                   semanticModel,
                                   cancellationToken,
                                   out sourceType));
                    }
                }
            }

            return(false);

            void AddReturnType(PooledSet <ITypeSymbol> returnTypes, ExpressionSyntax returnValue)
            {
                switch (returnValue)
                {
                case LiteralExpressionSyntax literal when literal.IsKind(SyntaxKind.NullLiteralExpression):
                    break;

                case ConditionalExpressionSyntax ternary:
                    AddReturnType(returnTypes, ternary.WhenTrue);
                    AddReturnType(returnTypes, ternary.WhenFalse);
                    break;

                case BinaryExpressionSyntax coalesce when coalesce.IsKind(SyntaxKind.CoalesceExpression):
                    AddReturnType(returnTypes, coalesce.Left);

                    AddReturnType(returnTypes, coalesce.Right);
                    break;

                case IdentifierNameSyntax _:
                case MemberAccessExpressionSyntax _:
                    var type = semanticModel.GetTypeInfoSafe(returnValue, cancellationToken).Type;
                    if (type == KnownSymbol.Object &&
                        semanticModel.GetSymbolSafe(returnValue, cancellationToken) is ISymbol symbol &&
                        symbol.IsEither <IFieldSymbol, IPropertySymbol>())
                    {
                        switch (symbol)
                        {
                        case IFieldSymbol field:
                            if (field.Type == KnownSymbol.Object &&
                                field.DeclaredAccessibility == Accessibility.Private &&
                                returnValue.FirstAncestor <TypeDeclarationSyntax>() is TypeDeclarationSyntax typeDeclaration)
                            {
                                using (var walker = AssignmentExecutionWalker.Borrow(typeDeclaration, Scope.Instance, semanticModel, cancellationToken))
                                {
                                    foreach (var assignment in walker.Assignments)
                                    {
                                        if (semanticModel.TryGetSymbol(assignment.Left, cancellationToken, out IFieldSymbol assigned) &&
                                            FieldSymbolComparer.Equals(assigned, field))
                                        {
                                            returnTypes.Add(semanticModel.GetTypeInfoSafe(assignment.Right, cancellationToken).Type);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                returnTypes.Add(field.Type);
                            }

                            return;

                        case IPropertySymbol property:
                            returnTypes.Add(property.Type);
                            return;
                        }
                    }
                    else
                    {
                        returnTypes.Add(type);
                    }

                    break;
Example #3
0
        internal static bool IsValueValidForRegisteredType(ExpressionSyntax value, ITypeSymbol registeredType, SemanticModel semanticModel, CancellationToken cancellationToken, PooledSet <SyntaxNode>?visited = null)
        {
            switch (value)
            {
            case ConditionalExpressionSyntax conditional:
                return(IsValueValidForRegisteredType(conditional.WhenTrue, registeredType, semanticModel, cancellationToken, visited) &&
                       IsValueValidForRegisteredType(conditional.WhenFalse, registeredType, semanticModel, cancellationToken, visited));

            case BinaryExpressionSyntax binary when binary.IsKind(SyntaxKind.CoalesceExpression):
                return(IsValueValidForRegisteredType(binary.Left, registeredType, semanticModel, cancellationToken, visited) &&
                       IsValueValidForRegisteredType(binary.Right, registeredType, semanticModel, cancellationToken, visited));
            }

            if (registeredType.TypeKind == TypeKind.Enum)
            {
                return(semanticModel.TryGetType(value, cancellationToken, out var valueType) &&
                       valueType.MetadataName == registeredType.MetadataName &&
                       Equals(valueType.ContainingType, registeredType.ContainingType) &&
                       NamespaceSymbolComparer.Equals(valueType.ContainingNamespace, registeredType.ContainingNamespace));
            }

            if (semanticModel.IsRepresentationPreservingConversion(value, registeredType))
            {
                return(true);
            }

            if (semanticModel.TryGetSymbol(value, cancellationToken, out var symbol))
            {
                if (symbol is IFieldSymbol field)
                {
                    if (field.TrySingleDeclaration(cancellationToken, out var fieldDeclaration))
                    {
                        if (fieldDeclaration.Declaration is { } variableDeclaration&&
                            variableDeclaration.Variables.TryLast(out var variable) &&
                            variable.Initializer is { } initializer&&
                            !IsValueValidForRegisteredType(initializer.Value, registeredType, semanticModel, cancellationToken, visited))
                        {
                            return(false);
                        }

                        return(IsAssignedValueOfRegisteredType(symbol, fieldDeclaration));
                    }

                    return(field.Type == KnownSymbols.Object);
                }

                if (symbol is IPropertySymbol property)
                {
                    if (property.TrySingleDeclaration(cancellationToken, out PropertyDeclarationSyntax? propertyDeclaration))
                    {
                        if (propertyDeclaration.Initializer is { } initializer&&
                            !IsValueValidForRegisteredType(initializer.Value, registeredType, semanticModel, cancellationToken, visited))
                        {
                            return(false);
                        }

                        if (property.SetMethod == null &&
                            property.GetMethod is { } getMethod)
                        {
                            return(IsReturnValueOfRegisteredType(getMethod));
                        }

                        return(IsAssignedValueOfRegisteredType(symbol, propertyDeclaration));
                    }

                    return(property.Type == KnownSymbols.Object);
                }

                if (symbol is IMethodSymbol method)
                {
                    return(IsReturnValueOfRegisteredType(method));
                }
            }

            return(false);

            bool IsAssignedValueOfRegisteredType(ISymbol memberSymbol, MemberDeclarationSyntax declaration)
            {
                if (declaration.TryFirstAncestor(out TypeDeclarationSyntax? typeDeclaration))
                {
                    using (var walker = AssignmentExecutionWalker.For(memberSymbol, typeDeclaration, SearchScope.Type, semanticModel, cancellationToken))
                    {
                        foreach (var assignment in walker.Assignments)
                        {
                            if (!IsValueValidForRegisteredType(assignment.Right, registeredType, semanticModel, cancellationToken, visited))
                            {
                                return(false);
                            }
                        }
                    }
                }

                return(true);
            }

            bool IsReturnValueOfRegisteredType(IMethodSymbol method)
            {
                if (method.TrySingleMethodDeclaration(cancellationToken, out var target))
                {
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                    using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                    {
                        if (visited.Add(target))
                        {
                            using (var walker = ReturnValueWalker.Borrow(target))
                            {
                                foreach (var returnValue in walker.ReturnValues)
                                {
                                    if (!IsValueValidForRegisteredType(returnValue, registeredType, semanticModel, cancellationToken, visited))
                                    {
                                        return(false);
                                    }
                                }
                            }
                        }

                        return(true);
                    }
                }

                return(method.ReturnType == KnownSymbols.Object);
            }
        }
Example #4
0
        private static bool IsDefaultValueOfRegisteredType(ExpressionSyntax defaultValue, ITypeSymbol registeredType, SyntaxNodeAnalysisContext context, PooledSet <SyntaxNode> visited = null)
        {
            switch (defaultValue)
            {
            case ConditionalExpressionSyntax conditional:
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                {
                    return(visited.Add(defaultValue) &&
                           IsDefaultValueOfRegisteredType(conditional.WhenTrue, registeredType, context, visited) &&
                           IsDefaultValueOfRegisteredType(conditional.WhenFalse, registeredType, context, visited));
                }

            case BinaryExpressionSyntax binary when binary.IsKind(SyntaxKind.CoalesceExpression):
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                {
                    return(visited.Add(defaultValue) &&
                           IsDefaultValueOfRegisteredType(binary.Left, registeredType, context, visited) &&
                           IsDefaultValueOfRegisteredType(binary.Right, registeredType, context, visited));
                }
            }

            if (context.SemanticModel.IsRepresentationPreservingConversion(defaultValue, registeredType, context.CancellationToken))
            {
                return(true);
            }

            if (context.SemanticModel.TryGetSymbol(defaultValue, context.CancellationToken, out ISymbol symbol))
            {
                if (symbol is IFieldSymbol field)
                {
                    if (field.TrySingleDeclaration(context.CancellationToken, out var fieldDeclaration))
                    {
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                        using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                        {
                            if (fieldDeclaration.Declaration is VariableDeclarationSyntax variableDeclaration &&
                                variableDeclaration.Variables.TryLast(out var variable) &&
                                variable.Initializer is EqualsValueClauseSyntax initializer)
                            {
                                return(visited.Add(initializer.Value) &&
                                       IsDefaultValueOfRegisteredType(initializer.Value, registeredType, context, visited));
                            }

                            return(fieldDeclaration.TryFirstAncestor <TypeDeclarationSyntax>(out var typeDeclaration) &&
                                   AssignmentExecutionWalker.SingleFor(symbol, typeDeclaration, Scope.Instance, context.SemanticModel, context.CancellationToken, out var assignedValue) &&
                                   visited.Add(assignedValue) &&
                                   IsDefaultValueOfRegisteredType(assignedValue, registeredType, context, visited));
                        }
                    }

                    return(field.Type == KnownSymbol.Object);
                }

                if (symbol is IPropertySymbol property)
                {
                    if (property.TrySingleDeclaration(context.CancellationToken, out PropertyDeclarationSyntax propertyDeclaration))
                    {
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                        using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                        {
                            if (propertyDeclaration.Initializer is EqualsValueClauseSyntax initializer)
                            {
                                return(visited.Add(initializer.Value) &&
                                       IsDefaultValueOfRegisteredType(initializer.Value, registeredType, context, visited));
                            }

                            if (property.SetMethod == null &&
                                property.GetMethod is IMethodSymbol getMethod)
                            {
                                return(IsReturnValueOfRegisteredType(getMethod, visited));
                            }

                            return(propertyDeclaration.TryFirstAncestor <TypeDeclarationSyntax>(out var typeDeclaration) &&
                                   AssignmentExecutionWalker.SingleFor(symbol, typeDeclaration, Scope.Instance, context.SemanticModel, context.CancellationToken, out var assignedValue) &&
                                   visited.Add(assignedValue) &&
                                   IsDefaultValueOfRegisteredType(assignedValue, registeredType, context, visited));
                        }
                    }

                    return(property.Type == KnownSymbol.Object);
                }

                if (symbol is IMethodSymbol method)
                {
                    return(IsReturnValueOfRegisteredType(method, visited));
                }
            }

            return(false);

            bool IsReturnValueOfRegisteredType(IMethodSymbol method, PooledSet <SyntaxNode> v)
            {
                if (method.TrySingleMethodDeclaration(context.CancellationToken, out var target))
                {
                    using (var walker = ReturnValueWalker.Borrow(target))
                    {
                        foreach (var returnValue in walker.ReturnValues)
                        {
                            if (!IsDefaultValueOfRegisteredType(returnValue, registeredType, context, v))
                            {
                                return(false);
                            }
                        }

                        return(true);
                    }
                }

                return(method.ReturnType == KnownSymbol.Object);
            }
        }