Search in sources :

Example 1 with EnumType

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

the class TypeCheck method visit.

/**
 * This is the meat of the type checking. It is basically one big switch, with each case
 * representing one type of parse tree node. The individual cases are usually pretty
 * straightforward.
 *
 * @param t The node traversal object that supplies context, such as the scope chain to use in
 *     name lookups as well as error reporting.
 * @param n The node being visited.
 * @param parent The parent of the node n.
 */
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
    JSType childType;
    JSType leftType;
    JSType rightType;
    Node left;
    Node right;
    // To be explicitly set to false if the node is not typeable.
    boolean typeable = true;
    validator.expectWellFormedTemplatizedType(n);
    switch(n.getToken()) {
        case CAST:
            Node expr = n.getFirstChild();
            JSType exprType = getJSType(expr);
            JSType castType = getJSType(n);
            // way.
            if (!expr.isObjectLit()) {
                validator.expectCanCast(n, castType, exprType);
            }
            ensureTyped(n, castType);
            expr.setJSTypeBeforeCast(exprType);
            if (castType.restrictByNotNullOrUndefined().isSubtypeOf(exprType) || expr.isObjectLit()) {
                expr.setJSType(castType);
            }
            break;
        case NAME:
            typeable = visitName(t, n, parent);
            break;
        case COMMA:
            ensureTyped(n, getJSType(n.getLastChild()));
            break;
        case THIS:
            ensureTyped(n, t.getTypedScope().getTypeOfThis());
            break;
        case NULL:
            ensureTyped(n, NULL_TYPE);
            break;
        case NUMBER:
            ensureTyped(n, NUMBER_TYPE);
            break;
        case BIGINT:
            ensureTyped(n, BIGINT_TYPE);
            break;
        case GETTER_DEF:
        case SETTER_DEF:
            // Object literal keys are handled with OBJECTLIT
            break;
        case ARRAYLIT:
            ensureTyped(n, ARRAY_TYPE);
            break;
        case REGEXP:
            ensureTyped(n, REGEXP_TYPE);
            break;
        case GETPROP:
            visitGetProp(t, n);
            typeable = !(parent.isAssign() && parent.getFirstChild() == n);
            break;
        case OPTCHAIN_GETPROP:
            visitOptChainGetProp(n);
            break;
        case OPTCHAIN_GETELEM:
            visitOptChainGetElem(n);
            break;
        case GETELEM:
            visitGetElem(n);
            // The type of GETELEM is always unknown, so no point counting that.
            // If that unknown leaks elsewhere (say by an assignment to another
            // variable), then it will be counted.
            typeable = false;
            break;
        case VAR:
        case LET:
        case CONST:
            visitVar(t, n);
            typeable = false;
            break;
        case NEW:
            visitNew(n);
            break;
        case OPTCHAIN_CALL:
            // We reuse the `visitCall` functionality for OptChain call nodes because we don't report
            // an error for regular calls when the callee is null or undefined. However, we make sure
            // `typeable` isn't explicitly unset as OptChain nodes are always typed during inference.
            visitCall(t, n);
            break;
        case CALL:
            visitCall(t, n);
            typeable = !parent.isExprResult();
            break;
        case RETURN:
            visitReturn(t, n);
            typeable = false;
            break;
        case YIELD:
            visitYield(t, n);
            break;
        case DEC:
        case INC:
            left = n.getFirstChild();
            checkPropCreation(left);
            if (getJSType(n).isNumber()) {
                validator.expectNumber(left, getJSType(left), "increment/decrement");
                ensureTyped(n, NUMBER_TYPE);
            } else {
                validator.expectBigIntOrNumber(left, getJSType(left), "increment/decrement");
            }
            break;
        case VOID:
            ensureTyped(n, VOID_TYPE);
            break;
        case STRINGLIT:
        case TYPEOF:
        case TEMPLATELIT:
        case TEMPLATELIT_STRING:
            ensureTyped(n, STRING_TYPE);
            break;
        case TAGGED_TEMPLATELIT:
            visitTaggedTemplateLit(n);
            ensureTyped(n);
            break;
        case BITNOT:
            visitBitwiseNOT(n);
            break;
        case POS:
            visitUnaryPlus(n);
            break;
        case NEG:
            visitUnaryMinus(n);
            break;
        case EQ:
        case NE:
        case SHEQ:
        case SHNE:
            {
                left = n.getFirstChild();
                right = n.getLastChild();
                if (left.isTypeOf()) {
                    if (right.isStringLit()) {
                        checkTypeofString(right, right.getString());
                    }
                } else if (right.isTypeOf() && left.isStringLit()) {
                    checkTypeofString(left, left.getString());
                }
                leftType = getJSType(left);
                rightType = getJSType(right);
                // We do not want to warn about explicit comparisons to VOID. People
                // often do this if they think their type annotations screwed up.
                // 
                // We do want to warn about cases where people compare things like
                // (Array|null) == (Function|null)
                // because it probably means they screwed up.
                // 
                // This heuristic here is not perfect, but should catch cases we
                // care about without too many false negatives.
                JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
                JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
                Tri result = Tri.UNKNOWN;
                if (n.getToken() == Token.EQ || n.isNE()) {
                    result = leftTypeRestricted.testForEquality(rightTypeRestricted);
                    if (n.isNE()) {
                        result = result.not();
                    }
                } else {
                    // SHEQ or SHNE
                    if (!leftTypeRestricted.canTestForShallowEqualityWith(rightTypeRestricted)) {
                        result = n.getToken() == Token.SHEQ ? Tri.FALSE : Tri.TRUE;
                    }
                }
                if (result != Tri.UNKNOWN) {
                    report(n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString());
                }
                ensureTyped(n, BOOLEAN_TYPE);
                break;
            }
        case LT:
        case LE:
        case GT:
        case GE:
            Node leftSide = n.getFirstChild();
            Node rightSide = n.getLastChild();
            leftType = getJSType(leftSide);
            rightType = getJSType(rightSide);
            if (rightType.isUnknownType()) {
                // validate comparable left
                validator.expectUnknownOrComparable(leftSide, leftType, "left side of comparison");
            } else if (leftType.isUnknownType()) {
                // validate comparable right
                validator.expectUnknownOrComparable(rightSide, rightType, "right side of comparison");
            } else if (rightType.isBigIntOrNumber()) {
                // validate left operand for numeric comparison
                validator.expectBigIntOrNumber(leftSide, leftType, "left side of numeric comparison");
            } else if (leftType.isBigIntOrNumber()) {
                // validate right operand for numeric comparison
                validator.expectBigIntOrNumber(rightSide, rightType, "right side of numeric comparison");
            } else {
                String errorMsg = "expected matching types in comparison";
                this.validator.expectMatchingTypesStrict(n, leftType, rightType, errorMsg);
                if (!leftType.matchesNumberContext() || !rightType.matchesNumberContext()) {
                    // Whether the comparison is numeric will be determined at runtime
                    // each time the expression is evaluated. Regardless, both operands
                    // should match a string context.
                    String message = "left side of comparison";
                    validator.expectString(leftSide, leftType, message);
                    validator.expectNotNullOrUndefined(t, leftSide, leftType, message, getNativeType(STRING_TYPE));
                    message = "right side of comparison";
                    validator.expectString(rightSide, rightType, message);
                    validator.expectNotNullOrUndefined(t, rightSide, rightType, message, getNativeType(STRING_TYPE));
                }
            }
            ensureTyped(n, BOOLEAN_TYPE);
            break;
        case IN:
            left = n.getFirstChild();
            right = n.getLastChild();
            rightType = getJSType(right);
            validator.expectStringOrSymbol(left, getJSType(left), "left side of 'in'");
            validator.expectObject(n, rightType, "'in' requires an object");
            if (rightType.isStruct()) {
                report(right, IN_USED_WITH_STRUCT);
            }
            ensureTyped(n, BOOLEAN_TYPE);
            break;
        case INSTANCEOF:
            left = n.getFirstChild();
            right = n.getLastChild();
            rightType = getJSType(right).restrictByNotNullOrUndefined();
            validator.expectAnyObject(left, getJSType(left), "deterministic instanceof yields false");
            validator.expectActualObject(right, rightType, "instanceof requires an object");
            ensureTyped(n, BOOLEAN_TYPE);
            break;
        case ASSIGN:
        case ASSIGN_OR:
        case ASSIGN_AND:
        case ASSIGN_COALESCE:
            visitAssign(t, n);
            typeable = false;
            break;
        case ASSIGN_LSH:
        case ASSIGN_RSH:
        case ASSIGN_URSH:
        case ASSIGN_DIV:
        case ASSIGN_MOD:
        case ASSIGN_BITOR:
        case ASSIGN_BITXOR:
        case ASSIGN_BITAND:
        case ASSIGN_SUB:
        case ASSIGN_ADD:
        case ASSIGN_MUL:
        case ASSIGN_EXPONENT:
            checkPropCreation(n.getFirstChild());
        case LSH:
        case RSH:
        case URSH:
        case DIV:
        case MOD:
        case BITOR:
        case BITXOR:
        case BITAND:
        case SUB:
        case ADD:
        case MUL:
        case EXPONENT:
            visitBinaryOperator(n.getToken(), n);
            break;
        case TRUE:
        case FALSE:
        case NOT:
        case DELPROP:
            ensureTyped(n, BOOLEAN_TYPE);
            break;
        case CASE:
            JSType switchType = getJSType(parent.getFirstChild());
            JSType caseType = getJSType(n.getFirstChild());
            validator.expectSwitchMatchesCase(n, switchType, caseType);
            typeable = false;
            break;
        case WITH:
            {
                Node child = n.getFirstChild();
                childType = getJSType(child);
                validator.expectObject(child, childType, "with requires an object");
                typeable = false;
                break;
            }
        case FUNCTION:
            visitFunction(n);
            break;
        case CLASS:
            visitClass(n);
            break;
        case MODULE_BODY:
            visitModuleBody(t, n);
            break;
        // These nodes require data flow analysis.
        case PARAM_LIST:
        case STRING_KEY:
        case MEMBER_FUNCTION_DEF:
        case COMPUTED_PROP:
        case MEMBER_FIELD_DEF:
        case COMPUTED_FIELD_DEF:
        case LABEL:
        case LABEL_NAME:
        case SWITCH:
        case BREAK:
        case CATCH:
        case TRY:
        case SCRIPT:
        case EXPORT:
        case EXPORT_SPEC:
        case EXPORT_SPECS:
        case IMPORT:
        case IMPORT_SPEC:
        case IMPORT_SPECS:
        case IMPORT_STAR:
        case EXPR_RESULT:
        case BLOCK:
        case ROOT:
        case EMPTY:
        case DEFAULT_CASE:
        case CONTINUE:
        case DEBUGGER:
        case THROW:
        case DO:
        case IF:
        case WHILE:
        case FOR:
        case TEMPLATELIT_SUB:
        case ITER_REST:
        case OBJECT_REST:
        case DESTRUCTURING_LHS:
            typeable = false;
            break;
        case DYNAMIC_IMPORT:
            visitDynamicImport(t, n);
            break;
        case ARRAY_PATTERN:
            ensureTyped(n);
            validator.expectAutoboxesToIterable(n, getJSType(n), "array pattern destructuring requires an Iterable");
            break;
        case OBJECT_PATTERN:
            visitObjectPattern(n);
            break;
        case DEFAULT_VALUE:
            checkCanAssignToWithScope(t, n, n.getFirstChild(), getJSType(n.getSecondChild()), /* info= */
            null, "default value has wrong type");
            // Every other usage of a destructuring pattern is checked while visiting the pattern,
            // but default values are different because they are a conditional assignment and the
            // pattern is not given the default value's type
            Node lhs = n.getFirstChild();
            Node rhs = n.getSecondChild();
            if (lhs.isArrayPattern()) {
                validator.expectAutoboxesToIterable(rhs, getJSType(rhs), "array pattern destructuring requires an Iterable");
            } else if (lhs.isObjectPattern()) {
                // Verify that the value is not null/undefined, since those can't be destructured.
                validator.expectObject(rhs, getJSType(rhs), "cannot destructure a 'null' or 'undefined' default value");
            }
            typeable = false;
            break;
        case CLASS_MEMBERS:
            {
                JSType typ = parent.getJSType().toMaybeFunctionType().getInstanceType();
                for (Node child = n.getFirstChild(); child != null; child = child.getNext()) {
                    visitObjectOrClassLiteralKey(child, n.getParent(), typ);
                    if (child.isSetterDef() || child.isGetterDef()) {
                        checkGetterOrSetterType(child, parent.getJSType().toMaybeFunctionType());
                    }
                }
                typeable = false;
                break;
            }
        case FOR_IN:
            Node obj = n.getSecondChild();
            if (getJSType(obj).isStruct()) {
                report(obj, IN_USED_WITH_STRUCT);
            }
            typeable = false;
            break;
        case FOR_OF:
        case FOR_AWAIT_OF:
            ensureTyped(n.getSecondChild());
            typeable = false;
            break;
        // These nodes are typed during the type inference.
        case SUPER:
        case NEW_TARGET:
        case IMPORT_META:
        case AWAIT:
        case AND:
        case HOOK:
        case OR:
        case COALESCE:
            ensureTyped(n);
            break;
        case OBJECTLIT:
            // If this is an enum, then give that type to the objectlit as well.
            if (parent.getJSType() instanceof EnumType) {
                ensureTyped(n, parent.getJSType());
            } else {
                ensureTyped(n);
            }
            JSType typ = getJSType(n);
            for (Node key = n.getFirstChild(); key != null; key = key.getNext()) {
                visitObjectOrClassLiteralKey(key, n, typ);
            }
            break;
        case ITER_SPREAD:
        case OBJECT_SPREAD:
            checkSpread(n);
            typeable = false;
            break;
        default:
            report(n, UNEXPECTED_TOKEN, n.getToken().toString());
            ensureTyped(n);
            break;
    }
    // Visit the body of blockless arrow functions
    if (NodeUtil.isBlocklessArrowFunctionResult(n)) {
        visitImplicitReturnExpression(t, n);
    }
    // TypedScope.
    if ((n.getParent().isForOf() || n.getParent().isForAwaitOf()) && n.getParent().getFirstChild() == n) {
        checkForOfTypes(t, n.getParent());
    }
    // Don't count externs since the user's code may not even use that part.
    typeable = typeable && !inExterns;
    if (typeable) {
        doPercentTypedAccounting(n);
    }
    checkJsdocInfoContainsObjectWithBadKey(n);
}
Also used : JSType(com.google.javascript.rhino.jstype.JSType) EnumType(com.google.javascript.rhino.jstype.EnumType) Node(com.google.javascript.rhino.Node) Tri(com.google.javascript.jscomp.base.Tri)

