/******************************************************************************* * Copyright (c) 2017 Alex Xu and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Alex Xu - initial API and implementation *******************************************************************************/ package org.eclipse.php.internal.ui.text.correction; import java.util.Iterator; import java.util.List; import org.eclipse.php.core.ast.nodes.*; public class ASTResolving { public static ITypeBinding guessBindingForReference(ASTNode node) { return getPossibleReferenceBinding(node); } private static ITypeBinding getPossibleReferenceBinding(ASTNode node) { ASTNode parent = node.getParent(); switch (parent.getType()) { case ASTNode.ASSIGNMENT: Assignment assignment = (Assignment) parent; if (node.equals(assignment.getLeftHandSide())) { return assignment.getRightHandSide().resolveTypeBinding(); } return assignment.getLeftHandSide().resolveTypeBinding(); case ASTNode.INFIX_EXPRESSION: InfixExpression infix = (InfixExpression) parent; int op = infix.getOperator(); if (op == InfixExpression.OP_AND || op == InfixExpression.OP_OR) { // boolean operation return infix.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$ } else if (op == InfixExpression.OP_SL || op == InfixExpression.OP_SR) { // asymmetric operation return infix.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ } if (node.equals(infix.getLeft())) { // xx operation expression ITypeBinding rigthHandBinding = infix.getRight().resolveTypeBinding(); if (rigthHandBinding != null) { return rigthHandBinding; } } else { // expression operation xx ITypeBinding leftHandBinding = infix.getLeft().resolveTypeBinding(); if (leftHandBinding != null) { return leftHandBinding; } } if (op != InfixExpression.OP_IS_EQUAL && op != InfixExpression.OP_IS_NOT_EQUAL) { return infix.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ } break; case ASTNode.INSTANCE_OF_EXPRESSION: InstanceOfExpression instanceofExpression = (InstanceOfExpression) parent; return instanceofExpression.getExpression().resolveTypeBinding(); case ASTNode.SINGLE_FIELD_DECLARATION: SingleFieldDeclaration frag = (SingleFieldDeclaration) parent; if (frag.getName().equals(node)) { return frag.getName().resolveTypeBinding(); } break; case ASTNode.METHOD_INVOCATION: MethodInvocation methodInvocation = (MethodInvocation) parent; IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); if (methodBinding != null) { return getParameterTypeBinding(node, methodInvocation.getMethod().parameters(), methodBinding); } break; case ASTNode.CLASS_INSTANCE_CREATION: { ClassInstanceCreation creation = (ClassInstanceCreation) parent; IMethodBinding creationBinding = creation.resolveConstructorBinding(); if (creationBinding != null) { return getParameterTypeBinding(node, creation.ctorParams(), creationBinding); } break; } case ASTNode.PARENTHESIS_EXPRESSION: return guessBindingForReference(parent); case ASTNode.ARRAY_ACCESS: if (((ArrayAccess) parent).getIndex().equals(node)) { return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ } else { ITypeBinding parentBinding = getPossibleReferenceBinding(parent); if (parentBinding == null) { parentBinding = parent.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ } return parentBinding.createArrayType(1); } case ASTNode.CONDITIONAL_EXPRESSION: ConditionalExpression expression = (ConditionalExpression) parent; if (node.equals(expression.getCondition())) { return parent.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$ } if (node.equals(expression.getIfFalse())) { return expression.getIfTrue().resolveTypeBinding(); } return expression.getIfFalse().resolveTypeBinding(); case ASTNode.IF_STATEMENT: case ASTNode.WHILE_STATEMENT: case ASTNode.DO_STATEMENT: if (node instanceof Expression) { return parent.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$ } break; case ASTNode.SWITCH_STATEMENT: if (((SwitchStatement) parent).getExpression().equals(node)) { return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ } break; case ASTNode.CAST_EXPRESSION: return ((CastExpression) parent).resolveTypeBinding(); case ASTNode.THROW_STATEMENT: case ASTNode.CATCH_CLAUSE: return parent.getAST().resolveWellKnownType("Exception"); //$NON-NLS-1$ case ASTNode.FIELD_ACCESS: if (node.equals(((FieldAccess) parent).getField())) { return getPossibleReferenceBinding(parent); } break; case ASTNode.SWITCH_CASE: if (node.equals(((SwitchCase) parent).getValue()) && parent.getParent() instanceof SwitchStatement) { return ((SwitchStatement) parent.getParent()).getExpression().resolveTypeBinding(); } break; default: // do nothing } return null; } private static ITypeBinding getParameterTypeBinding(ASTNode node, List<Expression> args, IMethodBinding binding) { ITypeBinding[] paramTypes = binding.getParameterTypes(); int index = args.indexOf(node); if (binding.isVarargs() && index >= paramTypes.length - 1) { return paramTypes[paramTypes.length - 1].getComponentType(); } if (index >= 0 && index < paramTypes.length) { return paramTypes[index]; } return null; } public static ITypeBinding guessBindingForTypeReference(ASTNode node) { StructuralPropertyDescriptor locationInParent = node.getLocationInParent(); if (locationInParent == Identifier.NAME_PROPERTY) { node = node.getParent(); } return getPossibleTypeBinding(node); } private static ITypeBinding getPossibleTypeBinding(ASTNode node) { ASTNode parent = node.getParent(); switch (parent.getType()) { case ASTNode.FIELD_DECLARATION: return guessVariableType(((FieldsDeclaration) parent).fields()); case ASTNode.ARRAY_CREATION: ArrayCreation creation = (ArrayCreation) parent; return creation.resolveTypeBinding(); case ASTNode.CLASS_INSTANCE_CREATION: return getPossibleReferenceBinding(parent); case ASTNode.CAST_EXPRESSION: return getPossibleReferenceBinding(parent); } return null; } private static ITypeBinding guessVariableType(List<SingleFieldDeclaration> fragments) { for (Iterator<SingleFieldDeclaration> iter = fragments.iterator(); iter.hasNext();) { SingleFieldDeclaration frag = (SingleFieldDeclaration) iter.next(); if (frag.getName() != null) { return frag.getName().resolveTypeBinding(); } } return null; } /** * Returns the method binding of the node's parent method declaration or * <code>null</code> if the node is not inside a method. * * @param node * the ast node * @return the method binding of the node's parent method declaration or * <code>null</code> if the node */ public static MethodDeclaration findParentMethodDeclaration(ASTNode node) { while (node != null) { if (node.getType() == ASTNode.METHOD_DECLARATION) { return (MethodDeclaration) node; } node = node.getParent(); } return null; } public static int getPossibleTypeKinds(ASTNode node) { int kinds = internalGetPossibleTypeKinds(node); return kinds; } private static int internalGetPossibleTypeKinds(ASTNode node) { int kind = SimilarElementsRequestor.ALL_TYPES; ASTNode parent = node.getParent(); switch (parent.getType()) { case ASTNode.CLASS_DECLARATION: case ASTNode.INTERFACE_DECLARATION: kind = SimilarElementsRequestor.CLASSES | SimilarElementsRequestor.INTERFACES; break; case ASTNode.METHOD_DECLARATION: kind = SimilarElementsRequestor.ALL_TYPES; break; case ASTNode.INSTANCE_OF_EXPRESSION: kind = SimilarElementsRequestor.REF_TYPES; break; case ASTNode.THROW_STATEMENT: kind = SimilarElementsRequestor.CLASSES; break; case ASTNode.CLASS_INSTANCE_CREATION: kind = SimilarElementsRequestor.CLASSES | SimilarElementsRequestor.INTERFACES; break; case ASTNode.SINGLE_FIELD_DECLARATION: int superParent = parent.getParent().getType(); if (superParent == ASTNode.CATCH_CLAUSE) { kind = SimilarElementsRequestor.CLASSES; } break; default: } return kind; } }