// NOTE: This file is generated by the templates/template.rb script and should not
// be modified manually. See /home/runner/work/herb/herb/templates/src/errors.c.erb

#include "include/errors.h"
#include "include/location/location.h"
#include "include/location/position.h"
#include "include/ast/pretty_print.h"
#include "include/lexer/token.h"
#include "include/util/util.h"
#include "include/lib/hb_allocator.h"
#include "include/lib/hb_array.h"
#include "include/lib/hb_string.h"

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#define ERROR_MESSAGES_TRUNCATED_LENGTH 128

size_t error_sizeof(void) {
  return sizeof(struct ERROR_STRUCT);
}

void error_init(ERROR_T* error, const error_type_T type, position_T start, position_T end) {
  if (!error) { return; }

  error->type = type;
  error->location.start = start;
  error->location.end = end;
}

UNEXPECTED_ERROR_T* unexpected_error_init(hb_string_T description, hb_string_T expected, hb_string_T found, position_T start, position_T end, hb_allocator_T* allocator) {
  UNEXPECTED_ERROR_T* unexpected_error = hb_allocator_alloc(allocator, sizeof(UNEXPECTED_ERROR_T));

  if (!unexpected_error) { return NULL; }

  error_init(&unexpected_error->base, UNEXPECTED_ERROR, start, end);

  const char* message_template = "%s. Expected: %s, found: %s.";

  hb_string_T truncated_argument_0 = hb_string_truncate(description, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_1 = hb_string_truncate(expected, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_1 = hb_string_to_c_string_using_malloc(truncated_argument_1);

  hb_string_T truncated_argument_2 = hb_string_truncate(found, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_2 = hb_string_to_c_string_using_malloc(truncated_argument_2);

  char message[413];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    truncated_c_string_1 ? truncated_c_string_1 : "",
    truncated_c_string_2 ? truncated_c_string_2 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_1) { free(truncated_c_string_1); }
  if (truncated_c_string_2) { free(truncated_c_string_2); }
  unexpected_error->base.message = hb_string_copy(hb_string(message), allocator);

  unexpected_error->description = hb_string_copy(description, allocator);
  unexpected_error->expected = hb_string_copy(expected, allocator);
  unexpected_error->found = hb_string_copy(found, allocator);
  return unexpected_error;
}

void append_unexpected_error(hb_string_T description, hb_string_T expected, hb_string_T found, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, unexpected_error_init(description, expected, found, start, end, allocator));
}

UNEXPECTED_TOKEN_ERROR_T* unexpected_token_error_init(token_type_T expected_type, token_T* found, position_T start, position_T end, hb_allocator_T* allocator) {
  UNEXPECTED_TOKEN_ERROR_T* unexpected_token_error = hb_allocator_alloc(allocator, sizeof(UNEXPECTED_TOKEN_ERROR_T));

  if (!unexpected_token_error) { return NULL; }

  error_init(&unexpected_token_error->base, UNEXPECTED_TOKEN_ERROR, start, end);

  const char* message_template = "Found %s when expecting %s at (%u:%u).";

  hb_string_T truncated_argument_0 = hb_string_truncate(token_type_to_friendly_string(found->type), ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_1 = hb_string_truncate(token_type_to_friendly_string(expected_type), ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_1 = hb_string_to_c_string_using_malloc(truncated_argument_1);

  char message[315];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    truncated_c_string_1 ? truncated_c_string_1 : "",
    found->location.start.line,
    found->location.start.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_1) { free(truncated_c_string_1); }
  unexpected_token_error->base.message = hb_string_copy(hb_string(message), allocator);

  unexpected_token_error->expected_type = expected_type;
  unexpected_token_error->found = token_copy(found, allocator);
  return unexpected_token_error;
}

void append_unexpected_token_error(token_type_T expected_type, token_T* found, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, unexpected_token_error_init(expected_type, found, start, end, allocator));
}

MISSING_OPENING_TAG_ERROR_T* missing_opening_tag_error_init(token_T* closing_tag, position_T start, position_T end, hb_allocator_T* allocator) {
  MISSING_OPENING_TAG_ERROR_T* missing_opening_tag_error = hb_allocator_alloc(allocator, sizeof(MISSING_OPENING_TAG_ERROR_T));

  if (!missing_opening_tag_error) { return NULL; }

  error_init(&missing_opening_tag_error->base, MISSING_OPENING_TAG_ERROR, start, end);

  const char* message_template = "Found closing tag `</%s>` at (%u:%u) without a matching opening tag in the same scope.";

  hb_string_T truncated_argument_0 = hb_string_truncate(closing_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[235];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    closing_tag->location.start.line,
    closing_tag->location.start.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  missing_opening_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  missing_opening_tag_error->closing_tag = token_copy(closing_tag, allocator);
  return missing_opening_tag_error;
}

void append_missing_opening_tag_error(token_T* closing_tag, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, missing_opening_tag_error_init(closing_tag, start, end, allocator));
}

MISSING_CLOSING_TAG_ERROR_T* missing_closing_tag_error_init(token_T* opening_tag, position_T start, position_T end, hb_allocator_T* allocator) {
  MISSING_CLOSING_TAG_ERROR_T* missing_closing_tag_error = hb_allocator_alloc(allocator, sizeof(MISSING_CLOSING_TAG_ERROR_T));

  if (!missing_closing_tag_error) { return NULL; }

  error_init(&missing_closing_tag_error->base, MISSING_CLOSING_TAG_ERROR, start, end);

  const char* message_template = "Opening tag `<%s>` at (%u:%u) doesn't have a matching closing tag `</%s>` in the same scope.";

  hb_string_T truncated_argument_0 = hb_string_truncate(opening_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_3 = hb_string_truncate(opening_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_3 = hb_string_to_c_string_using_malloc(truncated_argument_3);

  char message[369];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    opening_tag->location.start.line,
    opening_tag->location.start.column,
    truncated_c_string_3 ? truncated_c_string_3 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_3) { free(truncated_c_string_3); }
  missing_closing_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  missing_closing_tag_error->opening_tag = token_copy(opening_tag, allocator);
  return missing_closing_tag_error;
}

void append_missing_closing_tag_error(token_T* opening_tag, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, missing_closing_tag_error_init(opening_tag, start, end, allocator));
}

TAG_NAMES_MISMATCH_ERROR_T* tag_names_mismatch_error_init(token_T* opening_tag, token_T* closing_tag, position_T start, position_T end, hb_allocator_T* allocator) {
  TAG_NAMES_MISMATCH_ERROR_T* tag_names_mismatch_error = hb_allocator_alloc(allocator, sizeof(TAG_NAMES_MISMATCH_ERROR_T));

  if (!tag_names_mismatch_error) { return NULL; }

  error_init(&tag_names_mismatch_error->base, TAG_NAMES_MISMATCH_ERROR, start, end);

  const char* message_template = "Opening tag `<%s>` at (%u:%u) closed with `</%s>` at (%u:%u).";

  hb_string_T truncated_argument_0 = hb_string_truncate(opening_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_3 = hb_string_truncate(closing_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_3 = hb_string_to_c_string_using_malloc(truncated_argument_3);

  char message[358];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    opening_tag->location.start.line,
    opening_tag->location.start.column,
    truncated_c_string_3 ? truncated_c_string_3 : "",
    closing_tag->location.start.line,
    closing_tag->location.start.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_3) { free(truncated_c_string_3); }
  tag_names_mismatch_error->base.message = hb_string_copy(hb_string(message), allocator);

  tag_names_mismatch_error->opening_tag = token_copy(opening_tag, allocator);
  tag_names_mismatch_error->closing_tag = token_copy(closing_tag, allocator);
  return tag_names_mismatch_error;
}

void append_tag_names_mismatch_error(token_T* opening_tag, token_T* closing_tag, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, tag_names_mismatch_error_init(opening_tag, closing_tag, start, end, allocator));
}

VOID_ELEMENT_CLOSING_TAG_ERROR_T* void_element_closing_tag_error_init(token_T* tag_name, hb_string_T expected, hb_string_T found, position_T start, position_T end, hb_allocator_T* allocator) {
  VOID_ELEMENT_CLOSING_TAG_ERROR_T* void_element_closing_tag_error = hb_allocator_alloc(allocator, sizeof(VOID_ELEMENT_CLOSING_TAG_ERROR_T));

  if (!void_element_closing_tag_error) { return NULL; }

  error_init(&void_element_closing_tag_error->base, VOID_ELEMENT_CLOSING_TAG_ERROR, start, end);

  const char* message_template = "`%s` is a void element and should not be used as a closing tag. Use `<%s>` or `<%s />` instead of `</%s>`.";

  hb_string_T truncated_argument_0 = hb_string_truncate(tag_name->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_1 = hb_string_truncate(tag_name->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_1 = hb_string_to_c_string_using_malloc(truncated_argument_1);

  hb_string_T truncated_argument_2 = hb_string_truncate(tag_name->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_2 = hb_string_to_c_string_using_malloc(truncated_argument_2);

  hb_string_T truncated_argument_3 = hb_string_truncate(tag_name->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_3 = hb_string_to_c_string_using_malloc(truncated_argument_3);

  char message[619];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    truncated_c_string_1 ? truncated_c_string_1 : "",
    truncated_c_string_2 ? truncated_c_string_2 : "",
    truncated_c_string_3 ? truncated_c_string_3 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_1) { free(truncated_c_string_1); }
  if (truncated_c_string_2) { free(truncated_c_string_2); }
  if (truncated_c_string_3) { free(truncated_c_string_3); }
  void_element_closing_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  void_element_closing_tag_error->tag_name = token_copy(tag_name, allocator);
  void_element_closing_tag_error->expected = hb_string_copy(expected, allocator);
  void_element_closing_tag_error->found = hb_string_copy(found, allocator);
  return void_element_closing_tag_error;
}

void append_void_element_closing_tag_error(token_T* tag_name, hb_string_T expected, hb_string_T found, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, void_element_closing_tag_error_init(tag_name, expected, found, start, end, allocator));
}

UNCLOSED_ELEMENT_ERROR_T* unclosed_element_error_init(token_T* opening_tag, position_T start, position_T end, hb_allocator_T* allocator) {
  UNCLOSED_ELEMENT_ERROR_T* unclosed_element_error = hb_allocator_alloc(allocator, sizeof(UNCLOSED_ELEMENT_ERROR_T));

  if (!unclosed_element_error) { return NULL; }

  error_init(&unclosed_element_error->base, UNCLOSED_ELEMENT_ERROR, start, end);

  const char* message_template = "Tag `<%s>` opened at (%u:%u) was never closed before the end of document.";

  hb_string_T truncated_argument_0 = hb_string_truncate(opening_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[222];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    opening_tag->location.start.line,
    opening_tag->location.start.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  unclosed_element_error->base.message = hb_string_copy(hb_string(message), allocator);

  unclosed_element_error->opening_tag = token_copy(opening_tag, allocator);
  return unclosed_element_error;
}

void append_unclosed_element_error(token_T* opening_tag, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, unclosed_element_error_init(opening_tag, start, end, allocator));
}

RUBY_PARSE_ERROR_T* ruby_parse_error_init(hb_string_T error_message, hb_string_T diagnostic_id, hb_string_T level, position_T start, position_T end, hb_allocator_T* allocator) {
  RUBY_PARSE_ERROR_T* ruby_parse_error = hb_allocator_alloc(allocator, sizeof(RUBY_PARSE_ERROR_T));

  if (!ruby_parse_error) { return NULL; }

  error_init(&ruby_parse_error->base, RUBY_PARSE_ERROR, start, end);

  const char* message_template = "%s: %s";

  hb_string_T truncated_argument_0 = hb_string_truncate(diagnostic_id, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_1 = hb_string_truncate(error_message, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_1 = hb_string_to_c_string_using_malloc(truncated_argument_1);

  char message[263];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    truncated_c_string_1 ? truncated_c_string_1 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_1) { free(truncated_c_string_1); }
  ruby_parse_error->base.message = hb_string_copy(hb_string(message), allocator);

  ruby_parse_error->error_message = hb_string_copy(error_message, allocator);
  ruby_parse_error->diagnostic_id = hb_string_copy(diagnostic_id, allocator);
  ruby_parse_error->level = hb_string_copy(level, allocator);
  return ruby_parse_error;
}

void append_ruby_parse_error(hb_string_T error_message, hb_string_T diagnostic_id, hb_string_T level, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, ruby_parse_error_init(error_message, diagnostic_id, level, start, end, allocator));
}

ERB_CONTROL_FLOW_SCOPE_ERROR_T* erb_control_flow_scope_error_init(hb_string_T keyword, position_T start, position_T end, hb_allocator_T* allocator) {
  ERB_CONTROL_FLOW_SCOPE_ERROR_T* erb_control_flow_scope_error = hb_allocator_alloc(allocator, sizeof(ERB_CONTROL_FLOW_SCOPE_ERROR_T));

  if (!erb_control_flow_scope_error) { return NULL; }

  error_init(&erb_control_flow_scope_error->base, ERB_CONTROL_FLOW_SCOPE_ERROR, start, end);

  const char* message_template = "%s appears outside its control flow block. Keep ERB control flow statements together within the same HTML scope (tag, attribute, or content).";

  hb_string_T truncated_argument_0 = hb_string_truncate(keyword, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[270];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  erb_control_flow_scope_error->base.message = hb_string_copy(hb_string(message), allocator);

  erb_control_flow_scope_error->keyword = hb_string_copy(keyword, allocator);
  return erb_control_flow_scope_error;
}

void append_erb_control_flow_scope_error(hb_string_T keyword, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, erb_control_flow_scope_error_init(keyword, start, end, allocator));
}

MISSING_ERB_END_TAG_ERROR_T* missing_erb_end_tag_error_init(hb_string_T keyword, position_T start, position_T end, hb_allocator_T* allocator) {
  MISSING_ERB_END_TAG_ERROR_T* missing_erb_end_tag_error = hb_allocator_alloc(allocator, sizeof(MISSING_ERB_END_TAG_ERROR_T));

  if (!missing_erb_end_tag_error) { return NULL; }

  error_init(&missing_erb_end_tag_error->base, MISSING_ERB_END_TAG_ERROR, start, end);

  const char* message_template = "%s started here but never closed with an end tag. The end tag may be in a different scope.";

  hb_string_T truncated_argument_0 = hb_string_truncate(keyword, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[219];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  missing_erb_end_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  missing_erb_end_tag_error->keyword = hb_string_copy(keyword, allocator);
  return missing_erb_end_tag_error;
}

void append_missing_erb_end_tag_error(hb_string_T keyword, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, missing_erb_end_tag_error_init(keyword, start, end, allocator));
}

ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T* erb_multiple_blocks_in_tag_error_init(position_T start, position_T end, hb_allocator_T* allocator) {
  ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T* erb_multiple_blocks_in_tag_error = hb_allocator_alloc(allocator, sizeof(ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T));

  if (!erb_multiple_blocks_in_tag_error) { return NULL; }

  error_init(&erb_multiple_blocks_in_tag_error->base, ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR, start, end);

  erb_multiple_blocks_in_tag_error->base.message = hb_string_copy(hb_string("Multiple unclosed control flow blocks in a single ERB tag. Split each block into its own ERB tag, or close all blocks within the same tag."), allocator);

  return erb_multiple_blocks_in_tag_error;
}

void append_erb_multiple_blocks_in_tag_error(position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, erb_multiple_blocks_in_tag_error_init(start, end, allocator));
}

ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error_init(position_T start, position_T end, hb_allocator_T* allocator) {
  ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error = hb_allocator_alloc(allocator, sizeof(ERB_CASE_WITH_CONDITIONS_ERROR_T));

  if (!erb_case_with_conditions_error) { return NULL; }

  error_init(&erb_case_with_conditions_error->base, ERB_CASE_WITH_CONDITIONS_ERROR, start, end);

  erb_case_with_conditions_error->base.message = hb_string_copy(hb_string("A `case` statement with `when`/`in` conditions in a single ERB tag cannot be reliably parsed, compiled, and formatted. Use separate ERB tags for `case` and its conditions (e.g., `<% case x %>` followed by `<% when y %>`)."), allocator);

  return erb_case_with_conditions_error;
}

void append_erb_case_with_conditions_error(position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, erb_case_with_conditions_error_init(start, end, allocator));
}

CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR_T* conditional_element_multiple_tags_error_init(size_t line, size_t column, position_T start, position_T end, hb_allocator_T* allocator) {
  CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR_T* conditional_element_multiple_tags_error = hb_allocator_alloc(allocator, sizeof(CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR_T));

  if (!conditional_element_multiple_tags_error) { return NULL; }

  error_init(&conditional_element_multiple_tags_error->base, CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR, start, end);

  const char* message_template = "Conditional element pattern detected at (%zu:%zu) but the conditional block contains multiple HTML tags. Each conditional element must wrap exactly one opening and one closing tag. Consider nesting the conditionals individually.";

  char message[269];
  snprintf(
    message,
    sizeof(message),
    message_template,
    line,
    column
  );

  conditional_element_multiple_tags_error->base.message = hb_string_copy(hb_string(message), allocator);

  conditional_element_multiple_tags_error->line = line;
  conditional_element_multiple_tags_error->column = column;
  return conditional_element_multiple_tags_error;
}

void append_conditional_element_multiple_tags_error(size_t line, size_t column, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, conditional_element_multiple_tags_error_init(line, column, start, end, allocator));
}

CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR_T* conditional_element_condition_mismatch_error_init(hb_string_T tag_name, hb_string_T open_condition, size_t open_line, size_t open_column, hb_string_T close_condition, size_t close_line, size_t close_column, position_T start, position_T end, hb_allocator_T* allocator) {
  CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR_T* conditional_element_condition_mismatch_error = hb_allocator_alloc(allocator, sizeof(CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR_T));

  if (!conditional_element_condition_mismatch_error) { return NULL; }

  error_init(&conditional_element_condition_mismatch_error->base, CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR, start, end);

  const char* message_template = "Conditional element `<%s>` has mismatched conditions: opening uses `%s` at (%zu:%zu) but closing uses `%s` at (%zu:%zu). Both conditions must be identical.";

  hb_string_T truncated_argument_0 = hb_string_truncate(tag_name, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_1 = hb_string_truncate(open_condition, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_1 = hb_string_to_c_string_using_malloc(truncated_argument_1);

  hb_string_T truncated_argument_4 = hb_string_truncate(close_condition, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_4 = hb_string_to_c_string_using_malloc(truncated_argument_4);

  char message[620];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    truncated_c_string_1 ? truncated_c_string_1 : "",
    open_line,
    open_column,
    truncated_c_string_4 ? truncated_c_string_4 : "",
    close_line,
    close_column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_1) { free(truncated_c_string_1); }
  if (truncated_c_string_4) { free(truncated_c_string_4); }
  conditional_element_condition_mismatch_error->base.message = hb_string_copy(hb_string(message), allocator);

  conditional_element_condition_mismatch_error->tag_name = hb_string_copy(tag_name, allocator);
  conditional_element_condition_mismatch_error->open_condition = hb_string_copy(open_condition, allocator);
  conditional_element_condition_mismatch_error->open_line = open_line;
  conditional_element_condition_mismatch_error->open_column = open_column;
  conditional_element_condition_mismatch_error->close_condition = hb_string_copy(close_condition, allocator);
  conditional_element_condition_mismatch_error->close_line = close_line;
  conditional_element_condition_mismatch_error->close_column = close_column;
  return conditional_element_condition_mismatch_error;
}

void append_conditional_element_condition_mismatch_error(hb_string_T tag_name, hb_string_T open_condition, size_t open_line, size_t open_column, hb_string_T close_condition, size_t close_line, size_t close_column, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, conditional_element_condition_mismatch_error_init(tag_name, open_condition, open_line, open_column, close_condition, close_line, close_column, start, end, allocator));
}

INVALID_COMMENT_CLOSING_TAG_ERROR_T* invalid_comment_closing_tag_error_init(token_T* closing_tag, position_T start, position_T end, hb_allocator_T* allocator) {
  INVALID_COMMENT_CLOSING_TAG_ERROR_T* invalid_comment_closing_tag_error = hb_allocator_alloc(allocator, sizeof(INVALID_COMMENT_CLOSING_TAG_ERROR_T));

  if (!invalid_comment_closing_tag_error) { return NULL; }

  error_init(&invalid_comment_closing_tag_error->base, INVALID_COMMENT_CLOSING_TAG_ERROR, start, end);

  const char* message_template = "Invalid comment closing tag `%s` at (%u:%u). Use `-->` instead of `--!>`.";

  hb_string_T truncated_argument_0 = hb_string_truncate(closing_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[222];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    closing_tag->location.start.line,
    closing_tag->location.start.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  invalid_comment_closing_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  invalid_comment_closing_tag_error->closing_tag = token_copy(closing_tag, allocator);
  return invalid_comment_closing_tag_error;
}

void append_invalid_comment_closing_tag_error(token_T* closing_tag, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, invalid_comment_closing_tag_error_init(closing_tag, start, end, allocator));
}

OMITTED_CLOSING_TAG_ERROR_T* omitted_closing_tag_error_init(token_T* opening_tag, position_T insertion_point, position_T start, position_T end, hb_allocator_T* allocator) {
  OMITTED_CLOSING_TAG_ERROR_T* omitted_closing_tag_error = hb_allocator_alloc(allocator, sizeof(OMITTED_CLOSING_TAG_ERROR_T));

  if (!omitted_closing_tag_error) { return NULL; }

  error_init(&omitted_closing_tag_error->base, OMITTED_CLOSING_TAG_ERROR, start, end);

  const char* message_template = "Element `<%s>` at (%u:%u) has its closing tag omitted. While valid HTML, consider adding an explicit `</%s>` closing tag at (%u:%u) for clarity, or set `strict: false` to allow this.";

  hb_string_T truncated_argument_0 = hb_string_truncate(opening_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_3 = hb_string_truncate(opening_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_3 = hb_string_to_c_string_using_malloc(truncated_argument_3);

  char message[479];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    opening_tag->location.start.line,
    opening_tag->location.start.column,
    truncated_c_string_3 ? truncated_c_string_3 : "",
    insertion_point.line,
    insertion_point.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_3) { free(truncated_c_string_3); }
  omitted_closing_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  omitted_closing_tag_error->opening_tag = token_copy(opening_tag, allocator);
  omitted_closing_tag_error->insertion_point = insertion_point;
  return omitted_closing_tag_error;
}

void append_omitted_closing_tag_error(token_T* opening_tag, position_T insertion_point, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, omitted_closing_tag_error_init(opening_tag, insertion_point, start, end, allocator));
}

UNCLOSED_OPEN_TAG_ERROR_T* unclosed_open_tag_error_init(token_T* tag_name, position_T start, position_T end, hb_allocator_T* allocator) {
  UNCLOSED_OPEN_TAG_ERROR_T* unclosed_open_tag_error = hb_allocator_alloc(allocator, sizeof(UNCLOSED_OPEN_TAG_ERROR_T));

  if (!unclosed_open_tag_error) { return NULL; }

  error_init(&unclosed_open_tag_error->base, UNCLOSED_OPEN_TAG_ERROR, start, end);

  const char* message_template = "Opening tag `<%s>` at (%u:%u) is missing closing `>`.";

  hb_string_T truncated_argument_0 = hb_string_truncate(tag_name->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[202];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    tag_name->location.start.line,
    tag_name->location.start.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  unclosed_open_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  unclosed_open_tag_error->tag_name = token_copy(tag_name, allocator);
  return unclosed_open_tag_error;
}

void append_unclosed_open_tag_error(token_T* tag_name, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, unclosed_open_tag_error_init(tag_name, start, end, allocator));
}

UNCLOSED_CLOSE_TAG_ERROR_T* unclosed_close_tag_error_init(token_T* tag_name, position_T start, position_T end, hb_allocator_T* allocator) {
  UNCLOSED_CLOSE_TAG_ERROR_T* unclosed_close_tag_error = hb_allocator_alloc(allocator, sizeof(UNCLOSED_CLOSE_TAG_ERROR_T));

  if (!unclosed_close_tag_error) { return NULL; }

  error_init(&unclosed_close_tag_error->base, UNCLOSED_CLOSE_TAG_ERROR, start, end);

  const char* message_template = "Closing tag `</%s>` at (%u:%u) is missing closing `>`.";

  hb_string_T truncated_argument_0 = hb_string_truncate(tag_name->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[203];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    tag_name->location.start.line,
    tag_name->location.start.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  unclosed_close_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  unclosed_close_tag_error->tag_name = token_copy(tag_name, allocator);
  return unclosed_close_tag_error;
}

void append_unclosed_close_tag_error(token_T* tag_name, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, unclosed_close_tag_error_init(tag_name, start, end, allocator));
}

UNCLOSED_QUOTE_ERROR_T* unclosed_quote_error_init(token_T* opening_quote, position_T start, position_T end, hb_allocator_T* allocator) {
  UNCLOSED_QUOTE_ERROR_T* unclosed_quote_error = hb_allocator_alloc(allocator, sizeof(UNCLOSED_QUOTE_ERROR_T));

  if (!unclosed_quote_error) { return NULL; }

  error_init(&unclosed_quote_error->base, UNCLOSED_QUOTE_ERROR, start, end);

  const char* message_template = "Attribute value opened with %s at (%u:%u) was never closed.";

  hb_string_T truncated_argument_0 = hb_string_truncate(opening_quote->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[208];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    opening_quote->location.start.line,
    opening_quote->location.start.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  unclosed_quote_error->base.message = hb_string_copy(hb_string(message), allocator);

  unclosed_quote_error->opening_quote = token_copy(opening_quote, allocator);
  return unclosed_quote_error;
}

void append_unclosed_quote_error(token_T* opening_quote, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, unclosed_quote_error_init(opening_quote, start, end, allocator));
}

MISSING_ATTRIBUTE_VALUE_ERROR_T* missing_attribute_value_error_init(hb_string_T attribute_name, position_T start, position_T end, hb_allocator_T* allocator) {
  MISSING_ATTRIBUTE_VALUE_ERROR_T* missing_attribute_value_error = hb_allocator_alloc(allocator, sizeof(MISSING_ATTRIBUTE_VALUE_ERROR_T));

  if (!missing_attribute_value_error) { return NULL; }

  error_init(&missing_attribute_value_error->base, MISSING_ATTRIBUTE_VALUE_ERROR, start, end);

  const char* message_template = "Attribute `%s` at (%u:%u) is missing a value after the equals sign.";

  hb_string_T truncated_argument_0 = hb_string_truncate(attribute_name, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[216];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    start.line,
    start.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  missing_attribute_value_error->base.message = hb_string_copy(hb_string(message), allocator);

  missing_attribute_value_error->attribute_name = hb_string_copy(attribute_name, allocator);
  return missing_attribute_value_error;
}

void append_missing_attribute_value_error(hb_string_T attribute_name, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, missing_attribute_value_error_init(attribute_name, start, end, allocator));
}

UNCLOSED_ERB_TAG_ERROR_T* unclosed_erb_tag_error_init(token_T* opening_tag, position_T start, position_T end, hb_allocator_T* allocator) {
  UNCLOSED_ERB_TAG_ERROR_T* unclosed_erb_tag_error = hb_allocator_alloc(allocator, sizeof(UNCLOSED_ERB_TAG_ERROR_T));

  if (!unclosed_erb_tag_error) { return NULL; }

  error_init(&unclosed_erb_tag_error->base, UNCLOSED_ERB_TAG_ERROR, start, end);

  const char* message_template = "ERB tag `%s` at (%u:%u) is missing closing `%%>`.";

  hb_string_T truncated_argument_0 = hb_string_truncate(opening_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[198];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    opening_tag->location.start.line,
    opening_tag->location.start.column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  unclosed_erb_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  unclosed_erb_tag_error->opening_tag = token_copy(opening_tag, allocator);
  return unclosed_erb_tag_error;
}

void append_unclosed_erb_tag_error(token_T* opening_tag, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, unclosed_erb_tag_error_init(opening_tag, start, end, allocator));
}

STRAY_ERB_CLOSING_TAG_ERROR_T* stray_erb_closing_tag_error_init(position_T start, position_T end, hb_allocator_T* allocator) {
  STRAY_ERB_CLOSING_TAG_ERROR_T* stray_erb_closing_tag_error = hb_allocator_alloc(allocator, sizeof(STRAY_ERB_CLOSING_TAG_ERROR_T));

  if (!stray_erb_closing_tag_error) { return NULL; }

  error_init(&stray_erb_closing_tag_error->base, STRAY_ERB_CLOSING_TAG_ERROR, start, end);

  const char* message_template = "Stray `%%>` found at (%u:%u). This closing delimiter is not part of an ERB tag and will be treated as plain text. If you want a literal `%%>`, use the HTML entities `&percnt;&gt;` instead.";

  char message[209];
  snprintf(
    message,
    sizeof(message),
    message_template,
    start.line,
    start.column
  );

  stray_erb_closing_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  return stray_erb_closing_tag_error;
}

void append_stray_erb_closing_tag_error(position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, stray_erb_closing_tag_error_init(start, end, allocator));
}