Example 2 with EnumType

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

the class TypeCheck method checkEnumAlias.

/**
 * Checks enum aliases.
 *
 * <p>We verify that the enum element type of the enum used for initialization is a subtype of the
 * enum element type of the enum the value is being copied in.
 *
 * <p>Example:
 *
 * <pre>var myEnum = myOtherEnum;</pre>
 *
 * <p>Enum aliases are irregular, so we need special code for this :(
 *
 * @param valueType the type of the value used for initialization of the enum
 * @param nodeToWarn the node on which to issue warnings on
 */
private void checkEnumAlias(NodeTraversal t, JSDocInfo declInfo, JSType valueType, Node nodeToWarn) {
    if (declInfo == null || !declInfo.hasEnumParameterType()) {
        return;
    }
    if (!valueType.isEnumType()) {
        return;
    }
    EnumType valueEnumType = valueType.toMaybeEnumType();
    JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType();
    validator.expectCanAssignTo(nodeToWarn, valueEnumPrimitiveType, declInfo.getEnumParameterType().evaluate(t.getTypedScope(), typeRegistry), "incompatible enum element types");
}
Also used : JSType(com.google.javascript.rhino.jstype.JSType) EnumType(com.google.javascript.rhino.jstype.EnumType)

Example 3 with EnumType

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

