diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index 4e2b31358072..2206f81655c4 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -25,6 +25,28 @@ namespace Carbon::Check { auto Handle##Name(Context& context, Parse::Name##Id parse_node) -> bool; #include "toolchain/parse/node_kind.def" +// Handles the transformation of a SemIRLocation to a DiagnosticLocation. +class SemIRLocationTranslator + : public DiagnosticLocationTranslator { + public: + explicit SemIRLocationTranslator( + Parse::NodeLocationTranslator* node_translator, const SemIR::File* sem_ir) + : node_translator_(node_translator), sem_ir_(sem_ir) {} + + auto GetLocation(SemIRLocation loc) -> DiagnosticLocation override { + if (loc.is_inst_id) { + return node_translator_->GetLocation( + sem_ir_->insts().GetParseNode(loc.inst_id)); + } else { + return node_translator_->GetLocation(loc.node_location); + } + } + + private: + Parse::NodeLocationTranslator* node_translator_; + const SemIR::File* sem_ir_; +}; + struct UnitInfo { // A given import within the file, with its destination. struct Import { @@ -57,7 +79,6 @@ struct UnitInfo { Unit* unit; // Emitter information. - // TODO: Augment the translator to translate InstIds for locations. Parse::NodeLocationTranslator translator; ErrorTrackingDiagnosticConsumer err_tracker; DiagnosticEmitter emitter; @@ -179,8 +200,10 @@ static auto CheckParseTree(const SemIR::File& builtin_ir, UnitInfo& unit_info, // For ease-of-access. SemIR::File& sem_ir = **unit_info.unit->sem_ir; - Context context(*unit_info.unit->tokens, unit_info.emitter, - *unit_info.unit->parse_tree, sem_ir, vlog_stream); + SemIRLocationTranslator translator(&unit_info.translator, &sem_ir); + Context::DiagnosticEmitter emitter(translator, unit_info.err_tracker); + Context context(*unit_info.unit->tokens, emitter, *unit_info.unit->parse_tree, + sem_ir, vlog_stream); PrettyStackTraceFunction context_dumper( [&](llvm::raw_ostream& output) { context.PrintForStackDump(output); }); diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 19cbfa86233e..233007b83dc6 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -104,7 +104,7 @@ auto Context::DiagnoseDuplicateName(Parse::NodeId parse_node, CARBON_DIAGNOSTIC(NameDeclPrevious, Note, "Name is previously declared here."); emitter_->Build(parse_node, NameDeclDuplicate) - .Note(insts().GetParseNode(prev_def_id), NameDeclPrevious) + .Note(prev_def_id, NameDeclPrevious) .Emit(); } @@ -123,11 +123,9 @@ auto Context::NoteIncompleteClass(SemIR::ClassId class_id, const auto& class_info = classes().Get(class_id); CARBON_CHECK(!class_info.is_defined()) << "Class is not incomplete"; if (class_info.definition_id.is_valid()) { - builder.Note(insts().GetParseNode(class_info.definition_id), - ClassIncompleteWithinDefinition); + builder.Note(class_info.definition_id, ClassIncompleteWithinDefinition); } else { - builder.Note(insts().GetParseNode(class_info.decl_id), - ClassForwardDeclaredHere); + builder.Note(class_info.decl_id, ClassForwardDeclaredHere); } } diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 7a28d4751295..c22a3f83f754 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -23,10 +23,31 @@ namespace Carbon::Check { +// Diagnostic locations produced by checking may be either a parse node +// directly, or an inst ID which is later translated to a parse node. +struct SemIRLocation { + // NOLINTNEXTLINE(google-explicit-constructor) + SemIRLocation(SemIR::InstId inst_id) : inst_id(inst_id), is_inst_id(true) {} + + // NOLINTNEXTLINE(google-explicit-constructor) + SemIRLocation(Parse::NodeLocation node_location) + : node_location(node_location), is_inst_id(false) {} + // NOLINTNEXTLINE(google-explicit-constructor) + SemIRLocation(Parse::NodeId node_id) + : SemIRLocation(Parse::NodeLocation(node_id)) {} + + union { + SemIR::InstId inst_id; + Parse::NodeLocation node_location; + }; + + bool is_inst_id; +}; + // Context and shared functionality for semantics handlers. class Context { public: - using DiagnosticEmitter = Carbon::DiagnosticEmitter; + using DiagnosticEmitter = Carbon::DiagnosticEmitter; using DiagnosticBuilder = DiagnosticEmitter::DiagnosticBuilder; // A scope in which `break` and `continue` can be used. diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index 63595099f1f6..9e4237a83659 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -555,8 +555,7 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type, "Cannot construct instance of abstract class. " "Consider using `partial {0}` instead.", std::string); - context.emitter().Emit(context.insts().GetParseNode(value_id), - ConstructionOfAbstractClass, + context.emitter().Emit(value_id, ConstructionOfAbstractClass, context.sem_ir().StringifyType(target.type_id)); return SemIR::InstId::BuiltinError; } @@ -864,8 +863,7 @@ static auto PerformCopy(Context& context, SemIR::InstId expr_id) // copyable, or how to perform the copy. CARBON_DIAGNOSTIC(CopyOfUncopyableType, Error, "Cannot copy value of type `{0}`.", std::string); - context.emitter().Emit(context.insts().GetParseNode(expr_id), - CopyOfUncopyableType, + context.emitter().Emit(expr_id, CopyOfUncopyableType, context.sem_ir().StringifyType(type_id)); return SemIR::InstId::BuiltinError; } @@ -888,8 +886,7 @@ auto Convert(Context& context, Parse::NodeId parse_node, SemIR::InstId expr_id, // namespace names, and allow use of functions as values. CARBON_DIAGNOSTIC(UseOfNonExprAsValue, Error, "Expression cannot be used as a value."); - context.emitter().Emit(sem_ir.insts().GetParseNode(expr_id), - UseOfNonExprAsValue); + context.emitter().Emit(expr_id, UseOfNonExprAsValue); return SemIR::InstId::BuiltinError; } @@ -1100,8 +1097,7 @@ static auto ConvertSelf(Context& context, Parse::NodeId call_parse_node, InCallToFunctionSelf, Note, "Initializing `{0}` parameter of method declared here.", llvm::StringLiteral); - builder.Note(context.insts().GetParseNode(self_param_id), - InCallToFunctionSelf, + builder.Note(self_param_id, InCallToFunctionSelf, addr_pattern ? llvm::StringLiteral("addr self") : llvm::StringLiteral("self")); }); diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index 2e70b141e606..7c53dea5aff1 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -73,8 +73,7 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId parse_node) "Previously declared here."); context.emitter() .Build(parse_node, ClassRedeclarationDifferentIntroducer) - .Note(context.insts().GetParseNode(existing_id), - ClassRedeclarationDifferentIntroducerPrevious) + .Note(existing_id, ClassRedeclarationDifferentIntroducerPrevious) .Emit(); } @@ -134,8 +133,7 @@ auto HandleClassDefinitionStart(Context& context, context.emitter() .Build(parse_node, ClassRedefinition, context.names().GetFormatted(class_info.name_id).str()) - .Note(context.insts().GetParseNode(class_info.definition_id), - ClassPreviousDefinition) + .Note(class_info.definition_id, ClassPreviousDefinition) .Emit(); } else { class_info.definition_id = class_decl_id; @@ -280,7 +278,7 @@ auto HandleBaseDecl(Context& context, Parse::BaseDeclId parse_node) -> bool { "Previous `base` declaration is here."); context.emitter() .Build(parse_node, BaseRepeated) - .Note(context.insts().GetParseNode(class_info.base_id), BasePrevious) + .Note(class_info.base_id, BasePrevious) .Emit(); return true; } diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 87565567e22b..60ab5e5828f3 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -224,8 +224,7 @@ auto HandleFunctionDefinitionStart(Context& context, context.emitter() .Build(parse_node, FunctionRedefinition, context.names().GetFormatted(function.name_id).str()) - .Note(context.insts().GetParseNode(function.definition_id), - FunctionPreviousDefinition) + .Note(function.definition_id, FunctionPreviousDefinition) .Emit(); } else { function.definition_id = decl_id; diff --git a/toolchain/check/handle_interface.cpp b/toolchain/check/handle_interface.cpp index 3a015ca70f6d..0ff351dc044f 100644 --- a/toolchain/check/handle_interface.cpp +++ b/toolchain/check/handle_interface.cpp @@ -109,8 +109,7 @@ auto HandleInterfaceDefinitionStart( context.emitter() .Build(parse_node, InterfaceRedefinition, context.names().GetFormatted(interface_info.name_id).str()) - .Note(context.insts().GetParseNode(interface_info.definition_id), - InterfacePreviousDefinition) + .Note(interface_info.definition_id, InterfacePreviousDefinition) .Emit(); } else { interface_info.definition_id = interface_decl_id; diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index 3f3f83e1842c..bc0173b6ee0f 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -26,8 +26,7 @@ static auto GetAsNameScope(Context& context, SemIR::InstId base_id) "Member access into incomplete class `{0}`.", std::string); auto builder = - context.emitter().Build(context.insts().GetParseNode(base_id), - QualifiedExprInIncompleteClassScope, + context.emitter().Build(base_id, QualifiedExprInIncompleteClassScope, context.sem_ir().StringifyTypeExpr(base_id)); context.NoteIncompleteClass(base_as_class->class_id, builder); builder.Emit(); @@ -119,7 +118,7 @@ auto HandleMemberAccessExpr(Context& context, "Member access into object of incomplete type `{0}`.", std::string); return context.emitter().Build( - context.insts().GetParseNode(base_id), IncompleteTypeInMemberAccess, + base_id, IncompleteTypeInMemberAccess, context.sem_ir().StringifyType(base_type_id)); })) { context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError); diff --git a/toolchain/check/handle_struct.cpp b/toolchain/check/handle_struct.cpp index 89351c294a00..0b87700b9ab7 100644 --- a/toolchain/check/handle_struct.cpp +++ b/toolchain/check/handle_struct.cpp @@ -79,10 +79,9 @@ static auto DiagnoseDuplicateNames(Context& context, CARBON_DIAGNOSTIC(StructNamePrevious, Note, "Field with the same name here."); context.emitter() - .Build(context.insts().GetParseNode(field_inst_id), - StructNameDuplicate, construct.str(), + .Build(field_inst_id, StructNameDuplicate, construct.str(), sem_ir.names().GetFormatted(field_inst.name_id).str()) - .Note(context.insts().GetParseNode(it->second), StructNamePrevious) + .Note(it->second, StructNamePrevious) .Emit(); return true; } diff --git a/toolchain/check/return.cpp b/toolchain/check/return.cpp index 123d2af328cf..2674e1b0c92c 100644 --- a/toolchain/check/return.cpp +++ b/toolchain/check/return.cpp @@ -29,32 +29,29 @@ static auto GetCurrentReturnedVar(Context& context) -> SemIR::InstId { } // Produces a note that the given function has no explicit return type. -static auto NoteNoReturnTypeProvided(Context& context, - Context::DiagnosticBuilder& diag, +static auto NoteNoReturnTypeProvided(Context::DiagnosticBuilder& diag, const SemIR::Function& function) { CARBON_DIAGNOSTIC(ReturnTypeOmittedNote, Note, "There was no return type provided."); - diag.Note(context.insts().GetParseNode(function.decl_id), - ReturnTypeOmittedNote); + diag.Note(function.decl_id, ReturnTypeOmittedNote); } // Produces a note describing the return type of the given function. static auto NoteReturnType(Context& context, Context::DiagnosticBuilder& diag, const SemIR::Function& function) { - // TODO: This is the location of the `fn` keyword. Find the location of the - // return type. - auto type_parse_node = context.insts().GetParseNode(function.decl_id); CARBON_DIAGNOSTIC(ReturnTypeHereNote, Note, "Return type of function is `{0}`.", std::string); - diag.Note(type_parse_node, ReturnTypeHereNote, + // TODO: This is using the location of the `fn` keyword. Find the location of + // the return type. + diag.Note(function.decl_id, ReturnTypeHereNote, context.sem_ir().StringifyType(function.return_type_id)); } // Produces a note pointing at the currently in scope `returned var`. -static auto NoteReturnedVar(Context& context, Context::DiagnosticBuilder& diag, +static auto NoteReturnedVar(Context::DiagnosticBuilder& diag, SemIR::InstId returned_var_id) { CARBON_DIAGNOSTIC(ReturnedVarHere, Note, "`returned var` was declared here."); - diag.Note(context.insts().GetParseNode(returned_var_id), ReturnedVarHere); + diag.Note(returned_var_id, ReturnedVarHere); } auto CheckReturnedVar(Context& context, Parse::NodeId returned_node, @@ -68,7 +65,7 @@ auto CheckReturnedVar(Context& context, Parse::NodeId returned_node, "Cannot declare a `returned var` in this function."); auto diag = context.emitter().Build(returned_node, ReturnedVarWithNoReturnType); - NoteNoReturnTypeProvided(context, diag, function); + NoteNoReturnTypeProvided(diag, function); diag.Emit(); return SemIR::InstId::BuiltinError; } @@ -101,9 +98,8 @@ auto RegisterReturnedVar(Context& context, SemIR::InstId bind_id) -> void { CARBON_DIAGNOSTIC(ReturnedVarShadowed, Error, "Cannot declare a `returned var` in the scope of " "another `returned var`."); - auto diag = context.emitter().Build(context.insts().GetParseNode(bind_id), - ReturnedVarShadowed); - NoteReturnedVar(context, diag, existing_id); + auto diag = context.emitter().Build(bind_id, ReturnedVarShadowed); + NoteReturnedVar(diag, existing_id); diag.Emit(); } } @@ -134,7 +130,7 @@ auto BuildReturnWithExpr(Context& context, Parse::ReturnStatementId parse_node, "No return expression should be provided in this context."); auto diag = context.emitter().Build(parse_node, ReturnStatementDisallowExpr); - NoteNoReturnTypeProvided(context, diag, function); + NoteNoReturnTypeProvided(diag, function); diag.Emit(); expr_id = SemIR::InstId::BuiltinError; } else if (returned_var_id.is_valid()) { @@ -142,7 +138,7 @@ auto BuildReturnWithExpr(Context& context, Parse::ReturnStatementId parse_node, ReturnExprWithReturnedVar, Error, "Can only `return var;` in the scope of a `returned var`."); auto diag = context.emitter().Build(parse_node, ReturnExprWithReturnedVar); - NoteReturnedVar(context, diag, returned_var_id); + NoteReturnedVar(diag, returned_var_id); diag.Emit(); expr_id = SemIR::InstId::BuiltinError; } else if (function.return_slot_id.is_valid()) {