Skip to content

Commit 12be35f

Browse files
committed
feat(typescript): parse 'export' inside 'declare namespace'
1 parent 4b1782a commit 12be35f

14 files changed

+414
-36
lines changed

po/de.po

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,14 +317,18 @@ msgid "'declare' here"
317317
msgstr "Funktionsaufruf beginnt hier"
318318

319319
#: src/quick-lint-js/diag/diagnostic-types.h
320-
msgid "cannot import a module from inside a 'declare namespace'"
320+
msgid "cannot 'export default' from inside a 'declare namespace'"
321321
msgstr ""
322322

323323
#: src/quick-lint-js/diag/diagnostic-types.h
324324
#, fuzzy
325325
msgid "'declare namespace' starts here"
326326
msgstr "Array beginnt hier"
327327

328+
#: src/quick-lint-js/diag/diagnostic-types.h
329+
msgid "cannot import a module from inside a 'declare namespace'"
330+
msgstr ""
331+
328332
#: src/quick-lint-js/diag/diagnostic-types.h
329333
#, fuzzy
330334
msgid "'declare {1}' cannot have initializer"

po/[email protected]

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,14 +272,18 @@ msgid "'declare' here"
272272
msgstr "IIFE started here"
273273

274274
#: src/quick-lint-js/diag/diagnostic-types.h
275-
msgid "cannot import a module from inside a 'declare namespace'"
275+
msgid "cannot 'export default' from inside a 'declare namespace'"
276276
msgstr ""
277277

278278
#: src/quick-lint-js/diag/diagnostic-types.h
279279
#, fuzzy
280280
msgid "'declare namespace' starts here"
281281
msgstr "you opened Pandora's Box here"
282282

283+
#: src/quick-lint-js/diag/diagnostic-types.h
284+
msgid "cannot import a module from inside a 'declare namespace'"
285+
msgstr ""
286+
283287
#: src/quick-lint-js/diag/diagnostic-types.h
284288
#, fuzzy
285289
msgid "'declare {1}' cannot have initializer"

po/fr_FR.po

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,14 +325,18 @@ msgid "'declare' here"
325325
msgstr "appel de fonction débuté ici"
326326

327327
#: src/quick-lint-js/diag/diagnostic-types.h
328-
msgid "cannot import a module from inside a 'declare namespace'"
328+
msgid "cannot 'export default' from inside a 'declare namespace'"
329329
msgstr ""
330330

331331
#: src/quick-lint-js/diag/diagnostic-types.h
332332
#, fuzzy
333333
msgid "'declare namespace' starts here"
334334
msgstr "tableau débuté ici"
335335

336+
#: src/quick-lint-js/diag/diagnostic-types.h
337+
msgid "cannot import a module from inside a 'declare namespace'"
338+
msgstr ""
339+
336340
#: src/quick-lint-js/diag/diagnostic-types.h
337341
#, fuzzy
338342
msgid "'declare {1}' cannot have initializer"

po/messages.pot

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,13 +260,17 @@ msgid "'declare' here"
260260
msgstr ""
261261

262262
#: src/quick-lint-js/diag/diagnostic-types.h
263-
msgid "cannot import a module from inside a 'declare namespace'"
263+
msgid "cannot 'export default' from inside a 'declare namespace'"
264264
msgstr ""
265265

266266
#: src/quick-lint-js/diag/diagnostic-types.h
267267
msgid "'declare namespace' starts here"
268268
msgstr ""
269269

270+
#: src/quick-lint-js/diag/diagnostic-types.h
271+
msgid "cannot import a module from inside a 'declare namespace'"
272+
msgstr ""
273+
270274
#: src/quick-lint-js/diag/diagnostic-types.h
271275
msgid "'declare {1}' cannot have initializer"
272276
msgstr ""

po/pt_BR.po

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,14 +273,18 @@ msgid "'declare' here"
273273
msgstr "'function' está aqui"
274274

275275
#: src/quick-lint-js/diag/diagnostic-types.h
276-
msgid "cannot import a module from inside a 'declare namespace'"
276+
msgid "cannot 'export default' from inside a 'declare namespace'"
277277
msgstr ""
278278

279279
#: src/quick-lint-js/diag/diagnostic-types.h
280280
#, fuzzy
281281
msgid "'declare namespace' starts here"
282282
msgstr "array iniciou aqui"
283283

284+
#: src/quick-lint-js/diag/diagnostic-types.h
285+
msgid "cannot import a module from inside a 'declare namespace'"
286+
msgstr ""
287+
284288
#: src/quick-lint-js/diag/diagnostic-types.h
285289
#, fuzzy
286290
msgid "'declare {1}' cannot have initializer"

