diff --git a/toolchain/check/handle_package.cpp b/toolchain/check/handle_import_and_package.cpp similarity index 67% rename from toolchain/check/handle_package.cpp rename to toolchain/check/handle_import_and_package.cpp index f1ff722ee18f..8f75770ce09f 100644 --- a/toolchain/check/handle_package.cpp +++ b/toolchain/check/handle_import_and_package.cpp @@ -6,24 +6,32 @@ namespace Carbon::Check { -auto HandlePackageApi(Context& context, Parse::Node parse_node) -> bool { - return context.TODO(parse_node, "HandlePackageApi"); -} - -auto HandlePackageDirective(Context& context, Parse::Node parse_node) -> bool { - return context.TODO(parse_node, "HandlePackageDirective"); -} - -auto HandlePackageImpl(Context& context, Parse::Node parse_node) -> bool { - return context.TODO(parse_node, "HandlePackageImpl"); +auto HandleImportIntroducer(Context& context, Parse::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleImportIntroducer"); } auto HandlePackageIntroducer(Context& context, Parse::Node parse_node) -> bool { return context.TODO(parse_node, "HandlePackageIntroducer"); } -auto HandlePackageLibrary(Context& context, Parse::Node parse_node) -> bool { - return context.TODO(parse_node, "HandlePackageLibrary"); +auto HandleLibrary(Context& context, Parse::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleLibrary"); +} + +auto HandlePackageApi(Context& context, Parse::Node parse_node) -> bool { + return context.TODO(parse_node, "HandlePackageApi"); +} + +auto HandlePackageImpl(Context& context, Parse::Node parse_node) -> bool { + return context.TODO(parse_node, "HandlePackageImpl"); +} + +auto HandleImportDirective(Context& context, Parse::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleImportDirective"); +} + +auto HandlePackageDirective(Context& context, Parse::Node parse_node) -> bool { + return context.TODO(parse_node, "HandlePackageDirective"); } } // namespace Carbon::Check diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 6aa1305decdb..3e54ea62a4b9 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -81,7 +81,11 @@ CARBON_DIAGNOSTIC_KIND(UnexpectedTokenAfterListElement) CARBON_DIAGNOSTIC_KIND(UnrecognizedDeclaration) // Package-related diagnostics. -CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterPackage) +CARBON_DIAGNOSTIC_KIND(FirstDeclaration) +CARBON_DIAGNOSTIC_KIND(FirstNonCommentLine) +CARBON_DIAGNOSTIC_KIND(PackageTooLate) +CARBON_DIAGNOSTIC_KIND(ImportTooLate) +CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterKeyword) CARBON_DIAGNOSTIC_KIND(ExpectedLibraryName) CARBON_DIAGNOSTIC_KIND(MissingLibraryKeyword) CARBON_DIAGNOSTIC_KIND(ExpectedApiOrImpl) diff --git a/toolchain/lex/tokenized_buffer.h b/toolchain/lex/tokenized_buffer.h index 23ec20ea5d3b..f406784b8757 100644 --- a/toolchain/lex/tokenized_buffer.h +++ b/toolchain/lex/tokenized_buffer.h @@ -40,9 +40,15 @@ class TokenizedBuffer; // // All other APIs to query a `Token` are on the `TokenizedBuffer`. struct Token : public ComparableIndexBase { + static const Token Invalid; + // Comments aren't tokenized, so this is the first token after StartOfFile. + static const Token FirstNonCommentToken; using ComparableIndexBase::ComparableIndexBase; }; +constexpr Token Token::Invalid(Token::InvalidIndex); +constexpr Token Token::FirstNonCommentToken(1); + // A lightweight handle to a lexed line in a `TokenizedBuffer`. // // `Line` objects are designed to be passed by value, not reference or @@ -55,9 +61,8 @@ struct Token : public ComparableIndexBase { // // All other APIs to query a `Line` are on the `TokenizedBuffer`. struct Line : public ComparableIndexBase { - using ComparableIndexBase::ComparableIndexBase; - static const Line Invalid; + using ComparableIndexBase::ComparableIndexBase; }; constexpr Line Line::Invalid(Line::InvalidIndex); diff --git a/toolchain/parse/context.h b/toolchain/parse/context.h index be7db8040e12..b73b2eba0ac9 100644 --- a/toolchain/parse/context.h +++ b/toolchain/parse/context.h @@ -44,6 +44,17 @@ class Context { NamedConstraint, }; + // Used for restricting ordering of `package` and `import` directives. + enum class PackagingState : int8_t { + StartOfFile, + InImports, + AfterNonPackagingDeclaration, + // A warning about `import` placement has been issued so we don't keep + // issuing more (when `import` is repeated) until more non-`import` + // declarations come up. + InImportsAfterNonPackagingDeclaration, + }; + // Used to track state on state_stack_. struct StateStackEntry : public Printable { explicit StateStackEntry(State state, PrecedenceGroup ambient_precedence, @@ -301,6 +312,18 @@ class Context { return state_stack_; } + auto packaging_state() const -> PackagingState { return packaging_state_; } + auto set_packaging_state(PackagingState packaging_state) -> void { + packaging_state_ = packaging_state; + } + auto first_non_packaging_token() const -> Lex::Token { + return first_non_packaging_token_; + } + auto set_first_non_packaging_token(Lex::Token token) -> void { + CARBON_CHECK(!first_non_packaging_token_.is_valid()); + first_non_packaging_token_ = token; + } + private: // Prints a single token for a stack dump. Used by PrintForStackDump. auto PrintTokenForStackDump(llvm::raw_ostream& output, Lex::Token token) const @@ -319,6 +342,12 @@ class Context { Lex::TokenIterator end_; llvm::SmallVector state_stack_; + + // The current packaging state, whether `import`/`package` are allowed. + PackagingState packaging_state_ = PackagingState::StartOfFile; + // The first non-packaging token, starting as invalid. Used for packaging + // state warnings. + Lex::Token first_non_packaging_token_ = Lex::Token::Invalid; }; // `clang-format` has a bug with spacing around `->` returns in macros. See diff --git a/toolchain/parse/handle_declaration_scope_loop.cpp b/toolchain/parse/handle_declaration_scope_loop.cpp index d4ce74c9fb24..2566d6018e48 100644 --- a/toolchain/parse/handle_declaration_scope_loop.cpp +++ b/toolchain/parse/handle_declaration_scope_loop.cpp @@ -22,49 +22,74 @@ static auto HandleUnrecognizedDeclaration(Context& context) -> void { auto HandleDeclarationScopeLoop(Context& context) -> void { // This maintains the current state unless we're at the end of the scope. - switch (context.PositionKind()) { + switch (auto position_kind = context.PositionKind()) { case Lex::TokenKind::CloseCurlyBrace: case Lex::TokenKind::EndOfFile: { // This is the end of the scope, so the loop state ends. context.PopAndDiscardState(); break; } - case Lex::TokenKind::Class: { - context.PushState(State::TypeIntroducerAsClass); + // `import` and `package` manage their packaging state. + case Lex::TokenKind::Import: { + context.PushState(State::Import); break; } - case Lex::TokenKind::Constraint: { - context.PushState(State::TypeIntroducerAsNamedConstraint); - break; - } - case Lex::TokenKind::Fn: { - context.PushState(State::FunctionIntroducer); - break; - } - case Lex::TokenKind::Interface: { - context.PushState(State::TypeIntroducerAsInterface); - break; - } - case Lex::TokenKind::Namespace: { - context.PushState(State::Namespace); - break; - } - case Lex::TokenKind::Semi: { - context.AddLeafNode(NodeKind::EmptyDeclaration, context.Consume()); - break; - } - case Lex::TokenKind::Var: { - context.PushState(State::VarAsSemicolon); - break; - } - case Lex::TokenKind::Let: { - context.PushState(State::Let); - break; - } - default: { - HandleUnrecognizedDeclaration(context); + case Lex::TokenKind::Package: { + context.PushState(State::Package); break; } + default: + // Because a non-packaging keyword was encountered, packaging is complete. + // Misplaced packaging keywords may lead to this being re-triggered. + if (context.packaging_state() != + Context::PackagingState::AfterNonPackagingDeclaration) { + if (!context.first_non_packaging_token().is_valid()) { + context.set_first_non_packaging_token(*context.position()); + } + context.set_packaging_state( + Context::PackagingState::AfterNonPackagingDeclaration); + } + switch (position_kind) { + // Remaining keywords are only valid after imports are complete, and + // so all result in a `set_packaging_state` call. Note, this may not + // always be necessary but is probably cheaper than validating. + case Lex::TokenKind::Class: { + context.PushState(State::TypeIntroducerAsClass); + break; + } + case Lex::TokenKind::Constraint: { + context.PushState(State::TypeIntroducerAsNamedConstraint); + break; + } + case Lex::TokenKind::Fn: { + context.PushState(State::FunctionIntroducer); + break; + } + case Lex::TokenKind::Interface: { + context.PushState(State::TypeIntroducerAsInterface); + break; + } + case Lex::TokenKind::Namespace: { + context.PushState(State::Namespace); + break; + } + case Lex::TokenKind::Semi: { + context.AddLeafNode(NodeKind::EmptyDeclaration, context.Consume()); + break; + } + case Lex::TokenKind::Var: { + context.PushState(State::VarAsSemicolon); + break; + } + case Lex::TokenKind::Let: { + context.PushState(State::Let); + break; + } + default: { + HandleUnrecognizedDeclaration(context); + break; + } + } } } diff --git a/toolchain/parse/handle_import_and_package.cpp b/toolchain/parse/handle_import_and_package.cpp new file mode 100644 index 000000000000..bf5262d02774 --- /dev/null +++ b/toolchain/parse/handle_import_and_package.cpp @@ -0,0 +1,163 @@ +// 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 + +#include "toolchain/lex/tokenized_buffer.h" +#include "toolchain/parse/context.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) { + 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, + Context::StateStackEntry state, + NodeKind directive, bool expect_api_or_impl) + -> void { + if (!context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::Identifier, + NodeKind::Name)) { + 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; + } + + bool library_parsed = false; + if (auto library_token = context.ConsumeIf(Lex::TokenKind::Library)) { + auto library_start = context.tree().size(); + + if (!context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::StringLiteral, + NodeKind::Literal)) { + CARBON_DIAGNOSTIC( + ExpectedLibraryName, Error, + "Expected a string literal to specify the library name."); + context.emitter().Emit(*context.position(), ExpectedLibraryName); + ExitOnParseError(context, state, directive); + return; + } + + context.AddNode(NodeKind::Library, *library_token, library_start, + /*has_error=*/false); + library_parsed = true; + } + + auto next_kind = context.tokens().GetKind(*(context.position())); + if (!library_parsed && 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; + } + + if (expect_api_or_impl) { + switch (next_kind) { + case Lex::TokenKind::Api: { + context.AddLeafNode(NodeKind::PackageApi, context.Consume()); + break; + } + case Lex::TokenKind::Impl: { + context.AddLeafNode(NodeKind::PackageImpl, context.Consume()); + break; + } + default: { + CARBON_DIAGNOSTIC(ExpectedApiOrImpl, Error, + "Expected `api` or `impl`."); + context.emitter().Emit(*context.position(), ExpectedApiOrImpl); + ExitOnParseError(context, state, directive); + return; + } + } + } + + if (!context.PositionIs(Lex::TokenKind::Semi)) { + context.EmitExpectedDeclarationSemi(context.tokens().GetKind(state.token)); + ExitOnParseError(context, state, directive); + return; + } + + 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(); + context.AddLeafNode(NodeKind::ImportIntroducer, intro_token); + + switch (context.packaging_state()) { + case Context::PackagingState::StartOfFile: + // `package` is no longer allowed, but `import` may repeat. + context.set_packaging_state(Context::PackagingState::InImports); + [[clang::fallthrough]]; + + case Context::PackagingState::InImports: + HandleImportAndPackage(context, state, NodeKind::ImportDirective, + /*expect_api_or_impl=*/false); + break; + + case Context::PackagingState::AfterNonPackagingDeclaration: { + context.set_packaging_state( + Context::PackagingState::InImportsAfterNonPackagingDeclaration); + CARBON_DIAGNOSTIC( + ImportTooLate, Error, + "`import` directives must come after the `package` directive (if " + "present) and before any other entities in the file."); + CARBON_DIAGNOSTIC(FirstDeclaration, Note, "First declaration is here."); + context.emitter() + .Build(intro_token, ImportTooLate) + .Note(context.first_non_packaging_token(), FirstDeclaration) + .Emit(); + ExitOnParseError(context, state, NodeKind::ImportDirective); + break; + } + case Context::PackagingState::InImportsAfterNonPackagingDeclaration: + // 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); + break; + } +} + +auto HandlePackage(Context& context) -> void { + auto state = context.PopState(); + + auto intro_token = context.Consume(); + context.AddLeafNode(NodeKind::PackageIntroducer, intro_token); + + if (intro_token != Lex::Token::FirstNonCommentToken) { + CARBON_DIAGNOSTIC( + PackageTooLate, Error, + "The `package` directive must be the first non-comment line."); + CARBON_DIAGNOSTIC(FirstNonCommentLine, Note, + "First non-comment line is here."); + context.emitter() + .Build(intro_token, PackageTooLate) + .Note(Lex::Token::FirstNonCommentToken, FirstNonCommentLine) + .Emit(); + ExitOnParseError(context, state, NodeKind::PackageDirective); + return; + } + + // `package` is no longer allowed, but `import` may repeat. + context.set_packaging_state(Context::PackagingState::InImports); + HandleImportAndPackage(context, state, NodeKind::PackageDirective, + /*expect_api_or_impl=*/true); +} + +} // namespace Carbon::Parse diff --git a/toolchain/parse/handle_package.cpp b/toolchain/parse/handle_package.cpp deleted file mode 100644 index 7328b9a0e916..000000000000 --- a/toolchain/parse/handle_package.cpp +++ /dev/null @@ -1,90 +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 - -#include "toolchain/parse/context.h" - -namespace Carbon::Parse { - -auto HandlePackage(Context& context) -> void { - auto state = context.PopState(); - - context.AddLeafNode(NodeKind::PackageIntroducer, context.Consume()); - - auto exit_on_parse_error = [&]() { - auto semi_token = context.SkipPastLikelyEnd(state.token); - return context.AddNode(NodeKind::PackageDirective, - semi_token ? *semi_token : state.token, - state.subtree_start, - /*has_error=*/true); - }; - - if (!context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::Identifier, - NodeKind::Name)) { - CARBON_DIAGNOSTIC(ExpectedIdentifierAfterPackage, Error, - "Expected identifier after `package`."); - context.emitter().Emit(*context.position(), ExpectedIdentifierAfterPackage); - exit_on_parse_error(); - return; - } - - bool library_parsed = false; - if (auto library_token = context.ConsumeIf(Lex::TokenKind::Library)) { - auto library_start = context.tree().size(); - - if (!context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::StringLiteral, - NodeKind::Literal)) { - CARBON_DIAGNOSTIC( - ExpectedLibraryName, Error, - "Expected a string literal to specify the library name."); - context.emitter().Emit(*context.position(), ExpectedLibraryName); - exit_on_parse_error(); - return; - } - - context.AddNode(NodeKind::PackageLibrary, *library_token, library_start, - /*has_error=*/false); - library_parsed = true; - } - - switch (auto api_or_impl_token = - context.tokens().GetKind(*(context.position()))) { - case Lex::TokenKind::Api: { - context.AddLeafNode(NodeKind::PackageApi, context.Consume()); - break; - } - case Lex::TokenKind::Impl: { - context.AddLeafNode(NodeKind::PackageImpl, context.Consume()); - break; - } - default: { - if (!library_parsed && - api_or_impl_token == 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); - } else { - CARBON_DIAGNOSTIC(ExpectedApiOrImpl, Error, - "Expected a `api` or `impl`."); - context.emitter().Emit(*context.position(), ExpectedApiOrImpl); - } - exit_on_parse_error(); - return; - } - } - - if (!context.PositionIs(Lex::TokenKind::Semi)) { - context.EmitExpectedDeclarationSemi(Lex::TokenKind::Package); - exit_on_parse_error(); - return; - } - - context.AddNode(NodeKind::PackageDirective, context.Consume(), - state.subtree_start, - /*has_error=*/false); -} - -} // namespace Carbon::Parse diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 25dd98de6347..6ce865ccfa31 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -92,21 +92,34 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(NameExpression, 0, CARBON_TOKEN(Identifier)) // ---------------------------------------------------------------------------- +// `library`: +// _external_: Literal +// Library +CARBON_PARSE_NODE_KIND_CHILD_COUNT(Library, 1, CARBON_TOKEN(Library)) + // `package`: // PackageIntroducer // _external_: Name -// _external_: Literal -// PackageLibrary +// _optional_ _external_: Library // PackageApi or PackageImpl // PackageDirective CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageIntroducer, 0, CARBON_TOKEN(Package)) CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageApi, 0, CARBON_TOKEN(Api)) CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageImpl, 0, CARBON_TOKEN(Impl)) -CARBON_PARSE_NODE_KIND_CHILD_COUNT(PackageLibrary, 1, CARBON_TOKEN(Library)) CARBON_PARSE_NODE_KIND_BRACKET(PackageDirective, PackageIntroducer, CARBON_TOKEN(Semi) CARBON_IF_ERROR(CARBON_TOKEN(Package))) +// `import`: +// ImportIntroducer +// _external_: Name +// _optional_ _external_: Library +// 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))) + // `namespace`: // NamespaceStart // _external_: Name or QualifiedDeclaration diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index 7dead03a53d2..636619eef4c3 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -419,6 +419,12 @@ CARBON_PARSE_STATE(FunctionSignatureFinish) // (state done) CARBON_PARSE_STATE(FunctionDefinitionFinish) +// Handles `import`. +// +// Always: +// (state done) +CARBON_PARSE_STATE(Import) + // Handles `namespace`. // // Always: diff --git a/toolchain/parse/testdata/packages/import/after_import.carbon b/toolchain/parse/testdata/packages/import/after_import.carbon new file mode 100644 index 000000000000..7f14322681e6 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/after_import.carbon @@ -0,0 +1,20 @@ +// 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 A; +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: 'Name', 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: '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 new file mode 100644 index 000000000000..7f859725ae1e --- /dev/null +++ b/toolchain/parse/testdata/packages/import/after_package.carbon @@ -0,0 +1,22 @@ +// 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 + +package A api; + +import B; + +// CHECK:STDOUT: - filename: after_package.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'Name', 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: '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 new file mode 100644 index 000000000000..4cfb8a73dbb5 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/basic.carbon @@ -0,0 +1,16 @@ +// 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:STDOUT: - filename: basic.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'Name', 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/fail_after_decl.carbon b/toolchain/parse/testdata/packages/import/fail_after_decl.carbon new file mode 100644 index 000000000000..0d5f0e416637 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/fail_after_decl.carbon @@ -0,0 +1,28 @@ +// 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 + +fn A(); + +// CHECK:STDERR: fail_after_decl.carbon:[[@LINE+6]]:1: ERROR: `import` directives must come after the `package` directive (if present) and before any other entities in the file. +// CHECK:STDERR: import B; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_after_decl.carbon:[[@LINE-5]]:1: First declaration is here. +// CHECK:STDERR: fn A(); +// CHECK:STDERR: ^ +import B; + +// CHECK:STDOUT: - filename: fail_after_decl.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'A'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 5}, +// 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_after_decl_repeated.carbon b/toolchain/parse/testdata/packages/import/fail_after_decl_repeated.carbon new file mode 100644 index 000000000000..0086c2c8d84e --- /dev/null +++ b/toolchain/parse/testdata/packages/import/fail_after_decl_repeated.carbon @@ -0,0 +1,63 @@ +// 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 + +fn A(); + +// CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE+6]]:1: ERROR: `import` directives must come after the `package` directive (if present) and before any other entities in the file. +// CHECK:STDERR: import B; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE-5]]:1: First declaration is here. +// CHECK:STDERR: fn A(); +// CHECK:STDERR: ^ +import B; +// Note this is still invalid, but doesn't warn because it's sequential. +import C; + +// CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE+6]]:1: ERROR: The `package` directive must be the first non-comment line. +// CHECK:STDERR: package D; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE-15]]:1: First non-comment line is here. +// CHECK:STDERR: fn A(); +// CHECK:STDERR: ^ +package D; + +fn E(); + +// CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE+6]]:1: ERROR: `import` directives must come after the `package` directive (if present) and before any other entities in the file. +// CHECK:STDERR: import F; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE-25]]:1: First declaration is here. +// CHECK:STDERR: fn A(); +// CHECK:STDERR: ^ +import F; +// Note this is still invalid, but doesn't warn because it's sequential. +import G; + +// CHECK:STDOUT: - filename: fail_after_decl_repeated.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'A'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'E'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 2}, +// 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_extra_string.carbon b/toolchain/parse/testdata/packages/import/fail_extra_string.carbon new file mode 100644 index 000000000000..6d523bad9a56 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/fail_extra_string.carbon @@ -0,0 +1,21 @@ +// 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_extra_string.carbon:[[@LINE+3]]:26: ERROR: `import` declarations must end with a `;`. +// CHECK:STDERR: import Foo library "bar" "baz"; +// CHECK:STDERR: ^ +import Foo library "bar" "baz"; + +// CHECK:STDOUT: - filename: fail_extra_string.carbon +// 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: '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 new file mode 100644 index 000000000000..e2a745738e28 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/fail_library_is_identifier.carbon @@ -0,0 +1,19 @@ +// 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_is_identifier.carbon:[[@LINE+3]]:25: ERROR: Expected a string literal to specify the library name. +// CHECK:STDERR: import Geometry library Shapes; +// CHECK:STDERR: ^ +import Geometry library Shapes; + +// CHECK:STDOUT: - filename: fail_library_is_identifier.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'Name', 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 new file mode 100644 index 000000000000..0bad8f5bcce7 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/fail_library_skips_name.carbon @@ -0,0 +1,18 @@ +// 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 new file mode 100644 index 000000000000..27d3b2950004 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/fail_name_is_keyword.carbon @@ -0,0 +1,18 @@ +// 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_name_is_keyword.carbon:[[@LINE+3]]:8: ERROR: Expected identifier after `import`. +// CHECK:STDERR: import fn; +// CHECK:STDERR: ^ +import fn; + +// CHECK:STDOUT: - filename: fail_name_is_keyword.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_no_name.carbon b/toolchain/parse/testdata/packages/import/fail_no_name.carbon new file mode 100644 index 000000000000..1b867b767483 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/fail_no_name.carbon @@ -0,0 +1,18 @@ +// 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_no_name.carbon:[[@LINE+3]]:7: ERROR: Expected identifier after `import`. +// CHECK:STDERR: import; +// CHECK:STDERR: ^ +import; + +// CHECK:STDOUT: - filename: fail_no_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_no_semi.carbon b/toolchain/parse/testdata/packages/import/fail_no_semi.carbon new file mode 100644 index 000000000000..93580a7f2720 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/fail_no_semi.carbon @@ -0,0 +1,19 @@ +// 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_no_semi.carbon:[[@LINE+10]]:21: ERROR: `import` declarations must end with a `;`. +// CHECK:STDERR: // CHECK:STDOUT: ] +// CHECK:STDERR: ^ +// CHECK:STDOUT: - filename: fail_no_semi.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'Name', 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 new file mode 100644 index 000000000000..815fb103cd4f --- /dev/null +++ b/toolchain/parse/testdata/packages/import/fail_omit_library_keyword.carbon @@ -0,0 +1,19 @@ +// 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_omit_library_keyword.carbon:[[@LINE+3]]:17: ERROR: Missing `library` keyword. +// CHECK:STDERR: import Geometry "Shapes"; +// CHECK:STDERR: ^ +import Geometry "Shapes"; + +// CHECK:STDOUT: - filename: fail_omit_library_keyword.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'Name', 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 new file mode 100644 index 000000000000..6fa8f8d5c475 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/fail_type.carbon @@ -0,0 +1,19 @@ +// 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_type.carbon:[[@LINE+3]]:17: ERROR: `import` declarations must end with a `;`. +// CHECK:STDERR: import Geometry api; +// CHECK:STDERR: ^ +import Geometry api; + +// CHECK:STDOUT: - filename: fail_type.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'Name', 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 new file mode 100644 index 000000000000..3d5b756ce3a8 --- /dev/null +++ b/toolchain/parse/testdata/packages/import/library.carbon @@ -0,0 +1,18 @@ +// 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 library "Shapes"; + +// CHECK:STDOUT: - filename: library.carbon +// 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: 'ImportDirective', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/package/api.carbon b/toolchain/parse/testdata/packages/package/api.carbon similarity index 100% rename from toolchain/parse/testdata/package/api.carbon rename to toolchain/parse/testdata/packages/package/api.carbon diff --git a/toolchain/parse/testdata/package/api_library.carbon b/toolchain/parse/testdata/packages/package/api_library.carbon similarity index 90% rename from toolchain/parse/testdata/package/api_library.carbon rename to toolchain/parse/testdata/packages/package/api_library.carbon index e5f746307a9e..4ab898ef3034 100644 --- a/toolchain/parse/testdata/package/api_library.carbon +++ b/toolchain/parse/testdata/packages/package/api_library.carbon @@ -12,7 +12,7 @@ package Geometry library "Shapes" api; // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, // CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'Literal', text: '"Shapes"'}, -// CHECK:STDOUT: {kind: 'PackageLibrary', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Library', 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_decl.carbon b/toolchain/parse/testdata/packages/package/fail_after_decl.carbon new file mode 100644 index 000000000000..aa22481fb542 --- /dev/null +++ b/toolchain/parse/testdata/packages/package/fail_after_decl.carbon @@ -0,0 +1,28 @@ +// 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 + +fn A(); + +// CHECK:STDERR: fail_after_decl.carbon:[[@LINE+6]]:1: ERROR: The `package` directive must be the first non-comment line. +// CHECK:STDERR: package B api; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_after_decl.carbon:[[@LINE-5]]:1: First non-comment line is here. +// CHECK:STDERR: fn A(); +// CHECK:STDERR: ^ +package B api; + +// CHECK:STDOUT: - filename: fail_after_decl.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'A'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/package/fail_after_import.carbon b/toolchain/parse/testdata/packages/package/fail_after_import.carbon new file mode 100644 index 000000000000..843dfa929f67 --- /dev/null +++ b/toolchain/parse/testdata/packages/package/fail_after_import.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 A; + +// CHECK:STDERR: fail_after_import.carbon:[[@LINE+6]]:1: ERROR: The `package` directive must be the first non-comment line. +// CHECK:STDERR: package B api; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_after_import.carbon:[[@LINE-5]]:1: First non-comment line is here. +// CHECK:STDERR: import A; +// CHECK:STDERR: ^ +package B api; + +// CHECK:STDOUT: - filename: fail_after_import.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'Name', 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}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/package/fail_after_package.carbon b/toolchain/parse/testdata/packages/package/fail_after_package.carbon new file mode 100644 index 000000000000..341c0b2a1315 --- /dev/null +++ b/toolchain/parse/testdata/packages/package/fail_after_package.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 + +package A api; +// CHECK:STDERR: fail_after_package.carbon:[[@LINE+6]]:1: ERROR: The `package` directive must be the first non-comment line. +// CHECK:STDERR: package B api; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_after_package.carbon:[[@LINE-4]]:1: First non-comment line is here. +// CHECK:STDERR: package A api; +// CHECK:STDERR: ^ +package B api; + +// CHECK:STDOUT: - filename: fail_after_package.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'Name', text: 'A'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/package/fail_extra_string.carbon b/toolchain/parse/testdata/packages/package/fail_extra_string.carbon similarity index 88% rename from toolchain/parse/testdata/package/fail_extra_string.carbon rename to toolchain/parse/testdata/packages/package/fail_extra_string.carbon index d3dab82d6aa6..9432f4c7ee34 100644 --- a/toolchain/parse/testdata/package/fail_extra_string.carbon +++ b/toolchain/parse/testdata/packages/package/fail_extra_string.carbon @@ -4,7 +4,7 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_extra_string.carbon:[[@LINE+3]]:27: ERROR: Expected a `api` or `impl`. +// CHECK:STDERR: fail_extra_string.carbon:[[@LINE+3]]:27: ERROR: Expected `api` or `impl`. // CHECK:STDERR: package Foo library "bar" "baz"; // CHECK:STDERR: ^ package Foo library "bar" "baz"; @@ -15,7 +15,7 @@ package Foo library "bar" "baz"; // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, // CHECK:STDOUT: {kind: 'Name', text: 'Foo'}, // CHECK:STDOUT: {kind: 'Literal', text: '"bar"'}, -// CHECK:STDOUT: {kind: 'PackageLibrary', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Library', 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/package/fail_library_is_identifier.carbon b/toolchain/parse/testdata/packages/package/fail_library_is_identifier.carbon similarity index 100% rename from toolchain/parse/testdata/package/fail_library_is_identifier.carbon rename to toolchain/parse/testdata/packages/package/fail_library_is_identifier.carbon diff --git a/toolchain/parse/testdata/package/fail_library_skips_name.carbon b/toolchain/parse/testdata/packages/package/fail_library_skips_name.carbon similarity index 100% rename from toolchain/parse/testdata/package/fail_library_skips_name.carbon rename to toolchain/parse/testdata/packages/package/fail_library_skips_name.carbon diff --git a/toolchain/parse/testdata/package/fail_name_is_keyword.carbon b/toolchain/parse/testdata/packages/package/fail_name_is_keyword.carbon similarity index 100% rename from toolchain/parse/testdata/package/fail_name_is_keyword.carbon rename to toolchain/parse/testdata/packages/package/fail_name_is_keyword.carbon diff --git a/toolchain/parse/testdata/package/fail_no_name.carbon b/toolchain/parse/testdata/packages/package/fail_no_name.carbon similarity index 100% rename from toolchain/parse/testdata/package/fail_no_name.carbon rename to toolchain/parse/testdata/packages/package/fail_no_name.carbon diff --git a/toolchain/parse/testdata/package/fail_no_semi.carbon b/toolchain/parse/testdata/packages/package/fail_no_semi.carbon similarity index 100% rename from toolchain/parse/testdata/package/fail_no_semi.carbon rename to toolchain/parse/testdata/packages/package/fail_no_semi.carbon diff --git a/toolchain/parse/testdata/package/fail_no_type.carbon b/toolchain/parse/testdata/packages/package/fail_no_type.carbon similarity index 97% rename from toolchain/parse/testdata/package/fail_no_type.carbon rename to toolchain/parse/testdata/packages/package/fail_no_type.carbon index 9b549782c031..e574d90e24e8 100644 --- a/toolchain/parse/testdata/package/fail_no_type.carbon +++ b/toolchain/parse/testdata/packages/package/fail_no_type.carbon @@ -4,7 +4,7 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_no_type.carbon:[[@LINE+3]]:17: ERROR: Expected a `api` or `impl`. +// CHECK:STDERR: fail_no_type.carbon:[[@LINE+3]]:17: ERROR: Expected `api` or `impl`. // CHECK:STDERR: package Geometry; // CHECK:STDERR: ^ package Geometry; diff --git a/toolchain/parse/testdata/package/fail_omit_library_keyword.carbon b/toolchain/parse/testdata/packages/package/fail_omit_library_keyword.carbon similarity index 100% rename from toolchain/parse/testdata/package/fail_omit_library_keyword.carbon rename to toolchain/parse/testdata/packages/package/fail_omit_library_keyword.carbon diff --git a/toolchain/parse/testdata/package/impl.carbon b/toolchain/parse/testdata/packages/package/impl.carbon similarity index 100% rename from toolchain/parse/testdata/package/impl.carbon rename to toolchain/parse/testdata/packages/package/impl.carbon diff --git a/toolchain/parse/testdata/package/impl_library.carbon b/toolchain/parse/testdata/packages/package/impl_library.carbon similarity index 90% rename from toolchain/parse/testdata/package/impl_library.carbon rename to toolchain/parse/testdata/packages/package/impl_library.carbon index 25141406eaf4..878b96811005 100644 --- a/toolchain/parse/testdata/package/impl_library.carbon +++ b/toolchain/parse/testdata/packages/package/impl_library.carbon @@ -12,7 +12,7 @@ package Geometry library "Shapes" impl; // CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, // CHECK:STDOUT: {kind: 'Name', text: 'Geometry'}, // CHECK:STDOUT: {kind: 'Literal', text: '"Shapes"'}, -// CHECK:STDOUT: {kind: 'PackageLibrary', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Library', text: 'library', subtree_size: 2}, // CHECK:STDOUT: {kind: 'PackageImpl', text: 'impl'}, // CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 6}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parse/tree.cpp b/toolchain/parse/tree.cpp index 85c088474435..09d31cda2fd5 100644 --- a/toolchain/parse/tree.cpp +++ b/toolchain/parse/tree.cpp @@ -31,12 +31,6 @@ auto Tree::Parse(Lex::TokenizedBuffer& tokens, DiagnosticConsumer& consumer, context.PushState(State::DeclarationScopeLoop); - // The package should always be the first token, if it's present. Any other - // use is invalid. - if (context.PositionIs(Lex::TokenKind::Package)) { - context.PushState(State::Package); - } - while (!context.state_stack().empty()) { // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case)