Repository: carbon-language/carbon-lang
File: toolchain/check/decl_name_stack.h (C/C++)

Code before:

    State state = State::Empty;

    // Whether there have been qualifiers in the name.
    bool has_qualifiers = false;

    // The scope which qualified names are added to. For unqualified names in
    // an unnamed scope, this will be `None` to indicate the current scope
    // should be used.
    SemIR::NameScopeId parent_scope_id;

    // The last location ID used.
    SemIR::LocId loc_id = SemIR::LocId::None;

    union {
      // The ID of a resolved qualifier, including both identifiers and
      // expressions. `None` indicates resolution failed.
      SemIR::InstId resolved_inst_id;

      // The ID of an unresolved identifier.
      SemIR::NameId unresolved_name_id = SemIR::NameId::None;
    };

    // When `state` is `Poisoned` (name is unresolved due to name poisoning),
    // the poisoning location.
    SemIR::LocId poisoning_loc_id = SemIR::LocId::None;
  };

  // Information about a declaration name that has been temporarily removed from
  // the stack and will later be restored. Names can only be suspended once they
  // are finished.
  struct SuspendedName {
    // The declaration name information.
    NameContext name_context;

    // Suspended scopes. We only preallocate space for two of these, because
    // suspended names are usually used for classes and functions with
    // unqualified names, which only need at most two scopes -- one scope for
    // the parameter and one scope for the entity itself, and we can store quite
    // a few of these when processing a large class definition.
    llvm::SmallVector<ScopeStack::SuspendedScope, 2> scopes;
  };

  explicit DeclNameStack(Context* context) : context_(context) {}

  // Pushes processing of a new declaration name, which will be used
  // contextually, and prepares to enter scopes for that name. To pop this
  // state, `FinishName` and `PopScope` must be called, in that order.
  auto PushScopeAndStartName() -> void;

  // Creates and returns a name context corresponding to declaring an

Review comment (suggestion):
I'm realizing you're changing this more than just the diagnostic. Can you please update the PR and explain why you're changing semantics here? Looking at the PR, it's not clear to me why this should be changing.

Code after:

    State state = State::Empty;

    // Whether there have been qualifiers in the name.
    bool has_qualifiers = false;

    // The scope which qualified names are added to. For unqualified names in
    // an unnamed scope, this will be `None` to indicate the current scope
    // should be used.
    SemIR::NameScopeId parent_scope_id;

    // The last location ID used.
    SemIR::LocId loc_id = SemIR::LocId::None;

    union {
      // The ID of a resolved qualifier, including both identifiers and
      // expressions. `None` indicates resolution failed.
      SemIR::InstId resolved_inst_id;

      // When `state` is `Poisoned` (name is unresolved due to name poisoning),
      // the poisoning location.
      SemIR::LocId poisoning_loc_id = SemIR::LocId::None;
    };

    // The ID of an identifier.
    SemIR::NameId name_id = SemIR::NameId::None;
  };

  // Information about a declaration name that has been temporarily removed from
  // the stack and will later be restored. Names can only be suspended once they
  // are finished.
  struct SuspendedName {
    // The declaration name information.
    NameContext name_context;

    // Suspended scopes. We only preallocate space for two of these, because
    // suspended names are usually used for classes and functions with
    // unqualified names, which only need at most two scopes -- one scope for
    // the parameter and one scope for the entity itself, and we can store quite
    // a few of these when processing a large class definition.
    llvm::SmallVector<ScopeStack::SuspendedScope, 2> scopes;
  };

  explicit DeclNameStack(Context* context) : context_(context) {}

  // Pushes processing of a new declaration name, which will be used
  // contextually, and prepares to enter scopes for that name. To pop this
  // state, `FinishName` and `PopScope` must be called, in that order.
  auto PushScopeAndStartName() -> void;

  // Creates and returns a name context corresponding to declaring an