Merge pull request #6006 from Microsoft/fix5953_crashJSX

Fix crash inside JSX Closing tag
This commit is contained in:
Yui 2016-01-10 06:23:16 -08:00
commit 8cf347c088
23 changed files with 229 additions and 6 deletions

View file

@ -65,6 +65,7 @@ namespace ts {
getTypeCount: () => typeCount,
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
isUnknownSymbol: symbol => symbol === unknownSymbol,
getDiagnostics,
getGlobalDiagnostics,
@ -8112,6 +8113,7 @@ namespace ts {
if (compilerOptions.noImplicitAny) {
error(node, Diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, JsxNames.IntrinsicElements);
}
return unknownSymbol;
}
}

View file

@ -1738,6 +1738,7 @@ namespace ts {
isImplementationOfOverload(node: FunctionLikeDeclaration): boolean;
isUndefinedSymbol(symbol: Symbol): boolean;
isArgumentsSymbol(symbol: Symbol): boolean;
isUnknownSymbol(symbol: Symbol): boolean;
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;

View file

@ -3100,6 +3100,7 @@ namespace ts {
}
else if (kind === SyntaxKind.SlashToken && contextToken.parent.kind === SyntaxKind.JsxClosingElement) {
isStartingCloseTag = true;
location = contextToken;
}
}
}
@ -3125,8 +3126,11 @@ namespace ts {
}
else if (isStartingCloseTag) {
const tagName = (<JsxElement>contextToken.parent.parent).openingElement.tagName;
symbols = [typeChecker.getSymbolAtLocation(tagName)];
const tagSymbol = typeChecker.getSymbolAtLocation(tagName);
if (!typeChecker.isUnknownSymbol(tagSymbol)) {
symbols = [tagSymbol];
}
isMemberCompletion = true;
isNewIdentifierLocation = false;
}
@ -3832,7 +3836,23 @@ namespace ts {
}
else {
if (!symbols || symbols.length === 0) {
return undefined;
if (sourceFile.languageVariant === LanguageVariant.JSX &&
location.parent && location.parent.kind === SyntaxKind.JsxClosingElement) {
// In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag,
// instead of simply giving unknown value, the completion will return the tag-name of an associated opening-element.
// For example:
// var x = <div> </ /*1*/> completion list at "1" will contain "div" with type any
const tagName = (<JsxElement>location.parent.parent).openingElement.tagName;
entries.push({
name: (<Identifier>tagName).text,
kind: undefined,
kindModifiers: undefined,
sortText: "0",
});
}
else {
return undefined;
}
}
getCompletionEntriesFromSymbols(symbols, entries);
@ -4440,7 +4460,7 @@ namespace ts {
const typeChecker = program.getTypeChecker();
const symbol = typeChecker.getSymbolAtLocation(node);
if (!symbol) {
if (!symbol || typeChecker.isUnknownSymbol(symbol)) {
// Try getting just type at this position and show
switch (node.kind) {
case SyntaxKind.Identifier:

View file

@ -4,5 +4,6 @@ declare var React: any;
>React : Symbol(React, Decl(jsxEmitAttributeWithPreserve.tsx, 1, 11))
<foo data/>
>foo : Symbol(unknown)
>data : Symbol(unknown)

View file

@ -1,34 +1,68 @@
=== tests/cases/compiler/jsxHash.tsx ===
var t02 = <a>{0}#</a>;
>t02 : Symbol(t02, Decl(jsxHash.tsx, 0, 3))
>a : Symbol(unknown)
>a : Symbol(unknown)
var t03 = <a>#{0}</a>;
>t03 : Symbol(t03, Decl(jsxHash.tsx, 1, 3))
>a : Symbol(unknown)
>a : Symbol(unknown)
var t04 = <a>#{0}#</a>;
>t04 : Symbol(t04, Decl(jsxHash.tsx, 2, 3))
>a : Symbol(unknown)
>a : Symbol(unknown)
var t05 = <a>#<i></i></a>;
>t05 : Symbol(t05, Decl(jsxHash.tsx, 3, 3))
>a : Symbol(unknown)
>i : Symbol(unknown)
>i : Symbol(unknown)
>a : Symbol(unknown)
var t06 = <a>#<i></i></a>;
>t06 : Symbol(t06, Decl(jsxHash.tsx, 4, 3))
>a : Symbol(unknown)
>i : Symbol(unknown)
>i : Symbol(unknown)
>a : Symbol(unknown)
var t07 = <a>#<i>#</i></a>;
>t07 : Symbol(t07, Decl(jsxHash.tsx, 5, 3))
>a : Symbol(unknown)
>i : Symbol(unknown)
>i : Symbol(unknown)
>a : Symbol(unknown)
var t08 = <a><i></i>#</a>;
>t08 : Symbol(t08, Decl(jsxHash.tsx, 6, 3))
>a : Symbol(unknown)
>i : Symbol(unknown)
>i : Symbol(unknown)
>a : Symbol(unknown)
var t09 = <a>#<i></i>#</a>;
>t09 : Symbol(t09, Decl(jsxHash.tsx, 7, 3))
>a : Symbol(unknown)
>i : Symbol(unknown)
>i : Symbol(unknown)
>a : Symbol(unknown)
var t10 = <a><i/>#</a>;
>t10 : Symbol(t10, Decl(jsxHash.tsx, 8, 3))
>a : Symbol(unknown)
>i : Symbol(unknown)
>a : Symbol(unknown)
var t11 = <a>#<i/></a>;
>t11 : Symbol(t11, Decl(jsxHash.tsx, 9, 3))
>a : Symbol(unknown)
>i : Symbol(unknown)
>a : Symbol(unknown)
var t12 = <a>#</a>;
>t12 : Symbol(t12, Decl(jsxHash.tsx, 10, 3))
>a : Symbol(unknown)
>a : Symbol(unknown)

View file

@ -8,6 +8,7 @@ let x = Test; // emit test_1.default
>Test : Symbol(Test, Decl(consumer.tsx, 1, 6))
<anything attr={Test} />; // ?
>anything : Symbol(unknown)
>attr : Symbol(unknown)
>Test : Symbol(Test, Decl(consumer.tsx, 1, 6))

View file

@ -37,21 +37,36 @@ declare var hasOwnProperty:any;
>hasOwnProperty : Symbol(hasOwnProperty, Decl(jsxReactTestSuite.tsx, 12, 11))
<div>text</div>;
>div : Symbol(unknown)
>div : Symbol(unknown)
<div>
>div : Symbol(unknown)
{this.props.children}
</div>;
>div : Symbol(unknown)
<div>
>div : Symbol(unknown)
<div><br /></div>
>div : Symbol(unknown)
>br : Symbol(unknown)
>div : Symbol(unknown)
<Component>{foo}<br />{bar}</Component>
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
>foo : Symbol(foo, Decl(jsxReactTestSuite.tsx, 7, 11))
>br : Symbol(unknown)
>bar : Symbol(bar, Decl(jsxReactTestSuite.tsx, 8, 11))
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
<br />
>br : Symbol(unknown)
</div>;
>div : Symbol(unknown)
<Composite>
@ -74,6 +89,8 @@ var x =
>x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3))
<div
>div : Symbol(unknown)
attr1={
>attr1 : Symbol(unknown)
@ -97,41 +114,64 @@ var x =
>attr4 : Symbol(unknown)
</div>;
>div : Symbol(unknown)
(
<div>
>div : Symbol(unknown)
{/* A comment at the beginning */}
{/* A second comment at the beginning */}
<span>
>span : Symbol(unknown)
{/* A nested comment */}
</span>
>span : Symbol(unknown)
{/* A sandwiched comment */}
<br />
>br : Symbol(unknown)
{/* A comment at the end */}
{/* A second comment at the end */}
</div>
>div : Symbol(unknown)
);
(
<div
>div : Symbol(unknown)
/* a multi-line
comment */
attr1="foo">
>attr1 : Symbol(unknown)
<span // a double-slash comment
>span : Symbol(unknown)
attr2="bar"
>attr2 : Symbol(unknown)
/>
</div>
>div : Symbol(unknown)
);
<div>&nbsp;</div>;
>div : Symbol(unknown)
>div : Symbol(unknown)
<div>&nbsp; </div>;
>div : Symbol(unknown)
>div : Symbol(unknown)
<hasOwnProperty>testing</hasOwnProperty>;
>hasOwnProperty : Symbol(unknown)
>hasOwnProperty : Symbol(unknown)
<Component constructor="foo" />;
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
@ -158,6 +198,7 @@ var x =
>sound : Symbol(unknown)
<font-face />;
>font-face : Symbol(unknown)
<Component x={y} />;
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
@ -165,6 +206,7 @@ var x =
>y : Symbol(y, Decl(jsxReactTestSuite.tsx, 9, 11))
<x-component />;
>x-component : Symbol(unknown)
<Component {...x} />;
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))

View file

@ -4,14 +4,18 @@ declare var React: any;
>React : Symbol(React, Decl(keywordInJsxIdentifier.tsx, 1, 11))
<foo class-id/>;
>foo : Symbol(unknown)
>class-id : Symbol(unknown)
<foo class/>;
>foo : Symbol(unknown)
>class : Symbol(unknown)
<foo class-id="1"/>;
>foo : Symbol(unknown)
>class-id : Symbol(unknown)
<foo class="1"/>;
>foo : Symbol(unknown)
>class : Symbol(unknown)

View file

@ -16,5 +16,6 @@ declare var foo: any;
>foo : Symbol(foo, Decl(test.tsx, 1, 11))
<foo data/>;
>foo : Symbol(unknown)
>data : Symbol(unknown)

View file

@ -13,6 +13,7 @@ declare var x: any;
>x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11))
<foo data/>;
>foo : Symbol(unknown)
>data : Symbol(unknown)
<Bar x={x} />;
@ -21,6 +22,8 @@ declare var x: any;
>x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11))
<x-component />;
>x-component : Symbol(unknown)
<Bar {...x} />;
>Bar : Symbol(Bar, Decl(reactNamespaceJSXEmit.tsx, 3, 11))
>x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11))

