Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unexpected 'await' keyword on function declaration #1187

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/errors/E0719.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# E0719: unexpected 'await' keyword on function declaration

If you meant to define an asynchronous function, you need to explicitly annotate
the function with `async` keyword, not `await`.

```javascript
await function f() {}
```

To fix this warning, simply replace `await` by `async`. Now, you have an
asynchronous function and you're able to use `await` inside it.

```javascript
async function f() {}
```
4 changes: 4 additions & 0 deletions po/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -2417,6 +2417,10 @@ msgstr ""
msgid "using a '.' after a '?.' might fail, since '?.' might return 'undefined'."
msgstr ""

#: src/quick-lint-js/diag/diagnostic-metadata-generated.cpp
msgid "unexpected 'await' keyword on function declaration; maybe you meant 'async'?"
msgstr ""

#: test/test-diagnostic-formatter.cpp
#: test/test-vim-qflist-json-diag-reporter.cpp
msgid "something happened"
Expand Down
14 changes: 14 additions & 0 deletions src/quick-lint-js/diag/diagnostic-metadata-generated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6927,6 +6927,20 @@ const QLJS_CONSTINIT Diagnostic_Info all_diagnostic_infos[] = {
},
},
},

// Diag_Unexpected_Await_On_Function_Declaration
{
.code = 719,
.severity = Diagnostic_Severity::warning,
.message_formats = {
QLJS_TRANSLATABLE("unexpected 'await' keyword on function declaration; maybe you meant 'async'?"),
},
.message_args = {
{
Diagnostic_Message_Arg_Info(offsetof(Diag_Unexpected_Await_On_Function_Declaration, await_keyword), Diagnostic_Arg_Type::source_code_span),
},
},
},
};
}

Expand Down
3 changes: 2 additions & 1 deletion src/quick-lint-js/diag/diagnostic-metadata-generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -473,10 +473,11 @@ namespace quick_lint_js {
QLJS_DIAG_TYPE_NAME(Diag_Unintuitive_Bitshift_Precedence) \
QLJS_DIAG_TYPE_NAME(Diag_TypeScript_Namespace_Alias_Cannot_Use_Import_Type) \
QLJS_DIAG_TYPE_NAME(Diag_Using_Dot_After_Optional_Chaining) \
QLJS_DIAG_TYPE_NAME(Diag_Unexpected_Await_On_Function_Declaration) \
/* END */
// clang-format on

inline constexpr int Diag_Type_Count = 462;
inline constexpr int Diag_Type_Count = 463;

extern const Diagnostic_Info all_diagnostic_infos[Diag_Type_Count];
}
Expand Down
9 changes: 9 additions & 0 deletions src/quick-lint-js/diag/diagnostic-types-2.h
Original file line number Diff line number Diff line change
Expand Up @@ -3599,6 +3599,15 @@ struct Diag_Using_Dot_After_Optional_Chaining {
Source_Code_Span dot_op;
Source_Code_Span optional_chain_op;
};

struct Diag_Unexpected_Await_On_Function_Declaration {
[[qljs::diag("E0719", Diagnostic_Severity::warning)]] //
// clang-format off
[[qljs::message("unexpected 'await' keyword on function declaration; maybe you meant 'async'?",
ARG(await_keyword))]] //
// clang-format on
Source_Code_Span await_keyword;
};
}
QLJS_WARNING_POP

