Преглед изворни кода

Move diagnostic helpers from Context to other files (#4949)

Trying to find more specific homes for shared diagnostic function calls.
Jon Ross-Perkins пре 1 година
родитељ
комит
e70f9cd71d

+ 0 - 70
toolchain/check/context.cpp

@@ -214,76 +214,6 @@ auto Context::ReplaceInstPreservingConstantValue(SemIR::InstId inst_id,
   CARBON_CHECK(old_const_id == new_const_id);
 }
 
-auto Context::DiagnoseDuplicateName(SemIRLoc dup_def, SemIRLoc prev_def)
-    -> void {
-  CARBON_DIAGNOSTIC(NameDeclDuplicate, Error,
-                    "duplicate name being declared in the same scope");
-  CARBON_DIAGNOSTIC(NameDeclPrevious, Note, "name is previously declared here");
-  emitter_->Build(dup_def, NameDeclDuplicate)
-      .Note(prev_def, NameDeclPrevious)
-      .Emit();
-}
-
-auto Context::DiagnosePoisonedName(SemIR::LocId poisoning_loc_id,
-                                   SemIR::LocId decl_name_loc_id) -> void {
-  CARBON_CHECK(poisoning_loc_id.has_value(),
-               "Trying to diagnose poisoned name with no poisoning location");
-  CARBON_DIAGNOSTIC(NameUseBeforeDecl, Error,
-                    "name used before it was declared");
-  CARBON_DIAGNOSTIC(NameUseBeforeDeclNote, Note, "declared here");
-  emitter_->Build(poisoning_loc_id, NameUseBeforeDecl)
-      .Note(decl_name_loc_id, NameUseBeforeDeclNote)
-      .Emit();
-}
-
-auto Context::DiagnoseNameNotFound(SemIRLoc loc, SemIR::NameId name_id)
-    -> void {
-  CARBON_DIAGNOSTIC(NameNotFound, Error, "name `{0}` not found", SemIR::NameId);
-  emitter_->Emit(loc, NameNotFound, name_id);
-}
-
-auto Context::NoteAbstractClass(SemIR::ClassId class_id,
-                                DiagnosticBuilder& builder) -> void {
-  const auto& class_info = classes().Get(class_id);
-  CARBON_CHECK(
-      class_info.inheritance_kind == SemIR::Class::InheritanceKind::Abstract,
-      "Class is not abstract");
-  CARBON_DIAGNOSTIC(ClassAbstractHere, Note,
-                    "class was declared abstract here");
-  builder.Note(class_info.definition_id, ClassAbstractHere);
-}
-
-auto Context::NoteIncompleteClass(SemIR::ClassId class_id,
-                                  DiagnosticBuilder& builder) -> void {
-  const auto& class_info = classes().Get(class_id);
-  CARBON_CHECK(!class_info.is_defined(), "Class is not incomplete");
-  if (class_info.has_definition_started()) {
-    CARBON_DIAGNOSTIC(ClassIncompleteWithinDefinition, Note,
-                      "class is incomplete within its definition");
-    builder.Note(class_info.definition_id, ClassIncompleteWithinDefinition);
-  } else {
-    CARBON_DIAGNOSTIC(ClassForwardDeclaredHere, Note,
-                      "class was forward declared here");
-    builder.Note(class_info.latest_decl_id(), ClassForwardDeclaredHere);
-  }
-}
-
-auto Context::NoteUndefinedInterface(SemIR::InterfaceId interface_id,
-                                     DiagnosticBuilder& builder) -> void {
-  const auto& interface_info = interfaces().Get(interface_id);
-  CARBON_CHECK(!interface_info.is_defined(), "Interface is not incomplete");
-  if (interface_info.is_being_defined()) {
-    CARBON_DIAGNOSTIC(InterfaceUndefinedWithinDefinition, Note,
-                      "interface is currently being defined");
-    builder.Note(interface_info.definition_id,
-                 InterfaceUndefinedWithinDefinition);
-  } else {
-    CARBON_DIAGNOSTIC(InterfaceForwardDeclaredHere, Note,
-                      "interface was forward declared here");
-    builder.Note(interface_info.latest_decl_id(), InterfaceForwardDeclaredHere);
-  }
-}
-
 auto Context::Finalize() -> void {
   // Pop information for the file-level scope.
   sem_ir().set_top_inst_block_id(inst_block_stack().Pop());

+ 0 - 22
toolchain/check/context.h

@@ -178,28 +178,6 @@ class Context {
     sem_ir().insts().SetLocId(inst_id, SemIR::LocId(node_id));
   }
 
-  // Prints a diagnostic for a duplicate name.
-  auto DiagnoseDuplicateName(SemIRLoc dup_def, SemIRLoc prev_def) -> void;
-
-  // Prints a diagnostic for a poisoned name when it's later declared.
-  auto DiagnosePoisonedName(SemIR::LocId poisoning_loc_id,
-                            SemIR::LocId decl_name_loc_id) -> void;
-
-  // Prints a diagnostic for a missing name.
-  auto DiagnoseNameNotFound(SemIRLoc loc, SemIR::NameId name_id) -> void;
-
-  // Adds a note to a diagnostic explaining that a class is incomplete.
-  auto NoteIncompleteClass(SemIR::ClassId class_id, DiagnosticBuilder& builder)
-      -> void;
-
-  // Adds a note to a diagnostic explaining that a class is abstract.
-  auto NoteAbstractClass(SemIR::ClassId class_id, DiagnosticBuilder& builder)
-      -> void;
-
-  // Adds a note to a diagnostic explaining that an interface is not defined.
-  auto NoteUndefinedInterface(SemIR::InterfaceId interface_id,
-                              DiagnosticBuilder& builder) -> void;
-
   // Returns the type ID for a constant that is a type value, i.e. it is a value
   // of type `TypeType`.
   //

+ 8 - 7
toolchain/check/decl_name_stack.cpp

@@ -11,6 +11,7 @@
 #include "toolchain/check/merge.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type_completion.h"
 #include "toolchain/diagnostics/diagnostic.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/name_scope.h"
@@ -176,10 +177,10 @@ auto DeclNameStack::AddNameOrDiagnose(NameContext name_context,
                                       SemIR::InstId target_id,
                                       SemIR::AccessKind access_kind) -> void {
   if (name_context.state == DeclNameStack::NameContext::State::Poisoned) {
-    context_->DiagnosePoisonedName(name_context.poisoning_loc_id,
-                                   name_context.loc_id);
+    DiagnosePoisonedName(*context_, name_context.poisoning_loc_id,
+                         name_context.loc_id);
   } else if (auto id = name_context.prev_inst_id(); id.has_value()) {
-    context_->DiagnoseDuplicateName(target_id, id);
+    DiagnoseDuplicateName(*context_, target_id, id);
   } else {
     AddName(name_context, target_id, access_kind);
   }
@@ -299,8 +300,8 @@ static auto CheckQualifierIsResolved(
     case DeclNameStack::NameContext::State::Unresolved:
       // Because more qualifiers were found, we diagnose that the earlier
       // qualifier failed to resolve.
-      context.DiagnoseNameNotFound(name_context.loc_id,
-                                   name_context.unresolved_name_id);
+      DiagnoseNameNotFound(context, name_context.loc_id,
+                           name_context.unresolved_name_id);
       return false;
 
     case DeclNameStack::NameContext::State::Finished:
@@ -324,7 +325,7 @@ static auto DiagnoseQualifiedDeclInIncompleteClassScope(Context& context,
   auto builder =
       context.emitter().Build(loc, QualifiedDeclInIncompleteClassScope,
                               context.classes().Get(class_id).self_type_id);
-  context.NoteIncompleteClass(class_id, builder);
+  NoteIncompleteClass(context, class_id, builder);
   builder.Emit();
 }
 
@@ -338,7 +339,7 @@ static auto DiagnoseQualifiedDeclInUndefinedInterfaceScope(
                     InstIdAsType);
   auto builder = context.emitter().Build(
       loc, QualifiedDeclInUndefinedInterfaceScope, interface_inst_id);
-  context.NoteUndefinedInterface(interface_id, builder);
+  NoteUndefinedInterface(context, interface_id, builder);
   builder.Emit();
 }
 

+ 4 - 3
toolchain/check/handle_class.cpp

@@ -15,6 +15,7 @@
 #include "toolchain/check/merge.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
+#include "toolchain/check/name_lookup.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/parse/node_ids.h"
 #include "toolchain/sem_ir/function.h"
@@ -111,8 +112,8 @@ static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id,
                                                 access_kind);
   if (lookup_result.is_poisoned()) {
     // This is a declaration of a poisoned name.
-    context.DiagnosePoisonedName(lookup_result.poisoning_loc_id(),
-                                 name_context.loc_id);
+    DiagnosePoisonedName(context, lookup_result.poisoning_loc_id(),
+                         name_context.loc_id);
     return;
   }
 
@@ -161,7 +162,7 @@ static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id,
 
   if (!prev_class_id.has_value()) {
     // This is a redeclaration of something other than a class.
-    context.DiagnoseDuplicateName(class_decl_id, prev_id);
+    DiagnoseDuplicateName(context, class_decl_id, prev_id);
     return;
   }
 

+ 2 - 1
toolchain/check/handle_export.cpp

@@ -7,6 +7,7 @@
 #include "toolchain/check/handle.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
+#include "toolchain/check/name_lookup.h"
 #include "toolchain/parse/typed_nodes.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/typed_insts.h"
@@ -37,7 +38,7 @@ auto HandleParseNode(Context& context, Parse::ExportDeclId node_id) -> bool {
 
   auto inst_id = name_context.prev_inst_id();
   if (!inst_id.has_value()) {
-    context.DiagnoseNameNotFound(node_id, name_context.name_id_for_new_inst());
+    DiagnoseNameNotFound(context, node_id, name_context.name_id_for_new_inst());
     return true;
   }
 

+ 4 - 3
toolchain/check/handle_function.cpp

@@ -18,6 +18,7 @@
 #include "toolchain/check/merge.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
+#include "toolchain/check/name_lookup.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/sem_ir/builtin_function_kind.h"
 #include "toolchain/sem_ir/entry_point.h"
@@ -176,7 +177,7 @@ static auto TryMergeRedecl(Context& context, Parse::AnyFunctionDeclId node_id,
   }
 
   if (!prev_function_id.has_value()) {
-    context.DiagnoseDuplicateName(function_info.latest_decl_id(), prev_id);
+    DiagnoseDuplicateName(context, function_info.latest_decl_id(), prev_id);
     return;
   }
 
@@ -272,8 +273,8 @@ static auto BuildFunctionDecl(Context& context,
   }
 
   if (name_context.state == DeclNameStack::NameContext::State::Poisoned) {
-    context.DiagnosePoisonedName(name_context.poisoning_loc_id,
-                                 name_context.loc_id);
+    DiagnosePoisonedName(context, name_context.poisoning_loc_id,
+                         name_context.loc_id);
   } else {
     TryMergeRedecl(context, node_id, name_context.prev_inst_id(), function_decl,
                    function_info, is_definition);

+ 4 - 3
toolchain/check/handle_interface.cpp

@@ -9,6 +9,7 @@
 #include "toolchain/check/merge.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
+#include "toolchain/check/name_lookup.h"
 #include "toolchain/sem_ir/typed_insts.h"
 
 namespace Carbon::Check {
@@ -65,8 +66,8 @@ static auto BuildInterfaceDecl(Context& context,
           introducer.modifier_set.GetAccessKind());
   if (lookup_result.is_poisoned()) {
     // This is a declaration of a poisoned name.
-    context.DiagnosePoisonedName(lookup_result.poisoning_loc_id(),
-                                 name_context.loc_id);
+    DiagnosePoisonedName(context, lookup_result.poisoning_loc_id(),
+                         name_context.loc_id);
   } else if (lookup_result.is_found()) {
     SemIR::InstId existing_id = lookup_result.target_inst_id();
     if (auto existing_interface_decl =
@@ -102,7 +103,7 @@ static auto BuildInterfaceDecl(Context& context,
       }
     } else {
       // This is a redeclaration of something other than a interface.
-      context.DiagnoseDuplicateName(interface_decl_id, existing_id);
+      DiagnoseDuplicateName(context, interface_decl_id, existing_id);
     }
   }
 

+ 5 - 4
toolchain/check/handle_namespace.cpp

@@ -7,6 +7,7 @@
 #include "toolchain/check/handle.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
+#include "toolchain/check/name_lookup.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/name_scope.h"
@@ -46,8 +47,8 @@ auto HandleParseNode(Context& context, Parse::NamespaceId node_id) -> bool {
       context.decl_name_stack().LookupOrAddName(name_context, namespace_id,
                                                 SemIR::AccessKind::Public);
   if (lookup_result.is_poisoned()) {
-    context.DiagnosePoisonedName(lookup_result.poisoning_loc_id(),
-                                 name_context.loc_id);
+    DiagnosePoisonedName(context, lookup_result.poisoning_loc_id(),
+                         name_context.loc_id);
   } else if (lookup_result.is_found()) {
     SemIR::InstId existing_inst_id = lookup_result.target_inst_id();
     if (auto existing =
@@ -62,7 +63,7 @@ auto HandleParseNode(Context& context, Parse::NamespaceId node_id) -> bool {
               .Get(existing->name_scope_id)
               .is_closed_import()) {
         // The existing name is a package name, so this is a name conflict.
-        context.DiagnoseDuplicateName(namespace_id, existing_inst_id);
+        DiagnoseDuplicateName(context, namespace_id, existing_inst_id);
 
         // Treat this as a local namespace name from now on to avoid further
         // diagnostics.
@@ -76,7 +77,7 @@ auto HandleParseNode(Context& context, Parse::NamespaceId node_id) -> bool {
         context.SetNamespaceNodeId(existing_inst_id, node_id);
       }
     } else {
-      context.DiagnoseDuplicateName(namespace_id, existing_inst_id);
+      DiagnoseDuplicateName(context, namespace_id, existing_inst_id);
     }
   }
 

+ 3 - 2
toolchain/check/import.cpp

@@ -10,6 +10,7 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/import_ref.h"
 #include "toolchain/check/merge.h"
+#include "toolchain/check/name_lookup.h"
 #include "toolchain/parse/node_ids.h"
 #include "toolchain/sem_ir/file.h"
 #include "toolchain/sem_ir/ids.h"
@@ -105,7 +106,7 @@ auto AddImportNamespace(Context& context, SemIR::TypeId namespace_type_id,
         if (diagnose_duplicate_namespace) {
           auto import_id = make_import_id();
           CARBON_CHECK(import_id.has_value());
-          context.DiagnoseDuplicateName(import_id, prev_inst_id);
+          DiagnoseDuplicateName(context, import_id, prev_inst_id);
         }
         return {.name_scope_id = namespace_inst->name_scope_id,
                 .inst_id = prev_inst_id,
@@ -146,7 +147,7 @@ auto AddImportNamespace(Context& context, SemIR::TypeId namespace_type_id,
   // may be overwriting a poisoned entry here.
   auto& result = parent_scope->GetEntry(entry_id).result;
   if (!result.is_poisoned() && !inserted) {
-    context.DiagnoseDuplicateName(namespace_id, result.target_inst_id());
+    DiagnoseDuplicateName(context, namespace_id, result.target_inst_id());
   }
   result = SemIR::ScopeLookupResult::MakeFound(namespace_id,
                                                SemIR::AccessKind::Public);

+ 2 - 1
toolchain/check/import_ref.cpp

@@ -9,6 +9,7 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/eval.h"
 #include "toolchain/check/generic.h"
+#include "toolchain/check/name_lookup.h"
 #include "toolchain/parse/node_ids.h"
 #include "toolchain/sem_ir/constant.h"
 #include "toolchain/sem_ir/file.h"
@@ -147,7 +148,7 @@ auto VerifySameCanonicalImportIRInst(Context& context, SemIR::InstId prev_id,
   }
   auto conflict_id =
       AddImportRef(context, {.ir_id = new_ir_id, .inst_id = new_inst_id});
-  context.DiagnoseDuplicateName(conflict_id, prev_id);
+  DiagnoseDuplicateName(context, conflict_id, prev_id);
 }
 
 // Returns an instruction that has the specified constant value.

+ 32 - 2
toolchain/check/name_lookup.cpp

@@ -17,7 +17,7 @@ auto AddNameToLookup(Context& context, SemIR::NameId name_id,
   if (auto existing = context.scope_stack().LookupOrAddName(name_id, target_id,
                                                             scope_index);
       existing.has_value()) {
-    context.DiagnoseDuplicateName(target_id, existing);
+    DiagnoseDuplicateName(context, target_id, existing);
   }
 }
 
@@ -120,7 +120,7 @@ auto LookupUnqualifiedName(Context& context, Parse::NodeId node_id,
 
   // We didn't find anything at all.
   if (required) {
-    context.DiagnoseNameNotFound(node_id, name_id);
+    DiagnoseNameNotFound(context, node_id, name_id);
   }
 
   return {.specific_id = SemIR::SpecificId::None,
@@ -480,4 +480,34 @@ auto LookupNameInCore(Context& context, SemIR::LocId loc_id,
       scope_result.target_inst_id());
 }
 
+auto DiagnoseDuplicateName(Context& context, SemIRLoc dup_def,
+                           SemIRLoc prev_def) -> void {
+  CARBON_DIAGNOSTIC(NameDeclDuplicate, Error,
+                    "duplicate name being declared in the same scope");
+  CARBON_DIAGNOSTIC(NameDeclPrevious, Note, "name is previously declared here");
+  context.emitter()
+      .Build(dup_def, NameDeclDuplicate)
+      .Note(prev_def, NameDeclPrevious)
+      .Emit();
+}
+
+auto DiagnosePoisonedName(Context& context, SemIR::LocId poisoning_loc_id,
+                          SemIR::LocId decl_name_loc_id) -> void {
+  CARBON_CHECK(poisoning_loc_id.has_value(),
+               "Trying to diagnose poisoned name with no poisoning location");
+  CARBON_DIAGNOSTIC(NameUseBeforeDecl, Error,
+                    "name used before it was declared");
+  CARBON_DIAGNOSTIC(NameUseBeforeDeclNote, Note, "declared here");
+  context.emitter()
+      .Build(poisoning_loc_id, NameUseBeforeDecl)
+      .Note(decl_name_loc_id, NameUseBeforeDeclNote)
+      .Emit();
+}
+
+auto DiagnoseNameNotFound(Context& context, SemIRLoc loc, SemIR::NameId name_id)
+    -> void {
+  CARBON_DIAGNOSTIC(NameNotFound, Error, "name `{0}` not found", SemIR::NameId);
+  context.emitter().Emit(loc, NameNotFound, name_id);
+}
+
 }  // namespace Carbon::Check

+ 12 - 0
toolchain/check/name_lookup.h

@@ -97,6 +97,18 @@ auto LookupQualifiedName(Context& context, SemIR::LocId loc_id,
 auto LookupNameInCore(Context& context, SemIR::LocId loc_id,
                       llvm::StringRef name) -> SemIR::InstId;
 
+// Prints a diagnostic for a duplicate name.
+auto DiagnoseDuplicateName(Context& context, SemIRLoc dup_def,
+                           SemIRLoc prev_def) -> void;
+
+// Prints a diagnostic for a poisoned name when it's later declared.
+auto DiagnosePoisonedName(Context& context, SemIR::LocId poisoning_loc_id,
+                          SemIR::LocId decl_name_loc_id) -> void;
+
+// Prints a diagnostic for a missing name.
+auto DiagnoseNameNotFound(Context& context, SemIRLoc loc, SemIR::NameId name_id)
+    -> void;
+
 }  // namespace Carbon::Check
 
 #endif  // CARBON_TOOLCHAIN_CHECK_NAME_LOOKUP_H_

+ 46 - 3
toolchain/check/type_completion.cpp

@@ -261,7 +261,7 @@ auto TypeCompleter::AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool {
       if (!class_info.is_defined()) {
         if (diagnoser_) {
           auto builder = diagnoser_();
-          context_.NoteIncompleteClass(inst.class_id, builder);
+          NoteIncompleteClass(context_, inst.class_id, builder);
           builder.Emit();
         }
         return false;
@@ -497,6 +497,18 @@ auto RequireCompleteType(Context& context, SemIR::TypeId type_id,
   return true;
 }
 
+// Adds a note to a diagnostic explaining that a class is abstract.
+static auto NoteAbstractClass(Context& context, SemIR::ClassId class_id,
+                              Context::DiagnosticBuilder& builder) -> void {
+  const auto& class_info = context.classes().Get(class_id);
+  CARBON_CHECK(
+      class_info.inheritance_kind == SemIR::Class::InheritanceKind::Abstract,
+      "Class is not abstract");
+  CARBON_DIAGNOSTIC(ClassAbstractHere, Note,
+                    "class was declared abstract here");
+  builder.Note(class_info.definition_id, ClassAbstractHere);
+}
+
 auto RequireConcreteType(Context& context, SemIR::TypeId type_id,
                          SemIR::LocId loc_id,
                          Context::BuildDiagnosticFn diagnoser,
@@ -519,7 +531,7 @@ auto RequireConcreteType(Context& context, SemIR::TypeId type_id,
     if (!builder) {
       return false;
     }
-    context.NoteAbstractClass(class_type->class_id, builder);
+    NoteAbstractClass(context, class_type->class_id, builder);
     builder.Emit();
     return false;
   }
@@ -541,7 +553,7 @@ auto RequireDefinedType(Context& context, SemIR::TypeId type_id,
       auto interface_id = interface.interface_id;
       if (!context.interfaces().Get(interface_id).is_defined()) {
         auto builder = diagnoser();
-        context.NoteUndefinedInterface(interface_id, builder);
+        NoteUndefinedInterface(context, interface_id, builder);
         builder.Emit();
         return false;
       }
@@ -590,4 +602,35 @@ auto AsConcreteType(Context& context, SemIR::TypeId type_id,
              : SemIR::ErrorInst::SingletonTypeId;
 }
 
+auto NoteIncompleteClass(Context& context, SemIR::ClassId class_id,
+                         Context::DiagnosticBuilder& builder) -> void {
+  const auto& class_info = context.classes().Get(class_id);
+  CARBON_CHECK(!class_info.is_defined(), "Class is not incomplete");
+  if (class_info.has_definition_started()) {
+    CARBON_DIAGNOSTIC(ClassIncompleteWithinDefinition, Note,
+                      "class is incomplete within its definition");
+    builder.Note(class_info.definition_id, ClassIncompleteWithinDefinition);
+  } else {
+    CARBON_DIAGNOSTIC(ClassForwardDeclaredHere, Note,
+                      "class was forward declared here");
+    builder.Note(class_info.latest_decl_id(), ClassForwardDeclaredHere);
+  }
+}
+
+auto NoteUndefinedInterface(Context& context, SemIR::InterfaceId interface_id,
+                            Context::DiagnosticBuilder& builder) -> void {
+  const auto& interface_info = context.interfaces().Get(interface_id);
+  CARBON_CHECK(!interface_info.is_defined(), "Interface is not incomplete");
+  if (interface_info.is_being_defined()) {
+    CARBON_DIAGNOSTIC(InterfaceUndefinedWithinDefinition, Note,
+                      "interface is currently being defined");
+    builder.Note(interface_info.definition_id,
+                 InterfaceUndefinedWithinDefinition);
+  } else {
+    CARBON_DIAGNOSTIC(InterfaceForwardDeclaredHere, Note,
+                      "interface was forward declared here");
+    builder.Note(interface_info.latest_decl_id(), InterfaceForwardDeclaredHere);
+  }
+}
+
 }  // namespace Carbon::Check

+ 8 - 0
toolchain/check/type_completion.h

@@ -73,6 +73,14 @@ auto AsConcreteType(Context& context, SemIR::TypeId type_id,
                     Context::BuildDiagnosticFn abstract_diagnoser)
     -> SemIR::TypeId;
 
+// Adds a note to a diagnostic explaining that a class is incomplete.
+auto NoteIncompleteClass(Context& context, SemIR::ClassId class_id,
+                         Context::DiagnosticBuilder& builder) -> void;
+
+// Adds a note to a diagnostic explaining that an interface is not defined.
+auto NoteUndefinedInterface(Context& context, SemIR::InterfaceId interface_id,
+                            Context::DiagnosticBuilder& builder) -> void;
+
 }  // namespace Carbon::Check
 
 #endif  // CARBON_TOOLCHAIN_CHECK_TYPE_COMPLETION_H_