po/sv_SE.po

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,14 +288,18 @@ msgid "'declare' here"
288288
msgstr "funktionkallelse startar här"
289289

290290
#: src/quick-lint-js/diag/diagnostic-types.h
291-
msgid "cannot import a module from inside a 'declare namespace'"
291+
msgid "cannot 'export default' from inside a 'declare namespace'"
292292
msgstr ""
293293

294294
#: src/quick-lint-js/diag/diagnostic-types.h
295295
#, fuzzy
296296
msgid "'declare namespace' starts here"
297297
msgstr "lista startar här"
298298

299+
#: src/quick-lint-js/diag/diagnostic-types.h
300+
msgid "cannot import a module from inside a 'declare namespace'"
301+
msgstr ""
302+
299303
#: src/quick-lint-js/diag/diagnostic-types.h
300304
#, fuzzy
301305
msgid "'declare {1}' cannot have initializer"

src/quick-lint-js/diag/diagnostic-types.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,16 +329,31 @@
329329
first_statement_token) \
330330
MESSAGE(QLJS_TRANSLATABLE("'declare' here"), declare_keyword)) \
331331
\
332+
QLJS_DIAG_TYPE( \
333+
diag_declare_namespace_cannot_export_default, "E0363", \
334+
diagnostic_severity::error, \
335+
{ \
336+
source_code_span default_keyword; \
337+
source_code_span declare_keyword; \
338+
}, \
339+
MESSAGE( \
340+
QLJS_TRANSLATABLE( \
341+
"cannot 'export default' from inside a 'declare namespace'"), \
342+
default_keyword) \
343+
MESSAGE(QLJS_TRANSLATABLE("'declare namespace' starts here"), \
344+
declare_keyword)) \
345+
\
332346
QLJS_DIAG_TYPE( \
333347
diag_declare_namespace_cannot_import_module, "E0362", \
334348
diagnostic_severity::error, \
335349
{ \
336-
source_code_span import_keyword; \
350+
/* importing_keyword is either 'import', 'from', or 'require'. */ \
351+
source_code_span importing_keyword; \
337352
source_code_span declare_keyword; \
338353
}, \
339354
MESSAGE(QLJS_TRANSLATABLE( \
340355
"cannot import a module from inside a 'declare namespace'"), \
341-
import_keyword) \
356+
importing_keyword) \
342357
MESSAGE(QLJS_TRANSLATABLE("'declare namespace' starts here"), \
343358
declare_keyword)) \
344359
\

src/quick-lint-js/fe/parse-statement.cpp

Lines changed: 81 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,12 @@ void parser::parse_end_of_expression_statement() {
828828
}
829829

