public void resolveEnum(EnumType e, DeclaredTypeRegistry registry) { Preconditions.checkState( e != null, "getEnum should only be " + "called when we know that the enum is defined"); if (e.isResolved()) { return; } JSTypeExpression texp = e.getTypeExpr(); JSType enumeratedType; if (texp == null) { warnings.add(JSError.make(e.getTypeExprForErrorReporting().getRoot(), CIRCULAR_TYPEDEF_ENUM)); enumeratedType = JSType.UNKNOWN; } else { int numTypeVars = howmanyTypeVars; enumeratedType = getTypeFromJSTypeExpression(texp, registry, null); if (howmanyTypeVars > numTypeVars) { warnings.add(JSError.make(texp.getRoot(), ENUM_WITH_TYPEVARS)); enumeratedType = JSType.UNKNOWN; howmanyTypeVars = numTypeVars; } else if (enumeratedType.isTop()) { warnings.add(JSError.make(texp.getRoot(), ENUM_IS_TOP)); enumeratedType = JSType.UNKNOWN; } else if (enumeratedType.isUnion()) { warnings.add(JSError.make(texp.getRoot(), ENUM_IS_UNION)); enumeratedType = JSType.UNKNOWN; } } e.resolveEnum(enumeratedType); }
private JSType getTypeFromJSTypeExpression( JSTypeExpression expr, DeclaredTypeRegistry registry, ImmutableList<String> typeParameters) { if (expr == null) { return null; } return getTypeFromComment(expr.getRoot(), registry, typeParameters); }
// /** @param {...?} var_args */ function f(var_args) { ... } // var_args shouldn't be used in the body of f public static boolean isRestArg(JSDocInfo funJsdoc, String formalParamName) { if (funJsdoc == null) { return false; } JSTypeExpression texp = funJsdoc.getParameterType(formalParamName); Node jsdocNode = texp == null ? null : texp.getRoot(); return jsdocNode != null && jsdocNode.getType() == Token.ELLIPSIS; }
private ParameterType parseParameter( JSTypeExpression jsdoc, ParameterKind p, DeclaredTypeRegistry registry, ImmutableList<String> typeParameters) { if (jsdoc == null) { return null; } return parseParameter(jsdoc.getRoot(), p, registry, typeParameters); }
/** * Adds a usage for the given type expression (unless it references a variable that is defined in * the externs, in which case no goog.require() is needed). When a usage is added, it means that * there should be a goog.require for that type. */ private void maybeAddUsage(NodeTraversal t, Node n, final JSTypeExpression expr) { // Just look at the root node, don't traverse. Predicate<Node> pred = new Predicate<Node>() { @Override public boolean apply(Node n) { return n == expr.getRoot(); } }; maybeAddUsage(t, n, expr.getRoot(), this.usages, pred); }
private ImmutableSet<NominalType> getInterfacesHelper( JSDocInfo jsdoc, DeclaredTypeRegistry registry, ImmutableList<String> typeParameters, boolean implementedIntfs) { ImmutableSet.Builder<NominalType> builder = ImmutableSet.builder(); for (JSTypeExpression texp : (implementedIntfs ? jsdoc.getImplementedInterfaces() : jsdoc.getExtendedInterfaces())) { Node expRoot = texp.getRoot(); JSType interfaceType = getMaybeTypeFromComment(expRoot, registry, typeParameters); if (interfaceType != null) { NominalType nt = interfaceType.getNominalTypeIfSingletonObj(); if (nt != null && nt.isInterface()) { builder.add(nt); } else if (implementedIntfs) { warnings.add(JSError.make(expRoot, IMPLEMENTS_NON_INTERFACE, interfaceType.toString())); } else { warnings.add(JSError.make(expRoot, EXTENDS_NON_INTERFACE, interfaceType.toString())); } } } return builder.build(); }
/** Processes a rest parameter */ private void visitRestParam(Node restParam, Node paramList) { Node functionBody = paramList.getLastSibling(); restParam.setType(Token.NAME); restParam.setVarArgs(true); // Make sure rest parameters are typechecked JSTypeExpression type = null; JSDocInfo info = restParam.getJSDocInfo(); String paramName = restParam.getString(); if (info != null) { type = info.getType(); } else { JSDocInfo functionInfo = paramList.getParent().getJSDocInfo(); if (functionInfo != null) { type = functionInfo.getParameterType(paramName); } } if (type != null && type.getRoot().getType() != Token.ELLIPSIS) { compiler.report(JSError.make(restParam, BAD_REST_PARAMETER_ANNOTATION)); } if (!functionBody.hasChildren()) { // If function has no body, we are done! compiler.reportCodeChange(); return; } Node newBlock = IR.block().useSourceInfoFrom(functionBody); Node name = IR.name(paramName); Node let = IR.let(name, IR.name(REST_PARAMS)).useSourceInfoIfMissingFromForTree(functionBody); newBlock.addChildToFront(let); for (Node child : functionBody.children()) { newBlock.addChildToBack(child.detachFromParent()); } if (type != null) { Node arrayType = IR.string("Array"); Node typeNode = type.getRoot(); Node memberType = typeNode.getType() == Token.ELLIPSIS ? typeNode.getFirstChild().cloneNode() : typeNode.cloneNode(); arrayType.addChildToFront( new Node(Token.BLOCK, memberType).useSourceInfoIfMissingFrom(typeNode)); JSDocInfoBuilder builder = new JSDocInfoBuilder(false); builder.recordType( new JSTypeExpression(new Node(Token.BANG, arrayType), restParam.getSourceFileName())); name.setJSDocInfo(builder.build()); } int restIndex = paramList.getIndexOfChild(restParam); Node newArr = IR.var(IR.name(REST_PARAMS), IR.arraylit()); functionBody.addChildToFront(newArr.useSourceInfoIfMissingFromForTree(restParam)); Node init = IR.var(IR.name(REST_INDEX), IR.number(restIndex)); Node cond = IR.lt(IR.name(REST_INDEX), IR.getprop(IR.name("arguments"), IR.string("length"))); Node incr = IR.inc(IR.name(REST_INDEX), false); Node body = IR.block( IR.exprResult( IR.assign( IR.getelem( IR.name(REST_PARAMS), IR.sub(IR.name(REST_INDEX), IR.number(restIndex))), IR.getelem(IR.name("arguments"), IR.name(REST_INDEX))))); functionBody.addChildAfter( IR.forNode(init, cond, incr, body).useSourceInfoIfMissingFromForTree(restParam), newArr); functionBody.addChildToBack(newBlock); compiler.reportCodeChange(); // For now, we are running transpilation before type-checking, so we'll // need to make sure changes don't invalidate the JSDoc annotations. // Therefore we keep the parameter list the same length and only initialize // the values if they are set to undefined. }