Ver Fonte

Parse all kinds of declarations at function scope. (#4779)

These don't fully work in check and beyond yet, because they're not
added into lexical lookup, but already mostly do the right thing.

Per #3407, disallow namespace declarations anywhere other than at file
scope for now.

We don't treat statements starting with a packaging introducer keyword
(`package`, `library`, `import`) as declarations because they're
sufficiently unlikely to occur that the error recovery doesn't seem
important, and this avoids needing to disambiguate `package.` at the
start of an expression.
Richard Smith há 1 ano atrás
pai
commit
d42128ef9a
25 ficheiros alterados com 1223 adições e 116 exclusões
  1. 12 0
      toolchain/check/handle_namespace.cpp
  2. 66 1
      toolchain/check/testdata/class/adapter/adapt.carbon
  3. 57 7
      toolchain/check/testdata/class/fail_base_misplaced.carbon
  4. 103 16
      toolchain/check/testdata/class/fail_todo_local_class.carbon
  5. 106 0
      toolchain/check/testdata/function/definition/no_prelude/fail_local_decl.carbon
  6. 57 2
      toolchain/check/testdata/impl/fail_impl_as_scope.carbon
  7. 132 0
      toolchain/check/testdata/namespace/fail_not_top_level.carbon
  8. 4 0
      toolchain/diagnostics/diagnostic_kind.def
  9. 14 15
      toolchain/parse/handle_decl_scope_loop.cpp
  10. 6 0
      toolchain/parse/handle_expr.cpp
  11. 22 15
      toolchain/parse/handle_statement.cpp
  12. 2 1
      toolchain/parse/node_ids.h
  13. 24 24
      toolchain/parse/state.def
  14. 35 0
      toolchain/parse/testdata/choice/local.carbon
  15. 5 8
      toolchain/parse/testdata/class/adapt.carbon
  16. 25 3
      toolchain/parse/testdata/class/base_misplaced.carbon
  17. 52 0
      toolchain/parse/testdata/class/local.carbon
  18. 178 0
      toolchain/parse/testdata/function/definition/decl_statement.carbon
  19. 39 0
      toolchain/parse/testdata/function/definition/nested.carbon
  20. 29 0
      toolchain/parse/testdata/namespace/local.carbon
  21. 8 15
      toolchain/parse/testdata/package_expr/fail_standalone.carbon
  22. 50 0
      toolchain/parse/testdata/packages/import/fail_in_nested_scope.carbon
  23. 92 0
      toolchain/parse/testdata/packages/library/fail_in_nested_scope.carbon
  24. 98 0
      toolchain/parse/testdata/packages/package/fail_in_nested_scope.carbon
  25. 7 9
      toolchain/parse/typed_nodes.h

+ 12 - 0
toolchain/check/handle_namespace.cpp

@@ -10,6 +10,7 @@
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/name_scope.h"
+#include "toolchain/sem_ir/typed_insts.h"
 
 namespace Carbon::Check {
 
@@ -26,6 +27,12 @@ auto HandleParseNode(Context& context, Parse::NamespaceStartId /*node_id*/)
   return true;
 }
 
+static auto IsNamespaceScope(Context& context, SemIR::NameScopeId name_scope_id)
+    -> bool {
+  auto [_, inst] = context.name_scopes().GetInstIfValid(name_scope_id);
+  return inst && inst->Is<SemIR::Namespace>();
+}
+
 auto HandleParseNode(Context& context, Parse::NamespaceId node_id) -> bool {
   auto name_context = context.decl_name_stack().FinishName(
       PopNameComponentWithoutParams(context, Lex::TokenKind::Namespace));
@@ -83,6 +90,11 @@ auto HandleParseNode(Context& context, Parse::NamespaceId node_id) -> bool {
     namespace_inst.name_scope_id = context.name_scopes().Add(
         namespace_id, name_context.name_id_for_new_inst(),
         name_context.parent_scope_id);
+    if (!IsNamespaceScope(context, name_context.parent_scope_id)) {
+      CARBON_DIAGNOSTIC(NamespaceDeclNotAtTopLevel, Error,
+                        "`namespace` declaration not at top level");
+      context.emitter().Emit(node_id, NamespaceDeclNotAtTopLevel);
+    }
   }
 
   context.ReplaceInstBeforeConstantUse(namespace_id, namespace_inst);

+ 66 - 1
toolchain/check/testdata/class/adapter/adapt.carbon

@@ -39,12 +39,30 @@ class AdaptNotExtend {
 
 fn F(a: AdaptNotExtend) {
   // `Adapted` is not extended, so lookup for `F` finds nothing.
-  // CHECK:STDERR: fail_not_extend.carbon:[[@LINE+3]]:3: error: member name `F` not found in `AdaptNotExtend` [MemberNameNotFoundInScope]
+  // CHECK:STDERR: fail_not_extend.carbon:[[@LINE+4]]:3: error: member name `F` not found in `AdaptNotExtend` [MemberNameNotFoundInScope]
   // CHECK:STDERR:   a.F();
   // CHECK:STDERR:   ^~~
+  // CHECK:STDERR:
   a.F();
 }
 
+// --- fail_misplaced.carbon
+
+fn F() {
+  // CHECK:STDERR: fail_misplaced.carbon:[[@LINE+4]]:3: error: `adapt` declaration outside class [ClassSpecificDeclOutsideClass]
+  // CHECK:STDERR:   adapt i32;
+  // CHECK:STDERR:   ^~~~~~~~~~
+  // CHECK:STDERR:
+  adapt i32;
+}
+
+interface I {
+  // CHECK:STDERR: fail_misplaced.carbon:[[@LINE+3]]:3: error: `adapt` declaration outside class [ClassSpecificDeclOutsideClass]
+  // CHECK:STDERR:   adapt i32;
+  // CHECK:STDERR:   ^~~~~~~~~~
+  adapt i32;
+}
+
 // CHECK:STDOUT: --- basic.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -184,3 +202,50 @@ fn F(a: AdaptNotExtend) {
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_misplaced.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [template]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [template]
+// CHECK:STDOUT:   %I.type: type = facet_type <@I> [template]
+// CHECK:STDOUT:   %Self: %I.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     .Int = %import_ref.187
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:     .I = %I.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {} {}
+// CHECK:STDOUT:   %I.decl: type = interface_decl @I [template = constants.%I.type] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @I {
+// CHECK:STDOUT:   %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [template = constants.%int_32]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [template = constants.%int_32]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 57 - 7
toolchain/check/testdata/class/fail_base_misplaced.carbon

@@ -17,25 +17,55 @@ base class B {}
 extend base: B;
 
 fn F() {
-  // CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+7]]:3: error: expected expression [ExpectedExpr]
+  // CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+4]]:3: error: `base` declaration outside class [ClassSpecificDeclOutsideClass]
   // CHECK:STDERR:   extend base: B;
-  // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  // CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+3]]:3: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
-  // CHECK:STDERR:   extend base: B;
-  // CHECK:STDERR:   ^~~~~~
   extend base: B;
 }
 
+class C {
+  fn F() {
+    // CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+3]]:5: error: `base` declaration outside class [ClassSpecificDeclOutsideClass]
+    // CHECK:STDERR:     extend base: B;
+    // CHECK:STDERR:     ^~~~~~~~~~~~~~~
+    extend base: B;
+  }
+}
+
 // CHECK:STDOUT: --- fail_base_misplaced.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %B: type = class_type @B [template]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [template]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [template]
+// CHECK:STDOUT:   %F.type.b25: type = fn_type @F.1 [template]
+// CHECK:STDOUT:   %F.c41: %F.type.b25 = struct_value () [template]
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %F.type.675: type = fn_type @F.2 [template]
+// CHECK:STDOUT:   %F.2c9: %F.type.675 = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: file {}
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .B = %B.decl
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %B.decl: type = class_decl @B [template = constants.%B] {} {}
+// CHECK:STDOUT:   %B.ref: type = name_ref B, %B.decl [template = constants.%B]
+// CHECK:STDOUT:   %F.decl: %F.type.b25 = fn_decl @F.1 [template = constants.%F.c41] {} {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {} {}
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @B {
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [template = constants.%complete_type]
@@ -45,5 +75,25 @@ fn F() {
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %F.decl: %F.type.675 = fn_decl @F.2 [template = constants.%F.2c9] {} {}
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [template = constants.%complete_type]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT:   .F = %F.decl
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.1() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [template = constants.%B]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [template = constants.%B]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 103 - 16
toolchain/check/testdata/class/fail_todo_local_class.carbon

@@ -8,21 +8,22 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_todo_local_class.carbon
 
-// TODO: Support parsing classes at function scope.
 class A {
-  fn F() {
-    // CHECK:STDERR: fail_todo_local_class.carbon:[[@LINE+7]]:5: error: expected expression [ExpectedExpr]
-    // CHECK:STDERR:     class B {
-    // CHECK:STDERR:     ^~~~~
-    // CHECK:STDERR:
-    // CHECK:STDERR: fail_todo_local_class.carbon:[[@LINE+3]]:5: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
-    // CHECK:STDERR:     class B {
-    // CHECK:STDERR:     ^~~~~
+  fn F() -> i32 {
     class B {
-      fn G() {
-        var b: B = {};
+      fn Make() -> Self {
+        returned var b: Self = {.n = 1};
+        return var;
       }
+
+      var n: i32;
     }
+
+    // TODO: Add the name `B` to the lexical scope.
+    // CHECK:STDERR: fail_todo_local_class.carbon:[[@LINE+3]]:12: error: name `B` not found [NameNotFound]
+    // CHECK:STDERR:     return B.Make().n;
+    // CHECK:STDERR:            ^
+    return B.Make().n;
   }
 }
 
@@ -30,17 +31,59 @@ class A {
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %A: type = class_type @A [template]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [template]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [template]
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [template]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [template]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [template]
+// CHECK:STDOUT:   %complete_type.357: <witness> = complete_type_witness %empty_struct_type [template]
+// CHECK:STDOUT:   %B: type = class_type @B [template]
+// CHECK:STDOUT:   %Make.type: type = fn_type @Make [template]
+// CHECK:STDOUT:   %Make: %Make.type = struct_value () [template]
+// CHECK:STDOUT:   %B.elem: type = unbound_element_type %B, %i32 [template]
+// CHECK:STDOUT:   %struct_type.n.5cd: type = struct_type {.n: %i32} [template]
+// CHECK:STDOUT:   %complete_type.9b7: <witness> = complete_type_witness %struct_type.n.5cd [template]
+// CHECK:STDOUT:   %int_1.5b8: Core.IntLiteral = int_value 1 [template]
+// CHECK:STDOUT:   %struct_type.n.44a: type = struct_type {.n: Core.IntLiteral} [template]
+// CHECK:STDOUT:   %Convert.type.cd1: type = fn_type @Convert.1, @ImplicitAs(%i32) [template]
+// CHECK:STDOUT:   %impl_witness.5b0: <witness> = impl_witness (imports.%import_ref.723), @impl.1(%int_32) [template]
+// CHECK:STDOUT:   %Convert.type.466: type = fn_type @Convert.2, @impl.1(%int_32) [template]
+// CHECK:STDOUT:   %Convert.925: %Convert.type.466 = struct_value () [template]
+// CHECK:STDOUT:   %Convert.bound: <bound method> = bound_method %int_1.5b8, %Convert.925 [template]
+// CHECK:STDOUT:   %Convert.specific_fn: <specific function> = specific_function %Convert.bound, @Convert.2(%int_32) [template]
+// CHECK:STDOUT:   %int_1.c60: %i32 = int_value 1 [template]
+// CHECK:STDOUT:   %B.val: %B = struct_value (%int_1.c60) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: file {}
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     .Int = %import_ref.187
+// CHECK:STDOUT:     .ImplicitAs = %import_ref.a69
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .A = %A.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %A.decl: type = class_decl @A [template = constants.%A] {} {}
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @A {
-// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {} {}
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [template = constants.%complete_type]
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
+// CHECK:STDOUT:     %return.patt: %i32 = return_slot_pattern
+// CHECK:STDOUT:     %return.param_patt: %i32 = out_param_pattern %return.patt, runtime_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [template = constants.%int_32]
+// CHECK:STDOUT:     %i32: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32]
+// CHECK:STDOUT:     %return.param: ref %i32 = out_param runtime_param0
+// CHECK:STDOUT:     %return: ref %i32 = return_slot %return.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [template = constants.%complete_type.357]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%A
@@ -48,5 +91,49 @@ class A {
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT: class @B {
+// CHECK:STDOUT:   %Make.decl: %Make.type = fn_decl @Make [template = constants.%Make] {
+// CHECK:STDOUT:     %return.patt: %B = return_slot_pattern
+// CHECK:STDOUT:     %return.param_patt: %B = out_param_pattern %return.patt, runtime_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%B [template = constants.%B]
+// CHECK:STDOUT:     %return.param: ref %B = out_param runtime_param0
+// CHECK:STDOUT:     %return: ref %B = return_slot %return.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc19: %B.elem = field_decl n, element0 [template]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.n.5cd [template = constants.%complete_type.9b7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%B
+// CHECK:STDOUT:   .Make = %Make.decl
+// CHECK:STDOUT:   .n = %.loc19
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %B.decl: type = class_decl @B [template = constants.%B] {} {}
+// CHECK:STDOUT:   %B.ref: <error> = name_ref B, <error> [template = <error>]
+// CHECK:STDOUT:   %Make.ref: <error> = name_ref Make, <error> [template = <error>]
+// CHECK:STDOUT:   %n.ref: <error> = name_ref n, <error> [template = <error>]
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Make() -> %return.param_patt: %B {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %b: ref %B = bind_name b, %return
+// CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [template = constants.%int_1.5b8]
+// CHECK:STDOUT:   %.loc15_39.1: %struct_type.n.44a = struct_literal (%int_1)
+// CHECK:STDOUT:   %impl.elem0: %Convert.type.cd1 = impl_witness_access constants.%impl_witness.5b0, element0 [template = constants.%Convert.925]
+// CHECK:STDOUT:   %Convert.bound: <bound method> = bound_method %int_1, %impl.elem0 [template = constants.%Convert.bound]
+// CHECK:STDOUT:   %Convert.specific_fn: <specific function> = specific_function %Convert.bound, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn]
+// CHECK:STDOUT:   %int.convert_checked: init %i32 = call %Convert.specific_fn(%int_1) [template = constants.%int_1.c60]
+// CHECK:STDOUT:   %.loc15_39.2: init %i32 = converted %int_1, %int.convert_checked [template = constants.%int_1.c60]
+// CHECK:STDOUT:   %.loc15_39.3: ref %i32 = class_element_access %return, element0
+// CHECK:STDOUT:   %.loc15_39.4: init %i32 = initialize_from %.loc15_39.2 to %.loc15_39.3 [template = constants.%int_1.c60]
+// CHECK:STDOUT:   %.loc15_39.5: init %B = class_init (%.loc15_39.4), %return [template = constants.%B.val]
+// CHECK:STDOUT:   %.loc15_40: init %B = converted %.loc15_39.1, %.loc15_39.5 [template = constants.%B.val]
+// CHECK:STDOUT:   assign %return, %.loc15_40
+// CHECK:STDOUT:   return %b to %return
+// CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 106 - 0
toolchain/check/testdata/function/definition/no_prelude/fail_local_decl.carbon

@@ -0,0 +1,106 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/no_prelude/fail_local_decl.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/no_prelude/fail_local_decl.carbon
+
+// --- fail_virtual.carbon
+
+library "[[@TEST_NAME]]";
+
+fn F() {
+  // CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:3: error: `default` not allowed; requires interface scope [ModifierRequiresInterface]
+  // CHECK:STDERR:   default fn F();
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  default fn F();
+  // CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:3: error: `impl` not allowed; requires class scope [ModifierRequiresClass]
+  // CHECK:STDERR:   impl fn G();
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR:
+  impl fn G();
+  // CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:3: error: `virtual` not allowed; requires class scope [ModifierRequiresClass]
+  // CHECK:STDERR:   virtual fn H();
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  virtual fn H();
+}
+
+// --- fail_access.carbon
+
+library "[[@TEST_NAME]]";
+
+fn F() {
+  // CHECK:STDERR: fail_access.carbon:[[@LINE+4]]:3: error: `private` not allowed; requires class or file scope [ModifierPrivateNotAllowed]
+  // CHECK:STDERR:   private var v: {};
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  private var v: {};
+  // CHECK:STDERR: fail_access.carbon:[[@LINE+3]]:3: error: `protected` not allowed; requires class scope [ModifierProtectedNotAllowed]
+  // CHECK:STDERR:   protected var w: {};
+  // CHECK:STDERR:   ^~~~~~~~~
+  protected var w: {};
+}
+
+// CHECK:STDOUT: --- fail_virtual.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type.b25: type = fn_type @F.1 [template]
+// CHECK:STDOUT:   %F.c41: %F.type.b25 = struct_value () [template]
+// CHECK:STDOUT:   %F.type.21a: type = fn_type @F.2 [template]
+// CHECK:STDOUT:   %F.c14: %F.type.21a = struct_value () [template]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [template]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [template]
+// CHECK:STDOUT:   %H.type: type = fn_type @H [template]
+// CHECK:STDOUT:   %H: %H.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type.b25 = fn_decl @F.1 [template = constants.%F.c41] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.1() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.decl: %F.type.21a = fn_decl @F.2 [template = constants.%F.c14] {} {}
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [template = constants.%G] {} {}
+// CHECK:STDOUT:   %H.decl: %H.type = fn_decl @H [template = constants.%H] {} {}
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @H();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_access.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %v.var: ref %empty_struct_type = var v
+// CHECK:STDOUT:   %v: ref %empty_struct_type = bind_name v, %v.var
+// CHECK:STDOUT:   %w.var: ref %empty_struct_type = var w
+// CHECK:STDOUT:   %w: ref %empty_struct_type = bind_name w, %w.var
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 57 - 2
toolchain/check/testdata/impl/fail_impl_as_scope.carbon

@@ -8,18 +8,40 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_impl_as_scope.carbon
 
+// --- fail_global.carbon
+
+library "[[@TEST_NAME]]";
+
 interface Simple {
   fn F();
 }
 
-// CHECK:STDERR: fail_impl_as_scope.carbon:[[@LINE+3]]:6: error: `impl as` can only be used in a class [ImplAsOutsideClass]
+// CHECK:STDERR: fail_global.carbon:[[@LINE+4]]:6: error: `impl as` can only be used in a class [ImplAsOutsideClass]
 // CHECK:STDERR: impl as Simple {
 // CHECK:STDERR:      ^~
+// CHECK:STDERR:
 impl as Simple {
   fn F() {}
 }
 
-// CHECK:STDOUT: --- fail_impl_as_scope.carbon
+// --- fail_function.carbon
+
+library "[[@TEST_NAME]]";
+
+interface Simple {
+  fn F();
+}
+
+fn Function() {
+  // CHECK:STDERR: fail_function.carbon:[[@LINE+3]]:8: error: `impl as` can only be used in a class [ImplAsOutsideClass]
+  // CHECK:STDERR:   impl as Simple {
+  // CHECK:STDERR:        ^~
+  impl as Simple {
+    fn F() {}
+  }
+}
+
+// CHECK:STDOUT: --- fail_global.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %Simple.type: type = facet_type <@Simple> [template]
@@ -50,3 +72,36 @@ impl as Simple {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%Self) {}
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Simple.type: type = facet_type <@Simple> [template]
+// CHECK:STDOUT:   %Self: %Simple.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %F.assoc_type: type = assoc_entity_type %Simple.type, %F.type [template]
+// CHECK:STDOUT:   %assoc0: %F.assoc_type = assoc_entity element0, @Simple.%F.decl [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Simple {
+// CHECK:STDOUT:   %Self: %Simple.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {} {}
+// CHECK:STDOUT:   %assoc0: %F.assoc_type = assoc_entity element0, %F.decl [template = constants.%assoc0]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .F = %assoc0
+// CHECK:STDOUT:   witness = (%F.decl)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @F(@Simple.%Self: %Simple.type) {
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn();
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Function();
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(constants.%Self) {}
+// CHECK:STDOUT:

+ 132 - 0
toolchain/check/testdata/namespace/fail_not_top_level.carbon

@@ -0,0 +1,132 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/namespace/fail_not_top_level.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/namespace/fail_not_top_level.carbon
+
+fn F() {
+  // CHECK:STDERR: fail_not_top_level.carbon:[[@LINE+4]]:3: error: `namespace` declaration not at top level [NamespaceDeclNotAtTopLevel]
+  // CHECK:STDERR:   namespace N;
+  // CHECK:STDERR:   ^~~~~~~~~~~~
+  // CHECK:STDERR:
+  namespace N;
+  // CHECK:STDERR: fail_not_top_level.carbon:[[@LINE+4]]:6: error: name `N` not found [NameNotFound]
+  // CHECK:STDERR:   fn N.F() {}
+  // CHECK:STDERR:      ^
+  // CHECK:STDERR:
+  fn N.F() {}
+}
+
+class C {
+  // CHECK:STDERR: fail_not_top_level.carbon:[[@LINE+4]]:3: error: `namespace` declaration not at top level [NamespaceDeclNotAtTopLevel]
+  // CHECK:STDERR:   namespace N;
+  // CHECK:STDERR:   ^~~~~~~~~~~~
+  // CHECK:STDERR:
+  namespace N;
+  fn N.F() {}
+}
+
+interface I {
+  // CHECK:STDERR: fail_not_top_level.carbon:[[@LINE+3]]:3: error: `namespace` declaration not at top level [NamespaceDeclNotAtTopLevel]
+  // CHECK:STDERR:   namespace N;
+  // CHECK:STDERR:   ^~~~~~~~~~~~
+  namespace N;
+  fn N.I() {}
+}
+
+// CHECK:STDOUT: --- fail_not_top_level.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type.b25: type = fn_type @F.1 [template]
+// CHECK:STDOUT:   %F.c41: %F.type.b25 = struct_value () [template]
+// CHECK:STDOUT:   %.type: type = fn_type @.1 [template]
+// CHECK:STDOUT:   %.3ad: %.type = struct_value () [template]
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %F.type.2e2: type = fn_type @F.2 [template]
+// CHECK:STDOUT:   %F.5e0: %F.type.2e2 = struct_value () [template]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [template]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [template]
+// CHECK:STDOUT:   %I.type.733: type = facet_type <@I.2> [template]
+// CHECK:STDOUT:   %Self: %I.type.733 = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %I.type.ad2: type = fn_type @I.1 [template]
+// CHECK:STDOUT:   %I: %I.type.ad2 = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .I = %I.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %F.decl: %F.type.b25 = fn_decl @F.1 [template = constants.%F.c41] {} {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {} {}
+// CHECK:STDOUT:   %I.decl: type = interface_decl @I.2 [template = constants.%I.type.733] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @I.2 {
+// CHECK:STDOUT:   %Self: %I.type.733 = bind_symbolic_name Self, 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:   %N: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .I = %I.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %I.decl: %I.type.ad2 = fn_decl @I.1 [template = constants.%I] {} {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .N = %N
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %N: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type.2e2 = fn_decl @F.2 [template = constants.%F.5e0] {} {}
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [template = constants.%complete_type]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT:   .N = %N
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.1() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %N: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %.decl: %.type = fn_decl @.1 [template = constants.%.3ad] {} {}
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @.1() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @I.1(@I.2.%Self: %I.type.733) {
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn() {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     return
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @I.1(constants.%Self) {}
+// CHECK:STDOUT:

+ 4 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -106,6 +106,7 @@ CARBON_DIAGNOSTIC_KIND(ExpectedLibraryName)
 CARBON_DIAGNOSTIC_KIND(ExpectedLibraryNameOrDefault)
 CARBON_DIAGNOSTIC_KIND(MissingLibraryKeyword)
 CARBON_DIAGNOSTIC_KIND(ExportImportPackage)
+CARBON_DIAGNOSTIC_KIND(ExpectedPeriodAfterPackage)
 
 // For-specific diagnostics.
 CARBON_DIAGNOSTIC_KIND(ExpectedIn)
@@ -187,6 +188,9 @@ CARBON_DIAGNOSTIC_KIND(ExternLibraryIncorrect)
 CARBON_DIAGNOSTIC_KIND(ExternLibraryExpected)
 CARBON_DIAGNOSTIC_KIND(ExternRequiresDeclInApiFile)
 
+// Namespace checking.
+CARBON_DIAGNOSTIC_KIND(NamespaceDeclNotAtTopLevel)
+
 // Function call checking.
 CARBON_DIAGNOSTIC_KIND(AddrSelfIsNonRef)
 CARBON_DIAGNOSTIC_KIND(CallArgCountMismatch)

+ 14 - 15
toolchain/parse/handle_decl_scope_loop.cpp

@@ -242,21 +242,8 @@ static auto TryHandleAsModifier(Context& context) -> bool {
   }
 }
 
-auto HandleDeclScopeLoop(Context& context) -> void {
-  // This maintains the current state unless we're at the end of the scope.
-
-  if (context.PositionIs(Lex::TokenKind::CloseCurlyBrace) ||
-      context.PositionIs(Lex::TokenKind::FileEnd)) {
-    // This is the end of the scope, so the loop state ends.
-    context.PopAndDiscardState();
-    return;
-  }
-
-  // Create a state with the correct starting position, with a dummy kind
-  // until we see the declaration's introducer.
-  Context::StateStackEntry state{.state = State::Invalid,
-                                 .token = *context.position(),
-                                 .subtree_start = context.tree().size()};
+auto HandleDecl(Context& context) -> void {
+  auto state = context.PopState();
 
   // Add a placeholder node, to be replaced by the declaration introducer once
   // it is found.
@@ -271,4 +258,16 @@ auto HandleDeclScopeLoop(Context& context) -> void {
   }
 }
 
+auto HandleDeclScopeLoop(Context& context) -> void {
+  // This maintains the current state unless we're at the end of the scope.
+  if (context.PositionIs(Lex::TokenKind::CloseCurlyBrace) ||
+      context.PositionIs(Lex::TokenKind::FileEnd)) {
+    // This is the end of the scope, so the loop state ends.
+    context.PopAndDiscardState();
+    return;
+  }
+
+  context.PushState(State::Decl);
+}
+
 }  // namespace Carbon::Parse

+ 6 - 0
toolchain/parse/handle_expr.cpp

@@ -156,6 +156,12 @@ auto HandleExprInPostfix(Context& context) -> void {
     }
     case Lex::TokenKind::Package: {
       context.AddLeafNode(NodeKind::PackageExpr, context.Consume());
+      if (context.PositionKind() != Lex::TokenKind::Period) {
+        CARBON_DIAGNOSTIC(ExpectedPeriodAfterPackage, Error,
+                          "expected `.` after `package` expression");
+        context.emitter().Emit(*context.position(), ExpectedPeriodAfterPackage);
+        state.has_error = true;
+      }
       context.PushState(state);
       break;
     }

+ 22 - 15
toolchain/parse/handle_statement.cpp

@@ -11,11 +11,6 @@ auto HandleStatement(Context& context) -> void {
   context.PopAndDiscardState();
 
   switch (context.PositionKind()) {
-    case Lex::TokenKind::Alias: {
-      context.PushState(State::Alias);
-      context.AddLeafNode(NodeKind::AliasIntroducer, context.Consume());
-      break;
-    }
     case Lex::TokenKind::Break: {
       context.PushState(State::StatementBreakFinish);
       context.AddLeafNode(NodeKind::BreakStatementStart, context.Consume());
@@ -36,24 +31,15 @@ auto HandleStatement(Context& context) -> void {
       context.PushState(State::StatementIf);
       break;
     }
-    case Lex::TokenKind::Let: {
-      context.PushState(State::Let);
-      context.AddLeafNode(NodeKind::LetIntroducer, context.Consume());
-      break;
-    }
     case Lex::TokenKind::Return: {
       context.PushState(State::StatementReturn);
       break;
     }
     case Lex::TokenKind::Returned: {
+      // TODO: Consider handling this as a modifier.
       context.PushState(State::VarAsReturned);
       break;
     }
-    case Lex::TokenKind::Var: {
-      context.PushState(State::VarAsDecl);
-      context.AddLeafNode(NodeKind::VariableIntroducer, context.Consume());
-      break;
-    }
     case Lex::TokenKind::While: {
       context.PushState(State::StatementWhile);
       break;
@@ -62,6 +48,27 @@ auto HandleStatement(Context& context) -> void {
       context.PushState(State::MatchIntroducer);
       break;
     }
+#define CARBON_PARSE_NODE_KIND(...)
+#define CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Name, ...) \
+  case Lex::TokenKind::Name:
+#include "toolchain/parse/node_kind.def"
+    case Lex::TokenKind::Adapt:
+    case Lex::TokenKind::Alias:
+    case Lex::TokenKind::Choice:
+    case Lex::TokenKind::Class:
+    case Lex::TokenKind::Constraint:
+    case Lex::TokenKind::Fn:
+    case Lex::TokenKind::Import:
+    case Lex::TokenKind::Interface:
+    case Lex::TokenKind::Let:
+    case Lex::TokenKind::Library:
+    case Lex::TokenKind::Namespace:
+    // We intentionally don't handle Package here, because `package.` can be
+    // used at the start of an expression, and it's not worth disambiguating it.
+    case Lex::TokenKind::Var: {
+      context.PushState(State::Decl);
+      break;
+    }
     default: {
       context.PushState(State::ExprStatementFinish);
       context.PushStateForExpr(PrecedenceGroup::ForExprStatement());

+ 2 - 1
toolchain/parse/node_ids.h

@@ -78,7 +78,8 @@ using AnyMemberAccessId =
                      NodeCategory::IntConst>;
 using AnyModifierId = NodeIdInCategory<NodeCategory::Modifier>;
 using AnyPatternId = NodeIdInCategory<NodeCategory::Pattern>;
-using AnyStatementId = NodeIdInCategory<NodeCategory::Statement>;
+using AnyStatementId =
+    NodeIdInCategory<NodeCategory::Statement | NodeCategory::Decl>;
 using AnyRequirementId = NodeIdInCategory<NodeCategory::Requirement>;
 
 // NodeId with kind that matches one of the `T::Kind`s.

+ 24 - 24
toolchain/parse/state.def

@@ -275,8 +275,8 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterImplicit)
 //   (state done)
 CARBON_PARSE_STATE(DeclNameAndParamsAfterParams)
 
-// Handles processing of a declaration scope. Things like fn, class, interface,
-// and so on.
+// Handles processing of a declaration. Things like fn, class, interface, and so
+// on.
 //
 // abstract
 // ^~~~~~~~
@@ -298,95 +298,95 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterParams)
 // ^~~~~~~~~
 // virtual
 // ^~~~~~~
-//   1. DeclScopeLoop
+//   1. Decl
 //
 // adapt ...
 // ^~~~~
 //   1. AdaptAfterIntroducer
-//   2. DeclScopeLoop
 //
 // alias ...
 // ^~~~~
 //   1. Alias
-//   2. DeclScopeLoop
 //
 // base : ...
 // ^~~~
 //   1. BaseAfterIntroducer
-//   2. DeclScopeLoop
 //
 // choice ...
 // ^~~~~~
 //   1. ChoiceIntroducer
-//   2. DeclScopeLoop
 //
 // class ...
 // ^~~~~
 //   1. TypeAfterIntroducerAsClass
-//   2. DeclScopeLoop
 //
 // constraint ...
 // ^~~~~~~~~~
 //   1. TypeAfterIntroducerAsNamedConstraint
-//   2. DeclScopeLoop
 //
 // export <name> ...
 // ^~~~~~
 //   1. ExportName
-//   2. DeclScopeLoop
 //
 // fn ...
 // ^~
 //   1. FunctionIntroducer
-//   2. DeclScopeLoop
 //
 // impl ...
 // ^~~~
 //   1. ImplAfterIntroducer
-//   2. DeclScopeLoop
 //
 // import ...    (in packaging directives)
 // ^~~~~~
 //   1. Import
-//   2. DeclScopeLoop
 //
 // interface ...
 // ^~~~~~~~~
 //   1. TypeAfterIntroducerAsInterface
-//   2. DeclScopeLoop
 //
 // let ...
 // ^~~
 //   1. Let
-//   2. DeclScopeLoop
 //
 // library ...    (in packaging directives)
 // ^~~~~~~
 //   1. Library
-//   2. DeclScopeLoop
 //
 // namespace ...
 // ^~~~~~~~~
 //   1. Namespace
-//   2. DeclScopeLoop
 //
 // package ...    (in packaging directives)
 // ^~~~~~~
 //   1. Package
-//   2. DeclScopeLoop
 //
 // var ...
 // ^~~
 //   1. VarAsDecl
-//   2. DeclScopeLoop
 //
 // ;
 // ^
-//   1. DeclScopeLoop
+//   (state done)
 //
 // ??? ;
 // ^~~~~
 //   (state done)
+CARBON_PARSE_STATE(Decl)
+
+// Handles processing of a declaration scope, which contains a sequence of
+// declarations.
+//
+//  }
+// ^
+//  <eof>
+// ^
+//   (state done)
+//
+//  ...
+// ^
+//   1. Decl
+//   2. DeclScopeLoop
+//
 CARBON_PARSE_STATE(DeclScopeLoop)
 
 // Handles periods. Only does one `.<expression>` segment; the source is
@@ -1069,10 +1069,6 @@ CARBON_PARSE_STATE_VARIANTS2(BindingPatternFinish, Generic, Regular)
 // ^
 //   1. StatementReturn
 //
-//  var ...
-// ^
-//   1. VarAsDecl
-//
 //  returned ...
 // ^
 //   1. VarAsReturned
@@ -1089,6 +1085,10 @@ CARBON_PARSE_STATE_VARIANTS2(BindingPatternFinish, Generic, Regular)
 //  match ...
 // ^
 //   1. MatchIntroducer
+//
+//  <decl introducer or modifier>
+// ^
+//   1. Decl
 CARBON_PARSE_STATE(Statement)
 
 // Handles `break` processing at the `;`.

+ 35 - 0
toolchain/parse/testdata/choice/local.carbon

@@ -0,0 +1,35 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/choice/local.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/choice/local.carbon
+
+fn F() {
+  choice C {
+    X,
+    Y
+  }
+}
+
+// CHECK:STDOUT: - filename: local.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'C'},
+// CHECK:STDOUT:         {kind: 'ChoiceDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'X'},
+// CHECK:STDOUT:         {kind: 'ChoiceAlternativeListComma', text: ','},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'Y'},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinition', text: '}', subtree_size: 7},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 13},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 5 - 8
toolchain/parse/testdata/class/adapt.carbon

@@ -60,17 +60,13 @@ class C {
 }
 
 class D {
-  // CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:9: error: expected expression [ExpectedExpr]
+  // CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+3]]:9: error: expected expression [ExpectedExpr]
   // CHECK:STDERR:   adapt class;
   // CHECK:STDERR:         ^~~~~
-  // CHECK:STDERR:
   adapt class;
 }
 
 fn F() {
-  // CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+3]]:3: error: expected expression [ExpectedExpr]
-  // CHECK:STDERR:   adapt i32;
-  // CHECK:STDERR:   ^~~~~
   adapt i32;
 }
 
@@ -168,8 +164,9 @@ fn F() {
 // CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:         {kind: 'InvalidParse', text: 'adapt', has_error: yes},
-// CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 2},
-// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 8},
+// CHECK:STDOUT:         {kind: 'AdaptIntroducer', text: 'adapt'},
+// CHECK:STDOUT:         {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'AdaptDecl', text: ';', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 25 - 3
toolchain/parse/testdata/class/base_misplaced.carbon

@@ -8,15 +8,37 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/class/base_misplaced.carbon
 
-// This is rejected by check.
-base: i32;
+base class B {}
+
+// These are rejected by check.
+base: B;
+
+fn F() {
+  base: B;
+}
+
 
 // CHECK:STDOUT: - filename: base_misplaced.carbon
 // CHECK:STDOUT:   parse_tree: [
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:         {kind: 'BaseModifier', text: 'base'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'B'},
+// CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 4},
+// CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'BaseIntroducer', text: 'base'},
 // CHECK:STDOUT:       {kind: 'BaseColon', text: ':'},
-// CHECK:STDOUT:       {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'IdentifierNameExpr', text: 'B'},
 // CHECK:STDOUT:     {kind: 'BaseDecl', text: ';', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'BaseIntroducer', text: 'base'},
+// CHECK:STDOUT:         {kind: 'BaseColon', text: ':'},
+// CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'B'},
+// CHECK:STDOUT:       {kind: 'BaseDecl', text: ';', subtree_size: 4},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 10},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 52 - 0
toolchain/parse/testdata/class/local.carbon

@@ -0,0 +1,52 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/class/local.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/class/local.carbon
+
+fn F() {
+  class C {
+    fn G() {}
+    fn H();
+  }
+  fn C.H() {}
+}
+
+// CHECK:STDOUT: - filename: local.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'C'},
+// CHECK:STDOUT:         {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:             {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:             {kind: 'IdentifierName', text: 'G'},
+// CHECK:STDOUT:               {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'FunctionDefinition', text: '}', subtree_size: 6},
+// CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'H'},
+// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'FunctionDecl', text: ';', subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ClassDefinition', text: '}', subtree_size: 15},
+// CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:             {kind: 'IdentifierName', text: 'C'},
+// CHECK:STDOUT:           {kind: 'NameQualifier', text: '.', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'H'},
+// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},
+// CHECK:STDOUT:       {kind: 'FunctionDefinition', text: '}', subtree_size: 8},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 29},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 178 - 0
toolchain/parse/testdata/function/definition/decl_statement.carbon

@@ -0,0 +1,178 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/function/definition/decl_statement.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/function/definition/decl_statement.carbon
+
+// --- valid.carbon
+
+// These are expected to work.
+fn F() {
+  alias A = i32;
+  class A {}
+  base class B {}
+  abstract class Abstract {}
+  fn F() {}
+  constraint C {}
+  interface I {}
+  impl A as I {}
+  final impl A as I {}
+  choice C {}
+  let n: i32 = 0;
+  var v: i32;
+}
+
+// --- syntactically_valid.carbon
+
+// These parse but should not type-check.
+fn F() {
+  adapt A;
+  extend adapt A;
+  extend base: B;
+  extend impl as I;
+  namespace N;
+  default fn F();
+  impl fn G();
+  virtual fn H();
+  private var v: i32;
+  protected var v: i32;
+}
+
+// CHECK:STDOUT: - filename: valid.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'AliasIntroducer', text: 'alias'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'A'},
+// CHECK:STDOUT:         {kind: 'AliasInitializer', text: '='},
+// CHECK:STDOUT:         {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:       {kind: 'Alias', text: ';', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'A'},
+// CHECK:STDOUT:         {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ClassDefinition', text: '}', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:           {kind: 'BaseModifier', text: 'base'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'B'},
+// CHECK:STDOUT:         {kind: 'ClassDefinitionStart', text: '{', subtree_size: 4},
+// CHECK:STDOUT:       {kind: 'ClassDefinition', text: '}', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:           {kind: 'AbstractModifier', text: 'abstract'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'Abstract'},
+// CHECK:STDOUT:         {kind: 'ClassDefinitionStart', text: '{', subtree_size: 4},
+// CHECK:STDOUT:       {kind: 'ClassDefinition', text: '}', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'FunctionDefinition', text: '}', subtree_size: 6},
+// CHECK:STDOUT:           {kind: 'NamedConstraintIntroducer', text: 'constraint'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'C'},
+// CHECK:STDOUT:         {kind: 'NamedConstraintDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'NamedConstraintDefinition', text: '}', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'I'},
+// CHECK:STDOUT:         {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'InterfaceDefinition', text: '}', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'ImplIntroducer', text: 'impl'},
+// CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'A'},
+// CHECK:STDOUT:           {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'I'},
+// CHECK:STDOUT:         {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ImplDefinition', text: '}', subtree_size: 6},
+// CHECK:STDOUT:           {kind: 'ImplIntroducer', text: 'impl'},
+// CHECK:STDOUT:           {kind: 'FinalModifier', text: 'final'},
+// CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'A'},
+// CHECK:STDOUT:           {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'I'},
+// CHECK:STDOUT:         {kind: 'ImplDefinitionStart', text: '{', subtree_size: 6},
+// CHECK:STDOUT:       {kind: 'ImplDefinition', text: '}', subtree_size: 7},
+// CHECK:STDOUT:           {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'C'},
+// CHECK:STDOUT:         {kind: 'ChoiceDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinition', text: '}', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'LetIntroducer', text: 'let'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'n'},
+// CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:         {kind: 'BindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'LetInitializer', text: '='},
+// CHECK:STDOUT:         {kind: 'IntLiteral', text: '0'},
+// CHECK:STDOUT:       {kind: 'LetDecl', text: ';', subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'VariableIntroducer', text: 'var'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'v'},
+// CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:         {kind: 'BindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'VariableDecl', text: ';', subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 68},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]
+// CHECK:STDOUT: - filename: syntactically_valid.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'AdaptIntroducer', text: 'adapt'},
+// CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'A'},
+// CHECK:STDOUT:       {kind: 'AdaptDecl', text: ';', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'AdaptIntroducer', text: 'adapt'},
+// CHECK:STDOUT:         {kind: 'ExtendModifier', text: 'extend'},
+// CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'A'},
+// CHECK:STDOUT:       {kind: 'AdaptDecl', text: ';', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'BaseIntroducer', text: 'base'},
+// CHECK:STDOUT:         {kind: 'ExtendModifier', text: 'extend'},
+// CHECK:STDOUT:         {kind: 'BaseColon', text: ':'},
+// CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'B'},
+// CHECK:STDOUT:       {kind: 'BaseDecl', text: ';', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
+// CHECK:STDOUT:         {kind: 'ExtendModifier', text: 'extend'},
+// CHECK:STDOUT:         {kind: 'DefaultSelfImplAs', text: 'as'},
+// CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'I'},
+// CHECK:STDOUT:       {kind: 'ImplDecl', text: ';', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'NamespaceStart', text: 'namespace'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'N'},
+// CHECK:STDOUT:       {kind: 'Namespace', text: ';', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'DefaultModifier', text: 'default'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'ImplModifier', text: 'impl'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'G'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'VirtualModifier', text: 'virtual'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'H'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'VariableIntroducer', text: 'var'},
+// CHECK:STDOUT:         {kind: 'PrivateModifier', text: 'private'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'v'},
+// CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:         {kind: 'BindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'VariableDecl', text: ';', subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'VariableIntroducer', text: 'var'},
+// CHECK:STDOUT:         {kind: 'ProtectedModifier', text: 'protected'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'v'},
+// CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:         {kind: 'BindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'VariableDecl', text: ';', subtree_size: 6},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 56},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 39 - 0
toolchain/parse/testdata/function/definition/nested.carbon

@@ -0,0 +1,39 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/function/definition/nested.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/function/definition/nested.carbon
+
+fn F() {
+  fn NamedParams(a: i32) {}
+  fn PositionalParams {}
+}
+
+// CHECK:STDOUT: - filename: nested.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'NamedParams'},
+// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:               {kind: 'IdentifierName', text: 'a'},
+// CHECK:STDOUT:               {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:             {kind: 'BindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 8},
+// CHECK:STDOUT:       {kind: 'FunctionDefinition', text: '}', subtree_size: 9},
+// CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'PositionalParams'},
+// CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'FunctionDefinition', text: '}', subtree_size: 4},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 19},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 29 - 0
toolchain/parse/testdata/namespace/local.carbon

@@ -0,0 +1,29 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/namespace/local.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/namespace/local.carbon
+
+fn F() {
+  // This is rejected by check.
+  namespace N;
+}
+
+// CHECK:STDOUT: - filename: local.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'NamespaceStart', text: 'namespace'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'N'},
+// CHECK:STDOUT:       {kind: 'Namespace', text: ';', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 9},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 8 - 15
toolchain/parse/testdata/class/fail_base_misplaced.carbon → toolchain/parse/testdata/package_expr/fail_standalone.carbon

@@ -4,33 +4,26 @@
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
-// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/class/fail_base_misplaced.carbon
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/package_expr/fail_standalone.carbon
 // TIP: To dump output, run:
-// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/class/fail_base_misplaced.carbon
-
-base class B {}
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/package_expr/fail_standalone.carbon
 
 fn F() {
-  // CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+3]]:3: error: expected expression [ExpectedExpr]
-  // CHECK:STDERR:   base: B;
-  // CHECK:STDERR:   ^~~~
-  base: B;
+  // CHECK:STDERR: fail_standalone.carbon:[[@LINE+3]]:10: error: expected `.` after `package` expression [ExpectedPeriodAfterPackage]
+  // CHECK:STDERR:   package;
+  // CHECK:STDERR:          ^
+  package;
 }
 
-// CHECK:STDOUT: - filename: fail_base_misplaced.carbon
+// CHECK:STDOUT: - filename: fail_standalone.carbon
 // CHECK:STDOUT:   parse_tree: [
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
-// CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
-// CHECK:STDOUT:         {kind: 'BaseModifier', text: 'base'},
-// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'B'},
-// CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 4},
-// CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
 // CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:         {kind: 'InvalidParse', text: 'base', has_error: yes},
+// CHECK:STDOUT:         {kind: 'PackageExpr', text: 'package'},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 50 - 0
toolchain/parse/testdata/packages/import/fail_in_nested_scope.carbon

@@ -0,0 +1,50 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/packages/import/fail_in_nested_scope.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/packages/import/fail_in_nested_scope.carbon
+
+class A {
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE+7]]:3: error: `import` declarations must come after the `package` declaration (if present) and before any other entities in the file [ImportTooLate]
+  // CHECK:STDERR:   import B;
+  // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE-4]]:1: note: first declaration is here [FirstDecl]
+  // CHECK:STDERR: class A {
+  // CHECK:STDERR: ^~~~~
+  // CHECK:STDERR:
+  import B;
+}
+
+fn F() {
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE+6]]:3: error: `import` declarations must come after the `package` declaration (if present) and before any other entities in the file [ImportTooLate]
+  // CHECK:STDERR:   import C;
+  // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE-15]]:1: note: first declaration is here [FirstDecl]
+  // CHECK:STDERR: class A {
+  // CHECK:STDERR: ^~~~~
+  import C;
+}
+
+// CHECK:STDOUT: - filename: fail_in_nested_scope.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'A'},
+// CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'ImportIntroducer', text: 'import'},
+// CHECK:STDOUT:       {kind: 'ImportDecl', text: ';', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ImportIntroducer', text: 'import'},
+// CHECK:STDOUT:       {kind: 'ImportDecl', text: ';', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 8},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 92 - 0
toolchain/parse/testdata/packages/library/fail_in_nested_scope.carbon

@@ -0,0 +1,92 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/packages/library/fail_in_nested_scope.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/packages/library/fail_in_nested_scope.carbon
+
+// api.carbon
+
+class C {
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE+7]]:3: error: the `library` declaration must be the first non-comment line [PackageTooLate]
+  // CHECK:STDERR:   library "foo";
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE-4]]:1: note: first non-comment line is here [FirstNonCommentLine]
+  // CHECK:STDERR: class C {
+  // CHECK:STDERR: ^~~~~
+  // CHECK:STDERR:
+  library "foo";
+}
+
+fn F() {
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE+7]]:3: error: the `library` declaration must be the first non-comment line [PackageTooLate]
+  // CHECK:STDERR:   library "foo";
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE-15]]:1: note: first non-comment line is here [FirstNonCommentLine]
+  // CHECK:STDERR: class C {
+  // CHECK:STDERR: ^~~~~
+  // CHECK:STDERR:
+  library "foo";
+}
+
+// impl.carbon
+
+class C {
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE+7]]:3: error: the `library` declaration must be the first non-comment line [PackageTooLate]
+  // CHECK:STDERR:   impl library "foo";
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE-28]]:1: note: first non-comment line is here [FirstNonCommentLine]
+  // CHECK:STDERR: class C {
+  // CHECK:STDERR: ^~~~~
+  // CHECK:STDERR:
+  impl library "foo";
+}
+
+fn F() {
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE+6]]:3: error: the `library` declaration must be the first non-comment line [PackageTooLate]
+  // CHECK:STDERR:   impl library "foo";
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE-39]]:1: note: first non-comment line is here [FirstNonCommentLine]
+  // CHECK:STDERR: class C {
+  // CHECK:STDERR: ^~~~~
+  impl library "foo";
+}
+
+// CHECK:STDOUT: - filename: fail_in_nested_scope.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'C'},
+// CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'LibraryIntroducer', text: 'library'},
+// CHECK:STDOUT:       {kind: 'LibraryDecl', text: ';', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'LibraryIntroducer', text: 'library'},
+// CHECK:STDOUT:       {kind: 'LibraryDecl', text: ';', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 8},
+// CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'C'},
+// CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'LibraryIntroducer', text: 'library'},
+// CHECK:STDOUT:         {kind: 'ImplModifier', text: 'impl'},
+// CHECK:STDOUT:       {kind: 'LibraryDecl', text: ';', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'LibraryIntroducer', text: 'library'},
+// CHECK:STDOUT:         {kind: 'ImplModifier', text: 'impl'},
+// CHECK:STDOUT:       {kind: 'LibraryDecl', text: ';', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 9},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 98 - 0
toolchain/parse/testdata/packages/package/fail_in_nested_scope.carbon

