diff --git a/.travis.yml b/.travis.yml index d24e155b580..06e912c55f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ language: node_js node_js: - 'stable' + - '8' - '6' - - '4' sudo: false diff --git a/Gulpfile.ts b/Gulpfile.ts index 5a27eb52b32..283535208ed 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -171,9 +171,9 @@ const librarySourceMap = [ // JavaScript + all host library { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) }, { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }, - { target: "lib.es2016.full.d.ts", sources: ["header.d.ts", "es2016.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }, - { target: "lib.es2017.full.d.ts", sources: ["header.d.ts", "es2017.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }, - { target: "lib.esnext.full.d.ts", sources: ["header.d.ts", "esnext.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }, + { target: "lib.es2016.full.d.ts", sources: ["header.d.ts", "es2016.d.ts"].concat(hostsLibrarySources, "dom.iterable.d.ts") }, + { target: "lib.es2017.full.d.ts", sources: ["header.d.ts", "es2017.d.ts"].concat(hostsLibrarySources, "dom.iterable.d.ts") }, + { target: "lib.esnext.full.d.ts", sources: ["header.d.ts", "esnext.d.ts"].concat(hostsLibrarySources, "dom.iterable.d.ts") }, ].concat(es2015LibrarySourceMap, es2016LibrarySourceMap, es2017LibrarySourceMap, esnextLibrarySourceMap); const libraryTargets = librarySourceMap.map(function(f) { diff --git a/netci.groovy b/netci.groovy index fc6d00e4e7f..5fa8b02baf2 100644 --- a/netci.groovy +++ b/netci.groovy @@ -5,7 +5,7 @@ import jobs.generation.Utilities; def project = GithubProject def branch = GithubBranchName -def nodeVersions = ['stable', '6', '4'] +def nodeVersions = ['stable', '8', '6'] nodeVersions.each { nodeVer -> diff --git a/scripts/generateLocalizedDiagnosticMessages.ts b/scripts/generateLocalizedDiagnosticMessages.ts index 36df92590c7..00bd8314a9b 100644 --- a/scripts/generateLocalizedDiagnosticMessages.ts +++ b/scripts/generateLocalizedDiagnosticMessages.ts @@ -65,10 +65,11 @@ function main(): void { * There are three exceptions, zh-CN, zh-TW and pt-BR. */ function getPreferedLocaleName(localeName: string) { + localeName = localeName.toLowerCase(); switch (localeName) { - case "zh-CN": - case "zh-TW": - case "pt-BR": + case "zh-cn": + case "zh-tw": + case "pt-br": return localeName; default: return localeName.split("-")[0]; diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 35a62a644d8..602a9c505ad 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -192,7 +192,7 @@ namespace ts { return bindSourceFile; function bindInStrictMode(file: SourceFile, opts: CompilerOptions): boolean { - if ((opts.alwaysStrict === undefined ? opts.strict : opts.alwaysStrict) && !file.isDeclarationFile) { + if (getStrictOptionValue(opts, "alwaysStrict") && !file.isDeclarationFile) { // bind in strict mode source files with alwaysStrict option return true; } @@ -3296,6 +3296,9 @@ namespace ts { case SyntaxKind.JsxOpeningElement: case SyntaxKind.JsxText: case SyntaxKind.JsxClosingElement: + case SyntaxKind.JsxFragment: + case SyntaxKind.JsxOpeningFragment: + case SyntaxKind.JsxClosingFragment: case SyntaxKind.JsxAttribute: case SyntaxKind.JsxAttributes: case SyntaxKind.JsxSpreadAttribute: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 146be93c5d5..9cd21263b8d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -65,10 +65,10 @@ namespace ts { const modulekind = getEmitModuleKind(compilerOptions); const noUnusedIdentifiers = !!compilerOptions.noUnusedLocals || !!compilerOptions.noUnusedParameters; const allowSyntheticDefaultImports = typeof compilerOptions.allowSyntheticDefaultImports !== "undefined" ? compilerOptions.allowSyntheticDefaultImports : modulekind === ModuleKind.System; - const strictNullChecks = compilerOptions.strictNullChecks === undefined ? compilerOptions.strict : compilerOptions.strictNullChecks; - const strictFunctionTypes = compilerOptions.strictFunctionTypes === undefined ? compilerOptions.strict : compilerOptions.strictFunctionTypes; - const noImplicitAny = compilerOptions.noImplicitAny === undefined ? compilerOptions.strict : compilerOptions.noImplicitAny; - const noImplicitThis = compilerOptions.noImplicitThis === undefined ? compilerOptions.strict : compilerOptions.noImplicitThis; + const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks"); + const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes"); + const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny"); + const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis"); const emitResolver = createResolver(); const nodeBuilder = createNodeBuilder(); @@ -1368,7 +1368,7 @@ namespace ts { } } else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) { - const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); + const symbol = resolveSymbol(resolveName(errorLocation, name, (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); if (symbol) { error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name)); return true; @@ -2444,18 +2444,21 @@ namespace ts { function createNodeBuilder() { return { typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { + Debug.assert(enclosingDeclaration === undefined || (enclosingDeclaration.flags & NodeFlags.Synthesized) === 0); const context = createNodeBuilderContext(enclosingDeclaration, flags); const resultingNode = typeToTypeNodeHelper(type, context); const result = context.encounteredError ? undefined : resultingNode; return result; }, indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { + Debug.assert(enclosingDeclaration === undefined || (enclosingDeclaration.flags & NodeFlags.Synthesized) === 0); const context = createNodeBuilderContext(enclosingDeclaration, flags); const resultingNode = indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind, context); const result = context.encounteredError ? undefined : resultingNode; return result; }, signatureToSignatureDeclaration: (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { + Debug.assert(enclosingDeclaration === undefined || (enclosingDeclaration.flags & NodeFlags.Synthesized) === 0); const context = createNodeBuilderContext(enclosingDeclaration, flags); const resultingNode = signatureToSignatureDeclarationHelper(signature, kind, context); const result = context.encounteredError ? undefined : resultingNode; @@ -2496,7 +2499,7 @@ namespace ts { if (type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union)) { const parentSymbol = getParentOfSymbol(type.symbol); const parentName = symbolToName(parentSymbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); - const enumLiteralName = getDeclaredTypeOfSymbol(parentSymbol) === type ? parentName : createQualifiedName(parentName, getNameOfSymbol(type.symbol, context)); + const enumLiteralName = getDeclaredTypeOfSymbol(parentSymbol) === type ? parentName : createQualifiedName(parentName, symbolName(type.symbol)); return createTypeReferenceNode(enumLiteralName, /*typeArguments*/ undefined); } if (type.flags & TypeFlags.EnumLike) { @@ -3016,8 +3019,7 @@ namespace ts { typeParameterNodes = mapToTypeNodes(typeParameters, context); } - const symbolName = getNameOfSymbol(symbol, context); - const identifier = setEmitFlags(createIdentifier(symbolName, typeParameterNodes), EmitFlags.NoAsciiEscaping); + const identifier = setEmitFlags(createIdentifier(getNameOfSymbolAsWritten(symbol, context), typeParameterNodes), EmitFlags.NoAsciiEscaping); return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier; } @@ -3129,7 +3131,14 @@ namespace ts { symbolStack: Symbol[] | undefined; } - function getNameOfSymbol(symbol: Symbol, context?: NodeBuilderContext): string { + /** + * Gets a human-readable name for a symbol. + * Should *not* be used for the right-hand side of a `.` -- use `symbolName(symbol)` for that instead. + * + * Unlike `symbolName(symbol)`, this will include quotes if the name is from a string literal. + * It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`. + */ + function getNameOfSymbolAsWritten(symbol: Symbol, context?: NodeBuilderContext): string { if (symbol.declarations && symbol.declarations.length) { const declaration = symbol.declarations[0]; const name = getNameOfDeclaration(declaration); @@ -3166,7 +3175,7 @@ namespace ts { * for the name of the symbol if it is available to match how the user wrote the name. */ function appendSymbolNameOnly(symbol: Symbol, writer: SymbolWriter): void { - writer.writeSymbol(getNameOfSymbol(symbol), symbol); + writer.writeSymbol(getNameOfSymbolAsWritten(symbol), symbol); } /** @@ -3175,7 +3184,7 @@ namespace ts { * ensuring that any names written with literals use element accesses. */ function appendPropertyOrElementAccessForSymbol(symbol: Symbol, writer: SymbolWriter): void { - const symbolName = getNameOfSymbol(symbol); + const symbolName = symbol.escapedName === "default" ? "default" : getNameOfSymbolAsWritten(symbol); const firstChar = symbolName.charCodeAt(0); const needsElementAccess = !isIdentifierStart(firstChar, languageVersion); @@ -6567,7 +6576,7 @@ namespace ts { if (!node) return false; switch (node.kind) { case SyntaxKind.Identifier: - return (node).escapedText === "arguments" && isPartOfExpression(node); + return (node).escapedText === "arguments" && isExpressionNode(node); case SyntaxKind.PropertyDeclaration: case SyntaxKind.MethodDeclaration: @@ -6886,7 +6895,7 @@ namespace ts { const numTypeArguments = length(node.typeArguments); const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); const isJs = isInJavaScriptFile(node); - const isJsImplicitAny = !compilerOptions.noImplicitAny && isJs; + const isJsImplicitAny = !noImplicitAny && isJs; if (!isJsImplicitAny && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) { const missingAugmentsTag = isJs && node.parent.kind !== SyntaxKind.JSDocAugmentsTag; const diag = minTypeArgumentCount === typeParameters.length @@ -9462,32 +9471,30 @@ namespace ts { } } } + else if (isGenericMappedType(target) && !isGenericMappedType(source) && getConstraintTypeFromMappedType(target) === getIndexType(source)) { + // A source type T is related to a target type { [P in keyof T]: X } if T[P] is related to X. + const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(target)); + const templateType = getTemplateTypeFromMappedType(target); + if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) { + errorInfo = saveErrorInfo; + return result; + } + } if (source.flags & TypeFlags.TypeParameter) { - // A source type T is related to a target type { [P in keyof T]: X } if T[P] is related to X. - if (getObjectFlags(target) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(target) === getIndexType(source)) { - const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(target)); - const templateType = getTemplateTypeFromMappedType(target); - if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) { + let constraint = getConstraintOfTypeParameter(source); + // A type parameter with no constraint is not related to the non-primitive object type. + if (constraint || !(target.flags & TypeFlags.NonPrimitive)) { + if (!constraint || constraint.flags & TypeFlags.Any) { + constraint = emptyObjectType; + } + // Report constraint errors only if the constraint is not the empty object type + const reportConstraintErrors = reportErrors && constraint !== emptyObjectType; + if (result = isRelatedTo(constraint, target, reportConstraintErrors)) { errorInfo = saveErrorInfo; return result; } } - else { - let constraint = getConstraintOfTypeParameter(source); - // A type parameter with no constraint is not related to the non-primitive object type. - if (constraint || !(target.flags & TypeFlags.NonPrimitive)) { - if (!constraint || constraint.flags & TypeFlags.Any) { - constraint = emptyObjectType; - } - // Report constraint errors only if the constraint is not the empty object type - const reportConstraintErrors = reportErrors && constraint !== emptyObjectType; - if (result = isRelatedTo(constraint, target, reportConstraintErrors)) { - errorInfo = saveErrorInfo; - return result; - } - } - } } else if (source.flags & TypeFlags.IndexedAccess) { // A type S[K] is related to a type T if A[K] is related to T, where K is string-like and @@ -12528,7 +12535,7 @@ namespace ts { if (isRightSideOfQualifiedNameOrPropertyAccess(location)) { location = location.parent; } - if (isPartOfExpression(location) && !isAssignmentTarget(location)) { + if (isExpressionNode(location) && !isAssignmentTarget(location)) { const type = getTypeOfExpression(location); if (getExportSymbolOfValueSymbolIfExported(getNodeLinks(location).resolvedSymbol) === symbol) { return type; @@ -13603,7 +13610,13 @@ namespace ts { // JSX expression can appear in two position : JSX Element's children or JSX attribute const jsxAttributes = isJsxAttributeLike(node.parent) ? node.parent.parent : - node.parent.openingElement.attributes; // node.parent is JsxElement + isJsxElement(node.parent) ? + node.parent.openingElement.attributes : + undefined; // node.parent is JsxFragment with no attributes + + if (!jsxAttributes) { + return undefined; // don't check children of a fragment + } // When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type // which is a type of the parameter of the signature we are trying out. @@ -14182,13 +14195,13 @@ namespace ts { } function checkJsxSelfClosingElement(node: JsxSelfClosingElement): Type { - checkJsxOpeningLikeElement(node); + checkJsxOpeningLikeElementOrOpeningFragment(node); return getJsxGlobalElementType() || anyType; } function checkJsxElement(node: JsxElement): Type { // Check attributes - checkJsxOpeningLikeElement(node.openingElement); + checkJsxOpeningLikeElementOrOpeningFragment(node.openingElement); // Perform resolution on the closing tag so that rename/go to definition/etc work if (isJsxIntrinsicIdentifier(node.closingElement.tagName)) { @@ -14201,6 +14214,16 @@ namespace ts { return getJsxGlobalElementType() || anyType; } + function checkJsxFragment(node: JsxFragment): Type { + checkJsxOpeningLikeElementOrOpeningFragment(node.openingFragment); + + if (compilerOptions.jsx === JsxEmit.React && compilerOptions.jsxFactory) { + error(node, Diagnostics.JSX_fragment_is_not_supported_when_using_jsxFactory); + } + + return getJsxGlobalElementType() || anyType; + } + /** * Returns true iff the JSX element name would be a valid JS identifier, ignoring restrictions about keywords not being identifiers */ @@ -14864,14 +14887,19 @@ namespace ts { } } - function checkJsxOpeningLikeElement(node: JsxOpeningLikeElement) { - checkGrammarJsxElement(node); + function checkJsxOpeningLikeElementOrOpeningFragment(node: JsxOpeningLikeElement | JsxOpeningFragment) { + const isNodeOpeningLikeElement = isJsxOpeningLikeElement(node); + + if (isNodeOpeningLikeElement) { + checkGrammarJsxElement(node); + } checkJsxPreconditions(node); // The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import. // And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error. const reactRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined; const reactNamespace = getJsxNamespace(); - const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace, /*isUse*/ true); + const reactLocation = isNodeOpeningLikeElement ? (node).tagName : node; + const reactSym = resolveName(reactLocation, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace, /*isUse*/ true); if (reactSym) { // Mark local symbol as referenced here because it might not have been marked // if jsx emit was not react as there wont be error being emitted @@ -14883,7 +14911,9 @@ namespace ts { } } - checkJsxAttributesAssignableToTagNameAttributes(node); + if (isNodeOpeningLikeElement) { + checkJsxAttributesAssignableToTagNameAttributes(node); + } } /** @@ -15039,12 +15069,11 @@ namespace ts { } } - // Referencing Abstract Properties within Constructors is not allowed - if ((flags & ModifierFlags.Abstract) && symbolHasNonMethodDeclaration(prop)) { + // Referencing abstract properties within their own constructors is not allowed + if ((flags & ModifierFlags.Abstract) && isThisProperty(node) && symbolHasNonMethodDeclaration(prop)) { const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)); - - if (declaringClassDeclaration && isNodeWithinConstructor(node, declaringClassDeclaration)) { - error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), typeToString(getDeclaringClass(prop))); + if (declaringClassDeclaration && isNodeWithinConstructorOfClass(node, declaringClassDeclaration)) { + error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), getTextOfIdentifierOrLiteral(declaringClassDeclaration.name)); return false; } } @@ -15216,7 +15245,7 @@ namespace ts { // We might be in `a = { b: this.b }`, so keep looking. See `tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts`. return false; default: - return isPartOfExpression(node) ? false : "quit"; + return isExpressionNode(node) ? false : "quit"; } }); } @@ -18652,6 +18681,8 @@ namespace ts { return checkJsxElement(node); case SyntaxKind.JsxSelfClosingElement: return checkJsxSelfClosingElement(node); + case SyntaxKind.JsxFragment: + return checkJsxFragment(node); case SyntaxKind.JsxAttributes: return checkJsxAttributes(node, checkMode); case SyntaxKind.JsxOpeningElement: @@ -19003,7 +19034,7 @@ namespace ts { case "arguments": case "prototype": const message = Diagnostics.Static_property_0_conflicts_with_built_in_property_Function_0_of_constructor_function_1; - const className = getNameOfSymbol(getSymbolOfNode(node)); + const className = getNameOfSymbolAsWritten(getSymbolOfNode(node)); error(memberNameNode, message, memberName, className); break; } @@ -23366,9 +23397,9 @@ namespace ts { return result; } - function isNodeWithinConstructor(node: Node, classDeclaration: ClassLikeDeclaration) { + function isNodeWithinConstructorOfClass(node: Node, classDeclaration: ClassLikeDeclaration) { return findAncestor(node, element => { - if (isConstructorDeclaration(element) && nodeIsPresent(element.body)) { + if (isConstructorDeclaration(element) && nodeIsPresent(element.body) && element.parent === classDeclaration) { return true; } else if (element === classDeclaration || isFunctionLikeDeclaration(element)) { @@ -23479,7 +23510,7 @@ namespace ts { return typeParameter && typeParameter.symbol; } - if (isPartOfExpression(entityName)) { + if (isExpressionNode(entityName)) { if (nodeIsMissing(entityName)) { // Missing entity name. return undefined; @@ -23653,7 +23684,7 @@ namespace ts { return typeFromTypeNode; } - if (isPartOfExpression(node)) { + if (isExpressionNode(node)) { return getRegularTypeOfExpression(node); } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index f050c4a5d16..b7003fbffd9 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1138,6 +1138,13 @@ namespace ts { reportInvalidOptionValue(option && option.type !== "number"); return Number((valueExpression).text); + case SyntaxKind.PrefixUnaryExpression: + if ((valueExpression).operator !== SyntaxKind.MinusToken || (valueExpression).operand.kind !== SyntaxKind.NumericLiteral) { + break; // not valid JSON syntax + } + reportInvalidOptionValue(option && option.type !== "number"); + return -Number(((valueExpression).operand).text); + case SyntaxKind.ObjectLiteralExpression: reportInvalidOptionValue(option && option.type !== "object"); const objectLiteralExpression = valueExpression; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 12b8dd2f87e..23493b3087d 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -985,32 +985,6 @@ namespace ts { return initial; } - export function reduceRight(array: ReadonlyArray, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; - export function reduceRight(array: ReadonlyArray, f: (memo: T, value: T, i: number) => T): T; - export function reduceRight(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T { - if (array) { - const size = array.length; - if (size > 0) { - let pos = start === undefined || start > size - 1 ? size - 1 : start; - const end = count === undefined || pos - count < 0 ? 0 : pos - count; - let result: T; - if (arguments.length <= 2) { - result = array[pos]; - pos--; - } - else { - result = initial; - } - while (pos >= end) { - result = f(result, array[pos], pos); - pos--; - } - return result; - } - } - return initial; - } - const hasOwnProperty = Object.prototype.hasOwnProperty; /** @@ -1710,6 +1684,12 @@ namespace ts { return moduleResolution; } + export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "alwaysStrict"; + + export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean { + return compilerOptions[flag] === undefined ? compilerOptions.strict : compilerOptions[flag]; + } + export function hasZeroOrOneAsteriskCharacter(str: string): boolean { let seenAsterisk = false; for (let i = 0; i < str.length; i++) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 0f88d5e4ac5..8f855347bd8 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3615,6 +3615,18 @@ "category": "Error", "code": 17013 }, + "JSX fragment has no corresponding closing tag.": { + "category": "Error", + "code": 17014 + }, + "Expected corresponding closing tag for JSX fragment.": { + "category": "Error", + "code": 17015 + }, + "JSX fragment is not supported when using --jsxFactory": { + "category": "Error", + "code":17016 + }, "Circularity detected while resolving configuration: {0}": { "category": "Error", diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 9ce220e723f..8c41eda192a 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -699,9 +699,11 @@ namespace ts { case SyntaxKind.JsxText: return emitJsxText(node); case SyntaxKind.JsxOpeningElement: - return emitJsxOpeningElement(node); + case SyntaxKind.JsxOpeningFragment: + return emitJsxOpeningElementOrFragment(node); case SyntaxKind.JsxClosingElement: - return emitJsxClosingElement(node); + case SyntaxKind.JsxClosingFragment: + return emitJsxClosingElementOrFragment(node); case SyntaxKind.JsxAttribute: return emitJsxAttribute(node); case SyntaxKind.JsxAttributes: @@ -836,6 +838,8 @@ namespace ts { return emitJsxElement(node); case SyntaxKind.JsxSelfClosingElement: return emitJsxSelfClosingElement(node); + case SyntaxKind.JsxFragment: + return emitJsxFragment(node); // Transformation nodes case SyntaxKind.PartiallyEmittedExpression: @@ -2060,7 +2064,7 @@ namespace ts { function emitJsxElement(node: JsxElement) { emit(node.openingElement); - emitList(node, node.children, ListFormat.JsxElementChildren); + emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); emit(node.closingElement); } @@ -2075,14 +2079,24 @@ namespace ts { write("/>"); } - function emitJsxOpeningElement(node: JsxOpeningElement) { + function emitJsxFragment(node: JsxFragment) { + emit(node.openingFragment); + emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); + emit(node.closingFragment); + } + + function emitJsxOpeningElementOrFragment(node: JsxOpeningElement | JsxOpeningFragment) { write("<"); - emitJsxTagName(node.tagName); - writeIfAny(node.attributes.properties, " "); - // We are checking here so we won't re-enter the emitting pipeline and emit extra sourcemap - if (node.attributes.properties && node.attributes.properties.length > 0) { - emit(node.attributes); + + if (isJsxOpeningElement(node)) { + emitJsxTagName(node.tagName); + // We are checking here so we won't re-enter the emitting pipeline and emit extra sourcemap + if (node.attributes.properties && node.attributes.properties.length > 0) { + write(" "); + emit(node.attributes); + } } + write(">"); } @@ -2090,9 +2104,11 @@ namespace ts { writer.writeLiteral(getTextOfNode(node, /*includeTrivia*/ true)); } - function emitJsxClosingElement(node: JsxClosingElement) { + function emitJsxClosingElementOrFragment(node: JsxClosingElement | JsxClosingFragment) { write(""); } @@ -2611,12 +2627,6 @@ namespace ts { writer.decreaseIndent(); } - function writeIfAny(nodes: NodeArray, text: string) { - if (some(nodes)) { - write(text); - } - } - function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) { return onEmitSourceMapOfToken ? onEmitSourceMapOfToken(contextNode, token, pos, writeTokenText) @@ -3176,7 +3186,7 @@ namespace ts { EnumMembers = CommaDelimited | Indented | MultiLine, CaseBlockClauses = Indented | MultiLine, NamedImportsOrExportsElements = CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | SingleLine | SpaceBetweenBraces, - JsxElementChildren = SingleLine | NoInterveningComments, + JsxElementOrFragmentChildren = SingleLine | NoInterveningComments, JsxElementAttributes = SingleLine | SpaceBetweenSiblings | NoInterveningComments, CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty, HeritageClauseTypes = CommaDelimited | SpaceBetweenSiblings | SingleLine, diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index de744ca3f00..d16903bc300 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1982,7 +1982,7 @@ namespace ts { node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); node.isExportEquals = isExportEquals; - node.expression = expression; + node.expression = isExportEquals ? parenthesizeBinaryOperand(SyntaxKind.EqualsToken, expression, /*isLeftSideOfBinary*/ false, /*leftOperand*/ undefined) : parenthesizeDefaultExpression(expression); return node; } @@ -2115,6 +2115,22 @@ namespace ts { : node; } + export function createJsxFragment(openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment) { + const node = createSynthesizedNode(SyntaxKind.JsxFragment); + node.openingFragment = openingFragment; + node.children = createNodeArray(children); + node.closingFragment = closingFragment; + return node; + } + + export function updateJsxFragment(node: JsxFragment, openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment) { + return node.openingFragment !== openingFragment + || node.children !== children + || node.closingFragment !== closingFragment + ? updateNode(createJsxFragment(openingFragment, children, closingFragment), node) + : node; + } + export function createJsxAttribute(name: Identifier, initializer: StringLiteral | JsxExpression) { const node = createSynthesizedNode(SyntaxKind.JsxAttribute); node.name = name; @@ -2951,7 +2967,7 @@ namespace ts { ); } - function createReactNamespace(reactNamespace: string, parent: JsxOpeningLikeElement) { + function createReactNamespace(reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment) { // To ensure the emit resolver can properly resolve the namespace, we need to // treat this identifier as if it were a source tree node by clearing the `Synthesized` // flag and setting a parent node. @@ -2963,7 +2979,7 @@ namespace ts { return react; } - function createJsxFactoryExpressionFromEntityName(jsxFactory: EntityName, parent: JsxOpeningLikeElement): Expression { + function createJsxFactoryExpressionFromEntityName(jsxFactory: EntityName, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { if (isQualifiedName(jsxFactory)) { const left = createJsxFactoryExpressionFromEntityName(jsxFactory.left, parent); const right = createIdentifier(idText(jsxFactory.right)); @@ -2975,7 +2991,7 @@ namespace ts { } } - function createJsxFactoryExpression(jsxFactoryEntity: EntityName, reactNamespace: string, parent: JsxOpeningLikeElement): Expression { + function createJsxFactoryExpression(jsxFactoryEntity: EntityName, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { return jsxFactoryEntity ? createJsxFactoryExpressionFromEntityName(jsxFactoryEntity, parent) : createPropertyAccess( @@ -3016,6 +3032,37 @@ namespace ts { ); } + export function createExpressionForJsxFragment(jsxFactoryEntity: EntityName, reactNamespace: string, children: Expression[], parentElement: JsxOpeningFragment, location: TextRange): LeftHandSideExpression { + const tagName = createPropertyAccess( + createReactNamespace(reactNamespace, parentElement), + "Fragment" + ); + + const argumentsList = [tagName]; + argumentsList.push(createNull()); + + if (children && children.length > 0) { + if (children.length > 1) { + for (const child of children) { + child.startsOnNewLine = true; + argumentsList.push(child); + } + } + else { + argumentsList.push(children[0]); + } + } + + return setTextRange( + createCall( + createJsxFactoryExpression(jsxFactoryEntity, reactNamespace, parentElement), + /*typeArguments*/ undefined, + argumentsList + ), + location + ); + } + // Helpers export function getHelperName(name: string) { @@ -3885,6 +3932,27 @@ namespace ts { : e; } + /** + * [Per the spec](https://tc39.github.io/ecma262/#prod-ExportDeclaration), `export default` accepts _AssigmentExpression_ but + * has a lookahead restriction for `function`, `async function`, and `class`. + * + * Basically, that means we need to parenthesize in the following cases: + * + * - BinaryExpression of CommaToken + * - CommaList (synthetic list of multiple comma expressions) + * - FunctionExpression + * - ClassExpression + */ + export function parenthesizeDefaultExpression(e: Expression) { + const check = skipPartiallyEmittedExpressions(e); + return (check.kind === SyntaxKind.ClassExpression || + check.kind === SyntaxKind.FunctionExpression || + check.kind === SyntaxKind.CommaListExpression || + isBinaryExpression(check) && check.operatorToken.kind === SyntaxKind.CommaToken) + ? createParen(e) + : e; + } + /** * Wraps an expression in parentheses if it is needed in order to use the expression * as the expression of a NewExpression node. diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b27d4811ecb..648f3e30d1c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -377,6 +377,10 @@ namespace ts { return visitNode(cbNode, (node).openingElement) || visitNodes(cbNode, cbNodes, (node).children) || visitNode(cbNode, (node).closingElement); + case SyntaxKind.JsxFragment: + return visitNode(cbNode, (node).openingFragment) || + visitNodes(cbNode, cbNodes, (node).children) || + visitNode(cbNode, (node).closingFragment); case SyntaxKind.JsxSelfClosingElement: case SyntaxKind.JsxOpeningElement: return visitNode(cbNode, (node).tagName) || @@ -1423,6 +1427,11 @@ namespace ts { return tokenIsIdentifierOrKeyword(token()); } + function nextTokenIsIdentifierOrKeywordOrGreaterThan() { + nextToken(); + return tokenIsIdentifierOrKeywordOrGreaterThan(token()); + } + function isHeritageClauseExtendsOrImplementsKeyword(): boolean { if (token() === SyntaxKind.ImplementsKeyword || token() === SyntaxKind.ExtendsKeyword) { @@ -3802,9 +3811,9 @@ namespace ts { node.operand = parseLeftHandSideExpressionOrHigher(); return finishNode(node); } - else if (sourceFile.languageVariant === LanguageVariant.JSX && token() === SyntaxKind.LessThanToken && lookAhead(nextTokenIsIdentifierOrKeyword)) { + else if (sourceFile.languageVariant === LanguageVariant.JSX && token() === SyntaxKind.LessThanToken && lookAhead(nextTokenIsIdentifierOrKeywordOrGreaterThan)) { // JSXElement is part of primaryExpression - return parseJsxElementOrSelfClosingElement(/*inExpressionContext*/ true); + return parseJsxElementOrSelfClosingElementOrFragment(/*inExpressionContext*/ true); } const expression = parseLeftHandSideExpressionOrHigher(); @@ -3959,14 +3968,14 @@ namespace ts { } - function parseJsxElementOrSelfClosingElement(inExpressionContext: boolean): JsxElement | JsxSelfClosingElement { - const opening = parseJsxOpeningOrSelfClosingElement(inExpressionContext); - let result: JsxElement | JsxSelfClosingElement; + function parseJsxElementOrSelfClosingElementOrFragment(inExpressionContext: boolean): JsxElement | JsxSelfClosingElement | JsxFragment { + const opening = parseJsxOpeningOrSelfClosingElementOrOpeningFragment(inExpressionContext); + let result: JsxElement | JsxSelfClosingElement | JsxFragment; if (opening.kind === SyntaxKind.JsxOpeningElement) { const node = createNode(SyntaxKind.JsxElement, opening.pos); node.openingElement = opening; - node.children = parseJsxChildren(node.openingElement.tagName); + node.children = parseJsxChildren(node.openingElement); node.closingElement = parseJsxClosingElement(inExpressionContext); if (!tagNamesAreEquivalent(node.openingElement.tagName, node.closingElement.tagName)) { @@ -3975,6 +3984,14 @@ namespace ts { result = finishNode(node); } + else if (opening.kind === SyntaxKind.JsxOpeningFragment) { + const node = createNode(SyntaxKind.JsxFragment, opening.pos); + node.openingFragment = opening; + node.children = parseJsxChildren(node.openingFragment); + node.closingFragment = parseJsxClosingFragment(inExpressionContext); + + result = finishNode(node); + } else { Debug.assert(opening.kind === SyntaxKind.JsxSelfClosingElement); // Nothing else to do for self-closing elements @@ -3989,7 +4006,7 @@ namespace ts { // Since JSX elements are invalid < operands anyway, this lookahead parse will only occur in error scenarios // of one sort or another. if (inExpressionContext && token() === SyntaxKind.LessThanToken) { - const invalidElement = tryParse(() => parseJsxElementOrSelfClosingElement(/*inExpressionContext*/ true)); + const invalidElement = tryParse(() => parseJsxElementOrSelfClosingElementOrFragment(/*inExpressionContext*/ true)); if (invalidElement) { parseErrorAtCurrentToken(Diagnostics.JSX_expressions_must_have_one_parent_element); const badNode = createNode(SyntaxKind.BinaryExpression, result.pos); @@ -4020,12 +4037,12 @@ namespace ts { case SyntaxKind.OpenBraceToken: return parseJsxExpression(/*inExpressionContext*/ false); case SyntaxKind.LessThanToken: - return parseJsxElementOrSelfClosingElement(/*inExpressionContext*/ false); + return parseJsxElementOrSelfClosingElementOrFragment(/*inExpressionContext*/ false); } Debug.fail("Unknown JSX child kind " + token()); } - function parseJsxChildren(openingTagName: LeftHandSideExpression): NodeArray { + function parseJsxChildren(openingTag: JsxOpeningElement | JsxOpeningFragment): NodeArray { const list = []; const listPos = getNodePos(); const saveParsingContext = parsingContext; @@ -4040,7 +4057,13 @@ namespace ts { else if (token() === SyntaxKind.EndOfFileToken) { // If we hit EOF, issue the error at the tag that lacks the closing element // rather than at the end of the file (which is useless) - parseErrorAtPosition(openingTagName.pos, openingTagName.end - openingTagName.pos, Diagnostics.JSX_element_0_has_no_corresponding_closing_tag, getTextOfNodeFromSourceText(sourceText, openingTagName)); + if (isJsxOpeningFragment(openingTag)) { + parseErrorAtPosition(openingTag.pos, openingTag.end - openingTag.pos, Diagnostics.JSX_fragment_has_no_corresponding_closing_tag); + } + else { + const openingTagName = openingTag.tagName; + parseErrorAtPosition(openingTagName.pos, openingTagName.end - openingTagName.pos, Diagnostics.JSX_element_0_has_no_corresponding_closing_tag, getTextOfNodeFromSourceText(sourceText, openingTagName)); + } break; } else if (token() === SyntaxKind.ConflictMarkerTrivia) { @@ -4063,11 +4086,17 @@ namespace ts { return finishNode(jsxAttributes); } - function parseJsxOpeningOrSelfClosingElement(inExpressionContext: boolean): JsxOpeningElement | JsxSelfClosingElement { + function parseJsxOpeningOrSelfClosingElementOrOpeningFragment(inExpressionContext: boolean): JsxOpeningElement | JsxSelfClosingElement | JsxOpeningFragment { const fullStart = scanner.getStartPos(); parseExpected(SyntaxKind.LessThanToken); + if (token() === SyntaxKind.GreaterThanToken) { + parseExpected(SyntaxKind.GreaterThanToken); + const node: JsxOpeningFragment = createNode(SyntaxKind.JsxOpeningFragment, fullStart); + return finishNode(node); + } + const tagName = parseJsxElementName(); const attributes = parseJsxAttributes(); @@ -4179,6 +4208,23 @@ namespace ts { return finishNode(node); } + function parseJsxClosingFragment(inExpressionContext: boolean): JsxClosingFragment { + const node = createNode(SyntaxKind.JsxClosingFragment); + parseExpected(SyntaxKind.LessThanSlashToken); + if (tokenIsIdentifierOrKeyword(token())) { + const unexpectedTagName = parseJsxElementName(); + parseErrorAtPosition(unexpectedTagName.pos, unexpectedTagName.end - unexpectedTagName.pos, Diagnostics.Expected_corresponding_closing_tag_for_JSX_fragment); + } + if (inExpressionContext) { + parseExpected(SyntaxKind.GreaterThanToken); + } + else { + parseExpected(SyntaxKind.GreaterThanToken, /*diagnostic*/ undefined, /*shouldAdvance*/ false); + scanJsxText(); + } + return finishNode(node); + } + function parseTypeAssertion(): TypeAssertion { const node = createNode(SyntaxKind.TypeAssertionExpression); parseExpected(SyntaxKind.LessThanToken); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 0ff78a77ecd..e00e803b489 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2131,7 +2131,7 @@ namespace ts { createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib"); } - if (options.noImplicitUseStrict && (options.alwaysStrict === undefined ? options.strict : options.alwaysStrict)) { + if (options.noImplicitUseStrict && getStrictOptionValue(options, "alwaysStrict")) { createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict"); } @@ -2360,7 +2360,7 @@ namespace ts { return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set; } function needAllowJs() { - return options.allowJs || !options.noImplicitAny ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type; + return options.allowJs || !getStrictOptionValue(options, "noImplicitAny") ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type; } } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 6dab127ca33..521a1983254 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -11,6 +11,11 @@ namespace ts { return token >= SyntaxKind.Identifier; } + /* @internal */ + export function tokenIsIdentifierOrKeywordOrGreaterThan(token: SyntaxKind): boolean { + return token === SyntaxKind.GreaterThanToken || tokenIsIdentifierOrKeyword(token); + } + export interface Scanner { getStartPos(): number; getToken(): SyntaxKind; diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index bbe05afe878..2be6cdef0bc 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -41,6 +41,9 @@ namespace ts { case SyntaxKind.JsxSelfClosingElement: return visitJsxSelfClosingElement(node, /*isChild*/ false); + case SyntaxKind.JsxFragment: + return visitJsxFragment(node, /*isChild*/ false); + case SyntaxKind.JsxExpression: return visitJsxExpression(node); @@ -63,6 +66,9 @@ namespace ts { case SyntaxKind.JsxSelfClosingElement: return visitJsxSelfClosingElement(node, /*isChild*/ true); + case SyntaxKind.JsxFragment: + return visitJsxFragment(node, /*isChild*/ true); + default: Debug.failBadSyntaxKind(node); return undefined; @@ -77,6 +83,10 @@ namespace ts { return visitJsxOpeningLikeElement(node, /*children*/ undefined, isChild, /*location*/ node); } + function visitJsxFragment(node: JsxFragment, isChild: boolean) { + return visitJsxOpeningFragment(node.openingFragment, node.children, isChild, /*location*/ node); + } + function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: ReadonlyArray, isChild: boolean, location: TextRange) { const tagName = getTagName(node); let objectProperties: Expression; @@ -126,6 +136,22 @@ namespace ts { return element; } + function visitJsxOpeningFragment(node: JsxOpeningFragment, children: ReadonlyArray, isChild: boolean, location: TextRange) { + const element = createExpressionForJsxFragment( + context.getEmitResolver().getJsxFactoryEntity(), + compilerOptions.reactNamespace, + mapDefined(children, transformJsxChildToExpression), + node, + location + ); + + if (isChild) { + startOnNewLine(element); + } + + return element; + } + function transformJsxSpreadAttributeToExpression(node: JsxSpreadAttribute) { return visitNode(node.expression, visitor, isExpression); } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index bd360fdffe4..f60e50ea365 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -91,7 +91,7 @@ namespace ts { startLexicalEnvironment(); const statements: Statement[] = []; - const ensureUseStrict = compilerOptions.alwaysStrict || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile)); + const ensureUseStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile)); const statementOffset = addPrologue(statements, node.statements, ensureUseStrict, sourceElementVisitor); if (shouldEmitUnderscoreUnderscoreESModule()) { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index d674546efb9..a481f0c1d40 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -225,7 +225,7 @@ namespace ts { startLexicalEnvironment(); // Add any prologue directives. - const ensureUseStrict = compilerOptions.alwaysStrict || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile)); + const ensureUseStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile)); const statementOffset = addPrologue(statements, node.statements, ensureUseStrict, sourceElementVisitor); // var __moduleName = context_1 && context_1.id; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index ba4b5fcdf52..ffaecd77d71 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -45,7 +45,7 @@ namespace ts { const resolver = context.getEmitResolver(); const compilerOptions = context.getCompilerOptions(); - const strictNullChecks = typeof compilerOptions.strictNullChecks === "undefined" ? compilerOptions.strict : compilerOptions.strictNullChecks; + const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks"); const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); @@ -521,7 +521,7 @@ namespace ts { } function visitSourceFile(node: SourceFile) { - const alwaysStrict = (compilerOptions.alwaysStrict === undefined ? compilerOptions.strict : compilerOptions.alwaysStrict) && + const alwaysStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") && !(isExternalModule(node) && moduleKind >= ModuleKind.ES2015); return updateSourceFileNode( node, diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 680daeeb485..827dfa152c7 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -43,20 +43,12 @@ namespace ts { return s; } - function isJSONSupported() { - return typeof JSON === "object" && typeof JSON.parse === "function"; - } - export function executeCommandLine(args: string[]): void { const commandLine = parseCommandLine(args); // Configuration file name (if any) let configFileName: string; if (commandLine.options.locale) { - if (!isJSONSupported()) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--locale")); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } validateLocaleAndSetLanguage(commandLine.options.locale, sys, commandLine.errors); } @@ -84,10 +76,6 @@ namespace ts { } if (commandLine.options.project) { - if (!isJSONSupported()) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--project")); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } if (commandLine.fileNames.length !== 0) { reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line)); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); @@ -109,7 +97,7 @@ namespace ts { } } } - else if (commandLine.fileNames.length === 0 && isJSONSupported()) { + else if (commandLine.fileNames.length === 0) { const searchPath = normalizePath(sys.getCurrentDirectory()); configFileName = findConfigFile(searchPath, sys.fileExists); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e1830274c93..549c0f8d52f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -328,6 +328,9 @@ namespace ts { JsxSelfClosingElement, JsxOpeningElement, JsxClosingElement, + JsxFragment, + JsxOpeningFragment, + JsxClosingFragment, JsxAttribute, JsxAttributes, JsxSpreadAttribute, @@ -362,6 +365,7 @@ namespace ts { JSDocFunctionType, JSDocVariadicType, JSDocComment, + JSDocTypeLiteral, JSDocTag, JSDocAugmentsTag, JSDocClassTag, @@ -371,7 +375,6 @@ namespace ts { JSDocTemplateTag, JSDocTypedefTag, JSDocPropertyTag, - JSDocTypeLiteral, // Synthesized list SyntaxList, @@ -413,9 +416,9 @@ namespace ts { LastBinaryOperator = CaretEqualsToken, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocTypeLiteral, + LastJSDocNode = JSDocPropertyTag, FirstJSDocTagNode = JSDocTag, - LastJSDocTagNode = JSDocTypeLiteral + LastJSDocTagNode = JSDocPropertyTag } export const enum NodeFlags { @@ -1620,7 +1623,7 @@ namespace ts { closingElement: JsxClosingElement; } - /// Either the opening tag in a ... pair, or the lone in a self-closing form + /// Either the opening tag in a ... pair or the lone in a self-closing form export type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement; export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute; @@ -1646,6 +1649,26 @@ namespace ts { attributes: JsxAttributes; } + /// A JSX expression of the form <>... + export interface JsxFragment extends PrimaryExpression { + kind: SyntaxKind.JsxFragment; + openingFragment: JsxOpeningFragment; + children: NodeArray; + closingFragment: JsxClosingFragment; + } + + /// The opening element of a <>... JsxFragment + export interface JsxOpeningFragment extends Expression { + kind: SyntaxKind.JsxOpeningFragment; + parent?: JsxFragment; + } + + /// The closing element of a <>... JsxFragment + export interface JsxClosingFragment extends Expression { + kind: SyntaxKind.JsxClosingFragment; + parent?: JsxFragment; + } + export interface JsxAttribute extends ObjectLiteralElement { kind: SyntaxKind.JsxAttribute; parent?: JsxAttributes; @@ -1679,7 +1702,7 @@ namespace ts { parent?: JsxElement; } - export type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement; + export type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment; export interface Statement extends Node { _statementBrand: any; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 01bb36e9065..080172b8106 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1132,7 +1132,7 @@ namespace ts { } /** - * Determines whether a node is a property or element access expression for super. + * Determines whether a node is a property or element access expression for `super`. */ export function isSuperProperty(node: Node): node is SuperProperty { const kind = node.kind; @@ -1140,7 +1140,16 @@ namespace ts { && (node).expression.kind === SyntaxKind.SuperKeyword; } - export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression { + /** + * Determines whether a node is a property or element access expression for `this`. + */ + export function isThisProperty(node: Node): boolean { + const kind = node.kind; + return (kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression) + && (node).expression.kind === SyntaxKind.ThisKeyword; + } + + export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression { switch (node.kind) { case SyntaxKind.TypeReference: return (node).typeName; @@ -1228,7 +1237,7 @@ namespace ts { return false; } - export function isPartOfExpression(node: Node): boolean { + export function isExpressionNode(node: Node): boolean { switch (node.kind) { case SyntaxKind.SuperKeyword: case SyntaxKind.NullKeyword: @@ -1262,6 +1271,7 @@ namespace ts { case SyntaxKind.OmittedExpression: case SyntaxKind.JsxElement: case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxFragment: case SyntaxKind.YieldExpression: case SyntaxKind.AwaitExpression: case SyntaxKind.MetaProperty: @@ -1331,7 +1341,7 @@ namespace ts { case SyntaxKind.ExpressionWithTypeArguments: return (parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(parent); default: - return isPartOfExpression(parent); + return isExpressionNode(parent); } } @@ -2170,6 +2180,7 @@ namespace ts { case SyntaxKind.ClassExpression: case SyntaxKind.JsxElement: case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxFragment: case SyntaxKind.RegularExpressionLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.TemplateExpression: @@ -4794,6 +4805,18 @@ namespace ts { return node.kind === SyntaxKind.JsxClosingElement; } + export function isJsxFragment(node: Node): node is JsxFragment { + return node.kind === SyntaxKind.JsxFragment; + } + + export function isJsxOpeningFragment(node: Node): node is JsxOpeningFragment { + return node.kind === SyntaxKind.JsxOpeningFragment; + } + + export function isJsxClosingFragment(node: Node): node is JsxClosingFragment { + return node.kind === SyntaxKind.JsxClosingFragment; + } + export function isJsxAttribute(node: Node): node is JsxAttribute { return node.kind === SyntaxKind.JsxAttribute; } @@ -5319,6 +5342,7 @@ namespace ts { case SyntaxKind.CallExpression: case SyntaxKind.JsxElement: case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxFragment: case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.ParenthesizedExpression: @@ -5381,7 +5405,7 @@ namespace ts { /* @internal */ /** * Determines whether a node is an expression based only on its kind. - * Use `isPartOfExpression` if not in transforms. + * Use `isExpressionNode` if not in transforms. */ export function isExpression(node: Node): node is Expression { return isExpressionKind(skipPartiallyEmittedExpressions(node).kind); @@ -5640,7 +5664,8 @@ namespace ts { return kind === SyntaxKind.JsxElement || kind === SyntaxKind.JsxExpression || kind === SyntaxKind.JsxSelfClosingElement - || kind === SyntaxKind.JsxText; + || kind === SyntaxKind.JsxText + || kind === SyntaxKind.JsxFragment; } /* @internal */ diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 7d46630e227..0428d2d2d2e 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -819,6 +819,12 @@ namespace ts { return updateJsxClosingElement(node, visitNode((node).tagName, visitor, isJsxTagNameExpression)); + case SyntaxKind.JsxFragment: + return updateJsxFragment(node, + visitNode((node).openingFragment, visitor, isJsxOpeningFragment), + nodesVisitor((node).children, visitor, isJsxChild), + visitNode((node).closingFragment, visitor, isJsxClosingFragment)); + case SyntaxKind.JsxAttribute: return updateJsxAttribute(node, visitNode((node).name, visitor, isIdentifier), @@ -1334,6 +1340,12 @@ namespace ts { result = reduceNode((node).closingElement, cbNode, result); break; + case SyntaxKind.JsxFragment: + result = reduceNode((node).openingFragment, cbNode, result); + result = reduceLeft((node).children, cbNode, result); + result = reduceNode((node).closingFragment, cbNode, result); + break; + case SyntaxKind.JsxSelfClosingElement: case SyntaxKind.JsxOpeningElement: result = reduceNode((node).tagName, cbNode, result); diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 7ba4e94902d..652737f9b57 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -314,7 +314,7 @@ namespace FourSlash { const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions); this.languageServiceAdapterHost = languageServiceAdapter.getHost(); - this.languageService = languageServiceAdapter.getLanguageService(); + this.languageService = memoWrap(languageServiceAdapter.getLanguageService(), this); // Wrap the LS to cache some expensive operations certain tests call repeatedly if (startResolveFileRef) { // Add the entry-point file itself into the languageServiceShimHost @@ -381,6 +381,39 @@ namespace FourSlash { // Open the first file by default this.openFile(0); + + function memoWrap(ls: ts.LanguageService, target: TestState): ts.LanguageService { + const cacheableMembers: (keyof typeof ls)[] = [ + "getCompletionsAtPosition", + "getCompletionEntryDetails", + "getCompletionEntrySymbol", + "getQuickInfoAtPosition", + "getSignatureHelpItems", + "getReferencesAtPosition", + "getDocumentHighlights", + ]; + const proxy = {} as ts.LanguageService; + for (const k in ls) { + const key = k as keyof typeof ls; + if (cacheableMembers.indexOf(key) === -1) { + proxy[key] = (...args: any[]) => (ls[key] as Function)(...args); + continue; + } + const memo = Utils.memoize( + (_version: number, _active: string, _caret: number, _selectEnd: number, _marker: string, ...args: any[]) => (ls[key] as Function)(...args), + (...args) => args.join("|,|") + ); + proxy[key] = (...args: any[]) => memo( + target.languageServiceAdapterHost.getScriptInfo(target.activeFile.fileName).version, + target.activeFile.fileName, + target.currentCaretPosition, + target.selectionEnd, + target.lastKnownMarker, + ...args + ); + } + return proxy; + } } private getFileContent(fileName: string): string { @@ -583,19 +616,23 @@ namespace FourSlash { } public verifyGoToDefinition(arg0: any, endMarkerNames?: string | string[]) { - this.verifyGoToX(arg0, endMarkerNames, () => this.getGoToDefinition()); + this.verifyGoToX(arg0, endMarkerNames, () => this.getGoToDefinitionAndBoundSpan()); } private getGoToDefinition(): ts.DefinitionInfo[] { return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition); } + private getGoToDefinitionAndBoundSpan(): ts.DefinitionInfoAndBoundSpan { + return this.languageService.getDefinitionAndBoundSpan(this.activeFile.fileName, this.currentCaretPosition); + } + public verifyGoToType(arg0: any, endMarkerNames?: string | string[]) { this.verifyGoToX(arg0, endMarkerNames, () => this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition)); } - private verifyGoToX(arg0: any, endMarkerNames: string | string[] | undefined, getDefs: () => ts.DefinitionInfo[] | undefined) { + private verifyGoToX(arg0: any, endMarkerNames: string | string[] | undefined, getDefs: () => ts.DefinitionInfo[] | ts.DefinitionInfoAndBoundSpan | undefined) { if (endMarkerNames) { this.verifyGoToXPlain(arg0, endMarkerNames, getDefs); } @@ -615,7 +652,7 @@ namespace FourSlash { } } - private verifyGoToXPlain(startMarkerNames: string | string[], endMarkerNames: string | string[], getDefs: () => ts.DefinitionInfo[] | undefined) { + private verifyGoToXPlain(startMarkerNames: string | string[], endMarkerNames: string | string[], getDefs: () => ts.DefinitionInfo[] | ts.DefinitionInfoAndBoundSpan | undefined) { for (const start of toArray(startMarkerNames)) { this.verifyGoToXSingle(start, endMarkerNames, getDefs); } @@ -627,26 +664,57 @@ namespace FourSlash { } } - private verifyGoToXSingle(startMarkerName: string, endMarkerNames: string | string[], getDefs: () => ts.DefinitionInfo[] | undefined) { + private verifyGoToXSingle(startMarkerName: string, endMarkerNames: string | string[], getDefs: () => ts.DefinitionInfo[] | ts.DefinitionInfoAndBoundSpan | undefined) { this.goToMarker(startMarkerName); - this.verifyGoToXWorker(toArray(endMarkerNames), getDefs); + this.verifyGoToXWorker(toArray(endMarkerNames), getDefs, startMarkerName); } - private verifyGoToXWorker(endMarkers: string[], getDefs: () => ts.DefinitionInfo[] | undefined) { - const definitions = getDefs() || []; + private verifyGoToXWorker(endMarkers: string[], getDefs: () => ts.DefinitionInfo[] | ts.DefinitionInfoAndBoundSpan | undefined, startMarkerName?: string) { + const defs = getDefs(); + let definitions: ts.DefinitionInfo[] | ReadonlyArray; + let testName: string; + + if (!defs || Array.isArray(defs)) { + definitions = defs as ts.DefinitionInfo[] || []; + testName = "goToDefinitions"; + } + else { + this.verifyDefinitionTextSpan(defs, startMarkerName); + + definitions = defs.definitions; + testName = "goToDefinitionsAndBoundSpan"; + } if (endMarkers.length !== definitions.length) { - this.raiseError(`goToDefinitions failed - expected to find ${endMarkers.length} definitions but got ${definitions.length}`); + this.raiseError(`${testName} failed - expected to find ${endMarkers.length} definitions but got ${definitions.length}`); } ts.zipWith(endMarkers, definitions, (endMarker, definition, i) => { const marker = this.getMarkerByName(endMarker); if (marker.fileName !== definition.fileName || marker.position !== definition.textSpan.start) { - this.raiseError(`goToDefinition failed for definition ${endMarker} (${i}): expected ${marker.fileName} at ${marker.position}, got ${definition.fileName} at ${definition.textSpan.start}`); + this.raiseError(`${testName} failed for definition ${endMarker} (${i}): expected ${marker.fileName} at ${marker.position}, got ${definition.fileName} at ${definition.textSpan.start}`); } }); } + private verifyDefinitionTextSpan(defs: ts.DefinitionInfoAndBoundSpan, startMarkerName: string) { + const range = this.testData.ranges.find(range => this.markerName(range.marker) === startMarkerName); + + if (!range && !defs.textSpan) { + return; + } + + if (!range) { + this.raiseError(`goToDefinitionsAndBoundSpan failed - found a TextSpan ${JSON.stringify(defs.textSpan)} when it wasn't expected.`); + } + else if (defs.textSpan.start !== range.start || defs.textSpan.length !== range.end - range.start) { + const expected: ts.TextSpan = { + start: range.start, length: range.end - range.start + }; + this.raiseError(`goToDefinitionsAndBoundSpan failed - expected to find TextSpan ${JSON.stringify(expected)} but got ${JSON.stringify(defs.textSpan)}`); + } + } + public verifyGetEmitOutputForCurrentFile(expected: string): void { const emit = this.languageService.getEmitOutput(this.activeFile.fileName); if (emit.outputFiles.length !== 1) { @@ -3900,6 +3968,7 @@ namespace FourSlashInterface { } public goToDefinition(startMarkerName: string | string[], endMarkerName: string | string[]): void; + public goToDefinition(startMarkerName: string | string[], endMarkerName: string | string[], range: FourSlash.Range): void; public goToDefinition(startsAndEnds: [string | string[], string | string[]][]): void; public goToDefinition(startsAndEnds: { [startMarkerName: string]: string | string[] }): void; public goToDefinition(arg0: any, endMarkerName?: string | string[]) { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 64ef1b552f5..55d43758734 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -416,8 +416,8 @@ namespace Harness.LanguageService { getCompletionsAtPosition(fileName: string, position: number): ts.CompletionInfo { return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position)); } - getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: ts.FormatCodeOptions): ts.CompletionEntryDetails { - return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(options))); + getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: ts.FormatCodeOptions | undefined, source: string | undefined): ts.CompletionEntryDetails { + return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(options), source)); } getCompletionEntrySymbol(): ts.Symbol { throw new Error("getCompletionEntrySymbol not implemented across the shim layer."); @@ -443,6 +443,9 @@ namespace Harness.LanguageService { getDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] { return unwrapJSONCallResult(this.shim.getDefinitionAtPosition(fileName, position)); } + getDefinitionAndBoundSpan(fileName: string, position: number): ts.DefinitionInfoAndBoundSpan { + return unwrapJSONCallResult(this.shim.getDefinitionAndBoundSpan(fileName, position)); + } getTypeDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] { return unwrapJSONCallResult(this.shim.getTypeDefinitionAtPosition(fileName, position)); } diff --git a/src/harness/loggedIO.ts b/src/harness/loggedIO.ts index fcc163e77f3..55be4068e83 100644 --- a/src/harness/loggedIO.ts +++ b/src/harness/loggedIO.ts @@ -249,13 +249,33 @@ namespace Playback { wrapper.endRecord = () => { if (recordLog !== undefined) { let i = 0; - const fn = () => recordLogFileNameBase + i; - while (underlying.fileExists(ts.combinePaths(fn(), "test.json"))) i++; - underlying.writeFile(ts.combinePaths(fn(), "test.json"), JSON.stringify(oldStyleLogIntoNewStyleLog(recordLog, (path, string) => underlying.writeFile(path, string), fn()), null, 4)); // tslint:disable-line:no-null-keyword + const getBase = () => recordLogFileNameBase + i; + while (underlying.fileExists(ts.combinePaths(getBase(), "test.json"))) i++; + const newLog = oldStyleLogIntoNewStyleLog(recordLog, (path, string) => underlying.writeFile(path, string), getBase()); + underlying.writeFile(ts.combinePaths(getBase(), "test.json"), JSON.stringify(newLog, null, 4)); // tslint:disable-line:no-null-keyword + const syntheticTsconfig = generateTsconfig(newLog); + if (syntheticTsconfig) { + underlying.writeFile(ts.combinePaths(getBase(), "tsconfig.json"), JSON.stringify(syntheticTsconfig, null, 4)); // tslint:disable-line:no-null-keyword + } recordLog = undefined; } }; + function generateTsconfig(newLog: IOLog): undefined | { compilerOptions: ts.CompilerOptions, files: string[] } { + if (newLog.filesRead.some(file => /tsconfig.+json$/.test(file.path))) { + return; + } + const files = []; + for (const file of newLog.filesRead) { + if (file.result.contentsPath && + Harness.isDefaultLibraryFile(file.result.contentsPath) && + /\.[tj]s$/.test(file.result.contentsPath)) { + files.push(file.result.contentsPath); + } + } + return { compilerOptions: ts.parseCommandLine(newLog.arguments).options, files }; + } + wrapper.fileExists = recordReplay(wrapper.fileExists, underlying)( path => callAndRecord(underlying.fileExists(path), recordLog.fileExists, { path }), memoize(path => { diff --git a/src/harness/typeWriter.ts b/src/harness/typeWriter.ts index af626f1e548..17eda776bda 100644 --- a/src/harness/typeWriter.ts +++ b/src/harness/typeWriter.ts @@ -52,7 +52,7 @@ class TypeWriterWalker { } private *visitNode(node: ts.Node, isSymbolWalk: boolean): IterableIterator { - if (ts.isPartOfExpression(node) || node.kind === ts.SyntaxKind.Identifier) { + if (ts.isExpressionNode(node) || node.kind === ts.SyntaxKind.Identifier) { const result = this.writeTypeOrSymbol(node, isSymbolWalk); if (result) { yield result; diff --git a/src/harness/unittests/convertCompilerOptionsFromJson.ts b/src/harness/unittests/convertCompilerOptionsFromJson.ts index c55dee26dc4..09659762288 100644 --- a/src/harness/unittests/convertCompilerOptionsFromJson.ts +++ b/src/harness/unittests/convertCompilerOptionsFromJson.ts @@ -421,6 +421,24 @@ namespace ts { ); }); + it("Convert negative numbers in tsconfig.json ", () => { + assertCompilerOptions( + { + "compilerOptions": { + "allowJs": true, + "maxNodeModuleJsDepth": -1 + } + }, "tsconfig.json", + { + compilerOptions: { + allowJs: true, + maxNodeModuleJsDepth: -1 + }, + errors: [] + } + ); + }); + // jsconfig.json it("Convert correctly format jsconfig.json to compiler-options ", () => { assertCompilerOptions( diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 3d9f782b2e0..e40e7d11b1a 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -128,7 +128,7 @@ namespace ts.server { body: undefined }); }); - it ("should handle literal types in request", () => { + it("should handle literal types in request", () => { const configureRequest: protocol.ConfigureRequest = { command: CommandNames.Configure, seq: 0, @@ -186,6 +186,8 @@ namespace ts.server { CommandNames.Configure, CommandNames.Definition, CommandNames.DefinitionFull, + CommandNames.DefinitionAndBoundSpan, + CommandNames.DefinitionAndBoundSpanFull, CommandNames.Implementation, CommandNames.ImplementationFull, CommandNames.Exit, @@ -352,7 +354,7 @@ namespace ts.server { session.addProtocolHandler(command, () => resp); expect(() => session.addProtocolHandler(command, () => resp)) - .to.throw(`Protocol handler already exists for command "${command}"`); + .to.throw(`Protocol handler already exists for command "${command}"`); }); }); diff --git a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 06f8e9220bc..d96fb42d4ea 100644 --- a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -839,12 +839,12 @@ - + - + - + @@ -2907,6 +2907,15 @@ + + + + + + + + + @@ -2934,6 +2943,15 @@ + + + + + + + + + @@ -3689,12 +3707,12 @@ - + - + - + @@ -6375,11 +6393,11 @@ - + - + - + diff --git a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl index a30e3d898dc..34f080a97cc 100644 --- a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -823,12 +823,12 @@ - + - + - + @@ -2891,6 +2891,15 @@ + + + + + + + + + @@ -2918,6 +2927,15 @@ + + + + + + + + + @@ -3673,12 +3691,12 @@ - + - + - + @@ -6359,11 +6377,11 @@ - + - + - + diff --git a/src/server/client.ts b/src/server/client.ts index 2781d7cf20a..bf46301ff86 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -270,6 +270,25 @@ namespace ts.server { })); } + getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan { + const args: protocol.FileLocationRequestArgs = this.createFileLocationRequestArgs(fileName, position); + + const request = this.processRequest(CommandNames.DefinitionAndBoundSpan, args); + const response = this.processResponse(request); + + return { + definitions: response.body.definitions.map(entry => ({ + containerKind: ScriptElementKind.unknown, + containerName: "", + fileName: entry.file, + textSpan: this.decodeSpan(entry), + kind: ScriptElementKind.unknown, + name: "" + })), + textSpan: this.decodeSpan(response.body.textSpan, request.arguments.file) + }; + } + getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] { const args: protocol.FileLocationRequestArgs = this.createFileLocationRequestArgs(fileName, position); @@ -324,7 +343,7 @@ namespace ts.server { } getSyntacticDiagnostics(file: string): Diagnostic[] { - const args: protocol.SyntacticDiagnosticsSyncRequestArgs = { file, includeLinePosition: true }; + const args: protocol.SyntacticDiagnosticsSyncRequestArgs = { file, includeLinePosition: true }; const request = this.processRequest(CommandNames.SyntacticDiagnosticsSync, args); const response = this.processResponse(request); diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 6092f235942..9a94c265f5d 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -21,6 +21,9 @@ namespace ts.server.protocol { Definition = "definition", /* @internal */ DefinitionFull = "definition-full", + DefinitionAndBoundSpan = "definitionAndBoundSpan", + /* @internal */ + DefinitionAndBoundSpanFull = "definitionAndBoundSpan-full", Implementation = "implementation", /* @internal */ ImplementationFull = "implementation-full", @@ -708,6 +711,11 @@ namespace ts.server.protocol { file: string; } + export interface DefinitionInfoAndBoundSpan { + definitions: ReadonlyArray; + textSpan: TextSpan; + } + /** * Definition response message. Gives text range for definition. */ @@ -715,6 +723,10 @@ namespace ts.server.protocol { body?: FileSpan[]; } + export interface DefinitionInfoAndBoundSpanReponse extends Response { + body?: DefinitionInfoAndBoundSpan; + } + /** * Definition response message. Gives text range for definition. */ diff --git a/src/server/session.ts b/src/server/session.ts index 7ada8d2ff9c..f02e1c620b9 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -167,7 +167,7 @@ namespace ts.server { private timerHandle: any; private immediateId: number | undefined; - constructor(private readonly operationHost: MultistepOperationHost) {} + constructor(private readonly operationHost: MultistepOperationHost) { } public startNew(action: (next: NextStep) => void) { this.complete(); @@ -587,7 +587,7 @@ namespace ts.server { private getDiagnosticsWorker( args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => ReadonlyArray, includeLinePosition: boolean - ): ReadonlyArray | ReadonlyArray { + ): ReadonlyArray | ReadonlyArray { const { project, file } = this.getFileAndProject(args); if (isSemantic && isDeclarationFileInJSOnlyNonConfiguredProject(project, file)) { return emptyArray; @@ -609,20 +609,51 @@ namespace ts.server { } if (simplifiedResult) { - return definitions.map(def => { - const defScriptInfo = project.getScriptInfo(def.fileName); - return { - file: def.fileName, - start: defScriptInfo.positionToLineOffset(def.textSpan.start), - end: defScriptInfo.positionToLineOffset(textSpanEnd(def.textSpan)) - }; - }); + return this.mapDefinitionInfo(definitions, project); } else { return definitions; } } + private getDefinitionAndBoundSpan(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.DefinitionInfoAndBoundSpan | DefinitionInfoAndBoundSpan { + const { file, project } = this.getFileAndProject(args); + const position = this.getPositionInFile(args, file); + const scriptInfo = project.getScriptInfo(file); + + const definitionAndBoundSpan = project.getLanguageService().getDefinitionAndBoundSpan(file, position); + + if (!definitionAndBoundSpan || !definitionAndBoundSpan.definitions) { + return { + definitions: emptyArray, + textSpan: undefined + }; + } + + if (simplifiedResult) { + return { + definitions: this.mapDefinitionInfo(definitionAndBoundSpan.definitions, project), + textSpan: this.toLocationTextSpan(definitionAndBoundSpan.textSpan, scriptInfo) + }; + } + + return definitionAndBoundSpan; + } + + private mapDefinitionInfo(definitions: ReadonlyArray, project: Project): ReadonlyArray { + return definitions.map(def => this.toFileSpan(def.fileName, def.textSpan, project)); + } + + private toFileSpan(fileName: string, textSpan: TextSpan, project: Project): protocol.FileSpan { + const scriptInfo = project.getScriptInfo(fileName); + + return { + file: fileName, + start: scriptInfo.positionToLineOffset(textSpan.start), + end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan)) + }; + } + private getTypeDefinition(args: protocol.FileLocationRequestArgs): ReadonlyArray { const { file, project } = this.getFileAndProject(args); const position = this.getPositionInFile(args, file); @@ -632,14 +663,7 @@ namespace ts.server { return emptyArray; } - return definitions.map(def => { - const defScriptInfo = project.getScriptInfo(def.fileName); - return { - file: def.fileName, - start: defScriptInfo.positionToLineOffset(def.textSpan.start), - end: defScriptInfo.positionToLineOffset(textSpanEnd(def.textSpan)) - }; - }); + return this.mapDefinitionInfo(definitions, project); } private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { @@ -650,14 +674,7 @@ namespace ts.server { return emptyArray; } if (simplifiedResult) { - return implementations.map(({ fileName, textSpan }) => { - const scriptInfo = project.getScriptInfo(fileName); - return { - file: fileName, - start: scriptInfo.positionToLineOffset(textSpan.start), - end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan)) - }; - }); + return implementations.map(({ fileName, textSpan }) => this.toFileSpan(fileName, textSpan, project)); } else { return implementations; @@ -666,6 +683,7 @@ namespace ts.server { private getOccurrences(args: protocol.FileLocationRequestArgs): ReadonlyArray { const { file, project } = this.getFileAndProject(args); + const position = this.getPositionInFile(args, file); const occurrences = project.getLanguageService().getOccurrencesAtPosition(file, position); @@ -677,11 +695,9 @@ namespace ts.server { return occurrences.map(occurrence => { const { fileName, isWriteAccess, textSpan, isInString } = occurrence; const scriptInfo = project.getScriptInfo(fileName); - const start = scriptInfo.positionToLineOffset(textSpan.start); - const end = scriptInfo.positionToLineOffset(textSpanEnd(textSpan)); const result: protocol.OccurrencesResponseItem = { - start, - end, + start: scriptInfo.positionToLineOffset(textSpan.start), + end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan)), file: fileName, isWriteAccess, }; @@ -1189,7 +1205,7 @@ namespace ts.server { return mapDefined(completions && completions.entries, entry => { if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) { const { name, kind, kindModifiers, sortText, replacementSpan, hasAction, source } = entry; - const convertedSpan = replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined; + const convertedSpan = replacementSpan ? this.toLocationTextSpan(replacementSpan, scriptInfo) : undefined; // Use `hasAction || undefined` to avoid serializing `false`. return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan, hasAction: hasAction || undefined, source }; } @@ -1337,13 +1353,13 @@ namespace ts.server { this.projectService.closeClientFile(file); } - private decorateNavigationBarItems(items: NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] { + private mapLocationNavigationBarItems(items: NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] { return map(items, item => ({ text: item.text, kind: item.kind, kindModifiers: item.kindModifiers, - spans: item.spans.map(span => this.decorateSpan(span, scriptInfo)), - childItems: this.decorateNavigationBarItems(item.childItems, scriptInfo), + spans: item.spans.map(span => this.toLocationTextSpan(span, scriptInfo)), + childItems: this.mapLocationNavigationBarItems(item.childItems, scriptInfo), indent: item.indent })); } @@ -1354,21 +1370,21 @@ namespace ts.server { return !items ? undefined : simplifiedResult - ? this.decorateNavigationBarItems(items, this.projectService.getScriptInfoForNormalizedPath(file)) + ? this.mapLocationNavigationBarItems(items, this.projectService.getScriptInfoForNormalizedPath(file)) : items; } - private decorateNavigationTree(tree: NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree { + private toLocationNavigationTree(tree: NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree { return { text: tree.text, kind: tree.kind, kindModifiers: tree.kindModifiers, - spans: tree.spans.map(span => this.decorateSpan(span, scriptInfo)), - childItems: map(tree.childItems, item => this.decorateNavigationTree(item, scriptInfo)) + spans: tree.spans.map(span => this.toLocationTextSpan(span, scriptInfo)), + childItems: map(tree.childItems, item => this.toLocationNavigationTree(item, scriptInfo)) }; } - private decorateSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan { + private toLocationTextSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan { return { start: scriptInfo.positionToLineOffset(span.start), end: scriptInfo.positionToLineOffset(textSpanEnd(span)) @@ -1381,7 +1397,7 @@ namespace ts.server { return !tree ? undefined : simplifiedResult - ? this.decorateNavigationTree(tree, this.projectService.getScriptInfoForNormalizedPath(file)) + ? this.toLocationNavigationTree(tree, this.projectService.getScriptInfoForNormalizedPath(file)) : tree; } @@ -1400,14 +1416,12 @@ namespace ts.server { return navItems.map((navItem) => { const scriptInfo = project.getScriptInfo(navItem.fileName); - const start = scriptInfo.positionToLineOffset(navItem.textSpan.start); - const end = scriptInfo.positionToLineOffset(textSpanEnd(navItem.textSpan)); const bakedItem: protocol.NavtoItem = { name: navItem.name, kind: navItem.kind, file: navItem.fileName, - start, - end, + start: scriptInfo.positionToLineOffset(navItem.textSpan.start), + end: scriptInfo.positionToLineOffset(textSpanEnd(navItem.textSpan)) }; if (navItem.kindModifiers && (navItem.kindModifiers !== "")) { bakedItem.kindModifiers = navItem.kindModifiers; @@ -1594,7 +1608,7 @@ namespace ts.server { fileName: change.fileName, textChanges: change.textChanges.map(textChange => this.convertTextChangeToCodeEdit(textChange, scriptInfo)) })); - return { description, changes, commands }; + return { description, changes, commands }; } private mapTextChangesToCodeEdits(project: Project, textChanges: FileTextChanges): protocol.FileCodeEdits { @@ -1622,7 +1636,7 @@ namespace ts.server { return !spans ? undefined : simplifiedResult - ? spans.map(span => this.decorateSpan(span, scriptInfo)) + ? spans.map(span => this.toLocationTextSpan(span, scriptInfo)) : spans; } @@ -1738,6 +1752,12 @@ namespace ts.server { [CommandNames.DefinitionFull]: (request: protocol.DefinitionRequest) => { return this.requiredResponse(this.getDefinition(request.arguments, /*simplifiedResult*/ false)); }, + [CommandNames.DefinitionAndBoundSpan]: (request: protocol.DefinitionRequest) => { + return this.requiredResponse(this.getDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ true)); + }, + [CommandNames.DefinitionAndBoundSpanFull]: (request: protocol.DefinitionRequest) => { + return this.requiredResponse(this.getDefinitionAndBoundSpan(request.arguments, /*simplifiedResult*/ false)); + }, [CommandNames.TypeDefinition]: (request: protocol.FileLocationRequest) => { return this.requiredResponse(this.getTypeDefinition(request.arguments)); }, diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index 37aa8df0ca1..73176aa4b5f 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -297,7 +297,7 @@ namespace ts.BreakpointResolver { } } - if (isPartOfExpression(node)) { + if (isExpressionNode(node)) { switch (node.parent.kind) { case SyntaxKind.DoStatement: // Set span as if on while keyword diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index b952cc5a98d..378c73f2f75 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -368,7 +368,7 @@ namespace ts.codefix { } function inferTypeFromContextualType(node: Expression, checker: TypeChecker, usageContext: UsageContext): void { - if (isPartOfExpression(node)) { + if (isExpressionNode(node)) { addCandidateType(usageContext, checker.getContextualType(node)); } } diff --git a/src/services/completions.ts b/src/services/completions.ts index 562cdd5cbb2..f4feb8a1c10 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -241,11 +241,15 @@ namespace ts.Completions { // a['/*completion position*/'] return getStringLiteralCompletionEntriesFromElementAccess(node.parent, typeChecker, compilerOptions.target, log); } - else if (node.parent.kind === SyntaxKind.ImportDeclaration || isExpressionOfExternalModuleImportEqualsDeclaration(node) || isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false)) { + else if (node.parent.kind === SyntaxKind.ImportDeclaration || node.parent.kind === SyntaxKind.ExportDeclaration + || isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false) || isImportCall(node.parent) + || isExpressionOfExternalModuleImportEqualsDeclaration(node)) { // Get all known external module names or complete a path to a module // i.e. import * as ns from "/*completion position*/"; + // var y = import("/*completion position*/"); // import x = require("/*completion position*/"); // var y = require("/*completion position*/"); + // export * from "/*completion position*/"; return PathCompletions.getStringLiteralCompletionEntriesFromModuleNames(node, compilerOptions, host, typeChecker); } else if (isEqualityExpression(node.parent)) { @@ -1839,6 +1843,18 @@ namespace ts.Completions { } } + // If the symbol is for a member of an object type and is the internal name of an ES + // symbol, it is not a valid entry. Internal names for ES symbols start with "__@" + if (symbol.flags & SymbolFlags.ClassMember) { + const escapedName = symbol.escapedName as string; + if (escapedName.length >= 3 && + escapedName.charCodeAt(0) === CharacterCodes._ && + escapedName.charCodeAt(1) === CharacterCodes._ && + escapedName.charCodeAt(2) === CharacterCodes.at) { + return undefined; + } + } + return getCompletionEntryDisplayName(name, target, performCharacterChecks, allowStringLiteral); } diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index d41d9dfbfd8..32d01eb1a16 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -849,7 +849,7 @@ namespace ts.formatting { } static NodeIsInDecoratorContext(node: Node): boolean { - while (isPartOfExpression(node)) { + while (isExpressionNode(node)) { node = node.parent; } return node.kind === SyntaxKind.Decorator; diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index cb2be7d484c..35f4d079e29 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -88,7 +88,7 @@ namespace ts.GoToDefinition { // } // bar(({pr/*goto*/op1})=>{}); if (isPropertyName(node) && isBindingElement(node.parent) && isObjectBindingPattern(node.parent.parent) && - (node === (node.parent.propertyName || node.parent.name))) { + (node === (node.parent.propertyName || node.parent.name))) { const type = typeChecker.getTypeAtLocation(node.parent.parent); if (type) { const propSymbols = getPropertySymbolsFromType(type, node); @@ -149,6 +149,28 @@ namespace ts.GoToDefinition { return getDefinitionFromSymbol(typeChecker, type.symbol, node); } + export function getDefinitionAndBoundSpan(program: Program, sourceFile: SourceFile, position: number): DefinitionInfoAndBoundSpan { + const definitions = getDefinitionAtPosition(program, sourceFile, position); + + if (!definitions || definitions.length === 0) { + return undefined; + } + + // Check if position is on triple slash reference. + const comment = findReferenceInPosition(sourceFile.referencedFiles, position) || findReferenceInPosition(sourceFile.typeReferenceDirectives, position); + if (comment) { + return { + definitions, + textSpan: createTextSpanFromBounds(comment.pos, comment.end) + }; + } + + const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true); + const textSpan = createTextSpan(node.getStart(), node.getWidth()); + + return { definitions, textSpan }; + } + // Go to the original declaration for cases: // // (1) when the aliased symbol was declared in the location(parent). diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 3e9913fc6c7..9c8b085d198 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -65,19 +65,44 @@ namespace ts.JsDoc { return documentationComment; } - export function getJsDocTagsFromDeclarations(declarations?: Declaration[]) { + export function getJsDocTagsFromDeclarations(declarations?: Declaration[]): JSDocTagInfo[] { // Only collect doc comments from duplicate declarations once. const tags: JSDocTagInfo[] = []; forEachUnique(declarations, declaration => { for (const tag of getJSDocTags(declaration)) { - if (tag.kind === SyntaxKind.JSDocTag) { - tags.push({ name: tag.tagName.text, text: tag.comment }); - } + tags.push({ name: tag.tagName.text, text: getCommentText(tag) }); } }); return tags; } + function getCommentText(tag: JSDocTag): string { + const { comment } = tag; + switch (tag.kind) { + case SyntaxKind.JSDocAugmentsTag: + return withNode((tag as JSDocAugmentsTag).class); + case SyntaxKind.JSDocTemplateTag: + return withList((tag as JSDocTemplateTag).typeParameters); + case SyntaxKind.JSDocTypeTag: + return withNode((tag as JSDocTypeTag).typeExpression); + case SyntaxKind.JSDocTypedefTag: + case SyntaxKind.JSDocPropertyTag: + case SyntaxKind.JSDocParameterTag: + const { name } = tag as JSDocTypedefTag | JSDocPropertyTag | JSDocParameterTag; + return name ? withNode(name) : comment; + default: + return comment; + } + + function withNode(node: Node) { + return `${node.getText()} ${comment}`; + } + + function withList(list: NodeArray): string { + return `${list.map(x => x.getText())} ${comment}`; + } + } + /** * Iterates through 'array' by index and performs the callback on each element of array until the callback * returns a truthy value, then returns that value. diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index b18e12d2b96..3106ea9576c 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -331,7 +331,7 @@ namespace ts.refactor.extractSymbol { Continue = 1 << 1, Return = 1 << 2 } - if (!isStatement(nodeToCheck) && !(isPartOfExpression(nodeToCheck) && isExtractableExpression(nodeToCheck))) { + if (!isStatement(nodeToCheck) && !(isExpressionNode(nodeToCheck) && isExtractableExpression(nodeToCheck))) { return [createDiagnosticForNode(nodeToCheck, Messages.StatementOrExpressionExpected)]; } @@ -491,7 +491,7 @@ namespace ts.refactor.extractSymbol { if (isStatement(node)) { return [node]; } - else if (isPartOfExpression(node)) { + else if (isExpressionNode(node)) { // If our selection is the expression in an ExpressionStatement, expand // the selection to include the enclosing Statement (this stops us // from trying to care about the return value of the extracted function @@ -730,7 +730,7 @@ namespace ts.refactor.extractSymbol { let type = checker.getTypeOfSymbolAtLocation(usage.symbol, usage.node); // Widen the type so we don't emit nonsense annotations like "function fn(x: 3) {" type = checker.getBaseTypeOfLiteralType(type); - typeNode = checker.typeToTypeNode(type, node, NodeBuilderFlags.NoTruncation); + typeNode = checker.typeToTypeNode(type, scope, NodeBuilderFlags.NoTruncation); } const paramDecl = createParameter( diff --git a/src/services/refactors/installTypesForPackage.ts b/src/services/refactors/installTypesForPackage.ts index 5edc5570710..996645fc15d 100644 --- a/src/services/refactors/installTypesForPackage.ts +++ b/src/services/refactors/installTypesForPackage.ts @@ -12,8 +12,7 @@ namespace ts.refactor.installTypesForPackage { registerRefactor(installTypesForPackage); function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { - const options = context.program.getCompilerOptions(); - if (options.noImplicitAny || options.strict) { + if (getStrictOptionValue(context.program.getCompilerOptions(), "noImplicitAny")) { // Then it will be available via `fixCannotFindModule`. return undefined; } diff --git a/src/services/services.ts b/src/services/services.ts index d6e75868852..06edd621e96 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -31,7 +31,7 @@ namespace ts { /** The version of the language service API */ - export const servicesVersion = "0.5"; + export const servicesVersion = "0.6"; /* @internal */ let ruleProvider: formatting.RulesProvider; @@ -724,9 +724,9 @@ namespace ts { case SyntaxKind.BinaryExpression: if (getSpecialPropertyAssignmentKind(node as BinaryExpression) !== SpecialPropertyAssignmentKind.None) { - addDeclaration(node as BinaryExpression); + addDeclaration(node as BinaryExpression); } - // falls through + // falls through default: forEachChild(node, visit); @@ -1428,6 +1428,11 @@ namespace ts { return GoToDefinition.getDefinitionAtPosition(program, getValidSourceFile(fileName), position); } + function getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan { + synchronizeHostData(); + return GoToDefinition.getDefinitionAndBoundSpan(program, getValidSourceFile(fileName), position); + } + function getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] { synchronizeHostData(); return GoToDefinition.getTypeDefinitionAtPosition(program.getTypeChecker(), getValidSourceFile(fileName), position); @@ -2040,6 +2045,7 @@ namespace ts { getSignatureHelpItems, getQuickInfoAtPosition, getDefinitionAtPosition, + getDefinitionAndBoundSpan, getImplementationAtPosition, getTypeDefinitionAtPosition, getReferencesAtPosition, diff --git a/src/services/shims.ts b/src/services/shims.ts index 6e979e22023..9048d68c828 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -141,7 +141,7 @@ namespace ts { getEncodedSemanticClassifications(fileName: string, start: number, length: number): string; getCompletionsAtPosition(fileName: string, position: number): string; - getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/): string; + getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/, source: string | undefined): string; getQuickInfoAtPosition(fileName: string, position: number): string; @@ -170,6 +170,8 @@ namespace ts { */ getDefinitionAtPosition(fileName: string, position: number): string; + getDefinitionAndBoundSpan(fileName: string, position: number): string; + /** * Returns a JSON-encoded value of the type: * { fileName: string; textSpan: { start: number; length: number}; kind: string; name: string; containerKind: string; containerName: string } @@ -772,6 +774,17 @@ namespace ts { ); } + /** + * Computes the definition location and file for the symbol + * at the requested position. + */ + public getDefinitionAndBoundSpan(fileName: string, position: number): string { + return this.forwardJSONCall( + `getDefinitionAndBoundSpan('${fileName}', ${position})`, + () => this.languageService.getDefinitionAndBoundSpan(fileName, position) + ); + } + /// GOTO Type /** @@ -893,12 +906,12 @@ namespace ts { } /** Get a string based representation of a completion list entry details */ - public getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/) { + public getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/, source: string | undefined) { return this.forwardJSONCall( `getCompletionEntryDetails('${fileName}', ${position}, '${entryName}')`, () => { const localOptions: ts.FormatCodeOptions = JSON.parse(options); - return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions); + return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions, source); } ); } diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index d38d21f1092..d210a045513 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -90,9 +90,16 @@ namespace ts.SymbolDisplay { : ScriptElementKindModifier.none; } + interface SymbolDisplayPartsDocumentationAndSymbolKind { + displayParts: SymbolDisplayPart[]; + documentation: SymbolDisplayPart[]; + symbolKind: ScriptElementKind; + tags: JSDocTagInfo[]; + } + // TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: TypeChecker, symbol: Symbol, sourceFile: SourceFile, enclosingDeclaration: Node, - location: Node, semanticMeaning = getMeaningFromLocation(location)) { + location: Node, semanticMeaning = getMeaningFromLocation(location)): SymbolDisplayPartsDocumentationAndSymbolKind { const displayParts: SymbolDisplayPart[] = []; let documentation: SymbolDisplayPart[]; diff --git a/src/services/types.ts b/src/services/types.ts index c854eb8aacc..2d629041d1f 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -238,8 +238,14 @@ namespace ts { getCompletionsAtPosition(fileName: string, position: number): CompletionInfo; // "options" and "source" are optional only for backwards-compatibility - getCompletionEntryDetails(fileName: string, position: number, name: string, options?: FormatCodeOptions | FormatCodeSettings, source?: string): CompletionEntryDetails; - getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string): Symbol; + getCompletionEntryDetails( + fileName: string, + position: number, + name: string, + options: FormatCodeOptions | FormatCodeSettings | undefined, + source: string | undefined, + ): CompletionEntryDetails; + getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; @@ -253,6 +259,7 @@ namespace ts { findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[]; getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; + getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan; getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[]; @@ -575,6 +582,11 @@ namespace ts { containerName: string; } + export interface DefinitionInfoAndBoundSpan { + definitions: ReadonlyArray; + textSpan: TextSpan; + } + export interface ReferencedSymbolDefinitionInfo extends DefinitionInfo { displayParts: SymbolDisplayPart[]; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 2b116eb1cd0..2580ff18410 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -178,7 +178,7 @@ namespace ts { switch (node.kind) { case SyntaxKind.ThisKeyword: - return !isPartOfExpression(node); + return !isExpressionNode(node); case SyntaxKind.ThisType: return true; } diff --git a/tests/baselines/reference/abstractPropertyInConstructor.errors.txt b/tests/baselines/reference/abstractPropertyInConstructor.errors.txt index 461dd713d3b..0798d91ce78 100644 --- a/tests/baselines/reference/abstractPropertyInConstructor.errors.txt +++ b/tests/baselines/reference/abstractPropertyInConstructor.errors.txt @@ -5,7 +5,7 @@ tests/cases/compiler/abstractPropertyInConstructor.ts(9,14): error TS2715: Abstr ==== tests/cases/compiler/abstractPropertyInConstructor.ts (3 errors) ==== abstract class AbstractClass { - constructor(str: string) { + constructor(str: string, other: AbstractClass) { this.method(parseInt(str)); let val = this.prop.toLowerCase(); ~~~~ @@ -20,9 +20,13 @@ tests/cases/compiler/abstractPropertyInConstructor.ts(9,14): error TS2715: Abstr ~~ !!! error TS2715: Abstract property 'cb' in class 'AbstractClass' cannot be accessed in the constructor. + // OK, reference is inside function const innerFunction = () => { return this.prop; } + + // OK, references are to another instance + other.cb(other.prop); } abstract prop: string; @@ -34,4 +38,13 @@ tests/cases/compiler/abstractPropertyInConstructor.ts(9,14): error TS2715: Abstr this.prop = this.prop + "!"; } } + + class User { + constructor(a: AbstractClass) { + a.prop; + a.cb("hi"); + a.method(12); + a.method2(); + } + } \ No newline at end of file diff --git a/tests/baselines/reference/abstractPropertyInConstructor.js b/tests/baselines/reference/abstractPropertyInConstructor.js index 18a2937a191..5a4feda110f 100644 --- a/tests/baselines/reference/abstractPropertyInConstructor.js +++ b/tests/baselines/reference/abstractPropertyInConstructor.js @@ -1,6 +1,6 @@ //// [abstractPropertyInConstructor.ts] abstract class AbstractClass { - constructor(str: string) { + constructor(str: string, other: AbstractClass) { this.method(parseInt(str)); let val = this.prop.toLowerCase(); @@ -9,9 +9,13 @@ abstract class AbstractClass { } this.cb(str); + // OK, reference is inside function const innerFunction = () => { return this.prop; } + + // OK, references are to another instance + other.cb(other.prop); } abstract prop: string; @@ -23,11 +27,20 @@ abstract class AbstractClass { this.prop = this.prop + "!"; } } + +class User { + constructor(a: AbstractClass) { + a.prop; + a.cb("hi"); + a.method(12); + a.method2(); + } +} //// [abstractPropertyInConstructor.js] var AbstractClass = /** @class */ (function () { - function AbstractClass(str) { + function AbstractClass(str, other) { var _this = this; this.method(parseInt(str)); var val = this.prop.toLowerCase(); @@ -35,12 +48,24 @@ var AbstractClass = /** @class */ (function () { this.prop = "Hello World"; } this.cb(str); + // OK, reference is inside function var innerFunction = function () { return _this.prop; }; + // OK, references are to another instance + other.cb(other.prop); } AbstractClass.prototype.method2 = function () { this.prop = this.prop + "!"; }; return AbstractClass; }()); +var User = /** @class */ (function () { + function User(a) { + a.prop; + a.cb("hi"); + a.method(12); + a.method2(); + } + return User; +}()); diff --git a/tests/baselines/reference/abstractPropertyInConstructor.symbols b/tests/baselines/reference/abstractPropertyInConstructor.symbols index 0d542ffb0a8..42cd3bb6827 100644 --- a/tests/baselines/reference/abstractPropertyInConstructor.symbols +++ b/tests/baselines/reference/abstractPropertyInConstructor.symbols @@ -2,69 +2,110 @@ abstract class AbstractClass { >AbstractClass : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0)) - constructor(str: string) { + constructor(str: string, other: AbstractClass) { >str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16)) +>other : Symbol(other, Decl(abstractPropertyInConstructor.ts, 1, 28)) +>AbstractClass : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0)) this.method(parseInt(str)); ->this.method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 16, 37)) +>this.method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 20, 37)) >this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0)) ->method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 16, 37)) +>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 20, 37)) >parseInt : Symbol(parseInt, Decl(lib.d.ts, --, --)) >str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16)) let val = this.prop.toLowerCase(); >val : Symbol(val, Decl(abstractPropertyInConstructor.ts, 3, 11)) >this.prop.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) ->this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) +>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) >this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0)) ->prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) +>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) >toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) if (!str) { >str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16)) this.prop = "Hello World"; ->this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) +>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) >this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0)) ->prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) +>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) } this.cb(str); ->this.cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 15, 26)) +>this.cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26)) >this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0)) ->cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 15, 26)) +>cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26)) >str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16)) + // OK, reference is inside function const innerFunction = () => { ->innerFunction : Symbol(innerFunction, Decl(abstractPropertyInConstructor.ts, 10, 13)) +>innerFunction : Symbol(innerFunction, Decl(abstractPropertyInConstructor.ts, 11, 13)) return this.prop; ->this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) +>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) >this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0)) ->prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) +>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) } + + // OK, references are to another instance + other.cb(other.prop); +>other.cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26)) +>other : Symbol(other, Decl(abstractPropertyInConstructor.ts, 1, 28)) +>cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26)) +>other.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) +>other : Symbol(other, Decl(abstractPropertyInConstructor.ts, 1, 28)) +>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) } abstract prop: string; ->prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) +>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) abstract cb: (s: string) => void; ->cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 15, 26)) ->s : Symbol(s, Decl(abstractPropertyInConstructor.ts, 16, 18)) +>cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26)) +>s : Symbol(s, Decl(abstractPropertyInConstructor.ts, 20, 18)) abstract method(num: number): void; ->method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 16, 37)) ->num : Symbol(num, Decl(abstractPropertyInConstructor.ts, 18, 20)) +>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 20, 37)) +>num : Symbol(num, Decl(abstractPropertyInConstructor.ts, 22, 20)) method2() { ->method2 : Symbol(AbstractClass.method2, Decl(abstractPropertyInConstructor.ts, 18, 39)) +>method2 : Symbol(AbstractClass.method2, Decl(abstractPropertyInConstructor.ts, 22, 39)) this.prop = this.prop + "!"; ->this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) +>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) >this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0)) ->prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) ->this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) +>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) +>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) >this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0)) ->prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) +>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) + } +} + +class User { +>User : Symbol(User, Decl(abstractPropertyInConstructor.ts, 27, 1)) + + constructor(a: AbstractClass) { +>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 30, 16)) +>AbstractClass : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0)) + + a.prop; +>a.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) +>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 30, 16)) +>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5)) + + a.cb("hi"); +>a.cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26)) +>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 30, 16)) +>cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26)) + + a.method(12); +>a.method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 20, 37)) +>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 30, 16)) +>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 20, 37)) + + a.method2(); +>a.method2 : Symbol(AbstractClass.method2, Decl(abstractPropertyInConstructor.ts, 22, 39)) +>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 30, 16)) +>method2 : Symbol(AbstractClass.method2, Decl(abstractPropertyInConstructor.ts, 22, 39)) } } diff --git a/tests/baselines/reference/abstractPropertyInConstructor.types b/tests/baselines/reference/abstractPropertyInConstructor.types index 0ffb5f1bdfd..6f8970a7702 100644 --- a/tests/baselines/reference/abstractPropertyInConstructor.types +++ b/tests/baselines/reference/abstractPropertyInConstructor.types @@ -2,8 +2,10 @@ abstract class AbstractClass { >AbstractClass : AbstractClass - constructor(str: string) { + constructor(str: string, other: AbstractClass) { >str : string +>other : AbstractClass +>AbstractClass : AbstractClass this.method(parseInt(str)); >this.method(parseInt(str)) : void @@ -41,6 +43,7 @@ abstract class AbstractClass { >cb : (s: string) => void >str : string + // OK, reference is inside function const innerFunction = () => { >innerFunction : () => string >() => { return this.prop; } : () => string @@ -50,6 +53,16 @@ abstract class AbstractClass { >this : this >prop : string } + + // OK, references are to another instance + other.cb(other.prop); +>other.cb(other.prop) : void +>other.cb : (s: string) => void +>other : AbstractClass +>cb : (s: string) => void +>other.prop : string +>other : AbstractClass +>prop : string } abstract prop: string; @@ -79,3 +92,37 @@ abstract class AbstractClass { } } +class User { +>User : User + + constructor(a: AbstractClass) { +>a : AbstractClass +>AbstractClass : AbstractClass + + a.prop; +>a.prop : string +>a : AbstractClass +>prop : string + + a.cb("hi"); +>a.cb("hi") : void +>a.cb : (s: string) => void +>a : AbstractClass +>cb : (s: string) => void +>"hi" : "hi" + + a.method(12); +>a.method(12) : void +>a.method : (num: number) => void +>a : AbstractClass +>method : (num: number) => void +>12 : 12 + + a.method2(); +>a.method2() : void +>a.method2 : () => void +>a : AbstractClass +>method2 : () => void + } +} + diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 26c03954b45..7fbff83009f 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -313,46 +313,49 @@ declare namespace ts { JsxSelfClosingElement = 250, JsxOpeningElement = 251, JsxClosingElement = 252, - JsxAttribute = 253, - JsxAttributes = 254, - JsxSpreadAttribute = 255, - JsxExpression = 256, - CaseClause = 257, - DefaultClause = 258, - HeritageClause = 259, - CatchClause = 260, - PropertyAssignment = 261, - ShorthandPropertyAssignment = 262, - SpreadAssignment = 263, - EnumMember = 264, - SourceFile = 265, - Bundle = 266, - JSDocTypeExpression = 267, - JSDocAllType = 268, - JSDocUnknownType = 269, - JSDocNullableType = 270, - JSDocNonNullableType = 271, - JSDocOptionalType = 272, - JSDocFunctionType = 273, - JSDocVariadicType = 274, - JSDocComment = 275, - JSDocTag = 276, - JSDocAugmentsTag = 277, - JSDocClassTag = 278, - JSDocParameterTag = 279, - JSDocReturnTag = 280, - JSDocTypeTag = 281, - JSDocTemplateTag = 282, - JSDocTypedefTag = 283, - JSDocPropertyTag = 284, - JSDocTypeLiteral = 285, - SyntaxList = 286, - NotEmittedStatement = 287, - PartiallyEmittedExpression = 288, - CommaListExpression = 289, - MergeDeclarationMarker = 290, - EndOfDeclarationMarker = 291, - Count = 292, + JsxFragment = 253, + JsxOpeningFragment = 254, + JsxClosingFragment = 255, + JsxAttribute = 256, + JsxAttributes = 257, + JsxSpreadAttribute = 258, + JsxExpression = 259, + CaseClause = 260, + DefaultClause = 261, + HeritageClause = 262, + CatchClause = 263, + PropertyAssignment = 264, + ShorthandPropertyAssignment = 265, + SpreadAssignment = 266, + EnumMember = 267, + SourceFile = 268, + Bundle = 269, + JSDocTypeExpression = 270, + JSDocAllType = 271, + JSDocUnknownType = 272, + JSDocNullableType = 273, + JSDocNonNullableType = 274, + JSDocOptionalType = 275, + JSDocFunctionType = 276, + JSDocVariadicType = 277, + JSDocComment = 278, + JSDocTypeLiteral = 279, + JSDocTag = 280, + JSDocAugmentsTag = 281, + JSDocClassTag = 282, + JSDocParameterTag = 283, + JSDocReturnTag = 284, + JSDocTypeTag = 285, + JSDocTemplateTag = 286, + JSDocTypedefTag = 287, + JSDocPropertyTag = 288, + SyntaxList = 289, + NotEmittedStatement = 290, + PartiallyEmittedExpression = 291, + CommaListExpression = 292, + MergeDeclarationMarker = 293, + EndOfDeclarationMarker = 294, + Count = 295, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -378,10 +381,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 143, - FirstJSDocNode = 267, - LastJSDocNode = 285, - FirstJSDocTagNode = 276, - LastJSDocTagNode = 285, + FirstJSDocNode = 270, + LastJSDocNode = 288, + FirstJSDocTagNode = 280, + LastJSDocTagNode = 288, } enum NodeFlags { None = 0, @@ -1061,6 +1064,20 @@ declare namespace ts { tagName: JsxTagNameExpression; attributes: JsxAttributes; } + interface JsxFragment extends PrimaryExpression { + kind: SyntaxKind.JsxFragment; + openingFragment: JsxOpeningFragment; + children: NodeArray; + closingFragment: JsxClosingFragment; + } + interface JsxOpeningFragment extends Expression { + kind: SyntaxKind.JsxOpeningFragment; + parent?: JsxFragment; + } + interface JsxClosingFragment extends Expression { + kind: SyntaxKind.JsxClosingFragment; + parent?: JsxFragment; + } interface JsxAttribute extends ObjectLiteralElement { kind: SyntaxKind.JsxAttribute; parent?: JsxAttributes; @@ -1088,7 +1105,7 @@ declare namespace ts { containsOnlyWhiteSpaces: boolean; parent?: JsxElement; } - type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement; + type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment; interface Statement extends Node { _statementBrand: any; } @@ -3002,6 +3019,9 @@ declare namespace ts { function isJsxSelfClosingElement(node: Node): node is JsxSelfClosingElement; function isJsxOpeningElement(node: Node): node is JsxOpeningElement; function isJsxClosingElement(node: Node): node is JsxClosingElement; + function isJsxFragment(node: Node): node is JsxFragment; + function isJsxOpeningFragment(node: Node): node is JsxOpeningFragment; + function isJsxClosingFragment(node: Node): node is JsxClosingFragment; function isJsxAttribute(node: Node): node is JsxAttribute; function isJsxAttributes(node: Node): node is JsxAttributes; function isJsxSpreadAttribute(node: Node): node is JsxSpreadAttribute; @@ -3501,6 +3521,8 @@ declare namespace ts { function updateJsxOpeningElement(node: JsxOpeningElement, tagName: JsxTagNameExpression, attributes: JsxAttributes): JsxOpeningElement; function createJsxClosingElement(tagName: JsxTagNameExpression): JsxClosingElement; function updateJsxClosingElement(node: JsxClosingElement, tagName: JsxTagNameExpression): JsxClosingElement; + function createJsxFragment(openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment): JsxFragment; + function updateJsxFragment(node: JsxFragment, openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment): JsxFragment; function createJsxAttribute(name: Identifier, initializer: StringLiteral | JsxExpression): JsxAttribute; function updateJsxAttribute(node: JsxAttribute, name: Identifier, initializer: StringLiteral | JsxExpression): JsxAttribute; function createJsxAttributes(properties: ReadonlyArray): JsxAttributes; @@ -3916,8 +3938,8 @@ declare namespace ts { getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; getCompletionsAtPosition(fileName: string, position: number): CompletionInfo; - getCompletionEntryDetails(fileName: string, position: number, name: string, options?: FormatCodeOptions | FormatCodeSettings, source?: string): CompletionEntryDetails; - getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string): Symbol; + getCompletionEntryDetails(fileName: string, position: number, name: string, options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails; + getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan; getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan; @@ -3925,6 +3947,7 @@ declare namespace ts { getRenameInfo(fileName: string, position: number): RenameInfo; findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[]; getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; + getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan; getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; @@ -4188,6 +4211,10 @@ declare namespace ts { containerKind: ScriptElementKind; containerName: string; } + interface DefinitionInfoAndBoundSpan { + definitions: ReadonlyArray; + textSpan: TextSpan; + } interface ReferencedSymbolDefinitionInfo extends DefinitionInfo { displayParts: SymbolDisplayPart[]; } @@ -4599,7 +4626,7 @@ declare namespace ts { } declare namespace ts { /** The version of the language service API */ - const servicesVersion = "0.5"; + const servicesVersion = "0.6"; interface DisplayPartsSymbolWriter extends SymbolWriter { displayParts(): SymbolDisplayPart[]; } @@ -4823,6 +4850,7 @@ declare namespace ts.server.protocol { CompileOnSaveEmitFile = "compileOnSaveEmitFile", Configure = "configure", Definition = "definition", + DefinitionAndBoundSpan = "definitionAndBoundSpan", Implementation = "implementation", Exit = "exit", Format = "format", @@ -5342,12 +5370,19 @@ declare namespace ts.server.protocol { */ file: string; } + interface DefinitionInfoAndBoundSpan { + definitions: ReadonlyArray; + textSpan: TextSpan; + } /** * Definition response message. Gives text range for definition. */ interface DefinitionResponse extends Response { body?: FileSpan[]; } + interface DefinitionInfoAndBoundSpanReponse extends Response { + body?: DefinitionInfoAndBoundSpan; + } /** * Definition response message. Gives text range for definition. */ @@ -6923,6 +6958,9 @@ declare namespace ts.server { private convertToDiagnosticsWithLinePosition(diagnostics, scriptInfo); private getDiagnosticsWorker(args, isSemantic, selector, includeLinePosition); private getDefinition(args, simplifiedResult); + private getDefinitionAndBoundSpan(args, simplifiedResult); + private mapDefinitionInfo(definitions, project); + private toFileSpan(fileName, textSpan, project); private getTypeDefinition(args); private getImplementation(args, simplifiedResult); private getOccurrences(args); @@ -6972,10 +7010,10 @@ declare namespace ts.server { private reload(args, reqSeq); private saveToTmp(fileName, tempFileName); private closeClientFile(fileName); - private decorateNavigationBarItems(items, scriptInfo); + private mapLocationNavigationBarItems(items, scriptInfo); private getNavigationBarItems(args, simplifiedResult); - private decorateNavigationTree(tree, scriptInfo); - private decorateSpan(span, scriptInfo); + private toLocationNavigationTree(tree, scriptInfo); + private toLocationTextSpan(span, scriptInfo); private getNavigationTree(args, simplifiedResult); private getNavigateToItems(args, simplifiedResult); private getSupportedCodeFixes(); diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 6abf9d0cb6e..73d069757df 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -313,46 +313,49 @@ declare namespace ts { JsxSelfClosingElement = 250, JsxOpeningElement = 251, JsxClosingElement = 252, - JsxAttribute = 253, - JsxAttributes = 254, - JsxSpreadAttribute = 255, - JsxExpression = 256, - CaseClause = 257, - DefaultClause = 258, - HeritageClause = 259, - CatchClause = 260, - PropertyAssignment = 261, - ShorthandPropertyAssignment = 262, - SpreadAssignment = 263, - EnumMember = 264, - SourceFile = 265, - Bundle = 266, - JSDocTypeExpression = 267, - JSDocAllType = 268, - JSDocUnknownType = 269, - JSDocNullableType = 270, - JSDocNonNullableType = 271, - JSDocOptionalType = 272, - JSDocFunctionType = 273, - JSDocVariadicType = 274, - JSDocComment = 275, - JSDocTag = 276, - JSDocAugmentsTag = 277, - JSDocClassTag = 278, - JSDocParameterTag = 279, - JSDocReturnTag = 280, - JSDocTypeTag = 281, - JSDocTemplateTag = 282, - JSDocTypedefTag = 283, - JSDocPropertyTag = 284, - JSDocTypeLiteral = 285, - SyntaxList = 286, - NotEmittedStatement = 287, - PartiallyEmittedExpression = 288, - CommaListExpression = 289, - MergeDeclarationMarker = 290, - EndOfDeclarationMarker = 291, - Count = 292, + JsxFragment = 253, + JsxOpeningFragment = 254, + JsxClosingFragment = 255, + JsxAttribute = 256, + JsxAttributes = 257, + JsxSpreadAttribute = 258, + JsxExpression = 259, + CaseClause = 260, + DefaultClause = 261, + HeritageClause = 262, + CatchClause = 263, + PropertyAssignment = 264, + ShorthandPropertyAssignment = 265, + SpreadAssignment = 266, + EnumMember = 267, + SourceFile = 268, + Bundle = 269, + JSDocTypeExpression = 270, + JSDocAllType = 271, + JSDocUnknownType = 272, + JSDocNullableType = 273, + JSDocNonNullableType = 274, + JSDocOptionalType = 275, + JSDocFunctionType = 276, + JSDocVariadicType = 277, + JSDocComment = 278, + JSDocTypeLiteral = 279, + JSDocTag = 280, + JSDocAugmentsTag = 281, + JSDocClassTag = 282, + JSDocParameterTag = 283, + JSDocReturnTag = 284, + JSDocTypeTag = 285, + JSDocTemplateTag = 286, + JSDocTypedefTag = 287, + JSDocPropertyTag = 288, + SyntaxList = 289, + NotEmittedStatement = 290, + PartiallyEmittedExpression = 291, + CommaListExpression = 292, + MergeDeclarationMarker = 293, + EndOfDeclarationMarker = 294, + Count = 295, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -378,10 +381,10 @@ declare namespace ts { FirstBinaryOperator = 27, LastBinaryOperator = 70, FirstNode = 143, - FirstJSDocNode = 267, - LastJSDocNode = 285, - FirstJSDocTagNode = 276, - LastJSDocTagNode = 285, + FirstJSDocNode = 270, + LastJSDocNode = 288, + FirstJSDocTagNode = 280, + LastJSDocTagNode = 288, } enum NodeFlags { None = 0, @@ -1061,6 +1064,20 @@ declare namespace ts { tagName: JsxTagNameExpression; attributes: JsxAttributes; } + interface JsxFragment extends PrimaryExpression { + kind: SyntaxKind.JsxFragment; + openingFragment: JsxOpeningFragment; + children: NodeArray; + closingFragment: JsxClosingFragment; + } + interface JsxOpeningFragment extends Expression { + kind: SyntaxKind.JsxOpeningFragment; + parent?: JsxFragment; + } + interface JsxClosingFragment extends Expression { + kind: SyntaxKind.JsxClosingFragment; + parent?: JsxFragment; + } interface JsxAttribute extends ObjectLiteralElement { kind: SyntaxKind.JsxAttribute; parent?: JsxAttributes; @@ -1088,7 +1105,7 @@ declare namespace ts { containsOnlyWhiteSpaces: boolean; parent?: JsxElement; } - type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement; + type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment; interface Statement extends Node { _statementBrand: any; } @@ -3057,6 +3074,9 @@ declare namespace ts { function isJsxSelfClosingElement(node: Node): node is JsxSelfClosingElement; function isJsxOpeningElement(node: Node): node is JsxOpeningElement; function isJsxClosingElement(node: Node): node is JsxClosingElement; + function isJsxFragment(node: Node): node is JsxFragment; + function isJsxOpeningFragment(node: Node): node is JsxOpeningFragment; + function isJsxClosingFragment(node: Node): node is JsxClosingFragment; function isJsxAttribute(node: Node): node is JsxAttribute; function isJsxAttributes(node: Node): node is JsxAttributes; function isJsxSpreadAttribute(node: Node): node is JsxSpreadAttribute; @@ -3448,6 +3468,8 @@ declare namespace ts { function updateJsxOpeningElement(node: JsxOpeningElement, tagName: JsxTagNameExpression, attributes: JsxAttributes): JsxOpeningElement; function createJsxClosingElement(tagName: JsxTagNameExpression): JsxClosingElement; function updateJsxClosingElement(node: JsxClosingElement, tagName: JsxTagNameExpression): JsxClosingElement; + function createJsxFragment(openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment): JsxFragment; + function updateJsxFragment(node: JsxFragment, openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment): JsxFragment; function createJsxAttribute(name: Identifier, initializer: StringLiteral | JsxExpression): JsxAttribute; function updateJsxAttribute(node: JsxAttribute, name: Identifier, initializer: StringLiteral | JsxExpression): JsxAttribute; function createJsxAttributes(properties: ReadonlyArray): JsxAttributes; @@ -3916,8 +3938,8 @@ declare namespace ts { getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; getCompletionsAtPosition(fileName: string, position: number): CompletionInfo; - getCompletionEntryDetails(fileName: string, position: number, name: string, options?: FormatCodeOptions | FormatCodeSettings, source?: string): CompletionEntryDetails; - getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string): Symbol; + getCompletionEntryDetails(fileName: string, position: number, name: string, options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails; + getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan; getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan; @@ -3925,6 +3947,7 @@ declare namespace ts { getRenameInfo(fileName: string, position: number): RenameInfo; findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[]; getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; + getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan; getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; @@ -4188,6 +4211,10 @@ declare namespace ts { containerKind: ScriptElementKind; containerName: string; } + interface DefinitionInfoAndBoundSpan { + definitions: ReadonlyArray; + textSpan: TextSpan; + } interface ReferencedSymbolDefinitionInfo extends DefinitionInfo { displayParts: SymbolDisplayPart[]; } @@ -4599,7 +4626,7 @@ declare namespace ts { } declare namespace ts { /** The version of the language service API */ - const servicesVersion = "0.5"; + const servicesVersion = "0.6"; interface DisplayPartsSymbolWriter extends SymbolWriter { displayParts(): SymbolDisplayPart[]; } diff --git a/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt b/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt new file mode 100644 index 00000000000..ecae349e19e --- /dev/null +++ b/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt @@ -0,0 +1,56 @@ +tests/cases/conformance/jsx/file.tsx(42,27): error TS2322: Type '{ a: 10; b: "hi"; children: Element[]; }' is not assignable to type 'IntrinsicAttributes & SingleChildProp'. + Type '{ a: 10; b: "hi"; children: Element[]; }' is not assignable to type 'SingleChildProp'. + Types of property 'children' are incompatible. + Type 'Element[]' is not assignable to type 'Element'. + Property 'type' is missing in type 'Element[]'. + + +==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== + import React = require('react'); + + interface Prop { + a: number, + b: string, + children: JSX.Element | JSX.Element[]; + } + + class Button extends React.Component { + render() { + return (
My Button
) + } + } + + function AnotherButton(p: any) { + return

Just Another Button

; + } + + function Comp(p: Prop) { + return
{p.b}
; + } + + // OK + let k1 = <>