diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d0c3e1f863..7ed9dbcd37 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index edf3a9d63a..9c5690901b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -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; diff --git a/src/services/services.ts b/src/services/services.ts index 2c8982f10e..52f6e12d39 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -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 = (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 =
completion list at "1" will contain "div" with type any + const tagName = (location.parent.parent).openingElement.tagName; + entries.push({ + name: (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: diff --git a/tests/baselines/reference/jsxEmitAttributeWithPreserve.symbols b/tests/baselines/reference/jsxEmitAttributeWithPreserve.symbols index 4ffadb8e88..82eceb8e63 100644 --- a/tests/baselines/reference/jsxEmitAttributeWithPreserve.symbols +++ b/tests/baselines/reference/jsxEmitAttributeWithPreserve.symbols @@ -4,5 +4,6 @@ declare var React: any; >React : Symbol(React, Decl(jsxEmitAttributeWithPreserve.tsx, 1, 11)) +>foo : Symbol(unknown) >data : Symbol(unknown) diff --git a/tests/baselines/reference/jsxHash.symbols b/tests/baselines/reference/jsxHash.symbols index 8a6ad0849f..ddba4832d2 100644 --- a/tests/baselines/reference/jsxHash.symbols +++ b/tests/baselines/reference/jsxHash.symbols @@ -1,34 +1,68 @@ === tests/cases/compiler/jsxHash.tsx === var t02 = {0}#; >t02 : Symbol(t02, Decl(jsxHash.tsx, 0, 3)) +>a : Symbol(unknown) +>a : Symbol(unknown) var t03 = #{0}; >t03 : Symbol(t03, Decl(jsxHash.tsx, 1, 3)) +>a : Symbol(unknown) +>a : Symbol(unknown) var t04 = #{0}#; >t04 : Symbol(t04, Decl(jsxHash.tsx, 2, 3)) +>a : Symbol(unknown) +>a : Symbol(unknown) var t05 = #; >t05 : Symbol(t05, Decl(jsxHash.tsx, 3, 3)) +>a : Symbol(unknown) +>i : Symbol(unknown) +>i : Symbol(unknown) +>a : Symbol(unknown) var t06 = #; >t06 : Symbol(t06, Decl(jsxHash.tsx, 4, 3)) +>a : Symbol(unknown) +>i : Symbol(unknown) +>i : Symbol(unknown) +>a : Symbol(unknown) var t07 = ##; >t07 : Symbol(t07, Decl(jsxHash.tsx, 5, 3)) +>a : Symbol(unknown) +>i : Symbol(unknown) +>i : Symbol(unknown) +>a : Symbol(unknown) var t08 = #; >t08 : Symbol(t08, Decl(jsxHash.tsx, 6, 3)) +>a : Symbol(unknown) +>i : Symbol(unknown) +>i : Symbol(unknown) +>a : Symbol(unknown) var t09 = ##; >t09 : Symbol(t09, Decl(jsxHash.tsx, 7, 3)) +>a : Symbol(unknown) +>i : Symbol(unknown) +>i : Symbol(unknown) +>a : Symbol(unknown) var t10 = #; >t10 : Symbol(t10, Decl(jsxHash.tsx, 8, 3)) +>a : Symbol(unknown) +>i : Symbol(unknown) +>a : Symbol(unknown) var t11 = #; >t11 : Symbol(t11, Decl(jsxHash.tsx, 9, 3)) +>a : Symbol(unknown) +>i : Symbol(unknown) +>a : Symbol(unknown) var t12 = #; >t12 : Symbol(t12, Decl(jsxHash.tsx, 10, 3)) +>a : Symbol(unknown) +>a : Symbol(unknown) diff --git a/tests/baselines/reference/jsxImportInAttribute.symbols b/tests/baselines/reference/jsxImportInAttribute.symbols index 845001b22c..252b5cc98e 100644 --- a/tests/baselines/reference/jsxImportInAttribute.symbols +++ b/tests/baselines/reference/jsxImportInAttribute.symbols @@ -8,6 +8,7 @@ let x = Test; // emit test_1.default >Test : Symbol(Test, Decl(consumer.tsx, 1, 6)) ; // ? +>anything : Symbol(unknown) >attr : Symbol(unknown) >Test : Symbol(Test, Decl(consumer.tsx, 1, 6)) diff --git a/tests/baselines/reference/jsxReactTestSuite.symbols b/tests/baselines/reference/jsxReactTestSuite.symbols index b86054ac56..cca1dae915 100644 --- a/tests/baselines/reference/jsxReactTestSuite.symbols +++ b/tests/baselines/reference/jsxReactTestSuite.symbols @@ -37,21 +37,36 @@ declare var hasOwnProperty:any; >hasOwnProperty : Symbol(hasOwnProperty, Decl(jsxReactTestSuite.tsx, 12, 11))
text
; +>div : Symbol(unknown) +>div : Symbol(unknown)
+>div : Symbol(unknown) + {this.props.children}
; +>div : Symbol(unknown)
+>div : Symbol(unknown) +

+>div : Symbol(unknown) +>br : Symbol(unknown) +>div : Symbol(unknown) + {foo}
{bar}
>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 : Symbol(unknown) +
; +>div : Symbol(unknown) @@ -74,6 +89,8 @@ var x = >x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3))
div : Symbol(unknown) + attr1={ >attr1 : Symbol(unknown) @@ -97,41 +114,64 @@ var x = >attr4 : Symbol(unknown)
; +>div : Symbol(unknown) (
+>div : Symbol(unknown) + {/* A comment at the beginning */} {/* A second comment at the beginning */} +>span : Symbol(unknown) + {/* A nested comment */} +>span : Symbol(unknown) + {/* A sandwiched comment */}
+>br : Symbol(unknown) + {/* A comment at the end */} {/* A second comment at the end */}
+>div : Symbol(unknown) + ); (
div : Symbol(unknown) + /* a multi-line comment */ attr1="foo"> >attr1 : Symbol(unknown) span : Symbol(unknown) + attr2="bar" >attr2 : Symbol(unknown) />
+>div : Symbol(unknown) + );
 
; +>div : Symbol(unknown) +>div : Symbol(unknown)
 
; +>div : Symbol(unknown) +>div : Symbol(unknown) testing; +>hasOwnProperty : Symbol(unknown) +>hasOwnProperty : Symbol(unknown) ; >Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) @@ -158,6 +198,7 @@ var x = >sound : Symbol(unknown) ; +>font-face : Symbol(unknown) ; >Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) @@ -165,6 +206,7 @@ var x = >y : Symbol(y, Decl(jsxReactTestSuite.tsx, 9, 11)) ; +>x-component : Symbol(unknown) ; >Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) diff --git a/tests/baselines/reference/keywordInJsxIdentifier.symbols b/tests/baselines/reference/keywordInJsxIdentifier.symbols index 874d7801a7..3cb977bee8 100644 --- a/tests/baselines/reference/keywordInJsxIdentifier.symbols +++ b/tests/baselines/reference/keywordInJsxIdentifier.symbols @@ -4,14 +4,18 @@ declare var React: any; >React : Symbol(React, Decl(keywordInJsxIdentifier.tsx, 1, 11)) ; +>foo : Symbol(unknown) >class-id : Symbol(unknown) ; +>foo : Symbol(unknown) >class : Symbol(unknown) ; +>foo : Symbol(unknown) >class-id : Symbol(unknown) ; +>foo : Symbol(unknown) >class : Symbol(unknown) diff --git a/tests/baselines/reference/reactNamespaceImportPresevation.symbols b/tests/baselines/reference/reactNamespaceImportPresevation.symbols index 8a4407c816..e2f530d31b 100644 --- a/tests/baselines/reference/reactNamespaceImportPresevation.symbols +++ b/tests/baselines/reference/reactNamespaceImportPresevation.symbols @@ -16,5 +16,6 @@ declare var foo: any; >foo : Symbol(foo, Decl(test.tsx, 1, 11)) ; +>foo : Symbol(unknown) >data : Symbol(unknown) diff --git a/tests/baselines/reference/reactNamespaceJSXEmit.symbols b/tests/baselines/reference/reactNamespaceJSXEmit.symbols index d79c1cf531..3ca5b91e53 100644 --- a/tests/baselines/reference/reactNamespaceJSXEmit.symbols +++ b/tests/baselines/reference/reactNamespaceJSXEmit.symbols @@ -13,6 +13,7 @@ declare var x: any; >x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11)) ; +>foo : Symbol(unknown) >data : Symbol(unknown) ; @@ -21,6 +22,8 @@ declare var x: any; >x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11)) ; +>x-component : Symbol(unknown) + ; >Bar : Symbol(Bar, Decl(reactNamespaceJSXEmit.tsx, 3, 11)) >x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11)) diff --git a/tests/baselines/reference/tsxElementResolution13.symbols b/tests/baselines/reference/tsxElementResolution13.symbols index 94758b291a..4b6a5b4f4e 100644 --- a/tests/baselines/reference/tsxElementResolution13.symbols +++ b/tests/baselines/reference/tsxElementResolution13.symbols @@ -22,5 +22,6 @@ var obj1: Obj1; >Obj1 : Symbol(Obj1, Decl(file.tsx, 3, 1)) ; // Error +>obj1 : Symbol(unknown) >x : Symbol(unknown) diff --git a/tests/baselines/reference/tsxElementResolution14.symbols b/tests/baselines/reference/tsxElementResolution14.symbols index 2400ef620e..a605606b1e 100644 --- a/tests/baselines/reference/tsxElementResolution14.symbols +++ b/tests/baselines/reference/tsxElementResolution14.symbols @@ -17,5 +17,6 @@ var obj1: Obj1; >Obj1 : Symbol(Obj1, Decl(file.tsx, 2, 1)) ; // OK +>obj1 : Symbol(unknown) >x : Symbol(unknown) diff --git a/tests/baselines/reference/tsxElementResolution5.symbols b/tests/baselines/reference/tsxElementResolution5.symbols index 461ffd78aa..e0fc114708 100644 --- a/tests/baselines/reference/tsxElementResolution5.symbols +++ b/tests/baselines/reference/tsxElementResolution5.symbols @@ -8,5 +8,6 @@ declare module JSX { // OK, but implicit any
; +>div : Symbol(unknown) >n : Symbol(unknown) diff --git a/tests/baselines/reference/tsxExternalModuleEmit1.symbols b/tests/baselines/reference/tsxExternalModuleEmit1.symbols index 5e8eb2fed4..129edcca0d 100644 --- a/tests/baselines/reference/tsxExternalModuleEmit1.symbols +++ b/tests/baselines/reference/tsxExternalModuleEmit1.symbols @@ -44,6 +44,8 @@ export class Button extends React.Component { >render : Symbol(render, Decl(button.tsx, 2, 55)) return ; +>button : Symbol(unknown) +>button : Symbol(unknown) } } diff --git a/tests/baselines/reference/tsxNoJsx.symbols b/tests/baselines/reference/tsxNoJsx.symbols index 6744c2edb1..4492e83cf1 100644 --- a/tests/baselines/reference/tsxNoJsx.symbols +++ b/tests/baselines/reference/tsxNoJsx.symbols @@ -1,5 +1,5 @@ === tests/cases/conformance/jsx/tsxNoJsx.tsx === -No type information for this code.; -No type information for this code. -No type information for this code. \ No newline at end of file +; +>nope : Symbol(unknown) + diff --git a/tests/baselines/reference/tsxTypeErrors.symbols b/tests/baselines/reference/tsxTypeErrors.symbols index 94399b95d1..2b87e99447 100644 --- a/tests/baselines/reference/tsxTypeErrors.symbols +++ b/tests/baselines/reference/tsxTypeErrors.symbols @@ -3,11 +3,13 @@ // A built-in element (OK) var a1 =
; >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 = >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 =
>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 = >e1 : Symbol(e1, Decl(tsxTypeErrors.tsx, 12, 3)) +>imag : Symbol(unknown) >src : Symbol(unknown) // A custom type diff --git a/tests/cases/fourslash/tsxCompletionOnClosingTag1.ts b/tests/cases/fourslash/tsxCompletionOnClosingTag1.ts new file mode 100644 index 0000000000..54a0b61879 --- /dev/null +++ b/tests/cases/fourslash/tsxCompletionOnClosingTag1.ts @@ -0,0 +1,14 @@ +/// + +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// div: { ONE: string; TWO: number; } +//// } +//// } +//// var x1 =
+ +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// div: { ONE: string; TWO: number; } +//// } +//// } +//// var x1 =
+////

