diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index b571530c5d9..20183785dfd 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -1211,7 +1211,7 @@ namespace ts {
bind(node.statement);
popActiveLabel();
if (!activeLabel.referenced && !options.allowUnusedLabels) {
- errorOrSuggestionOnFirstToken(unusedLabelIsError(options), node, Diagnostics.Unused_label);
+ errorOrSuggestionOnNode(unusedLabelIsError(options), node.label, Diagnostics.Unused_label);
}
if (!node.statement || node.statement.kind !== SyntaxKind.DoStatement) {
// do statement sets current flow inside bindDoStatement
@@ -1918,9 +1918,16 @@ namespace ts {
file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2));
}
- function errorOrSuggestionOnFirstToken(isError: boolean, node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) {
- const span = getSpanOfTokenAtPosition(file, node.pos);
- const diag = createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2);
+ function errorOrSuggestionOnNode(isError: boolean, node: Node, message: DiagnosticMessage): void {
+ errorOrSuggestionOnRange(isError, node, node, message);
+ }
+
+ function errorOrSuggestionOnRange(isError: boolean, startNode: Node, endNode: Node, message: DiagnosticMessage): void {
+ addErrorOrSuggestionDiagnostic(isError, { pos: getTokenPosOfNode(startNode, file), end: endNode.end }, message);
+ }
+
+ function addErrorOrSuggestionDiagnostic(isError: boolean, range: TextRange, message: DiagnosticMessage): void {
+ const diag = createFileDiagnostic(file, range.pos, range.end - range.pos, message);
if (isError) {
file.bindDiagnostics.push(diag);
}
@@ -2792,7 +2799,7 @@ namespace ts {
node.declarationList.declarations.some(d => !!d.initializer)
);
- errorOrSuggestionOnFirstToken(isError, node, Diagnostics.Unreachable_code_detected);
+ eachUnreachableRange(node, (start, end) => errorOrSuggestionOnRange(isError, start, end, Diagnostics.Unreachable_code_detected));
}
}
}
@@ -2800,6 +2807,38 @@ namespace ts {
}
}
+ function eachUnreachableRange(node: Node, cb: (start: Node, last: Node) => void): void {
+ if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) {
+ const { statements } = node.parent;
+ const slice = sliceAfter(statements, node);
+ getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1]));
+ }
+ else {
+ cb(node, node);
+ }
+ }
+ // As opposed to a pure declaration like an `interface`
+ function isExecutableStatement(s: Statement): boolean {
+ // Don't remove statements that can validly be used before they appear.
+ return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) &&
+ // `var x;` may declare a variable used above
+ !(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.Let | NodeFlags.Const)) && s.declarationList.declarations.some(d => !d.initializer));
+ }
+
+ function isPurelyTypeDeclaration(s: Statement): boolean {
+ switch (s.kind) {
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.TypeAliasDeclaration:
+ return true;
+ case SyntaxKind.ModuleDeclaration:
+ return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated;
+ case SyntaxKind.EnumDeclaration:
+ return hasModifier(s, ModifierFlags.Const);
+ default:
+ return false;
+ }
+ }
+
/* @internal */
export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean {
return isExportsIdentifier(node) ||
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index e006a24c6f3..5ed4573c9b4 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -1059,6 +1059,7 @@ namespace ts {
// 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ)
// or if usage is in a type context:
// 1. inside a type query (typeof in type position)
+ // 2. inside a jsdoc comment
if (usage.parent.kind === SyntaxKind.ExportSpecifier || (usage.parent.kind === SyntaxKind.ExportAssignment && (usage.parent as ExportAssignment).isExportEquals)) {
// export specifiers do not use the variable, they only make it available for use
return true;
@@ -1069,7 +1070,7 @@ namespace ts {
}
const container = getEnclosingBlockScopeContainer(declaration);
- return isInTypeQuery(usage) || isUsedInFunctionOrInstanceProperty(usage, declaration, container);
+ return !!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || isUsedInFunctionOrInstanceProperty(usage, declaration, container);
function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
const container = getEnclosingBlockScopeContainer(declaration);
@@ -10367,9 +10368,9 @@ namespace ts {
}
}
- if (!issuedElaboration && (length(targetProp && targetProp.declarations) || length(target.symbol && target.symbol.declarations))) {
+ if (!issuedElaboration && (targetProp && length(targetProp.declarations) || target.symbol && length(target.symbol.declarations))) {
addRelatedInfo(reportedDiag, createDiagnosticForNode(
- targetProp ? targetProp.declarations[0] : target.symbol.declarations[0],
+ targetProp && length(targetProp.declarations) ? targetProp.declarations[0] : target.symbol.declarations[0],
Diagnostics.The_expected_type_comes_from_property_0_which_is_declared_here_on_type_1,
propertyName && !(nameType.flags & TypeFlags.UniqueESSymbol) ? unescapeLeadingUnderscores(propertyName) : typeToString(nameType),
typeToString(target)
@@ -19345,6 +19346,38 @@ namespace ts {
return resolveErrorCall(node);
}
+ function typeHasProtectedAccessibleBase(target: Symbol, type: InterfaceType): boolean {
+ const baseTypes = getBaseTypes(type);
+ if (!length(baseTypes)) {
+ return false;
+ }
+ const firstBase = baseTypes[0];
+ if (firstBase.flags & TypeFlags.Intersection) {
+ const types = (firstBase as IntersectionType).types;
+ const mixinCount = countWhere(types, isMixinConstructorType);
+ let i = 0;
+ for (const intersectionMember of (firstBase as IntersectionType).types) {
+ i++;
+ // We want to ignore mixin ctors
+ if (mixinCount === 0 || mixinCount === types.length && i === 0 || !isMixinConstructorType(intersectionMember)) {
+ if (getObjectFlags(intersectionMember) & (ObjectFlags.Class | ObjectFlags.Interface)) {
+ if (intersectionMember.symbol === target) {
+ return true;
+ }
+ if (typeHasProtectedAccessibleBase(target, intersectionMember as InterfaceType)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+ if (firstBase.symbol === target) {
+ return true;
+ }
+ return typeHasProtectedAccessibleBase(target, firstBase as InterfaceType);
+ }
+
function isConstructorAccessible(node: NewExpression, signature: Signature) {
if (!signature || !signature.declaration) {
return true;
@@ -19364,16 +19397,10 @@ namespace ts {
// A private or protected constructor can only be instantiated within its own class (or a subclass, for protected)
if (!isNodeWithinClass(node, declaringClassDeclaration)) {
const containingClass = getContainingClass(node);
- if (containingClass) {
+ if (containingClass && modifiers & ModifierFlags.Protected) {
const containingType = getTypeOfNode(containingClass);
- let baseTypes = getBaseTypes(containingType as InterfaceType);
- while (baseTypes.length) {
- const baseType = baseTypes[0];
- if (modifiers & ModifierFlags.Protected &&
- baseType.symbol === declaration.parent.symbol) {
- return true;
- }
- baseTypes = getBaseTypes(baseType as InterfaceType);
+ if (typeHasProtectedAccessibleBase(declaration.parent.symbol, containingType as InterfaceType)) {
+ return true;
}
}
if (modifiers & ModifierFlags.Private) {
@@ -21102,7 +21129,7 @@ namespace ts {
getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], UnionReduction.Subtype) :
leftType;
case SyntaxKind.EqualsToken:
- const special = getSpecialPropertyAssignmentKind(left.parent as BinaryExpression);
+ const special = isBinaryExpression(left.parent) ? getSpecialPropertyAssignmentKind(left.parent) : SpecialPropertyAssignmentKind.None;
checkSpecialAssignment(special, right);
if (isJSSpecialPropertyAssignment(special)) {
return leftType;
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index 87b425f6ed4..548d4c18c53 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -43,7 +43,7 @@ namespace ts {
if (sourceFile.kind === SyntaxKind.Bundle) {
const jsFilePath = options.outFile || options.out!;
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
- const declarationFilePath = (forceDtsPaths || options.declaration) ? removeFileExtension(jsFilePath) + Extension.Dts : undefined;
+ const declarationFilePath = (forceDtsPaths || getEmitDeclarations(options)) ? removeFileExtension(jsFilePath) + Extension.Dts : undefined;
const declarationMapPath = getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
const bundleInfoPath = options.references && jsFilePath ? (removeFileExtension(jsFilePath) + infoExtension) : undefined;
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath };
@@ -53,7 +53,7 @@ namespace ts {
const sourceMapFilePath = isJsonSourceFile(sourceFile) ? undefined : getSourceMapFilePath(jsFilePath, options);
// For legacy reasons (ie, we have baselines capturing the behavior), js files don't report a .d.ts output path - this would only matter if `declaration` and `allowJs` were both on, which is currently an error
const isJs = isSourceFileJavaScript(sourceFile);
- const declarationFilePath = ((forceDtsPaths || options.declaration) && !isJs) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
+ const declarationFilePath = ((forceDtsPaths || getEmitDeclarations(options)) && !isJs) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
const declarationMapPath = getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath: undefined };
}
@@ -205,7 +205,7 @@ namespace ts {
// Setup and perform the transformation to retrieve declarations from the input files
const nonJsFiles = filter(sourceFiles, isSourceFileNotJavaScript);
const inputListOrBundle = (compilerOptions.outFile || compilerOptions.out) ? [createBundle(nonJsFiles, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : nonJsFiles;
- if (emitOnlyDtsFiles && !compilerOptions.declaration) {
+ if (emitOnlyDtsFiles && !getEmitDeclarations(compilerOptions)) {
// Checker wont collect the linked aliases since thats only done when declaration is enabled.
// Do that here when emitting only dts files
nonJsFiles.forEach(collectLinkedAliases);
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index 75cdcaea328..5c34e4115a1 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -2329,7 +2329,7 @@ namespace ts {
if (!sourceFile.isDeclarationFile) {
const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
- programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, sourceFile.fileName, options.rootDir));
+ programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, sourceFile.fileName, rootDirectory));
allFilesBelongToPath = false;
}
}
@@ -2500,7 +2500,7 @@ namespace ts {
}
}
- if (options.declarationMap && !options.declaration) {
+ if (options.declarationMap && !getEmitDeclarations(options)) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "declarationMap", "declaration");
}
diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts
index d5f574762a1..d05d8350508 100644
--- a/src/compiler/resolutionCache.ts
+++ b/src/compiler/resolutionCache.ts
@@ -81,6 +81,7 @@ namespace ts {
let filesWithInvalidatedResolutions: Map | undefined;
let filesWithInvalidatedNonRelativeUnresolvedImports: Map> | undefined;
let allFilesHaveInvalidatedResolution = false;
+ const nonRelativeExternalModuleResolutions = createMultiMap();
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory!()); // TODO: GH#18217
const cachedDirectoryStructureHost = resolutionHost.getCachedDirectoryStructureHost();
@@ -154,6 +155,7 @@ namespace ts {
function clear() {
clearMap(directoryWatchesOfFailedLookups, closeFileWatcherOf);
customFailedLookupPaths.clear();
+ nonRelativeExternalModuleResolutions.clear();
closeTypeRootsWatch();
resolvedModuleNames.clear();
resolvedTypeReferenceDirectives.clear();
@@ -199,19 +201,20 @@ namespace ts {
perDirectoryResolvedModuleNames.clear();
nonRelaticeModuleNameCache.clear();
perDirectoryResolvedTypeReferenceDirectives.clear();
+ nonRelativeExternalModuleResolutions.forEach(watchFailedLookupLocationOfNonRelativeModuleResolutions);
+ nonRelativeExternalModuleResolutions.clear();
}
function finishCachingPerDirectoryResolution() {
allFilesHaveInvalidatedResolution = false;
filesWithInvalidatedNonRelativeUnresolvedImports = undefined;
+ clearPerDirectoryResolutions();
directoryWatchesOfFailedLookups.forEach((watcher, path) => {
if (watcher.refCount === 0) {
directoryWatchesOfFailedLookups.delete(path);
watcher.watcher.close();
}
});
-
- clearPerDirectoryResolutions();
}
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): CachedResolvedModuleWithFailedLookupLocations {
@@ -275,7 +278,7 @@ namespace ts {
perDirectoryResolution.set(name, resolution);
}
resolutionsInFile.set(name, resolution);
- watchFailedLookupLocationOfResolution(resolution);
+ watchFailedLookupLocationsOfExternalModuleResolutions(name, resolution);
if (existingResolution) {
stopWatchFailedLookupLocationOfResolution(existingResolution);
}
@@ -441,18 +444,27 @@ namespace ts {
return fileExtensionIsOneOf(path, failedLookupDefaultExtensions);
}
- function watchFailedLookupLocationOfResolution(resolution: ResolutionWithFailedLookupLocations) {
+ function watchFailedLookupLocationsOfExternalModuleResolutions(name: string, resolution: ResolutionWithFailedLookupLocations) {
// No need to set the resolution refCount
- if (!resolution.failedLookupLocations || !resolution.failedLookupLocations.length) {
- return;
+ if (resolution.failedLookupLocations && resolution.failedLookupLocations.length) {
+ if (resolution.refCount) {
+ resolution.refCount++;
+ }
+ else {
+ resolution.refCount = 1;
+ if (isExternalModuleNameRelative(name)) {
+ watchFailedLookupLocationOfResolution(resolution);
+ }
+ else {
+ nonRelativeExternalModuleResolutions.add(name, resolution);
+ }
+ }
}
+ }
- if (resolution.refCount !== undefined) {
- resolution.refCount++;
- return;
- }
+ function watchFailedLookupLocationOfResolution(resolution: ResolutionWithFailedLookupLocations) {
+ Debug.assert(!!resolution.refCount);
- resolution.refCount = 1;
const { failedLookupLocations } = resolution;
let setAtRoot = false;
for (const failedLookupLocation of failedLookupLocations) {
@@ -480,6 +492,16 @@ namespace ts {
}
}
+ function setRefCountToUndefined(resolution: ResolutionWithFailedLookupLocations) {
+ resolution.refCount = undefined;
+ }
+
+ function watchFailedLookupLocationOfNonRelativeModuleResolutions(resolutions: ResolutionWithFailedLookupLocations[], name: string) {
+ const updateResolution = resolutionHost.getCurrentProgram().getTypeChecker().tryFindAmbientModuleWithoutAugmentations(name) ?
+ setRefCountToUndefined : watchFailedLookupLocationOfResolution;
+ resolutions.forEach(updateResolution);
+ }
+
function setDirectoryWatcher(dir: string, dirPath: Path, nonRecursive?: boolean) {
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
if (dirWatcher) {
@@ -492,11 +514,11 @@ namespace ts {
}
function stopWatchFailedLookupLocationOfResolution(resolution: ResolutionWithFailedLookupLocations) {
- if (!resolution.failedLookupLocations || !resolution.failedLookupLocations.length) {
+ if (!resolution.refCount) {
return;
}
- resolution.refCount!--;
+ resolution.refCount--;
if (resolution.refCount) {
return;
}
diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts
index 751f5221167..695b9f8622f 100644
--- a/src/compiler/tsbuild.ts
+++ b/src/compiler/tsbuild.ts
@@ -304,7 +304,7 @@ namespace ts {
const outputs: string[] = [];
outputs.push(getOutputJavaScriptFileName(inputFileName, configFile));
- if (configFile.options.declaration && !fileExtensionIs(inputFileName, Extension.Json)) {
+ if (getEmitDeclarations(configFile.options) && !fileExtensionIs(inputFileName, Extension.Json)) {
const dts = getOutputDeclarationFileName(inputFileName, configFile);
outputs.push(dts);
if (configFile.options.declarationMap) {
@@ -320,7 +320,7 @@ namespace ts {
}
const outputs: string[] = [];
outputs.push(project.options.outFile);
- if (project.options.declaration) {
+ if (getEmitDeclarations(project.options)) {
const dts = changeExtension(project.options.outFile, Extension.Dts);
outputs.push(dts);
if (project.options.declarationMap) {
@@ -769,7 +769,10 @@ namespace ts {
const program = createProgram(programOptions);
// Don't emit anything in the presence of syntactic errors or options diagnostics
- const syntaxDiagnostics = [...program.getOptionsDiagnostics(), ...program.getSyntacticDiagnostics()];
+ const syntaxDiagnostics = [
+ ...program.getOptionsDiagnostics(),
+ ...program.getConfigFileParsingDiagnostics(),
+ ...program.getSyntacticDiagnostics()];
if (syntaxDiagnostics.length) {
resultFlags |= BuildResultFlags.SyntaxErrors;
for (const diag of syntaxDiagnostics) {
@@ -780,7 +783,7 @@ namespace ts {
}
// Don't emit .d.ts if there are decl file errors
- if (program.getCompilerOptions().declaration) {
+ if (getEmitDeclarations(program.getCompilerOptions())) {
const declDiagnostics = program.getDeclarationDiagnostics();
if (declDiagnostics.length) {
resultFlags |= BuildResultFlags.DeclarationEmitErrors;
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 32a64ee97fd..eb629dc5144 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2994,9 +2994,9 @@ namespace ts {
*/
/* @internal */ tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
getApparentType(type: Type): Type;
- getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
- getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
- getSuggestionForNonexistentExport(node: Identifier, target: Symbol): string | undefined;
+ /* @internal */ getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
+ /* @internal */ getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
+ /* @internal */ getSuggestionForNonexistentExport(node: Identifier, target: Symbol): string | undefined;
getBaseConstraintOfType(type: Type): Type | undefined;
getDefaultFromTypeParameter(type: Type): Type | undefined;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 2246586c357..c3b42d595d1 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -7273,7 +7273,7 @@ namespace ts {
}
export function getAreDeclarationMapsEnabled(options: CompilerOptions) {
- return !!(options.declaration && options.declarationMap);
+ return !!(getEmitDeclarations(options) && options.declarationMap);
}
export function getAllowSyntheticDefaultImports(compilerOptions: CompilerOptions) {
@@ -8442,4 +8442,10 @@ namespace ts {
}
export type Mutable = { -readonly [K in keyof T]: T[K] };
+
+ export function sliceAfter(arr: ReadonlyArray, value: T): ReadonlyArray {
+ const index = arr.indexOf(value);
+ Debug.assert(index !== -1);
+ return arr.slice(index);
+ }
}
diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index 3465ef333a0..095de4cecb0 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -1515,7 +1515,9 @@ Actual: ${stringify(fullActual)}`);
"argumentCount",
];
for (const key in options) {
- ts.Debug.assert(ts.contains(allKeys, key));
+ if (!ts.contains(allKeys, key)) {
+ ts.Debug.fail("Unexpected key " + key);
+ }
}
}
@@ -3367,7 +3369,7 @@ Actual: ${stringify(fullActual)}`);
this.languageServiceAdapterHost.renameFileOrDirectory(oldPath, newPath);
this.languageService.cleanupSemanticCache();
- const pathUpdater = ts.getPathUpdater(oldPath, newPath, ts.createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ false));
+ const pathUpdater = ts.getPathUpdater(oldPath, newPath, ts.createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ false), /*sourceMapper*/ undefined);
test(renameKeys(newFileContents, key => pathUpdater(key) || key), "with file moved");
}
diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts
index 761be1ae999..fa9c88d3ffa 100644
--- a/src/harness/harnessLanguageService.ts
+++ b/src/harness/harnessLanguageService.ts
@@ -155,7 +155,7 @@ namespace Harness.LanguageService {
this.vfs.mkdirpSync(ts.getDirectoryPath(newPath));
this.vfs.renameSync(oldPath, newPath);
- const updater = ts.getPathUpdater(oldPath, newPath, ts.createGetCanonicalFileName(this.useCaseSensitiveFileNames()));
+ const updater = ts.getPathUpdater(oldPath, newPath, ts.createGetCanonicalFileName(this.useCaseSensitiveFileNames()), /*sourceMapper*/ undefined);
this.scriptInfos.forEach((scriptInfo, key) => {
const newFileName = updater(key);
if (newFileName !== undefined) {
diff --git a/src/server/session.ts b/src/server/session.ts
index abb20371b95..8f32cc7c68b 100644
--- a/src/server/session.ts
+++ b/src/server/session.ts
@@ -286,6 +286,16 @@ namespace ts.server {
: deduplicate(outputs, areEqual);
}
+ function combineProjectOutputFromEveryProject(projectService: ProjectService, action: (project: Project) => ReadonlyArray, areEqual: (a: T, b: T) => boolean) {
+ const outputs: T[] = [];
+ projectService.forEachProject(project => {
+ if (project.isOrphan() || !project.languageServiceEnabled) return;
+ const theseOutputs = action(project);
+ outputs.push(...theseOutputs.filter(output => !outputs.some(o => areEqual(o, output))));
+ });
+ return outputs;
+ }
+
function combineProjectOutputWhileOpeningReferencedProjects(
projects: Projects,
projectService: ProjectService,
@@ -1749,19 +1759,11 @@ namespace ts.server {
const newPath = toNormalizedPath(args.newFilePath);
const formatOptions = this.getHostFormatOptions();
const preferences = this.getHostPreferences();
-
- const changes: (protocol.FileCodeEdits | FileTextChanges)[] = [];
- this.projectService.forEachProject(project => {
- if (project.isOrphan() || !project.languageServiceEnabled) return;
- for (const fileTextChanges of project.getLanguageService().getEditsForFileRename(oldPath, newPath, formatOptions, preferences)) {
- // Subsequent projects may make conflicting edits to the same file -- just go with the first.
- if (!changes.some(f => f.fileName === fileTextChanges.fileName)) {
- changes.push(simplifiedResult ? this.mapTextChangeToCodeEdit(project, fileTextChanges) : fileTextChanges);
- }
- }
- });
-
- return changes as ReadonlyArray | ReadonlyArray;
+ const changes = combineProjectOutputFromEveryProject(
+ this.projectService,
+ project => project.getLanguageService().getEditsForFileRename(oldPath, newPath, formatOptions, preferences),
+ (a, b) => a.fileName === b.fileName);
+ return simplifiedResult ? changes.map(c => this.mapTextChangeToCodeEditUsingScriptInfo(c)) : changes;
}
private getCodeFixes(args: protocol.CodeFixRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray | undefined {
@@ -1835,8 +1837,15 @@ namespace ts.server {
}
private mapTextChangeToCodeEdit(project: Project, change: FileTextChanges): protocol.FileCodeEdits {
- const path = normalizedPathToPath(toNormalizedPath(change.fileName), this.host.getCurrentDirectory(), fileName => this.getCanonicalFileName(fileName));
- return mapTextChangesToCodeEdits(change, project.getSourceFileOrConfigFile(path));
+ return mapTextChangesToCodeEdits(change, project.getSourceFileOrConfigFile(this.normalizePath(change.fileName)));
+ }
+
+ private mapTextChangeToCodeEditUsingScriptInfo(change: FileTextChanges): protocol.FileCodeEdits {
+ return mapTextChangesToCodeEditsUsingScriptInfo(change, this.projectService.getScriptInfo(this.normalizePath(change.fileName)));
+ }
+
+ private normalizePath(fileName: string) {
+ return normalizedPathToPath(toNormalizedPath(fileName), this.host.getCurrentDirectory(), fileName => this.getCanonicalFileName(fileName));
}
private convertTextChangeToCodeEdit(change: TextChange, scriptInfo: ScriptInfo): protocol.CodeEdit {
@@ -2361,6 +2370,13 @@ namespace ts.server {
}
}
+ function mapTextChangesToCodeEditsUsingScriptInfo(textChanges: FileTextChanges, scriptInfo: ScriptInfo | undefined): protocol.FileCodeEdits {
+ Debug.assert(!!textChanges.isNewFile === !scriptInfo);
+ return scriptInfo
+ ? { fileName: textChanges.fileName, textChanges: textChanges.textChanges.map(textChange => convertTextChangeToCodeEditUsingScriptInfo(textChange, scriptInfo)) }
+ : convertNewFileTextChangeToCodeEdit(textChanges);
+ }
+
function convertTextChangeToCodeEdit(change: TextChange, sourceFile: SourceFile): protocol.CodeEdit {
return {
start: convertToLocation(sourceFile.getLineAndCharacterOfPosition(change.span.start)),
@@ -2369,6 +2385,10 @@ namespace ts.server {
};
}
+ function convertTextChangeToCodeEditUsingScriptInfo(change: TextChange, scriptInfo: ScriptInfo) {
+ return { start: scriptInfo.positionToLineOffset(change.span.start), end: scriptInfo.positionToLineOffset(textSpanEnd(change.span)), newText: change.newText };
+ }
+
function convertNewFileTextChangeToCodeEdit(textChanges: FileTextChanges): protocol.FileCodeEdits {
Debug.assert(textChanges.textChanges.length === 1);
const change = first(textChanges.textChanges);
diff --git a/src/services/codefixes/fixUnreachableCode.ts b/src/services/codefixes/fixUnreachableCode.ts
index 1c6c53efe8e..4fdb2c2016f 100644
--- a/src/services/codefixes/fixUnreachableCode.ts
+++ b/src/services/codefixes/fixUnreachableCode.ts
@@ -5,14 +5,14 @@ namespace ts.codefix {
registerCodeFix({
errorCodes,
getCodeActions(context) {
- const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start));
+ const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start, context.span.length));
return [createCodeFixAction(fixId, changes, Diagnostics.Remove_unreachable_code, fixId, Diagnostics.Remove_all_unreachable_code)];
},
fixIds: [fixId],
- getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, diag.start)),
+ getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, diag.start, diag.length)),
});
- function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number): void {
+ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number, length: number): void {
const token = getTokenAtPosition(sourceFile, start);
const statement = findAncestor(token, isStatement)!;
Debug.assert(statement.getStart(sourceFile) === token.getStart(sourceFile));
@@ -36,7 +36,9 @@ namespace ts.codefix {
break;
default:
if (isBlock(statement.parent)) {
- split(sliceAfter(statement.parent.statements, statement), shouldRemove, (start, end) => changes.deleteNodeRange(sourceFile, start, end));
+ const end = start + length;
+ const lastStatement = Debug.assertDefined(lastWhere(sliceAfter(statement.parent.statements, statement), s => s.pos < end));
+ changes.deleteNodeRange(sourceFile, statement, lastStatement);
}
else {
changes.delete(sourceFile, statement);
@@ -44,35 +46,12 @@ namespace ts.codefix {
}
}
- function shouldRemove(s: Statement): boolean {
- // Don't remove statements that can validly be used before they appear.
- return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) &&
- // `var x;` may declare a variable used above
- !(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.Let | NodeFlags.Const)) && s.declarationList.declarations.some(d => !d.initializer));
- }
-
- function isPurelyTypeDeclaration(s: Statement): boolean {
- switch (s.kind) {
- case SyntaxKind.InterfaceDeclaration:
- case SyntaxKind.TypeAliasDeclaration:
- return true;
- case SyntaxKind.ModuleDeclaration:
- return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated;
- case SyntaxKind.EnumDeclaration:
- return hasModifier(s, ModifierFlags.Const);
- default:
- return false;
+ function lastWhere(a: ReadonlyArray, pred: (value: T) => boolean): T | undefined {
+ let last: T | undefined;
+ for (const value of a) {
+ if (!pred(value)) break;
+ last = value;
}
- }
-
- function sliceAfter(arr: ReadonlyArray, value: T): ReadonlyArray {
- const index = arr.indexOf(value);
- Debug.assert(index !== -1);
- return arr.slice(index);
- }
-
- // Calls 'cb' with the start and end of each range where 'pred' is true.
- function split(arr: ReadonlyArray, pred: (t: T) => boolean, cb: (start: T, end: T) => void): void {
- getRangesWhere(arr, pred, (start, afterEnd) => cb(arr[start], arr[afterEnd - 1]));
+ return last;
}
}
diff --git a/src/services/completions.ts b/src/services/completions.ts
index c049c1e4dae..8939fea2012 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -2,12 +2,20 @@
namespace ts.Completions {
export type Log = (message: string) => void;
- type SymbolOriginInfo = { type: "this-type" } | { type: "symbol-member" } | SymbolOriginInfoExport;
+ const enum SymbolOriginInfoKind { ThisType, SymbolMemberNoExport, SymbolMemberExport, Export }
+ type SymbolOriginInfo = { kind: SymbolOriginInfoKind.ThisType } | { kind: SymbolOriginInfoKind.SymbolMemberNoExport } | SymbolOriginInfoExport;
interface SymbolOriginInfoExport {
- type: "export";
+ kind: SymbolOriginInfoKind.SymbolMemberExport | SymbolOriginInfoKind.Export;
moduleSymbol: Symbol;
isDefaultExport: boolean;
}
+ function originIsSymbolMember(origin: SymbolOriginInfo): boolean {
+ return origin.kind === SymbolOriginInfoKind.SymbolMemberExport || origin.kind === SymbolOriginInfoKind.SymbolMemberNoExport;
+ }
+ function originIsExport(origin: SymbolOriginInfo): origin is SymbolOriginInfoExport {
+ return origin.kind === SymbolOriginInfoKind.SymbolMemberExport || origin.kind === SymbolOriginInfoKind.Export;
+ }
+
/**
* Map from symbol id -> SymbolOriginInfo.
* Only populated for symbols that come from other modules.
@@ -214,12 +222,12 @@ namespace ts.Completions {
let insertText: string | undefined;
let replacementSpan: TextSpan | undefined;
- if (origin && origin.type === "this-type") {
+ if (origin && origin.kind === SymbolOriginInfoKind.ThisType) {
insertText = needsConvertPropertyAccess ? `this[${quote(name, preferences)}]` : `this.${name}`;
}
// We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790.
// Somehow there was a global with a non-identifier name. Hopefully someone will complain about getting a "foo bar" global completion and provide a repro.
- else if ((origin && origin.type === "symbol-member" || needsConvertPropertyAccess) && propertyAccessToConvert) {
+ else if ((origin && originIsSymbolMember(origin) || needsConvertPropertyAccess) && propertyAccessToConvert) {
insertText = needsConvertPropertyAccess ? `[${quote(name, preferences)}]` : `[${name}]`;
const dot = findChildOfKind(propertyAccessToConvert, SyntaxKind.DotToken, sourceFile)!;
// If the text after the '.' starts with this name, write over it. Else, add new text.
@@ -253,7 +261,7 @@ namespace ts.Completions {
kindModifiers: SymbolDisplay.getSymbolModifiers(symbol),
sortText: "0",
source: getSourceFromOrigin(origin),
- hasAction: trueOrUndefined(!!origin && origin.type === "export"),
+ hasAction: trueOrUndefined(!!origin && originIsExport(origin)),
isRecommended: trueOrUndefined(isRecommendedCompletionMatch(symbol, recommendedCompletion, typeChecker)),
insertText,
replacementSpan,
@@ -283,7 +291,7 @@ namespace ts.Completions {
}
function getSourceFromOrigin(origin: SymbolOriginInfo | undefined): string | undefined {
- return origin && origin.type === "export" ? stripQuotes(origin.moduleSymbol.name) : undefined;
+ return origin && originIsExport(origin) ? stripQuotes(origin.moduleSymbol.name) : undefined;
}
function getCompletionEntriesFromSymbols(
@@ -529,7 +537,7 @@ namespace ts.Completions {
}
function getSymbolName(symbol: Symbol, origin: SymbolOriginInfo | undefined, target: ScriptTarget): string {
- return origin && origin.type === "export" && origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default
+ return origin && originIsExport(origin) && origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default
// 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)
@@ -648,7 +656,7 @@ namespace ts.Completions {
preferences: UserPreferences,
): CodeActionsAndSourceDisplay {
const symbolOriginInfo = symbolToOriginInfoMap[getSymbolId(symbol)];
- if (!symbolOriginInfo || symbolOriginInfo.type !== "export") {
+ if (!symbolOriginInfo || !originIsExport(symbolOriginInfo)) {
return { codeActions: undefined, sourceDisplay: undefined };
}
@@ -1124,7 +1132,9 @@ namespace ts.Completions {
const firstAccessibleSymbol = nameSymbol && getFirstSymbolInChain(nameSymbol, contextToken, typeChecker);
if (firstAccessibleSymbol && !symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)]) {
symbols.push(firstAccessibleSymbol);
- symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)] = { type: "symbol-member" };
+ const moduleSymbol = firstAccessibleSymbol.parent;
+ symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)] =
+ !moduleSymbol || !isExternalModuleSymbol(moduleSymbol) ? { kind: SymbolOriginInfoKind.SymbolMemberNoExport } : { kind: SymbolOriginInfoKind.SymbolMemberExport, moduleSymbol, isDefaultExport: false };
}
}
else {
@@ -1222,7 +1232,7 @@ namespace ts.Completions {
const thisType = typeChecker.tryGetThisTypeAt(scopeNode);
if (thisType) {
for (const symbol of getPropertiesForCompletion(thisType, typeChecker)) {
- symbolToOriginInfoMap[getSymbolId(symbol)] = { type: "this-type" };
+ symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.ThisType };
symbols.push(symbol);
}
}
@@ -1374,7 +1384,7 @@ namespace ts.Completions {
symbol = getLocalSymbolForExportDefault(symbol) || symbol;
}
- const origin: SymbolOriginInfo = { type: "export", moduleSymbol, isDefaultExport };
+ const origin: SymbolOriginInfoExport = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport };
if (detailsEntryId || stringContainsCharactersInOrder(getSymbolName(symbol, origin, target).toLowerCase(), tokenTextLowerCase)) {
symbols.push(symbol);
symbolToOriginInfoMap[getSymbolId(symbol)] = origin;
diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts
index 6bc55f54ff7..35da81535fe 100644
--- a/src/services/findAllReferences.ts
+++ b/src/services/findAllReferences.ts
@@ -234,7 +234,8 @@ namespace ts.FindAllReferences.Core {
export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray, cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlyMap = arrayToSet(sourceFiles, f => f.fileName)): SymbolAndEntries[] | undefined {
if (isSourceFile(node)) {
const reference = GoToDefinition.getReferenceAtPosition(node, position, program);
- return reference && getReferencedSymbolsForModule(program, program.getTypeChecker().getMergedSymbol(reference.file.symbol), /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet);
+ const moduleSymbol = reference && program.getTypeChecker().getMergedSymbol(reference.file.symbol);
+ return moduleSymbol && getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet);
}
if (!options.implementations) {
@@ -703,7 +704,7 @@ namespace ts.FindAllReferences.Core {
- But if the parent has `export as namespace`, the symbol is globally visible through that namespace.
*/
const exposedByParent = parent && !(symbol.flags & SymbolFlags.TypeParameter);
- if (exposedByParent && !((parent!.flags & SymbolFlags.Module) && isExternalModuleSymbol(parent!) && !parent!.globalExports)) {
+ if (exposedByParent && !(isExternalModuleSymbol(parent!) && !parent!.globalExports)) {
return undefined;
}
diff --git a/src/services/getEditsForFileRename.ts b/src/services/getEditsForFileRename.ts
index bfb737ef03c..932c777907e 100644
--- a/src/services/getEditsForFileRename.ts
+++ b/src/services/getEditsForFileRename.ts
@@ -1,10 +1,18 @@
/* @internal */
namespace ts {
- export function getEditsForFileRename(program: Program, oldFileOrDirPath: string, newFileOrDirPath: string, host: LanguageServiceHost, formatContext: formatting.FormatContext, preferences: UserPreferences): ReadonlyArray {
+ export function getEditsForFileRename(
+ program: Program,
+ oldFileOrDirPath: string,
+ newFileOrDirPath: string,
+ host: LanguageServiceHost,
+ formatContext: formatting.FormatContext,
+ preferences: UserPreferences,
+ sourceMapper: SourceMapper,
+ ): ReadonlyArray {
const useCaseSensitiveFileNames = hostUsesCaseSensitiveFileNames(host);
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
- const oldToNew = getPathUpdater(oldFileOrDirPath, newFileOrDirPath, getCanonicalFileName);
- const newToOld = getPathUpdater(newFileOrDirPath, oldFileOrDirPath, getCanonicalFileName);
+ const oldToNew = getPathUpdater(oldFileOrDirPath, newFileOrDirPath, getCanonicalFileName, sourceMapper);
+ const newToOld = getPathUpdater(newFileOrDirPath, oldFileOrDirPath, getCanonicalFileName, sourceMapper);
return textChanges.ChangeTracker.with({ host, formatContext }, changeTracker => {
updateTsconfigFiles(program, changeTracker, oldToNew, newFileOrDirPath, host.getCurrentDirectory(), useCaseSensitiveFileNames);
updateImports(program, changeTracker, oldToNew, newToOld, host, getCanonicalFileName, preferences);
@@ -14,13 +22,27 @@ namespace ts {
/** If 'path' refers to an old directory, returns path in the new directory. */
type PathUpdater = (path: string) => string | undefined;
// exported for tests
- export function getPathUpdater(oldFileOrDirPath: string, newFileOrDirPath: string, getCanonicalFileName: GetCanonicalFileName): PathUpdater {
+ export function getPathUpdater(oldFileOrDirPath: string, newFileOrDirPath: string, getCanonicalFileName: GetCanonicalFileName, sourceMapper: SourceMapper | undefined): PathUpdater {
const canonicalOldPath = getCanonicalFileName(oldFileOrDirPath);
return path => {
- if (getCanonicalFileName(path) === canonicalOldPath) return newFileOrDirPath;
- const suffix = tryRemoveDirectoryPrefix(path, canonicalOldPath, getCanonicalFileName);
- return suffix === undefined ? undefined : newFileOrDirPath + "/" + suffix;
+ const originalPath = sourceMapper && sourceMapper.tryGetOriginalLocation({ fileName: path, position: 0 });
+ const updatedPath = getUpdatedPath(originalPath ? originalPath.fileName : path);
+ return originalPath
+ ? updatedPath === undefined ? undefined : makeCorrespondingRelativeChange(originalPath.fileName, updatedPath, path, getCanonicalFileName)
+ : updatedPath;
};
+
+ function getUpdatedPath(pathToUpdate: string): string | undefined {
+ if (getCanonicalFileName(pathToUpdate) === canonicalOldPath) return newFileOrDirPath;
+ const suffix = tryRemoveDirectoryPrefix(pathToUpdate, canonicalOldPath, getCanonicalFileName);
+ return suffix === undefined ? undefined : newFileOrDirPath + "/" + suffix;
+ }
+ }
+
+ // Relative path from a0 to b0 should be same as relative path from a1 to b1. Returns b1.
+ function makeCorrespondingRelativeChange(a0: string, b0: string, a1: string, getCanonicalFileName: GetCanonicalFileName): string {
+ const rel = getRelativePathFromFile(a0, b0, getCanonicalFileName);
+ return combinePathsSafe(getDirectoryPath(a1), rel);
}
function updateTsconfigFiles(program: Program, changeTracker: textChanges.ChangeTracker, oldToNew: PathUpdater, newFileOrDirPath: string, currentDirectory: string, useCaseSensitiveFileNames: boolean): void {
diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts
index bae6bc7222c..9d2c61a1a7d 100644
--- a/src/services/outliningElementsCollector.ts
+++ b/src/services/outliningElementsCollector.ts
@@ -183,6 +183,26 @@ namespace ts.OutliningElementsCollector {
return spanForObjectOrArrayLiteral(n);
case SyntaxKind.ArrayLiteralExpression:
return spanForObjectOrArrayLiteral(n, SyntaxKind.OpenBracketToken);
+ case SyntaxKind.JsxElement:
+ return spanForJSXElement(n);
+ case SyntaxKind.JsxSelfClosingElement:
+ case SyntaxKind.JsxOpeningElement:
+ return spanForJSXAttributes((n).attributes);
+ }
+
+ function spanForJSXElement(node: JsxElement): OutliningSpan | undefined {
+ const textSpan = createTextSpanFromBounds(node.openingElement.getStart(sourceFile), node.closingElement.getEnd());
+ const tagName = node.openingElement.tagName.getText(sourceFile);
+ const bannerText = "<" + tagName + ">..." + tagName + ">";
+ return createOutliningSpan(textSpan, OutliningSpanKind.Code, textSpan, /*autoCollapse*/ false, bannerText);
+ }
+
+ function spanForJSXAttributes(node: JsxAttributes): OutliningSpan | undefined {
+ if (node.properties.length === 0) {
+ return undefined;
+ }
+
+ return createOutliningSpanFromBounds(node.getStart(sourceFile), node.getEnd(), OutliningSpanKind.Code);
}
function spanForObjectOrArrayLiteral(node: Node, open: SyntaxKind.OpenBraceToken | SyntaxKind.OpenBracketToken = SyntaxKind.OpenBraceToken): OutliningSpan | undefined {
diff --git a/src/services/services.ts b/src/services/services.ts
index d294f53830c..ceda2a744a2 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -1537,7 +1537,8 @@ namespace ts {
}
function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray): DocumentHighlights[] | undefined {
- Debug.assert(filesToSearch.some(f => normalizePath(f) === fileName));
+ const normalizedFileName = normalizePath(fileName);
+ Debug.assert(filesToSearch.some(f => normalizePath(f) === normalizedFileName));
synchronizeHostData();
const sourceFilesToSearch = map(filesToSearch, f => Debug.assertDefined(program.getSourceFile(f)));
const sourceFile = getValidSourceFile(fileName);
@@ -1812,7 +1813,7 @@ namespace ts {
}
function getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): ReadonlyArray {
- return ts.getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, formatting.getFormatContext(formatOptions), preferences);
+ return ts.getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, formatting.getFormatContext(formatOptions), preferences, sourceMapper);
}
function applyCodeActionCommand(action: CodeActionCommand): Promise;
@@ -1883,11 +1884,16 @@ namespace ts {
if (!token) return undefined;
const element = token.kind === SyntaxKind.GreaterThanToken && isJsxOpeningElement(token.parent) ? token.parent.parent
: isJsxText(token) ? token.parent : undefined;
- if (element && !tagNamesAreEquivalent(element.openingElement.tagName, element.closingElement.tagName)) {
+ if (element && isUnclosedTag(element)) {
return { newText: `${element.openingElement.tagName.getText(sourceFile)}>` };
}
}
+ function isUnclosedTag({ openingElement, closingElement, parent }: JsxElement): boolean {
+ return !tagNamesAreEquivalent(openingElement.tagName, closingElement.tagName) ||
+ isJsxElement(parent) && tagNamesAreEquivalent(openingElement.tagName, parent.openingElement.tagName) && isUnclosedTag(parent);
+ }
+
function getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined {
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
const range = formatting.getRangeOfEnclosingComment(sourceFile, position);
diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts
index 2884f7a7a0c..491c905785e 100644
--- a/src/services/signatureHelp.ts
+++ b/src/services/signatureHelp.ts
@@ -29,9 +29,12 @@ namespace ts.SignatureHelp {
return undefined;
}
- if (shouldCarefullyCheckContext(triggerReason)) {
- // In the middle of a string, don't provide signature help unless the user explicitly requested it.
- if (isInString(sourceFile, position, startingToken)) {
+ // Only need to be careful if the user typed a character and signature help wasn't showing.
+ const shouldCarefullyCheckContext = !!triggerReason && triggerReason.kind === "characterTyped";
+
+ // Bail out quickly in the middle of a string or comment, don't provide signature help unless the user explicitly requested it.
+ if (shouldCarefullyCheckContext) {
+ if (isInString(sourceFile, position, startingToken) || isInComment(sourceFile, position)) {
return undefined;
}
}
@@ -41,8 +44,8 @@ namespace ts.SignatureHelp {
cancellationToken.throwIfCancellationRequested();
- // Semantic filtering of signature help
- const candidateInfo = getCandidateInfo(argumentInfo, typeChecker);
+ // Extra syntactic and semantic filtering of signature help
+ const candidateInfo = getCandidateInfo(argumentInfo, typeChecker, sourceFile, startingToken, shouldCarefullyCheckContext);
cancellationToken.throwIfCancellationRequested();
if (!candidateInfo) {
@@ -57,24 +60,57 @@ namespace ts.SignatureHelp {
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker => createSignatureHelpItems(candidateInfo.candidates, candidateInfo.resolvedSignature, argumentInfo, sourceFile, typeChecker));
}
- function shouldCarefullyCheckContext(reason: SignatureHelpTriggerReason | undefined) {
- // Only need to be careful if the user typed a character and signature help wasn't showing.
- return !!reason && reason.kind === "characterTyped";
- }
+ function getCandidateInfo(
+ argumentInfo: ArgumentListInfo, checker: TypeChecker, sourceFile: SourceFile, startingToken: Node, onlyUseSyntacticOwners: boolean):
+ { readonly candidates: ReadonlyArray, readonly resolvedSignature: Signature } | undefined {
- function getCandidateInfo(argumentInfo: ArgumentListInfo, checker: TypeChecker): { readonly candidates: ReadonlyArray, readonly resolvedSignature: Signature } | undefined {
const { invocation } = argumentInfo;
if (invocation.kind === InvocationKind.Call) {
+ if (onlyUseSyntacticOwners) {
+ if (isCallOrNewExpression(invocation.node)) {
+ const invocationChildren = invocation.node.getChildren(sourceFile);
+ switch (startingToken.kind) {
+ case SyntaxKind.OpenParenToken:
+ if (!contains(invocationChildren, startingToken)) {
+ return undefined;
+ }
+ break;
+ case SyntaxKind.CommaToken:
+ const containingList = findContainingList(startingToken);
+ if (!containingList || !contains(invocationChildren, findContainingList(startingToken))) {
+ return undefined;
+ }
+ break;
+ case SyntaxKind.LessThanToken:
+ if (!lessThanFollowsCalledExpression(startingToken, sourceFile, invocation.node.expression)) {
+ return undefined;
+ }
+ break;
+ default:
+ return undefined;
+ }
+ }
+ else {
+ return undefined;
+ }
+ }
+
const candidates: Signature[] = [];
const resolvedSignature = checker.getResolvedSignature(invocation.node, candidates, argumentInfo.argumentCount)!; // TODO: GH#18217
return candidates.length === 0 ? undefined : { candidates, resolvedSignature };
}
- else {
+ else if (invocation.kind === InvocationKind.TypeArgs) {
+ if (onlyUseSyntacticOwners && !lessThanFollowsCalledExpression(startingToken, sourceFile, invocation.called)) {
+ return undefined;
+ }
const type = checker.getTypeAtLocation(invocation.called)!; // TODO: GH#18217
const signatures = isNewExpression(invocation.called.parent) ? type.getConstructSignatures() : type.getCallSignatures();
const candidates = signatures.filter(candidate => !!candidate.typeParameters && candidate.typeParameters.length >= argumentInfo.argumentCount);
return candidates.length === 0 ? undefined : { candidates, resolvedSignature: first(candidates) };
}
+ else {
+ Debug.assertNever(invocation);
+ }
}
function createJavaScriptSignatureHelpItems(argumentInfo: ArgumentListInfo, program: Program, cancellationToken: CancellationToken): SignatureHelpItems | undefined {
@@ -107,6 +143,14 @@ namespace ts.SignatureHelp {
}
}
+ function lessThanFollowsCalledExpression(startingToken: Node, sourceFile: SourceFile, calledExpression: Expression) {
+ const precedingToken = Debug.assertDefined(
+ findPrecedingToken(startingToken.getFullStart(), sourceFile, startingToken.parent, /*excludeJsdoc*/ true)
+ );
+
+ return rangeContainsRange(calledExpression, precedingToken);
+ }
+
export interface ArgumentInfoForCompletions {
readonly invocation: CallLikeExpression;
readonly argumentIndex: number;
diff --git a/src/services/utilities.ts b/src/services/utilities.ts
index 4cb0672518a..dc213567830 100644
--- a/src/services/utilities.ts
+++ b/src/services/utilities.ts
@@ -679,7 +679,7 @@ namespace ts {
let current: Node = sourceFile;
outer: while (true) {
// find the child that contains 'position'
- for (const child of current.getChildren()) {
+ for (const child of current.getChildren(sourceFile)) {
const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, /*includeJsDoc*/ true);
if (start > position) {
// If this child begins after position, then all subsequent children will as well.
@@ -1184,8 +1184,7 @@ namespace ts {
/** True if the symbol is for an external module, as opposed to a namespace. */
export function isExternalModuleSymbol(moduleSymbol: Symbol): boolean {
- Debug.assert(!!(moduleSymbol.flags & SymbolFlags.Module));
- return moduleSymbol.name.charCodeAt(0) === CharacterCodes.doubleQuote;
+ return !!(moduleSymbol.flags & SymbolFlags.Module) && moduleSymbol.name.charCodeAt(0) === CharacterCodes.doubleQuote;
}
/** Returns `true` the first time it encounters a node and `false` afterwards. */
diff --git a/src/testRunner/unittests/projectReferences.ts b/src/testRunner/unittests/projectReferences.ts
index 070d26cccd5..4702c0c26ce 100644
--- a/src/testRunner/unittests/projectReferences.ts
+++ b/src/testRunner/unittests/projectReferences.ts
@@ -285,4 +285,23 @@ namespace ts {
});
});
+ describe("errors when a file in a composite project occurs outside the root", () => {
+ it("Errors when a file is outside the rootdir", () => {
+ const spec: TestSpecification = {
+ "/alpha": {
+ files: { "/alpha/src/a.ts": "import * from '../../beta/b'", "/beta/b.ts": "export { }" },
+ options: {
+ declaration: true,
+ outDir: "bin"
+ },
+ references: []
+ }
+ };
+ testProjectReferences(spec, "/alpha/tsconfig.json", (program) => {
+ assertHasError("Issues an error about the rootDir", program.getOptionsDiagnostics(), Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files);
+ assertHasError("Issues an error about the fileList", program.getOptionsDiagnostics(), Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern);
+ });
+ });
+ });
+
}
diff --git a/src/testRunner/unittests/tsserverProjectSystem.ts b/src/testRunner/unittests/tsserverProjectSystem.ts
index 9ffc98c8c20..b989327a9c9 100644
--- a/src/testRunner/unittests/tsserverProjectSystem.ts
+++ b/src/testRunner/unittests/tsserverProjectSystem.ts
@@ -8486,7 +8486,7 @@ new C();`
});
});
- it("when watching directories for failed lookup locations in amd resolution", () => {
+ describe("when watching directories for failed lookup locations in amd resolution", () => {
const projectRoot = "/user/username/projects/project";
const nodeFile: File = {
path: `${projectRoot}/src/typings/node.d.ts`,
@@ -8530,19 +8530,35 @@ export const x = 10;`
}
})
};
- const files = [nodeFile, electronFile, srcFile, moduleFile, configFile, libFile];
- const host = createServerHost(files);
- const service = createProjectService(host);
- service.openClientFile(srcFile.path, srcFile.content, ScriptKind.TS, projectRoot);
- checkProjectActualFiles(service.configuredProjects.get(configFile.path)!, files.map(f => f.path));
- checkWatchedFilesDetailed(host, mapDefined(files, f => f === srcFile ? undefined : f.path), 1);
- checkWatchedDirectoriesDetailed(host, [`${projectRoot}`], 1, /*recursive*/ false); // failed lookup for fs
- const expectedWatchedDirectories = createMap();
- expectedWatchedDirectories.set(`${projectRoot}/src`, 2); // Wild card and failed lookup
- expectedWatchedDirectories.set(`${projectRoot}/somefolder`, 1); // failed lookup for somefolder/module2
- expectedWatchedDirectories.set(`${projectRoot}/node_modules`, 1); // failed lookup for with node_modules/@types/fs
- expectedWatchedDirectories.set(`${projectRoot}/src/typings`, 1); // typeroot directory
- checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, /*recursive*/ true);
+
+ function verifyModuleResolution(useNodeFile: boolean) {
+ const files = [...(useNodeFile ? [nodeFile] : []), electronFile, srcFile, moduleFile, configFile, libFile];
+ const host = createServerHost(files);
+ const service = createProjectService(host);
+ service.openClientFile(srcFile.path, srcFile.content, ScriptKind.TS, projectRoot);
+ checkProjectActualFiles(service.configuredProjects.get(configFile.path)!, files.map(f => f.path));
+ checkWatchedFilesDetailed(host, mapDefined(files, f => f === srcFile ? undefined : f.path), 1);
+ if (useNodeFile) {
+ checkWatchedDirectories(host, emptyArray, /*recursive*/ false); // since fs resolves to ambient module, shouldnt watch failed lookup
+ }
+ else {
+ checkWatchedDirectoriesDetailed(host, [`${projectRoot}`], 1, /*recursive*/ false); // failed lookup for fs
+ }
+ const expectedWatchedDirectories = createMap();
+ expectedWatchedDirectories.set(`${projectRoot}/src`, 2); // Wild card and failed lookup
+ expectedWatchedDirectories.set(`${projectRoot}/somefolder`, 1); // failed lookup for somefolder/module2
+ expectedWatchedDirectories.set(`${projectRoot}/node_modules`, 1); // failed lookup for with node_modules/@types/fs
+ expectedWatchedDirectories.set(`${projectRoot}/src/typings`, 1); // typeroot directory
+ checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, /*recursive*/ true);
+ }
+
+ it("when resolves to ambient module", () => {
+ verifyModuleResolution(/*useNodeFile*/ true);
+ });
+
+ it("when resolution fails", () => {
+ verifyModuleResolution(/*useNodeFile*/ false);
+ });
});
});
@@ -9194,6 +9210,22 @@ export function Test2() {
});
});
+
+ it("getEditsForFileRename", () => {
+ const { session, aTs, userTs } = makeSampleProjects();
+ const response = executeSessionRequest(session, protocol.CommandTypes.GetEditsForFileRename, {
+ oldFilePath: aTs.path,
+ newFilePath: "/a/aNew.ts",
+ });
+ assert.deepEqual>(response, [
+ {
+ fileName: userTs.path,
+ textChanges: [
+ { ...protocolTextSpanFromSubstring(userTs.content, "../a/bin/a"), newText: "../a/bin/aNew" },
+ ],
+ },
+ ]);
+ });
});
function makeReferenceItem(file: File, isDefinition: boolean, text: string, lineText: string, options?: SpanFromSubstringOptions): protocol.ReferencesResponseItem {
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index 414a2a889f6..99fa48091b5 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -1933,9 +1933,6 @@ declare namespace ts {
getAmbientModules(): Symbol[];
tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
getApparentType(type: Type): Type;
- getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
- getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
- getSuggestionForNonexistentExport(node: Identifier, target: Symbol): string | undefined;
getBaseConstraintOfType(type: Type): Type | undefined;
getDefaultFromTypeParameter(type: Type): Type | undefined;
/**
@@ -8900,6 +8897,8 @@ declare namespace ts.server {
private mapCodeFixAction;
private mapTextChangesToCodeEdits;
private mapTextChangeToCodeEdit;
+ private mapTextChangeToCodeEditUsingScriptInfo;
+ private normalizePath;
private convertTextChangeToCodeEdit;
private getBraceMatching;
private getDiagnosticsForProject;
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index 50bca4eea4f..c3508ec353c 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -1933,9 +1933,6 @@ declare namespace ts {
getAmbientModules(): Symbol[];
tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
getApparentType(type: Type): Type;
- getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
- getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
- getSuggestionForNonexistentExport(node: Identifier, target: Symbol): string | undefined;
getBaseConstraintOfType(type: Type): Type | undefined;
getDefaultFromTypeParameter(type: Type): Type | undefined;
/**
diff --git a/tests/baselines/reference/cf.errors.txt b/tests/baselines/reference/cf.errors.txt
index 8f5f82f8169..910ffe5d88d 100644
--- a/tests/baselines/reference/cf.errors.txt
+++ b/tests/baselines/reference/cf.errors.txt
@@ -14,7 +14,7 @@ tests/cases/compiler/cf.ts(36,13): error TS7027: Unreachable code detected.
if (y==7) {
continue L1;
x=11;
- ~
+ ~~~~~
!!! error TS7027: Unreachable code detected.
}
if (y==3) {
@@ -28,7 +28,7 @@ tests/cases/compiler/cf.ts(36,13): error TS7027: Unreachable code detected.
if (y==20) {
break;
x=12;
- ~
+ ~~~~~
!!! error TS7027: Unreachable code detected.
}
} while (y<41);
@@ -41,13 +41,13 @@ tests/cases/compiler/cf.ts(36,13): error TS7027: Unreachable code detected.
L3: if (xTest : Symbol(Test, Decl(bug25434.js, 0, 0))
+>b : Symbol(b, Decl(bug25434.js, 1, 15))
+
+Test(({ b = '5' } = {}));
+>Test : Symbol(Test, Decl(bug25434.js, 0, 0))
+>b : Symbol(b, Decl(bug25434.js, 3, 7))
+
diff --git a/tests/baselines/reference/checkDestructuringShorthandAssigment.types b/tests/baselines/reference/checkDestructuringShorthandAssigment.types
new file mode 100644
index 00000000000..9eccb886051
--- /dev/null
+++ b/tests/baselines/reference/checkDestructuringShorthandAssigment.types
@@ -0,0 +1,17 @@
+=== tests/cases/compiler/bug25434.js ===
+// should not crash while checking
+function Test({ b = '' } = {}) {}
+>Test : ({ b }?: { [x: string]: any; }) => void
+>b : string
+>'' : ""
+>{} : { b?: string; }
+
+Test(({ b = '5' } = {}));
+>Test(({ b = '5' } = {})) : void
+>Test : ({ b }?: { [x: string]: any; }) => void
+>({ b = '5' } = {}) : { b?: any; }
+>{ b = '5' } = {} : { b?: any; }
+>{ b = '5' } : { [x: string]: any; b?: any; }
+>b : any
+>{} : { b?: any; }
+
diff --git a/tests/baselines/reference/errorElaboration.errors.txt b/tests/baselines/reference/errorElaboration.errors.txt
index 94742d7d98b..57971cad269 100644
--- a/tests/baselines/reference/errorElaboration.errors.txt
+++ b/tests/baselines/reference/errorElaboration.errors.txt
@@ -2,9 +2,10 @@ tests/cases/compiler/errorElaboration.ts(12,5): error TS2345: Argument of type '
Type 'Container[>' is not assignable to type 'Container][>'.
Type 'Ref' is not assignable to type 'Ref'.
Type 'string' is not assignable to type 'number'.
+tests/cases/compiler/errorElaboration.ts(17,11): error TS2322: Type '"bar"' is not assignable to type '"foo"'.
-==== tests/cases/compiler/errorElaboration.ts (1 errors) ====
+==== tests/cases/compiler/errorElaboration.ts (2 errors) ====
// Repro for #5712
interface Ref {
@@ -22,4 +23,13 @@ tests/cases/compiler/errorElaboration.ts(12,5): error TS2345: Argument of type '
!!! error TS2345: Type 'Container][>' is not assignable to type 'Container][>'.
!!! error TS2345: Type 'Ref' is not assignable to type 'Ref'.
!!! error TS2345: Type 'string' is not assignable to type 'number'.
+
+ // Repro for #25498
+
+ function test(): {[A in "foo"]: A} {
+ return {foo: "bar"};
+ ~~~
+!!! error TS2322: Type '"bar"' is not assignable to type '"foo"'.
+!!! related TS6500 tests/cases/compiler/errorElaboration.ts:16:18: The expected type comes from property 'foo' which is declared here on type '{ foo: "foo"; }'
+ }
\ No newline at end of file
diff --git a/tests/baselines/reference/errorElaboration.js b/tests/baselines/reference/errorElaboration.js
index eed8bbd7c6b..c1dd0a470d3 100644
--- a/tests/baselines/reference/errorElaboration.js
+++ b/tests/baselines/reference/errorElaboration.js
@@ -11,9 +11,19 @@ interface Container {
declare function foo(x: () => Container][>): void;
let a: () => Container][>;
foo(a);
+
+// Repro for #25498
+
+function test(): {[A in "foo"]: A} {
+ return {foo: "bar"};
+}
//// [errorElaboration.js]
// Repro for #5712
var a;
foo(a);
+// Repro for #25498
+function test() {
+ return { foo: "bar" };
+}
diff --git a/tests/baselines/reference/errorElaboration.symbols b/tests/baselines/reference/errorElaboration.symbols
index 72206e3caef..839b19c0599 100644
--- a/tests/baselines/reference/errorElaboration.symbols
+++ b/tests/baselines/reference/errorElaboration.symbols
@@ -38,3 +38,14 @@ foo(a);
>foo : Symbol(foo, Decl(errorElaboration.ts, 8, 1))
>a : Symbol(a, Decl(errorElaboration.ts, 10, 3))
+// Repro for #25498
+
+function test(): {[A in "foo"]: A} {
+>test : Symbol(test, Decl(errorElaboration.ts, 11, 7))
+>A : Symbol(A, Decl(errorElaboration.ts, 15, 19))
+>A : Symbol(A, Decl(errorElaboration.ts, 15, 19))
+
+ return {foo: "bar"};
+>foo : Symbol(foo, Decl(errorElaboration.ts, 16, 10))
+}
+
diff --git a/tests/baselines/reference/errorElaboration.types b/tests/baselines/reference/errorElaboration.types
index 96aaba6ce30..385ab481bb2 100644
--- a/tests/baselines/reference/errorElaboration.types
+++ b/tests/baselines/reference/errorElaboration.types
@@ -39,3 +39,16 @@ foo(a);
>foo : (x: () => Container][>) => void
>a : () => Container][>
+// Repro for #25498
+
+function test(): {[A in "foo"]: A} {
+>test : () => { foo: "foo"; }
+>A : A
+>A : A
+
+ return {foo: "bar"};
+>{foo: "bar"} : { foo: "bar"; }
+>foo : "bar"
+>"bar" : "bar"
+}
+
diff --git a/tests/baselines/reference/jsFileCompilationBindErrors.errors.txt b/tests/baselines/reference/jsFileCompilationBindErrors.errors.txt
index eeae7053333..64bd79d7fd9 100644
--- a/tests/baselines/reference/jsFileCompilationBindErrors.errors.txt
+++ b/tests/baselines/reference/jsFileCompilationBindErrors.errors.txt
@@ -15,7 +15,7 @@ tests/cases/compiler/a.js(11,9): error TS1100: Invalid use of 'arguments' in str
function f() {
return;
return; // Error: Unreachable code detected.
- ~~~~~~
+ ~~~~~~~
!!! error TS7027: Unreachable code detected.
}
diff --git a/tests/baselines/reference/jsFileCompilationBindReachabilityErrors.errors.txt b/tests/baselines/reference/jsFileCompilationBindReachabilityErrors.errors.txt
index a883e66e607..c2c572aa304 100644
--- a/tests/baselines/reference/jsFileCompilationBindReachabilityErrors.errors.txt
+++ b/tests/baselines/reference/jsFileCompilationBindReachabilityErrors.errors.txt
@@ -22,7 +22,7 @@ tests/cases/compiler/a.js(19,1): error TS7028: Unused label.
function bar2() {
}
var x = 10; // error
- ~~~
+ ~~~~~~~~~~~
!!! error TS7027: Unreachable code detected.
}
diff --git a/tests/baselines/reference/jsdocTypeReferenceUseBeforeDef.symbols b/tests/baselines/reference/jsdocTypeReferenceUseBeforeDef.symbols
new file mode 100644
index 00000000000..798c892f520
--- /dev/null
+++ b/tests/baselines/reference/jsdocTypeReferenceUseBeforeDef.symbols
@@ -0,0 +1,9 @@
+=== tests/cases/conformance/jsdoc/bug25097.js ===
+/** @type {C | null} */
+const c = null
+>c : Symbol(c, Decl(bug25097.js, 1, 5))
+
+class C {
+>C : Symbol(C, Decl(bug25097.js, 1, 14))
+}
+
diff --git a/tests/baselines/reference/jsdocTypeReferenceUseBeforeDef.types b/tests/baselines/reference/jsdocTypeReferenceUseBeforeDef.types
new file mode 100644
index 00000000000..0a79a140c7d
--- /dev/null
+++ b/tests/baselines/reference/jsdocTypeReferenceUseBeforeDef.types
@@ -0,0 +1,10 @@
+=== tests/cases/conformance/jsdoc/bug25097.js ===
+/** @type {C | null} */
+const c = null
+>c : C
+>null : null
+
+class C {
+>C : C
+}
+
diff --git a/tests/baselines/reference/noCrashOnMixin.errors.txt b/tests/baselines/reference/noCrashOnMixin.errors.txt
new file mode 100644
index 00000000000..01baff88417
--- /dev/null
+++ b/tests/baselines/reference/noCrashOnMixin.errors.txt
@@ -0,0 +1,29 @@
+tests/cases/compiler/noCrashOnMixin.ts(21,9): error TS2674: Constructor of class 'Abstract' is protected and only accessible within the class declaration.
+
+
+==== tests/cases/compiler/noCrashOnMixin.ts (1 errors) ====
+ class Abstract {
+ protected constructor() {
+ }
+ }
+
+ class Concrete extends Abstract {
+ }
+
+ type Constructor = new (...args: any[]) => T;
+
+ function Mixin(Base: TBase) {
+ return class extends Base {
+ };
+ }
+
+ class Empty {
+ }
+
+ class CrashTrigger extends Mixin(Empty) {
+ public trigger() {
+ new Concrete();
+ ~~~~~~~~~~~~~~
+!!! error TS2674: Constructor of class 'Abstract' is protected and only accessible within the class declaration.
+ }
+ }
\ No newline at end of file
diff --git a/tests/baselines/reference/noCrashOnMixin.js b/tests/baselines/reference/noCrashOnMixin.js
new file mode 100644
index 00000000000..049b61e8a1c
--- /dev/null
+++ b/tests/baselines/reference/noCrashOnMixin.js
@@ -0,0 +1,75 @@
+//// [noCrashOnMixin.ts]
+class Abstract {
+ protected constructor() {
+ }
+}
+
+class Concrete extends Abstract {
+}
+
+type Constructor = new (...args: any[]) => T;
+
+function Mixin(Base: TBase) {
+ return class extends Base {
+ };
+}
+
+class Empty {
+}
+
+class CrashTrigger extends Mixin(Empty) {
+ public trigger() {
+ new Concrete();
+ }
+}
+
+//// [noCrashOnMixin.js]
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ }
+ return function (d, b) {
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+var Abstract = /** @class */ (function () {
+ function Abstract() {
+ }
+ return Abstract;
+}());
+var Concrete = /** @class */ (function (_super) {
+ __extends(Concrete, _super);
+ function Concrete() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ return Concrete;
+}(Abstract));
+function Mixin(Base) {
+ return /** @class */ (function (_super) {
+ __extends(class_1, _super);
+ function class_1() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ return class_1;
+ }(Base));
+}
+var Empty = /** @class */ (function () {
+ function Empty() {
+ }
+ return Empty;
+}());
+var CrashTrigger = /** @class */ (function (_super) {
+ __extends(CrashTrigger, _super);
+ function CrashTrigger() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ CrashTrigger.prototype.trigger = function () {
+ new Concrete();
+ };
+ return CrashTrigger;
+}(Mixin(Empty)));
diff --git a/tests/baselines/reference/noCrashOnMixin.symbols b/tests/baselines/reference/noCrashOnMixin.symbols
new file mode 100644
index 00000000000..3f99f99aa49
--- /dev/null
+++ b/tests/baselines/reference/noCrashOnMixin.symbols
@@ -0,0 +1,48 @@
+=== tests/cases/compiler/noCrashOnMixin.ts ===
+class Abstract {
+>Abstract : Symbol(Abstract, Decl(noCrashOnMixin.ts, 0, 0))
+
+ protected constructor() {
+ }
+}
+
+class Concrete extends Abstract {
+>Concrete : Symbol(Concrete, Decl(noCrashOnMixin.ts, 3, 1))
+>Abstract : Symbol(Abstract, Decl(noCrashOnMixin.ts, 0, 0))
+}
+
+type Constructor = new (...args: any[]) => T;
+>Constructor : Symbol(Constructor, Decl(noCrashOnMixin.ts, 6, 1))
+>T : Symbol(T, Decl(noCrashOnMixin.ts, 8, 17))
+>args : Symbol(args, Decl(noCrashOnMixin.ts, 8, 32))
+>T : Symbol(T, Decl(noCrashOnMixin.ts, 8, 17))
+
+function Mixin(Base: TBase) {
+>Mixin : Symbol(Mixin, Decl(noCrashOnMixin.ts, 8, 53))
+>TBase : Symbol(TBase, Decl(noCrashOnMixin.ts, 10, 15))
+>Constructor : Symbol(Constructor, Decl(noCrashOnMixin.ts, 6, 1))
+>Base : Symbol(Base, Decl(noCrashOnMixin.ts, 10, 42))
+>TBase : Symbol(TBase, Decl(noCrashOnMixin.ts, 10, 15))
+
+ return class extends Base {
+>Base : Symbol(Base, Decl(noCrashOnMixin.ts, 10, 42))
+
+ };
+}
+
+class Empty {
+>Empty : Symbol(Empty, Decl(noCrashOnMixin.ts, 13, 1))
+}
+
+class CrashTrigger extends Mixin(Empty) {
+>CrashTrigger : Symbol(CrashTrigger, Decl(noCrashOnMixin.ts, 16, 1))
+>Mixin : Symbol(Mixin, Decl(noCrashOnMixin.ts, 8, 53))
+>Empty : Symbol(Empty, Decl(noCrashOnMixin.ts, 13, 1))
+
+ public trigger() {
+>trigger : Symbol(CrashTrigger.trigger, Decl(noCrashOnMixin.ts, 18, 41))
+
+ new Concrete();
+>Concrete : Symbol(Concrete, Decl(noCrashOnMixin.ts, 3, 1))
+ }
+}
diff --git a/tests/baselines/reference/noCrashOnMixin.types b/tests/baselines/reference/noCrashOnMixin.types
new file mode 100644
index 00000000000..4d31e483b90
--- /dev/null
+++ b/tests/baselines/reference/noCrashOnMixin.types
@@ -0,0 +1,51 @@
+=== tests/cases/compiler/noCrashOnMixin.ts ===
+class Abstract {
+>Abstract : Abstract
+
+ protected constructor() {
+ }
+}
+
+class Concrete extends Abstract {
+>Concrete : Concrete
+>Abstract : Abstract
+}
+
+type Constructor = new (...args: any[]) => T;
+>Constructor : Constructor
+>T : T
+>args : any[]
+>T : T
+
+function Mixin(Base: TBase) {
+>Mixin : >(Base: TBase) => { new (...args: any[]): (Anonymous class); prototype: Mixin.(Anonymous class); } & TBase
+>TBase : TBase
+>Constructor : Constructor
+>Base : TBase
+>TBase : TBase
+
+ return class extends Base {
+>class extends Base { } : { new (...args: any[]): (Anonymous class); prototype: Mixin.(Anonymous class); } & TBase
+>Base : {}
+
+ };
+}
+
+class Empty {
+>Empty : Empty
+}
+
+class CrashTrigger extends Mixin(Empty) {
+>CrashTrigger : CrashTrigger
+>Mixin(Empty) : Mixin.(Anonymous class) & Empty
+>Mixin : >(Base: TBase) => { new (...args: any[]): (Anonymous class); prototype: Mixin.(Anonymous class); } & TBase
+>Empty : typeof Empty
+
+ public trigger() {
+>trigger : () => void
+
+ new Concrete();
+>new Concrete() : any
+>Concrete : typeof Concrete
+ }
+}
diff --git a/tests/baselines/reference/reachabilityChecks1.errors.txt b/tests/baselines/reference/reachabilityChecks1.errors.txt
index 0f1a9b7bb1e..041d10bfecd 100644
--- a/tests/baselines/reference/reachabilityChecks1.errors.txt
+++ b/tests/baselines/reference/reachabilityChecks1.errors.txt
@@ -10,13 +10,13 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
==== tests/cases/compiler/reachabilityChecks1.ts (7 errors) ====
while (true);
var x = 1;
- ~~~
+ ~~~~~~~~~~
!!! error TS7027: Unreachable code detected.
module A {
while (true);
let x;
- ~~~
+ ~~~~~~
!!! error TS7027: Unreachable code detected.
}
@@ -30,10 +30,12 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
module A2 {
while (true);
module A {
- ~~~~~~
-!!! error TS7027: Unreachable code detected.
+ ~~~~~~~~~~
var x = 1;
+ ~~~~~~~~~~~~~~~~~~
}
+ ~~~~~
+!!! error TS7027: Unreachable code detected.
}
module A3 {
@@ -44,10 +46,12 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
module A4 {
while (true);
module A {
- ~~~~~~
-!!! error TS7027: Unreachable code detected.
+ ~~~~~~~~~~
const enum E { X }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
}
+ ~~~~~
+!!! error TS7027: Unreachable code detected.
}
function f1(x) {
@@ -63,9 +67,10 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
function f2() {
return;
class A {
- ~~~~~
-!!! error TS7027: Unreachable code detected.
+ ~~~~~~~~~
}
+ ~~~~~
+!!! error TS7027: Unreachable code detected.
}
module B {
@@ -78,10 +83,12 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
do {
} while (true);
enum E {
- ~~~~
-!!! error TS7027: Unreachable code detected.
+ ~~~~~~~~
X = 1
+ ~~~~~~~~~~~~~
}
+ ~~~~~
+!!! error TS7027: Unreachable code detected.
}
function f4() {
@@ -89,10 +96,12 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
throw new Error();
}
const enum E {
- ~~~~~
-!!! error TS7027: Unreachable code detected.
+ ~~~~~~~~~~~~~~
X = 1
+ ~~~~~~~~~~~~~
}
+ ~~~~~
+!!! error TS7027: Unreachable code detected.
}
\ No newline at end of file
diff --git a/tests/baselines/reference/reachabilityChecks2.errors.txt b/tests/baselines/reference/reachabilityChecks2.errors.txt
index 9061effeb66..922f888fad3 100644
--- a/tests/baselines/reference/reachabilityChecks2.errors.txt
+++ b/tests/baselines/reference/reachabilityChecks2.errors.txt
@@ -6,12 +6,17 @@ tests/cases/compiler/reachabilityChecks2.ts(4,1): error TS7027: Unreachable code
const enum E { X }
module A4 {
- ~~~~~~
-!!! error TS7027: Unreachable code detected.
+ ~~~~~~~~~~~
while (true);
+ ~~~~~~~~~~~~~~~~~
module A {
+ ~~~~~~~~~~~~~~
const enum E { X }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
}
+ ~~~~~
}
+ ~
+!!! error TS7027: Unreachable code detected.
\ No newline at end of file
diff --git a/tests/baselines/reference/reachabilityChecks5.errors.txt b/tests/baselines/reference/reachabilityChecks5.errors.txt
index 4dc7c1705f1..f8c7e6f4dbc 100644
--- a/tests/baselines/reference/reachabilityChecks5.errors.txt
+++ b/tests/baselines/reference/reachabilityChecks5.errors.txt
@@ -109,7 +109,7 @@ tests/cases/compiler/reachabilityChecks5.ts(122,13): error TS7027: Unreachable c
}
else {
return 1;
- ~~~~~~
+ ~~~~~~~~~
!!! error TS7027: Unreachable code detected.
}
}
@@ -124,7 +124,7 @@ tests/cases/compiler/reachabilityChecks5.ts(122,13): error TS7027: Unreachable c
try {
while (false) {
return 1;
- ~~~~~~
+ ~~~~~~~~~
!!! error TS7027: Unreachable code detected.
}
}
@@ -154,7 +154,7 @@ tests/cases/compiler/reachabilityChecks5.ts(122,13): error TS7027: Unreachable c
break test;
} while (true);
x++;
- ~
+ ~~~~
!!! error TS7027: Unreachable code detected.
} while (true);
}
diff --git a/tests/baselines/reference/reachabilityChecks6.errors.txt b/tests/baselines/reference/reachabilityChecks6.errors.txt
index 9dadf3b2d6f..66744af3e03 100644
--- a/tests/baselines/reference/reachabilityChecks6.errors.txt
+++ b/tests/baselines/reference/reachabilityChecks6.errors.txt
@@ -106,7 +106,7 @@ tests/cases/compiler/reachabilityChecks6.ts(122,13): error TS7027: Unreachable c
}
else {
return 1;
- ~~~~~~
+ ~~~~~~~~~
!!! error TS7027: Unreachable code detected.
}
}
@@ -121,7 +121,7 @@ tests/cases/compiler/reachabilityChecks6.ts(122,13): error TS7027: Unreachable c
try {
while (false) {
return 1;
- ~~~~~~
+ ~~~~~~~~~
!!! error TS7027: Unreachable code detected.
}
}
@@ -151,7 +151,7 @@ tests/cases/compiler/reachabilityChecks6.ts(122,13): error TS7027: Unreachable c
break test;
} while (true);
x++;
- ~
+ ~~~~
!!! error TS7027: Unreachable code detected.
} while (true);
}
diff --git a/tests/baselines/reference/unreachableJavascriptChecked.errors.txt b/tests/baselines/reference/unreachableJavascriptChecked.errors.txt
index c66b342177f..a4aee3b7805 100644
--- a/tests/baselines/reference/unreachableJavascriptChecked.errors.txt
+++ b/tests/baselines/reference/unreachableJavascriptChecked.errors.txt
@@ -1,10 +1,18 @@
tests/cases/compiler/unreachable.js(3,5): error TS7027: Unreachable code detected.
+tests/cases/compiler/unreachable.js(6,5): error TS7027: Unreachable code detected.
-==== tests/cases/compiler/unreachable.js (1 errors) ====
+==== tests/cases/compiler/unreachable.js (2 errors) ====
function unreachable() {
- return 1;
+ return f();
return 2;
- ~~~~~~
+ ~~~~~~~~~
+ return 3;
+ ~~~~~~~~~~~~~
!!! error TS7027: Unreachable code detected.
- }
\ No newline at end of file
+ function f() {}
+ return 4;
+ ~~~~~~~~~
+!!! error TS7027: Unreachable code detected.
+ }
+
\ No newline at end of file
diff --git a/tests/baselines/reference/unreachableJavascriptChecked.js b/tests/baselines/reference/unreachableJavascriptChecked.js
index 6bf264bf3d2..d12d4af6753 100644
--- a/tests/baselines/reference/unreachableJavascriptChecked.js
+++ b/tests/baselines/reference/unreachableJavascriptChecked.js
@@ -1,11 +1,18 @@
//// [unreachable.js]
function unreachable() {
- return 1;
+ return f();
return 2;
-}
+ return 3;
+ function f() {}
+ return 4;
+}
+
//// [unreachable.js]
function unreachable() {
- return 1;
+ return f();
return 2;
+ return 3;
+ function f() { }
+ return 4;
}
diff --git a/tests/baselines/reference/unreachableJavascriptChecked.symbols b/tests/baselines/reference/unreachableJavascriptChecked.symbols
index f2db6a6a347..11610239f59 100644
--- a/tests/baselines/reference/unreachableJavascriptChecked.symbols
+++ b/tests/baselines/reference/unreachableJavascriptChecked.symbols
@@ -2,6 +2,14 @@
function unreachable() {
>unreachable : Symbol(unreachable, Decl(unreachable.js, 0, 0))
- return 1;
+ return f();
+>f : Symbol(f, Decl(unreachable.js, 3, 13))
+
return 2;
+ return 3;
+ function f() {}
+>f : Symbol(f, Decl(unreachable.js, 3, 13))
+
+ return 4;
}
+
diff --git a/tests/baselines/reference/unreachableJavascriptChecked.types b/tests/baselines/reference/unreachableJavascriptChecked.types
index 266a9038875..d27af1d33d8 100644
--- a/tests/baselines/reference/unreachableJavascriptChecked.types
+++ b/tests/baselines/reference/unreachableJavascriptChecked.types
@@ -1,10 +1,21 @@
=== tests/cases/compiler/unreachable.js ===
function unreachable() {
->unreachable : () => 1 | 2
+>unreachable : () => void | 2 | 3 | 4
- return 1;
->1 : 1
+ return f();
+>f() : void
+>f : () => void
return 2;
>2 : 2
+
+ return 3;
+>3 : 3
+
+ function f() {}
+>f : () => void
+
+ return 4;
+>4 : 4
}
+
diff --git a/tests/cases/compiler/checkDestructuringShorthandAssigment.ts b/tests/cases/compiler/checkDestructuringShorthandAssigment.ts
new file mode 100644
index 00000000000..e3422aae753
--- /dev/null
+++ b/tests/cases/compiler/checkDestructuringShorthandAssigment.ts
@@ -0,0 +1,8 @@
+// @allowjs: true
+// @checkjs: true
+// @noEmit: true
+// @Filename: bug25434.js
+// should not crash while checking
+function Test({ b = '' } = {}) {}
+
+Test(({ b = '5' } = {}));
diff --git a/tests/cases/compiler/errorElaboration.ts b/tests/cases/compiler/errorElaboration.ts
index 97ec0dde61c..87575f1df11 100644
--- a/tests/cases/compiler/errorElaboration.ts
+++ b/tests/cases/compiler/errorElaboration.ts
@@ -10,3 +10,9 @@ interface Container {
declare function foo(x: () => Container][>): void;
let a: () => Container][>;
foo(a);
+
+// Repro for #25498
+
+function test(): {[A in "foo"]: A} {
+ return {foo: "bar"};
+}
diff --git a/tests/cases/compiler/noCrashOnMixin.ts b/tests/cases/compiler/noCrashOnMixin.ts
new file mode 100644
index 00000000000..900e827fa46
--- /dev/null
+++ b/tests/cases/compiler/noCrashOnMixin.ts
@@ -0,0 +1,23 @@
+class Abstract {
+ protected constructor() {
+ }
+}
+
+class Concrete extends Abstract {
+}
+
+type Constructor = new (...args: any[]) => T;
+
+function Mixin(Base: TBase) {
+ return class extends Base {
+ };
+}
+
+class Empty {
+}
+
+class CrashTrigger extends Mixin(Empty) {
+ public trigger() {
+ new Concrete();
+ }
+}
\ No newline at end of file
diff --git a/tests/cases/compiler/unreachableJavascriptChecked.ts b/tests/cases/compiler/unreachableJavascriptChecked.ts
index afddcba6a23..3d9c906dc05 100644
--- a/tests/cases/compiler/unreachableJavascriptChecked.ts
+++ b/tests/cases/compiler/unreachableJavascriptChecked.ts
@@ -4,6 +4,9 @@
// @outDir: out
// @allowUnreachableCode: false
function unreachable() {
- return 1;
+ return f();
return 2;
-}
\ No newline at end of file
+ return 3;
+ function f() {}
+ return 4;
+}
diff --git a/tests/cases/conformance/jsdoc/jsdocTypeReferenceUseBeforeDef.ts b/tests/cases/conformance/jsdoc/jsdocTypeReferenceUseBeforeDef.ts
new file mode 100644
index 00000000000..93e735e1fa6
--- /dev/null
+++ b/tests/cases/conformance/jsdoc/jsdocTypeReferenceUseBeforeDef.ts
@@ -0,0 +1,8 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: bug25097.js
+/** @type {C | null} */
+const c = null
+class C {
+}
diff --git a/tests/cases/fourslash/autoCloseTag.ts b/tests/cases/fourslash/autoCloseTag.ts
index ba828f207a2..6ba0b0f209f 100644
--- a/tests/cases/fourslash/autoCloseTag.ts
+++ b/tests/cases/fourslash/autoCloseTag.ts
@@ -1,20 +1,51 @@
///
-// @Filename: /a.tsx
+// Using separate files for each example to avoid unclosed JSX tags affecting other tests.
+
+// @Filename: /0.tsx
////const x = ]/*0*/;
+
+// @Filename: /1.tsx
////const x =
foo/*1*/
;
+
+// @Filename: /2.tsx
////const x =
/*2*/;
+
+// @Filename: /3.tsx
////const x =
/*3*/;
+
+// @Filename: /4.tsx
////const x =
////;
+
+// @Filename: /5.tsx
////const x =
text /*5*/;
+// @Filename: /6.tsx
+////const x =
+////
/*6*/
+////
;
+
+// @Filename: /7.tsx
+////const x =
;
+
+// @Filename: /8.tsx
+////const x =
;
+
verify.jsxClosingTag({
0: { newText: "
" },
1: undefined,
2: undefined,
3: undefined,
4: { newText: "" },
+ 5: { newText: "
" },
+ 6: { newText: "
" },
+ 7: { newText: "
" },
+ 8: undefined,
});
diff --git a/tests/cases/fourslash/codeFixUnreachableCode.ts b/tests/cases/fourslash/codeFixUnreachableCode.ts
index 5f70d51c1ee..9c9eea6dae4 100644
--- a/tests/cases/fourslash/codeFixUnreachableCode.ts
+++ b/tests/cases/fourslash/codeFixUnreachableCode.ts
@@ -2,29 +2,30 @@
////function f() {
//// return f();
-//// [|return|] 1;
+//// [|return 1;|]
//// function f() {}
-//// return 2;
+//// [|return 2;|]
//// type T = number;
//// interface I {}
//// const enum E {}
-//// enum E {}
+//// [|enum E {}|]
//// namespace N { export type T = number; }
-//// namespace N { export const x: T = 0; }
+//// [|namespace N { export const x: T = 0; }|]
//// var x: I;
-//// var y: T = 0;
-//// E; N; x; y;
+//// [|var y: T = 0;
+//// E; N; x; y;|]
////}
-verify.getSuggestionDiagnostics([{
+verify.getSuggestionDiagnostics(test.ranges().map((range): FourSlashInterface.Diagnostic => ({
message: "Unreachable code detected.",
code: 7027,
reportsUnnecessary: true,
-}]);
+ range,
+})));
-verify.codeFix({
- description: "Remove unreachable code",
- index: 0,
+verify.codeFixAll({
+ fixId: "fixUnreachableCode",
+ fixAllDescription: "Remove all unreachable code",
newFileContent:
`function f() {
return f();
diff --git a/tests/cases/fourslash/completionsUniqueSymbol_import.ts b/tests/cases/fourslash/completionsUniqueSymbol_import.ts
index f15bc59abff..df3034e56fe 100644
--- a/tests/cases/fourslash/completionsUniqueSymbol_import.ts
+++ b/tests/cases/fourslash/completionsUniqueSymbol_import.ts
@@ -1,12 +1,17 @@
///
-// @Filename: /a.ts
+// @noLib: true
+
+// @Filename: /globals.d.ts
////declare const Symbol: () => symbol;
+
+// @Filename: /a.ts
////const privateSym = Symbol();
////export const publicSym = Symbol();
////export interface I {
//// [privateSym]: number;
//// [publicSym]: number;
+//// [defaultPublicSym]: number;
//// n: number;
////}
////export const i: I;
@@ -17,10 +22,21 @@
verify.completions({
marker: "",
- // TODO: GH#25095 Should include `publicSym`
- exact: "n",
+ exact: [
+ "n",
+ { name: "publicSym", insertText: "[publicSym]", replacementSpan: test.ranges()[0], hasAction: true },
+ ],
preferences: {
includeInsertTextCompletions: true,
includeCompletionsForModuleExports: true,
},
});
+
+verify.applyCodeActionFromCompletion("", {
+ name: "publicSym",
+ source: "/a",
+ description: `Add 'publicSym' to existing import declaration from "./a"`,
+ newFileContent:
+`import { i, publicSym } from "./a";
+i.;`
+});
diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts
index 81cb387b73d..ddad4ca52a6 100644
--- a/tests/cases/fourslash/fourslash.ts
+++ b/tests/cases/fourslash/fourslash.ts
@@ -554,7 +554,6 @@ declare namespace FourSlashInterface {
overloadsCount?: number;
docComment?: string;
text?: string;
- name?: string;
parameterName?: string;
parameterSpan?: string;
parameterDocComment?: string;
diff --git a/tests/cases/fourslash/getJSXOutliningSpans.tsx b/tests/cases/fourslash/getJSXOutliningSpans.tsx
new file mode 100644
index 00000000000..fa4f541b193
--- /dev/null
+++ b/tests/cases/fourslash/getJSXOutliningSpans.tsx
@@ -0,0 +1,33 @@
+////import React, { Component } from 'react';
+////
+////export class Home extends Component[| {
+//// render()[| {
+//// return (
+//// [|
+//// [|
Hello, world!
|]
+//// [|
|]
+////
+////
+////
|]
+//// );
+//// }|]
+////}|]
+
+verify.outliningSpansInCurrentFile(test.ranges(), "code");
diff --git a/tests/cases/fourslash/signatureHelpFilteredTriggerCharacters01.ts b/tests/cases/fourslash/signatureHelpFilteredTriggerCharacters01.ts
deleted file mode 100644
index ed97ef020d0..00000000000
--- a/tests/cases/fourslash/signatureHelpFilteredTriggerCharacters01.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-///
-
-////function foo(x: T): T {
-//// throw null;
-////}
-////
-////foo("/**/")
-
-goTo.marker();
-for (const triggerCharacter of ["<", "(", ","]) {
- edit.insert(triggerCharacter);
- verify.noSignatureHelpForTriggerReason({
- kind: "characterTyped",
- triggerCharacter,
- });
- verify.signatureHelpPresentForTriggerReason({
- kind: "retrigger",
- triggerCharacter,
- });
- edit.backspace();
-}
-verify.signatureHelpPresentForTriggerReason(/*triggerReason*/ undefined);
-verify.signatureHelpPresentForTriggerReason({ kind: "invoked" });
\ No newline at end of file
diff --git a/tests/cases/fourslash/signatureHelpFilteredTriggers01.ts b/tests/cases/fourslash/signatureHelpFilteredTriggers01.ts
new file mode 100644
index 00000000000..ebf5ba10f2d
--- /dev/null
+++ b/tests/cases/fourslash/signatureHelpFilteredTriggers01.ts
@@ -0,0 +1,31 @@
+///
+
+////function foo(x: T): T {
+//// throw null;
+////}
+////
+////foo("/*1*/");
+////foo('/*2*/');
+////foo(` ${100}/*3*/`);
+////foo(/* /*4*/ */);
+////foo(
+//// ///*5*/
+////);
+
+for (const marker of test.markers()) {
+ goTo.marker(marker);
+ for (const triggerCharacter of ["<", "(", ","]) {
+ edit.insert(triggerCharacter);
+ verify.noSignatureHelpForTriggerReason({
+ kind: "characterTyped",
+ triggerCharacter,
+ });
+ verify.signatureHelpPresentForTriggerReason({
+ kind: "retrigger",
+ triggerCharacter,
+ });
+ edit.backspace();
+ }
+ verify.signatureHelpPresentForTriggerReason(/*triggerReason*/ undefined);
+ verify.signatureHelpPresentForTriggerReason({ kind: "invoked" });
+}
diff --git a/tests/cases/fourslash/signatureHelpFilteredTriggers02.ts b/tests/cases/fourslash/signatureHelpFilteredTriggers02.ts
new file mode 100644
index 00000000000..e193f919e05
--- /dev/null
+++ b/tests/cases/fourslash/signatureHelpFilteredTriggers02.ts
@@ -0,0 +1,36 @@
+///
+
+////function foo(x: T): T {
+//// throw null;
+////}
+////
+////foo(/*1*/"");
+////foo(` ${100/*2*/}`);
+////foo(/*3*/);
+////foo(100 /*4*/)
+////foo([/*5*/])
+////foo({ hello: "hello"/*6*/})
+
+const charMap = {
+ 1: "(",
+ 2: ",",
+ 3: "(",
+ 4: "<",
+ 5: ",",
+ 6: ",",
+}
+
+for (const markerName of Object.keys(charMap)) {
+ const triggerCharacter = charMap[markerName];
+ goTo.marker(markerName);
+ edit.insert(triggerCharacter);
+ verify.noSignatureHelpForTriggerReason({
+ kind: "characterTyped",
+ triggerCharacter,
+ });
+ verify.signatureHelpPresentForTriggerReason({
+ kind: "retrigger",
+ triggerCharacter,
+ });
+ edit.backspace(triggerCharacter.length);
+}
diff --git a/tests/cases/fourslash/signatureHelpFilteredTriggers03.ts b/tests/cases/fourslash/signatureHelpFilteredTriggers03.ts
new file mode 100644
index 00000000000..7b170823610
--- /dev/null
+++ b/tests/cases/fourslash/signatureHelpFilteredTriggers03.ts
@@ -0,0 +1,23 @@
+///
+
+////declare class ViewJayEss {
+//// constructor(obj: object);
+////}
+////new ViewJayEss({
+//// methods: {
+//// sayHello/**/
+//// }
+////});
+
+goTo.marker();
+edit.insert("(");
+verify.noSignatureHelpForTriggerReason({
+ kind: "characterTyped",
+ triggerCharacter: "(",
+});
+
+edit.insert(") {},");
+verify.noSignatureHelpForTriggerReason({
+ kind: "characterTyped",
+ triggerCharacter: ",",
+});
diff --git a/tests/cases/fourslash/signatureHelpWithTriggers01.ts b/tests/cases/fourslash/signatureHelpWithTriggers01.ts
new file mode 100644
index 00000000000..ae73b5fe000
--- /dev/null
+++ b/tests/cases/fourslash/signatureHelpWithTriggers01.ts
@@ -0,0 +1,31 @@
+///
+
+////declare function foo(x: T, y: T): T;
+////
+////foo/*1*//*2*/;
+////foo(/*3*/100/*4*/);
+////foo/*5*//*6*/();
+
+const charMap = {
+ 1: "(",
+ 2: "<",
+ 3: ",",
+ 4: ",",
+ 5: "(",
+ 6: "<",
+}
+
+for (const markerName of Object.keys(charMap)) {
+ const triggerCharacter = charMap[markerName];
+ goTo.marker(markerName);
+ edit.insert(triggerCharacter);
+ verify.signatureHelpPresentForTriggerReason({
+ kind: "characterTyped",
+ triggerCharacter,
+ });
+ verify.signatureHelpPresentForTriggerReason({
+ kind: "retrigger",
+ triggerCharacter,
+ });
+ edit.backspace(triggerCharacter.length);
+}
diff --git a/tests/cases/fourslash/signatureHelpWithTriggers02.ts b/tests/cases/fourslash/signatureHelpWithTriggers02.ts
new file mode 100644
index 00000000000..cb73237f8fd
--- /dev/null
+++ b/tests/cases/fourslash/signatureHelpWithTriggers02.ts
@@ -0,0 +1,38 @@
+///
+
+////declare function foo(x: T, y: T): T;
+////declare function bar(x: U, y: U): U;
+////
+////foo(bar/*1*/)
+
+goTo.marker("1");
+
+edit.insert("(");
+verify.signatureHelp({
+ text: "bar(x: U, y: U): U",
+ triggerReason: {
+ kind: "characterTyped",
+ triggerCharacter: "(",
+ }
+});
+edit.backspace();
+
+edit.insert("<");
+verify.signatureHelp({
+ text: "bar(x: U, y: U): U",
+ triggerReason: {
+ kind: "characterTyped",
+ triggerCharacter: "(",
+ }
+});
+edit.backspace();
+
+edit.insert(",");
+verify.signatureHelp({
+ text: "foo(x: (x: U, y: U) => U, y: (x: U, y: U) => U): (x: U, y: U) => U",
+ triggerReason: {
+ kind: "characterTyped",
+ triggerCharacter: "(",
+ }
+});
+edit.backspace();
\ No newline at end of file
diff --git a/tests/projects/outfile-concat/first/tsconfig.json b/tests/projects/outfile-concat/first/tsconfig.json
index f71a8182585..8d63a0c4a06 100644
--- a/tests/projects/outfile-concat/first/tsconfig.json
+++ b/tests/projects/outfile-concat/first/tsconfig.json
@@ -6,7 +6,6 @@
"strict": false,
"sourceMap": true,
"declarationMap": true,
- "declaration": true,
"outFile": "./bin/first-output.js"
},
"files": [