Merge branch 'master' into removeWhitespace

This commit is contained in:
Ron Buckton
2018-07-10 14:55:29 -07:00
64 changed files with 1094 additions and 232 deletions
+44 -5
View File
@@ -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) ||
+40 -13
View File
@@ -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;
+3 -3
View File
@@ -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);
+2 -2
View File
@@ -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");
}
+35 -13
View File
@@ -81,6 +81,7 @@ namespace ts {
let filesWithInvalidatedResolutions: Map<true> | undefined;
let filesWithInvalidatedNonRelativeUnresolvedImports: Map<ReadonlyArray<string>> | undefined;
let allFilesHaveInvalidatedResolution = false;
const nonRelativeExternalModuleResolutions = createMultiMap<ResolutionWithFailedLookupLocations>();
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;
}
+7 -4
View File
@@ -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;
+3 -3
View File
@@ -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;
+7 -1
View File
@@ -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<T extends object> = { -readonly [K in keyof T]: T[K] };
export function sliceAfter<T>(arr: ReadonlyArray<T>, value: T): ReadonlyArray<T> {
const index = arr.indexOf(value);
Debug.assert(index !== -1);
return arr.slice(index);
}
}
+4 -2
View File
@@ -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");
}
+1 -1
View File
@@ -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) {
+35 -15
View File
@@ -286,6 +286,16 @@ namespace ts.server {
: deduplicate(outputs, areEqual);
}
function combineProjectOutputFromEveryProject<T>(projectService: ProjectService, action: (project: Project) => ReadonlyArray<T>, 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<T>(
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<protocol.FileCodeEdits> | ReadonlyArray<FileTextChanges>;
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<protocol.CodeFixAction> | ReadonlyArray<CodeFixAction> | 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);
+12 -33
View File
@@ -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<T>(a: ReadonlyArray<T>, pred: (value: T) => boolean): T | undefined {
let last: T | undefined;
for (const value of a) {
if (!pred(value)) break;
last = value;
}
}
function sliceAfter<T>(arr: ReadonlyArray<T>, value: T): ReadonlyArray<T> {
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<T>(arr: ReadonlyArray<T>, pred: (t: T) => boolean, cb: (start: T, end: T) => void): void {
getRangesWhere(arr, pred, (start, afterEnd) => cb(arr[start], arr[afterEnd - 1]));
return last;
}
}
+21 -11
View File
@@ -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;
+3 -2
View File
@@ -234,7 +234,8 @@ namespace ts.FindAllReferences.Core {
export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlyMap<true> = 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;
}
+29 -7
View File
@@ -1,10 +1,18 @@
/* @internal */
namespace ts {
export function getEditsForFileRename(program: Program, oldFileOrDirPath: string, newFileOrDirPath: string, host: LanguageServiceHost, formatContext: formatting.FormatContext, preferences: UserPreferences): ReadonlyArray<FileTextChanges> {
export function getEditsForFileRename(
program: Program,
oldFileOrDirPath: string,
newFileOrDirPath: string,
host: LanguageServiceHost,
formatContext: formatting.FormatContext,
preferences: UserPreferences,
sourceMapper: SourceMapper,
): ReadonlyArray<FileTextChanges> {
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 {
@@ -183,6 +183,26 @@ namespace ts.OutliningElementsCollector {
return spanForObjectOrArrayLiteral(n);
case SyntaxKind.ArrayLiteralExpression:
return spanForObjectOrArrayLiteral(n, SyntaxKind.OpenBracketToken);
case SyntaxKind.JsxElement:
return spanForJSXElement(<JsxElement>n);
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.JsxOpeningElement:
return spanForJSXAttributes((<JsxOpeningLikeElement>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 {
+9 -3
View File
@@ -1537,7 +1537,8 @@ namespace ts {
}
function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray<string>): 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<FileTextChanges> {
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<ApplyCodeActionCommandResult>;
@@ -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);
+55 -11
View File
@@ -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<Signature>, readonly resolvedSignature: Signature } | undefined {
function getCandidateInfo(argumentInfo: ArgumentListInfo, checker: TypeChecker): { readonly candidates: ReadonlyArray<Signature>, 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;
+2 -3
View File
@@ -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. */
@@ -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);
});
});
});
}
@@ -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<number>();
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<number>();
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<protocol.GetEditsForFileRenameRequest, protocol.GetEditsForFileRenameResponse>(session, protocol.CommandTypes.GetEditsForFileRename, {
oldFilePath: aTs.path,
newFilePath: "/a/aNew.ts",
});
assert.deepEqual<ReadonlyArray<protocol.FileCodeEdits>>(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 {
+2 -3
View File
@@ -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;
-3
View File
@@ -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;
/**
+4 -4
View File
@@ -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 (x<y) {
break L2;
x=13;
~
~~~~~
!!! error TS7027: Unreachable code detected.
}
else {
break L3;
x=14;
~
~~~~~
!!! error TS7027: Unreachable code detected.
}
}
@@ -0,0 +1,11 @@
tests/cases/compiler/bug25434.js(4,9): error TS2304: Cannot find name 'b'.
==== tests/cases/compiler/bug25434.js (1 errors) ====
// should not crash while checking
function Test({ b = '' } = {}) {}
Test(({ b = '5' } = {}));
~
!!! error TS2304: Cannot find name 'b'.
@@ -0,0 +1,10 @@
=== tests/cases/compiler/bug25434.js ===
// should not crash while checking
function Test({ b = '' } = {}) {}
>Test : 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))
@@ -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; }
@@ -2,9 +2,10 @@ tests/cases/compiler/errorElaboration.ts(12,5): error TS2345: Argument of type '
Type 'Container<Ref<string>>' is not assignable to type 'Container<Ref<number>>'.
Type 'Ref<string>' is not assignable to type 'Ref<number>'.
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<T> {
@@ -22,4 +23,13 @@ tests/cases/compiler/errorElaboration.ts(12,5): error TS2345: Argument of type '
!!! error TS2345: Type 'Container<Ref<string>>' is not assignable to type 'Container<Ref<number>>'.
!!! error TS2345: Type 'Ref<string>' is not assignable to type 'Ref<number>'.
!!! 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"; }'
}
@@ -11,9 +11,19 @@ interface Container<T> {
declare function foo(x: () => Container<Ref<number>>): void;
let a: () => Container<Ref<string>>;
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" };
}
@@ -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))
}
@@ -39,3 +39,16 @@ foo(a);
>foo : (x: () => Container<Ref<number>>) => void
>a : () => Container<Ref<string>>
// 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"
}
@@ -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.
}
@@ -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.
}
@@ -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))
}
@@ -0,0 +1,10 @@
=== tests/cases/conformance/jsdoc/bug25097.js ===
/** @type {C | null} */
const c = null
>c : C
>null : null
class C {
>C : C
}
@@ -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<T = {}> = new (...args: any[]) => T;
function Mixin<TBase extends Constructor>(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.
}
}
@@ -0,0 +1,75 @@
//// [noCrashOnMixin.ts]
class Abstract {
protected constructor() {
}
}
class Concrete extends Abstract {
}
type Constructor<T = {}> = new (...args: any[]) => T;
function Mixin<TBase extends Constructor>(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)));
@@ -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<T = {}> = 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<TBase extends Constructor>(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))
}
}
@@ -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<T = {}> = new (...args: any[]) => T;
>Constructor : Constructor<T>
>T : T
>args : any[]
>T : T
function Mixin<TBase extends Constructor>(Base: TBase) {
>Mixin : <TBase extends Constructor<{}>>(Base: TBase) => { new (...args: any[]): (Anonymous class); prototype: Mixin<any>.(Anonymous class); } & TBase
>TBase : TBase
>Constructor : Constructor<T>
>Base : TBase
>TBase : TBase
return class extends Base {
>class extends Base { } : { new (...args: any[]): (Anonymous class); prototype: Mixin<any>.(Anonymous class); } & TBase
>Base : {}
};
}
class Empty {
>Empty : Empty
}
class CrashTrigger extends Mixin(Empty) {
>CrashTrigger : CrashTrigger
>Mixin(Empty) : Mixin<typeof Empty>.(Anonymous class) & Empty
>Mixin : <TBase extends Constructor<{}>>(Base: TBase) => { new (...args: any[]): (Anonymous class); prototype: Mixin<any>.(Anonymous class); } & TBase
>Empty : typeof Empty
public trigger() {
>trigger : () => void
new Concrete();
>new Concrete() : any
>Concrete : typeof Concrete
}
}
@@ -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.
}
@@ -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.
@@ -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);
}
@@ -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);
}
@@ -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.
}
function f() {}
return 4;
~~~~~~~~~
!!! error TS7027: Unreachable code detected.
}
@@ -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;
}
@@ -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;
}
@@ -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
}
@@ -0,0 +1,8 @@
// @allowjs: true
// @checkjs: true
// @noEmit: true
// @Filename: bug25434.js
// should not crash while checking
function Test({ b = '' } = {}) {}
Test(({ b = '5' } = {}));
+6
View File
@@ -10,3 +10,9 @@ interface Container<T> {
declare function foo(x: () => Container<Ref<number>>): void;
let a: () => Container<Ref<string>>;
foo(a);
// Repro for #25498
function test(): {[A in "foo"]: A} {
return {foo: "bar"};
}
+23
View File
@@ -0,0 +1,23 @@
class Abstract {
protected constructor() {
}
}
class Concrete extends Abstract {
}
type Constructor<T = {}> = new (...args: any[]) => T;
function Mixin<TBase extends Constructor>(Base: TBase) {
return class extends Base {
};
}
class Empty {
}
class CrashTrigger extends Mixin(Empty) {
public trigger() {
new Concrete();
}
}
@@ -4,6 +4,9 @@
// @outDir: out
// @allowUnreachableCode: false
function unreachable() {
return 1;
return f();
return 2;
}
return 3;
function f() {}
return 4;
}
@@ -0,0 +1,8 @@
// @noEmit: true
// @allowJs: true
// @checkJs: true
// @Filename: bug25097.js
/** @type {C | null} */
const c = null
class C {
}
+32 -1
View File
@@ -1,20 +1,51 @@
/// <reference path='fourslash.ts' />
// @Filename: /a.tsx
// Using separate files for each example to avoid unclosed JSX tags affecting other tests.
// @Filename: /0.tsx
////const x = <div>/*0*/;
// @Filename: /1.tsx
////const x = <div> foo/*1*/ </div>;
// @Filename: /2.tsx
////const x = <div></div>/*2*/;
// @Filename: /3.tsx
////const x = <div/>/*3*/;
// @Filename: /4.tsx
////const x = <div>
//// <p>/*4*/
//// </div>
////</p>;
// @Filename: /5.tsx
////const x = <div> text /*5*/;
// @Filename: /6.tsx
////const x = <div>
//// <div>/*6*/
////</div>;
// @Filename: /7.tsx
////const x = <div>
//// <p>/*7*/
////</div>;
// @Filename: /8.tsx
////const x = <div>
//// <div>/*8*/</div>
////</div>;
verify.jsxClosingTag({
0: { newText: "</div>" },
1: undefined,
2: undefined,
3: undefined,
4: { newText: "</p>" },
5: { newText: "</div>" },
6: { newText: "</div>" },
7: { newText: "</p>" },
8: undefined,
});
+12 -11
View File
@@ -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();
@@ -1,12 +1,17 @@
/// <reference path="fourslash.ts" />
// @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.;`
});
-1
View File
@@ -554,7 +554,6 @@ declare namespace FourSlashInterface {
overloadsCount?: number;
docComment?: string;
text?: string;
name?: string;
parameterName?: string;
parameterSpan?: string;
parameterDocComment?: string;
@@ -0,0 +1,33 @@
////import React, { Component } from 'react';
////
////export class Home extends Component[| {
//// render()[| {
//// return (
//// [|<div>
//// [|<h1>Hello, world!</h1>|]
//// [|<ul>
//// [|<li>
//// [|<a [|href='https://get.asp.net/'|]>
//// ASP.NET Core
//// </a>|]
//// </li>|]
//// [|<li>[|<a [|href='https://facebook.github.io/react/'|]>React</a>|] for client-side code</li>|]
//// [|<li>[|<a [|href='http://getbootstrap.com/'|]>Bootstrap</a>|] for layout and styling</li>|]
//// </ul>|]
//// <div
//// [|accesskey="test"
//// class="active"
//// dir="auto"|] />
//// <PageHeader [|title="Log in"
//// {...[|{
//// item: true,
//// xs: 9,
//// md: 5
//// }|]}|]
//// />
//// </div>|]
//// );
//// }|]
////}|]
verify.outliningSpansInCurrentFile(test.ranges(), "code");
@@ -1,23 +0,0 @@
/// <reference path="fourslash.ts" />
////function foo<T>(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" });
@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />
////function foo<T>(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" });
}
@@ -0,0 +1,36 @@
/// <reference path="fourslash.ts" />
////function foo<T>(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);
}
@@ -0,0 +1,23 @@
/// <reference path="fourslash.ts" />
////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: ",",
});
@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />
////declare function foo<T>(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);
}
@@ -0,0 +1,38 @@
/// <reference path="fourslash.ts" />
////declare function foo<T>(x: T, y: T): T;
////declare function bar<U>(x: U, y: U): U;
////
////foo(bar/*1*/)
goTo.marker("1");
edit.insert("(");
verify.signatureHelp({
text: "bar<U>(x: U, y: U): U",
triggerReason: {
kind: "characterTyped",
triggerCharacter: "(",
}
});
edit.backspace();
edit.insert("<");
verify.signatureHelp({
text: "bar<U>(x: U, y: U): U",
triggerReason: {
kind: "characterTyped",
triggerCharacter: "(",
}
});
edit.backspace();
edit.insert(",");
verify.signatureHelp({
text: "foo(x: <U>(x: U, y: U) => U, y: <U>(x: U, y: U) => U): <U>(x: U, y: U) => U",
triggerReason: {
kind: "characterTyped",
triggerCharacter: "(",
}
});
edit.backspace();
@@ -6,7 +6,6 @@
"strict": false,
"sourceMap": true,
"declarationMap": true,
"declaration": true,
"outFile": "./bin/first-output.js"
},
"files": [