@@ -0,0 +1,98 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/packages/package/fail_in_nested_scope.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/packages/package/fail_in_nested_scope.carbon
+
+// api.carbon
+
+class C {
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE+7]]:3: error: the `package` declaration must be the first non-comment line [PackageTooLate]
+  // CHECK:STDERR:   package P;
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE-4]]:1: note: first non-comment line is here [FirstNonCommentLine]
+  // CHECK:STDERR: class C {
+  // CHECK:STDERR: ^~~~~
+  // CHECK:STDERR:
+  package P;
+}
+
+fn F() {
+  // `package` here is an expression naming the package scope.
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE+4]]:11: error: expected `.` after `package` expression [ExpectedPeriodAfterPackage]
+  // CHECK:STDERR:   package P;
+  // CHECK:STDERR:           ^
+  // CHECK:STDERR:
+  package P;
+
+  // OK
+  package.P;
+}
+
+// impl.carbon
+
+class C {
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE+7]]:3: error: the `package` declaration must be the first non-comment line [PackageTooLate]
+  // CHECK:STDERR:   impl package P;
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE-29]]:1: note: first non-comment line is here [FirstNonCommentLine]
+  // CHECK:STDERR: class C {
+  // CHECK:STDERR: ^~~~~
+  // CHECK:STDERR:
+  impl package P;
+}
+
+fn F() {
+  // This reaches decl parsing because `impl` is a valid modifier or introducer.
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE+6]]:3: error: the `package` declaration must be the first non-comment line [PackageTooLate]
+  // CHECK:STDERR:   impl package P;
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR: fail_in_nested_scope.carbon:[[@LINE-41]]:1: note: first non-comment line is here [FirstNonCommentLine]
+  // CHECK:STDERR: class C {
+  // CHECK:STDERR: ^~~~~
+  impl package P;
+}
+
+// CHECK:STDOUT: - filename: fail_in_nested_scope.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'C'},
+// CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'PackageIntroducer', text: 'package'},
+// CHECK:STDOUT:       {kind: 'PackageDecl', text: ';', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'PackageExpr', text: 'package'},
+// CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'PackageExpr', text: 'package'},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'P'},
+// CHECK:STDOUT:         {kind: 'MemberAccessExpr', text: '.', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 4},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 12},
+// CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'C'},
+// CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'PackageIntroducer', text: 'package'},
+// CHECK:STDOUT:         {kind: 'ImplModifier', text: 'impl'},
+// CHECK:STDOUT:       {kind: 'PackageDecl', text: ';', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'F'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'PackageIntroducer', text: 'package'},
+// CHECK:STDOUT:         {kind: 'ImplModifier', text: 'impl'},
+// CHECK:STDOUT:       {kind: 'PackageDecl', text: ';', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 9},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 7 - 9
toolchain/parse/typed_nodes.h

