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);
}
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");
}
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);
}
}
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");
}
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);
}
}
Aggregations