private static bool TryGet(ExpressionSyntax expression, SyntaxNodeAnalysisContext context, PooledSet <ExpressionSyntax> visited, out ITypeSymbol result, out ExpressionSyntax source)
        {
            switch (expression)
            {
            case MemberAccessExpressionSyntax memberAccess when memberAccess.Name.Identifier.ValueText == "ReturnType" &&
                memberAccess.Expression is InvocationExpressionSyntax invocation &&
                GetX.TryMatchGetMethod(invocation, context, out var reflectedMember, out _, out _, out _) &&
                reflectedMember.Match == FilterMatch.Single &&
                reflectedMember.Symbol is IMethodSymbol method:
                source = memberAccess;
                result = method.ReturnType;
                return(true);

            case MemberAccessExpressionSyntax memberAccess when memberAccess.Name.Identifier.ValueText == "FieldType" &&
                memberAccess.Expression is InvocationExpressionSyntax invocation &&
                GetX.TryMatchGetField(invocation, context, out var reflectedMember, out _, out _) &&
                reflectedMember.Match == FilterMatch.Single &&
                reflectedMember.Symbol is IFieldSymbol field:
                source = memberAccess;
                result = field.Type;
                return(true);

            case MemberAccessExpressionSyntax memberAccess when memberAccess.Name.Identifier.ValueText == "PropertyType" &&
                memberAccess.Expression is InvocationExpressionSyntax invocation &&
                GetX.TryMatchGetProperty(invocation, context, out var reflectedMember, out _, out _, out _) &&
                reflectedMember.Match == FilterMatch.Single &&
                reflectedMember.Symbol is IPropertySymbol field:
                source = memberAccess;
                result = field.Type;
                return(true);

            case TypeOfExpressionSyntax typeOf:
                source = typeOf;
                return(context.SemanticModel.TryGetType(typeOf.Type, context.CancellationToken, out result));

            case InvocationExpressionSyntax invocation when invocation.ArgumentList is ArgumentListSyntax args &&
                args.Arguments.Count == 0 &&
                invocation.TryGetMethodName(out var name) &&
                name == "GetType":
                switch (invocation.Expression)
                {
                case MemberAccessExpressionSyntax typeAccess:
                    source = invocation;
                    if (context.SemanticModel.TryGetType(typeAccess.Expression, context.CancellationToken, out result))
                    {
                        if (result is INamedTypeSymbol namedType &&
                            namedType.ConstructedFrom?.SpecialType == SpecialType.System_Nullable_T)
                        {
                            result = namedType.TypeArguments[0];
                        }

                        return(true);
                    }

                    return(false);

                case IdentifierNameSyntax _ when expression.TryFirstAncestor(out TypeDeclarationSyntax containingType):
                    source = invocation;

                    return(context.SemanticModel.TryGetSymbol(containingType, context.CancellationToken, out result));

                case MemberBindingExpressionSyntax memberBinding when memberBinding.Parent?.Parent is ConditionalAccessExpressionSyntax conditionalAccess:
                    source = invocation;
                    return(context.SemanticModel.TryGetType(conditionalAccess.Expression, context.CancellationToken, out result));
                }

                break;

            case InvocationExpressionSyntax candidate when TryMatchTypeGetType(candidate, context, out var typeName, out var ignoreCase):
                source = candidate;

                result = context.Compilation.GetTypeByMetadataName(typeName, ignoreCase.Value);
                return(result != null);

            case InvocationExpressionSyntax candidate when TryMatchAssemblyGetType(candidate, context, out var typeName, out var ignoreCase):
                source = candidate;

                result = Assembly.TryGet(candidate.Expression, context, out var assembly)
                        ? assembly.GetTypeByMetadataName(typeName, ignoreCase.Value)
                        : null;
                return(result != null);

            case InvocationExpressionSyntax invocation when invocation.TryGetTarget(KnownSymbol.Type.GetGenericTypeDefinition, context.SemanticModel, context.CancellationToken, out _) &&
                invocation.Expression is MemberAccessExpressionSyntax memberAccess &&
                TryGet(memberAccess.Expression, context, visited, out var definingType, out _) &&
                definingType is INamedTypeSymbol namedType:
                source = invocation;

                result = namedType.ConstructedFrom;
                return(true);

            case InvocationExpressionSyntax invocation when GetX.TryMatchGetNestedType(invocation, context, out var reflectedMember, out _, out _):
                source = invocation;

                result = reflectedMember.Symbol as ITypeSymbol;
                return(result != null && reflectedMember.Match == FilterMatch.Single);

            case InvocationExpressionSyntax invocation when invocation.TryGetTarget(KnownSymbol.Type.MakeGenericType, context.SemanticModel, context.CancellationToken, out _) &&
                invocation.Expression is MemberAccessExpressionSyntax memberAccess &&
                TypeArguments.TryCreate(invocation, context, out var typeArguments) &&
                typeArguments.TryGetArgumentsTypes(context, out var types):
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                {
                    source = invocation;
                    if (visited.Add(invocation) &&
                        TryGet(memberAccess.Expression, context, visited, out var definition, out _) &&
                        definition is INamedTypeSymbol namedType &&
                        ReferenceEquals(namedType, namedType.ConstructedFrom) &&
                        namedType.Arity == types.Length)
                    {
                        result = namedType.Construct(types);
                        return(result != null);
                    }

                    result = null;
                    return(false);
                }

            case ConditionalAccessExpressionSyntax conditionalAccess:
                source = conditionalAccess;
                return(TryGet(conditionalAccess.WhenNotNull, context, out result, out _));
            }

            if (expression.IsEither(SyntaxKind.IdentifierName, SyntaxKind.SimpleMemberAccessExpression) &&
                context.SemanticModel.TryGetSymbol(expression, context.CancellationToken, out ISymbol local))
            {
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                {
                    source = null;
                    result = null;
                    return(AssignedValue.TryGetSingle(local, context.SemanticModel, context.CancellationToken, out var assignedValue) &&
                           visited.Add(assignedValue) &&
                           TryGet(assignedValue, context, visited, out result, out source));
                }
            }

            source = null;
            result = null;
            return(false);
        }
Example #2
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);
            }
        }