Example #1
0
        private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (!context.IsExcludedFromAnalysis() &&
                context.Node is InvocationExpressionSyntax invocation &&
                invocation.ArgumentList != null &&
                invocation.Expression is MemberAccessExpressionSyntax memberAccess &&
                invocation.TryGetMethodName(out var name) &&
                name == "Invoke" &&
                context.SemanticModel.TryGetSymbol(invocation, context.CancellationToken, out var invoke) &&
                invoke.ContainingType.IsAssignableTo(KnownSymbol.MemberInfo, context.Compilation) &&
                invoke.TryFindParameter("parameters", out var parametersParameter) &&
                invocation.TryFindArgument(parametersParameter, out var parametersArg))
            {
                if (context.SemanticModel.TryGetType(parametersArg.Expression, context.CancellationToken, out var type) &&
                    type is IArrayTypeSymbol arrayType &&
                    arrayType.ElementType == KnownSymbol.Object &&
                    Array.IsCreatingEmpty(parametersArg.Expression, context))
                {
                    context.ReportDiagnostic(Diagnostic.Create(REFL024PreferNullOverEmptyArray.Descriptor, parametersArg.GetLocation()));
                }

                if (GetX.TryGetMethodInfo(memberAccess, context, out var method))
                {
                    if (!method.ReturnsVoid &&
                        ReturnValue.ShouldCast(invocation, method.ReturnType, context))
                    {
                        context.ReportDiagnostic(
                            Diagnostic.Create(
                                REFL001CastReturnValue.Descriptor,
                                invocation.GetLocation(),
                                ImmutableDictionary <string, string> .Empty.Add(
                                    nameof(TypeSyntax),
                                    method.ReturnType.ToString(context)),
                                method.ReturnType.ToString(context)));
                    }

                    if (method.ReturnsVoid &&
                        !IsResultDiscarded(invocation, context))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(REFL002DiscardReturnValue.Descriptor, invocation.GetLocation()));
                    }

                    if (Array.TryGetValues(parametersArg.Expression, context, out var values) &&
                        Arguments.TryFindFirstMisMatch(method.Parameters, values, context, out var misMatch) == true)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(REFL025ArgumentsDontMatchParameters.Descriptor, misMatch?.GetLocation() ?? parametersArg.GetLocation()));
                    }

                    if (parametersArg.Expression.IsKind(SyntaxKind.NullLiteralExpression) &&
                        method.Parameters.Length > 0)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(REFL025ArgumentsDontMatchParameters.Descriptor, parametersArg.GetLocation()));
                    }

                    if (Type.IsCastToWrongType(invocation, method.ReturnType, context, out var castType))
                    {
                        context.ReportDiagnostic(
                            Diagnostic.Create(
                                REFL028CastReturnValueToCorrectType.Descriptor,
                                castType.GetLocation(),
                                ImmutableDictionary <string, string> .Empty.Add(
                                    nameof(TypeSyntax),
                                    method.ReturnType.ToString(context)),
                                method.ReturnType.ToString(context)));
                    }

                    if (invoke.TryFindParameter("obj", out var objParameter) &&
                        invocation.TryFindArgument(objParameter, out var objArg))
                    {
                        if (method.IsStatic &&
                            objArg.Expression?.IsKind(SyntaxKind.NullLiteralExpression) == false)
                        {
                            context.ReportDiagnostic(Diagnostic.Create(REFL030UseCorrectObj.Descriptor, objArg.GetLocation(), $"The method {method} is static and null should be passed as obj."));
                        }

                        if (!method.IsStatic)
                        {
                            if (objArg.Expression?.IsKind(SyntaxKind.NullLiteralExpression) == true)
                            {
                                context.ReportDiagnostic(Diagnostic.Create(REFL030UseCorrectObj.Descriptor, objArg.GetLocation(), $"The method {method} is an instance method and the instance should be passed as obj."));
                            }

                            if (context.SemanticModel.TryGetType(objArg.Expression, context.CancellationToken, out var objType) &&
                                objType != KnownSymbol.Object &&
                                !objType.IsAssignableTo(method.ContainingType, context.Compilation))
                            {
                                context.ReportDiagnostic(Diagnostic.Create(REFL030UseCorrectObj.Descriptor, objArg.GetLocation(), $"Expected an argument of type {method.ContainingType}."));
                            }
                        }
                    }

                    if (method.IsGenericDefinition())
                    {
                        if (values != null &&
                            values.Length > 0 &&
                            Array.TryGetAccessibleTypes(values, context, out var types))
                        {
                            context.ReportDiagnostic(
                                Diagnostic.Create(
                                    REFL035DontInvokeGenericDefinition.Descriptor,
                                    invocation.GetNameLocation(),
                                    ImmutableDictionary <string, string> .Empty.Add(
                                        nameof(TypeSyntax),
                                        string.Join(", ", types.Select(x => $"typeof({x.ToString(context)})")))));
                        }
                        else
                        {
                            context.ReportDiagnostic(Diagnostic.Create(REFL035DontInvokeGenericDefinition.Descriptor, invocation.GetNameLocation()));
                        }
                    }
                }
                else if (GetX.TryGetConstructorInfo(memberAccess, context, out var ctor))
                {
                    if (ReturnValue.ShouldCast(invocation, ctor.ReturnType, context))
                    {
                        context.ReportDiagnostic(
                            Diagnostic.Create(
                                REFL001CastReturnValue.Descriptor,
                                invocation.GetLocation(),
                                ImmutableDictionary <string, string> .Empty.Add(
                                    nameof(TypeSyntax),
                                    ctor.ContainingType.ToString(context)),
                                ctor.ContainingType.ToString(context)));
                    }

                    if (Array.TryGetValues(parametersArg.Expression, context, out var values) &&
                        Arguments.TryFindFirstMisMatch(ctor.Parameters, values, context, out var misMatch) == true)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(REFL025ArgumentsDontMatchParameters.Descriptor, misMatch?.GetLocation() ?? parametersArg.GetLocation()));
                    }

                    if (invoke.TryFindParameter("obj", out var objParameter) &&
                        invocation.TryFindArgument(objParameter, out var objArg))
                    {
                        if (objArg.Expression.IsKind(SyntaxKind.NullLiteralExpression))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(REFL030UseCorrectObj.Descriptor, objArg.GetLocation(), "Use overload of Invoke without obj parameter."));
                        }
                        else
                        {
                            if (!IsResultDiscarded(invocation, context))
                            {
                                context.ReportDiagnostic(Diagnostic.Create(REFL002DiscardReturnValue.Descriptor, invocation.GetLocation()));
                            }

                            if (!context.SemanticModel.TryGetType(objArg.Expression, context.CancellationToken, out var instanceType) ||
                                (instanceType != KnownSymbol.Object && !instanceType.Equals(ctor.ContainingType)))
                            {
                                context.ReportDiagnostic(Diagnostic.Create(REFL030UseCorrectObj.Descriptor, objArg.GetLocation(), $"Use an instance of type {ctor.ContainingType}."));
                            }
                        }
                    }
                    else
                    {
                        if (!ctor.IsStatic &&
                            Type.IsCastToWrongType(invocation, ctor.ContainingType, context, out var castType))
                        {
                            context.ReportDiagnostic(
                                Diagnostic.Create(
                                    REFL028CastReturnValueToCorrectType.Descriptor,
                                    castType.GetLocation(),
                                    ImmutableDictionary <string, string> .Empty.Add(
                                        nameof(TypeSyntax),
                                        ctor.ContainingType.ToString(context)),
                                    ctor.ContainingType.ToString(context)));
                        }
                    }

                    if (ctor.IsStatic)
                    {
                        context.ReportDiagnostic(
                            Diagnostic.Create(
                                REFL038PreferRunClassConstructor.Descriptor,
                                invocation.GetLocation(),
                                ImmutableDictionary <string, string> .Empty.Add(
                                    nameof(TypeSyntax),
                                    ctor.ContainingType.ToMinimalDisplayString(context.SemanticModel, invocation.SpanStart))));
                    }
                }
            }
        }
Example #2
0
        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.
                {
                    if (visited.Add(invocation) &&
                        TryGet(memberAccess.Expression, context, visited, out var definition, out _) &&
                        definition is INamedTypeSymbol namedType)
                    {
                        source = invocation;
                        result = namedType.Construct(types);
                        return(result != null);
                    }
                }

                break;

            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);
        }