浏览代码

Add import context for locations. (#3807)

As discussed around #3792, identify the import a diagnostic message came
from prior to the diagnostic message itself. This occurs during location
translation so that the logic can be central.

I'd considered associating the parse node with ImportRef instructions,
but I realized about halfway through that because I need to store the
ImportDirectiveId on the ImportIR for cross-package imports, it's there
for use in location translation without extra work. That saves a fair
amount of stringing it through declarations, as well as an oddity where
ImportRef instructions would have a node that didn't really represent
them.
Jon Ross-Perkins 2 年之前
父节点
当前提交
6c458ffe7e
共有 29 个文件被更改,包括 262 次插入129 次删除
  1. 20 11
      toolchain/check/check.cpp
  2. 6 4
      toolchain/check/context.cpp
  3. 13 13
      toolchain/check/import.cpp
  4. 5 3
      toolchain/check/import.h
  5. 3 2
      toolchain/check/import_ref.cpp
  6. 35 23
      toolchain/check/testdata/class/cross_package_import.carbon
  7. 4 1
      toolchain/check/testdata/class/fail_import_misuses.carbon
  8. 4 1
      toolchain/check/testdata/class/fail_todo_import_forward_decl.carbon
  9. 5 2
      toolchain/check/testdata/function/definition/fail_todo_import_forward_decl.carbon
  10. 5 2
      toolchain/check/testdata/namespace/fail_conflict_imported_namespace_first.carbon
  11. 6 3
      toolchain/check/testdata/namespace/fail_conflict_imported_namespace_second.carbon
  12. 12 6
      toolchain/check/testdata/namespace/fail_conflict_in_imports_namespace_first.carbon
  13. 12 6
      toolchain/check/testdata/namespace/fail_conflict_in_imports_namespace_second.carbon
  14. 36 18
      toolchain/check/testdata/packages/cross_package_import.carbon
  15. 12 6
      toolchain/check/testdata/packages/fail_conflict_no_namespaces.carbon
  16. 10 1
      toolchain/diagnostics/diagnostic_converter.h
  17. 25 5
      toolchain/diagnostics/diagnostic_emitter.h
  18. 2 1
      toolchain/diagnostics/diagnostic_emitter_test.cpp
  19. 3 0
      toolchain/diagnostics/diagnostic_kind.def
  20. 4 2
      toolchain/diagnostics/null_diagnostics.h
  21. 1 1
      toolchain/diagnostics/sorting_diagnostic_consumer_test.cpp
  22. 2 1
      toolchain/lex/test_helpers.h
  23. 4 3
      toolchain/lex/tokenized_buffer.cpp
  24. 4 2
      toolchain/lex/tokenized_buffer.h
  25. 5 4
      toolchain/parse/tree_node_diagnostic_converter.h
  26. 9 6
      toolchain/sem_ir/file.cpp
  27. 11 0
      toolchain/sem_ir/file.h
  28. 2 1
      toolchain/sem_ir/ids.h
  29. 2 1
      toolchain/source/source_buffer.cpp

+ 20 - 11
toolchain/check/check.cpp

@@ -36,10 +36,11 @@ class SemIRDiagnosticConverter : public DiagnosticConverter<SemIRLocation> {
       const SemIR::File* sem_ir)
       const SemIR::File* sem_ir)
       : node_converters_(node_converters), sem_ir_(sem_ir) {}
       : node_converters_(node_converters), sem_ir_(sem_ir) {}
 
 
-  auto ConvertLocation(SemIRLocation loc) const -> DiagnosticLocation override {
+  auto ConvertLocation(SemIRLocation loc, ContextFnT context_fn) const
+      -> DiagnosticLocation override {
     // Parse nodes always refer to the current IR.
     // Parse nodes always refer to the current IR.
     if (!loc.is_inst_id) {
     if (!loc.is_inst_id) {
-      return ConvertLocationInFile(sem_ir_, loc.node_location);
+      return ConvertLocationInFile(sem_ir_, loc.node_location, context_fn);
     }
     }
 
 
     const auto* cursor_ir = sem_ir_;
     const auto* cursor_ir = sem_ir_;
@@ -48,14 +49,19 @@ class SemIRDiagnosticConverter : public DiagnosticConverter<SemIRLocation> {
       // If the parse node is valid, use it for the location.
       // If the parse node is valid, use it for the location.
       if (auto node_id = cursor_ir->insts().GetNodeId(cursor_inst_id);
       if (auto node_id = cursor_ir->insts().GetNodeId(cursor_inst_id);
           node_id.is_valid()) {
           node_id.is_valid()) {
-        return ConvertLocationInFile(cursor_ir, node_id);
+        return ConvertLocationInFile(cursor_ir, node_id, context_fn);
       }
       }
 
 
       // If the parse node was invalid, recurse through import references when
       // If the parse node was invalid, recurse through import references when
       // possible.
       // possible.
       if (auto import_ref = cursor_ir->insts().TryGetAs<SemIR::AnyImportRef>(
       if (auto import_ref = cursor_ir->insts().TryGetAs<SemIR::AnyImportRef>(
               cursor_inst_id)) {
               cursor_inst_id)) {
-        cursor_ir = cursor_ir->import_irs().Get(import_ref->ir_id);
+        const auto& import_ir = cursor_ir->import_irs().Get(import_ref->ir_id);
+        auto context_loc =
+            ConvertLocationInFile(cursor_ir, import_ir.node_id, context_fn);
+        CARBON_DIAGNOSTIC(InImport, Note, "In import.");
+        context_fn(context_loc, InImport);
+        cursor_ir = import_ir.sem_ir;
         cursor_inst_id = import_ref->inst_id;
         cursor_inst_id = import_ref->inst_id;
         continue;
         continue;
       }
       }
@@ -71,7 +77,8 @@ class SemIRDiagnosticConverter : public DiagnosticConverter<SemIRLocation> {
       }
       }
 
 
       // Invalid parse node but not an import; just nothing to point at.
       // Invalid parse node but not an import; just nothing to point at.
-      return ConvertLocationInFile(cursor_ir, Parse::NodeId::Invalid);
+      return ConvertLocationInFile(cursor_ir, Parse::NodeId::Invalid,
+                                   context_fn);
     }
     }
   }
   }
 
 
@@ -91,11 +98,12 @@ class SemIRDiagnosticConverter : public DiagnosticConverter<SemIRLocation> {
 
 
  private:
  private:
   auto ConvertLocationInFile(const SemIR::File* sem_ir,
   auto ConvertLocationInFile(const SemIR::File* sem_ir,
-                             Parse::NodeLocation node_location) const
+                             Parse::NodeLocation node_location,
+                             ContextFnT context_fn) const
       -> DiagnosticLocation {
       -> DiagnosticLocation {
     auto it = node_converters_->find(sem_ir);
     auto it = node_converters_->find(sem_ir);
     CARBON_CHECK(it != node_converters_->end());
     CARBON_CHECK(it != node_converters_->end());
-    return it->second->ConvertLocation(node_location);
+    return it->second->ConvertLocation(node_location, context_fn);
   }
   }
 
 
   const llvm::DenseMap<const SemIR::File*, Parse::NodeLocationConverter*>*
   const llvm::DenseMap<const SemIR::File*, Parse::NodeLocationConverter*>*
@@ -191,7 +199,7 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info)
     for (const auto& import : self_import->second.imports) {
     for (const auto& import : self_import->second.imports) {
       const auto& import_sem_ir = **import.unit_info->unit->sem_ir;
       const auto& import_sem_ir = **import.unit_info->unit->sem_ir;
       ImportLibraryFromCurrentPackage(context, namespace_type_id,
       ImportLibraryFromCurrentPackage(context, namespace_type_id,
-                                      import_sem_ir);
+                                      import.names.node_id, import_sem_ir);
       error_in_import |= import_sem_ir.name_scopes()
       error_in_import |= import_sem_ir.name_scopes()
                              .Get(SemIR::NameScopeId::Package)
                              .Get(SemIR::NameScopeId::Package)
                              .has_error;
                              .has_error;
@@ -216,13 +224,14 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info)
       continue;
       continue;
     }
     }
 
 