the class TypeCheck method checkPropertyAccess.

/**
 * Emit a warning if we can prove that a property cannot possibly be
 * defined on an object. Note the difference between JS and a strictly
 * statically typed language: we're checking if the property
 * *cannot be defined*, whereas a java compiler would check if the
 * property *can be undefined*.
 */
private void checkPropertyAccess(JSType childType, String propName, NodeTraversal t, Node n) {
    // If the property type is unknown, check the object type to see if it
    // can ever be defined. We explicitly exclude CHECKED_UNKNOWN (for
    // properties where we've checked that it exists, or for properties on
    // objects that aren't in this binary).
    JSType propType = getJSType(n);
    if (propType.isEquivalentTo(typeRegistry.getNativeType(UNKNOWN_TYPE))) {
        childType = childType.autobox();
        ObjectType objectType = ObjectType.cast(childType);
        if (objectType != null) {
            // faster in most cases).
            if (!objectType.hasProperty(propName) || objectType.isEquivalentTo(typeRegistry.getNativeType(UNKNOWN_TYPE))) {
                if (objectType instanceof EnumType) {
                    report(t, n, INEXISTENT_ENUM_ELEMENT, propName);
                } else {
                    checkPropertyAccessHelper(objectType, propName, t, n, false);
                }
            }
        } else {
            checkPropertyAccessHelper(childType, propName, t, n, false);
        }
    } else if (childType.isUnionType() && !isLValueGetProp(n)) {
        // NOTE: strict property assignment checks are done on assignment.
        checkPropertyAccessHelper(childType, propName, t, n, true);
    }
}
Also used : ObjectType(com.google.javascript.rhino.jstype.ObjectType) JSType(com.google.javascript.rhino.jstype.JSType) EnumType(com.google.javascript.rhino.jstype.EnumType)

