mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into master-14217
# Conflicts: # tests/baselines/reference/controlFlowIterationErrors.errors.txt # tests/baselines/reference/implicitAnyFromCircularInference.errors.txt
This commit is contained in:
+9
-3
@@ -328,8 +328,14 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts
|
||||
if (opts.stripInternal) {
|
||||
options += " --stripInternal";
|
||||
}
|
||||
|
||||
options += " --target es5 --lib es5,scripthost --noUnusedLocals --noUnusedParameters";
|
||||
options += " --target es5";
|
||||
if (opts.lib) {
|
||||
options += " --lib " + opts.lib
|
||||
}
|
||||
else {
|
||||
options += " --lib es5,scripthost"
|
||||
}
|
||||
options += " --noUnusedLocals --noUnusedParameters";
|
||||
|
||||
var cmd = host + " " + compilerPath + " " + options + " ";
|
||||
cmd = cmd + sources.join(" ");
|
||||
@@ -1110,7 +1116,7 @@ desc("Compiles tslint rules to js");
|
||||
task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(["build-rules-end"]));
|
||||
tslintRulesFiles.forEach(function (ruleFile, i) {
|
||||
compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false,
|
||||
{ noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint") });
|
||||
{ noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint"), lib: "es6" });
|
||||
});
|
||||
|
||||
desc("Emit the start of the build-rules fold");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var tslint = require("tslint");
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
function getLinterOptions() {
|
||||
return {
|
||||
@@ -9,7 +10,7 @@ function getLinterOptions() {
|
||||
};
|
||||
}
|
||||
function getLinterConfiguration() {
|
||||
return require("../tslint.json");
|
||||
return tslint.Configuration.loadConfigurationFromPath(path.join(__dirname, "../tslint.json"));
|
||||
}
|
||||
|
||||
function lintFileContents(options, configuration, path, contents) {
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace ts {
|
||||
getAugmentedPropertiesOfType,
|
||||
getRootSymbols,
|
||||
getContextualType: node => {
|
||||
node = getParseTreeNode(node, isExpression)
|
||||
node = getParseTreeNode(node, isExpression);
|
||||
return node ? getContextualType(node) : undefined;
|
||||
},
|
||||
getFullyQualifiedName,
|
||||
@@ -16131,7 +16131,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkDeclarationInitializer(declaration: VariableLikeDeclaration) {
|
||||
const type = checkExpressionCached(declaration.initializer);
|
||||
const type = getTypeOfExpression(declaration.initializer, /*cache*/ true);
|
||||
return getCombinedNodeFlags(declaration) & NodeFlags.Const ||
|
||||
getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration) ||
|
||||
isTypeAssertion(declaration.initializer) ? type : getWidenedLiteralType(type);
|
||||
@@ -16204,10 +16204,12 @@ namespace ts {
|
||||
|
||||
// Returns the type of an expression. Unlike checkExpression, this function is simply concerned
|
||||
// with computing the type and may not fully check all contained sub-expressions for errors.
|
||||
function getTypeOfExpression(node: Expression) {
|
||||
// A cache argument of true indicates that if the function performs a full type check, it is ok
|
||||
// to cache the result.
|
||||
function getTypeOfExpression(node: Expression, cache?: boolean) {
|
||||
// Optimize for the common case of a call to a function with a single non-generic call
|
||||
// signature where we can just fetch the return type without checking the arguments.
|
||||
if (node.kind === SyntaxKind.CallExpression && (<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword) {
|
||||
if (node.kind === SyntaxKind.CallExpression && (<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(node, /*checkArgumentIsStringLiteral*/true)) {
|
||||
const funcType = checkNonNullExpression((<CallExpression>node).expression);
|
||||
const signature = getSingleCallSignature(funcType);
|
||||
if (signature && !signature.typeParameters) {
|
||||
@@ -16217,7 +16219,7 @@ namespace ts {
|
||||
// Otherwise simply call checkExpression. Ideally, the entire family of checkXXX functions
|
||||
// should have a parameter that indicates whether full error checking is required such that
|
||||
// we can perform the optimizations locally.
|
||||
return checkExpression(node);
|
||||
return cache ? checkExpressionCached(node) : checkExpression(node);
|
||||
}
|
||||
|
||||
// Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When
|
||||
@@ -20674,7 +20676,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (potentialNewTargetCollisions.length) {
|
||||
forEach(potentialNewTargetCollisions, checkIfNewTargetIsCapturedInEnclosingScope)
|
||||
forEach(potentialNewTargetCollisions, checkIfNewTargetIsCapturedInEnclosingScope);
|
||||
potentialNewTargetCollisions.length = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace ts {
|
||||
this.index++;
|
||||
return { value: this.selector(this.data, this.keys[index]), done: false };
|
||||
}
|
||||
return { value: undefined as never, done: true }
|
||||
return { value: undefined as never, done: true };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace ts {
|
||||
action(this.data[key], key);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function createFileMap<T>(keyMapper?: (key: string) => string): FileMap<T> {
|
||||
|
||||
@@ -1164,7 +1164,7 @@ namespace ts {
|
||||
emitTypeParameters(node.typeParameters);
|
||||
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
|
||||
if (baseTypeNode) {
|
||||
node.name
|
||||
node.name;
|
||||
emitHeritageClause(node.name, [baseTypeNode], /*isImplementsList*/ false);
|
||||
}
|
||||
emitHeritageClause(node.name, getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true);
|
||||
|
||||
@@ -675,7 +675,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
|
||||
return nodeModuleNameResolverWorker(moduleName, containingFile, compilerOptions, host, cache, /* jsOnly*/ false);
|
||||
return nodeModuleNameResolverWorker(moduleName, containingFile, compilerOptions, host, cache, /*jsOnly*/ false);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -962,7 +962,7 @@ namespace ts {
|
||||
const result = cache && cache.get(containingDirectory);
|
||||
if (result) {
|
||||
if (traceEnabled) {
|
||||
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName)
|
||||
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName);
|
||||
}
|
||||
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension } };
|
||||
}
|
||||
|
||||
@@ -121,13 +121,13 @@ namespace ts {
|
||||
enableEmitNotification,
|
||||
isSubstitutionEnabled,
|
||||
isEmitNotificationEnabled,
|
||||
get onSubstituteNode() { return onSubstituteNode },
|
||||
get onSubstituteNode() { return onSubstituteNode; },
|
||||
set onSubstituteNode(value) {
|
||||
Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed.");
|
||||
Debug.assert(value !== undefined, "Value must not be 'undefined'");
|
||||
onSubstituteNode = value;
|
||||
},
|
||||
get onEmitNode() { return onEmitNode },
|
||||
get onEmitNode() { return onEmitNode; },
|
||||
set onEmitNode(value) {
|
||||
Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed.");
|
||||
Debug.assert(value !== undefined, "Value must not be 'undefined'");
|
||||
|
||||
@@ -2690,7 +2690,7 @@ namespace ts {
|
||||
if (loopOutParameters.length) {
|
||||
copyOutParameters(loopOutParameters, CopyDirection.ToOutParameter, statements);
|
||||
}
|
||||
addRange(statements, lexicalEnvironment)
|
||||
addRange(statements, lexicalEnvironment);
|
||||
loopBody = createBlock(statements, /*multiline*/ true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1152,7 +1152,7 @@ namespace ts {
|
||||
createIdentifier("__esModule"),
|
||||
createLiteral(true)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
statement = createStatement(
|
||||
|
||||
@@ -3297,7 +3297,7 @@
|
||||
}
|
||||
|
||||
export interface PluginImport {
|
||||
name: string
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[];
|
||||
|
||||
@@ -585,7 +585,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
private getGoToDefinition(): ts.DefinitionInfo[] {
|
||||
return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition)
|
||||
return this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
}
|
||||
|
||||
public verifyGoToType(arg0: any, endMarkerNames?: string | string[]) {
|
||||
@@ -926,7 +926,7 @@ namespace FourSlash {
|
||||
function rangeToReferenceEntry(r: Range) {
|
||||
let { isWriteAccess, isDefinition } = (r.marker && r.marker.data) || { isWriteAccess: false, isDefinition: false };
|
||||
isWriteAccess = !!isWriteAccess; isDefinition = !!isDefinition;
|
||||
return { fileName: r.fileName, textSpan: { start: r.start, length: r.end - r.start }, isWriteAccess, isDefinition }
|
||||
return { fileName: r.fileName, textSpan: { start: r.start, length: r.end - r.start }, isWriteAccess, isDefinition };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2136,7 +2136,7 @@ namespace FourSlash {
|
||||
|
||||
const result = includeWhiteSpace
|
||||
? actualText === expectedText
|
||||
: this.removeWhitespace(actualText) === this.removeWhitespace(expectedText)
|
||||
: this.removeWhitespace(actualText) === this.removeWhitespace(expectedText);
|
||||
|
||||
if (!result) {
|
||||
this.raiseError(`Actual text doesn't match expected text. Actual:\n'${actualText}'\nExpected:\n'${expectedText}'`);
|
||||
@@ -2173,7 +2173,7 @@ namespace FourSlash {
|
||||
start: diagnostic.start,
|
||||
length: diagnostic.length,
|
||||
code: diagnostic.code
|
||||
}
|
||||
};
|
||||
});
|
||||
const dedupedDiagnositcs = ts.deduplicate(diagnosticsForCodeFix, ts.equalOwnProperties);
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ declare namespace NodeJS {
|
||||
declare var window: {};
|
||||
declare var XMLHttpRequest: {
|
||||
new(): XMLHttpRequest;
|
||||
}
|
||||
};
|
||||
interface XMLHttpRequest {
|
||||
readonly readyState: number;
|
||||
readonly responseText: string;
|
||||
@@ -1017,7 +1017,7 @@ namespace Harness {
|
||||
}
|
||||
else {
|
||||
if (!es6TestLibFileNameSourceFileMap.get(libFileName)) {
|
||||
es6TestLibFileNameSourceFileMap.set(libFileName, createSourceFileAndAssertInvariants(libFileName, IO.readFile(libFileName), scriptTarget))
|
||||
es6TestLibFileNameSourceFileMap.set(libFileName, createSourceFileAndAssertInvariants(libFileName, IO.readFile(libFileName), scriptTarget));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,7 +779,7 @@ namespace Harness.LanguageService {
|
||||
start: 0
|
||||
});
|
||||
return prev;
|
||||
}
|
||||
};
|
||||
return proxy;
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -496,7 +496,7 @@ namespace ts.projectSystem {
|
||||
const emitOutput = host.readFile(path + ".js");
|
||||
assert.equal(emitOutput, f.content + newLine, "content of emit output should be identical with the input + newline");
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it("should emit specified file", () => {
|
||||
const file1 = {
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace ts {
|
||||
Harness.Baseline.runBaseline(`printerApi/${prefix}.${name}.js`, () =>
|
||||
printCallback(createPrinter({ newLine: NewLineKind.CarriageReturnLineFeed, ...options })));
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe("printFile", () => {
|
||||
|
||||
@@ -65,6 +65,6 @@ namespace ts.textStorage {
|
||||
|
||||
ts1.getLineInfo(0);
|
||||
assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 2");
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -628,7 +628,7 @@ namespace ts.projectSystem {
|
||||
|
||||
checkProjectActualFiles(service.configuredProjects[0], []);
|
||||
checkProjectActualFiles(service.inferredProjects[0], [f1.path]);
|
||||
})
|
||||
});
|
||||
|
||||
it("create configured project without file list", () => {
|
||||
const configFile: FileOrFolder = {
|
||||
@@ -1181,7 +1181,7 @@ namespace ts.projectSystem {
|
||||
|
||||
const host = createServerHost([f1, f2, libFile]);
|
||||
const service = createProjectService(host);
|
||||
service.openExternalProject({ projectFileName: "/a/b/project", rootFiles: toExternalFiles([f1.path, f2.path]), options: {} })
|
||||
service.openExternalProject({ projectFileName: "/a/b/project", rootFiles: toExternalFiles([f1.path, f2.path]), options: {} });
|
||||
|
||||
service.openClientFile(f1.path);
|
||||
service.openClientFile(f2.path, "let x: string");
|
||||
@@ -1213,7 +1213,7 @@ namespace ts.projectSystem {
|
||||
|
||||
const host = createServerHost([f1, f2, libFile]);
|
||||
const service = createProjectService(host);
|
||||
service.openExternalProject({ projectFileName: "/a/b/project", rootFiles: [{ fileName: f1.path }, { fileName: f2.path, hasMixedContent: true }], options: {} })
|
||||
service.openExternalProject({ projectFileName: "/a/b/project", rootFiles: [{ fileName: f1.path }, { fileName: f2.path, hasMixedContent: true }], options: {} });
|
||||
|
||||
service.openClientFile(f1.path);
|
||||
service.openClientFile(f2.path, "let somelongname: string");
|
||||
@@ -2040,7 +2040,7 @@ namespace ts.projectSystem {
|
||||
|
||||
for (const f of [f2, f3]) {
|
||||
const scriptInfo = projectService.getScriptInfoForNormalizedPath(server.toNormalizedPath(f.path));
|
||||
assert.equal(scriptInfo.containingProjects.length, 0, `expect 0 containing projects for '${f.path}'`)
|
||||
assert.equal(scriptInfo.containingProjects.length, 0, `expect 0 containing projects for '${f.path}'`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2156,7 +2156,7 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
rootFiles: [toExternalFile(f1.path)],
|
||||
options: {}
|
||||
})
|
||||
});
|
||||
projectService.openClientFile(f1.path, "let x = 1;\nlet y = 2;");
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
@@ -3307,12 +3307,12 @@ namespace ts.projectSystem {
|
||||
isCancellationRequested: () => false,
|
||||
setRequest: requestId => {
|
||||
if (expectedRequestId === undefined) {
|
||||
assert.isTrue(false, "unexpected call")
|
||||
assert.isTrue(false, "unexpected call");
|
||||
}
|
||||
assert.equal(requestId, expectedRequestId);
|
||||
},
|
||||
resetRequest: noop
|
||||
}
|
||||
};
|
||||
const session = createSession(host, /*typingsInstaller*/ undefined, /*projectServiceEventHandler*/ undefined, cancellationToken);
|
||||
|
||||
expectedRequestId = session.getNextSeq();
|
||||
@@ -3359,13 +3359,13 @@ namespace ts.projectSystem {
|
||||
currentId = requestId;
|
||||
},
|
||||
resetRequest(requestId) {
|
||||
assert.equal(requestId, currentId, "unexpected request id in cancellation")
|
||||
assert.equal(requestId, currentId, "unexpected request id in cancellation");
|
||||
currentId = undefined;
|
||||
},
|
||||
isCancellationRequested() {
|
||||
return requestToCancel === currentId;
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
const host = createServerHost([f1, config]);
|
||||
const session = createSession(host, /*typingsInstaller*/ undefined, () => {}, cancellationToken);
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace ts.projectSystem {
|
||||
path: "/a/config.js",
|
||||
content: "export let x = 1"
|
||||
};
|
||||
const typesCache = "/cache"
|
||||
const typesCache = "/cache";
|
||||
const typesConfig = {
|
||||
path: typesCache + "/node_modules/@types/config/index.d.ts",
|
||||
content: "export let y: number;"
|
||||
@@ -74,7 +74,7 @@ namespace ts.projectSystem {
|
||||
super(host, { typesRegistry: createTypesRegistry("config"), globalTypingsCacheLocation: typesCache });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, _cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
assert(false, "should not be called")
|
||||
assert(false, "should not be called");
|
||||
}
|
||||
})();
|
||||
const service = createProjectService(host, { typingsInstaller: installer });
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace ts.server {
|
||||
getScriptKind: _ => undefined,
|
||||
hasMixedContent: (fileName, extraFileExtensions) => {
|
||||
const mixedContentExtensions = ts.map(ts.filter(extraFileExtensions, item => item.isMixedContent), item => item.extension);
|
||||
return forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension))
|
||||
return forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1377,7 +1377,7 @@ namespace ts.server {
|
||||
|
||||
// close projects that were missing in the input list
|
||||
forEachKey(projectsToClose, externalProjectName => {
|
||||
this.closeExternalProject(externalProjectName, /*suppressRefresh*/ true)
|
||||
this.closeExternalProject(externalProjectName, /*suppressRefresh*/ true);
|
||||
});
|
||||
|
||||
this.refreshInferredProjects();
|
||||
|
||||
@@ -723,7 +723,7 @@ namespace ts.server {
|
||||
const fileName = resolvedTypeReferenceDirective.resolvedFileName;
|
||||
const typeFilePath = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
referencedFiles.set(typeFilePath, true);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const allFileNames = arrayFrom(referencedFiles.keys()) as Path[];
|
||||
@@ -745,7 +745,7 @@ namespace ts.server {
|
||||
const id = nextId;
|
||||
nextId++;
|
||||
return makeInferredProjectName(id);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
private _isJsInferredProject = false;
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace ts.server {
|
||||
|
||||
public reloadFromFile(tempFileName?: string) {
|
||||
if (this.svc || (tempFileName !== this.fileName)) {
|
||||
this.reload(this.getFileText(tempFileName))
|
||||
this.reload(this.getFileText(tempFileName));
|
||||
}
|
||||
else {
|
||||
this.setText(undefined);
|
||||
|
||||
@@ -47,14 +47,14 @@ namespace ts.server {
|
||||
if (process.env.XDG_CACHE_HOME) {
|
||||
return process.env.XDG_CACHE_HOME;
|
||||
}
|
||||
const usersDir = platformIsDarwin ? "Users" : "home"
|
||||
const usersDir = platformIsDarwin ? "Users" : "home";
|
||||
const homePath = (os.homedir && os.homedir()) ||
|
||||
process.env.HOME ||
|
||||
((process.env.LOGNAME || process.env.USER) && `/${usersDir}/${process.env.LOGNAME || process.env.USER}`) ||
|
||||
os.tmpdir();
|
||||
const cacheFolder = platformIsDarwin
|
||||
? "Library/Caches"
|
||||
: ".cache"
|
||||
: ".cache";
|
||||
return combinePaths(normalizeSlashes(homePath), cacheFolder);
|
||||
}
|
||||
|
||||
@@ -653,7 +653,7 @@ namespace ts.server {
|
||||
// this drive is unsafe - return no-op watcher
|
||||
return { close() { } };
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Override sys.write because fs.writeSync is not reliable on Node 4
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace ts.server {
|
||||
|
||||
/**
|
||||
* Represents operation that can schedule its next step to be executed later.
|
||||
* Scheduling is done via instance of NextStep. If on current step subsequent step was not scheduled - operation is assumed to be completed.
|
||||
* Scheduling is done via instance of NextStep. If on current step subsequent step was not scheduled - operation is assumed to be completed.
|
||||
*/
|
||||
class MultistepOperation {
|
||||
private requestId: number;
|
||||
@@ -239,7 +239,7 @@ namespace ts.server {
|
||||
this.next = {
|
||||
immediate: action => this.immediate(action),
|
||||
delay: (ms, action) => this.delay(ms, action)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public startNew(action: (next: NextStep) => void) {
|
||||
@@ -262,7 +262,7 @@ namespace ts.server {
|
||||
|
||||
private immediate(action: () => void) {
|
||||
const requestId = this.requestId;
|
||||
Debug.assert(requestId === this.operationHost.getCurrentRequestId(), "immediate: incorrect request id")
|
||||
Debug.assert(requestId === this.operationHost.getCurrentRequestId(), "immediate: incorrect request id");
|
||||
this.setImmediateId(this.operationHost.getServerHost().setImmediate(() => {
|
||||
this.immediateId = undefined;
|
||||
this.operationHost.executeWithRequestId(requestId, () => this.executeAction(action));
|
||||
@@ -271,7 +271,7 @@ namespace ts.server {
|
||||
|
||||
private delay(ms: number, action: () => void) {
|
||||
const requestId = this.requestId;
|
||||
Debug.assert(requestId === this.operationHost.getCurrentRequestId(), "delay: incorrect request id")
|
||||
Debug.assert(requestId === this.operationHost.getCurrentRequestId(), "delay: incorrect request id");
|
||||
this.setTimerHandle(this.operationHost.getServerHost().setTimeout(() => {
|
||||
this.timerHandle = undefined;
|
||||
this.operationHost.executeWithRequestId(requestId, () => this.executeAction(action));
|
||||
@@ -351,7 +351,7 @@ namespace ts.server {
|
||||
logError: (err, cmd) => this.logError(err, cmd),
|
||||
sendRequestCompletedEvent: requestId => this.sendRequestCompletedEvent(requestId),
|
||||
isCancellationRequested: () => cancellationToken.isCancellationRequested()
|
||||
}
|
||||
};
|
||||
this.errorCheck = new MultistepOperation(multistepOperationHost);
|
||||
this.projectService = new ProjectService(host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, this.eventHander);
|
||||
this.gcTimer = new GcTimer(host, /*delay*/ 7000, logger);
|
||||
|
||||
@@ -61,9 +61,7 @@ namespace ts.server.typingsInstaller {
|
||||
return combinePaths(normalizeSlashes(globalTypingsCacheLocation), `node_modules/${TypesRegistryPackageName}/index.json`);
|
||||
}
|
||||
|
||||
type ExecSync = {
|
||||
(command: string, options: { cwd: string, stdio?: "ignore" }): any
|
||||
}
|
||||
type ExecSync = (command: string, options: { cwd: string, stdio?: "ignore" }) => any;
|
||||
|
||||
export class NodeTypingsInstaller extends TypingsInstaller {
|
||||
private readonly execSync: ExecSync;
|
||||
|
||||
@@ -11,7 +11,7 @@ const fs: { watch(directoryName: string, options: any, callback: () => {}): any
|
||||
// This means that here we treat any result (success or exception) from fs.watch as success since it does not tear down the process.
|
||||
// The only case that should be considered as failure - when watchGuard process crashes.
|
||||
try {
|
||||
const watcher = fs.watch(directoryName, { recursive: true }, () => ({}))
|
||||
const watcher = fs.watch(directoryName, { recursive: true }, () => ({}));
|
||||
watcher.close();
|
||||
}
|
||||
catch (_e) {
|
||||
|
||||
@@ -3,8 +3,8 @@ namespace ts.codefix {
|
||||
|
||||
type ImportCodeActionKind = "CodeChange" | "InsertingIntoExistingImport" | "NewImport";
|
||||
interface ImportCodeAction extends CodeAction {
|
||||
kind: ImportCodeActionKind,
|
||||
moduleSpecifier?: string
|
||||
kind: ImportCodeActionKind;
|
||||
moduleSpecifier?: string;
|
||||
}
|
||||
|
||||
enum ModuleSpecifierComparison {
|
||||
@@ -75,7 +75,7 @@ namespace ts.codefix {
|
||||
getAllActions() {
|
||||
let result: ImportCodeAction[] = [];
|
||||
for (const key in this.symbolIdToActionMap) {
|
||||
result = concatenate(result, this.symbolIdToActionMap[key])
|
||||
result = concatenate(result, this.symbolIdToActionMap[key]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+13
-549
@@ -1,10 +1,12 @@
|
||||
/// <reference path="./pathCompletions.ts" />
|
||||
|
||||
/* @internal */
|
||||
namespace ts.Completions {
|
||||
export type Log = (message: string) => void;
|
||||
|
||||
export function getCompletionsAtPosition(host: LanguageServiceHost, typeChecker: TypeChecker, log: Log, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number): CompletionInfo | undefined {
|
||||
if (isInReferenceComment(sourceFile, position)) {
|
||||
return getTripleSlashReferenceCompletion(sourceFile, position, compilerOptions, host);
|
||||
return PathCompletions.getTripleSlashReferenceCompletion(sourceFile, position, compilerOptions, host);
|
||||
}
|
||||
|
||||
if (isInString(sourceFile, position)) {
|
||||
@@ -176,7 +178,7 @@ namespace ts.Completions {
|
||||
// i.e. import * as ns from "/*completion position*/";
|
||||
// import x = require("/*completion position*/");
|
||||
// var y = require("/*completion position*/");
|
||||
return getStringLiteralCompletionEntriesFromModuleNames(<StringLiteral>node, compilerOptions, host, typeChecker);
|
||||
return PathCompletions.getStringLiteralCompletionEntriesFromModuleNames(<StringLiteral>node, compilerOptions, host, typeChecker);
|
||||
}
|
||||
else if (isEqualityExpression(node.parent)) {
|
||||
// Get completions from the type of the other operand
|
||||
@@ -278,489 +280,6 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionInfo {
|
||||
const literalValue = normalizeSlashes(node.text);
|
||||
|
||||
const scriptPath = node.getSourceFile().path;
|
||||
const scriptDirectory = getDirectoryPath(scriptPath);
|
||||
|
||||
const span = getDirectoryFragmentTextSpan((<StringLiteral>node).text, node.getStart() + 1);
|
||||
let entries: CompletionEntry[];
|
||||
if (isPathRelativeToScript(literalValue) || isRootedDiskPath(literalValue)) {
|
||||
const extensions = getSupportedExtensions(compilerOptions);
|
||||
if (compilerOptions.rootDirs) {
|
||||
entries = getCompletionEntriesForDirectoryFragmentWithRootDirs(
|
||||
compilerOptions.rootDirs, literalValue, scriptDirectory, extensions, /*includeExtensions*/false, span, compilerOptions, host, scriptPath);
|
||||
}
|
||||
else {
|
||||
entries = getCompletionEntriesForDirectoryFragment(
|
||||
literalValue, scriptDirectory, extensions, /*includeExtensions*/false, span, host, scriptPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Check for node modules
|
||||
entries = getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span, compilerOptions, host, typeChecker);
|
||||
}
|
||||
return {
|
||||
isGlobalCompletion: false,
|
||||
isMemberCompletion: false,
|
||||
isNewIdentifierLocation: true,
|
||||
entries
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a script path and returns paths for all potential folders that could be merged with its
|
||||
* containing folder via the "rootDirs" compiler option
|
||||
*/
|
||||
function getBaseDirectoriesFromRootDirs(rootDirs: string[], basePath: string, scriptPath: string, ignoreCase: boolean): string[] {
|
||||
// Make all paths absolute/normalized if they are not already
|
||||
rootDirs = map(rootDirs, rootDirectory => normalizePath(isRootedDiskPath(rootDirectory) ? rootDirectory : combinePaths(basePath, rootDirectory)));
|
||||
|
||||
// Determine the path to the directory containing the script relative to the root directory it is contained within
|
||||
let relativeDirectory: string;
|
||||
for (const rootDirectory of rootDirs) {
|
||||
if (containsPath(rootDirectory, scriptPath, basePath, ignoreCase)) {
|
||||
relativeDirectory = scriptPath.substr(rootDirectory.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now find a path for each potential directory that is to be merged with the one containing the script
|
||||
return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory)));
|
||||
}
|
||||
|
||||
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] {
|
||||
const basePath = compilerOptions.project || host.getCurrentDirectory();
|
||||
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
|
||||
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase);
|
||||
|
||||
const result: CompletionEntry[] = [];
|
||||
|
||||
for (const baseDirectory of baseDirectories) {
|
||||
getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, host, exclude, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename.
|
||||
*/
|
||||
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] {
|
||||
if (fragment === undefined) {
|
||||
fragment = "";
|
||||
}
|
||||
|
||||
fragment = normalizeSlashes(fragment);
|
||||
|
||||
/**
|
||||
* Remove the basename from the path. Note that we don't use the basename to filter completions;
|
||||
* the client is responsible for refining completions.
|
||||
*/
|
||||
fragment = getDirectoryPath(fragment);
|
||||
|
||||
if (fragment === "") {
|
||||
fragment = "." + directorySeparator;
|
||||
}
|
||||
|
||||
fragment = ensureTrailingDirectorySeparator(fragment);
|
||||
|
||||
const absolutePath = normalizeAndPreserveTrailingSlash(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment));
|
||||
const baseDirectory = getDirectoryPath(absolutePath);
|
||||
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
|
||||
|
||||
if (tryDirectoryExists(host, baseDirectory)) {
|
||||
// Enumerate the available files if possible
|
||||
const files = tryReadDirectory(host, baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]);
|
||||
|
||||
if (files) {
|
||||
/**
|
||||
* Multiple file entries might map to the same truncated name once we remove extensions
|
||||
* (happens iff includeExtensions === false)so we use a set-like data structure. Eg:
|
||||
*
|
||||
* both foo.ts and foo.tsx become foo
|
||||
*/
|
||||
const foundFiles = createMap<boolean>();
|
||||
for (let filePath of files) {
|
||||
filePath = normalizePath(filePath);
|
||||
if (exclude && comparePaths(filePath, exclude, scriptPath, ignoreCase) === Comparison.EqualTo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const foundFileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath));
|
||||
|
||||
if (!foundFiles.get(foundFileName)) {
|
||||
foundFiles.set(foundFileName, true);
|
||||
}
|
||||
}
|
||||
|
||||
forEachKey(foundFiles, foundFile => {
|
||||
result.push(createCompletionEntryForModule(foundFile, ScriptElementKind.scriptElement, span));
|
||||
});
|
||||
}
|
||||
|
||||
// If possible, get folder completion as well
|
||||
const directories = tryGetDirectories(host, baseDirectory);
|
||||
|
||||
if (directories) {
|
||||
for (const directory of directories) {
|
||||
const directoryName = getBaseFileName(normalizePath(directory));
|
||||
|
||||
result.push(createCompletionEntryForModule(directoryName, ScriptElementKind.directory, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all of the declared modules and those in node modules. Possible sources of modules:
|
||||
* Modules that are found by the type checker
|
||||
* Modules found relative to "baseUrl" compliler options (including patterns from "paths" compiler option)
|
||||
* Modules from node_modules (i.e. those listed in package.json)
|
||||
* This includes all files that are found in node_modules/moduleName/ with acceptable file extensions
|
||||
*/
|
||||
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionEntry[] {
|
||||
const { baseUrl, paths } = compilerOptions;
|
||||
|
||||
let result: CompletionEntry[];
|
||||
|
||||
if (baseUrl) {
|
||||
const fileExtensions = getSupportedExtensions(compilerOptions);
|
||||
const projectDir = compilerOptions.project || host.getCurrentDirectory();
|
||||
const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl);
|
||||
result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false, span, host);
|
||||
|
||||
if (paths) {
|
||||
for (const path in paths) {
|
||||
if (paths.hasOwnProperty(path)) {
|
||||
if (path === "*") {
|
||||
if (paths[path]) {
|
||||
for (const pattern of paths[path]) {
|
||||
for (const match of getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions, host)) {
|
||||
result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (startsWith(path, fragment)) {
|
||||
const entry = paths[path] && paths[path].length === 1 && paths[path][0];
|
||||
if (entry) {
|
||||
result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = [];
|
||||
}
|
||||
|
||||
getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span, result);
|
||||
|
||||
for (const moduleName of enumeratePotentialNonRelativeModules(fragment, scriptPath, compilerOptions, typeChecker, host)) {
|
||||
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[], host: LanguageServiceHost): string[] {
|
||||
if (host.readDirectory) {
|
||||
const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined;
|
||||
if (parsed) {
|
||||
// The prefix has two effective parts: the directory path and the base component after the filepath that is not a
|
||||
// full directory component. For example: directory/path/of/prefix/base*
|
||||
const normalizedPrefix = normalizeAndPreserveTrailingSlash(parsed.prefix);
|
||||
const normalizedPrefixDirectory = getDirectoryPath(normalizedPrefix);
|
||||
const normalizedPrefixBase = getBaseFileName(normalizedPrefix);
|
||||
|
||||
const fragmentHasPath = fragment.indexOf(directorySeparator) !== -1;
|
||||
|
||||
// Try and expand the prefix to include any path from the fragment so that we can limit the readDirectory call
|
||||
const expandedPrefixDirectory = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + getDirectoryPath(fragment)) : normalizedPrefixDirectory;
|
||||
|
||||
const normalizedSuffix = normalizePath(parsed.suffix);
|
||||
const baseDirectory = combinePaths(baseUrl, expandedPrefixDirectory);
|
||||
const completePrefix = fragmentHasPath ? baseDirectory : ensureTrailingDirectorySeparator(baseDirectory) + normalizedPrefixBase;
|
||||
|
||||
// If we have a suffix, then we need to read the directory all the way down. We could create a glob
|
||||
// that encodes the suffix, but we would have to escape the character "?" which readDirectory
|
||||
// doesn't support. For now, this is safer but slower
|
||||
const includeGlob = normalizedSuffix ? "**/*" : "./*";
|
||||
|
||||
const matches = tryReadDirectory(host, baseDirectory, fileExtensions, undefined, [includeGlob]);
|
||||
if (matches) {
|
||||
const result: string[] = [];
|
||||
|
||||
// Trim away prefix and suffix
|
||||
for (const match of matches) {
|
||||
const normalizedMatch = normalizePath(match);
|
||||
if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const start = completePrefix.length;
|
||||
const length = normalizedMatch.length - start - normalizedSuffix.length;
|
||||
|
||||
result.push(removeFileExtension(normalizedMatch.substr(start, length)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string, options: CompilerOptions, typeChecker: TypeChecker, host: LanguageServiceHost): string[] {
|
||||
// Check If this is a nested module
|
||||
const isNestedModule = fragment.indexOf(directorySeparator) !== -1;
|
||||
const moduleNameFragment = isNestedModule ? fragment.substr(0, fragment.lastIndexOf(directorySeparator)) : undefined;
|
||||
|
||||
// Get modules that the type checker picked up
|
||||
const ambientModules = map(typeChecker.getAmbientModules(), sym => stripQuotes(sym.name));
|
||||
let nonRelativeModules = filter(ambientModules, moduleName => startsWith(moduleName, fragment));
|
||||
|
||||
// Nested modules of the form "module-name/sub" need to be adjusted to only return the string
|
||||
// after the last '/' that appears in the fragment because that's where the replacement span
|
||||
// starts
|
||||
if (isNestedModule) {
|
||||
const moduleNameWithSeperator = ensureTrailingDirectorySeparator(moduleNameFragment);
|
||||
nonRelativeModules = map(nonRelativeModules, moduleName => {
|
||||
if (startsWith(fragment, moduleNameWithSeperator)) {
|
||||
return moduleName.substr(moduleNameWithSeperator.length);
|
||||
}
|
||||
return moduleName;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (!options.moduleResolution || options.moduleResolution === ModuleResolutionKind.NodeJs) {
|
||||
for (const visibleModule of enumerateNodeModulesVisibleToScript(host, scriptPath)) {
|
||||
if (!isNestedModule) {
|
||||
nonRelativeModules.push(visibleModule.moduleName);
|
||||
}
|
||||
else if (startsWith(visibleModule.moduleName, moduleNameFragment)) {
|
||||
const nestedFiles = tryReadDirectory(host, visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]);
|
||||
if (nestedFiles) {
|
||||
for (let f of nestedFiles) {
|
||||
f = normalizePath(f);
|
||||
const nestedModule = removeFileExtension(getBaseFileName(f));
|
||||
nonRelativeModules.push(nestedModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deduplicate(nonRelativeModules);
|
||||
}
|
||||
|
||||
function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): CompletionInfo {
|
||||
const token = getTokenAtPosition(sourceFile, position);
|
||||
if (!token) {
|
||||
return undefined;
|
||||
}
|
||||
const commentRanges: CommentRange[] = getLeadingCommentRanges(sourceFile.text, token.pos);
|
||||
|
||||
if (!commentRanges || !commentRanges.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const range = forEach(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end && commentRange);
|
||||
|
||||
if (!range) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const completionInfo: CompletionInfo = {
|
||||
/**
|
||||
* We don't want the editor to offer any other completions, such as snippets, inside a comment.
|
||||
*/
|
||||
isGlobalCompletion: false,
|
||||
isMemberCompletion: false,
|
||||
/**
|
||||
* The user may type in a path that doesn't yet exist, creating a "new identifier"
|
||||
* with respect to the collection of identifiers the server is aware of.
|
||||
*/
|
||||
isNewIdentifierLocation: true,
|
||||
|
||||
entries: []
|
||||
};
|
||||
|
||||
const text = sourceFile.text.substr(range.pos, position - range.pos);
|
||||
|
||||
const match = tripleSlashDirectiveFragmentRegex.exec(text);
|
||||
|
||||
if (match) {
|
||||
const prefix = match[1];
|
||||
const kind = match[2];
|
||||
const toComplete = match[3];
|
||||
|
||||
const scriptPath = getDirectoryPath(sourceFile.path);
|
||||
if (kind === "path") {
|
||||
// Give completions for a relative path
|
||||
const span: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length);
|
||||
completionInfo.entries = getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(compilerOptions), /*includeExtensions*/true, span, host, sourceFile.path);
|
||||
}
|
||||
else {
|
||||
// Give completions based on the typings available
|
||||
const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length };
|
||||
completionInfo.entries = getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span);
|
||||
}
|
||||
}
|
||||
|
||||
return completionInfo;
|
||||
}
|
||||
|
||||
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: CompletionEntry[] = []): CompletionEntry[] {
|
||||
// Check for typings specified in compiler options
|
||||
if (options.types) {
|
||||
for (const moduleName of options.types) {
|
||||
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
}
|
||||
else if (host.getDirectories) {
|
||||
let typeRoots: string[];
|
||||
try {
|
||||
// Wrap in try catch because getEffectiveTypeRoots touches the filesystem
|
||||
typeRoots = getEffectiveTypeRoots(options, host);
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
if (typeRoots) {
|
||||
for (const root of typeRoots) {
|
||||
getCompletionEntriesFromDirectories(host, root, span, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (host.getDirectories) {
|
||||
// Also get all @types typings installed in visible node_modules directories
|
||||
for (const packageJson of findPackageJsons(scriptPath, host)) {
|
||||
const typesDir = combinePaths(getDirectoryPath(packageJson), "node_modules/@types");
|
||||
getCompletionEntriesFromDirectories(host, typesDir, span, result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getCompletionEntriesFromDirectories(host: LanguageServiceHost, directory: string, span: TextSpan, result: Push<CompletionEntry>) {
|
||||
if (host.getDirectories && tryDirectoryExists(host, directory)) {
|
||||
const directories = tryGetDirectories(host, directory);
|
||||
if (directories) {
|
||||
for (let typeDirectory of directories) {
|
||||
typeDirectory = normalizePath(typeDirectory);
|
||||
result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findPackageJsons(currentDir: string, host: LanguageServiceHost): string[] {
|
||||
const paths: string[] = [];
|
||||
let currentConfigPath: string;
|
||||
while (true) {
|
||||
currentConfigPath = findConfigFile(currentDir, (f) => tryFileExists(host, f), "package.json");
|
||||
if (currentConfigPath) {
|
||||
paths.push(currentConfigPath);
|
||||
|
||||
currentDir = getDirectoryPath(currentConfigPath);
|
||||
const parent = getDirectoryPath(currentDir);
|
||||
if (currentDir === parent) {
|
||||
break;
|
||||
}
|
||||
currentDir = parent;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string) {
|
||||
const result: VisibleModuleInfo[] = [];
|
||||
|
||||
if (host.readFile && host.fileExists) {
|
||||
for (const packageJson of findPackageJsons(scriptPath, host)) {
|
||||
const contents = tryReadingPackageJson(packageJson);
|
||||
if (!contents) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules");
|
||||
const foundModuleNames: string[] = [];
|
||||
|
||||
// Provide completions for all non @types dependencies
|
||||
for (const key of nodeModulesDependencyKeys) {
|
||||
addPotentialPackageNames(contents[key], foundModuleNames);
|
||||
}
|
||||
|
||||
for (const moduleName of foundModuleNames) {
|
||||
const moduleDir = combinePaths(nodeModulesDir, moduleName);
|
||||
result.push({
|
||||
moduleName,
|
||||
moduleDir
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
function tryReadingPackageJson(filePath: string) {
|
||||
try {
|
||||
const fileText = tryReadFile(host, filePath);
|
||||
return fileText ? JSON.parse(fileText) : undefined;
|
||||
}
|
||||
catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function addPotentialPackageNames(dependencies: any, result: string[]) {
|
||||
if (dependencies) {
|
||||
for (const dep in dependencies) {
|
||||
if (dependencies.hasOwnProperty(dep) && !startsWith(dep, "@types/")) {
|
||||
result.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createCompletionEntryForModule(name: string, kind: string, replacementSpan: TextSpan): CompletionEntry {
|
||||
return { name, kind, kindModifiers: ScriptElementKindModifier.none, sortText: name, replacementSpan };
|
||||
}
|
||||
|
||||
// Replace everything after the last directory seperator that appears
|
||||
function getDirectoryFragmentTextSpan(text: string, textStart: number): TextSpan {
|
||||
const index = text.lastIndexOf(directorySeparator);
|
||||
const offset = index !== -1 ? index + 1 : 0;
|
||||
return { start: textStart + offset, length: text.length - offset };
|
||||
}
|
||||
|
||||
// Returns true if the path is explicitly relative to the script (i.e. relative to . or ..)
|
||||
function isPathRelativeToScript(path: string) {
|
||||
if (path && path.length >= 2 && path.charCodeAt(0) === CharacterCodes.dot) {
|
||||
const slashIndex = path.length >= 3 && path.charCodeAt(1) === CharacterCodes.dot ? 2 : 1;
|
||||
const slashCharCode = path.charCodeAt(slashIndex);
|
||||
return slashCharCode === CharacterCodes.slash || slashCharCode === CharacterCodes.backslash;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function normalizeAndPreserveTrailingSlash(path: string) {
|
||||
return hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(normalizePath(path)) : normalizePath(path);
|
||||
}
|
||||
|
||||
export function getCompletionEntryDetails(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): CompletionEntryDetails {
|
||||
// Compute all the completion symbols again.
|
||||
const completionData = getCompletionData(typeChecker, log, sourceFile, position);
|
||||
@@ -1487,20 +1006,18 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function isFunction(kind: SyntaxKind): boolean {
|
||||
if (!isFunctionLikeKind(kind)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
case SyntaxKind.IndexSignature:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.ConstructorType:
|
||||
case SyntaxKind.FunctionType:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1778,59 +1295,6 @@ namespace ts.Completions {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a triple slash reference directive with an incomplete string literal for its path. Used
|
||||
* to determine if the caret is currently within the string literal and capture the literal fragment
|
||||
* for completions.
|
||||
* For example, this matches
|
||||
*
|
||||
* /// <reference path="fragment
|
||||
*
|
||||
* but not
|
||||
*
|
||||
* /// <reference path="fragment"
|
||||
*/
|
||||
const tripleSlashDirectiveFragmentRegex = /^(\/\/\/\s*<reference\s+(path|types)\s*=\s*(?:'|"))([^\3"]*)$/;
|
||||
|
||||
interface VisibleModuleInfo {
|
||||
moduleName: string;
|
||||
moduleDir: string;
|
||||
}
|
||||
|
||||
const nodeModulesDependencyKeys = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
|
||||
|
||||
function tryGetDirectories(host: LanguageServiceHost, directoryName: string): string[] {
|
||||
return tryIOAndConsumeErrors(host, host.getDirectories, directoryName);
|
||||
}
|
||||
|
||||
function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
|
||||
return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include);
|
||||
}
|
||||
|
||||
function tryReadFile(host: LanguageServiceHost, path: string): string {
|
||||
return tryIOAndConsumeErrors(host, host.readFile, path);
|
||||
}
|
||||
|
||||
function tryFileExists(host: LanguageServiceHost, path: string): boolean {
|
||||
return tryIOAndConsumeErrors(host, host.fileExists, path);
|
||||
}
|
||||
|
||||
function tryDirectoryExists(host: LanguageServiceHost, path: string): boolean {
|
||||
try {
|
||||
return directoryProbablyExists(path, host);
|
||||
}
|
||||
catch (e) {}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function tryIOAndConsumeErrors<T>(host: LanguageServiceHost, toApply: (...a: any[]) => T, ...args: any[]) {
|
||||
try {
|
||||
return toApply && toApply.apply(host, args);
|
||||
}
|
||||
catch (e) {}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isEqualityExpression(node: Node): node is BinaryExpression {
|
||||
return isBinaryExpression(node) && isEqualityOperatorKind(node.operatorToken.kind);
|
||||
}
|
||||
|
||||
@@ -422,7 +422,7 @@ namespace ts.FindAllReferences {
|
||||
name,
|
||||
textSpan: references[0].textSpan,
|
||||
displayParts: [{ text: name, kind: ScriptElementKind.keyword }]
|
||||
}
|
||||
};
|
||||
|
||||
return [{ definition, references }];
|
||||
}
|
||||
@@ -613,7 +613,7 @@ namespace ts.FindAllReferences {
|
||||
const result: Node[] = [];
|
||||
|
||||
for (const decl of classSymbol.members.get("__constructor").declarations) {
|
||||
const ctrKeyword = ts.findChildOfKind(decl, ts.SyntaxKind.ConstructorKeyword, sourceFile)!
|
||||
const ctrKeyword = ts.findChildOfKind(decl, ts.SyntaxKind.ConstructorKeyword, sourceFile)!;
|
||||
Debug.assert(decl.kind === SyntaxKind.Constructor && !!ctrKeyword);
|
||||
result.push(ctrKeyword);
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace ts.JsDoc {
|
||||
kind: ScriptElementKind.keyword,
|
||||
kindModifiers: "",
|
||||
sortText: "0"
|
||||
}
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,538 @@
|
||||
/* @internal */
|
||||
namespace ts.Completions.PathCompletions {
|
||||
export function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionInfo {
|
||||
const literalValue = normalizeSlashes(node.text);
|
||||
|
||||
const scriptPath = node.getSourceFile().path;
|
||||
const scriptDirectory = getDirectoryPath(scriptPath);
|
||||
|
||||
const span = getDirectoryFragmentTextSpan((<StringLiteral>node).text, node.getStart() + 1);
|
||||
let entries: CompletionEntry[];
|
||||
if (isPathRelativeToScript(literalValue) || isRootedDiskPath(literalValue)) {
|
||||
const extensions = getSupportedExtensions(compilerOptions);
|
||||
if (compilerOptions.rootDirs) {
|
||||
entries = getCompletionEntriesForDirectoryFragmentWithRootDirs(
|
||||
compilerOptions.rootDirs, literalValue, scriptDirectory, extensions, /*includeExtensions*/false, span, compilerOptions, host, scriptPath);
|
||||
}
|
||||
else {
|
||||
entries = getCompletionEntriesForDirectoryFragment(
|
||||
literalValue, scriptDirectory, extensions, /*includeExtensions*/false, span, host, scriptPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Check for node modules
|
||||
entries = getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span, compilerOptions, host, typeChecker);
|
||||
}
|
||||
return {
|
||||
isGlobalCompletion: false,
|
||||
isMemberCompletion: false,
|
||||
isNewIdentifierLocation: true,
|
||||
entries
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a script path and returns paths for all potential folders that could be merged with its
|
||||
* containing folder via the "rootDirs" compiler option
|
||||
*/
|
||||
function getBaseDirectoriesFromRootDirs(rootDirs: string[], basePath: string, scriptPath: string, ignoreCase: boolean): string[] {
|
||||
// Make all paths absolute/normalized if they are not already
|
||||
rootDirs = map(rootDirs, rootDirectory => normalizePath(isRootedDiskPath(rootDirectory) ? rootDirectory : combinePaths(basePath, rootDirectory)));
|
||||
|
||||
// Determine the path to the directory containing the script relative to the root directory it is contained within
|
||||
let relativeDirectory: string;
|
||||
for (const rootDirectory of rootDirs) {
|
||||
if (containsPath(rootDirectory, scriptPath, basePath, ignoreCase)) {
|
||||
relativeDirectory = scriptPath.substr(rootDirectory.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now find a path for each potential directory that is to be merged with the one containing the script
|
||||
return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory)));
|
||||
}
|
||||
|
||||
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] {
|
||||
const basePath = compilerOptions.project || host.getCurrentDirectory();
|
||||
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
|
||||
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase);
|
||||
|
||||
const result: CompletionEntry[] = [];
|
||||
|
||||
for (const baseDirectory of baseDirectories) {
|
||||
getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, host, exclude, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename.
|
||||
*/
|
||||
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] {
|
||||
if (fragment === undefined) {
|
||||
fragment = "";
|
||||
}
|
||||
|
||||
fragment = normalizeSlashes(fragment);
|
||||
|
||||
/**
|
||||
* Remove the basename from the path. Note that we don't use the basename to filter completions;
|
||||
* the client is responsible for refining completions.
|
||||
*/
|
||||
fragment = getDirectoryPath(fragment);
|
||||
|
||||
if (fragment === "") {
|
||||
fragment = "." + directorySeparator;
|
||||
}
|
||||
|
||||
fragment = ensureTrailingDirectorySeparator(fragment);
|
||||
|
||||
const absolutePath = normalizeAndPreserveTrailingSlash(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment));
|
||||
const baseDirectory = getDirectoryPath(absolutePath);
|
||||
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
|
||||
|
||||
if (tryDirectoryExists(host, baseDirectory)) {
|
||||
// Enumerate the available files if possible
|
||||
const files = tryReadDirectory(host, baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]);
|
||||
|
||||
if (files) {
|
||||
/**
|
||||
* Multiple file entries might map to the same truncated name once we remove extensions
|
||||
* (happens iff includeExtensions === false)so we use a set-like data structure. Eg:
|
||||
*
|
||||
* both foo.ts and foo.tsx become foo
|
||||
*/
|
||||
const foundFiles = createMap<boolean>();
|
||||
for (let filePath of files) {
|
||||
filePath = normalizePath(filePath);
|
||||
if (exclude && comparePaths(filePath, exclude, scriptPath, ignoreCase) === Comparison.EqualTo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const foundFileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath));
|
||||
|
||||
if (!foundFiles.get(foundFileName)) {
|
||||
foundFiles.set(foundFileName, true);
|
||||
}
|
||||
}
|
||||
|
||||
forEachKey(foundFiles, foundFile => {
|
||||
result.push(createCompletionEntryForModule(foundFile, ScriptElementKind.scriptElement, span));
|
||||
});
|
||||
}
|
||||
|
||||
// If possible, get folder completion as well
|
||||
const directories = tryGetDirectories(host, baseDirectory);
|
||||
|
||||
if (directories) {
|
||||
for (const directory of directories) {
|
||||
const directoryName = getBaseFileName(normalizePath(directory));
|
||||
|
||||
result.push(createCompletionEntryForModule(directoryName, ScriptElementKind.directory, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all of the declared modules and those in node modules. Possible sources of modules:
|
||||
* Modules that are found by the type checker
|
||||
* Modules found relative to "baseUrl" compliler options (including patterns from "paths" compiler option)
|
||||
* Modules from node_modules (i.e. those listed in package.json)
|
||||
* This includes all files that are found in node_modules/moduleName/ with acceptable file extensions
|
||||
*/
|
||||
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionEntry[] {
|
||||
const { baseUrl, paths } = compilerOptions;
|
||||
|
||||
let result: CompletionEntry[];
|
||||
|
||||
if (baseUrl) {
|
||||
const fileExtensions = getSupportedExtensions(compilerOptions);
|
||||
const projectDir = compilerOptions.project || host.getCurrentDirectory();
|
||||
const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl);
|
||||
result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false, span, host);
|
||||
|
||||
if (paths) {
|
||||
for (const path in paths) {
|
||||
if (paths.hasOwnProperty(path)) {
|
||||
if (path === "*") {
|
||||
if (paths[path]) {
|
||||
for (const pattern of paths[path]) {
|
||||
for (const match of getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions, host)) {
|
||||
result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (startsWith(path, fragment)) {
|
||||
const entry = paths[path] && paths[path].length === 1 && paths[path][0];
|
||||
if (entry) {
|
||||
result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = [];
|
||||
}
|
||||
|
||||
getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span, result);
|
||||
|
||||
for (const moduleName of enumeratePotentialNonRelativeModules(fragment, scriptPath, compilerOptions, typeChecker, host)) {
|
||||
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[], host: LanguageServiceHost): string[] {
|
||||
if (host.readDirectory) {
|
||||
const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined;
|
||||
if (parsed) {
|
||||
// The prefix has two effective parts: the directory path and the base component after the filepath that is not a
|
||||
// full directory component. For example: directory/path/of/prefix/base*
|
||||
const normalizedPrefix = normalizeAndPreserveTrailingSlash(parsed.prefix);
|
||||
const normalizedPrefixDirectory = getDirectoryPath(normalizedPrefix);
|
||||
const normalizedPrefixBase = getBaseFileName(normalizedPrefix);
|
||||
|
||||
const fragmentHasPath = fragment.indexOf(directorySeparator) !== -1;
|
||||
|
||||
// Try and expand the prefix to include any path from the fragment so that we can limit the readDirectory call
|
||||
const expandedPrefixDirectory = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + getDirectoryPath(fragment)) : normalizedPrefixDirectory;
|
||||
|
||||
const normalizedSuffix = normalizePath(parsed.suffix);
|
||||
const baseDirectory = combinePaths(baseUrl, expandedPrefixDirectory);
|
||||
const completePrefix = fragmentHasPath ? baseDirectory : ensureTrailingDirectorySeparator(baseDirectory) + normalizedPrefixBase;
|
||||
|
||||
// If we have a suffix, then we need to read the directory all the way down. We could create a glob
|
||||
// that encodes the suffix, but we would have to escape the character "?" which readDirectory
|
||||
// doesn't support. For now, this is safer but slower
|
||||
const includeGlob = normalizedSuffix ? "**/*" : "./*";
|
||||
|
||||
const matches = tryReadDirectory(host, baseDirectory, fileExtensions, undefined, [includeGlob]);
|
||||
if (matches) {
|
||||
const result: string[] = [];
|
||||
|
||||
// Trim away prefix and suffix
|
||||
for (const match of matches) {
|
||||
const normalizedMatch = normalizePath(match);
|
||||
if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const start = completePrefix.length;
|
||||
const length = normalizedMatch.length - start - normalizedSuffix.length;
|
||||
|
||||
result.push(removeFileExtension(normalizedMatch.substr(start, length)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string, options: CompilerOptions, typeChecker: TypeChecker, host: LanguageServiceHost): string[] {
|
||||
// Check If this is a nested module
|
||||
const isNestedModule = fragment.indexOf(directorySeparator) !== -1;
|
||||
const moduleNameFragment = isNestedModule ? fragment.substr(0, fragment.lastIndexOf(directorySeparator)) : undefined;
|
||||
|
||||
// Get modules that the type checker picked up
|
||||
const ambientModules = map(typeChecker.getAmbientModules(), sym => stripQuotes(sym.name));
|
||||
let nonRelativeModules = filter(ambientModules, moduleName => startsWith(moduleName, fragment));
|
||||
|
||||
// Nested modules of the form "module-name/sub" need to be adjusted to only return the string
|
||||
// after the last '/' that appears in the fragment because that's where the replacement span
|
||||
// starts
|
||||
if (isNestedModule) {
|
||||
const moduleNameWithSeperator = ensureTrailingDirectorySeparator(moduleNameFragment);
|
||||
nonRelativeModules = map(nonRelativeModules, moduleName => {
|
||||
if (startsWith(fragment, moduleNameWithSeperator)) {
|
||||
return moduleName.substr(moduleNameWithSeperator.length);
|
||||
}
|
||||
return moduleName;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (!options.moduleResolution || options.moduleResolution === ModuleResolutionKind.NodeJs) {
|
||||
for (const visibleModule of enumerateNodeModulesVisibleToScript(host, scriptPath)) {
|
||||
if (!isNestedModule) {
|
||||
nonRelativeModules.push(visibleModule.moduleName);
|
||||
}
|
||||
else if (startsWith(visibleModule.moduleName, moduleNameFragment)) {
|
||||
const nestedFiles = tryReadDirectory(host, visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]);
|
||||
if (nestedFiles) {
|
||||
for (let f of nestedFiles) {
|
||||
f = normalizePath(f);
|
||||
const nestedModule = removeFileExtension(getBaseFileName(f));
|
||||
nonRelativeModules.push(nestedModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deduplicate(nonRelativeModules);
|
||||
}
|
||||
|
||||
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): CompletionInfo {
|
||||
const token = getTokenAtPosition(sourceFile, position);
|
||||
if (!token) {
|
||||
return undefined;
|
||||
}
|
||||
const commentRanges: CommentRange[] = getLeadingCommentRanges(sourceFile.text, token.pos);
|
||||
|
||||
if (!commentRanges || !commentRanges.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const range = forEach(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end && commentRange);
|
||||
|
||||
if (!range) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const completionInfo: CompletionInfo = {
|
||||
/**
|
||||
* We don't want the editor to offer any other completions, such as snippets, inside a comment.
|
||||
*/
|
||||
isGlobalCompletion: false,
|
||||
isMemberCompletion: false,
|
||||
/**
|
||||
* The user may type in a path that doesn't yet exist, creating a "new identifier"
|
||||
* with respect to the collection of identifiers the server is aware of.
|
||||
*/
|
||||
isNewIdentifierLocation: true,
|
||||
|
||||
entries: []
|
||||
};
|
||||
|
||||
const text = sourceFile.text.substr(range.pos, position - range.pos);
|
||||
|
||||
const match = tripleSlashDirectiveFragmentRegex.exec(text);
|
||||
|
||||
if (match) {
|
||||
const prefix = match[1];
|
||||
const kind = match[2];
|
||||
const toComplete = match[3];
|
||||
|
||||
const scriptPath = getDirectoryPath(sourceFile.path);
|
||||
if (kind === "path") {
|
||||
// Give completions for a relative path
|
||||
const span: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length);
|
||||
completionInfo.entries = getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(compilerOptions), /*includeExtensions*/true, span, host, sourceFile.path);
|
||||
}
|
||||
else {
|
||||
// Give completions based on the typings available
|
||||
const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length };
|
||||
completionInfo.entries = getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span);
|
||||
}
|
||||
}
|
||||
|
||||
return completionInfo;
|
||||
}
|
||||
|
||||
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: CompletionEntry[] = []): CompletionEntry[] {
|
||||
// Check for typings specified in compiler options
|
||||
if (options.types) {
|
||||
for (const moduleName of options.types) {
|
||||
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
}
|
||||
else if (host.getDirectories) {
|
||||
let typeRoots: string[];
|
||||
try {
|
||||
// Wrap in try catch because getEffectiveTypeRoots touches the filesystem
|
||||
typeRoots = getEffectiveTypeRoots(options, host);
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
if (typeRoots) {
|
||||
for (const root of typeRoots) {
|
||||
getCompletionEntriesFromDirectories(host, root, span, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (host.getDirectories) {
|
||||
// Also get all @types typings installed in visible node_modules directories
|
||||
for (const packageJson of findPackageJsons(scriptPath, host)) {
|
||||
const typesDir = combinePaths(getDirectoryPath(packageJson), "node_modules/@types");
|
||||
getCompletionEntriesFromDirectories(host, typesDir, span, result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getCompletionEntriesFromDirectories(host: LanguageServiceHost, directory: string, span: TextSpan, result: Push<CompletionEntry>) {
|
||||
if (host.getDirectories && tryDirectoryExists(host, directory)) {
|
||||
const directories = tryGetDirectories(host, directory);
|
||||
if (directories) {
|
||||
for (let typeDirectory of directories) {
|
||||
typeDirectory = normalizePath(typeDirectory);
|
||||
result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findPackageJsons(currentDir: string, host: LanguageServiceHost): string[] {
|
||||
const paths: string[] = [];
|
||||
let currentConfigPath: string;
|
||||
while (true) {
|
||||
currentConfigPath = findConfigFile(currentDir, (f) => tryFileExists(host, f), "package.json");
|
||||
if (currentConfigPath) {
|
||||
paths.push(currentConfigPath);
|
||||
|
||||
currentDir = getDirectoryPath(currentConfigPath);
|
||||
const parent = getDirectoryPath(currentDir);
|
||||
if (currentDir === parent) {
|
||||
break;
|
||||
}
|
||||
currentDir = parent;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string) {
|
||||
const result: VisibleModuleInfo[] = [];
|
||||
|
||||
if (host.readFile && host.fileExists) {
|
||||
for (const packageJson of findPackageJsons(scriptPath, host)) {
|
||||
const contents = tryReadingPackageJson(packageJson);
|
||||
if (!contents) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules");
|
||||
const foundModuleNames: string[] = [];
|
||||
|
||||
// Provide completions for all non @types dependencies
|
||||
for (const key of nodeModulesDependencyKeys) {
|
||||
addPotentialPackageNames(contents[key], foundModuleNames);
|
||||
}
|
||||
|
||||
for (const moduleName of foundModuleNames) {
|
||||
const moduleDir = combinePaths(nodeModulesDir, moduleName);
|
||||
result.push({
|
||||
moduleName,
|
||||
moduleDir
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
function tryReadingPackageJson(filePath: string) {
|
||||
try {
|
||||
const fileText = tryReadFile(host, filePath);
|
||||
return fileText ? JSON.parse(fileText) : undefined;
|
||||
}
|
||||
catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function addPotentialPackageNames(dependencies: any, result: string[]) {
|
||||
if (dependencies) {
|
||||
for (const dep in dependencies) {
|
||||
if (dependencies.hasOwnProperty(dep) && !startsWith(dep, "@types/")) {
|
||||
result.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createCompletionEntryForModule(name: string, kind: string, replacementSpan: TextSpan): CompletionEntry {
|
||||
return { name, kind, kindModifiers: ScriptElementKindModifier.none, sortText: name, replacementSpan };
|
||||
}
|
||||
|
||||
// Replace everything after the last directory seperator that appears
|
||||
function getDirectoryFragmentTextSpan(text: string, textStart: number): TextSpan {
|
||||
const index = text.lastIndexOf(directorySeparator);
|
||||
const offset = index !== -1 ? index + 1 : 0;
|
||||
return { start: textStart + offset, length: text.length - offset };
|
||||
}
|
||||
|
||||
// Returns true if the path is explicitly relative to the script (i.e. relative to . or ..)
|
||||
function isPathRelativeToScript(path: string) {
|
||||
if (path && path.length >= 2 && path.charCodeAt(0) === CharacterCodes.dot) {
|
||||
const slashIndex = path.length >= 3 && path.charCodeAt(1) === CharacterCodes.dot ? 2 : 1;
|
||||
const slashCharCode = path.charCodeAt(slashIndex);
|
||||
return slashCharCode === CharacterCodes.slash || slashCharCode === CharacterCodes.backslash;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function normalizeAndPreserveTrailingSlash(path: string) {
|
||||
return hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(normalizePath(path)) : normalizePath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a triple slash reference directive with an incomplete string literal for its path. Used
|
||||
* to determine if the caret is currently within the string literal and capture the literal fragment
|
||||
* for completions.
|
||||
* For example, this matches
|
||||
*
|
||||
* /// <reference path="fragment
|
||||
*
|
||||
* but not
|
||||
*
|
||||
* /// <reference path="fragment"
|
||||
*/
|
||||
const tripleSlashDirectiveFragmentRegex = /^(\/\/\/\s*<reference\s+(path|types)\s*=\s*(?:'|"))([^\3"]*)$/;
|
||||
|
||||
interface VisibleModuleInfo {
|
||||
moduleName: string;
|
||||
moduleDir: string;
|
||||
}
|
||||
|
||||
const nodeModulesDependencyKeys = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
|
||||
|
||||
function tryGetDirectories(host: LanguageServiceHost, directoryName: string): string[] {
|
||||
return tryIOAndConsumeErrors(host, host.getDirectories, directoryName);
|
||||
}
|
||||
|
||||
function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
|
||||
return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include);
|
||||
}
|
||||
|
||||
function tryReadFile(host: LanguageServiceHost, path: string): string {
|
||||
return tryIOAndConsumeErrors(host, host.readFile, path);
|
||||
}
|
||||
|
||||
function tryFileExists(host: LanguageServiceHost, path: string): boolean {
|
||||
return tryIOAndConsumeErrors(host, host.fileExists, path);
|
||||
}
|
||||
|
||||
function tryDirectoryExists(host: LanguageServiceHost, path: string): boolean {
|
||||
try {
|
||||
return directoryProbablyExists(path, host);
|
||||
}
|
||||
catch (e) {}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function tryIOAndConsumeErrors<T>(host: LanguageServiceHost, toApply: (...a: any[]) => T, ...args: any[]) {
|
||||
try {
|
||||
return toApply && toApply.apply(host, args);
|
||||
}
|
||||
catch (e) {}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace ts.SymbolDisplay {
|
||||
// TODO(drosen): use contextual SemanticMeaning.
|
||||
export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): string {
|
||||
const flags = symbol.getFlags();
|
||||
const { flags } = symbol;
|
||||
|
||||
if (flags & SymbolFlags.Class) return getDeclarationOfKind(symbol, SyntaxKind.ClassExpression) ?
|
||||
ScriptElementKind.localClassElement : ScriptElementKind.classElement;
|
||||
@@ -11,10 +11,10 @@ namespace ts.SymbolDisplay {
|
||||
if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement;
|
||||
if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement;
|
||||
|
||||
const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, flags, location);
|
||||
const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location);
|
||||
if (result === ScriptElementKind.unknown) {
|
||||
if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement;
|
||||
if (flags & SymbolFlags.EnumMember) return ScriptElementKind.variableElement;
|
||||
if (flags & SymbolFlags.EnumMember) return ScriptElementKind.enumMemberElement;
|
||||
if (flags & SymbolFlags.Alias) return ScriptElementKind.alias;
|
||||
if (flags & SymbolFlags.Module) return ScriptElementKind.moduleElement;
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace ts.SymbolDisplay {
|
||||
return result;
|
||||
}
|
||||
|
||||
function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker: TypeChecker, symbol: Symbol, flags: SymbolFlags, location: Node) {
|
||||
function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker: TypeChecker, symbol: Symbol, location: Node) {
|
||||
if (typeChecker.isUndefinedSymbol(symbol)) {
|
||||
return ScriptElementKind.variableElement;
|
||||
}
|
||||
@@ -32,6 +32,7 @@ namespace ts.SymbolDisplay {
|
||||
if (location.kind === SyntaxKind.ThisKeyword && isExpression(location)) {
|
||||
return ScriptElementKind.parameterElement;
|
||||
}
|
||||
const { flags } = symbol;
|
||||
if (flags & SymbolFlags.Variable) {
|
||||
if (isFirstDeclarationOfSymbolParameter(symbol)) {
|
||||
return ScriptElementKind.parameterElement;
|
||||
@@ -93,7 +94,7 @@ namespace ts.SymbolDisplay {
|
||||
const displayParts: SymbolDisplayPart[] = [];
|
||||
let documentation: SymbolDisplayPart[];
|
||||
const symbolFlags = symbol.flags;
|
||||
let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, symbolFlags, location);
|
||||
let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location);
|
||||
let hasAddedSymbolInfo: boolean;
|
||||
const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isExpression(location);
|
||||
let type: Type;
|
||||
@@ -319,6 +320,7 @@ namespace ts.SymbolDisplay {
|
||||
}
|
||||
}
|
||||
if (symbolFlags & SymbolFlags.EnumMember) {
|
||||
symbolKind = ScriptElementKind.enumMemberElement;
|
||||
addPrefixForAnyFunctionOrVar(symbol, "enum member");
|
||||
const declaration = symbol.declarations[0];
|
||||
if (declaration.kind === SyntaxKind.EnumMember) {
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
"navigateTo.ts",
|
||||
"navigationBar.ts",
|
||||
"outliningElementsCollector.ts",
|
||||
"pathCompletions.ts",
|
||||
"patternMatcher.ts",
|
||||
"preProcess.ts",
|
||||
"rename.ts",
|
||||
|
||||
@@ -706,8 +706,7 @@ namespace ts {
|
||||
|
||||
/** enum E */
|
||||
export const enumElement = "enum";
|
||||
// TODO: GH#9983
|
||||
export const enumMemberElement = "const";
|
||||
export const enumMemberElement = "enum member";
|
||||
|
||||
/**
|
||||
* Inside module and script only
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
const fs = require("fs");
|
||||
>fs : typeof "fs"
|
||||
>require("fs") : any
|
||||
>require("fs") : typeof "fs"
|
||||
>require : (moduleName: string) => any
|
||||
>"fs" : "fs"
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
//// [circularInferredTypeOfVariable.ts]
|
||||
|
||||
// Repro from #14428
|
||||
|
||||
(async () => {
|
||||
function foo(p: string[]): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
function bar(p: string[]): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
let a1: string[] | undefined = [];
|
||||
|
||||
while (true) {
|
||||
let a2 = foo(a1!);
|
||||
a1 = await bar(a2);
|
||||
}
|
||||
});
|
||||
|
||||
//// [circularInferredTypeOfVariable.js]
|
||||
// Repro from #14428
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
(() => __awaiter(this, void 0, void 0, function* () {
|
||||
function foo(p) {
|
||||
return [];
|
||||
}
|
||||
function bar(p) {
|
||||
return [];
|
||||
}
|
||||
let a1 = [];
|
||||
while (true) {
|
||||
let a2 = foo(a1);
|
||||
a1 = yield bar(a2);
|
||||
}
|
||||
}));
|
||||
@@ -0,0 +1,34 @@
|
||||
=== tests/cases/compiler/circularInferredTypeOfVariable.ts ===
|
||||
|
||||
// Repro from #14428
|
||||
|
||||
(async () => {
|
||||
function foo(p: string[]): string[] {
|
||||
>foo : Symbol(foo, Decl(circularInferredTypeOfVariable.ts, 3, 14))
|
||||
>p : Symbol(p, Decl(circularInferredTypeOfVariable.ts, 4, 17))
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
function bar(p: string[]): string[] {
|
||||
>bar : Symbol(bar, Decl(circularInferredTypeOfVariable.ts, 6, 5))
|
||||
>p : Symbol(p, Decl(circularInferredTypeOfVariable.ts, 8, 17))
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
let a1: string[] | undefined = [];
|
||||
>a1 : Symbol(a1, Decl(circularInferredTypeOfVariable.ts, 12, 7))
|
||||
|
||||
while (true) {
|
||||
let a2 = foo(a1!);
|
||||
>a2 : Symbol(a2, Decl(circularInferredTypeOfVariable.ts, 15, 11))
|
||||
>foo : Symbol(foo, Decl(circularInferredTypeOfVariable.ts, 3, 14))
|
||||
>a1 : Symbol(a1, Decl(circularInferredTypeOfVariable.ts, 12, 7))
|
||||
|
||||
a1 = await bar(a2);
|
||||
>a1 : Symbol(a1, Decl(circularInferredTypeOfVariable.ts, 12, 7))
|
||||
>bar : Symbol(bar, Decl(circularInferredTypeOfVariable.ts, 6, 5))
|
||||
>a2 : Symbol(a2, Decl(circularInferredTypeOfVariable.ts, 15, 11))
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
=== tests/cases/compiler/circularInferredTypeOfVariable.ts ===
|
||||
|
||||
// Repro from #14428
|
||||
|
||||
(async () => {
|
||||
>(async () => { function foo(p: string[]): string[] { return []; } function bar(p: string[]): string[] { return []; } let a1: string[] | undefined = []; while (true) { let a2 = foo(a1!); a1 = await bar(a2); }}) : () => Promise<never>
|
||||
>async () => { function foo(p: string[]): string[] { return []; } function bar(p: string[]): string[] { return []; } let a1: string[] | undefined = []; while (true) { let a2 = foo(a1!); a1 = await bar(a2); }} : () => Promise<never>
|
||||
|
||||
function foo(p: string[]): string[] {
|
||||
>foo : (p: string[]) => string[]
|
||||
>p : string[]
|
||||
|
||||
return [];
|
||||
>[] : undefined[]
|
||||
}
|
||||
|
||||
function bar(p: string[]): string[] {
|
||||
>bar : (p: string[]) => string[]
|
||||
>p : string[]
|
||||
|
||||
return [];
|
||||
>[] : undefined[]
|
||||
}
|
||||
|
||||
let a1: string[] | undefined = [];
|
||||
>a1 : string[]
|
||||
>[] : undefined[]
|
||||
|
||||
while (true) {
|
||||
>true : true
|
||||
|
||||
let a2 = foo(a1!);
|
||||
>a2 : string[]
|
||||
>foo(a1!) : string[]
|
||||
>foo : (p: string[]) => string[]
|
||||
>a1! : string[]
|
||||
>a1 : string[]
|
||||
|
||||
a1 = await bar(a2);
|
||||
>a1 = await bar(a2) : string[]
|
||||
>a1 : string[]
|
||||
>await bar(a2) : string[]
|
||||
>bar(a2) : string[]
|
||||
>bar : (p: string[]) => string[]
|
||||
>a2 : string[]
|
||||
}
|
||||
});
|
||||
@@ -6,15 +6,9 @@ tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(34,17): error
|
||||
Type 'string' is not assignable to type 'number'.
|
||||
tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(45,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'.
|
||||
Type 'string' is not assignable to type 'number'.
|
||||
tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(76,13): error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
|
||||
tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(76,26): error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'.
|
||||
Type 'true' is not assignable to type 'string | number'.
|
||||
tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(87,13): error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
|
||||
tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(87,26): error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'.
|
||||
Type 'true' is not assignable to type 'string | number'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts (8 errors) ====
|
||||
==== tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts (4 errors) ====
|
||||
let cond: boolean;
|
||||
|
||||
function len(s: string) {
|
||||
@@ -103,11 +97,6 @@ tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(87,26): error
|
||||
x = "0";
|
||||
while (cond) {
|
||||
let y = asNumber(x);
|
||||
~
|
||||
!!! error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
|
||||
~
|
||||
!!! error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'.
|
||||
!!! error TS2345: Type 'true' is not assignable to type 'string | number'.
|
||||
x = y + 1;
|
||||
x;
|
||||
}
|
||||
@@ -119,11 +108,6 @@ tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(87,26): error
|
||||
while (cond) {
|
||||
x;
|
||||
let y = asNumber(x);
|
||||
~
|
||||
!!! error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
|
||||
~
|
||||
!!! error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'.
|
||||
!!! error TS2345: Type 'true' is not assignable to type 'string | number'.
|
||||
x = y + 1;
|
||||
x;
|
||||
}
|
||||
|
||||
@@ -7,11 +7,10 @@ tests/cases/compiler/implicitAnyFromCircularInference.ts(17,10): error TS7024: F
|
||||
tests/cases/compiler/implicitAnyFromCircularInference.ts(22,10): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
|
||||
tests/cases/compiler/implicitAnyFromCircularInference.ts(25,10): error TS7023: 'h' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
|
||||
tests/cases/compiler/implicitAnyFromCircularInference.ts(27,14): error TS7023: 'foo' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
|
||||
tests/cases/compiler/implicitAnyFromCircularInference.ts(40,5): error TS7022: 's' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
|
||||
tests/cases/compiler/implicitAnyFromCircularInference.ts(45,9): error TS7023: 'x' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
|
||||
|
||||
|
||||
==== tests/cases/compiler/implicitAnyFromCircularInference.ts (11 errors) ====
|
||||
==== tests/cases/compiler/implicitAnyFromCircularInference.ts (10 errors) ====
|
||||
// Error expected
|
||||
var a: typeof a;
|
||||
~
|
||||
@@ -70,8 +69,6 @@ tests/cases/compiler/implicitAnyFromCircularInference.ts(45,9): error TS7023: 'x
|
||||
class C {
|
||||
// Error expected
|
||||
s = foo(this);
|
||||
~~~~~~~~~~~~~~
|
||||
!!! error TS7022: 's' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
|
||||
}
|
||||
|
||||
class D {
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"position": 13
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 13,
|
||||
@@ -95,7 +95,7 @@
|
||||
"position": 21
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 21,
|
||||
@@ -156,7 +156,7 @@
|
||||
"position": 34
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 34,
|
||||
@@ -357,7 +357,7 @@
|
||||
"position": 71
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 71,
|
||||
@@ -488,7 +488,7 @@
|
||||
"position": 89
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 89,
|
||||
@@ -619,7 +619,7 @@
|
||||
"position": 107
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 107,
|
||||
@@ -717,7 +717,7 @@
|
||||
"position": 135
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 135,
|
||||
@@ -778,7 +778,7 @@
|
||||
"position": 143
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 143,
|
||||
@@ -839,7 +839,7 @@
|
||||
"position": 156
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 156,
|
||||
@@ -1056,7 +1056,7 @@
|
||||
"position": 205
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 205,
|
||||
@@ -1195,7 +1195,7 @@
|
||||
"position": 229
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 229,
|
||||
@@ -1334,7 +1334,7 @@
|
||||
"position": 253
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 253,
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"position": 13
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 13,
|
||||
@@ -99,7 +99,7 @@
|
||||
"position": 23
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 23,
|
||||
@@ -164,7 +164,7 @@
|
||||
"position": 38
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 38,
|
||||
@@ -369,7 +369,7 @@
|
||||
"position": 77
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 77,
|
||||
@@ -504,7 +504,7 @@
|
||||
"position": 95
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 95,
|
||||
@@ -639,7 +639,7 @@
|
||||
"position": 113
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 113,
|
||||
@@ -741,7 +741,7 @@
|
||||
"position": 141
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 141,
|
||||
@@ -806,7 +806,7 @@
|
||||
"position": 151
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 151,
|
||||
@@ -871,7 +871,7 @@
|
||||
"position": 166
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 166,
|
||||
@@ -1092,7 +1092,7 @@
|
||||
"position": 217
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 217,
|
||||
@@ -1235,7 +1235,7 @@
|
||||
"position": 241
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 241,
|
||||
@@ -1378,7 +1378,7 @@
|
||||
"position": 265
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 265,
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"position": 13
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 13,
|
||||
@@ -99,7 +99,7 @@
|
||||
"position": 23
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 23,
|
||||
@@ -164,7 +164,7 @@
|
||||
"position": 38
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 38,
|
||||
@@ -369,7 +369,7 @@
|
||||
"position": 77
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 77,
|
||||
@@ -504,7 +504,7 @@
|
||||
"position": 98
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 98,
|
||||
@@ -639,7 +639,7 @@
|
||||
"position": 119
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 119,
|
||||
@@ -741,7 +741,7 @@
|
||||
"position": 150
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 150,
|
||||
@@ -806,7 +806,7 @@
|
||||
"position": 160
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 160,
|
||||
@@ -871,7 +871,7 @@
|
||||
"position": 175
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 175,
|
||||
@@ -1092,7 +1092,7 @@
|
||||
"position": 226
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 226,
|
||||
@@ -1235,7 +1235,7 @@
|
||||
"position": 253
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 253,
|
||||
@@ -1378,7 +1378,7 @@
|
||||
"position": 280
|
||||
},
|
||||
"quickInfo": {
|
||||
"kind": "var",
|
||||
"kind": "enum member",
|
||||
"kindModifiers": "",
|
||||
"textSpan": {
|
||||
"start": 280,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// @target: es6
|
||||
|
||||
// Repro from #14428
|
||||
|
||||
(async () => {
|
||||
function foo(p: string[]): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
function bar(p: string[]): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
let a1: string[] | undefined = [];
|
||||
|
||||
while (true) {
|
||||
let a2 = foo(a1!);
|
||||
a1 = await bar(a2);
|
||||
}
|
||||
});
|
||||
@@ -16,15 +16,15 @@ verify.navigationTree({
|
||||
"childItems": [
|
||||
{
|
||||
"text": "a",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "b",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "c",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -48,15 +48,15 @@ verify.navigationBar([
|
||||
"childItems": [
|
||||
{
|
||||
"text": "a",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "b",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "c",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
}
|
||||
],
|
||||
"indent": 1
|
||||
|
||||
@@ -10,7 +10,7 @@ verify.numberOfErrorsInCurrentFile(1);
|
||||
// - Supplied parameters do not match any signature of call target.
|
||||
// - Could not select overload for 'call' expression.
|
||||
|
||||
verify.quickInfoAt("y", "var y: any");
|
||||
verify.quickInfoAt("y", "var y: number");
|
||||
|
||||
goTo.eof();
|
||||
edit.insert("interface Array<T> { pop(def: T): T; }");
|
||||
|
||||
@@ -36,7 +36,7 @@ verify.navigationTree({
|
||||
"childItems": [
|
||||
{
|
||||
"text": "LocalEnumMemberInConstructor",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -64,7 +64,7 @@ verify.navigationTree({
|
||||
"childItems": [
|
||||
{
|
||||
"text": "LocalEnumMemberInMethod",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -144,7 +144,7 @@ verify.navigationBar([
|
||||
"childItems": [
|
||||
{
|
||||
"text": "LocalEnumMemberInConstructor",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
}
|
||||
],
|
||||
"indent": 3
|
||||
@@ -184,7 +184,7 @@ verify.navigationBar([
|
||||
"childItems": [
|
||||
{
|
||||
"text": "LocalEnumMemberInMethod",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
}
|
||||
],
|
||||
"indent": 3
|
||||
|
||||
@@ -130,15 +130,15 @@ verify.navigationTree({
|
||||
"childItems": [
|
||||
{
|
||||
"text": "value1",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "value2",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "value3",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -263,15 +263,15 @@ verify.navigationBar([
|
||||
"childItems": [
|
||||
{
|
||||
"text": "value1",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "value2",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "value3",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
}
|
||||
],
|
||||
"indent": 2
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
/////*25*/eInstance1 = /*26*/constE./*27*/e2;
|
||||
/////*28*/eInstance1 = /*29*/constE./*30*/e3;
|
||||
|
||||
verify.baselineQuickInfo();
|
||||
verify.baselineQuickInfo();
|
||||
|
||||
@@ -129,15 +129,15 @@ verify.navigationTree({
|
||||
"childItems": [
|
||||
{
|
||||
"text": "value1",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "value2",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "value3",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -262,15 +262,15 @@ verify.navigationBar([
|
||||
"childItems": [
|
||||
{
|
||||
"text": "value1",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "value2",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
},
|
||||
{
|
||||
"text": "value3",
|
||||
"kind": "const"
|
||||
"kind": "enum member"
|
||||
}
|
||||
],
|
||||
"indent": 2
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
"double",
|
||||
"avoid-escape"
|
||||
],
|
||||
"semicolon": [true, "ignore-bound-class-methods"],
|
||||
"semicolon": [true, "always", "ignore-bound-class-methods"],
|
||||
"whitespace": [true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
|
||||
Reference in New Issue
Block a user