From 15c7f31d73246bb35cb5f4932c0bf66d6c933905 Mon Sep 17 00:00:00 2001 From: Mike Vitousek Date: Fri, 18 Feb 2022 13:18:14 -0800 Subject: [PATCH] Fix parsing of f<()=>S>() Summary: As reported [here](https://github.com/facebook/flow/issues/8824), we parse function calls with explicit instantiation incorrectly if the type begins with `<`. In such cases, we lex `<<` as the left shift operator. We don't have similar problems with `>>` or with `<<` in type definitions because we lex those cases as types, but we lex function call explicit instantiation as regular tokens. This diff switches to parsing as types eariler. Closes #8824 Changelog: [parser] Fixed a bug where type parameter instantiations that start with `<` (e.g., `f<()=>S>()`) would be incorrectly parsed as the left shift operator. Reviewed By: mroch Differential Revision: D34050306 fbshipit-source-id: 0c16b393e1f8e7c0fdc8c9439d97256379e4eb66 --- src/parser/expression_parser.ml | 17 +- .../test/flow/lshift_type_arg/example.js | 4 + .../flow/lshift_type_arg/example.tree.json | 422 ++++++++++++++++++ 3 files changed, 438 insertions(+), 5 deletions(-) create mode 100644 src/parser/test/flow/lshift_type_arg/example.js create mode 100644 src/parser/test/flow/lshift_type_arg/example.tree.json diff --git a/src/parser/expression_parser.ml b/src/parser/expression_parser.ml index 88ba7e3f445..79d5cca3bdf 100644 --- a/src/parser/expression_parser.ml +++ b/src/parser/expression_parser.ml @@ -761,7 +761,9 @@ module Expression else match Peek.token env with | T_LPAREN -> arguments env (left_to_callee env) - | T_LESS_THAN when should_parse_types env -> + | T_LSHIFT + | T_LESS_THAN + when should_parse_types env -> (* If we are parsing types, then f(e) is a function call with a type application. If we aren't, it's a nested binary expression. *) let error_callback _ _ = raise Try.Rollback in @@ -901,10 +903,15 @@ module Expression } in fun env -> - if Peek.token env = T_LESS_THAN then - Some (with_loc args env) - else - None + Eat.push_lex_mode env Lex_mode.TYPE; + let node = + if Peek.token env = T_LESS_THAN then + Some (with_loc args env) + else + None + in + Eat.pop_lex_mode env; + node and arguments = let spread_element env = diff --git a/src/parser/test/flow/lshift_type_arg/example.js b/src/parser/test/flow/lshift_type_arg/example.js new file mode 100644 index 00000000000..2fed60d4161 --- /dev/null +++ b/src/parser/test/flow/lshift_type_arg/example.js @@ -0,0 +1,4 @@ +f< (v: T) => T>(); +f<(v: T) => T>(); +f<`" + } + ], + "type":"Program", + "loc":{ + "source":null, + "start":{"line":1,"column":0}, + "end":{"line":4,"column":6} + }, + "range":[0,55], + "body":[ + { + "type":"ExpressionStatement", + "loc":{ + "source":null, + "start":{"line":1,"column":0}, + "end":{"line":1,"column":21} + }, + "range":[0,21], + "expression":{ + "type":"CallExpression", + "loc":{ + "source":null, + "start":{"line":1,"column":0}, + "end":{"line":1,"column":20} + }, + "range":[0,20], + "callee":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":1,"column":0}, + "end":{"line":1,"column":1} + }, + "range":[0,1], + "name":"f", + "typeAnnotation":null, + "optional":false + }, + "typeArguments":{ + "type":"TypeParameterInstantiation", + "loc":{ + "source":null, + "start":{"line":1,"column":1}, + "end":{"line":1,"column":18} + }, + "range":[1,18], + "params":[ + { + "type":"FunctionTypeAnnotation", + "loc":{ + "source":null, + "start":{"line":1,"column":3}, + "end":{"line":1,"column":17} + }, + "range":[3,17], + "params":[ + { + "type":"FunctionTypeParam", + "loc":{ + "source":null, + "start":{"line":1,"column":7}, + "end":{"line":1,"column":11} + }, + "range":[7,11], + "name":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":1,"column":7}, + "end":{"line":1,"column":8} + }, + "range":[7,8], + "name":"v", + "typeAnnotation":null, + "optional":false + }, + "typeAnnotation":{ + "type":"GenericTypeAnnotation", + "loc":{ + "source":null, + "start":{"line":1,"column":10}, + "end":{"line":1,"column":11} + }, + "range":[10,11], + "id":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":1,"column":10}, + "end":{"line":1,"column":11} + }, + "range":[10,11], + "name":"T", + "typeAnnotation":null, + "optional":false + }, + "typeParameters":null + }, + "optional":false + } + ], + "this":null, + "returnType":{ + "type":"GenericTypeAnnotation", + "loc":{ + "source":null, + "start":{"line":1,"column":16}, + "end":{"line":1,"column":17} + }, + "range":[16,17], + "id":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":1,"column":16}, + "end":{"line":1,"column":17} + }, + "range":[16,17], + "name":"T", + "typeAnnotation":null, + "optional":false + }, + "typeParameters":null + }, + "rest":null, + "typeParameters":{ + "type":"TypeParameterDeclaration", + "loc":{ + "source":null, + "start":{"line":1,"column":3}, + "end":{"line":1,"column":6} + }, + "range":[3,6], + "params":[ + { + "type":"TypeParameter", + "loc":{ + "source":null, + "start":{"line":1,"column":4}, + "end":{"line":1,"column":5} + }, + "range":[4,5], + "name":"T", + "bound":null, + "variance":null, + "default":null + } + ] + } + } + ] + }, + "arguments":[] + }, + "directive":null + }, + { + "type":"ExpressionStatement", + "loc":{ + "source":null, + "start":{"line":2,"column":0}, + "end":{"line":2,"column":20} + }, + "range":[22,42], + "expression":{ + "type":"CallExpression", + "loc":{ + "source":null, + "start":{"line":2,"column":0}, + "end":{"line":2,"column":19} + }, + "range":[22,41], + "callee":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":2,"column":0}, + "end":{"line":2,"column":1} + }, + "range":[22,23], + "name":"f", + "typeAnnotation":null, + "optional":false + }, + "typeArguments":{ + "type":"TypeParameterInstantiation", + "loc":{ + "source":null, + "start":{"line":2,"column":1}, + "end":{"line":2,"column":17} + }, + "range":[23,39], + "params":[ + { + "type":"FunctionTypeAnnotation", + "loc":{ + "source":null, + "start":{"line":2,"column":2}, + "end":{"line":2,"column":16} + }, + "range":[24,38], + "params":[ + { + "type":"FunctionTypeParam", + "loc":{ + "source":null, + "start":{"line":2,"column":6}, + "end":{"line":2,"column":10} + }, + "range":[28,32], + "name":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":2,"column":6}, + "end":{"line":2,"column":7} + }, + "range":[28,29], + "name":"v", + "typeAnnotation":null, + "optional":false + }, + "typeAnnotation":{ + "type":"GenericTypeAnnotation", + "loc":{ + "source":null, + "start":{"line":2,"column":9}, + "end":{"line":2,"column":10} + }, + "range":[31,32], + "id":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":2,"column":9}, + "end":{"line":2,"column":10} + }, + "range":[31,32], + "name":"T", + "typeAnnotation":null, + "optional":false + }, + "typeParameters":null + }, + "optional":false + } + ], + "this":null, + "returnType":{ + "type":"GenericTypeAnnotation", + "loc":{ + "source":null, + "start":{"line":2,"column":15}, + "end":{"line":2,"column":16} + }, + "range":[37,38], + "id":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":2,"column":15}, + "end":{"line":2,"column":16} + }, + "range":[37,38], + "name":"T", + "typeAnnotation":null, + "optional":false + }, + "typeParameters":null + }, + "rest":null, + "typeParameters":{ + "type":"TypeParameterDeclaration", + "loc":{ + "source":null, + "start":{"line":2,"column":2}, + "end":{"line":2,"column":5} + }, + "range":[24,27], + "params":[ + { + "type":"TypeParameter", + "loc":{ + "source":null, + "start":{"line":2,"column":3}, + "end":{"line":2,"column":4} + }, + "range":[25,26], + "name":"T", + "bound":null, + "variance":null, + "default":null + } + ] + } + } + ] + }, + "arguments":[] + }, + "directive":null + }, + { + "type":"ExpressionStatement", + "loc":{ + "source":null, + "start":{"line":3,"column":0}, + "end":{"line":3,"column":5} + }, + "range":[43,48], + "expression":{ + "type":"BinaryExpression", + "loc":{ + "source":null, + "start":{"line":3,"column":0}, + "end":{"line":3,"column":4} + }, + "range":[43,47], + "operator":"<<", + "left":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":3,"column":0}, + "end":{"line":3,"column":1} + }, + "range":[43,44], + "name":"f", + "typeAnnotation":null, + "optional":false + }, + "right":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":3,"column":3}, + "end":{"line":3,"column":4} + }, + "range":[46,47], + "name":"T", + "typeAnnotation":null, + "optional":false + } + }, + "directive":null + }, + { + "type":"ExpressionStatement", + "loc":{ + "source":null, + "start":{"line":4,"column":0}, + "end":{"line":4,"column":6} + }, + "range":[49,55], + "expression":{ + "type":"BinaryExpression", + "loc":{ + "source":null, + "start":{"line":4,"column":0}, + "end":{"line":4,"column":5} + }, + "range":[49,54], + "operator":"<", + "left":{ + "type":"Identifier", + "loc":{ + "source":null, + "start":{"line":4,"column":0}, + "end":{"line":4,"column":1} + }, + "range":[49,50], + "name":"f", + "typeAnnotation":null, + "optional":false + }, + "right":{ + "type":"JSXElement", + "loc":{ + "source":null, + "start":{"line":4,"column":3}, + "end":{"line":4,"column":5} + }, + "range":[52,54], + "openingElement":{ + "type":"JSXOpeningElement", + "loc":{ + "source":null, + "start":{"line":4,"column":3}, + "end":{"line":4,"column":5} + }, + "range":[52,54], + "name":{ + "type":"JSXIdentifier", + "loc":{ + "source":null, + "start":{"line":4,"column":4}, + "end":{"line":4,"column":5} + }, + "range":[53,54], + "name":"T" + }, + "attributes":[], + "selfClosing":false + }, + "closingElement":null, + "children":[] + } + }, + "directive":null + } + ], + "comments":[] +}