Merge branch 'master' into weak-type-errors-on-signature-only-types

This commit is contained in:
Nathan Shively-Sanders 2017-08-08 10:45:52 -07:00
commit 781da2332d
33 changed files with 626 additions and 168 deletions

View File

@ -34,6 +34,7 @@ function walk(ctx: Lint.WalkContext<void>): void {
switch (methodName) {
case "apply":
case "assert":
case "assertEqual":
case "call":
case "equal":
case "fail":
@ -69,7 +70,7 @@ function walk(ctx: Lint.WalkContext<void>): void {
const ranges = ts.getTrailingCommentRanges(sourceFile.text, arg.pos) || ts.getLeadingCommentRanges(sourceFile.text, arg.pos);
if (ranges === undefined || ranges.length !== 1 || ranges[0].kind !== ts.SyntaxKind.MultiLineCommentTrivia) {
ctx.addFailureAtNode(arg, "Tag boolean argument with parameter name");
ctx.addFailureAtNode(arg, "Tag argument with parameter name");
return;
}

View File

@ -0,0 +1,45 @@
import * as Lint from "tslint/lib";
import * as ts from "typescript";
export class Rule extends Lint.Rules.AbstractRule {
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, ctx => walk(ctx));
}
}
function walk(ctx: Lint.WalkContext<void>): void {
ts.forEachChild(ctx.sourceFile, function recur(node) {
if (ts.isCallExpression(node)) {
checkCall(node);
}
ts.forEachChild(node, recur);
});
function checkCall(node: ts.CallExpression) {
if (!isDebugAssert(node.expression) || node.arguments.length < 2) {
return;
}
const message = node.arguments[1];
if (!ts.isStringLiteral(message)) {
ctx.addFailureAtNode(message, "Second argument to 'Debug.assert' should be a string literal.");
}
if (node.arguments.length < 3) {
return;
}
const message2 = node.arguments[2];
if (!ts.isStringLiteral(message2) && !ts.isArrowFunction(message2)) {
ctx.addFailureAtNode(message, "Third argument to 'Debug.assert' should be a string literal or arrow function.");
}
}
function isDebugAssert(expr: ts.Node): boolean {
return ts.isPropertyAccessExpression(expr) && isName(expr.expression, "Debug") && isName(expr.name, "assert");
}
function isName(expr: ts.Node, text: string): boolean {
return ts.isIdentifier(expr) && expr.text === text;
}
}

View File

@ -8074,7 +8074,7 @@ namespace ts {
function cloneTypeMapper(mapper: TypeMapper): TypeMapper {
return mapper && isInferenceContext(mapper) ?
createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.inferences) :
createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.compareTypes, mapper.inferences) :
mapper;
}
@ -8515,7 +8515,7 @@ namespace ts {
ignoreReturnTypes: boolean,
reportErrors: boolean,
errorReporter: ErrorReporter,
compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary {
compareTypes: TypeComparer): Ternary {
// TODO (drosen): De-duplicate code between related functions.
if (source === target) {
return Ternary.True;
@ -8525,7 +8525,7 @@ namespace ts {
}
if (source.typeParameters) {
source = instantiateSignatureInContextOf(source, target);
source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes);
}
let result = Ternary.True;
@ -9673,6 +9673,11 @@ namespace ts {
if (sourceInfo) {
return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors);
}
if (isGenericMappedType(source)) {
// A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U }
// if T is related to U.
return kind === IndexKind.String && isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), targetInfo.type, reportErrors);
}
if (isObjectLiteralType(source)) {
let related = Ternary.True;
if (kind === IndexKind.String) {
@ -10275,13 +10280,14 @@ namespace ts {
}
}
function createInferenceContext(signature: Signature, flags: InferenceFlags, baseInferences?: InferenceInfo[]): InferenceContext {
function createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext {
const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo);
const context = mapper as InferenceContext;
context.mappedTypes = signature.typeParameters;
context.signature = signature;
context.inferences = inferences;
context.flags = flags;
context.compareTypes = compareTypes || compareTypesAssignable;
return context;
function mapper(t: Type): Type {
@ -10729,7 +10735,7 @@ namespace ts {
const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]);
if (constraint) {
const instantiatedConstraint = instantiateType(constraint, context);
if (!isTypeAssignableTo(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
inference.inferredType = inferredType = instantiatedConstraint;
}
}
@ -15130,8 +15136,8 @@ namespace ts {
}
// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper): Signature {
const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes);
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper, compareTypes?: TypeComparer): Signature {
const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes, compareTypes);
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
inferTypes(context.inferences, instantiateType(source, contextualMapper || identityMapper), target);

View File

@ -1446,14 +1446,10 @@ namespace ts {
}
}
else {
// If no includes were specified, exclude common package folders and the outDir
const specs = includeSpecs ? [] : ["node_modules", "bower_components", "jspm_packages"];
const outDir = raw["compilerOptions"] && raw["compilerOptions"]["outDir"];
if (outDir) {
specs.push(outDir);
excludeSpecs = [outDir];
}
excludeSpecs = specs;
}
if (fileNames === undefined && includeSpecs === undefined) {

View File

@ -11,20 +11,6 @@ namespace ts {
/* @internal */
namespace ts {
/**
* Ternary values are defined such that
* x & y is False if either x or y is False.
* x & y is Maybe if either x or y is Maybe, but neither x or y is False.
* x & y is True if both x and y are True.
* x | y is False if both x and y are False.
* x | y is Maybe if either x or y is Maybe, but neither x or y is True.
* x | y is True if either x or y is True.
*/
export const enum Ternary {
False = 0,
Maybe = 1,
True = -1
}
// More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times.
export const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "accent" }) : undefined;
@ -1304,12 +1290,12 @@ namespace ts {
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): Diagnostic {
const end = start + length;
Debug.assert(start >= 0, "start must be non-negative, is " + start);
Debug.assert(length >= 0, "length must be non-negative, is " + length);
Debug.assertGreaterThanOrEqual(start, 0);
Debug.assertGreaterThanOrEqual(length, 0);
if (file) {
Debug.assert(start <= file.text.length, `start must be within the bounds of the file. ${start} > ${file.text.length}`);
Debug.assert(end <= file.text.length, `end must be the bounds of the file. ${end} > ${file.text.length}`);
Debug.assertLessThanOrEqual(start, file.text.length);
Debug.assertLessThanOrEqual(end, file.text.length);
}
let text = getLocaleSpecificMessage(message);
@ -1889,14 +1875,54 @@ namespace ts {
const reservedCharacterPattern = /[^\w\s\/]/g;
const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question];
/**
* Matches any single directory segment unless it is the last segment and a .min.js file
* Breakdown:
* [^./] # matches everything up to the first . character (excluding directory seperators)
* (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
*/
const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*";
const singleAsteriskRegexFragmentOther = "[^/]*";
/* @internal */
export const commonPackageFolders: ReadonlyArray<string> = ["node_modules", "bower_components", "jspm_packages"];
const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`;
interface WildcardMatcher {
singleAsteriskRegexFragment: string;
doubleAsteriskRegexFragment: string;
replaceWildcardCharacter: (match: string) => string;
}
const filesMatcher: WildcardMatcher = {
/**
* Matches any single directory segment unless it is the last segment and a .min.js file
* Breakdown:
* [^./] # matches everything up to the first . character (excluding directory seperators)
* (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
*/
singleAsteriskRegexFragment: "([^./]|(\\.(?!min\\.js$))?)*",
/**
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
* files or directories, does not match subdirectories that start with a . character
*/
doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`,
replaceWildcardCharacter: match => replaceWildcardCharacter(match, filesMatcher.singleAsteriskRegexFragment)
};
const directoriesMatcher: WildcardMatcher = {
singleAsteriskRegexFragment: "[^/]*",
/**
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
* files or directories, does not match subdirectories that start with a . character
*/
doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`,
replaceWildcardCharacter: match => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment)
};
const excludeMatcher: WildcardMatcher = {
singleAsteriskRegexFragment: "[^/]*",
doubleAsteriskRegexFragment: "(/.+?)?",
replaceWildcardCharacter: match => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment)
};
const wildcardMatchers = {
files: filesMatcher,
directories: directoriesMatcher,
exclude: excludeMatcher
};
export function getRegularExpressionForWildcard(specs: ReadonlyArray<string>, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined {
const patterns = getRegularExpressionsForWildcards(specs, basePath, usage);
@ -1915,17 +1941,8 @@ namespace ts {
return undefined;
}
const replaceWildcardCharacter = usage === "files" ? replaceWildCardCharacterFiles : replaceWildCardCharacterOther;
const singleAsteriskRegexFragment = usage === "files" ? singleAsteriskRegexFragmentFiles : singleAsteriskRegexFragmentOther;
/**
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
* files or directories, does not match subdirectories that start with a . character
*/
const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?";
return flatMap(specs, spec =>
spec && getSubPatternFromSpec(spec, basePath, usage, singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter));
spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]));
}
/**
@ -1936,7 +1953,7 @@ namespace ts {
return !/[.*?]/.test(lastPathComponent);
}
function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", singleAsteriskRegexFragment: string, doubleAsteriskRegexFragment: string, replaceWildcardCharacter: (match: string) => string): string | undefined {
function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined {
let subpattern = "";
let hasRecursiveDirectoryWildcard = false;
let hasWrittenComponent = false;
@ -1975,20 +1992,36 @@ namespace ts {
}
if (usage !== "exclude") {
let componentPattern = "";
// The * and ? wildcards should not match directories or files that start with . if they
// appear first in a component. Dotted directories and files can be included explicitly
// like so: **/.*/.*
if (component.charCodeAt(0) === CharacterCodes.asterisk) {
subpattern += "([^./]" + singleAsteriskRegexFragment + ")?";
componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?";
component = component.substr(1);
}
else if (component.charCodeAt(0) === CharacterCodes.question) {
subpattern += "[^./]";
componentPattern += "[^./]";
component = component.substr(1);
}
}
subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
componentPattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
// Patterns should not include subfolders like node_modules unless they are
// explicitly included as part of the path.
//
// As an optimization, if the component pattern is the same as the component,
// then there definitely were no wildcard characters and we do not need to
// add the exclusion pattern.
if (componentPattern !== component) {
subpattern += implicitExcludePathRegexPattern;
}
subpattern += componentPattern;
}
else {
subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
}
}
hasWrittenComponent = true;
@ -2002,14 +2035,6 @@ namespace ts {
return subpattern;
}
function replaceWildCardCharacterFiles(match: string) {
return replaceWildcardCharacter(match, singleAsteriskRegexFragmentFiles);
}
function replaceWildCardCharacterOther(match: string) {
return replaceWildcardCharacter(match, singleAsteriskRegexFragmentOther);
}
function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: string) {
return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match;
}
@ -2364,15 +2389,40 @@ namespace ts {
return currentAssertionLevel >= level;
}
export function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string, stackCrawlMark?: Function): void {
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: Function): void {
if (!expression) {
if (verboseDebugInfo) {
message += "\r\nVerbose Debug Information: " + verboseDebugInfo();
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
}
fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert);
}
}
export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string): void {
if (a !== b) {
const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
fail(`Expected ${a} === ${b}. ${message}`);
}
}
export function assertLessThan(a: number, b: number, msg?: string): void {
if (a >= b) {
fail(`Expected ${a} < ${b}. ${msg || ""}`);
}
}
export function assertLessThanOrEqual(a: number, b: number): void {
if (a > b) {
fail(`Expected ${a} <= ${b}`);
}
}
export function assertGreaterThanOrEqual(a: number, b: number): void {
if (a < b) {
fail(`Expected ${a} >= ${b}`);
}
}
export function fail(message?: string, stackCrawlMark?: Function): void {
debugger;
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");

View File

@ -678,7 +678,7 @@ namespace ts {
const { resolvedModule, failedLookupLocations } =
nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ts.ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true);
if (!resolvedModule) {
throw new Error(`Could not resolve JS module ${moduleName} starting at ${initialDir}. Looked in: ${failedLookupLocations.join(", ")}`);
throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations.join(", ")}`);
}
return resolvedModule.resolvedFileName;
}

