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

Code before:
    case FullPatternStack::Kind::ExplicitParamList: {
      if (!IsValidParamForIntroducer(context, node_id, name_id, introducer.kind,
                                     is_generic)) {
        if (name_id != SemIR::NameId::Underscore) {
          AddNameToLookup(context, name_id, SemIR::ErrorInst::InstId);
        }
        // Replace the parameter with `ErrorInst` so that we don't try
        // constructing a generic based on it.
        context.node_stack().Push(node_id, SemIR::ErrorInst::InstId);
        break;
      }

      // Using `AsConcreteType` here causes `fn F[var self: Self]();`
      // to fail since `Self` is an incomplete type.
      if (node_kind == Parse::NodeKind::VarBindingPattern) {
        auto unqualified_type_id =
            context.types().GetUnqualifiedType(cast_type_id);
        if (context.types().Is<SemIR::ClassType>(unqualified_type_id)) {
          auto class_type =
              context.types().GetAs<SemIR::ClassType>(unqualified_type_id);
          auto& class_info = context.classes().Get(class_type.class_id);
          if (class_info.inheritance_kind ==
              SemIR::Class::InheritanceKind::Abstract) {
            auto builder = abstract_diagnoser();
            CARBON_DIAGNOSTIC(
                ClassAbstractHere, Note,
                "{0:=0:uses class that|=1:class} was declared abstract here",
                Diagnostics::IntAsSelect);
            builder.Note(class_info.definition_id, ClassAbstractHere, 1);
            builder.Emit();
            cast_type_id = SemIR::ErrorInst::TypeId;
          }
        }
      }

      auto result_inst_id = make_binding_pattern();

      // A binding pattern in a function signature is a `Call` parameter
      // unless it's nested inside a `var` pattern (because then the
      // enclosing `var` pattern is), or it's a compile-time binding pattern
      // (because then it's not passed to the `Call` inst).
      if (node_kind == Parse::NodeKind::LetBindingPattern) {
        auto type_id = context.insts().GetAttachedType(result_inst_id);
        if (is_ref) {
          result_inst_id = AddPatternInst<SemIR::RefParamPattern>(
              context, node_id,
              {.type_id = type_id,
               .subpattern_id = result_inst_id,
               .index = SemIR::CallParamIndex::None});
        } else {
          result_inst_id = AddPatternInst<SemIR::ValueParamPattern>(

Review comment (bug):
We don't copy-paste diagnostics, and you'll have a unit test failure because of the `ClassAbstractHere` duplication. Had you considered instead exposing `NoteAbstractClass` and calling it?

Code after:
    case FullPatternStack::Kind::ExplicitParamList: {
      if (!IsValidParamForIntroducer(context, node_id, name_id, introducer.kind,
                                     is_generic)) {
        if (name_id != SemIR::NameId::Underscore) {
          AddNameToLookup(context, name_id, SemIR::ErrorInst::InstId);
        }
        // Replace the parameter with `ErrorInst` so that we don't try
        // constructing a generic based on it.
        context.node_stack().Push(node_id, SemIR::ErrorInst::InstId);
        break;
      }

      // Using `AsConcreteType` here causes `fn F[var self: Self]();`
      // to fail since `Self` is an incomplete type.
      if (node_kind == Parse::NodeKind::VarBindingPattern) {
        auto [unqualified_type_id, qualifiers] =
            context.types().GetUnqualifiedTypeAndQualifiers(cast_type_id);
        if ((qualifiers & SemIR::TypeQualifiers::Partial) !=
                SemIR::TypeQualifiers::Partial &&
            context.types().Is<SemIR::ClassType>(unqualified_type_id)) {
          auto class_type =
              context.types().GetAs<SemIR::ClassType>(unqualified_type_id);
          auto& class_info = context.classes().Get(class_type.class_id);
          if (class_info.inheritance_kind ==
              SemIR::Class::InheritanceKind::Abstract) {
            auto builder = abstract_diagnoser();
            auto direct_use = true;
            NoteAbstractClass(context, class_type.class_id, direct_use,
                              builder);
            builder.Emit();
            cast_type_id = SemIR::ErrorInst::TypeId;
          }
        }
      }

      auto result_inst_id = make_binding_pattern();

      // A binding pattern in a function signature is a `Call` parameter
      // unless it's nested inside a `var` pattern (because then the
      // enclosing `var` pattern is), or it's a compile-time binding pattern
      // (because then it's not passed to the `Call` inst).
      if (node_kind == Parse::NodeKind::LetBindingPattern) {
        auto type_id = context.insts().GetAttachedType(result_inst_id);
        if (is_ref) {
          result_inst_id = AddPatternInst<SemIR::RefParamPattern>(
              context, node_id,
              {.type_id = type_id,
               .subpattern_id = result_inst_id,
               .index = SemIR::CallParamIndex::None});
        } else {
          result_inst_id = AddPatternInst<SemIR::ValueParamPattern>(