/** * Internal helper method to find the {@link AbstractMethodDeclaration} for a given {@link * MethodBinding} by searching a given set of statements. * * @param methodBinding {@link MethodBinding} to find the {@link AbstractMethodDeclaration} for * @param originalStatements statements to search for the {@link AbstractMethodDeclaration} in * @return {@link AbstractMethodDeclaration} for the given {@link MethodBinding} found in the * given {@link ProgramElement}s, or <code>null</code> if it could not be found */ private static AbstractMethodDeclaration declarationOf( MethodBinding methodBinding, ProgramElement[] originalStatements) { if (methodBinding != null && originalStatements != null) { List statements = new ArrayList(originalStatements.length); statements.addAll(Arrays.asList(originalStatements)); for (int i = 0; i < statements.size(); i++) { IProgramElement statement = (IProgramElement) statements.get(i); if (statement instanceof MessageSend) { MessageSend msgSend = (MessageSend) statement; // search arguments of message send if (msgSend.arguments != null) { statements.addAll(Arrays.asList(msgSend.arguments)); } /* add anonymous function message send * * function() { foo = "test" }(); */ if (msgSend.receiver instanceof IFunctionExpression) { statements.add(msgSend.receiver); } continue; } else if (statement instanceof ObjectLiteral) { ObjectLiteral objLit = (ObjectLiteral) statement; if (objLit.fields != null) { statements.addAll(Arrays.asList(objLit.fields)); } continue; } else if (statement instanceof ObjectLiteralField) { ObjectLiteralField objLitField = (ObjectLiteralField) statement; if (objLitField.initializer != null && (objLitField.initializer instanceof ObjectLiteral || objLitField.initializer instanceof FunctionExpression)) { statements.add(objLitField.initializer); continue; } } AbstractMethodDeclaration methodDecl = AbstractMethodDeclaration.findMethodDeclaration(statement); // check statements inside of method declarations as well if (methodDecl != null && methodDecl.statements != null) { statements.addAll(Arrays.asList(methodDecl.statements)); } // check if the found method declaration is the one that is being searched for if (methodDecl != null && (methodDecl.getBinding() == methodBinding || methodDecl.getBinding() == methodBinding.original())) { return methodDecl; } } } return null; }
public void resolve(BlockScope scope) { MethodScope methodScope = scope.methodScope(); if (methodScope == null) { /* return statement outside of a method */ scope.problemReporter().cannotReturnOutsideFunction(this); return; } MethodBinding methodBinding = null; TypeBinding methodType = (methodScope.referenceContext instanceof AbstractMethodDeclaration) ? ((methodBinding = ((AbstractMethodDeclaration) methodScope.referenceContext).binding) == null ? null : methodBinding.returnType) : TypeBinding.ANY; TypeBinding expressionType; if (this.expression == null) { if (methodType != null && !methodType.isAnyType()) scope.problemReporter().shouldReturn(methodType, this); return; } this.expression.setExpectedType(methodType); // needed in case of generic method invocation if ((expressionType = this.expression.resolveType(scope)) == null) return; if (methodType == null) return; if (methodType != expressionType) // must call before computeConversion() and typeMismatchError() scope.compilationUnitScope().recordTypeConversion(methodType, expressionType); if (this.expression.isConstantValueOfTypeAssignableToType(expressionType, methodType) || expressionType.isCompatibleWith(methodType)) { return; } if (methodBinding != null && !methodBinding.isConstructor()) scope.problemReporter().typeMismatchError(expressionType, methodType, this.expression); }
public TypeBinding resolveType(BlockScope scope) { // Propagate the type checking to the arguments, and check if the constructor is defined. constant = Constant.NotAConstant; if (this.member != null) { this.resolvedType = this.member.resolveForAllocation(scope, this); if (this.resolvedType != null && !this.resolvedType.isValidBinding()) { scope.problemReporter().invalidType(this, this.resolvedType); } } else if (this.type == null) { // initialization of an enum constant this.resolvedType = scope.enclosingReceiverType(); } else { this.resolvedType = this.type.resolveType(scope, true /* check bounds*/); } // will check for null after args are resolved // buffering the arguments' types boolean argsContainCast = false; TypeBinding[] argumentTypes = Binding.NO_PARAMETERS; if (arguments != null) { boolean argHasError = false; int length = arguments.length; argumentTypes = new TypeBinding[length]; for (int i = 0; i < length; i++) { Expression argument = this.arguments[i]; if ((argumentTypes[i] = argument.resolveType(scope)) == null) { argHasError = true; argumentTypes[i] = TypeBinding.UNKNOWN; } } if (argHasError) { // if (this.resolvedType instanceof ReferenceBinding) { // // record a best guess, for clients who need hint about possible contructor match // TypeBinding[] pseudoArgs = new TypeBinding[length]; // for (int i = length; --i >= 0;) // pseudoArgs[i] = argumentTypes[i] == null ? this.resolvedType : argumentTypes[i]; // // replace args with errors with receiver // this.binding = scope.findMethod((ReferenceBinding) this.resolvedType, // TypeConstants.INIT, pseudoArgs, this); // } // return this.resolvedType; } } if (this.resolvedType == null || this.resolvedType.isAnyType() || this.resolvedType instanceof ProblemReferenceBinding) { this.binding = new ProblemMethodBinding( TypeConstants.INIT, Binding.NO_PARAMETERS, ProblemReasons.NotFound); this.resolvedType = TypeBinding.UNKNOWN; return this.resolvedType; } if (!this.resolvedType.isValidBinding()) return null; if (this.resolvedType instanceof ReferenceBinding) { ReferenceBinding allocationType = (ReferenceBinding) this.resolvedType; if (!(binding = scope.getConstructor(allocationType, argumentTypes, this)).isValidBinding()) { if (binding.declaringClass == null) binding.declaringClass = allocationType; scope.problemReporter().invalidConstructor(this, binding); return this.resolvedType; } if (argumentTypes.length != binding.parameters.length) scope.problemReporter().wrongNumberOfArguments(this, binding); if (isMethodUseDeprecated(binding, scope, true)) scope.problemReporter().deprecatedMethod(binding, this); checkInvocationArguments( scope, null, allocationType, this.binding, this.arguments, argumentTypes, argsContainCast, this); } return this.resolvedType; }
/** * The flowInfo corresponds to non-static field initialization infos. It may be unreachable * (155423), but still the explicit constructor call must be analysed as reachable, since it will * be generated in the end. */ public void analyseCode( ClassScope classScope, InitializationFlowContext initializerFlowContext, FlowInfo flowInfo, int initialReachMode) { if (this.ignoreFurtherInvestigation) return; int nonStaticFieldInfoReachMode = flowInfo.reachMode(); flowInfo.setReachMode(initialReachMode); checkUnused: { MethodBinding constructorBinding; if ((constructorBinding = this.binding) == null) break checkUnused; if (this.isDefaultConstructor) break checkUnused; if (constructorBinding.isUsed()) break checkUnused; if (constructorBinding.isPrivate()) { if ((this.binding.declaringClass.tagBits & TagBits.HasNonPrivateConstructor) == 0) break checkUnused; // tolerate as known pattern to block instantiation } else if ((this.binding.declaringClass.tagBits & (TagBits.IsAnonymousType | TagBits.IsLocalType)) != TagBits.IsLocalType) { break checkUnused; } // complain unused this.scope.problemReporter().unusedPrivateConstructor(this); } // check constructor recursion, once all constructor got resolved if (isRecursive(null /*lazy initialized visited list*/)) { this.scope.problemReporter().recursiveConstructorInvocation(this.constructorCall); } try { ExceptionHandlingFlowContext constructorContext = new ExceptionHandlingFlowContext( initializerFlowContext.parent, this, null, this.scope, FlowInfo.DEAD_END); initializerFlowContext.checkInitializerExceptions(this.scope, constructorContext, flowInfo); // anonymous constructor can gain extra thrown exceptions from unhandled ones if (this.binding.declaringClass.isAnonymousType()) { ArrayList computedExceptions = constructorContext.extendedExceptions; if (computedExceptions != null) { int size; if ((size = computedExceptions.size()) > 0) { ReferenceBinding[] actuallyThrownExceptions; computedExceptions.toArray(actuallyThrownExceptions = new ReferenceBinding[size]); } } } // tag parameters as being set if (this.arguments != null) { for (int i = 0, count = this.arguments.length; i < count; i++) { flowInfo.markAsDefinitelyAssigned(this.arguments[i].binding); } } // propagate to constructor call if (this.constructorCall != null) { // if calling 'this(...)', then flag all non-static fields as definitely // set since they are supposed to be set inside other local constructor if (this.constructorCall.accessMode == ExplicitConstructorCall.This) { FieldBinding[] fields = this.binding.declaringClass.fields(); for (int i = 0, count = fields.length; i < count; i++) { FieldBinding field; if (!(field = fields[i]).isStatic()) { flowInfo.markAsDefinitelyAssigned(field); } } } flowInfo = this.constructorCall.analyseCode(this.scope, constructorContext, flowInfo); } // reuse the reachMode from non static field info flowInfo.setReachMode(nonStaticFieldInfoReachMode); // propagate to statements if (this.statements != null) { boolean didAlreadyComplain = false; for (int i = 0, count = this.statements.length; i < count; i++) { Statement stat = this.statements[i]; if (!stat.complainIfUnreachable(flowInfo, this.scope, didAlreadyComplain)) { flowInfo = stat.analyseCode(this.scope, constructorContext, flowInfo); } else { didAlreadyComplain = true; } } } // check for missing returning path this.needFreeReturn = (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0; // reuse the initial reach mode for diagnosing missing blank finals flowInfo.setReachMode(initialReachMode); // check missing blank final field initializations if ((this.constructorCall != null) && (this.constructorCall.accessMode != ExplicitConstructorCall.This)) { flowInfo = flowInfo.mergedWith(constructorContext.initsOnReturn); } // check unreachable catch blocks constructorContext.complainIfUnusedExceptionHandlers(this); } catch (AbortMethod e) { this.ignoreFurtherInvestigation = true; } }