diff --git a/src/com/google/javascript/jscomp/parsing/IRFactory.java b/src/com/google/javascript/jscomp/parsing/IRFactory.java index cc1c3836b54..d6ae7d0dde6 100644 --- a/src/com/google/javascript/jscomp/parsing/IRFactory.java +++ b/src/com/google/javascript/jscomp/parsing/IRFactory.java @@ -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); @@ -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; @@ -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 commaNodes = exprNode.expression.asCommaExpression().expressions; diff --git a/src/com/google/javascript/jscomp/parsing/parser/Parser.java b/src/com/google/javascript/jscomp/parsing/parser/Parser.java index 757f052a8e6..c8fe624cc0f 100644 --- a/src/com/google/javascript/jscomp/parsing/parser/Parser.java +++ b/src/com/google/javascript/jscomp/parsing/parser/Parser.java @@ -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(); @@ -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); } } diff --git a/src/com/google/javascript/jscomp/parsing/parser/trees/ComputedPropertySetterTree.java b/src/com/google/javascript/jscomp/parsing/parser/trees/ComputedPropertySetterTree.java index 8e595153bea..00572e4d947 100644 --- a/src/com/google/javascript/jscomp/parsing/parser/trees/ComputedPropertySetterTree.java +++ b/src/com/google/javascript/jscomp/parsing/parser/trees/ComputedPropertySetterTree.java @@ -16,18 +16,15 @@ 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( @@ -35,8 +32,7 @@ public ComputedPropertySetterTree( ParseTree property, boolean isStatic, @Nullable TokenType access, - IdentifierToken parameter, - @Nullable ParseTree type, + FormalParameterListTree parameter, BlockTree body) { super(ParseTreeType.COMPUTED_PROPERTY_SETTER, location); @@ -44,7 +40,6 @@ public ComputedPropertySetterTree( this.isStatic = isStatic; this.access = access; this.parameter = parameter; - this.type = type; this.body = body; } } diff --git a/src/com/google/javascript/jscomp/parsing/parser/trees/SetAccessorTree.java b/src/com/google/javascript/jscomp/parsing/parser/trees/SetAccessorTree.java index 42654beb374..2ac6ef73eed 100644 --- a/src/com/google/javascript/jscomp/parsing/parser/trees/SetAccessorTree.java +++ b/src/com/google/javascript/jscomp/parsing/parser/trees/SetAccessorTree.java @@ -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; } } diff --git a/test/com/google/javascript/jscomp/parsing/ParserTest.java b/test/com/google/javascript/jscomp/parsing/ParserTest.java index 524b37fdf6d..cced4a2debd 100644 --- a/test/com/google/javascript/jscomp/parsing/ParserTest.java +++ b/test/com/google/javascript/jscomp/parsing/ParserTest.java @@ -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() {