NESTED_ERB_TAG_ERROR_T* nested_erb_tag_error_init(token_T* opening_tag, size_t nested_tag_line, size_t nested_tag_column, position_T start, position_T end, hb_allocator_T* allocator) {
  NESTED_ERB_TAG_ERROR_T* nested_erb_tag_error = hb_allocator_alloc(allocator, sizeof(NESTED_ERB_TAG_ERROR_T));

  if (!nested_erb_tag_error) { return NULL; }

  error_init(&nested_erb_tag_error->base, NESTED_ERB_TAG_ERROR, start, end);

  const char* message_template = "ERB tag `%s` at (%u:%u) was terminated by nested `<%%` tag at (%zu:%zu). Nesting `<%%` tags is not supported.";

  hb_string_T truncated_argument_0 = hb_string_truncate(opening_tag->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[298];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    opening_tag->location.start.line,
    opening_tag->location.start.column,
    nested_tag_line,
    nested_tag_column
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  nested_erb_tag_error->base.message = hb_string_copy(hb_string(message), allocator);

  nested_erb_tag_error->opening_tag = token_copy(opening_tag, allocator);
  nested_erb_tag_error->nested_tag_line = nested_tag_line;
  nested_erb_tag_error->nested_tag_column = nested_tag_column;
  return nested_erb_tag_error;
}

void append_nested_erb_tag_error(token_T* opening_tag, size_t nested_tag_line, size_t nested_tag_column, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, nested_erb_tag_error_init(opening_tag, nested_tag_line, nested_tag_column, start, end, allocator));
}

RENDER_AMBIGUOUS_LOCALS_ERROR_T* render_ambiguous_locals_error_init(hb_string_T partial, position_T start, position_T end, hb_allocator_T* allocator) {
  RENDER_AMBIGUOUS_LOCALS_ERROR_T* render_ambiguous_locals_error = hb_allocator_alloc(allocator, sizeof(RENDER_AMBIGUOUS_LOCALS_ERROR_T));

  if (!render_ambiguous_locals_error) { return NULL; }

  error_init(&render_ambiguous_locals_error->base, RENDER_AMBIGUOUS_LOCALS_ERROR, start, end);

  const char* message_template = "Did you mean `render partial: '%s', locals: { ... }`? Using `render '%s', locals: { ... }` passes a local variable named `locals` to the partial instead of setting the `locals:` option.";

  hb_string_T truncated_argument_0 = hb_string_truncate(partial, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_1 = hb_string_truncate(partial, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_1 = hb_string_to_c_string_using_malloc(truncated_argument_1);

  char message[442];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    truncated_c_string_1 ? truncated_c_string_1 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_1) { free(truncated_c_string_1); }
  render_ambiguous_locals_error->base.message = hb_string_copy(hb_string(message), allocator);

  render_ambiguous_locals_error->partial = hb_string_copy(partial, allocator);
  return render_ambiguous_locals_error;
}

void append_render_ambiguous_locals_error(hb_string_T partial, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, render_ambiguous_locals_error_init(partial, start, end, allocator));
}

RENDER_MISSING_LOCALS_ERROR_T* render_missing_locals_error_init(hb_string_T partial, hb_string_T keywords, position_T start, position_T end, hb_allocator_T* allocator) {
  RENDER_MISSING_LOCALS_ERROR_T* render_missing_locals_error = hb_allocator_alloc(allocator, sizeof(RENDER_MISSING_LOCALS_ERROR_T));

  if (!render_missing_locals_error) { return NULL; }

  error_init(&render_missing_locals_error->base, RENDER_MISSING_LOCALS_ERROR, start, end);

  const char* message_template = "Wrap `%s` in `locals: { ... }` when using `partial:`. Use `render partial: '%s', locals: { %s }` instead. Without `locals:`, these keyword arguments are ignored.";

  hb_string_T truncated_argument_0 = hb_string_truncate(keywords, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_1 = hb_string_truncate(partial, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_1 = hb_string_to_c_string_using_malloc(truncated_argument_1);

  hb_string_T truncated_argument_2 = hb_string_truncate(keywords, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_2 = hb_string_to_c_string_using_malloc(truncated_argument_2);

  char message[546];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    truncated_c_string_1 ? truncated_c_string_1 : "",
    truncated_c_string_2 ? truncated_c_string_2 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_1) { free(truncated_c_string_1); }
  if (truncated_c_string_2) { free(truncated_c_string_2); }
  render_missing_locals_error->base.message = hb_string_copy(hb_string(message), allocator);

  render_missing_locals_error->partial = hb_string_copy(partial, allocator);
  render_missing_locals_error->keywords = hb_string_copy(keywords, allocator);
  return render_missing_locals_error;
}

void append_render_missing_locals_error(hb_string_T partial, hb_string_T keywords, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, render_missing_locals_error_init(partial, keywords, start, end, allocator));
}

RENDER_NO_ARGUMENTS_ERROR_T* render_no_arguments_error_init(position_T start, position_T end, hb_allocator_T* allocator) {
  RENDER_NO_ARGUMENTS_ERROR_T* render_no_arguments_error = hb_allocator_alloc(allocator, sizeof(RENDER_NO_ARGUMENTS_ERROR_T));

  if (!render_no_arguments_error) { return NULL; }

  error_init(&render_no_arguments_error->base, RENDER_NO_ARGUMENTS_ERROR, start, end);

  render_no_arguments_error->base.message = hb_string_copy(hb_string("No arguments passed to `render`. It needs a partial name, a keyword argument like `partial:`, `template:`, or `layout:`, or a renderable object."), allocator);

  return render_no_arguments_error;
}

void append_render_no_arguments_error(position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, render_no_arguments_error_init(start, end, allocator));
}