View File

@ -2448,7 +2448,7 @@ namespace ts {
* @param location An optional source map location for the statement.
*/
function createInlineBreak(label: Label, location?: TextRange): ReturnStatement {
Debug.assert(label > 0, `Invalid label: ${label}`);
Debug.assertLessThan(0, label, "Invalid label");
return setTextRange(
createReturn(
createArrayLiteral([

View File

@ -3425,11 +3425,29 @@ namespace ts {
AnyDefault = 1 << 2, // Infer anyType for no inferences (otherwise emptyObjectType)
}
/**
* Ternary values are defined such that
* x & y is False if either x or y is False.
* x & y is Maybe if either x or y is Maybe, but neither x or y is False.
* x & y is True if both x and y are True.
* x | y is False if both x and y are False.
* x | y is Maybe if either x or y is Maybe, but neither x or y is True.
* x | y is True if either x or y is True.
*/
export const enum Ternary {
False = 0,
Maybe = 1,
True = -1
}
export type TypeComparer = (s: Type, t: Type, reportErrors?: boolean) => Ternary;
/* @internal */
export interface InferenceContext extends TypeMapper {
signature: Signature; // Generic signature for which inferences are made
inferences: InferenceInfo[]; // Inferences made for each type parameter
flags: InferenceFlags; // Inference flags
compareTypes: TypeComparer; // Type comparer function
}
/* @internal */

View File

@ -420,7 +420,7 @@ namespace Utils {
const maxHarnessFrames = 1;
export function filterStack(error: Error, stackTraceLimit: number = Infinity) {
export function filterStack(error: Error, stackTraceLimit = Infinity) {
const stack = <string>(<any>error).stack;
if (stack) {
const lines = stack.split(/\r\n?|\n/g);

View File

@ -795,7 +795,7 @@ namespace Harness.LanguageService {
default:
return {
module: undefined,
error: "Could not resolve module"
error: new Error("Could not resolve module")
};
}

View File

@ -73,6 +73,7 @@ namespace ts {
"c:/dev/a.d.ts",
"c:/dev/a.js",
"c:/dev/b.ts",
"c:/dev/x/a.ts",
"c:/dev/node_modules/a.ts",
"c:/dev/bower_components/a.ts",
"c:/dev/jspm_packages/a.ts"
@ -141,7 +142,8 @@ namespace ts {
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/b.ts"
"c:/dev/b.ts",
"c:/dev/x/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
@ -462,7 +464,6 @@ namespace ts {
};
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
});
it("same named declarations are excluded", () => {
const json = {
include: [
@ -651,71 +652,127 @@ namespace ts {
};
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
});
it("with common package folders and no exclusions", () => {
const json = {
include: [
"**/a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/bower_components/a.ts",
"c:/dev/jspm_packages/a.ts",
"c:/dev/node_modules/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("with common package folders and exclusions", () => {
const json = {
include: [
"**/a.ts"
],
exclude: [
"a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/bower_components/a.ts",
"c:/dev/jspm_packages/a.ts",
"c:/dev/node_modules/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("with common package folders and empty exclude", () => {
const json = {
include: [
"**/a.ts"
],
exclude: <string[]>[]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/bower_components/a.ts",
"c:/dev/jspm_packages/a.ts",
"c:/dev/node_modules/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
describe("with common package folders", () => {
it("and no exclusions", () => {
const json = {
include: [
"**/a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/x/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("and exclusions", () => {
const json = {
include: [
"**/?.ts"
],
exclude: [
"a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/b.ts",
"c:/dev/x/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("and empty exclude", () => {
const json = {
include: [
"**/a.ts"
],
exclude: <string[]>[]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/x/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("and explicit recursive include", () => {
const json = {
include: [
"**/a.ts",
"**/node_modules/a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/x/a.ts",
"c:/dev/node_modules/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("and wildcard include", () => {
const json = {
include: [
"*/a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/x/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("and explicit wildcard include", () => {
const json = {
include: [
"*/a.ts",
"node_modules/a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/x/a.ts",
"c:/dev/node_modules/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
});
it("exclude .js files when allowJs=false", () => {
const json = {
@ -1066,6 +1123,7 @@ namespace ts {
};
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
});
describe("with trailing recursive directory", () => {
it("in includes", () => {
const json = {
@ -1264,6 +1322,7 @@ namespace ts {
});
});
});
describe("with files or folders that begin with a .", () => {
it("that are not explicitly included", () => {
const json = {

View File

@ -110,7 +110,7 @@ interface Map<K, V> {
readonly [Symbol.toStringTag]: "Map";
}
interface WeakMap<K extends object, V>{
interface WeakMap<K extends object, V> {
readonly [Symbol.toStringTag]: "WeakMap";
}

View File

@ -170,7 +170,8 @@ namespace ts.server {
log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`);
const result = host.require(resolvedPath, moduleName);
if (result.error) {
log(`Failed to load module: ${JSON.stringify(result.error)}`);
const err = result.error.stack || result.error.message || JSON.stringify(result.error);
log(`Failed to load module '${moduleName}': ${err}`);
return undefined;
}
return result.module;
@ -362,7 +363,7 @@ namespace ts.server {
return map(this.program.getSourceFiles(), sourceFile => {
const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.path);
if (!scriptInfo) {
Debug.assert(false, `scriptInfo for a file '${sourceFile.fileName}' is missing.`);
Debug.fail(`scriptInfo for a file '${sourceFile.fileName}' is missing.`);
}
return scriptInfo;
});

View File

@ -116,8 +116,6 @@ namespace ts.server {
birthtime: Date;
}
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} };
const readline: {
createInterface(options: ReadLineOptions): NodeJS.EventEmitter;
} = require("readline");
@ -762,8 +760,16 @@ namespace ts.server {
const typingSafeListLocation = findArgument(Arguments.TypingSafeListLocation);
const npmLocation = findArgument(Arguments.NpmLocation);
const globalPlugins = (findArgument("--globalPlugins") || "").split(",");
const pluginProbeLocations = (findArgument("--pluginProbeLocations") || "").split(",");
function parseStringArray(argName: string): string[] {
const arg = findArgument(argName);
if (arg === undefined) {
return emptyArray as string[]; // TODO: https://github.com/Microsoft/TypeScript/issues/16312
}
return arg.split(",").filter(name => name !== "");
}
const globalPlugins = parseStringArray("--globalPlugins");
const pluginProbeLocations = parseStringArray("--pluginProbeLocations");
const allowLocalPluginLoads = hasArgument("--allowLocalPluginLoads");
const useSingleInferredProject = hasArgument("--useSingleInferredProject");

View File

@ -337,7 +337,7 @@ namespace ts.server {
case ContextEvent:
const { project, fileName } = event.data;
this.projectService.logger.info(`got context event, updating diagnostics for ${fileName}`);
this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], this.changeSeq, (n) => n === this.changeSeq, 100));
this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], 100));
break;
case ConfigFileDiagEvent:
const { triggerFile, configFileName, diagnostics } = event.data;
@ -453,22 +453,23 @@ namespace ts.server {
}
}
private updateProjectStructure(seq: number, matchSeq: (seq: number) => boolean, ms = 1500) {
private updateProjectStructure() {
const ms = 1500;
const seq = this.changeSeq;
this.host.setTimeout(() => {
if (matchSeq(seq)) {
if (this.changeSeq === seq) {
this.projectService.refreshInferredProjects();
}
}, ms);
}
private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], seq: number, matchSeq: (seq: number) => boolean, ms = 1500, followMs = 200, requireOpen = true) {
if (followMs > ms) {
followMs = ms;
}
private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], ms: number, requireOpen = true) {
const seq = this.changeSeq;
const followMs = Math.min(ms, 200);
let index = 0;
const checkOne = () => {
if (matchSeq(seq)) {
if (this.changeSeq === seq) {
const checkSpec = checkList[index];
index++;
if (checkSpec.project.containsFile(checkSpec.fileName, requireOpen)) {
@ -483,7 +484,7 @@ namespace ts.server {
}
};
if ((checkList.length > index) && (matchSeq(seq))) {
if (checkList.length > index && this.changeSeq === seq) {
next.delay(ms, checkOne);
}
}
@ -1262,14 +1263,14 @@ namespace ts.server {
}
private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void {
const checkList = mapDefined(fileNames, uncheckedFileName => {
const checkList = mapDefined<string, PendingErrorCheck>(fileNames, uncheckedFileName => {
const fileName = toNormalizedPath(uncheckedFileName);
const project = this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true);
return project && { fileName, project };
});
if (checkList.length > 0) {
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay);
this.updateErrorCheck(next, checkList, delay);
}
}
@ -1283,7 +1284,7 @@ namespace ts.server {
scriptInfo.editContent(start, end, args.insertString);
this.changeSeq++;
}
this.updateProjectStructure(this.changeSeq, n => n === this.changeSeq);
this.updateProjectStructure();
}
}
@ -1638,7 +1639,7 @@ namespace ts.server {
const checkList = fileNamesInProject.map(fileName => ({ fileName, project }));
// Project level error analysis runs on background files too, therefore
// doesn't require the file to be opened
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay, 200, /*requireOpen*/ false);
this.updateErrorCheck(next, checkList, delay, /*requireOpen*/ false);
}
}

View File

@ -9,7 +9,7 @@ declare namespace ts.server {
data: any;
}
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} };
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: { stack?: string, message?: string } };
export interface ServerHost extends System {
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
clearTimeout(timeoutId: any): void;

View File

@ -260,11 +260,11 @@ namespace ts {
templateStack.pop();
}
else {
Debug.assert(token === SyntaxKind.TemplateMiddle, "Should have been a template middle. Was " + token);
Debug.assertEqual(token, SyntaxKind.TemplateMiddle, "Should have been a template middle.");
}
}
else {
Debug.assert(lastTemplateStackToken === SyntaxKind.OpenBraceToken, "Should have been an open brace. Was: " + token);
Debug.assertEqual(lastTemplateStackToken, SyntaxKind.OpenBraceToken, "Should have been an open brace");
templateStack.pop();
}
}

View File

@ -1000,7 +1000,7 @@ namespace ts.Completions {
const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
if (!typeForObject) return false;
// In a binding pattern, get only known properties. Everywhere else we will get all possible properties.
typeMembers = typeChecker.getPropertiesOfType(typeForObject);
typeMembers = typeChecker.getPropertiesOfType(typeForObject).filter((symbol) => !(getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.NonPublicAccessibilityModifier));
existingMembers = (<ObjectBindingPattern>objectLikeContainer).elements;
}
}

View File

@ -1258,7 +1258,7 @@ namespace ts {
// We do not support the scenario where a host can modify a registered
// file's script kind, i.e. in one project some file is treated as ".ts"
// and in another as ".js"
Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind + ") for file: " + path);
Debug.assertEqual(hostFileInformation.scriptKind, oldSourceFile.scriptKind, "Registered script kind should match new script kind.", path);
return documentRegistry.updateDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind);
}

View File

@ -136,7 +136,9 @@ namespace ts.SignatureHelp {
const kind = invocation.typeArguments && invocation.typeArguments.pos === list.pos ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments;
const argumentCount = getArgumentCount(list);
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
if (argumentIndex !== 0) {
Debug.assertLessThan(argumentIndex, argumentCount);
}
const argumentsSpan = getApplicableSpanForArguments(list, sourceFile);
return { kind, invocation, argumentsSpan, argumentIndex, argumentCount };
}
@ -270,7 +272,9 @@ namespace ts.SignatureHelp {
? 1
: (<TemplateExpression>tagExpression.template).templateSpans.length + 1;
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
if (argumentIndex !== 0) {
Debug.assertLessThan(argumentIndex, argumentCount);
}
return {
kind: ArgumentListKind.TaggedTemplateArguments,
invocation: tagExpression,
@ -402,7 +406,9 @@ namespace ts.SignatureHelp {
};
});
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
if (argumentIndex !== 0) {
Debug.assertLessThan(argumentIndex, argumentCount);
}
const selectedItemIndex = candidates.indexOf(resolvedSignature);
Debug.assert(selectedItemIndex !== -1); // If candidates is non-empty it should always include bestSignature. We check for an empty candidates before calling this function.

View File

@ -78,11 +78,11 @@ namespace ts {
getSourceFile: (fileName) => fileName === normalizePath(inputFileName) ? sourceFile : undefined,
writeFile: (name, text) => {
if (fileExtensionIs(name, ".map")) {
Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`);
Debug.assertEqual(sourceMapText, undefined, "Unexpected multiple source map outputs, file:", name);
sourceMapText = text;
}
else {
Debug.assert(outputText === undefined, `Unexpected multiple outputs for the file: '${name}'`);
Debug.assertEqual(outputText, undefined, "Unexpected multiple outputs, file:", name);
outputText = text;
}
},

View File

@ -0,0 +1,47 @@
tests/cases/compiler/indexSignatureAndMappedType.ts(6,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, T>'.
tests/cases/compiler/indexSignatureAndMappedType.ts(15,5): error TS2322: Type 'Record<K, U>' is not assignable to type '{ [key: string]: T; }'.
Type 'U' is not assignable to type 'T'.
tests/cases/compiler/indexSignatureAndMappedType.ts(16,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, U>'.
==== tests/cases/compiler/indexSignatureAndMappedType.ts (3 errors) ====
// A mapped type { [P in K]: X }, where K is a generic type, is related to
// { [key: string]: Y } if X is related to Y.
function f1<T, K extends string>(x: { [key: string]: T }, y: Record<K, T>) {
x = y;
y = x; // Error
~
!!! error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, T>'.
}
function f2<T>(x: { [key: string]: T }, y: Record<string, T>) {
x = y;
y = x;
}
function f3<T, U, K extends string>(x: { [key: string]: T }, y: Record<K, U>) {
x = y; // Error
~
!!! error TS2322: Type 'Record<K, U>' is not assignable to type '{ [key: string]: T; }'.
!!! error TS2322: Type 'U' is not assignable to type 'T'.
y = x; // Error
~
!!! error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, U>'.
}
// Repro from #14548
type Dictionary = {
[key: string]: string;
};
interface IBaseEntity {
name: string;
properties: Dictionary;
}
interface IEntity<T extends string> extends IBaseEntity {
properties: Record<T, string>;
}

View File

@ -0,0 +1,73 @@
//// [indexSignatureAndMappedType.ts]
// A mapped type { [P in K]: X }, where K is a generic type, is related to
// { [key: string]: Y } if X is related to Y.
function f1<T, K extends string>(x: { [key: string]: T }, y: Record<K, T>) {
x = y;
y = x; // Error
}
function f2<T>(x: { [key: string]: T }, y: Record<string, T>) {
x = y;
y = x;
}
function f3<T, U, K extends string>(x: { [key: string]: T }, y: Record<K, U>) {
x = y; // Error
y = x; // Error
}
// Repro from #14548
type Dictionary = {
[key: string]: string;
};
interface IBaseEntity {
name: string;
properties: Dictionary;
}
interface IEntity<T extends string> extends IBaseEntity {
properties: Record<T, string>;
}
//// [indexSignatureAndMappedType.js]
"use strict";
// A mapped type { [P in K]: X }, where K is a generic type, is related to
// { [key: string]: Y } if X is related to Y.
function f1(x, y) {
x = y;
y = x; // Error
}
function f2(x, y) {
x = y;
y = x;
}
function f3(x, y) {
x = y; // Error
y = x; // Error
}
//// [indexSignatureAndMappedType.d.ts]
declare function f1<T, K extends string>(x: {
[key: string]: T;
}, y: Record<K, T>): void;
declare function f2<T>(x: {
[key: string]: T;
}, y: Record<string, T>): void;
declare function f3<T, U, K extends string>(x: {
[key: string]: T;
}, y: Record<K, U>): void;
declare type Dictionary = {
[key: string]: string;
};
interface IBaseEntity {
name: string;
properties: Dictionary;
}
interface IEntity<T extends string> extends IBaseEntity {
properties: Record<T, string>;
}

View File

@ -9,7 +9,7 @@ maxDepthExceeded/root.ts(4,4): error TS2540: Cannot assign to 'rel' because it i
"maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file
"outDir": "built"
},
"include": ["**/*"],
"include": ["**/*", "node_modules/**/*"],
"exclude": ["node_modules/m2/**/*"]
}

View File

@ -9,7 +9,7 @@ maxDepthExceeded/root.ts(4,4): error TS2540: Cannot assign to 'rel' because it i
"maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file
"outDir": "built"
},
"include": ["**/*"],
"include": ["**/*", "node_modules/**/*"],
"exclude": ["node_modules/m2/**/*"]
}

View File

@ -0,0 +1,30 @@
//// [signatureInstantiationWithRecursiveConstraints.ts]
// Repro from #17148
class Foo {
myFunc<T extends Foo>(arg: T) {}
}
class Bar {
myFunc<T extends Bar>(arg: T) {}
}
const myVar: Foo = new Bar();
//// [signatureInstantiationWithRecursiveConstraints.js]
"use strict";
// Repro from #17148
var Foo = (function () {
function Foo() {
}
Foo.prototype.myFunc = function (arg) { };
return Foo;
}());
var Bar = (function () {
function Bar() {
}
Bar.prototype.myFunc = function (arg) { };
return Bar;
}());
var myVar = new Bar();

View File

@ -0,0 +1,30 @@
=== tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts ===
// Repro from #17148
class Foo {
>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0))
myFunc<T extends Foo>(arg: T) {}
>myFunc : Symbol(Foo.myFunc, Decl(signatureInstantiationWithRecursiveConstraints.ts, 2, 11))
>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 9))
>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0))
>arg : Symbol(arg, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 24))
>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 9))
}
class Bar {
>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1))
myFunc<T extends Bar>(arg: T) {}
>myFunc : Symbol(Bar.myFunc, Decl(signatureInstantiationWithRecursiveConstraints.ts, 6, 11))
>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 9))
>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1))
>arg : Symbol(arg, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 24))
>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 9))
}
const myVar: Foo = new Bar();
>myVar : Symbol(myVar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 10, 5))
>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0))
>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1))

View File

@ -0,0 +1,31 @@
=== tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts ===
// Repro from #17148
class Foo {
>Foo : Foo
myFunc<T extends Foo>(arg: T) {}
>myFunc : <T extends Foo>(arg: T) => void
>T : T
>Foo : Foo
>arg : T
>T : T
}
class Bar {
>Bar : Bar
myFunc<T extends Bar>(arg: T) {}
>myFunc : <T extends Bar>(arg: T) => void
>T : T
>Bar : Bar
>arg : T
>T : T
}
const myVar: Foo = new Bar();
>myVar : Foo
>Foo : Foo
>new Bar() : Bar
>Bar : typeof Bar

View File

@ -0,0 +1,35 @@
// @strict: true
// @declaration: true
// A mapped type { [P in K]: X }, where K is a generic type, is related to
// { [key: string]: Y } if X is related to Y.
function f1<T, K extends string>(x: { [key: string]: T }, y: Record<K, T>) {
x = y;
y = x; // Error
}
function f2<T>(x: { [key: string]: T }, y: Record<string, T>) {
x = y;
y = x;
}
function f3<T, U, K extends string>(x: { [key: string]: T }, y: Record<K, U>) {
x = y; // Error
y = x; // Error
}
// Repro from #14548
type Dictionary = {
[key: string]: string;
};
interface IBaseEntity {
name: string;
properties: Dictionary;
}
interface IEntity<T extends string> extends IBaseEntity {
properties: Record<T, string>;
}

View File

@ -0,0 +1,13 @@
// @strict: true
// Repro from #17148
class Foo {
myFunc<T extends Foo>(arg: T) {}
}
class Bar {
myFunc<T extends Bar>(arg: T) {}
}
const myVar: Foo = new Bar();

View File

@ -0,0 +1,9 @@
/// <reference path="fourslash.ts"/>
////const { b/**/ } = new class {
//// private ab;
//// protected bc;
////}
goTo.marker();
verify.completionListIsEmpty();

View File

@ -4,6 +4,6 @@
"maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file
"outDir": "built"
},
"include": ["**/*"],
"include": ["**/*", "node_modules/**/*"],
"exclude": ["node_modules/m2/**/*"]
}

View File

@ -7,6 +7,7 @@
"check-space"
],
"curly":[true, "ignore-same-line"],
"debug-assert": true,
"indent": [true,
"spaces"
],