function is_tag_valid_with_parent(child_tag, parent_tag, child_loc, parent_loc) {
	if (child_tag.includes('-') || parent_tag?.includes('-')) return null; // custom elements can be anything

	if (parent_tag === 'template') return null; // no errors or warning should be thrown in immediate children of template tags

	const disallowed = disallowed_children[parent_tag];

	const child = child_loc ? `\`<${child_tag}>\` (${child_loc})` : `\`<${child_tag}>\``;
	const parent = parent_loc ? `\`<${parent_tag}>\` (${parent_loc})` : `\`<${parent_tag}>\``;

	if (disallowed) {
		if ('direct' in disallowed && disallowed.direct.includes(child_tag)) {
			return `${child} cannot be a direct child of ${parent}`;
		}

		if ('descendant' in disallowed && disallowed.descendant.includes(child_tag)) {
			return `${child} cannot be a child of ${parent}`;
		}

		if ('only' in disallowed && disallowed.only) {
			if (disallowed.only.includes(child_tag)) {
				return null;
			} else {
				return `${child} cannot be a child of ${parent}. \`<${parent_tag}>\` only allows these children: ${disallowed.only.map((d) => `\`<${d}>\``).join(', ')}`;
			}
		}
	}

	// These tags are only valid with a few parents that have special child
	// parsing rules - if we're down here, then none of those matched and
	// so we allow it only if we don't know what the parent is, as all other
	// cases are invalid (and we only get into this function if we know the parent).
	switch (child_tag) {
		case 'body':
		case 'caption':
		case 'col':
		case 'colgroup':
		case 'frameset':
		case 'frame':
		case 'head':
		case 'html':
			return `${child} cannot be a child of ${parent}`;
		case 'thead':
		case 'tbody':
		case 'tfoot':
			return `${child} must be the child of a \`<table>\`, not a ${parent}`;
		case 'td':
		case 'th':
			return `${child} must be the child of a \`<tr>\`, not a ${parent}`;
		case 'tr':
			return `\`<tr>\` must be the child of a \`<thead>\`, \`<tbody>\`, or \`<tfoot>\`, not a ${parent}`;
	}

	return null;
}
Returns an error message if the tag is not allowed inside the parent tag such that it will result
in the browser repairing the HTML, which will likely result in an error during hydration.
@param {string} child_tag
@param {string} parent_tag
@param {string} [child_loc]
@param {string} [parent_loc]
@returns {string | null}