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

Trying to find more specific homes for shared diagnostic function calls.
This commit is contained in:
Jon Ross-Perkins
2025-02-13 18:08:11 +00:00
committed by GitHub
parent 6dda094928
commit e70f9cd71d
14 changed files with 130 additions and 121 deletions
-70
View File
@@ -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());
-22
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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_