1515import com .semmle .jcorn .TokenType ;
1616import com .semmle .jcorn .TokenType .Properties ;
1717import com .semmle .jcorn .Whitespace ;
18+ import com .semmle .js .ast .BinaryExpression ;
1819import com .semmle .js .ast .ExportDeclaration ;
1920import com .semmle .js .ast .ExportSpecifier ;
2021import com .semmle .js .ast .Expression ;
2122import com .semmle .js .ast .ExpressionStatement ;
2223import com .semmle .js .ast .FieldDefinition ;
2324import com .semmle .js .ast .Identifier ;
25+ import com .semmle .js .ast .ImportDeclaration ;
2426import com .semmle .js .ast .ImportSpecifier ;
27+ import com .semmle .js .ast .Literal ;
2528import com .semmle .js .ast .MethodDefinition ;
2629import com .semmle .js .ast .Node ;
2730import com .semmle .js .ast .Position ;
2831import com .semmle .js .ast .SourceLocation ;
2932import com .semmle .js .ast .Statement ;
3033import com .semmle .js .ast .Token ;
34+ import com .semmle .js .ast .UnaryExpression ;
35+ import com .semmle .util .data .Pair ;
3136import com .semmle .util .exception .Exceptions ;
3237
3338/**
@@ -226,30 +231,21 @@ private void flowParseDeclareModule(Position start) {
226231 this .expect (TokenType .braceL );
227232 while (this .type != TokenType .braceR ) {
228233 Position stmtStart = startLoc ;
229-
230- if (this .eat (TokenType ._import )) {
231- this .flowParseDeclareImport (stmtStart );
232- } else {
233- // todo: declare check
234- this .next ();
235-
234+ if (this .eatContextual ("declare" )) {
236235 this .flowParseDeclare (stmtStart );
236+ } else if (this .eat (TokenType ._import )) {
237+ if (peekAtSpecialFlowImportSpecifier () == null ) {
238+ this .raise (stmtStart ,
239+ "Imports within a `declare module` body must always be `import type` or `import typeof`." );
240+ }
241+ this .parseImportRest (new SourceLocation (stmtStart ));
242+ } else {
243+ unexpected ();
237244 }
238245 }
239246 this .expect (TokenType .braceR );
240247 }
241248
242- private void flowParseDeclareImport (Position stmtStart ) {
243- String kind = flowParseImportSpecifiers ();
244- if (kind == null ) {
245- this .raise (stmtStart , "Imports within a `declare module` body must always be `import type` or `import typeof`." );
246- }
247- this .expect (TokenType .name );
248- this .expectContextual ("from" );
249- this .expect (TokenType .string );
250- this .semicolon ();
251- }
252-
253249 private void flowParseDeclareModuleExports () {
254250 this .expectContextual ("module" );
255251 this .expect (TokenType .dot );
@@ -919,6 +915,11 @@ protected ExportDeclaration parseExportRest(SourceLocation loc, Set<String> expo
919915 List <ExportSpecifier > specifiers = this .parseExportSpecifiers (exports );
920916 this .parseExportFrom (specifiers , null , false );
921917 return null ;
918+ } else if (this .eat (TokenType .star )) {
919+ if (this .eatContextual ("as" ))
920+ this .parseIdent (true );
921+ this .parseExportFrom (null , null , true );
922+ return null ;
922923 } else {
923924 // `export type Foo = Bar;`
924925 this .flowParseTypeAlias (startLoc );
@@ -1004,12 +1005,7 @@ protected Expression processBindingListItem(Expression param) {
10041005 }
10051006
10061007 private String flowParseImportSpecifiers () {
1007- String kind = null ;
1008- if (this .type == TokenType ._typeof ) {
1009- kind = "typeof" ;
1010- } else if (this .isContextual ("type" )) {
1011- kind = "type" ;
1012- }
1008+ String kind = peekAtSpecialFlowImportSpecifier ();
10131009 if (kind != null ) {
10141010 String lh = lookahead (4 );
10151011 if (!lh .isEmpty ()) {
@@ -1022,6 +1018,16 @@ private String flowParseImportSpecifiers() {
10221018 return kind ;
10231019 }
10241020
1021+ private String peekAtSpecialFlowImportSpecifier () {
1022+ String kind = null ;
1023+ if (this .type == TokenType ._typeof ) {
1024+ kind = "typeof" ;
1025+ } else if (this .isContextual ("type" )) {
1026+ kind = "type" ;
1027+ }
1028+ return kind ;
1029+ }
1030+
10251031 @ Override
10261032 protected List <ImportSpecifier > parseImportSpecifiers () {
10271033 String kind = null ;
@@ -1037,7 +1043,7 @@ protected List<ImportSpecifier> parseImportSpecifiers() {
10371043
10381044 @ Override
10391045 protected ImportSpecifier parseImportSpecifier () {
1040- if (this . type == TokenType . _typeof || this . isContextual ( "type" ) ) {
1046+ if (peekAtSpecialFlowImportSpecifier () != null ) {
10411047 String lh = lookahead (2 );
10421048 if (lh .charAt (0 ) == ',' || lh .charAt (0 ) == '}' || lh .equals ("as" ))
10431049 return super .parseImportSpecifier ();
@@ -1201,4 +1207,43 @@ protected boolean atGetterSetterName(PropertyInfo pi) {
12011207 return super .atGetterSetterName (pi );
12021208 }
12031209
1210+ @ Override
1211+ protected Pair <Expression , Boolean > parseSubscript (final Expression base , Position startLoc , boolean noCalls ) {
1212+ if (!noCalls ) {
1213+ maybeFlowParseTypeParameterInstantiation (base , true );
1214+ }
1215+ return super .parseSubscript (base , startLoc , noCalls );
1216+ }
1217+
1218+ private void maybeFlowParseTypeParameterInstantiation (Expression left , boolean requireParenL ) {
1219+ if (flow () && this .isRelational ("<" )) {
1220+ // Ambiguous case: `e1<e2>(e3)` is parsed differently as JS and Flow code:
1221+ // JS: two relational comparisons: `e1 < e2 > e3`
1222+ // Flow: a call `e1(e3)` with explicit type parameter `e2`
1223+
1224+ // Heuristic: if the left operand of the `<` token is a primitive from a literal or unary/binary expression, then it probably isn't a call, as that would always crash
1225+ left = left .stripParens ();
1226+ if (left instanceof Literal || left instanceof UnaryExpression || left instanceof BinaryExpression )
1227+ return ;
1228+
1229+ // If it can be parsed as Flow, we use that, otherwise we parse it as JS
1230+ State backup = new State ();
1231+ try {
1232+ this .flowParseTypeParameterInstantiation ();
1233+ if (requireParenL && this .type != TokenType .parenL ) {
1234+ unexpected ();
1235+ }
1236+ backup .commit ();
1237+ } catch (SyntaxError e ) {
1238+ Exceptions .ignore (e , "Backtracking parser." );
1239+ backup .reset ();
1240+ }
1241+ }
1242+ }
1243+
1244+ @ Override
1245+ protected Expression parseNewArguments (Position startLoc , Expression callee ) {
1246+ maybeFlowParseTypeParameterInstantiation (callee , false /* case: new e1<e2>e3 */ );
1247+ return super .parseNewArguments (startLoc , callee );
1248+ }
12041249}
0 commit comments