Expand Down
14 changes: 14 additions & 0 deletions src/quick-lint-js/fe/parse-statement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,20 @@ bool Parser::parse_and_visit_statement(Parse_Visitor_Base &v,
this->skip();
this->check_body_after_label();
goto parse_statement;
} else if (this->peek().type == Token_Type::kw_function) {
this->parse_and_visit_function_declaration(
v, Function_Declaration_Options{
.attributes = Function_Attributes::normal,
.begin = this->peek().begin,
.require_name = Name_Requirement::required_for_statement,
.async_keyword = std::nullopt,
.declare_keyword = std::nullopt,
});
if (this->peek().type != Token_Type::left_paren) {
this->diag_reporter_->report(
Diag_Unexpected_Await_On_Function_Declaration{
.await_keyword = await_token.span()});
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must fix: Your special handling of function is wrong in a few ways:

  • await function foo() {}(); is parsed incorrectly. You have a test for it, but test_parse_and_visit_statement doesn't assert that the whole input is parsed. (It probably should... I should fix that.) This code is parsing await function foo() {} as a statement, leaving (); as a second statement (which is wrong!).
  • await function () {} falsely reports E0061 ("missing name in function statement").

Also, adding code here in parse_and_visit_expression doesn't catch mistakes such as the following:

foo(await function() {
});

parse_await_expression is probably a better place to put your logic.

} else {
Expression *ast =
this->parse_await_expression(v, await_token, Precedence{});
Expand Down
4 changes: 3 additions & 1 deletion src/quick-lint-js/i18n/translation-table-generated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,8 @@ const Translation_Table translation_data = {
{0, 0, 0, 0, 0, 15}, //
{0, 0, 0, 15, 0, 62}, //
{16, 12, 14, 32, 17, 34}, //
{31, 12, 34, 35, 33, 29}, //
{0, 0, 0, 0, 0, 29}, //
{31, 12, 34, 35, 33, 77}, //
{53, 46, 52, 49, 41, 43}, //
{45, 43, 53, 32, 39, 33}, //
{56, 49, 55, 52, 44, 46}, //
Expand Down Expand Up @@ -2429,6 +2430,7 @@ const Translation_Table translation_data = {
u8"unexpected '?' in type; use '| void' to make an optional type\0"
u8"unexpected '?' when destructuring\0"
u8"unexpected '\\' in identifier\0"
u8"unexpected 'await' keyword on function declaration; maybe you meant 'async'?\0"
u8"unexpected 'case' outside switch statement\0"
u8"unexpected 'catch' without 'try'\0"
u8"unexpected 'default' outside switch statement\0"
Expand Down
5 changes: 3 additions & 2 deletions src/quick-lint-js/i18n/translation-table-generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ namespace quick_lint_js {
using namespace std::literals::string_view_literals;

constexpr std::uint32_t translation_table_locale_count = 5;
constexpr std::uint16_t translation_table_mapping_table_size = 606;
constexpr std::size_t translation_table_string_table_size = 82482;
constexpr std::uint16_t translation_table_mapping_table_size = 607;
constexpr std::size_t translation_table_string_table_size = 82559;
constexpr std::size_t translation_table_locale_table_size = 35;

QLJS_CONSTEVAL std::uint16_t translation_table_const_look_up(
Expand Down Expand Up @@ -572,6 +572,7 @@ QLJS_CONSTEVAL std::uint16_t translation_table_const_look_up(
"unexpected '?' in type; use '| void' to make an optional type"sv,
"unexpected '?' when destructuring"sv,
"unexpected '\\' in identifier"sv,
"unexpected 'await' keyword on function declaration; maybe you meant 'async'?"sv,
"unexpected 'case' outside switch statement"sv,
"unexpected 'catch' without 'try'"sv,
"unexpected 'default' outside switch statement"sv,
Expand Down
13 changes: 12 additions & 1 deletion src/quick-lint-js/i18n/translation-table-test-generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct Translated_String {
};

// clang-format off
inline const Translated_String test_translation_table[605] = {
inline const Translated_String test_translation_table[606] = {
{
"\"global-groups\" entries must be strings"_translatable,
u8"\"global-groups\" entries must be strings",
Expand Down Expand Up @@ -6034,6 +6034,17 @@ inline const Translated_String test_translation_table[605] = {
u8"of\u00f6rv\u00e4ntad '\\' i identifierare",
},
},
{
"unexpected 'await' keyword on function declaration; maybe you meant 'async'?"_translatable,
u8"unexpected 'await' keyword on function declaration; maybe you meant 'async'?",
{
u8"unexpected 'await' keyword on function declaration; maybe you meant 'async'?",
u8"unexpected 'await' keyword on function declaration; maybe you meant 'async'?",
u8"unexpected 'await' keyword on function declaration; maybe you meant 'async'?",
u8"unexpected 'await' keyword on function declaration; maybe you meant 'async'?",
u8"unexpected 'await' keyword on function declaration; maybe you meant 'async'?",
},
},
{
"unexpected 'case' outside switch statement"_translatable,
u8"unexpected 'case' outside switch statement",
Expand Down
15 changes: 15 additions & 0 deletions test/test-parse-function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1799,6 +1799,21 @@ TEST_F(Test_Parse_Function, return_with_comma_operator_missing_arguments) {
u8" ^ Diag_Missing_Operand_For_Operator"_diag);
}
}

TEST_F(Test_Parse_Function, unexpected_await_on_function_declaration) {
test_parse_and_visit_statement(
u8"await function f() { }"_sv, //
u8"^^^^^ Diag_Unexpected_Await_On_Function_Declaration"_diag);

test_parse_and_visit_statement(u8"async function f() { }"_sv, //
no_diags);

test_parse_and_visit_statement(u8"await function foo() {}();"_sv, //
no_diags);

test_parse_and_visit_statement(u8"await (function foo() {});"_sv, //
no_diags);
}
}

// quick-lint-js finds bugs in JavaScript programs.
Expand Down
Loading