From a21013dced4f91b04b6ed3e478e7c731a9f59946 Mon Sep 17 00:00:00 2001 From: Valekh Bayramov Date: Sat, 30 Dec 2023 19:23:48 +0300 Subject: [PATCH] feat(typescript): add new diag for "import type" with namespace alias --- docs/errors/E0717.md | 19 +++++++++++++++++++ po/messages.pot | 4 ++++ .../diag/diagnostic-metadata-generated.cpp | 14 ++++++++++++++ .../diag/diagnostic-metadata-generated.h | 3 ++- src/quick-lint-js/diag/diagnostic-types-2.h | 7 +++++++ src/quick-lint-js/fe/parse-statement.cpp | 17 ++++++++++++----- .../i18n/translation-table-generated.cpp | 2 ++ .../i18n/translation-table-generated.h | 5 +++-- .../i18n/translation-table-test-generated.h | 13 ++++++++++++- test/test-parse-typescript-namespace.cpp | 14 ++++++++++++++ 10 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 docs/errors/E0717.md diff --git a/docs/errors/E0717.md b/docs/errors/E0717.md new file mode 100644 index 0000000000..1107ff5072 --- /dev/null +++ b/docs/errors/E0717.md @@ -0,0 +1,19 @@ +# E0717: namespace alias cannot use 'import type' + + +The error message "namespace alias cannot use 'import type'" occurs when +trying to use the 'import type' syntax with an alias. + + +```typescript +import type A = ns; +``` + + +To fix this error, you need to use the regular 'import' syntax instead +of 'import type' when using an alias. + + +```typescript +import A = ns; +``` \ No newline at end of file diff --git a/po/messages.pot b/po/messages.pot index a3f6633a21..087680b8e8 100644 --- a/po/messages.pot +++ b/po/messages.pot @@ -2401,6 +2401,10 @@ msgstr "" msgid "'&' here" msgstr "" +#: src/quick-lint-js/diag/diagnostic-metadata-generated.cpp +msgid "namespace alias cannot use 'import type'" +msgstr "" + #: test/test-diagnostic-formatter.cpp #: test/test-vim-qflist-json-diag-reporter.cpp msgid "something happened" diff --git a/src/quick-lint-js/diag/diagnostic-metadata-generated.cpp b/src/quick-lint-js/diag/diagnostic-metadata-generated.cpp index 15f87ab6aa..a7d7073ff3 100644 --- a/src/quick-lint-js/diag/diagnostic-metadata-generated.cpp +++ b/src/quick-lint-js/diag/diagnostic-metadata-generated.cpp @@ -6870,6 +6870,20 @@ const QLJS_CONSTINIT Diagnostic_Info all_diagnostic_infos[] = { }, }, }, + + // Diag_TypeScript_Namespace_Alias_Cannot_Use_Import_Type + { + .code = 717, + .severity = Diagnostic_Severity::error, + .message_formats = { + QLJS_TRANSLATABLE("namespace alias cannot use 'import type'"), + }, + .message_args = { + { + Diagnostic_Message_Arg_Info(offsetof(Diag_TypeScript_Namespace_Alias_Cannot_Use_Import_Type, type_keyword), Diagnostic_Arg_Type::source_code_span), + }, + }, + }, }; } diff --git a/src/quick-lint-js/diag/diagnostic-metadata-generated.h b/src/quick-lint-js/diag/diagnostic-metadata-generated.h index 12d28d2fdd..ad3ff48c18 100644 --- a/src/quick-lint-js/diag/diagnostic-metadata-generated.h +++ b/src/quick-lint-js/diag/diagnostic-metadata-generated.h @@ -469,10 +469,11 @@ namespace quick_lint_js { QLJS_DIAG_TYPE_NAME(Diag_Class_Async_On_Getter_Or_Setter) \ QLJS_DIAG_TYPE_NAME(Diag_Multiple_Export_Defaults) \ QLJS_DIAG_TYPE_NAME(Diag_Unintuitive_Bitshift_Precedence) \ + QLJS_DIAG_TYPE_NAME(Diag_TypeScript_Namespace_Alias_Cannot_Use_Import_Type) \ /* END */ // clang-format on -inline constexpr int Diag_Type_Count = 458; +inline constexpr int Diag_Type_Count = 459; extern const Diagnostic_Info all_diagnostic_infos[Diag_Type_Count]; } diff --git a/src/quick-lint-js/diag/diagnostic-types-2.h b/src/quick-lint-js/diag/diagnostic-types-2.h index 4397a0dd66..032ab7f987 100644 --- a/src/quick-lint-js/diag/diagnostic-types-2.h +++ b/src/quick-lint-js/diag/diagnostic-types-2.h @@ -3567,6 +3567,13 @@ struct Diag_Unintuitive_Bitshift_Precedence { Source_Code_Span bitshift_operator; Source_Code_Span and_operator; }; + +struct Diag_TypeScript_Namespace_Alias_Cannot_Use_Import_Type { + [[qljs::diag("E0717", Diagnostic_Severity::error)]] // + [[qljs::message("namespace alias cannot use 'import type'", + ARG(type_keyword))]] // + Source_Code_Span type_keyword; +}; } QLJS_WARNING_POP diff --git a/src/quick-lint-js/fe/parse-statement.cpp b/src/quick-lint-js/fe/parse-statement.cpp index 03c7852b92..235eaef229 100644 --- a/src/quick-lint-js/fe/parse-statement.cpp +++ b/src/quick-lint-js/fe/parse-statement.cpp @@ -4607,6 +4607,7 @@ void Parser::parse_and_visit_import( } }; + std::optional type_span = std::nullopt; switch (this->peek().type) { // import var from "module"; // Invalid. QLJS_CASE_RESERVED_KEYWORD_EXCEPT_AWAIT_AND_YIELD: @@ -4703,12 +4704,12 @@ void Parser::parse_and_visit_import( // import type from "module"; case Token_Type::kw_type: { // Do not set is_current_typescript_namespace_non_empty_. - Source_Code_Span type_span = this->peek().span(); + type_span = this->peek().span(); auto report_type_only_import_in_javascript_if_needed = [&] { if (!this->options_.typescript) { this->diag_reporter_->report( Diag_TypeScript_Type_Import_Not_Allowed_In_JavaScript{ - .type_keyword = type_span, + .type_keyword = *type_span, }); } }; @@ -4741,7 +4742,7 @@ void Parser::parse_and_visit_import( case Token_Type::left_curly: this->diag_reporter_->report( Diag_TypeScript_Type_Only_Import_Cannot_Import_Default_And_Named{ - .type_keyword = type_span, + .type_keyword = *type_span, }); // Parse the named exports as if 'type' didn't exist. The user might // be thinking that 'type' only applies to 'T' and not '{U}'. @@ -4752,7 +4753,7 @@ void Parser::parse_and_visit_import( case Token_Type::star: this->diag_reporter_->report( Diag_TypeScript_Type_Only_Import_Cannot_Import_Default_And_Named{ - .type_keyword = type_span, + .type_keyword = *type_span, }); this->parse_and_visit_name_space_import(v); break; @@ -4771,7 +4772,7 @@ void Parser::parse_and_visit_import( this->lexer_.commit_transaction(std::move(transaction)); report_type_only_import_in_javascript_if_needed(); this->parse_and_visit_named_exports_for_typescript_type_only_import( - v, type_span); + v, *type_span); break; // import type * as M from "module"; // TypeScript only @@ -4855,6 +4856,12 @@ void Parser::parse_and_visit_import( QLJS_PARSER_UNIMPLEMENTED_IF_NOT_TOKEN(Token_Type::right_paren); this->skip(); } else { + if (declared_variable_kind == Variable_Kind::_import_type) { + // import type a = b; // Invalid. + this->diag_reporter_->report( + Diag_TypeScript_Namespace_Alias_Cannot_Use_Import_Type{ + .type_keyword = *type_span}); + } // import myns = ns; // import C = ns.C; declared_variable_kind = Variable_Kind::_import_alias; diff --git a/src/quick-lint-js/i18n/translation-table-generated.cpp b/src/quick-lint-js/i18n/translation-table-generated.cpp index 653b6f43a0..b4f1bbe7db 100644 --- a/src/quick-lint-js/i18n/translation-table-generated.cpp +++ b/src/quick-lint-js/i18n/translation-table-generated.cpp @@ -457,6 +457,7 @@ const Translation_Table translation_data = { {0, 0, 0, 0, 0, 51}, // {0, 0, 0, 0, 0, 47}, // {0, 0, 0, 0, 0, 10}, // + {0, 0, 0, 0, 0, 41}, // {69, 26, 0, 59, 0, 22}, // {0, 0, 0, 46, 0, 39}, // {0, 0, 0, 0, 0, 40}, // @@ -2324,6 +2325,7 @@ const Translation_Table translation_data = { u8"move the 'extends' clause before 'implements' here\0" u8"move the parameter decorator before '{0}' here\0" u8"namespace\0" + u8"namespace alias cannot use 'import type'\0" u8"namespace starts here\0" u8"new variable shadows existing variable\0" u8"newline is not allowed after 'abstract'\0" diff --git a/src/quick-lint-js/i18n/translation-table-generated.h b/src/quick-lint-js/i18n/translation-table-generated.h index f410cfda3e..a9bfbb5dfb 100644 --- a/src/quick-lint-js/i18n/translation-table-generated.h +++ b/src/quick-lint-js/i18n/translation-table-generated.h @@ -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 = 602; -constexpr std::size_t translation_table_string_table_size = 82276; +constexpr std::uint16_t translation_table_mapping_table_size = 603; +constexpr std::size_t translation_table_string_table_size = 82317; constexpr std::size_t translation_table_locale_table_size = 35; QLJS_CONSTEVAL std::uint16_t translation_table_const_look_up( @@ -471,6 +471,7 @@ QLJS_CONSTEVAL std::uint16_t translation_table_const_look_up( "move the 'extends' clause before 'implements' here"sv, "move the parameter decorator before '{0}' here"sv, "namespace"sv, + "namespace alias cannot use 'import type'"sv, "namespace starts here"sv, "new variable shadows existing variable"sv, "newline is not allowed after 'abstract'"sv, diff --git a/src/quick-lint-js/i18n/translation-table-test-generated.h b/src/quick-lint-js/i18n/translation-table-test-generated.h index 00551ea639..862694bf27 100644 --- a/src/quick-lint-js/i18n/translation-table-test-generated.h +++ b/src/quick-lint-js/i18n/translation-table-test-generated.h @@ -27,7 +27,7 @@ struct Translated_String { }; // clang-format off -inline const Translated_String test_translation_table[601] = { +inline const Translated_String test_translation_table[602] = { { "\"global-groups\" entries must be strings"_translatable, u8"\"global-groups\" entries must be strings", @@ -4923,6 +4923,17 @@ inline const Translated_String test_translation_table[601] = { u8"namespace", }, }, + { + "namespace alias cannot use 'import type'"_translatable, + u8"namespace alias cannot use 'import type'", + { + u8"namespace alias cannot use 'import type'", + u8"namespace alias cannot use 'import type'", + u8"namespace alias cannot use 'import type'", + u8"namespace alias cannot use 'import type'", + u8"namespace alias cannot use 'import type'", + }, + }, { "namespace starts here"_translatable, u8"namespace starts here", diff --git a/test/test-parse-typescript-namespace.cpp b/test/test-parse-typescript-namespace.cpp index aa16025480..3272d5ea9b 100644 --- a/test/test-parse-typescript-namespace.cpp +++ b/test/test-parse-typescript-namespace.cpp @@ -668,6 +668,20 @@ TEST_F(Test_Parse_TypeScript_Namespace, } } +TEST_F(Test_Parse_TypeScript_Namespace, + namespace_alias_cannot_use_import_type) { + { + Spy_Visitor p = test_parse_and_visit_statement( + u8"import type A = ns;"_sv, // + u8" ^^^^ Diag_TypeScript_Namespace_Alias_Cannot_Use_Import_Type.type_keyword"_diag, + typescript_options); + EXPECT_THAT(p.visits, ElementsAreArray({ + "visit_variable_declaration", // A + "visit_variable_namespace_use", // ns + })); + } +} + TEST_F(Test_Parse_TypeScript_Namespace, namespace_alias_cannot_be_used_with_declare_keyword) { {