Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw syntax error for } and > in JSX text #36636

Merged
merged 2 commits into from
Feb 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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++;

Expand Down
42 changes: 42 additions & 0 deletions src/services/codefixes/fixInvalidJsxCharacters.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
1 change: 1 addition & 0 deletions src/services/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
29 changes: 28 additions & 1 deletion tests/baselines/reference/jsxAndTypeAssertion.errors.txt
Original file line number Diff line number Diff line change
@@ -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: '</' expected.


==== tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx (14 errors) ====
==== tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx (23 errors) ====
declare var createElement: any;

class foo {}
Expand All @@ -27,6 +36,8 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' ex
!!! 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`.
~
!!! error TS1005: '}' expected.
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?

x = <any><any></any>;
~~~
Expand All @@ -35,16 +46,28 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' ex
x = <foo>hello {<foo>{}} </foo>;
~~~
!!! error TS17008: JSX element 'foo' has no corresponding closing tag.
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
~
!!! error TS1005: '}' expected.

x = <foo test={<foo>{}}>hello</foo>;
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
~
!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?
~
!!! error TS1005: '}' expected.

x = <foo test={<foo>{}}>hello{<foo>{}}</foo>;
~~~
!!! error TS17008: JSX element 'foo' has no corresponding closing tag.
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
~
!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
~
!!! error TS1005: '}' expected.

Expand All @@ -57,6 +80,10 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' ex
!!! error TS17008: JSX element 'foo' has no corresponding closing tag.
~~~
!!! error TS17008: JSX element 'foo' has no corresponding closing tag.
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?



Expand Down
5 changes: 4 additions & 1 deletion tests/baselines/reference/jsxEsprimaFbTestSuite.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,1): error TS2695: Left
tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,17): error TS1005: '{' expected.
tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,23): error TS2304: Cannot find name 'right'.
tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,23): error TS2657: JSX expressions must have one parent element.
tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,41): error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?
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;
Expand Down Expand Up @@ -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 `&gt;`?
~
!!! error TS1109: Expression expected.
~
Expand Down
20 changes: 16 additions & 4 deletions tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 `&rbrace;`?
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.
Expand All @@ -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 `&gt;`?
tests/cases/conformance/jsx/27.tsx(1,5): error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?
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: '</' expected.
Expand All @@ -67,6 +70,7 @@ tests/cases/conformance/jsx/3.tsx(1,2): error TS1109: Expression expected.
tests/cases/conformance/jsx/3.tsx(1,3): error TS2304: Cannot find name 'a'.
tests/cases/conformance/jsx/3.tsx(1,6): error TS1109: Expression expected.
tests/cases/conformance/jsx/3.tsx(1,7): error TS1109: Expression expected.
tests/cases/conformance/jsx/30.tsx(1,4): error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
tests/cases/conformance/jsx/31.tsx(1,4): error TS1003: Identifier expected.
tests/cases/conformance/jsx/4.tsx(1,6): error TS1005: '{' expected.
tests/cases/conformance/jsx/5.tsx(1,2): error TS17008: JSX element 'a' has no corresponding closing tag.
Expand Down Expand Up @@ -236,10 +240,12 @@ tests/cases/conformance/jsx/9.tsx(1,16): error TS1109: Expression expected.
!!! error TS2695: Left side of comma operator is unused and has no side effects.
~
!!! error TS2657: JSX expressions must have one parent element.
==== tests/cases/conformance/jsx/20.tsx (1 errors) ====
==== tests/cases/conformance/jsx/20.tsx (2 errors) ====
<a>{"str";}</a>;
~
!!! error TS1005: '}' expected.
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
==== tests/cases/conformance/jsx/21.tsx (1 errors) ====
<span className="a", id="b" />;
~
Expand Down Expand Up @@ -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) ====
<a>></a>;
~
!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?

==== tests/cases/conformance/jsx/27.tsx (0 errors) ====
==== tests/cases/conformance/jsx/27.tsx (1 errors) ====
<a> ></a>;
~
!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?

==== tests/cases/conformance/jsx/28.tsx (3 errors) ====
<a b=}>;
Expand All @@ -312,8 +322,10 @@ tests/cases/conformance/jsx/9.tsx(1,16): error TS1109: Expression expected.


!!! error TS1005: '</' expected.
==== tests/cases/conformance/jsx/30.tsx (0 errors) ====
==== tests/cases/conformance/jsx/30.tsx (1 errors) ====
<a>}</a>;
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?

==== tests/cases/conformance/jsx/31.tsx (1 errors) ====
<a .../*hai*/asdf/>;
Expand Down
46 changes: 46 additions & 0 deletions tests/baselines/reference/jsxParsingError3.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
tests/cases/conformance/jsx/Error1.tsx(1,15): error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
tests/cases/conformance/jsx/Error2.tsx(1,15): error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?
tests/cases/conformance/jsx/Error3.tsx(1,22): error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
tests/cases/conformance/jsx/Error4.tsx(1,22): error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?
tests/cases/conformance/jsx/Error5.tsx(1,15): error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
tests/cases/conformance/jsx/Error6.tsx(1,15): error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?


==== 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 = <div>}</div>;
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?

==== tests/cases/conformance/jsx/Error2.tsx (1 errors) ====
let x2 = <div>></div>;
~
!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?

==== tests/cases/conformance/jsx/Error3.tsx (1 errors) ====
let x3 = <div>{"foo"}}</div>;
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?

==== tests/cases/conformance/jsx/Error4.tsx (1 errors) ====
let x4 = <div>{"foo"}></div>;
~
!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?

==== tests/cases/conformance/jsx/Error5.tsx (1 errors) ====
let x5 = <div>}{"foo"}</div>;
~
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?

==== tests/cases/conformance/jsx/Error6.tsx (1 errors) ====
let x6 = <div>>{"foo"}</div>;
~
!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?

42 changes: 42 additions & 0 deletions tests/baselines/reference/jsxParsingError3.js
Original file line number Diff line number Diff line change
@@ -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 = <div>}</div>;

//// [Error2.tsx]
let x2 = <div>></div>;

//// [Error3.tsx]
let x3 = <div>{"foo"}}</div>;

//// [Error4.tsx]
let x4 = <div>{"foo"}></div>;

//// [Error5.tsx]
let x5 = <div>}{"foo"}</div>;

//// [Error6.tsx]
let x6 = <div>>{"foo"}</div>;


//// [file.jsx]
//// [Error1.jsx]
var x1 = <div>}</div>;
//// [Error2.jsx]
var x2 = <div>></div>;
//// [Error3.jsx]
var x3 = <div>{"foo"}}</div>;
//// [Error4.jsx]
var x4 = <div>{"foo"}></div>;
//// [Error5.jsx]
var x5 = <div>}{"foo"}</div>;
//// [Error6.jsx]
var x6 = <div>>{"foo"}</div>;
Loading