Skip to content

Commit

Permalink
better JSX unexpected EOF error message
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Feb 1, 2023
1 parent e81750f commit 362cae8
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 29 deletions.
10 changes: 3 additions & 7 deletions internal/js_lexer/js_lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -703,19 +703,15 @@ func (lexer *Lexer) NextJSXElementChild() {
stringLiteral:
for {
switch lexer.codePoint {
case -1:
// Reaching the end of the file without a closing element is an error
lexer.SyntaxError()
case -1, '{', '<':
// Stop when the string ends
break stringLiteral

case '&', '\r', '\n', '\u2028', '\u2029':
// This needs fixing if it has an entity or if it's a multi-line string
needsFixing = true
lexer.step()

case '{', '<':
// Stop when the string ends
break stringLiteral

case '}', '>':
// These technically aren't valid JSX: https://facebook.github.io/jsx/
//
Expand Down
14 changes: 12 additions & 2 deletions internal/js_parser/js_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -5019,8 +5019,8 @@ func (p *parser) parseJSXElement(loc logger.Loc) js_ast.Expr {
if startText != endText {
msg := logger.Msg{
Kind: logger.Error,
Data: p.tracker.MsgData(endRange, fmt.Sprintf("Expected closing tag %q to match opening tag %q", endText, startText)),
Notes: []logger.MsgData{p.tracker.MsgData(startRange, fmt.Sprintf("The opening tag %q is here:", startText))},
Data: p.tracker.MsgData(endRange, fmt.Sprintf("Expected closing %q tag to match opening %q tag", endText, startText)),
Notes: []logger.MsgData{p.tracker.MsgData(startRange, fmt.Sprintf("The opening %q tag is here:", startText))},
}
msg.Data.Location.Suggestion = startText
p.log.AddMsg(msg)
Expand All @@ -5037,6 +5037,16 @@ func (p *parser) parseJSXElement(loc logger.Loc) js_ast.Expr {
IsTagSingleLine: isSingleLine,
}}

case js_lexer.TEndOfFile:
msg := logger.Msg{
Kind: logger.Error,
Data: p.tracker.MsgData(p.lexer.Range(), fmt.Sprintf("Unexpected end of file before a closing %q tag", startText)),
Notes: []logger.MsgData{p.tracker.MsgData(startRange, fmt.Sprintf("The opening %q tag is here:", startText))},
}
msg.Data.Location.Suggestion = fmt.Sprintf("</%s>", startText)
p.log.AddMsg(msg)
panic(js_lexer.LexerPanic{})

default:
p.lexer.Unexpected()
}
Expand Down
12 changes: 6 additions & 6 deletions internal/js_parser/js_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4948,11 +4948,11 @@ func TestJSX(t *testing.T) {

expectParseErrorJSX(t, "<a b=true/>", "<stdin>: ERROR: Expected \"{\" but found \"true\"\n")
expectParseErrorJSX(t, "</a>", "<stdin>: ERROR: Expected identifier but found \"/\"\n")
expectParseErrorJSX(t, "<></b>", "<stdin>: ERROR: Expected closing tag \"b\" to match opening tag \"\"\n<stdin>: NOTE: The opening tag \"\" is here:\n")
expectParseErrorJSX(t, "<a></>", "<stdin>: ERROR: Expected closing tag \"\" to match opening tag \"a\"\n<stdin>: NOTE: The opening tag \"a\" is here:\n")
expectParseErrorJSX(t, "<a></b>", "<stdin>: ERROR: Expected closing tag \"b\" to match opening tag \"a\"\n<stdin>: NOTE: The opening tag \"a\" is here:\n")
expectParseErrorJSX(t, "<></b>", "<stdin>: ERROR: Expected closing \"b\" tag to match opening \"\" tag\n<stdin>: NOTE: The opening \"\" tag is here:\n")
expectParseErrorJSX(t, "<a></>", "<stdin>: ERROR: Expected closing \"\" tag to match opening \"a\" tag\n<stdin>: NOTE: The opening \"a\" tag is here:\n")
expectParseErrorJSX(t, "<a></b>", "<stdin>: ERROR: Expected closing \"b\" tag to match opening \"a\" tag\n<stdin>: NOTE: The opening \"a\" tag is here:\n")
expectParseErrorJSX(t, "<\na\n.\nb\n>\n<\n/\nc\n.\nd\n>",
"<stdin>: ERROR: Expected closing tag \"c.d\" to match opening tag \"a.b\"\n<stdin>: NOTE: The opening tag \"a.b\" is here:\n")
"<stdin>: ERROR: Expected closing \"c.d\" tag to match opening \"a.b\" tag\n<stdin>: NOTE: The opening \"a.b\" tag is here:\n")
expectParseErrorJSX(t, "<a-b.c>", "<stdin>: ERROR: Expected \">\" but found \".\"\n")
expectParseErrorJSX(t, "<a.b-c>", "<stdin>: ERROR: Unexpected \"-\"\n")

Expand Down Expand Up @@ -4982,13 +4982,13 @@ func TestJSX(t *testing.T) {
expectParseErrorJSX(t, "<a /* />", "<stdin>: ERROR: Expected \"*/\" to terminate multi-line comment\n<stdin>: NOTE: The multi-line comment starts here:\n")
expectParseErrorJSX(t, "<a /*/ />", "<stdin>: ERROR: Expected \"*/\" to terminate multi-line comment\n<stdin>: NOTE: The multi-line comment starts here:\n")
expectParseErrorJSX(t, "<a // />", "<stdin>: ERROR: Expected \">\" but found end of file\n")
expectParseErrorJSX(t, "<a /**/>", "<stdin>: ERROR: Unexpected end of file\n")
expectParseErrorJSX(t, "<a /**/>", "<stdin>: ERROR: Unexpected end of file before a closing \"a\" tag\n<stdin>: NOTE: The opening \"a\" tag is here:\n")
expectParseErrorJSX(t, "<a /**/ />", "")
expectParseErrorJSX(t, "<a // \n />", "")
expectParseErrorJSX(t, "<a b/* />", "<stdin>: ERROR: Expected \"*/\" to terminate multi-line comment\n<stdin>: NOTE: The multi-line comment starts here:\n")
expectParseErrorJSX(t, "<a b/*/ />", "<stdin>: ERROR: Expected \"*/\" to terminate multi-line comment\n<stdin>: NOTE: The multi-line comment starts here:\n")
expectParseErrorJSX(t, "<a b// />", "<stdin>: ERROR: Expected \">\" but found end of file\n")
expectParseErrorJSX(t, "<a b/**/>", "<stdin>: ERROR: Unexpected end of file\n")
expectParseErrorJSX(t, "<a b/**/>", "<stdin>: ERROR: Unexpected end of file before a closing \"a\" tag\n<stdin>: NOTE: The opening \"a\" tag is here:\n")
expectParseErrorJSX(t, "<a b/**/ />", "")
expectParseErrorJSX(t, "<a b// \n />", "")

Expand Down
27 changes: 13 additions & 14 deletions internal/js_parser/ts_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,7 @@ func TestTSTypes(t *testing.T) {

// TypeScript 4.7
jsxErrorArrow := "<stdin>: ERROR: The character \">\" is not valid inside a JSX element\n" +
"NOTE: Did you mean to escape it as \"{'>'}\" instead?\n" +
"<stdin>: ERROR: Unexpected end of file\n"
"NOTE: Did you mean to escape it as \"{'>'}\" instead?\n"
expectPrintedTS(t, "type Foo<in T> = T", "")
expectPrintedTS(t, "type Foo<out T> = T", "")
expectPrintedTS(t, "type Foo<in out> = T", "")
Expand Down Expand Up @@ -370,9 +369,9 @@ func TestTSTypes(t *testing.T) {
expectParseErrorTSX(t, "<in T,>() => {}", "<stdin>: ERROR: Expected \">\" but found \",\"\n")
expectParseErrorTSX(t, "<out T,>() => {}", "<stdin>: ERROR: Expected \">\" but found \",\"\n")
expectParseErrorTSX(t, "<in out T,>() => {}", "<stdin>: ERROR: Expected \">\" but found \",\"\n")
expectParseErrorTSX(t, "<in T extends any>() => {}", jsxErrorArrow)
expectParseErrorTSX(t, "<out T extends any>() => {}", jsxErrorArrow)
expectParseErrorTSX(t, "<in out T extends any>() => {}", jsxErrorArrow)
expectParseErrorTSX(t, "<in T extends any>() => {}", jsxErrorArrow+"<stdin>: ERROR: Unexpected end of file before a closing \"in\" tag\n<stdin>: NOTE: The opening \"in\" tag is here:\n")
expectParseErrorTSX(t, "<out T extends any>() => {}", jsxErrorArrow+"<stdin>: ERROR: Unexpected end of file before a closing \"out\" tag\n<stdin>: NOTE: The opening \"out\" tag is here:\n")
expectParseErrorTSX(t, "<in out T extends any>() => {}", jsxErrorArrow+"<stdin>: ERROR: Unexpected end of file before a closing \"in\" tag\n<stdin>: NOTE: The opening \"in\" tag is here:\n")
expectPrintedTS(t, "class Container { get data(): typeof this.#data {} }", "class Container {\n get data() {\n }\n}\n")
expectPrintedTS(t, "const a: typeof this.#a = 1;", "const a = 1;\n")
expectParseErrorTS(t, "const a: typeof #a = 1;", "<stdin>: ERROR: Expected identifier but found \"#a\"\n")
Expand Down Expand Up @@ -435,10 +434,10 @@ func TestTSTypes(t *testing.T) {
expectPrintedTSX(t, "async <const T, const X>() => {}", "async () => {\n};\n")
expectPrintedTSX(t, "async <const T, const const X>() => {}", "async () => {\n};\n")
expectPrintedTSX(t, "async <const T extends X>() => {}", "async () => {\n};\n")
expectParseErrorTSX(t, "<const T>() => {}", jsxErrorArrow)
expectParseErrorTSX(t, "<const const>() => {}", jsxErrorArrow)
expectParseErrorTSX(t, "<const T>() => {}", jsxErrorArrow+"<stdin>: ERROR: Unexpected end of file before a closing \"const\" tag\n<stdin>: NOTE: The opening \"const\" tag is here:\n")
expectParseErrorTSX(t, "<const const>() => {}", jsxErrorArrow+"<stdin>: ERROR: Unexpected end of file before a closing \"const\" tag\n<stdin>: NOTE: The opening \"const\" tag is here:\n")
expectParseErrorTSX(t, "<const const T,>() => {}", "<stdin>: ERROR: Expected \">\" but found \",\"\n")
expectParseErrorTSX(t, "<const const T extends X>() => {}", jsxErrorArrow)
expectParseErrorTSX(t, "<const const T extends X>() => {}", jsxErrorArrow+"<stdin>: ERROR: Unexpected end of file before a closing \"const\" tag\n<stdin>: NOTE: The opening \"const\" tag is here:\n")
expectParseErrorTSX(t, "async <const T>() => {}", "<stdin>: ERROR: Unexpected \"const\"\n")
expectParseErrorTSX(t, "async <const const>() => {}", "<stdin>: ERROR: Unexpected \"const\"\n")
expectParseErrorTSX(t, "async <const const T,>() => {}", "<stdin>: ERROR: Unexpected \"const\"\n")
Expand Down Expand Up @@ -2078,9 +2077,9 @@ func TestTSInstantiationExpression(t *testing.T) {
expectPrintedTS(t, "interface Foo { \n (a: number): typeof a \n <T>(): void \n }", "")
expectPrintedTSX(t, "interface Foo { \n (a: number): typeof a \n <T>(): void \n }", "")
expectParseErrorTS(t, "type x = y\n<number>\nz\n</number>", "<stdin>: ERROR: Unterminated regular expression\n")
expectParseErrorTSX(t, "type x = y\n<number>\nz", "<stdin>: ERROR: Unexpected end of file\n")
expectParseErrorTSX(t, "type x = y\n<number>\nz", "<stdin>: ERROR: Unexpected end of file before a closing \"number\" tag\n<stdin>: NOTE: The opening \"number\" tag is here:\n")
expectParseErrorTS(t, "type x = typeof y\n<number>\nz\n</number>", "<stdin>: ERROR: Unterminated regular expression\n")
expectParseErrorTSX(t, "type x = typeof y\n<number>\nz", "<stdin>: ERROR: Unexpected end of file\n")
expectParseErrorTSX(t, "type x = typeof y\n<number>\nz", "<stdin>: ERROR: Unexpected end of file before a closing \"number\" tag\n<stdin>: NOTE: The opening \"number\" tag is here:\n")

// See: https://github.com/microsoft/TypeScript/issues/48654
expectPrintedTS(t, "x<true>\ny", "x < true > y;\n")
Expand Down Expand Up @@ -2391,7 +2390,7 @@ func TestTSJSX(t *testing.T) {

expectPrintedTS(t, "const x = <number>1", "const x = 1;\n")
expectPrintedTSX(t, "const x = <number>1</number>", "const x = /* @__PURE__ */ React.createElement(\"number\", null, \"1\");\n")
expectParseErrorTSX(t, "const x = <number>1", "<stdin>: ERROR: Unexpected end of file\n")
expectParseErrorTSX(t, "const x = <number>1", "<stdin>: ERROR: Unexpected end of file before a closing \"number\" tag\n<stdin>: NOTE: The opening \"number\" tag is here:\n")

expectPrintedTSX(t, "<x>a{}c</x>", "/* @__PURE__ */ React.createElement(\"x\", null, \"a\", \"c\");\n")
expectPrintedTSX(t, "<x>a{b}c</x>", "/* @__PURE__ */ React.createElement(\"x\", null, \"a\", b, \"c\");\n")
Expand Down Expand Up @@ -2460,9 +2459,9 @@ func TestTSJSX(t *testing.T) {
expectPrintedTSX(t, "(<T,>() => {})", "() => {\n};\n")
expectPrintedTSX(t, "(<T, X>(y) => {})", "(y) => {\n};\n")
expectPrintedTSX(t, "(<T, X>(y): (() => {}) => {})", "(y) => {\n};\n")
expectParseErrorTSX(t, "(<T>() => {})", invalidWithHint+"<stdin>: ERROR: Unexpected end of file\n")
expectParseErrorTSX(t, "(<T>(x: X<Y>) => {})", invalidWithHint+"<stdin>: ERROR: Unexpected end of file\n")
expectParseErrorTSX(t, "(<T>(x: X<Y>) => {})</Y>", invalidWithHint+"<stdin>: ERROR: Unexpected end of file\n")
expectParseErrorTSX(t, "(<T>() => {})", invalidWithHint+"<stdin>: ERROR: Unexpected end of file before a closing \"T\" tag\n<stdin>: NOTE: The opening \"T\" tag is here:\n")
expectParseErrorTSX(t, "(<T>(x: X<Y>) => {})", invalidWithHint+"<stdin>: ERROR: Unexpected end of file before a closing \"Y\" tag\n<stdin>: NOTE: The opening \"Y\" tag is here:\n")
expectParseErrorTSX(t, "(<T>(x: X<Y>) => {})</Y>", invalidWithHint+"<stdin>: ERROR: Unexpected end of file before a closing \"T\" tag\n<stdin>: NOTE: The opening \"T\" tag is here:\n")
expectParseErrorTSX(t, "(<[]>(y))", "<stdin>: ERROR: Expected identifier but found \"[\"\n")
expectParseErrorTSX(t, "(<T[]>(y))", "<stdin>: ERROR: Expected \">\" but found \"[\"\n")
expectParseErrorTSX(t, "(<T = X>(y))", "<stdin>: ERROR: Expected \"=>\" but found \")\"\n")
Expand Down

0 comments on commit 362cae8

Please sign in to comment.