Skip to content

Commit

Permalink
Clean up async function parsing.
Browse files Browse the repository at this point in the history
1. Better error message if someone tries to define an async generator function.
2. Correct handling of implicit semicolons between 'async' and 'function'
3. Added a code printer test.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=125060090
  • Loading branch information
brad4d authored and blickly committed Jun 20, 2016
1 parent e90c2ee commit bbe7ab4
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 35 deletions.
23 changes: 13 additions & 10 deletions src/com/google/javascript/jscomp/parsing/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -984,16 +984,7 @@ private boolean peekSourceElement() {
}

private boolean peekAsyncFunctionStart() {
if (peekPredefinedString(ASYNC) && peekFunction(1)) {
Token asyncToken = peekToken();
Token functionToken = peekToken(1);
if (asyncToken.location.start.line != functionToken.location.start.line) {
reportError(functionToken, "Newline is not allowed between 'async' and 'function'");
}
return true;
} else {
return false;
}
return peekPredefinedString(ASYNC) && !peekImplicitSemiColon(1) && peekFunction(1);
}

private void eatAsyncFunctionStart() {
Expand Down Expand Up @@ -1154,6 +1145,12 @@ private ParseTree parseAsyncFunctionDeclaration() {
features.require(Feature.ASYNC_FUNCTIONS);
eatAsyncFunctionStart();

if (peek(TokenType.STAR)) {
reportError("async functions cannot be generators");
// ignore the star to see how much more we can parse for errors
eat(TokenType.STAR);
}

FunctionDeclarationTree.Builder builder =
FunctionDeclarationTree.builder(FunctionDeclarationTree.Kind.DECLARATION)
.setName(eatId())
Expand All @@ -1168,6 +1165,12 @@ private ParseTree parseAsyncFunctionExpression() {
features.require(Feature.ASYNC_FUNCTIONS);
eatAsyncFunctionStart();

if (peek(TokenType.STAR)) {
reportError("async functions cannot be generators");
// ignore the star to see how much more we can parse for errors
eat(TokenType.STAR);
}

FunctionDeclarationTree.Builder builder =
FunctionDeclarationTree.builder(FunctionDeclarationTree.Kind.EXPRESSION)
.setName(eatIdOpt())
Expand Down
10 changes: 10 additions & 0 deletions test/com/google/javascript/jscomp/CodePrinterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2098,6 +2098,16 @@ public void testArrowFunction() {
assertPrintSame("[1,2].forEach((x)=>y)");
}

public void testAsyncFunction() {
languageMode = LanguageMode.ECMASCRIPT8;
assertPrintSame("async function f(){}");
assertPrintSame("let f=async function f(){}");
assertPrintSame("let f=async function(){}");
// implicit semicolon prevents async being treated as a keyword
assertPrint("async\nfunction f(){}", "async;function f(){}");
assertPrint("let f=async\nfunction f(){}", "let f=async;function f(){}");
}

/**
* Regression test for b/28633247 - necessary parens dropped around arrow functions.
*/
Expand Down
45 changes: 20 additions & 25 deletions test/com/google/javascript/jscomp/parsing/ParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2755,36 +2755,31 @@ public void testArrowInvalid() {
parseError("f( (x,y)\n=>2)", "No newline allowed before '=>'");
}

public void testAsyncFunctionExpression() {
mode = LanguageMode.ECMASCRIPT8;
public void testAsyncFunction() {
String asyncFunctionExpressionSource = "f = async function() {};";
String asyncFunctionDeclarationSource = "async function f() {}";
expectFeatures(Feature.ASYNC_FUNCTIONS);
parse("f = async function() {}");

mode = LanguageMode.ECMASCRIPT5;
parseWarning(
"f = async function() {}",
requiresLanguageModeMessage(LanguageMode.ECMASCRIPT8, Feature.ASYNC_FUNCTIONS));

mode = LanguageMode.ECMASCRIPT3;
parseWarning(
"f = async function() {}",
requiresLanguageModeMessage(LanguageMode.ECMASCRIPT8, Feature.ASYNC_FUNCTIONS));
for (LanguageMode m : LanguageMode.values()) {
mode = m;
if (m.featureSet.contains(Feature.ASYNC_FUNCTIONS)) {
parse(asyncFunctionExpressionSource);
parse(asyncFunctionDeclarationSource);
} else {
parseWarning(
asyncFunctionExpressionSource,
requiresLanguageModeMessage(LanguageMode.ECMASCRIPT8, Feature.ASYNC_FUNCTIONS));
parseWarning(
asyncFunctionDeclarationSource,
requiresLanguageModeMessage(LanguageMode.ECMASCRIPT8, Feature.ASYNC_FUNCTIONS));
}
}
}

public void testAsyncFunctionDeclaration() {
public void testInvalidAsyncFunction() {
mode = LanguageMode.ECMASCRIPT8;
expectFeatures(Feature.ASYNC_FUNCTIONS);
parse("async function f() {}");

mode = LanguageMode.ECMASCRIPT5;
parseWarning(
"async function f() {}",
requiresLanguageModeMessage(LanguageMode.ECMASCRIPT8, Feature.ASYNC_FUNCTIONS));

mode = LanguageMode.ECMASCRIPT3;
parseWarning(
"async function f() {}",
requiresLanguageModeMessage(LanguageMode.ECMASCRIPT8, Feature.ASYNC_FUNCTIONS));
parseError("async function *f(){}", "async functions cannot be generators");
parseError("f = async function *(){}", "async functions cannot be generators");
}

public void testFor_ES5() {
Expand Down

0 comments on commit bbe7ab4

Please sign in to comment.