Hello world +//// + +goTo.marker("1"); +verify.memberListCount(1); +verify.completionListContains('div'); + +goTo.marker("2"); +verify.memberListCount(1); +verify.completionListContains('h1') diff --git a/tests/cases/fourslash/tsxCompletionOnClosingTagWithoutJSX1.ts b/tests/cases/fourslash/tsxCompletionOnClosingTagWithoutJSX1.ts new file mode 100644 index 0000000000..742009b487 --- /dev/null +++ b/tests/cases/fourslash/tsxCompletionOnClosingTagWithoutJSX1.ts @@ -0,0 +1,8 @@ +/// + +//@Filename: file.tsx +//// var x1 =
+ +//@Filename: file.tsx +//// var x1 =
+////

Hello world +//// + +goTo.marker("1"); +verify.memberListCount(1); +verify.completionListContains('div'); + +goTo.marker("2"); +verify.memberListCount(1); +verify.completionListContains('h1') diff --git a/tests/cases/fourslash/tsxCompletionOnOpeningTagWithoutJSX1.ts b/tests/cases/fourslash/tsxCompletionOnOpeningTagWithoutJSX1.ts new file mode 100644 index 0000000000..1b745f6e41 --- /dev/null +++ b/tests/cases/fourslash/tsxCompletionOnOpeningTagWithoutJSX1.ts @@ -0,0 +1,7 @@ +/// + +//@Filename: file.tsx +//// var x = + +//@Filename: file.tsx +//// var x1 = +//// class MyElement {} +//// var z = + +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);; \ No newline at end of file diff --git a/tests/cases/fourslash/tsxQuickInfo2.ts b/tests/cases/fourslash/tsxQuickInfo2.ts new file mode 100644 index 0000000000..0eb7a5100c --- /dev/null +++ b/tests/cases/fourslash/tsxQuickInfo2.ts @@ -0,0 +1,24 @@ +/// + +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// div: any +//// } +//// } +//// var x1 = +//// class MyElement {} +//// var z = + +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);; \ No newline at end of file