Merge pull request #6291 from RyanCavanaugh/fix6280

Issue correct errors for missing JSX closing tags
This commit is contained in:
Ryan Cavanaugh 2016-01-05 10:23:49 -08:00
commit aed73bd139
9 changed files with 217 additions and 34 deletions

View file

@ -7954,31 +7954,12 @@ namespace ts {
return jsxElementType || anyType;
}
function tagNamesAreEquivalent(lhs: EntityName, rhs: EntityName): boolean {
if (lhs.kind !== rhs.kind) {
return false;
}
if (lhs.kind === SyntaxKind.Identifier) {
return (<Identifier>lhs).text === (<Identifier>rhs).text;
}
return (<QualifiedName>lhs).right.text === (<QualifiedName>rhs).right.text &&
tagNamesAreEquivalent((<QualifiedName>lhs).left, (<QualifiedName>rhs).left);
}
function checkJsxElement(node: JsxElement) {
// Check attributes
checkJsxOpeningLikeElement(node.openingElement);
// Check that the closing tag matches
if (!tagNamesAreEquivalent(node.openingElement.tagName, node.closingElement.tagName)) {
error(node.closingElement, Diagnostics.Expected_corresponding_JSX_closing_tag_for_0, getTextOfNode(node.openingElement.tagName));
}
else {
// Perform resolution on the closing tag so that rename/go to definition/etc work
getJsxElementTagSymbol(node.closingElement);
}
// Perform resolution on the closing tag so that rename/go to definition/etc work
getJsxElementTagSymbol(node.closingElement);
// Check children
for (const child of node.children) {

View file

@ -2586,5 +2586,9 @@
"A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.": {
"category": "Error",
"code": 17007
},
"JSX element '{0}' has no corresponding closing tag.": {
"category": "Error",
"code": 17008
}
}

View file

@ -3497,6 +3497,20 @@ namespace ts {
return finishNode(node);
}
function tagNamesAreEquivalent(lhs: EntityName, rhs: EntityName): boolean {
if (lhs.kind !== rhs.kind) {
return false;
}
if (lhs.kind === SyntaxKind.Identifier) {
return (<Identifier>lhs).text === (<Identifier>rhs).text;
}
return (<QualifiedName>lhs).right.text === (<QualifiedName>rhs).right.text &&
tagNamesAreEquivalent((<QualifiedName>lhs).left, (<QualifiedName>rhs).left);
}
function parseJsxElementOrSelfClosingElement(inExpressionContext: boolean): JsxElement | JsxSelfClosingElement {
const opening = parseJsxOpeningOrSelfClosingElement(inExpressionContext);
let result: JsxElement | JsxSelfClosingElement;
@ -3506,6 +3520,11 @@ namespace ts {
node.children = parseJsxChildren(node.openingElement.tagName);
node.closingElement = parseJsxClosingElement(inExpressionContext);
if (!tagNamesAreEquivalent(node.openingElement.tagName, node.closingElement.tagName)) {
parseErrorAtPosition(node.closingElement.pos, node.closingElement.end - node.closingElement.pos, Diagnostics.Expected_corresponding_JSX_closing_tag_for_0, getTextOfNodeFromSourceText(sourceText, node.openingElement.tagName));
}
result = finishNode(node);
}
else {
@ -3565,10 +3584,13 @@ namespace ts {
while (true) {
token = scanner.reScanJsxToken();
if (token === SyntaxKind.LessThanSlashToken) {
// Closing tag
break;
}
else if (token === SyntaxKind.EndOfFileToken) {
parseErrorAtCurrentToken(Diagnostics.Expected_corresponding_JSX_closing_tag_for_0, getTextOfNodeFromSourceText(sourceText, openingTagName));
// If we hit EOF, issue the error at the tag that lacks the closing element
// rather than at the end of the file (which is useless)
parseErrorAtPosition(openingTagName.pos, openingTagName.end - openingTagName.pos, Diagnostics.JSX_element_0_has_no_corresponding_closing_tag, getTextOfNodeFromSourceText(sourceText, openingTagName));
break;
}
result.push(parseJsxChild());

View file

@ -1,14 +1,20 @@
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(7,6): error TS17008: JSX element 'any' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(7,13): error TS2304: Cannot find name 'test'.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(7,17): error TS1005: '}' expected.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(9,6): error TS17008: JSX element 'any' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(11,6): error TS17008: JSX element 'foo' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(11,32): error TS1005: '}' expected.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(13,36): error TS1005: '}' expected.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(15,17): error TS17008: JSX element 'foo' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(15,45): error TS1005: '}' expected.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(19,2): error TS17008: JSX element 'foo' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(19,8): error TS17008: JSX element 'foo' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(19,13): error TS17008: JSX element 'foo' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(22,1): error TS1005: ':' expected.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(22,1): error TS17002: Expected corresponding JSX closing tag for 'any'.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(22,1): error TS17002: Expected corresponding JSX closing tag for 'foo'.
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(22,1): error TS1005: '</' expected.
==== tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx (8 errors) ====
==== tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx (14 errors) ====
declare var createElement: any;
@ -16,14 +22,20 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(22,1): error TS17002: Expect
var x: any;
x = <any> { test: <any></any> };
~~~
!!! error TS17008: JSX element 'any' has no corresponding closing tag.
~~~~
!!! error TS2304: Cannot find name 'test'.
~
!!! error TS1005: '}' expected.
x = <any><any></any>;
~~~
!!! error TS17008: JSX element 'any' has no corresponding closing tag.
x = <foo>hello {<foo>{}} </foo>;
~~~
!!! error TS17008: JSX element 'foo' has no corresponding closing tag.
~
!!! error TS1005: '}' expected.
@ -32,18 +44,24 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(22,1): error TS17002: Expect
!!! error TS1005: '}' expected.
x = <foo test={<foo>{}}>hello{<foo>{}}</foo>;
~~~
!!! error TS17008: JSX element 'foo' has no corresponding closing tag.
~
!!! error TS1005: '}' expected.
x = <foo>x</foo>, x = <foo/>;
<foo>{<foo><foo>{/foo/.test(x) ? <foo><foo></foo> : <foo><foo></foo>}</foo>}</foo>
~~~
!!! error TS17008: JSX element 'foo' has no corresponding closing tag.
~~~
!!! error TS17008: JSX element 'foo' has no corresponding closing tag.
~~~
!!! error TS17008: JSX element 'foo' has no corresponding closing tag.
!!! error TS1005: ':' expected.
!!! error TS17002: Expected corresponding JSX closing tag for 'any'.
!!! error TS17002: Expected corresponding JSX closing tag for 'foo'.
!!! error TS1005: '</' expected.

View file

@ -50,6 +50,8 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(17,3): error TS1003:
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(17,11): error TS1109: Expression expected.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(17,13): error TS2304: Cannot find name 'a'.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(17,22): error TS1109: Expression expected.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(18,2): error TS17008: JSX element 'a' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(19,2): error TS17008: JSX element 'a' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(22,10): error TS1005: '}' expected.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(23,20): error TS1003: Identifier expected.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(24,15): error TS1003: Identifier expected.
@ -58,14 +60,16 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(25,7): error TS2304:
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(27,17): error TS1005: '>' expected.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(28,10): error TS2304: Cannot find name 'props'.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(28,28): error TS1005: '>' expected.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(32,2): error TS17008: JSX element 'a' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(32,6): error TS1005: '{' expected.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(33,2): error TS17008: JSX element 'a' has no corresponding closing tag.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(33,6): error TS1005: '{' expected.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(33,7): error TS1109: Expression expected.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,4): error TS1003: Identifier expected.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,21): error TS17002: Expected corresponding JSX closing tag for 'a'.
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,21): error TS1005: '</' expected.
==== tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx (65 errors) ====
==== tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx (69 errors) ====
declare var React: any;
</>;
@ -188,7 +192,11 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,21): error TS17002
~
!!! error TS1109: Expression expected.
<a><a />;
~
!!! error TS17008: JSX element 'a' has no corresponding closing tag.
<a b={}>;
~
!!! error TS17008: JSX element 'a' has no corresponding closing tag.
var x = <div>one</div><div>two</div>;;
var x = <div>one</div> /* intervening comment */ <div>two</div>;;
<a>{"str";}</a>;
@ -218,9 +226,13 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,21): error TS17002
<a>></a>;
<a> ></a>;
<a b=}>;
~
!!! error TS17008: JSX element 'a' has no corresponding closing tag.
~
!!! error TS1005: '{' expected.
<a b=<}>;
~
!!! error TS17008: JSX element 'a' has no corresponding closing tag.
~
!!! error TS1005: '{' expected.
~
@ -230,4 +242,4 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,21): error TS17002
~~~
!!! error TS1003: Identifier expected.
!!! error TS17002: Expected corresponding JSX closing tag for 'a'.
!!! error TS1005: '</' expected.

