diff --git a/completionAtDottedNamespace.ts b/completionAtDottedNamespace.ts new file mode 100644 index 00000000000..cbf16435a91 --- /dev/null +++ b/completionAtDottedNamespace.ts @@ -0,0 +1,5 @@ +/// + +////namespace wwer./**/w + +verify.completions({ marker: "", exact: [], isNewIdentifierLocation: true }); diff --git a/scripts/build/baselineAccept.js b/scripts/build/baselineAccept.js index 4710161027e..df61b87cd32 100644 --- a/scripts/build/baselineAccept.js +++ b/scripts/build/baselineAccept.js @@ -12,7 +12,7 @@ function baselineAccept(subfolder = "") { } function baselineCopy(subfolder = "") { - return gulp.src([`${localBaseline}${subfolder ? `${subfolder}/` : ``}**`, `!${localBaseline}${subfolder}/**/*.delete`], { base: localBaseline, read: false }) + return gulp.src([`${localBaseline}${subfolder ? `${subfolder}/` : ``}**`, `!${localBaseline}${subfolder}/**/*.delete`], { base: localBaseline }) .pipe(gulp.dest(refBaseline)); } @@ -21,4 +21,4 @@ function baselineDelete(subfolder = "") { .pipe(rm()) .pipe(rename({ extname: "" })) .pipe(rm(refBaseline)); -} \ No newline at end of file +} diff --git a/scripts/tslint/tsconfig.json b/scripts/tslint/tsconfig.json index 045abc24f6b..e9f90ad4266 100644 --- a/scripts/tslint/tsconfig.json +++ b/scripts/tslint/tsconfig.json @@ -12,6 +12,7 @@ "module": "commonjs", "outDir": "../../built/local/tslint", "baseUrl": "../..", + "types": ["node"], "paths": { "typescript": ["lib/typescript.d.ts"] } diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 79fef8b4e61..5ef96056e55 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -753,7 +753,7 @@ namespace ts { function isNarrowableReference(expr: Expression): boolean { return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword || - isPropertyAccessExpression(expr) && isNarrowableReference(expr.expression) || + (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) || isElementAccessExpression(expr) && expr.argumentExpression && (isStringLiteral(expr.argumentExpression) || isNumericLiteral(expr.argumentExpression)) && isNarrowableReference(expr.expression); diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 637e77c545f..06615b3a97f 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -64,7 +64,9 @@ namespace ts { const state = BuilderState.create(newProgram, getCanonicalFileName, oldState) as BuilderProgramState; state.program = newProgram; const compilerOptions = newProgram.getCompilerOptions(); - if (!compilerOptions.outFile && !compilerOptions.out) { + // With --out or --outFile, any change affects all semantic diagnostics so no need to cache them + // With --isolatedModules, emitting changed file doesnt emit dependent files so we cant know of dependent files to retrieve errors so dont cache the errors + if (!compilerOptions.outFile && !compilerOptions.out && !compilerOptions.isolatedModules) { state.semanticDiagnosticsPerFile = createMap>(); } state.changedFilesSet = createMap(); @@ -76,7 +78,8 @@ namespace ts { if (useOldState) { // Verify the sanity of old state if (!oldState!.currentChangedFilePath) { - Debug.assert(!oldState!.affectedFiles && (!oldState!.currentAffectedFilesSignatures || !oldState!.currentAffectedFilesSignatures!.size), "Cannot reuse if only few affected files of currentChangedFile were iterated"); + const affectedSignatures = oldState!.currentAffectedFilesSignatures; + Debug.assert(!oldState!.affectedFiles && (!affectedSignatures || !affectedSignatures.size), "Cannot reuse if only few affected files of currentChangedFile were iterated"); } if (canCopySemanticDiagnostics) { Debug.assert(!forEachKey(oldState!.changedFilesSet, path => oldState!.semanticDiagnosticsPerFile!.has(path)), "Semantic diagnostics shouldnt be available for changed files"); @@ -281,10 +284,19 @@ namespace ts { } // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected - return !!forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) => + if (forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) => !state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it exportedModules.has(filePath) && removeSemanticDiagnosticsOfFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile) + )) { + return true; + } + + // Remove diagnostics of files that import this file (without going to exports of referencing files) + return !!forEachEntry(state.referencedMap!, (referencesInFile, referencingFilePath) => + referencesInFile.has(filePath) && + !seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file + removeSemanticDiagnosticsOf(state, referencingFilePath as Path) // Dont add to seen since this is not yet done with the export removal ); } @@ -329,15 +341,19 @@ namespace ts { */ function getSemanticDiagnosticsOfFile(state: BuilderProgramState, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { const path = sourceFile.path; - const cachedDiagnostics = state.semanticDiagnosticsPerFile!.get(path); - // Report the semantic diagnostics from the cache if we already have those diagnostics present - if (cachedDiagnostics) { - return cachedDiagnostics; + if (state.semanticDiagnosticsPerFile) { + const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path); + // Report the semantic diagnostics from the cache if we already have those diagnostics present + if (cachedDiagnostics) { + return cachedDiagnostics; + } } // Diagnostics werent cached, get them from program, and cache the result const diagnostics = state.program.getSemanticDiagnostics(sourceFile, cancellationToken); - state.semanticDiagnosticsPerFile!.set(path, diagnostics); + if (state.semanticDiagnosticsPerFile) { + state.semanticDiagnosticsPerFile.set(path, diagnostics); + } return diagnostics; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 23c5568f2e5..f7cec89fe80 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -444,10 +444,10 @@ namespace ts { const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const resolvingDefaultType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const markerSuperType = createType(TypeFlags.TypeParameter); - const markerSubType = createType(TypeFlags.TypeParameter); + const markerSuperType = createTypeParameter(); + const markerSubType = createTypeParameter(); markerSubType.constraint = markerSuperType; - const markerOtherType = createType(TypeFlags.TypeParameter); + const markerOtherType = createTypeParameter(); const noTypePredicate = createIdentifierTypePredicate("<>", 0, anyType); @@ -663,7 +663,6 @@ namespace ts { const subtypeRelation = createMap(); const assignableRelation = createMap(); - const definitelyAssignableRelation = createMap(); const comparableRelation = createMap(); const identityRelation = createMap(); const enumRelation = createMap(); @@ -2408,11 +2407,18 @@ namespace ts { // combine other declarations with the module or variable (e.g. a class/module, function/module, interface/variable). function resolveESModuleSymbol(moduleSymbol: Symbol | undefined, referencingLocation: Node, dontResolveAlias: boolean): Symbol | undefined { const symbol = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias); + if (!dontResolveAlias && symbol) { if (!(symbol.flags & (SymbolFlags.Module | SymbolFlags.Variable)) && !getDeclarationOfKind(symbol, SyntaxKind.SourceFile)) { - error(referencingLocation, Diagnostics.Module_0_resolves_to_a_non_module_entity_and_cannot_be_imported_using_this_construct, symbolToString(moduleSymbol!)); + const compilerOptionName = moduleKind >= ModuleKind.ES2015 + ? "allowSyntheticDefaultImports" + : "esModuleInterop"; + + error(referencingLocation, Diagnostics.This_module_can_only_be_referenced_with_ECMAScript_imports_Slashexports_by_turning_on_the_0_flag_and_referencing_its_default_export, compilerOptionName); + return symbol; } + if (compilerOptions.esModuleInterop) { const referenceParent = referencingLocation.parent; if ( @@ -2745,6 +2751,12 @@ namespace ts { return getUnionType(arrayFrom(typeofEQFacts.keys(), getLiteralType)); } + function createTypeParameter(symbol?: Symbol) { + const type = createType(TypeFlags.TypeParameter); + if (symbol) type.symbol = symbol; + return type; + } + // A reserved member name starts with two underscores, but the third character cannot be an underscore // or the @ symbol. A third underscore indicates an escaped form of an identifer that started // with at least two underscores. The @ character indicates that the name is denoted by a well known ES @@ -4919,7 +4931,7 @@ namespace ts { if (strictNullChecks && declaration.initializer && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined)) { type = getTypeWithFacts(type, TypeFacts.NEUndefined); } - return declaration.initializer && !getContextualTypeForVariableLikeDeclaration(walkUpBindingElementsAndPatterns(declaration)) ? + return declaration.initializer && !getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration)) ? getUnionType([type, checkDeclarationInitializer(declaration)], UnionReduction.Subtype) : type; } @@ -5419,6 +5431,10 @@ namespace ts { // Handle variable, parameter or property if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { + // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` + if (symbol.flags & SymbolFlags.ValueModule) { + return getTypeOfFuncClassEnumModule(symbol); + } return errorType; } let type: Type | undefined; @@ -5474,6 +5490,10 @@ namespace ts { } if (!popTypeResolution()) { + // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` + if (symbol.flags & SymbolFlags.ValueModule) { + return getTypeOfFuncClassEnumModule(symbol); + } type = reportCircularityError(symbol); } return type; @@ -6085,9 +6105,8 @@ namespace ts { (type).instantiations.set(getTypeListId(type.typeParameters), type); (type).target = type; (type).typeArguments = type.typeParameters; - type.thisType = createType(TypeFlags.TypeParameter); + type.thisType = createTypeParameter(symbol); type.thisType.isThisType = true; - type.thisType.symbol = symbol; type.thisType.constraint = type; } } @@ -6228,20 +6247,12 @@ namespace ts { function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter { const links = getSymbolLinks(symbol); - if (!links.declaredType) { - const type = createType(TypeFlags.TypeParameter); - type.symbol = symbol; - links.declaredType = type; - } - return links.declaredType; + return links.declaredType || (links.declaredType = createTypeParameter(symbol)); } function getDeclaredTypeOfAlias(symbol: Symbol): Type { const links = getSymbolLinks(symbol); - if (!links.declaredType) { - links.declaredType = getDeclaredTypeOfSymbol(resolveAlias(symbol)); - } - return links.declaredType; + return links.declaredType || (links.declaredType = getDeclaredTypeOfSymbol(resolveAlias(symbol))); } function getDeclaredTypeOfSymbol(symbol: Symbol): Type { @@ -6930,7 +6941,7 @@ namespace ts { function resolveUnionTypeMembers(type: UnionType) { // The members and properties collections are empty for union types. To get all properties of a union // type use getPropertiesOfType (only the language service uses this). - const callSignatures = getUnionSignatures(map(type.types, t => getSignaturesOfType(t, SignatureKind.Call))); + const callSignatures = getUnionSignatures(map(type.types, t => t === globalFunctionType ? [unknownSignature] : getSignaturesOfType(t, SignatureKind.Call))); const constructSignatures = getUnionSignatures(map(type.types, t => getSignaturesOfType(t, SignatureKind.Construct))); const stringIndexInfo = getUnionIndexInfo(type.types, IndexKind.String); const numberIndexInfo = getUnionIndexInfo(type.types, IndexKind.Number); @@ -7084,6 +7095,39 @@ namespace ts { setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined); } + // Return the lower bound of the key type in a mapped type. Intuitively, the lower + // bound includes those keys that are known to always be present, for example because + // because of constraints on type parameters (e.g. 'keyof T' for a constrained T). + function getLowerBoundOfKeyType(type: Type): Type { + if (type.flags & (TypeFlags.Any | TypeFlags.Primitive)) { + return type; + } + if (type.flags & TypeFlags.Index) { + return getIndexType(getApparentType((type).type)); + } + if (type.flags & TypeFlags.Conditional) { + return getLowerBoundOfConditionalType(type); + } + if (type.flags & TypeFlags.Union) { + return getUnionType(sameMap((type).types, getLowerBoundOfKeyType)); + } + if (type.flags & TypeFlags.Intersection) { + return getIntersectionType(sameMap((type).types, getLowerBoundOfKeyType)); + } + return neverType; + } + + function getLowerBoundOfConditionalType(type: ConditionalType) { + if (type.root.isDistributive) { + const constraint = getLowerBoundOfKeyType(type.checkType); + if (constraint !== type.checkType) { + const mapper = makeUnaryTypeMapper(type.root.checkType, constraint); + return getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper)); + } + } + return type; + } + /** Resolve the members of a mapped type { [P in K]: T } */ function resolveMappedTypeMembers(type: MappedType) { const members: SymbolTable = createSymbolTable(); @@ -7112,10 +7156,7 @@ namespace ts { } } else { - // If the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X. - // Then iterate over the constituents of the key type. - const iterationType = constraintType.flags & TypeFlags.Index ? getIndexType(getApparentType((constraintType).type)) : constraintType; - forEachType(iterationType, addMemberForKeyType); + forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType); } setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); @@ -7383,7 +7424,7 @@ namespace ts { if (type.root.isDistributive) { const simplified = getSimplifiedType(type.checkType); const constraint = simplified === type.checkType ? getConstraintOfType(simplified) : simplified; - if (constraint) { + if (constraint && constraint !== type.checkType) { const mapper = makeUnaryTypeMapper(type.root.checkType, constraint); const instantiated = getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper)); if (!(instantiated.flags & TypeFlags.Never)) { @@ -7669,38 +7710,37 @@ namespace ts { return props[0]; } let declarations: Declaration[] | undefined; - let commonType: Type | undefined; + let firstType: Type | undefined; let nameType: Type | undefined; const propTypes: Type[] = []; - let first = true; - let commonValueDeclaration: Declaration | undefined; + let firstValueDeclaration: Declaration | undefined; let hasNonUniformValueDeclaration = false; for (const prop of props) { - if (!commonValueDeclaration) { - commonValueDeclaration = prop.valueDeclaration; + if (!firstValueDeclaration) { + firstValueDeclaration = prop.valueDeclaration; } - else if (prop.valueDeclaration !== commonValueDeclaration) { + else if (prop.valueDeclaration !== firstValueDeclaration) { hasNonUniformValueDeclaration = true; } declarations = addRange(declarations, prop.declarations); const type = getTypeOfSymbol(prop); - if (first) { - commonType = type; + if (!firstType) { + firstType = type; nameType = prop.nameType; - first = false; } - else { - if (type !== commonType) { - checkFlags |= CheckFlags.HasNonUniformType; - } + else if (type !== firstType) { + checkFlags |= CheckFlags.HasNonUniformType; + } + if (isLiteralType(type)) { + checkFlags |= CheckFlags.HasLiteralType; } propTypes.push(type); } addRange(propTypes, indexTypes); const result = createSymbol(SymbolFlags.Property | commonFlags, name, syntheticFlag | checkFlags); result.containingType = containingType; - if (!hasNonUniformValueDeclaration && commonValueDeclaration) { - result.valueDeclaration = commonValueDeclaration; + if (!hasNonUniformValueDeclaration && firstValueDeclaration) { + result.valueDeclaration = firstValueDeclaration; } result.declarations = declarations!; result.nameType = nameType; @@ -7928,22 +7968,17 @@ namespace ts { const numTypeArguments = length(typeArguments); if (isJavaScriptImplicitAny || (numTypeArguments >= minTypeArgumentCount && numTypeArguments <= numTypeParameters)) { const result = typeArguments ? typeArguments.slice() : []; - - // Map an unsatisfied type parameter with a default type. - // If a type parameter does not have a default type, or if the default type - // is a forward reference, the empty object type is used. - const baseDefaultType = getDefaultTypeArgumentType(isJavaScriptImplicitAny); - const circularityMapper = createTypeMapper(typeParameters!, map(typeParameters!, () => baseDefaultType)); + // Map invalid forward references in default types to the error type for (let i = numTypeArguments; i < numTypeParameters; i++) { - result[i] = instantiateType(getConstraintFromTypeParameter(typeParameters![i]) || baseDefaultType, circularityMapper); + result[i] = errorType; } + const baseDefaultType = getDefaultTypeArgumentType(isJavaScriptImplicitAny); for (let i = numTypeArguments; i < numTypeParameters; i++) { - const mapper = createTypeMapper(typeParameters!, result); let defaultType = getDefaultFromTypeParameter(typeParameters![i]); if (isJavaScriptImplicitAny && defaultType && isTypeIdenticalTo(defaultType, emptyObjectType)) { defaultType = anyType; } - result[i] = defaultType ? instantiateType(defaultType, mapper) : baseDefaultType; + result[i] = defaultType ? instantiateType(defaultType, createTypeMapper(typeParameters!, result)) : baseDefaultType; } result.length = typeParameters!.length; return result; @@ -9025,7 +9060,7 @@ namespace ts { if (arity) { typeParameters = new Array(arity); for (let i = 0; i < arity; i++) { - const typeParameter = typeParameters[i] = createType(TypeFlags.TypeParameter); + const typeParameter = typeParameters[i] = createTypeParameter(); if (i < maxLength) { const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), "" + i as __String); property.type = typeParameter; @@ -9046,7 +9081,7 @@ namespace ts { type.instantiations.set(getTypeListId(type.typeParameters), type); type.target = type; type.typeArguments = type.typeParameters; - type.thisType = createType(TypeFlags.TypeParameter); + type.thisType = createTypeParameter(); type.thisType.isThisType = true; type.thisType.constraint = type; type.declaredProperties = properties; @@ -9947,7 +9982,7 @@ namespace ts { if (checkType === wildcardType || extendsType === wildcardType) { return wildcardType; } - const checkTypeInstantiable = maybeTypeOfKind(checkType, TypeFlags.Instantiable); + const checkTypeInstantiable = maybeTypeOfKind(checkType, TypeFlags.Instantiable | TypeFlags.GenericMappedType); let combinedMapper: TypeMapper | undefined; if (root.inferTypeParameters) { const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None); @@ -9962,7 +9997,7 @@ namespace ts { // Instantiate the extends type including inferences for 'infer T' type parameters const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType; // We attempt to resolve the conditional type only when the check and extends types are non-generic - if (!checkTypeInstantiable && !maybeTypeOfKind(inferredExtendsType, TypeFlags.Instantiable)) { + if (!checkTypeInstantiable && !maybeTypeOfKind(inferredExtendsType, TypeFlags.Instantiable | TypeFlags.GenericMappedType)) { if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) { return instantiateType(root.trueType, mapper); } @@ -9974,14 +10009,15 @@ namespace ts { // types with type parameters mapped to the wildcard type, the most permissive instantiations // possible (the wildcard type is assignable to and from all types). If those are not related, // then no instatiations will be and we can just return the false branch type. - if (!isTypeAssignableTo(getWildcardInstantiation(checkType), getWildcardInstantiation(inferredExtendsType))) { + if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { return instantiateType(root.falseType, mapper); } - // Return trueType for a definitely true extends check. The definitely assignable relation excludes - // type variable constraints from consideration. Without the definitely assignable relation, the type + // Return trueType for a definitely true extends check. We check instantiations of the two + // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter + // that has no constraint. This ensures that, for example, the type // type Foo = T extends { x: string } ? string : number - // would immediately resolve to 'string' instead of being deferred. - if (checkTypeRelatedTo(checkType, inferredExtendsType, definitelyAssignableRelation, /*errorNode*/ undefined)) { + // doesn't immediately resolve to 'string' instead of being deferred. + if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) { return instantiateType(root.trueType, combinedMapper || mapper); } } @@ -10587,13 +10623,20 @@ namespace ts { return t => t === source ? target : baseMapper(t); } - function wildcardMapper(type: Type) { + function permissiveMapper(type: Type) { return type.flags & TypeFlags.TypeParameter ? wildcardType : type; } + function getRestrictiveTypeParameter(tp: TypeParameter) { + return !tp.constraint ? tp : tp.restrictiveInstantiation || (tp.restrictiveInstantiation = createTypeParameter(tp.symbol)); + } + + function restrictiveMapper(type: Type) { + return type.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(type) : type; + } + function cloneTypeParameter(typeParameter: TypeParameter): TypeParameter { - const result = createType(TypeFlags.TypeParameter); - result.symbol = typeParameter.symbol; + const result = createTypeParameter(typeParameter.symbol); result.target = typeParameter; return result; } @@ -10945,9 +10988,14 @@ namespace ts { return type; } - function getWildcardInstantiation(type: Type) { + function getPermissiveInstantiation(type: Type) { return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type : - type.wildcardInstantiation || (type.wildcardInstantiation = instantiateType(type, wildcardMapper)); + type.permissiveInstantiation || (type.permissiveInstantiation = instantiateType(type, permissiveMapper)); + } + + function getRestrictiveInstantiation(type: Type) { + return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type : + type.restrictiveInstantiation || (type.restrictiveInstantiation = instantiateType(type, restrictiveMapper)); } function instantiateIndexInfo(info: IndexInfo | undefined, mapper: TypeMapper): IndexInfo | undefined { @@ -11016,8 +11064,7 @@ namespace ts { function hasContextSensitiveReturnExpression(node: FunctionLikeDeclaration) { // TODO(anhans): A block should be context-sensitive if it has a context-sensitive return value. - const body = node.body!; - return body.kind === SyntaxKind.Block ? false : isContextSensitive(body); + return !!node.body && node.body.kind !== SyntaxKind.Block && isContextSensitive(node.body); } function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | ArrowFunction | MethodDeclaration { @@ -11149,7 +11196,7 @@ namespace ts { case SyntaxKind.ArrayLiteralExpression: return elaborateArrayLiteral(node as ArrayLiteralExpression, source, target, relation); case SyntaxKind.JsxAttributes: - return elaborateJsxAttributes(node as JsxAttributes, source, target, relation); + return elaborateJsxComponents(node as JsxAttributes, source, target, relation); case SyntaxKind.ArrowFunction: return elaborateArrowFunction(node as ArrowFunction, source, target, relation); } @@ -11289,8 +11336,113 @@ namespace ts { } } - function elaborateJsxAttributes(node: JsxAttributes, source: Type, target: Type, relation: Map) { - return elaborateElementwise(generateJsxAttributes(node), source, target, relation); + function *generateJsxChildren(node: JsxElement, getInvalidTextDiagnostic: () => DiagnosticMessage): ElaborationIterator { + if (!length(node.children)) return; + let memberOffset = 0; + for (let i = 0; i < node.children.length; i++) { + const child = node.children[i]; + const nameType = getLiteralType(i - memberOffset); + const elem = getElaborationElementForJsxChild(child, nameType, getInvalidTextDiagnostic); + if (elem) { + yield elem; + } + else { + memberOffset++; + } + } + } + + function getElaborationElementForJsxChild(child: JsxChild, nameType: LiteralType, getInvalidTextDiagnostic: () => DiagnosticMessage) { + switch (child.kind) { + case SyntaxKind.JsxExpression: + // child is of the type of the expression + return { errorNode: child, innerExpression: child.expression, nameType }; + case SyntaxKind.JsxText: + if (child.containsOnlyWhiteSpaces) { + break; // Whitespace only jsx text isn't real jsx text + } + // child is a string + return { errorNode: child, innerExpression: undefined, nameType, errorMessage: getInvalidTextDiagnostic() }; + case SyntaxKind.JsxElement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxFragment: + // child is of type JSX.Element + return { errorNode: child, innerExpression: child, nameType }; + default: + return Debug.assertNever(child, "Found invalid jsx child"); + } + } + + function elaborateJsxComponents(node: JsxAttributes, source: Type, target: Type, relation: Map) { + let result = elaborateElementwise(generateJsxAttributes(node), source, target, relation); + let invalidTextDiagnostic: DiagnosticMessage | undefined; + if (isJsxOpeningElement(node.parent) && isJsxElement(node.parent.parent)) { + const containingElement = node.parent.parent; + const childPropName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node)); + const childrenPropName = childPropName === undefined ? "children" : unescapeLeadingUnderscores(childPropName); + const childrenNameType = getLiteralType(childrenPropName); + const childrenTargetType = getIndexedAccessType(target, childrenNameType); + const validChildren = filter(containingElement.children, i => !isJsxText(i) || !i.containsOnlyWhiteSpaces); + if (!length(validChildren)) { + return result; + } + const moreThanOneRealChildren = length(validChildren) > 1; + const arrayLikeTargetParts = filterType(childrenTargetType, isArrayOrTupleLikeType); + const nonArrayLikeTargetParts = filterType(childrenTargetType, t => !isArrayOrTupleLikeType(t)); + if (moreThanOneRealChildren) { + if (arrayLikeTargetParts !== neverType) { + const realSource = createTupleType(checkJsxChildren(containingElement, CheckMode.Normal)); + result = elaborateElementwise(generateJsxChildren(containingElement, getInvalidTextualChildDiagnostic), realSource, arrayLikeTargetParts, relation) || result; + } + else if (!isTypeRelatedTo(getIndexedAccessType(source, childrenNameType), childrenTargetType, relation)) { + // arity mismatch + result = true; + error( + containingElement.openingElement.tagName, + Diagnostics.This_JSX_tag_s_0_prop_expects_a_single_child_of_type_1_but_multiple_children_were_provided, + childrenPropName, + typeToString(childrenTargetType) + ); + } + } + else { + if (nonArrayLikeTargetParts !== neverType) { + const child = validChildren[0]; + const elem = getElaborationElementForJsxChild(child, childrenNameType, getInvalidTextualChildDiagnostic); + if (elem) { + result = elaborateElementwise( + (function*() { yield elem; })(), + source, + target, + relation + ) || result; + } + } + else if (!isTypeRelatedTo(getIndexedAccessType(source, childrenNameType), childrenTargetType, relation)) { + // arity mismatch + result = true; + error( + containingElement.openingElement.tagName, + Diagnostics.This_JSX_tag_s_0_prop_expects_type_1_which_requires_multiple_children_but_only_a_single_child_was_provided, + childrenPropName, + typeToString(childrenTargetType) + ); + } + } + } + return result; + + function getInvalidTextualChildDiagnostic() { + if (!invalidTextDiagnostic) { + const tagNameText = getTextOfNode(node.parent.tagName); + const childPropName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node)); + const childrenPropName = childPropName === undefined ? "children" : unescapeLeadingUnderscores(childPropName); + const childrenTargetType = getIndexedAccessType(target, getLiteralType(childrenPropName)); + const diagnostic = Diagnostics._0_components_don_t_accept_text_as_child_elements_Text_in_JSX_has_the_type_string_but_the_expected_type_of_1_is_2; + invalidTextDiagnostic = { ...diagnostic, key: "!!ALREADY FORMATTED!!", message: formatMessage(/*_dummy*/ undefined, diagnostic, tagNameText, childrenPropName, typeToString(childrenTargetType)) }; + } + return invalidTextDiagnostic; + } } function *generateLimitedTupleElements(node: ArrayLiteralExpression, target: Type): ElaborationIterator { @@ -11620,8 +11772,7 @@ namespace ts { if (s & TypeFlags.Undefined && (!strictNullChecks || t & (TypeFlags.Undefined | TypeFlags.Void))) return true; if (s & TypeFlags.Null && (!strictNullChecks || t & TypeFlags.Null)) return true; if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive) return true; - if (s & TypeFlags.UniqueESSymbol || t & TypeFlags.UniqueESSymbol) return false; - if (relation === assignableRelation || relation === definitelyAssignableRelation || relation === comparableRelation) { + if (relation === assignableRelation || relation === comparableRelation) { if (s & TypeFlags.Any) return true; // Type number or any numeric literal type is assignable to any numeric enum type or any // numeric enum literal type. This rule exists for backwards compatibility reasons because @@ -11813,7 +11964,7 @@ namespace ts { target = (target).regularType; } if (source.flags & TypeFlags.Substitution) { - source = relation === definitelyAssignableRelation ? (source).typeVariable : (source).substitute; + source = (source).substitute; } if (target.flags & TypeFlags.Substitution) { target = (target).typeVariable; @@ -11999,7 +12150,7 @@ namespace ts { } if (isExcessPropertyCheckTarget(target)) { const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); - if ((relation === assignableRelation || relation === definitelyAssignableRelation || relation === comparableRelation) && + if ((relation === assignableRelation || relation === comparableRelation) && (isTypeSubsetOf(globalObjectType, target) || (!isComparingJsxAttributes && isEmptyObjectType(target)))) { return false; } @@ -12402,24 +12553,22 @@ namespace ts { } // A type S is assignable to keyof T if S is assignable to keyof C, where C is the // simplified form of T or, if T doesn't simplify, the constraint of T. - if (relation !== definitelyAssignableRelation) { - const simplified = getSimplifiedType((target).type); - const constraint = simplified !== (target).type ? simplified : getConstraintOfType((target).type); - if (constraint) { - // We require Ternary.True here such that circular constraints don't cause - // false positives. For example, given 'T extends { [K in keyof T]: string }', - // 'keyof T' has itself as its constraint and produces a Ternary.Maybe when - // related to other types. - if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), reportErrors) === Ternary.True) { - return Ternary.True; - } + const simplified = getSimplifiedType((target).type); + const constraint = simplified !== (target).type ? simplified : getConstraintOfType((target).type); + if (constraint) { + // We require Ternary.True here such that circular constraints don't cause + // false positives. For example, given 'T extends { [K in keyof T]: string }', + // 'keyof T' has itself as its constraint and produces a Ternary.Maybe when + // related to other types. + if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), reportErrors) === Ternary.True) { + return Ternary.True; } } } else if (target.flags & TypeFlags.IndexedAccess) { // A type S is related to a type T[K], where T and K aren't both type variables, if S is related to C, // where C is the base constraint of T[K] - if (relation !== identityRelation && relation !== definitelyAssignableRelation && + if (relation !== identityRelation && !(isGenericObjectType((target).objectType) && isGenericIndexType((target).indexType))) { const constraint = getBaseConstraintOfType(target); if (constraint && constraint !== target) { @@ -12462,26 +12611,24 @@ namespace ts { return result; } } - if (relation !== definitelyAssignableRelation) { - const constraint = getConstraintOfType(source); - if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) { - // A type variable with no constraint is not related to the non-primitive object type. - if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) { - errorInfo = saveErrorInfo; - return result; - } - } - // hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed - else if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, isIntersectionConstituent)) { - errorInfo = saveErrorInfo; - return result; - } - // slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example - else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent)) { + const constraint = getConstraintOfType(source); + if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) { + // A type variable with no constraint is not related to the non-primitive object type. + if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) { errorInfo = saveErrorInfo; return result; } } + // hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed + else if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, isIntersectionConstituent)) { + errorInfo = saveErrorInfo; + return result; + } + // slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example + else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent)) { + errorInfo = saveErrorInfo; + return result; + } } else if (source.flags & TypeFlags.Index) { if (result = isRelatedTo(keyofConstraintType, target, reportErrors)) { @@ -12505,7 +12652,7 @@ namespace ts { } } } - else if (relation !== definitelyAssignableRelation) { + else { const distributiveConstraint = getConstraintOfDistributiveConditionalType(source); if (distributiveConstraint) { if (result = isRelatedTo(distributiveConstraint, target, reportErrors)) { @@ -12536,9 +12683,6 @@ namespace ts { } return Ternary.False; } - if (relation === definitelyAssignableRelation && isGenericMappedType(source)) { - return Ternary.False; - } const sourceIsPrimitive = !!(source.flags & TypeFlags.Primitive); if (relation !== identityRelation) { source = getApparentType(source); @@ -13438,6 +13582,10 @@ namespace ts { return isTupleType(type) || !!getPropertyOfType(type, "0" as __String); } + function isArrayOrTupleLikeType(type: Type): boolean { + return isArrayLikeType(type) || isTupleLikeType(type); + } + function getTupleElementType(type: Type, index: number) { const propType = getTypeOfPropertyOfType(type, "" + index as __String); if (propType) { @@ -14741,6 +14889,9 @@ namespace ts { return symbol !== unknownSymbol ? (isConstraintPosition(node) ? "@" : "") + getSymbolId(symbol) : undefined; case SyntaxKind.ThisKeyword: return "0"; + case SyntaxKind.NonNullExpression: + case SyntaxKind.ParenthesizedExpression: + return getFlowCacheKey((node).expression); case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: const propName = getAccessedPropertyName(node); @@ -14753,6 +14904,11 @@ namespace ts { } function isMatchingReference(source: Node, target: Node): boolean { + switch (target.kind) { + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.NonNullExpression: + return isMatchingReference(source, (target as NonNullExpression | ParenthesizedExpression).expression); + } switch (source.kind) { case SyntaxKind.Identifier: return target.kind === SyntaxKind.Identifier && getResolvedSymbol(source) === getResolvedSymbol(target) || @@ -14762,6 +14918,9 @@ namespace ts { return target.kind === SyntaxKind.ThisKeyword; case SyntaxKind.SuperKeyword: return target.kind === SyntaxKind.SuperKeyword; + case SyntaxKind.NonNullExpression: + case SyntaxKind.ParenthesizedExpression: + return isMatchingReference((source as NonNullExpression | ParenthesizedExpression).expression, target); case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: return isAccessExpression(target) && @@ -14814,17 +14973,8 @@ namespace ts { } function isDiscriminantType(type: Type): boolean { - if (type.flags & TypeFlags.Union) { - if (type.flags & (TypeFlags.Boolean | TypeFlags.EnumLiteral)) { - return true; - } - let combined = 0; - for (const t of (type).types) combined |= t.flags; - if (combined & TypeFlags.Unit && !(combined & TypeFlags.Instantiable)) { - return true; - } - } - return false; + return !!(type.flags & TypeFlags.Union && + (type.flags & (TypeFlags.Boolean | TypeFlags.EnumLiteral) || !isGenericIndexType(type))); } function isDiscriminantProperty(type: Type | undefined, name: __String) { @@ -14832,7 +14982,9 @@ namespace ts { const prop = getUnionOrIntersectionProperty(type, name); if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) { if ((prop).isDiscriminantProperty === undefined) { - (prop).isDiscriminantProperty = !!((prop).checkFlags & CheckFlags.HasNonUniformType) && isDiscriminantType(getTypeOfSymbol(prop)); + (prop).isDiscriminantProperty = + ((prop).checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant && + isDiscriminantType(getTypeOfSymbol(prop)); } return !!(prop).isDiscriminantProperty; } @@ -16609,7 +16761,7 @@ namespace ts { function checkThisBeforeSuper(node: Node, container: Node, diagnosticMessage: DiagnosticMessage) { const containingClassDecl = container.parent; - const baseTypeNode = getEffectiveBaseTypeNode(containingClassDecl); + const baseTypeNode = getClassExtendsHeritageElement(containingClassDecl); // If a containing class does not have extends clause or the class extends null // skip checking whether super statement is called before "this" accessing. @@ -16958,7 +17110,7 @@ namespace ts { // at this point the only legal case for parent is ClassLikeDeclaration const classLikeDeclaration = container.parent; - if (!getEffectiveBaseTypeNode(classLikeDeclaration)) { + if (!getClassExtendsHeritageElement(classLikeDeclaration)) { error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class); return errorType; } @@ -17449,11 +17601,23 @@ namespace ts { return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined; } - function getContextualTypeForChildJsxExpression(node: JsxElement) { + function getContextualTypeForChildJsxExpression(node: JsxElement, child: JsxChild) { const attributesType = getApparentTypeOfContextualType(node.openingElement.tagName); // JSX expression is in children of JSX Element, we will look for an "children" atttribute (we get the name from JSX.ElementAttributesProperty) const jsxChildrenPropertyName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node)); - return attributesType && !isTypeAny(attributesType) && jsxChildrenPropertyName && jsxChildrenPropertyName !== "" ? getTypeOfPropertyOfContextualType(attributesType, jsxChildrenPropertyName) : undefined; + if (!(attributesType && !isTypeAny(attributesType) && jsxChildrenPropertyName && jsxChildrenPropertyName !== "")) { + return undefined; + } + const childIndex = node.children.indexOf(child); + const childFieldType = getTypeOfPropertyOfContextualType(attributesType, jsxChildrenPropertyName); + return childFieldType && mapType(childFieldType, t => { + if (isArrayLikeType(t)) { + return getIndexedAccessType(t, getLiteralType(childIndex)); + } + else { + return t; + } + }, /*noReductions*/ true); } function getContextualTypeForJsxExpression(node: JsxExpression): Type | undefined { @@ -17461,7 +17625,7 @@ namespace ts { return isJsxAttributeLike(exprParent) ? getContextualType(node) : isJsxElement(exprParent) - ? getContextualTypeForChildJsxExpression(exprParent) + ? getContextualTypeForChildJsxExpression(exprParent, node) : undefined; } @@ -19263,8 +19427,8 @@ namespace ts { } } - function isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode, type: Type, property: Symbol): boolean { - return isValidPropertyAccessWithType(node, node.kind !== SyntaxKind.ImportType && node.expression.kind === SyntaxKind.SuperKeyword, property.escapedName, type) + function isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode | QualifiedName, type: Type, property: Symbol): boolean { + return isValidPropertyAccessWithType(node, node.kind === SyntaxKind.PropertyAccessExpression && node.expression.kind === SyntaxKind.SuperKeyword, property.escapedName, type) && (!(property.flags & SymbolFlags.Method) || isValidMethodAccess(property, type)); } function isValidMethodAccess(method: Symbol, actualThisType: Type): boolean { @@ -20490,9 +20654,9 @@ namespace ts { * If FuncExpr is of type Any, or of an object type that has no call or construct signatures * but is a subtype of the Function interface, the call is an untyped function call. */ - function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number) { + function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number): boolean { // We exclude union types because we may have a union of function types that happen to have no common signatures. - return isTypeAny(funcType) || isTypeAny(apparentFuncType) && funcType.flags & TypeFlags.TypeParameter || + return isTypeAny(funcType) || isTypeAny(apparentFuncType) && !!(funcType.flags & TypeFlags.TypeParameter) || !numCallSignatures && !numConstructSignatures && !(apparentFuncType.flags & (TypeFlags.Union | TypeFlags.Never)) && isTypeAssignableTo(funcType, globalFunctionType); } @@ -22810,7 +22974,8 @@ namespace ts { const type = getTypeOfExpression(initializer, /*cache*/ true); const widened = getCombinedNodeFlags(declaration) & NodeFlags.Const || isDeclarationReadonly(declaration) || - isTypeAssertion(initializer) ? type : getWidenedLiteralType(type); + isTypeAssertion(initializer) || + isLiteralOfContextualType(type, getContextualType(initializer)) ? type : getWidenedLiteralType(type); if (isInJSFile(declaration)) { if (widened.flags & TypeFlags.Nullable) { reportImplicitAny(declaration, anyType); @@ -23558,7 +23723,7 @@ namespace ts { // Constructors of classes with no extends clause may not contain super calls, whereas // constructors of derived classes must contain at least one super call somewhere in their function body. const containingClassDecl = node.parent; - if (getEffectiveBaseTypeNode(containingClassDecl)) { + if (getClassExtendsHeritageElement(containingClassDecl)) { captureLexicalThis(node.parent, containingClassDecl); const classExtendsNull = classDeclarationExtendsNull(containingClassDecl); const superCall = getSuperCallInConstructor(node); @@ -24740,9 +24905,17 @@ namespace ts { return; } if (!containsArgumentsReference(decl)) { - error(node.name, - Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name, - idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name)); + if (isQualifiedName(node.name)) { + error(node.name, + Diagnostics.Qualified_name_0_is_not_allowed_without_a_leading_param_object_1, + entityNameToString(node.name), + entityNameToString(node.name.left)); + } + else { + error(node.name, + Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name, + idText(node.name)); + } } else if (findLast(getJSDocTags(decl), isJSDocParameterTag) === node && node.typeExpression && node.typeExpression.type && @@ -25880,7 +26053,7 @@ namespace ts { ? downlevelIteration ? Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator : isIterable - ? Diagnostics.Type_0_is_not_an_array_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators + ? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators : Diagnostics.Type_0_is_not_an_array_type : downlevelIteration ? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator @@ -26465,6 +26638,7 @@ namespace ts { if (produceDiagnostics) { if (node.default) { seenDefault = true; + checkTypeParametersNotReferenced(node.default, typeParameterDeclarations, i); } else if (seenDefault) { error(node, Diagnostics.Required_type_parameters_may_not_follow_optional_type_parameters); @@ -26479,6 +26653,24 @@ namespace ts { } } + /** Check that type parameter defaults only reference previously declared type parameters */ + function checkTypeParametersNotReferenced(root: TypeNode, typeParameters: ReadonlyArray, index: number) { + visit(root); + function visit(node: Node) { + if (node.kind === SyntaxKind.TypeReference) { + const type = getTypeFromTypeReference(node); + if (type.flags & TypeFlags.TypeParameter) { + for (let i = index; i < typeParameters.length; i++) { + if (type.symbol === getSymbolOfNode(typeParameters[i])) { + error(node, Diagnostics.Type_parameter_defaults_can_only_reference_previously_declared_type_parameters); + } + } + } + } + forEachChild(node, visit); + } + } + /** Check that type parameter lists are identical across multiple declarations */ function checkTypeParameterListsIdentical(symbol: Symbol) { if (symbol.declarations.length === 1) { @@ -29932,10 +30124,6 @@ namespace ts { checkGrammarForDisallowedTrailingComma(parameters, Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma); } - if (isBindingPattern(parameter.name)) { - return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern); - } - if (parameter.questionToken) { return grammarErrorOnNode(parameter.questionToken, Diagnostics.A_rest_parameter_cannot_be_optional); } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ee2d3532e50..df477f0d8f1 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1776,7 +1776,7 @@ "category": "Error", "code": 2496 }, - "Module '{0}' resolves to a non-module entity and cannot be imported using this construct.": { + "This module can only be referenced with ECMAScript imports/exports by turning on the '{0}' flag and referencing its default export.": { "category": "Error", "code": 2497 }, @@ -2056,10 +2056,6 @@ "category": "Error", "code": 2567 }, - "Type '{0}' is not an array type. Use compiler option '--downlevelIteration' to allow iterating of iterators.": { - "category": "Error", - "code": 2568 - }, "Type '{0}' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.": { "category": "Error", "code": 2569 @@ -2537,6 +2533,22 @@ "category": "Error", "code": 2743 }, + "Type parameter defaults can only reference previously declared type parameters.": { + "category": "Error", + "code": 2744 + }, + "This JSX tag's '{0}' prop expects type '{1}' which requires multiple children, but only a single child was provided.": { + "category": "Error", + "code": 2745 + }, + "This JSX tag's '{0}' prop expects a single child of type '{1}', but multiple children were provided.": { + "category": "Error", + "code": 2746 + }, + "'{0}' components don't accept text as child elements. Text in JSX has the type 'string', but the expected type of '{1}' is '{2}'.": { + "category": "Error", + "code": 2747 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", @@ -4262,6 +4274,10 @@ "category": "Error", "code": 8031 }, + "Qualified name '{0}' is not allowed without a leading '@param {object} {1}'.": { + "category": "Error", + "code": 8032 + }, "Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": { "category": "Error", "code": 9002 @@ -4807,5 +4823,9 @@ "Add names to all parameters without names": { "category": "Message", "code": 95073 + }, + "Enable the 'experimentalDecorators' option in your configuration file": { + "category": "Message", + "code": 95074 } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 9c09ed79a77..395f6448890 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2561,6 +2561,7 @@ namespace ts { function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { writePunctuation("<"); emitJsxTagName(node.tagName); + emitTypeArguments(node, node.typeArguments); writeSpace(); emit(node.attributes); writePunctuation("/>"); @@ -2577,6 +2578,7 @@ namespace ts { if (isJsxOpeningElement(node)) { emitJsxTagName(node.tagName); + emitTypeArguments(node, node.typeArguments); if (node.attributes.properties && node.attributes.properties.length > 0) { writeSpace(); } @@ -4389,7 +4391,7 @@ namespace ts { return; } - const { line: sourceLine, character: sourceCharacter } = getLineAndCharacterOfPosition(currentSourceFile!, pos); + const { line: sourceLine, character: sourceCharacter } = getLineAndCharacterOfPosition(sourceMapSource, pos); sourceMapGenerator!.addMapping( writer.getLine(), writer.getColumn(), diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f976f77ea98..53e45505014 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -7774,17 +7774,18 @@ namespace ts { const libReferenceDirectives = context.libReferenceDirectives; forEach(toArray(entryOrList), (arg: PragmaPseudoMap["reference"]) => { // TODO: GH#18217 + const { types, lib, path } = arg!.arguments; if (arg!.arguments["no-default-lib"]) { context.hasNoDefaultLib = true; } - else if (arg!.arguments.types) { - typeReferenceDirectives.push({ pos: arg!.arguments.types!.pos, end: arg!.arguments.types!.end, fileName: arg!.arguments.types!.value }); + else if (types) { + typeReferenceDirectives.push({ pos: types.pos, end: types.end, fileName: types.value }); } - else if (arg!.arguments.lib) { - libReferenceDirectives.push({ pos: arg!.arguments.lib!.pos, end: arg!.arguments.lib!.end, fileName: arg!.arguments.lib!.value }); + else if (lib) { + libReferenceDirectives.push({ pos: lib.pos, end: lib.end, fileName: lib.value }); } - else if (arg!.arguments.path) { - referencedFiles.push({ pos: arg!.arguments.path!.pos, end: arg!.arguments.path!.end, fileName: arg!.arguments.path!.value }); + else if (path) { + referencedFiles.push({ pos: path.pos, end: path.end, fileName: path.value }); } else { reportDiagnostic(arg!.range.pos, arg!.range.end - arg!.range.pos, Diagnostics.Invalid_reference_directive_syntax); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 080b17b8cbe..271bdab976e 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2227,8 +2227,9 @@ namespace ts { processReferencedFiles(file, isDefaultLib); processTypeReferenceDirectives(file); } - - processLibReferenceDirectives(file); + if (!options.noLib) { + processLibReferenceDirectives(file); + } modulesWithElidedImports.set(file.path, false); processImportedModules(file); @@ -2315,8 +2316,10 @@ namespace ts { processReferencedFiles(file, isDefaultLib); processTypeReferenceDirectives(file); } + if (!options.noLib) { + processLibReferenceDirectives(file); + } - processLibReferenceDirectives(file); // always process imported modules to record module name resolutions processImportedModules(file); diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index b54cf5bb858..d03c3b8d30b 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -634,7 +634,10 @@ namespace ts { if (!isLateVisibilityPaintedStatement(i)) { return Debug.fail(`Late replaced statement was found which is not handled by the declaration transformer!: ${(ts as any).SyntaxKind ? (ts as any).SyntaxKind[(i as any).kind] : (i as any).kind}`); } + const priorNeedsDeclare = needsDeclare; + needsDeclare = i.parent && isSourceFile(i.parent) && !(isExternalModule(i.parent) && isBundledEmit); const result = transformTopLevelDeclaration(i, /*privateDeclaration*/ true); + needsDeclare = priorNeedsDeclare; lateStatementReplacementMap.set("" + getOriginalNodeId(i), result); } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 66f635a31e9..f97cec89cdf 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -780,7 +780,7 @@ namespace ts { enableSubstitutionsForBlockScopedBindings(); } - const extendsClauseElement = getEffectiveBaseTypeNode(node); + const extendsClauseElement = getClassExtendsHeritageElement(node); const classFunction = createFunctionExpression( /*modifiers*/ undefined, /*asteriskToken*/ undefined, @@ -1350,8 +1350,8 @@ namespace ts { * part of a constructor declaration with a * synthesized call to `super` */ - function shouldAddRestParameter(node: ParameterDeclaration | undefined, inConstructorWithSynthesizedSuper: boolean) { - return node && node.dotDotDotToken && node.name.kind === SyntaxKind.Identifier && !inConstructorWithSynthesizedSuper; + function shouldAddRestParameter(node: ParameterDeclaration | undefined, inConstructorWithSynthesizedSuper: boolean): node is ParameterDeclaration { + return !!(node && node.dotDotDotToken && !inConstructorWithSynthesizedSuper); } /** @@ -1370,11 +1370,11 @@ namespace ts { } // `declarationName` is the name of the local declaration for the parameter. - const declarationName = getMutableClone(parameter!.name); + const declarationName = parameter.name.kind === SyntaxKind.Identifier ? getMutableClone(parameter.name) : createTempVariable(/*recordTempVariable*/ undefined); setEmitFlags(declarationName, EmitFlags.NoSourceMap); // `expressionName` is the name of the parameter used in expressions. - const expressionName = getSynthesizedClone(parameter!.name); + const expressionName = parameter.name.kind === SyntaxKind.Identifier ? getSynthesizedClone(parameter.name) : declarationName; const restIndex = node.parameters.length - 1; const temp = createLoopVariable(); @@ -1439,6 +1439,24 @@ namespace ts { setEmitFlags(forStatement, EmitFlags.CustomPrologue); startOnNewLine(forStatement); statements.push(forStatement); + + if (parameter.name.kind !== SyntaxKind.Identifier) { + // do the actual destructuring of the rest parameter if necessary + statements.push( + setEmitFlags( + setTextRange( + createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList( + flattenDestructuringBinding(parameter, visitor, context, FlattenLevel.All, expressionName), + ) + ), + parameter + ), + EmitFlags.CustomPrologue + ) + ); + } } /** diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 700c0c8af25..a22c6df8d06 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1898,6 +1898,7 @@ namespace ts { case SyntaxKind.StringLiteral: return createIdentifier("String"); + case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.NumericLiteral: return createIdentifier("Number"); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a4285cfd775..a1b4f172c6b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3037,8 +3037,10 @@ namespace ts { /* @internal */ typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker): TypeNode | undefined; // tslint:disable-line unified-signatures /** Note that the resulting nodes cannot be checked. */ signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration & {typeArguments?: NodeArray} | undefined; + /* @internal */ signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker): SignatureDeclaration & {typeArguments?: NodeArray} | undefined; // tslint:disable-line unified-signatures /** Note that the resulting nodes cannot be checked. */ indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): IndexSignatureDeclaration | undefined; + /* @internal */ indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker): IndexSignatureDeclaration | undefined; // tslint:disable-line unified-signatures /** Note that the resulting nodes cannot be checked. */ symbolToEntityName(symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): EntityName | undefined; /** Note that the resulting nodes cannot be checked. */ @@ -3108,7 +3110,7 @@ namespace ts { getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined; isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: string): boolean; /** Exclude accesses to private properties or methods with a `this` parameter that `type` doesn't satisfy. */ - /* @internal */ isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode, type: Type, property: Symbol): boolean; + /* @internal */ isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode | QualifiedName, type: Type, property: Symbol): boolean; /** Follow all aliases to get the original symbol. */ getAliasedSymbol(symbol: Symbol): Symbol; /** Follow a *single* alias to get the immediately aliased symbol. */ @@ -3677,15 +3679,17 @@ namespace ts { Readonly = 1 << 3, // Readonly transient symbol Partial = 1 << 4, // Synthetic property present in some but not all constituents HasNonUniformType = 1 << 5, // Synthetic property with non-uniform type in constituents - ContainsPublic = 1 << 6, // Synthetic property with public constituent(s) - ContainsProtected = 1 << 7, // Synthetic property with protected constituent(s) - ContainsPrivate = 1 << 8, // Synthetic property with private constituent(s) - ContainsStatic = 1 << 9, // Synthetic property with static constituent(s) - Late = 1 << 10, // Late-bound symbol for a computed property with a dynamic name - ReverseMapped = 1 << 11, // Property of reverse-inferred homomorphic mapped type - OptionalParameter = 1 << 12, // Optional parameter - RestParameter = 1 << 13, // Rest parameter - Synthetic = SyntheticProperty | SyntheticMethod + HasLiteralType = 1 << 6, // Synthetic property with at least one literal type in constituents + ContainsPublic = 1 << 7, // Synthetic property with public constituent(s) + ContainsProtected = 1 << 8, // Synthetic property with protected constituent(s) + ContainsPrivate = 1 << 9, // Synthetic property with private constituent(s) + ContainsStatic = 1 << 10, // Synthetic property with static constituent(s) + Late = 1 << 11, // Late-bound symbol for a computed property with a dynamic name + ReverseMapped = 1 << 12, // Property of reverse-inferred homomorphic mapped type + OptionalParameter = 1 << 13, // Optional parameter + RestParameter = 1 << 14, // Rest parameter + Synthetic = SyntheticProperty | SyntheticMethod, + Discriminant = HasNonUniformType | HasLiteralType } /* @internal */ @@ -3914,7 +3918,9 @@ namespace ts { aliasTypeArguments?: ReadonlyArray; // Alias type arguments (if any) /* @internal */ aliasTypeArgumentsContainsMarker?: boolean; // Alias type arguments (if any) /* @internal */ - wildcardInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type + permissiveInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type + /* @internal */ + restrictiveInstantiation?: Type; // Instantiation with type parameters mapped to unconstrained form /* @internal */ immediateBaseConstraint?: Type; // Immediate base constraint cache } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ca83129bb3f..1613c5036e9 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -888,7 +888,7 @@ namespace ts { } const isMissing = nodeIsMissing(errorNode); - const pos = isMissing + const pos = isMissing || isJsxText(node) ? errorNode.pos : skipTrivia(sourceFile.text, errorNode.pos); @@ -2121,7 +2121,7 @@ namespace ts { : undefined; } - function getSingleInitializerOfVariableStatementOrPropertyDeclaration(node: Node): Expression | undefined { + export function getSingleInitializerOfVariableStatementOrPropertyDeclaration(node: Node): Expression | undefined { switch (node.kind) { case SyntaxKind.VariableStatement: const v = getSingleVariableOfVariableStatement(node); @@ -7026,6 +7026,7 @@ namespace ts { }; } + export function formatMessage(_dummy: any, message: DiagnosticMessage, ...args: (string | number | undefined)[]): string; export function formatMessage(_dummy: any, message: DiagnosticMessage): string { let text = getLocaleSpecificMessage(message); diff --git a/src/harness/client.ts b/src/harness/client.ts index 27365e3ecf1..1c7c0adb834 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -384,7 +384,8 @@ namespace ts.server { return notImplemented(); } - getRenameInfo(fileName: string, position: number, findInStrings?: boolean, findInComments?: boolean): RenameInfo { + getRenameInfo(fileName: string, position: number, _options?: RenameInfoOptions, findInStrings?: boolean, findInComments?: boolean): RenameInfo { + // Not passing along 'options' because server should already have those from the 'configure' command const args: protocol.RenameRequestArgs = { ...this.createFileLocationRequestArgs(fileName, position), findInStrings, findInComments }; const request = this.processRequest(CommandNames.Rename, args); @@ -428,7 +429,7 @@ namespace ts.server { this.lastRenameEntry.inputs.position !== position || this.lastRenameEntry.inputs.findInStrings !== findInStrings || this.lastRenameEntry.inputs.findInComments !== findInComments) { - this.getRenameInfo(fileName, position, findInStrings, findInComments); + this.getRenameInfo(fileName, position, { allowRenameOfImportPath: true }, findInStrings, findInComments); } return this.lastRenameEntry!.locations; diff --git a/src/harness/compiler.ts b/src/harness/compiler.ts index 532a2d65578..70bda90ef32 100644 --- a/src/harness/compiler.ts +++ b/src/harness/compiler.ts @@ -183,8 +183,9 @@ namespace compiler { } public getSourceMapRecord(): string | undefined { - if (this.result!.sourceMaps && this.result!.sourceMaps!.length > 0) { - return Harness.SourceMapRecorder.getSourceMapRecord(this.result!.sourceMaps!, this.program!, Array.from(this.js.values()).filter(d => !ts.fileExtensionIs(d.file, ts.Extension.Json)), Array.from(this.dts.values())); + const maps = this.result!.sourceMaps; + if (maps && maps.length > 0) { + return Harness.SourceMapRecorder.getSourceMapRecord(maps, this.program!, Array.from(this.js.values()).filter(d => !ts.fileExtensionIs(d.file, ts.Extension.Json)), Array.from(this.dts.values())); } } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index db1c326a504..1276de7de44 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1308,8 +1308,8 @@ Actual: ${stringify(fullActual)}`); } } - public verifyRenameInfoSucceeded(displayName: string | undefined, fullDisplayName: string | undefined, kind: string | undefined, kindModifiers: string | undefined, fileToRename: string | undefined, expectedRange: Range | undefined): void { - const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition); + public verifyRenameInfoSucceeded(displayName: string | undefined, fullDisplayName: string | undefined, kind: string | undefined, kindModifiers: string | undefined, fileToRename: string | undefined, expectedRange: Range | undefined, renameInfoOptions: ts.RenameInfoOptions | undefined): void { + const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition, renameInfoOptions || { allowRenameOfImportPath: true }); if (!renameInfo.canRename) { throw this.raiseError("Rename did not succeed"); } @@ -1334,8 +1334,9 @@ Actual: ${stringify(fullActual)}`); } } - public verifyRenameInfoFailed(message?: string) { - const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition); + public verifyRenameInfoFailed(message?: string, allowRenameOfImportPath?: boolean) { + allowRenameOfImportPath = allowRenameOfImportPath === undefined ? true : allowRenameOfImportPath; + const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition, { allowRenameOfImportPath }); if (renameInfo.canRename) { throw this.raiseError("Rename was expected to fail"); } @@ -4091,12 +4092,12 @@ namespace FourSlashInterface { this.state.verifySemanticClassifications(classifications); } - public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, expectedRange?: FourSlash.Range) { - this.state.verifyRenameInfoSucceeded(displayName, fullDisplayName, kind, kindModifiers, fileToRename, expectedRange); + public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, expectedRange?: FourSlash.Range, options?: ts.RenameInfoOptions) { + this.state.verifyRenameInfoSucceeded(displayName, fullDisplayName, kind, kindModifiers, fileToRename, expectedRange, options); } - public renameInfoFailed(message?: string) { - this.state.verifyRenameInfoFailed(message); + public renameInfoFailed(message?: string, allowRenameOfImportPath?: boolean) { + this.state.verifyRenameInfoFailed(message, allowRenameOfImportPath); } public renameLocations(startRanges: ArrayOrSingle, options: RenameLocationsOptions) { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 581d5cc045d..d233ddf4585 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -469,8 +469,8 @@ namespace Harness.LanguageService { getSignatureHelpItems(fileName: string, position: number, options: ts.SignatureHelpItemsOptions | undefined): ts.SignatureHelpItems { return unwrapJSONCallResult(this.shim.getSignatureHelpItems(fileName, position, options)); } - getRenameInfo(fileName: string, position: number): ts.RenameInfo { - return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position)); + getRenameInfo(fileName: string, position: number, options?: ts.RenameInfoOptions): ts.RenameInfo { + return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position, options)); } findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ts.RenameLocation[] { return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments)); diff --git a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl index ec3ab3244b0..32430664b71 100644 --- a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -2083,7 +2083,7 @@ - + @@ -7567,7 +7567,7 @@ - + @@ -9172,7 +9172,7 @@ - + diff --git a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl index 7fade1f0ae3..0b60df175fe 100644 --- a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -1,10132 +1,10132 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - or -. For example '{0}' or '{1}'.]]> - - veya - biçiminde olmalıdır. Örneğin, '{0}' veya '{1}'.]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - type.]]> - - türü olmalıdır.]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ()' instead.]]> - - ()' kullanın.]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + or -. For example '{0}' or '{1}'.]]> + + veya - biçiminde olmalıdır. Örneğin, '{0}' veya '{1}'.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + type.]]> + + türü olmalıdır.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()' instead.]]> + + ()' kullanın.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 3050c976231..bc8197f9354 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1277,7 +1277,8 @@ namespace ts.server { private setConfigFileExistenceByNewConfiguredProject(project: ConfiguredProject) { const configFileExistenceInfo = this.getConfigFileExistenceInfo(project); if (configFileExistenceInfo) { - Debug.assert(configFileExistenceInfo.exists); + // The existance might not be set if the file watcher is not invoked by the time config project is created by external project + configFileExistenceInfo.exists = true; // close existing watcher if (configFileExistenceInfo.configFileWatcherForRootOfInferredProject) { const configFileName = project.getConfigFilePath(); @@ -2007,7 +2008,7 @@ namespace ts.server { const path = toNormalizedPath(uncheckedFileName); const info = this.getScriptInfoForNormalizedPath(path); if (info) return info; - const configProject = this.configuredProjects.get(uncheckedFileName); + const configProject = this.configuredProjects.get(this.toPath(uncheckedFileName)); return configProject && configProject.getCompilerOptions().configFile; } @@ -2776,7 +2777,12 @@ namespace ts.server { return; } - const info: OpenFileInfo = { checkJs: !!scriptInfo.getDefaultProject().getSourceFile(scriptInfo.path)!.checkJsDirective }; + const project = scriptInfo.getDefaultProject(); + if (!project.languageServiceEnabled) { + return; + } + + const info: OpenFileInfo = { checkJs: !!project.getSourceFile(scriptInfo.path)!.checkJsDirective }; this.eventHandler({ eventName: OpenFileInfoTelemetryEvent, data: { info } }); } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 193621da82a..8b9df926d82 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2905,6 +2905,7 @@ namespace ts.server.protocol { readonly importModuleSpecifierPreference?: "relative" | "non-relative"; readonly allowTextChangesInNewFiles?: boolean; readonly lazyConfiguredProjectsFromExternalProject?: boolean; + readonly allowRenameOfImportPath?: boolean; } export interface CompilerOptions { diff --git a/src/server/session.ts b/src/server/session.ts index d534f445ee3..8c300b121e1 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1177,7 +1177,8 @@ namespace ts.server { private getRenameInfo(args: protocol.FileLocationRequestArgs): RenameInfo { const { file, project } = this.getFileAndProject(args); const position = this.getPositionInFile(args, file); - return project.getLanguageService().getRenameInfo(file, position); + const preferences = this.getHostPreferences(); + return project.getLanguageService().getRenameInfo(file, position, { allowRenameOfImportPath: preferences.allowRenameOfImportPath }); } private getProjects(args: protocol.FileRequestArgs, getScriptInfoEnsuringProjectsUptoDate?: boolean, ignoreNoProjectError?: boolean): Projects { @@ -1236,7 +1237,7 @@ namespace ts.server { if (!simplifiedResult) return locations; const defaultProject = this.getDefaultProject(args); - const renameInfo: protocol.RenameInfo = this.mapRenameInfo(defaultProject.getLanguageService().getRenameInfo(file, position), Debug.assertDefined(this.projectService.getScriptInfo(file))); + const renameInfo: protocol.RenameInfo = this.mapRenameInfo(defaultProject.getLanguageService().getRenameInfo(file, position, { allowRenameOfImportPath: this.getHostPreferences().allowRenameOfImportPath }), Debug.assertDefined(this.projectService.getScriptInfo(file))); return { info: renameInfo, locs: this.toSpanGroups(locations) }; } diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index bbad3e03824..98ddee61f8e 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -275,7 +275,7 @@ namespace ts.codefix { inJs: boolean, preferences: UserPreferences, ): void { - const methodDeclaration = createMethodFromCallExpression(context, callExpression, token.text, inJs, makeStatic, preferences, !isInterfaceDeclaration(typeDecl)); + const methodDeclaration = createMethodFromCallExpression(context, callExpression, token.text, inJs, makeStatic, preferences, typeDecl); const containingMethodDeclaration = getAncestor(callExpression, SyntaxKind.MethodDeclaration); if (containingMethodDeclaration && containingMethodDeclaration.parent === typeDecl) { diff --git a/src/services/codefixes/fixCannotFindModule.ts b/src/services/codefixes/fixCannotFindModule.ts index 1c7070c74bf..1baaa9714f2 100644 --- a/src/services/codefixes/fixCannotFindModule.ts +++ b/src/services/codefixes/fixCannotFindModule.ts @@ -74,7 +74,7 @@ namespace ts.codefix { const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile); if (!tsconfigObjectLiteral) return undefined; - const compilerOptionsProperty = findProperty(tsconfigObjectLiteral, "compilerOptions"); + const compilerOptionsProperty = findJsonProperty(tsconfigObjectLiteral, "compilerOptions"); if (!compilerOptionsProperty) { const newCompilerOptions = createObjectLiteral([makeDefaultBaseUrl(), makeDefaultPaths()]); changes.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createJsonPropertyAssignment("compilerOptions", newCompilerOptions)); @@ -94,7 +94,7 @@ namespace ts.codefix { return createJsonPropertyAssignment("baseUrl", createStringLiteral(defaultBaseUrl)); } function getOrAddBaseUrl(changes: textChanges.ChangeTracker, tsconfig: TsConfigSourceFile, compilerOptions: ObjectLiteralExpression): string { - const baseUrlProp = findProperty(compilerOptions, "baseUrl"); + const baseUrlProp = findJsonProperty(compilerOptions, "baseUrl"); if (baseUrlProp) { return isStringLiteral(baseUrlProp.initializer) ? baseUrlProp.initializer.text : defaultBaseUrl; } @@ -112,7 +112,7 @@ namespace ts.codefix { return createJsonPropertyAssignment("paths", createObjectLiteral([makeDefaultPathMapping()])); } function getOrAddPathMapping(changes: textChanges.ChangeTracker, tsconfig: TsConfigSourceFile, compilerOptions: ObjectLiteralExpression) { - const paths = findProperty(compilerOptions, "paths"); + const paths = findJsonProperty(compilerOptions, "paths"); if (!paths || !isObjectLiteralExpression(paths.initializer)) { changes.insertNodeAtObjectStart(tsconfig, compilerOptions, makeDefaultPaths()); return defaultTypesDirectoryName; @@ -129,14 +129,6 @@ namespace ts.codefix { return defaultTypesDirectoryName; } - function createJsonPropertyAssignment(name: string, initializer: Expression) { - return createPropertyAssignment(createStringLiteral(name), initializer); - } - - function findProperty(obj: ObjectLiteralExpression, name: string): PropertyAssignment | undefined { - return find(obj.properties, (p): p is PropertyAssignment => isPropertyAssignment(p) && !!p.name && isStringLiteral(p.name) && p.name.text === name); - } - function getInstallCommand(fileName: string, packageName: string): InstallPackageAction { return { type: "install package", file: fileName, packageName }; } diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index 9d9972c03e8..d18bc50639a 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -8,9 +8,9 @@ namespace ts.codefix { registerCodeFix({ errorCodes, getCodeActions(context) { - const { program, sourceFile, span } = context; + const { sourceFile, span } = context; const changes = textChanges.ChangeTracker.with(context, t => - addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t, context.preferences)); + addMissingMembers(getClass(sourceFile, span.start), sourceFile, context, t, context.preferences)); return changes.length === 0 ? undefined : [createCodeFixAction(fixId, changes, Diagnostics.Implement_inherited_abstract_class, fixId, Diagnostics.Implement_all_inherited_abstract_classes)]; }, fixIds: [fixId], @@ -19,7 +19,7 @@ namespace ts.codefix { return codeFixAll(context, errorCodes, (changes, diag) => { const classDeclaration = getClass(diag.file, diag.start); if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) { - addMissingMembers(classDeclaration, context.sourceFile, context.program.getTypeChecker(), changes, context.preferences); + addMissingMembers(classDeclaration, context.sourceFile, context, changes, context.preferences); } }); }, @@ -32,15 +32,16 @@ namespace ts.codefix { return cast(token.parent, isClassLike); } - function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, changeTracker: textChanges.ChangeTracker, preferences: UserPreferences): void { + function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, context: TypeConstructionContext, changeTracker: textChanges.ChangeTracker, preferences: UserPreferences): void { const extendsNode = getEffectiveBaseTypeNode(classDeclaration)!; + const checker = context.program.getTypeChecker(); const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode); // Note that this is ultimately derived from a map indexed by symbol names, // so duplicates cannot occur. const abstractAndNonPrivateExtendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType).filter(symbolPointsToNonPrivateAndAbstractMember); - createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); + createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, context, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); } function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean { diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 4401625000a..59385f2e2e9 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -6,11 +6,10 @@ namespace ts.codefix { registerCodeFix({ errorCodes, getCodeActions(context) { - const { program, sourceFile, span } = context; + const { sourceFile, span } = context; const classDeclaration = getClass(sourceFile, span.start); - const checker = program.getTypeChecker(); return mapDefined(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => { - const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t, context.preferences)); + const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(context, implementedTypeNode, sourceFile, classDeclaration, t, context.preferences)); return changes.length === 0 ? undefined : createCodeFixAction(fixId, changes, [Diagnostics.Implement_interface_0, implementedTypeNode.getText(sourceFile)], fixId, Diagnostics.Implement_all_unimplemented_interfaces); }); }, @@ -21,7 +20,7 @@ namespace ts.codefix { const classDeclaration = getClass(diag.file, diag.start); if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) { for (const implementedTypeNode of getClassImplementsHeritageClauseElements(classDeclaration)!) { - addMissingDeclarations(context.program.getTypeChecker(), implementedTypeNode, diag.file, classDeclaration, changes, context.preferences); + addMissingDeclarations(context, implementedTypeNode, diag.file, classDeclaration, changes, context.preferences); } } }); @@ -37,13 +36,14 @@ namespace ts.codefix { } function addMissingDeclarations( - checker: TypeChecker, + context: TypeConstructionContext, implementedTypeNode: ExpressionWithTypeArguments, sourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, changeTracker: textChanges.ChangeTracker, preferences: UserPreferences, ): void { + const checker = context.program.getTypeChecker(); const maybeHeritageClauseSymbol = getHeritageClauseSymbolTable(classDeclaration, checker); // Note that this is ultimately derived from a map indexed by symbol names, // so duplicates cannot occur. @@ -60,12 +60,12 @@ namespace ts.codefix { createMissingIndexSignatureDeclaration(implementedType, IndexKind.String); } - createMissingMemberNodes(classDeclaration, nonPrivateAndNotExistedInHeritageClauseMembers, checker, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); + createMissingMemberNodes(classDeclaration, nonPrivateAndNotExistedInHeritageClauseMembers, context, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); function createMissingIndexSignatureDeclaration(type: InterfaceType, kind: IndexKind): void { const indexInfoOfKind = checker.getIndexInfoOfType(type, kind); if (indexInfoOfKind) { - changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration)!); + changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration, /*flags*/ undefined, getNoopSymbolTrackerWithResolver(context))!); } } } diff --git a/src/services/codefixes/fixEnableExperimentalDecorators.ts b/src/services/codefixes/fixEnableExperimentalDecorators.ts new file mode 100644 index 00000000000..8aeefe48a47 --- /dev/null +++ b/src/services/codefixes/fixEnableExperimentalDecorators.ts @@ -0,0 +1,24 @@ +/* @internal */ +namespace ts.codefix { + const fixId = "enableExperimentalDecorators"; + const errorCodes = [ + Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_to_remove_this_warning.code + ]; + registerCodeFix({ + errorCodes, + getCodeActions: (context) => { + const { configFile } = context.program.getCompilerOptions(); + if (configFile === undefined) { + return undefined; + } + + const changes = textChanges.ChangeTracker.with(context, changeTracker => makeChange(changeTracker, configFile)); + return [createCodeFixActionNoFixId(fixId, changes, Diagnostics.Enable_the_experimentalDecorators_option_in_your_configuration_file)]; + }, + fixIds: [fixId], + }); + + function makeChange(changeTracker: textChanges.ChangeTracker, configFile: TsConfigSourceFile) { + setJsonCompilerOptionValue(changeTracker, configFile, "experimentalDecorators", createTrue()); + } +} diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 461771c0b5e..4309e6ec35d 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -6,23 +6,48 @@ namespace ts.codefix { * @param possiblyMissingSymbols The collection of symbols to filter and then get insertions for. * @returns Empty string iff there are no member insertions. */ - export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: ReadonlyArray, checker: TypeChecker, preferences: UserPreferences, out: (node: ClassElement) => void): void { + export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: ReadonlyArray, context: TypeConstructionContext, preferences: UserPreferences, out: (node: ClassElement) => void): void { const classMembers = classDeclaration.symbol.members!; for (const symbol of possiblyMissingSymbols) { if (!classMembers.has(symbol.escapedName)) { - addNewNodeForMemberSymbol(symbol, classDeclaration, checker, preferences, out); + addNewNodeForMemberSymbol(symbol, classDeclaration, context, preferences, out); } } } + function getModuleSpecifierResolverHost(context: TypeConstructionContext): SymbolTracker["moduleResolverHost"] { + return { + directoryExists: context.host.directoryExists ? d => context.host.directoryExists!(d) : undefined, + fileExists: context.host.fileExists ? f => context.host.fileExists!(f) : undefined, + getCurrentDirectory: context.host.getCurrentDirectory ? () => context.host.getCurrentDirectory!() : undefined, + readFile: context.host.readFile ? f => context.host.readFile!(f) : undefined, + useCaseSensitiveFileNames: context.host.useCaseSensitiveFileNames ? () => context.host.useCaseSensitiveFileNames!() : undefined, + getSourceFiles: () => context.program.getSourceFiles(), + getCommonSourceDirectory: () => context.program.getCommonSourceDirectory(), + }; + } + + export function getNoopSymbolTrackerWithResolver(context: TypeConstructionContext): SymbolTracker { + return { + trackSymbol: noop, + moduleResolverHost: getModuleSpecifierResolverHost(context), + }; + } + + export interface TypeConstructionContext { + program: Program; + host: ModuleSpecifierResolutionHost; + } + /** * @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`. */ - function addNewNodeForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, preferences: UserPreferences, out: (node: Node) => void): void { + function addNewNodeForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, context: TypeConstructionContext, preferences: UserPreferences, out: (node: Node) => void): void { const declarations = symbol.getDeclarations(); if (!(declarations && declarations.length)) { return undefined; } + const checker = context.program.getTypeChecker(); const declaration = declarations[0]; const name = getSynthesizedDeepClone(getNameOfDeclaration(declaration), /*includeTrivia*/ false) as PropertyName; @@ -36,7 +61,7 @@ namespace ts.codefix { case SyntaxKind.SetAccessor: case SyntaxKind.PropertySignature: case SyntaxKind.PropertyDeclaration: - const typeNode = checker.typeToTypeNode(type, enclosingDeclaration); + const typeNode = checker.typeToTypeNode(type, enclosingDeclaration, /*flags*/ undefined, getNoopSymbolTrackerWithResolver(context)); out(createProperty( /*decorators*/undefined, modifiers, @@ -83,13 +108,13 @@ namespace ts.codefix { } function outputMethod(signature: Signature, modifiers: NodeArray | undefined, name: PropertyName, body?: Block): void { - const method = signatureToMethodDeclaration(checker, signature, enclosingDeclaration, modifiers, name, optional, body); + const method = signatureToMethodDeclaration(context, signature, enclosingDeclaration, modifiers, name, optional, body); if (method) out(method); } } function signatureToMethodDeclaration( - checker: TypeChecker, + context: TypeConstructionContext, signature: Signature, enclosingDeclaration: ClassLikeDeclaration, modifiers: NodeArray | undefined, @@ -97,7 +122,8 @@ namespace ts.codefix { optional: boolean, body: Block | undefined, ): MethodDeclaration | undefined { - const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.NoTruncation | NodeBuilderFlags.SuppressAnyReturnType); + const program = context.program; + const signatureDeclaration = program.getTypeChecker().signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.NoTruncation | NodeBuilderFlags.SuppressAnyReturnType, getNoopSymbolTrackerWithResolver(context)); if (!signatureDeclaration) { return undefined; } @@ -117,18 +143,20 @@ namespace ts.codefix { inJs: boolean, makeStatic: boolean, preferences: UserPreferences, - body: boolean, + contextNode: Node, ): MethodDeclaration { + const body = !isInterfaceDeclaration(contextNode); const { typeArguments, arguments: args, parent } = call; const checker = context.program.getTypeChecker(); + const tracker = getNoopSymbolTrackerWithResolver(context); const types = map(args, arg => // Widen the type so we don't emit nonsense annotations like "function fn(x: 3) {" - checker.typeToTypeNode(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(arg)))); + checker.typeToTypeNode(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(arg)), contextNode, /*flags*/ undefined, tracker)); const names = map(args, arg => isIdentifier(arg) ? arg.text : isPropertyAccessExpression(arg) ? arg.name.text : undefined); const contextualType = checker.getContextualType(call); - const returnType = inJs ? undefined : contextualType && checker.typeToTypeNode(contextualType, call) || createKeywordTypeNode(SyntaxKind.AnyKeyword); + const returnType = inJs ? undefined : contextualType && checker.typeToTypeNode(contextualType, contextNode, /*flags*/ undefined, tracker) || createKeywordTypeNode(SyntaxKind.AnyKeyword); return createMethod( /*decorators*/ undefined, /*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined, @@ -249,4 +277,46 @@ namespace ts.codefix { } return undefined; } + + export function setJsonCompilerOptionValue( + changeTracker: textChanges.ChangeTracker, + configFile: TsConfigSourceFile, + optionName: string, + optionValue: Expression, + ) { + const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile); + if (!tsconfigObjectLiteral) return undefined; + + const compilerOptionsProperty = findJsonProperty(tsconfigObjectLiteral, "compilerOptions"); + if (compilerOptionsProperty === undefined) { + changeTracker.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createJsonPropertyAssignment( + "compilerOptions", + createObjectLiteral([ + createJsonPropertyAssignment(optionName, optionValue), + ]))); + return; + } + + const compilerOptions = compilerOptionsProperty.initializer; + if (!isObjectLiteralExpression(compilerOptions)) { + return; + } + + const optionProperty = findJsonProperty(compilerOptions, optionName); + + if (optionProperty === undefined) { + changeTracker.insertNodeAtObjectStart(configFile, compilerOptions, createJsonPropertyAssignment(optionName, optionValue)); + } + else { + changeTracker.replaceNode(configFile, optionProperty.initializer, optionValue); + } + } + + export function createJsonPropertyAssignment(name: string, initializer: Expression) { + return createPropertyAssignment(createStringLiteral(name), initializer); + } + + export function findJsonProperty(obj: ObjectLiteralExpression, name: string): PropertyAssignment | undefined { + return find(obj.properties, (p): p is PropertyAssignment => isPropertyAssignment(p) && !!p.name && isStringLiteral(p.name) && p.name.text === name); + } } diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 14bfc8b2761..8006d1a0cfd 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -375,7 +375,7 @@ namespace ts.codefix { const symbolName = isJsxOpeningLikeElement(symbolToken.parent) && symbolToken.parent.tagName === symbolToken && (isIntrinsicJsxName(symbolToken.text) || checker.resolveName(symbolToken.text, symbolToken, SymbolFlags.All, /*excludeGlobals*/ false)) - ? checker.getJsxNamespace() + ? checker.getJsxNamespace(sourceFile) : symbolToken.text; // "default" is a keyword and not a legal identifier for the import, so we don't expect it here Debug.assert(symbolName !== InternalSymbolName.Default); diff --git a/src/services/completions.ts b/src/services/completions.ts index 43f00c58acf..f17ee862cca 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -104,7 +104,7 @@ namespace ts.Completions { getJSCompletionEntries(sourceFile, location!.pos, uniqueNames, compilerOptions.target!, entries); // TODO: GH#18217 } else { - if ((!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) { + if (!isNewIdentifierLocation && (!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) { return undefined; } @@ -367,7 +367,9 @@ namespace ts.Completions { } function getSymbolName(symbol: Symbol, origin: SymbolOriginInfo | undefined, target: ScriptTarget): string { - return origin && originIsExport(origin) && origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default + return origin && originIsExport(origin) && ( + (origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default) || + (symbol.escapedName === InternalSymbolName.ExportEquals)) // Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase. ? firstDefined(symbol.declarations, d => isExportAssignment(d) && isIdentifier(d.expression) ? d.expression.text : undefined) || codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target) @@ -913,7 +915,7 @@ namespace ts.Completions { } else { for (const symbol of type.getApparentProperties()) { - if (typeChecker.isValidPropertyAccessForCompletions(node.kind === SyntaxKind.ImportType ? node : node.parent, type, symbol)) { + if (typeChecker.isValidPropertyAccessForCompletions(node.kind === SyntaxKind.ImportType ? node : node.parent, type, symbol)) { addPropertySymbol(symbol); } } diff --git a/src/services/getEditsForFileRename.ts b/src/services/getEditsForFileRename.ts index ae031da5728..66cc88c2677 100644 --- a/src/services/getEditsForFileRename.ts +++ b/src/services/getEditsForFileRename.ts @@ -151,7 +151,7 @@ namespace ts { const toImport = oldFromNew !== undefined // If we're at the new location (file was already renamed), need to redo module resolution starting from the old location. // TODO:GH#18217 - ? getSourceFileToImportFromResolved(resolveModuleName(importLiteral.text, oldImportFromPath, program.getCompilerOptions(), host as ModuleResolutionHost), oldToNew, host) + ? getSourceFileToImportFromResolved(resolveModuleName(importLiteral.text, oldImportFromPath, program.getCompilerOptions(), host as ModuleResolutionHost), oldToNew) : getSourceFileToImport(importedModuleSymbol, importLiteral, sourceFile, program, host, oldToNew); // Need an update if the imported file moved, or the importing file moved and was using a relative path. @@ -192,28 +192,35 @@ namespace ts { const resolved = host.resolveModuleNames ? host.getResolvedModuleWithFailedLookupLocationsFromCache && host.getResolvedModuleWithFailedLookupLocationsFromCache(importLiteral.text, importingSourceFile.fileName) : program.getResolvedModuleWithFailedLookupLocationsFromCache(importLiteral.text, importingSourceFile.fileName); - return getSourceFileToImportFromResolved(resolved, oldToNew, host); + return getSourceFileToImportFromResolved(resolved, oldToNew); } } - function getSourceFileToImportFromResolved(resolved: ResolvedModuleWithFailedLookupLocations | undefined, oldToNew: PathUpdater, host: LanguageServiceHost): ToImport | undefined { + function getSourceFileToImportFromResolved(resolved: ResolvedModuleWithFailedLookupLocations | undefined, oldToNew: PathUpdater): ToImport | undefined { // Search through all locations looking for a moved file, and only then test already existing files. // This is because if `a.ts` is compiled to `a.js` and `a.ts` is moved, we don't want to resolve anything to `a.js`, but to `a.ts`'s new location. - return tryEach(tryGetNewFile) || tryEach(tryGetOldFile); + if (!resolved) return undefined; - function tryEach(cb: (oldFileName: string) => ToImport | undefined): ToImport | undefined { - return resolved && ( - (resolved.resolvedModule && cb(resolved.resolvedModule.resolvedFileName)) || firstDefined(resolved.failedLookupLocations, cb)); + // First try resolved module + if (resolved.resolvedModule) { + const result = tryChange(resolved.resolvedModule.resolvedFileName); + if (result) return result; } - function tryGetNewFile(oldFileName: string): ToImport | undefined { - const newFileName = oldToNew(oldFileName); - return newFileName !== undefined && host.fileExists!(newFileName) ? { newFileName, updated: true } : undefined; // TODO: GH#18217 + // Then failed lookups except package.json since we dont want to touch them (only included ts/js files) + const result = forEach(resolved.failedLookupLocations, tryChangeWithIgnoringPackageJson); + if (result) return result; + + // If nothing changed, then result is resolved module file thats not updated + return resolved.resolvedModule && { newFileName: resolved.resolvedModule.resolvedFileName, updated: false }; + + function tryChangeWithIgnoringPackageJson(oldFileName: string) { + return !endsWith(oldFileName, "/package.json") ? tryChange(oldFileName) : undefined; } - function tryGetOldFile(oldFileName: string): ToImport | undefined { + function tryChange(oldFileName: string) { const newFileName = oldToNew(oldFileName); - return host.fileExists!(oldFileName) ? newFileName !== undefined ? { newFileName, updated: true } : { newFileName: oldFileName, updated: false } : undefined; // TODO: GH#18217 + return newFileName && { newFileName, updated: true }; } } diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 760d6e71031..60f1283f7e6 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -660,7 +660,7 @@ namespace ts.NavigationBar { else if (isCallExpression(parent)) { const name = getCalledExpressionName(parent.expression); if (name !== undefined) { - const args = mapDefined(parent.arguments, a => isStringLiteral(a) ? a.getText(curSourceFile) : undefined).join(", "); + const args = mapDefined(parent.arguments, a => isStringLiteralLike(a) ? a.getText(curSourceFile) : undefined).join(", "); return `${name}(${args}) callback`; } } diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index 9c504357102..157ba98e262 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -28,12 +28,12 @@ namespace ts.OrganizeImports { organizeImportsWorker(topLevelExportDecls, coalesceExports); for (const ambientModule of sourceFile.statements.filter(isAmbientModule)) { - const ambientModuleBody = getModuleBlock(ambientModule as ModuleDeclaration)!; // TODO: GH#18217 + if (!ambientModule.body) { continue; } - const ambientModuleImportDecls = ambientModuleBody.statements.filter(isImportDeclaration); + const ambientModuleImportDecls = ambientModule.body.statements.filter(isImportDeclaration); organizeImportsWorker(ambientModuleImportDecls, coalesceAndOrganizeImports); - const ambientModuleExportDecls = ambientModuleBody.statements.filter(isExportDeclaration); + const ambientModuleExportDecls = ambientModule.body.statements.filter(isExportDeclaration); organizeImportsWorker(ambientModuleExportDecls, coalesceExports); } @@ -81,14 +81,9 @@ namespace ts.OrganizeImports { } } - function getModuleBlock(moduleDecl: ModuleDeclaration): ModuleBlock | undefined { - const body = moduleDecl.body; - return body && !isIdentifier(body) ? (isModuleBlock(body) ? body : getModuleBlock(body)) : undefined; - } - function removeUnusedImports(oldImports: ReadonlyArray, sourceFile: SourceFile, program: Program) { const typeChecker = program.getTypeChecker(); - const jsxNamespace = typeChecker.getJsxNamespace(); + const jsxNamespace = typeChecker.getJsxNamespace(sourceFile); const jsxElementsPresent = !!(sourceFile.transformFlags & TransformFlags.ContainsJsx); const usedImports: ImportDeclaration[] = []; diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 9d2c61a1a7d..6f834512f0b 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -37,6 +37,10 @@ namespace ts.OutliningElementsCollector { addOutliningForLeadingCommentsForNode(n, sourceFile, cancellationToken, out); } + if (isFunctionExpressionAssignedToVariable(n)) { + addOutliningForLeadingCommentsForNode(n.parent.parent.parent, sourceFile, cancellationToken, out); + } + const span = getOutliningSpanForNode(n, sourceFile); if (span) out.push(span); @@ -54,6 +58,14 @@ namespace ts.OutliningElementsCollector { } depthRemaining++; } + + function isFunctionExpressionAssignedToVariable(n: Node) { + if (!isFunctionExpression(n) && !isArrowFunction(n)) { + return false; + } + const ancestor = findAncestor(n, isVariableStatement); + return !!ancestor && getSingleInitializerOfVariableStatementOrPropertyDeclaration(ancestor) === n; + } } function addRegionOutliningSpans(sourceFile: SourceFile, out: Push): void { @@ -175,6 +187,7 @@ namespace ts.OutliningElementsCollector { case SyntaxKind.ModuleBlock: return spanForNode(n.parent); case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.CaseBlock: @@ -206,10 +219,10 @@ namespace ts.OutliningElementsCollector { } function spanForObjectOrArrayLiteral(node: Node, open: SyntaxKind.OpenBraceToken | SyntaxKind.OpenBracketToken = SyntaxKind.OpenBraceToken): OutliningSpan | undefined { - // If the block has no leading keywords and is inside an array literal, + // If the block has no leading keywords and is inside an array literal or call expression, // we only want to collapse the span of the block. // Otherwise, the collapsed section will include the end of the previous line. - return spanForNode(node, /*autoCollapse*/ false, /*useFullStart*/ !isArrayLiteralExpression(node.parent), open); + return spanForNode(node, /*autoCollapse*/ false, /*useFullStart*/ !isArrayLiteralExpression(node.parent) && !isCallExpression(node.parent), open); } function spanForNode(hintSpanNode: Node, autoCollapse = false, useFullStart = true, open: SyntaxKind.OpenBraceToken | SyntaxKind.OpenBracketToken = SyntaxKind.OpenBraceToken): OutliningSpan | undefined { diff --git a/src/services/rename.ts b/src/services/rename.ts index a51d79797bf..484349acc86 100644 --- a/src/services/rename.ts +++ b/src/services/rename.ts @@ -1,14 +1,14 @@ /* @internal */ namespace ts.Rename { - export function getRenameInfo(program: Program, sourceFile: SourceFile, position: number): RenameInfo { + export function getRenameInfo(program: Program, sourceFile: SourceFile, position: number, options?: RenameInfoOptions): RenameInfo { const node = getTouchingPropertyName(sourceFile, position); const renameInfo = node && nodeIsEligibleForRename(node) - ? getRenameInfoForNode(node, program.getTypeChecker(), sourceFile, declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile())) + ? getRenameInfoForNode(node, program.getTypeChecker(), sourceFile, declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile()), options) : undefined; return renameInfo || getRenameInfoError(Diagnostics.You_cannot_rename_this_element); } - function getRenameInfoForNode(node: Node, typeChecker: TypeChecker, sourceFile: SourceFile, isDefinedInLibraryFile: (declaration: Node) => boolean): RenameInfo | undefined { + function getRenameInfoForNode(node: Node, typeChecker: TypeChecker, sourceFile: SourceFile, isDefinedInLibraryFile: (declaration: Node) => boolean, options?: RenameInfoOptions): RenameInfo | undefined { const symbol = typeChecker.getSymbolAtLocation(node); if (!symbol) return; // Only allow a symbol to be renamed if it actually has at least one declaration. @@ -26,7 +26,7 @@ namespace ts.Rename { } if (isStringLiteralLike(node) && tryGetImportFromModuleSpecifier(node)) { - return getRenameInfoForModule(node, sourceFile, symbol); + return options && options.allowRenameOfImportPath ? getRenameInfoForModule(node, sourceFile, symbol) : undefined; } const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, node); diff --git a/src/services/services.ts b/src/services/services.ts index 15f9709cb38..6bf6566da36 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2062,9 +2062,9 @@ namespace ts { } } - function getRenameInfo(fileName: string, position: number): RenameInfo { + function getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo { synchronizeHostData(); - return Rename.getRenameInfo(program, getValidSourceFile(fileName), position); + return Rename.getRenameInfo(program, getValidSourceFile(fileName), position, options); } function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings): RefactorContext { diff --git a/src/services/shims.ts b/src/services/shims.ts index ff66680bdbc..33ea4333e6f 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -164,7 +164,7 @@ namespace ts { * Returns a JSON-encoded value of the type: * { canRename: boolean, localizedErrorMessage: string, displayName: string, fullDisplayName: string, kind: string, kindModifiers: string, triggerSpan: { start; length } } */ - getRenameInfo(fileName: string, position: number): string; + getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): string; /** * Returns a JSON-encoded value of the type: @@ -831,10 +831,10 @@ namespace ts { ); } - public getRenameInfo(fileName: string, position: number): string { + public getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): string { return this.forwardJSONCall( `getRenameInfo('${fileName}', ${position})`, - () => this.languageService.getRenameInfo(fileName, position) + () => this.languageService.getRenameInfo(fileName, position, options) ); } diff --git a/src/services/suggestionDiagnostics.ts b/src/services/suggestionDiagnostics.ts index 5c175146e34..63691a72c8b 100644 --- a/src/services/suggestionDiagnostics.ts +++ b/src/services/suggestionDiagnostics.ts @@ -1,5 +1,7 @@ /* @internal */ namespace ts { + const visitedNestedConvertibleFunctions = createMap(); + export function computeSuggestionDiagnostics(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): DiagnosticWithLocation[] { program.getSemanticDiagnostics(sourceFile, cancellationToken); const diags: DiagnosticWithLocation[] = []; @@ -13,6 +15,7 @@ namespace ts { const isJsFile = isSourceFileJS(sourceFile); + visitedNestedConvertibleFunctions.clear(); check(sourceFile); if (getAllowSyntheticDefaultImports(program.getCompilerOptions())) { @@ -114,17 +117,22 @@ namespace ts { } function addConvertToAsyncFunctionDiagnostics(node: FunctionLikeDeclaration, checker: TypeChecker, diags: Push): void { - if (!isAsyncFunction(node) && - node.body && - isBlock(node.body) && - hasReturnStatementWithPromiseHandler(node.body) && - returnsPromise(node, checker)) { + // need to check function before checking map so that deeper levels of nested callbacks are checked + if (isConvertibleFunction(node, checker) && !visitedNestedConvertibleFunctions.has(getKeyFromNode(node))) { diags.push(createDiagnosticForNode( !node.name && isVariableDeclaration(node.parent) && isIdentifier(node.parent.name) ? node.parent.name : node, Diagnostics.This_may_be_converted_to_an_async_function)); } } + function isConvertibleFunction(node: FunctionLikeDeclaration, checker: TypeChecker) { + return !isAsyncFunction(node) && + node.body && + isBlock(node.body) && + hasReturnStatementWithPromiseHandler(node.body) && + returnsPromise(node, checker); + } + function returnsPromise(node: FunctionLikeDeclaration, checker: TypeChecker): boolean { const functionType = checker.getTypeAtLocation(node); const callSignatures = checker.getSignaturesOfType(functionType, SignatureKind.Call); @@ -169,14 +177,20 @@ namespace ts { // should be kept up to date with getTransformationBody in convertToAsyncFunction.ts function isFixablePromiseArgument(arg: Expression): boolean { switch (arg.kind) { - case SyntaxKind.NullKeyword: - case SyntaxKind.Identifier: // identifier includes undefined case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: + visitedNestedConvertibleFunctions.set(getKeyFromNode(arg as FunctionLikeDeclaration), true); + /* falls through */ + case SyntaxKind.NullKeyword: + case SyntaxKind.Identifier: // identifier includes undefined return true; default: return false; } } + + function getKeyFromNode(exp: FunctionLikeDeclaration) { + return `${exp.pos.toString()}:${exp.end.toString()}`; + } } diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 15044416f84..21be663055a 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -61,6 +61,7 @@ "codefixes/fixClassDoesntImplementInheritedAbstractMember.ts", "codefixes/fixClassSuperMustPrecedeThisAccess.ts", "codefixes/fixConstructorForDerivedNeedSuperCall.ts", + "codefixes/fixEnableExperimentalDecorators.ts", "codefixes/fixExtendsInterfaceBecomesImplements.ts", "codefixes/fixForgottenThisPropertyAccess.ts", "codefixes/fixUnusedIdentifier.ts", diff --git a/src/services/types.ts b/src/services/types.ts index 5100a999306..b45a816d6e0 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -294,7 +294,7 @@ namespace ts { getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): SignatureHelpItems | undefined; - getRenameInfo(fileName: string, position: number): RenameInfo; + getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo; findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReadonlyArray | undefined; getDefinitionAtPosition(fileName: string, position: number): ReadonlyArray | undefined; @@ -848,6 +848,10 @@ namespace ts { localizedErrorMessage: string; } + export interface RenameInfoOptions { + readonly allowRenameOfImportPath?: boolean; + } + export interface SignatureHelpParameter { name: string; documentation: SymbolDisplayPart[]; diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index 9dcc98eef86..2e5341732b5 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -89,6 +89,7 @@ "unittests/services/transpile.ts", "unittests/tscWatch/consoleClearing.ts", "unittests/tscWatch/emit.ts", + "unittests/tscWatch/emitAndErrorUpdates.ts", "unittests/tscWatch/programUpdates.ts", "unittests/tscWatch/resolutionCache.ts", "unittests/tscWatch/watchEnvironment.ts", diff --git a/src/testRunner/unittests/customTransforms.ts b/src/testRunner/unittests/customTransforms.ts index 304c0a55c59..aef9ed745ec 100644 --- a/src/testRunner/unittests/customTransforms.ts +++ b/src/testRunner/unittests/customTransforms.ts @@ -95,6 +95,39 @@ namespace ts { module: ModuleKind.ES2015, emitDecoratorMetadata: true, experimentalDecorators: true - }); + }); + + emitsCorrectly("sourceMapExternalSourceFiles", + [ + { + file: "source.ts", + // The text of length 'changed' is made to be on two lines so we know the line map change + text: `\`multi + line\` +'change'` + }, + ], + { + before: [ + context => node => visitNode(node, function visitor(node: Node): Node { + if (isStringLiteral(node) && node.text === "change") { + const text = "'changed'"; + const lineMap = computeLineStarts(text); + setSourceMapRange(node, { + pos: 0, end: text.length, source: { + text, + fileName: "another.html", + lineMap, + getLineAndCharacterOfPosition: pos => computeLineAndCharacterOfPosition(lineMap, pos) + } + }); + return node; + } + return visitEachChild(node, visitor, context); + }) + ] + }, + { sourceMap: true } + ); }); } diff --git a/src/testRunner/unittests/services/convertToAsyncFunction.ts b/src/testRunner/unittests/services/convertToAsyncFunction.ts index b89e684183b..8c426effa4c 100644 --- a/src/testRunner/unittests/services/convertToAsyncFunction.ts +++ b/src/testRunner/unittests/services/convertToAsyncFunction.ts @@ -255,7 +255,7 @@ interface String { charAt: any; } interface Array {}` }; - function testConvertToAsyncFunction(caption: string, text: string, baselineFolder: string, includeLib?: boolean, expectFailure = false) { + function testConvertToAsyncFunction(caption: string, text: string, baselineFolder: string, includeLib?: boolean, expectFailure = false, onlyProvideAction = false) { const t = extractTest(text); const selectionRange = t.ranges.get("selection")!; if (!selectionRange) { @@ -307,7 +307,7 @@ interface Array {}` const actions = codefix.getFixes(context); const action = find(actions, action => action.description === Diagnostics.Convert_to_async_function.message); - if (expectFailure) { + if (expectFailure && !onlyProvideAction) { assert.isNotTrue(action && action.changes.length > 0); return; } @@ -1151,25 +1151,25 @@ function [#|f|]() { _testConvertToAsyncFunction("convertToAsyncFunction_simpleFunctionExpression", ` const [#|foo|] = function () { return fetch('https://typescriptlang.org').then(result => { console.log(result) }); -} +} `); _testConvertToAsyncFunction("convertToAsyncFunction_simpleFunctionExpressionWithName", ` const foo = function [#|f|]() { return fetch('https://typescriptlang.org').then(result => { console.log(result) }); -} +} `); _testConvertToAsyncFunction("convertToAsyncFunction_simpleFunctionExpressionAssignedToBindingPattern", ` const { length } = [#|function|] () { return fetch('https://typescriptlang.org').then(result => { console.log(result) }); -} +} `); _testConvertToAsyncFunction("convertToAsyncFunction_catchBlockUniqueParams", ` function [#|f|]() { - return Promise.resolve().then(x => 1).catch(x => "a").then(x => !!x); -} + return Promise.resolve().then(x => 1).catch(x => "a").then(x => !!x); +} `); _testConvertToAsyncFunction("convertToAsyncFunction_bindingPattern", ` @@ -1178,7 +1178,7 @@ function [#|f|]() { } function res({ status, trailer }){ console.log(status); -} +} `); _testConvertToAsyncFunction("convertToAsyncFunction_bindingPatternNameCollision", ` @@ -1188,7 +1188,7 @@ function [#|f|]() { } function res({ status, trailer }){ console.log(status); -} +} `); _testConvertToAsyncFunctionFailed("convertToAsyncFunction_thenArgumentNotFunction", ` @@ -1209,7 +1209,7 @@ function [#|f|]() { } function res(result) { return Promise.resolve().then(x => console.log(result)); -} +} `); _testConvertToAsyncFunction("convertToAsyncFunction_callbackReturnsPromise", ` @@ -1241,7 +1241,7 @@ function [#|f|]() { return Promise.resolve(1) .then(x => Promise.reject(x)) .catch(err => console.log(err)); -} +} `); _testConvertToAsyncFunction("convertToAsyncFunction_nestedPromises", ` @@ -1266,6 +1266,22 @@ _testConvertToAsyncFunction("convertToAsyncFunction_exportModifier", ` export function [#|foo|]() { return fetch('https://typescriptlang.org').then(s => console.log(s)); } +`); + +_testConvertToAsyncFunction("convertToAsyncFunction_OutermostOnlySuccess", ` +function [#|foo|]() { + return fetch('a').then(() => { + return fetch('b').then(() => 'c'); + }) +} +`); + +_testConvertToAsyncFunctionFailedSuggestion("convertToAsyncFunction_OutermostOnlyFailure", ` +function foo() { + return fetch('a').then([#|() => {|] + return fetch('b').then(() => 'c'); + }) +} `); }); @@ -1276,4 +1292,8 @@ export function [#|foo|]() { function _testConvertToAsyncFunctionFailed(caption: string, text: string) { testConvertToAsyncFunction(caption, text, "convertToAsyncFunction", /*includeLib*/ true, /*expectFailure*/ true); } + + function _testConvertToAsyncFunctionFailedSuggestion(caption: string, text: string) { + testConvertToAsyncFunction(caption, text, "convertToAsyncFunction", /*includeLib*/ true, /*expectFailure*/ true, /*onlyProvideAction*/ true); + } } diff --git a/src/testRunner/unittests/services/organizeImports.ts b/src/testRunner/unittests/services/organizeImports.ts index 1421780a4b4..8116b1e268a 100644 --- a/src/testRunner/unittests/services/organizeImports.ts +++ b/src/testRunner/unittests/services/organizeImports.ts @@ -274,6 +274,16 @@ export const Other = 1; assert.isEmpty(changes); }); + it("doesn't crash on shorthand ambient module", () => { + const testFile = { + path: "/a.ts", + content: "declare module '*';", + }; + const languageService = makeLanguageService(testFile); + const changes = languageService.organizeImports({ type: "file", fileName: testFile.path }, testFormatSettings, emptyOptions); + assert.isEmpty(changes); + }); + testOrganizeImports("Renamed_used", { path: "/test.ts", @@ -586,6 +596,34 @@ import { React, Other } from "react"; }, reactLibFile); + testOrganizeImports("JsxPragmaTsx", + { + path: "/test.tsx", + content: `/** @jsx jsx */ + +import { Global, jsx } from '@emotion/core'; +import * as React from 'react'; + +export const App: React.FunctionComponent = _ =>

