From 348c4dddc68b84148b753b48ecf9b97cc2d07ebb Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 11 Feb 2020 08:44:17 -0800 Subject: [PATCH] Throw syntax error for `}` and `>` in JSX text (#36636) * Throw syntax error for `}` and `>` in JSX text Fixes #36341 * Add codefix for error --- src/compiler/diagnosticMessages.json | 16 ++++++ src/compiler/scanner.ts | 6 ++ .../codefixes/fixInvalidJsxCharacters.ts | 42 ++++++++++++++ src/services/tsconfig.json | 1 + .../reference/jsxAndTypeAssertion.errors.txt | 29 +++++++++- .../jsxEsprimaFbTestSuite.errors.txt | 5 +- .../jsxInvalidEsprimaTestSuite.errors.txt | 20 +++++-- .../reference/jsxParsingError3.errors.txt | 46 ++++++++++++++++ tests/baselines/reference/jsxParsingError3.js | 42 ++++++++++++++ .../reference/jsxParsingError3.symbols | 51 +++++++++++++++++ .../reference/jsxParsingError3.types | 55 +++++++++++++++++++ .../tsxGenericArrowFunctionParsing.errors.txt | 39 +++++++++++++ .../conformance/jsx/jsxParsingError3.tsx | 27 +++++++++ .../fourslash/codeFixInvalidJsxCharacters1.ts | 19 +++++++ .../fourslash/codeFixInvalidJsxCharacters2.ts | 19 +++++++ .../fourslash/codeFixInvalidJsxCharacters3.ts | 19 +++++++ .../fourslash/codeFixInvalidJsxCharacters4.ts | 19 +++++++ .../fourslash/codeFixInvalidJsxCharacters5.ts | 19 +++++++ .../fourslash/codeFixInvalidJsxCharacters6.ts | 19 +++++++ 19 files changed, 487 insertions(+), 6 deletions(-) create mode 100644 src/services/codefixes/fixInvalidJsxCharacters.ts create mode 100644 tests/baselines/reference/jsxParsingError3.errors.txt create mode 100644 tests/baselines/reference/jsxParsingError3.js create mode 100644 tests/baselines/reference/jsxParsingError3.symbols create mode 100644 tests/baselines/reference/jsxParsingError3.types create mode 100644 tests/baselines/reference/tsxGenericArrowFunctionParsing.errors.txt create mode 100644 tests/cases/conformance/jsx/jsxParsingError3.tsx create mode 100644 tests/cases/fourslash/codeFixInvalidJsxCharacters1.ts create mode 100644 tests/cases/fourslash/codeFixInvalidJsxCharacters2.ts create mode 100644 tests/cases/fourslash/codeFixInvalidJsxCharacters3.ts create mode 100644 tests/cases/fourslash/codeFixInvalidJsxCharacters4.ts create mode 100644 tests/cases/fourslash/codeFixInvalidJsxCharacters5.ts create mode 100644 tests/cases/fourslash/codeFixInvalidJsxCharacters6.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 2dc894e71fb50..ed963997830fe 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1135,6 +1135,14 @@ "category": "Error", "code": 1380 }, + "Unexpected token. Did you mean `{'}'}` or `}`?": { + "category": "Error", + "code": 1381 + }, + "Unexpected token. Did you mean `{'>'}` or `>`?": { + "category": "Error", + "code": 1382 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", @@ -5457,6 +5465,14 @@ "category": "Message", "code": 95099 }, + "Convert invalid character to its html entity code": { + "category": "Message", + "code": 95100 + }, + "Wrap invalid character in an expression container": { + "category": "Message", + "code": 95101 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 7172552a3ea69..bf76051b6adb3 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -2156,6 +2156,12 @@ namespace ts { } break; } + if (char === CharacterCodes.greaterThan) { + error(Diagnostics.Unexpected_token_Did_you_mean_or_gt, pos, 1); + } + if (char === CharacterCodes.closeBrace) { + error(Diagnostics.Unexpected_token_Did_you_mean_or_rbrace, pos, 1); + } if (lastNonWhitespace > 0) lastNonWhitespace++; diff --git a/src/services/codefixes/fixInvalidJsxCharacters.ts b/src/services/codefixes/fixInvalidJsxCharacters.ts new file mode 100644 index 0000000000000..f765c3558dc76 --- /dev/null +++ b/src/services/codefixes/fixInvalidJsxCharacters.ts @@ -0,0 +1,42 @@ +/* @internal */ +namespace ts.codefix { + const fixIdHtmlEntity = "invalidJsxCharactersConvertToHtmlEntity"; + const fixIdExpression = "invalidJsxCharactersConvertToExpression"; + + const errorCodes = [Diagnostics.Unexpected_token_Did_you_mean_or_gt.code, Diagnostics.Unexpected_token_Did_you_mean_or_rbrace.code]; + + registerCodeFix({ + errorCodes, + getCodeActions: context => { + const { sourceFile, span } = context; + const changeToExpression = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, span.start, /* useHtmlEntity */ false)); + const changeToHtmlEntity = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, span.start, /* useHtmlEntity */ true)); + return [ + createCodeFixActionWithoutFixAll(fixIdExpression, changeToExpression, Diagnostics.Wrap_invalid_character_in_an_expression_container), + createCodeFixAction(fixIdHtmlEntity, changeToHtmlEntity, Diagnostics.Convert_invalid_character_to_its_html_entity_code, fixIdHtmlEntity, Diagnostics.Convert_invalid_character_to_its_html_entity_code), + ]; + }, + fixIds: [fixIdExpression, fixIdHtmlEntity], + }); + + const htmlEntity = { + ">": ">", + "}": "}", + }; + function isValidCharacter(character: string): character is keyof typeof htmlEntity { + return hasProperty(htmlEntity, character); + } + + function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number, useHtmlEntity: boolean) { + const character = sourceFile.getText()[start]; + // sanity check + if (!isValidCharacter(character)) { + return; + } + + const replacement = useHtmlEntity + ? htmlEntity[character] + : `{'${character}'}`; + changes.replaceRangeWithText(sourceFile, { pos: start, end: start + 1 }, replacement); + } +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 47b525771eda3..190e61fffcc3c 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -74,6 +74,7 @@ "codefixes/fixModuleAndTargetOptions.ts", "codefixes/fixExtendsInterfaceBecomesImplements.ts", "codefixes/fixForgottenThisPropertyAccess.ts", + "codefixes/fixInvalidJsxCharacters.ts", "codefixes/fixUnusedIdentifier.ts", "codefixes/fixUnreachableCode.ts", "codefixes/fixUnusedLabel.ts", diff --git a/tests/baselines/reference/jsxAndTypeAssertion.errors.txt b/tests/baselines/reference/jsxAndTypeAssertion.errors.txt index 6d3f6874a88b1..c62cbf4be0598 100644 --- a/tests/baselines/reference/jsxAndTypeAssertion.errors.txt +++ b/tests/baselines/reference/jsxAndTypeAssertion.errors.txt @@ -1,20 +1,29 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(6,6): error TS17008: JSX element 'any' has no corresponding closing tag. tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(6,13): error TS2582: Cannot find name 'test'. Do you need to install type definitions for a test runner? Try `npm i @types/jest` or `npm i @types/mocha`. tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(6,17): error TS1005: '}' expected. +tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(6,31): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(8,6): error TS17008: JSX element 'any' has no corresponding closing tag. tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(10,6): error TS17008: JSX element 'foo' has no corresponding closing tag. +tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(10,24): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(10,32): error TS1005: '}' expected. +tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(12,23): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? +tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(12,24): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(12,36): error TS1005: '}' expected. tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(14,17): error TS17008: JSX element 'foo' has no corresponding closing tag. +tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(14,23): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? +tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(14,24): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? +tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(14,38): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(14,45): error TS1005: '}' expected. tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(18,2): error TS17008: JSX element 'foo' has no corresponding closing tag. tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(18,8): error TS17008: JSX element 'foo' has no corresponding closing tag. tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(18,13): error TS17008: JSX element 'foo' has no corresponding closing tag. +tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(18,69): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? +tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(18,76): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: ':' expected. tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '; ~~~ @@ -35,16 +46,28 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: 'hello {{}} ; ~~~ !!! error TS17008: JSX element 'foo' has no corresponding closing tag. + ~ +!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? ~ !!! error TS1005: '}' expected. x = {}}>hello; + ~ +!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? ~ !!! error TS1005: '}' expected. x = {}}>hello{{}}; ~~~ !!! error TS17008: JSX element 'foo' has no corresponding closing tag. + ~ +!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? + ~ +!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? ~ !!! error TS1005: '}' expected. @@ -57,6 +80,10 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: ''}` or `>`? tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,57): error TS1109: Expression expected. tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,58): error TS1109: Expression expected. -==== tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx (6 errors) ==== +==== tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx (7 errors) ==== declare var React: any; declare var 日本語; declare var AbC_def; @@ -54,6 +55,8 @@ tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,58): error TS1109: Expr !!! error TS2304: Cannot find name 'right'. ~~~~~ !!! error TS2657: JSX expressions must have one parent element. + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? ~ !!! error TS1109: Expression expected. ~ diff --git a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt index 68b615525b536..1195ff4457e32 100644 --- a/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt +++ b/tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt @@ -39,6 +39,7 @@ tests/cases/conformance/jsx/19.tsx(1,9): error TS2695: Left side of comma operat tests/cases/conformance/jsx/19.tsx(1,64): error TS2657: JSX expressions must have one parent element. tests/cases/conformance/jsx/2.tsx(1,3): error TS1003: Identifier expected. tests/cases/conformance/jsx/20.tsx(1,10): error TS1005: '}' expected. +tests/cases/conformance/jsx/20.tsx(1,11): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? tests/cases/conformance/jsx/21.tsx(1,20): error TS1003: Identifier expected. tests/cases/conformance/jsx/22.tsx(1,15): error TS1003: Identifier expected. tests/cases/conformance/jsx/22.tsx(1,21): error TS1109: Expression expected. @@ -55,6 +56,8 @@ tests/cases/conformance/jsx/25.tsx(1,29): error TS1128: Declaration or statement tests/cases/conformance/jsx/25.tsx(1,32): error TS2304: Cannot find name 'props'. tests/cases/conformance/jsx/25.tsx(1,38): error TS1109: Expression expected. tests/cases/conformance/jsx/25.tsx(1,39): error TS1109: Expression expected. +tests/cases/conformance/jsx/26.tsx(1,4): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? +tests/cases/conformance/jsx/27.tsx(1,5): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? tests/cases/conformance/jsx/28.tsx(1,2): error TS17008: JSX element 'a' has no corresponding closing tag. tests/cases/conformance/jsx/28.tsx(1,6): error TS1005: '{' expected. tests/cases/conformance/jsx/28.tsx(2,1): error TS1005: '{"str";}; ~ !!! error TS1005: '}' expected. + ~ +!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? ==== tests/cases/conformance/jsx/21.tsx (1 errors) ==== ; ~ @@ -286,11 +292,15 @@ tests/cases/conformance/jsx/9.tsx(1,16): error TS1109: Expression expected. !!! error TS1109: Expression expected. -==== tests/cases/conformance/jsx/26.tsx (0 errors) ==== +==== tests/cases/conformance/jsx/26.tsx (1 errors) ==== >; + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? -==== tests/cases/conformance/jsx/27.tsx (0 errors) ==== +==== tests/cases/conformance/jsx/27.tsx (1 errors) ==== >; + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? ==== tests/cases/conformance/jsx/28.tsx (3 errors) ==== ; @@ -312,8 +322,10 @@ tests/cases/conformance/jsx/9.tsx(1,16): error TS1109: Expression expected. !!! error TS1005: '}; + ~ +!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? ==== tests/cases/conformance/jsx/31.tsx (1 errors) ==== ; diff --git a/tests/baselines/reference/jsxParsingError3.errors.txt b/tests/baselines/reference/jsxParsingError3.errors.txt new file mode 100644 index 0000000000000..d6c9790d42aa4 --- /dev/null +++ b/tests/baselines/reference/jsxParsingError3.errors.txt @@ -0,0 +1,46 @@ +tests/cases/conformance/jsx/Error1.tsx(1,15): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? +tests/cases/conformance/jsx/Error2.tsx(1,15): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? +tests/cases/conformance/jsx/Error3.tsx(1,22): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? +tests/cases/conformance/jsx/Error4.tsx(1,22): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? +tests/cases/conformance/jsx/Error5.tsx(1,15): error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? +tests/cases/conformance/jsx/Error6.tsx(1,15): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? + + +==== tests/cases/conformance/jsx/file.tsx (0 errors) ==== + declare module JSX { + interface Element {} + interface IntrinsicElements { + [s: string]: any; + } + } + +==== tests/cases/conformance/jsx/Error1.tsx (1 errors) ==== + let x1 =
}
; + ~ +!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? + +==== tests/cases/conformance/jsx/Error2.tsx (1 errors) ==== + let x2 =
>
; + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? + +==== tests/cases/conformance/jsx/Error3.tsx (1 errors) ==== + let x3 =
{"foo"}}
; + ~ +!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? + +==== tests/cases/conformance/jsx/Error4.tsx (1 errors) ==== + let x4 =
{"foo"}>
; + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? + +==== tests/cases/conformance/jsx/Error5.tsx (1 errors) ==== + let x5 =
}{"foo"}
; + ~ +!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `}`? + +==== tests/cases/conformance/jsx/Error6.tsx (1 errors) ==== + let x6 =
>{"foo"}
; + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? + \ No newline at end of file diff --git a/tests/baselines/reference/jsxParsingError3.js b/tests/baselines/reference/jsxParsingError3.js new file mode 100644 index 0000000000000..e10727128906f --- /dev/null +++ b/tests/baselines/reference/jsxParsingError3.js @@ -0,0 +1,42 @@ +//// [tests/cases/conformance/jsx/jsxParsingError3.tsx] //// + +//// [file.tsx] +declare module JSX { + interface Element {} + interface IntrinsicElements { + [s: string]: any; + } +} + +//// [Error1.tsx] +let x1 =
}
; + +//// [Error2.tsx] +let x2 =
>
; + +//// [Error3.tsx] +let x3 =
{"foo"}}
; + +//// [Error4.tsx] +let x4 =
{"foo"}>
; + +//// [Error5.tsx] +let x5 =
}{"foo"}
; + +//// [Error6.tsx] +let x6 =
>{"foo"}
; + + +//// [file.jsx] +//// [Error1.jsx] +var x1 =
}
; +//// [Error2.jsx] +var x2 =
>
; +//// [Error3.jsx] +var x3 =
{"foo"}}
; +//// [Error4.jsx] +var x4 =
{"foo"}>
; +//// [Error5.jsx] +var x5 =
}{"foo"}
; +//// [Error6.jsx] +var x6 =
>{"foo"}
; diff --git a/tests/baselines/reference/jsxParsingError3.symbols b/tests/baselines/reference/jsxParsingError3.symbols new file mode 100644 index 0000000000000..6051929f2c309 --- /dev/null +++ b/tests/baselines/reference/jsxParsingError3.symbols @@ -0,0 +1,51 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare module JSX { +>JSX : Symbol(JSX, Decl(file.tsx, 0, 0)) + + interface Element {} +>Element : Symbol(Element, Decl(file.tsx, 0, 20)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(file.tsx, 1, 22)) + + [s: string]: any; +>s : Symbol(s, Decl(file.tsx, 3, 5)) + } +} + +=== tests/cases/conformance/jsx/Error1.tsx === +let x1 =
}
; +>x1 : Symbol(x1, Decl(Error1.tsx, 0, 3)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) + +=== tests/cases/conformance/jsx/Error2.tsx === +let x2 =
>
; +>x2 : Symbol(x2, Decl(Error2.tsx, 0, 3)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) + +=== tests/cases/conformance/jsx/Error3.tsx === +let x3 =
{"foo"}}
; +>x3 : Symbol(x3, Decl(Error3.tsx, 0, 3)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) + +=== tests/cases/conformance/jsx/Error4.tsx === +let x4 =
{"foo"}>
; +>x4 : Symbol(x4, Decl(Error4.tsx, 0, 3)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) + +=== tests/cases/conformance/jsx/Error5.tsx === +let x5 =
}{"foo"}
; +>x5 : Symbol(x5, Decl(Error5.tsx, 0, 3)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) + +=== tests/cases/conformance/jsx/Error6.tsx === +let x6 =
>{"foo"}
; +>x6 : Symbol(x6, Decl(Error6.tsx, 0, 3)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) +>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) + diff --git a/tests/baselines/reference/jsxParsingError3.types b/tests/baselines/reference/jsxParsingError3.types new file mode 100644 index 0000000000000..eceb955362dfd --- /dev/null +++ b/tests/baselines/reference/jsxParsingError3.types @@ -0,0 +1,55 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare module JSX { + interface Element {} + interface IntrinsicElements { + [s: string]: any; +>s : string + } +} + +=== tests/cases/conformance/jsx/Error1.tsx === +let x1 =
}
; +>x1 : JSX.Element +>
}
: JSX.Element +>div : any +>div : any + +=== tests/cases/conformance/jsx/Error2.tsx === +let x2 =
>
; +>x2 : JSX.Element +>
>
: JSX.Element +>div : any +>div : any + +=== tests/cases/conformance/jsx/Error3.tsx === +let x3 =
{"foo"}}
; +>x3 : JSX.Element +>
{"foo"}}
: JSX.Element +>div : any +>"foo" : "foo" +>div : any + +=== tests/cases/conformance/jsx/Error4.tsx === +let x4 =
{"foo"}>
; +>x4 : JSX.Element +>
{"foo"}>
: JSX.Element +>div : any +>"foo" : "foo" +>div : any + +=== tests/cases/conformance/jsx/Error5.tsx === +let x5 =
}{"foo"}
; +>x5 : JSX.Element +>
}{"foo"}
: JSX.Element +>div : any +>"foo" : "foo" +>div : any + +=== tests/cases/conformance/jsx/Error6.tsx === +let x6 =
>{"foo"}
; +>x6 : JSX.Element +>
>{"foo"}
: JSX.Element +>div : any +>"foo" : "foo" +>div : any + diff --git a/tests/baselines/reference/tsxGenericArrowFunctionParsing.errors.txt b/tests/baselines/reference/tsxGenericArrowFunctionParsing.errors.txt new file mode 100644 index 0000000000000..9fa3091643322 --- /dev/null +++ b/tests/baselines/reference/tsxGenericArrowFunctionParsing.errors.txt @@ -0,0 +1,39 @@ +tests/cases/conformance/jsx/file.tsx(8,17): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? +tests/cases/conformance/jsx/file.tsx(20,32): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? +tests/cases/conformance/jsx/file.tsx(24,25): error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? + + +==== tests/cases/conformance/jsx/file.tsx (3 errors) ==== + declare module JSX { + interface Element { isElement; } + } + + var T: any, T1: any, T2: any; + + // This is an element + var x1 = () => {}; + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? + x1.isElement; + + // This is a generic function + var x2 = () => {}; + x2(); + + // This is a generic function + var x3 = () => {}; + x3(); + + // This is an element + var x4 = () => {}; + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? + x4.isElement; + + // This is an element + var x5 = () => {}; + ~ +!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `>`? + x5.isElement; + + \ No newline at end of file diff --git a/tests/cases/conformance/jsx/jsxParsingError3.tsx b/tests/cases/conformance/jsx/jsxParsingError3.tsx new file mode 100644 index 0000000000000..2913dfc7daf6f --- /dev/null +++ b/tests/cases/conformance/jsx/jsxParsingError3.tsx @@ -0,0 +1,27 @@ +//@jsx: preserve + +//@filename: file.tsx +declare module JSX { + interface Element {} + interface IntrinsicElements { + [s: string]: any; + } +} + +// @filename: Error1.tsx +let x1 =
}
; + +// @filename: Error2.tsx +let x2 =
>
; + +// @filename: Error3.tsx +let x3 =
{"foo"}}
; + +// @filename: Error4.tsx +let x4 =
{"foo"}>
; + +// @filename: Error5.tsx +let x5 =
}{"foo"}
; + +// @filename: Error6.tsx +let x6 =
>{"foo"}
; diff --git a/tests/cases/fourslash/codeFixInvalidJsxCharacters1.ts b/tests/cases/fourslash/codeFixInvalidJsxCharacters1.ts new file mode 100644 index 0000000000000..60495c8b8142a --- /dev/null +++ b/tests/cases/fourslash/codeFixInvalidJsxCharacters1.ts @@ -0,0 +1,19 @@ +/// + +// @jsx: react +// @filename: main.tsx + +//// let x1 =
}
; + +verify.codeFix({ + description: "Wrap invalid character in an expression container", + newFileContent: +`let x1 =
{'}'}
;`, + index: 0, +}); +verify.codeFix({ + description: "Convert invalid character to its html entity code", + newFileContent: +`let x1 =
}
;`, + index: 1, +}); diff --git a/tests/cases/fourslash/codeFixInvalidJsxCharacters2.ts b/tests/cases/fourslash/codeFixInvalidJsxCharacters2.ts new file mode 100644 index 0000000000000..ef8747d48652d --- /dev/null +++ b/tests/cases/fourslash/codeFixInvalidJsxCharacters2.ts @@ -0,0 +1,19 @@ +/// + +// @jsx: react +// @filename: main.tsx + +//// let x2 =
>
; + +verify.codeFix({ + description: "Wrap invalid character in an expression container", + newFileContent: +`let x2 =
{'>'}
;`, + index: 0, +}); +verify.codeFix({ + description: "Convert invalid character to its html entity code", + newFileContent: +`let x2 =
>
;`, + index: 1, +}); diff --git a/tests/cases/fourslash/codeFixInvalidJsxCharacters3.ts b/tests/cases/fourslash/codeFixInvalidJsxCharacters3.ts new file mode 100644 index 0000000000000..d9d97c0bec282 --- /dev/null +++ b/tests/cases/fourslash/codeFixInvalidJsxCharacters3.ts @@ -0,0 +1,19 @@ +/// + +// @jsx: react +// @filename: main.tsx + +//// let x3 =
{"foo"}}
; + +verify.codeFix({ + description: "Wrap invalid character in an expression container", + newFileContent: +`let x3 =
{"foo"}{'}'}
;`, + index: 0, +}); +verify.codeFix({ + description: "Convert invalid character to its html entity code", + newFileContent: +`let x3 =
{"foo"}}
;`, + index: 1, +}); diff --git a/tests/cases/fourslash/codeFixInvalidJsxCharacters4.ts b/tests/cases/fourslash/codeFixInvalidJsxCharacters4.ts new file mode 100644 index 0000000000000..cbb0e01d29377 --- /dev/null +++ b/tests/cases/fourslash/codeFixInvalidJsxCharacters4.ts @@ -0,0 +1,19 @@ +/// + +// @jsx: react +// @filename: main.tsx + +//// let x4 =
{"foo"}>
; + +verify.codeFix({ + description: "Wrap invalid character in an expression container", + newFileContent: +`let x4 =
{"foo"}{'>'}
;`, + index: 0, +}); +verify.codeFix({ + description: "Convert invalid character to its html entity code", + newFileContent: +`let x4 =
{"foo"}>
;`, + index: 1, +}); diff --git a/tests/cases/fourslash/codeFixInvalidJsxCharacters5.ts b/tests/cases/fourslash/codeFixInvalidJsxCharacters5.ts new file mode 100644 index 0000000000000..abc3e7ba16e1c --- /dev/null +++ b/tests/cases/fourslash/codeFixInvalidJsxCharacters5.ts @@ -0,0 +1,19 @@ +/// + +// @jsx: react +// @filename: main.tsx + +//// let x5 =
}{"foo"}
; + +verify.codeFix({ + description: "Wrap invalid character in an expression container", + newFileContent: +`let x5 =
{'}'}{"foo"}
;`, + index: 0, +}); +verify.codeFix({ + description: "Convert invalid character to its html entity code", + newFileContent: +`let x5 =
}{"foo"}
;`, + index: 1, +}); diff --git a/tests/cases/fourslash/codeFixInvalidJsxCharacters6.ts b/tests/cases/fourslash/codeFixInvalidJsxCharacters6.ts new file mode 100644 index 0000000000000..f29e9ed2b8b2c --- /dev/null +++ b/tests/cases/fourslash/codeFixInvalidJsxCharacters6.ts @@ -0,0 +1,19 @@ +/// + +// @jsx: react +// @filename: main.tsx + +//// let x6 =
>{"foo"}
; + +verify.codeFix({ + description: "Wrap invalid character in an expression container", + newFileContent: +`let x6 =
{'>'}{"foo"}
;`, + index: 0, +}); +verify.codeFix({ + description: "Convert invalid character to its html entity code", + newFileContent: +`let x6 =
>{"foo"}
;`, + index: 1, +});