View file

@ -0,0 +1,66 @@
tests/cases/conformance/jsx/Error1.tsx(2,11): error TS17008: JSX element 'div' has no corresponding closing tag.
tests/cases/conformance/jsx/Error1.tsx(2,21): error TS17002: Expected corresponding JSX closing tag for 'span'.
tests/cases/conformance/jsx/Error1.tsx(3,1): error TS1005: '</' expected.
tests/cases/conformance/jsx/Error2.tsx(1,15): error TS17002: Expected corresponding JSX closing tag for 'div'.
tests/cases/conformance/jsx/Error3.tsx(1,11): error TS17008: JSX element 'div' has no corresponding closing tag.
tests/cases/conformance/jsx/Error3.tsx(3,1): error TS1005: '</' expected.
tests/cases/conformance/jsx/Error4.tsx(1,11): error TS17008: JSX element 'div' has no corresponding closing tag.
tests/cases/conformance/jsx/Error4.tsx(1,20): error TS17002: Expected corresponding JSX closing tag for 'div'.
tests/cases/conformance/jsx/Error4.tsx(2,1): error TS1005: '</' expected.
tests/cases/conformance/jsx/Error5.tsx(1,11): error TS17008: JSX element 'div' has no corresponding closing tag.
tests/cases/conformance/jsx/Error5.tsx(1,16): error TS17008: JSX element 'span' has no corresponding closing tag.
tests/cases/conformance/jsx/Error5.tsx(3,1): error TS1005: '</' expected.
==== tests/cases/conformance/jsx/file.tsx (0 errors) ====
declare module JSX {
interface Element { }
interface IntrinsicElements {
[s: string]: any;
}
}
==== tests/cases/conformance/jsx/Error1.tsx (3 errors) ====
// Issue error about missing span closing tag, not missing div closing tag
let x1 = <div><span></div>;
~~~
!!! error TS17008: JSX element 'div' has no corresponding closing tag.
~~~~~~
!!! error TS17002: Expected corresponding JSX closing tag for 'span'.
!!! error TS1005: '</' expected.
==== tests/cases/conformance/jsx/Error2.tsx (1 errors) ====
let x2 = <div></span>;
~~~~~~~
!!! error TS17002: Expected corresponding JSX closing tag for 'div'.
==== tests/cases/conformance/jsx/Error3.tsx (2 errors) ====
let x3 = <div>;
~~~
!!! error TS17008: JSX element 'div' has no corresponding closing tag.
!!! error TS1005: '</' expected.
==== tests/cases/conformance/jsx/Error4.tsx (3 errors) ====
let x4 = <div><div></span>;
~~~
!!! error TS17008: JSX element 'div' has no corresponding closing tag.
~~~~~~~
!!! error TS17002: Expected corresponding JSX closing tag for 'div'.
!!! error TS1005: '</' expected.
==== tests/cases/conformance/jsx/Error5.tsx (3 errors) ====
let x5 = <div><span>
~~~
!!! error TS17008: JSX element 'div' has no corresponding closing tag.
~~~~
!!! error TS17008: JSX element 'span' has no corresponding closing tag.
!!! error TS1005: '</' expected.

