diff --git a/packages/compiler/src/ml_parser/parser.ts b/packages/compiler/src/ml_parser/parser.ts index 905c25d583..24465f8e97 100644 --- a/packages/compiler/src/ml_parser/parser.ts +++ b/packages/compiler/src/ml_parser/parser.ts @@ -313,6 +313,7 @@ class _TreeBuilder { * opening tag is recovered). */ private _popElement(fullName: string, endSourceSpan: ParseSourceSpan|null): boolean { + let unexpectedCloseTagDetected = false; for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) { const el = this._elementStack[stackIndex]; if (el.name == fullName) { @@ -323,11 +324,14 @@ class _TreeBuilder { el.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : el.sourceSpan.end; this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex); - return true; + return !unexpectedCloseTagDetected; } if (!this.getTagDefinition(el.name).closedByParent) { - return false; + // Note that we encountered an unexpected close tag but continue processing the element + // stack so we can assign an `endSourceSpan` if there is a corresponding start tag for this + // end tag in the stack. + unexpectedCloseTagDetected = true; } } return false; diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts index 48e87d07cb..b971d9187a 100644 --- a/packages/compiler/test/ml_parser/html_parser_spec.ts +++ b/packages/compiler/test/ml_parser/html_parser_spec.ts @@ -857,6 +857,20 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} ]]); }); + it('gets correct close tag for parent when a child is not closed', () => { + const {errors, rootNodes} = parser.parse('
', 'TestComp'); + expect(errors.length).toEqual(1); + expect(humanizeErrors(errors)).toEqual([[ + 'div', + 'Unexpected closing tag "div". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags', + '0:11' + ]]); + expect(humanizeNodes(rootNodes, true)).toEqual([ + [html.Element, 'div', 0, '
', '
', '
'], + [html.Element, 'span', 1, '', '', null], + ]); + }); + describe('incomplete element tag', () => { it('should parse and report incomplete tags after the tag name', () => { const {errors, rootNodes} = parser.parse('
', 'TestComp');