14[[nodiscard]] FormattableError make_duplicate_error(
15 const std::string &header_message,
16 SourcePosition duplicate_pos,
17 const std::string ¬e_message,
18 SourcePosition original_pos,
19 const std::vector<std::string> &file_lines,
20 const TextFormatter *format)
22 std::vector<std::string> lines;
23 FileHighlightPrinter printer{format};
26 lines.push_back(header_message);
29 assert_or_panic(duplicate_pos.line > 0,
"duplicate_pos.line must be positive (1-based)");
30 assert_or_panic(duplicate_pos.line <= file_lines.size(),
"duplicate_pos.line exceeds file bounds");
31 assert_or_panic(duplicate_pos.column > 0,
"duplicate_pos.column must be positive (1-based)");
32 auto dup_context = printer.print(file_lines, duplicate_pos.line - 1, duplicate_pos.column - 1);
33 for (
auto &line : dup_context) {
34 lines.push_back(std::move(line));
38 lines.emplace_back(
"");
41 lines.push_back(note_message);
44 assert_or_panic(original_pos.line > 0,
"original_pos.line must be positive (1-based)");
45 assert_or_panic(original_pos.line <= file_lines.size(),
"original_pos.line exceeds file bounds");
46 assert_or_panic(original_pos.column > 0,
"original_pos.column must be positive (1-based)");
47 auto orig_context = printer.print(file_lines, original_pos.line - 1, original_pos.column - 1);
48 for (
auto &line : orig_context) {
49 lines.push_back(std::move(line));
52 return FormattableError{std::move(lines)};
57template <
typename Entry>
58ChainableResult<void> HeaderEnumMapProvider::try_add_entry(
const Entry &entry)
const
60 const auto &
name = entry.name();
72 auto raw_value = entry.int_value();
73 const auto &new_pos = entry.position();
78 if (raw_value < 0 || raw_value > definition_.
max_value) {
80 std::vector<std::string> lines;
81 FileHighlightPrinter printer{format_};
83 lines.push_back(format_->
format(
84 "{}:{}:{}: '{}' has value '{}', which does not fit in the {}-bit field '{}'.",
85 FormatParam{header_path_, Style::bold},
88 FormatParam{name, Style::bold},
89 FormatParam{raw_value, Style::bold},
90 FormatParam{std::bit_width(definition_.max_value)},
91 FormatParam{definition_.field_display_name, Style::bold}));
93 assert_or_panic(new_pos.line > 0,
"new_pos.line must be positive (1-based)");
94 assert_or_panic(new_pos.line <= driver_->file_lines().size(),
"new_pos.line exceeds file bounds");
95 assert_or_panic(new_pos.column > 0,
"new_pos.column must be positive (1-based)");
96 auto context = printer.print(driver_->file_lines(), new_pos.line - 1, new_pos.column - 1);
97 for (
auto &line : context) {
98 lines.push_back(std::move(line));
101 lines.emplace_back(
"");
102 lines.push_back(format_->
format(
103 "{} widen the field's mask to cover this value, or add '{}' to the provider's skipped names to ignore it.",
104 FormatParam{
"note:", Style::cyan | Style::bold},
105 FormatParam{name, Style::bold}));
107 return FormattableError{std::move(lines)};
110 auto value =
static_cast<std::uint32_t
>(raw_value);
113 if (name_to_value_.contains(
name)) {
115 const auto &orig_pos = name_to_position_.at(
name);
116 return make_duplicate_error(
118 "{}:{}:{}: duplicate {} name '{}'.",
119 FormatParam{header_path_, Style::bold},
122 FormatParam{definition_.field_display_name},
123 FormatParam{name, Style::bold}),
126 "{} originally defined at line {}:", FormatParam{
"note:", Style::cyan | Style::bold}, orig_pos.line),
128 driver_->file_lines(),
133 if (value_to_name_.contains(value)) {
135 const auto &orig_name = value_to_name_.at(value);
136 const auto &orig_pos = value_to_position_.at(value);
137 return make_duplicate_error(
139 "{}:{}:{}: duplicate {} value '{}': both '{}' and '{}' have this value.",
140 FormatParam{header_path_, Style::bold},
143 FormatParam{definition_.field_display_name},
144 FormatParam{value, Style::bold},
145 FormatParam{orig_name, Style::bold},
146 FormatParam{name, Style::bold}),
149 "{} '{}' originally defined at line {}:",
150 FormatParam{
"note:", Style::cyan | Style::bold},
151 FormatParam{orig_name, Style::bold},
154 driver_->file_lines(),
159 name_to_value_[
name] = value;
160 value_to_name_[value] =
name;
161 name_to_position_[
name] = new_pos;
162 value_to_position_[value] = new_pos;
171 "Invalid {} name '{}': expected prefix '{}'.",
177 auto load_result = ensure_loaded();
178 if (!load_result.has_value()) {
185 const auto it = name_to_value_.find(
name);
186 if (it == name_to_value_.end()) {
188 "No {} named '{}' exists in '{}'.",
198 auto load_result = ensure_loaded();
199 if (!load_result.has_value()) {
206 const auto it = value_to_name_.find(value);
207 if (it == value_to_name_.end()) {
209 "No {} with value '{}' exists in '{}'.",
222 "Header file for field '{}' previously failed to load.",
231 driver_ = std::make_unique<CParserFacade>(header_path_, format_);
235 auto defines_result = driver_->parse_defines();
236 if (!defines_result.has_value()) {
238 return ChainableResult<void>{defines_result};
240 for (
const auto &def : defines_result.value()) {
241 if (!def.has_int_value()) {
244 auto insert_result = try_add_entry(def);
245 if (!insert_result.has_value()) {
246 return insert_result;
253 auto enums_result = driver_->parse_enums();
254 if (!enums_result.has_value()) {
256 return ChainableResult<void>{enums_result};
258 for (
const auto &enum_decl : enums_result.value()) {
259 for (
const auto &member : enum_decl.members()) {
260 auto insert_result = try_add_entry(member);
261 if (!insert_result.has_value()) {
262 return insert_result;
268 if (name_to_value_.empty()) {
270 return FormattableError{
271 "Field '{}' declared provider prefix '{}' in '{}' but no matching names were found.",
281 const std::filesystem::path &project_root,
283 gsl::not_null<const TextFormatter *> format,
284 gsl::not_null<const UserDiagnostics *> diag)
290 if (!field.has_provider()) {
296 std::make_unique<HeaderEnumMapProvider>(
297 project_root / definition.
header,
A result type that maintains a chainable sequence of errors for debugging and error reporting.
One named bit-field within a metatile attribute layout.
A validated metatile attribute layout: an ordered set of non-overlapping fields.
const std::vector< Field > & fields() const
static const Style bold
Bold text formatting.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > ¶ms) const
Formats a string with styled parameters using fmtlib syntax.
void assert_or_panic(bool condition, const StringViewSourceLoc &s)
Conditionally panics if the given condition is false.
std::map< std::string, std::unique_ptr< EnumMapProvider >, std::less<> > ProviderMap
Maps schema field names to the provider that names that field's values.
ProviderMap build_provider_map(const std::filesystem::path &project_root, const Schema &schema, gsl::not_null< const TextFormatter * > format, gsl::not_null< const UserDiagnostics * > diag)
Builds a header provider for every provider-backed field in a schema.
std::string field_display_name
std::unordered_set< std::string > skipped
Describes where and how a field's named values are declared.
EnumDefinition to_enum_definition(std::string field_display_name, std::uint32_t max_value) const
Builds the resolvable enum definition a header provider needs from this description.
std::filesystem::path header