private static void checkAlternateBinding( BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) { InvocationSite fakeInvocationSite = new InvocationSite() { public TypeBinding[] genericTypeArguments() { return null; } public boolean isSuperAccess() { return invocationSite.isSuperAccess(); } public boolean isTypeAccess() { return invocationSite.isTypeAccess(); } public void setActualReceiverType(ReferenceBinding actualReceiverType) { /* ignore */ } public void setDepth(int depth) { /* ignore */ } public void setFieldIndex(int depth) { /* ignore */ } public int sourceStart() { return 0; } public int sourceEnd() { return 0; } }; MethodBinding bindingIfNoCast; if (binding.isConstructor()) { bindingIfNoCast = scope.getConstructor( (ReferenceBinding) receiverType, alternateArgumentTypes, fakeInvocationSite); } else { bindingIfNoCast = receiver.isImplicitThis() ? scope.getImplicitMethod( binding.selector, alternateArgumentTypes, fakeInvocationSite) : scope.getMethod( receiverType, binding.selector, alternateArgumentTypes, fakeInvocationSite); } if (bindingIfNoCast == binding) { int argumentLength = originalArgumentTypes.length; if (binding.isVarargs()) { int paramLength = binding.parameters.length; if (paramLength == argumentLength) { int varargsIndex = paramLength - 1; ArrayBinding varargsType = (ArrayBinding) binding.parameters[varargsIndex]; TypeBinding lastArgType = alternateArgumentTypes[varargsIndex]; // originalType may be compatible already, but cast mandated // to clarify between varargs/non-varargs call if (varargsType.dimensions != lastArgType.dimensions()) { return; } if (lastArgType.isCompatibleWith(varargsType.elementsType()) && lastArgType.isCompatibleWith(varargsType)) { return; } } } for (int i = 0; i < argumentLength; i++) { if (originalArgumentTypes[i] != alternateArgumentTypes[i]) { scope.problemReporter().unnecessaryCast((CastExpression) arguments[i]); } } } }
public TypeBinding resolveType(BlockScope scope) { // Answer the signature return type // Base type promotion this.constant = Constant.NotAConstant; boolean receiverCast = false, argsContainCast = false; if (this.receiver instanceof CastExpression) { this.receiver.bits |= DisableUnnecessaryCastCheck; // will check later on receiverCast = true; } this.actualReceiverType = this.receiver.resolveType(scope); if (receiverCast && this.actualReceiverType != null) { // due to change of declaring class with receiver type, only identity cast should be notified if (((CastExpression) this.receiver).expression.resolvedType == this.actualReceiverType) { scope.problemReporter().unnecessaryCast((CastExpression) this.receiver); } } // resolve type arguments (for generic constructor call) if (this.typeArguments != null) { int length = this.typeArguments.length; boolean argHasError = false; // typeChecks all arguments this.genericTypeArguments = new TypeBinding[length]; for (int i = 0; i < length; i++) { if ((this.genericTypeArguments[i] = this.typeArguments[i].resolveType(scope, true /* check bounds*/)) == null) { argHasError = true; } } if (argHasError) { return null; } } // will check for null after args are resolved TypeBinding[] argumentTypes = Binding.NO_PARAMETERS; if (this.arguments != null) { boolean argHasError = false; // typeChecks all arguments int length = this.arguments.length; argumentTypes = new TypeBinding[length]; for (int i = 0; i < length; i++) { Expression argument = this.arguments[i]; if (argument instanceof CastExpression) { argument.bits |= DisableUnnecessaryCastCheck; // will check later on argsContainCast = true; } if ((argumentTypes[i] = this.arguments[i].resolveType(scope)) == null) argHasError = true; } if (argHasError) { if (this.actualReceiverType instanceof ReferenceBinding) { // record any selector match, for clients who may still need hint about possible method // match this.binding = scope.findMethod( (ReferenceBinding) this.actualReceiverType, this.selector, new TypeBinding[] {}, this); } return null; } } if (this.actualReceiverType == null) { return null; } // base type cannot receive any message if (this.actualReceiverType.isBaseType()) { scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes); return null; } this.binding = this.receiver.isImplicitThis() ? scope.getImplicitMethod(this.selector, argumentTypes, this) : scope.getMethod(this.actualReceiverType, this.selector, argumentTypes, this); if (!this.binding.isValidBinding()) { if (this.binding instanceof ProblemMethodBinding && ((ProblemMethodBinding) this.binding).problemId() == ProblemReasons.NotVisible) { if (this.evaluationContext.declaringTypeName != null) { this.delegateThis = scope.getField(scope.enclosingSourceType(), EvaluationConstants.DELEGATE_THIS, this); if (this.delegateThis == null) { // if not found then internal error, field should have been found this.constant = Constant.NotAConstant; scope.problemReporter().invalidMethod(this, this.binding); return null; } } else { this.constant = Constant.NotAConstant; scope.problemReporter().invalidMethod(this, this.binding); return null; } CodeSnippetScope localScope = new CodeSnippetScope(scope); MethodBinding privateBinding = this.receiver instanceof CodeSnippetThisReference && ((CodeSnippetThisReference) this.receiver).isImplicit ? localScope.getImplicitMethod( (ReferenceBinding) this.delegateThis.type, this.selector, argumentTypes, this) : localScope.getMethod(this.delegateThis.type, this.selector, argumentTypes, this); if (!privateBinding.isValidBinding()) { if (this.binding.declaringClass == null) { if (this.actualReceiverType instanceof ReferenceBinding) { this.binding.declaringClass = (ReferenceBinding) this.actualReceiverType; } else { // really bad error .... scope .problemReporter() .errorNoMethodFor(this, this.actualReceiverType, argumentTypes); return null; } } scope.problemReporter().invalidMethod(this, this.binding); return null; } else { this.binding = privateBinding; } } else { if (this.binding.declaringClass == null) { if (this.actualReceiverType instanceof ReferenceBinding) { this.binding.declaringClass = (ReferenceBinding) this.actualReceiverType; } else { // really bad error .... scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes); return null; } } scope.problemReporter().invalidMethod(this, this.binding); return null; } } if (!this.binding.isStatic()) { // the "receiver" must not be a type, in other words, a NameReference that the TC has bound to // a Type if (this.receiver instanceof NameReference && (((NameReference) this.receiver).bits & Binding.TYPE) != 0) { scope.problemReporter().mustUseAStaticMethod(this, this.binding); } else { // handle indirect inheritance thru variable secondary bound // receiver may receive generic cast, as part of implicit conversion TypeBinding oldReceiverType = this.actualReceiverType; this.actualReceiverType = this.actualReceiverType.getErasureCompatibleType(this.binding.declaringClass); this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType); if (this.actualReceiverType != oldReceiverType && this.receiver.postConversionType(scope) != this .actualReceiverType) { // record need for explicit cast at codegen since // receiver could not handle it this.bits |= NeedReceiverGenericCast; } } } if (checkInvocationArguments( scope, this.receiver, this.actualReceiverType, this.binding, this.arguments, argumentTypes, argsContainCast, this)) { this.bits |= ASTNode.Unchecked; } // -------message send that are known to fail at compile time----------- if (this.binding.isAbstract()) { if (this.receiver.isSuper()) { scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, this.binding); } // abstract private methods cannot occur nor abstract static............ } if (isMethodUseDeprecated(this.binding, scope, true)) scope.problemReporter().deprecatedMethod(this.binding, this); // from 1.5 compliance on, array#clone() returns the array type (but binding still shows Object) if (this.actualReceiverType.isArrayType() && this.binding.parameters == Binding.NO_PARAMETERS && scope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_5 && CharOperation.equals(this.binding.selector, CLONE)) { this.resolvedType = this.actualReceiverType; } else { TypeBinding returnType = this.binding.returnType; if (returnType != null) { if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) { returnType = scope.environment().convertToRawType(returnType.erasure(), true); } returnType = returnType.capture(scope, this.sourceEnd); } this.resolvedType = returnType; } return this.resolvedType; }
public static TypeBinding resolveType( TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) { List<Extension> extensions = new ArrayList<Extension>(); TypeDeclaration decl = scope.classScope().referenceContext; EclipseNode owningType = null; for (EclipseNode typeNode = getTypeNode(decl); typeNode != null; typeNode = upToType(typeNode)) { Annotation ann = getAnnotation(ExtensionMethod.class, typeNode); if (ann != null) { extensions.addAll( 0, getApplicableExtensionMethods(typeNode, ann, methodCall.receiver.resolvedType)); if (owningType == null) owningType = typeNode; } } boolean skip = false; if (methodCall.receiver instanceof ThisReference && (((ThisReference) methodCall.receiver).bits & ASTNode.IsImplicitThis) != 0) skip = true; if (methodCall.receiver instanceof SuperReference) skip = true; if (methodCall.receiver instanceof NameReference) { Binding binding = ((NameReference) methodCall.receiver).binding; if (binding instanceof TypeBinding) skip = true; } if (!skip) for (Extension extension : extensions) { if (!extension.suppressBaseMethods && !(methodCall.binding instanceof ProblemMethodBinding)) continue; for (MethodBinding extensionMethod : extension.extensionMethods) { if (!Arrays.equals(methodCall.selector, extensionMethod.selector)) continue; MessageSend_postponedErrors.clear(methodCall); if (methodCall.receiver instanceof ThisReference) { methodCall.receiver.bits &= ~ASTNode.IsImplicitThis; } List<Expression> arguments = new ArrayList<Expression>(); arguments.add(methodCall.receiver); if (methodCall.arguments != null) arguments.addAll(Arrays.asList(methodCall.arguments)); List<TypeBinding> argumentTypes = new ArrayList<TypeBinding>(); for (Expression argument : arguments) { if (argument.resolvedType != null) argumentTypes.add(argument.resolvedType); // TODO: Instead of just skipping nulls entirely, there is probably a 'unresolved type' // placeholder. THAT is what we ought to be adding here! } Expression[] originalArgs = methodCall.arguments; methodCall.arguments = arguments.toArray(new Expression[0]); MethodBinding fixedBinding = scope.getMethod( extensionMethod.declaringClass, methodCall.selector, argumentTypes.toArray(new TypeBinding[0]), methodCall); if (fixedBinding instanceof ProblemMethodBinding) { methodCall.arguments = originalArgs; if (fixedBinding.declaringClass != null) { scope.problemReporter().invalidMethod(methodCall, fixedBinding); } } else { for (int i = 0, iend = arguments.size(); i < iend; i++) { Expression arg = arguments.get(i); if (fixedBinding.parameters[i].isArrayType() != arg.resolvedType.isArrayType()) break; if (arg instanceof MessageSend) { ((MessageSend) arg).valueCast = arg.resolvedType; } if (!fixedBinding.parameters[i].isBaseType() && arg.resolvedType.isBaseType()) { int id = arg.resolvedType.id; arg.implicitConversion = TypeIds.BOXING | (id + (id << 4)); // magic see TypeIds } else if (fixedBinding.parameters[i].isBaseType() && !arg.resolvedType.isBaseType()) { int id = fixedBinding.parameters[i].id; arg.implicitConversion = TypeIds.UNBOXING | (id + (id << 4)); // magic see TypeIds } } methodCall.receiver = createNameRef(extensionMethod.declaringClass, methodCall); methodCall.actualReceiverType = extensionMethod.declaringClass; methodCall.binding = fixedBinding; methodCall.resolvedType = methodCall.binding.returnType; } return methodCall.resolvedType; } } PostponedError error = MessageSend_postponedErrors.get(methodCall); if (error != null) error.fire(); MessageSend_postponedErrors.clear(methodCall); return resolvedType; }
public TypeBinding resolveType(BlockScope scope) { // Answer the signature return type // Base type promotion this.constant = Constant.NotAConstant; boolean receiverCast = false, argsContainCast = false; if (this.receiver instanceof CastExpression) { this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on receiverCast = true; } this.actualReceiverType = this.receiver.resolveType(scope); boolean receiverIsType = this.receiver instanceof NameReference && (((NameReference) this.receiver).bits & Binding.TYPE) != 0; if (receiverCast && this.actualReceiverType != null) { // due to change of declaring class with receiver type, only identity cast should be notified if (((CastExpression) this.receiver).expression.resolvedType == this.actualReceiverType) { scope.problemReporter().unnecessaryCast((CastExpression) this.receiver); } } // resolve type arguments (for generic constructor call) if (this.typeArguments != null) { int length = this.typeArguments.length; boolean argHasError = scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_5; // typeChecks all arguments this.genericTypeArguments = new TypeBinding[length]; for (int i = 0; i < length; i++) { TypeReference typeReference = this.typeArguments[i]; if ((this.genericTypeArguments[i] = typeReference.resolveType(scope, true /* check bounds*/)) == null) { argHasError = true; } if (argHasError && typeReference instanceof Wildcard) { scope.problemReporter().illegalUsageOfWildcard(typeReference); } } if (argHasError) { if (this.arguments != null) { // still attempt to resolve arguments for (int i = 0, max = this.arguments.length; i < max; i++) { this.arguments[i].resolveType(scope); } } return null; } } // will check for null after args are resolved TypeBinding[] argumentTypes = Binding.NO_PARAMETERS; if (this.arguments != null) { boolean argHasError = false; // typeChecks all arguments int length = this.arguments.length; argumentTypes = new TypeBinding[length]; for (int i = 0; i < length; i++) { Expression argument = this.arguments[i]; if (argument instanceof CastExpression) { argument.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on argsContainCast = true; } if ((argumentTypes[i] = argument.resolveType(scope)) == null) { argHasError = true; } } if (argHasError) { if (this.actualReceiverType instanceof ReferenceBinding) { // record a best guess, for clients who need hint about possible method match TypeBinding[] pseudoArgs = new TypeBinding[length]; for (int i = length; --i >= 0; ) pseudoArgs[i] = argumentTypes[i] == null ? TypeBinding.NULL : argumentTypes[i]; // replace args with errors with null type this.binding = this.receiver.isImplicitThis() ? scope.getImplicitMethod(this.selector, pseudoArgs, this) : scope.findMethod( (ReferenceBinding) this.actualReceiverType, this.selector, pseudoArgs, this); if (this.binding != null && !this.binding.isValidBinding()) { MethodBinding closestMatch = ((ProblemMethodBinding) this.binding).closestMatch; // record the closest match, for clients who may still need hint about possible method // match if (closestMatch != null) { if (closestMatch.original().typeVariables != Binding.NO_TYPE_VARIABLES) { // generic method // shouldn't return generic method outside its context, rather convert it to raw // method (175409) closestMatch = scope .environment() .createParameterizedGenericMethod( closestMatch.original(), (RawTypeBinding) null); } this.binding = closestMatch; MethodBinding closestMatchOriginal = closestMatch.original(); if (closestMatchOriginal.isOrEnclosedByPrivateType() && !scope.isDefinedInMethod(closestMatchOriginal)) { // ignore cases where method is used from within inside itself (e.g. direct // recursions) closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; } } } } return null; } } if (this.actualReceiverType == null) { return null; } // base type cannot receive any message if (this.actualReceiverType.isBaseType()) { scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes); return null; } this.binding = this.receiver.isImplicitThis() ? scope.getImplicitMethod(this.selector, argumentTypes, this) : scope.getMethod(this.actualReceiverType, this.selector, argumentTypes, this); if (!this.binding.isValidBinding()) { if (this.binding.declaringClass == null) { if (this.actualReceiverType instanceof ReferenceBinding) { this.binding.declaringClass = (ReferenceBinding) this.actualReceiverType; } else { scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes); return null; } } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=245007 avoid secondary errors in case of // missing super type for anonymous classes ... ReferenceBinding declaringClass = this.binding.declaringClass; boolean avoidSecondary = declaringClass != null && declaringClass.isAnonymousType() && declaringClass.superclass() instanceof MissingTypeBinding; if (!avoidSecondary) scope.problemReporter().invalidMethod(this, this.binding); MethodBinding closestMatch = ((ProblemMethodBinding) this.binding).closestMatch; switch (this.binding.problemId()) { case ProblemReasons.Ambiguous: break; // no resilience on ambiguous case ProblemReasons.NotVisible: case ProblemReasons.NonStaticReferenceInConstructorInvocation: case ProblemReasons.NonStaticReferenceInStaticContext: case ProblemReasons.ReceiverTypeNotVisible: case ProblemReasons.ParameterBoundMismatch: // only steal returnType in cases listed above if (closestMatch != null) this.resolvedType = closestMatch.returnType; break; } // record the closest match, for clients who may still need hint about possible method match if (closestMatch != null) { this.binding = closestMatch; MethodBinding closestMatchOriginal = closestMatch.original(); if (closestMatchOriginal.isOrEnclosedByPrivateType() && !scope.isDefinedInMethod(closestMatchOriginal)) { // ignore cases where method is used from within inside itself (e.g. direct recursions) closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; } } return (this.resolvedType != null && (this.resolvedType.tagBits & TagBits.HasMissingType) == 0) ? this.resolvedType : null; } final CompilerOptions compilerOptions = scope.compilerOptions(); if (compilerOptions.complianceLevel <= ClassFileConstants.JDK1_6 && this.binding.isPolymorphic()) { scope.problemReporter().polymorphicMethodNotBelow17(this); return null; } if (((this.bits & ASTNode.InsideExpressionStatement) != 0) && this.binding.isPolymorphic()) { // we only set the return type to be void if this method invocation is used inside an // expression statement this.binding = scope .environment() .updatePolymorphicMethodReturnType( (PolymorphicMethodBinding) this.binding, TypeBinding.VOID); } if ((this.binding.tagBits & TagBits.HasMissingType) != 0) { scope.problemReporter().missingTypeInMethod(this, this.binding); } if (!this.binding.isStatic()) { // the "receiver" must not be a type if (receiverIsType) { scope.problemReporter().mustUseAStaticMethod(this, this.binding); if (this.actualReceiverType.isRawType() && (this.receiver.bits & ASTNode.IgnoreRawTypeCheck) == 0 && compilerOptions.getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore) { scope.problemReporter().rawTypeReference(this.receiver, this.actualReceiverType); } } else { // handle indirect inheritance thru variable secondary bound // receiver may receive generic cast, as part of implicit conversion TypeBinding oldReceiverType = this.actualReceiverType; this.actualReceiverType = this.actualReceiverType.getErasureCompatibleType(this.binding.declaringClass); this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType); if (this.actualReceiverType != oldReceiverType && this.receiver.postConversionType(scope) != this .actualReceiverType) { // record need for explicit cast at codegen since // receiver could not handle it this.bits |= NeedReceiverGenericCast; } } } else { // static message invoked through receiver? legal but unoptimal (optional warning). if (!(this.receiver.isImplicitThis() || this.receiver.isSuper() || receiverIsType)) { scope.problemReporter().nonStaticAccessToStaticMethod(this, this.binding); } if (!this.receiver.isImplicitThis() && this.binding.declaringClass != this.actualReceiverType) { scope.problemReporter().indirectAccessToStaticMethod(this, this.binding); } } if (checkInvocationArguments( scope, this.receiver, this.actualReceiverType, this.binding, this.arguments, argumentTypes, argsContainCast, this)) { this.bits |= ASTNode.Unchecked; } // -------message send that are known to fail at compile time----------- if (this.binding.isAbstract()) { if (this.receiver.isSuper()) { scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, this.binding); } // abstract private methods cannot occur nor abstract static............ } if (isMethodUseDeprecated(this.binding, scope, true)) scope.problemReporter().deprecatedMethod(this.binding, this); // from 1.5 source level on, array#clone() returns the array type (but binding still shows // Object) if (this.binding == scope.environment().arrayClone && compilerOptions.sourceLevel >= ClassFileConstants.JDK1_5) { this.resolvedType = this.actualReceiverType; } else { TypeBinding returnType; if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6 returnType = this.binding.returnType; if (returnType != null) { returnType = scope.environment().convertToRawType(returnType.erasure(), true); } } else { returnType = this.binding.returnType; if (returnType != null) { returnType = returnType.capture(scope, this.sourceEnd); } } this.resolvedType = returnType; } if (this.receiver.isSuper() && compilerOptions.getSeverity(CompilerOptions.OverridingMethodWithoutSuperInvocation) != ProblemSeverities.Ignore) { final ReferenceContext referenceContext = scope.methodScope().referenceContext; if (referenceContext instanceof AbstractMethodDeclaration) { final AbstractMethodDeclaration abstractMethodDeclaration = (AbstractMethodDeclaration) referenceContext; MethodBinding enclosingMethodBinding = abstractMethodDeclaration.binding; if (enclosingMethodBinding.isOverriding() && CharOperation.equals(this.binding.selector, enclosingMethodBinding.selector) && this.binding.areParametersEqual(enclosingMethodBinding)) { abstractMethodDeclaration.bits |= ASTNode.OverridingMethodWithSupercall; } } } if (this.typeArguments != null && this.binding.original().typeVariables == Binding.NO_TYPE_VARIABLES) { scope .problemReporter() .unnecessaryTypeArgumentsForMethodInvocation( this.binding, this.genericTypeArguments, this.typeArguments); } return (this.resolvedType.tagBits & TagBits.HasMissingType) == 0 ? this.resolvedType : null; }