Merge pull request #6291 from RyanCavanaugh/fix6280
Issue correct errors for missing JSX closing tags
This commit is contained in:
commit
aed73bd139
9 changed files with 217 additions and 34 deletions
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
66
tests/baselines/reference/jsxParsingError2.errors.txt
Normal file
66
tests/baselines/reference/jsxParsingError2.errors.txt
Normal 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.
|
49
tests/baselines/reference/jsxParsingError2.js
Normal file
49
tests/baselines/reference/jsxParsingError2.js
Normal 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>
|
||||
|
||||
</></>;
|
|
@ -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.
|
28
tests/cases/conformance/jsx/jsxParsingError2.tsx
Normal file
28
tests/cases/conformance/jsx/jsxParsingError2.tsx
Normal 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>
|
||||
|
Loading…
Reference in a new issue