Hello!

+`, + }, + { + path: "/@emotion/core/index.d.ts", + content: `import { createElement } from 'react' +export const jsx: typeof createElement; +export function Global(props: any): ReactElement;` + }, + { + path: reactLibFile.path, + content: `${reactLibFile.content} +export namespace React { + interface FunctionComponent { + } +} +` + } + ); + describe("Exports", () => { testOrganizeExports("MoveToTop", diff --git a/src/testRunner/unittests/tsbuildWatchMode.ts b/src/testRunner/unittests/tsbuildWatchMode.ts index 72da8dbf85e..c1ddb0aa4e7 100644 --- a/src/testRunner/unittests/tsbuildWatchMode.ts +++ b/src/testRunner/unittests/tsbuildWatchMode.ts @@ -479,7 +479,7 @@ let x: string = 10;`); const { host, solutionBuilder } = createSolutionOfProject(allFiles, currentDirectory, solutionBuilderconfig, getOutputFileStamps); // Build in watch mode - const watch = createWatchOfConfigFileReturningBuilder(watchConfig, host); + const watch = createWatchOfConfigFile(watchConfig, host); checkOutputErrorsInitial(host, emptyArray); return { host, solutionBuilder, watch }; @@ -505,10 +505,8 @@ let x: string = 10;`); projectSystem.checkProjectActualFiles(service.configuredProjects.get(configFile.toLowerCase())!, expectedFiles); } - type Watch = () => BuilderProgram; - function verifyDependencies(watch: Watch, filePath: string, expected: ReadonlyArray) { - checkArray(`${filePath} dependencies`, watch().getAllDependencies(watch().getSourceFile(filePath)!), expected); + checkArray(`${filePath} dependencies`, watch.getBuilderProgram().getAllDependencies(watch().getSourceFile(filePath)!), expected); } describe("on sample project", () => { @@ -542,7 +540,7 @@ let x: string = 10;`); host.checkTimeoutQueueLengthAndRun(1); checkOutputErrorsIncremental(host, emptyArray); - checkProgramActualFiles(watch().getProgram(), expectedFilesAfterEdit); + checkProgramActualFiles(watch(), expectedFilesAfterEdit); }); @@ -642,7 +640,7 @@ export function gfoo() { expectedWatchedDirectoriesRecursive: ReadonlyArray, dependencies: ReadonlyArray<[string, ReadonlyArray]>, expectedWatchedDirectories?: ReadonlyArray) { - checkProgramActualFiles(watch().getProgram(), expectedProgramFiles); + checkProgramActualFiles(watch(), expectedProgramFiles); verifyWatchesOfProject(host, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, expectedWatchedDirectories); for (const [file, deps] of dependencies) { verifyDependencies(watch, file, deps); diff --git a/src/testRunner/unittests/tscWatch/emitAndErrorUpdates.ts b/src/testRunner/unittests/tscWatch/emitAndErrorUpdates.ts new file mode 100644 index 00000000000..b76c9a985d4 --- /dev/null +++ b/src/testRunner/unittests/tscWatch/emitAndErrorUpdates.ts @@ -0,0 +1,307 @@ +namespace ts.tscWatch { + describe("unittests:: tsc-watch:: Emit times and Error updates in builder after program changes", () => { + const currentDirectory = "/user/username/projects/myproject"; + const config: File = { + path: `${currentDirectory}/tsconfig.json`, + content: `{}` + }; + function getOutputFileStampAndError(host: WatchedSystem, watch: Watch, file: File) { + const builderProgram = watch.getBuilderProgram(); + const state = builderProgram.getState(); + return { + file, + fileStamp: host.getModifiedTime(file.path.replace(".ts", ".js")), + errors: builderProgram.getSemanticDiagnostics(watch().getSourceFileByPath(file.path as Path)), + errorsFromOldState: !!state.semanticDiagnosticsFromOldState && state.semanticDiagnosticsFromOldState.has(file.path) + }; + } + + function getOutputFileStampsAndErrors(host: WatchedSystem, watch: Watch, directoryFiles: ReadonlyArray) { + return directoryFiles.map(d => getOutputFileStampAndError(host, watch, d)); + } + + function findStampAndErrors(stampsAndErrors: ReadonlyArray>, file: File) { + return find(stampsAndErrors, info => info.file === file)!; + } + + function verifyOutputFileStampsAndErrors( + file: File, + emitExpected: boolean, + errorRefershExpected: boolean, + beforeChangeFileStampsAndErrors: ReadonlyArray>, + afterChangeFileStampsAndErrors: ReadonlyArray> + ) { + const beforeChange = findStampAndErrors(beforeChangeFileStampsAndErrors, file); + const afterChange = findStampAndErrors(afterChangeFileStampsAndErrors, file); + if (emitExpected) { + assert.notStrictEqual(afterChange.fileStamp, beforeChange.fileStamp, `Expected emit for file ${file.path}`); + } + else { + assert.strictEqual(afterChange.fileStamp, beforeChange.fileStamp, `Did not expect new emit for file ${file.path}`); + } + if (errorRefershExpected) { + if (afterChange.errors !== emptyArray || beforeChange.errors !== emptyArray) { + assert.notStrictEqual(afterChange.errors, beforeChange.errors, `Expected new errors for file ${file.path}`); + } + assert.isFalse(afterChange.errorsFromOldState, `Expected errors to be not copied from old state for file ${file.path}`); + } + else { + assert.strictEqual(afterChange.errors, beforeChange.errors, `Expected errors to not change for file ${file.path}`); + assert.isTrue(afterChange.errorsFromOldState, `Expected errors to be copied from old state for file ${file.path}`); + } + } + + interface VerifyEmitAndErrorUpdates { + change: (host: WatchedSystem) => void; + getInitialErrors: (watch: Watch) => ReadonlyArray | ReadonlyArray; + getIncrementalErrors: (watch: Watch) => ReadonlyArray | ReadonlyArray; + filesWithNewEmit: ReadonlyArray; + filesWithOnlyErrorRefresh: ReadonlyArray; + filesNotTouched: ReadonlyArray; + configFile?: File; + } + + function verifyEmitAndErrorUpdates({ filesWithNewEmit, filesWithOnlyErrorRefresh, filesNotTouched, configFile = config, change, getInitialErrors, getIncrementalErrors }: VerifyEmitAndErrorUpdates) { + const nonLibFiles = [...filesWithNewEmit, ...filesWithOnlyErrorRefresh, ...filesNotTouched]; + const files = [...nonLibFiles, configFile, libFile]; + const host = createWatchedSystem(files, { currentDirectory }); + const watch = createWatchOfConfigFile("tsconfig.json", host); + checkProgramActualFiles(watch(), [...nonLibFiles.map(f => f.path), libFile.path]); + checkOutputErrorsInitial(host, getInitialErrors(watch)); + const beforeChange = getOutputFileStampsAndErrors(host, watch, nonLibFiles); + change(host); + host.runQueuedTimeoutCallbacks(); + checkOutputErrorsIncremental(host, getIncrementalErrors(watch)); + const afterChange = getOutputFileStampsAndErrors(host, watch, nonLibFiles); + filesWithNewEmit.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ true, /*errorRefershExpected*/ true, beforeChange, afterChange)); + filesWithOnlyErrorRefresh.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ false, /*errorRefershExpected*/ true, beforeChange, afterChange)); + filesNotTouched.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ false, /*errorRefershExpected*/ false, beforeChange, afterChange)); + } + + describe("deep import changes", () => { + const aFile: File = { + path: `${currentDirectory}/a.ts`, + content: `import {B} from './b'; +declare var console: any; +let b = new B(); +console.log(b.c.d);` + }; + + function verifyDeepImportChange(bFile: File, cFile: File) { + const filesWithNewEmit: File[] = []; + const filesWithOnlyErrorRefresh = [aFile]; + addImportedModule(bFile); + addImportedModule(cFile); + verifyEmitAndErrorUpdates({ + filesWithNewEmit, + filesWithOnlyErrorRefresh, + filesNotTouched: emptyArray, + change: host => host.writeFile(cFile.path, cFile.content.replace("d", "d2")), + getInitialErrors: () => emptyArray, + getIncrementalErrors: watch => [ + getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.lastIndexOf("d"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "d", "C") + ] + }); + + function addImportedModule(file: File) { + if (file.path.endsWith(".d.ts")) { + filesWithOnlyErrorRefresh.push(file); + } + else { + filesWithNewEmit.push(file); + } + } + } + + it("updates errors when deep import file changes", () => { + const bFile: File = { + path: `${currentDirectory}/b.ts`, + content: `import {C} from './c'; +export class B +{ + c = new C(); +}` + }; + const cFile: File = { + path: `${currentDirectory}/c.ts`, + content: `export class C +{ + d = 1; +}` + }; + verifyDeepImportChange(bFile, cFile); + }); + + it("updates errors when deep import through declaration file changes", () => { + const bFile: File = { + path: `${currentDirectory}/b.d.ts`, + content: `import {C} from './c'; +export class B +{ + c: C; +}` + }; + const cFile: File = { + path: `${currentDirectory}/c.d.ts`, + content: `export class C +{ + d: number; +}` + }; + verifyDeepImportChange(bFile, cFile); + }); + }); + + it("updates errors in file not exporting a deep multilevel import that changes", () => { + const aFile: File = { + path: `${currentDirectory}/a.ts`, + content: `export interface Point { + name: string; + c: Coords; +} +export interface Coords { + x2: number; + y: number; +}` + }; + const bFile: File = { + path: `${currentDirectory}/b.ts`, + content: `import { Point } from "./a"; +export interface PointWrapper extends Point { +}` + }; + const cFile: File = { + path: `${currentDirectory}/c.ts`, + content: `import { PointWrapper } from "./b"; +export function getPoint(): PointWrapper { + return { + name: "test", + c: { + x: 1, + y: 2 + } + } +};` + }; + const dFile: File = { + path: `${currentDirectory}/d.ts`, + content: `import { getPoint } from "./c"; +getPoint().c.x;` + }; + const eFile: File = { + path: `${currentDirectory}/e.ts`, + content: `import "./d";` + }; + verifyEmitAndErrorUpdates({ + filesWithNewEmit: [aFile, bFile], + filesWithOnlyErrorRefresh: [cFile, dFile], + filesNotTouched: [eFile], + change: host => host.writeFile(aFile.path, aFile.content.replace("x2", "x")), + getInitialErrors: watch => [ + getDiagnosticOfFileFromProgram(watch(), cFile.path, cFile.content.indexOf("x: 1"), 4, chainDiagnosticMessages( + chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, "x", "Coords"), + Diagnostics.Type_0_is_not_assignable_to_type_1, + "{ x: number; y: number; }", + "Coords" + )), + getDiagnosticOfFileFromProgram(watch(), dFile.path, dFile.content.lastIndexOf("x"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "x", "Coords") + ], + getIncrementalErrors: () => emptyArray + }); + }); + + describe("updates errors when file transitively exported file changes", () => { + const config: File = { + path: `${currentDirectory}/tsconfig.json`, + content: JSON.stringify({ + files: ["app.ts"], + compilerOptions: { baseUrl: "." } + }) + }; + const app: File = { + path: `${currentDirectory}/app.ts`, + content: `import { Data } from "lib2/public"; +export class App { + public constructor() { + new Data().test(); + } +}` + }; + const lib2Public: File = { + path: `${currentDirectory}/lib2/public.ts`, + content: `export * from "./data";` + }; + const lib2Data: File = { + path: `${currentDirectory}/lib2/data.ts`, + content: `import { ITest } from "lib1/public"; +export class Data { + public test() { + const result: ITest = { + title: "title" + } + return result; + } +}` + }; + const lib1Public: File = { + path: `${currentDirectory}/lib1/public.ts`, + content: `export * from "./tools/public";` + }; + const lib1ToolsPublic: File = { + path: `${currentDirectory}/lib1/tools/public.ts`, + content: `export * from "./tools.interface";` + }; + const lib1ToolsInterface: File = { + path: `${currentDirectory}/lib1/tools/tools.interface.ts`, + content: `export interface ITest { + title: string; +}` + }; + + function verifyTransitiveExports(lib2Data: File, lib2Data2?: File) { + const filesWithNewEmit = [lib1ToolsInterface, lib1ToolsPublic]; + const filesWithOnlyErrorRefresh = [app, lib2Public, lib1Public, lib2Data]; + if (lib2Data2) { + filesWithOnlyErrorRefresh.push(lib2Data2); + } + verifyEmitAndErrorUpdates({ + filesWithNewEmit, + filesWithOnlyErrorRefresh, + filesNotTouched: emptyArray, + configFile: config, + change: host => host.writeFile(lib1ToolsInterface.path, lib1ToolsInterface.content.replace("title", "title2")), + getInitialErrors: () => emptyArray, + getIncrementalErrors: () => [ + "lib2/data.ts(5,13): error TS2322: Type '{ title: string; }' is not assignable to type 'ITest'.\n Object literal may only specify known properties, but 'title' does not exist in type 'ITest'. Did you mean to write 'title2'?\n" + ] + }); + } + it("when there are no circular import and exports", () => { + verifyTransitiveExports(lib2Data); + }); + + it("when there are circular import and exports", () => { + const lib2Data: File = { + path: `${currentDirectory}/lib2/data.ts`, + content: `import { ITest } from "lib1/public"; import { Data2 } from "./data2"; +export class Data { + public dat?: Data2; public test() { + const result: ITest = { + title: "title" + } + return result; + } +}` + }; + const lib2Data2: File = { + path: `${currentDirectory}/lib2/data2.ts`, + content: `import { Data } from "./data"; +export class Data2 { + public dat?: Data; +}` + }; + verifyTransitiveExports(lib2Data, lib2Data2); + }); + }); + }); +} diff --git a/src/testRunner/unittests/tscWatch/helpers.ts b/src/testRunner/unittests/tscWatch/helpers.ts index af896009c0d..b593cabbec6 100644 --- a/src/testRunner/unittests/tscWatch/helpers.ts +++ b/src/testRunner/unittests/tscWatch/helpers.ts @@ -29,18 +29,18 @@ namespace ts.tscWatch { checkArray(`Program rootFileNames`, program.getRootFileNames(), expectedFiles); } - export function createWatchOfConfigFileReturningBuilder(configFileName: string, host: WatchedSystem, maxNumberOfFilesToIterateForInvalidation?: number) { - const compilerHost = createWatchCompilerHostOfConfigFile(configFileName, {}, host); - compilerHost.maxNumberOfFilesToIterateForInvalidation = maxNumberOfFilesToIterateForInvalidation; - const watch = createWatchProgram(compilerHost); - return () => watch.getCurrentProgram(); + export interface Watch { + (): Program; + getBuilderProgram(): EmitAndSemanticDiagnosticsBuilderProgram; } export function createWatchOfConfigFile(configFileName: string, host: WatchedSystem, maxNumberOfFilesToIterateForInvalidation?: number) { const compilerHost = createWatchCompilerHostOfConfigFile(configFileName, {}, host); compilerHost.maxNumberOfFilesToIterateForInvalidation = maxNumberOfFilesToIterateForInvalidation; const watch = createWatchProgram(compilerHost); - return () => watch.getCurrentProgram().getProgram(); + const result = (() => watch.getCurrentProgram().getProgram()) as Watch; + result.getBuilderProgram = () => watch.getCurrentProgram(); + return result; } export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], host: WatchedSystem, options: CompilerOptions = {}, maxNumberOfFilesToIterateForInvalidation?: number) { @@ -152,7 +152,21 @@ namespace ts.tscWatch { assert.equal(host.exitCode, expectedExitCode); } - export function getDiagnosticOfFileFrom(file: SourceFile | undefined, text: string, start: number | undefined, length: number | undefined, message: DiagnosticMessage): Diagnostic { + function isDiagnosticMessageChain(message: DiagnosticMessage | DiagnosticMessageChain): message is DiagnosticMessageChain { + return !!(message as DiagnosticMessageChain).messageText; + } + + export function getDiagnosticOfFileFrom(file: SourceFile | undefined, start: number | undefined, length: number | undefined, message: DiagnosticMessage | DiagnosticMessageChain, ..._args: (string | number)[]): Diagnostic { + let text: DiagnosticMessageChain | string; + if (isDiagnosticMessageChain(message)) { + text = message; + } + else { + text = getLocaleSpecificMessage(message); + if (arguments.length > 4) { + text = formatStringFromArgs(text, arguments, 4); + } + } return { file, start, @@ -164,35 +178,17 @@ namespace ts.tscWatch { }; } - export function getDiagnosticWithoutFile(message: DiagnosticMessage, ..._args: (string | number)[]): Diagnostic { - let text = getLocaleSpecificMessage(message); - - if (arguments.length > 1) { - text = formatStringFromArgs(text, arguments, 1); - } - - return getDiagnosticOfFileFrom(/*file*/ undefined, text, /*start*/ undefined, /*length*/ undefined, message); + export function getDiagnosticWithoutFile(message: DiagnosticMessage | DiagnosticMessageChain, ...args: (string | number)[]): Diagnostic { + return getDiagnosticOfFileFrom(/*file*/ undefined, /*start*/ undefined, /*length*/ undefined, message, ...args); } - export function getDiagnosticOfFile(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ..._args: (string | number)[]): Diagnostic { - let text = getLocaleSpecificMessage(message); - - if (arguments.length > 4) { - text = formatStringFromArgs(text, arguments, 4); - } - - return getDiagnosticOfFileFrom(file, text, start, length, message); + export function getDiagnosticOfFile(file: SourceFile, start: number, length: number, message: DiagnosticMessage | DiagnosticMessageChain, ...args: (string | number)[]): Diagnostic { + return getDiagnosticOfFileFrom(file, start, length, message, ...args); } - export function getDiagnosticOfFileFromProgram(program: Program, filePath: string, start: number, length: number, message: DiagnosticMessage, ..._args: (string | number)[]): Diagnostic { - let text = getLocaleSpecificMessage(message); - - if (arguments.length > 5) { - text = formatStringFromArgs(text, arguments, 5); - } - + export function getDiagnosticOfFileFromProgram(program: Program, filePath: string, start: number, length: number, message: DiagnosticMessage | DiagnosticMessageChain, ...args: (string | number)[]): Diagnostic { return getDiagnosticOfFileFrom(program.getSourceFileByPath(toPath(filePath, program.getCurrentDirectory(), s => s.toLowerCase()))!, - text, start, length, message); + start, length, message, ...args); } export function getUnknownCompilerOption(program: Program, configFile: File, option: string) { diff --git a/src/testRunner/unittests/tscWatch/programUpdates.ts b/src/testRunner/unittests/tscWatch/programUpdates.ts index 6109db5a341..b322c08bfe7 100644 --- a/src/testRunner/unittests/tscWatch/programUpdates.ts +++ b/src/testRunner/unittests/tscWatch/programUpdates.ts @@ -1070,92 +1070,6 @@ export default test;`; } }); - it("updates errors when deep import file changes", () => { - const currentDirectory = "/user/username/projects/myproject"; - const aFile: File = { - path: `${currentDirectory}/a.ts`, - content: `import {B} from './b'; -declare var console: any; -let b = new B(); -console.log(b.c.d);` - }; - const bFile: File = { - path: `${currentDirectory}/b.ts`, - content: `import {C} from './c'; -export class B -{ - c = new C(); -}` - }; - const cFile: File = { - path: `${currentDirectory}/c.ts`, - content: `export class C -{ - d = 1; -}` - }; - const config: File = { - path: `${currentDirectory}/tsconfig.json`, - content: `{}` - }; - const files = [aFile, bFile, cFile, config, libFile]; - const host = createWatchedSystem(files, { currentDirectory }); - const watch = createWatchOfConfigFile("tsconfig.json", host); - checkProgramActualFiles(watch(), [aFile.path, bFile.path, cFile.path, libFile.path]); - checkOutputErrorsInitial(host, emptyArray); - const modifiedTimeOfAJs = host.getModifiedTime(`${currentDirectory}/a.js`); - host.writeFile(cFile.path, cFile.content.replace("d", "d2")); - host.runQueuedTimeoutCallbacks(); - checkOutputErrorsIncremental(host, [ - getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.lastIndexOf("d"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "d", "C") - ]); - // File a need not be rewritten - assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs); - }); - - it("updates errors when deep import through declaration file changes", () => { - const currentDirectory = "/user/username/projects/myproject"; - const aFile: File = { - path: `${currentDirectory}/a.ts`, - content: `import {B} from './b'; -declare var console: any; -let b = new B(); -console.log(b.c.d);` - }; - const bFile: File = { - path: `${currentDirectory}/b.d.ts`, - content: `import {C} from './c'; -export class B -{ - c: C; -}` - }; - const cFile: File = { - path: `${currentDirectory}/c.d.ts`, - content: `export class C -{ - d: number; -}` - }; - const config: File = { - path: `${currentDirectory}/tsconfig.json`, - content: `{}` - }; - const files = [aFile, bFile, cFile, config, libFile]; - const host = createWatchedSystem(files, { currentDirectory }); - const watch = createWatchOfConfigFile("tsconfig.json", host); - checkProgramActualFiles(watch(), [aFile.path, bFile.path, cFile.path, libFile.path]); - checkOutputErrorsInitial(host, emptyArray); - const modifiedTimeOfAJs = host.getModifiedTime(`${currentDirectory}/a.js`); - host.writeFile(cFile.path, cFile.content.replace("d", "d2")); - host.runQueuedTimeoutCallbacks(); - checkOutputErrorsIncremental(host, [ - getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.lastIndexOf("d"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "d", "C") - ]); - // File a need not be rewritten - assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs); - }); - it("updates errors when strictNullChecks changes", () => { const currentDirectory = "/user/username/projects/myproject"; const aFile: File = { @@ -1228,98 +1142,6 @@ foo().hello` checkOutputErrorsIncremental(host, emptyArray); }); - describe("updates errors when file transitively exported file changes", () => { - const projectLocation = "/user/username/projects/myproject"; - const config: File = { - path: `${projectLocation}/tsconfig.json`, - content: JSON.stringify({ - files: ["app.ts"], - compilerOptions: { baseUrl: "." } - }) - }; - const app: File = { - path: `${projectLocation}/app.ts`, - content: `import { Data } from "lib2/public"; -export class App { - public constructor() { - new Data().test(); - } -}` - }; - const lib2Public: File = { - path: `${projectLocation}/lib2/public.ts`, - content: `export * from "./data";` - }; - const lib2Data: File = { - path: `${projectLocation}/lib2/data.ts`, - content: `import { ITest } from "lib1/public"; -export class Data { - public test() { - const result: ITest = { - title: "title" - } - return result; - } -}` - }; - const lib1Public: File = { - path: `${projectLocation}/lib1/public.ts`, - content: `export * from "./tools/public";` - }; - const lib1ToolsPublic: File = { - path: `${projectLocation}/lib1/tools/public.ts`, - content: `export * from "./tools.interface";` - }; - const lib1ToolsInterface: File = { - path: `${projectLocation}/lib1/tools/tools.interface.ts`, - content: `export interface ITest { - title: string; -}` - }; - - function verifyTransitiveExports(filesWithoutConfig: ReadonlyArray) { - const files = [config, ...filesWithoutConfig]; - const host = createWatchedSystem(files, { currentDirectory: projectLocation }); - const watch = createWatchOfConfigFile(config.path, host); - checkProgramActualFiles(watch(), filesWithoutConfig.map(f => f.path)); - checkOutputErrorsInitial(host, emptyArray); - - host.writeFile(lib1ToolsInterface.path, lib1ToolsInterface.content.replace("title", "title2")); - host.checkTimeoutQueueLengthAndRun(1); - checkProgramActualFiles(watch(), filesWithoutConfig.map(f => f.path)); - checkOutputErrorsIncremental(host, [ - "lib2/data.ts(5,13): error TS2322: Type '{ title: string; }' is not assignable to type 'ITest'.\n Object literal may only specify known properties, but 'title' does not exist in type 'ITest'. Did you mean to write 'title2'?\n" - ]); - - } - it("when there are no circular import and exports", () => { - verifyTransitiveExports([libFile, app, lib2Public, lib2Data, lib1Public, lib1ToolsPublic, lib1ToolsInterface]); - }); - - it("when there are circular import and exports", () => { - const lib2Data: File = { - path: `${projectLocation}/lib2/data.ts`, - content: `import { ITest } from "lib1/public"; import { Data2 } from "./data2"; -export class Data { - public dat?: Data2; public test() { - const result: ITest = { - title: "title" - } - return result; - } -}` - }; - const lib2Data2: File = { - path: `${projectLocation}/lib2/data2.ts`, - content: `import { Data } from "./data"; -export class Data2 { - public dat?: Data; -}` - }; - verifyTransitiveExports([libFile, app, lib2Public, lib2Data, lib2Data2, lib1Public, lib1ToolsPublic, lib1ToolsInterface]); - }); - }); - describe("updates errors in lib file", () => { const currentDirectory = "/user/username/projects/myproject"; const field = "fullscreen"; @@ -1459,7 +1281,59 @@ interface Document { checkProgramActualFiles(watch(), [aFile.path, bFile.path, libFile.path]); } }); + + it("reports errors correctly with isolatedModules", () => { + const currentDirectory = "/user/username/projects/myproject"; + const aFile: File = { + path: `${currentDirectory}/a.ts`, + content: `export const a: string = "";` + }; + const bFile: File = { + path: `${currentDirectory}/b.ts`, + content: `import { a } from "./a"; +const b: string = a;` + }; + const configFile: File = { + path: `${currentDirectory}/tsconfig.json`, + content: JSON.stringify({ + compilerOptions: { + isolatedModules: true + } + }) + }; + + const files = [aFile, bFile, libFile, configFile]; + + const host = createWatchedSystem(files, { currentDirectory }); + const watch = createWatchOfConfigFile("tsconfig.json", host); + verifyProgramFiles(); + checkOutputErrorsInitial(host, emptyArray); + assert.equal(host.readFile(`${currentDirectory}/a.js`), `"use strict"; +exports.__esModule = true; +exports.a = ""; +`, "Contents of a.js"); + assert.equal(host.readFile(`${currentDirectory}/b.js`), `"use strict"; +exports.__esModule = true; +var a_1 = require("./a"); +var b = a_1.a; +`, "Contents of b.js"); + const modifiedTime = host.getModifiedTime(`${currentDirectory}/b.js`); + + host.writeFile(aFile.path, `export const a: number = 1`); + host.runQueuedTimeoutCallbacks(); + verifyProgramFiles(); + checkOutputErrorsIncremental(host, [ + getDiagnosticOfFileFromProgram(watch(), bFile.path, bFile.content.indexOf("b"), 1, Diagnostics.Type_0_is_not_assignable_to_type_1, "number", "string") + ]); + assert.equal(host.readFile(`${currentDirectory}/a.js`), `"use strict"; +exports.__esModule = true; +exports.a = 1; +`, "Contents of a.js"); + assert.equal(host.getModifiedTime(`${currentDirectory}/b.js`), modifiedTime, "Timestamp of b.js"); + + function verifyProgramFiles() { + checkProgramActualFiles(watch(), [aFile.path, bFile.path, libFile.path]); + } + }); }); - - } diff --git a/src/testRunner/unittests/tsserver/externalProjects.ts b/src/testRunner/unittests/tsserver/externalProjects.ts index 12a97151f4e..2055141538a 100644 --- a/src/testRunner/unittests/tsserver/externalProjects.ts +++ b/src/testRunner/unittests/tsserver/externalProjects.ts @@ -760,5 +760,63 @@ namespace ts.projectSystem { assert.equal(project2.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded checkProjectActualFiles(project2, [config.path, f1.path]); }); + + it("handles creation of external project with jsconfig before jsconfig creation watcher is invoked", () => { + const projectLocation = `/user/username/projects/WebApplication36/WebApplication36`; + const projectFileName = `${projectLocation}/WebApplication36.csproj`; + const tsconfig: File = { + path: `${projectLocation}/tsconfig.json`, + content: "{}" + }; + const files = [libFile, tsconfig]; + const host = createServerHost(files); + const service = createProjectService(host); + + // Create external project + service.openExternalProjects([{ + projectFileName, + rootFiles: [{ fileName: tsconfig.path }], + options: { allowJs: false } + }]); + checkNumberOfProjects(service, { configuredProjects: 1 }); + const configProject = service.configuredProjects.get(tsconfig.path.toLowerCase())!; + checkProjectActualFiles(configProject, [tsconfig.path]); + + // write js file, open external project and open it for edit + const jsFilePath = `${projectLocation}/javascript.js`; + host.writeFile(jsFilePath, ""); + service.openExternalProjects([{ + projectFileName, + rootFiles: [{ fileName: tsconfig.path }, { fileName: jsFilePath }], + options: { allowJs: false } + }]); + service.applyChangesInOpenFiles([ + { fileName: jsFilePath, scriptKind: ScriptKind.JS, content: "" } + ], /*changedFiles*/ undefined, /*closedFiles*/ undefined); + checkNumberOfProjects(service, { configuredProjects: 1, inferredProjects: 1 }); + checkProjectActualFiles(configProject, [tsconfig.path]); + const inferredProject = service.inferredProjects[0]; + checkProjectActualFiles(inferredProject, [libFile.path, jsFilePath]); + + // write jsconfig file + const jsConfig: File = { + path: `${projectLocation}/jsconfig.json`, + content: "{}" + }; + // Dont invoke file creation watchers as the repro suggests + host.ensureFileOrFolder(jsConfig, /*ignoreWatchInvokedWithTriggerAsFileCreate*/ true); + + // Open external project + service.openExternalProjects([{ + projectFileName, + rootFiles: [{ fileName: jsConfig.path }, { fileName: tsconfig.path }, { fileName: jsFilePath }], + options: { allowJs: false } + }]); + checkNumberOfProjects(service, { configuredProjects: 2, inferredProjects: 1 }); + checkProjectActualFiles(configProject, [tsconfig.path]); + assert.isTrue(inferredProject.isOrphan()); + const jsConfigProject = service.configuredProjects.get(jsConfig.path.toLowerCase())!; + checkProjectActualFiles(jsConfigProject, [jsConfig.path, jsFilePath, libFile.path]); + }); }); } diff --git a/src/testRunner/unittests/tsserver/helpers.ts b/src/testRunner/unittests/tsserver/helpers.ts index 1db6a9b36c8..3e79451cd11 100644 --- a/src/testRunner/unittests/tsserver/helpers.ts +++ b/src/testRunner/unittests/tsserver/helpers.ts @@ -57,8 +57,8 @@ namespace ts.projectSystem { export const nullLogger: server.Logger = { close: noop, - hasLevel: () => false, - loggingEnabled: () => false, + hasLevel: returnFalse, + loggingEnabled: returnFalse, perftrc: noop, info: noop, msg: noop, @@ -80,6 +80,21 @@ namespace ts.projectSystem { return { logger, hasErrorMsg: () => hasErrorMsg }; } + export function createLoggerWritingToConsole(): server.Logger { + const { close, startGroup, endGroup, getLogFileName } = nullLogger; + return { + close, + hasLevel: returnTrue, + loggingEnabled: returnTrue, + perftrc: s => console.log(s), + info: s => console.log(s), + msg: (s, type) => console.log(`${type}:: ${s}`), + startGroup, + endGroup, + getLogFileName + }; + } + export class TestTypingsInstaller extends TI.TypingsInstaller implements server.ITypingsInstaller { protected projectService!: server.ProjectService; constructor( diff --git a/src/testRunner/unittests/tsserver/projects.ts b/src/testRunner/unittests/tsserver/projects.ts index 00941a1d3c9..3a648c9189e 100644 --- a/src/testRunner/unittests/tsserver/projects.ts +++ b/src/testRunner/unittests/tsserver/projects.ts @@ -103,6 +103,30 @@ namespace ts.projectSystem { assert.isFalse(proj3.languageServiceEnabled); }); + it("should not crash when opening a file in a project with a disabled language service", () => { + const file1 = { + path: "/a/b/f1.js", + content: "let x =1;", + fileSize: 50 * 1024 * 1024 + }; + const file2 = { + path: "/a/b/f2.js", + content: "let x =1;", + fileSize: 100 + }; + + const projName = "proj1"; + + const host = createServerHost([file1, file2]); + const projectService = createProjectService(host, { useSingleInferredProject: true }, { eventHandler: noop }); + + projectService.openExternalProject({ rootFiles: toExternalFiles([file1.path, file2.path]), options: {}, projectFileName: projName }); + const proj1 = projectService.findProject(projName)!; + assert.isFalse(proj1.languageServiceEnabled); + + assert.doesNotThrow(() => projectService.openClientFile(file2.path)); + }); + describe("ignoreConfigFiles", () => { it("external project including config file", () => { const file1 = { diff --git a/src/testRunner/unittests/tsserver/refactors.ts b/src/testRunner/unittests/tsserver/refactors.ts index 2f4531f61a5..04eb552c316 100644 --- a/src/testRunner/unittests/tsserver/refactors.ts +++ b/src/testRunner/unittests/tsserver/refactors.ts @@ -116,5 +116,40 @@ namespace ts.projectSystem { renameLocation: undefined, }); }); + + it("handles canonicalization of tsconfig path", () => { + const aTs: File = { path: "/Foo/a.ts", content: "const x = 0;" }; + const tsconfig: File = { path: "/Foo/tsconfig.json", content: '{ "files": ["./a.ts"] }' }; + const session = createSession(createServerHost([aTs, tsconfig])); + openFilesForSession([aTs], session); + + const result = executeSessionRequest(session, protocol.CommandTypes.GetEditsForRefactor, { + file: aTs.path, + startLine: 1, + startOffset: 1, + endLine: 2, + endOffset: aTs.content.length, + refactor: "Move to a new file", + action: "Move to a new file", + }); + assert.deepEqual(result, { + edits: [ + { + fileName: aTs.path, + textChanges: [{ start: { line: 1, offset: 1 }, end: { line: 1, offset: aTs.content.length + 1 }, newText: "" }], + }, + { + fileName: tsconfig.path, + textChanges: [{ start: { line: 1, offset: 21 }, end: { line: 1, offset: 21 }, newText: ', "./x.ts"' }], + }, + { + fileName: "/Foo/x.ts", + textChanges: [{ start: { line: 0, offset: 0 }, end: { line: 0, offset: 0 }, newText: "const x = 0;\n" }], + }, + ], + renameFilename: undefined, + renameLocation: undefined, + }); + }); }); } diff --git a/src/testRunner/unittests/tsserver/rename.ts b/src/testRunner/unittests/tsserver/rename.ts index 75c08bb8bb0..4e95e79e31f 100644 --- a/src/testRunner/unittests/tsserver/rename.ts +++ b/src/testRunner/unittests/tsserver/rename.ts @@ -7,8 +7,18 @@ namespace ts.projectSystem { const session = createSession(createServerHost([aTs, bTs])); openFilesForSession([bTs], session); - const response = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";')); - assert.deepEqual(response, { + const response1 = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";')); + assert.deepEqual(response1, { + info: { + canRename: false, + localizedErrorMessage: "You cannot rename this element." + }, + locs: [{ file: bTs.path, locs: [protocolRenameSpanFromSubstring(bTs.content, "./a")] }], + }); + + session.getProjectService().setHostConfiguration({ preferences: { allowRenameOfImportPath: true } }); + const response2 = executeSessionRequest(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";')); + assert.deepEqual(response2, { info: { canRename: true, fileToRename: aTs.path, diff --git a/tests/baselines/reference/ES5For-ofTypeCheck14.errors.txt b/tests/baselines/reference/ES5For-ofTypeCheck14.errors.txt index 3a03491f110..1fde67722db 100644 --- a/tests/baselines/reference/ES5For-ofTypeCheck14.errors.txt +++ b/tests/baselines/reference/ES5For-ofTypeCheck14.errors.txt @@ -1,8 +1,8 @@ -tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck14.ts(2,17): error TS2568: Type 'Set' is not an array type. Use compiler option '--downlevelIteration' to allow iterating of iterators. +tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck14.ts(2,17): error TS2569: Type 'Set' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators. ==== tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck14.ts (1 errors) ==== var union: string | Set for (const e of union) { } ~~~~~ -!!! error TS2568: Type 'Set' is not an array type. Use compiler option '--downlevelIteration' to allow iterating of iterators. \ No newline at end of file +!!! error TS2569: Type 'Set' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators. \ No newline at end of file diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index d8ad3955edc..f60bf3346db 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4707,7 +4707,7 @@ declare namespace ts { getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan | undefined; getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan | undefined; getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): SignatureHelpItems | undefined; - getRenameInfo(fileName: string, position: number): RenameInfo; + getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo; findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReadonlyArray | undefined; getDefinitionAtPosition(fileName: string, position: number): ReadonlyArray | undefined; getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined; @@ -5151,6 +5151,9 @@ declare namespace ts { canRename: false; localizedErrorMessage: string; } + interface RenameInfoOptions { + readonly allowRenameOfImportPath?: boolean; + } interface SignatureHelpParameter { name: string; documentation: SymbolDisplayPart[]; @@ -7924,6 +7927,7 @@ declare namespace ts.server.protocol { readonly importModuleSpecifierPreference?: "relative" | "non-relative"; readonly allowTextChangesInNewFiles?: boolean; readonly lazyConfiguredProjectsFromExternalProject?: boolean; + readonly allowRenameOfImportPath?: boolean; } interface CompilerOptions { allowJs?: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 7465a4500cc..f005bfe168d 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4707,7 +4707,7 @@ declare namespace ts { getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan | undefined; getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan | undefined; getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): SignatureHelpItems | undefined; - getRenameInfo(fileName: string, position: number): RenameInfo; + getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo; findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReadonlyArray | undefined; getDefinitionAtPosition(fileName: string, position: number): ReadonlyArray | undefined; getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined; @@ -5151,6 +5151,9 @@ declare namespace ts { canRename: false; localizedErrorMessage: string; } + interface RenameInfoOptions { + readonly allowRenameOfImportPath?: boolean; + } interface SignatureHelpParameter { name: string; documentation: SymbolDisplayPart[]; diff --git a/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt b/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt index b401345a5e1..0f44d023a76 100644 --- a/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt +++ b/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt @@ -1,6 +1,4 @@ -tests/cases/conformance/jsx/file.tsx(42,11): error TS2322: Type '{ children: Element[]; a: number; b: string; }' is not assignable to type 'SingleChildProp'. - Types of property 'children' are incompatible. - Type 'Element[]' is missing the following properties from type 'Element': type, props +tests/cases/conformance/jsx/file.tsx(42,11): error TS2746: This JSX tag's 'children' prop expects a single child of type 'Element', but multiple children were provided. ==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== @@ -47,6 +45,4 @@ tests/cases/conformance/jsx/file.tsx(42,11): error TS2322: Type '{ children: Ele // Error let k5 = <>