View file

@ -22,5 +22,6 @@ var obj1: Obj1;
>Obj1 : Symbol(Obj1, Decl(file.tsx, 3, 1))
<obj1 x={10} />; // Error
>obj1 : Symbol(unknown)
>x : Symbol(unknown)

View file

@ -17,5 +17,6 @@ var obj1: Obj1;
>Obj1 : Symbol(Obj1, Decl(file.tsx, 2, 1))
<obj1 x={10} />; // OK
>obj1 : Symbol(unknown)
>x : Symbol(unknown)

View file

@ -8,5 +8,6 @@ declare module JSX {
// OK, but implicit any
<div n='x' />;
>div : Symbol(unknown)
>n : Symbol(unknown)

View file

@ -44,6 +44,8 @@ export class Button extends React.Component<any, any> {
>render : Symbol(render, Decl(button.tsx, 2, 55))
return <button>Some button</button>;
>button : Symbol(unknown)
>button : Symbol(unknown)
}
}

View file

@ -1,5 +1,5 @@
=== tests/cases/conformance/jsx/tsxNoJsx.tsx ===
No type information for this code.<nope />;
No type information for this code.
No type information for this code.
<nope />;
>nope : Symbol(unknown)

View file

@ -3,11 +3,13 @@
// A built-in element (OK)
var a1 = <div id="foo" />;
>a1 : Symbol(a1, Decl(tsxTypeErrors.tsx, 2, 3))
>div : Symbol(unknown)
>id : Symbol(unknown)
// A built-in element with a mistyped property (error)
var a2 = <img srce="foo.jpg" />
>a2 : Symbol(a2, Decl(tsxTypeErrors.tsx, 5, 3))
>img : Symbol(unknown)
>srce : Symbol(unknown)
// A built-in element with a badly-typed attribute value (error)
@ -17,12 +19,14 @@ var thing = { oops: 100 };
var a3 = <div id={thing} />
>a3 : Symbol(a3, Decl(tsxTypeErrors.tsx, 9, 3))
>div : Symbol(unknown)
>id : Symbol(unknown)
>thing : Symbol(thing, Decl(tsxTypeErrors.tsx, 8, 3))
// Mistyped html name (error)
var e1 = <imag src="bar.jpg" />
>e1 : Symbol(e1, Decl(tsxTypeErrors.tsx, 12, 3))
>imag : Symbol(unknown)
>src : Symbol(unknown)
// A custom type

