Search in sources :

Example 51 with JSType

use of com.google.javascript.rhino.jstype.JSType in project closure-compiler by google.

the class FunctionTypeBuilder method provideDefaultReturnType.

/**
 * Sets the returnType for this function using very basic type inference.
 */
private void provideDefaultReturnType() {
    if (contents.getSourceNode() != null && contents.getSourceNode().isAsyncGeneratorFunction()) {
        // Set the return type of a generator function to:
        // @return {!AsyncGenerator<?>}
        ObjectType generatorType = typeRegistry.getNativeObjectType(ASYNC_GENERATOR_TYPE);
        returnType = typeRegistry.createTemplatizedType(generatorType, typeRegistry.getNativeType(UNKNOWN_TYPE));
        return;
    } else if (contents.getSourceNode() != null && contents.getSourceNode().isGeneratorFunction()) {
        // Set the return type of a generator function to:
        // @return {!Generator<?>}
        ObjectType generatorType = typeRegistry.getNativeObjectType(GENERATOR_TYPE);
        returnType = typeRegistry.createTemplatizedType(generatorType, typeRegistry.getNativeType(UNKNOWN_TYPE));
        return;
    }
    JSType inferredReturnType = typeRegistry.getNativeType(UNKNOWN_TYPE);
    if (!contents.mayHaveNonEmptyReturns() && !contents.mayHaveSingleThrow() && !contents.mayBeFromExterns()) {
        // Infer return types for non-generator functions.
        // We need to be extremely conservative about this, because of two
        // competing needs.
        // 1) If we infer the return type of f too widely, then we won't be able
        // to assign f to other functions.
        // 2) If we infer the return type of f too narrowly, then we won't be
        // able to override f in subclasses.
        // So we only infer in cases where the user doesn't expect to write
        // @return annotations--when it's very obvious that the function returns
        // nothing.
        inferredReturnType = typeRegistry.getNativeType(VOID_TYPE);
        returnTypeInferred = true;
    }
    if (contents.getSourceNode() != null && contents.getSourceNode().isAsyncFunction()) {
        // Set the return type of an async function:
        // @return {!Promise<?>} or @return {!Promise<undefined>}
        ObjectType promiseType = typeRegistry.getNativeObjectType(PROMISE_TYPE);
        returnType = typeRegistry.createTemplatizedType(promiseType, inferredReturnType);
    } else {
        returnType = inferredReturnType;
    }
}
Also used : ObjectType(com.google.javascript.rhino.jstype.ObjectType) JSType(com.google.javascript.rhino.jstype.JSType)

Example 52 with JSType

use of com.google.javascript.rhino.jstype.JSType in project closure-compiler by google.

the class FunctionTypeBuilder method buildTemplateTypesFromJSDocInfo.

private ImmutableList<TemplateType> buildTemplateTypesFromJSDocInfo(JSDocInfo info, boolean allowTypeTransformations) {
    ImmutableMap<String, JSTypeExpression> infoTypeKeys = info.getTemplateTypes();
    ImmutableMap<String, Node> infoTypeTransformations = info.getTypeTransformations();
    if (infoTypeKeys.isEmpty() && infoTypeTransformations.isEmpty()) {
        return ImmutableList.of();
    }
    // Temporarily bootstrap the template environment with unbound (unknown bound) template types
    List<TemplateType> unboundedTemplates = new ArrayList<>();
    for (String templateKey : infoTypeKeys.keySet()) {
        unboundedTemplates.add(typeRegistry.createTemplateType(templateKey));
    }
    this.templateScope = typeRegistry.createScopeWithTemplates(templateScope, unboundedTemplates);
    // Evaluate template type bounds with bootstrapped environment and reroute the bounds to these
    ImmutableList.Builder<TemplateType> templates = ImmutableList.builder();
    Map<TemplateType, JSType> templatesToBounds = new LinkedHashMap<>();
    for (Map.Entry<String, JSTypeExpression> entry : infoTypeKeys.entrySet()) {
        JSTypeExpression expr = entry.getValue();
        JSType typeBound = typeRegistry.evaluateTypeExpression(entry.getValue(), templateScope);
        // treatment in the future, since "unknown" is currently used as a proxy for "implicit".
        if (expr.isExplicitUnknownTemplateBound()) {
            reportError(TEMPLATE_TYPE_ILLEGAL_BOUND, String.valueOf(typeBound), entry.getKey());
        }
        TemplateType template = typeRegistry.getType(templateScope, entry.getKey()).toMaybeTemplateType();
        if (template != null) {
            templatesToBounds.put(template, typeBound);
        } else {
            templatesToBounds.put(typeRegistry.createTemplateType(entry.getKey(), typeBound), typeBound);
        }
    }
    for (Map.Entry<TemplateType, JSType> entry : templatesToBounds.entrySet()) {
        TemplateType template = entry.getKey();
        JSType bound = entry.getValue();
        template.setBound(bound);
        templates.add(template);
    }
    for (Map.Entry<String, Node> entry : infoTypeTransformations.entrySet()) {
        if (allowTypeTransformations) {
            templates.add(typeRegistry.createTemplateTypeWithTransformation(entry.getKey(), entry.getValue()));
        } else {
            reportWarning(TEMPLATE_TRANSFORMATION_ON_CLASS, entry.getKey());
        }
    }
    ImmutableList<TemplateType> builtTemplates = templates.build();
    for (TemplateType template : builtTemplates) {
        if (template.containsCycle()) {
            reportError(RhinoErrorReporter.PARSE_ERROR, "Cycle detected in inheritance chain of type " + template.getReferenceName());
        }
    }
    return builtTemplates;
}
Also used : JSType(com.google.javascript.rhino.jstype.JSType) ImmutableList(com.google.common.collect.ImmutableList) Node(com.google.javascript.rhino.Node) ArrayList(java.util.ArrayList) TemplateType(com.google.javascript.rhino.jstype.TemplateType) LinkedHashMap(java.util.LinkedHashMap) JSTypeExpression(com.google.javascript.rhino.JSTypeExpression) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) ImmutableMap(com.google.common.collect.ImmutableMap)

