Skip to content

Commit

Permalink
fix(typescript): allow nested TS module declare blocks
Browse files Browse the repository at this point in the history
The following is legal TypeScript:

    declare module "foo" {
        module "bar" {
        }
    }
  • Loading branch information
strager committed Oct 14, 2023
1 parent b25b200 commit 9576b8d
Show file tree
Hide file tree
Showing 9 changed files with 37 additions and 21 deletions.
5 changes: 5 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ Semantic Versioning.
* TypeScript support (still experimental):
* A newline after `public`, `protected`, `private`, or `readonly` inside a
class is now interpreted correctly.
* Nested `module` declarations no longer falsely report [E0361][]. E0361's
message has been changed:
* Before: "module with string name is only allowed at the top level"
* After: "TypeScript 'declare module' with string name is not allowed in
namespaces"

## 2.16.0 (2023-09-06)

Expand Down
2 changes: 1 addition & 1 deletion po/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1278,7 +1278,7 @@ msgid "string module name is only allowed with 'declare module'"
msgstr ""

#: src/quick-lint-js/diag/diagnostic-metadata-generated.cpp
msgid "module with string name is only allowed at the top level"
msgid "TypeScript 'declare module' with string name is not allowed in namespaces"
msgstr ""

#: src/quick-lint-js/diag/diagnostic-metadata-generated.cpp
Expand Down
2 changes: 1 addition & 1 deletion src/quick-lint-js/diag/diagnostic-metadata-generated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3801,7 +3801,7 @@ const QLJS_CONSTINIT Diagnostic_Info all_diagnostic_infos[] = {
.code = 361,
.severity = Diagnostic_Severity::error,
.message_formats = {
QLJS_TRANSLATABLE("module with string name is only allowed at the top level"),
QLJS_TRANSLATABLE("TypeScript 'declare module' with string name is not allowed in namespaces"),
},
.message_args = {
{
Expand Down
7 changes: 5 additions & 2 deletions src/quick-lint-js/diag/diagnostic-types-2.h
Original file line number Diff line number Diff line change
Expand Up @@ -1948,10 +1948,13 @@ struct Diag_String_Namespace_Name_Is_Only_Allowed_With_Declare_Module {
Source_Code_Span module_name;
};

// @@@ rename
struct Diag_String_Namespace_Name_Is_Only_Allowed_At_Top_Level {
[[qljs::diag("E0361", Diagnostic_Severity::error)]] //
[[qljs::message("module with string name is only allowed at the top level",
ARG(module_name))]] //
[
[qljs::message("TypeScript 'declare module' with string name is not "
"allowed in namespaces",
ARG(module_name))]] //
Source_Code_Span module_name;
};

Expand Down
3 changes: 2 additions & 1 deletion src/quick-lint-js/fe/parse-statement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2939,7 +2939,8 @@ Parser::parse_and_visit_typescript_namespace_or_module_head(
Diag_String_Namespace_Name_Is_Only_Allowed_With_Declare_Module{
.module_name = this->peek().span(),
});
} else if (this->in_typescript_namespace_or_module_) {
} else if (this->in_typescript_namespace_or_module_ &&
!this->in_typescript_module_) {
this->diag_reporter_->report(
Diag_String_Namespace_Name_Is_Only_Allowed_At_Top_Level{
.module_name = this->peek().span(),
Expand Down
6 changes: 3 additions & 3 deletions src/quick-lint-js/i18n/translation-table-generated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ const Translation_Table translation_data = {
{0, 0, 0, 0, 0, 59}, //
{0, 0, 0, 0, 0, 57}, //
{0, 0, 0, 0, 0, 57}, //
{0, 0, 0, 0, 0, 74}, //
{0, 0, 0, 0, 0, 54}, //
{0, 0, 0, 78, 0, 58}, //
{0, 0, 0, 0, 0, 53}, //
Expand Down Expand Up @@ -396,8 +397,7 @@ const Translation_Table translation_data = {
{72, 55, 79, 36, 61, 37}, //
{33, 41, 45, 43, 34, 34}, //
{20, 29, 0, 26, 0, 22}, //
{0, 0, 0, 0, 0, 48}, //
{0, 0, 0, 52, 0, 57}, //
{0, 0, 0, 52, 0, 48}, //
{0, 0, 0, 0, 0, 51}, //
{69, 26, 0, 59, 0, 22}, //
{0, 0, 0, 46, 0, 39}, //
Expand Down Expand Up @@ -1910,6 +1910,7 @@ const Translation_Table translation_data = {
u8"TypeScript 'declare function' is not allowed in JavaScript\0"
u8"TypeScript 'declare global' is not allowed in JavaScript\0"
u8"TypeScript 'declare global' is not allowed in namespaces\0"
u8"TypeScript 'declare module' with string name is not allowed in namespaces\0"
u8"TypeScript 'declare {1}' is not allowed in JavaScript\0"
u8"TypeScript 'declare' fields are now allowed in JavaScript\0"
u8"TypeScript 'implements' is not allowed in JavaScript\0"
Expand Down Expand Up @@ -2189,7 +2190,6 @@ const Translation_Table translation_data = {
u8"missing value for object property\0"
u8"missing variable name\0"
u8"misspelled React attribute; write '{1}' instead\0"
u8"module with string name is only allowed at the top level\0"
u8"move the 'extends' clause before 'implements' here\0"
u8"namespace starts here\0"
u8"new variable shadows existing variable\0"
Expand Down
4 changes: 2 additions & 2 deletions src/quick-lint-js/i18n/translation-table-generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ 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 = 522;
constexpr std::size_t translation_table_string_table_size = 79924;
constexpr std::size_t translation_table_string_table_size = 79941;
constexpr std::size_t translation_table_locale_table_size = 35;

QLJS_CONSTEVAL std::uint16_t translation_table_const_look_up(
Expand Down Expand Up @@ -132,6 +132,7 @@ QLJS_CONSTEVAL std::uint16_t translation_table_const_look_up(
"TypeScript 'declare function' is not allowed in JavaScript"sv,
"TypeScript 'declare global' is not allowed in JavaScript"sv,
"TypeScript 'declare global' is not allowed in namespaces"sv,
"TypeScript 'declare module' with string name is not allowed in namespaces"sv,
"TypeScript 'declare {1}' is not allowed in JavaScript"sv,
"TypeScript 'declare' fields are now allowed in JavaScript"sv,
"TypeScript 'implements' is not allowed in JavaScript"sv,
Expand Down Expand Up @@ -411,7 +412,6 @@ QLJS_CONSTEVAL std::uint16_t translation_table_const_look_up(
"missing value for object property"sv,
"missing variable name"sv,
"misspelled React attribute; write '{1}' instead"sv,
"module with string name is only allowed at the top level"sv,
"move the 'extends' clause before 'implements' here"sv,
"namespace starts here"sv,
"new variable shadows existing variable"sv,
Expand Down
22 changes: 11 additions & 11 deletions src/quick-lint-js/i18n/translation-table-test-generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,17 @@ inline const Translated_String test_translation_table[521] = {
u8"TypeScript 'declare global' is not allowed in namespaces",
},
},
{
"TypeScript 'declare module' with string name is not allowed in namespaces"_translatable,
u8"TypeScript 'declare module' with string name is not allowed in namespaces",
{
u8"TypeScript 'declare module' with string name is not allowed in namespaces",
u8"TypeScript 'declare module' with string name is not allowed in namespaces",
u8"TypeScript 'declare module' with string name is not allowed in namespaces",
u8"TypeScript 'declare module' with string name is not allowed in namespaces",
u8"TypeScript 'declare module' with string name is not allowed in namespaces",
},
},
{
"TypeScript 'declare {1}' is not allowed in JavaScript"_translatable,
u8"TypeScript 'declare {1}' is not allowed in JavaScript",
Expand Down Expand Up @@ -4263,17 +4274,6 @@ inline const Translated_String test_translation_table[521] = {
u8"misspelled React attribute; write '{1}' instead",
},
},
{
"module with string name is only allowed at the top level"_translatable,
u8"module with string name is only allowed at the top level",
{
u8"module with string name is only allowed at the top level",
u8"module with string name is only allowed at the top level",
u8"module with string name is only allowed at the top level",
u8"module with string name is only allowed at the top level",
u8"module with string name is only allowed at the top level",
},
},
{
"move the 'extends' clause before 'implements' here"_translatable,
u8"move the 'extends' clause before 'implements' here",
Expand Down
7 changes: 7 additions & 0 deletions test/test-parse-typescript-declare-tsmodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ TEST_F(Test_Parse_TypeScript_Declare_TSModule,
typescript_options);
}

TEST_F(Test_Parse_TypeScript_Declare_TSModule,
declaring_module_is_allowed_inside_containing_module) {
test_parse_and_visit_module(
u8"declare module 'outer' { module 'inner' {} }"_sv, no_diags,
typescript_options);
}

TEST_F(Test_Parse_TypeScript_Declare_TSModule,
declare_module_allows_import_from_module) {
{
Expand Down

0 comments on commit 9576b8d

Please sign in to comment.