/** Returns the type of the getter, falling back on unknown if the return type was inferred. */ private JSType determineGetterType(FunctionType methodType) { // TODO(sdh): consider only falling back on unknown if the function body is empty? But we // need to not report a conflicting type error if there's different unknowns. return !methodType.isReturnTypeInferred() ? methodType.getReturnType() : unknownType; } } // end ClassScopeBuilder
@Override public TypeI convertMethodToFunction() { List<JSType> paramTypes = new ArrayList<JSType>(); paramTypes.add(getTypeOfThis()); for (Node param : getParameters()) { paramTypes.add(param.getJSType()); } return registry.createFunctionTypeWithInstanceType( registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE), getReturnType(), paramTypes); }
private Node callInferringJSType(Node callee, Node... args) { Node call = IR.call(callee, args); JSType calleeType = callee.getJSType(); if (calleeType == null || !(calleeType instanceof FunctionType)) { return call; } JSType returnType = ((FunctionType) calleeType).getReturnType(); return call.setJSType(returnType); } }
private JSType convertMethodToFunction(FunctionType method) { List<JSType> paramTypes = new ArrayList<>(); paramTypes.add(method.getTypeOfThis()); for (Node param : method.getParameters()) { paramTypes.add(param.getJSType()); } ObjectType unknown = compiler.getTypeRegistry().getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); return compiler.getTypeRegistry().createFunctionTypeWithInstanceType( unknown, method.getReturnType(), paramTypes); }
Node createCall(Node callee, Node... args) { Node result = NodeUtil.newCallNode(callee, args); if (isAddingTypes()) { FunctionType calleeType = JSType.toMaybeFunctionType(callee.getJSType()); // TODO(sdh): this does not handle generic functions - we'd need to unify the argument types. // checkState(calleeType == null || !calleeType.hasAnyTemplateTypes(), calleeType); // TODO(bradfordcsmith): Consider throwing an exception if calleeType is null. JSType returnType = calleeType != null ? calleeType.getReturnType() : unknownType; result.setJSType(returnType); } return result; }
private void validateCallType(Node callNode) { // TODO(b/74537281): Shouldn't CALL nodes always have a type, even if it is unknown? Node callee = callNode.getFirstChild(); JSType calleeTypeI = checkNotNull(callee.getJSType(), "Callee of\n\n%s\nhas no type.", callNode.toStringTree()); if (calleeTypeI.isFunctionType()) { FunctionType calleeFunctionTypeI = calleeTypeI.toMaybeFunctionType(); JSType returnTypeI = calleeFunctionTypeI.getReturnType(); // Skip this check if the call node was originally in a cast, because the cast type may be // narrower than the return type. if (callNode.getJSTypeBeforeCast() == null) { expectMatchingTypeInformation(callNode, returnTypeI); } } // TODO(b/74537281): What other cases should be covered? }
/** Traverse a return value. */ @CheckReturnValue private FlowScope traverseReturn(Node n, FlowScope scope) { scope = traverseChildren(n, scope); Node retValue = n.getFirstChild(); if (retValue != null) { JSType type = functionScope.getRootNode().getJSType(); if (type != null) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null) { inferPropertyTypesToMatchConstraint( retValue.getJSType(), fnType.getReturnType()); } } } return scope; }
/** Copies all the information from another function type. */ public FunctionBuilder copyFromOtherFunction(FunctionType otherType) { this.name = otherType.getReferenceName(); this.sourceNode = otherType.getSource(); this.parametersNode = otherType.getParametersNode(); this.returnType = otherType.getReturnType(); this.typeOfThis = otherType.getTypeOfThis(); this.templateTypeMap = otherType.getTemplateTypeMap(); this.isConstructor = otherType.isConstructor(); this.isNativeType = otherType.isNativeObjectType(); return this; }
private void visitFunctionDeclaration(FunctionType ftype, List<String> skipTemplateParams) { visitFunctionParameters(ftype, true, skipTemplateParams); JSType type = ftype.getReturnType(); final JSType typeOfThis = ftype.getTypeOfThis(); if (type == null) return; emit(":"); // Closure conflates 'undefined' and 'void', and in general visitType always emits `undefined` // for that type. // In idiomatic TypeScript, `void` is used for function return types, and the "void", // "undefined" types are not the same. if (type.isVoidType()) { emit("void"); } else if (typeOfThis != null && typeOfThis.isTemplateType() && typeOfThis.equals(type)) { // Special case: prefer polymorphic `this` type to templatized `this` param emit("this"); } else { visitType(type); } }
/** Creates node that make a call to a context function. */ Node callContextMethod(Node sourceNode, String methodName, Node... args) { Node contextField = getContextField(sourceNode, methodName); Node callNode = IR.call(contextField, args).useSourceInfoFrom(sourceNode); if (shouldAddTypes) { callNode.setJSType( contextField.getJSType().isFunctionType() ? contextField.getJSType().toMaybeFunctionType().getReturnType() : unknownType); } return callNode; }
/** * @return True if n is a function node which is explicitly annotated * as returning a nullable type, other than {?}. */ private static boolean isReturnTypeNullable(Node n) { if (n == null || !n.isFunction()) { return false; } FunctionType functionType = n.getJSType().toMaybeFunctionType(); if (functionType == null) { // If the JSDoc declares a non-function type on a function node, we still shouldn't crash. return false; } JSType returnType = functionType.getReturnType(); if (returnType == null || returnType.isUnknownType() || !returnType.isNullable()) { return false; } JSDocInfo info = NodeUtil.getBestJSDocInfo(n); return info != null && info.hasReturnType(); }
/** * Traverse a return value. */ private FlowScope traverseReturn(Node n, FlowScope scope) { scope = traverseChildren(n, scope); Node retValue = n.getFirstChild(); if (retValue != null) { JSType type = functionScope.getRootNode().getJSType(); if (type != null) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null) { inferPropertyTypesToMatchConstraint( retValue.getJSType(), fnType.getReturnType()); } } } return scope; }
private void visitReturn(NodeTraversal t, Node n) { Node function = t.getEnclosingFunction(); FunctionType funType = function.getJSType().toMaybeFunctionType(); Node retValue = n.getFirstChild(); if (retValue == null) { return; } Node checkNode = createCheckTypeCallNode( funType.getReturnType(), retValue.cloneTree()); if (checkNode == null) { return; } n.replaceChild(retValue, checkNode); compiler.reportCodeChange(); }
/** * @return True if n is a function node which is explicitly annotated * as returning a nullable type, other than {?}. */ private static boolean isReturnTypeNullable(Node n) { if (n == null || !n.isFunction()) { return false; } FunctionType functionType = n.getJSType().toMaybeFunctionType(); if (functionType == null) { // If the JSDoc declares a non-function type on a function node, we still shouldn't crash. return false; } JSType returnType = functionType.getReturnType(); if (returnType == null || returnType.isUnknownType() || !returnType.isNullable()) { return false; } JSDocInfo info = NodeUtil.getBestJSDocInfo(n); return info != null && info.hasReturnType(); }
/** Copies all the information from another function type. */ public FunctionBuilder copyFromOtherFunction(FunctionType otherType) { int isNative = otherType.isNativeObjectType() ? IS_NATIVE : 0; int isAbstract = otherType.isAbstract() ? IS_ABSTRACT : 0; int inferredReturnType = otherType.isReturnTypeInferred() ? INFERRED_RETURN_TYPE : 0; this.name = otherType.getReferenceName(); this.sourceNode = otherType.getSource(); this.parametersNode = otherType.getParametersNode(); this.returnType = otherType.getReturnType(); this.typeOfThis = otherType.getTypeOfThis(); this.templateTypeMap = otherType.getTemplateTypeMap(); this.kind = otherType.getKind(); this.properties = isNative | isAbstract | inferredReturnType; return this; }
/** Visits an arrow function expression body. */ private void visitImplicitReturnExpression(NodeTraversal t, Node exprNode) { Node enclosingFunction = t.getEnclosingFunction(); JSType jsType = getJSType(enclosingFunction); if (jsType.isFunctionType()) { FunctionType functionType = jsType.toMaybeFunctionType(); JSType expectedReturnType = functionType.getReturnType(); // if no return type is specified, undefined must be returned // (it's a void function) if (expectedReturnType == null) { expectedReturnType = getNativeType(VOID_TYPE); } else if (enclosingFunction.isAsyncFunction()) { // Unwrap the async function's declared return type. expectedReturnType = Promises.createAsyncReturnableType(typeRegistry, expectedReturnType); } // Fetch the returned value's type JSType actualReturnType = getJSType(exprNode); validator.expectCanAssignTo(t, exprNode, actualReturnType, expectedReturnType, "inconsistent return type"); } }
private FlowScope traverseCall(Node n, FlowScope scope) { scope = traverseChildren(n, scope); Node left = n.getFirstChild(); JSType functionType = getJSType(left).restrictByNotNullOrUndefined(); if (functionType.isFunctionType()) { FunctionType fnType = functionType.toMaybeFunctionType(); n.setJSType(fnType.getReturnType()); backwardsInferenceFromCallSite(n, fnType); } else if (functionType.isEquivalentTo( getNativeType(CHECKED_UNKNOWN_TYPE))) { n.setJSType(getNativeType(CHECKED_UNKNOWN_TYPE)); } scope = tightenTypesAfterAssertions(scope, n); return scope; }
SingleGeneratorFunctionTranspiler(Node genFunc, int genaratorNestingLevel) { this.generatorNestingLevel = genaratorNestingLevel; this.originalGeneratorBody = genFunc.getLastChild(); ObjectType contextType = null; if (shouldAddTypes) { // Find the yield type of the generator. // e.g. given @return {!Generator<number>}, we want this.yieldType to be number. yieldType = unknownType; if (genFunc.getJSType() != null && genFunc.getJSType().isFunctionType()) { FunctionType fnType = genFunc.getJSType().toMaybeFunctionType(); this.originalGenReturnType = fnType.getReturnType(); yieldType = JsIterables.getElementType(originalGenReturnType, registry); } JSType globalContextType = registry.getGlobalType("$jscomp.generator.Context"); if (globalContextType == null) { // We don't have the es6/generator polyfill, which can happen in tests using a // NonInjectingCompiler or if someone sets --inject_libraries=false. Don't crash, just // back off on giving some type information. contextType = registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); } else { contextType = registry.createTemplatizedType(globalContextType.toMaybeObjectType(), yieldType); } } this.context = new TranspilationContext(contextType); }
/** * Get the return value of calling "bind" on this function with the specified number of arguments. * * <p>If -1 is passed, then we will return a result that accepts any parameters. */ public final FunctionType getBindReturnType(int argsToBind) { FunctionBuilder builder = new FunctionBuilder(registry) .withReturnType(getReturnType()) .withTemplateKeys(getTemplateTypeMap().getTemplateKeys()); if (argsToBind >= 0) { Node origParams = getParametersNode(); if (origParams != null) { Node params = origParams.cloneTree(); for (int i = 1; i < argsToBind && params.getFirstChild() != null; i++) { if (params.getFirstChild().isVarArgs()) { break; } params.removeFirstChild(); } builder.withParamsNode(params); } } return builder.build(); }
/** @param n A non-constructor function invocation, i.e. CALL or TAGGED_TEMPLATELIT */ private FlowScope traverseFunctionInvocation(Node n, FlowScope scope) { checkArgument(n.isCall() || n.isTaggedTemplateLit(), n); scope = traverseChildren(n, scope); Node left = n.getFirstChild(); JSType functionType = getJSType(left).restrictByNotNullOrUndefined(); if (left.isSuper()) { // TODO(sdh): This will probably return the super type; might want to return 'this' instead? return traverseInstantiation(n, functionType, scope); } else if (functionType.isFunctionType()) { FunctionType fnType = functionType.toMaybeFunctionType(); n.setJSType(fnType.getReturnType()); backwardsInferenceFromCallSite(n, fnType, scope); } else if (functionType.isEquivalentTo(getNativeType(CHECKED_UNKNOWN_TYPE))) { n.setJSType(getNativeType(CHECKED_UNKNOWN_TYPE)); } else if (left.getJSType() != null && left.getJSType().isUnknownType()) { // TODO(lharker): do we also want to set this to unknown if the left's type is null? We would // lose some inference that TypeCheck does when given a null type. n.setJSType(getNativeType(UNKNOWN_TYPE)); } return scope; }