This repository has been archived on 2023-06-18. You can view files and clone it, but cannot push or open issues or pull requests.
ima02/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/parsing/ParserTest.java

1096 lines
34 KiB
Java
Raw Permalink Normal View History

2023-04-25 11:33:41 +00:00
/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp.parsing;
import com.google.common.collect.ImmutableList;
import com.google.javascript.jscomp.parsing.Config.LanguageMode;
import com.google.javascript.jscomp.testing.TestErrorReporter;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.head.ScriptRuntime;
import com.google.javascript.rhino.jstype.SimpleSourceFile;
import com.google.javascript.rhino.jstype.StaticSourceFile;
import com.google.javascript.rhino.testing.BaseJSTypeTestCase;
import java.io.IOException;
import java.util.List;
import java.util.logging.Logger;
public class ParserTest extends BaseJSTypeTestCase {
private static final String SUSPICIOUS_COMMENT_WARNING =
IRFactory.SUSPICIOUS_COMMENT_WARNING;
private static final String TRAILING_COMMA_MESSAGE =
ScriptRuntime.getMessage0("msg.extra.trailing.comma");
private static final String BAD_PROPERTY_MESSAGE =
ScriptRuntime.getMessage0("msg.bad.prop");
private static final String MISSING_GT_MESSAGE =
"Bad type annotation. " +
com.google.javascript.rhino.ScriptRuntime.getMessage0(
"msg.jsdoc.missing.gt");
private static final String MISPLACED_TYPE_ANNOTATION =
IRFactory.MISPLACED_TYPE_ANNOTATION;
private Config.LanguageMode mode;
private boolean isIdeMode = false;
@Override
protected void setUp() throws Exception {
super.setUp();
mode = LanguageMode.ECMASCRIPT3;
isIdeMode = false;
}
public void testLinenoCharnoAssign1() throws Exception {
Node assign = parse("a = b").getFirstChild().getFirstChild();
assertEquals(Token.ASSIGN, assign.getType());
assertEquals(1, assign.getLineno());
assertEquals(0, assign.getCharno());
}
public void testLinenoCharnoAssign2() throws Exception {
Node assign = parse("\n a.g.h.k = 45").getFirstChild().getFirstChild();
assertEquals(Token.ASSIGN, assign.getType());
assertEquals(2, assign.getLineno());
assertEquals(1, assign.getCharno());
}
public void testLinenoCharnoCall() throws Exception {
Node call = parse("\n foo(123);").getFirstChild().getFirstChild();
assertEquals(Token.CALL, call.getType());
assertEquals(2, call.getLineno());
assertEquals(1, call.getCharno());
}
public void testLinenoCharnoGetProp1() throws Exception {
Node getprop = parse("\n foo.bar").getFirstChild().getFirstChild();
assertEquals(Token.GETPROP, getprop.getType());
assertEquals(2, getprop.getLineno());
assertEquals(1, getprop.getCharno());
Node name = getprop.getFirstChild().getNext();
assertEquals(Token.STRING, name.getType());
assertEquals(2, name.getLineno());
assertEquals(5, name.getCharno());
}
public void testLinenoCharnoGetProp2() throws Exception {
Node getprop = parse("\n foo.\nbar").getFirstChild().getFirstChild();
assertEquals(Token.GETPROP, getprop.getType());
assertEquals(2, getprop.getLineno());
assertEquals(1, getprop.getCharno());
Node name = getprop.getFirstChild().getNext();
assertEquals(Token.STRING, name.getType());
assertEquals(3, name.getLineno());
assertEquals(0, name.getCharno());
}
public void testLinenoCharnoGetelem1() throws Exception {
Node call = parse("\n foo[123]").getFirstChild().getFirstChild();
assertEquals(Token.GETELEM, call.getType());
assertEquals(2, call.getLineno());
assertEquals(1, call.getCharno());
}
public void testLinenoCharnoGetelem2() throws Exception {
Node call = parse("\n \n foo()[123]").getFirstChild().getFirstChild();
assertEquals(Token.GETELEM, call.getType());
assertEquals(3, call.getLineno());
assertEquals(1, call.getCharno());
}
public void testLinenoCharnoGetelem3() throws Exception {
Node call = parse("\n \n (8 + kl)[123]").getFirstChild().getFirstChild();
assertEquals(Token.GETELEM, call.getType());
assertEquals(3, call.getLineno());
assertEquals(2, call.getCharno());
}
public void testLinenoCharnoForComparison() throws Exception {
Node lt =
parse("for (; i < j;){}").getFirstChild().getFirstChild().getNext();
assertEquals(Token.LT, lt.getType());
assertEquals(1, lt.getLineno());
assertEquals(7, lt.getCharno());
}
public void testLinenoCharnoHook() throws Exception {
Node n = parse("\n a ? 9 : 0").getFirstChild().getFirstChild();
assertEquals(Token.HOOK, n.getType());
assertEquals(2, n.getLineno());
assertEquals(1, n.getCharno());
}
public void testLinenoCharnoArrayLiteral() throws Exception {
Node n = parse("\n [8, 9]").getFirstChild().getFirstChild();
assertEquals(Token.ARRAYLIT, n.getType());
assertEquals(2, n.getLineno());
assertEquals(2, n.getCharno());
n = n.getFirstChild();
assertEquals(Token.NUMBER, n.getType());
assertEquals(2, n.getLineno());
assertEquals(3, n.getCharno());
n = n.getNext();
assertEquals(Token.NUMBER, n.getType());
assertEquals(2, n.getLineno());
assertEquals(6, n.getCharno());
}
public void testLinenoCharnoObjectLiteral() throws Exception {
Node n = parse("\n\n var a = {a:0\n,b :1};")
.getFirstChild().getFirstChild().getFirstChild();
assertEquals(Token.OBJECTLIT, n.getType());
assertEquals(3, n.getLineno());
assertEquals(9, n.getCharno());
Node key = n.getFirstChild();
assertEquals(Token.STRING_KEY, key.getType());
assertEquals(3, key.getLineno());
assertEquals(10, key.getCharno());
Node value = key.getFirstChild();
assertEquals(Token.NUMBER, value.getType());
assertEquals(3, value.getLineno());
assertEquals(12, value.getCharno());
key = key.getNext();
assertEquals(Token.STRING_KEY, key.getType());
assertEquals(4, key.getLineno());
assertEquals(1, key.getCharno());
value = key.getFirstChild();
assertEquals(Token.NUMBER, value.getType());
assertEquals(4, value.getLineno());
assertEquals(4, value.getCharno());
}
public void testLinenoCharnoAdd() throws Exception {
testLinenoCharnoBinop("+");
}
public void testLinenoCharnoSub() throws Exception {
testLinenoCharnoBinop("-");
}
public void testLinenoCharnoMul() throws Exception {
testLinenoCharnoBinop("*");
}
public void testLinenoCharnoDiv() throws Exception {
testLinenoCharnoBinop("/");
}
public void testLinenoCharnoMod() throws Exception {
testLinenoCharnoBinop("%");
}
public void testLinenoCharnoShift() throws Exception {
testLinenoCharnoBinop("<<");
}
public void testLinenoCharnoBinaryAnd() throws Exception {
testLinenoCharnoBinop("&");
}
public void testLinenoCharnoAnd() throws Exception {
testLinenoCharnoBinop("&&");
}
public void testLinenoCharnoBinaryOr() throws Exception {
testLinenoCharnoBinop("|");
}
public void testLinenoCharnoOr() throws Exception {
testLinenoCharnoBinop("||");
}
public void testLinenoCharnoLt() throws Exception {
testLinenoCharnoBinop("<");
}
public void testLinenoCharnoLe() throws Exception {
testLinenoCharnoBinop("<=");
}
public void testLinenoCharnoGt() throws Exception {
testLinenoCharnoBinop(">");
}
public void testLinenoCharnoGe() throws Exception {
testLinenoCharnoBinop(">=");
}
private void testLinenoCharnoBinop(String binop) {
Node op = parse("var a = 89 " + binop + " 76").getFirstChild().
getFirstChild().getFirstChild();
assertEquals(1, op.getLineno());
assertEquals(8, op.getCharno());
}
public void testJSDocAttachment1() {
Node varNode = parse("/** @type number */var a;").getFirstChild();
// VAR
assertEquals(Token.VAR, varNode.getType());
JSDocInfo info = varNode.getJSDocInfo();
assertNotNull(info);
assertTypeEquals(NUMBER_TYPE, info.getType());
// NAME
Node nameNode = varNode.getFirstChild();
assertEquals(Token.NAME, nameNode.getType());
assertNull(nameNode.getJSDocInfo());
}
public void testJSDocAttachment2() {
Node varNode = parse("/** @type number */var a,b;").getFirstChild();
// VAR
assertEquals(Token.VAR, varNode.getType());
JSDocInfo info = varNode.getJSDocInfo();
assertNotNull(info);
assertTypeEquals(NUMBER_TYPE, info.getType());
// First NAME
Node nameNode1 = varNode.getFirstChild();
assertEquals(Token.NAME, nameNode1.getType());
assertNull(nameNode1.getJSDocInfo());
// Second NAME
Node nameNode2 = nameNode1.getNext();
assertEquals(Token.NAME, nameNode2.getType());
assertNull(nameNode2.getJSDocInfo());
}
public void testJSDocAttachment3() {
Node assignNode = parse(
"/** @type number */goog.FOO = 5;").getFirstChild().getFirstChild();
// ASSIGN
assertEquals(Token.ASSIGN, assignNode.getType());
JSDocInfo info = assignNode.getJSDocInfo();
assertNotNull(info);
assertTypeEquals(NUMBER_TYPE, info.getType());
}
public void testJSDocAttachment4() {
Node varNode = parse(
"var a, /** @define {number} */b = 5;").getFirstChild();
// ASSIGN
assertEquals(Token.VAR, varNode.getType());
assertNull(varNode.getJSDocInfo());
// a
Node a = varNode.getFirstChild();
assertNull(a.getJSDocInfo());
// b
Node b = a.getNext();
JSDocInfo info = b.getJSDocInfo();
assertNotNull(info);
assertTrue(info.isDefine());
assertTypeEquals(NUMBER_TYPE, info.getType());
}
public void testJSDocAttachment5() {
Node varNode = parse(
"var /** @type number */a, /** @define {number} */b = 5;")
.getFirstChild();
// ASSIGN
assertEquals(Token.VAR, varNode.getType());
assertNull(varNode.getJSDocInfo());
// a
Node a = varNode.getFirstChild();
assertNotNull(a.getJSDocInfo());
JSDocInfo info = a.getJSDocInfo();
assertNotNull(info);
assertFalse(info.isDefine());
assertTypeEquals(NUMBER_TYPE, info.getType());
// b
Node b = a.getNext();
info = b.getJSDocInfo();
assertNotNull(info);
assertTrue(info.isDefine());
assertTypeEquals(NUMBER_TYPE, info.getType());
}
/**
* Tests that a JSDoc comment in an unexpected place of the code does not
* propagate to following code due to {@link JSDocInfo} aggregation.
*/
public void testJSDocAttachment6() throws Exception {
Node functionNode = parse(
"var a = /** @param {number} index */5;" +
"/** @return boolean */function f(index){}")
.getFirstChild().getNext();
assertEquals(Token.FUNCTION, functionNode.getType());
JSDocInfo info = functionNode.getJSDocInfo();
assertNotNull(info);
assertFalse(info.hasParameter("index"));
assertTrue(info.hasReturnType());
assertTypeEquals(UNKNOWN_TYPE, info.getReturnType());
}
public void testJSDocAttachment7() {
Node varNode = parse("/** */var a;").getFirstChild();
// VAR
assertEquals(Token.VAR, varNode.getType());
// NAME
Node nameNode = varNode.getFirstChild();
assertEquals(Token.NAME, nameNode.getType());
assertNull(nameNode.getJSDocInfo());
}
public void testJSDocAttachment8() {
Node varNode = parse("/** x */var a;").getFirstChild();
// VAR
assertEquals(Token.VAR, varNode.getType());
// NAME
Node nameNode = varNode.getFirstChild();
assertEquals(Token.NAME, nameNode.getType());
assertNull(nameNode.getJSDocInfo());
}
public void testJSDocAttachment9() {
Node varNode = parse("/** \n x */var a;").getFirstChild();
// VAR
assertEquals(Token.VAR, varNode.getType());
// NAME
Node nameNode = varNode.getFirstChild();
assertEquals(Token.NAME, nameNode.getType());
assertNull(nameNode.getJSDocInfo());
}
public void testJSDocAttachment10() {
Node varNode = parse("/** x\n */var a;").getFirstChild();
// VAR
assertEquals(Token.VAR, varNode.getType());
// NAME
Node nameNode = varNode.getFirstChild();
assertEquals(Token.NAME, nameNode.getType());
assertNull(nameNode.getJSDocInfo());
}
public void testJSDocAttachment11() {
Node varNode =
parse("/** @type {{x : number, 'y' : string, z}} */var a;")
.getFirstChild();
// VAR
assertEquals(Token.VAR, varNode.getType());
JSDocInfo info = varNode.getJSDocInfo();
assertNotNull(info);
assertTypeEquals(createRecordTypeBuilder().
addProperty("x", NUMBER_TYPE, null).
addProperty("y", STRING_TYPE, null).
addProperty("z", UNKNOWN_TYPE, null).
build(),
info.getType());
// NAME
Node nameNode = varNode.getFirstChild();
assertEquals(Token.NAME, nameNode.getType());
assertNull(nameNode.getJSDocInfo());
}
public void testJSDocAttachment12() {
Node varNode =
parse("var a = {/** @type {Object} */ b: c};")
.getFirstChild();
Node objectLitNode = varNode.getFirstChild().getFirstChild();
assertEquals(Token.OBJECTLIT, objectLitNode.getType());
assertNotNull(objectLitNode.getFirstChild().getJSDocInfo());
}
public void testJSDocAttachment13() {
Node varNode = parse("/** foo */ var a;").getFirstChild();
assertNotNull(varNode.getJSDocInfo());
}
public void testJSDocAttachment14() {
Node varNode = parse("/** */ var a;").getFirstChild();
assertNull(varNode.getJSDocInfo());
}
public void testJSDocAttachment15() {
Node varNode = parse("/** \n * \n */ var a;").getFirstChild();
assertNull(varNode.getJSDocInfo());
}
public void testJSDocAttachment16() {
Node exprCall =
parse("/** @private */ x(); function f() {};").getFirstChild();
assertEquals(Token.EXPR_RESULT, exprCall.getType());
assertNull(exprCall.getNext().getJSDocInfo());
assertNotNull(exprCall.getFirstChild().getJSDocInfo());
}
public void testIncorrectJSDocDoesNotAlterJSParsing1() throws Exception {
assertNodeEquality(
parse("var a = [1,2]"),
parse("/** @type Array.<number*/var a = [1,2]",
MISSING_GT_MESSAGE));
}
public void testIncorrectJSDocDoesNotAlterJSParsing2() throws Exception {
assertNodeEquality(
parse("var a = [1,2]"),
parse("/** @type {Array.<number}*/var a = [1,2]",
MISSING_GT_MESSAGE));
}
public void testIncorrectJSDocDoesNotAlterJSParsing3() throws Exception {
assertNodeEquality(
parse("C.prototype.say=function(nums) {alert(nums.join(','));};"),
parse("/** @param {Array.<number} nums */" +
"C.prototype.say=function(nums) {alert(nums.join(','));};",
MISSING_GT_MESSAGE));
}
public void testIncorrectJSDocDoesNotAlterJSParsing4() throws Exception {
assertNodeEquality(
parse("C.prototype.say=function(nums) {alert(nums.join(','));};"),
parse("/** @return boolean */" +
"C.prototype.say=function(nums) {alert(nums.join(','));};"));
}
public void testIncorrectJSDocDoesNotAlterJSParsing5() throws Exception {
assertNodeEquality(
parse("C.prototype.say=function(nums) {alert(nums.join(','));};"),
parse("/** @param boolean this is some string*/" +
"C.prototype.say=function(nums) {alert(nums.join(','));};"));
}
public void testIncorrectJSDocDoesNotAlterJSParsing6() throws Exception {
assertNodeEquality(
parse("C.prototype.say=function(nums) {alert(nums.join(','));};"),
parse("/** @param {bool!*%E$} */" +
"C.prototype.say=function(nums) {alert(nums.join(','));};",
"Bad type annotation. expected closing }",
"Bad type annotation. expecting a variable name in a @param tag"));
}
public void testIncorrectJSDocDoesNotAlterJSParsing7() throws Exception {
assertNodeEquality(
parse("C.prototype.say=function(nums) {alert(nums.join(','));};"),
parse("/** @see */" +
"C.prototype.say=function(nums) {alert(nums.join(','));};",
"@see tag missing description"));
}
public void testIncorrectJSDocDoesNotAlterJSParsing8() throws Exception {
assertNodeEquality(
parse("C.prototype.say=function(nums) {alert(nums.join(','));};"),
parse("/** @author */" +
"C.prototype.say=function(nums) {alert(nums.join(','));};",
"@author tag missing author"));
}
public void testIncorrectJSDocDoesNotAlterJSParsing9() throws Exception {
assertNodeEquality(
parse("C.prototype.say=function(nums) {alert(nums.join(','));};"),
parse("/** @someillegaltag */" +
"C.prototype.say=function(nums) {alert(nums.join(','));};",
"illegal use of unknown JSDoc tag \"someillegaltag\";"
+ " ignoring it"));
}
public void testUnescapedSlashInRegexpCharClass() throws Exception {
// The tokenizer without the fix for this bug throws an error.
parse("var foo = /[/]/;");
parse("var foo = /[hi there/]/;");
parse("var foo = /[/yo dude]/;");
parse("var foo = /\\/[@#$/watashi/wa/suteevu/desu]/;");
}
private void assertNodeEquality(Node expected, Node found) {
String message = expected.checkTreeEquals(found);
if (message != null) {
fail(message);
}
}
@SuppressWarnings("unchecked")
public void testParse() {
Node a = Node.newString(Token.NAME, "a");
a.addChildToFront(Node.newString(Token.NAME, "b"));
List<ParserResult> testCases = ImmutableList.of(
new ParserResult(
"3;",
createScript(new Node(Token.EXPR_RESULT, Node.newNumber(3.0)))),
new ParserResult(
"var a = b;",
createScript(new Node(Token.VAR, a))),
new ParserResult(
"\"hell\\\no\\ world\\\n\\\n!\"",
createScript(new Node(Token.EXPR_RESULT,
Node.newString(Token.STRING, "hello world!")))));
for (ParserResult testCase : testCases) {
assertNodeEquality(testCase.node, parse(testCase.code));
}
}
private Node createScript(Node n) {
Node script = new Node(Token.SCRIPT);
script.addChildToBack(n);
return script;
}
public void testTrailingCommaWarning1() {
parse("var a = ['foo', 'bar'];");
}
public void testTrailingCommaWarning2() {
parse("var a = ['foo',,'bar'];");
}
public void testTrailingCommaWarning3() {
parse("var a = ['foo', 'bar',];", TRAILING_COMMA_MESSAGE);
mode = LanguageMode.ECMASCRIPT5;
parse("var a = ['foo', 'bar',];");
}
public void testTrailingCommaWarning4() {
parse("var a = [,];", TRAILING_COMMA_MESSAGE);
mode = LanguageMode.ECMASCRIPT5;
parse("var a = [,];");
}
public void testTrailingCommaWarning5() {
parse("var a = {'foo': 'bar'};");
}
public void testTrailingCommaWarning6() {
parse("var a = {'foo': 'bar',};", TRAILING_COMMA_MESSAGE);
mode = LanguageMode.ECMASCRIPT5;
parse("var a = {'foo': 'bar',};");
}
public void testTrailingCommaWarning7() {
parseError("var a = {,};", BAD_PROPERTY_MESSAGE);
}
public void testSuspiciousBlockCommentWarning1() {
parse("/* @type {number} */ var x = 3;", SUSPICIOUS_COMMENT_WARNING);
}
public void testSuspiciousBlockCommentWarning2() {
parse("/* \n * @type {number} */ var x = 3;", SUSPICIOUS_COMMENT_WARNING);
}
public void testCatchClauseForbidden() {
parseError("try { } catch (e if true) {}",
"Catch clauses are not supported");
}
public void testConstForbidden() {
parseError("const x = 3;", "Unsupported syntax: CONST");
}
public void testDestructuringAssignForbidden() {
parseError("var [x, y] = foo();", "destructuring assignment forbidden");
}
public void testDestructuringAssignForbidden2() {
parseError("var {x, y} = foo();", "missing : after property id");
}
public void testDestructuringAssignForbidden3() {
parseError("var {x: x, y: y} = foo();",
"destructuring assignment forbidden");
}
public void testDestructuringAssignForbidden4() {
parseError("[x, y] = foo();",
"destructuring assignment forbidden",
"invalid assignment target");
}
public void testLetForbidden() {
parseError("function f() { let (x = 3) { alert(x); }; }",
"missing ; before statement", "syntax error");
}
public void testYieldForbidden() {
parseError("function f() { yield 3; }", "missing ; before statement");
}
public void testBracelessFunctionForbidden() {
parseError("var sq = function(x) x * x;",
"missing { before function body");
}
public void testGeneratorsForbidden() {
parseError("var i = (x for (x in obj));",
"Unsupported syntax: GENEXPR");
}
public void testGettersForbidden1() {
parseError("var x = {get foo() { return 3; }};",
IRFactory.GETTER_ERROR_MESSAGE);
}
public void testGettersForbidden2() {
parseError("var x = {get foo bar() { return 3; }};",
"invalid property id");
}
public void testGettersForbidden3() {
parseError("var x = {a getter:function b() { return 3; }};",
"missing : after property id", "syntax error");
}
public void testGettersForbidden4() {
parseError("var x = {\"a\" getter:function b() { return 3; }};",
"missing : after property id", "syntax error");
}
public void testGettersForbidden5() {
parseError("var x = {a: 2, get foo() { return 3; }};",
IRFactory.GETTER_ERROR_MESSAGE);
}
public void testSettersForbidden() {
parseError("var x = {set foo() { return 3; }};",
IRFactory.SETTER_ERROR_MESSAGE);
}
public void testSettersForbidden2() {
parseError("var x = {a setter:function b() { return 3; }};",
"missing : after property id", "syntax error");
}
public void testFileOverviewJSDoc1() {
Node n = parse("/** @fileoverview Hi mom! */ function Foo() {}");
assertEquals(Token.FUNCTION, n.getFirstChild().getType());
assertTrue(n.getJSDocInfo() != null);
assertNull(n.getFirstChild().getJSDocInfo());
assertEquals("Hi mom!",
n.getJSDocInfo().getFileOverview());
}
public void testFileOverviewJSDocDoesNotHoseParsing() {
assertEquals(
Token.FUNCTION,
parse("/** @fileoverview Hi mom! \n */ function Foo() {}")
.getFirstChild().getType());
assertEquals(
Token.FUNCTION,
parse("/** @fileoverview Hi mom! \n * * * */ function Foo() {}")
.getFirstChild().getType());
assertEquals(
Token.FUNCTION,
parse("/** @fileoverview \n * x */ function Foo() {}")
.getFirstChild().getType());
assertEquals(
Token.FUNCTION,
parse("/** @fileoverview \n * x \n */ function Foo() {}")
.getFirstChild().getType());
}
public void testFileOverviewJSDoc2() {
Node n = parse("/** @fileoverview Hi mom! */ " +
"/** @constructor */ function Foo() {}");
assertTrue(n.getJSDocInfo() != null);
assertEquals("Hi mom!", n.getJSDocInfo().getFileOverview());
assertTrue(n.getFirstChild().getJSDocInfo() != null);
assertFalse(n.getFirstChild().getJSDocInfo().hasFileOverview());
assertTrue(n.getFirstChild().getJSDocInfo().isConstructor());
}
public void testObjectLiteralDoc1() {
Node n = parse("var x = {/** @type {number} */ 1: 2};");
Node objectLit = n.getFirstChild().getFirstChild().getFirstChild();
assertEquals(Token.OBJECTLIT, objectLit.getType());
Node number = objectLit.getFirstChild();
assertEquals(Token.STRING_KEY, number.getType());
assertNotNull(number.getJSDocInfo());
}
public void testDuplicatedParam() {
parse("function foo(x, x) {}", "Duplicate parameter name \"x\".");
}
public void testGetter() {
mode = LanguageMode.ECMASCRIPT3;
parseError("var x = {get 1(){}};",
IRFactory.GETTER_ERROR_MESSAGE);
parseError("var x = {get 'a'(){}};",
IRFactory.GETTER_ERROR_MESSAGE);
parseError("var x = {get a(){}};",
IRFactory.GETTER_ERROR_MESSAGE);
mode = LanguageMode.ECMASCRIPT5;
parse("var x = {get 1(){}};");
parse("var x = {get 'a'(){}};");
parse("var x = {get a(){}};");
parseError("var x = {get a(b){}};", "getters may not have parameters");
}
public void testSetter() {
mode = LanguageMode.ECMASCRIPT3;
parseError("var x = {set 1(x){}};",
IRFactory.SETTER_ERROR_MESSAGE);
parseError("var x = {set 'a'(x){}};",
IRFactory.SETTER_ERROR_MESSAGE);
parseError("var x = {set a(x){}};",
IRFactory.SETTER_ERROR_MESSAGE);
mode = LanguageMode.ECMASCRIPT5;
parse("var x = {set 1(x){}};");
parse("var x = {set 'a'(x){}};");
parse("var x = {set a(x){}};");
parseError("var x = {set a(){}};",
"setters must have exactly one parameter");
}
public void testLamestWarningEver() {
// This used to be a warning.
parse("var x = /** @type {undefined} */ (y);");
parse("var x = /** @type {void} */ (y);");
}
public void testUnfinishedComment() {
parseError("/** this is a comment ", "unterminated comment");
}
public void testParseBlockDescription() {
Node n = parse("/** This is a variable. */ var x;");
Node var = n.getFirstChild();
assertNotNull(var.getJSDocInfo());
assertEquals("This is a variable.",
var.getJSDocInfo().getBlockDescription());
}
public void testUnnamedFunctionStatement() {
// Statements
parseError("function() {};", "unnamed function statement");
parseError("if (true) { function() {}; }", "unnamed function statement");
parse("function f() {};");
// Expressions
parse("(function f() {});");
parse("(function () {});");
}
public void testReservedKeywords() {
boolean isIdeMode = false;
mode = LanguageMode.ECMASCRIPT3;
parseError("var boolean;", "missing variable name");
parseError("function boolean() {};",
"missing ( before function parameters.");
parseError("boolean = 1;", "identifier is a reserved word");
parseError("class = 1;", "identifier is a reserved word");
parseError("public = 2;", "identifier is a reserved word");
mode = LanguageMode.ECMASCRIPT5;
parse("var boolean;");
parse("function boolean() {};");
parse("boolean = 1;");
parseError("class = 1;", "identifier is a reserved word");
parse("public = 2;");
mode = LanguageMode.ECMASCRIPT5_STRICT;
parse("var boolean;");
parse("function boolean() {};");
parse("boolean = 1;");
parseError("class = 1;", "identifier is a reserved word");
parseError("public = 2;", "identifier is a reserved word");
}
public void testKeywordsAsProperties() {
boolean isIdeMode = false;
mode = LanguageMode.ECMASCRIPT3;
parseError("var x = {function: 1};", "invalid property id");
parseError("x.function;", "missing name after . operator");
parseError("var x = {get x(){} };",
IRFactory.GETTER_ERROR_MESSAGE);
parseError("var x = {get function(){} };", "invalid property id");
parseError("var x = {get 'function'(){} };",
IRFactory.GETTER_ERROR_MESSAGE);
parseError("var x = {get 1(){} };",
IRFactory.GETTER_ERROR_MESSAGE);
parseError("var x = {set function(a){} };", "invalid property id");
parseError("var x = {set 'function'(a){} };",
IRFactory.SETTER_ERROR_MESSAGE);
parseError("var x = {set 1(a){} };",
IRFactory.SETTER_ERROR_MESSAGE);
parseError("var x = {class: 1};", "invalid property id");
parseError("x.class;", "missing name after . operator");
parse("var x = {let: 1};");
parse("x.let;");
parse("var x = {yield: 1};");
parse("x.yield;");
mode = LanguageMode.ECMASCRIPT5;
parse("var x = {function: 1};");
parse("x.function;");
parse("var x = {get function(){} };");
parse("var x = {get 'function'(){} };");
parse("var x = {get 1(){} };");
parse("var x = {set function(a){} };");
parse("var x = {set 'function'(a){} };");
parse("var x = {set 1(a){} };");
parse("var x = {class: 1};");
parse("x.class;");
parse("var x = {let: 1};");
parse("x.let;");
parse("var x = {yield: 1};");
parse("x.yield;");
mode = LanguageMode.ECMASCRIPT5_STRICT;
parse("var x = {function: 1};");
parse("x.function;");
parse("var x = {get function(){} };");
parse("var x = {get 'function'(){} };");
parse("var x = {get 1(){} };");
parse("var x = {set function(a){} };");
parse("var x = {set 'function'(a){} };");
parse("var x = {set 1(a){} };");
parse("var x = {class: 1};");
parse("x.class;");
parse("var x = {let: 1};");
parse("x.let;");
parse("var x = {yield: 1};");
parse("x.yield;");
}
public void testGetPropFunctionName() {
parseError("function a.b() {}",
"missing ( before function parameters.");
parseError("var x = function a.b() {}",
"missing ( before function parameters.");
}
public void testGetPropFunctionNameIdeMode() {
// In IDE mode, we try to fix up the tree, but sometimes
// this leads to even more errors.
isIdeMode = true;
parseError("function a.b() {}",
"missing ( before function parameters.",
"missing formal parameter",
"missing ) after formal parameters",
"missing { before function body",
"syntax error",
"missing ; before statement",
"missing ; before statement",
"missing } after function body",
"Unsupported syntax: ERROR",
"Unsupported syntax: ERROR");
parseError("var x = function a.b() {}",
"missing ( before function parameters.",
"missing formal parameter",
"missing ) after formal parameters",
"missing { before function body",
"syntax error",
"missing ; before statement",
"missing ; before statement",
"missing } after function body",
"Unsupported syntax: ERROR",
"Unsupported syntax: ERROR");
}
public void testIdeModePartialTree() {
Node partialTree = parseError("function Foo() {} f.",
"missing name after . operator");
assertNull(partialTree);
isIdeMode = true;
partialTree = parseError("function Foo() {} f.",
"missing name after . operator");
assertNotNull(partialTree);
}
public void testForEach() {
parseError(
"function f(stamp, status) {\n" +
" for each ( var curTiming in this.timeLog.timings ) {\n" +
" if ( curTiming.callId == stamp ) {\n" +
" curTiming.flag = status;\n" +
" break;\n" +
" }\n" +
" }\n" +
"};",
"unsupported language extension: for each");
}
public void testMisplacedTypeAnnotation1() {
// misuse with COMMA
parse(
"var o = {};" +
"/** @type {string} */ o.prop1 = 1, o.prop2 = 2;",
MISPLACED_TYPE_ANNOTATION);
}
public void testMisplacedTypeAnnotation2() {
// missing parenthese for the cast.
parse(
"var o = /** @type {string} */ getValue();",
MISPLACED_TYPE_ANNOTATION);
}
public void testMisplacedTypeAnnotation3() {
// missing parenthese for the cast.
parse(
"var o = 1 + /** @type {string} */ value;",
MISPLACED_TYPE_ANNOTATION);
}
public void testMisplacedTypeAnnotation4() {
// missing parenthese for the cast.
parse(
"var o = /** @type {!Array.<string>} */ ['hello', 'you'];",
MISPLACED_TYPE_ANNOTATION);
}
public void testMisplacedTypeAnnotation5() {
// missing parenthese for the cast.
parse(
"var o = (/** @type {!Foo} */ {});",
MISPLACED_TYPE_ANNOTATION);
}
public void testMisplacedTypeAnnotation6() {
parse("var o = /** @type {function():string} */ function() {return 'str';}",
MISPLACED_TYPE_ANNOTATION);
}
public void testValidTypeAnnotation1() {
parse("/** @type {string} */ var o = 'str';");
parse("var /** @type {string} */ o = 'str', /** @type {number} */ p = 0;");
parse("/** @type {function():string} */ function o() { return 'str'; }");
parse("var o = {}; /** @type {string} */ o.prop = 'str';");
parse("var o = {}; /** @type {string} */ o['prop'] = 'str';");
parse("var o = { /** @type {string} */ prop : 'str' };");
parse("var o = { /** @type {string} */ 'prop' : 'str' };");
parse("var o = { /** @type {string} */ 1 : 'str' };");
}
public void testValidTypeAnnotation2() {
mode = LanguageMode.ECMASCRIPT5;
parse("var o = { /** @type {string} */ get prop() { return 'str' }};");
parse("var o = { /** @type {string} */ set prop(s) {}};");
}
public void testValidTypeAnnotation3() {
// These two we don't currently support in the type checker but
// we would like to.
parse("try {} catch (/** @type {Error} */ e) {}");
parse("function f(/** @type {string} */ a) {}");
}
/**
* Verify that the given code has the given parse errors.
* @return If in IDE mode, returns a partial tree.
*/
private Node parseError(String string, String... errors) {
TestErrorReporter testErrorReporter = new TestErrorReporter(errors, null);
Node script = null;
try {
StaticSourceFile file = new SimpleSourceFile("input", false);
script = ParserRunner.parse(
file, string, ParserRunner.createConfig(isIdeMode, mode, false),
testErrorReporter, Logger.getAnonymousLogger()).ast;
} catch (IOException e) {
throw new RuntimeException(e);
}
// verifying that all warnings were seen
assertTrue(testErrorReporter.hasEncounteredAllErrors());
assertTrue(testErrorReporter.hasEncounteredAllWarnings());
return script;
}
private Node parse(String string, String... warnings) {
TestErrorReporter testErrorReporter = new TestErrorReporter(null, warnings);
Node script = null;
try {
StaticSourceFile file = new SimpleSourceFile("input", false);
script = ParserRunner.parse(
file, string, ParserRunner.createConfig(true, mode, false),
testErrorReporter, Logger.getAnonymousLogger()).ast;
} catch (IOException e) {
throw new RuntimeException(e);
}
// verifying that all warnings were seen
assertTrue(testErrorReporter.hasEncounteredAllErrors());
assertTrue(testErrorReporter.hasEncounteredAllWarnings());
return script;
}
private static class ParserResult {
private final String code;
private final Node node;
private ParserResult(String code, Node node) {
this.code = code;
this.node = node;
}
}
}