Example 4 with EnumType

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

the class TypeCheck method checkEnumAlias.

/**
 * <p>Checks enum aliases.
 *
 * <p>We verify that the enum element type of the enum used
 * for initialization is a subtype of the enum element type of
 * the enum the value is being copied in.</p>
 *
 * <p>Example:</p>
 * <pre>var myEnum = myOtherEnum;</pre>
 *
 * <p>Enum aliases are irregular, so we need special code for this :(</p>
 *
 * @param value the value used for initialization of the enum
 */
private void checkEnumAlias(NodeTraversal t, JSDocInfo declInfo, Node value) {
    if (declInfo == null || !declInfo.hasEnumParameterType()) {
        return;
    }
    JSType valueType = getJSType(value);
    if (!valueType.isEnumType()) {
        return;
    }
    EnumType valueEnumType = valueType.toMaybeEnumType();
    JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType();
    validator.expectCanAssignTo(t, value, valueEnumPrimitiveType, declInfo.getEnumParameterType().evaluate(t.getTypedScope(), typeRegistry), "incompatible enum element types");
}
Also used : JSType(com.google.javascript.rhino.jstype.JSType) EnumType(com.google.javascript.rhino.jstype.EnumType)

Example 5 with EnumType

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

the class TypeCheck method checkPropertyAccess.