Example 53 with JSType

use of com.google.javascript.rhino.jstype.JSType in project closure-compiler by google.

the class FunctionTypeBuilder method getOrCreateConstructor.

/**
 * Returns a constructor function either by returning it from the
 * registry if it exists or creating and registering a new type. If
 * there is already a type, then warn if the existing type is
 * different than the one we are creating, though still return the
 * existing function if possible.  The primary purpose of this is
 * that registering a constructor will fail for all built-in types
 * that are initialized in {@link JSTypeRegistry}.  We a) want to
 * make sure that the type information specified in the externs file
 * matches what is in the registry and b) annotate the externs with
 * the {@link JSType} from the registry so that there are not two
 * separate JSType objects for one type.
 */
private FunctionType getOrCreateConstructor() {
    FunctionType fnType = this.createDefaultBuilder().forConstructor().withParameters(parameters).withReturnType(returnType).withConstructorTemplateKeys(constructorTemplateTypeNames).withIsAbstract(isAbstract).build();
    if (makesStructs) {
        fnType.setStruct();
    } else if (makesDicts) {
        fnType.setDict();
    } else if (makesUnrestricted) {
        fnType.setExplicitUnrestricted();
    }
    // There are two cases where this type already exists in the current scope:
    // 1. The type is a built-in that we initalized in JSTypeRegistry and is also defined in
    // externs.
    // 2. Cases like "class C {} C = class {}"
    // See https://github.com/google/closure-compiler/issues/2928 for some related bugs.
    JSType existingType = typeRegistry.getType(declarationScope, fnName);
    if (existingType != null) {
        boolean isInstanceObject = existingType.isInstanceType();
        if (isInstanceObject || fnName.equals("Function")) {
            FunctionType existingFn = isInstanceObject ? existingType.toObjectType().getConstructor() : typeRegistry.getNativeFunctionType(FUNCTION_FUNCTION_TYPE);
            if (existingFn.getSource() == null) {
                existingFn.setSource(contents.getSourceNode());
            }
            if (!existingFn.hasEqualCallType(fnType)) {
                reportWarning(TYPE_REDEFINITION, formatFnName(), fnType.toString(), existingFn.toString());
            }
            // another function (since we don't set its prototype in JSTypeRegistry)
            if (existingFn.isNativeObjectType()) {
                maybeSetBaseType(existingFn);
            }
            return existingFn;
        } else {
        // We fall through and return the created type, even though it will fail
        // to register. We have no choice as we have to return a function. We
        // issue an error elsewhere though, so the user should fix it.
        }
    }
    maybeSetBaseType(fnType);
    // 
    if (!syntacticFnName.isEmpty() && !syntacticFnName.startsWith("this.")) {
        typeRegistry.declareTypeForExactScope(declarationScope, syntacticFnName, fnType.getInstanceType());
    }
    return fnType;
}
Also used : JSType(com.google.javascript.rhino.jstype.JSType) FunctionType(com.google.javascript.rhino.jstype.FunctionType)