RENDER_CONFLICTING_PARTIAL_ERROR_T* render_conflicting_partial_error_init(hb_string_T positional_partial, hb_string_T keyword_partial, position_T start, position_T end, hb_allocator_T* allocator) {
  RENDER_CONFLICTING_PARTIAL_ERROR_T* render_conflicting_partial_error = hb_allocator_alloc(allocator, sizeof(RENDER_CONFLICTING_PARTIAL_ERROR_T));

  if (!render_conflicting_partial_error) { return NULL; }

  error_init(&render_conflicting_partial_error->base, RENDER_CONFLICTING_PARTIAL_ERROR, start, end);

  const char* message_template = "Both a positional partial `'%s'` and a keyword `partial: '%s'` were passed to `render`. Use one form or the other, not both.";

  hb_string_T truncated_argument_0 = hb_string_truncate(positional_partial, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_1 = hb_string_truncate(keyword_partial, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_1 = hb_string_to_c_string_using_malloc(truncated_argument_1);

  char message[381];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    truncated_c_string_1 ? truncated_c_string_1 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_1) { free(truncated_c_string_1); }
  render_conflicting_partial_error->base.message = hb_string_copy(hb_string(message), allocator);

  render_conflicting_partial_error->positional_partial = hb_string_copy(positional_partial, allocator);
  render_conflicting_partial_error->keyword_partial = hb_string_copy(keyword_partial, allocator);
  return render_conflicting_partial_error;
}

void append_render_conflicting_partial_error(hb_string_T positional_partial, hb_string_T keyword_partial, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, render_conflicting_partial_error_init(positional_partial, keyword_partial, start, end, allocator));
}

RENDER_INVALID_AS_OPTION_ERROR_T* render_invalid_as_option_error_init(hb_string_T as_value, position_T start, position_T end, hb_allocator_T* allocator) {
  RENDER_INVALID_AS_OPTION_ERROR_T* render_invalid_as_option_error = hb_allocator_alloc(allocator, sizeof(RENDER_INVALID_AS_OPTION_ERROR_T));

  if (!render_invalid_as_option_error) { return NULL; }

  error_init(&render_invalid_as_option_error->base, RENDER_INVALID_AS_OPTION_ERROR, start, end);

  const char* message_template = "The `as:` value `'%s'` is not a valid Ruby identifier. Use a name that starts with a lowercase letter or underscore, like `as: :item`.";

  hb_string_T truncated_argument_0 = hb_string_truncate(as_value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[263];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  render_invalid_as_option_error->base.message = hb_string_copy(hb_string(message), allocator);

  render_invalid_as_option_error->as_value = hb_string_copy(as_value, allocator);
  return render_invalid_as_option_error;
}

void append_render_invalid_as_option_error(hb_string_T as_value, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, render_invalid_as_option_error_init(as_value, start, end, allocator));
}

RENDER_OBJECT_AND_COLLECTION_ERROR_T* render_object_and_collection_error_init(position_T start, position_T end, hb_allocator_T* allocator) {
  RENDER_OBJECT_AND_COLLECTION_ERROR_T* render_object_and_collection_error = hb_allocator_alloc(allocator, sizeof(RENDER_OBJECT_AND_COLLECTION_ERROR_T));

  if (!render_object_and_collection_error) { return NULL; }

  error_init(&render_object_and_collection_error->base, RENDER_OBJECT_AND_COLLECTION_ERROR, start, end);

  render_object_and_collection_error->base.message = hb_string_copy(hb_string("The `object:` and `collection:` options are mutually exclusive. Use one or the other in your `render` call."), allocator);

  return render_object_and_collection_error;
}

void append_render_object_and_collection_error(position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, render_object_and_collection_error_init(start, end, allocator));
}

RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T* render_layout_without_block_error_init(hb_string_T layout, position_T start, position_T end, hb_allocator_T* allocator) {
  RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T* render_layout_without_block_error = hb_allocator_alloc(allocator, sizeof(RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T));

  if (!render_layout_without_block_error) { return NULL; }

  error_init(&render_layout_without_block_error->base, RENDER_LAYOUT_WITHOUT_BLOCK_ERROR, start, end);

  const char* message_template = "Layout rendering needs a block. Use `render layout: '%s' do ... end` so the layout has content to wrap.";

  hb_string_T truncated_argument_0 = hb_string_truncate(layout, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[232];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  render_layout_without_block_error->base.message = hb_string_copy(hb_string(message), allocator);

  render_layout_without_block_error->layout = hb_string_copy(layout, allocator);
  return render_layout_without_block_error;
}

void append_render_layout_without_block_error(hb_string_T layout, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, render_layout_without_block_error_init(layout, start, end, allocator));
}

STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T* strict_locals_positional_argument_error_init(hb_string_T name, position_T start, position_T end, hb_allocator_T* allocator) {
  STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T* strict_locals_positional_argument_error = hb_allocator_alloc(allocator, sizeof(STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T));

  if (!strict_locals_positional_argument_error) { return NULL; }

  error_init(&strict_locals_positional_argument_error->base, STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR, start, end);

  const char* message_template = "Strict locals only support keyword arguments. Positional argument `%s` is not allowed. Use keyword argument format: `%s:`.";

  hb_string_T truncated_argument_0 = hb_string_truncate(name, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_1 = hb_string_truncate(name, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_1 = hb_string_to_c_string_using_malloc(truncated_argument_1);

  char message[379];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    truncated_c_string_1 ? truncated_c_string_1 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_1) { free(truncated_c_string_1); }
  strict_locals_positional_argument_error->base.message = hb_string_copy(hb_string(message), allocator);

  strict_locals_positional_argument_error->name = hb_string_copy(name, allocator);
  return strict_locals_positional_argument_error;
}

void append_strict_locals_positional_argument_error(hb_string_T name, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, strict_locals_positional_argument_error_init(name, start, end, allocator));
}

STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T* strict_locals_block_argument_error_init(hb_string_T name, position_T start, position_T end, hb_allocator_T* allocator) {
  STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T* strict_locals_block_argument_error = hb_allocator_alloc(allocator, sizeof(STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T));

  if (!strict_locals_block_argument_error) { return NULL; }

  error_init(&strict_locals_block_argument_error->base, STRICT_LOCALS_BLOCK_ARGUMENT_ERROR, start, end);

  const char* message_template = "Strict locals only support keyword arguments. Block argument `&%s` is not allowed.";

  hb_string_T truncated_argument_0 = hb_string_truncate(name, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[211];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  strict_locals_block_argument_error->base.message = hb_string_copy(hb_string(message), allocator);

  strict_locals_block_argument_error->name = hb_string_copy(name, allocator);
  return strict_locals_block_argument_error;
}

void append_strict_locals_block_argument_error(hb_string_T name, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, strict_locals_block_argument_error_init(name, start, end, allocator));
}

STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T* strict_locals_splat_argument_error_init(hb_string_T name, position_T start, position_T end, hb_allocator_T* allocator) {
  STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T* strict_locals_splat_argument_error = hb_allocator_alloc(allocator, sizeof(STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T));

  if (!strict_locals_splat_argument_error) { return NULL; }

  error_init(&strict_locals_splat_argument_error->base, STRICT_LOCALS_SPLAT_ARGUMENT_ERROR, start, end);

  const char* message_template = "Strict locals only support keyword arguments. Splat argument `*%s` is not allowed.";

  hb_string_T truncated_argument_0 = hb_string_truncate(name, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[211];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  strict_locals_splat_argument_error->base.message = hb_string_copy(hb_string(message), allocator);

  strict_locals_splat_argument_error->name = hb_string_copy(name, allocator);
  return strict_locals_splat_argument_error;
}

void append_strict_locals_splat_argument_error(hb_string_T name, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, strict_locals_splat_argument_error_init(name, start, end, allocator));
}

STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T* strict_locals_missing_parenthesis_error_init(hb_string_T rest, position_T start, position_T end, hb_allocator_T* allocator) {
  STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T* strict_locals_missing_parenthesis_error = hb_allocator_alloc(allocator, sizeof(STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T));

  if (!strict_locals_missing_parenthesis_error) { return NULL; }

  error_init(&strict_locals_missing_parenthesis_error->base, STRICT_LOCALS_MISSING_PARENTHESIS_ERROR, start, end);

  const char* message_template = "Strict locals declaration requires parentheses. Expected `locals: (...)` but got `locals: %s`.";

  hb_string_T truncated_argument_0 = hb_string_truncate(rest, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[223];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  strict_locals_missing_parenthesis_error->base.message = hb_string_copy(hb_string(message), allocator);

  strict_locals_missing_parenthesis_error->rest = hb_string_copy(rest, allocator);
  return strict_locals_missing_parenthesis_error;
}

void append_strict_locals_missing_parenthesis_error(hb_string_T rest, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, strict_locals_missing_parenthesis_error_init(rest, start, end, allocator));
}

STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T* strict_locals_duplicate_declaration_error_init(position_T start, position_T end, hb_allocator_T* allocator) {
  STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T* strict_locals_duplicate_declaration_error = hb_allocator_alloc(allocator, sizeof(STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T));

  if (!strict_locals_duplicate_declaration_error) { return NULL; }

  error_init(&strict_locals_duplicate_declaration_error->base, STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR, start, end);

  strict_locals_duplicate_declaration_error->base.message = hb_string_copy(hb_string("Duplicate strict locals declaration. Only the first `<%# locals: (...) %>` declaration is used by Rails."), allocator);

  return strict_locals_duplicate_declaration_error;
}

void append_strict_locals_duplicate_declaration_error(position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, strict_locals_duplicate_declaration_error_init(start, end, allocator));
}

