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