From e29bcdb897d0271cae41cf64c1c36592ef1b857d Mon Sep 17 00:00:00 2001 From: bradfordcsmith Date: Mon, 13 Jun 2016 10:29:57 -0700 Subject: [PATCH] Further cleanup for definition & checking of input language feature support. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=124740265 --- .../google/javascript/jscomp/Compiler.java | 8 ++ .../javascript/jscomp/RhinoErrorReporter.java | 2 +- .../javascript/jscomp/parsing/Config.java | 48 +++++++++-- .../javascript/jscomp/parsing/IRFactory.java | 79 ++++++++++--------- .../jscomp/parsing/ParserRunner.java | 4 + .../jscomp/parsing/parser/FeatureSet.java | 53 +++++++++---- .../jscomp/parsing/parser/Parser.java | 2 + .../javascript/jscomp/parsing/ParserTest.java | 70 +++++++++------- 8 files changed, 176 insertions(+), 90 deletions(-) diff --git a/src/com/google/javascript/jscomp/Compiler.java b/src/com/google/javascript/jscomp/Compiler.java index db4344490e8..41e98b4086f 100644 --- a/src/com/google/javascript/jscomp/Compiler.java +++ b/src/com/google/javascript/jscomp/Compiler.java @@ -2150,6 +2150,14 @@ Config getParserConfig(ConfigContext context) { parserConfig = createConfig(Config.LanguageMode.ECMASCRIPT6_TYPED); externsParserConfig = parserConfig; break; + case ECMASCRIPT7: + parserConfig = createConfig(Config.LanguageMode.ECMASCRIPT7); + externsParserConfig = parserConfig; + break; + case ECMASCRIPT8: + parserConfig = createConfig(Config.LanguageMode.ECMASCRIPT8); + externsParserConfig = parserConfig; + break; default: throw new IllegalStateException("unexpected language mode: " + options.getLanguageIn()); diff --git a/src/com/google/javascript/jscomp/RhinoErrorReporter.java b/src/com/google/javascript/jscomp/RhinoErrorReporter.java index 6aa99ac0e3f..bc10a9f87ee 100644 --- a/src/com/google/javascript/jscomp/RhinoErrorReporter.java +++ b/src/com/google/javascript/jscomp/RhinoErrorReporter.java @@ -168,7 +168,7 @@ private RhinoErrorReporter(AbstractCompiler compiler) { .put(Pattern.compile("^Octal .*literal.*"), INVALID_OCTAL_LITERAL) .put( - Pattern.compile("^this language feature is only supported in es6 mode.*"), + Pattern.compile("^this language feature is only supported for ECMASCRIPT6 mode.*"), ES6_FEATURE) .put(Pattern.compile("^type syntax is only supported in ES6 typed mode.*"), ES6_TYPED) diff --git a/src/com/google/javascript/jscomp/parsing/Config.java b/src/com/google/javascript/jscomp/parsing/Config.java index 8dac17b1d8e..fcd993bfc4e 100644 --- a/src/com/google/javascript/jscomp/parsing/Config.java +++ b/src/com/google/javascript/jscomp/parsing/Config.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.javascript.jscomp.parsing.parser.FeatureSet; import java.util.Set; @@ -29,14 +30,49 @@ */ public final class Config { + /** + * Level of language strictness required for the input source code. + */ + public enum StrictMode { + STRICT, SLOPPY; + } + /** JavaScript mode */ public enum LanguageMode { - ECMASCRIPT3, - ECMASCRIPT5, - ECMASCRIPT5_STRICT, - ECMASCRIPT6, - ECMASCRIPT6_STRICT, - ECMASCRIPT6_TYPED, // Implies STRICT. + + // Note that minimumRequiredFor() relies on these being defined in order from fewest features to + // most features, and _STRICT versions should be supplied after unspecified strictness. + ECMASCRIPT3(FeatureSet.ES3, StrictMode.SLOPPY), + ECMASCRIPT5(FeatureSet.ES5, StrictMode.SLOPPY), + ECMASCRIPT5_STRICT(FeatureSet.ES5, StrictMode.STRICT), + ECMASCRIPT6(FeatureSet.ES6_MODULES, StrictMode.SLOPPY), + ECMASCRIPT6_STRICT(FeatureSet.ES6_MODULES, StrictMode.STRICT), + ECMASCRIPT7(FeatureSet.ES7_MODULES, StrictMode.STRICT), + ECMASCRIPT8(FeatureSet.ES8_MODULES, StrictMode.STRICT), + // TODO(bradfordcsmith): This should be renamed so it doesn't seem tied to es6 + ECMASCRIPT6_TYPED(FeatureSet.TYPESCRIPT, StrictMode.STRICT), + ; + + public final FeatureSet featureSet; + public final StrictMode strictMode; + + LanguageMode(FeatureSet featureSet, StrictMode strictMode) { + this.featureSet = featureSet; + this.strictMode = strictMode; + } + + /** + * Returns the lowest {@link LanguageMode} that supports the specified feature. + */ + public static LanguageMode minimumRequiredFor(FeatureSet.Feature feature) { + // relies on the LanguageMode enums being in the right order + for (LanguageMode mode : LanguageMode.values()) { + if (mode.featureSet.contains(feature)) { + return mode; + } + } + throw new IllegalStateException("No input language mode supports feature: " + feature); + } } /** diff --git a/src/com/google/javascript/jscomp/parsing/IRFactory.java b/src/com/google/javascript/jscomp/parsing/IRFactory.java index db8fd150944..85fdb5b6b55 100644 --- a/src/com/google/javascript/jscomp/parsing/IRFactory.java +++ b/src/com/google/javascript/jscomp/parsing/IRFactory.java @@ -35,6 +35,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.UnmodifiableIterator; import com.google.javascript.jscomp.parsing.Config.LanguageMode; +import com.google.javascript.jscomp.parsing.Config.StrictMode; import com.google.javascript.jscomp.parsing.parser.FeatureSet; import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature; import com.google.javascript.jscomp.parsing.parser.IdentifierToken; @@ -313,6 +314,8 @@ private IRFactory(String sourceString, case ECMASCRIPT5_STRICT: case ECMASCRIPT6_STRICT: case ECMASCRIPT6_TYPED: + case ECMASCRIPT7: + case ECMASCRIPT8: reservedKeywords = ES5_STRICT_RESERVED_KEYWORDS; break; default: @@ -995,7 +998,7 @@ Node processArrayLiteral(ArrayLiteralExpressionTree tree) { } Node processArrayPattern(ArrayPatternTree tree) { - maybeWarnEs6Feature(tree, Feature.DESTRUCTURING); + maybeWarnForFeature(tree, Feature.DESTRUCTURING); Node node = newNode(Token.ARRAY_PATTERN); for (ParseTree child : tree.elements) { @@ -1005,7 +1008,7 @@ Node processArrayPattern(ArrayPatternTree tree) { } Node processObjectPattern(ObjectPatternTree tree) { - maybeWarnEs6Feature(tree, Feature.DESTRUCTURING); + maybeWarnForFeature(tree, Feature.DESTRUCTURING); Node node = newNode(Token.OBJECT_PATTERN); for (ParseTree child : tree.fields) { @@ -1150,7 +1153,7 @@ Node processForInLoop(ForInStatementTree loopNode) { } Node processForOf(ForOfStatementTree loopNode) { - maybeWarnEs6Feature(loopNode, Feature.FOR_OF); + maybeWarnForFeature(loopNode, Feature.FOR_OF); Node initializer = transform(loopNode.initializer); ImmutableSet invalidInitializers = ImmutableSet.of(Token.ARRAYLIT, Token.OBJECTLIT); @@ -1210,15 +1213,15 @@ Node processFunction(FunctionDeclarationTree functionTree) { boolean isSignature = (functionTree.functionBody.type == ParseTreeType.EMPTY_STATEMENT); if (isGenerator) { - maybeWarnEs6Feature(functionTree, Feature.GENERATORS); + maybeWarnForFeature(functionTree, Feature.GENERATORS); } if (isMember) { - maybeWarnEs6Feature(functionTree, Feature.MEMBER_DECLARATIONS); + maybeWarnForFeature(functionTree, Feature.MEMBER_DECLARATIONS); } if (isArrow) { - maybeWarnEs6Feature(functionTree, Feature.ARROW_FUNCTIONS); + maybeWarnForFeature(functionTree, Feature.ARROW_FUNCTIONS); } IdentifierToken name = functionTree.name; @@ -1301,23 +1304,23 @@ Node processFormalParameterList(FormalParameterListTree tree) { } Node processDefaultParameter(DefaultParameterTree tree) { - maybeWarnEs6Feature(tree, Feature.DEFAULT_PARAMETERS); + maybeWarnForFeature(tree, Feature.DEFAULT_PARAMETERS); return newNode(Token.DEFAULT_VALUE, transform(tree.lhs), transform(tree.defaultValue)); } Node processRestParameter(RestParameterTree tree) { - maybeWarnEs6Feature(tree, Feature.REST_PARAMETERS); + maybeWarnForFeature(tree, Feature.REST_PARAMETERS); Node assignmentTarget = transformNodeWithInlineJsDoc(tree.assignmentTarget); if (assignmentTarget.isDestructuringPattern()) { - maybeWarnEs6Feature(tree.assignmentTarget, Feature.DESTRUCTURING); + maybeWarnForFeature(tree.assignmentTarget, Feature.DESTRUCTURING); } return newNode(Token.REST, assignmentTarget); } Node processSpreadExpression(SpreadExpressionTree tree) { - maybeWarnEs6Feature(tree, Feature.SPREAD_EXPRESSIONS); + maybeWarnForFeature(tree, Feature.SPREAD_EXPRESSIONS); return newNode(Token.SPREAD, transform(tree.expression)); } @@ -1540,20 +1543,20 @@ Node processObjectLiteral(ObjectLiteralExpressionTree objTree) { node.addChildToBack(key); } if (maybeWarn) { - maybeWarnEs6Feature(objTree, Feature.EXTENDED_OBJECT_LITERALS); + maybeWarnForFeature(objTree, Feature.EXTENDED_OBJECT_LITERALS); } return node; } Node processComputedPropertyDefinition(ComputedPropertyDefinitionTree tree) { - maybeWarnEs6Feature(tree, Feature.COMPUTED_PROPERTIES); + maybeWarnForFeature(tree, Feature.COMPUTED_PROPERTIES); return newNode(Token.COMPUTED_PROP, transform(tree.property), transform(tree.value)); } Node processComputedPropertyMemberVariable(ComputedPropertyMemberVariableTree tree) { - maybeWarnEs6Feature(tree, Feature.COMPUTED_PROPERTIES); + maybeWarnForFeature(tree, Feature.COMPUTED_PROPERTIES); maybeWarnTypeSyntax(tree, Feature.COMPUTED_PROPERTIES); Node n = newNode(Token.COMPUTED_PROP, transform(tree.property)); @@ -1566,7 +1569,7 @@ Node processComputedPropertyMemberVariable(ComputedPropertyMemberVariableTree tr } Node processComputedPropertyMethod(ComputedPropertyMethodTree tree) { - maybeWarnEs6Feature(tree, Feature.COMPUTED_PROPERTIES); + maybeWarnForFeature(tree, Feature.COMPUTED_PROPERTIES); Node n = newNode(Token.COMPUTED_PROP, transform(tree.property), transform(tree.method)); @@ -1579,7 +1582,7 @@ Node processComputedPropertyMethod(ComputedPropertyMethodTree tree) { } Node processComputedPropertyGetter(ComputedPropertyGetterTree tree) { - maybeWarnEs6Feature(tree, Feature.COMPUTED_PROPERTIES); + maybeWarnForFeature(tree, Feature.COMPUTED_PROPERTIES); Node key = transform(tree.property); Node body = transform(tree.body); @@ -1592,7 +1595,7 @@ Node processComputedPropertyGetter(ComputedPropertyGetterTree tree) { } Node processComputedPropertySetter(ComputedPropertySetterTree tree) { - maybeWarnEs6Feature(tree, Feature.COMPUTED_PROPERTIES); + maybeWarnForFeature(tree, Feature.COMPUTED_PROPERTIES); Node key = transform(tree.property); Node body = transform(tree.body); @@ -1718,7 +1721,7 @@ private void validateRegExpFlags(LiteralExpressionTree tree, String flags) { break; case 'u': case 'y': Feature feature = flag == 'u' ? Feature.REGEXP_FLAG_U : Feature.REGEXP_FLAG_Y; - maybeWarnEs6Feature(tree, feature); + maybeWarnForFeature(tree, feature); break; default: errorReporter.error( @@ -1767,7 +1770,7 @@ Node processStringLiteral(LiteralExpressionTree literalTree) { } Node processTemplateLiteral(TemplateLiteralExpressionTree tree) { - maybeWarnEs6Feature(tree, Feature.TEMPLATE_LITERALS); + maybeWarnForFeature(tree, Feature.TEMPLATE_LITERALS); Node templateLitNode = newNode(Token.TEMPLATELIT); setSourceInfo(templateLitNode, tree); Node node = tree.operand == null @@ -1931,11 +1934,11 @@ Node processVariableDeclarationList(VariableDeclarationListTree decl) { Token declType; switch (decl.declarationType) { case CONST: - maybeWarnEs6Feature(decl, Feature.CONST_DECLARATIONS); + maybeWarnForFeature(decl, Feature.CONST_DECLARATIONS); declType = Token.CONST; break; case LET: - maybeWarnEs6Feature(decl, Feature.LET_DECLARATIONS); + maybeWarnForFeature(decl, Feature.LET_DECLARATIONS); declType = Token.LET; break; case VAR: @@ -2068,7 +2071,7 @@ Node processCommaExpression(CommaExpressionTree tree) { } Node processClassDeclaration(ClassDeclarationTree tree) { - maybeWarnEs6Feature(tree, Feature.CLASSES); + maybeWarnForFeature(tree, Feature.CLASSES); Node name = transformOrEmpty(tree.name, tree); maybeProcessGenerics(name, tree.generics); @@ -2125,12 +2128,12 @@ Node processEnumDeclaration(EnumDeclarationTree tree) { } Node processSuper(SuperExpressionTree tree) { - maybeWarnEs6Feature(tree, Feature.SUPER); + maybeWarnForFeature(tree, Feature.SUPER); return newNode(Token.SUPER); } Node processNewTarget(NewTargetExpressionTree tree) { - maybeWarnEs6Feature(tree, Feature.NEW_TARGET); + maybeWarnForFeature(tree, Feature.NEW_TARGET); return newNode(Token.NEW_TARGET); } @@ -2153,7 +2156,7 @@ Node processYield(YieldExpressionTree tree) { } Node processExportDecl(ExportDeclarationTree tree) { - maybeWarnEs6Feature(tree, Feature.MODULES); + maybeWarnForFeature(tree, Feature.MODULES); Node decls = null; if (tree.isExportAll) { Preconditions.checkState( @@ -2193,7 +2196,7 @@ Node processExportSpec(ExportSpecifierTree tree) { } Node processImportDecl(ImportDeclarationTree tree) { - maybeWarnEs6Feature(tree, Feature.MODULES); + maybeWarnForFeature(tree, Feature.MODULES); Node firstChild = transformOrEmpty(tree.defaultBindingIdentifier, tree); Node secondChild = (tree.nameSpaceImportIdentifier != null) @@ -2525,11 +2528,15 @@ private Node transformListOrEmpty( } } - void maybeWarnEs6Feature(ParseTree node, Feature feature) { + void maybeWarnForFeature(ParseTree node, Feature feature) { features = features.require(feature); - if (!isEs6Mode()) { + if (!isSupportedForInputLanguageMode(feature)) { + errorReporter.warning( - "this language feature is only supported in es6 mode: " + feature, + "this language feature is only supported for " + + LanguageMode.minimumRequiredFor(feature) + + " mode or better: " + + feature, sourceName, lineno(node), charno(node)); } @@ -2934,22 +2941,18 @@ String normalizeString(LiteralToken token, boolean templateLiteral) { return result.toString(); } - boolean isEs6Mode() { - return config.languageMode == LanguageMode.ECMASCRIPT6 - || config.languageMode == LanguageMode.ECMASCRIPT6_STRICT - || config.languageMode == LanguageMode.ECMASCRIPT6_TYPED; + boolean isSupportedForInputLanguageMode(Feature feature) { + return config.languageMode.featureSet.contains(feature); } boolean isEs5OrBetterMode() { - return config.languageMode != LanguageMode.ECMASCRIPT3; + return config.languageMode.featureSet.contains(FeatureSet.ES5); } private boolean inStrictContext() { // TODO(johnlenz): in ECMASCRIPT5/6 is a "mixed" mode and we should track the context // that we are in, if we want to support it. - return config.languageMode == LanguageMode.ECMASCRIPT5_STRICT - || config.languageMode == LanguageMode.ECMASCRIPT6_STRICT - || config.languageMode == LanguageMode.ECMASCRIPT6_TYPED; + return config.languageMode.strictMode == StrictMode.STRICT; } double normalizeNumber(LiteralToken token) { @@ -2970,7 +2973,7 @@ private boolean inStrictContext() { case 'b': case 'B': { features = features.require(Feature.BINARY_LITERALS); - if (!isEs6Mode()) { + if (!isSupportedForInputLanguageMode(Feature.BINARY_LITERALS)) { errorReporter.warning(BINARY_NUMBER_LITERAL_WARNING, sourceName, lineno(token.location.start), charno(token.location.start)); @@ -2985,7 +2988,7 @@ private boolean inStrictContext() { case 'o': case 'O': { features = features.require(Feature.OCTAL_LITERALS); - if (!isEs6Mode()) { + if (!isSupportedForInputLanguageMode(Feature.OCTAL_LITERALS)) { errorReporter.warning(OCTAL_NUMBER_LITERAL_WARNING, sourceName, lineno(token.location.start), charno(token.location.start)); diff --git a/src/com/google/javascript/jscomp/parsing/ParserRunner.java b/src/com/google/javascript/jscomp/parsing/ParserRunner.java index 4ba1bfc6297..5637afd4c54 100644 --- a/src/com/google/javascript/jscomp/parsing/ParserRunner.java +++ b/src/com/google/javascript/jscomp/parsing/ParserRunner.java @@ -200,6 +200,10 @@ private static Mode mode(LanguageMode mode) { return Mode.ES6_STRICT; case ECMASCRIPT6_TYPED: return Mode.ES6_TYPED; + case ECMASCRIPT7: + return Mode.ES7; + case ECMASCRIPT8: + return Mode.ES8; default: throw new IllegalStateException("unexpected language mode: " + mode); } diff --git a/src/com/google/javascript/jscomp/parsing/parser/FeatureSet.java b/src/com/google/javascript/jscomp/parsing/parser/FeatureSet.java index 84d99e1a83d..0cca13eb60a 100644 --- a/src/com/google/javascript/jscomp/parsing/parser/FeatureSet.java +++ b/src/com/google/javascript/jscomp/parsing/parser/FeatureSet.java @@ -21,7 +21,8 @@ /** * Represents various aspects of language version and support. - * This is somewhat redundant with LanguageMode, but is separate + * + *

This is somewhat redundant with LanguageMode, but is separate * for two reasons: (1) it's used for parsing, which cannot * depend on LanguageMode, and (2) it's concerned with slightly * different nuances: implemented features and modules rather @@ -31,6 +32,8 @@ * concerns and pull out a single LanguageSyntax enum with a * separate strict mode flag, and then these could possibly be * unified. + * + *

Instances of this class are immutable. */ public final class FeatureSet implements Serializable { @@ -53,10 +56,12 @@ public final class FeatureSet implements Serializable { public static final FeatureSet ES6 = new FeatureSet(6, true, false, false); /** All ES6 features, including modules. */ public static final FeatureSet ES6_MODULES = new FeatureSet(6, true, true, false); - /** TypeScript syntax. */ - public static final FeatureSet TYPESCRIPT = new FeatureSet(6, true, false, true); public static final FeatureSet ES7 = new FeatureSet(7, true, false, false); + public static final FeatureSet ES7_MODULES = new FeatureSet(7, true, true, false); public static final FeatureSet ES8 = new FeatureSet(8, true, false, false); + public static final FeatureSet ES8_MODULES = new FeatureSet(8, true, true, false); + /** TypeScript syntax. */ + public static final FeatureSet TYPESCRIPT = new FeatureSet(8, true, true, true); /** * Specific features that can be included (indirectly) in a FeatureSet. @@ -109,7 +114,7 @@ public enum Feature { EXPONENT_OP("exponent operator (**)", ES7), // http://tc39.github.io/ecmascript-asyncawait/ - ASYNC_FUNCTIONS("async functions", ES8), + ASYNC_FUNCTIONS("async function", ES8), // ES6 typed features that are not at all implemented in browsers AMBIENT_DECLARATION("ambient declaration", TYPESCRIPT), @@ -179,17 +184,28 @@ public boolean isTypeScript() { /** Returns a feature set combining all the features from {@code this} and {@code other}. */ public FeatureSet require(FeatureSet other) { - if (other.number > number - || (other.unsupported && !unsupported) - || (other.es6Modules && !es6Modules) - || (other.typeScript && !typeScript)) { - return new FeatureSet( - Math.max(number, other.number), - unsupported || other.unsupported, - es6Modules || other.es6Modules, - typeScript || other.typeScript); - } - return this; + return this.contains(other) ? this : this.union(other); + } + + /** + * Returns a new {@link FeatureSet} including all features of both {@code this} and {@code other}. + */ + public FeatureSet union(FeatureSet other) { + return new FeatureSet( + Math.max(number, other.number), + unsupported || other.unsupported, + es6Modules || other.es6Modules, + typeScript || other.typeScript); + } + + /** + * Does this {@link FeatureSet} contain all of the features of {@code other}? + */ + public boolean contains(FeatureSet other) { + return this.number >= other.number + && (this.unsupported || !other.unsupported) + && (this.es6Modules || !other.es6Modules) + && (this.typeScript || !other.typeScript); } /** Returns a feature set combining all the features from {@code this} and {@code feature}. */ @@ -197,6 +213,13 @@ public FeatureSet require(Feature feature) { return require(feature.features); } + /** + * Does this {@link FeatureSet} include {@code feature}? + */ + public boolean contains(Feature feature) { + return contains(feature.features()); + } + @Override public boolean equals(Object other) { return other instanceof FeatureSet diff --git a/src/com/google/javascript/jscomp/parsing/parser/Parser.java b/src/com/google/javascript/jscomp/parsing/parser/Parser.java index 5e468461b01..1ada219fa21 100644 --- a/src/com/google/javascript/jscomp/parsing/parser/Parser.java +++ b/src/com/google/javascript/jscomp/parsing/parser/Parser.java @@ -201,6 +201,8 @@ public static enum Mode { ES6, ES6_STRICT, ES6_TYPED, + ES7, + ES8 } public final boolean is6Typed; diff --git a/test/com/google/javascript/jscomp/parsing/ParserTest.java b/test/com/google/javascript/jscomp/parsing/ParserTest.java index e386a0dd186..e6d3d42c245 100644 --- a/test/com/google/javascript/jscomp/parsing/ParserTest.java +++ b/test/com/google/javascript/jscomp/parsing/ParserTest.java @@ -1067,7 +1067,7 @@ private void testMethodInObjectLiteral(String js) { parse(js); mode = LanguageMode.ECMASCRIPT5; - parseWarning(js, "this language feature is only supported in es6 mode: member declaration"); + parseWarning(js, getRequiresEs6Message(Feature.MEMBER_DECLARATIONS)); } public void testExtendedObjectLiteral() { @@ -1092,8 +1092,7 @@ private void testExtendedObjectLiteral(String js) { parse(js); mode = LanguageMode.ECMASCRIPT5; - parseWarning(js, "this language feature is only supported in es6 mode:" - + " extended object literal"); + parseWarning(js, getRequiresEs6Message(Feature.EXTENDED_OBJECT_LITERALS)); } public void testComputedPropertiesObjLit() { @@ -1177,7 +1176,7 @@ public void testComputedProperty() { mode = LanguageMode.ECMASCRIPT6; parse(js); mode = LanguageMode.ECMASCRIPT5; - String warning = "this language feature is only supported in es6 mode: computed property"; + String warning = getRequiresEs6Message(Feature.COMPUTED_PROPERTIES); parseWarning(js, warning, warning); } @@ -1186,7 +1185,7 @@ private void testComputedProperty(String js) { parse(js); mode = LanguageMode.ECMASCRIPT5; - parseWarning(js, "this language feature is only supported in es6 mode: computed property"); + parseWarning(js, getRequiresEs6Message(Feature.COMPUTED_PROPERTIES)); } public void testTrailingCommaWarning1() { @@ -1282,7 +1281,7 @@ public void testCatchClauseForbidden() { public void testConstForbidden() { expectFeatures(Feature.CONST_DECLARATIONS); parseWarning("const x = 3;", - "this language feature is only supported in es6 mode: const declaration"); + getRequiresEs6Message(Feature.CONST_DECLARATIONS)); } public void testAnonymousFunctionExpression() { @@ -1297,7 +1296,7 @@ public void testArrayDestructuringVar() { mode = LanguageMode.ECMASCRIPT5; expectFeatures(Feature.DESTRUCTURING); parseWarning("var [x,y] = foo();", - "this language feature is only supported in es6 mode: destructuring"); + getRequiresEs6Message(Feature.DESTRUCTURING)); mode = LanguageMode.ECMASCRIPT6; parse("var [x,y] = foo();"); @@ -1310,7 +1309,7 @@ public void testArrayDestructuringAssign() { mode = LanguageMode.ECMASCRIPT5; expectFeatures(Feature.DESTRUCTURING); parseWarning("[x,y] = foo();", - "this language feature is only supported in es6 mode: destructuring"); + getRequiresEs6Message(Feature.DESTRUCTURING)); mode = LanguageMode.ECMASCRIPT6; parse("[x,y] = foo();"); @@ -1366,7 +1365,7 @@ public void testArrayDestructuringDeclarationRest() { mode = LanguageMode.ECMASCRIPT5; parseWarning( "var [first, ...rest] = foo();", - "this language feature is only supported in es6 mode: destructuring"); + getRequiresEs6Message(Feature.DESTRUCTURING)); } public void testArrayDestructuringAssignRest() { @@ -1389,7 +1388,7 @@ public void testArrayDestructuringAssignRest() { mode = LanguageMode.ECMASCRIPT5; parseWarning("var [first, ...rest] = foo();", - "this language feature is only supported in es6 mode: destructuring"); + getRequiresEs6Message(Feature.DESTRUCTURING)); } public void testArrayDestructuringFnDeclaration() { @@ -1651,13 +1650,13 @@ public void testComprehensions() { public void testLetForbidden1() { expectFeatures(Feature.LET_DECLARATIONS); parseWarning("let x = 3;", - "this language feature is only supported in es6 mode: let declaration"); + getRequiresEs6Message(Feature.LET_DECLARATIONS)); } public void testLetForbidden2() { expectFeatures(Feature.LET_DECLARATIONS); parseWarning("function f() { let x = 3; };", - "this language feature is only supported in es6 mode: let declaration"); + getRequiresEs6Message(Feature.LET_DECLARATIONS)); } public void testLetForbidden3() { @@ -1684,10 +1683,10 @@ public void testGenerator() { mode = LanguageMode.ECMASCRIPT5_STRICT; parseWarning("function* f() { yield 3; }", - "this language feature is only supported in es6 mode: generator"); + getRequiresEs6Message(Feature.GENERATORS)); parseWarning("var obj = { * f() { yield 3; } };", - "this language feature is only supported in es6 mode: generator", - "this language feature is only supported in es6 mode: member declaration"); + getRequiresEs6Message(Feature.GENERATORS), + getRequiresEs6Message(Feature.MEMBER_DECLARATIONS)); } public void testBracelessFunctionForbidden() { @@ -1908,7 +1907,7 @@ public void testStringLiteral() { private Node testTemplateLiteral(String s) { mode = LanguageMode.ECMASCRIPT5; parseWarning(s, - "this language feature is only supported in es6 mode: template literal"); + getRequiresEs6Message(Feature.TEMPLATE_LITERALS)); mode = LanguageMode.ECMASCRIPT6; return parse(s); @@ -2555,13 +2554,13 @@ public void testES6RegExpFlags() { mode = LanguageMode.ECMASCRIPT5; expectFeatures(Feature.REGEXP_FLAG_Y); parseWarning("/a/y", - "this language feature is only supported in es6 mode: RegExp flag 'y'"); + getRequiresEs6Message(Feature.REGEXP_FLAG_Y)); expectFeatures(Feature.REGEXP_FLAG_U); parseWarning("/a/u", - "this language feature is only supported in es6 mode: RegExp flag 'u'"); + getRequiresEs6Message(Feature.REGEXP_FLAG_U)); parseWarning("/a/yu", - "this language feature is only supported in es6 mode: RegExp flag 'y'", - "this language feature is only supported in es6 mode: RegExp flag 'u'"); + getRequiresEs6Message(Feature.REGEXP_FLAG_Y), + getRequiresEs6Message(Feature.REGEXP_FLAG_U)); } public void testDefaultParameters() { @@ -2572,7 +2571,7 @@ public void testDefaultParameters() { mode = LanguageMode.ECMASCRIPT5; parseWarning("function f(a, b=0) {}", - "this language feature is only supported in es6 mode: default parameter"); + getRequiresEs6Message(Feature.DEFAULT_PARAMETERS)); } public void testRestParameters() { @@ -2601,7 +2600,7 @@ public void testRestParameters_ES5() { mode = LanguageMode.ECMASCRIPT5; expectFeatures(Feature.REST_PARAMETERS); parseWarning("function f(...b) {}", - "this language feature is only supported in es6 mode: rest parameter"); + getRequiresEs6Message(Feature.REST_PARAMETERS)); } public void testExpressionsThatLookLikeParameters() { @@ -2629,11 +2628,11 @@ public void testClass1() { mode = LanguageMode.ECMASCRIPT5; parseWarning("class C {}", - "this language feature is only supported in es6 mode: class"); + getRequiresEs6Message(Feature.CLASSES)); mode = LanguageMode.ECMASCRIPT3; parseWarning("class C {}", - "this language feature is only supported in es6 mode: class"); + getRequiresEs6Message(Feature.CLASSES)); } @@ -2693,11 +2692,11 @@ public void testSuper1() { mode = LanguageMode.ECMASCRIPT5; parseWarning("super;", - "this language feature is only supported in es6 mode: super"); + getRequiresEs6Message(Feature.SUPER)); mode = LanguageMode.ECMASCRIPT3; parseWarning("super;", - "this language feature is only supported in es6 mode: super"); + getRequiresEs6Message(Feature.SUPER)); } public void testNewTarget() { @@ -2712,12 +2711,12 @@ public void testNewTarget() { mode = LanguageMode.ECMASCRIPT5; parseWarning( "function f() { new.target; }", - "this language feature is only supported in es6 mode: new.target"); + getRequiresEs6Message(Feature.NEW_TARGET)); mode = LanguageMode.ECMASCRIPT3; parseWarning( "function f() { new.target; }", - "this language feature is only supported in es6 mode: new.target"); + getRequiresEs6Message(Feature.NEW_TARGET)); } public void testNewDotSomethingInvalid() { @@ -2741,11 +2740,11 @@ public void testArrow1() { mode = LanguageMode.ECMASCRIPT5; parseWarning("a => b", - "this language feature is only supported in es6 mode: arrow function"); + getRequiresEs6Message(Feature.ARROW_FUNCTIONS)); mode = LanguageMode.ECMASCRIPT3; parseWarning("a => b;", - "this language feature is only supported in es6 mode: arrow function"); + getRequiresEs6Message(Feature.ARROW_FUNCTIONS)); } public void testArrowInvalid() { @@ -3155,6 +3154,17 @@ public void testParseDeep4() { } } + private String getRequiresEs6Message(Feature feature) { + return requiresLanguageModeMessage(LanguageMode.ECMASCRIPT6, feature); + } + + private String requiresLanguageModeMessage(LanguageMode languageMode, Feature feature) { + return String.format( + "this language feature is only supported for %s mode or better: %s", + languageMode, + feature); + } + private Node script(Node stmt) { Node n = new Node(Token.SCRIPT, stmt); n.setIsSyntheticBlock(true);