Search in sources :

Example 1 with NonJSDocComment

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

the class IRFactory method parseNonJSDocCommentAt.

/**
 * Creates a single NonJSDocComment from every comment associated with this node; or null if there
 * are no such comments.
 *
 * <p>It would be legal to replace all comments associated with this node with that one string.
 */
@Nullable
private NonJSDocComment parseNonJSDocCommentAt(SourcePosition pos, boolean isInline) {
    if (config.jsDocParsingMode() != JsDocParsing.INCLUDE_ALL_COMMENTS) {
        return null;
    }
    if (!this.nonJsdocTracker.hasPendingCommentBefore(pos)) {
        return null;
    }
    StringBuilder result = new StringBuilder();
    Comment firstComment = this.nonJsdocTracker.current();
    Comment lastComment = null;
    while (this.nonJsdocTracker.hasPendingCommentBefore(pos)) {
        Comment currentComment = this.nonJsdocTracker.current();
        if (lastComment != null) {
            for (int blankCount = currentComment.location.start.line - lastComment.location.end.line; blankCount > 0; blankCount--) {
                result.append("\n");
            }
        }
        result.append(currentComment.value);
        lastComment = currentComment;
        this.nonJsdocTracker.advance();
    }
    NonJSDocComment nonJSDocComment = new NonJSDocComment(firstComment.location.start, lastComment.location.end, result.toString());
    nonJSDocComment.setEndsAsLineComment(lastComment.type == Comment.Type.LINE);
    nonJSDocComment.setIsInline(isInline);
    return nonJSDocComment;
}
Also used : NonJSDocComment(com.google.javascript.rhino.NonJSDocComment) Comment(com.google.javascript.jscomp.parsing.parser.trees.Comment) NonJSDocComment(com.google.javascript.rhino.NonJSDocComment) Nullable(javax.annotation.Nullable)

Example 2 with NonJSDocComment

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

the class IRFactory method transformNodeWithInlineComments.

/**
 * Names and destructuring patterns, in parameters or variable declarations are special, because
 * they can have inline type docs attached.
 *
 * <pre>function f(/** string &#42;/ x) {}</pre>
 *
 * annotates 'x' as a string.
 *
 * @see <a href="http://code.google.com/p/jsdoc-toolkit/wiki/InlineDocs">Using Inline Doc
 *     Comments</a>
 */
Node transformNodeWithInlineComments(ParseTree tree) {
    JSDocInfo info = parseInlineJSDocAt(tree.getStart());
    NonJSDocComment comment = parseNonJSDocCommentAt(tree.getStart(), true);
    Node node = transformDispatcher.process(tree);
    if (info != null) {
        node.setJSDocInfo(info);
    }
    if (comment != null) {
        node.setNonJSDocComment(comment);
    }
    setSourceInfo(node, tree);
    return node;
}
Also used : Node(com.google.javascript.rhino.Node) JSDocInfo(com.google.javascript.rhino.JSDocInfo) NonJSDocComment(com.google.javascript.rhino.NonJSDocComment)

Example 3 with NonJSDocComment

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

the class SuggestedFix method getStartPositionForNodeConsideringComments.

/**
 * Helper function to return the source offset of this node considering that JSDoc comments,
 * non-JDDoc comments, or both may or may not be attached.
 */
private static int getStartPositionForNodeConsideringComments(Node node) {
    JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(node);
    NonJSDocComment associatedNonJSDocComment = node.getNonJSDocComment();
    int start = node.getSourceOffset();
    if (jsdoc != null) {
        start = jsdoc.getOriginalCommentPosition();
    }
    if (associatedNonJSDocComment != null) {
        start = min(start, associatedNonJSDocComment.getStartPosition().getOffset());
    }
    return start;
}
Also used : JSDocInfo(com.google.javascript.rhino.JSDocInfo) NonJSDocComment(com.google.javascript.rhino.NonJSDocComment)

Example 4 with NonJSDocComment

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

the class CodeGenerator method add.

