Repository: carbon-language/carbon-lang
File: toolchain/check/handle_class.cpp (C++)

Code before:
  // Process modifiers. `extend` is required, no others are allowed.
  auto introducer =
      context.decl_introducer_state_stack().Pop<Lex::TokenKind::Base>();
  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Extend);
  if (!introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
    CARBON_DIAGNOSTIC(BaseMissingExtend, Error,
                      "missing `extend` before `base` declaration");
    context.emitter().Emit(node_id, BaseMissingExtend);
  }

  auto parent_class_decl =
      GetCurrentScopeAsClassOrDiagnose(context, node_id, Lex::TokenKind::Base);
  if (!parent_class_decl) {
    return true;
  }

  auto& class_info = context.classes().Get(parent_class_decl->class_id);
  if (class_info.base_id.is_valid()) {
    DiagnoseClassSpecificDeclRepeated(context, node_id, class_info.base_id,
                                      Lex::TokenKind::Base);
    return true;
  }

  if (!context.struct_type_fields_stack().PeekArray().empty()) {
    CARBON_DIAGNOSTIC(BaseDeclAfterFieldDecl, Error,
                      "`{0}` declaration appears after field declaration(s)",
                      Lex::TokenKind);
    context.emitter().Emit(node_id, BaseDeclAfterFieldDecl,
                           Lex::TokenKind::Base);
    return true;
  }

  auto base_info = CheckBaseType(context, base_type_node_id, base_type_expr_id);

  // The `base` value in the class scope has an unbound element type. Instance
  // binding will be performed when it's found by name lookup into an instance.
  auto field_type_id =
      context.GetUnboundElementType(class_info.self_type_id, base_info.type_id);
  class_info.base_id = context.AddInst<SemIR::BaseDecl>(
      node_id, {.type_id = field_type_id,
                .base_type_id = base_info.type_id,
                .index = SemIR::ElementIndex(
                    context.struct_type_fields_stack().PeekArray().size())});

  if (base_info.type_id != SemIR::TypeId::Error) {
    auto base_class_info = context.classes().Get(
        context.types().GetAs<SemIR::ClassType>(base_info.type_id).class_id);
    class_info.is_dynamic |= base_class_info.is_dynamic;
  }

  // Add a corresponding field to the object representation of the class.

Review comment (suggestion):
```suggestion
                      "`{0}` declaration must come before field declarations",
```

Suggesting different phrasing to avoid the `(s)`

Code after:
  // Process modifiers. `extend` is required, no others are allowed.
  auto introducer =
      context.decl_introducer_state_stack().Pop<Lex::TokenKind::Base>();
  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Extend);
  if (!introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
    CARBON_DIAGNOSTIC(BaseMissingExtend, Error,
                      "missing `extend` before `base` declaration");
    context.emitter().Emit(node_id, BaseMissingExtend);
  }

  auto parent_class_decl =
      GetCurrentScopeAsClassOrDiagnose(context, node_id, Lex::TokenKind::Base);
  if (!parent_class_decl) {
    return true;
  }

  auto& class_info = context.classes().Get(parent_class_decl->class_id);
  if (class_info.base_id.is_valid()) {
    DiagnoseClassSpecificDeclRepeated(context, node_id, class_info.base_id,
                                      Lex::TokenKind::Base);
    return true;
  }

  if (!context.struct_type_fields_stack().PeekArray().empty()) {
    // TODO: Add note that includes the first field location as an example.
    CARBON_DIAGNOSTIC(
        BaseDeclAfterFieldDecl, Error,
        "`base` declaration must appear before field declarations");
    context.emitter().Emit(node_id, BaseDeclAfterFieldDecl);
    return true;
  }

  auto base_info = CheckBaseType(context, base_type_node_id, base_type_expr_id);

  // The `base` value in the class scope has an unbound element type. Instance
  // binding will be performed when it's found by name lookup into an instance.
  auto field_type_id =
      context.GetUnboundElementType(class_info.self_type_id, base_info.type_id);
  class_info.base_id = context.AddInst<SemIR::BaseDecl>(
      node_id, {.type_id = field_type_id,
                .base_type_id = base_info.type_id,
                .index = SemIR::ElementIndex(
                    context.struct_type_fields_stack().PeekArray().size())});

  if (base_info.type_id != SemIR::TypeId::Error) {
    auto base_class_info = context.classes().Get(
        context.types().GetAs<SemIR::ClassType>(base_info.type_id).class_id);
    class_info.is_dynamic |= base_class_info.is_dynamic;
  }

  // Add a corresponding field to the object representation of the class.