feat(KT-51123): save comments from js-function call inside arguments list.

This commit is contained in:
Artem Kobzar
2022-06-13 12:58:18 +00:00
committed by Space
parent ec9d929532
commit e790607af5
19 changed files with 1858 additions and 172 deletions
@@ -173,9 +173,11 @@ public class JsToStringGenerationVisitor extends JsVisitor {
}
}
protected boolean insideComments = false;
protected boolean needSemi = true;
private boolean lineBreakAfterBlock = true;
/**
* "Global" blocks are either the global block of a fragment, or a block
* nested directly within some other global block. This definition matters
@@ -198,6 +200,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitArrayAccess(@NotNull JsArrayAccess x) {
printCommentsBeforeNode(x);
pushSourceInfo(x.getSource());
printPair(x, x.getArrayExpression());
@@ -205,17 +208,20 @@ public class JsToStringGenerationVisitor extends JsVisitor {
accept(x.getIndexExpression());
rightSquare();
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitArray(@NotNull JsArrayLiteral x) {
printCommentsBeforeNode(x);
pushSourceInfo(x.getSource());
leftSquare();
printExpressions(x.getExpressions());
rightSquare();
printCommentsAfterNode(x);
popSourceInfo();
}
@@ -233,6 +239,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitBinaryExpression(@NotNull JsBinaryOperation binaryOperation) {
printCommentsBeforeNode(binaryOperation);
pushSourceInfo(binaryOperation.getSource());
JsBinaryOperator operator = binaryOperation.getOperator();
@@ -277,6 +284,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
rightParen();
}
printCommentsAfterNode(binaryOperation);
popSourceInfo();
}
@@ -288,6 +296,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitBoolean(@NotNull JsBooleanLiteral x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
if (x.getValue()) {
p.print(CHARS_TRUE);
@@ -296,26 +305,31 @@ public class JsToStringGenerationVisitor extends JsVisitor {
p.print(CHARS_FALSE);
}
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitBreak(@NotNull JsBreak x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_BREAK);
continueOrBreakLabel(x);
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitContinue(@NotNull JsContinue x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_CONTINUE);
continueOrBreakLabel(x);
printCommentsAfterNode(x);
popSourceInfo();
}
@@ -330,12 +344,14 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitCase(@NotNull JsCase x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_CASE);
space();
accept(x.getCaseExpression());
_colon();
printCommentsAfterNode(x);
popSourceInfo();
newlineOpt();
@@ -362,6 +378,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitCatch(@NotNull JsCatch x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
spaceOpt();
p.print(CHARS_CATCH);
@@ -382,6 +399,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
rightParen();
spaceOpt();
printCommentsAfterNode(x);
popSourceInfo();
sourceLocationConsumer.pushSourceInfo(null);
@@ -392,6 +410,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitConditional(@NotNull JsConditional x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
// Associativity: for the then and else branches, it is safe to insert
// another
@@ -407,6 +426,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
spaceOpt();
printPair(x, x.getElseExpression());
printCommentsAfterNode(x);
popSourceInfo();
}
@@ -428,19 +448,23 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitDebugger(@NotNull JsDebugger x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_DEBUGGER);
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitDefault(@NotNull JsDefault x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_DEFAULT);
_colon();
printCommentsAfterNode(x);
popSourceInfo();
newlineOpt();
@@ -452,6 +476,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitWhile(@NotNull JsWhile x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
_while();
spaceOpt();
@@ -459,6 +484,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
accept(x.getCondition());
rightParen();
printCommentsAfterNode(x);
popSourceInfo();
JsStatement body = materialize(x.getBody());
@@ -473,6 +499,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitDoWhile(@NotNull JsDoWhile x) {
sourceLocationConsumer.pushSourceInfo(null);
printCommentsBeforeNode(x);
p.print(CHARS_DO);
@@ -499,6 +526,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
accept(x.getCondition());
rightParen();
printCommentsAfterNode(x);
popSourceInfo();
}
@@ -513,6 +541,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
source = x.getExpression().getSource();
}
pushSourceInfo(source);
printCommentsBeforeNode(x);
boolean surroundWithParentheses = JsFirstExpressionVisitor.exec(x);
if (surroundWithParentheses) {
@@ -523,12 +552,14 @@ public class JsToStringGenerationVisitor extends JsVisitor {
rightParen();
}
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitFor(@NotNull JsFor x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
_for();
spaceOpt();
@@ -563,6 +594,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
rightParen();
printCommentsAfterNode(x);
popSourceInfo();
JsStatement body = materialize(x.getBody());
@@ -579,6 +611,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitForIn(@NotNull JsForIn x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
_for();
spaceOpt();
@@ -609,6 +642,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
rightParen();
printCommentsAfterNode(x);
popSourceInfo();
JsStatement body = materialize(x.getBody());
@@ -622,12 +656,14 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitFunction(@NotNull JsFunction x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_FUNCTION);
space();
printFunction(x);
printCommentsAfterNode(x);
popSourceInfo();
}
@@ -659,6 +695,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitClass(@NotNull JsClass x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_CLASS);
space();
@@ -699,12 +736,14 @@ public class JsToStringGenerationVisitor extends JsVisitor {
needSemi = false;
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitIf(@NotNull JsIf x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
_if();
spaceOpt();
@@ -712,6 +751,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
accept(x.getIfExpression());
rightParen();
printCommentsAfterNode(x);
popSourceInfo();
JsStatement thenStmt = x.getThenStatement();
@@ -777,6 +817,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitInvocation(@NotNull JsInvocation invocation) {
pushSourceInfo(invocation.getSource());
printCommentsBeforeNode(invocation);
printPair(invocation, invocation.getQualifier());
@@ -784,6 +825,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
printExpressions(invocation.getArguments());
rightParen();
printCommentsAfterNode(invocation);
popSourceInfo();
}
@@ -800,10 +842,15 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitNameRef(@NotNull JsNameRef nameRef) {
visitNameRef(nameRef, true);
}
public void visitNameRef(@NotNull JsNameRef nameRef, boolean withQualifier) {
pushSourceInfo(nameRef.getSource());
printCommentsBeforeNode(nameRef);
JsExpression qualifier = nameRef.getQualifier();
if (qualifier != null) {
if (qualifier != null && withQualifier) {
boolean enclose;
if (qualifier instanceof JsLiteral.JsValueLiteral) {
// "42.foo" is not allowed, but "(42).foo" is.
@@ -826,12 +873,14 @@ public class JsToStringGenerationVisitor extends JsVisitor {
p.maybeIndent();
p.print(nameRef.getIdent());
printCommentsAfterNode(nameRef);
popSourceInfo();
}
@Override
public void visitNew(@NotNull JsNew x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_NEW);
space();
@@ -850,39 +899,47 @@ public class JsToStringGenerationVisitor extends JsVisitor {
printExpressions(x.getArguments());
rightParen();
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitNull(@NotNull JsNullLiteral x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_NULL);
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitInt(@NotNull JsIntLiteral x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(x.value);
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitDouble(@NotNull JsDoubleLiteral x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(x.value);
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitObjectLiteral(@NotNull JsObjectLiteral objectLiteral) {
pushSourceInfo(objectLiteral.getSource());
printCommentsBeforeNode(objectLiteral);
p.print('{');
@@ -908,26 +965,18 @@ public class JsToStringGenerationVisitor extends JsVisitor {
pushSourceInfo(item.getSource());
JsExpression labelExpr = item.getLabelExpr();
// labels can be either string, integral, or decimal literals
if (labelExpr instanceof JsNameRef) {
p.print(((JsNameRef) labelExpr).getIdent());
}
else if (labelExpr instanceof JsStringLiteral) {
if (labelExpr instanceof JsStringLiteral) {
JsStringLiteral stringLiteral = (JsStringLiteral) labelExpr;
String value = stringLiteral.getValue();
boolean isValidIdentifier = IdentifierPolicyKt.isValidES5Identifier(value);
if (!isValidIdentifier) {
p.print('\'');
}
p.print(value);
if (!isValidIdentifier) {
p.print('\'');
if (IdentifierPolicyKt.isValidES5Identifier(value)) {
labelExpr = new JsNameRef(value).withMetadataFrom(stringLiteral);
}
}
else {
// labels can be either string, integral, or decimal literals
if (labelExpr instanceof JsNameRef) {
visitNameRef((JsNameRef) labelExpr, false);
} else {
accept(labelExpr);
}
@@ -949,6 +998,8 @@ public class JsToStringGenerationVisitor extends JsVisitor {
}
p.print('}');
printCommentsAfterNode(objectLiteral);
popSourceInfo();
}
@@ -960,6 +1011,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitPostfixOperation(@NotNull JsPostfixOperation x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
JsUnaryOperator op = x.getOperator();
JsExpression arg = x.getArg();
@@ -967,12 +1019,14 @@ public class JsToStringGenerationVisitor extends JsVisitor {
printPair(x, arg);
p.print(op.getSymbol());
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitPrefixOperation(@NotNull JsPrefixOperation x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
JsUnaryOperator op = x.getOperator();
p.print(op.getSymbol());
@@ -983,6 +1037,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
// unary operators always associate correctly (I think)
printPair(x, arg);
printCommentsAfterNode(x);
popSourceInfo();
}
@@ -994,6 +1049,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitRegExp(@NotNull JsRegExp x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
slash();
p.print(x.getPattern());
@@ -1003,12 +1059,14 @@ public class JsToStringGenerationVisitor extends JsVisitor {
p.print(flags);
}
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitReturn(@NotNull JsReturn x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_RETURN);
JsExpression expr = x.getExpression();
@@ -1017,21 +1075,25 @@ public class JsToStringGenerationVisitor extends JsVisitor {
accept(expr);
}
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitString(@NotNull JsStringLiteral x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(javaScriptString(x.getValue()));
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visit(@NotNull JsSwitch x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_SWITCH);
spaceOpt();
@@ -1039,6 +1101,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
accept(x.getExpression());
rightParen();
printCommentsAfterNode(x);
popSourceInfo();
@@ -1053,25 +1116,30 @@ public class JsToStringGenerationVisitor extends JsVisitor {
@Override
public void visitThis(@NotNull JsThisRef x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_THIS);
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitThrow(@NotNull JsThrow x) {
pushSourceInfo(x.getSource());
printCommentsBeforeNode(x);
p.print(CHARS_THROW);
space();
accept(x.getExpression());
printCommentsAfterNode(x);
popSourceInfo();
}
@Override
public void visitTry(@NotNull JsTry x) {
printCommentsBeforeNode(x);
p.print(CHARS_TRY);
spaceOpt();
lineBreakAfterBlock = false;
@@ -1085,11 +1153,13 @@ public class JsToStringGenerationVisitor extends JsVisitor {
spaceOpt();
accept(finallyBlock);
}
printCommentsAfterNode(x);
}
@Override
public void visit(@NotNull JsVar var) {
pushSourceInfo(var.getSource());
printCommentsBeforeNode(var);
nameOf(var);
JsExpression initExpr = var.getInitExpression();
@@ -1104,12 +1174,14 @@ public class JsToStringGenerationVisitor extends JsVisitor {
}
}
printCommentsAfterNode(var);
popSourceInfo();
}
@Override
public void visitVars(@NotNull JsVars vars) {
pushSourceInfo(vars.getSource());
printCommentsBeforeNode(vars);
var();
space();
@@ -1129,15 +1201,20 @@ public class JsToStringGenerationVisitor extends JsVisitor {
accept(var);
}
printCommentsAfterNode(vars);
popSourceInfo();
}
@Override
public void visitSingleLineComment(@NotNull JsSingleLineComment comment) {
if (needSemi && insideComments) {
semi();
space();
}
p.print("//");
p.print(comment.getText());
needSemi = false;
newline();
needSemi = false;
}
@Override
@@ -1153,8 +1230,6 @@ public class JsToStringGenerationVisitor extends JsVisitor {
}
p.print("*/");
needSemi = false;
newline();
}
@Override
@@ -1295,6 +1370,32 @@ public class JsToStringGenerationVisitor extends JsVisitor {
}
}
private void printCommentsBeforeNode(JsNode x) {
printComments(x.getCommentsBeforeNode(), false);
}
private void printCommentsAfterNode(JsNode x) {
printComments(x.getCommentsAfterNode(), true);
}
private void printComments(List<JsComment> comments, boolean isAfterNode) {
if (comments == null) return;
boolean previousNeedSemi = needSemi;
needSemi = isAfterNode;
insideComments = true;
for (JsComment comment : comments) {
comment.accept(this);
}
insideComments = false;
if (!isAfterNode) {
needSemi = previousNeedSemi;
}
}
private void popSourceInfo() {
if (!sourceInfoStack.isEmpty() && sourceInfoStack.remove(sourceInfoStack.size() - 1) != null) {
sourceLocationConsumer.popSourceInfo();
@@ -1308,6 +1409,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
}
sourceLocationConsumer.pushSourceInfo(null);
printCommentsBeforeNode(x);
boolean needBraces = !x.isTransparent();
@@ -1390,6 +1492,8 @@ public class JsToStringGenerationVisitor extends JsVisitor {
}
needSemi = false;
printCommentsAfterNode(x);
sourceLocationConsumer.popSourceInfo();
}
@@ -20,7 +20,13 @@ import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor;
import org.jetbrains.kotlin.js.backend.ast.metadata.HasMetadata;
import org.jetbrains.kotlin.js.util.TextOutputImpl;
import java.util.LinkedList;
import java.util.List;
abstract class AbstractNode extends HasMetadata implements JsNode {
private List<JsComment> commentsBefore = null;
private List<JsComment> commentsAfter = null;
@Override
public String toString() {
TextOutputImpl out = new TextOutputImpl();
@@ -29,12 +35,34 @@ abstract class AbstractNode extends HasMetadata implements JsNode {
}
@SuppressWarnings("unchecked")
protected <T extends HasMetadata & JsNode> T withMetadataFrom(T other) {
public <T extends HasMetadata & JsNode> T withMetadataFrom(T other) {
this.copyMetadataFrom(other);
Object otherSource = other.getSource();
if (otherSource != null) {
source(otherSource);
}
setCommentsBeforeNode(other.getCommentsBeforeNode());
setCommentsAfterNode(other.getCommentsAfterNode());
return (T) this;
}
@Override
public List<JsComment> getCommentsBeforeNode() {
return commentsBefore;
}
@Override
public List<JsComment> getCommentsAfterNode() {
return commentsAfter;
}
@Override
public void setCommentsBeforeNode(List<JsComment> comments) {
commentsBefore = comments;
}
@Override
public void setCommentsAfterNode(List<JsComment> comments) {
commentsAfter = comments;
}
}
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.js.backend.ast
interface JsComment : JsStatement {
val text: String
}
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.js.backend.ast
class JsMultiLineComment(val text: String) : SourceInfoAwareJsNode(), JsStatement {
class JsMultiLineComment(override val text: String) : SourceInfoAwareJsNode(), JsComment {
override fun accept(visitor: JsVisitor) {
visitor.visitMultiLineComment(this)
}
@@ -6,6 +6,9 @@ package org.jetbrains.kotlin.js.backend.ast;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedList;
import java.util.List;
public interface JsNode {
/**
* Causes this object to have the visitor visit itself and its children.
@@ -40,4 +43,12 @@ public interface JsNode {
* @param ctx the context of an existing traversal
*/
void traverse(JsVisitorWithContext visitor, JsContext ctx);
List<JsComment> getCommentsBeforeNode();
List<JsComment> getCommentsAfterNode();
void setCommentsBeforeNode(List<JsComment> comment);
void setCommentsAfterNode(List<JsComment> comment);
}
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.js.backend.ast
class JsSingleLineComment(val text: String) : SourceInfoAwareJsNode(), JsStatement {
class JsSingleLineComment(override val text: String) : SourceInfoAwareJsNode(), JsComment {
override fun accept(visitor: JsVisitor) {
visitor.visitSingleLineComment(this)
}
@@ -17,6 +17,7 @@ package com.google.gwt.dev.js;
import com.google.gwt.dev.js.parserExceptions.JsParserException;
import com.google.gwt.dev.js.rhino.CodePosition;
import com.google.gwt.dev.js.rhino.Comment;
import com.google.gwt.dev.js.rhino.Node;
import com.google.gwt.dev.js.rhino.TokenStream;
import com.intellij.util.SmartList;
@@ -25,6 +26,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.backend.ast.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class JsAstMapper {
@@ -44,7 +46,18 @@ public class JsAstMapper {
}
private JsNode map(Node node) throws JsParserException {
return withLocation(mapWithoutLocation(node), node);
return withLocation(mapWithComments(node), node);
}
private JsNode mapWithComments(Node node) throws JsParserException {
JsNode jsNode = mapWithoutLocation(node);
if (jsNode != null) {
jsNode.setCommentsBeforeNode(mapComments(node.getCommentsBeforeNode()));
jsNode.setCommentsAfterNode(mapComments(node.getCommentsAfterNode()));
}
return jsNode;
}
private JsNode mapWithoutLocation(Node node) throws JsParserException {
@@ -216,12 +229,6 @@ public class JsAstMapper {
case TokenStream.LABEL:
return mapLabel(node);
case TokenStream.SINGLE_LINE_COMMENT:
return mapSingleLineComment(node);
case TokenStream.MULTI_LINE_COMMENT:
return mapMultiLineComment(node);
default:
int tokenType = node.getType();
throw createParserException("Unexpected top-level token type: "
@@ -1139,4 +1146,19 @@ public class JsAstMapper {
}
return astNode;
}
private List<JsComment> mapComments(Comment comment) {
if (comment == null) return null;
List<JsComment> comments = new LinkedList<>();
while (comment != null) {
String text = comment.getText();
JsComment jsComment = comment.isMultiLine() ? new JsMultiLineComment(text) : new JsSingleLineComment(text);
comments.add(jsComment);
comment = comment.getNext();
}
return comments;
}
}
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package com.google.gwt.dev.js.rhino
class Comment(val text: String, val isMultiLine: Boolean) {
var next: Comment? = null;
}
@@ -331,14 +331,6 @@ public class IRFactory {
return new Node(nodeType, left, right, nodeOp, location);
}
public Node createSingleLineComment(String string, CodePosition position) {
return Node.newComment(TokenStream.SINGLE_LINE_COMMENT, string, position);
}
public Node createMultiLineComment(String string, CodePosition position) {
return Node.newComment(TokenStream.MULTI_LINE_COMMENT, string, position);
}
public Node createAssignment(int nodeOp, Node left, Node right, CodePosition location) {
int nodeType = left.getType();
switch (nodeType) {
@@ -90,37 +90,6 @@ public class Node implements Cloneable {
private String str;
}
private static class CommentNode extends Node {
CommentNode(int type, String str, CodePosition position) {
super(type, position);
if (null == str) {
throw new IllegalArgumentException("CommentNode: str is null");
}
this.str = str;
}
/** returns the string content.
* @return non null.
*/
@Override
public String getString() {
return this.str;
}
/** sets the string content.
* @param str the new value. Non null.
*/
@Override
public void setString(String str) {
if (null == str) {
throw new IllegalArgumentException("CommentNode: str is null");
}
this.str = str;
}
private String str;
}
public Node(int nodeType) {
type = nodeType;
}
@@ -222,10 +191,6 @@ public class Node implements Cloneable {
return new StringNode(type, str, position);
}
public static Node newComment(int type, String str, CodePosition position) {
return new CommentNode(type, str, position);
}
public int getType() {
return type;
}
@@ -278,6 +243,24 @@ public class Node implements Cloneable {
}
}
public Comment getCommentsBeforeNode() {
return commentBefore;
}
public Comment getCommentsAfterNode() {
return commentAfter;
}
public void setCommentsBeforeNode(Comment comment) {
if (comment == null) return;
commentBefore = comment;
}
public void setCommentsAfterNode(Comment comment) {
if (comment == null) return;
commentAfter = comment;
}
public static final int
TARGET_PROP = 1,
BREAK_PROP = 2,
@@ -441,5 +424,9 @@ public class Node implements Cloneable {
private Node first; // first element of a linked list of children
private Node last; // last element of a linked list of children
private CodePosition position;
private Comment commentBefore;
private Comment commentAfter;
private int operation;
}
@@ -110,7 +110,7 @@ public class Parser {
while (true) {
ts.flags |= TokenStream.TSF_REGEXP;
tt = ts.getTokenWithComment();
tt = ts.getToken();
ts.flags &= ~TokenStream.TSF_REGEXP;
if (tt <= TokenStream.EOF) {
@@ -153,7 +153,7 @@ public class Parser {
Node pn = nf.createBlock(ts.tokenPosition);
try {
int tt;
while ((tt = ts.peekTokenWithComment()) > TokenStream.EOF && tt != TokenStream.RC) {
while ((tt = ts.peekToken()) > TokenStream.EOF && tt != TokenStream.RC) {
if (tt == TokenStream.FUNCTION) {
ts.getToken();
pn.addChildToBack(function(ts, false));
@@ -334,7 +334,13 @@ public class Parser {
private Node statement(TokenStream ts) throws IOException {
CodePosition position = ts.lastPosition;
try {
return statementHelper(ts);
Comment commentsBefore = getComments(ts);
ts.collectCommentsAfter();
Node result = statementHelper(ts);
result.setCommentsBeforeNode(commentsBefore);
ts.collectCommentsAfter();
result.setCommentsAfterNode(getComments(ts));
return result;
}
catch (JavaScriptException e) {
// skip to end of statement
@@ -360,7 +366,7 @@ public class Parser {
int lastExprType; // For wellTerminated
tt = ts.getTokenWithComment();
tt = ts.getToken();
CodePosition position = ts.tokenPosition;
switch (tt) {
@@ -594,14 +600,6 @@ public class Parser {
break;
}
case TokenStream.SINGLE_LINE_COMMENT:
pn = nf.createSingleLineComment(ts.getString(), ts.tokenPosition);
break;
case TokenStream.MULTI_LINE_COMMENT:
pn = nf.createMultiLineComment(ts.getString(), ts.tokenPosition);
break;
case TokenStream.RETURN: {
Node retExpr = null;
int lineno;
@@ -703,9 +701,7 @@ public class Parser {
}
}
if (pn.type != TokenStream.SINGLE_LINE_COMMENT && pn.type != TokenStream.MULTI_LINE_COMMENT) {
ts.matchToken(TokenStream.SEMI);
}
ts.matchToken(TokenStream.SEMI);
return pn;
}
@@ -740,22 +736,32 @@ public class Parser {
public Node expr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Node pn = assignExpr(ts, inForInit);
while (ts.matchToken(TokenStream.COMMA)) {
CodePosition position = ts.tokenPosition;
pn = nf.createBinary(TokenStream.COMMA, pn, assignExpr(ts, inForInit), position);
}
return pn;
}
private Node assignExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Comment commentBeforeNode = getComments(ts);
Node pn = condExpr(ts, inForInit);
pn.setCommentsBeforeNode(commentBeforeNode);
if (ts.matchToken(TokenStream.ASSIGN)) {
// omitted: "invalid assignment left-hand side" check.
CodePosition position = ts.tokenPosition;
pn = nf.createBinary(TokenStream.ASSIGN, ts.getOp(), pn, assignExpr(ts, inForInit), position);
}
ts.collectCommentsAfter();
pn.setCommentsAfterNode(getComments(ts));
return pn;
}
@@ -952,8 +958,7 @@ public class Parser {
if (!matched) {
do {
listNode.addChildToBack(assignExpr(ts, false));
}
while (ts.matchToken(TokenStream.COMMA));
} while (ts.matchToken(TokenStream.COMMA));
mustMatchToken(ts, TokenStream.GWT, "msg.no.paren.arg");
}
@@ -1012,7 +1017,7 @@ public class Parser {
) throws IOException, JavaScriptException {
lastExprEndLine = ts.getLineno();
int tt;
while ((tt = ts.getTokenWithComment()) > TokenStream.EOF) {
while ((tt = ts.getToken()) > TokenStream.EOF) {
CodePosition position = ts.tokenPosition;
if (tt == TokenStream.DOT) {
ts.treatKeywordAsIdentifier = true;
@@ -1050,6 +1055,15 @@ public class Parser {
}
public Node primaryExpr(TokenStream ts) throws IOException, JavaScriptException {
Comment commentsBeforeNode = getComments(ts);
Node node = primaryExprHelper(ts);
node.setCommentsBeforeNode(commentsBeforeNode);
ts.collectCommentsAfter();
node.setCommentsAfterNode(getComments(ts));
return node;
}
private Node primaryExprHelper(TokenStream ts) throws IOException, JavaScriptException {
int tt;
Node pn;
@@ -1195,6 +1209,14 @@ public class Parser {
return null; // should never reach here
}
private Comment getComments(TokenStream ts) {
Comment comment = ts.getHeadComment();
if (comment != null) {
ts.releaseComments();
}
return comment;
}
private int lastExprEndLine; // Hack to handle function expr termination.
private final IRFactory nf;
private boolean ok; // Did the parse encounter an error?
@@ -253,8 +253,6 @@ public class TokenStream {
LAST_TOKEN = 147,
NUMBER_INT = 148,
SINGLE_LINE_COMMENT = 149,
MULTI_LINE_COMMENT = 150,
// This value is only used as a return value for getTokenHelper,
// which is only called from getToken and exists to avoid an excessive
@@ -415,8 +413,6 @@ public class TokenStream {
case NEWLOCAL: return "newlocal";
case USELOCAL: return "uselocal";
case SCRIPT: return "script";
case SINGLE_LINE_COMMENT: return "singlelinecomment";
case MULTI_LINE_COMMENT: return "multilinecomment";
}
return "<unknown="+token+">";
}
@@ -539,10 +535,6 @@ public class TokenStream {
return peekTokenHelper(getToken());
}
public int peekTokenWithComment() throws IOException {
return peekTokenHelper(getTokenWithComment());
}
private int peekTokenHelper(int token) throws IOException {
this.pushbackToken = token;
lastPosition = secondToLastPosition;
@@ -596,15 +588,8 @@ public class TokenStream {
in.unread();
}
public int getTokenWithComment() throws IOException {
lastTokenPosition = tokenPosition;
int c;
do {
c = getTokenHelper();
} while (c == RETRY_TOKEN);
updatePosition();
return c;
public void collectCommentsAfter() throws IOException {
ungetToken(getToken());
}
public int getToken() throws IOException {
@@ -612,7 +597,7 @@ public class TokenStream {
int c;
do {
c = getTokenHelper();
} while (c == RETRY_TOKEN || c == SINGLE_LINE_COMMENT || c == MULTI_LINE_COMMENT);
} while (c == RETRY_TOKEN);
updatePosition();
return c;
@@ -1084,8 +1069,8 @@ public class TokenStream {
while ((c = in.read()) != -1 && c != '\n') {
addToString(c);
}
this.string = getStringFromBuffer();
return SINGLE_LINE_COMMENT;
addCommentToQueue(new Comment(getStringFromBuffer(), false));
return RETRY_TOKEN;
}
if (in.match('*')) {
stringBufferTop = 0;
@@ -1097,8 +1082,8 @@ public class TokenStream {
reportTokenError("msg.unterminated.comment", null);
return ERROR;
}
this.string = getStringFromBuffer();
return MULTI_LINE_COMMENT; // `goto retry'
addCommentToQueue(new Comment(getStringFromBuffer(), true));
return RETRY_TOKEN; // `goto retry'
}
// is it a regexp?
@@ -1481,6 +1466,17 @@ public class TokenStream {
}
}
private void addCommentToQueue(Comment comment) {
if (headComment == null) {
headComment = comment;
lastComment = comment;
}
else {
lastComment.setNext(comment);
lastComment = comment;
}
}
public String getSourceName() { return sourceName; }
public int getLineno() { return in.getLineno(); }
public int getOp() { return op; }
@@ -1491,6 +1487,15 @@ public class TokenStream {
public int getTokenno() { return tokenno; }
public boolean eof() { return in.eof(); }
public Comment getHeadComment() {
return headComment;
}
public void releaseComments() {
headComment = null;
lastComment = null;
}
// instance variables
private LineBuffer in;
@@ -1511,6 +1516,9 @@ public class TokenStream {
CodePosition tokenPosition;
CodePosition lastTokenPosition;
private Comment headComment;
private Comment lastComment;
private int op;
public boolean treatKeywordAsIdentifier;
+9
View File
@@ -47,6 +47,8 @@ message Expression {
optional bool synthetic = 3 [default = false];
optional SideEffects side_effects = 4 [default = AFFECTS_STATE];
optional JsImportedModule local_alias = 5;
repeated Comment before_comments = 6;
repeated Comment after_comments = 7;
oneof expression {
int32 simple_name_reference = 22;
@@ -128,6 +130,11 @@ message DocCommentTag {
}
}
message Comment {
required string text = 1;
required bool multiline = 2;
}
message BinaryOperation {
required Expression left = 1;
required Expression right = 2;
@@ -233,6 +240,8 @@ message Statement {
optional int32 fileId = 1;
optional Location location = 2;
optional bool synthetic = 3 [default = false];
repeated Comment before_comments = 4;
repeated Comment after_comments = 5;
oneof statement {
Return return_statement = 21;
@@ -28,7 +28,19 @@ abstract class JsAstDeserializerBase {
return statement
}
protected fun deserializeNoMetadata(proto: JsAstProtoBuf.Statement): JsStatement = when (proto.statementCase) {
protected fun deserializeNoMetadata(proto: JsAstProtoBuf.Statement): JsStatement {
return deserializeNoMetadataHelper(proto).apply {
if (proto.beforeCommentsCount != 0) {
commentsBeforeNode = proto.beforeCommentsList.map(::deserializeComment)
}
if (proto.afterCommentsCount != 0) {
commentsAfterNode = proto.afterCommentsList.map(::deserializeComment)
}
}
}
protected fun deserializeNoMetadataHelper(proto: JsAstProtoBuf.Statement): JsStatement = when (proto.statementCase) {
JsAstProtoBuf.Statement.StatementCase.RETURN_STATEMENT -> {
val returnProto = proto.returnStatement
JsReturn(if (returnProto.hasValue()) deserialize(returnProto.value) else null)
@@ -192,7 +204,19 @@ abstract class JsAstDeserializerBase {
)
}
protected fun deserializeNoMetadata(proto: JsAstProtoBuf.Expression): JsExpression = when (proto.expressionCase) {
protected fun deserializeNoMetadata(proto: JsAstProtoBuf.Expression): JsExpression {
return deserializeNoMetadataHelper(proto).apply {
if (proto.beforeCommentsCount != 0) {
commentsBeforeNode = proto.beforeCommentsList.map(::deserializeComment)
}
if (proto.afterCommentsCount != 0) {
commentsAfterNode = proto.afterCommentsList.map(::deserializeComment)
}
}
}
protected fun deserializeNoMetadataHelper(proto: JsAstProtoBuf.Expression): JsExpression = when (proto.expressionCase) {
JsAstProtoBuf.Expression.ExpressionCase.THIS_LITERAL -> JsThisRef()
JsAstProtoBuf.Expression.ExpressionCase.NULL_LITERAL -> JsNullLiteral()
JsAstProtoBuf.Expression.ExpressionCase.TRUE_LITERAL -> JsBooleanLiteral(true)
@@ -480,5 +504,13 @@ abstract class JsAstDeserializerBase {
return node
}
protected fun deserializeComment(comment: JsAstProtoBuf.Comment): JsComment {
return if (comment.multiline) {
JsMultiLineComment(comment.text)
} else {
JsSingleLineComment(comment.text)
}
}
protected abstract fun embedSources(deserializedLocation: JsLocation, file: String): JsLocationWithSource?
}
@@ -174,8 +174,10 @@ abstract class JsAstSerializerBase {
}
}
withLocation(statement, { visitor.builder.fileId = it }, { visitor.builder.location = it }) {
statement.accept(visitor)
withComments(statement, { visitor.builder.addBeforeComments(it) }, { visitor.builder.addAfterComments(it) }) {
withLocation(statement, { visitor.builder.fileId = it }, { visitor.builder.location = it }) {
statement.accept(visitor)
}
}
if (statement is HasMetadata && statement.synthetic) {
@@ -348,8 +350,10 @@ abstract class JsAstSerializerBase {
}
}
withLocation(expression, { visitor.builder.fileId = it }, { visitor.builder.location = it }) {
expression.accept(visitor)
withComments(expression, { visitor.builder.addBeforeComments(it) }, { visitor.builder.addAfterComments(it) }) {
withLocation(expression, { visitor.builder.fileId = it }, { visitor.builder.location = it }) {
expression.accept(visitor)
}
}
with(visitor.builder) {
@@ -522,6 +526,18 @@ abstract class JsAstSerializerBase {
result
}
protected fun serialize(comment: JsComment): JsAstProtoBuf.Comment {
val builder = JsAstProtoBuf.Comment.newBuilder().apply {
text = comment.text
multiline = when (comment) {
is JsSingleLineComment -> false
is JsMultiLineComment -> true
else -> error("Unknown type of comment ${comment.javaClass.name}")
}
}
return builder.build()
}
private inline fun withLocation(node: JsNode, fileConsumer: (Int) -> Unit, locationConsumer: (JsAstProtoBuf.Location) -> Unit, inner: () -> Unit) {
val location = extractLocation(node)
var fileChanged = false
@@ -546,5 +562,16 @@ abstract class JsAstSerializerBase {
}
}
private inline fun withComments(
node: JsNode,
beforeCommentsConsumer: (JsAstProtoBuf.Comment) -> Unit,
afterCommentsConsumer: (JsAstProtoBuf.Comment) -> Unit,
inner: () -> Unit
) {
node.commentsBeforeNode?.forEach { beforeCommentsConsumer(serialize(it))}
node.commentsAfterNode?.forEach { afterCommentsConsumer(serialize(it))}
inner()
}
abstract fun extractLocation(node: JsNode): JsLocation?
}
@@ -184,6 +184,10 @@ public class DirectiveTestUtils {
this.isElementExists = isElementExists;
}
protected boolean isElementExists() {
return isElementExists;
}
@Override
void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception {
loadArguments(arguments);
@@ -306,26 +310,41 @@ public class DirectiveTestUtils {
@Override
protected JsVisitor getJsVisitorForElement() {
return isMultiLine ? new RecursiveJsVisitor() {
return new RecursiveJsVisitor() {
@Override
public void visitMultiLineComment(@NotNull JsMultiLineComment comment) {
if (isTheSameText(comment.getText(), text)) {
setElementExists(true);
protected void visitElement(@NotNull JsNode node) {
checkCommentExistsIn(node.getCommentsBeforeNode());
checkCommentExistsIn(node.getCommentsAfterNode());
super.visitElement(node);
}
@Override
public void visitSingleLineComment(JsSingleLineComment comment) {
checkCommentExistsIn(Arrays.asList(comment));
}
@Override
public void visitMultiLineComment(JsMultiLineComment comment) {
checkCommentExistsIn(Arrays.asList(comment));
}
private void checkCommentExistsIn(List<JsComment> comments) {
if (comments == null) return;
for (JsComment comment : comments) {
if (isNeededCommentType(comment) && isTheSameText(comment.getText(), text)) {
setElementExists(true);
}
}
}
} : new RecursiveJsVisitor() {
@Override
public void visitSingleLineComment(@NotNull JsSingleLineComment comment) {
if (isTheSameText(comment.getText(), text)) {
setElementExists(true);
}
private boolean isNeededCommentType(JsComment comment) {
return isMultiLine ? comment instanceof JsMultiLineComment : comment instanceof JsSingleLineComment;
}
};
}
@Override
protected void loadArguments(@NotNull ArgumentsHelper arguments) {
this.text = arguments.findNamedArgument("text").replace("\\n", System.lineSeparator());
this.text = arguments.findNamedArgument("text").replace("\\n", System.lineSeparator());;
this.isMultiLine = Boolean.parseBoolean(arguments.findNamedArgument("multiline"));
}
@@ -338,44 +357,10 @@ public class DirectiveTestUtils {
for (int i = 0; i < lines1.size(); i++) {
if (!lines1.get(i).trim().equals(lines2.get(i).trim())) return false;
}
return true;
}
};
private static final DirectiveHandler CHECK_COMMENT_DOESNT_EXIST = new NodeExistenceDirective("CHECK_COMMENT_DOESNT_EXIST", false) {
private String text;
private boolean isMultiLine;
@Override
protected String getTextForError() {
return (isMultiLine ? "Multi line" : "Single line") + " comment with text '" + text + "' exists, but it should not";
}
@Override
protected JsVisitor getJsVisitorForElement() {
return isMultiLine ? new RecursiveJsVisitor() {
@Override
public void visitMultiLineComment(@NotNull JsMultiLineComment comment) {
if (comment.getText().trim() == text) {
setElementExists(true);
}
}
} : new RecursiveJsVisitor() {
@Override
public void visitSingleLineComment(@NotNull JsSingleLineComment comment) {
if (comment.getText().trim() == text) {
setElementExists(true);
}
}
};
}
@Override
protected void loadArguments(@NotNull ArgumentsHelper arguments) {
this.text = arguments.findNamedArgument("text");
this.isMultiLine = Boolean.parseBoolean(arguments.findNamedArgument("multiline"));
}
};
private static final DirectiveHandler ONLY_THIS_QUALIFIED_REFERENCES = new DirectiveHandler("ONLY_THIS_QUALIFIED_REFERENCES") {
@@ -496,7 +481,6 @@ public class DirectiveTestUtils {
FUNCTIONS_HAVE_SAME_LINES,
ONLY_THIS_QUALIFIED_REFERENCES,
CHECK_COMMENT_EXISTS,
CHECK_COMMENT_DOESNT_EXIST,
COUNT_LABELS,
COUNT_VARS,
COUNT_BREAKS,
+33 -5
View File
@@ -9,13 +9,28 @@
// CHECK_COMMENT_EXISTS: text="After call single line comment" multiline=false
// CHECK_COMMENT_EXISTS: text="After call multi line comment" multiline=true
// CHECK_COMMENT_EXISTS: text="The header multiline\ncomment" multiline=true
// CHECK_COMMENT_DOESNT_EXIST: text="random position comment 1" multiline=true
// CHECK_COMMENT_DOESNT_EXIST: text="random position comment 2" multiline=true
// CHECK_COMMENT_DOESNT_EXIST: text="random position comment 3" multiline=true
// CHECK_COMMENT_EXISTS: text="1Multi line comment\n" multiline=true
// CHECK_COMMENT_EXISTS: text="2Multi line comment\n\n\n" multiline=true
// CHECK_COMMENT_EXISTS: text="3Multi line\n\n\n\n\ncomment\n" multiline=true
// CHECK_COMMENT_EXISTS: text="" multiline=true
// CHECK_COMMENT_EXISTS: text="Multi line comment inside function" multiline=true
// CHECK_COMMENT_EXISTS: text="After call single line comment" multiline=false
// CHECK_COMMENT_EXISTS: text="After call multi line comment" multiline=true
// CHECK_COMMENT_EXISTS: text="Before argument 1" multiline=true
// CHECK_COMMENT_EXISTS: text="Before argument 2" multiline=true
// CHECK_COMMENT_EXISTS: text="After argument 1" multiline=true
// CHECK_COMMENT_EXISTS: text="After argument 2" multiline=true
// CHECK_COMMENT_EXISTS: text="object:" multiline=true
// CHECK_COMMENT_EXISTS: text="property:" multiline=true
// CHECK_COMMENT_EXISTS: text="descriptor:" multiline=true
// CHECK_COMMENT_EXISTS: text="Descriptor end" multiline=true
/*
* java.lang.AssertionError(Multi line comment with text 'The header multiline\ncomment' doesn't exist)
java.lang.AssertionError(Multi line comment with text '1Multi line comment\n' doesn't exist)
java.lang.AssertionError(Multi line comment with text '2Multi line comment\n\n\n' doesn't exist)
java.lang.AssertionError(Multi line comment with text '3Multi line\n\n\n\n\ncomment\n' doesn't exist)
* */
package foo
@@ -42,8 +57,6 @@ fun box(): String {
foo(); /* After call multi line comment */
var /*random position comment 1*/ c /*random position comment 2*/ = /*random position comment 3*/ "Random position";
/* 1Multi line comment
*/
foo();
@@ -63,6 +76,21 @@ fun box(): String {
/**/
foo();
foo(
/* Before argument 1 */
/* Before argument 2 */
4
/* After argument 1 */
/* After argument 2 */
);
var test = {
test: Object.defineProperty(/* object: */{}, /* property: */'some_property', /* descriptor: */ {
value: 42,
writable: false
} /* Descriptor end */)
}
""")
return "OK"
}
@@ -1,5 +1,4 @@
function test(n) {
/*synthetic*/
return n >= 0 ? n : -n;
}