VOID_ELEMENT_CONTENT_ERROR_T* void_element_content_error_init(token_T* tag_name, hb_string_T helper_name, hb_string_T content_type, position_T start, position_T end, hb_allocator_T* allocator) {
  VOID_ELEMENT_CONTENT_ERROR_T* void_element_content_error = hb_allocator_alloc(allocator, sizeof(VOID_ELEMENT_CONTENT_ERROR_T));

  if (!void_element_content_error) { return NULL; }

  error_init(&void_element_content_error->base, VOID_ELEMENT_CONTENT_ERROR, start, end);

  const char* message_template = "Void element `%s` cannot have content. `%s` does not accept %s for content.";

  hb_string_T truncated_argument_0 = hb_string_truncate(tag_name->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  hb_string_T truncated_argument_1 = hb_string_truncate(helper_name, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_1 = hb_string_to_c_string_using_malloc(truncated_argument_1);

  hb_string_T truncated_argument_2 = hb_string_truncate(content_type, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_2 = hb_string_to_c_string_using_malloc(truncated_argument_2);

  char message[460];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : "",
    truncated_c_string_1 ? truncated_c_string_1 : "",
    truncated_c_string_2 ? truncated_c_string_2 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  if (truncated_c_string_1) { free(truncated_c_string_1); }
  if (truncated_c_string_2) { free(truncated_c_string_2); }
  void_element_content_error->base.message = hb_string_copy(hb_string(message), allocator);

  void_element_content_error->tag_name = token_copy(tag_name, allocator);
  void_element_content_error->helper_name = hb_string_copy(helper_name, allocator);
  void_element_content_error->content_type = hb_string_copy(content_type, allocator);
  return void_element_content_error;
}

void append_void_element_content_error(token_T* tag_name, hb_string_T helper_name, hb_string_T content_type, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, void_element_content_error_init(tag_name, helper_name, content_type, start, end, allocator));
}

DOT_NOTATION_CASING_ERROR_T* dot_notation_casing_error_init(token_T* segment, position_T start, position_T end, hb_allocator_T* allocator) {
  DOT_NOTATION_CASING_ERROR_T* dot_notation_casing_error = hb_allocator_alloc(allocator, sizeof(DOT_NOTATION_CASING_ERROR_T));

  if (!dot_notation_casing_error) { return NULL; }

  error_init(&dot_notation_casing_error->base, DOT_NOTATION_CASING_ERROR, start, end);

  const char* message_template = "Dot-notation component tags require the first segment to start with an uppercase letter. `%s` does not start with an uppercase letter.";

  hb_string_T truncated_argument_0 = hb_string_truncate(segment->value, ERROR_MESSAGES_TRUNCATED_LENGTH);
  char* truncated_c_string_0 = hb_string_to_c_string_using_malloc(truncated_argument_0);

  char message[263];
  snprintf(
    message,
    sizeof(message),
    message_template,
    truncated_c_string_0 ? truncated_c_string_0 : ""
  );

  if (truncated_c_string_0) { free(truncated_c_string_0); }
  dot_notation_casing_error->base.message = hb_string_copy(hb_string(message), allocator);

  dot_notation_casing_error->segment = token_copy(segment, allocator);
  return dot_notation_casing_error;
}

void append_dot_notation_casing_error(token_T* segment, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
  hb_array_append(errors, dot_notation_casing_error_init(segment, start, end, allocator));
}

hb_string_T error_type_to_string(ERROR_T* error) {
  switch (error->type) {
    case UNEXPECTED_ERROR: return hb_string("UNEXPECTED_ERROR");
    case UNEXPECTED_TOKEN_ERROR: return hb_string("UNEXPECTED_TOKEN_ERROR");
    case MISSING_OPENING_TAG_ERROR: return hb_string("MISSING_OPENING_TAG_ERROR");
    case MISSING_CLOSING_TAG_ERROR: return hb_string("MISSING_CLOSING_TAG_ERROR");
    case TAG_NAMES_MISMATCH_ERROR: return hb_string("TAG_NAMES_MISMATCH_ERROR");
    case VOID_ELEMENT_CLOSING_TAG_ERROR: return hb_string("VOID_ELEMENT_CLOSING_TAG_ERROR");
    case UNCLOSED_ELEMENT_ERROR: return hb_string("UNCLOSED_ELEMENT_ERROR");
    case RUBY_PARSE_ERROR: return hb_string("RUBY_PARSE_ERROR");
    case ERB_CONTROL_FLOW_SCOPE_ERROR: return hb_string("ERB_CONTROL_FLOW_SCOPE_ERROR");
    case MISSING_ERB_END_TAG_ERROR: return hb_string("MISSING_ERB_END_TAG_ERROR");
    case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: return hb_string("ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR");
    case ERB_CASE_WITH_CONDITIONS_ERROR: return hb_string("ERB_CASE_WITH_CONDITIONS_ERROR");
    case CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR: return hb_string("CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR");
    case CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR: return hb_string("CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR");
    case INVALID_COMMENT_CLOSING_TAG_ERROR: return hb_string("INVALID_COMMENT_CLOSING_TAG_ERROR");
    case OMITTED_CLOSING_TAG_ERROR: return hb_string("OMITTED_CLOSING_TAG_ERROR");
    case UNCLOSED_OPEN_TAG_ERROR: return hb_string("UNCLOSED_OPEN_TAG_ERROR");
    case UNCLOSED_CLOSE_TAG_ERROR: return hb_string("UNCLOSED_CLOSE_TAG_ERROR");
    case UNCLOSED_QUOTE_ERROR: return hb_string("UNCLOSED_QUOTE_ERROR");
    case MISSING_ATTRIBUTE_VALUE_ERROR: return hb_string("MISSING_ATTRIBUTE_VALUE_ERROR");
    case UNCLOSED_ERB_TAG_ERROR: return hb_string("UNCLOSED_ERB_TAG_ERROR");
    case STRAY_ERB_CLOSING_TAG_ERROR: return hb_string("STRAY_ERB_CLOSING_TAG_ERROR");
    case NESTED_ERB_TAG_ERROR: return hb_string("NESTED_ERB_TAG_ERROR");
    case RENDER_AMBIGUOUS_LOCALS_ERROR: return hb_string("RENDER_AMBIGUOUS_LOCALS_ERROR");
    case RENDER_MISSING_LOCALS_ERROR: return hb_string("RENDER_MISSING_LOCALS_ERROR");
    case RENDER_NO_ARGUMENTS_ERROR: return hb_string("RENDER_NO_ARGUMENTS_ERROR");
    case RENDER_CONFLICTING_PARTIAL_ERROR: return hb_string("RENDER_CONFLICTING_PARTIAL_ERROR");
    case RENDER_INVALID_AS_OPTION_ERROR: return hb_string("RENDER_INVALID_AS_OPTION_ERROR");
    case RENDER_OBJECT_AND_COLLECTION_ERROR: return hb_string("RENDER_OBJECT_AND_COLLECTION_ERROR");
    case RENDER_LAYOUT_WITHOUT_BLOCK_ERROR: return hb_string("RENDER_LAYOUT_WITHOUT_BLOCK_ERROR");
    case STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR: return hb_string("STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR");
    case STRICT_LOCALS_BLOCK_ARGUMENT_ERROR: return hb_string("STRICT_LOCALS_BLOCK_ARGUMENT_ERROR");
    case STRICT_LOCALS_SPLAT_ARGUMENT_ERROR: return hb_string("STRICT_LOCALS_SPLAT_ARGUMENT_ERROR");
    case STRICT_LOCALS_MISSING_PARENTHESIS_ERROR: return hb_string("STRICT_LOCALS_MISSING_PARENTHESIS_ERROR");
    case STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR: return hb_string("STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR");
    case VOID_ELEMENT_CONTENT_ERROR: return hb_string("VOID_ELEMENT_CONTENT_ERROR");
    case DOT_NOTATION_CASING_ERROR: return hb_string("DOT_NOTATION_CASING_ERROR");
  }

  return hb_string("Unknown error_type_T");
}

hb_string_T error_human_type(ERROR_T* error) {
  switch (error->type) {
    case UNEXPECTED_ERROR: return hb_string("UnexpectedError");
    case UNEXPECTED_TOKEN_ERROR: return hb_string("UnexpectedTokenError");
    case MISSING_OPENING_TAG_ERROR: return hb_string("MissingOpeningTagError");
    case MISSING_CLOSING_TAG_ERROR: return hb_string("MissingClosingTagError");
    case TAG_NAMES_MISMATCH_ERROR: return hb_string("TagNamesMismatchError");
    case VOID_ELEMENT_CLOSING_TAG_ERROR: return hb_string("VoidElementClosingTagError");
    case UNCLOSED_ELEMENT_ERROR: return hb_string("UnclosedElementError");
    case RUBY_PARSE_ERROR: return hb_string("RubyParseError");
    case ERB_CONTROL_FLOW_SCOPE_ERROR: return hb_string("ERBControlFlowScopeError");
    case MISSING_ERB_END_TAG_ERROR: return hb_string("MissingERBEndTagError");
    case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: return hb_string("ERBMultipleBlocksInTagError");
    case ERB_CASE_WITH_CONDITIONS_ERROR: return hb_string("ERBCaseWithConditionsError");
    case CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR: return hb_string("ConditionalElementMultipleTagsError");
    case CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR: return hb_string("ConditionalElementConditionMismatchError");
    case INVALID_COMMENT_CLOSING_TAG_ERROR: return hb_string("InvalidCommentClosingTagError");
    case OMITTED_CLOSING_TAG_ERROR: return hb_string("OmittedClosingTagError");
    case UNCLOSED_OPEN_TAG_ERROR: return hb_string("UnclosedOpenTagError");
    case UNCLOSED_CLOSE_TAG_ERROR: return hb_string("UnclosedCloseTagError");
    case UNCLOSED_QUOTE_ERROR: return hb_string("UnclosedQuoteError");
    case MISSING_ATTRIBUTE_VALUE_ERROR: return hb_string("MissingAttributeValueError");
    case UNCLOSED_ERB_TAG_ERROR: return hb_string("UnclosedERBTagError");
    case STRAY_ERB_CLOSING_TAG_ERROR: return hb_string("StrayERBClosingTagError");
    case NESTED_ERB_TAG_ERROR: return hb_string("NestedERBTagError");
    case RENDER_AMBIGUOUS_LOCALS_ERROR: return hb_string("RenderAmbiguousLocalsError");
    case RENDER_MISSING_LOCALS_ERROR: return hb_string("RenderMissingLocalsError");
    case RENDER_NO_ARGUMENTS_ERROR: return hb_string("RenderNoArgumentsError");
    case RENDER_CONFLICTING_PARTIAL_ERROR: return hb_string("RenderConflictingPartialError");
    case RENDER_INVALID_AS_OPTION_ERROR: return hb_string("RenderInvalidAsOptionError");
    case RENDER_OBJECT_AND_COLLECTION_ERROR: return hb_string("RenderObjectAndCollectionError");
    case RENDER_LAYOUT_WITHOUT_BLOCK_ERROR: return hb_string("RenderLayoutWithoutBlockError");
    case STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR: return hb_string("StrictLocalsPositionalArgumentError");
    case STRICT_LOCALS_BLOCK_ARGUMENT_ERROR: return hb_string("StrictLocalsBlockArgumentError");
    case STRICT_LOCALS_SPLAT_ARGUMENT_ERROR: return hb_string("StrictLocalsSplatArgumentError");
    case STRICT_LOCALS_MISSING_PARENTHESIS_ERROR: return hb_string("StrictLocalsMissingParenthesisError");
    case STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR: return hb_string("StrictLocalsDuplicateDeclarationError");
    case VOID_ELEMENT_CONTENT_ERROR: return hb_string("VoidElementContentError");
    case DOT_NOTATION_CASING_ERROR: return hb_string("DotNotationCasingError");
  }

  return hb_string("Unknown error_type_T");
}

void error_free_base_error(ERROR_T* error, hb_allocator_T* allocator) {
  if (error == NULL) { return; }

  if (!hb_string_is_null(error->message)) { hb_allocator_dealloc(allocator, error->message.data); }

  hb_allocator_dealloc(allocator, error);
}

static void error_free_unexpected_error(UNEXPECTED_ERROR_T* unexpected_error, hb_allocator_T* allocator) {
  if (unexpected_error->description.data != NULL) { hb_allocator_dealloc(allocator, unexpected_error->description.data); }
  if (unexpected_error->expected.data != NULL) { hb_allocator_dealloc(allocator, unexpected_error->expected.data); }
  if (unexpected_error->found.data != NULL) { hb_allocator_dealloc(allocator, unexpected_error->found.data); }

  error_free_base_error(&unexpected_error->base, allocator);
}

static void error_free_unexpected_token_error(UNEXPECTED_TOKEN_ERROR_T* unexpected_token_error, hb_allocator_T* allocator) {
  // token_type_T is part of struct
  if (unexpected_token_error->found != NULL) { token_free(unexpected_token_error->found, allocator); }

  error_free_base_error(&unexpected_token_error->base, allocator);
}

static void error_free_missing_opening_tag_error(MISSING_OPENING_TAG_ERROR_T* missing_opening_tag_error, hb_allocator_T* allocator) {
  if (missing_opening_tag_error->closing_tag != NULL) { token_free(missing_opening_tag_error->closing_tag, allocator); }

  error_free_base_error(&missing_opening_tag_error->base, allocator);
}

static void error_free_missing_closing_tag_error(MISSING_CLOSING_TAG_ERROR_T* missing_closing_tag_error, hb_allocator_T* allocator) {
  if (missing_closing_tag_error->opening_tag != NULL) { token_free(missing_closing_tag_error->opening_tag, allocator); }

  error_free_base_error(&missing_closing_tag_error->base, allocator);
}

static void error_free_tag_names_mismatch_error(TAG_NAMES_MISMATCH_ERROR_T* tag_names_mismatch_error, hb_allocator_T* allocator) {
  if (tag_names_mismatch_error->opening_tag != NULL) { token_free(tag_names_mismatch_error->opening_tag, allocator); }
  if (tag_names_mismatch_error->closing_tag != NULL) { token_free(tag_names_mismatch_error->closing_tag, allocator); }

  error_free_base_error(&tag_names_mismatch_error->base, allocator);
}

static void error_free_void_element_closing_tag_error(VOID_ELEMENT_CLOSING_TAG_ERROR_T* void_element_closing_tag_error, hb_allocator_T* allocator) {
  if (void_element_closing_tag_error->tag_name != NULL) { token_free(void_element_closing_tag_error->tag_name, allocator); }
  if (void_element_closing_tag_error->expected.data != NULL) { hb_allocator_dealloc(allocator, void_element_closing_tag_error->expected.data); }
  if (void_element_closing_tag_error->found.data != NULL) { hb_allocator_dealloc(allocator, void_element_closing_tag_error->found.data); }

  error_free_base_error(&void_element_closing_tag_error->base, allocator);
}

static void error_free_unclosed_element_error(UNCLOSED_ELEMENT_ERROR_T* unclosed_element_error, hb_allocator_T* allocator) {
  if (unclosed_element_error->opening_tag != NULL) { token_free(unclosed_element_error->opening_tag, allocator); }

  error_free_base_error(&unclosed_element_error->base, allocator);
}

static void error_free_ruby_parse_error(RUBY_PARSE_ERROR_T* ruby_parse_error, hb_allocator_T* allocator) {
  if (ruby_parse_error->error_message.data != NULL) { hb_allocator_dealloc(allocator, ruby_parse_error->error_message.data); }
  if (ruby_parse_error->diagnostic_id.data != NULL) { hb_allocator_dealloc(allocator, ruby_parse_error->diagnostic_id.data); }
  if (ruby_parse_error->level.data != NULL) { hb_allocator_dealloc(allocator, ruby_parse_error->level.data); }

  error_free_base_error(&ruby_parse_error->base, allocator);
}

static void error_free_erb_control_flow_scope_error(ERB_CONTROL_FLOW_SCOPE_ERROR_T* erb_control_flow_scope_error, hb_allocator_T* allocator) {
  if (erb_control_flow_scope_error->keyword.data != NULL) { hb_allocator_dealloc(allocator, erb_control_flow_scope_error->keyword.data); }

  error_free_base_error(&erb_control_flow_scope_error->base, allocator);
}

static void error_free_missing_erb_end_tag_error(MISSING_ERB_END_TAG_ERROR_T* missing_erb_end_tag_error, hb_allocator_T* allocator) {
  if (missing_erb_end_tag_error->keyword.data != NULL) { hb_allocator_dealloc(allocator, missing_erb_end_tag_error->keyword.data); }

  error_free_base_error(&missing_erb_end_tag_error->base, allocator);
}

static void error_free_erb_multiple_blocks_in_tag_error(ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T* erb_multiple_blocks_in_tag_error, hb_allocator_T* allocator) {
  /* no ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T specific fields to free up */

  error_free_base_error(&erb_multiple_blocks_in_tag_error->base, allocator);
}

static void error_free_erb_case_with_conditions_error(ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error, hb_allocator_T* allocator) {
  /* no ERB_CASE_WITH_CONDITIONS_ERROR_T specific fields to free up */

  error_free_base_error(&erb_case_with_conditions_error->base, allocator);
}

static void error_free_conditional_element_multiple_tags_error(CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR_T* conditional_element_multiple_tags_error, hb_allocator_T* allocator) {
  // size_t is part of struct
  // size_t is part of struct

  error_free_base_error(&conditional_element_multiple_tags_error->base, allocator);
}

static void error_free_conditional_element_condition_mismatch_error(CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR_T* conditional_element_condition_mismatch_error, hb_allocator_T* allocator) {
  if (conditional_element_condition_mismatch_error->tag_name.data != NULL) { hb_allocator_dealloc(allocator, conditional_element_condition_mismatch_error->tag_name.data); }
  if (conditional_element_condition_mismatch_error->open_condition.data != NULL) { hb_allocator_dealloc(allocator, conditional_element_condition_mismatch_error->open_condition.data); }
  // size_t is part of struct
  // size_t is part of struct
  if (conditional_element_condition_mismatch_error->close_condition.data != NULL) { hb_allocator_dealloc(allocator, conditional_element_condition_mismatch_error->close_condition.data); }
  // size_t is part of struct
  // size_t is part of struct

  error_free_base_error(&conditional_element_condition_mismatch_error->base, allocator);
}

static void error_free_invalid_comment_closing_tag_error(INVALID_COMMENT_CLOSING_TAG_ERROR_T* invalid_comment_closing_tag_error, hb_allocator_T* allocator) {
  if (invalid_comment_closing_tag_error->closing_tag != NULL) { token_free(invalid_comment_closing_tag_error->closing_tag, allocator); }

  error_free_base_error(&invalid_comment_closing_tag_error->base, allocator);
}

static void error_free_omitted_closing_tag_error(OMITTED_CLOSING_TAG_ERROR_T* omitted_closing_tag_error, hb_allocator_T* allocator) {
  if (omitted_closing_tag_error->opening_tag != NULL) { token_free(omitted_closing_tag_error->opening_tag, allocator); }
  // position_T is part of struct

  error_free_base_error(&omitted_closing_tag_error->base, allocator);
}

static void error_free_unclosed_open_tag_error(UNCLOSED_OPEN_TAG_ERROR_T* unclosed_open_tag_error, hb_allocator_T* allocator) {
  if (unclosed_open_tag_error->tag_name != NULL) { token_free(unclosed_open_tag_error->tag_name, allocator); }

  error_free_base_error(&unclosed_open_tag_error->base, allocator);
}

static void error_free_unclosed_close_tag_error(UNCLOSED_CLOSE_TAG_ERROR_T* unclosed_close_tag_error, hb_allocator_T* allocator) {
  if (unclosed_close_tag_error->tag_name != NULL) { token_free(unclosed_close_tag_error->tag_name, allocator); }

  error_free_base_error(&unclosed_close_tag_error->base, allocator);
}

static void error_free_unclosed_quote_error(UNCLOSED_QUOTE_ERROR_T* unclosed_quote_error, hb_allocator_T* allocator) {
  if (unclosed_quote_error->opening_quote != NULL) { token_free(unclosed_quote_error->opening_quote, allocator); }

  error_free_base_error(&unclosed_quote_error->base, allocator);
}

static void error_free_missing_attribute_value_error(MISSING_ATTRIBUTE_VALUE_ERROR_T* missing_attribute_value_error, hb_allocator_T* allocator) {
  if (missing_attribute_value_error->attribute_name.data != NULL) { hb_allocator_dealloc(allocator, missing_attribute_value_error->attribute_name.data); }

  error_free_base_error(&missing_attribute_value_error->base, allocator);
}

static void error_free_unclosed_erb_tag_error(UNCLOSED_ERB_TAG_ERROR_T* unclosed_erb_tag_error, hb_allocator_T* allocator) {
  if (unclosed_erb_tag_error->opening_tag != NULL) { token_free(unclosed_erb_tag_error->opening_tag, allocator); }

  error_free_base_error(&unclosed_erb_tag_error->base, allocator);
}

static void error_free_stray_erb_closing_tag_error(STRAY_ERB_CLOSING_TAG_ERROR_T* stray_erb_closing_tag_error, hb_allocator_T* allocator) {
  /* no STRAY_ERB_CLOSING_TAG_ERROR_T specific fields to free up */

  error_free_base_error(&stray_erb_closing_tag_error->base, allocator);
}

static void error_free_nested_erb_tag_error(NESTED_ERB_TAG_ERROR_T* nested_erb_tag_error, hb_allocator_T* allocator) {
  if (nested_erb_tag_error->opening_tag != NULL) { token_free(nested_erb_tag_error->opening_tag, allocator); }
  // size_t is part of struct
  // size_t is part of struct

  error_free_base_error(&nested_erb_tag_error->base, allocator);
}

static void error_free_render_ambiguous_locals_error(RENDER_AMBIGUOUS_LOCALS_ERROR_T* render_ambiguous_locals_error, hb_allocator_T* allocator) {
  if (render_ambiguous_locals_error->partial.data != NULL) { hb_allocator_dealloc(allocator, render_ambiguous_locals_error->partial.data); }

  error_free_base_error(&render_ambiguous_locals_error->base, allocator);
}

static void error_free_render_missing_locals_error(RENDER_MISSING_LOCALS_ERROR_T* render_missing_locals_error, hb_allocator_T* allocator) {
  if (render_missing_locals_error->partial.data != NULL) { hb_allocator_dealloc(allocator, render_missing_locals_error->partial.data); }
  if (render_missing_locals_error->keywords.data != NULL) { hb_allocator_dealloc(allocator, render_missing_locals_error->keywords.data); }

  error_free_base_error(&render_missing_locals_error->base, allocator);
}

static void error_free_render_no_arguments_error(RENDER_NO_ARGUMENTS_ERROR_T* render_no_arguments_error, hb_allocator_T* allocator) {
  /* no RENDER_NO_ARGUMENTS_ERROR_T specific fields to free up */

  error_free_base_error(&render_no_arguments_error->base, allocator);
}

static void error_free_render_conflicting_partial_error(RENDER_CONFLICTING_PARTIAL_ERROR_T* render_conflicting_partial_error, hb_allocator_T* allocator) {
  if (render_conflicting_partial_error->positional_partial.data != NULL) { hb_allocator_dealloc(allocator, render_conflicting_partial_error->positional_partial.data); }
  if (render_conflicting_partial_error->keyword_partial.data != NULL) { hb_allocator_dealloc(allocator, render_conflicting_partial_error->keyword_partial.data); }

  error_free_base_error(&render_conflicting_partial_error->base, allocator);
}

static void error_free_render_invalid_as_option_error(RENDER_INVALID_AS_OPTION_ERROR_T* render_invalid_as_option_error, hb_allocator_T* allocator) {
  if (render_invalid_as_option_error->as_value.data != NULL) { hb_allocator_dealloc(allocator, render_invalid_as_option_error->as_value.data); }

  error_free_base_error(&render_invalid_as_option_error->base, allocator);
}

static void error_free_render_object_and_collection_error(RENDER_OBJECT_AND_COLLECTION_ERROR_T* render_object_and_collection_error, hb_allocator_T* allocator) {
  /* no RENDER_OBJECT_AND_COLLECTION_ERROR_T specific fields to free up */

  error_free_base_error(&render_object_and_collection_error->base, allocator);
}

static void error_free_render_layout_without_block_error(RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T* render_layout_without_block_error, hb_allocator_T* allocator) {
  if (render_layout_without_block_error->layout.data != NULL) { hb_allocator_dealloc(allocator, render_layout_without_block_error->layout.data); }

  error_free_base_error(&render_layout_without_block_error->base, allocator);
}

static void error_free_strict_locals_positional_argument_error(STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T* strict_locals_positional_argument_error, hb_allocator_T* allocator) {
  if (strict_locals_positional_argument_error->name.data != NULL) { hb_allocator_dealloc(allocator, strict_locals_positional_argument_error->name.data); }

  error_free_base_error(&strict_locals_positional_argument_error->base, allocator);
}

static void error_free_strict_locals_block_argument_error(STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T* strict_locals_block_argument_error, hb_allocator_T* allocator) {
  if (strict_locals_block_argument_error->name.data != NULL) { hb_allocator_dealloc(allocator, strict_locals_block_argument_error->name.data); }

  error_free_base_error(&strict_locals_block_argument_error->base, allocator);
}

static void error_free_strict_locals_splat_argument_error(STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T* strict_locals_splat_argument_error, hb_allocator_T* allocator) {
  if (strict_locals_splat_argument_error->name.data != NULL) { hb_allocator_dealloc(allocator, strict_locals_splat_argument_error->name.data); }

  error_free_base_error(&strict_locals_splat_argument_error->base, allocator);
}

static void error_free_strict_locals_missing_parenthesis_error(STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T* strict_locals_missing_parenthesis_error, hb_allocator_T* allocator) {
  if (strict_locals_missing_parenthesis_error->rest.data != NULL) { hb_allocator_dealloc(allocator, strict_locals_missing_parenthesis_error->rest.data); }

  error_free_base_error(&strict_locals_missing_parenthesis_error->base, allocator);
}

static void error_free_strict_locals_duplicate_declaration_error(STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T* strict_locals_duplicate_declaration_error, hb_allocator_T* allocator) {
  /* no STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T specific fields to free up */

  error_free_base_error(&strict_locals_duplicate_declaration_error->base, allocator);
}

static void error_free_void_element_content_error(VOID_ELEMENT_CONTENT_ERROR_T* void_element_content_error, hb_allocator_T* allocator) {
  if (void_element_content_error->tag_name != NULL) { token_free(void_element_content_error->tag_name, allocator); }
  if (void_element_content_error->helper_name.data != NULL) { hb_allocator_dealloc(allocator, void_element_content_error->helper_name.data); }
  if (void_element_content_error->content_type.data != NULL) { hb_allocator_dealloc(allocator, void_element_content_error->content_type.data); }

  error_free_base_error(&void_element_content_error->base, allocator);
}

static void error_free_dot_notation_casing_error(DOT_NOTATION_CASING_ERROR_T* dot_notation_casing_error, hb_allocator_T* allocator) {
  if (dot_notation_casing_error->segment != NULL) { token_free(dot_notation_casing_error->segment, allocator); }

  error_free_base_error(&dot_notation_casing_error->base, allocator);
}

void error_free(ERROR_T* error, hb_allocator_T* allocator) {
  if (!error) { return; }

  switch (error->type) {
    case UNEXPECTED_ERROR: error_free_unexpected_error((UNEXPECTED_ERROR_T*) error, allocator); break;
    case UNEXPECTED_TOKEN_ERROR: error_free_unexpected_token_error((UNEXPECTED_TOKEN_ERROR_T*) error, allocator); break;
    case MISSING_OPENING_TAG_ERROR: error_free_missing_opening_tag_error((MISSING_OPENING_TAG_ERROR_T*) error, allocator); break;
    case MISSING_CLOSING_TAG_ERROR: error_free_missing_closing_tag_error((MISSING_CLOSING_TAG_ERROR_T*) error, allocator); break;
    case TAG_NAMES_MISMATCH_ERROR: error_free_tag_names_mismatch_error((TAG_NAMES_MISMATCH_ERROR_T*) error, allocator); break;
    case VOID_ELEMENT_CLOSING_TAG_ERROR: error_free_void_element_closing_tag_error((VOID_ELEMENT_CLOSING_TAG_ERROR_T*) error, allocator); break;
    case UNCLOSED_ELEMENT_ERROR: error_free_unclosed_element_error((UNCLOSED_ELEMENT_ERROR_T*) error, allocator); break;
    case RUBY_PARSE_ERROR: error_free_ruby_parse_error((RUBY_PARSE_ERROR_T*) error, allocator); break;
    case ERB_CONTROL_FLOW_SCOPE_ERROR: error_free_erb_control_flow_scope_error((ERB_CONTROL_FLOW_SCOPE_ERROR_T*) error, allocator); break;
    case MISSING_ERB_END_TAG_ERROR: error_free_missing_erb_end_tag_error((MISSING_ERB_END_TAG_ERROR_T*) error, allocator); break;
    case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: error_free_erb_multiple_blocks_in_tag_error((ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T*) error, allocator); break;
    case ERB_CASE_WITH_CONDITIONS_ERROR: error_free_erb_case_with_conditions_error((ERB_CASE_WITH_CONDITIONS_ERROR_T*) error, allocator); break;
    case CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR: error_free_conditional_element_multiple_tags_error((CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR_T*) error, allocator); break;
    case CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR: error_free_conditional_element_condition_mismatch_error((CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR_T*) error, allocator); break;
    case INVALID_COMMENT_CLOSING_TAG_ERROR: error_free_invalid_comment_closing_tag_error((INVALID_COMMENT_CLOSING_TAG_ERROR_T*) error, allocator); break;
    case OMITTED_CLOSING_TAG_ERROR: error_free_omitted_closing_tag_error((OMITTED_CLOSING_TAG_ERROR_T*) error, allocator); break;
    case UNCLOSED_OPEN_TAG_ERROR: error_free_unclosed_open_tag_error((UNCLOSED_OPEN_TAG_ERROR_T*) error, allocator); break;
    case UNCLOSED_CLOSE_TAG_ERROR: error_free_unclosed_close_tag_error((UNCLOSED_CLOSE_TAG_ERROR_T*) error, allocator); break;
    case UNCLOSED_QUOTE_ERROR: error_free_unclosed_quote_error((UNCLOSED_QUOTE_ERROR_T*) error, allocator); break;
    case MISSING_ATTRIBUTE_VALUE_ERROR: error_free_missing_attribute_value_error((MISSING_ATTRIBUTE_VALUE_ERROR_T*) error, allocator); break;
    case UNCLOSED_ERB_TAG_ERROR: error_free_unclosed_erb_tag_error((UNCLOSED_ERB_TAG_ERROR_T*) error, allocator); break;
    case STRAY_ERB_CLOSING_TAG_ERROR: error_free_stray_erb_closing_tag_error((STRAY_ERB_CLOSING_TAG_ERROR_T*) error, allocator); break;
    case NESTED_ERB_TAG_ERROR: error_free_nested_erb_tag_error((NESTED_ERB_TAG_ERROR_T*) error, allocator); break;
    case RENDER_AMBIGUOUS_LOCALS_ERROR: error_free_render_ambiguous_locals_error((RENDER_AMBIGUOUS_LOCALS_ERROR_T*) error, allocator); break;
    case RENDER_MISSING_LOCALS_ERROR: error_free_render_missing_locals_error((RENDER_MISSING_LOCALS_ERROR_T*) error, allocator); break;
    case RENDER_NO_ARGUMENTS_ERROR: error_free_render_no_arguments_error((RENDER_NO_ARGUMENTS_ERROR_T*) error, allocator); break;
    case RENDER_CONFLICTING_PARTIAL_ERROR: error_free_render_conflicting_partial_error((RENDER_CONFLICTING_PARTIAL_ERROR_T*) error, allocator); break;
    case RENDER_INVALID_AS_OPTION_ERROR: error_free_render_invalid_as_option_error((RENDER_INVALID_AS_OPTION_ERROR_T*) error, allocator); break;
    case RENDER_OBJECT_AND_COLLECTION_ERROR: error_free_render_object_and_collection_error((RENDER_OBJECT_AND_COLLECTION_ERROR_T*) error, allocator); break;
    case RENDER_LAYOUT_WITHOUT_BLOCK_ERROR: error_free_render_layout_without_block_error((RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T*) error, allocator); break;
    case STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR: error_free_strict_locals_positional_argument_error((STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T*) error, allocator); break;
    case STRICT_LOCALS_BLOCK_ARGUMENT_ERROR: error_free_strict_locals_block_argument_error((STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T*) error, allocator); break;
    case STRICT_LOCALS_SPLAT_ARGUMENT_ERROR: error_free_strict_locals_splat_argument_error((STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T*) error, allocator); break;
    case STRICT_LOCALS_MISSING_PARENTHESIS_ERROR: error_free_strict_locals_missing_parenthesis_error((STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T*) error, allocator); break;
    case STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR: error_free_strict_locals_duplicate_declaration_error((STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T*) error, allocator); break;
    case VOID_ELEMENT_CONTENT_ERROR: error_free_void_element_content_error((VOID_ELEMENT_CONTENT_ERROR_T*) error, allocator); break;
    case DOT_NOTATION_CASING_ERROR: error_free_dot_notation_casing_error((DOT_NOTATION_CASING_ERROR_T*) error, allocator); break;
  }
}

#ifndef HERB_EXCLUDE_PRETTYPRINT

void error_pretty_print_array(
  const char* name, hb_array_T* array, const size_t indent, const size_t relative_indent, const bool last_property,
  hb_buffer_T* buffer
) {
  if (array == NULL) {
    pretty_print_property(hb_string(name), hb_string("∅"), indent, relative_indent, last_property, buffer);

    return;
  }

  if (hb_array_size(array) == 0) {
    pretty_print_property(hb_string(name), hb_string("[]"), indent, relative_indent, last_property, buffer);

    return;
  }

  pretty_print_label(hb_string(name), indent, relative_indent, last_property, buffer);

  hb_buffer_append(buffer, "(");

  char count[16];
  sprintf(count, "%zu", hb_array_size(array));
  hb_buffer_append(buffer, count);
  hb_buffer_append(buffer, ")\n");

  if (indent < 20) {
    for (size_t i = 0; i < hb_array_size(array); i++) {
      ERROR_T* child = hb_array_get(array, i);
      pretty_print_indent(buffer, indent);
      pretty_print_indent(buffer, relative_indent + 1);

      if (i == hb_array_size(array) - 1) {
        hb_buffer_append(buffer, "└── ");
      } else {
        hb_buffer_append(buffer, "├── ");
      }

      error_pretty_print(child, indent + 1, relative_indent + 1, buffer);

      if (i != hb_array_size(array) - 1) { pretty_print_newline(indent + 1, relative_indent, buffer); }
    }
  }
}

static void error_pretty_print_unexpected_error(UNEXPECTED_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("description"), error->description, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("expected"), error->expected, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("found"), error->found, indent, relative_indent, true, buffer);
}

static void error_pretty_print_unexpected_token_error(UNEXPECTED_TOKEN_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_property(token_type_to_string(error->expected_type), hb_string("expected_type"), indent, relative_indent, false, buffer);
  pretty_print_token_property(error->found, hb_string("found"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_missing_opening_tag_error(MISSING_OPENING_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->closing_tag, hb_string("closing_tag"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_missing_closing_tag_error(MISSING_CLOSING_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->opening_tag, hb_string("opening_tag"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_tag_names_mismatch_error(TAG_NAMES_MISMATCH_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->opening_tag, hb_string("opening_tag"), indent, relative_indent, false, buffer);
  pretty_print_token_property(error->closing_tag, hb_string("closing_tag"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_void_element_closing_tag_error(VOID_ELEMENT_CLOSING_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->tag_name, hb_string("tag_name"), indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("expected"), error->expected, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("found"), error->found, indent, relative_indent, true, buffer);
}

static void error_pretty_print_unclosed_element_error(UNCLOSED_ELEMENT_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->opening_tag, hb_string("opening_tag"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_ruby_parse_error(RUBY_PARSE_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("error_message"), error->error_message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("diagnostic_id"), error->diagnostic_id, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("level"), error->level, indent, relative_indent, true, buffer);
}

static void error_pretty_print_erb_control_flow_scope_error(ERB_CONTROL_FLOW_SCOPE_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("keyword"), error->keyword, indent, relative_indent, true, buffer);
}

static void error_pretty_print_missing_erb_end_tag_error(MISSING_ERB_END_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("keyword"), error->keyword, indent, relative_indent, true, buffer);
}

static void error_pretty_print_erb_multiple_blocks_in_tag_error(ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, true, buffer);
}

static void error_pretty_print_erb_case_with_conditions_error(ERB_CASE_WITH_CONDITIONS_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, true, buffer);
}

static void error_pretty_print_conditional_element_multiple_tags_error(CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_size_t_property(error->line, hb_string("line"), indent, relative_indent, false, buffer);
  pretty_print_size_t_property(error->column, hb_string("column"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_conditional_element_condition_mismatch_error(CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("tag_name"), error->tag_name, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("open_condition"), error->open_condition, indent, relative_indent, false, buffer);
  pretty_print_size_t_property(error->open_line, hb_string("open_line"), indent, relative_indent, false, buffer);
  pretty_print_size_t_property(error->open_column, hb_string("open_column"), indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("close_condition"), error->close_condition, indent, relative_indent, false, buffer);
  pretty_print_size_t_property(error->close_line, hb_string("close_line"), indent, relative_indent, false, buffer);
  pretty_print_size_t_property(error->close_column, hb_string("close_column"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_invalid_comment_closing_tag_error(INVALID_COMMENT_CLOSING_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->closing_tag, hb_string("closing_tag"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_omitted_closing_tag_error(OMITTED_CLOSING_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->opening_tag, hb_string("opening_tag"), indent, relative_indent, false, buffer);
  pretty_print_position_property(&error->insertion_point, hb_string("insertion_point"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_unclosed_open_tag_error(UNCLOSED_OPEN_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->tag_name, hb_string("tag_name"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_unclosed_close_tag_error(UNCLOSED_CLOSE_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->tag_name, hb_string("tag_name"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_unclosed_quote_error(UNCLOSED_QUOTE_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->opening_quote, hb_string("opening_quote"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_missing_attribute_value_error(MISSING_ATTRIBUTE_VALUE_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("attribute_name"), error->attribute_name, indent, relative_indent, true, buffer);
}

static void error_pretty_print_unclosed_erb_tag_error(UNCLOSED_ERB_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->opening_tag, hb_string("opening_tag"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_stray_erb_closing_tag_error(STRAY_ERB_CLOSING_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, true, buffer);
}

static void error_pretty_print_nested_erb_tag_error(NESTED_ERB_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->opening_tag, hb_string("opening_tag"), indent, relative_indent, false, buffer);
  pretty_print_size_t_property(error->nested_tag_line, hb_string("nested_tag_line"), indent, relative_indent, false, buffer);
  pretty_print_size_t_property(error->nested_tag_column, hb_string("nested_tag_column"), indent, relative_indent, true, buffer);
}

static void error_pretty_print_render_ambiguous_locals_error(RENDER_AMBIGUOUS_LOCALS_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("partial"), error->partial, indent, relative_indent, true, buffer);
}

static void error_pretty_print_render_missing_locals_error(RENDER_MISSING_LOCALS_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("partial"), error->partial, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("keywords"), error->keywords, indent, relative_indent, true, buffer);
}

static void error_pretty_print_render_no_arguments_error(RENDER_NO_ARGUMENTS_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, true, buffer);
}

static void error_pretty_print_render_conflicting_partial_error(RENDER_CONFLICTING_PARTIAL_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("positional_partial"), error->positional_partial, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("keyword_partial"), error->keyword_partial, indent, relative_indent, true, buffer);
}

static void error_pretty_print_render_invalid_as_option_error(RENDER_INVALID_AS_OPTION_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("as_value"), error->as_value, indent, relative_indent, true, buffer);
}

static void error_pretty_print_render_object_and_collection_error(RENDER_OBJECT_AND_COLLECTION_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, true, buffer);
}

static void error_pretty_print_render_layout_without_block_error(RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("layout"), error->layout, indent, relative_indent, true, buffer);
}

static void error_pretty_print_strict_locals_positional_argument_error(STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("name"), error->name, indent, relative_indent, true, buffer);
}

static void error_pretty_print_strict_locals_block_argument_error(STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("name"), error->name, indent, relative_indent, true, buffer);
}

static void error_pretty_print_strict_locals_splat_argument_error(STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("name"), error->name, indent, relative_indent, true, buffer);
}

static void error_pretty_print_strict_locals_missing_parenthesis_error(STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("rest"), error->rest, indent, relative_indent, true, buffer);
}

static void error_pretty_print_strict_locals_duplicate_declaration_error(STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, true, buffer);
}

static void error_pretty_print_void_element_content_error(VOID_ELEMENT_CONTENT_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->tag_name, hb_string("tag_name"), indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("helper_name"), error->helper_name, indent, relative_indent, false, buffer);
  pretty_print_quoted_property(hb_string("content_type"), error->content_type, indent, relative_indent, true, buffer);
}

static void error_pretty_print_dot_notation_casing_error(DOT_NOTATION_CASING_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  hb_buffer_append(buffer, "@ ");
  hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
  hb_buffer_append(buffer, " ");

  pretty_print_location(error->base.location, buffer);
  hb_buffer_append(buffer, "\n");

  pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
  pretty_print_token_property(error->segment, hb_string("segment"), indent, relative_indent, true, buffer);
}

void error_pretty_print(ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
  if (!error) { return; }

  switch (error->type) {
    case UNEXPECTED_ERROR: error_pretty_print_unexpected_error((UNEXPECTED_ERROR_T*) error, indent, relative_indent, buffer); break;
    case UNEXPECTED_TOKEN_ERROR: error_pretty_print_unexpected_token_error((UNEXPECTED_TOKEN_ERROR_T*) error, indent, relative_indent, buffer); break;
    case MISSING_OPENING_TAG_ERROR: error_pretty_print_missing_opening_tag_error((MISSING_OPENING_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case MISSING_CLOSING_TAG_ERROR: error_pretty_print_missing_closing_tag_error((MISSING_CLOSING_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case TAG_NAMES_MISMATCH_ERROR: error_pretty_print_tag_names_mismatch_error((TAG_NAMES_MISMATCH_ERROR_T*) error, indent, relative_indent, buffer); break;
    case VOID_ELEMENT_CLOSING_TAG_ERROR: error_pretty_print_void_element_closing_tag_error((VOID_ELEMENT_CLOSING_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case UNCLOSED_ELEMENT_ERROR: error_pretty_print_unclosed_element_error((UNCLOSED_ELEMENT_ERROR_T*) error, indent, relative_indent, buffer); break;
    case RUBY_PARSE_ERROR: error_pretty_print_ruby_parse_error((RUBY_PARSE_ERROR_T*) error, indent, relative_indent, buffer); break;
    case ERB_CONTROL_FLOW_SCOPE_ERROR: error_pretty_print_erb_control_flow_scope_error((ERB_CONTROL_FLOW_SCOPE_ERROR_T*) error, indent, relative_indent, buffer); break;
    case MISSING_ERB_END_TAG_ERROR: error_pretty_print_missing_erb_end_tag_error((MISSING_ERB_END_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: error_pretty_print_erb_multiple_blocks_in_tag_error((ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case ERB_CASE_WITH_CONDITIONS_ERROR: error_pretty_print_erb_case_with_conditions_error((ERB_CASE_WITH_CONDITIONS_ERROR_T*) error, indent, relative_indent, buffer); break;
    case CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR: error_pretty_print_conditional_element_multiple_tags_error((CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR_T*) error, indent, relative_indent, buffer); break;
    case CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR: error_pretty_print_conditional_element_condition_mismatch_error((CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR_T*) error, indent, relative_indent, buffer); break;
    case INVALID_COMMENT_CLOSING_TAG_ERROR: error_pretty_print_invalid_comment_closing_tag_error((INVALID_COMMENT_CLOSING_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case OMITTED_CLOSING_TAG_ERROR: error_pretty_print_omitted_closing_tag_error((OMITTED_CLOSING_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case UNCLOSED_OPEN_TAG_ERROR: error_pretty_print_unclosed_open_tag_error((UNCLOSED_OPEN_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case UNCLOSED_CLOSE_TAG_ERROR: error_pretty_print_unclosed_close_tag_error((UNCLOSED_CLOSE_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case UNCLOSED_QUOTE_ERROR: error_pretty_print_unclosed_quote_error((UNCLOSED_QUOTE_ERROR_T*) error, indent, relative_indent, buffer); break;
    case MISSING_ATTRIBUTE_VALUE_ERROR: error_pretty_print_missing_attribute_value_error((MISSING_ATTRIBUTE_VALUE_ERROR_T*) error, indent, relative_indent, buffer); break;
    case UNCLOSED_ERB_TAG_ERROR: error_pretty_print_unclosed_erb_tag_error((UNCLOSED_ERB_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case STRAY_ERB_CLOSING_TAG_ERROR: error_pretty_print_stray_erb_closing_tag_error((STRAY_ERB_CLOSING_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case NESTED_ERB_TAG_ERROR: error_pretty_print_nested_erb_tag_error((NESTED_ERB_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
    case RENDER_AMBIGUOUS_LOCALS_ERROR: error_pretty_print_render_ambiguous_locals_error((RENDER_AMBIGUOUS_LOCALS_ERROR_T*) error, indent, relative_indent, buffer); break;
    case RENDER_MISSING_LOCALS_ERROR: error_pretty_print_render_missing_locals_error((RENDER_MISSING_LOCALS_ERROR_T*) error, indent, relative_indent, buffer); break;
    case RENDER_NO_ARGUMENTS_ERROR: error_pretty_print_render_no_arguments_error((RENDER_NO_ARGUMENTS_ERROR_T*) error, indent, relative_indent, buffer); break;
    case RENDER_CONFLICTING_PARTIAL_ERROR: error_pretty_print_render_conflicting_partial_error((RENDER_CONFLICTING_PARTIAL_ERROR_T*) error, indent, relative_indent, buffer); break;
    case RENDER_INVALID_AS_OPTION_ERROR: error_pretty_print_render_invalid_as_option_error((RENDER_INVALID_AS_OPTION_ERROR_T*) error, indent, relative_indent, buffer); break;
    case RENDER_OBJECT_AND_COLLECTION_ERROR: error_pretty_print_render_object_and_collection_error((RENDER_OBJECT_AND_COLLECTION_ERROR_T*) error, indent, relative_indent, buffer); break;
    case RENDER_LAYOUT_WITHOUT_BLOCK_ERROR: error_pretty_print_render_layout_without_block_error((RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T*) error, indent, relative_indent, buffer); break;
    case STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR: error_pretty_print_strict_locals_positional_argument_error((STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T*) error, indent, relative_indent, buffer); break;
    case STRICT_LOCALS_BLOCK_ARGUMENT_ERROR: error_pretty_print_strict_locals_block_argument_error((STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T*) error, indent, relative_indent, buffer); break;
    case STRICT_LOCALS_SPLAT_ARGUMENT_ERROR: error_pretty_print_strict_locals_splat_argument_error((STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T*) error, indent, relative_indent, buffer); break;
    case STRICT_LOCALS_MISSING_PARENTHESIS_ERROR: error_pretty_print_strict_locals_missing_parenthesis_error((STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T*) error, indent, relative_indent, buffer); break;
    case STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR: error_pretty_print_strict_locals_duplicate_declaration_error((STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T*) error, indent, relative_indent, buffer); break;
    case VOID_ELEMENT_CONTENT_ERROR: error_pretty_print_void_element_content_error((VOID_ELEMENT_CONTENT_ERROR_T*) error, indent, relative_indent, buffer); break;
    case DOT_NOTATION_CASING_ERROR: error_pretty_print_dot_notation_casing_error((DOT_NOTATION_CASING_ERROR_T*) error, indent, relative_indent, buffer); break;
  }
}

#endif