-    llvm::SmallVector<const SemIR::File*> sem_irs;
+    llvm::SmallVector<SemIR::ImportIR> import_irs;
     for (auto import : package_imports.imports) {
     for (auto import : package_imports.imports) {
-      sem_irs.push_back(&**import.unit_info->unit->sem_ir);
+      import_irs.push_back({.node_id = import.names.node_id,
+                            .sem_ir = &**import.unit_info->unit->sem_ir});
     }
     }
     ImportLibrariesFromOtherPackage(context, namespace_type_id,
     ImportLibrariesFromOtherPackage(context, namespace_type_id,
                                     package_imports.node_id, package_id,
                                     package_imports.node_id, package_id,
-                                    sem_irs, package_imports.has_load_error);
+                                    import_irs, package_imports.has_load_error);
   }
   }
 
 
   CARBON_CHECK(context.import_irs().size() == num_irs)
   CARBON_CHECK(context.import_irs().size() == num_irs)

+ 6 - 4
toolchain/check/context.cpp

@@ -137,7 +137,7 @@ auto Context::ReplaceInstBeforeConstantUse(
 auto Context::AddImportRef(SemIR::ImportIRId ir_id, SemIR::InstId inst_id)
 auto Context::AddImportRef(SemIR::ImportIRId ir_id, SemIR::InstId inst_id)
     -> SemIR::InstId {
     -> SemIR::InstId {
   auto import_ref_id =
   auto import_ref_id =
-      AddPlaceholderInstInNoBlock(SemIR::ImportRefUnused{ir_id, inst_id});
+      AddPlaceholderInstInNoBlock({SemIR::ImportRefUnused{ir_id, inst_id}});
 
 
   // We can't insert this instruction into whatever block we happen to be in,
   // We can't insert this instruction into whatever block we happen to be in,
   // because this function is typically called by name lookup in the middle of
   // because this function is typically called by name lookup in the middle of
@@ -303,7 +303,8 @@ static auto LookupInImportIRScopes(Context& context, SemIRLocation loc,
     // Determine the NameId in the import IR.
     // Determine the NameId in the import IR.
     SemIR::NameId import_name_id = name_id;
     SemIR::NameId import_name_id = name_id;
     if (identifier_id.is_valid()) {
     if (identifier_id.is_valid()) {
-      auto import_identifier_id = import_ir->identifiers().Lookup(identifier);
+      auto import_identifier_id =
+          import_ir.sem_ir->identifiers().Lookup(identifier);
       if (!import_identifier_id.is_valid()) {
       if (!import_identifier_id.is_valid()) {
         // Name doesn't exist in the import IR.
         // Name doesn't exist in the import IR.
         continue;
         continue;
@@ -312,7 +313,8 @@ static auto LookupInImportIRScopes(Context& context, SemIRLocation loc,
     }
     }
 
 
     // Look up the name in the import scope.
     // Look up the name in the import scope.
-    const auto& import_scope = import_ir->name_scopes().Get(import_scope_id);
+    const auto& import_scope =
+        import_ir.sem_ir->name_scopes().Get(import_scope_id);
     auto it = import_scope.names.find(import_name_id);
     auto it = import_scope.names.find(import_name_id);
     if (it == import_scope.names.end()) {
     if (it == import_scope.names.end()) {
       // Name doesn't exist in the import scope.
       // Name doesn't exist in the import scope.
@@ -742,7 +744,7 @@ class TypeCompleter {
   auto BuildImportRefUsedValueRepr(SemIR::TypeId type_id,
   auto BuildImportRefUsedValueRepr(SemIR::TypeId type_id,
                                    SemIR::ImportRefUsed import_ref) const
                                    SemIR::ImportRefUsed import_ref) const
       -> SemIR::ValueRepr {
       -> SemIR::ValueRepr {
-    const auto& import_ir = context_.import_irs().Get(import_ref.ir_id);
+    const auto& import_ir = context_.import_irs().Get(import_ref.ir_id).sem_ir;
     auto import_inst = import_ir->insts().Get(import_ref.inst_id);
     auto import_inst = import_ir->insts().Get(import_ref.inst_id);
     CARBON_CHECK(import_inst.kind() != SemIR::InstKind::ImportRefUsed)
     CARBON_CHECK(import_inst.kind() != SemIR::InstKind::ImportRefUsed)
         << "If ImportRefUsed can point at another, this would be recursive.";
         << "If ImportRefUsed can point at another, this would be recursive.";

+ 13 - 13
toolchain/check/import.cpp

@@ -130,12 +130,11 @@ static auto CacheCopiedNamespace(
 // name conflicts, but that won't change the result because namespaces supersede
 // name conflicts, but that won't change the result because namespaces supersede
 // other names in conflicts.
 // other names in conflicts.
 static auto CopySingleNameScopeFromImportIR(
 static auto CopySingleNameScopeFromImportIR(
-    Context& context,
+    Context& context, SemIR::TypeId namespace_type_id,
     llvm::DenseMap<SemIR::NameScopeId, SemIR::NameScopeId>& copied_namespaces,
     llvm::DenseMap<SemIR::NameScopeId, SemIR::NameScopeId>& copied_namespaces,
     SemIR::ImportIRId ir_id, SemIR::InstId import_inst_id,
     SemIR::ImportIRId ir_id, SemIR::InstId import_inst_id,
     SemIR::NameScopeId import_scope_id, SemIR::NameScopeId enclosing_scope_id,
     SemIR::NameScopeId import_scope_id, SemIR::NameScopeId enclosing_scope_id,
-    SemIR::NameId name_id, SemIR::TypeId namespace_type_id)
-    -> SemIR::NameScopeId {
+    SemIR::NameId name_id) -> SemIR::NameScopeId {
   // Produce the namespace for the entry.
   // Produce the namespace for the entry.
   auto make_import_id = [&]() {
   auto make_import_id = [&]() {
     return context.AddInst(SemIR::ImportRefUsed{.type_id = namespace_type_id,
     return context.AddInst(SemIR::ImportRefUsed{.type_id = namespace_type_id,
@@ -199,8 +198,8 @@ static auto CopyEnclosingNameScopesFromImportIR(
     auto name_id =
     auto name_id =
         CopyNameFromImportIR(context, import_sem_ir, import_scope.name_id);
         CopyNameFromImportIR(context, import_sem_ir, import_scope.name_id);
     scope_cursor = CopySingleNameScopeFromImportIR(
     scope_cursor = CopySingleNameScopeFromImportIR(
-        context, copied_namespaces, ir_id, import_scope.inst_id,
-        import_scope_id, scope_cursor, name_id, namespace_type_id);
+        context, namespace_type_id, copied_namespaces, ir_id,
+        import_scope.inst_id, import_scope_id, scope_cursor, name_id);
   }
   }
 
 
   return scope_cursor;
   return scope_cursor;
@@ -208,8 +207,10 @@ static auto CopyEnclosingNameScopesFromImportIR(
 
 
 auto ImportLibraryFromCurrentPackage(Context& context,
 auto ImportLibraryFromCurrentPackage(Context& context,
                                      SemIR::TypeId namespace_type_id,
                                      SemIR::TypeId namespace_type_id,
+                                     Parse::ImportDirectiveId node_id,
                                      const SemIR::File& import_sem_ir) -> void {
                                      const SemIR::File& import_sem_ir) -> void {
-  auto ir_id = context.import_irs().Add(&import_sem_ir);
+  auto ir_id =
+      context.import_irs().Add({.node_id = node_id, .sem_ir = &import_sem_ir});
   context.import_ir_constant_values()[ir_id.index].Set(
   context.import_ir_constant_values()[ir_id.index].Set(
       SemIR::InstId::PackageNamespace,
       SemIR::InstId::PackageNamespace,
       context.constant_values().Get(SemIR::InstId::PackageNamespace));
       context.constant_values().Get(SemIR::InstId::PackageNamespace));
@@ -236,9 +237,8 @@ auto ImportLibraryFromCurrentPackage(Context& context,
       // Namespaces are always imported because they're essential for
       // Namespaces are always imported because they're essential for
       // qualifiers, and the type is simple.
       // qualifiers, and the type is simple.
       CopySingleNameScopeFromImportIR(
       CopySingleNameScopeFromImportIR(
-          context, copied_namespaces, ir_id, import_inst_id,
-          import_namespace_inst->name_scope_id, enclosing_scope_id, name_id,
-          namespace_type_id);
+          context, namespace_type_id, copied_namespaces, ir_id, import_inst_id,
+          import_namespace_inst->name_scope_id, enclosing_scope_id, name_id);
     } else {
     } else {
       // Leave a placeholder that the inst comes from the other IR.
       // Leave a placeholder that the inst comes from the other IR.
       auto target_id = context.AddImportRef(ir_id, import_inst_id);
       auto target_id = context.AddImportRef(ir_id, import_inst_id);
@@ -260,9 +260,9 @@ auto ImportLibrariesFromOtherPackage(Context& context,
                                      SemIR::TypeId namespace_type_id,
                                      SemIR::TypeId namespace_type_id,
                                      Parse::ImportDirectiveId node_id,
                                      Parse::ImportDirectiveId node_id,
                                      IdentifierId package_id,
                                      IdentifierId package_id,
-                                     llvm::ArrayRef<const SemIR::File*> sem_irs,
+                                     llvm::ArrayRef<SemIR::ImportIR> import_irs,
                                      bool has_load_error) -> void {
                                      bool has_load_error) -> void {
-  CARBON_CHECK(has_load_error || !sem_irs.empty())
+  CARBON_CHECK(has_load_error || !import_irs.empty())
       << "There should be either a load error or at least one IR.";
       << "There should be either a load error or at least one IR.";
 
 
   auto name_id = SemIR::NameId::ForIdentifier(package_id);
   auto name_id = SemIR::NameId::ForIdentifier(package_id);
@@ -273,8 +273,8 @@ auto ImportLibrariesFromOtherPackage(Context& context,
 
 
   auto& scope = context.name_scopes().Get(namespace_scope_id);
   auto& scope = context.name_scopes().Get(namespace_scope_id);
   scope.is_closed_import = !is_duplicate;
   scope.is_closed_import = !is_duplicate;
-  for (const auto* sem_ir : sem_irs) {
-    auto ir_id = context.import_irs().Add(sem_ir);
+  for (auto import_ir : import_irs) {
+    auto ir_id = context.import_irs().Add(import_ir);
     scope.import_ir_scopes.push_back({ir_id, SemIR::NameScopeId::Package});
     scope.import_ir_scopes.push_back({ir_id, SemIR::NameScopeId::Package});
     context.import_ir_constant_values()[ir_id.index].Set(
     context.import_ir_constant_values()[ir_id.index].Set(
         SemIR::InstId::PackageNamespace, namespace_const_id);
         SemIR::InstId::PackageNamespace, namespace_const_id);

+ 5 - 3
toolchain/check/import.h

@@ -6,6 +6,7 @@
 #define CARBON_TOOLCHAIN_CHECK_IMPORT_H_
 #define CARBON_TOOLCHAIN_CHECK_IMPORT_H_
 
 
 #include "toolchain/check/context.h"
 #include "toolchain/check/context.h"
+#include "toolchain/parse/node_ids.h"
 #include "toolchain/sem_ir/file.h"
 #include "toolchain/sem_ir/file.h"
 
 
 namespace Carbon::Check {
 namespace Carbon::Check {
@@ -15,6 +16,7 @@ namespace Carbon::Check {
 // though they are several layers deep.
 // though they are several layers deep.
 auto ImportLibraryFromCurrentPackage(Context& context,
 auto ImportLibraryFromCurrentPackage(Context& context,
                                      SemIR::TypeId namespace_type_id,
                                      SemIR::TypeId namespace_type_id,
+                                     Parse::ImportDirectiveId node_id,
                                      const SemIR::File& import_sem_ir) -> void;
                                      const SemIR::File& import_sem_ir) -> void;
 
 
 // Adds another package's imports to name lookup, with all libraries together.
 // Adds another package's imports to name lookup, with all libraries together.
@@ -22,13 +24,13 @@ auto ImportLibraryFromCurrentPackage(Context& context,
 // will resolve, and will provide a name scope that can be used for further
 // will resolve, and will provide a name scope that can be used for further
 // qualified name lookups.
 // qualified name lookups.
 //
 //
-// sem_irs will all be non-null, and may be empty. has_load_error is used to
-// indicate if any library in the package failed to import correctly.
+// import_irs may be empty. has_load_error is used to indicate if any library in
+// the package failed to import correctly.
 auto ImportLibrariesFromOtherPackage(Context& context,
 auto ImportLibrariesFromOtherPackage(Context& context,
                                      SemIR::TypeId namespace_type_id,
                                      SemIR::TypeId namespace_type_id,
                                      Parse::ImportDirectiveId node_id,
                                      Parse::ImportDirectiveId node_id,
                                      IdentifierId package_id,
                                      IdentifierId package_id,
-                                     llvm::ArrayRef<const SemIR::File*> sem_irs,
+                                     llvm::ArrayRef<SemIR::ImportIR> import_irs,
                                      bool has_load_error) -> void;
                                      bool has_load_error) -> void;
 
 
 }  // namespace Carbon::Check
 }  // namespace Carbon::Check

+ 3 - 2
toolchain/check/import_ref.cpp

@@ -57,7 +57,7 @@ class ImportRefResolver {
   explicit ImportRefResolver(Context& context, SemIR::ImportIRId import_ir_id)
   explicit ImportRefResolver(Context& context, SemIR::ImportIRId import_ir_id)
       : context_(context),
       : context_(context),
         import_ir_id_(import_ir_id),
         import_ir_id_(import_ir_id),
-        import_ir_(*context_.import_irs().Get(import_ir_id)),
+        import_ir_(*context_.import_irs().Get(import_ir_id).sem_ir),
         import_ir_constant_values_(
         import_ir_constant_values_(
             context_.import_ir_constant_values()[import_ir_id.index]) {}
             context_.import_ir_constant_values()[import_ir_id.index]) {}
 
 
@@ -912,7 +912,8 @@ auto TryResolveImportRefUnused(Context& context, SemIR::InstId inst_id)
     return;
     return;
   }
   }
 
 
-  const SemIR::File& import_ir = *context.import_irs().Get(import_ref->ir_id);
+  const SemIR::File& import_ir =
+      *context.import_irs().Get(import_ref->ir_id).sem_ir;
   auto import_inst = import_ir.insts().Get(import_ref->inst_id);
   auto import_inst = import_ir.insts().Get(import_ref->inst_id);
 
 
   ImportRefResolver resolver(context, import_ref->ir_id);
   ImportRefResolver resolver(context, import_ref->ir_id);

+ 35 - 23
toolchain/check/testdata/class/cross_package_import.carbon

@@ -45,17 +45,11 @@ library "extern" api;
 
 
 import Other library "extern";
 import Other library "extern";
 
 
-// CHECK:STDERR: fail_extern.carbon:[[@LINE+11]]:8: ERROR: Variable has incomplete type `C`.
+// CHECK:STDERR: fail_extern.carbon:[[@LINE+5]]:8: ERROR: Variable has incomplete type `C`.
 // CHECK:STDERR: var c: Other.C = {};
 // CHECK:STDERR: var c: Other.C = {};
 // CHECK:STDERR:        ^~~~~~~
 // CHECK:STDERR:        ^~~~~~~
 // CHECK:STDERR: fail_extern.carbon: Class was forward declared here.
 // CHECK:STDERR: fail_extern.carbon: Class was forward declared here.
 // CHECK:STDERR:
 // CHECK:STDERR:
-// CHECK:STDERR: other_extern.carbon:5:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: class C;
-// CHECK:STDERR: ^~~~~~~~
-// CHECK:STDERR: other_define.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: class C {}
-// CHECK:STDERR: ^~~~~~~~~
 var c: Other.C = {};
 var c: Other.C = {};
 
 
 // --- fail_merge_define_extern.carbon
 // --- fail_merge_define_extern.carbon
@@ -63,18 +57,24 @@ var c: Other.C = {};
 library "fail_merge_define_extern" api;
 library "fail_merge_define_extern" api;
 
 
 import Other library "define";
 import Other library "define";
+// CHECK:STDERR: fail_merge_define_extern.carbon:[[@LINE+12]]:1: In import.
+// CHECK:STDERR: import Other library "extern";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: other_extern.carbon:5:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: class C;
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: fail_merge_define_extern.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import Other library "define";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: other_define.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
 import Other library "extern";
 import Other library "extern";
 
 
-// CHECK:STDERR: fail_merge_define_extern.carbon:[[@LINE+10]]:8: In name lookup for `C`.
+// CHECK:STDERR: fail_merge_define_extern.carbon:[[@LINE+4]]:8: In name lookup for `C`.
 // CHECK:STDERR: var c: Other.C = {};
 // CHECK:STDERR: var c: Other.C = {};
 // CHECK:STDERR:        ^~~~~~~
 // CHECK:STDERR:        ^~~~~~~
 // CHECK:STDERR:
 // CHECK:STDERR:
-// CHECK:STDERR: other_conflict.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: fn C() {}
-// CHECK:STDERR: ^~~~~~~~
-// CHECK:STDERR: other_define.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: class C {}
-// CHECK:STDERR: ^~~~~~~~~
 var c: Other.C = {};
 var c: Other.C = {};
 
 
 // --- fail_conflict.carbon
 // --- fail_conflict.carbon
@@ -82,6 +82,18 @@ var c: Other.C = {};
 library "conflict" api;
 library "conflict" api;
 
 
 import Other library "define";
 import Other library "define";
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE+12]]:1: In import.
+// CHECK:STDERR: import Other library "conflict";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: other_conflict.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fn C() {}
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import Other library "define";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: other_define.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
 import Other library "conflict";
 import Other library "conflict";
 
 
 // CHECK:STDERR: fail_conflict.carbon:[[@LINE+3]]:8: In name lookup for `C`.
 // CHECK:STDERR: fail_conflict.carbon:[[@LINE+3]]:8: In name lookup for `C`.
@@ -201,7 +213,7 @@ var c: Other.C = {};
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc17: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc11: {} = struct_literal ()
 // CHECK:STDOUT:   assign file.%c.var, <error>
 // CHECK:STDOUT:   assign file.%c.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
@@ -243,10 +255,10 @@ var c: Other.C = {};
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc17_19.1: {} = struct_literal ()
-// CHECK:STDOUT:   %.loc17_19.2: init C = class_init (), file.%c.var [template = constants.%.4]
-// CHECK:STDOUT:   %.loc17_19.3: init C = converted %.loc17_19.1, %.loc17_19.2 [template = constants.%.4]
-// CHECK:STDOUT:   assign file.%c.var, %.loc17_19.3
+// CHECK:STDOUT:   %.loc23_19.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc23_19.2: init C = class_init (), file.%c.var [template = constants.%.4]
+// CHECK:STDOUT:   %.loc23_19.3: init C = converted %.loc23_19.1, %.loc23_19.2 [template = constants.%.4]
+// CHECK:STDOUT:   assign file.%c.var, %.loc23_19.3
 // CHECK:STDOUT:   return
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -285,10 +297,10 @@ var c: Other.C = {};
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc10_19.1: {} = struct_literal ()
-// CHECK:STDOUT:   %.loc10_19.2: init C = class_init (), file.%c.var [template = constants.%.4]
-// CHECK:STDOUT:   %.loc10_19.3: init C = converted %.loc10_19.1, %.loc10_19.2 [template = constants.%.4]
-// CHECK:STDOUT:   assign file.%c.var, %.loc10_19.3
+// CHECK:STDOUT:   %.loc22_19.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc22_19.2: init C = class_init (), file.%c.var [template = constants.%.4]
+// CHECK:STDOUT:   %.loc22_19.3: init C = converted %.loc22_19.1, %.loc22_19.2 [template = constants.%.4]
+// CHECK:STDOUT:   assign file.%c.var, %.loc22_19.3
 // CHECK:STDOUT:   return
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:

+ 4 - 1
toolchain/check/testdata/class/fail_import_misuses.carbon

@@ -19,9 +19,12 @@ library "b" api;
 
 
 import library "a";
 import library "a";
 
 
-// CHECK:STDERR: fail_b.carbon:[[@LINE+7]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fail_b.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: class Empty {
 // CHECK:STDERR: class Empty {
 // CHECK:STDERR: ^~~~~~~~~~~~~
 // CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR: fail_b.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "a";
+// CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: a.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: a.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: class Empty {
 // CHECK:STDERR: class Empty {
 // CHECK:STDERR: ^~~~~~~~~~~~~
 // CHECK:STDERR: ^~~~~~~~~~~~~

+ 4 - 1
toolchain/check/testdata/class/fail_todo_import_forward_decl.carbon

@@ -17,9 +17,12 @@ library "b" api;
 import library "a";
 import library "a";
 
 
 // TODO: This should probably have a valid form.
 // TODO: This should probably have a valid form.
-// CHECK:STDERR: fail_b.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fail_b.carbon:[[@LINE+9]]:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: class ForwardDecl {
 // CHECK:STDERR: class ForwardDecl {
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_b.carbon:[[@LINE-6]]:1: In import.
+// CHECK:STDERR: import library "a";
+// CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: a.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: a.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: class ForwardDecl;
 // CHECK:STDERR: class ForwardDecl;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~

+ 5 - 2
toolchain/check/testdata/function/definition/fail_todo_import_forward_decl.carbon

@@ -18,9 +18,12 @@ import library "a";
 
 
 // TODO: This should be an error until we can be clear whether `a` or `b` should
 // TODO: This should be an error until we can be clear whether `a` or `b` should
 // own the definition, as there might be a separate definition in `a`'s impl.
 // own the definition, as there might be a separate definition in `a`'s impl.
-// CHECK:STDERR: fail_b.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fail_b.carbon:[[@LINE+9]]:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: fn Foo() {}
 // CHECK:STDERR: fn Foo() {}
 // CHECK:STDERR: ^~~~~~~~~~
 // CHECK:STDERR: ^~~~~~~~~~
+// CHECK:STDERR: fail_b.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import library "a";
+// CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: a.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: a.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: fn Foo();
 // CHECK:STDERR: fn Foo();
 // CHECK:STDERR: ^~~~~~~~~
 // CHECK:STDERR: ^~~~~~~~~
@@ -44,7 +47,7 @@ fn Foo() {}
 // CHECK:STDOUT:     .Foo = %import_ref
 // CHECK:STDOUT:     .Foo = %import_ref
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref: <function> = import_ref ir1, inst+1, used [template = imports.%Foo]
 // CHECK:STDOUT:   %import_ref: <function> = import_ref ir1, inst+1, used [template = imports.%Foo]
-// CHECK:STDOUT:   %.loc14: <function> = fn_decl @.1 [template] {}
+// CHECK:STDOUT:   %.loc17: <function> = fn_decl @.1 [template] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Foo();
 // CHECK:STDOUT: fn @Foo();

+ 5 - 2
toolchain/check/testdata/namespace/fail_conflict_imported_namespace_first.carbon

@@ -16,9 +16,12 @@ package Example api;
 
 
 import library "namespace";
 import library "namespace";
 
 
-// CHECK:STDERR: fail_conflict.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE+9]]:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: fn NS();
 // CHECK:STDERR: fn NS();
 // CHECK:STDERR: ^~~~~~~~
 // CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "namespace";
+// CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: namespace.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: namespace.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: namespace NS;
 // CHECK:STDERR: namespace NS;
 // CHECK:STDERR: ^~~~~~~~~~~~~
 // CHECK:STDERR: ^~~~~~~~~~~~~
@@ -45,7 +48,7 @@ fn NS.Foo();
 // CHECK:STDOUT:   %NS: <namespace> = namespace %import_ref, [template] {
 // CHECK:STDOUT:   %NS: <namespace> = namespace %import_ref, [template] {
 // CHECK:STDOUT:     .Foo = %Foo
 // CHECK:STDOUT:     .Foo = %Foo
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc12: <function> = fn_decl @.1 [template] {}
+// CHECK:STDOUT:   %.loc15: <function> = fn_decl @.1 [template] {}
 // CHECK:STDOUT:   %Foo: <function> = fn_decl @Foo [template] {}
 // CHECK:STDOUT:   %Foo: <function> = fn_decl @Foo [template] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:

+ 6 - 3
toolchain/check/testdata/namespace/fail_conflict_imported_namespace_second.carbon

@@ -16,9 +16,12 @@ package Example api;
 
 
 import library "fn";
 import library "fn";
 
 
-// CHECK:STDERR: fail_conflict.carbon:[[@LINE+7]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: namespace NS;
 // CHECK:STDERR: namespace NS;
 // CHECK:STDERR: ^~~~~~~~~~~~~
 // CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "fn";
+// CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: fn.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: fn.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: fn NS();
 // CHECK:STDERR: fn NS();
 // CHECK:STDERR: ^~~~~~~~
 // CHECK:STDERR: ^~~~~~~~
@@ -51,8 +54,8 @@ fn NS.Foo();
 // CHECK:STDOUT:     .NS = %import_ref
 // CHECK:STDOUT:     .NS = %import_ref
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref: <function> = import_ref ir1, inst+1, used [template = imports.%NS]
 // CHECK:STDOUT:   %import_ref: <function> = import_ref ir1, inst+1, used [template = imports.%NS]
-// CHECK:STDOUT:   %.loc13: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %.loc21: <function> = fn_decl @.1 [template] {}
+// CHECK:STDOUT:   %.loc16: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %.loc24: <function> = fn_decl @.1 [template] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @NS();
 // CHECK:STDOUT: fn @NS();

+ 12 - 6
toolchain/check/testdata/namespace/fail_conflict_in_imports_namespace_first.carbon

@@ -15,12 +15,6 @@ fn NS.Foo() {}
 
 
 package Example library "fn" api;
 package Example library "fn" api;
 
 
-// CHECK:STDERR: fn.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: fn NS() {}
-// CHECK:STDERR: ^~~~~~~~~
-// CHECK:STDERR: namespace.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: namespace NS;
-// CHECK:STDERR: ^~~~~~~~~~~~~
 fn NS() {}
 fn NS() {}
 
 
 // --- fail_conflict.carbon
 // --- fail_conflict.carbon
@@ -28,6 +22,18 @@ fn NS() {}
 package Example library "namespace_then_fn" api;
 package Example library "namespace_then_fn" api;
 
 
 import library "namespace";
 import library "namespace";
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE+12]]:1: In import.
+// CHECK:STDERR: import library "fn";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: fn.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fn NS() {}
+// CHECK:STDERR: ^~~~~~~~~
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import library "namespace";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: namespace.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: namespace NS;
+// CHECK:STDERR: ^~~~~~~~~~~~~
 import library "fn";
 import library "fn";
 
 
 fn NS.Bar() {}
 fn NS.Bar() {}

+ 12 - 6
toolchain/check/testdata/namespace/fail_conflict_in_imports_namespace_second.carbon

@@ -14,12 +14,6 @@ fn NS() {}
 
 
 package Example library "namespace" api;
 package Example library "namespace" api;
 
 
-// CHECK:STDERR: namespace.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: namespace NS;
-// CHECK:STDERR: ^~~~~~~~~~~~~
-// CHECK:STDERR: fn.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: fn NS() {}
-// CHECK:STDERR: ^~~~~~~~~
 namespace NS;
 namespace NS;
 fn NS.Foo() {}
 fn NS.Foo() {}
 
 
@@ -28,6 +22,18 @@ fn NS.Foo() {}
 package Example library "fn_then_namespace" api;
 package Example library "fn_then_namespace" api;
 
 
 import library "fn";
 import library "fn";
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE+12]]:1: In import.
+// CHECK:STDERR: import library "namespace";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: namespace.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: namespace NS;
+// CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import library "fn";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: fn.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: fn NS() {}
+// CHECK:STDERR: ^~~~~~~~~
 import library "namespace";
 import library "namespace";
 
 
 fn NS.Bar() {}
 fn NS.Bar() {}

+ 36 - 18
toolchain/check/testdata/packages/cross_package_import.carbon

@@ -19,12 +19,6 @@ fn F() {}
 package Other library "fn_extern" api;
 package Other library "fn_extern" api;
 
 
 // TODO: Mark extern
 // TODO: Mark extern
-// CHECK:STDERR: other_fn_extern.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: fn F();
-// CHECK:STDERR: ^~~~~~~
-// CHECK:STDERR: other_fn.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: fn F() {}
-// CHECK:STDERR: ^~~~~~~~
 fn F();
 fn F();
 
 
 // --- other_fn_conflict.carbon
 // --- other_fn_conflict.carbon
@@ -66,19 +60,25 @@ fn Run() {
 library "use_other_extern" api;
 library "use_other_extern" api;
 
 
 import Other library "fn";
 import Other library "fn";
+// CHECK:STDERR: fail_main_use_other_extern.carbon:[[@LINE+12]]:1: In import.
+// CHECK:STDERR: import Other library "fn_extern";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: other_fn_extern.carbon:5:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fn F();
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR: fail_main_use_other_extern.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import Other library "fn";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: other_fn.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: fn F() {}
+// CHECK:STDERR: ^~~~~~~~
 import Other library "fn_extern";
 import Other library "fn_extern";
 
 
 fn Run() {
 fn Run() {
-  // CHECK:STDERR: fail_main_use_other_extern.carbon:[[@LINE+10]]:3: In name lookup for `F`.
+  // CHECK:STDERR: fail_main_use_other_extern.carbon:[[@LINE+4]]:3: In name lookup for `F`.
   // CHECK:STDERR:   Other.F();
   // CHECK:STDERR:   Other.F();
   // CHECK:STDERR:   ^~~~~~~
   // CHECK:STDERR:   ^~~~~~~
   // CHECK:STDERR:
   // CHECK:STDERR:
-  // CHECK:STDERR: other_fn_conflict.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
-  // CHECK:STDERR: fn F(x: i32) {}
-  // CHECK:STDERR: ^~~~~~~~~~~~~~
-  // CHECK:STDERR: other_fn.carbon:4:1: Name is previously declared here.
-  // CHECK:STDERR: fn F() {}
-  // CHECK:STDERR: ^~~~~~~~
   Other.F();
   Other.F();
 }
 }
 
 
@@ -94,6 +94,18 @@ import Other library "fn_conflict";
 library "use_other_ambiguous" api;
 library "use_other_ambiguous" api;
 
 
 import Other library "fn";
 import Other library "fn";
+// CHECK:STDERR: fail_main_use_other_ambiguous.carbon:[[@LINE+12]]:1: In import.
+// CHECK:STDERR: import Other library "fn_conflict";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: other_fn_conflict.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fn F(x: i32) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR: fail_main_use_other_ambiguous.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import Other library "fn";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: other_fn.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: fn F() {}
+// CHECK:STDERR: ^~~~~~~~
 import Other library "fn_conflict";
 import Other library "fn_conflict";
 
 
 fn Run() {
 fn Run() {
@@ -109,18 +121,24 @@ fn Run() {
 library "namespace_conflict" api;
 library "namespace_conflict" api;
 
 
 import library "other_ns";
 import library "other_ns";
-// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE+7]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: import Other library "fn";
 // CHECK:STDERR: import Other library "fn";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE-4]]:1: In import.
+// CHECK:STDERR: import library "other_ns";
+// CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: main_other_ns.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: main_other_ns.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: namespace Other;
 // CHECK:STDERR: namespace Other;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 // CHECK:STDERR:
 import Other library "fn";
 import Other library "fn";
 
 
-// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE+7]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: fn Other.F() {}
 // CHECK:STDERR: fn Other.F() {}
 // CHECK:STDERR: ^~~~~~~~~~~~~~
 // CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import Other library "fn";
+// CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: other_fn.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: other_fn.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: fn F() {}
 // CHECK:STDERR: fn F() {}
 // CHECK:STDERR: ^~~~~~~~
 // CHECK:STDERR: ^~~~~~~~
@@ -277,7 +295,7 @@ fn Other.G() {}
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Other.ref: <namespace> = name_ref Other, file.%Other [template = file.%Other]
 // CHECK:STDOUT:   %Other.ref: <namespace> = name_ref Other, file.%Other [template = file.%Other]
 // CHECK:STDOUT:   %F.ref: <function> = name_ref F, file.%import_ref.1 [template = imports.%F.1]
 // CHECK:STDOUT:   %F.ref: <function> = name_ref F, file.%import_ref.1 [template = imports.%F.1]
-// CHECK:STDOUT:   %.loc18: init () = call %F.ref()
+// CHECK:STDOUT:   %.loc24: init () = call %F.ref()
 // CHECK:STDOUT:   return
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -315,7 +333,7 @@ fn Other.G() {}
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Other.ref: <namespace> = name_ref Other, file.%Other [template = file.%Other]
 // CHECK:STDOUT:   %Other.ref: <namespace> = name_ref Other, file.%Other [template = file.%Other]
 // CHECK:STDOUT:   %F.ref: <function> = name_ref F, file.%import_ref.1 [template = imports.%F.1]
 // CHECK:STDOUT:   %F.ref: <function> = name_ref F, file.%import_ref.1 [template = imports.%F.1]
-// CHECK:STDOUT:   %.loc12: init () = call %F.ref()
+// CHECK:STDOUT:   %.loc24: init () = call %F.ref()
 // CHECK:STDOUT:   return
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -332,7 +350,7 @@ fn Other.G() {}
 // CHECK:STDOUT:   %import_ref.1: <namespace> = import_ref ir1, inst+1, used
 // CHECK:STDOUT:   %import_ref.1: <namespace> = import_ref ir1, inst+1, used
 // CHECK:STDOUT:   %Other: <namespace> = namespace %import_ref.1, [template] {}
 // CHECK:STDOUT:   %Other: <namespace> = namespace %import_ref.1, [template] {}
 // CHECK:STDOUT:   %import_ref.2: <function> = import_ref ir2, inst+1, used [template = imports.%F]
 // CHECK:STDOUT:   %import_ref.2: <function> = import_ref ir2, inst+1, used [template = imports.%F]
-// CHECK:STDOUT:   %.loc21: <function> = fn_decl @.1 [template] {}
+// CHECK:STDOUT:   %.loc27: <function> = fn_decl @.1 [template] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F();
 // CHECK:STDOUT: fn @F();

+ 12 - 6
toolchain/check/testdata/packages/fail_conflict_no_namespaces.carbon

@@ -14,12 +14,6 @@ fn Foo();
 
 
 package Example library "var" api;
 package Example library "var" api;
 
 
-// CHECK:STDERR: var.carbon:[[@LINE+6]]:5: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: var Foo: i32;
-// CHECK:STDERR:     ^~~
-// CHECK:STDERR: fn.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: fn Foo();
-// CHECK:STDERR: ^~~~~~~~~
 var Foo: i32;
 var Foo: i32;
 
 
 // --- fail_conflict.carbon
 // --- fail_conflict.carbon
@@ -27,6 +21,18 @@ var Foo: i32;
 package Example library "conflict" api;
 package Example library "conflict" api;
 
 
 import library "fn";
 import library "fn";
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE+12]]:1: In import.
+// CHECK:STDERR: import library "var";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: var.carbon:4:5: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: var Foo: i32;
+// CHECK:STDERR:     ^~~
+// CHECK:STDERR: fail_conflict.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import library "fn";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: fn.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: fn Foo();
+// CHECK:STDERR: ^~~~~~~~~
 import library "var";
 import library "var";
 
 
 // CHECK:STDOUT: --- fn.carbon
 // CHECK:STDOUT: --- fn.carbon

+ 10 - 1
toolchain/diagnostics/diagnostic_converter.h

@@ -15,9 +15,18 @@ namespace Carbon {
 template <typename LocationT>
 template <typename LocationT>
 class DiagnosticConverter {
 class DiagnosticConverter {
  public:
  public:
+  // Callback type used to report context messages from ConvertLocation.
+  // Note that the first parameter type is DiagnosticLocation rather than
+  // LocationT, because ConvertLocation must not recurse.
+  using ContextFnT = llvm::function_ref<void(
+      DiagnosticLocation, const Internal::DiagnosticBase<>&)>;
+
   virtual ~DiagnosticConverter() = default;
   virtual ~DiagnosticConverter() = default;
 
 
-  virtual auto ConvertLocation(LocationT loc) const -> DiagnosticLocation = 0;
+  // Converts a LocationT to a DiagnosticLocation. ConvertLocation may invoke
+  // context_fn to provide context messages.
+  virtual auto ConvertLocation(LocationT loc, ContextFnT context_fn) const
+      -> DiagnosticLocation = 0;
 
 
   // Converts arg types as needed. Not all uses require conversion, so the
   // Converts arg types as needed. Not all uses require conversion, so the
   // default returns the argument unchanged.
   // default returns the argument unchanged.

+ 25 - 5
toolchain/diagnostics/diagnostic_emitter.h

@@ -65,8 +65,7 @@ class DiagnosticEmitter {
               Internal::NoTypeDeduction<Args>... args) -> DiagnosticBuilder& {
               Internal::NoTypeDeduction<Args>... args) -> DiagnosticBuilder& {
       CARBON_CHECK(diagnostic_base.Level == DiagnosticLevel::Note)
       CARBON_CHECK(diagnostic_base.Level == DiagnosticLevel::Note)
           << static_cast<int>(diagnostic_base.Level);
           << static_cast<int>(diagnostic_base.Level);
-      AddMessage(emitter_, location, diagnostic_base,
-                 {emitter_->MakeAny<Args>(args)...});
+      AddMessage(location, diagnostic_base, {emitter_->MakeAny<Args>(args)...});
       return *this;
       return *this;
     }
     }
 
 
@@ -89,18 +88,39 @@ class DiagnosticEmitter {
         const Internal::DiagnosticBase<Args...>& diagnostic_base,
         const Internal::DiagnosticBase<Args...>& diagnostic_base,
         llvm::SmallVector<llvm::Any> args)
         llvm::SmallVector<llvm::Any> args)
         : emitter_(emitter), diagnostic_({.level = diagnostic_base.Level}) {
         : emitter_(emitter), diagnostic_({.level = diagnostic_base.Level}) {
-      AddMessage(emitter, location, diagnostic_base, std::move(args));
+      AddMessage(location, diagnostic_base, std::move(args));
       CARBON_CHECK(diagnostic_base.Level != DiagnosticLevel::Note);
       CARBON_CHECK(diagnostic_base.Level != DiagnosticLevel::Note);
     }
     }
 
 
+    // Adds a message to the diagnostic, handling conversion of the location and
+    // arguments.
     template <typename... Args>
     template <typename... Args>
-    auto AddMessage(DiagnosticEmitter<LocationT>* emitter, LocationT location,
+    auto AddMessage(LocationT location,
                     const Internal::DiagnosticBase<Args...>& diagnostic_base,
                     const Internal::DiagnosticBase<Args...>& diagnostic_base,
                     llvm::SmallVector<llvm::Any> args) -> void {
                     llvm::SmallVector<llvm::Any> args) -> void {
+      AddMessageWithDiagnosticLocation(
+          emitter_->converter_->ConvertLocation(
+              location,
+              [&](DiagnosticLocation location,
+                  const Internal::DiagnosticBase<>& diagnostic_base) {
+                AddMessageWithDiagnosticLocation(location, diagnostic_base,
+                                                 args);
+              }),
+          diagnostic_base, args);
+    }
+
+    // Adds a message to the diagnostic, handling conversion of the arguments. A
+    // DiagnosticLocation must be provided instead of a LocationT in order to
+    // avoid potential recursion.
+    template <typename... Args>
+    auto AddMessageWithDiagnosticLocation(
+        DiagnosticLocation location,
+        const Internal::DiagnosticBase<Args...>& diagnostic_base,
+        llvm::SmallVector<llvm::Any> args) {
       diagnostic_.messages.emplace_back(DiagnosticMessage{
       diagnostic_.messages.emplace_back(DiagnosticMessage{
           .kind = diagnostic_base.Kind,
           .kind = diagnostic_base.Kind,
           .level = diagnostic_base.Level,
           .level = diagnostic_base.Level,
-          .location = emitter->converter_->ConvertLocation(location),
+          .location = location,
           .format = diagnostic_base.Format,
           .format = diagnostic_base.Format,
           .format_args = std::move(args),
           .format_args = std::move(args),
           .format_fn = [](const DiagnosticMessage& message) -> std::string {
           .format_fn = [](const DiagnosticMessage& message) -> std::string {

+ 2 - 1
toolchain/diagnostics/diagnostic_emitter_test.cpp

@@ -18,7 +18,8 @@ using ::Carbon::Testing::IsSingleDiagnostic;
 using testing::ElementsAre;
 using testing::ElementsAre;
 
 
 struct FakeDiagnosticConverter : DiagnosticConverter<int> {
 struct FakeDiagnosticConverter : DiagnosticConverter<int> {
-  auto ConvertLocation(int n) const -> DiagnosticLocation override {
+  auto ConvertLocation(int n, ContextFnT /*context_fn*/) const
+      -> DiagnosticLocation override {
     return {.line_number = 1, .column_number = n};
     return {.line_number = 1, .column_number = n};
   }
   }
 };
 };

+ 3 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -127,6 +127,9 @@ CARBON_DIAGNOSTIC_KIND(ExpectedAliasInitializer)
 
 
 CARBON_DIAGNOSTIC_KIND(SemanticsTodo)
 CARBON_DIAGNOSTIC_KIND(SemanticsTodo)
 
 
+// Location context.
+CARBON_DIAGNOSTIC_KIND(InImport)
+
 // Package/import checking diagnostics.
 // Package/import checking diagnostics.
 CARBON_DIAGNOSTIC_KIND(IncorrectExtension)
 CARBON_DIAGNOSTIC_KIND(IncorrectExtension)
 CARBON_DIAGNOSTIC_KIND(IncorrectExtensionImplNote)
 CARBON_DIAGNOSTIC_KIND(IncorrectExtensionImplNote)

+ 4 - 2
toolchain/diagnostics/null_diagnostics.h

@@ -11,8 +11,10 @@ namespace Carbon {
 
 
 template <typename LocationT>
 template <typename LocationT>
 inline auto NullDiagnosticConverter() -> DiagnosticConverter<LocationT>& {
 inline auto NullDiagnosticConverter() -> DiagnosticConverter<LocationT>& {
-  struct Converter : DiagnosticConverter<LocationT> {
-    auto ConvertLocation(LocationT /*loc*/) const
+  struct Converter : public DiagnosticConverter<LocationT> {
+    auto ConvertLocation(
+        LocationT /*loc*/,
+        DiagnosticConverter<LocationT>::ContextFnT /*context_fn*/) const
         -> DiagnosticLocation override {
         -> DiagnosticLocation override {
       return {};
       return {};
     }
     }

+ 1 - 1
toolchain/diagnostics/sorting_diagnostic_consumer_test.cpp

@@ -20,7 +20,7 @@ using ::testing::InSequence;
 CARBON_DIAGNOSTIC(TestDiagnostic, Error, "{0}", llvm::StringLiteral);
 CARBON_DIAGNOSTIC(TestDiagnostic, Error, "{0}", llvm::StringLiteral);
 
 
 struct FakeDiagnosticConverter : DiagnosticConverter<DiagnosticLocation> {
 struct FakeDiagnosticConverter : DiagnosticConverter<DiagnosticLocation> {
-  auto ConvertLocation(DiagnosticLocation loc) const
+  auto ConvertLocation(DiagnosticLocation loc, ContextFnT /*context_fn*/) const
       -> DiagnosticLocation override {
       -> DiagnosticLocation override {
     return loc;
     return loc;
   }
   }

+ 2 - 1
toolchain/lex/test_helpers.h

@@ -24,7 +24,8 @@ class SingleTokenDiagnosticConverter : public DiagnosticConverter<const char*> {
   explicit SingleTokenDiagnosticConverter(llvm::StringRef token)
   explicit SingleTokenDiagnosticConverter(llvm::StringRef token)
       : token_(token) {}
       : token_(token) {}
 
 
-  auto ConvertLocation(const char* pos) const -> DiagnosticLocation override {
+  auto ConvertLocation(const char* pos, ContextFnT /*context_fn*/) const
+      -> DiagnosticLocation override {
     CARBON_CHECK(StringRefContainsPointer(token_, pos))
     CARBON_CHECK(StringRefContainsPointer(token_, pos))
         << "invalid diagnostic location";
         << "invalid diagnostic location";
     llvm::StringRef prefix = token_.take_front(pos - token_.begin());
     llvm::StringRef prefix = token_.take_front(pos - token_.begin());

+ 4 - 3
toolchain/lex/tokenized_buffer.cpp

@@ -346,7 +346,7 @@ auto TokenIterator::Print(llvm::raw_ostream& output) const -> void {
 }
 }
 
 
 auto TokenizedBuffer::SourceBufferDiagnosticConverter::ConvertLocation(
 auto TokenizedBuffer::SourceBufferDiagnosticConverter::ConvertLocation(
-    const char* loc) const -> DiagnosticLocation {
+    const char* loc, ContextFnT /*context_fn*/) const -> DiagnosticLocation {
   CARBON_CHECK(StringRefContainsPointer(buffer_->source_->text(), loc))
   CARBON_CHECK(StringRefContainsPointer(buffer_->source_->text(), loc))
       << "location not within buffer";
       << "location not within buffer";
   int64_t offset = loc - buffer_->source_->text().begin();
   int64_t offset = loc - buffer_->source_->text().begin();
@@ -390,7 +390,8 @@ auto TokenizedBuffer::SourceBufferDiagnosticConverter::ConvertLocation(
           .column_number = column_number + 1};
           .column_number = column_number + 1};
 }
 }
 
 
-auto TokenDiagnosticConverter::ConvertLocation(TokenIndex token) const
+auto TokenDiagnosticConverter::ConvertLocation(TokenIndex token,
+                                               ContextFnT context_fn) const
     -> DiagnosticLocation {
     -> DiagnosticLocation {
   // Map the token location into a position within the source buffer.
   // Map the token location into a position within the source buffer.
   const auto& token_info = buffer_->GetTokenInfo(token);
   const auto& token_info = buffer_->GetTokenInfo(token);
@@ -403,7 +404,7 @@ auto TokenDiagnosticConverter::ConvertLocation(TokenIndex token) const
   // is a recovery token that doesn't correspond to the original source?
   // is a recovery token that doesn't correspond to the original source?
   DiagnosticLocation loc =
   DiagnosticLocation loc =
       TokenizedBuffer::SourceBufferDiagnosticConverter(buffer_).ConvertLocation(
       TokenizedBuffer::SourceBufferDiagnosticConverter(buffer_).ConvertLocation(
-          token_start);
+          token_start, context_fn);
   loc.length = buffer_->GetTokenText(token).size();
   loc.length = buffer_->GetTokenText(token).size();
   return loc;
   return loc;
 }
 }

+ 4 - 2
toolchain/lex/tokenized_buffer.h

@@ -119,7 +119,8 @@ class TokenDiagnosticConverter : public DiagnosticConverter<TokenIndex> {
       : buffer_(buffer) {}
       : buffer_(buffer) {}
 
 
   // Map the given token into a diagnostic location.
   // Map the given token into a diagnostic location.
-  auto ConvertLocation(TokenIndex token) const -> DiagnosticLocation override;
+  auto ConvertLocation(TokenIndex token, ContextFnT context_fn) const
+      -> DiagnosticLocation override;
 
 
  private:
  private:
   const TokenizedBuffer* buffer_;
   const TokenizedBuffer* buffer_;
@@ -255,7 +256,8 @@ class TokenizedBuffer : public Printable<TokenizedBuffer> {
 
 
     // Map the given position within the source buffer into a diagnostic
     // Map the given position within the source buffer into a diagnostic
     // location.
     // location.
-    auto ConvertLocation(const char* loc) const -> DiagnosticLocation override;
+    auto ConvertLocation(const char* loc, ContextFnT context_fn) const
+        -> DiagnosticLocation override;
 
 
    private:
    private:
     const TokenizedBuffer* buffer_;
     const TokenizedBuffer* buffer_;

+ 5 - 4
toolchain/parse/tree_node_diagnostic_converter.h

@@ -44,7 +44,7 @@ class NodeLocationConverter : public DiagnosticConverter<NodeLocation> {
         parse_tree_(parse_tree) {}
         parse_tree_(parse_tree) {}
 
 
   // Map the given token into a diagnostic location.
   // Map the given token into a diagnostic location.
-  auto ConvertLocation(NodeLocation node_location) const
+  auto ConvertLocation(NodeLocation node_location, ContextFnT context_fn) const
       -> DiagnosticLocation override {
       -> DiagnosticLocation override {
     // Support the invalid token as a way to emit only the filename, when there
     // Support the invalid token as a way to emit only the filename, when there
     // is no line association.
     // is no line association.
@@ -54,7 +54,7 @@ class NodeLocationConverter : public DiagnosticConverter<NodeLocation> {
 
 
     if (node_location.token_only()) {
     if (node_location.token_only()) {
       return token_converter_.ConvertLocation(
       return token_converter_.ConvertLocation(
-          parse_tree_->node_token(node_location.node_id()));
+          parse_tree_->node_token(node_location.node_id()), context_fn);
     }
     }
 
 
     // Construct a location that encompasses all tokens that descend from this
     // Construct a location that encompasses all tokens that descend from this
@@ -74,11 +74,12 @@ class NodeLocationConverter : public DiagnosticConverter<NodeLocation> {
       }
       }
     }
     }
     DiagnosticLocation start_loc =
     DiagnosticLocation start_loc =
-        token_converter_.ConvertLocation(start_token);
+        token_converter_.ConvertLocation(start_token, context_fn);
     if (start_token == end_token) {
     if (start_token == end_token) {
       return start_loc;
       return start_loc;
     }
     }
-    DiagnosticLocation end_loc = token_converter_.ConvertLocation(end_token);
+    DiagnosticLocation end_loc =
+        token_converter_.ConvertLocation(end_token, context_fn);
     // For multiline locations we simply return the rest of the line for now
     // For multiline locations we simply return the rest of the line for now
     // since true multiline locations are not yet supported.
     // since true multiline locations are not yet supported.
     if (start_loc.line_number != end_loc.line_number) {
     if (start_loc.line_number != end_loc.line_number) {

+ 9 - 6
toolchain/sem_ir/file.cpp

@@ -9,6 +9,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/SmallVector.h"
 #include "toolchain/base/value_store.h"
 #include "toolchain/base/value_store.h"
 #include "toolchain/base/yaml.h"
 #include "toolchain/base/yaml.h"
+#include "toolchain/parse/node_ids.h"
 #include "toolchain/sem_ir/builtin_kind.h"
 #include "toolchain/sem_ir/builtin_kind.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/inst.h"
@@ -69,7 +70,8 @@ File::File(SharedValueStores& value_stores, std::string filename,
       inst_blocks_(allocator_),
       inst_blocks_(allocator_),
       constants_(*this, allocator_) {
       constants_(*this, allocator_) {
   CARBON_CHECK(builtins != nullptr);
   CARBON_CHECK(builtins != nullptr);
-  auto builtins_id = import_irs_.Add(builtins);
+  auto builtins_id =
+      import_irs_.Add({.node_id = Parse::NodeId::Invalid, .sem_ir = builtins});
   CARBON_CHECK(builtins_id == ImportIRId::Builtins)
   CARBON_CHECK(builtins_id == ImportIRId::Builtins)
       << "Builtins must be the first IR";
       << "Builtins must be the first IR";
 
 
@@ -105,8 +107,8 @@ File::File(SharedValueStores& value_stores, std::string filename,
         for (auto [i, inst] : llvm::enumerate(builtins->insts_.array_ref())) {
         for (auto [i, inst] : llvm::enumerate(builtins->insts_.array_ref())) {
           // We can reuse the type_id from the builtin IR's inst because they're
           // We can reuse the type_id from the builtin IR's inst because they're
           // special-cased values.
           // special-cased values.
-          insts_.AddInNoBlock({ImportRefUsed{
-              inst.type_id(), ImportIRId::Builtins, SemIR::InstId(i)}});
+          insts_.AddInNoBlock(ImportRefUsed{
+              inst.type_id(), ImportIRId::Builtins, SemIR::InstId(i)});
         }
         }
       }) {}
       }) {}
 
 
@@ -374,8 +376,9 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir,
       }
       }
       case ImportRefUsed::Kind: {
       case ImportRefUsed::Kind: {
         auto import_ref = inst.As<ImportRefUsed>();
         auto import_ref = inst.As<ImportRefUsed>();
-        steps.push_back({.sem_ir = *sem_ir.import_irs().Get(import_ref.ir_id),
-                         .inst_id = import_ref.inst_id});
+        steps.push_back(
+            {.sem_ir = *sem_ir.import_irs().Get(import_ref.ir_id).sem_ir,
+             .inst_id = import_ref.inst_id});
         break;
         break;
       }
       }
       case InterfaceType::Kind: {
       case InterfaceType::Kind: {
@@ -556,7 +559,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
 
 
       case ImportRefUsed::Kind: {
       case ImportRefUsed::Kind: {
         auto import_ref = inst.As<ImportRefUsed>();
         auto import_ref = inst.As<ImportRefUsed>();
-        ir = ir->import_irs().Get(import_ref.ir_id);
+        ir = ir->import_irs().Get(import_ref.ir_id).sem_ir;
         inst_id = import_ref.inst_id;
         inst_id = import_ref.inst_id;
         continue;
         continue;
       }
       }

+ 11 - 0
toolchain/sem_ir/file.h

@@ -38,6 +38,17 @@ struct BindNameInfo : public Printable<BindNameInfo> {
   NameScopeId enclosing_scope_id;
   NameScopeId enclosing_scope_id;
 };
 };
 
 
+class File;
+
+struct ImportIR : public Printable<ImportIR> {
+  auto Print(llvm::raw_ostream& out) const -> void { out << node_id; }
+
+  // The node ID for the import.
+  Parse::ImportDirectiveId node_id;
+  // The imported IR.
+  const File* sem_ir;
+};
+
 // Provides semantic analysis on a Parse::Tree.
 // Provides semantic analysis on a Parse::Tree.
 class File : public Printable<File> {
 class File : public Printable<File> {
  public:
  public:

+ 2 - 1
toolchain/sem_ir/ids.h

@@ -20,6 +20,7 @@ class Inst;
 struct BindNameInfo;
 struct BindNameInfo;
 struct Class;
 struct Class;
 struct Function;
 struct Function;
+struct ImportIR;
 struct Interface;
 struct Interface;
 struct Impl;
 struct Impl;
 struct NameScope;
 struct NameScope;
@@ -248,7 +249,7 @@ constexpr ImplId ImplId::Invalid = ImplId(InvalidIndex);
 
 
 // The ID of an imported IR.
 // The ID of an imported IR.
 struct ImportIRId : public IdBase, public Printable<ImportIRId> {
 struct ImportIRId : public IdBase, public Printable<ImportIRId> {
-  using ValueType = const File*;
+  using ValueType = ImportIR;
 
 
   static const ImportIRId Builtins;
   static const ImportIRId Builtins;
   using IdBase::IdBase;
   using IdBase::IdBase;

+ 2 - 1
toolchain/source/source_buffer.cpp

@@ -12,7 +12,8 @@ namespace Carbon {
 
 
 namespace {
 namespace {
 struct FilenameConverter : DiagnosticConverter<llvm::StringRef> {
 struct FilenameConverter : DiagnosticConverter<llvm::StringRef> {
-  auto ConvertLocation(llvm::StringRef filename) const
+  auto ConvertLocation(llvm::StringRef filename,
+                       ContextFnT /*context_fn*/) const
       -> DiagnosticLocation override {
       -> DiagnosticLocation override {
     return {.filename = filename};
     return {.filename = filename};
   }
   }