Skip to content

Commit

Permalink
Allow typescript keywords in import and export statements
Browse files Browse the repository at this point in the history
Default behavior forbids the use of TypeScript keywords in import and export statements: modify scanner to emit IdentifierTokens when parseTypeSyntax is false.

Export decl parser coerces identifiers in exports to keywords: check there also.

Addresses google#2005

Closes google#2835

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=188511416
  • Loading branch information
Jesse Bouwman authored and lauraharker committed Mar 9, 2018
1 parent 472860a commit c6327b8
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 12 deletions.
13 changes: 10 additions & 3 deletions src/com/google/javascript/jscomp/parsing/parser/Parser.java
Expand Up @@ -194,7 +194,8 @@ public Parser(
boolean initialGeneratorContext) {
this.config = config;
this.errorReporter = errorReporter;
this.scanner = new Scanner(errorReporter, commentRecorder, source, offset);
this.scanner =
new Scanner(config.parseTypeSyntax, errorReporter, commentRecorder, source, offset);
this.functionContextStack.addLast(
initialGeneratorContext ? FunctionFlavor.GENERATOR : FunctionFlavor.NORMAL);
lastSourcePosition = scanner.getPosition();
Expand Down Expand Up @@ -278,6 +279,12 @@ public String getSourceMapURL() {
return sourceMapURL;
}

/** Returns true if the string value should be treated as a keyword in the current context. */
private boolean isKeyword(String value) {
return Keywords.isKeyword(value)
&& (!Keywords.isTypeScriptSpecificKeyword(value) || config.parseTypeSyntax);
}

// 14 Program
public ProgramTree parseProgram() {
Timer t = new Timer("Parse Program");
Expand Down Expand Up @@ -459,7 +466,7 @@ private ParseTree parseImportSpecifier() {
if (peekPredefinedString(PredefinedName.AS)) {
eatPredefinedString(PredefinedName.AS);
destinationName = eatId();
} else if (Keywords.isKeyword(importedName.value)) {
} else if (isKeyword(importedName.value)) {
reportExpectedError(null, PredefinedName.AS);
}
return new ImportSpecifierTree(
Expand Down Expand Up @@ -565,7 +572,7 @@ private ParseTree parseExportDeclaration(boolean isAmbient) {
} else if (isExportSpecifier) {
for (ParseTree tree : exportSpecifierList) {
IdentifierToken importedName = tree.asExportSpecifier().importedName;
if (Keywords.isKeyword(importedName.value)) {
if (isKeyword(importedName.value)) {
reportError(importedName, "cannot use keyword '%s' here.", importedName.value);
}
}
Expand Down
19 changes: 14 additions & 5 deletions src/com/google/javascript/jscomp/parsing/parser/Scanner.java
Expand Up @@ -32,20 +32,29 @@
* <p>7 Lexical Conventions
*/
public class Scanner {
private final boolean parseTypeSyntax;
private final ErrorReporter errorReporter;
private final SourceFile source;
private final ArrayList<Token> currentTokens = new ArrayList<>();
private int index;
private final CommentRecorder commentRecorder;
private int typeParameterLevel;

public Scanner(ErrorReporter errorReporter, CommentRecorder commentRecorder,
public Scanner(
boolean parseTypeSyntax,
ErrorReporter errorReporter,
CommentRecorder commentRecorder,
SourceFile source) {
this(errorReporter, commentRecorder, source, 0);
this(parseTypeSyntax, errorReporter, commentRecorder, source, 0);
}

public Scanner(ErrorReporter errorReporter, CommentRecorder commentRecorder,
SourceFile file, int offset) {
public Scanner(
boolean parseTypeSyntax,
ErrorReporter errorReporter,
CommentRecorder commentRecorder,
SourceFile file,
int offset) {
this.parseTypeSyntax = parseTypeSyntax;
this.errorReporter = errorReporter;
this.commentRecorder = commentRecorder;
this.source = file;
Expand Down Expand Up @@ -725,7 +734,7 @@ private Token scanIdentifierOrKeyword(int beginToken, char ch) {
}

Keywords k = Keywords.get(value);
if (k != null) {
if (k != null && (!Keywords.isTypeScriptSpecificKeyword(value) || parseTypeSyntax)) {
return new Token(k.type, getTokenRange(beginToken));
}

Expand Down
9 changes: 9 additions & 0 deletions test/com/google/javascript/jscomp/parsing/ParserTest.java
Expand Up @@ -3657,6 +3657,15 @@ public void testExport() {
parseError("export * as s from './someModule';", "'from' expected");
}

public void testImportExportTypescriptKeyword() {
mode = LanguageMode.TYPESCRIPT;
parseError("export { namespace };", "cannot use keyword 'namespace' here.");

mode = LanguageMode.ECMASCRIPT6;
parse("export { namespace };");
parse("import { namespace } from './input0.js';");
}

public void testGoogModule() {
Node tree = parse("goog.module('example');");
assertNode(tree).hasType(Token.SCRIPT);
Expand Down
9 changes: 5 additions & 4 deletions test/com/google/javascript/jscomp/parsing/TypeSyntaxTest.java
Expand Up @@ -537,7 +537,7 @@ public void testTypeAlias() {
expectErrors("Parse error. Semi-colon expected");
parse("if (true) { type Foo = number; }");

testNotEs6Typed("type Foo = number;", "type alias");
testNotEs6TypedFullError("type Foo = number;", "Parse error. Semi-colon expected");
}

public void testAmbientDeclaration() {
Expand Down Expand Up @@ -566,7 +566,8 @@ public void testAmbientDeclaration() {
expectErrors("Parse error. Semi-colon expected");
parse("declare class Foo {\n constructor() {}\n};");

testNotEs6Typed("declare var x;", "ambient declaration");
testNotEs6TypedFullError("declare var x;", "Parse error. Semi-colon expected");

}

public void testExportDeclaration() {
Expand Down Expand Up @@ -728,7 +729,7 @@ public void testNamespace() {
expectErrors("Parse error. Semi-colon expected");
parse("namespace 'foo' {}"); // External modules are not supported

testNotEs6Typed("namespace foo {}", "namespace declaration");
testNotEs6TypedFullError("namespace foo {}", "Parse error. Semi-colon expected");
}

public void testAmbientNameSpace() {
Expand Down Expand Up @@ -765,7 +766,7 @@ public void testAmbientNameSpace() {
expectErrors("Parse error. '}' expected");
parse("declare namespace foo { type Foo = number; }");

testNotEs6Typed("declare namespace foo {}", "ambient declaration", "namespace declaration");
testNotEs6TypedFullError("declare namespace foo {}", "Parse error. Semi-colon expected");
}

private void assertVarType(String message, TypeDeclarationNode expectedType, String source) {
Expand Down

0 comments on commit c6327b8

Please sign in to comment.