@@ -124,8 +124,8 @@ using FileEnd = LeafNode<NodeKind::FileEnd, Lex::FileEndTokenIndex>;
 // ---------------------
 
 // An empty declaration, such as `;`.
-using EmptyDecl = LeafNode<NodeKind::EmptyDecl, Lex::SemiTokenIndex,
-                           NodeCategory::Decl | NodeCategory::Statement>;
+using EmptyDecl =
+    LeafNode<NodeKind::EmptyDecl, Lex::SemiTokenIndex, NodeCategory::Decl>;
 
 // A name in a non-expression context, such as a declaration.
 using IdentifierName =
@@ -421,8 +421,7 @@ using AliasInitializer =
 // An `alias` declaration: `alias a = b;`.
 struct Alias {
   static constexpr auto Kind = NodeKind::Alias.Define(
-      {.category = NodeCategory::Decl | NodeCategory::Statement,
-       .bracketed_by = AliasIntroducer::Kind});
+      {.category = NodeCategory::Decl, .bracketed_by = AliasIntroducer::Kind});
 
   AliasIntroducerId introducer;
   llvm::SmallVector<AnyModifierId> modifiers;
@@ -441,8 +440,7 @@ using LetInitializer = LeafNode<NodeKind::LetInitializer, Lex::EqualTokenIndex>;
 // A `let` declaration: `let a: i32 = 5;`.
 struct LetDecl {
   static constexpr auto Kind = NodeKind::LetDecl.Define(
-      {.category = NodeCategory::Decl | NodeCategory::Statement,
-       .bracketed_by = LetIntroducer::Kind});
+      {.category = NodeCategory::Decl, .bracketed_by = LetIntroducer::Kind});
 
   LetIntroducerId introducer;
   llvm::SmallVector<AnyModifierId> modifiers;
@@ -468,9 +466,9 @@ using VariableInitializer =
 
 // A `var` declaration: `var a: i32;` or `var a: i32 = 5;`.
 struct VariableDecl {
-  static constexpr auto Kind = NodeKind::VariableDecl.Define(
-      {.category = NodeCategory::Decl | NodeCategory::Statement,
-       .bracketed_by = VariableIntroducer::Kind});
+  static constexpr auto Kind =
+      NodeKind::VariableDecl.Define({.category = NodeCategory::Decl,
+                                     .bracketed_by = VariableIntroducer::Kind});
 
   VariableIntroducerId introducer;
   llvm::SmallVector<AnyModifierId> modifiers;