Skip to content

Commit 64f9ce9

Browse files
committed
Implement basic bool and int formatting for diagnostics
1 parent 3c58fb7 commit 64f9ce9

File tree

12 files changed

+386
-136
lines changed

12 files changed

+386
-136
lines changed

toolchain/check/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ cc_library(
7474
"//toolchain/check:generic_region_stack",
7575
"//toolchain/check:scope_stack",
7676
"//toolchain/diagnostics:diagnostic_emitter",
77+
"//toolchain/diagnostics:format_providers",
7778
"//toolchain/lex:token_kind",
7879
"//toolchain/lex:tokenized_buffer",
7980
"//toolchain/parse:node_kind",
@@ -116,6 +117,7 @@ cc_library(
116117
"//toolchain/base:pretty_stack_trace_function",
117118
"//toolchain/base:value_store",
118119
"//toolchain/diagnostics:diagnostic_emitter",
120+
"//toolchain/diagnostics:format_providers",
119121
"//toolchain/lex:token_index",
120122
"//toolchain/lex:token_kind",
121123
"//toolchain/lex:tokenized_buffer",

toolchain/check/convert.cpp

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "toolchain/check/context.h"
1515
#include "toolchain/check/operator.h"
1616
#include "toolchain/check/pattern_match.h"
17+
#include "toolchain/diagnostics/format_providers.h"
1718
#include "toolchain/sem_ir/copy_on_write_block.h"
1819
#include "toolchain/sem_ir/file.h"
1920
#include "toolchain/sem_ir/generic.h"
@@ -381,8 +382,13 @@ static auto ConvertStructToStructOrClass(Context& context,
381382
SemIR::StructType src_type,
382383
SemIR::StructType dest_type,
383384
SemIR::InstId value_id,
384-
ConversionTarget target, bool is_class)
385+
ConversionTarget target)
385386
-> SemIR::InstId {
387+
static_assert(std::is_same_v<SemIR::ClassElementAccess, TargetAccessInstT> ||
388+
std::is_same_v<SemIR::StructAccess, TargetAccessInstT>);
389+
constexpr bool ToClass =
390+
std::is_same_v<SemIR::ClassElementAccess, TargetAccessInstT>;
391+
386392
auto& sem_ir = context.sem_ir();
387393
auto src_elem_fields = sem_ir.inst_blocks().Get(src_type.fields_id);
388394
auto dest_elem_fields = sem_ir.inst_blocks().Get(dest_type.fields_id);
@@ -406,14 +412,14 @@ static auto ConvertStructToStructOrClass(Context& context,
406412
// TODO: If not, include the name of the first source field that doesn't
407413
// exist in the destination or vice versa in the diagnostic.
408414
if (src_elem_fields.size() != dest_elem_fields.size()) {
409-
CARBON_DIAGNOSTIC(StructInitElementCountMismatch, Error,
410-
"cannot initialize {0} with {1} field(s) from struct "
411-
"with {2} field(s).",
412-
llvm::StringLiteral, size_t, size_t);
413-
context.emitter().Emit(
414-
value_loc_id, StructInitElementCountMismatch,
415-
is_class ? llvm::StringLiteral("class") : llvm::StringLiteral("struct"),
416-
dest_elem_fields.size(), src_elem_fields.size());
415+
CARBON_DIAGNOSTIC(
416+
StructInitElementCountMismatch, Error,
417+
"cannot initialize {0:class|struct} with {1} field(s) from struct "
418+
"with {2} field(s).",
419+
FormatBool, size_t, size_t);
420+
context.emitter().Emit(value_loc_id, StructInitElementCountMismatch,
421+
{.value = ToClass}, dest_elem_fields.size(),
422+
src_elem_fields.size());
417423
return SemIR::InstId::BuiltinError;
418424
}
419425

@@ -493,7 +499,7 @@ static auto ConvertStructToStructOrClass(Context& context,
493499
new_block.Set(i, init_id);
494500
}
495501

496-
if (is_class) {
502+
if (ToClass) {
497503
target.init_block->InsertHere();
498504
CARBON_CHECK(is_init,
499505
"Converting directly to a class value is not supported");
@@ -522,7 +528,7 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
522528
SemIR::InstId value_id,
523529
ConversionTarget target) -> SemIR::InstId {
524530
return ConvertStructToStructOrClass<SemIR::StructAccess>(
525-
context, src_type, dest_type, value_id, target, /*is_class=*/false);
531+
context, src_type, dest_type, value_id, target);
526532
}
527533

528534
// Performs a conversion from a struct to a class type. This function only
@@ -554,7 +560,7 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type,
554560
}
555561

556562
auto result_id = ConvertStructToStructOrClass<SemIR::ClassElementAccess>(
557-
context, src_type, dest_struct_type, value_id, target, /*is_class=*/true);
563+
context, src_type, dest_struct_type, value_id, target);
558564

559565
if (need_temporary) {
560566
target_block.InsertHere();
@@ -1154,13 +1160,12 @@ static auto ConvertSelf(Context& context, SemIR::LocId call_loc_id,
11541160
bool addr_pattern = context.insts().Is<SemIR::AddrPattern>(self_param_id);
11551161
DiagnosticAnnotationScope annotate_diagnostics(
11561162
&context.emitter(), [&](auto& builder) {
1157-
CARBON_DIAGNOSTIC(
1158-
InCallToFunctionSelf, Note,
1159-
"initializing `{0}` parameter of method declared here",
1160-
llvm::StringLiteral);
1163+
CARBON_DIAGNOSTIC(InCallToFunctionSelf, Note,
1164+
"initializing `{0:addr self|self}` parameter of "
1165+
"method declared here",
1166+
FormatBool);
11611167
builder.Note(self_param_id, InCallToFunctionSelf,
1162-
addr_pattern ? llvm::StringLiteral("addr self")
1163-
: llvm::StringLiteral("self"));
1168+
{.value = addr_pattern});
11641169
});
11651170

11661171
return CallerPatternMatch(context, callee_specific_id, self_param_id,

toolchain/check/eval.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "toolchain/check/diagnostic_helpers.h"
99
#include "toolchain/check/generic.h"
1010
#include "toolchain/diagnostics/diagnostic_emitter.h"
11+
#include "toolchain/diagnostics/format_providers.h"
1112
#include "toolchain/sem_ir/builtin_function_kind.h"
1213
#include "toolchain/sem_ir/function.h"
1314
#include "toolchain/sem_ir/generic.h"
@@ -745,18 +746,17 @@ static auto PerformBuiltinBinaryIntOp(Context& context, SemIRLoc loc,
745746
// Bit shift.
746747
case SemIR::BuiltinFunctionKind::IntLeftShift:
747748
case SemIR::BuiltinFunctionKind::IntRightShift:
748-
op_str = (builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift)
749-
? llvm::StringLiteral("<<")
750-
: llvm::StringLiteral(">>");
751749
if (rhs_val.uge(lhs_val.getBitWidth()) ||
752750
(rhs_val.isNegative() && context.types().IsSignedInt(rhs.type_id))) {
753-
CARBON_DIAGNOSTIC(CompileTimeShiftOutOfRange, Error,
754-
"shift distance not in range [0, {0}) in {1} {2} {3}",
755-
unsigned, TypedInt, llvm::StringLiteral, TypedInt);
756-
context.emitter().Emit(loc, CompileTimeShiftOutOfRange,
757-
lhs_val.getBitWidth(),
758-
{.type = lhs.type_id, .value = lhs_val}, op_str,
759-
{.type = rhs.type_id, .value = rhs_val});
751+
CARBON_DIAGNOSTIC(
752+
CompileTimeShiftOutOfRange, Error,
753+
"shift distance not in range [0, {0}) in {1} {2:<<|>>} {3}",
754+
unsigned, TypedInt, FormatBool, TypedInt);
755+
context.emitter().Emit(
756+
loc, CompileTimeShiftOutOfRange, lhs_val.getBitWidth(),
757+
{.type = lhs.type_id, .value = lhs_val},
758+
{.value = builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift},
759+
{.type = rhs.type_id, .value = rhs_val});
760760
// TODO: Is it useful to recover by returning 0 or -1?
761761
return SemIR::ConstantId::Error;
762762
}

toolchain/check/handle_binding_pattern.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "toolchain/check/convert.h"
77
#include "toolchain/check/handle.h"
88
#include "toolchain/check/return.h"
9+
#include "toolchain/diagnostics/format_providers.h"
910
#include "toolchain/sem_ir/ids.h"
1011
#include "toolchain/sem_ir/inst.h"
1112

@@ -104,23 +105,19 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
104105
cast_type_id,
105106
[&] {
106107
CARBON_DIAGNOSTIC(IncompleteTypeInVarDecl, Error,
107-
"{0} has incomplete type {1}",
108-
llvm::StringLiteral, SemIR::TypeId);
108+
"{0:field|variable} has incomplete type {1}",
109+
FormatBool, SemIR::TypeId);
109110
return context.emitter().Build(
110111
type_node, IncompleteTypeInVarDecl,
111-
parent_class_decl ? llvm::StringLiteral("field")
112-
: llvm::StringLiteral("variable"),
113-
cast_type_id);
112+
{.value = parent_class_decl.has_value()}, cast_type_id);
114113
},
115114
[&] {
116115
CARBON_DIAGNOSTIC(AbstractTypeInVarDecl, Error,
117-
"{0} has abstract type {1}", llvm::StringLiteral,
118-
SemIR::TypeId);
116+
"{0:field|variable} has abstract type {1}",
117+
FormatBool, SemIR::TypeId);
119118
return context.emitter().Build(
120119
type_node, AbstractTypeInVarDecl,
121-
parent_class_decl ? llvm::StringLiteral("field")
122-
: llvm::StringLiteral("variable"),
123-
cast_type_id);
120+
{.value = parent_class_decl.has_value()}, cast_type_id);
124121
});
125122
if (parent_class_decl) {
126123
CARBON_CHECK(context_node_kind == Parse::NodeKind::VariableIntroducer,

toolchain/check/merge.cpp

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,35 @@
66

77
#include "toolchain/base/kind_switch.h"
88
#include "toolchain/check/import_ref.h"
9+
#include "toolchain/diagnostics/format_providers.h"
910
#include "toolchain/sem_ir/ids.h"
1011
#include "toolchain/sem_ir/typed_insts.h"
1112

1213
namespace Carbon::Check {
1314

15+
// Explicit or implicit parameters. Used in diagnostics.
16+
enum class ParamKind : uint8_t {
17+
Explicit,
18+
Implicit,
19+
};
20+
21+
} // namespace Carbon::Check
22+
23+
// `ParamKind` is formatted in diagnostics as `{0}parameters`, and we only add
24+
// text for implicit paramseters.
25+
template <>
26+
struct llvm::format_provider<Carbon::Check::ParamKind> {
27+
using ParamKind = Carbon::Check::ParamKind;
28+
static void format(const ParamKind& kind, raw_ostream& out,
29+
StringRef /*style*/) {
30+
if (kind == ParamKind::Implicit) {
31+
out << "implicit ";
32+
}
33+
}
34+
};
35+
36+
namespace Carbon::Check {
37+
1438
CARBON_DIAGNOSTIC(RedeclPrevDecl, Note, "previously declared here");
1539

1640
// Diagnoses a redeclaration which is redundant.
@@ -193,10 +217,12 @@ static auto EntityHasParamError(Context& context, const DeclParams& info)
193217

194218
// Returns false if a param differs for a redeclaration. The caller is expected
195219
// to provide a diagnostic.
196-
static auto CheckRedeclParam(
197-
Context& context, llvm::StringLiteral param_diag_label, int32_t param_index,
198-
SemIR::InstId new_param_pattern_id, SemIR::InstId prev_param_pattern_id,
199-
SemIR::SpecificId prev_specific_id, bool diagnose) -> bool {
220+
static auto CheckRedeclParam(Context& context, ParamKind param_kind,
221+
int32_t param_index,
222+
SemIR::InstId new_param_pattern_id,
223+
SemIR::InstId prev_param_pattern_id,
224+
SemIR::SpecificId prev_specific_id, bool diagnose)
225+
-> bool {
200226
// TODO: Consider differentiating between type and name mistakes. For now,
201227
// taking the simpler approach because I also think we may want to refactor
202228
// params.
@@ -205,15 +231,15 @@ static auto CheckRedeclParam(
205231
return;
206232
}
207233
CARBON_DIAGNOSTIC(RedeclParamDiffers, Error,
208-
"redeclaration differs at {0}parameter {1}",
209-
llvm::StringLiteral, int32_t);
234+
"redeclaration differs at {0}parameter {1}", ParamKind,
235+
int32_t);
210236
CARBON_DIAGNOSTIC(RedeclParamPrevious, Note,
211237
"previous declaration's corresponding {0}parameter here",
212-
llvm::StringLiteral);
238+
ParamKind);
213239
context.emitter()
214-
.Build(new_param_pattern_id, RedeclParamDiffers, param_diag_label,
240+
.Build(new_param_pattern_id, RedeclParamDiffers, param_kind,
215241
param_index + 1)
216-
.Note(prev_param_pattern_id, RedeclParamPrevious, param_diag_label)
242+
.Note(prev_param_pattern_id, RedeclParamPrevious, param_kind)
217243
.Emit();
218244
};
219245

@@ -265,7 +291,7 @@ static auto CheckRedeclParams(Context& context, SemIRLoc new_decl_loc,
265291
SemIR::InstBlockId new_param_patterns_id,
266292
SemIRLoc prev_decl_loc,
267293
SemIR::InstBlockId prev_param_patterns_id,
268-
llvm::StringLiteral param_diag_label,
294+
ParamKind param_kind,
269295
SemIR::SpecificId prev_specific_id, bool diagnose)
270296
-> bool {
271297
// This will often occur for empty params.
@@ -278,19 +304,18 @@ static auto CheckRedeclParams(Context& context, SemIRLoc new_decl_loc,
278304
if (!diagnose) {
279305
return false;
280306
}
281-
CARBON_DIAGNOSTIC(RedeclParamListDiffers, Error,
282-
"redeclaration differs because of {1}{0}parameter list",
283-
llvm::StringLiteral, llvm::StringLiteral);
307+
CARBON_DIAGNOSTIC(
308+
RedeclParamListDiffers, Error,
309+
"redeclaration differs because of {1:'|missing '}{0}parameter list",
310+
ParamKind, FormatBool);
284311
CARBON_DIAGNOSTIC(RedeclParamListPrevious, Note,
285-
"previously declared with{1} {0}parameter list",
286-
llvm::StringLiteral, llvm::StringLiteral);
312+
"previously declared {1:with|without} {0}parameter list",
313+
ParamKind, FormatBool);
287314
context.emitter()
288-
.Build(new_decl_loc, RedeclParamListDiffers, param_diag_label,
289-
new_param_patterns_id.is_valid() ? llvm::StringLiteral("")
290-
: "missing ")
291-
.Note(
292-
prev_decl_loc, RedeclParamListPrevious, param_diag_label,
293-
prev_param_patterns_id.is_valid() ? llvm::StringLiteral("") : "out")
315+
.Build(new_decl_loc, RedeclParamListDiffers, param_kind,
316+
{.value = new_param_patterns_id.is_valid()})
317+
.Note(prev_decl_loc, RedeclParamListPrevious, param_kind,
318+
{.value = prev_param_patterns_id.is_valid()})
294319
.Emit();
295320
return false;
296321
}
@@ -307,24 +332,23 @@ static auto CheckRedeclParams(Context& context, SemIRLoc new_decl_loc,
307332
}
308333
CARBON_DIAGNOSTIC(
309334
RedeclParamCountDiffers, Error,
310-
"redeclaration differs because of {0}parameter count of {1}",
311-
llvm::StringLiteral, int32_t);
335+
"redeclaration differs because of {0}parameter count of {1}", ParamKind,
336+
int32_t);
312337
CARBON_DIAGNOSTIC(RedeclParamCountPrevious, Note,
313338
"previously declared with {0}parameter count of {1}",
314-
llvm::StringLiteral, int32_t);
339+
ParamKind, int32_t);
315340
context.emitter()
316-
.Build(new_decl_loc, RedeclParamCountDiffers, param_diag_label,
341+
.Build(new_decl_loc, RedeclParamCountDiffers, param_kind,
317342
new_param_pattern_ids.size())
318-
.Note(prev_decl_loc, RedeclParamCountPrevious, param_diag_label,
343+
.Note(prev_decl_loc, RedeclParamCountPrevious, param_kind,
319344
prev_param_pattern_ids.size())
320345
.Emit();
321346
return false;
322347
}
323348
for (auto [index, new_param_pattern_id, prev_param_pattern_id] :
324349
llvm::enumerate(new_param_pattern_ids, prev_param_pattern_ids)) {
325-
if (!CheckRedeclParam(context, param_diag_label, index,
326-
new_param_pattern_id, prev_param_pattern_id,
327-
prev_specific_id, diagnose)) {
350+
if (!CheckRedeclParam(context, param_kind, index, new_param_pattern_id,
351+
prev_param_pattern_id, prev_specific_id, diagnose)) {
328352
return false;
329353
}
330354
}
@@ -412,13 +436,13 @@ auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity,
412436
}
413437
if (!CheckRedeclParams(context, new_entity.loc,
414438
new_entity.implicit_param_patterns_id, prev_entity.loc,
415-
prev_entity.implicit_param_patterns_id, "implicit ",
416-
prev_specific_id, diagnose)) {
439+
prev_entity.implicit_param_patterns_id,
440+
ParamKind::Implicit, prev_specific_id, diagnose)) {
417441
return false;
418442
}
419443
if (!CheckRedeclParams(context, new_entity.loc, new_entity.param_patterns_id,
420-
prev_entity.loc, prev_entity.param_patterns_id, "",
421-
prev_specific_id, diagnose)) {
444+
prev_entity.loc, prev_entity.param_patterns_id,
445+
ParamKind::Explicit, prev_specific_id, diagnose)) {
422446
return false;
423447
}
424448
if (check_syntax &&

toolchain/diagnostics/BUILD

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ cc_library(
2525
],
2626
deps = [
2727
":diagnostic_kind",
28+
":format_providers",
2829
"//common:check",
2930
"//common:ostream",
3031
"@llvm-project//llvm:Support",
@@ -56,6 +57,30 @@ cc_library(
5657
],
5758
)
5859

60+
cc_library(
61+
name = "format_providers",
62+
hdrs = ["format_providers.h"],
63+
deps = [
64+
"//common:check",
65+
"//common:ostream",
66+
"@llvm-project//llvm:Support",
67+
],
68+
)
69+
70+
cc_test(
71+
name = "format_providers_test",
72+
size = "small",
73+
srcs = ["format_providers_test.cpp"],
74+
deps = [
75+
":diagnostic_emitter",
76+
":format_providers",
77+
":mocks",
78+
"//testing/base:gtest_main",
79+
"@googletest//:gtest",
80+
"@llvm-project//llvm:Support",
81+
],
82+
)
83+
5984
cc_library(
6085
name = "null_diagnostics",
6186
hdrs = ["null_diagnostics.h"],

0 commit comments

Comments
 (0)