830830
void parser::parse_and_visit_export(parse_visitor_base &v) {
831+
this->parse_and_visit_export(v, /*declare_keyword_span=*/std::nullopt);
832+
}
833+
834+
void parser::parse_and_visit_export(
835+
parse_visitor_base &v,
836+
std::optional<source_code_span> declare_namespace_declare_keyword) {
831837
QLJS_ASSERT(this->peek().type == token_type::kw_export);
832838
source_code_span export_token_span = this->peek().span();
833839
this->skip();
@@ -837,6 +843,12 @@ void parser::parse_and_visit_export(parse_visitor_base &v) {
837843
switch (this->peek().type) {
838844
// export default class C {}
839845
case token_type::kw_default:
846+
if (declare_namespace_declare_keyword.has_value()) {
847+
this->diag_reporter_->report(diag_declare_namespace_cannot_export_default{
848+
.default_keyword = this->peek().span(),
849+
.declare_keyword = *declare_namespace_declare_keyword,
850+
});
851+
}
840852
this->skip();
841853
switch (this->peek().type) {
842854
// export default async function f() {}
@@ -954,6 +966,12 @@ void parser::parse_and_visit_export(parse_visitor_base &v) {
954966
}
955967
}
956968
QLJS_PARSER_UNIMPLEMENTED_IF_NOT_TOKEN(token_type::kw_from);
969+
if (declare_namespace_declare_keyword.has_value()) {
970+
this->diag_reporter_->report(diag_declare_namespace_cannot_import_module{
971+
.importing_keyword = this->peek().span(),
972+
.declare_keyword = *declare_namespace_declare_keyword,
973+
});
974+
}
957975
this->skip();
958976
QLJS_PARSER_UNIMPLEMENTED_IF_NOT_TOKEN(token_type::string);
959977
this->skip();
@@ -974,6 +992,13 @@ void parser::parse_and_visit_export(parse_visitor_base &v) {
974992
/*out_exported_bad_tokens=*/&exported_bad_tokens);
975993
if (this->peek().type == token_type::kw_from) {
976994
// export {a, b, c} from "module";
995+
if (declare_namespace_declare_keyword.has_value()) {
996+
this->diag_reporter_->report(
997+
diag_declare_namespace_cannot_import_module{
998+
.importing_keyword = this->peek().span(),
999+
.declare_keyword = *declare_namespace_declare_keyword,
1000+
});
1001+
}
9771002
this->skip();
9781003
QLJS_PARSER_UNIMPLEMENTED_IF_NOT_TOKEN(token_type::string);
9791004
this->skip();
@@ -1008,23 +1033,34 @@ void parser::parse_and_visit_export(parse_visitor_base &v) {
10081033
}
10091034

10101035
// export async function f() {}
1011-
case token_type::kw_async: {
1012-
const char8 *async_token_begin = this->peek().begin;
1013-
this->skip();
1014-
QLJS_PARSER_UNIMPLEMENTED_IF_NOT_TOKEN(token_type::kw_function);
1015-
this->parse_and_visit_function_declaration(
1016-
v, function_attributes::async,
1017-
/*begin=*/async_token_begin,
1018-
/*require_name=*/name_requirement::required_for_export);
1036+
case token_type::kw_async:
1037+
if (declare_namespace_declare_keyword.has_value()) {
1038+
// declare namespace ns { export async function f(); }
1039+
this->parse_and_visit_declare_statement(
1040+
v, *declare_namespace_declare_keyword);
1041+
} else {
1042+
const char8 *async_token_begin = this->peek().begin;
1043+
this->skip();
1044+
QLJS_PARSER_UNIMPLEMENTED_IF_NOT_TOKEN(token_type::kw_function);
1045+
this->parse_and_visit_function_declaration(
1046+
v, function_attributes::async,
1047+
/*begin=*/async_token_begin,
1048+
/*require_name=*/name_requirement::required_for_export);
1049+
}
10191050
break;
1020-
}
10211051

10221052
// export function f() {}
10231053
case token_type::kw_function:
1024-
this->parse_and_visit_function_declaration(
1025-
v, function_attributes::normal,
1026-
/*begin=*/this->peek().begin,
1027-
/*require_name=*/name_requirement::required_for_export);
1054+
if (declare_namespace_declare_keyword.has_value()) {
1055+
// declare namespace ns { export function f(); }
1056+
this->parse_and_visit_declare_statement(
1057+
v, *declare_namespace_declare_keyword);
1058+
} else {
1059+
this->parse_and_visit_function_declaration(
1060+
v, function_attributes::normal,
1061+
/*begin=*/this->peek().begin,
1062+
/*require_name=*/name_requirement::required_for_export);
1063+
}
10281064
break;
10291065

10301066
// export class C {}
@@ -1033,6 +1069,7 @@ void parser::parse_and_visit_export(parse_visitor_base &v) {
10331069
v, parse_class_options{
10341070
.require_name = name_requirement::required_for_export,
10351071
.abstract_keyword_span = std::nullopt,
1072+
.declare_keyword_span = declare_namespace_declare_keyword,
10361073
});
10371074
break;
10381075

@@ -1051,6 +1088,7 @@ void parser::parse_and_visit_export(parse_visitor_base &v) {
10511088
v, parse_class_options{
10521089
.require_name = name_requirement::required_for_export,
10531090
.abstract_keyword_span = abstract_keyword,
1091+
.declare_keyword_span = declare_namespace_declare_keyword,
10541092
});
10551093
break;
10561094
}
@@ -1060,7 +1098,14 @@ void parser::parse_and_visit_export(parse_visitor_base &v) {
10601098
case token_type::kw_const:
10611099
case token_type::kw_let:
10621100
case token_type::kw_var:
1063-
this->parse_and_visit_variable_declaration_statement(v);
1101+
if (declare_namespace_declare_keyword.has_value()) {
1102+
// declare namespace ns { export let x; }
1103+
// declare namespace ns { export const enum E {} }
1104+
this->parse_and_visit_declare_statement(
1105+
v, *declare_namespace_declare_keyword);
1106+
} else {
1107+
this->parse_and_visit_variable_declaration_statement(v);
1108+
}
10641109
break;
10651110

10661111
// export interface I {} // TypeScript only.
@@ -1109,17 +1154,27 @@ void parser::parse_and_visit_export(parse_visitor_base &v) {
11091154
// export namespace ns {} // TypeScript only.
11101155
case token_type::kw_module:
11111156
case token_type::kw_namespace: {
1112-
source_code_span namespace_keyword = this->peek().span();
1113-
this->skip();
1114-
this->parse_and_visit_typescript_namespace(
1115-
v,
1116-
/*export_keyword_span=*/export_token_span, namespace_keyword);
1157+
if (declare_namespace_declare_keyword.has_value()) {
1158+
// declare namespace ns { export namespace subns {} }
1159+
this->parse_and_visit_typescript_declare_namespace(
1160+
v, *declare_namespace_declare_keyword);
1161+
} else {
1162+
// export namespace ns {}
1163+
source_code_span namespace_keyword = this->peek().span();
1164+
this->skip();
1165+
this->parse_and_visit_typescript_namespace(
1166+
v,
1167+
/*export_keyword_span=*/export_token_span, namespace_keyword);
1168+
}
11171169
break;
11181170
}
11191171

11201172
// export enum E {} // TypeScript only.
11211173
case token_type::kw_enum:
1122-
this->parse_and_visit_typescript_enum(v, enum_kind::normal);
1174+
this->parse_and_visit_typescript_enum(
1175+
v, declare_namespace_declare_keyword.has_value()
1176+
? enum_kind::declare_enum
1177+
: enum_kind::normal);
11231178
break;
11241179

11251180
// export stuff; // Invalid.
@@ -2144,6 +2199,10 @@ void parser::parse_and_visit_typescript_declare_namespace(
21442199
this->parse_and_visit_declare_statement(v, declare_keyword_span);
21452200
break;
21462201

2202+
case token_type::kw_export:
2203+
this->parse_and_visit_export(v, declare_keyword_span);
2204+
break;
2205+
21472206
case token_type::right_curly:
21482207
this->skip();
21492208
goto done;
@@ -3487,7 +3546,7 @@ void parser::parse_and_visit_import(
34873546
if (declare_namespace_declare_keyword.has_value()) {
34883547
this->diag_reporter_->report(
34893548
diag_declare_namespace_cannot_import_module{
3490-
.import_keyword = import_span,
3549+
.importing_keyword = import_span,
34913550
.declare_keyword = *declare_namespace_declare_keyword,
34923551
});
34933552
}
@@ -3539,7 +3598,7 @@ void parser::parse_and_visit_import(
35393598
case token_type::string:
35403599
if (declare_namespace_declare_keyword.has_value()) {
35413600
this->diag_reporter_->report(diag_declare_namespace_cannot_import_module{
3542-
.import_keyword = import_span,
3601+
.importing_keyword = import_span,
35433602
.declare_keyword = *declare_namespace_declare_keyword,
35443603
});
35453604
}

src/quick-lint-js/fe/parse.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,9 @@ class parser {
433433
parse_visitor_base &v, source_code_span type_keyword);
434434

435435
void parse_and_visit_export(parse_visitor_base &v);
436+
void parse_and_visit_export(
437+
parse_visitor_base &v,
438+
std::optional<source_code_span> declare_namespace_declare_keyword);
436439
void parse_and_visit_named_exports(
437440
parse_visitor_base &v,
438441
std::optional<source_code_span> typescript_type_only_keyword,

src/quick-lint-js/i18n/translation-table-generated.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ const translation_table translation_data = {
151151
{40, 35, 0, 70, 0, 55}, //
152152
{80, 38, 43, 55, 49, 56}, //
153153
{36, 32, 52, 35, 42, 36}, //
154-
{73, 51, 90, 53, 0, 50}, //
154+
{0, 0, 0, 0, 0, 50}, //
155+
{73, 51, 90, 53, 0, 58}, //
155156
{54, 29, 70, 63, 0, 47}, //
156157
{66, 38, 70, 71, 49, 49}, //
157158
{62, 81, 69, 64, 48, 45}, //
@@ -1865,6 +1866,7 @@ const translation_table translation_data = {
18651866
u8"attribute has wrong capitalization; write '{1}' instead\0"
18661867
u8"binary number literal has no digits\0"
18671868
u8"break can only be used inside of a loop or switch\0"
1869+
u8"cannot 'export default' from inside a 'declare namespace'\0"
18681870
u8"cannot access private identifier outside class\0"
18691871
u8"cannot assign to loop variable in for of/in loop\0"
18701872
u8"cannot declare 'await' inside async function\0"

0 commit comments

Comments
 (0)