blob: 01cd66548e3d0f1bd1ace302fa6dce2a94022174 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2013 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Samrat Dhillon samrat.dhillon@gmail.com [generalize type] Generalize Declared Type does not consider use of variable in throw statement, which yields compilation error https://bugs.eclipse.org/bugs/show_bug.cgi?id=395989
*******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring.typeconstraints;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.InstanceofExpression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.ThrowStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.refactoring.rename.MethodChecks;
/**
* Default implementation of the creator. Creates all or nearly all constraints for program constructs.
* Subclasses can provide additional checks to avoid creating constraints that are not useful for their purposes.
*/
public class FullConstraintCreator extends ConstraintCreator{
private final IConstraintVariableFactory fConstraintVariableFactory;
private final ITypeConstraintFactory fTypeConstraintFactory;
private IContext fContext;
public FullConstraintCreator(){
this(new ConstraintVariableFactory(), new TypeConstraintFactory());
}
public FullConstraintCreator(IConstraintVariableFactory cFactory,
ITypeConstraintFactory tFactory) {
Assert.isTrue(cFactory != null);
fConstraintVariableFactory= cFactory;
fTypeConstraintFactory= tFactory;
fContext= new NullContext();
}
public IContext getContext() {
return fContext;
}
public void setContext(IContext context) {
fContext= context;
}
public ITypeConstraintFactory getConstraintFactory(){
return fTypeConstraintFactory;
}
public IConstraintVariableFactory getConstraintVariableFactory(){
return fConstraintVariableFactory;
}
@Override
public ITypeConstraint[] create(ArrayInitializer arrayInitializer){
ITypeBinding arrayBinding= arrayInitializer.resolveTypeBinding();
Assert.isTrue(arrayBinding.isArray());
List<Expression> expressions= arrayInitializer.expressions();
List<ITypeConstraint> constraints= new ArrayList<>();
Type type= getTypeParent(arrayInitializer);
ConstraintVariable typeVariable= fConstraintVariableFactory.makeTypeVariable(type);
for (int i= 0; i < expressions.size(); i++) {
Expression each= expressions.get(i);
ITypeConstraint[] c= fTypeConstraintFactory.createSubtypeConstraint(
fConstraintVariableFactory.makeExpressionOrTypeVariable(each, getContext()),
typeVariable);
constraints.addAll(Arrays.asList(c));
}
return constraints.toArray(new ITypeConstraint[constraints.size()]);
}
@Override
public ITypeConstraint[] create(Assignment assignment){
return fTypeConstraintFactory.createSubtypeConstraint(
fConstraintVariableFactory.makeExpressionOrTypeVariable(assignment.getRightHandSide(), getContext()),
fConstraintVariableFactory.makeExpressionOrTypeVariable(assignment.getLeftHandSide(), getContext()));
}
@Override
public ITypeConstraint[] create(CastExpression castExpression){
Expression expression= castExpression.getExpression();
Type type= castExpression.getType();
ITypeConstraint[] definesConstraint= fTypeConstraintFactory.createDefinesConstraint(fConstraintVariableFactory.makeExpressionOrTypeVariable(castExpression, getContext()),
fConstraintVariableFactory.makeTypeVariable(castExpression.getType()));
if (isClassBinding(expression.resolveTypeBinding()) && isClassBinding(type.resolveBinding())){
ConstraintVariable expressionVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(expression, getContext());
ConstraintVariable castExpressionVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(castExpression, getContext());
ITypeConstraint[] c2 = createOrOrSubtypeConstraint(expressionVariable, castExpressionVariable);
if (definesConstraint.length == 0){
return c2;
} else {
ITypeConstraint c1 = definesConstraint[0];
Collection<ITypeConstraint> constraints= new ArrayList<>();
constraints.add(c1);
constraints.addAll(Arrays.asList(c2));
return constraints.toArray(new ITypeConstraint[constraints.size()]);
}
} else
return definesConstraint;
}
@Override
public ITypeConstraint[] create(CatchClause node) {
SingleVariableDeclaration exception= node.getException();
ConstraintVariable nameVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(exception.getName(), getContext());
ITypeConstraint[] defines= fTypeConstraintFactory.createDefinesConstraint(
nameVariable,
fConstraintVariableFactory.makeTypeVariable(exception.getType()));
ITypeBinding throwable= node.getAST().resolveWellKnownType("java.lang.Throwable"); //$NON-NLS-1$
ITypeConstraint[] catchBound= fTypeConstraintFactory.createSubtypeConstraint(
nameVariable,
fConstraintVariableFactory.makeRawBindingVariable(throwable));
ArrayList<ITypeConstraint> result= new ArrayList<>();
result.addAll(Arrays.asList(defines));
result.addAll(Arrays.asList(catchBound));
return result.toArray(new ITypeConstraint[result.size()]);
}
@Override
public ITypeConstraint[] create(ClassInstanceCreation instanceCreation){
List<Expression> arguments= instanceCreation.arguments();
List<ITypeConstraint> result= new ArrayList<>(arguments.size());
IMethodBinding methodBinding= instanceCreation.resolveConstructorBinding();
result.addAll(Arrays.asList(getArgumentConstraints(arguments, methodBinding)));
if (instanceCreation.getAnonymousClassDeclaration() == null){
ConstraintVariable constructorVar= fConstraintVariableFactory.makeExpressionOrTypeVariable(instanceCreation, getContext());
ConstraintVariable typeVar= fConstraintVariableFactory.makeRawBindingVariable(instanceCreation.resolveTypeBinding());
result.addAll(Arrays.asList(fTypeConstraintFactory.createDefinesConstraint(constructorVar, typeVar)));
}
return result.toArray(new ITypeConstraint[result.size()]);
}
@Override
public ITypeConstraint[] create(ConstructorInvocation invocation){
List<Expression> arguments= invocation.arguments();
List<ITypeConstraint> result= new ArrayList<>(arguments.size());
IMethodBinding methodBinding= invocation.resolveConstructorBinding();
result.addAll(Arrays.asList(getArgumentConstraints(arguments, methodBinding)));
return result.toArray(new ITypeConstraint[result.size()]);
}
@Override
public ITypeConstraint[] create(FieldAccess access){
Expression expression= access.getExpression();
SimpleName name= access.getName();
IBinding binding= name.resolveBinding();
if (! (binding instanceof IVariableBinding))
return new ITypeConstraint[0];
IVariableBinding vb= (IVariableBinding)binding;
return createConstraintsForAccessToField(vb, expression, access);
}
@Override
public ITypeConstraint[] create(FieldDeclaration fd){
List<ITypeConstraint> result= new ArrayList<>();
result.addAll(Arrays.asList(getConstraintsFromFragmentList(fd.fragments(), fd.getType())));
result.addAll(getConstraintsForHiding(fd));
result.addAll(getConstraintsForFieldDeclaringTypes(fd));
return result.toArray(new ITypeConstraint[result.size()]);
}
@Override
public ITypeConstraint[] create(InstanceofExpression instanceofExpression){
Expression expression= instanceofExpression.getLeftOperand();
Type type= instanceofExpression.getRightOperand();
if (isClassBinding(expression.resolveTypeBinding()) && isClassBinding(type.resolveBinding())) {
ConstraintVariable expressionVar= fConstraintVariableFactory.makeExpressionOrTypeVariable(expression, getContext());
ConstraintVariable typeVariable= fConstraintVariableFactory.makeTypeVariable(type);
return createOrOrSubtypeConstraint(expressionVar, typeVariable);
} else
return new ITypeConstraint[0];
}
@Override
public ITypeConstraint[] create(ConditionalExpression node) {
List<ITypeConstraint> result= new ArrayList<>();
Expression thenExpression= node.getThenExpression();
Expression elseExpression= node.getElseExpression();
ConstraintVariable whole= fConstraintVariableFactory.makeExpressionOrTypeVariable(node, getContext());
ConstraintVariable ev1= fConstraintVariableFactory.makeExpressionOrTypeVariable(thenExpression, getContext());
ConstraintVariable ev2= fConstraintVariableFactory.makeExpressionOrTypeVariable(elseExpression, getContext());
ITypeConstraint[] constraints1= fTypeConstraintFactory.createEqualsConstraint(ev1, ev2);
ITypeConstraint[] constraints2= fTypeConstraintFactory.createSubtypeConstraint(ev1, whole);
ITypeConstraint[] constraints3= fTypeConstraintFactory.createSubtypeConstraint(ev2, whole);
result.addAll(Arrays.asList(constraints1));
result.addAll(Arrays.asList(constraints2));
result.addAll(Arrays.asList(constraints3));
return result.toArray(new ITypeConstraint[result.size()]);
}
@Override
public ITypeConstraint[] create(MethodDeclaration declaration){
List<ITypeConstraint> result= new ArrayList<>();
IMethodBinding methodBinding= declaration.resolveBinding();
if (methodBinding == null)
return new ITypeConstraint[0];
ITypeConstraint[] constraints = fTypeConstraintFactory.createDefinesConstraint(
fConstraintVariableFactory.makeDeclaringTypeVariable(methodBinding),
fConstraintVariableFactory.makeRawBindingVariable(methodBinding.getDeclaringClass()));
result.addAll(Arrays.asList(constraints));
if (! methodBinding.isConstructor() && ! methodBinding.getReturnType().isPrimitive()){
ConstraintVariable returnTypeBindingVariable= fConstraintVariableFactory.makeReturnTypeVariable(methodBinding);
ConstraintVariable returnTypeVariable= fConstraintVariableFactory.makeTypeVariable(declaration.getReturnType2());
ITypeConstraint[] defines= fTypeConstraintFactory.createDefinesConstraint(
returnTypeBindingVariable, returnTypeVariable);
result.addAll(Arrays.asList(defines));
}
for (int i= 0, n= declaration.parameters().size(); i < n; i++) {
SingleVariableDeclaration paramDecl= (SingleVariableDeclaration)declaration.parameters().get(i);
ConstraintVariable parameterTypeVariable= fConstraintVariableFactory.makeParameterTypeVariable(methodBinding, i);
ConstraintVariable parameterNameVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(paramDecl.getName(), getContext());
ITypeConstraint[] constraint= fTypeConstraintFactory.createDefinesConstraint(
parameterTypeVariable, parameterNameVariable);
result.addAll(Arrays.asList(constraint));
}
if (MethodChecks.isVirtual(methodBinding)){
Collection<ITypeConstraint> constraintsForOverriding = getConstraintsForOverriding(methodBinding);
result.addAll(constraintsForOverriding);
}
return result.toArray(new ITypeConstraint[result.size()]);
}
@Override
public ITypeConstraint[] create(ParenthesizedExpression node) {
ConstraintVariable v1= fConstraintVariableFactory.makeExpressionOrTypeVariable(node, getContext());
ConstraintVariable v2= fConstraintVariableFactory.makeExpressionOrTypeVariable(node.getExpression(), getContext());
ITypeConstraint[] equal= fTypeConstraintFactory.createEqualsConstraint(v1, v2);
return equal;
}
@Override
public ITypeConstraint[] create(MethodInvocation invocation){
List<Expression> arguments= invocation.arguments();
List<ITypeConstraint> result= new ArrayList<>(arguments.size());
IMethodBinding methodBinding= invocation.resolveMethodBinding();
if (methodBinding == null)
return new ITypeConstraint[0];
ITypeConstraint[] returnTypeConstraint= getReturnTypeConstraint(invocation, methodBinding);
result.addAll(Arrays.asList(returnTypeConstraint));
result.addAll(Arrays.asList(getArgumentConstraints(arguments, methodBinding)));
if (invocation.getExpression() != null){
if(MethodChecks.isVirtual(methodBinding)){
IMethodBinding[] rootDefs= getRootDefs(methodBinding);
Assert.isTrue(rootDefs.length > 0);
ConstraintVariable expressionVar= fConstraintVariableFactory.makeExpressionOrTypeVariable(invocation.getExpression(), getContext());
if (rootDefs.length == 1){
result.addAll(Arrays.asList(fTypeConstraintFactory.createSubtypeConstraint(expressionVar, fConstraintVariableFactory.makeDeclaringTypeVariable(rootDefs[0]))));
}else{
Collection<ITypeConstraint> constraints= new ArrayList<>();
for (IMethodBinding rootDef : rootDefs) {
ConstraintVariable rootDefTypeVar= fConstraintVariableFactory.makeDeclaringTypeVariable(rootDef);
ITypeConstraint[] tc= fTypeConstraintFactory.createSubtypeConstraint(expressionVar, rootDefTypeVar);
constraints.addAll(Arrays.asList(tc));
}
ITypeConstraint[] constraintsArray= constraints.toArray(new ITypeConstraint[constraints.size()]);
if (constraintsArray.length > 0){
result.add(fTypeConstraintFactory.createCompositeOrTypeConstraint(constraintsArray));
}
}
} else {
ConstraintVariable typeVar= fConstraintVariableFactory.makeDeclaringTypeVariable(methodBinding);
ConstraintVariable expressionVar= fConstraintVariableFactory.makeExpressionOrTypeVariable(invocation.getExpression(), getContext());
result.addAll(Arrays.asList(fTypeConstraintFactory.createSubtypeConstraint(expressionVar, typeVar)));
}
}
return result.toArray(new ITypeConstraint[result.size()]);
}
@Override
public ITypeConstraint[] create(QualifiedName qualifiedName){
SimpleName name= qualifiedName.getName();
Name qualifier= qualifiedName.getQualifier();
IBinding nameBinding= name.resolveBinding();
if (nameBinding instanceof IVariableBinding){
IVariableBinding vb= (IVariableBinding)nameBinding;
if (vb.isField())
return createConstraintsForAccessToField(vb, qualifier, qualifiedName);
} //TODO other bindings
return new ITypeConstraint[0];
}
@Override
public ITypeConstraint[] create(ReturnStatement returnStatement){
if (returnStatement.getExpression() == null)
return new ITypeConstraint[0];
ConstraintVariable returnTypeVariable= fConstraintVariableFactory.makeReturnTypeVariable(returnStatement);
return fTypeConstraintFactory.createSubtypeConstraint(
fConstraintVariableFactory.makeExpressionOrTypeVariable(returnStatement.getExpression(), getContext()),
returnTypeVariable);
}
@Override
public ITypeConstraint[] create(SingleVariableDeclaration svd){
ITypeConstraint[] defines= fTypeConstraintFactory.createDefinesConstraint(
fConstraintVariableFactory.makeExpressionOrTypeVariable(svd.getName(), getContext()),
fConstraintVariableFactory.makeTypeVariable(svd.getType()));
if (svd.getInitializer() == null)
return defines;
ITypeConstraint[] constraints = fTypeConstraintFactory.createSubtypeConstraint(
fConstraintVariableFactory.makeExpressionOrTypeVariable(svd.getInitializer(), getContext()),
fConstraintVariableFactory.makeExpressionOrTypeVariable(svd.getName(), getContext()));
if (defines.length == 0 && constraints.length == 0){
return new ITypeConstraint[0];
} else if (defines.length == 0){
return constraints;
} else if (constraints.length == 0){
return defines;
} else {
List<ITypeConstraint> all= new ArrayList<>();
all.addAll(Arrays.asList(defines));
all.addAll(Arrays.asList(constraints));
return all.toArray(new ITypeConstraint[all.size()]);
}
}
@Override
public ITypeConstraint[] create(SuperConstructorInvocation invocation){
List<Expression> arguments= invocation.arguments();
List<ITypeConstraint> result= new ArrayList<>(arguments.size());
IMethodBinding methodBinding= invocation.resolveConstructorBinding();
result.addAll(Arrays.asList(getArgumentConstraints(arguments, methodBinding)));
return result.toArray(new ITypeConstraint[result.size()]);
}
@Override
public ITypeConstraint[] create(SuperFieldAccess access){
SimpleName name= access.getName();
IBinding binding= name.resolveBinding();
if (! (binding instanceof IVariableBinding))
return new ITypeConstraint[0];
IVariableBinding vb= (IVariableBinding)binding;
return createConstraintsForAccessToField(vb, null, access);
}
@Override
public ITypeConstraint[] create(SuperMethodInvocation invocation){
List<Expression> arguments= invocation.arguments();
List<ITypeConstraint> result= new ArrayList<>(arguments.size());
IMethodBinding methodBinding= invocation.resolveMethodBinding();
ITypeConstraint[] returnTypeConstraint= getReturnTypeConstraint(invocation, methodBinding);
result.addAll(Arrays.asList(returnTypeConstraint));
result.addAll(Arrays.asList(getArgumentConstraints(arguments, methodBinding)));
return result.toArray(new ITypeConstraint[result.size()]);
}
@Override
public ITypeConstraint[] create(ThisExpression expression){
ConstraintVariable thisExpression= fConstraintVariableFactory.makeExpressionOrTypeVariable(expression, getContext());
ConstraintVariable declaringType= fConstraintVariableFactory.makeRawBindingVariable(expression.resolveTypeBinding());//TODO fix this - can't use Decl(M) because 'this' can live outside of methods
return fTypeConstraintFactory.createDefinesConstraint(thisExpression, declaringType);
}
@Override
public ITypeConstraint[] create(VariableDeclarationExpression vde){
return getConstraintsFromFragmentList(vde.fragments(), vde.getType());
}
@Override
public ITypeConstraint[] create(VariableDeclarationFragment vdf){
if (vdf.getInitializer() == null)
return new ITypeConstraint[0];
return fTypeConstraintFactory.createSubtypeConstraint(
fConstraintVariableFactory.makeExpressionOrTypeVariable(vdf.getInitializer(), getContext()),
fConstraintVariableFactory.makeExpressionOrTypeVariable(vdf.getName(), getContext()));
}
@Override
public ITypeConstraint[] create(VariableDeclarationStatement vds){
return getConstraintsFromFragmentList(vds.fragments(), vds.getType());
}
@Override
public ITypeConstraint[] create(ThrowStatement node) {
ConstraintVariable nameVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(node.getExpression(), getContext());
ITypeBinding throwable= node.getAST().resolveWellKnownType("java.lang.Throwable"); //$NON-NLS-1$
return fTypeConstraintFactory.createSubtypeConstraint(
nameVariable,
fConstraintVariableFactory.makeRawBindingVariable(throwable));
}
//--------- private helpers ----------------//
private Collection<ITypeConstraint> getConstraintsForFieldDeclaringTypes(FieldDeclaration fd) {
Collection<ITypeConstraint> result= new ArrayList<>(fd.fragments().size());
for (Iterator<VariableDeclarationFragment> iter= fd.fragments().iterator(); iter.hasNext();) {
VariableDeclarationFragment varDecl= iter.next();
IVariableBinding binding= varDecl.resolveBinding();
Assert.isTrue(binding.isField());
result.addAll(Arrays.asList(fTypeConstraintFactory.createDefinesConstraint(
fConstraintVariableFactory.makeDeclaringTypeVariable(binding),
fConstraintVariableFactory.makeRawBindingVariable(binding.getDeclaringClass()))));
}
return result;
}
private Collection<ITypeConstraint> getConstraintsForHiding(FieldDeclaration fd) {
Collection<ITypeConstraint> result= new ArrayList<>();
for (Iterator<VariableDeclarationFragment> iter= fd.fragments().iterator(); iter.hasNext();) {
result.addAll(getConstraintsForHiding(iter.next()));
}
return result;
}
private Collection<ITypeConstraint> getConstraintsForHiding(VariableDeclarationFragment fragment) {
Collection<ITypeConstraint> result= new ArrayList<>();
IVariableBinding fieldBinding= fragment.resolveBinding();
Assert.isTrue(fieldBinding.isField());
Set<ITypeBinding> declaringTypes= getDeclaringSuperTypes(fieldBinding);
ConstraintVariable hiddingFieldVar= fConstraintVariableFactory.makeDeclaringTypeVariable(fieldBinding);
for (Iterator<ITypeBinding> iter= declaringTypes.iterator(); iter.hasNext();) {
ITypeBinding declaringSuperType= iter.next();
IVariableBinding hiddenField= findField(fieldBinding, declaringSuperType);
Assert.isTrue(hiddenField.isField());
ConstraintVariable hiddenFieldVar= fConstraintVariableFactory.makeDeclaringTypeVariable(hiddenField);
result.addAll(Arrays.asList(fTypeConstraintFactory.createStrictSubtypeConstraint(hiddingFieldVar, hiddenFieldVar)));
}
return result;
}
private ITypeConstraint[] getConstraintsFromFragmentList(List<VariableDeclarationFragment> list, Type type) {
int size= list.size();
ConstraintVariable typeVariable= fConstraintVariableFactory.makeTypeVariable(type);
List<ITypeConstraint> result= new ArrayList<>((size * (size - 1))/2);
for (int i= 0; i < size; i++) {
VariableDeclarationFragment fragment1= list.get(i);
SimpleName fragment1Name= fragment1.getName();
result.addAll(Arrays.asList(fTypeConstraintFactory.createDefinesConstraint(
fConstraintVariableFactory.makeExpressionOrTypeVariable(fragment1Name, getContext()),
typeVariable)));
for (int j= i + 1; j < size; j++) {
VariableDeclarationFragment fragment2= list.get(j);
result.addAll(Arrays.asList(fTypeConstraintFactory.createEqualsConstraint(
fConstraintVariableFactory.makeExpressionOrTypeVariable(fragment1Name, getContext()),
fConstraintVariableFactory.makeExpressionOrTypeVariable(fragment2.getName(), getContext()))));
}
}
return result.toArray(new ITypeConstraint[result.size()]);
}
private Collection<ITypeConstraint> getConstraintsForOverriding(IMethodBinding overridingMethod) {
Collection<ITypeConstraint> result= new ArrayList<>();
Set<ITypeBinding> declaringSupertypes= getDeclaringSuperTypes(overridingMethod);
for (Iterator<ITypeBinding> iter= declaringSupertypes.iterator(); iter.hasNext();) {
ITypeBinding superType= iter.next();
IMethodBinding overriddenMethod= findMethod(overridingMethod, superType);
Assert.isNotNull(overriddenMethod);//because we asked for declaring types
if (Bindings.equals(overridingMethod, overriddenMethod))
continue;
ITypeConstraint[] returnTypeConstraint= fTypeConstraintFactory.createEqualsConstraint(
fConstraintVariableFactory.makeReturnTypeVariable(overriddenMethod),
fConstraintVariableFactory.makeReturnTypeVariable(overridingMethod));
result.addAll(Arrays.asList(returnTypeConstraint));
Assert.isTrue(overriddenMethod.getParameterTypes().length == overridingMethod.getParameterTypes().length);
for (int i= 0, n= overriddenMethod.getParameterTypes().length; i < n; i++) {
ITypeConstraint[] parameterTypeConstraint= fTypeConstraintFactory.createEqualsConstraint(
fConstraintVariableFactory.makeParameterTypeVariable(overriddenMethod, i),
fConstraintVariableFactory.makeParameterTypeVariable(overridingMethod, i));
result.addAll(Arrays.asList(parameterTypeConstraint));
}
ITypeConstraint[] declaringTypeConstraint= fTypeConstraintFactory.createStrictSubtypeConstraint(
fConstraintVariableFactory.makeDeclaringTypeVariable(overridingMethod),
fConstraintVariableFactory.makeDeclaringTypeVariable(overriddenMethod));
result.addAll(Arrays.asList(declaringTypeConstraint));
}
return result;
}
private ITypeConstraint[] getReturnTypeConstraint(Expression invocation, IMethodBinding methodBinding){
if (methodBinding == null || methodBinding.isConstructor() || methodBinding.getReturnType().isPrimitive())
return new ITypeConstraint[0];
ConstraintVariable returnTypeVariable= fConstraintVariableFactory.makeReturnTypeVariable(methodBinding);
ConstraintVariable invocationVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(invocation, getContext());
return fTypeConstraintFactory.createDefinesConstraint(invocationVariable, returnTypeVariable);
}
private ITypeConstraint[] getArgumentConstraints(List<Expression> arguments, IMethodBinding methodBinding) {
List<ITypeConstraint> result= new ArrayList<>(arguments.size());
if (methodBinding == null)
return new ITypeConstraint[0];
if (methodBinding.isVarargs()) {
ITypeBinding[] parameterTypes= methodBinding.getParameterTypes();
final int nParams= parameterTypes.length;
final int nArgs= arguments.size();
Assert.isTrue(nArgs >= nParams - 1); // there may be zero args for the vararg parameter
Assert.isTrue(nParams >= 1); // at least one parameter for a vararg method
int i= 0;
// add the normal argument constraints up to the last one
for (; i < nParams - 1; i++) {
Expression argument= arguments.get(i);
ConstraintVariable expressionVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(argument, getContext());
ConstraintVariable parameterTypeVariable= fConstraintVariableFactory.makeParameterTypeVariable(methodBinding, i);
ITypeConstraint[] argConstraint= fTypeConstraintFactory.createSubtypeConstraint(expressionVariable, parameterTypeVariable);
result.addAll(Arrays.asList(argConstraint));
}
// create argument constraints for all arguments wrapped into the vararg parameter
boolean directArray= false;
// a) there is exactly one remaining argument -> be careful as it may be a direct array param
// This is currently not used by Generalize Type Declaration as it does not support array types:
// if (i == nArgs - 1) {
// Expression argument= (Expression) arguments.get(i);
// if (TypeRules.canAssign(parameterTypes[nParams - 1], argument.resolveTypeBinding())) {
// ConstraintVariable parameterTypeVariable= fConstraintVariableFactory.makeParameterTypeVariable(methodBinding, nParams - 1);
// ConstraintVariable expressionVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(argument, getContext());
// ITypeConstraint[] argConstraint= fTypeConstraintFactory.createSubtypeConstraint(expressionVariable, parameterTypeVariable);
// result.addAll(Arrays.asList(argConstraint));
// directArray= true;
// //XXX there should be a constraint that logically ORs the direct array and element type constraints
// }
// }
// b) there are zero ore more than one arguments remaining
if (!directArray && i < nArgs) {
// get the component type of the vararg-array
ITypeBinding binding= methodBinding.getParameterTypes()[nParams - 1];
ITypeBinding componentBinding= binding.getComponentType();
Assert.isNotNull(componentBinding);
ConstraintVariable parameterTypeVariable= fConstraintVariableFactory.makeRawBindingVariable(componentBinding);
for (; i < nArgs; i++) {
Expression argument= arguments.get(i);
ConstraintVariable expressionVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(argument, getContext());
ITypeConstraint[] argConstraint= fTypeConstraintFactory.createSubtypeConstraint(expressionVariable, parameterTypeVariable);
result.addAll(Arrays.asList(argConstraint));
}
}
} else {
for (int i= 0, n= arguments.size(); i < n; i++) {
Expression argument= arguments.get(i);
ConstraintVariable expressionVariable= fConstraintVariableFactory.makeExpressionOrTypeVariable(argument, getContext());
ConstraintVariable parameterTypeVariable= fConstraintVariableFactory.makeParameterTypeVariable(methodBinding, i);
ITypeConstraint[] argConstraint= fTypeConstraintFactory.createSubtypeConstraint(expressionVariable, parameterTypeVariable);
result.addAll(Arrays.asList(argConstraint));
}
}
return result.toArray(new ITypeConstraint[result.size()]);
}
private static Type getTypeParent(ArrayInitializer arrayInitializer) {
if (arrayInitializer.getParent() instanceof ArrayCreation){
return ((ArrayCreation)arrayInitializer.getParent()).getType().getElementType();
} else if (arrayInitializer.getParent() instanceof ArrayInitializer){
return getTypeParent((ArrayInitializer) arrayInitializer.getParent());
} else if (arrayInitializer.getParent() instanceof VariableDeclaration){
VariableDeclaration parent= (VariableDeclaration)arrayInitializer.getParent();
if (parent.getParent() instanceof VariableDeclarationStatement){
Type type= ((VariableDeclarationStatement)parent.getParent()).getType();
return ASTNodes.getElementType(type);
} else if (parent.getParent() instanceof VariableDeclarationExpression){
Type type= ((VariableDeclarationExpression)parent.getParent()).getType();
return ASTNodes.getElementType(type);
} else if (parent.getParent() instanceof FieldDeclaration){
Type type= ((FieldDeclaration)parent.getParent()).getType();
return ASTNodes.getElementType(type);
}
}
Assert.isTrue(false);//array initializers are allowed in only 2 places
return null;
}
private ITypeConstraint[] createOrOrSubtypeConstraint(ConstraintVariable var1, ConstraintVariable var2){
ITypeConstraint[] c1= fTypeConstraintFactory.createSubtypeConstraint(var1, var2);
ITypeConstraint[] c2= fTypeConstraintFactory.createSubtypeConstraint(var2, var1);
if (c1.length == 0 && c2.length == 0){
return new ITypeConstraint[0];
}
return new ITypeConstraint[]{ fTypeConstraintFactory.createCompositeOrTypeConstraint(new ITypeConstraint[]{c1[0], c2[0]}) };
}
private ITypeConstraint[] createConstraintsForAccessToField(IVariableBinding fieldBinding, Expression qualifier, Expression accessExpression){
Assert.isTrue(fieldBinding.isField());
ITypeConstraint[] defines= fTypeConstraintFactory.createDefinesConstraint(
fConstraintVariableFactory.makeExpressionOrTypeVariable(accessExpression, getContext()),
fConstraintVariableFactory.makeRawBindingVariable(fieldBinding.getType()));
if (qualifier == null)
return defines;
ITypeConstraint[] subType= fTypeConstraintFactory.createSubtypeConstraint(
fConstraintVariableFactory.makeExpressionOrTypeVariable(qualifier, getContext()),
fConstraintVariableFactory.makeDeclaringTypeVariable(fieldBinding));
if (defines.length == 0){
return subType;
} else if (subType.length == 0){
return defines;
} else {
return new ITypeConstraint[]{defines[0], subType[0]};
}
}
private static IVariableBinding findField(IVariableBinding fieldBinding, ITypeBinding type) {
if (fieldBinding.getDeclaringClass().equals(type))
return fieldBinding;
return Bindings.findFieldInType(type, fieldBinding.getName());
}
/*
* return Set of ITypeBindings
*/
private static Set<ITypeBinding> getDeclaringSuperTypes(IVariableBinding fieldBinding) {
Set<ITypeBinding> result= new HashSet<>();
for (ITypeBinding type : Bindings.getAllSuperTypes(fieldBinding.getDeclaringClass())) {
if (findField(fieldBinding, type) != null)
result.add(type);
}
return result;
}
//--- RootDef ----//
protected static IMethodBinding[] getRootDefs(IMethodBinding methodBinding) {
Set<ITypeBinding> declaringSuperTypes= getDeclaringSuperTypes(methodBinding);
Set<IMethodBinding> result= new LinkedHashSet<>();
for (Iterator<ITypeBinding> iter= declaringSuperTypes.iterator(); iter.hasNext();) {
ITypeBinding type= iter.next();
if (! containsASuperType(type, declaringSuperTypes))
result.add(findMethod(methodBinding, type));
}
if (result.size() == 0){
result.add(methodBinding);
}
return result.toArray(new IMethodBinding[result.size()]);
}
/*
* @param declaringSuperTypes Set of ITypeBindings
* @return <code>true</code> iff <code>declaringSuperTypes</code> contains a type
* which is a strict supertype of <code>type</code>
*/
private static boolean containsASuperType(ITypeBinding type, Set<ITypeBinding> declaringSuperTypes) {
for (Iterator<ITypeBinding> iter= declaringSuperTypes.iterator(); iter.hasNext();) {
ITypeBinding maybeSuperType= iter.next();
if (! Bindings.equals(maybeSuperType, type) && Bindings.isSuperType(maybeSuperType, type))
return true;
}
return false;
}
/*
* return Set of ITypeBindings
*/
protected static Set<ITypeBinding> getDeclaringSuperTypes(IMethodBinding methodBinding) {
ITypeBinding superClass = methodBinding.getDeclaringClass();
Set<ITypeBinding> allSuperTypes= new LinkedHashSet<>();
allSuperTypes.addAll(Arrays.asList(Bindings.getAllSuperTypes(superClass)));
if (allSuperTypes.isEmpty())
allSuperTypes.add(methodBinding.getDeclaringClass()); //TODO: Why only iff empty? The declaring class is not a supertype ...
Set<ITypeBinding> result= new HashSet<>();
for (Iterator<ITypeBinding> iter= allSuperTypes.iterator(); iter.hasNext();) {
ITypeBinding type= iter.next();
if (findMethod(methodBinding, type) != null)
result.add(type);
}
return result;
}
protected static IMethodBinding findMethod(IMethodBinding methodBinding, ITypeBinding type) {
if (methodBinding.getDeclaringClass().equals(type))
return methodBinding;
return Bindings.findOverriddenMethodInType(type, methodBinding);
}
private static boolean isClassBinding(ITypeBinding typeBinding){
return typeBinding != null && typeBinding.isClass();
}
}