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