protected void add(Node node, Context context) {
    if (!cc.continueProcessing()) {
        return;
    }
    if (preserveTypeAnnotations && node.getJSDocInfo() != null) {
        String jsdocAsString = jsDocInfoPrinter.print(node.getJSDocInfo());
        // Don't print an empty jsdoc
        if (!jsdocAsString.equals("/** */ ")) {
            add(jsdocAsString);
            if (!node.isCast()) {
                cc.endLine();
            }
        }
    }
    // print any non-trailing non-JSDoc comment attached to this node
    if (printNonJSDocComments) {
        NonJSDocComment nonJSDocComment = node.getNonJSDocComment();
        if (nonJSDocComment != null && !nonJSDocComment.isTrailing()) {
            String nonJSDocCommentString = node.getNonJSDocCommentString();
            if (!nonJSDocCommentString.isEmpty()) {
                addNonJsDoc_nonTrailing(node, nonJSDocComment);
            }
        }
    }
    Token type = node.getToken();
    String opstr = NodeUtil.opToStr(type);
    int childCount = node.getChildCount();
    Node first = node.getFirstChild();
    Node last = node.getLastChild();
    // Handle all binary operators
    if (opstr != null && first != last) {
        Preconditions.checkState(childCount == 2, "Bad binary operator \"%s\": expected 2 arguments but got %s", opstr, childCount);
        int p = precedence(node);
        // For right-hand-side of operations, only pass context if it's
        // the IN_FOR_INIT_CLAUSE one.
        Context rhsContext = getContextForNoInOperator(context);
        boolean needsParens = (context == Context.START_OF_EXPR || context.atArrowFunctionBody()) && first.isObjectPattern();
        if (node.isAssign() && needsParens) {
            add("(");
        }
        if (NodeUtil.isAssignmentOp(node) || type == Token.EXPONENT) {
            // Assignment operators and '**' are the only right-associative binary operators
            addExpr(first, p + 1, context);
            cc.addOp(opstr, true);
            addExpr(last, p, rhsContext);
        } else {
            unrollBinaryOperator(node, type, opstr, context, rhsContext, p, p + 1);
        }
        if (node.isAssign() && needsParens) {
            add(")");
        }
        return;
    }
    cc.startSourceMapping(node);
    switch(type) {
        case TRY:
            {
                checkState(first.getNext().isBlock() && !first.getNext().hasMoreThanOneChild());
                checkState(childCount >= 2 && childCount <= 3);
                add("try");
                add(first);
                // second child contains the catch block, or nothing if there
                // isn't a catch block
                Node catchblock = first.getNext().getFirstChild();
                if (catchblock != null) {
                    add(catchblock);
                }
                if (childCount == 3) {
                    cc.maybeInsertSpace();
                    add("finally");
                    add(last);
                }
                break;
            }
        case CATCH:
            Preconditions.checkState(childCount == 2, node);
            cc.maybeInsertSpace();
            add("catch");
            cc.maybeInsertSpace();
            if (!first.isEmpty()) {
                // optional catch binding
                add("(");
                add(first);
                add(")");
            }
            add(last);
            break;
        case THROW:
            Preconditions.checkState(childCount == 1, node);
            add("throw");
            cc.maybeInsertSpace();
            add(first);
            // Must have a ';' after a throw statement, otherwise safari can't
            // parse this.
            cc.endStatement(true);
            break;
        case RETURN:
            add("return");
            if (childCount == 1) {
                cc.maybeInsertSpace();
                if (preserveTypeAnnotations && first.getJSDocInfo() != null) {
                    add("(");
                    add(first);
                    add(")");
                } else {
                    add(first);
                }
            } else {
                checkState(childCount == 0, node);
            }
            cc.endStatement();
            break;
        case VAR:
            add("var ");
            addList(first, false, getContextForNoInOperator(context), ",");
            if (node.getParent() == null || NodeUtil.isStatement(node)) {
                cc.endStatement();
            }
            break;
        case CONST:
            add("const ");
            addList(first, false, getContextForNoInOperator(context), ",");
            if (node.getParent() == null || NodeUtil.isStatement(node)) {
                cc.endStatement();
            }
            break;
        case LET:
            add("let ");
            addList(first, false, getContextForNoInOperator(context), ",");
            if (node.getParent() == null || NodeUtil.isStatement(node)) {
                cc.endStatement();
            }
            break;
        case LABEL_NAME:
            Preconditions.checkState(!node.getString().isEmpty(), node);
            addIdentifier(node.getString());
            break;
        case DESTRUCTURING_LHS:
            add(first);
            if (first != last) {
                checkState(childCount == 2, node);
                cc.addOp("=", true);
                addExpr(last, NodeUtil.precedence(Token.ASSIGN), getContextForNoInOperator(context));
            }
            break;
        case NAME:
            if (useOriginalName && node.getOriginalName() != null) {
                addIdentifier(node.getOriginalName());
            } else {
                addIdentifier(node.getString());
            }
            maybeAddOptional(node);
            maybeAddTypeDecl(node);
            if (first != null && !first.isEmpty()) {
                checkState(childCount == 1, node);
                cc.addOp("=", true);
                addExpr(first, NodeUtil.precedence(Token.ASSIGN), getContextForNoInOperator(context));
            }
            break;
        case ARRAYLIT:
            add("[");
            addArrayList(first);
            add("]");
            break;
        case ARRAY_PATTERN:
            add("[");
            addArrayList(first);
            add("]");
            maybeAddTypeDecl(node);
            break;
        case PARAM_LIST:
            // If this is the list for a non-TypeScript arrow function with one simple name param.
            if (node.getParent().isArrowFunction() && node.hasOneChild() && first.isName() && !outputFeatureSet.has(Feature.TYPE_ANNOTATION)) {
                add(first);
            } else {
                add("(");
                addList(first);
                add(")");
            }
            break;
        case DEFAULT_VALUE:
            add(first);
            maybeAddTypeDecl(node);
            cc.addOp("=", true);
            addExpr(first.getNext(), 1, Context.OTHER);
            break;
        case COMMA:
            Preconditions.checkState(childCount == 2, node);
            unrollBinaryOperator(node, Token.COMMA, ",", context, getContextForNoInOperator(context), 0, 0);
            break;
        case NUMBER:
            Preconditions.checkState(childCount == 0, node);
            cc.addNumber(node.getDouble(), node);
            break;
        case BIGINT:
            Preconditions.checkState(childCount == 0, node);
            cc.add(node.getBigInt() + "n");
            break;
        case TYPEOF:
        case VOID:
        case NOT:
        case BITNOT:
        case POS:
        case NEG:
            {
                // All of these unary operators are right-associative
                checkState(childCount == 1, node);
                cc.addOp(NodeUtil.opToStrNoFail(type), false);
                addExpr(first, NodeUtil.precedence(type), Context.OTHER);
                break;
            }
        case HOOK:
            {
                checkState(childCount == 3, "%s wrong number of children: %s", node, childCount);
                int p = NodeUtil.precedence(type);
                Context rhsContext = getContextForNoInOperator(context);
                addExpr(first, p + 1, context);
                cc.addOp("?", true);
                addExpr(first.getNext(), 1, rhsContext);
                cc.addOp(":", true);
                addExpr(last, 1, rhsContext);
                break;
            }
        case REGEXP:
            if (!first.isStringLit() || !last.isStringLit()) {
                throw new Error("Expected children to be strings");
            }
            String regexp = regexpEscape(first.getString());
            // I only use one .add because whitespace matters
            if (childCount == 2) {
                add(regexp + last.getString());
            } else {
                checkState(childCount == 1, node);
                add(regexp);
            }
            break;
        case FUNCTION:
            {
                if (node.getClass() != Node.class) {
                    throw new Error("Unexpected Node subclass.");
                }
                checkState(childCount == 3, node);
                if (node.isArrowFunction()) {
                    addArrowFunction(node, first, last, context);
                } else {
                    addFunction(node, first, last, context);
                }
                break;
            }
        case ITER_REST:
        case OBJECT_REST:
            add("...");
            add(first);
            maybeAddTypeDecl(node);
            break;
        case ITER_SPREAD:
        case OBJECT_SPREAD:
            add("...");
            addExpr(first, NodeUtil.precedence(type), Context.OTHER);
            break;
        case EXPORT:
            add("export");
            if (node.getBooleanProp(Node.EXPORT_DEFAULT)) {
                add("default");
            }
            if (node.getBooleanProp(Node.EXPORT_ALL_FROM)) {
                add("*");
                checkState(first != null && first.isEmpty(), node);
            } else {
                add(first);
            }
            if (childCount == 2) {
                add("from");
                add(last);
            }
            processEnd(first, context);
            break;
        case IMPORT:
            add("import");
            Node second = first.getNext();
            if (!first.isEmpty()) {
                add(first);
                if (!second.isEmpty()) {
                    cc.listSeparator();
                }
            }
            if (!second.isEmpty()) {
                add(second);
            }
            if (!first.isEmpty() || !second.isEmpty()) {
                add("from");
            }
            add(last);
            cc.endStatement();
            break;
        case EXPORT_SPECS:
        case IMPORT_SPECS:
            add("{");
            for (Node c = first; c != null; c = c.getNext()) {
                if (c != first) {
                    cc.listSeparator();
                }
                add(c);
            }
            add("}");
            break;
        case EXPORT_SPEC:
        case IMPORT_SPEC:
            add(first);
            if (node.isShorthandProperty() && first.getString().equals(last.getString())) {
                break;
            }
            add("as");
            add(last);
            break;
        case IMPORT_STAR:
            add("*");
            add("as");
            add(node.getString());
            break;
        case DYNAMIC_IMPORT:
            add("import(");
            addExpr(first, NodeUtil.precedence(type), context);
            add(")");
            break;
        case IMPORT_META:
            add("import.meta");
            break;
        // CLASS -> NAME,EXPR|EMPTY,BLOCK
        case CLASS:
            {
                checkState(childCount == 3, node);
                boolean classNeedsParens = (context == Context.START_OF_EXPR);
                if (classNeedsParens) {
                    add("(");
                }
                Node name = first;
                Node superClass = first.getNext();
                Node members = last;
                add("class");
                if (!name.isEmpty()) {
                    add(name);
                }
                maybeAddGenericTypes(first);
                if (!superClass.isEmpty()) {
                    add("extends");
                    add(superClass);
                }
                Node interfaces = (Node) node.getProp(Node.IMPLEMENTS);
                if (interfaces != null) {
                    add("implements");
                    Node child = interfaces.getFirstChild();
                    add(child);
                    while ((child = child.getNext()) != null) {
                        add(",");
                        cc.maybeInsertSpace();
                        add(child);
                    }
                }
                add(members);
                cc.endClass(context == Context.STATEMENT);
                if (classNeedsParens) {
                    add(")");
                }
            }
            break;
        case CLASS_MEMBERS:
        case INTERFACE_MEMBERS:
        case NAMESPACE_ELEMENTS:
            cc.beginBlock();
            for (Node c = first; c != null; c = c.getNext()) {
                add(c);
                processEnd(c, context);
                cc.endLine();
            }
            cc.endBlock(false);
            break;
        case ENUM_MEMBERS:
            cc.beginBlock();
            for (Node c = first; c != null; c = c.getNext()) {
                add(c);
                if (c.getNext() != null) {
                    add(",");
                }
                cc.endLine();
            }
            cc.endBlock(false);
            break;
        case GETTER_DEF:
        case SETTER_DEF:
        case MEMBER_FUNCTION_DEF:
        case MEMBER_VARIABLE_DEF:
            {
                checkState(node.getParent().isObjectLit() || node.getParent().isClassMembers() || node.getParent().isInterfaceMembers() || node.getParent().isRecordType() || node.getParent().isIndexSignature());
                maybeAddAccessibilityModifier(node);
                if (node.isStaticMember()) {
                    add("static ");
                }
                if (node.isMemberFunctionDef() && node.getFirstChild().isAsyncFunction()) {
                    add("async ");
                }
                if (!node.isMemberVariableDef() && node.getFirstChild().isGeneratorFunction()) {
                    checkState(type == Token.MEMBER_FUNCTION_DEF, node);
                    add("*");
                }
                switch(type) {
                    case GETTER_DEF:
                        // Get methods have no parameters.
                        Preconditions.checkState(!first.getSecondChild().hasChildren(), node);
                        add("get ");
                        break;
                    case SETTER_DEF:
                        // Set methods have one parameter.
                        Preconditions.checkState(first.getSecondChild().hasOneChild(), node);
                        add("set ");
                        break;
                    case MEMBER_FUNCTION_DEF:
                    case MEMBER_VARIABLE_DEF:
                        // nothing to do.
                        break;
                    default:
                        break;
                }
                // The name is on the GET or SET node.
                String name = node.getString();
                if (node.isMemberVariableDef()) {
                    add(node.getString());
                    maybeAddOptional(node);
                    maybeAddTypeDecl(node);
                } else {
                    checkState(childCount == 1, node);
                    checkState(first.isFunction(), first);
                    // The function referenced by the definition should always be unnamed.
                    checkState(first.getFirstChild().getString().isEmpty(), first);
                    Node fn = first;
                    Node parameters = fn.getSecondChild();
                    Node body = fn.getLastChild();
                    // Add the property name.
                    if (!node.isQuotedString() && TokenStream.isJSIdentifier(name) && // Unicode escaped.
                    NodeUtil.isLatin(name)) {
                        add(name);
                        maybeAddGenericTypes(fn.getFirstChild());
                    } else {
                        // Determine if the string is a simple number.
                        double d = getSimpleNumber(name);
                        if (!Double.isNaN(d)) {
                            cc.addNumber(d, node);
                        } else {
                            addJsString(node);
                        }
                    }
                    maybeAddOptional(fn);
                    add(parameters);
                    maybeAddTypeDecl(fn);
                    add(body);
                }
                break;
            }
        case MEMBER_FIELD_DEF:
        case COMPUTED_FIELD_DEF:
            {
                checkState(node.getParent().isClassMembers());
                if (node.getBooleanProp(Node.STATIC_MEMBER)) {
                    add("static ");
                }
                Node init = null;
                switch(type) {
                    case MEMBER_FIELD_DEF:
                        String propertyName = node.getString();
                        add(propertyName);
                        init = first;
                        break;
                    case COMPUTED_FIELD_DEF:
                        add("[");
                        // Must use addExpr() with a priority of 1, because comma expressions aren't allowed.
                        // https://www.ecma-international.org/ecma-262/9.0/index.html#prod-ComputedPropertyName
                        addExpr(first, 1, Context.OTHER);
                        add("]");
                        init = node.getSecondChild();
                        break;
                    default:
                        break;
                }
                if (init != null) {
                    add("=");
                    addExpr(init, 1, Context.OTHER);
                }
                add(";");
                break;
            }
        case SCRIPT:
        case MODULE_BODY:
        case BLOCK:
        case ROOT:
            {
                if (node.getClass() != Node.class) {
                    throw new Error("Unexpected Node subclass.");
                }
                boolean preserveBlock = node.isBlock() && !node.isSyntheticBlock();
                if (preserveBlock) {
                    cc.beginBlock();
                }
                boolean preferLineBreaks = type == Token.SCRIPT || (type == Token.BLOCK && !preserveBlock && node.getParent().isScript());
                for (Node c = first; c != null; c = c.getNext()) {
                    add(c, Context.STATEMENT);
                    if (c.isFunction() || c.isClass()) {
                        cc.maybeLineBreak();
                    }
                    // because top-level statements are more homogeneous.
                    if (preferLineBreaks) {
                        cc.notePreferredLineBreak();
                    }
                }
                if (preserveBlock) {
                    cc.endBlock(cc.breakAfterBlockFor(node, context == Context.STATEMENT));
                }
                break;
            }
        case FOR:
            Preconditions.checkState(childCount == 4, node);
            add("for");
            cc.maybeInsertSpace();
            add("(");
            if (NodeUtil.isNameDeclaration(first)) {
                add(first, Context.IN_FOR_INIT_CLAUSE);
            } else {
                addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE);
            }
            add(";");
            if (!first.getNext().isEmpty()) {
                cc.maybeInsertSpace();
            }
            add(first.getNext());
            add(";");
            if (!first.getNext().getNext().isEmpty()) {
                cc.maybeInsertSpace();
            }
            add(first.getNext().getNext());
            add(")");
            addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
            break;
        case FOR_IN:
            Preconditions.checkState(childCount == 3, node);
            add("for");
            cc.maybeInsertSpace();
            add("(");
            add(first);
            add("in");
            add(first.getNext());
            add(")");
            addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
            break;
        case FOR_OF:
            Preconditions.checkState(childCount == 3, node);
            add("for");
            cc.maybeInsertSpace();
            add("(");
            add(first);
            cc.maybeInsertSpace();
            add("of");
            cc.maybeInsertSpace();
            // the iterable must be an AssignmentExpression
            addExpr(first.getNext(), NodeUtil.precedence(Token.ASSIGN), Context.OTHER);
            add(")");
            addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
            break;
        case FOR_AWAIT_OF:
            Preconditions.checkState(childCount == 3, node);
            add("for await");
            cc.maybeInsertSpace();
            add("(");
            add(first);
            cc.maybeInsertSpace();
            add("of");
            cc.maybeInsertSpace();
            // the iterable must be an AssignmentExpression
            addExpr(first.getNext(), NodeUtil.precedence(Token.ASSIGN), Context.OTHER);
            add(")");
            addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
            break;
        case DO:
            Preconditions.checkState(childCount == 2, node);
            add("do");
            addNonEmptyStatement(first, Context.OTHER, false);
            cc.maybeInsertSpace();
            add("while");
            cc.maybeInsertSpace();
            add("(");
            add(last);
            add(")");
            cc.endStatement();
            break;
        case WHILE:
            Preconditions.checkState(childCount == 2, node);
            add("while");
            cc.maybeInsertSpace();
            add("(");
            add(first);
            add(")");
            addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
            break;
        case EMPTY:
            Preconditions.checkState(childCount == 0, node);
            break;
        case OPTCHAIN_GETPROP:
            {
                addExpr(first, NodeUtil.precedence(type), context);
                add(node.isOptionalChainStart() ? "?." : ".");
                addGetpropIdentifier(node);
                break;
            }
        case GETPROP:
            {
                // qualified names.
                if (useOriginalName && node.getOriginalName() != null) {
                    // rewrite it back to the original code.
                    if (node.getFirstChild().matchesQualifiedName("$jscomp.scope") && node.getParent().isAssign()) {
                        add("var ");
                    }
                    addGetpropIdentifier(node);
                    break;
                }
                // We need parentheses to distinguish
                // `a?.b.c` from `(a?.b).c`
                boolean breakOutOfOptionalChain = NodeUtil.isOptChainNode(first);
                // `2.toString()` is invalid - it must be `(2).toString()`
                boolean needsParens = first.isNumber() || breakOutOfOptionalChain;
                if (needsParens) {
                    add("(");
                }
                addExpr(first, NodeUtil.precedence(type), context);
                if (needsParens) {
                    add(")");
                }
                if (quoteKeywordProperties && TokenStream.isKeyword(node.getString())) {
                    // NOTE: We don't have to worry about quoting keyword properties in the
                    // OPTCHAIN_GETPROP case above, because we only need to quote keywords for
                    // ES3-compatible output.
                    // 
                    // Must be a single call to `add` otherwise the generator will add a trailing space.
                    add("[\"" + node.getString() + "\"]");
                } else {
                    add(".");
                    addGetpropIdentifier(node);
                }
                break;
            }
        case OPTCHAIN_GETELEM:
            {
                checkState(childCount == 2, "Bad GETELEM node: Expected 2 children but got %s. For node: %s", childCount, node);
                addExpr(first, NodeUtil.precedence(type), context);
                if (node.isOptionalChainStart()) {
                    add("?.");
                }
                add("[");
                add(first.getNext());
                add("]");
                break;
            }
        case GETELEM:
            {
                checkState(childCount == 2, "Bad GETELEM node: Expected 2 children but got %s. For node: %s", childCount, node);
                boolean needsParens = NodeUtil.isOptChainNode(first);
                if (needsParens) {
                    add("(");
                }
                addExpr(first, NodeUtil.precedence(type), context);
                if (needsParens) {
                    add(")");
                }
                add("[");
                add(first.getNext());
                add("]");
                break;
            }
        case WITH:
            Preconditions.checkState(childCount == 2, node);
            add("with(");
            add(first);
            add(")");
            addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
            break;
        case INC:
        case DEC:
            {
                checkState(childCount == 1, node);
                String o = type == Token.INC ? "++" : "--";
                boolean postProp = node.getBooleanProp(Node.INCRDECR_PROP);
                if (postProp) {
                    addExpr(first, NodeUtil.precedence(type), context);
                    cc.addOp(o, false);
                } else {
                    cc.addOp(o, false);
                    add(first);
                }
                break;
            }
        case OPTCHAIN_CALL:
            {
                // that must be preserved.
                if (isIndirectEval(first) || (node.getBooleanProp(Node.FREE_CALL) && NodeUtil.isNormalOrOptChainGet(first))) {
                    add("(0,");
                    addExpr(first, NodeUtil.precedence(Token.COMMA), Context.OTHER);
                    add(")");
                } else {
                    addExpr(first, NodeUtil.precedence(type), context);
                }
                Node args = first.getNext();
                if (node.isOptionalChainStart()) {
                    add("?.");
                }
                add("(");
                addList(args);
                add(")");
                break;
            }
        case CALL:
            this.addInvocationTarget(node, context);
            add("(");
            addList(first.getNext());
            add(")");
            break;
        case IF:
            Preconditions.checkState(childCount == 2 || childCount == 3, node);
            boolean hasElse = childCount == 3;
            boolean ambiguousElseClause = context == Context.BEFORE_DANGLING_ELSE && !hasElse;
            if (ambiguousElseClause) {
                cc.beginBlock();
            }
            add("if");
            cc.maybeInsertSpace();
            add("(");
            add(first);
            add(")");
            if (hasElse) {
                addNonEmptyStatement(first.getNext(), Context.BEFORE_DANGLING_ELSE, false);
                cc.maybeInsertSpace();
                add("else");
                addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
            } else {
                addNonEmptyStatement(first.getNext(), Context.OTHER, false);
            }
            if (ambiguousElseClause) {
                cc.endBlock();
            }
            break;
        case NULL:
            Preconditions.checkState(childCount == 0, node);
            cc.addConstant("null");
            break;
        case THIS:
            Preconditions.checkState(childCount == 0, node);
            add("this");
            break;
        case SUPER:
            Preconditions.checkState(childCount == 0, node);
            add("super");
            break;
        case NEW_TARGET:
            Preconditions.checkState(childCount == 0, node);
            add("new.target");
            break;
        case YIELD:
            add("yield");
            if (node.isYieldAll()) {
                checkNotNull(first);
                add("*");
            }
            if (first != null) {
                cc.maybeInsertSpace();
                addExpr(first, NodeUtil.precedence(type), Context.OTHER);
            }
            break;
        case AWAIT:
            add("await ");
            addExpr(first, NodeUtil.precedence(type), Context.OTHER);
            break;
        case FALSE:
            Preconditions.checkState(childCount == 0, node);
            cc.addConstant("false");
            break;
        case TRUE:
            Preconditions.checkState(childCount == 0, node);
            cc.addConstant("true");
            break;
        case CONTINUE:
            Preconditions.checkState(childCount <= 1, node);
            add("continue");
            if (childCount == 1) {
                if (!first.isLabelName()) {
                    throw new Error("Unexpected token type. Should be LABEL_NAME.");
                }
                add(" ");
                add(first);
            }
            cc.endStatement();
            break;
        case DEBUGGER:
            Preconditions.checkState(childCount == 0, node);
            add("debugger");
            cc.endStatement();
            break;
        case BREAK:
            Preconditions.checkState(childCount <= 1, node);
            add("break");
            if (childCount == 1) {
                if (!first.isLabelName()) {
                    throw new Error("Unexpected token type. Should be LABEL_NAME.");
                }
                add(" ");
                add(first);
            }
            cc.endStatement();
            break;
        case EXPR_RESULT:
            Preconditions.checkState(childCount == 1, node);
            add(first, Context.START_OF_EXPR);
            cc.endStatement();
            break;
        case NEW:
            add("new ");
            int precedence = NodeUtil.precedence(type);
            // `new void 0` is a syntax error add parenthese in this case.  This is only particularly
            // interesting for code in dead branches.
            int precedenceOfFirst = NodeUtil.precedence(first.getToken());
            if (precedenceOfFirst == precedence) {
                precedence = precedence + 1;
            }
            // If the first child is an arrow function, then parentheses is needed
            if (NodeUtil.has(first, Node::isCall, NodeUtil.MATCH_NOT_FUNCTION) || NodeUtil.isOptChainNode(first)) {
                precedence = NodeUtil.precedence(first.getToken()) + 1;
            }
            addExpr(first, precedence, Context.OTHER);
            // '()' is optional when no arguments are present
            Node next = first.getNext();
            if (next != null) {
                add("(");
                addList(next);
                add(")");
            } else {
                if (cc.shouldPreserveExtras(node)) {
                    add("(");
                    add(")");
                }
            }
            break;
        case STRING_KEY:
            addStringKey(node);
            break;
        case STRINGLIT:
            Preconditions.checkState(childCount == 0, "String node %s may not have children", node);
            addJsString(node);
            break;
        case DELPROP:
            Preconditions.checkState(childCount == 1, node);
            add("delete ");
            add(first);
            break;
        case OBJECTLIT:
            {
                boolean needsParens = context == Context.START_OF_EXPR || context.atArrowFunctionBody();
                if (needsParens) {
                    add("(");
                }
                add("{");
                for (Node c = first; c != null; c = c.getNext()) {
                    if (c != first) {
                        cc.listSeparator();
                    }
                    checkState(NodeUtil.isObjLitProperty(c) || c.isSpread(), c);
                    add(c);
                }
                if (first != null && node.hasTrailingComma()) {
                    cc.optionalListSeparator();
                }
                add("}");
                if (needsParens) {
                    add(")");
                }
                break;
            }
        case COMPUTED_PROP:
            maybeAddAccessibilityModifier(node);
            if (node.getBooleanProp(Node.STATIC_MEMBER)) {
                add("static ");
            }
            if (node.getBooleanProp(Node.COMPUTED_PROP_GETTER)) {
                add("get ");
            } else if (node.getBooleanProp(Node.COMPUTED_PROP_SETTER)) {
                add("set ");
            } else if (node.getBooleanProp(Node.COMPUTED_PROP_METHOD)) {
                if (last.isAsyncFunction()) {
                    add("async");
                }
                if (last.getBooleanProp(Node.GENERATOR_FN)) {
                    add("*");
                }
            }
            add("[");
            // Must use addExpr() with a priority of 1, because comma expressions aren't allowed.
            // https://www.ecma-international.org/ecma-262/9.0/index.html#prod-ComputedPropertyName
            addExpr(first, 1, Context.OTHER);
            add("]");
            // TODO(martinprobst): There's currently no syntax for properties in object literals that
            // have type declarations on them (a la `{foo: number: 12}`). This comes up for, e.g.,
            // function parameters with default values. Support when figured out.
            maybeAddTypeDecl(node);
            if (node.getBooleanProp(Node.COMPUTED_PROP_METHOD) || node.getBooleanProp(Node.COMPUTED_PROP_GETTER) || node.getBooleanProp(Node.COMPUTED_PROP_SETTER)) {
                Node function = first.getNext();
                Node params = function.getSecondChild();
                Node body = function.getLastChild();
                add(params);
                add(body);
            } else {
                // This is a field or object literal property.
                boolean isInClass = node.getParent().isClassMembers();
                Node initializer = first.getNext();
                if (initializer != null) {
                    // Object literal value.
                    checkState(!isInClass, "initializers should only exist in object literals, not classes");
                    cc.add(":");
                    // Must use addExpr() with a priority of 1, because a comma expression here would cause
                    // a syntax error within the object literal.
                    addExpr(initializer, 1, Context.OTHER);
                } else {
                    // Computed properties must either have an initializer or be computed member-variable
                    // properties that exist for their type declaration.
                    checkState(node.getBooleanProp(Node.COMPUTED_PROP_VARIABLE), node);
                }
            }
            break;
        case OBJECT_PATTERN:
            addObjectPattern(node);
            maybeAddTypeDecl(node);
            break;
        case SWITCH:
            add("switch(");
            add(first);
            add(")");
            cc.beginBlock();
            addAllSiblings(first.getNext());
            cc.endBlock(context == Context.STATEMENT);
            break;
        case CASE:
            Preconditions.checkState(childCount == 2, node);
            add("case ");
            add(first);
            addCaseBody(last);
            break;
        case DEFAULT_CASE:
            Preconditions.checkState(childCount == 1, node);
            add("default");
            addCaseBody(first);
            break;
        case LABEL:
            Preconditions.checkState(childCount == 2, node);
            if (!first.isLabelName()) {
                throw new Error("Unexpected token type. Should be LABEL_NAME.");
            }
            add(first);
            add(":");
            if (!last.isBlock()) {
                cc.maybeInsertSpace();
            }
            addNonEmptyStatement(last, getContextForNonEmptyExpression(context), true);
            break;
        case CAST:
            if (preserveTypeAnnotations) {
                add("(");
                // drop context because of added parentheses
                add(first);
                add(")");
            } else {
                // preserve context
                add(first, context);
            }
            break;
        case TAGGED_TEMPLATELIT:
            this.addInvocationTarget(node, context);
            add(first.getNext());
            break;
        case TEMPLATELIT:
            cc.beginTemplateLit();
            for (Node c = first; c != null; c = c.getNext()) {
                if (c.isTemplateLitString()) {
                    add(escapeUnrecognizedCharacters(c.getRawString()));
                } else {
                    cc.beginTemplateLitSub();
                    add(c.getFirstChild(), Context.START_OF_EXPR);
                    cc.endTemplateLitSub();
                }
            }
            cc.endTemplateLit();
            break;
        // Type Declaration ASTs.
        case STRING_TYPE:
            add("string");
            break;
        case BOOLEAN_TYPE:
            add("boolean");
            break;
        case NUMBER_TYPE:
            add("number");
            break;
        case ANY_TYPE:
            add("any");
            break;
        case VOID_TYPE:
            add("void");
            break;
        case NAMED_TYPE:
            // Children are a chain of getprop nodes.
            add(first);
            break;
        case ARRAY_TYPE:
            addExpr(first, NodeUtil.precedence(Token.ARRAY_TYPE), context);
            add("[]");
            break;
        case FUNCTION_TYPE:
            Node returnType = first;
            add("(");
            addList(first.getNext());
            add(")");
            cc.addOp("=>", true);
            add(returnType);
            break;
        case UNION_TYPE:
            addList(first, "|");
            break;
        case RECORD_TYPE:
            add("{");
            addList(first, false, Context.OTHER, ",");
            add("}");
            break;
        case PARAMETERIZED_TYPE:
            // First child is the type that's parameterized, later children are the arguments.
            add(first);
            add("<");
            addList(first.getNext());
            add(">");
            break;
        // CLASS -> NAME,EXPR|EMPTY,BLOCK
        case GENERIC_TYPE_LIST:
            add("<");
            addList(first, false, Context.STATEMENT, ",");
            add(">");
            break;
        case GENERIC_TYPE:
            addIdentifier(node.getString());
            if (node.hasChildren()) {
                add("extends");
                cc.maybeInsertSpace();
                add(node.getFirstChild());
            }
            break;
        case INTERFACE:
            {
                checkState(childCount == 3, node);
                Node name = first;
                Node superTypes = first.getNext();
                Node members = last;
                add("interface");
                add(name);
                maybeAddGenericTypes(name);
                if (!superTypes.isEmpty()) {
                    add("extends");
                    Node superType = superTypes.getFirstChild();
                    add(superType);
                    while ((superType = superType.getNext()) != null) {
                        add(",");
                        cc.maybeInsertSpace();
                        add(superType);
                    }
                }
                add(members);
            }
            break;
        case ENUM:
            {
                checkState(childCount == 2, node);
                Node name = first;
                Node members = last;
                add("enum");
                add(name);
                add(members);
                break;
            }
        case NAMESPACE:
            {
                checkState(childCount == 2, node);
                Node name = first;
                Node elements = last;
                add("namespace");
                add(name);
                add(elements);
                break;
            }
        case TYPE_ALIAS:
            add("type");
            add(node.getString());
            cc.addOp("=", true);
            add(last);
            cc.endStatement(true);
            break;
        case DECLARE:
            add("declare");
            add(first);
            processEnd(node, context);
            break;
        case INDEX_SIGNATURE:
            add("[");
            add(first);
            add("]");
            maybeAddTypeDecl(node);
            cc.endStatement(true);
            break;
        case CALL_SIGNATURE:
            if (node.getBooleanProp(Node.CONSTRUCT_SIGNATURE)) {
                add("new ");
            }
            maybeAddGenericTypes(node);
            add(first);
            maybeAddTypeDecl(node);
            cc.endStatement(true);
            break;
        default:
            throw new IllegalStateException("Unknown token " + type + "\n" + node.toStringTree());
    }
    // print any trailing nonJSDoc comment attached to this node
    if (printNonJSDocComments) {
        NonJSDocComment nonJSDocComment = node.getNonJSDocComment();
        if (nonJSDocComment != null && nonJSDocComment.isTrailing()) {
            String nonJSDocCommentString = node.getNonJSDocCommentString();
            if (!nonJSDocCommentString.isEmpty()) {
                addNonJsDoctrailing(nonJSDocComment);
            }
        }
    }
    cc.endSourceMapping(node);
}
Also used : Node(com.google.javascript.rhino.Node) Token(com.google.javascript.rhino.Token) NonJSDocComment(com.google.javascript.rhino.NonJSDocComment)

