From a83ce339c9a2fe51fd796167a19c935c693ea521 Mon Sep 17 00:00:00 2001 From: Allan Guwatudde Date: Thu, 19 Mar 2020 21:40:43 +0300 Subject: [PATCH] Fix poor error span for unclosed JSX tags in the presence of whitespace/comments (#37419) * Improve jsx tag error span * Move solution to parseJsxChild func * Add tests and update baselines * Update comment in src/compiler/parser.ts Co-Authored-By: Daniel Rosenwasser * Use skipTrivia to check for whitespaces and other trivia * Import React into errorSpanForUnclosedJsxTag.tsx * . * . Co-authored-by: Daniel Rosenwasser --- src/compiler/parser.ts | 6 +++- .../errorSpanForUnclosedJsxTag.errors.txt | 23 +++++++++++++++ .../reference/errorSpanForUnclosedJsxTag.js | 21 ++++++++++++++ .../errorSpanForUnclosedJsxTag.symbols | 23 +++++++++++++++ .../errorSpanForUnclosedJsxTag.types | 29 +++++++++++++++++++ .../compiler/errorSpanForUnclosedJsxTag.tsx | 12 ++++++++ 6 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/errorSpanForUnclosedJsxTag.errors.txt create mode 100644 tests/baselines/reference/errorSpanForUnclosedJsxTag.js create mode 100644 tests/baselines/reference/errorSpanForUnclosedJsxTag.symbols create mode 100644 tests/baselines/reference/errorSpanForUnclosedJsxTag.types create mode 100644 tests/cases/compiler/errorSpanForUnclosedJsxTag.tsx diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 06d0f22a2ad..6d95c31cdab 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4545,7 +4545,11 @@ namespace ts { parseErrorAtRange(openingTag, Diagnostics.JSX_fragment_has_no_corresponding_closing_tag); } else { - parseErrorAtRange(openingTag.tagName, Diagnostics.JSX_element_0_has_no_corresponding_closing_tag, getTextOfNodeFromSourceText(sourceText, openingTag.tagName)); + // We want the error span to cover only 'Foo.Bar' in < Foo.Bar > + // or to cover only 'Foo' in < Foo > + const tag = openingTag.tagName; + const start = skipTrivia(sourceText, tag.pos); + parseErrorAt(start, tag.end, Diagnostics.JSX_element_0_has_no_corresponding_closing_tag, getTextOfNodeFromSourceText(sourceText, openingTag.tagName)); } return undefined; case SyntaxKind.LessThanSlashToken: diff --git a/tests/baselines/reference/errorSpanForUnclosedJsxTag.errors.txt b/tests/baselines/reference/errorSpanForUnclosedJsxTag.errors.txt new file mode 100644 index 00000000000..86bf974d2b2 --- /dev/null +++ b/tests/baselines/reference/errorSpanForUnclosedJsxTag.errors.txt @@ -0,0 +1,23 @@ +tests/cases/compiler/errorSpanForUnclosedJsxTag.tsx(9,14): error TS17008: JSX element 'Foo.Bar' has no corresponding closing tag. +tests/cases/compiler/errorSpanForUnclosedJsxTag.tsx(11,13): error TS17008: JSX element 'Baz' has no corresponding closing tag. +tests/cases/compiler/errorSpanForUnclosedJsxTag.tsx(11,23): error TS1005: ' {} + + let x = < Foo.Bar >Hello + ~~~~~~~ +!!! error TS17008: JSX element 'Foo.Bar' has no corresponding closing tag. + + let y = < Baz >Hello + ~~~ +!!! error TS17008: JSX element 'Baz' has no corresponding closing tag. + +!!! error TS1005: ' {} + +let x = < Foo.Bar >Hello + +let y = < Baz >Hello + +//// [errorSpanForUnclosedJsxTag.js] +var Foo = { + Bar: function () { } +}; +var Baz = function () { }; +var x = React.createElement(Foo.Bar, null, + "Hello let y = ", + React.createElement(Baz, null, "Hello")); diff --git a/tests/baselines/reference/errorSpanForUnclosedJsxTag.symbols b/tests/baselines/reference/errorSpanForUnclosedJsxTag.symbols new file mode 100644 index 00000000000..b45c5370b4d --- /dev/null +++ b/tests/baselines/reference/errorSpanForUnclosedJsxTag.symbols @@ -0,0 +1,23 @@ +=== tests/cases/compiler/errorSpanForUnclosedJsxTag.tsx === +declare const React: any +>React : Symbol(React, Decl(errorSpanForUnclosedJsxTag.tsx, 0, 13)) + +let Foo = { +>Foo : Symbol(Foo, Decl(errorSpanForUnclosedJsxTag.tsx, 2, 3)) + + Bar() {} +>Bar : Symbol(Bar, Decl(errorSpanForUnclosedJsxTag.tsx, 2, 11)) +} + +let Baz = () => {} +>Baz : Symbol(Baz, Decl(errorSpanForUnclosedJsxTag.tsx, 6, 3)) + +let x = < Foo.Bar >Hello +>x : Symbol(x, Decl(errorSpanForUnclosedJsxTag.tsx, 8, 3)) +>Foo.Bar : Symbol(Bar, Decl(errorSpanForUnclosedJsxTag.tsx, 2, 11)) +>Foo : Symbol(Foo, Decl(errorSpanForUnclosedJsxTag.tsx, 2, 3)) +>Bar : Symbol(Bar, Decl(errorSpanForUnclosedJsxTag.tsx, 2, 11)) + +let y = < Baz >Hello +>Baz : Symbol(Baz, Decl(errorSpanForUnclosedJsxTag.tsx, 6, 3)) + diff --git a/tests/baselines/reference/errorSpanForUnclosedJsxTag.types b/tests/baselines/reference/errorSpanForUnclosedJsxTag.types new file mode 100644 index 00000000000..5fd88448635 --- /dev/null +++ b/tests/baselines/reference/errorSpanForUnclosedJsxTag.types @@ -0,0 +1,29 @@ +=== tests/cases/compiler/errorSpanForUnclosedJsxTag.tsx === +declare const React: any +>React : any + +let Foo = { +>Foo : { Bar(): void; } +>{ Bar() {}} : { Bar(): void; } + + Bar() {} +>Bar : () => void +} + +let Baz = () => {} +>Baz : () => void +>() => {} : () => void + +let x = < Foo.Bar >Hello +>x : any +>< Foo.Bar >Hellolet y = < Baz >Hello : any +>Foo.Bar : () => void +>Foo : { Bar(): void; } +>Bar : () => void + +let y = < Baz >Hello +>< Baz >Hello : any +>Baz : () => void +> : any +> : any + diff --git a/tests/cases/compiler/errorSpanForUnclosedJsxTag.tsx b/tests/cases/compiler/errorSpanForUnclosedJsxTag.tsx new file mode 100644 index 00000000000..bd44fd7f8dc --- /dev/null +++ b/tests/cases/compiler/errorSpanForUnclosedJsxTag.tsx @@ -0,0 +1,12 @@ +// @jsx: react +declare const React: any + +let Foo = { + Bar() {} +} + +let Baz = () => {} + +let x = < Foo.Bar >Hello + +let y = < Baz >Hello \ No newline at end of file