From 204c04dbb99f489a7a192dd2b89a6b3fbbb45d21 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Thu, 16 Nov 2023 09:09:36 -0800 Subject: [PATCH] Add more package and import support. (#3402) This should cover: ``` library "lib" api; import Foo library default; import library default; import library "lib"; ``` This splits out `PackageName` and `LibraryName` to their own parse nodes so that checking can ignore them and still get a balanced parse tree (otherwise, we essentially need to implement handling of the parse nodes only to remove the identifiers/string literals -- the optional names mean we can't blindly do that as before). For reference, these nodes don't need to be handled because CheckParseTree will need to directly funnel import information along with checked IRs. --- toolchain/check/check.cpp | 147 +++++++--- toolchain/check/handle_import_and_package.cpp | 54 ++-- .../testdata/packages/explicit_imports.carbon | 35 ++- .../packages/fail_api_not_found.carbon | 14 +- .../packages/fail_import_default.carbon | 48 ++++ .../packages/fail_import_invalid.carbon | 56 ++-- .../packages/fail_import_repeat.carbon | 52 ++-- .../packages/fail_package_main.carbon | 11 +- .../testdata/packages/implicit_imports.carbon | 31 +-- toolchain/diagnostics/diagnostic_kind.def | 6 +- toolchain/parse/handle_decl_scope_loop.cpp | 6 +- toolchain/parse/handle_import_and_package.cpp | 253 +++++++++++------- toolchain/parse/node_kind.def | 29 +- toolchain/parse/state.def | 6 + .../packages/import/after_import.carbon | 4 +- .../packages/import/after_package.carbon | 4 +- .../testdata/packages/import/basic.carbon | 2 +- .../import/current_package_library.carbon | 32 +++ .../packages/import/fail_extra_string.carbon | 6 +- .../import/fail_library_is_identifier.carbon | 2 +- .../import/fail_library_skips_name.carbon | 18 -- .../import/fail_name_is_keyword.carbon | 2 +- .../packages/import/fail_no_name.carbon | 2 +- .../packages/import/fail_no_semi.carbon | 2 +- .../import/fail_omit_library_keyword.carbon | 2 +- .../testdata/packages/import/fail_type.carbon | 2 +- .../testdata/packages/import/library.carbon | 6 +- .../testdata/packages/import/ordering.carbon | 58 ++++ .../testdata/packages/library/basic.carbon | 32 +++ .../packages/library/fail_invalid_name.carbon | 78 ++++++ .../packages/library/fail_too_late.carbon | 26 ++ .../testdata/packages/package/api.carbon | 2 +- .../packages/package/api_library.carbon | 6 +- .../packages/package/fail_after_import.carbon | 2 +- .../package/fail_after_package.carbon | 2 +- .../packages/package/fail_extra_string.carbon | 6 +- .../package/fail_library_is_identifier.carbon | 2 +- .../packages/package/fail_no_semi.carbon | 2 +- .../packages/package/fail_no_type.carbon | 2 +- .../package/fail_omit_library_keyword.carbon | 2 +- .../testdata/packages/package/impl.carbon | 2 +- .../packages/package/impl_library.carbon | 6 +- 42 files changed, 762 insertions(+), 298 deletions(-) create mode 100644 toolchain/check/testdata/packages/fail_import_default.carbon create mode 100644 toolchain/parse/testdata/packages/import/current_package_library.carbon delete mode 100644 toolchain/parse/testdata/packages/import/fail_library_skips_name.carbon create mode 100644 toolchain/parse/testdata/packages/import/ordering.carbon create mode 100644 toolchain/parse/testdata/packages/library/basic.carbon create mode 100644 toolchain/parse/testdata/packages/library/fail_invalid_name.carbon create mode 100644 toolchain/parse/testdata/packages/library/fail_too_late.carbon diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index 51392d65458c..47ff599b0790 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -8,6 +8,7 @@ #include "toolchain/base/pretty_stack_trace_function.h" #include "toolchain/base/value_store.h" #include "toolchain/check/context.h" +#include "toolchain/parse/tree.h" #include "toolchain/parse/tree_node_location_translator.h" #include "toolchain/sem_ir/file.h" @@ -100,42 +101,48 @@ static auto CheckParseTree(const SemIR::File& builtin_ir, UnitInfo& unit_info, // The package and library names, used as map keys. using ImportKey = std::pair; -// Returns a key form of the package object. -static auto GetImportKey(UnitInfo& unit_info, - Parse::Tree::PackagingNames package) -> ImportKey { +// Returns a key form of the package object. file_package_id is only used for +// imports, not the main package directive; as a consequence, it will be invalid +// for the main package directive. +static auto GetImportKey(UnitInfo& unit_info, IdentifierId file_package_id, + Parse::Tree::PackagingNames names) -> ImportKey { auto* stores = unit_info.unit->value_stores; - return {package.package_id.is_valid() - ? stores->identifiers().Get(package.package_id) - : "", - package.library_id.is_valid() - ? stores->string_literals().Get(package.library_id) - : ""}; + llvm::StringRef package_name = + names.package_id.is_valid() ? stores->identifiers().Get(names.package_id) + : file_package_id.is_valid() ? stores->identifiers().Get(file_package_id) + : ""; + llvm::StringRef library_name = + names.library_id.is_valid() + ? stores->string_literals().Get(names.library_id) + : ""; + return {package_name, library_name}; } static constexpr llvm::StringLiteral ExplicitMainName = "Main"; // Marks an import as required on both the source and target file. -// TODO: When importing without a package name is supported, check that it's -// used correctly. +// +// The ID comparisons between the import and unit are okay because they both +// come from the same file. static auto TrackImport( llvm::DenseMap& api_map, llvm::DenseMap* explicit_import_map, UnitInfo& unit_info, Parse::Tree::PackagingNames import) -> void { - auto import_key = GetImportKey(unit_info, import); + const auto& packaging = unit_info.unit->parse_tree->packaging_directive(); - // Specialize the error for imports from `Main`. - if (import_key.first == ExplicitMainName) { - // Implicit imports will have already warned. - if (explicit_import_map) { - CARBON_DIAGNOSTIC(ImportMainPackage, Error, - "Cannot import `Main` from other packages."); - unit_info.emitter.Emit(import.node, ImportMainPackage); - } - return; - } + IdentifierId file_package_id = + packaging ? packaging->names.package_id : IdentifierId::Invalid; + auto import_key = GetImportKey(unit_info, file_package_id, import); + // True if the import has `Main` as the package name, even if it comes from + // the file's packaging (diagnostics may differentiate). + bool is_explicit_main = import_key.first == ExplicitMainName; + + // Explicit imports need more validation than implicit ones. We try to do + // these in an order of imports that should be removed, followed by imports + // that might be valid with syntax fixes. if (explicit_import_map) { - // Check for redundant imports. + // Diagnose redundant imports. if (auto [insert_it, success] = explicit_import_map->insert({import_key, import.node}); !success) { @@ -148,21 +155,74 @@ static auto TrackImport( return; } - // Check for explicit imports of the same library. The ID comparison is okay - // in this case because both come from the same file. - auto packaging = unit_info.unit->parse_tree->packaging_directive(); - if (packaging && import.package_id == packaging->names.package_id && - import.library_id == packaging->names.library_id) { + // True if the file's package is implicitly `Main` (by omitting an explicit + // package name). + bool is_file_implicit_main = + !packaging || !packaging->names.package_id.is_valid(); + // True if the import is using implicit "current package" syntax (by + // omitting an explicit package name). + bool is_import_implicit_current_package = !import.package_id.is_valid(); + // True if the import is using `default` library syntax. + bool is_import_default_library = !import.library_id.is_valid(); + // True if the import and file point at the same package, even by + // incorrectly specifying the current package name to `import`. + bool is_same_package = is_import_implicit_current_package || + import.package_id == file_package_id; + // True if the import points at the same library as the file's library. + bool is_same_library = + is_same_package && + (packaging ? import.library_id == packaging->names.library_id + : is_import_default_library); + + // Diagnose explicit imports of the same library, whether from `api` or + // `impl`. + if (is_same_library) { CARBON_DIAGNOSTIC(ExplicitImportApi, Error, "Explicit import of `api` from `impl` file is " "redundant with implicit import."); CARBON_DIAGNOSTIC(ImportSelf, Error, "File cannot import itself."); - unit_info.emitter.Emit( - import.node, packaging->api_or_impl == Parse::Tree::ApiOrImpl::Impl - ? ExplicitImportApi - : ImportSelf); + bool is_impl = + !packaging || packaging->api_or_impl == Parse::Tree::ApiOrImpl::Impl; + unit_info.emitter.Emit(import.node, + is_impl ? ExplicitImportApi : ImportSelf); return; } + + // Diagnose explicit imports of `Main//default`. There is no `api` for it. + // This lets other diagnostics handle explicit `Main` package naming. + if (is_file_implicit_main && is_import_implicit_current_package && + is_import_default_library) { + CARBON_DIAGNOSTIC(ImportMainDefaultLibrary, Error, + "Cannot import `Main//default`."); + unit_info.emitter.Emit(import.node, ImportMainDefaultLibrary); + + return; + } + + if (!is_import_implicit_current_package) { + // Diagnose explicit imports of the same package that use the package + // name. + if (is_same_package || (is_file_implicit_main && is_explicit_main)) { + CARBON_DIAGNOSTIC( + ImportCurrentPackageByName, Error, + "Imports from the current package must omit the package name."); + unit_info.emitter.Emit(import.node, ImportCurrentPackageByName); + return; + } + + // Diagnose explicit imports from `Main`. + if (is_explicit_main) { + CARBON_DIAGNOSTIC(ImportMainPackage, Error, + "Cannot import `Main` from other packages."); + unit_info.emitter.Emit(import.node, ImportMainPackage); + return; + } + } + } else if (is_explicit_main) { + // An implicit import with an explicit `Main` occurs when a `package` rule + // has bad syntax, which will have been diagnosed when building the API map. + // As a consequence, we return silently. + return; } if (auto api = api_map.find(import_key); api != api_map.end()) { @@ -194,15 +254,18 @@ auto CheckParseTrees(const SemIR::File& builtin_ir, // Create a map of APIs which might be imported. llvm::DenseMap api_map; for (auto& unit_info : unit_infos) { + // TODO: It may be good to validate filenames here, but that would have use + // put .impl.carbon on almost all tests (which are in `Main//default`). We + // should probably get leads direction on filenames before enforcing. const auto& packaging = unit_info.unit->parse_tree->packaging_directive(); if (packaging) { - auto import_key = GetImportKey(unit_info, packaging->names); - // Catch explicit `Main` errors before they become marked as possible + auto import_key = + GetImportKey(unit_info, IdentifierId::Invalid, packaging->names); + // Diagnose explicit `Main` uses before they become marked as possible // APIs. if (import_key.first == ExplicitMainName) { - CARBON_DIAGNOSTIC( - ExplicitMainPackage, Error, - "Default `Main` library must omit `package` directive."); + CARBON_DIAGNOSTIC(ExplicitMainPackage, Error, + "`Main//default` must omit `package` directive."); CARBON_DIAGNOSTIC( ExplicitMainLibrary, Error, "Use `library` directive in `Main` package libraries."); @@ -230,10 +293,12 @@ auto CheckParseTrees(const SemIR::File& builtin_ir, llvm::SmallVector ready_to_check; ready_to_check.reserve(units.size()); for (auto& unit_info : unit_infos) { - const auto& packaging = unit_info.unit->parse_tree->packaging_directive(); - if (packaging && packaging->api_or_impl == Parse::Tree::ApiOrImpl::Impl) { - // An `impl` has an implicit import of its `api`. - TrackImport(api_map, nullptr, unit_info, packaging->names); + if (const auto& packaging = + unit_info.unit->parse_tree->packaging_directive()) { + if (packaging->api_or_impl == Parse::Tree::ApiOrImpl::Impl) { + // An `impl` has an implicit import of its `api`. + TrackImport(api_map, nullptr, unit_info, packaging->names); + } } llvm::DenseMap explicit_import_map; diff --git a/toolchain/check/handle_import_and_package.cpp b/toolchain/check/handle_import_and_package.cpp index ded009468445..40a7ef01281d 100644 --- a/toolchain/check/handle_import_and_package.cpp +++ b/toolchain/check/handle_import_and_package.cpp @@ -14,14 +14,48 @@ auto HandleImportIntroducer(Context& /*context*/, Parse::Node /*parse_node*/) return true; } +auto HandleImportDirective(Context& /*context*/, Parse::Node /*parse_node*/) + -> bool { + return true; +} + +auto HandleLibraryIntroducer(Context& /*context*/, Parse::Node /*parse_node*/) + -> bool { + return true; +} + +auto HandleLibraryDirective(Context& /*context*/, Parse::Node /*parse_node*/) + -> bool { + return true; +} + auto HandlePackageIntroducer(Context& /*context*/, Parse::Node /*parse_node*/) -> bool { return true; } -auto HandleLibrary(Context& context, Parse::Node /*parse_node*/) -> bool { - // Pop and discard the library name from the node stack. - context.node_stack().Pop(); +auto HandlePackageDirective(Context& /*context*/, Parse::Node /*parse_node*/) + -> bool { + return true; +} + +auto HandleLibrarySpecifier(Context& /*context*/, Parse::Node /*parse_node*/) + -> bool { + return true; +} + +auto HandlePackageName(Context& /*context*/, Parse::Node /*parse_node*/) + -> bool { + return true; +} + +auto HandleLibraryName(Context& /*context*/, Parse::Node /*parse_node*/) + -> bool { + return true; +} + +auto HandleDefaultLibrary(Context& /*context*/, Parse::Node /*parse_node*/) + -> bool { return true; } @@ -35,18 +69,4 @@ auto HandlePackageImpl(Context& /*context*/, Parse::Node /*parse_node*/) return true; } -auto HandleImportDirective(Context& context, Parse::Node /*parse_node*/) - -> bool { - // Pop and discard the identifier from the node stack. - context.node_stack().Pop(); - return true; -} - -auto HandlePackageDirective(Context& context, Parse::Node /*parse_node*/) - -> bool { - // Pop and discard the identifier from the node stack. - context.node_stack().Pop(); - return true; -} - } // namespace Carbon::Check diff --git a/toolchain/check/testdata/packages/explicit_imports.carbon b/toolchain/check/testdata/packages/explicit_imports.carbon index b1077e470e80..fcd5b188b6bb 100644 --- a/toolchain/check/testdata/packages/explicit_imports.carbon +++ b/toolchain/check/testdata/packages/explicit_imports.carbon @@ -12,24 +12,37 @@ package Api api; package Api library "lib" api; -// --- import_api.carbon +// --- same_package.carbon + +package Api library "other" api; + +import library default; +import library "lib"; + +// --- different_package.carbon + +package ApiOther api; import Api; import Api library "lib"; +// --- main_lib_api.carbon + +library "lib" api; + +// --- main_import.carbon + +import library "lib"; + // CHECK:STDOUT: file "api.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "api_lib.carbon" { -// CHECK:STDOUT: %.loc2: String = string_literal "lib" // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String +// CHECK:STDOUT: file "same_package.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file "import_api.carbon" { -// CHECK:STDOUT: %.loc3: String = string_literal "lib" +// CHECK:STDOUT: file "different_package.carbon" { +// CHECK:STDOUT: } +// CHECK:STDOUT: file "main_lib_api.carbon" { +// CHECK:STDOUT: } +// CHECK:STDOUT: file "main_import.carbon" { // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/packages/fail_api_not_found.carbon b/toolchain/check/testdata/packages/fail_api_not_found.carbon index 72b0dba05926..11c9d66841de 100644 --- a/toolchain/check/testdata/packages/fail_api_not_found.carbon +++ b/toolchain/check/testdata/packages/fail_api_not_found.carbon @@ -18,12 +18,16 @@ package Foo impl; // CHECK:STDERR: ^ package Foo library "Bar" impl; +// --- no_api_main_lib.carbon + +// CHECK:STDERR: no_api_main_lib.carbon:[[@LINE+3]]:1: ERROR: Corresponding API not found. +// CHECK:STDERR: library "Bar" impl; +// CHECK:STDERR: ^ +library "Bar" impl; + // CHECK:STDOUT: file "no_api.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "no_api_lib.carbon" { -// CHECK:STDOUT: %.loc5: String = string_literal "Bar" +// CHECK:STDOUT: } +// CHECK:STDOUT: file "no_api_main_lib.carbon" { // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/packages/fail_import_default.carbon b/toolchain/check/testdata/packages/fail_import_default.carbon new file mode 100644 index 000000000000..805c75e1080d --- /dev/null +++ b/toolchain/check/testdata/packages/fail_import_default.carbon @@ -0,0 +1,48 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- default_api.carbon + +package A api; + +// CHECK:STDERR: default_api.carbon:[[@LINE+3]]:1: ERROR: File cannot import itself. +// CHECK:STDERR: import library default; +// CHECK:STDERR: ^ +import library default; + +// --- default_impl.carbon + +package A impl; + +// CHECK:STDERR: default_impl.carbon:[[@LINE+3]]:1: ERROR: Explicit import of `api` from `impl` file is redundant with implicit import. +// CHECK:STDERR: import library default; +// CHECK:STDERR: ^ +import library default; + +// --- main_import_default.carbon + +// CHECK:STDERR: main_import_default.carbon:[[@LINE+3]]:1: ERROR: Explicit import of `api` from `impl` file is redundant with implicit import. +// CHECK:STDERR: import library default; +// CHECK:STDERR: ^ +import library default; + +// --- main_lib_import_default.carbon + +library "lib" api; + +// CHECK:STDERR: main_lib_import_default.carbon:[[@LINE+3]]:1: ERROR: Cannot import `Main//default`. +// CHECK:STDERR: import library default; +// CHECK:STDERR: ^ +import library default; + +// CHECK:STDOUT: file "default_api.carbon" { +// CHECK:STDOUT: } +// CHECK:STDOUT: file "default_impl.carbon" { +// CHECK:STDOUT: } +// CHECK:STDOUT: file "main_import_default.carbon" { +// CHECK:STDOUT: } +// CHECK:STDOUT: file "main_lib_import_default.carbon" { +// CHECK:STDOUT: } diff --git a/toolchain/check/testdata/packages/fail_import_invalid.carbon b/toolchain/check/testdata/packages/fail_import_invalid.carbon index c8e847933f9c..58ebbaacbb2f 100644 --- a/toolchain/check/testdata/packages/fail_import_invalid.carbon +++ b/toolchain/check/testdata/packages/fail_import_invalid.carbon @@ -6,14 +6,28 @@ // --- main.carbon -// CHECK:STDERR: main.carbon:[[@LINE+3]]:1: ERROR: Cannot import `Main` from other packages. +// CHECK:STDERR: main.carbon:[[@LINE+3]]:1: ERROR: Imports from the current package must omit the package name. // CHECK:STDERR: import Main; // CHECK:STDERR: ^ import Main; // --- main_lib.carbon -// CHECK:STDERR: main_lib.carbon:[[@LINE+3]]:1: ERROR: Cannot import `Main` from other packages. +// CHECK:STDERR: main_lib.carbon:[[@LINE+3]]:1: ERROR: Imports from the current package must omit the package name. +// CHECK:STDERR: import Main library "lib"; +// CHECK:STDERR: ^ +import Main library "lib"; + +// --- not_main.carbon + +package NotMain api; + +// CHECK:STDERR: not_main.carbon:[[@LINE+3]]:1: ERROR: Cannot import `Main` from other packages. +// CHECK:STDERR: import Main; +// CHECK:STDERR: ^ +import Main; + +// CHECK:STDERR: not_main.carbon:[[@LINE+3]]:1: ERROR: Cannot import `Main` from other packages. // CHECK:STDERR: import Main library "lib"; // CHECK:STDERR: ^ import Main library "lib"; @@ -32,9 +46,9 @@ import This; package This library "lib" api; // CHECK:STDERR: this_lib.carbon:[[@LINE+3]]:1: ERROR: File cannot import itself. -// CHECK:STDERR: import This library "lib"; +// CHECK:STDERR: import library "lib"; // CHECK:STDERR: ^ -import This library "lib"; +import library "lib"; // --- implicit_api.carbon @@ -62,50 +76,30 @@ package Implicit library "lib" impl; // CHECK:STDERR: ^ import Implicit library "lib"; -// --- unknown.carbon +// --- not_found.carbon -// CHECK:STDERR: unknown.carbon:[[@LINE+3]]:1: ERROR: Imported API not found. -// CHECK:STDERR: import Unknown; +// CHECK:STDERR: not_found.carbon:[[@LINE+3]]:1: ERROR: Imported API not found. +// CHECK:STDERR: import NotFound; // CHECK:STDERR: ^ -import Unknown; +import NotFound; // CHECK:STDOUT: file "main.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "main_lib.carbon" { -// CHECK:STDOUT: %.loc5: String = string_literal "lib" +// CHECK:STDOUT: } +// CHECK:STDOUT: file "not_main.carbon" { // CHECK:STDOUT: } // CHECK:STDOUT: file "this.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "this_lib.carbon" { -// CHECK:STDOUT: %.loc2: String = string_literal "lib" -// CHECK:STDOUT: %.loc7: String = string_literal "lib" // CHECK:STDOUT: } // CHECK:STDOUT: file "implicit_api.carbon" { // CHECK:STDOUT: } // CHECK:STDOUT: file "implicit_impl.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "implicit_lib_api.carbon" { -// CHECK:STDOUT: %.loc2: String = string_literal "lib" // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "implicit_lib_impl.carbon" { -// CHECK:STDOUT: %.loc2: String = string_literal "lib" -// CHECK:STDOUT: %.loc7: String = string_literal "lib" // CHECK:STDOUT: } -// CHECK:STDOUT: file "unknown.carbon" { +// CHECK:STDOUT: file "not_found.carbon" { // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/packages/fail_import_repeat.carbon b/toolchain/check/testdata/packages/fail_import_repeat.carbon index 473a28206361..712b2abcd683 100644 --- a/toolchain/check/testdata/packages/fail_import_repeat.carbon +++ b/toolchain/check/testdata/packages/fail_import_repeat.carbon @@ -12,39 +12,59 @@ package Api api; package Api library "lib" api; -// --- import_api.carbon +// --- main_lib.carbon + +library "lib" api; + +// --- import.carbon import Api; -// CHECK:STDERR: import_api.carbon:[[@LINE+6]]:1: ERROR: Library imported more than once. +// CHECK:STDERR: import.carbon:[[@LINE+6]]:1: ERROR: Library imported more than once. // CHECK:STDERR: import Api; // CHECK:STDERR: ^ -// CHECK:STDERR: import_api.carbon:[[@LINE-4]]:1: First import here. +// CHECK:STDERR: import.carbon:[[@LINE-4]]:1: First import here. // CHECK:STDERR: import Api; // CHECK:STDERR: ^ import Api; + import Api library "lib"; -// CHECK:STDERR: import_api.carbon:[[@LINE+6]]:1: ERROR: Library imported more than once. +// CHECK:STDERR: import.carbon:[[@LINE+6]]:1: ERROR: Library imported more than once. // CHECK:STDERR: import Api library "lib"; // CHECK:STDERR: ^ -// CHECK:STDERR: import_api.carbon:[[@LINE-4]]:1: First import here. +// CHECK:STDERR: import.carbon:[[@LINE-4]]:1: First import here. // CHECK:STDERR: import Api library "lib"; // CHECK:STDERR: ^ import Api library "lib"; +import library "lib"; +// CHECK:STDERR: import.carbon:[[@LINE+6]]:1: ERROR: Library imported more than once. +// CHECK:STDERR: import library "lib"; +// CHECK:STDERR: ^ +// CHECK:STDERR: import.carbon:[[@LINE-4]]:1: First import here. +// CHECK:STDERR: import library "lib"; +// CHECK:STDERR: ^ +import library "lib"; + +// --- default_import.carbon + +package Api library "not_default" api; + +import library default; +// CHECK:STDERR: default_import.carbon:[[@LINE+6]]:1: ERROR: Library imported more than once. +// CHECK:STDERR: import library default; +// CHECK:STDERR: ^ +// CHECK:STDERR: default_import.carbon:[[@LINE-4]]:1: First import here. +// CHECK:STDERR: import library default; +// CHECK:STDERR: ^ +import library default; + // CHECK:STDOUT: file "api.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "api_lib.carbon" { -// CHECK:STDOUT: %.loc2: String = string_literal "lib" // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String +// CHECK:STDOUT: file "main_lib.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file "import_api.carbon" { -// CHECK:STDOUT: %.loc10: String = string_literal "lib" -// CHECK:STDOUT: %.loc17: String = string_literal "lib" +// CHECK:STDOUT: file "import.carbon" { +// CHECK:STDOUT: } +// CHECK:STDOUT: file "default_import.carbon" { // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/packages/fail_package_main.carbon b/toolchain/check/testdata/packages/fail_package_main.carbon index ee572cbe578f..afb49f7a5b04 100644 --- a/toolchain/check/testdata/packages/fail_package_main.carbon +++ b/toolchain/check/testdata/packages/fail_package_main.carbon @@ -6,14 +6,14 @@ // --- main.carbon -// CHECK:STDERR: main.carbon:[[@LINE+3]]:1: ERROR: Default `Main` library must omit `package` directive. +// CHECK:STDERR: main.carbon:[[@LINE+3]]:1: ERROR: `Main//default` must omit `package` directive. // CHECK:STDERR: package Main api; // CHECK:STDERR: ^ package Main api; // --- main_impl.carbon -// CHECK:STDERR: main_impl.carbon:[[@LINE+3]]:1: ERROR: Default `Main` library must omit `package` directive. +// CHECK:STDERR: main_impl.carbon:[[@LINE+3]]:1: ERROR: `Main//default` must omit `package` directive. // CHECK:STDERR: package Main impl; // CHECK:STDERR: ^ package Main impl; @@ -21,7 +21,7 @@ package Main impl; // --- raw_main.carbon // `Main` isn't a keyword, so this fails the same way. -// CHECK:STDERR: raw_main.carbon:[[@LINE+3]]:1: ERROR: Default `Main` library must omit `package` directive. +// CHECK:STDERR: raw_main.carbon:[[@LINE+3]]:1: ERROR: `Main//default` must omit `package` directive. // CHECK:STDERR: package r#Main api; // CHECK:STDERR: ^ package r#Main api; @@ -39,10 +39,5 @@ package Main library "lib" api; // CHECK:STDOUT: } // CHECK:STDOUT: file "raw_main.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "main_lib.carbon" { -// CHECK:STDOUT: %.loc5: String = string_literal "lib" // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/packages/implicit_imports.carbon b/toolchain/check/testdata/packages/implicit_imports.carbon index 79c96e56e9ef..be20ccb938cc 100644 --- a/toolchain/check/testdata/packages/implicit_imports.carbon +++ b/toolchain/check/testdata/packages/implicit_imports.carbon @@ -33,14 +33,19 @@ package WithImpl library "lib" api; package WithImpl library "lib" impl; +// --- main.carbon + +// --- main_lib.carbon + +library "lib" api; + +// --- main_lib_impl.carbon + +library "lib" impl; + // CHECK:STDOUT: file "api_only.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "api_only_lib.carbon" { -// CHECK:STDOUT: %.loc2: String = string_literal "lib" // CHECK:STDOUT: } // CHECK:STDOUT: file "with_impl_api.carbon" { // CHECK:STDOUT: } @@ -48,17 +53,13 @@ package WithImpl library "lib" impl; // CHECK:STDOUT: } // CHECK:STDOUT: file "with_impl_impl_extra.carbon" { // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "with_impl_lib_api.carbon" { -// CHECK:STDOUT: %.loc2: String = string_literal "lib" // CHECK:STDOUT: } -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: file "with_impl_lib_impl.carbon" { -// CHECK:STDOUT: %.loc2: String = string_literal "lib" +// CHECK:STDOUT: } +// CHECK:STDOUT: file "main.carbon" { +// CHECK:STDOUT: } +// CHECK:STDOUT: file "main_lib.carbon" { +// CHECK:STDOUT: } +// CHECK:STDOUT: file "main_lib_impl.carbon" { // CHECK:STDOUT: } diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 760df86293bd..280712c32344 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -86,8 +86,10 @@ CARBON_DIAGNOSTIC_KIND(FirstDecl) CARBON_DIAGNOSTIC_KIND(FirstNonCommentLine) CARBON_DIAGNOSTIC_KIND(PackageTooLate) CARBON_DIAGNOSTIC_KIND(ImportTooLate) -CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterKeyword) +CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterPackage) +CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterImport) CARBON_DIAGNOSTIC_KIND(ExpectedLibraryName) +CARBON_DIAGNOSTIC_KIND(ExpectedLibraryNameOrDefault) CARBON_DIAGNOSTIC_KIND(MissingLibraryKeyword) CARBON_DIAGNOSTIC_KIND(ExpectedApiOrImpl) @@ -122,6 +124,8 @@ CARBON_DIAGNOSTIC_KIND(ImportCycleDetected) CARBON_DIAGNOSTIC_KIND(ExplicitMainPackage) CARBON_DIAGNOSTIC_KIND(ExplicitMainLibrary) CARBON_DIAGNOSTIC_KIND(ImportMainPackage) +CARBON_DIAGNOSTIC_KIND(ImportMainDefaultLibrary) +CARBON_DIAGNOSTIC_KIND(ImportCurrentPackageByName) CARBON_DIAGNOSTIC_KIND(ImportSelf) CARBON_DIAGNOSTIC_KIND(ExplicitImportApi) CARBON_DIAGNOSTIC_KIND(RepeatedImport) diff --git a/toolchain/parse/handle_decl_scope_loop.cpp b/toolchain/parse/handle_decl_scope_loop.cpp index c9ecca9df885..5bcf02087395 100644 --- a/toolchain/parse/handle_decl_scope_loop.cpp +++ b/toolchain/parse/handle_decl_scope_loop.cpp @@ -30,11 +30,15 @@ auto HandleDeclScopeLoop(Context& context) -> void { context.PopAndDiscardState(); return; } - // `import` and `package` manage their packaging state. + // `import`, `library`, and `package` manage their packaging state. case Lex::TokenKind::Import: { context.PushState(State::Import); return; } + case Lex::TokenKind::Library: { + context.PushState(State::Library); + return; + } case Lex::TokenKind::Package: { context.PushState(State::Package); return; diff --git a/toolchain/parse/handle_import_and_package.cpp b/toolchain/parse/handle_import_and_package.cpp index 1345a8c9c28f..506739d22dc3 100644 --- a/toolchain/parse/handle_import_and_package.cpp +++ b/toolchain/parse/handle_import_and_package.cpp @@ -5,113 +5,165 @@ #include "toolchain/base/value_store.h" #include "toolchain/lex/tokenized_buffer.h" #include "toolchain/parse/context.h" +#include "toolchain/parse/node_kind.h" namespace Carbon::Parse { -// Provides error exiting logic for `import`/`package`, skipping to the semi. -static auto ExitOnParseError(Context& context, Context::StateStackEntry state, - NodeKind directive) { +// Provides common error exiting logic that skips to the semi, if present. +static auto OnParseError(Context& context, Context::StateStackEntry state, + NodeKind directive) -> void { auto semi_token = context.SkipPastLikelyEnd(state.token); return context.AddNode(directive, semi_token ? *semi_token : state.token, state.subtree_start, /*has_error=*/true); } -// Handles the main parsing of `import`/`package`. It's expected that the -// introducer is already added. -static auto HandleImportAndPackage(Context& context, +// Handles parsing of the library name. Returns the name's ID on success, which +// may be invalid for `default`. +static auto HandleLibraryName(Context& context, bool accept_default) + -> std::optional { + if (auto library_name_token = + context.ConsumeIf(Lex::TokenKind::StringLiteral)) { + context.AddLeafNode(NodeKind::LibraryName, *library_name_token); + return context.tokens().GetStringLiteral(*library_name_token); + } + + if (accept_default) { + if (auto default_token = context.ConsumeIf(Lex::TokenKind::Default)) { + context.AddLeafNode(NodeKind::DefaultLibrary, *default_token); + return StringLiteralId::Invalid; + } + } + + CARBON_DIAGNOSTIC( + ExpectedLibraryNameOrDefault, Error, + "Expected `default` or a string literal to specify the library name."); + CARBON_DIAGNOSTIC(ExpectedLibraryName, Error, + "Expected a string literal to specify the library name."); + context.emitter().Emit(*context.position(), accept_default + ? ExpectedLibraryNameOrDefault + : ExpectedLibraryName); + return std::nullopt; +} + +// Returns whether `api` or `impl` is provided, or prints an error and returns +// nullopt. +static auto HandleApiOrImpl(Context& context) + -> std::optional { + switch (context.PositionKind()) { + case Lex::TokenKind::Api: { + context.AddLeafNode(NodeKind::PackageApi, + context.ConsumeChecked(Lex::TokenKind::Api)); + return Tree::ApiOrImpl::Api; + break; + } + case Lex::TokenKind::Impl: { + context.AddLeafNode(NodeKind::PackageImpl, + context.ConsumeChecked(Lex::TokenKind::Impl)); + return Tree::ApiOrImpl::Impl; + break; + } + default: { + CARBON_DIAGNOSTIC(ExpectedApiOrImpl, Error, "Expected `api` or `impl`."); + context.emitter().Emit(*context.position(), ExpectedApiOrImpl); + return std::nullopt; + } + } +} + +// Handles everything after the directive's introducer. +static auto HandleDirectiveContent(Context& context, Context::StateStackEntry state, - NodeKind directive, bool is_package) + NodeKind directive, + llvm::function_ref on_parse_error) -> void { Tree::PackagingNames names{.node = Node(state.subtree_start)}; - if (auto package_name_token = context.ConsumeIf(Lex::TokenKind::Identifier)) { - names.package_id = context.tokens().GetIdentifier(*package_name_token); - context.AddLeafNode(NodeKind::Name, *package_name_token); - } else { - CARBON_DIAGNOSTIC(ExpectedIdentifierAfterKeyword, Error, - "Expected identifier after `{0}`.", Lex::TokenKind); - context.emitter().Emit(*context.position(), ExpectedIdentifierAfterKeyword, - context.tokens().GetKind(state.token)); - ExitOnParseError(context, state, directive); - return; - } - - if (auto library_token = context.ConsumeIf(Lex::TokenKind::Library)) { - auto library_start = context.tree().size(); - - if (auto library_name_token = - context.ConsumeIf(Lex::TokenKind::StringLiteral)) { - names.library_id = context.tokens().GetStringLiteral(*library_name_token); - context.AddLeafNode(NodeKind::Literal, *library_name_token); - } else { - CARBON_DIAGNOSTIC( - ExpectedLibraryName, Error, - "Expected a string literal to specify the library name."); - context.emitter().Emit(*context.position(), ExpectedLibraryName); - ExitOnParseError(context, state, directive); + if (directive != NodeKind::LibraryDirective) { + if (auto package_name_token = + context.ConsumeIf(Lex::TokenKind::Identifier)) { + names.package_id = context.tokens().GetIdentifier(*package_name_token); + context.AddLeafNode(NodeKind::PackageName, *package_name_token); + } else if (directive == NodeKind::PackageDirective || + !context.PositionIs(Lex::TokenKind::Library)) { + CARBON_DIAGNOSTIC(ExpectedIdentifierAfterPackage, Error, + "Expected identifier after `package`."); + CARBON_DIAGNOSTIC(ExpectedIdentifierAfterImport, Error, + "Expected identifier or `library` after `import`."); + context.emitter().Emit(*context.position(), + directive == NodeKind::PackageDirective + ? ExpectedIdentifierAfterPackage + : ExpectedIdentifierAfterImport); + on_parse_error(); return; } - - context.AddNode(NodeKind::Library, *library_token, library_start, - /*has_error=*/false); } - auto next_kind = context.PositionKind(); - if (!names.library_id.is_valid() && - next_kind == Lex::TokenKind::StringLiteral) { - // If we come acroess a string literal and we didn't parse `library - // "..."` yet, then most probably the user forgot to add `library` - // before the library name. - CARBON_DIAGNOSTIC(MissingLibraryKeyword, Error, - "Missing `library` keyword."); - context.emitter().Emit(*context.position(), MissingLibraryKeyword); - ExitOnParseError(context, state, directive); - return; - } - - Tree::ApiOrImpl api_or_impl; - if (is_package) { - switch (next_kind) { - case Lex::TokenKind::Api: { - context.AddLeafNode(NodeKind::PackageApi, context.Consume()); - api_or_impl = Tree::ApiOrImpl::Api; - break; - } - case Lex::TokenKind::Impl: { - context.AddLeafNode(NodeKind::PackageImpl, context.Consume()); - api_or_impl = Tree::ApiOrImpl::Impl; - break; - } - default: { - CARBON_DIAGNOSTIC(ExpectedApiOrImpl, Error, - "Expected `api` or `impl`."); - context.emitter().Emit(*context.position(), ExpectedApiOrImpl); - ExitOnParseError(context, state, directive); + // Parse the optional library keyword. + bool accept_default = !names.package_id.is_valid(); + if (directive == NodeKind::LibraryDirective) { + auto library_id = HandleLibraryName(context, accept_default); + if (!library_id) { + on_parse_error(); + return; + } + names.library_id = *library_id; + } else { + auto next_kind = context.PositionKind(); + if (next_kind == Lex::TokenKind::Library) { + auto library_token = context.ConsumeChecked(Lex::TokenKind::Library); + auto library_subtree_start = context.tree().size(); + auto library_id = HandleLibraryName(context, accept_default); + if (!library_id) { + on_parse_error(); return; } + names.library_id = *library_id; + context.AddNode(NodeKind::LibrarySpecifier, library_token, + library_subtree_start, + /*has_error=*/false); + } else if (next_kind == Lex::TokenKind::StringLiteral || + (accept_default && next_kind == Lex::TokenKind::Default)) { + // If we come across a string literal and we didn't parse `library + // "..."` yet, then most probably the user forgot to add `library` + // before the library name. + CARBON_DIAGNOSTIC(MissingLibraryKeyword, Error, + "Missing `library` keyword."); + context.emitter().Emit(*context.position(), MissingLibraryKeyword); + on_parse_error(); + return; } } - if (!context.PositionIs(Lex::TokenKind::Semi)) { - context.EmitExpectedDeclSemi(context.tokens().GetKind(state.token)); - ExitOnParseError(context, state, directive); - return; + std::optional api_or_impl; + if (directive != NodeKind::ImportDirective) { + api_or_impl = HandleApiOrImpl(context); + if (!api_or_impl) { + on_parse_error(); + return; + } } - if (is_package) { - context.set_packaging_directive(names, api_or_impl); + if (auto semi = context.ConsumeIf(Lex::TokenKind::Semi)) { + if (directive == NodeKind::ImportDirective) { + context.AddImport(names); + } else { + context.set_packaging_directive(names, *api_or_impl); + } + + context.AddNode(directive, *semi, state.subtree_start, state.has_error); } else { - context.AddImport(names); + context.EmitExpectedDeclSemi(context.tokens().GetKind(state.token)); + on_parse_error(); } - - context.AddNode(directive, context.Consume(), state.subtree_start, - state.has_error); } auto HandleImport(Context& context) -> void { auto state = context.PopState(); - auto intro_token = context.Consume(); + auto directive = NodeKind::ImportDirective; + auto on_parse_error = [&] { OnParseError(context, state, directive); }; + + auto intro_token = context.ConsumeChecked(Lex::TokenKind::Import); context.AddLeafNode(NodeKind::ImportIntroducer, intro_token); switch (context.packaging_state()) { @@ -121,8 +173,7 @@ auto HandleImport(Context& context) -> void { [[clang::fallthrough]]; case Context::PackagingState::InImports: - HandleImportAndPackage(context, state, NodeKind::ImportDirective, - /*is_package=*/false); + HandleDirectiveContent(context, state, directive, on_parse_error); break; case Context::PackagingState::AfterNonPackagingDecl: { @@ -137,42 +188,60 @@ auto HandleImport(Context& context) -> void { .Build(intro_token, ImportTooLate) .Note(context.first_non_packaging_token(), FirstDecl) .Emit(); - ExitOnParseError(context, state, NodeKind::ImportDirective); + on_parse_error(); break; } case Context::PackagingState::InImportsAfterNonPackagingDecl: // There is a sequential block of misplaced `import` statements, which can // occur if a declaration is added above `import`s. Avoid duplicate // warnings. - ExitOnParseError(context, state, NodeKind::ImportDirective); + on_parse_error(); break; } } -auto HandlePackage(Context& context) -> void { +// Handles common logic for `package` and `library`. +static auto HandlePackageAndLibraryDirectives(Context& context, + Lex::TokenKind intro_token_kind, + NodeKind intro, + NodeKind directive) -> void { auto state = context.PopState(); - auto intro_token = context.Consume(); - context.AddLeafNode(NodeKind::PackageIntroducer, intro_token); + auto on_parse_error = [&] { OnParseError(context, state, directive); }; + + auto intro_token = context.ConsumeChecked(intro_token_kind); + context.AddLeafNode(intro, intro_token); if (intro_token != Lex::Token::FirstNonCommentToken) { - CARBON_DIAGNOSTIC( - PackageTooLate, Error, - "The `package` directive must be the first non-comment line."); + CARBON_DIAGNOSTIC(PackageTooLate, Error, + "The `{0}` directive must be the first non-comment line.", + Lex::TokenKind); CARBON_DIAGNOSTIC(FirstNonCommentLine, Note, "First non-comment line is here."); context.emitter() - .Build(intro_token, PackageTooLate) + .Build(intro_token, PackageTooLate, intro_token_kind) .Note(Lex::Token::FirstNonCommentToken, FirstNonCommentLine) .Emit(); - ExitOnParseError(context, state, NodeKind::PackageDirective); + on_parse_error(); return; } - // `package` is no longer allowed, but `import` may repeat. + // `package`/`library` is no longer allowed, but `import` may repeat. context.set_packaging_state(Context::PackagingState::InImports); - HandleImportAndPackage(context, state, NodeKind::PackageDirective, - /*is_package=*/true); + + HandleDirectiveContent(context, state, directive, on_parse_error); +} + +auto HandlePackage(Context& context) -> void { + HandlePackageAndLibraryDirectives(context, Lex::TokenKind::Package, + NodeKind::PackageIntroducer, + NodeKind::PackageDirective); +} + +auto HandleLibrary(Context& context) -> void { + HandlePackageAndLibraryDirectives(context, Lex::TokenKind::Library, + NodeKind::LibraryIntroducer, + NodeKind::LibraryDirective); } } // namespace Carbon::Parse diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 0a8a543b21d4..19157ac014bd 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -95,15 +95,14 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(NameExpr, 0, CARBON_TOKEN(Identifier)) // ---------------------------------------------------------------------------- -// `library`: -// _external_: Literal -// Library -CARBON_PARSE_NODE_KIND_CHILD_COUNT(Library, 1, CARBON_TOKEN(Library)) +// The name of a package or library for `package`, `import`, and `library`. +CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageName, 0, CARBON_TOKEN(Identifier)) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(LibraryName, 0, CARBON_TOKEN(StringLiteral)) // `package`: // PackageIntroducer -// _external_: Name -// _optional_ _external_: Library +// _optional_ _external_: PackageName +// _optional_ _external_: LibrarySpecifier // PackageApi or PackageImpl // PackageDirective CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageIntroducer, 0, CARBON_TOKEN(Package)) @@ -115,13 +114,27 @@ CARBON_PARSE_NODE_KIND_BRACKET(PackageDirective, PackageIntroducer, // `import`: // ImportIntroducer -// _external_: Name -// _optional_ _external_: Library +// _optional_ _external_: PackageName +// _optional_ _external_: LibrarySpecifier // ImportDirective CARBON_PARSE_NODE_KIND_CHILD_COUNT(ImportIntroducer, 0, CARBON_TOKEN(Import)) CARBON_PARSE_NODE_KIND_BRACKET(ImportDirective, ImportIntroducer, CARBON_TOKEN(Semi) CARBON_IF_ERROR(CARBON_TOKEN(Import))) +// `library` as directive: +// LibraryIntroducer +// DefaultLibrary or _external_: LibraryName +// LibraryDirective +CARBON_PARSE_NODE_KIND_CHILD_COUNT(DefaultLibrary, 0, CARBON_TOKEN(Default)) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(LibraryIntroducer, 0, CARBON_TOKEN(Library)) +CARBON_PARSE_NODE_KIND_BRACKET(LibraryDirective, LibraryIntroducer, + CARBON_TOKEN(Semi) + CARBON_IF_ERROR(CARBON_TOKEN(Library))) + +// `library` in `package` or `import`: +// _external_: LibraryName +// LibrarySpecifier +CARBON_PARSE_NODE_KIND_CHILD_COUNT(LibrarySpecifier, 1, CARBON_TOKEN(Library)) // `namespace`: // NamespaceStart diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index a8737ac4913d..d99f6e46acc4 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -591,6 +591,12 @@ CARBON_PARSE_STATE(FunctionDefinitionFinish) // (state done) CARBON_PARSE_STATE(Import) +// Handles `library` in directive form. +// +// Always: +// (state done) +CARBON_PARSE_STATE(Library) + // Handles `namespace`. // // namespace ... diff --git a/toolchain/parse/testdata/packages/import/after_import.carbon b/toolchain/parse/testdata/packages/import/after_import.carbon index 7f14322681e6..8a16817cf602 100644 --- a/toolchain/parse/testdata/packages/import/after_import.carbon +++ b/toolchain/parse/testdata/packages/import/after_import.carbon @@ -11,10 +11,10 @@ import B; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'A'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'A'}, // CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'B'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'B'}, // CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/after_package.carbon b/toolchain/parse/testdata/packages/import/after_package.carbon index 7f859725ae1e..fe482c558a9f 100644 --- a/toolchain/parse/testdata/packages/import/after_package.carbon +++ b/toolchain/parse/testdata/packages/import/after_package.carbon @@ -12,11 +12,11 @@ import B; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'A'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'A'}, // CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'B'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'B'}, // CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/basic.carbon b/toolchain/parse/testdata/packages/import/basic.carbon index 4cfb8a73dbb5..b37335dc3c7f 100644 --- a/toolchain/parse/testdata/packages/import/basic.carbon +++ b/toolchain/parse/testdata/packages/import/basic.carbon @@ -10,7 +10,7 @@ import Geometry; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/current_package_library.carbon b/toolchain/parse/testdata/packages/import/current_package_library.carbon new file mode 100644 index 000000000000..ab955a7e046c --- /dev/null +++ b/toolchain/parse/testdata/packages/import/current_package_library.carbon @@ -0,0 +1,32 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- default.carbon + +import library default; + +// --- named.carbon + +import library "Shapes"; + +// CHECK:STDOUT: - filename: default.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'DefaultLibrary', text: 'default'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: named.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"Shapes"'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/fail_extra_string.carbon b/toolchain/parse/testdata/packages/import/fail_extra_string.carbon index 6d523bad9a56..b58173ac77d4 100644 --- a/toolchain/parse/testdata/packages/import/fail_extra_string.carbon +++ b/toolchain/parse/testdata/packages/import/fail_extra_string.carbon @@ -13,9 +13,9 @@ import Foo library "bar" "baz"; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Foo'}, -// CHECK:STDOUT: {kind: 'Literal', text: '"bar"'}, -// CHECK:STDOUT: {kind: 'Library', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"bar"'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, // CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/fail_library_is_identifier.carbon b/toolchain/parse/testdata/packages/import/fail_library_is_identifier.carbon index e2a745738e28..911a3a43a50c 100644 --- a/toolchain/parse/testdata/packages/import/fail_library_is_identifier.carbon +++ b/toolchain/parse/testdata/packages/import/fail_library_is_identifier.carbon @@ -13,7 +13,7 @@ import Geometry library Shapes; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/fail_library_skips_name.carbon b/toolchain/parse/testdata/packages/import/fail_library_skips_name.carbon deleted file mode 100644 index 0bad8f5bcce7..000000000000 --- a/toolchain/parse/testdata/packages/import/fail_library_skips_name.carbon +++ /dev/null @@ -1,18 +0,0 @@ -// Part of the Carbon Language project, under the Apache License v2.0 with LLVM -// Exceptions. See /LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// AUTOUPDATE - -// CHECK:STDERR: fail_library_skips_name.carbon:[[@LINE+3]]:8: ERROR: Expected identifier after `import`. -// CHECK:STDERR: import library "Shapes"; -// CHECK:STDERR: ^ -import library "Shapes"; - -// CHECK:STDOUT: - filename: fail_library_skips_name.carbon -// CHECK:STDOUT: parse_tree: [ -// CHECK:STDOUT: {kind: 'FileStart', text: ''}, -// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 2}, -// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, -// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/fail_name_is_keyword.carbon b/toolchain/parse/testdata/packages/import/fail_name_is_keyword.carbon index 27d3b2950004..bc17d80201a6 100644 --- a/toolchain/parse/testdata/packages/import/fail_name_is_keyword.carbon +++ b/toolchain/parse/testdata/packages/import/fail_name_is_keyword.carbon @@ -4,7 +4,7 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_name_is_keyword.carbon:[[@LINE+3]]:8: ERROR: Expected identifier after `import`. +// CHECK:STDERR: fail_name_is_keyword.carbon:[[@LINE+3]]:8: ERROR: Expected identifier or `library` after `import`. // CHECK:STDERR: import fn; // CHECK:STDERR: ^ import fn; diff --git a/toolchain/parse/testdata/packages/import/fail_no_name.carbon b/toolchain/parse/testdata/packages/import/fail_no_name.carbon index 1b867b767483..4d52842078a3 100644 --- a/toolchain/parse/testdata/packages/import/fail_no_name.carbon +++ b/toolchain/parse/testdata/packages/import/fail_no_name.carbon @@ -4,7 +4,7 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_no_name.carbon:[[@LINE+3]]:7: ERROR: Expected identifier after `import`. +// CHECK:STDERR: fail_no_name.carbon:[[@LINE+3]]:7: ERROR: Expected identifier or `library` after `import`. // CHECK:STDERR: import; // CHECK:STDERR: ^ import; diff --git a/toolchain/parse/testdata/packages/import/fail_no_semi.carbon b/toolchain/parse/testdata/packages/import/fail_no_semi.carbon index 93580a7f2720..fca391a5aa48 100644 --- a/toolchain/parse/testdata/packages/import/fail_no_semi.carbon +++ b/toolchain/parse/testdata/packages/import/fail_no_semi.carbon @@ -13,7 +13,7 @@ import Geometry // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'ImportDirective', text: 'import', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/fail_omit_library_keyword.carbon b/toolchain/parse/testdata/packages/import/fail_omit_library_keyword.carbon index 815fb103cd4f..1fea016b2f3a 100644 --- a/toolchain/parse/testdata/packages/import/fail_omit_library_keyword.carbon +++ b/toolchain/parse/testdata/packages/import/fail_omit_library_keyword.carbon @@ -13,7 +13,7 @@ import Geometry "Shapes"; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/fail_type.carbon b/toolchain/parse/testdata/packages/import/fail_type.carbon index 6fa8f8d5c475..4b9f180cd299 100644 --- a/toolchain/parse/testdata/packages/import/fail_type.carbon +++ b/toolchain/parse/testdata/packages/import/fail_type.carbon @@ -13,7 +13,7 @@ import Geometry api; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/library.carbon b/toolchain/parse/testdata/packages/import/library.carbon index 3d5b756ce3a8..c43f73b11f60 100644 --- a/toolchain/parse/testdata/packages/import/library.carbon +++ b/toolchain/parse/testdata/packages/import/library.carbon @@ -10,9 +10,9 @@ import Geometry library "Shapes"; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, -// CHECK:STDOUT: {kind: 'Literal', text: '"Shapes"'}, -// CHECK:STDOUT: {kind: 'Library', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"Shapes"'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, // CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 5}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/ordering.carbon b/toolchain/parse/testdata/packages/import/ordering.carbon new file mode 100644 index 000000000000..284efbf995a0 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/ordering.carbon @@ -0,0 +1,58 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- after_import.carbon + +import A; +import B; + +// --- after_library.carbon + +library "A" api; + +import B; + +// --- after_package.carbon + +package A api; + +import B; + +// CHECK:STDOUT: - filename: after_import.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'A'}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'B'}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: after_library.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LibraryIntroducer', text: 'library'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"A"'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'LibraryDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'B'}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: after_package.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'A'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'B'}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/library/basic.carbon b/toolchain/parse/testdata/packages/library/basic.carbon new file mode 100644 index 000000000000..9c24f1b67797 --- /dev/null +++ b/toolchain/parse/testdata/packages/library/basic.carbon @@ -0,0 +1,32 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- default.carbon + +library default api; + +// --- named.carbon + +library "Shapes" impl; + +// CHECK:STDOUT: - filename: default.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LibraryIntroducer', text: 'library'}, +// CHECK:STDOUT: {kind: 'DefaultLibrary', text: 'default'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'LibraryDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: named.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LibraryIntroducer', text: 'library'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"Shapes"'}, +// CHECK:STDOUT: {kind: 'PackageImpl', text: 'impl'}, +// CHECK:STDOUT: {kind: 'LibraryDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/library/fail_invalid_name.carbon b/toolchain/parse/testdata/packages/library/fail_invalid_name.carbon new file mode 100644 index 000000000000..6739686812f1 --- /dev/null +++ b/toolchain/parse/testdata/packages/library/fail_invalid_name.carbon @@ -0,0 +1,78 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- identifier.carbon + +// CHECK:STDERR: identifier.carbon:[[@LINE+3]]:9: ERROR: Expected `default` or a string literal to specify the library name. +// CHECK:STDERR: library Shapes api; +// CHECK:STDERR: ^ +library Shapes api; + +// --- raw_identifier.carbon + +// CHECK:STDERR: raw_identifier.carbon:[[@LINE+3]]:9: ERROR: Expected `default` or a string literal to specify the library name. +// CHECK:STDERR: library r#default api; +// CHECK:STDERR: ^ +library r#default api; + +// --- missing.carbon + +// CHECK:STDERR: missing.carbon:[[@LINE+3]]:8: ERROR: Expected `default` or a string literal to specify the library name. +// CHECK:STDERR: library; +// CHECK:STDERR: ^ +library; + +// --- missing_with_api.carbon + +// CHECK:STDERR: missing_with_api.carbon:[[@LINE+3]]:9: ERROR: Expected `default` or a string literal to specify the library name. +// CHECK:STDERR: library api; +// CHECK:STDERR: ^ +library api; + +// --- no_semi.carbon + +library "NoSemi" api + +// CHECK:STDERR: no_semi.carbon:[[@LINE+39]]:21: ERROR: `library` declarations must end with a `;`. +// CHECK:STDERR: // CHECK:STDOUT: ] +// CHECK:STDERR: ^ +// CHECK:STDOUT: - filename: identifier.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LibraryIntroducer', text: 'library'}, +// CHECK:STDOUT: {kind: 'LibraryDirective', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: raw_identifier.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LibraryIntroducer', text: 'library'}, +// CHECK:STDOUT: {kind: 'LibraryDirective', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: missing.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LibraryIntroducer', text: 'library'}, +// CHECK:STDOUT: {kind: 'LibraryDirective', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: missing_with_api.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LibraryIntroducer', text: 'library'}, +// CHECK:STDOUT: {kind: 'LibraryDirective', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: no_semi.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'LibraryIntroducer', text: 'library'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"NoSemi"'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'LibraryDirective', text: 'library', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/library/fail_too_late.carbon b/toolchain/parse/testdata/packages/library/fail_too_late.carbon new file mode 100644 index 000000000000..54ea670b4f97 --- /dev/null +++ b/toolchain/parse/testdata/packages/library/fail_too_late.carbon @@ -0,0 +1,26 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +import Geometry; + +// CHECK:STDERR: fail_too_late.carbon:[[@LINE+6]]:1: ERROR: The `library` directive must be the first non-comment line. +// CHECK:STDERR: library "Shapes" api; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_too_late.carbon:[[@LINE-5]]:1: First non-comment line is here. +// CHECK:STDERR: import Geometry; +// CHECK:STDERR: ^ +library "Shapes" api; + +// CHECK:STDOUT: - filename: fail_too_late.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'LibraryIntroducer', text: 'library'}, +// CHECK:STDOUT: {kind: 'LibraryDirective', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/package/api.carbon b/toolchain/parse/testdata/packages/package/api.carbon index 3ac51c66dbdc..420ff101e435 100644 --- a/toolchain/parse/testdata/packages/package/api.carbon +++ b/toolchain/parse/testdata/packages/package/api.carbon @@ -10,7 +10,7 @@ package Geometry api; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parse/testdata/packages/package/api_library.carbon b/toolchain/parse/testdata/packages/package/api_library.carbon index 4ab898ef3034..b20bbb981a2a 100644 --- a/toolchain/parse/testdata/packages/package/api_library.carbon +++ b/toolchain/parse/testdata/packages/package/api_library.carbon @@ -10,9 +10,9 @@ package Geometry library "Shapes" api; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, -// CHECK:STDOUT: {kind: 'Literal', text: '"Shapes"'}, -// CHECK:STDOUT: {kind: 'Library', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"Shapes"'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, // CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 6}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parse/testdata/packages/package/fail_after_import.carbon b/toolchain/parse/testdata/packages/package/fail_after_import.carbon index 843dfa929f67..bb3b1e20407d 100644 --- a/toolchain/parse/testdata/packages/package/fail_after_import.carbon +++ b/toolchain/parse/testdata/packages/package/fail_after_import.carbon @@ -18,7 +18,7 @@ package B api; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, -// CHECK:STDOUT: {kind: 'Name', text: 'A'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'A'}, // CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 3}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', has_error: yes, subtree_size: 2}, diff --git a/toolchain/parse/testdata/packages/package/fail_after_package.carbon b/toolchain/parse/testdata/packages/package/fail_after_package.carbon index 341c0b2a1315..a9e4fdecd434 100644 --- a/toolchain/parse/testdata/packages/package/fail_after_package.carbon +++ b/toolchain/parse/testdata/packages/package/fail_after_package.carbon @@ -17,7 +17,7 @@ package B api; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'A'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'A'}, // CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, diff --git a/toolchain/parse/testdata/packages/package/fail_extra_string.carbon b/toolchain/parse/testdata/packages/package/fail_extra_string.carbon index 9432f4c7ee34..0de4de74db94 100644 --- a/toolchain/parse/testdata/packages/package/fail_extra_string.carbon +++ b/toolchain/parse/testdata/packages/package/fail_extra_string.carbon @@ -13,9 +13,9 @@ package Foo library "bar" "baz"; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Foo'}, -// CHECK:STDOUT: {kind: 'Literal', text: '"bar"'}, -// CHECK:STDOUT: {kind: 'Library', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"bar"'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/package/fail_library_is_identifier.carbon b/toolchain/parse/testdata/packages/package/fail_library_is_identifier.carbon index 03f76bb14973..501c5c532b82 100644 --- a/toolchain/parse/testdata/packages/package/fail_library_is_identifier.carbon +++ b/toolchain/parse/testdata/packages/package/fail_library_is_identifier.carbon @@ -13,7 +13,7 @@ package Geometry library Shapes api; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/package/fail_no_semi.carbon b/toolchain/parse/testdata/packages/package/fail_no_semi.carbon index b9aee3f1bf26..fc3f1d0c3a20 100644 --- a/toolchain/parse/testdata/packages/package/fail_no_semi.carbon +++ b/toolchain/parse/testdata/packages/package/fail_no_semi.carbon @@ -13,7 +13,7 @@ package Geometry api // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: 'package', has_error: yes, subtree_size: 4}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parse/testdata/packages/package/fail_no_type.carbon b/toolchain/parse/testdata/packages/package/fail_no_type.carbon index e574d90e24e8..ad70261b4d29 100644 --- a/toolchain/parse/testdata/packages/package/fail_no_type.carbon +++ b/toolchain/parse/testdata/packages/package/fail_no_type.carbon @@ -13,7 +13,7 @@ package Geometry; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/package/fail_omit_library_keyword.carbon b/toolchain/parse/testdata/packages/package/fail_omit_library_keyword.carbon index 744b5da749f8..a2035a7befa4 100644 --- a/toolchain/parse/testdata/packages/package/fail_omit_library_keyword.carbon +++ b/toolchain/parse/testdata/packages/package/fail_omit_library_keyword.carbon @@ -13,7 +13,7 @@ package Geometry "Shapes" api; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/package/impl.carbon b/toolchain/parse/testdata/packages/package/impl.carbon index d840b5fadc05..a8414405e701 100644 --- a/toolchain/parse/testdata/packages/package/impl.carbon +++ b/toolchain/parse/testdata/packages/package/impl.carbon @@ -10,7 +10,7 @@ package Geometry impl; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'PackageImpl', text: 'impl'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parse/testdata/packages/package/impl_library.carbon b/toolchain/parse/testdata/packages/package/impl_library.carbon index 878b96811005..f1dfc524cf3d 100644 --- a/toolchain/parse/testdata/packages/package/impl_library.carbon +++ b/toolchain/parse/testdata/packages/package/impl_library.carbon @@ -10,9 +10,9 @@ package Geometry library "Shapes" impl; // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, -// CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, -// CHECK:STDOUT: {kind: 'Literal', text: '"Shapes"'}, -// CHECK:STDOUT: {kind: 'Library', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Geometry'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"Shapes"'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, // CHECK:STDOUT: {kind: 'PackageImpl', text: 'impl'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 6}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''},