View file

@ -0,0 +1,49 @@
//// [tests/cases/conformance/jsx/jsxParsingError2.tsx] ////
//// [file.tsx]
declare module JSX {
interface Element { }
interface IntrinsicElements {
[s: string]: any;
}
}
//// [Error1.tsx]
// Issue error about missing span closing tag, not missing div closing tag
let x1 = <div><span></div>;
//// [Error2.tsx]
let x2 = <div></span>;
//// [Error3.tsx]
let x3 = <div>;
//// [Error4.tsx]
let x4 = <div><div></span>;
//// [Error5.tsx]
let x5 = <div><span>
//// [file.jsx]
//// [Error1.jsx]
// Issue error about missing span closing tag, not missing div closing tag
var x1 = <div><span></div>;
</>;
//// [Error2.jsx]
var x2 = <div></span>;
//// [Error3.jsx]
var x3 = <div>;
</>;
//// [Error4.jsx]
var x4 = <div><div></span>;
</>;
//// [Error5.jsx]
var x5 = <div><span>
</></>;

View file

@ -1,15 +1,18 @@
tests/cases/conformance/jsx/file.tsx(5,11): error TS17008: JSX element 'div' has no corresponding closing tag.
tests/cases/conformance/jsx/file.tsx(5,19): error TS1109: Expression expected.
tests/cases/conformance/jsx/file.tsx(8,11): error TS2304: Cannot find name 'a'.
tests/cases/conformance/jsx/file.tsx(8,12): error TS1005: '}' expected.
tests/cases/conformance/jsx/file.tsx(9,1): error TS17002: Expected corresponding JSX closing tag for 'div'.
tests/cases/conformance/jsx/file.tsx(9,1): error TS1005: '</' expected.
==== tests/cases/conformance/jsx/file.tsx (4 errors) ====
==== tests/cases/conformance/jsx/file.tsx (5 errors) ====
declare namespace JSX { interface Element { } }
function foo() {
var x = <div> { </div>
~~~
!!! error TS17008: JSX element 'div' has no corresponding closing tag.
~~
!!! error TS1109: Expression expected.
}
@ -21,4 +24,4 @@ tests/cases/conformance/jsx/file.tsx(9,1): error TS17002: Expected corresponding
!!! error TS1005: '}' expected.
!!! error TS17002: Expected corresponding JSX closing tag for 'div'.
!!! error TS1005: '</' expected.

View file

@ -0,0 +1,28 @@
//@jsx: preserve
//@filename: file.tsx
declare module JSX {
interface Element { }
interface IntrinsicElements {
[s: string]: any;
}
}
// @filename: Error1.tsx
// Issue error about missing span closing tag, not missing div closing tag
let x1 = <div><span></div>;
// @filename: Error2.tsx
let x2 = <div></span>;
// @filename: Error3.tsx
let x3 = <div>;
// @filename: Error4.tsx
let x4 = <div><div></span>;
// @filename: Error5.tsx
let x5 = <div><span>