Example 5 with NonJSDocComment

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

the class ParserTest method testNoInlineNonJSDocCommentAttachmentWithoutParsingMode.

@Test
public void testNoInlineNonJSDocCommentAttachmentWithoutParsingMode() {
    Node letNode = parse("let /* blah */ x = 'a';").getFirstChild();
    assertNode(letNode).hasType(Token.LET);
    NonJSDocComment nonJSDocComment = letNode.getFirstChild().getNonJSDocComment();
    assertThat(nonJSDocComment).isNull();
}
Also used : Node(com.google.javascript.rhino.Node) NodeSubject.assertNode(com.google.javascript.rhino.testing.NodeSubject.assertNode) NonJSDocComment(com.google.javascript.rhino.NonJSDocComment) Test(org.junit.Test)

Aggregations

NonJSDocComment (com.google.javascript.rhino.NonJSDocComment)7 Node (com.google.javascript.rhino.Node)5 JSDocInfo (com.google.javascript.rhino.JSDocInfo)3 NodeSubject.assertNode (com.google.javascript.rhino.testing.NodeSubject.assertNode)2 Test (org.junit.Test)2 Comment (com.google.javascript.jscomp.parsing.parser.trees.Comment)1 Token (com.google.javascript.rhino.Token)1 Nullable (javax.annotation.Nullable)1