Example 54 with JSType

use of com.google.javascript.rhino.jstype.JSType in project closure-compiler by google.

the class FunctionTypeBuilder method inferParameterTypes.

/**
 * Infer the parameter types from the list of parameter names and the JSDoc info.
 */
FunctionTypeBuilder inferParameterTypes(@Nullable Node paramsParent, @Nullable JSDocInfo info) {
    if (paramsParent == null) {
        if (info == null) {
            return this;
        } else {
            return inferParameterTypes(info);
        }
    }
    // arguments
    final Iterator<Parameter> oldParameters;
    Parameter oldParameterType = null;
    if (parameters != null) {
        oldParameters = parameters.iterator();
        oldParameterType = oldParameters.hasNext() ? oldParameters.next() : null;
    } else {
        oldParameters = Collections.emptyIterator();
    }
    FunctionParamBuilder builder = new FunctionParamBuilder(typeRegistry);
    boolean warnedAboutArgList = false;
    Set<String> allJsDocParams = (info == null) ? new HashSet<>() : new HashSet<>(info.getParameterNames());
    boolean isVarArgs = false;
    int paramIndex = 0;
    for (Node param = paramsParent.getFirstChild(); param != null; param = param.getNext()) {
        boolean isOptionalParam = false;
        final Node paramLhs;
        if (param.isRest()) {
            isVarArgs = true;
            paramLhs = param.getOnlyChild();
        } else if (param.isDefaultValue()) {
            // The first child is the actual positional parameter
            paramLhs = checkNotNull(param.getFirstChild(), param);
            isOptionalParam = true;
        } else {
            isVarArgs = isVarArgsParameterByConvention(param);
            isOptionalParam = isOptionalParameterByConvention(param);
            paramLhs = param;
        }
        String paramName = null;
        if (paramLhs.isName()) {
            paramName = paramLhs.getString();
        } else {
            checkState(paramLhs.isDestructuringPattern());
            // third JSDoc parameter.
            if (info != null) {
                paramName = info.getParameterNameAt(paramIndex);
            }
        }
        allJsDocParams.remove(paramName);
        // type from JSDocInfo
        JSType parameterType = null;
        if (info != null && info.hasParameterType(paramName)) {
            JSTypeExpression parameterTypeExpression = info.getParameterType(paramName);
            parameterType = parameterTypeExpression.evaluate(templateScope, typeRegistry);
            isOptionalParam = isOptionalParam || parameterTypeExpression.isOptionalArg();
            isVarArgs = isVarArgs || parameterTypeExpression.isVarArgs();
        } else if (paramLhs.getJSDocInfo() != null && paramLhs.getJSDocInfo().hasType()) {
            JSTypeExpression parameterTypeExpression = paramLhs.getJSDocInfo().getType();
            parameterType = parameterTypeExpression.evaluate(templateScope, typeRegistry);
            isOptionalParam = parameterTypeExpression.isOptionalArg();
            isVarArgs = parameterTypeExpression.isVarArgs();
        } else if (oldParameterType != null && oldParameterType.getJSType() != null) {
            parameterType = oldParameterType.getJSType();
            isOptionalParam = oldParameterType.isOptional();
            isVarArgs = oldParameterType.isVariadic();
        } else {
            parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE);
        }
        warnedAboutArgList |= addParameter(builder, parameterType, warnedAboutArgList, isOptionalParam, isVarArgs);
        oldParameterType = oldParameters.hasNext() ? oldParameters.next() : null;
        paramIndex++;
    }
    // Copy over any old parameters that aren't in the param list.
    if (!isVarArgs) {
        while (oldParameterType != null && !isVarArgs) {
            builder.newParameterFrom(oldParameterType);
            oldParameterType = oldParameters.hasNext() ? oldParameters.next() : null;
        }
    }
    for (String inexistentName : allJsDocParams) {
        reportWarning(INEXISTENT_PARAM, inexistentName, formatFnName());
    }
    parameters = builder.build();
    return this;
}
Also used : JSType(com.google.javascript.rhino.jstype.JSType) Node(com.google.javascript.rhino.Node) Parameter(com.google.javascript.rhino.jstype.FunctionType.Parameter) JSTypeExpression(com.google.javascript.rhino.JSTypeExpression) FunctionParamBuilder(com.google.javascript.rhino.jstype.FunctionParamBuilder)

