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