function is_tag_valid_with_ancestor(child_tag, ancestors, child_loc, ancestor_loc) {
	if (child_tag.includes('-')) return null; // custom elements can be anything

	const ancestor_tag = ancestors[ancestors.length - 1];
	const disallowed = disallowed_children[ancestor_tag];
	if (!disallowed) return null;

	if ('reset_by' in disallowed && disallowed.reset_by) {
		for (let i = ancestors.length - 2; i >= 0; i--) {
			const ancestor = ancestors[i];
			if (ancestor.includes('-')) return null; // custom elements can be anything

			// A reset means that forbidden descendants are allowed again
			if (disallowed.reset_by.includes(ancestors[i])) {
				return null;
			}
		}
	}

	if ('descendant' in disallowed && disallowed.descendant.includes(child_tag)) {
		const child = child_loc ? `\`<${child_tag}>\` (${child_loc})` : `\`<${child_tag}>\``;
		const ancestor = ancestor_loc
			? `\`<${ancestor_tag}>\` (${ancestor_loc})`
			: `\`<${ancestor_tag}>\``;

		return `${child} cannot be a descendant of ${ancestor}`;
	}

	return null;
}
Returns an error message if the tag is not allowed inside the ancestor tag (which is grandparent and above) 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[]} ancestors All nodes starting with the parent, up until the ancestor, which means two entries minimum
@param {string} [child_loc]
@param {string} [ancestor_loc]
@returns {string | null}