Skip to content

Commit

Permalink
Add support for ES6 parameter syntaxes (i.e. default parameters, arra…
Browse files Browse the repository at this point in the history
…y patterns, etc.) to setter declarations.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=203840265
  • Loading branch information
nreid260 authored and lauraharker committed Jul 11, 2018
1 parent e5d4332 commit 220454e
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 40 deletions.
25 changes: 13 additions & 12 deletions src/com/google/javascript/jscomp/parsing/IRFactory.java
Expand Up @@ -1848,10 +1848,15 @@ Node processComputedPropertySetter(ComputedPropertySetterTree tree) {
maybeWarnForFeature(tree, Feature.COMPUTED_PROPERTIES);

Node key = transform(tree.property);

Node paramList = processFormalParameterList(tree.parameter);
setSourceInfo(paramList, tree.parameter);

Node body = transform(tree.body);
Node paramList = IR.paramList(safeProcessName(tree.parameter));

Node function = IR.function(IR.name(""), paramList, body);
function.useSourceInfoIfMissingFromForTree(body);

Node n = newNode(Token.COMPUTED_PROP, key, function);
n.putBooleanProp(Node.COMPUTED_PROP_SETTER, true);
n.putBooleanProp(Node.STATIC_MEMBER, tree.isStatic);
Expand All @@ -1877,14 +1882,18 @@ Node processGetAccessor(GetAccessorTree tree) {
Node processSetAccessor(SetAccessorTree tree) {
Node key = processObjectLitKeyAsString(tree.propertyName);
key.setToken(Token.SETTER_DEF);

Node paramList = processFormalParameterList(tree.parameter);
setSourceInfo(paramList, tree.parameter);

Node body = transform(tree.body);

Node dummyName = newStringNode(Token.NAME, "");
setSourceInfo(dummyName, tree.propertyName);
Node paramList = newNode(Token.PARAM_LIST, safeProcessName(tree.parameter));
setSourceInfo(paramList, tree.parameter);
maybeProcessType(paramList.getFirstChild(), tree.type);

Node value = newNode(Token.FUNCTION, dummyName, paramList, body);
setSourceInfo(value, tree.body);

key.addChildToFront(value);
key.setStaticMember(tree.isStatic);
return key;
Expand All @@ -1904,14 +1913,6 @@ Node processPropertyNameAssignment(PropertyNameAssignmentTree tree) {
return key;
}

private Node safeProcessName(IdentifierToken identifierToken) {
if (identifierToken == null) {
return createMissingExpressionNode();
} else {
return processName(identifierToken);
}
}

private void checkParenthesizedExpression(ParenExpressionTree exprNode) {
if (exprNode.expression.type == ParseTreeType.COMMA_EXPRESSION) {
List<ParseTree> commaNodes = exprNode.expression.asCommaExpression().expressions;
Expand Down
37 changes: 27 additions & 10 deletions src/com/google/javascript/jscomp/parsing/parser/Parser.java
Expand Up @@ -1420,6 +1420,26 @@ private FormalParameterListTree parseFormalParameterList(ParamContext context) {
getTreeLocation(listStart), result.build());
}

private FormalParameterListTree parseSetterParameterList() {
FormalParameterListTree parameterList = parseFormalParameterList(ParamContext.IMPLEMENTATION);

if (parameterList.parameters.size() != 1) {
reportError(
parameterList,
"Setter must have exactly 1 parameter, found %d",
parameterList.parameters.size());
}

if (parameterList.parameters.size() >= 1) {
ParseTree parameter = parameterList.parameters.get(0);
if (parameter.isRestParameter()) {
reportError(parameter, "Setter must not have a rest parameter");
}
}

return parameterList;
}

private ParseTree parseTypeAnnotation() {
eat(TokenType.COLON);
return parseType();
Expand Down Expand Up @@ -2667,31 +2687,28 @@ private ParseTree parseSetAccessor(PartialClassElement partial) {
eatPredefinedString(PredefinedName.SET);
if (peekPropertyName(0)) {
Token propertyName = eatObjectLiteralPropertyName();
eat(TokenType.OPEN_PAREN);
IdentifierToken parameter = eatId();
ParseTree type = maybeParseColonType();
eat(TokenType.CLOSE_PAREN);
FormalParameterListTree parameter = parseSetterParameterList();

ParseTree returnType = maybeParseColonType();
if (returnType != null) {
reportError(scanner.peekToken(), "setter should not have any returns");
}

BlockTree body = parseFunctionBody();

return new SetAccessorTree(
getTreeLocation(partial.start), propertyName, partial.isStatic, parameter, type, body);
getTreeLocation(partial.start), propertyName, partial.isStatic, parameter, body);
} else {
ParseTree property = parseComputedPropertyName();
eat(TokenType.OPEN_PAREN);
IdentifierToken parameter = eatId();
ParseTree type = maybeParseColonType();
eat(TokenType.CLOSE_PAREN);
FormalParameterListTree parameter = parseSetterParameterList();
BlockTree body = parseFunctionBody();

return new ComputedPropertySetterTree(
getTreeLocation(partial.start),
property,
partial.isStatic,
partial.accessModifier,
parameter,
type,
body);
}
}
Expand Down
Expand Up @@ -16,35 +16,30 @@

package com.google.javascript.jscomp.parsing.parser.trees;

import com.google.javascript.jscomp.parsing.parser.IdentifierToken;
import com.google.javascript.jscomp.parsing.parser.TokenType;
import com.google.javascript.jscomp.parsing.parser.util.SourceRange;

import javax.annotation.Nullable;

public class ComputedPropertySetterTree extends ParseTree {
public final ParseTree property;
public final IdentifierToken parameter;
public final FormalParameterListTree parameter;
public final boolean isStatic;
@Nullable public final TokenType access;
@Nullable public final ParseTree type;
public final BlockTree body;

public ComputedPropertySetterTree(
SourceRange location,
ParseTree property,
boolean isStatic,
@Nullable TokenType access,
IdentifierToken parameter,
@Nullable ParseTree type,
FormalParameterListTree parameter,
BlockTree body) {
super(ParseTreeType.COMPUTED_PROPERTY_SETTER, location);

this.property = property;
this.isStatic = isStatic;
this.access = access;
this.parameter = parameter;
this.type = type;
this.body = body;
}
}
Expand Up @@ -16,33 +16,27 @@

package com.google.javascript.jscomp.parsing.parser.trees;

import com.google.javascript.jscomp.parsing.parser.IdentifierToken;
import com.google.javascript.jscomp.parsing.parser.Token;
import com.google.javascript.jscomp.parsing.parser.util.SourceRange;

import javax.annotation.Nullable;

public class SetAccessorTree extends ParseTree {

public final Token propertyName;
public final IdentifierToken parameter;
public final FormalParameterListTree parameter;
public final boolean isStatic;
@Nullable public final ParseTree type;
public final BlockTree body;

public SetAccessorTree(
SourceRange location,
Token propertyName,
boolean isStatic,
IdentifierToken parameter,
@Nullable ParseTree type,
FormalParameterListTree parameter,
BlockTree body) {
super(ParseTreeType.SET_ACCESSOR, location);

this.propertyName = propertyName;
this.isStatic = isStatic;
this.parameter = parameter;
this.type = type;
this.body = body;
}
}
61 changes: 58 additions & 3 deletions test/com/google/javascript/jscomp/parsing/ParserTest.java
Expand Up @@ -2822,19 +2822,74 @@ public void testSetter() {
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);

mode = LanguageMode.ECMASCRIPT5;

parse("var x = {set 1(x){}};");
parse("var x = {set 'a'(x){}};");
parse("var x = {set a(x){}};");
expectFeatures();
parseError("var x = {set a(){}};",
"'identifier' expected");

mode = LanguageMode.ECMASCRIPT6; // We only cover some of the common permutations though.

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

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

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

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.

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.
strictMode = SLOPPY;

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

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

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.

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 testLamestWarningEver() {
Expand Down

0 comments on commit 220454e

Please sign in to comment.