Merge pull request #4228 from RyanCavanaugh/jsxHash
Fix case where # occurs after > in JSX expression
This commit is contained in:
commit
5035559c59
|
@ -844,6 +844,10 @@ namespace ts {
|
|||
return token = scanner.scanJsxIdentifier();
|
||||
}
|
||||
|
||||
function scanJsxText(): SyntaxKind {
|
||||
return token = scanner.scanJsxToken();
|
||||
}
|
||||
|
||||
function speculationHelper<T>(callback: () => T, isLookAhead: boolean): T {
|
||||
// Keep track of the state we'll need to rollback to if lookahead fails (or if the
|
||||
// caller asked us to always reset our state).
|
||||
|
@ -913,9 +917,11 @@ namespace ts {
|
|||
return token > SyntaxKind.LastReservedWord;
|
||||
}
|
||||
|
||||
function parseExpected(kind: SyntaxKind, diagnosticMessage?: DiagnosticMessage): boolean {
|
||||
function parseExpected(kind: SyntaxKind, diagnosticMessage?: DiagnosticMessage, shouldAdvance = true): boolean {
|
||||
if (token === kind) {
|
||||
nextToken();
|
||||
if (shouldAdvance) {
|
||||
nextToken();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3178,7 +3184,7 @@ namespace ts {
|
|||
return parseTypeAssertion();
|
||||
}
|
||||
if (lookAhead(nextTokenIsIdentifierOrKeyword)) {
|
||||
return parseJsxElementOrSelfClosingElement();
|
||||
return parseJsxElementOrSelfClosingElement(/*inExpressionContext*/ true);
|
||||
}
|
||||
// Fall through
|
||||
default:
|
||||
|
@ -3308,14 +3314,14 @@ namespace ts {
|
|||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseJsxElementOrSelfClosingElement(): JsxElement|JsxSelfClosingElement {
|
||||
let opening = parseJsxOpeningOrSelfClosingElement();
|
||||
function parseJsxElementOrSelfClosingElement(inExpressionContext: boolean): JsxElement | JsxSelfClosingElement {
|
||||
let opening = parseJsxOpeningOrSelfClosingElement(inExpressionContext);
|
||||
if (opening.kind === SyntaxKind.JsxOpeningElement) {
|
||||
let node = <JsxElement>createNode(SyntaxKind.JsxElement, opening.pos);
|
||||
node.openingElement = opening;
|
||||
|
||||
node.children = parseJsxChildren(node.openingElement.tagName);
|
||||
node.closingElement = parseJsxClosingElement();
|
||||
node.closingElement = parseJsxClosingElement(inExpressionContext);
|
||||
return finishNode(node);
|
||||
}
|
||||
else {
|
||||
|
@ -3336,9 +3342,9 @@ namespace ts {
|
|||
case SyntaxKind.JsxText:
|
||||
return parseJsxText();
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
return parseJsxExpression();
|
||||
return parseJsxExpression(/*inExpressionContext*/ false);
|
||||
case SyntaxKind.LessThanToken:
|
||||
return parseJsxElementOrSelfClosingElement();
|
||||
return parseJsxElementOrSelfClosingElement(/*inExpressionContext*/ false);
|
||||
}
|
||||
Debug.fail("Unknown JSX child kind " + token);
|
||||
}
|
||||
|
@ -3368,7 +3374,7 @@ namespace ts {
|
|||
return result;
|
||||
}
|
||||
|
||||
function parseJsxOpeningOrSelfClosingElement(): JsxOpeningElement|JsxSelfClosingElement {
|
||||
function parseJsxOpeningOrSelfClosingElement(inExpressionContext: boolean): JsxOpeningElement|JsxSelfClosingElement {
|
||||
let fullStart = scanner.getStartPos();
|
||||
|
||||
parseExpected(SyntaxKind.LessThanToken);
|
||||
|
@ -3378,12 +3384,22 @@ namespace ts {
|
|||
let attributes = parseList(ParsingContext.JsxAttributes, parseJsxAttribute);
|
||||
let node: JsxOpeningLikeElement;
|
||||
|
||||
if (parseOptional(SyntaxKind.GreaterThanToken)) {
|
||||
if (token === SyntaxKind.GreaterThanToken) {
|
||||
// Closing tag, so scan the immediately-following text with the JSX scanning instead
|
||||
// of regular scanning to avoid treating illegal characters (e.g. '#') as immediate
|
||||
// scanning errors
|
||||
node = <JsxOpeningElement>createNode(SyntaxKind.JsxOpeningElement, fullStart);
|
||||
scanJsxText();
|
||||
}
|
||||
else {
|
||||
parseExpected(SyntaxKind.SlashToken);
|
||||
parseExpected(SyntaxKind.GreaterThanToken);
|
||||
if (inExpressionContext) {
|
||||
parseExpected(SyntaxKind.GreaterThanToken);
|
||||
}
|
||||
else {
|
||||
parseExpected(SyntaxKind.GreaterThanToken, /*diagnostic*/ undefined, /*advance*/ false);
|
||||
scanJsxText();
|
||||
}
|
||||
node = <JsxSelfClosingElement>createNode(SyntaxKind.JsxSelfClosingElement, fullStart);
|
||||
}
|
||||
|
||||
|
@ -3406,14 +3422,20 @@ namespace ts {
|
|||
return elementName;
|
||||
}
|
||||
|
||||
function parseJsxExpression(): JsxExpression {
|
||||
function parseJsxExpression(inExpressionContext: boolean): JsxExpression {
|
||||
let node = <JsxExpression>createNode(SyntaxKind.JsxExpression);
|
||||
|
||||
parseExpected(SyntaxKind.OpenBraceToken);
|
||||
if (token !== SyntaxKind.CloseBraceToken) {
|
||||
node.expression = parseExpression();
|
||||
}
|
||||
parseExpected(SyntaxKind.CloseBraceToken);
|
||||
if (inExpressionContext) {
|
||||
parseExpected(SyntaxKind.CloseBraceToken);
|
||||
}
|
||||
else {
|
||||
parseExpected(SyntaxKind.CloseBraceToken, /*message*/ undefined, /*advance*/ false);
|
||||
scanJsxText();
|
||||
}
|
||||
|
||||
return finishNode(node);
|
||||
}
|
||||
|
@ -3432,7 +3454,7 @@ namespace ts {
|
|||
node.initializer = parseLiteralNode();
|
||||
break;
|
||||
default:
|
||||
node.initializer = parseJsxExpression();
|
||||
node.initializer = parseJsxExpression(/*inExpressionContext*/ true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3448,11 +3470,17 @@ namespace ts {
|
|||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseJsxClosingElement(): JsxClosingElement {
|
||||
function parseJsxClosingElement(inExpressionContext: boolean): JsxClosingElement {
|
||||
let node = <JsxClosingElement>createNode(SyntaxKind.JsxClosingElement);
|
||||
parseExpected(SyntaxKind.LessThanSlashToken);
|
||||
node.tagName = parseJsxElementName();
|
||||
parseExpected(SyntaxKind.GreaterThanToken);
|
||||
if (inExpressionContext) {
|
||||
parseExpected(SyntaxKind.GreaterThanToken);
|
||||
}
|
||||
else {
|
||||
parseExpected(SyntaxKind.GreaterThanToken, /*diagnostic*/ undefined, /*advance*/ false);
|
||||
scanJsxText();
|
||||
}
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,21 +29,18 @@ var foo = (function () {
|
|||
return foo;
|
||||
})();
|
||||
var x;
|
||||
x = <any> {test}: <any></any> };
|
||||
x = <any> {test} <any></any> };
|
||||
|
||||
x = <any><any></any>;
|
||||
|
||||
x = <foo>hello {<foo>} </foo>};
|
||||
x = <foo>hello {<foo>} </foo>}
|
||||
|
||||
x = <foo test={<foo>}>hello</foo>}/>;
|
||||
x = <foo test={<foo>}>hello</foo>}/>
|
||||
|
||||
x = <foo test={<foo>}>hello{<foo>}</foo>};
|
||||
x = <foo test={<foo>}>hello{<foo>}</foo>}
|
||||
|
||||
x = <foo>x</foo>, x = <foo />;
|
||||
|
||||
<foo>{<foo><foo>{/foo/.test(x) ? <foo><foo></foo> : <foo><foo></foo>}</foo>}</foo>
|
||||
:
|
||||
}
|
||||
|
||||
|
||||
</></>}</></>}/></></></>;
|
||||
}</></>}</></>}/></></></>;
|
||||
|
|
26
tests/baselines/reference/jsxHash.js
Normal file
26
tests/baselines/reference/jsxHash.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
//// [jsxHash.tsx]
|
||||
var t02 = <a>{0}#</a>;
|
||||
var t03 = <a>#{0}</a>;
|
||||
var t04 = <a>#{0}#</a>;
|
||||
var t05 = <a>#<i></i></a>;
|
||||
var t06 = <a>#<i></i></a>;
|
||||
var t07 = <a>#<i>#</i></a>;
|
||||
var t08 = <a><i></i>#</a>;
|
||||
var t09 = <a>#<i></i>#</a>;
|
||||
var t10 = <a><i/>#</a>;
|
||||
var t11 = <a>#<i/></a>;
|
||||
var t12 = <a>#</a>;
|
||||
|
||||
|
||||
//// [jsxHash.jsx]
|
||||
var t02 = <a>{0}#</a>;
|
||||
var t03 = <a>#{0}</a>;
|
||||
var t04 = <a>#{0}#</a>;
|
||||
var t05 = <a>#<i></i></a>;
|
||||
var t06 = <a>#<i></i></a>;
|
||||
var t07 = <a>#<i>#</i></a>;
|
||||
var t08 = <a><i></i>#</a>;
|
||||
var t09 = <a>#<i></i>#</a>;
|
||||
var t10 = <a><i />#</a>;
|
||||
var t11 = <a>#<i /></a>;
|
||||
var t12 = <a>#</a>;
|
34
tests/baselines/reference/jsxHash.symbols
Normal file
34
tests/baselines/reference/jsxHash.symbols
Normal file
|
@ -0,0 +1,34 @@
|
|||
=== tests/cases/compiler/jsxHash.tsx ===
|
||||
var t02 = <a>{0}#</a>;
|
||||
>t02 : Symbol(t02, Decl(jsxHash.tsx, 0, 3))
|
||||
|
||||
var t03 = <a>#{0}</a>;
|
||||
>t03 : Symbol(t03, Decl(jsxHash.tsx, 1, 3))
|
||||
|
||||
var t04 = <a>#{0}#</a>;
|
||||
>t04 : Symbol(t04, Decl(jsxHash.tsx, 2, 3))
|
||||
|
||||
var t05 = <a>#<i></i></a>;
|
||||
>t05 : Symbol(t05, Decl(jsxHash.tsx, 3, 3))
|
||||
|
||||
var t06 = <a>#<i></i></a>;
|
||||
>t06 : Symbol(t06, Decl(jsxHash.tsx, 4, 3))
|
||||
|
||||
var t07 = <a>#<i>#</i></a>;
|
||||
>t07 : Symbol(t07, Decl(jsxHash.tsx, 5, 3))
|
||||
|
||||
var t08 = <a><i></i>#</a>;
|
||||
>t08 : Symbol(t08, Decl(jsxHash.tsx, 6, 3))
|
||||
|
||||
var t09 = <a>#<i></i>#</a>;
|
||||
>t09 : Symbol(t09, Decl(jsxHash.tsx, 7, 3))
|
||||
|
||||
var t10 = <a><i/>#</a>;
|
||||
>t10 : Symbol(t10, Decl(jsxHash.tsx, 8, 3))
|
||||
|
||||
var t11 = <a>#<i/></a>;
|
||||
>t11 : Symbol(t11, Decl(jsxHash.tsx, 9, 3))
|
||||
|
||||
var t12 = <a>#</a>;
|
||||
>t12 : Symbol(t12, Decl(jsxHash.tsx, 10, 3))
|
||||
|
86
tests/baselines/reference/jsxHash.types
Normal file
86
tests/baselines/reference/jsxHash.types
Normal file
|
@ -0,0 +1,86 @@
|
|||
=== tests/cases/compiler/jsxHash.tsx ===
|
||||
var t02 = <a>{0}#</a>;
|
||||
>t02 : any
|
||||
><a>{0}#</a> : any
|
||||
>a : any
|
||||
>a : any
|
||||
|
||||
var t03 = <a>#{0}</a>;
|
||||
>t03 : any
|
||||
><a>#{0}</a> : any
|
||||
>a : any
|
||||
>a : any
|
||||
|
||||
var t04 = <a>#{0}#</a>;
|
||||
>t04 : any
|
||||
><a>#{0}#</a> : any
|
||||
>a : any
|
||||
>a : any
|
||||
|
||||
var t05 = <a>#<i></i></a>;
|
||||
>t05 : any
|
||||
><a>#<i></i></a> : any
|
||||
>a : any
|
||||
><i></i> : any
|
||||
>i : any
|
||||
>i : any
|
||||
>a : any
|
||||
|
||||
var t06 = <a>#<i></i></a>;
|
||||
>t06 : any
|
||||
><a>#<i></i></a> : any
|
||||
>a : any
|
||||
><i></i> : any
|
||||
>i : any
|
||||
>i : any
|
||||
>a : any
|
||||
|
||||
var t07 = <a>#<i>#</i></a>;
|
||||
>t07 : any
|
||||
><a>#<i>#</i></a> : any
|
||||
>a : any
|
||||
><i>#</i> : any
|
||||
>i : any
|
||||
>i : any
|
||||
>a : any
|
||||
|
||||
var t08 = <a><i></i>#</a>;
|
||||
>t08 : any
|
||||
><a><i></i>#</a> : any
|
||||
>a : any
|
||||
><i></i> : any
|
||||
>i : any
|
||||
>i : any
|
||||
>a : any
|
||||
|
||||
var t09 = <a>#<i></i>#</a>;
|
||||
>t09 : any
|
||||
><a>#<i></i>#</a> : any
|
||||
>a : any
|
||||
><i></i> : any
|
||||
>i : any
|
||||
>i : any
|
||||
>a : any
|
||||
|
||||
var t10 = <a><i/>#</a>;
|
||||
>t10 : any
|
||||
><a><i/>#</a> : any
|
||||
>a : any
|
||||
><i/> : any
|
||||
>i : any
|
||||
>a : any
|
||||
|
||||
var t11 = <a>#<i/></a>;
|
||||
>t11 : any
|
||||
><a>#<i/></a> : any
|
||||
>a : any
|
||||
><i/> : any
|
||||
>i : any
|
||||
>a : any
|
||||
|
||||
var t12 = <a>#</a>;
|
||||
>t12 : any
|
||||
><a>#</a> : any
|
||||
>a : any
|
||||
>a : any
|
||||
|
|
@ -62,10 +62,8 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(24,15): error TS1003:
|
|||
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(25,7): error TS1005: '...' expected.
|
||||
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(25,7): error TS2304: Cannot find name 'props'.
|
||||
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(27,17): error TS1005: '>' expected.
|
||||
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(27,18): error TS1109: Expression 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(28,29): error TS1109: Expression expected.
|
||||
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(32,6): error TS1005: '{' expected.
|
||||
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(33,6): error TS1005: '{' expected.
|
||||
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(33,7): error TS1109: Expression expected.
|
||||
|
@ -73,7 +71,7 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,4): error TS1003:
|
|||
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,21): error TS17002: Expected corresponding JSX closing tag for 'a'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx (73 errors) ====
|
||||
==== tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx (71 errors) ====
|
||||
declare var React: any;
|
||||
|
||||
</>;
|
||||
|
@ -229,15 +227,11 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,21): error TS17002
|
|||
<div>stuff</div {...props}>;
|
||||
~
|
||||
!!! error TS1005: '>' expected.
|
||||
~~~
|
||||
!!! error TS1109: Expression expected.
|
||||
<div {...props}>stuff</div {...props}>;
|
||||
~~~~~
|
||||
!!! error TS2304: Cannot find name 'props'.
|
||||
~
|
||||
!!! error TS1005: '>' expected.
|
||||
~~~
|
||||
!!! error TS1109: Expression expected.
|
||||
|
||||
<a>></a>;
|
||||
<a> ></a>;
|
||||
|
|
|
@ -65,17 +65,17 @@ a['foo'] > ;
|
|||
<a b=>;
|
||||
var x = <div>one</div><div>two</div>;;
|
||||
var x = <div>one</div> /* intervening comment */ /* intervening comment */ <div>two</div>;;
|
||||
<a>{"str"};}</a>;
|
||||
<span className="a"/>, id="b" />;
|
||||
<div className=/>"app">;
|
||||
<a>{"str"}}</a>;
|
||||
<span className="a"/> id="b" />;
|
||||
<div className=/>>;
|
||||
<div {...props}/>;
|
||||
|
||||
<div>stuff</div> {}...props}>;
|
||||
<div {...props}>stuff</div> {}...props}>;
|
||||
<div>stuff</div>...props}>;
|
||||
<div {...props}>stuff</div>...props}>;
|
||||
|
||||
<a>></a>;
|
||||
<a> ></a>;
|
||||
<a b=>;
|
||||
<a b={ < }>;
|
||||
<a>}</a>;
|
||||
<a /> .../*hai*/asdf/>;</></></></>;
|
||||
<a /> /*hai*//*hai*/asdf/>;</></></></>;
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
tests/cases/conformance/jsx/tsxErrorRecovery1.tsx(5,19): error TS1109: Expression expected.
|
||||
tests/cases/conformance/jsx/tsxErrorRecovery1.tsx(8,11): error TS2304: Cannot find name 'a'.
|
||||
tests/cases/conformance/jsx/tsxErrorRecovery1.tsx(8,12): error TS1005: '}' expected.
|
||||
tests/cases/conformance/jsx/tsxErrorRecovery1.tsx(9,1): error TS17002: Expected corresponding JSX closing tag for 'div'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/jsx/tsxErrorRecovery1.tsx (1 errors) ====
|
||||
==== tests/cases/conformance/jsx/tsxErrorRecovery1.tsx (4 errors) ====
|
||||
|
||||
declare namespace JSX { interface Element { } }
|
||||
|
||||
|
@ -12,4 +15,10 @@ tests/cases/conformance/jsx/tsxErrorRecovery1.tsx(5,19): error TS1109: Expressio
|
|||
}
|
||||
// Shouldn't see any errors down here
|
||||
var y = { a: 1 };
|
||||
|
||||
~
|
||||
!!! error TS2304: Cannot find name 'a'.
|
||||
~
|
||||
!!! error TS1005: '}' expected.
|
||||
|
||||
|
||||
!!! error TS17002: Expected corresponding JSX closing tag for 'div'.
|
|
@ -11,7 +11,9 @@ var y = { a: 1 };
|
|||
|
||||
//// [tsxErrorRecovery1.jsx]
|
||||
function foo() {
|
||||
var x = <div> {} </div>;
|
||||
var x = <div> {}div>
|
||||
}
|
||||
// Shouldn't see any errors down here
|
||||
var y = {a} 1 };
|
||||
</>;
|
||||
}
|
||||
// Shouldn't see any errors down here
|
||||
var y = { a: 1 };
|
||||
|
|
12
tests/cases/compiler/jsxHash.tsx
Normal file
12
tests/cases/compiler/jsxHash.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
//@jsx: preserve
|
||||
var t02 = <a>{0}#</a>;
|
||||
var t03 = <a>#{0}</a>;
|
||||
var t04 = <a>#{0}#</a>;
|
||||
var t05 = <a>#<i></i></a>;
|
||||
var t06 = <a>#<i></i></a>;
|
||||
var t07 = <a>#<i>#</i></a>;
|
||||
var t08 = <a><i></i>#</a>;
|
||||
var t09 = <a>#<i></i>#</a>;
|
||||
var t10 = <a><i/>#</a>;
|
||||
var t11 = <a>#<i/></a>;
|
||||
var t12 = <a>#</a>;
|
Loading…
Reference in a new issue