Skip to content

Commit

Permalink
refactor(fe): merge type predicate parsing into main type parsing fun…
Browse files Browse the repository at this point in the history
…ction

Type predicate parsing is performed when it's unnecessary, and is not
performed when it would be nice (e.g. to provide better diagnostics).

Move the type predicate parsing code into
parse_and_visit_typescript_type_expression to give us more
flexibility.
  • Loading branch information
strager committed Nov 7, 2023
1 parent 274472b commit 15b65ab
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 56 deletions.
159 changes: 103 additions & 56 deletions src/quick-lint-js/fe/parse-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,31 +90,68 @@ void Parser::parse_and_visit_typescript_type_expression(
if (this->peek().type == Token_Type::kw_readonly) {
// readonly Type[]
// readonly [Type, Type]
readonly_keyword = this->peek().span();
// readonly is Type
Lexer_Transaction transaction = this->lexer_.begin_transaction();
Identifier readonly = this->peek().identifier_name();
this->skip();

if (parse_options.allow_type_predicate &&
this->peek().type == Token_Type::kw_is) {
// readonly is Type
this->lexer_.roll_back_transaction(std::move(transaction));
goto type_variable_or_namespace_or_type_predicate;
}
this->lexer_.commit_transaction(std::move(transaction));
readonly_keyword = readonly.span();
}

switch (this->peek().type) {
case Token_Type::complete_template:
case Token_Type::kw_false:
case Token_Type::kw_null:
case Token_Type::kw_true:
case Token_Type::kw_void:
case Token_Type::number:
case Token_Type::string:
this->skip();
break;

// any
// any is Type
case Token_Type::kw_any:
case Token_Type::kw_bigint:
case Token_Type::kw_boolean:
case Token_Type::kw_false:
case Token_Type::kw_never:
case Token_Type::kw_null:
case Token_Type::kw_number:
case Token_Type::kw_object:
case Token_Type::kw_string:
case Token_Type::kw_symbol:
case Token_Type::kw_this:
case Token_Type::kw_true:
case Token_Type::kw_undefined:
case Token_Type::kw_unknown:
case Token_Type::kw_void:
case Token_Type::number:
case Token_Type::string:
case Token_Type::kw_unknown: {
Lexer_Transaction transaction = this->lexer_.begin_transaction();
this->skip();
if (this->peek().type == Token_Type::kw_is) {
// param is Type
this->lexer_.roll_back_transaction(std::move(transaction));
goto type_variable_or_namespace_or_type_predicate;
}
this->lexer_.commit_transaction(std::move(transaction));
break;
}

// this
// this is Type
case Token_Type::kw_this: {
Lexer_Transaction transaction = this->lexer_.begin_transaction();
this->skip();
if (this->peek().type == Token_Type::kw_is) {
// this is Type
this->lexer_.roll_back_transaction(std::move(transaction));
goto type_variable_or_namespace_or_type_predicate;
}
this->lexer_.commit_transaction(std::move(transaction));
break;
}

// `template ${sometype}`
case Token_Type::incomplete_template:
Expand All @@ -134,6 +171,8 @@ void Parser::parse_and_visit_typescript_type_expression(

// Type
// ns.Type<T>
// param is Type
type_variable_or_namespace_or_type_predicate:
case Token_Type::kw_abstract:
case Token_Type::kw_accessor:
case Token_Type::kw_as:
Expand Down Expand Up @@ -168,10 +207,24 @@ void Parser::parse_and_visit_typescript_type_expression(
case Token_Type::kw_type:
case Token_Type::kw_yield:
case Token_Type::identifier: {
Token_Type name_type = this->peek().type;
Identifier name = this->peek().identifier_name();
bool had_dot = false;
this->skip();
if (this->peek().type == Token_Type::kw_is) {
// param is Type
// this is Type
this->skip();
if (name_type != Token_Type::kw_this) {
// this is Type
v.visit_variable_type_predicate_use(name);
}
this->parse_and_visit_typescript_type_expression(v);
return;
}

bool had_dot = false;
while (this->peek().type == Token_Type::dot) {
// ns.Type
had_dot = true;
this->skip();
switch (this->peek().type) {
Expand Down Expand Up @@ -215,6 +268,7 @@ void Parser::parse_and_visit_typescript_type_expression(
// T extends infer U ? V : W
// T extends infer U extends X ? V : W
case Token_Type::kw_infer: {
Lexer_Transaction transaction = this->lexer_.begin_transaction();
Source_Code_Span infer_keyword_span = this->peek().span();
this->skip();
switch (this->peek().type) {
Expand All @@ -232,7 +286,6 @@ void Parser::parse_and_visit_typescript_type_expression(
case Token_Type::kw_global:
case Token_Type::kw_infer:
case Token_Type::kw_intrinsic:
case Token_Type::kw_is:
case Token_Type::kw_keyof:
case Token_Type::kw_module:
case Token_Type::kw_namespace:
Expand All @@ -248,10 +301,21 @@ void Parser::parse_and_visit_typescript_type_expression(
case Token_Type::kw_unique:
break;

// infer is // 'is' is the declared name.
// infer is Type // Type predicate where 'infer' is the parameter name.
case Token_Type::kw_is:
if (parse_options.allow_type_predicate) {
// infer is Type
this->lexer_.roll_back_transaction(std::move(transaction));
goto type_variable_or_namespace_or_type_predicate;
}
break;

default:
QLJS_PARSER_UNIMPLEMENTED();
break;
}
this->lexer_.commit_transaction(std::move(transaction));
Identifier variable = this->peek().identifier_name();
this->skip();

Expand Down Expand Up @@ -288,13 +352,22 @@ void Parser::parse_and_visit_typescript_type_expression(
}

// unique
// unique.prop
// unique symbol
case Token_Type::kw_unique:
// unique is Type
case Token_Type::kw_unique: {
Lexer_Transaction transaction = this->lexer_.begin_transaction();
this->skip();
if (parse_options.allow_type_predicate &&
this->peek().type == Token_Type::kw_is) {
// unique is Type
this->lexer_.roll_back_transaction(std::move(transaction));
goto type_variable_or_namespace_or_type_predicate;
}
this->lexer_.commit_transaction(std::move(transaction));
QLJS_PARSER_UNIMPLEMENTED_IF_NOT_TOKEN(Token_Type::kw_symbol);
this->skip();
break;
}

// [A, B, C]
case Token_Type::left_square:
Expand Down Expand Up @@ -477,10 +550,21 @@ void Parser::parse_and_visit_typescript_type_expression(
break;

// keyof Type
case Token_Type::kw_keyof:
// keyof is Type
case Token_Type::kw_keyof: {
Lexer_Transaction transaction = this->lexer_.begin_transaction();
this->skip();
if (parse_options.allow_type_predicate &&
this->peek().type == Token_Type::kw_is) {
// keyof is Type
this->lexer_.roll_back_transaction(std::move(transaction));
goto type_variable_or_namespace_or_type_predicate;
}
// keyof Type
this->lexer_.commit_transaction(std::move(transaction));
this->parse_and_visit_typescript_type_expression(v, parse_options);
break;
}

// import("module").Name
case Token_Type::kw_import: {
Expand Down Expand Up @@ -620,48 +704,11 @@ void Parser::parse_and_visit_typescript_colon_type_expression_or_type_predicate(

void Parser::parse_and_visit_typescript_type_expression_or_type_predicate(
Parse_Visitor_Base &v, bool allow_parenthesized_type) {
switch (this->peek().type) {
// param is Type
// Type
QLJS_CASE_CONTEXTUAL_KEYWORD:
QLJS_CASE_STRICT_ONLY_RESERVED_KEYWORD:
case Token_Type::identifier:
case Token_Type::kw_await:
case Token_Type::kw_this:
case Token_Type::kw_yield: {
Token_Type parameter_type = this->peek().type;
Identifier parameter_name = this->peek().identifier_name();
Lexer_Transaction transaction = this->lexer_.begin_transaction();
this->skip();
if (this->peek().type == Token_Type::kw_is) {
// param is Type
this->lexer_.commit_transaction(std::move(transaction));
this->skip();
if (parameter_type != Token_Type::kw_this) {
v.visit_variable_type_predicate_use(parameter_name);
}
this->parse_and_visit_typescript_type_expression(v);
} else {
// Type
this->lexer_.roll_back_transaction(std::move(transaction));
this->parse_and_visit_typescript_type_expression(v);
}
break;
}

// {key: Value}
// typeof v
// (param, param) => ReturnType
// ((((param, param) => ReturnType)))
// // Only if allow_parenthesized_type:
// (typeexpr)
default:
this->parse_and_visit_typescript_type_expression(
v, TypeScript_Type_Parse_Options{
.allow_parenthesized_type = allow_parenthesized_type,
});
break;
}
this->parse_and_visit_typescript_type_expression(
v, TypeScript_Type_Parse_Options{
.allow_parenthesized_type = allow_parenthesized_type,
.allow_type_predicate = true,
});
}

void Parser::parse_and_visit_typescript_arrow_type_expression(
Expand Down
2 changes: 2 additions & 0 deletions src/quick-lint-js/fe/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ class Parser {
std::optional<Declaring_Type> type_being_declared = std::nullopt;
bool parse_question_as_invalid = true;
bool allow_parenthesized_type = true;
// If false, a type predicate is parsed but a diagnostic is reported.
bool allow_type_predicate = false;
};

void parse_and_visit_typescript_colon_type_expression(Parse_Visitor_Base &v);
Expand Down

0 comments on commit 15b65ab

Please sign in to comment.