Skip to content

Commit

Permalink
Update parser to record use of GETTER and SETTER features.
Browse files Browse the repository at this point in the history
Tests are added for the change as well as for parsing getters and setters within ES6 classes.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=204375343
  • Loading branch information
nreid260 authored and lauraharker committed Jul 12, 2018
1 parent 871bd70 commit c7d2139
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 49 deletions.
25 changes: 17 additions & 8 deletions src/com/google/javascript/jscomp/parsing/parser/Parser.java
Expand Up @@ -898,7 +898,7 @@ private ParseTree parseClassMemberDeclaration(PartialClassElement partial) {
nameExpr = null;
name = eatIdOrKeywordAsId();
if (Keywords.isKeyword(name.value, /* includeTypeScriptKeywords= */ false)) {
features = features.with(Feature.KEYWORDS_AS_PROPERTIES);
recordFeatureUsed(Feature.KEYWORDS_AS_PROPERTIES);
}
} else {
// { 'str'() {} }
Expand Down Expand Up @@ -1406,7 +1406,7 @@ private FormalParameterListTree parseFormalParameterList(ParamContext context) {
if (!peek(TokenType.CLOSE_PAREN)) {
Token comma = eat(TokenType.COMMA);
if (peek(TokenType.CLOSE_PAREN)) {
features = features.with(Feature.TRAILING_COMMA_IN_PARAM_LIST);
recordFeatureUsed(Feature.TRAILING_COMMA_IN_PARAM_LIST);
if (!config.atLeast8) {
reportError(comma, "Invalid trailing comma in formal parameter list");
}
Expand Down Expand Up @@ -2477,7 +2477,7 @@ private ParseTree parseArrayLiteral() {
elements.add(new NullTree(getTreeLocation(getTreeStartLocation())));
} else {
if (peek(TokenType.SPREAD)) {
features = features.with(Feature.SPREAD_EXPRESSIONS);
recordFeatureUsed(Feature.SPREAD_EXPRESSIONS);
elements.add(parseSpreadExpression());
} else {
elements.add(parseAssignmentExpression());
Expand Down Expand Up @@ -2522,7 +2522,7 @@ private ParseTree parseObjectLiteral() {

void maybeReportTrailingComma(Token commaToken) {
if (commaToken != null) {
features = features.with(Feature.TRAILING_COMMA);
recordFeatureUsed(Feature.TRAILING_COMMA);
if (config.warnTrailingCommas) {
// In ES3 mode warn about trailing commas which aren't accepted by
// older browsers (such as IE8).
Expand Down Expand Up @@ -2554,7 +2554,7 @@ private ParseTree parsePropertyAssignment() {
if (type == TokenType.STAR) {
return parsePropertyAssignmentGenerator();
} else if (peek(TokenType.SPREAD)) {
features = features.with(Feature.OBJECT_LITERALS_WITH_SPREAD);
recordFeatureUsed(Feature.OBJECT_LITERALS_WITH_SPREAD);
return parseSpreadExpression();
} else if (type == TokenType.STRING
|| type == TokenType.NUMBER
Expand Down Expand Up @@ -2657,6 +2657,7 @@ private ParseTree parseGetAccessor(PartialClassElement partial) {
eat(TokenType.CLOSE_PAREN);
ParseTree returnType = maybeParseColonType();
BlockTree body = parseFunctionBody();
recordFeatureUsed(Feature.GETTER);
return new GetAccessorTree(
getTreeLocation(partial.start), propertyName, partial.isStatic, returnType, body);
} else {
Expand All @@ -2665,6 +2666,7 @@ private ParseTree parseGetAccessor(PartialClassElement partial) {
eat(TokenType.CLOSE_PAREN);
ParseTree returnType = maybeParseColonType();
BlockTree body = parseFunctionBody();
recordFeatureUsed(Feature.GETTER);
return new ComputedPropertyGetterTree(
getTreeLocation(partial.start),
property,
Expand Down Expand Up @@ -2696,13 +2698,15 @@ private ParseTree parseSetAccessor(PartialClassElement partial) {

BlockTree body = parseFunctionBody();

recordFeatureUsed(Feature.SETTER);
return new SetAccessorTree(
getTreeLocation(partial.start), propertyName, partial.isStatic, parameter, body);
} else {
ParseTree property = parseComputedPropertyName();
FormalParameterListTree parameter = parseSetterParameterList();
BlockTree body = parseFunctionBody();

recordFeatureUsed(Feature.SETTER);
return new ComputedPropertySetterTree(
getTreeLocation(partial.start),
property,
Expand Down Expand Up @@ -3599,7 +3603,7 @@ private ArgumentListTree parseArguments() {
if (!peek(TokenType.CLOSE_PAREN)) {
Token comma = eat(TokenType.COMMA);
if (peek(TokenType.CLOSE_PAREN)) {
features = features.with(Feature.TRAILING_COMMA_IN_PARAM_LIST);
recordFeatureUsed(Feature.TRAILING_COMMA_IN_PARAM_LIST);
if (!config.atLeast8) {
reportError(comma, "Invalid trailing comma in arguments list");
}
Expand Down Expand Up @@ -3694,7 +3698,7 @@ private ParseTree parseArrayPattern(PatternKind kind) {
}
}
if (peek(TokenType.SPREAD)) {
features = features.with(Feature.ARRAY_PATTERN_REST);
recordFeatureUsed(Feature.ARRAY_PATTERN_REST);
elements.add(parsePatternRest(kind));
}
eat(TokenType.CLOSE_SQUARE);
Expand All @@ -3718,7 +3722,7 @@ private ParseTree parseObjectPattern(PatternKind kind) {
}
}
if (peek(TokenType.SPREAD)) {
features = features.with(Feature.OBJECT_PATTERN_REST);
recordFeatureUsed(Feature.OBJECT_PATTERN_REST);
fields.add(parsePatternRest(kind));
}
eat(TokenType.CLOSE_CURLY);
Expand Down Expand Up @@ -4204,4 +4208,9 @@ private void reportError(ParseTree parseTree, @FormatString String message, Obje
private void reportError(@FormatString String message, Object... arguments) {
errorReporter.reportError(scanner.getPosition(), message, arguments);
}

private Parser recordFeatureUsed(Feature feature) {
features = features.with(feature);
return this;
}
}
205 changes: 164 additions & 41 deletions test/com/google/javascript/jscomp/parsing/ParserTest.java
Expand Up @@ -2799,44 +2799,117 @@ public void testInvalidOldStyleOctalLiterals() {
"Invalid octal digit in octal literal.");
}

public void testGetter() {
public void testGetter_ObjectLiteral_Es3() {
expectFeatures(Feature.GETTER);
mode = LanguageMode.ECMASCRIPT3;
strictMode = SLOPPY;
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);

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

public void testGetter_ObjectLiteral_Es5() {
expectFeatures(Feature.GETTER);
mode = LanguageMode.ECMASCRIPT5;
strictMode = SLOPPY;

parse("var x = {get 1(){}};");
parse("var x = {get 'a'(){}};");
parse("var x = {get a(){}};");
}

public void testGetterInvalid_ObjectLiteral_EsNext() {
expectFeatures();
mode = LanguageMode.ES_NEXT;
strictMode = SLOPPY;

parseError("var x = {get a(b){}};", "')' expected");
}

public void testSetter() {
public void testGetter_Computed_ObjectLiteral_Es6() {
expectFeatures(Feature.GETTER, Feature.COMPUTED_PROPERTIES);
mode = LanguageMode.ECMASCRIPT6;
strictMode = SLOPPY;

parse("var x = {get [1](){}};");
parse("var x = {get ['a'](){}};");
parse("var x = {get [a](){}};");
}

public void testGetterInvalid_Computed_ObjectLiteral_EsNext() {
expectFeatures();
mode = LanguageMode.ES_NEXT;
strictMode = SLOPPY;

parseError("var x = {get [a](b){}};", "')' expected");
}

public void testGetter_ClassSyntax() {
expectFeatures(Feature.CLASSES, Feature.GETTER);
mode = LanguageMode.ECMASCRIPT6;
strictMode = SLOPPY;

parse("class Foo { get 1() {} };");
parse("class Foo { get 'a'() {} };");
parse("class Foo { get a() {} };");
}

public void testGetterInvalid_ClassSyntax_EsNext() {
expectFeatures();
mode = LanguageMode.ES_NEXT;
strictMode = SLOPPY;

parseError("class Foo { get a(b) {} };", "')' expected");
}

public void testGetter_Computed_ClassSyntax() {
expectFeatures(Feature.CLASSES, Feature.GETTER, Feature.COMPUTED_PROPERTIES);
mode = LanguageMode.ECMASCRIPT6;
strictMode = SLOPPY;

parse("class Foo { get [1]() {} };");
parse("class Foo { get ['a']() {} };");
parse("class Foo { get [a]() {} };");
}

public void testGetterInvalid_Computed_ClassSyntax_EsNext() {
expectFeatures();
mode = LanguageMode.ES_NEXT;
strictMode = SLOPPY;

parseError("class Foo { get [a](b) {} };", "')' expected");
}

public void testSetter_ObjectLiteral_Es3() {
expectFeatures(Feature.SETTER);
mode = LanguageMode.ECMASCRIPT3;
strictMode = SLOPPY;

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

public void testSetter_ObjectLiteral_Es5() {
expectFeatures(Feature.SETTER);
mode = LanguageMode.ECMASCRIPT5;
strictMode = SLOPPY;

parse("var x = {set 1(x){}};");
parse("var x = {set 'a'(x){}};");
parse("var x = {set a(x){}};");
}

mode = LanguageMode.ECMASCRIPT6; // We only cover some of the common permutations though.
// We only cover some of the common permutations though.
public void testSetter_ObjectLiteral_Es6() {
expectFeatures(Feature.SETTER);
mode = LanguageMode.ECMASCRIPT6;
strictMode = SLOPPY;

parse("var x = {set 1(x){}};");
parse("var x = {set 'a'(x){}};");
Expand All @@ -2854,19 +2927,23 @@ public void testSetter() {
parse("var x = {set setter({x, y, z}) {}};");
parse("var x = {set setter({x, y, z} = {x: 1, y: 2, z: 3}) {}};");
parse("var x = {set setter({x = 1, y = 2, z = 3}) {}};");
}

expectFeatures(); // Because these snippets don't contain valid setters.
public void testSetterInvalid_ObjectLiteral_EsNext() {
expectFeatures();
mode = LanguageMode.ES_NEXT;
strictMode = SLOPPY;

parseError("var x = {set a() {}};", "Setter must have exactly 1 parameter, found 0");
parseError("var x = {set a(x, y) {}};", "Setter must have exactly 1 parameter, found 2");
parseError("var x = {set a(...x, y) {}};", "Setter must have exactly 1 parameter, found 2");

parseError("var x = {set a(...x) {}};", "Setter must not have a rest parameter");
}

public void testComputedSetter() {
expectFeatures(Feature.COMPUTED_PROPERTIES); // Because computed setters aren't setters.
mode = LanguageMode.ECMASCRIPT6; // We only cover some of the common permutations though.
// We only cover some of the common permutations though.
public void testSetter_Computed_ObjectLiteral_Es6() {
expectFeatures(Feature.SETTER, Feature.COMPUTED_PROPERTIES);
mode = LanguageMode.ECMASCRIPT6;
strictMode = SLOPPY;

parse("var x = {set [setter](x = 5) {}};");
Expand All @@ -2881,17 +2958,82 @@ public void testComputedSetter() {
parse("var x = {set [setter]({x, y, z}) {}};");
parse("var x = {set [setter]({x, y, z} = {x: 1, y: 2, z: 3}) {}};");
parse("var x = {set [setter]({x = 1, y = 2, z = 3}) {}};");
}

expectFeatures(); // Because these snippets don't contain valid computed properties.
// We only cover some of the common permutations though.
public void testSetterInvalid_Computed_ObjectLiteral_EsNext() {
expectFeatures();
mode = LanguageMode.ES_NEXT;
strictMode = SLOPPY;

parseError("var x = {set [setter]() {}};", "Setter must have exactly 1 parameter, found 0");
parseError("var x = {set [setter](x, y) {}};", "Setter must have exactly 1 parameter, found 2");
parseError(
"var x = {set [setter](...x, y) {}};", "Setter must have exactly 1 parameter, found 2");

parseError("var x = {set [setter](...x) {}};", "Setter must not have a rest parameter");
}

public void testSetter_ClassSyntax() {
expectFeatures(Feature.CLASSES, Feature.SETTER);
mode = LanguageMode.ECMASCRIPT6; // We only cover some of the common permutations though.

parse("class Foo { set setter(x = 5) {} };");
parse("class Foo { set setter(x = a) {} };");
parse("class Foo { set setter(x = a + 5) {} };");

parse("class Foo { set setter([x, y, z]) {} };");
parse("class Foo { set setter([x, y, ...z]) {}};");
parse("class Foo { set setter([x, y, z] = [1, 2, 3]) {} };");
parse("class Foo { set setter([x = 1, y = 2, z = 3]) {} };");

parse("class Foo { set setter({x, y, z}) {}};");
parse("class Foo { set setter({x, y, z} = {x: 1, y: 2, z: 3}) {} };");
parse("class Foo { set setter({x = 1, y = 2, z = 3}) {} };");
}

public void testSetterInvalid_ClassSyntax_EsNext() {
expectFeatures();
mode = LanguageMode.ES_NEXT;

parseError("class Foo { set setter() {} };", "Setter must have exactly 1 parameter, found 0");
parseError(
"class Foo { set setter(x, y) {} };", "Setter must have exactly 1 parameter, found 2");
parseError(
"class Foo { set setter(...x, y) {} };", "Setter must have exactly 1 parameter, found 2");
parseError("class Foo { set setter(...x) {} };", "Setter must not have a rest parameter");
}

// We only cover some of the common permutations though.
public void testSetter_Computed_ClassSyntax() {
expectFeatures(Feature.CLASSES, Feature.SETTER, Feature.COMPUTED_PROPERTIES);
mode = LanguageMode.ECMASCRIPT6;

parse("class Foo { set [setter](x = 5) {} };");
parse("class Foo { set [setter](x = a) {} };");
parse("class Foo { set [setter](x = a + 5) {} };");

parse("class Foo { set [setter]([x, y, z]) {} };");
parse("class Foo { set [setter]([x, y, ...z]) {}};");
parse("class Foo { set [setter]([x, y, z] = [1, 2, 3]) {} };");
parse("class Foo { set [setter]([x = 1, y = 2, z = 3]) {} };");

parse("class Foo { set [setter]({x, y, z}) {}};");
parse("class Foo { set [setter]({x, y, z} = {x: 1, y: 2, z: 3}) {} };");
parse("class Foo { set [setter]({x = 1, y = 2, z = 3}) {} };");
}

public void testSetterInvalid_Computed_ClassSyntax_EsNext() {
expectFeatures();
mode = LanguageMode.ES_NEXT;

parseError("class Foo { set [setter]() {} };", "Setter must have exactly 1 parameter, found 0");
parseError(
"class Foo { set [setter](x, y) {} };", "Setter must have exactly 1 parameter, found 2");
parseError(
"class Foo { set [setter](...x, y) {} };", "Setter must have exactly 1 parameter, found 2");
parseError("class Foo { set [setter](...x) {} };", "Setter must not have a rest parameter");
}

public void testLamestWarningEver() {
// This used to be a warning.
parse("var x = /** @type {undefined} */ (y);");
Expand Down Expand Up @@ -3001,25 +3143,6 @@ public void testTypeScriptKeywords() {
parse("while (i--) { module = module[i]; }");
}

public void testGettersES3() {
mode = LanguageMode.ECMASCRIPT3;
strictMode = SLOPPY;

parseError("var x = {get x(){} };", IRFactory.GETTER_ERROR_MESSAGE);
parseError("var x = {get function(){} };", IRFactory.GETTER_ERROR_MESSAGE);
parseError("var x = {get 'function'(){} };", IRFactory.GETTER_ERROR_MESSAGE);
parseError("var x = {get 1(){} };", IRFactory.GETTER_ERROR_MESSAGE);
}

public void testSettersES3() {
mode = LanguageMode.ECMASCRIPT3;
strictMode = SLOPPY;

parseError("var x = {set function(a){} };", IRFactory.SETTER_ERROR_MESSAGE);
parseError("var x = {set 'function'(a){} };", IRFactory.SETTER_ERROR_MESSAGE);
parseError("var x = {set 1(a){} };", IRFactory.SETTER_ERROR_MESSAGE);
}

public void testKeywordsAsProperties1() {
expectFeatures(Feature.KEYWORDS_AS_PROPERTIES);
mode = LanguageMode.ECMASCRIPT3;
Expand Down

0 comments on commit c7d2139

Please sign in to comment.