Index: lib/parser/parserlib.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lib/parser/parserlib.php	(date 1531492965992)
+++ lib/parser/parserlib.php	(date 1531492965992)
@@ -2925,6 +2925,71 @@
 
 					$maketoc = $this->parse_data($maketoc, ['noparseplugins' => true]);
 
+					// Workaround issue #4235 "Tables of Contents (maketoc) can be broken when headings call plugins (such as ANAME and FOOTNOTE)"
+					// Heavy hack to remove invalid markup from each title
+					{
+						// Put non-parsed sections back so that HTML from plugins can be removed
+						$emptyArray = [];
+						$this->replace_preparse($maketoc, $emptyArray, $noparsed, $this->option['is_html']);
+
+						/* $matches[1] contains something like "<a href='#Buggy_heading' class='link'>".
+						$matches[2] contains something like "Buggy<a id="ref_footnote1" href="#footnote1" class="footlink ">1</a> heading".
+						In this example, the return value would be "<a href='#Buggy_heading' class='link'>Buggy heading</a>".
+
+						The conversion to and from DOM can alter the processed HTML fragment beyond the changes hoped for. Even if the rendering is identical, there may be changes in the HTML code (for example in whitespace). */
+						$cleanup = function ($matches)
+						{
+							$dom = new DOMDocument;
+							$encoding = 'utf-8'; // TODO: Verify - Works with a UTF-8 database, but may need adapting
+
+							// Wrap in a fake document to allow overriding charset
+							$fakeDocument = <<<START
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=$encoding"/>
+</head>
+<body>
+START
+. $matches[2] . '</body></html>';
+
+							$dom->loadHTML($fakeDocument, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+							$title = $dom->getElementsByTagName('body')->item(0);
+
+							$childrenToRemove = []; // Declare an array where removed elements are temporarily stored, since a foreach which uses removeChild() directly is buggy (as of PHP 7.1, has been that way for many versions).
+
+							foreach ($title->childNodes as $child) {
+								// Keep text and HTML comments
+								if ($child instanceof DOMText || $child instanceof DOMComment) {
+									continue;
+								}
+
+								// Keep superscript, which can be generated with the SUP plugin
+								if ($child instanceof DOMElement && $child->tagName == 'sup') {
+									continue;
+								}
+
+								$childrenToRemove[] = $child;
+							}
+							foreach ($childrenToRemove as $childToRemove) {
+								$title->removeChild($childToRemove);
+							}
+
+							$cleaned = $dom->saveHTML($title);
+							$length = strlen('<body>');
+							$link = $matches[1] .
+								substr($cleaned, $length, -($length + 1 /* length of a slash */)) . // substr() removes the opening ("<body>") and closing BODY tags.
+								'</a>';
+							return $link;
+						};
+						// preg_replace_callback() splits the TOC in titles before loading each one in the DOM extension, otherwise passing A elements generated by FOOTNOTE inside the mandatory A element causes the DOM extension to improvise a tree which is valid but does not match expectations.
+						$pattern = '$' .
+							"(?<=<li>)(<a href='[^']+' class='link'>)" . // Capturing subpattern 1, link start (preceded by a list item start tag, since each heading has its own LI element)
+							"((?s:.+?))(?# The s modifier allows newlines to match, so the pattern matches even if the title is on several lines. The final question mark makes the plus quantifier non-greedy.)" . // Capturing subpattern 2, link content
+							'</a>[\s]*(?=</li>|<ul>)' . // Link end. This needs to be specific enough since many things can generate a closing A tag, so use a lookahead. In most cases, the title links are immediately followed by closing LI tags, but headings of sections with subsections have their sub-headings in a UL element before they are closed, so accept "<ul>" as alternative too.
+							'$';
+						$maketoc = preg_replace_callback( $pattern, $cleanup, $maketoc);
+					}
+
 					if (preg_match("/^<ul>/", $maketoc)) {
 						$maketoc = preg_replace("/^<ul>/", '<ul class="toc">', $maketoc);
 						$maketoc .= '<!--toc-->';