View file

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// declare module JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// div: { ONE: string; TWO: number; }
//// }
//// }
//// var x1 = <div><//**/
goTo.marker();
verify.memberListCount(1);
verify.completionListContains('div');

View file

@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// declare module JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// div: { ONE: string; TWO: number; }
//// }
//// }
//// var x1 = <div>
//// <h1> Hello world </ /*2*/>
//// </ /*1*/>
goTo.marker("1");
verify.memberListCount(1);
verify.completionListContains('div');
goTo.marker("2");
verify.memberListCount(1);
verify.completionListContains('h1')

View file

@ -0,0 +1,8 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// var x1 = <div><//**/
goTo.marker();
verify.memberListCount(1);
verify.completionListContains('div');

View file

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// var x1 = <div>
//// <h1> Hello world </ /*2*/>
//// </ /*1*/>
goTo.marker("1");
verify.memberListCount(1);
verify.completionListContains('div');
goTo.marker("2");
verify.memberListCount(1);
verify.completionListContains('h1')

View file

@ -0,0 +1,7 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// var x = </**/;
goTo.marker();
verify.memberListCount(41);

View file

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// var x1 = <di/*1*/v></di/*2*/v>
//// class MyElement {}
//// var z = <My/*3*/Element></My/*4*/Element>
goTo.marker("1");
verify.quickInfoIs("any", undefined);
goTo.marker("2");
verify.quickInfoIs("any", undefined);;
goTo.marker("3");
verify.quickInfoIs("class MyElement", undefined);;
goTo.marker("4");
verify.quickInfoIs("class MyElement", undefined);;

View file

@ -0,0 +1,24 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// declare module JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// div: any
//// }
//// }
//// var x1 = <di/*1*/v></di/*2*/v>
//// class MyElement {}
//// var z = <My/*3*/Element></My/*4*/Element>
goTo.marker("1");
verify.quickInfoIs("(property) JSX.IntrinsicElements.div: any", undefined);
goTo.marker("2");
verify.quickInfoIs("(property) JSX.IntrinsicElements.div: any", undefined);;
goTo.marker("3");
verify.quickInfoIs("class MyElement", undefined);;
goTo.marker("4");
verify.quickInfoIs("class MyElement", undefined);;