/**
 * Emits a warning if we can prove that a property cannot possibly be defined on an object. Note
 * the difference between JS and a strictly statically typed language: we're checking if the
 * property *cannot be defined*, whereas a java compiler would check if the property *can be
 * undefined.
 *
 * <p>This method handles property access in both GETPROPs and object destructuring.
 * Consequentially some of its arguments are optional - the actual object node and the getprop -
 * while others are required.
 *
 * @param objNode the actual node representing the object we're accessing. optional because
 *     destructuring accesses MAY not have an actual object node
 */
private void checkPropertyAccess(JSType childType, Node propNode, JSType propType, @Nullable Node objNode) {
    final boolean isGetprop = NodeUtil.isNormalOrOptChainGetProp(propNode);
    final String propName = isGetprop ? propNode.getString() : propNode.getString();
    if (propType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) {
        childType = childType.autobox();
        ObjectType objectType = ObjectType.cast(childType);
        if (objectType != null) {
            // faster in most cases).
            if (!objectType.hasProperty(propName) || objectType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) {
                if (objectType instanceof EnumType) {
                    report(propNode, INEXISTENT_ENUM_ELEMENT, propName);
                } else {
                    checkPropertyAccessHelper(objectType, propNode, objNode, false);
                }
            }
        } else {
            checkPropertyAccessHelper(childType, propNode, objNode, false);
        }
    } else if (childType.isUnionType() && isGetprop && !isLValueGetProp(propNode)) {
        // NOTE: strict property assignment checks are done on assignment.
        checkPropertyAccessHelper(childType, propNode, objNode, true);
    }
}
Also used : ObjectType(com.google.javascript.rhino.jstype.ObjectType) EnumType(com.google.javascript.rhino.jstype.EnumType)

Aggregations

EnumType (com.google.javascript.rhino.jstype.EnumType)5 JSType (com.google.javascript.rhino.jstype.JSType)4 ObjectType (com.google.javascript.rhino.jstype.ObjectType)2 Tri (com.google.javascript.jscomp.base.Tri)1 Node (com.google.javascript.rhino.Node)1