Example 55 with JSType

use of com.google.javascript.rhino.jstype.JSType in project closure-compiler by google.

the class ExpressionDecomposer method rewriteCallExpression.

/**
 * Rewrite the call so "this" is preserved.
 *
 * <pre>a.b(c);</pre>
 *
 * becomes:
 *
 * <pre>
 * var temp1 = a; var temp0 = temp1.b;
 * temp0.call(temp1,c);
 * </pre>
 */
private void rewriteCallExpression(Node call, DecompositionState state) {
    checkArgument(call.isCall(), call);
    Node first = call.getFirstChild();
    checkArgument(NodeUtil.isNormalGet(first), first);
    // Find the type of (fn expression).call
    JSType fnCallType = null;
    if (astFactory.isAddingTypes()) {
        JSType fnType = first.getJSType();
        fnCallType = fnType.isFunctionType() ? fnType.toMaybeFunctionType().getPropertyType("call") : unknownType;
    }
    // Extracts the expression representing the function to call. For example:
    // "a['b'].c" from "a['b'].c()"
    Node getVarNode = extractExpression(first, state.extractBeforeStatement);
    state.extractBeforeStatement = getVarNode;
    // Extracts the object reference to be used as "this". For example:
    // "a['b']" from "a['b'].c"
    Node getExprNode = getVarNode.getFirstFirstChild();
    checkArgument(NodeUtil.isNormalGet(getExprNode), getExprNode);
    final Node origThisValue = getExprNode.getFirstChild();
    final Node functionNameNode = getVarNode.getFirstChild().cloneNode();
    final Node receiverNode;
    if (origThisValue.isThis()) {
        // No need to create a variable for `this`, just clone it.
        receiverNode = origThisValue.cloneNode();
    } else if (origThisValue.isSuper()) {
        // Original callee was like `super.prop(args)`.
        // The correct way to call the value `super.prop` from a temporary variable is
        // `tmpVar.call(this, args)`, so just create a `this` here.
        receiverNode = astFactory.createThis(type(origThisValue)).srcref(origThisValue);
    } else {
        final Node thisVarNode = extractExpression(origThisValue, state.extractBeforeStatement);
        state.extractBeforeStatement = thisVarNode;
        receiverNode = thisVarNode.getFirstChild().cloneNode();
    }
    // CALL
    // GETPROP "call"
    // functionName
    // thisName
    // original-parameter1
    // original-parameter2
    // ...
    // Reuse the existing CALL node instead of creating a new one to avoid breaking InlineFunction's
    // bookkeeping. See b/124253050.
    call.removeFirstChild();
    call.addChildToFront(receiverNode);
    call.addChildToFront(astFactory.createGetProp(functionNameNode, "call", type(fnCallType, StandardColors.TOP_OBJECT)).srcrefTreeIfMissing(call));
    call.putBooleanProp(Node.FREE_CALL, false);
}
Also used : JSType(com.google.javascript.rhino.jstype.JSType) Node(com.google.javascript.rhino.Node)

Aggregations

JSType (com.google.javascript.rhino.jstype.JSType)447 Test (org.junit.Test)182 Node (com.google.javascript.rhino.Node)158 FunctionType (com.google.javascript.rhino.jstype.FunctionType)71 ObjectType (com.google.javascript.rhino.jstype.ObjectType)71 NodeSubject.assertNode (com.google.javascript.rhino.testing.NodeSubject.assertNode)30 JSDocInfo (com.google.javascript.rhino.JSDocInfo)19 TemplateType (com.google.javascript.rhino.jstype.TemplateType)14 FlowScope (com.google.javascript.jscomp.type.FlowScope)13 CheckReturnValue (com.google.errorprone.annotations.CheckReturnValue)11 JSTypeExpression (com.google.javascript.rhino.JSTypeExpression)9 UnionType (com.google.javascript.rhino.jstype.UnionType)9 JSTypeRegistry (com.google.javascript.rhino.jstype.JSTypeRegistry)8 TemplateTypeMap (com.google.javascript.rhino.jstype.TemplateTypeMap)8 LinkedHashMap (java.util.LinkedHashMap)8 ImmutableList (com.google.common.collect.ImmutableList)7 Color (com.google.javascript.jscomp.colors.Color)7 StaticTypedSlot (com.google.javascript.rhino.jstype.StaticTypedSlot)7 ArrayList (java.util.ArrayList)7 ImmutableMap (com.google.common.collect.ImmutableMap)6