Skip to content

Commit

Permalink
Parse this argument in TypeScript function. (#1173)
Browse files Browse the repository at this point in the history
Summary:
Parse `this` argument in TypeScript function.

Pull Request resolved: #1173

Test Plan: Unit tests added.

Reviewed By: neildhar

Differential Revision: D50666819

Pulled By: avp

fbshipit-source-id: 1d61361605e5530ee91cf6105a2cc3e85cbb82af
  • Loading branch information
Seonggun Kim authored and facebook-github-bot committed Oct 26, 2023
1 parent 2305173 commit 04faa3f
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 4 deletions.
22 changes: 22 additions & 0 deletions lib/Parser/JSParserImpl-ts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,28 @@ Optional<ESTree::Node *> JSParserImpl::parseTSFunctionOrParenthesizedType(
ESTree::Node *type = nullptr;
ESTree::NodeList params{};

if (check(TokenKind::rw_this)) {
OptValue<TokenKind> optNext = lexer_.lookahead1(None);
if (optNext.hasValue() && *optNext == TokenKind::colon) {
SMLoc thisStart = advance(JSLexer::GrammarContext::Type).Start;
advance(JSLexer::GrammarContext::Type);
CHECK_RECURSION;
auto typeAnnotation = parseTypeAnnotationTS();
if (!typeAnnotation)
return None;

params.push_back(*setLocation(
thisStart,
getPrevTokenEndLoc(),
new (context_) ESTree::IdentifierNode(
thisIdent_, *typeAnnotation, /* optional */ false)));
checkAndEat(TokenKind::comma, JSLexer::GrammarContext::Type);
} else if (optNext.hasValue() && *optNext == TokenKind::question) {
error(tok_->getSourceRange(), "'this' constraint may not be optional");
return None;
}
}

if (allowAnonFunctionType_ &&
checkAndEat(TokenKind::dotdotdot, JSLexer::GrammarContext::Type)) {
isFunction = true;
Expand Down
8 changes: 4 additions & 4 deletions lib/Parser/JSParserImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -590,9 +590,9 @@ bool JSParserImpl::parseFormalParameters(
// (
SMLoc lparenLoc = advance().Start;

#if HERMES_PARSE_FLOW
// The first parameter can be 'this' in Flow mode.
if (context_.getParseFlow() && check(TokenKind::rw_this)) {
#if HERMES_PARSE_FLOW || HERMES_PARSE_TS
// The first parameter can be 'this' in Flow and TypeScript.
if (context_.getParseTypes() && check(TokenKind::rw_this)) {
auto *name = tok_->getResWordIdentifier();
SMLoc thisParamStart = advance().Start;

Expand All @@ -605,7 +605,7 @@ bool JSParserImpl::parseFormalParameters(
thisParamStart))
return false;

auto optType = parseTypeAnnotationFlow(annotStart);
auto optType = parseTypeAnnotation(annotStart);
if (!optType)
return false;
ESTree::Node *type = *optType;
Expand Down
26 changes: 26 additions & 0 deletions test/Parser/ts/function-alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,32 @@ type A = ({x}: number) => number;
// CHECK-NEXT: },
// CHECK-NEXT: "typeParameters": null
// CHECK-NEXT: }
// CHECK-NEXT: },

type A = (this: string) => void;
// CHECK-NEXT: {
// CHECK-NEXT: "type": "TSTypeAliasDeclaration",
// CHECK-NEXT: "id": {
// CHECK-NEXT: "type": "Identifier",
// CHECK-NEXT: "name": "A"
// CHECK-NEXT: },
// CHECK-NEXT: "typeParameters": null,
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSFunctionType",
// CHECK-NEXT: "params": [
// CHECK-NEXT: {
// CHECK-NEXT: "type": "Identifier",
// CHECK-NEXT: "name": "this",
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSStringKeyword"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: ],
// CHECK-NEXT: "returnType": {
// CHECK-NEXT: "type": "TSVoidKeyword"
// CHECK-NEXT: },
// CHECK-NEXT: "typeParameters": null
// CHECK-NEXT: }
// CHECK-NEXT: }

// CHECK-NEXT: ]
Expand Down
13 changes: 13 additions & 0 deletions test/Parser/ts/function-this-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: (! %hermes -parse-ts -dump-ast -pretty-json %s 2>&1) | %FileCheck %s --match-full-lines

type A = (this?: string) => void;
// CHECK: {{.*}}:10:11: error: 'this' constraint may not be optional
// CHECK-NEXT: type A = (this?: string) => void;
// CHECK-NEXT: ^~~~
85 changes: 85 additions & 0 deletions test/Parser/ts/function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: %hermesc -parse-ts -dump-ast -pretty-json %s | %FileCheck %s --match-full-lines

// CHECK-LABEL: {
// CHECK-NEXT: "type": "Program",
// CHECK-NEXT: "body": [

function foo(this: string, x: boolean, y?: number, ...args: string[]): void {}
// CHECK-NEXT: {
// CHECK-NEXT: "type": "FunctionDeclaration",
// CHECK-NEXT: "id": {
// CHECK-NEXT: "type": "Identifier",
// CHECK-NEXT: "name": "foo"
// CHECK-NEXT: },
// CHECK-NEXT: "params": [
// CHECK-NEXT: {
// CHECK-NEXT: "type": "Identifier",
// CHECK-NEXT: "name": "this",
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSTypeAnnotation",
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSStringKeyword"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "type": "Identifier",
// CHECK-NEXT: "name": "x",
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSTypeAnnotation",
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSBooleanKeyword"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "type": "Identifier",
// CHECK-NEXT: "name": "y",
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSTypeAnnotation",
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSNumberKeyword"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "optional": true
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "type": "RestElement",
// CHECK-NEXT: "argument": {
// CHECK-NEXT: "type": "Identifier",
// CHECK-NEXT: "name": "args",
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSTypeAnnotation",
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSArrayType",
// CHECK-NEXT: "elementType": {
// CHECK-NEXT: "type": "TSStringKeyword"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: ],
// CHECK-NEXT: "body": {
// CHECK-NEXT: "type": "BlockStatement",
// CHECK-NEXT: "body": []
// CHECK-NEXT: },
// CHECK-NEXT: "returnType": {
// CHECK-NEXT: "type": "TSTypeAnnotation",
// CHECK-NEXT: "typeAnnotation": {
// CHECK-NEXT: "type": "TSVoidKeyword"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "generator": false,
// CHECK-NEXT: "async": false
// CHECK-NEXT: }

// CHECK-NEXT: ]
// CHECK-NEXT: }

0 comments on commit 04faa3f

Please sign in to comment.