/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.Wildcard;
/**
* Binding denoting a generic method after type parameter substitutions got performed. On parameterized type bindings, all methods
* got substituted, regardless whether their signature did involve generics or not, so as to get the proper declaringClass for
* these methods.
*/
public class ParameterizedGenericMethodBinding extends ParameterizedMethodBinding implements Substitution {
public TypeBinding[] typeArguments;
private LookupEnvironment environment;
public boolean inferredReturnType;
public boolean wasInferred; // only set to true for instances resulting from method invocation inferrence
public boolean isRaw; // set to true for method behaving as raw for substitution purpose
private MethodBinding tiebreakMethod;
/** Perform inference of generic method type parameters and/or expected type */
public static MethodBinding computeCompatibleMethod(MethodBinding originalMethod, TypeBinding[] arguments,
Scope scope, InvocationSite invocationSite) {
ParameterizedGenericMethodBinding methodSubstitute;
TypeVariableBinding[] typeVariables = originalMethod.typeVariables;
TypeBinding[] substitutes = invocationSite.genericTypeArguments();
InferenceContext inferenceContext = null;
TypeBinding[] uncheckedArguments = null;
computeSubstitutes:
{
if (substitutes != null) {
// explicit type arguments got supplied
if (substitutes.length != typeVariables.length) {
// incompatible due to wrong arity
return new ProblemMethodBinding(originalMethod, originalMethod.selector, substitutes,
ProblemReasons.TypeParameterArityMismatch);
}
methodSubstitute = scope.environment().createParameterizedGenericMethod(originalMethod, substitutes);
break computeSubstitutes;
}
// perform type argument inference (15.12.2.7)
// initializes the map of substitutes (var --> type[][]{ equal, extends, super}
TypeBinding[] parameters = originalMethod.parameters;
inferenceContext = new InferenceContext(originalMethod);
methodSubstitute = inferFromArgumentTypes(scope, originalMethod, arguments, parameters, inferenceContext);
if (methodSubstitute == null)
return null;
// substitutes may hold null to denote unresolved vars, but null arguments got replaced with respective original variable
// in param method
// 15.12.2.8 - inferring unresolved type arguments
if (inferenceContext.hasUnresolvedTypeArgument()) {
if (inferenceContext.isUnchecked) { // only remember unchecked status post 15.12.2.7
int length = inferenceContext.substitutes.length;
System.arraycopy(inferenceContext.substitutes, 0, uncheckedArguments = new TypeBinding[length], 0,
length);
}
if (methodSubstitute.returnType != TypeBinding.VOID) {
TypeBinding expectedType = invocationSite.expectedType();
if (expectedType != null) {
// record it was explicit from context, as opposed to assumed by default (see below)
inferenceContext.hasExplicitExpectedType = true;
} else {
expectedType = scope.getJavaLangObject(); // assume Object by default
}
inferenceContext.expectedType = expectedType;
}
methodSubstitute = methodSubstitute.inferFromExpectedType(scope, inferenceContext);
if (methodSubstitute == null)
return null;
}
}
/*
* bounds check: https://bugs.eclipse.org/bugs/show_bug.cgi?id=242159, Inferred types may contain self reference in formal
* bounds. If "T extends I<T>" is a original type variable and T was inferred to be I<T> due possibly to under constraints
* and resultant glb application per 15.12.2.8, using this.typeArguments to drive the bounds check against itself is doomed
* to fail. For, the variable T would after substitution be I<I<T>> and would fail bounds check against I<T>. Use the
* inferred types from the context directly - see that there is one round of extra substitution that has taken place to
* properly substitute a remaining unresolved variable which also appears in a formal bound (So we really have a bounds
* mismatch between I<I<T>> and I<I<I<T>>>, in the absence of a fix.)
*/
Substitution substitution = null;
if (inferenceContext != null) {
substitution = new LingeringTypeVariableEliminator(typeVariables, inferenceContext.substitutes, scope);
} else {
substitution = methodSubstitute;
}
for (int i = 0, length = typeVariables.length; i < length; i++) {
TypeVariableBinding typeVariable = typeVariables[i];
TypeBinding substitute = methodSubstitute.typeArguments[i]; // retain for diagnostics
TypeBinding substituteForChecks =
Scope.substitute(new LingeringTypeVariableEliminator(typeVariables, null, scope), substitute); // while using this for
// bounds check
if (uncheckedArguments != null && uncheckedArguments[i] == null)
continue; // only bound check if inferred through 15.12.2.6
switch (typeVariable.boundCheck(substitution, substituteForChecks)) {
case TypeConstants.MISMATCH:
// incompatible due to bound check
int argLength = arguments.length;
TypeBinding[] augmentedArguments = new TypeBinding[argLength + 2]; // append offending substitute and typeVariable
System.arraycopy(arguments, 0, augmentedArguments, 0, argLength);
augmentedArguments[argLength] = substitute;
augmentedArguments[argLength + 1] = typeVariable;
return new ProblemMethodBinding(methodSubstitute, originalMethod.selector, augmentedArguments,
ProblemReasons.ParameterBoundMismatch);
case TypeConstants.UNCHECKED:
// tolerate unchecked bounds
methodSubstitute.tagBits |= TagBits.HasUncheckedTypeArgumentForBoundCheck;
break;
}
}
// check presence of unchecked argument conversion a posteriori (15.12.2.6)
return methodSubstitute;
}
/** Collect argument type mapping, handling varargs */
private static ParameterizedGenericMethodBinding inferFromArgumentTypes(Scope scope, MethodBinding originalMethod,
TypeBinding[] arguments, TypeBinding[] parameters,
InferenceContext inferenceContext) {
if (originalMethod.isVarargs()) {
int paramLength = parameters.length;
int minArgLength = paramLength - 1;
int argLength = arguments.length;
// process mandatory arguments
for (int i = 0; i < minArgLength; i++) {
parameters[i].collectSubstitutes(scope, arguments[i], inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
if (inferenceContext.status == InferenceContext.FAILED)
return null; // impossible substitution
}
// process optional arguments
if (minArgLength < argLength) {
TypeBinding varargType = parameters[minArgLength]; // last arg type - as is ?
TypeBinding lastArgument = arguments[minArgLength];
checkVarargDimension:
{
if (paramLength == argLength) {
if (lastArgument == TypeBinding.NULL)
break checkVarargDimension;
switch (lastArgument.dimensions()) {
case 0:
break; // will remove one dim
case 1:
if (!lastArgument.leafComponentType().isBaseType())
break checkVarargDimension;
break; // will remove one dim
default:
break checkVarargDimension;
}
}
// eliminate one array dimension
varargType = ((ArrayBinding)varargType).elementsType();
}
for (int i = minArgLength; i < argLength; i++) {
varargType.collectSubstitutes(scope, arguments[i], inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
if (inferenceContext.status == InferenceContext.FAILED)
return null; // impossible substitution
}
}
} else {
int paramLength = parameters.length;
for (int i = 0; i < paramLength; i++) {
parameters[i].collectSubstitutes(scope, arguments[i], inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
if (inferenceContext.status == InferenceContext.FAILED)
return null; // impossible substitution
}
}
TypeVariableBinding[] originalVariables = originalMethod.typeVariables;
if (!resolveSubstituteConstraints(scope, originalVariables, inferenceContext, false/*
* ignore Ti < : Uk
*/))
return null; // impossible substitution
// apply inferred variable substitutions - replacing unresolved variable with original ones in param method
TypeBinding[] inferredSustitutes = inferenceContext.substitutes;
TypeBinding[] actualSubstitutes = inferredSustitutes;
for (int i = 0, varLength = originalVariables.length; i < varLength; i++) {
if (inferredSustitutes[i] == null) {
if (actualSubstitutes == inferredSustitutes) {
System.arraycopy(inferredSustitutes, 0, actualSubstitutes = new TypeBinding[varLength], 0, i); // clone to replace
// null with
// original variable
// in param method
}
actualSubstitutes[i] = originalVariables[i];
} else if (actualSubstitutes != inferredSustitutes) {
actualSubstitutes[i] = inferredSustitutes[i];
}
}
ParameterizedGenericMethodBinding paramMethod =
scope.environment().createParameterizedGenericMethod(originalMethod, actualSubstitutes);
return paramMethod;
}
private static boolean resolveSubstituteConstraints(Scope scope, TypeVariableBinding[] typeVariables,
InferenceContext inferenceContext, boolean considerEXTENDSConstraints) {
TypeBinding[] substitutes = inferenceContext.substitutes;
int varLength = typeVariables.length;
// check Tj=U constraints
nextTypeParameter:
for (int i = 0; i < varLength; i++) {
TypeVariableBinding current = typeVariables[i];
TypeBinding substitute = substitutes[i];
if (substitute != null)
continue nextTypeParameter; // already inferred previously
TypeBinding[] equalSubstitutes = inferenceContext.getSubstitutes(current, TypeConstants.CONSTRAINT_EQUAL);
if (equalSubstitutes != null) {
nextConstraint:
for (int j = 0, equalLength = equalSubstitutes.length; j < equalLength; j++) {
TypeBinding equalSubstitute = equalSubstitutes[j];
if (equalSubstitute == null)
continue nextConstraint;
if (equalSubstitute == current) {
// try to find a better different match if any in subsequent equal candidates
for (int k = j + 1; k < equalLength; k++) {
equalSubstitute = equalSubstitutes[k];
if (equalSubstitute != current && equalSubstitute != null) {
substitutes[i] = equalSubstitute;
continue nextTypeParameter;
}
}
substitutes[i] = current;
continue nextTypeParameter;
}
// if (equalSubstitute.isTypeVariable()) {
// TypeVariableBinding variable = (TypeVariableBinding) equalSubstitute;
// // substituted by a variable of the same method, ignore
// if (variable.rank < varLength && typeVariables[variable.rank] == variable) {
// // TODO (philippe) rewrite all other constraints to use current instead.
// continue nextConstraint;
// }
// }
substitutes[i] = equalSubstitute;
continue nextTypeParameter; // pick first match, applicability check will rule out invalid scenario where others
// were present
}
}
}
if (inferenceContext.hasUnresolvedTypeArgument()) {
// check Tj>:U constraints
nextTypeParameter:
for (int i = 0; i < varLength; i++) {
TypeVariableBinding current = typeVariables[i];
TypeBinding substitute = substitutes[i];
if (substitute != null)
continue nextTypeParameter; // already inferred previously
TypeBinding[] bounds = inferenceContext.getSubstitutes(current, TypeConstants.CONSTRAINT_SUPER);
if (bounds == null)
continue nextTypeParameter;
TypeBinding mostSpecificSubstitute = scope.lowerUpperBound(bounds);
if (mostSpecificSubstitute == null) {
return false; // incompatible
}
if (mostSpecificSubstitute != TypeBinding.VOID) {
substitutes[i] = mostSpecificSubstitute;
}
}
}
if (considerEXTENDSConstraints && inferenceContext.hasUnresolvedTypeArgument()) {
// check Tj<:U constraints
nextTypeParameter:
for (int i = 0; i < varLength; i++) {
TypeVariableBinding current = typeVariables[i];
TypeBinding substitute = substitutes[i];
if (substitute != null)
continue nextTypeParameter; // already inferred previously
TypeBinding[] bounds = inferenceContext.getSubstitutes(current, TypeConstants.CONSTRAINT_EXTENDS);
if (bounds == null)
continue nextTypeParameter;
TypeBinding[] glb = Scope.greaterLowerBound(bounds);
TypeBinding mostSpecificSubstitute = null;
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=341795 - Per 15.12.2.8, we should fully apply glb
if (glb != null) {
if (glb.length == 1) {
mostSpecificSubstitute = glb[0];
} else {
TypeBinding[] otherBounds = new TypeBinding[glb.length - 1];
System.arraycopy(glb, 1, otherBounds, 0, glb.length - 1);
mostSpecificSubstitute =
scope.environment().createWildcard(null, 0, glb[0], otherBounds, Wildcard.EXTENDS);
}
}
if (mostSpecificSubstitute != null) {
substitutes[i] = mostSpecificSubstitute;
}
}
}
return true;
}
/**
* Create raw generic method for raw type (double substitution from type vars with raw type arguments, and erasure of method
* variables) Only invoked for non-static generic methods of raw type
*/
public ParameterizedGenericMethodBinding(MethodBinding originalMethod, RawTypeBinding rawType,
LookupEnvironment environment) {
TypeVariableBinding[] originalVariables = originalMethod.typeVariables;
int length = originalVariables.length;
TypeBinding[] rawArguments = new TypeBinding[length];
for (int i = 0; i < length; i++) {
rawArguments[i] = environment.convertToRawType(originalVariables[i].erasure(), false /*
* do not force conversion of
* enclosing types
*/);
}
this.isRaw = true;
this.tagBits = originalMethod.tagBits;
this.environment = environment;
this.modifiers = originalMethod.modifiers;
this.selector = originalMethod.selector;
this.declaringClass = rawType == null ? originalMethod.declaringClass : rawType;
this.typeVariables = Binding.NO_TYPE_VARIABLES;
this.typeArguments = rawArguments;
this.originalMethod = originalMethod;
boolean ignoreRawTypeSubstitution = rawType == null || originalMethod.isStatic();
this.parameters = Scope.substitute(this, ignoreRawTypeSubstitution ? originalMethod.parameters // no substitution if
// original was static
: Scope.substitute(rawType, originalMethod.parameters));
this.thrownExceptions = Scope.substitute(this, ignoreRawTypeSubstitution ? originalMethod.thrownExceptions // no
// substitution
// if original
// was static
: Scope
.substitute(rawType, originalMethod.thrownExceptions));
// error case where exception type variable would have been substituted by a non-reference type (207573)
if (this.thrownExceptions == null)
this.thrownExceptions = Binding.NO_EXCEPTIONS;
this.returnType = Scope.substitute(this, ignoreRawTypeSubstitution ? originalMethod.returnType // no substitution if
// original was static
: Scope.substitute(rawType, originalMethod.returnType));
this.wasInferred = false; // not resulting from method invocation inferrence
}
/** Create method of parameterized type, substituting original parameters with type arguments. */
public ParameterizedGenericMethodBinding(MethodBinding originalMethod, TypeBinding[] typeArguments,
LookupEnvironment environment) {
this.environment = environment;
this.modifiers = originalMethod.modifiers;
this.selector = originalMethod.selector;
this.declaringClass = originalMethod.declaringClass;
this.typeVariables = Binding.NO_TYPE_VARIABLES;
this.typeArguments = typeArguments;
this.isRaw = false;
this.tagBits = originalMethod.tagBits;
this.originalMethod = originalMethod;
this.parameters = Scope.substitute(this, originalMethod.parameters);
// error case where exception type variable would have been substituted by a non-reference type (207573)
this.returnType = Scope.substitute(this, originalMethod.returnType);
this.thrownExceptions = Scope.substitute(this, originalMethod.thrownExceptions);
if (this.thrownExceptions == null)
this.thrownExceptions = Binding.NO_EXCEPTIONS;
checkMissingType:
{
if ((this.tagBits & TagBits.HasMissingType) != 0)
break checkMissingType;
if ((this.returnType.tagBits & TagBits.HasMissingType) != 0) {
this.tagBits |= TagBits.HasMissingType;
break checkMissingType;
}
for (int i = 0, max = this.parameters.length; i < max; i++) {
if ((this.parameters[i].tagBits & TagBits.HasMissingType) != 0) {
this.tagBits |= TagBits.HasMissingType;
break checkMissingType;
}
}
for (int i = 0, max = this.thrownExceptions.length; i < max; i++) {
if ((this.thrownExceptions[i].tagBits & TagBits.HasMissingType) != 0) {
this.tagBits |= TagBits.HasMissingType;
break checkMissingType;
}
}
}
this.wasInferred = true;// resulting from method invocation inferrence
}
/*
* parameterizedDeclaringUniqueKey dot selector originalMethodGenericSignature percent typeArguments p.X<U> { <T> void bar(T t,
* U u) { new X<String>().bar(this, "") } } --> Lp/X<Ljava/lang/String
* ;>;.bar<T:Ljava/lang/Object;>(TT;Ljava/lang/String;)V%<Lp/X;>
*/
public char[] computeUniqueKey(boolean isLeaf) {
StringBuffer buffer = new StringBuffer();
buffer.append(this.originalMethod.computeUniqueKey(false/* not a leaf */));
buffer.append('%');
buffer.append('<');
if (!this.isRaw) {
int length = this.typeArguments.length;
for (int i = 0; i < length; i++) {
TypeBinding typeArgument = this.typeArguments[i];
buffer.append(typeArgument.computeUniqueKey(false/* not a leaf */));
}
}
buffer.append('>');
int resultLength = buffer.length();
char[] result = new char[resultLength];
buffer.getChars(0, resultLength, result, 0);
return result;
}
/** @see org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.Substitution#environment() */
public LookupEnvironment environment() {
return this.environment;
}
/**
* Returns true if some parameters got substituted. NOTE: generic method invocation delegates to its declaring method (could be
* a parameterized one)
*/
public boolean hasSubstitutedParameters() {
// generic parameterized method can represent either an invocation or a raw generic method
if (this.wasInferred)
return this.originalMethod.hasSubstitutedParameters();
return super.hasSubstitutedParameters();
}
/**
* Returns true if the return type got substituted. NOTE: generic method invocation delegates to its declaring method (could be
* a parameterized one)
*/
public boolean hasSubstitutedReturnType() {
if (this.inferredReturnType)
return this.originalMethod.hasSubstitutedReturnType();
return super.hasSubstitutedReturnType();
}
/**
* Given some type expectation, and type variable bounds, perform some inference. Returns true if still had unresolved type
* variable at the end of the operation
*/
private ParameterizedGenericMethodBinding inferFromExpectedType(Scope scope, InferenceContext inferenceContext) {
TypeVariableBinding[] originalVariables = this.originalMethod.typeVariables; // immediate parent (could be a parameterized
// method)
int varLength = originalVariables.length;
// infer from expected return type
if (inferenceContext.expectedType != null) {
this.returnType.collectSubstitutes(scope, inferenceContext.expectedType, inferenceContext,
TypeConstants.CONSTRAINT_SUPER);
if (inferenceContext.status == InferenceContext.FAILED)
return null; // impossible substitution
}
// infer from bounds of type parameters
for (int i = 0; i < varLength; i++) {
TypeVariableBinding originalVariable = originalVariables[i];
TypeBinding argument = this.typeArguments[i];
boolean argAlreadyInferred = argument != originalVariable;
if (originalVariable.firstBound == originalVariable.superclass) {
TypeBinding substitutedBound = Scope.substitute(this, originalVariable.superclass);
argument.collectSubstitutes(scope, substitutedBound, inferenceContext, TypeConstants.CONSTRAINT_SUPER);
if (inferenceContext.status == InferenceContext.FAILED)
return null; // impossible substitution
// JLS 15.12.2.8 claims reverse inference shouldn't occur, however it improves inference
// e.g. given: <E extends Object, S extends Collection<E>> S test1(S param)
// invocation: test1(new Vector<String>()) will infer: S=Vector<String> and with code below: E=String
if (argAlreadyInferred) {
substitutedBound.collectSubstitutes(scope, argument, inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
if (inferenceContext.status == InferenceContext.FAILED)
return null; // impossible substitution
}
}
for (int j = 0, max = originalVariable.superInterfaces.length; j < max; j++) {
TypeBinding substitutedBound = Scope.substitute(this, originalVariable.superInterfaces[j]);
argument.collectSubstitutes(scope, substitutedBound, inferenceContext, TypeConstants.CONSTRAINT_SUPER);
if (inferenceContext.status == InferenceContext.FAILED)
return null; // impossible substitution
// JLS 15.12.2.8 claims reverse inference shouldn't occur, however it improves inference
if (argAlreadyInferred) {
substitutedBound.collectSubstitutes(scope, argument, inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
if (inferenceContext.status == InferenceContext.FAILED)
return null; // impossible substitution
}
}
}
if (!resolveSubstituteConstraints(scope, originalVariables, inferenceContext, true/*
* consider Ti < : Uk
*/))
return null; // incompatible
// this.typeArguments = substitutes; - no op since side effects got performed during #resolveSubstituteConstraints
for (int i = 0; i < varLength; i++) {
TypeBinding substitute = inferenceContext.substitutes[i];
if (substitute != null) {
this.typeArguments[i] = substitute;
} else {
// remaining unresolved variable are considered to be Object (or their bound actually)
this.typeArguments[i] = inferenceContext.substitutes[i] = originalVariables[i].upperBound();
}
}
/*
* May still need an extra substitution at the end (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=121369) to properly
* substitute a remaining unresolved variable which also appear in a formal bound. See also
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5021635. It is questionable though whether this extra substitution
* should take place when the invocation site offers no guidance whatsoever and the type variables are inferred to be the
* glb of the published bounds - as there can recursion in the formal bounds, the inferred bounds would no longer be glb.
*/
this.typeArguments = Scope.substitute(this, this.typeArguments);
// adjust method types to reflect latest inference
TypeBinding oldReturnType = this.returnType;
this.returnType = Scope.substitute(this, this.returnType);
this.inferredReturnType = inferenceContext.hasExplicitExpectedType && this.returnType != oldReturnType;
this.parameters = Scope.substitute(this, this.parameters);
this.thrownExceptions = Scope.substitute(this, this.thrownExceptions);
// error case where exception type variable would have been substituted by a non-reference type (207573)
if (this.thrownExceptions == null)
this.thrownExceptions = Binding.NO_EXCEPTIONS;
checkMissingType:
{
if ((this.tagBits & TagBits.HasMissingType) != 0)
break checkMissingType;
if ((this.returnType.tagBits & TagBits.HasMissingType) != 0) {
this.tagBits |= TagBits.HasMissingType;
break checkMissingType;
}
for (int i = 0, max = this.parameters.length; i < max; i++) {
if ((this.parameters[i].tagBits & TagBits.HasMissingType) != 0) {
this.tagBits |= TagBits.HasMissingType;
break checkMissingType;
}
}
for (int i = 0, max = this.thrownExceptions.length; i < max; i++) {
if ((this.thrownExceptions[i].tagBits & TagBits.HasMissingType) != 0) {
this.tagBits |= TagBits.HasMissingType;
break checkMissingType;
}
}
}
return this;
}
/*
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=347600 && https://bugs.eclipse.org/bugs/show_bug.cgi?id=242159 Sometimes due
* to recursion/circularity in formal bounds, even *published bounds* fail bound check. We need to break the circularity/self
* reference in order not to be overly strict during type equivalence checks. See also
* http://bugs.sun.com/view_bug.do?bug_id=6932571
*/
private static class LingeringTypeVariableEliminator implements Substitution {
final private TypeVariableBinding[] variables;
final private TypeBinding[] substitutes; // when null, substitute type variables by unbounded wildcard
final private Scope scope;
/**
* @param variables
* @param substitutes
* when null, substitute type variable by unbounded wildcard
* @param scope
*/
public LingeringTypeVariableEliminator(TypeVariableBinding[] variables, TypeBinding[] substitutes, Scope scope) {
this.variables = variables;
this.substitutes = substitutes;
this.scope = scope;
}
// With T mapping to I<T>, answer of I<?>, when given T, having eliminated the circularity/self reference.
public TypeBinding substitute(TypeVariableBinding typeVariable) {
if (typeVariable.rank >= this.variables.length ||
this.variables[typeVariable.rank] != typeVariable) { // not kosher, don't touch.
return typeVariable;
}
if (this.substitutes != null) {
return Scope.substitute(new LingeringTypeVariableEliminator(this.variables, null, this.scope),
this.substitutes[typeVariable.rank]);
}
ReferenceBinding genericType =
(ReferenceBinding)(typeVariable.declaringElement instanceof ReferenceBinding
? typeVariable.declaringElement : null);
return this.scope.environment().createWildcard(genericType, typeVariable.rank, null, null, Wildcard.UNBOUND);
}
public LookupEnvironment environment() {
return this.scope.environment();
}
public boolean isRawSubstitution() {
return false;
}
}
/** @see org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.Substitution#isRawSubstitution() */
public boolean isRawSubstitution() {
return this.isRaw;
}
/** @see org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.Substitution#substitute(org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeVariableBinding) */
public TypeBinding substitute(TypeVariableBinding originalVariable) {
TypeVariableBinding[] variables = this.originalMethod.typeVariables;
int length = variables.length;
// check this variable can be substituted given parameterized type
if (originalVariable.rank < length && variables[originalVariable.rank] == originalVariable) {
return this.typeArguments[originalVariable.rank];
}
return originalVariable;
}
/** @see org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.MethodBinding#tiebreakMethod() */
public MethodBinding tiebreakMethod() {
if (this.tiebreakMethod == null)
this.tiebreakMethod = this.originalMethod.asRawMethod(this.environment);
return this.tiebreakMethod;
}
}