Skip to content

Commit ae1bd8b

Browse files
committed
Merge branch 'master' into transitiveReferences
2 parents 88f79de + 6365eb0 commit ae1bd8b

22 files changed

+333
-75
lines changed

src/compiler/checker.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,17 +1238,25 @@ namespace ts {
12381238
// local types not visible outside the function body
12391239
: false;
12401240
}
1241-
if (meaning & result.flags & SymbolFlags.FunctionScopedVariable) {
1242-
// parameters are visible only inside function body, parameter list and return type
1243-
// technically for parameter list case here we might mix parameters and variables declared in function,
1244-
// however it is detected separately when checking initializers of parameters
1245-
// to make sure that they reference no variables declared after them.
1246-
useResult =
1247-
lastLocation.kind === SyntaxKind.Parameter ||
1248-
(
1249-
lastLocation === (<FunctionLikeDeclaration>location).type &&
1250-
!!findAncestor(result.valueDeclaration, isParameter)
1251-
);
1241+
if (meaning & result.flags & SymbolFlags.Variable) {
1242+
// expression inside parameter will lookup as normal variable scope when targeting es2015+
1243+
const functionLocation = <FunctionLikeDeclaration>location;
1244+
if (compilerOptions.target && compilerOptions.target >= ScriptTarget.ES2015 && isParameter(lastLocation) &&
1245+
functionLocation.body && result.valueDeclaration.pos >= functionLocation.body.pos && result.valueDeclaration.end <= functionLocation.body.end) {
1246+
useResult = false;
1247+
}
1248+
else if (result.flags & SymbolFlags.FunctionScopedVariable) {
1249+
// parameters are visible only inside function body, parameter list and return type
1250+
// technically for parameter list case here we might mix parameters and variables declared in function,
1251+
// however it is detected separately when checking initializers of parameters
1252+
// to make sure that they reference no variables declared after them.
1253+
useResult =
1254+
lastLocation.kind === SyntaxKind.Parameter ||
1255+
(
1256+
lastLocation === (<FunctionLikeDeclaration>location).type &&
1257+
!!findAncestor(result.valueDeclaration, isParameter)
1258+
);
1259+
}
12521260
}
12531261
}
12541262
else if (location.kind === SyntaxKind.ConditionalType) {
@@ -26103,8 +26111,11 @@ namespace ts {
2610326111
if (!checkTypeAssignableTo(typeWithThis, baseWithThis, /*errorNode*/ undefined)) {
2610426112
issueMemberSpecificError(node, typeWithThis, baseWithThis, Diagnostics.Class_0_incorrectly_extends_base_class_1);
2610526113
}
26106-
checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node,
26107-
Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1);
26114+
else {
26115+
// Report static side error only when instance type is assignable
26116+
checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node,
26117+
Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1);
26118+
}
2610826119
if (baseConstructorType.flags & TypeFlags.TypeVariable && !isMixinConstructorType(staticType)) {
2610926120
error(node.name || node, Diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any);
2611026121
}

src/compiler/emitter.ts

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,23 @@ namespace ts {
4141
export function getOutputPathsFor(sourceFile: SourceFile | Bundle, host: EmitHost, forceDtsPaths: boolean): EmitFileNames {
4242
const options = host.getCompilerOptions();
4343
if (sourceFile.kind === SyntaxKind.Bundle) {
44-
const jsFilePath = options.outFile || options.out!;
45-
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
46-
const declarationFilePath = (forceDtsPaths || getEmitDeclarations(options)) ? removeFileExtension(jsFilePath) + Extension.Dts : undefined;
47-
const declarationMapPath = getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
44+
const outPath = options.outFile || options.out!;
45+
const jsFilePath = options.emitDeclarationOnly ? undefined : outPath;
46+
const sourceMapFilePath = jsFilePath && getSourceMapFilePath(jsFilePath, options);
47+
const declarationFilePath = (forceDtsPaths || getEmitDeclarations(options)) ? removeFileExtension(outPath) + Extension.Dts : undefined;
48+
const declarationMapPath = declarationFilePath && getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
4849
const bundleInfoPath = options.references && jsFilePath ? (removeFileExtension(jsFilePath) + infoExtension) : undefined;
4950
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath };
5051
}
5152
else {
52-
const jsFilePath = getOwnEmitOutputFilePath(sourceFile.fileName, host, getOutputExtension(sourceFile, options));
53-
const sourceMapFilePath = isJsonSourceFile(sourceFile) ? undefined : getSourceMapFilePath(jsFilePath, options);
53+
const ownOutputFilePath = getOwnEmitOutputFilePath(sourceFile.fileName, host, getOutputExtension(sourceFile, options));
54+
// If json file emits to the same location skip writing it, if emitDeclarationOnly skip writing it
55+
const jsFilePath = options.emitDeclarationOnly ? undefined : ownOutputFilePath;
56+
const sourceMapFilePath = !jsFilePath || isJsonSourceFile(sourceFile) ? undefined : getSourceMapFilePath(jsFilePath, options);
5457
// For legacy reasons (ie, we have baselines capturing the behavior), js files don't report a .d.ts output path - this would only matter if `declaration` and `allowJs` were both on, which is currently an error
5558
const isJs = isSourceFileJS(sourceFile);
5659
const declarationFilePath = ((forceDtsPaths || getEmitDeclarations(options)) && !isJs) ? getDeclarationEmitOutputFilePath(sourceFile.fileName, host) : undefined;
57-
const declarationMapPath = getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
60+
const declarationMapPath = declarationFilePath && getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
5861
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, bundleInfoPath: undefined };
5962
}
6063
}
@@ -135,27 +138,33 @@ namespace ts {
135138

136139
if (!emitSkipped && emittedFilesList) {
137140
if (!emitOnlyDtsFiles) {
138-
emittedFilesList.push(jsFilePath);
139-
}
140-
if (sourceMapFilePath) {
141-
emittedFilesList.push(sourceMapFilePath);
141+
if (jsFilePath) {
142+
emittedFilesList.push(jsFilePath);
143+
}
144+
if (sourceMapFilePath) {
145+
emittedFilesList.push(sourceMapFilePath);
146+
}
147+
if (bundleInfoPath) {
148+
emittedFilesList.push(bundleInfoPath);
149+
}
142150
}
143151
if (declarationFilePath) {
144152
emittedFilesList.push(declarationFilePath);
145153
}
146-
if (bundleInfoPath) {
147-
emittedFilesList.push(bundleInfoPath);
154+
if (declarationMapPath) {
155+
emittedFilesList.push(declarationMapPath);
148156
}
149157
}
150158
}
151159

152-
function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string, sourceMapFilePath: string | undefined, bundleInfoPath: string | undefined) {
153-
// Make sure not to write js file and source map file if any of them cannot be written
154-
if (host.isEmitBlocked(jsFilePath) || compilerOptions.noEmit || compilerOptions.emitDeclarationOnly) {
155-
emitSkipped = true;
160+
function emitJsFileOrBundle(sourceFileOrBundle: SourceFile | Bundle, jsFilePath: string | undefined, sourceMapFilePath: string | undefined, bundleInfoPath: string | undefined) {
161+
if (emitOnlyDtsFiles || !jsFilePath) {
156162
return;
157163
}
158-
if (emitOnlyDtsFiles) {
164+
165+
// Make sure not to write js file and source map file if any of them cannot be written
166+
if ((jsFilePath && host.isEmitBlocked(jsFilePath)) || compilerOptions.noEmit) {
167+
emitSkipped = true;
159168
return;
160169
}
161170
// Transform the source files

src/compiler/transformers/declarations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ namespace ts {
275275
else {
276276
if (isBundledEmit && contains((node as Bundle).sourceFiles, file)) return; // Omit references to files which are being merged
277277
const paths = getOutputPathsFor(file, host, /*forceDtsPaths*/ true);
278-
declFileName = paths.declarationFilePath || paths.jsFilePath;
278+
declFileName = paths.declarationFilePath || paths.jsFilePath || file.fileName;
279279
}
280280

281281
if (declFileName) {

src/compiler/tsbuild.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,9 @@ namespace ts {
10661066
type: UpToDateStatusType.UpToDate,
10671067
newestDeclarationFileContentChangedTime: anyDtsChanged ? maximumDate : newestDeclarationFileContentChangedTime
10681068
};
1069+
if (options.watch) {
1070+
diagnostics.removeKey(proj);
1071+
}
10691072
projectStatus.setValue(proj, status);
10701073
return resultFlags;
10711074

src/compiler/utilities.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3245,7 +3245,7 @@ namespace ts {
32453245
}
32463246

32473247
export interface EmitFileNames {
3248-
jsFilePath: string;
3248+
jsFilePath: string | undefined;
32493249
sourceMapFilePath: string | undefined;
32503250
declarationFilePath: string | undefined;
32513251
declarationMapPath: string | undefined;

src/server/editorServices.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ namespace ts.server {
486486

487487
private readonly hostConfiguration: HostConfiguration;
488488
private safelist: SafeList = defaultTypeSafeList;
489-
private legacySafelist: { [key: string]: string } = {};
489+
private readonly legacySafelist = createMap<string>();
490490

491491
private pendingProjectUpdates = createMap<Project>();
492492
/* @internal */
@@ -638,14 +638,14 @@ namespace ts.server {
638638
this.safelist = raw.typesMap;
639639
for (const key in raw.simpleMap) {
640640
if (raw.simpleMap.hasOwnProperty(key)) {
641-
this.legacySafelist[key] = raw.simpleMap[key].toLowerCase();
641+
this.legacySafelist.set(key, raw.simpleMap[key].toLowerCase());
642642
}
643643
}
644644
}
645645
catch (e) {
646646
this.logger.info(`Error loading types map: ${e}`);
647647
this.safelist = defaultTypeSafeList;
648-
this.legacySafelist = {};
648+
this.legacySafelist.clear();
649649
}
650650
}
651651

@@ -2723,13 +2723,13 @@ namespace ts.server {
27232723
if (fileExtensionIs(baseName, "js")) {
27242724
const inferredTypingName = removeFileExtension(baseName);
27252725
const cleanedTypingName = removeMinAndVersionNumbers(inferredTypingName);
2726-
if (this.legacySafelist[cleanedTypingName]) {
2726+
const typeName = this.legacySafelist.get(cleanedTypingName);
2727+
if (typeName !== undefined) {
27272728
this.logger.info(`Excluded '${normalizedNames[i]}' because it matched ${cleanedTypingName} from the legacy safelist`);
27282729
excludedFiles.push(normalizedNames[i]);
27292730
// *exclude* it from the project...
27302731
exclude = true;
27312732
// ... but *include* it in the list of types to acquire
2732-
const typeName = this.legacySafelist[cleanedTypingName];
27332733
// Same best-effort dedupe as above
27342734
if (typeAcqInclude.indexOf(typeName) < 0) {
27352735
typeAcqInclude.push(typeName);

src/services/codefixes/importFixes.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,20 +185,20 @@ namespace ts.codefix {
185185

186186
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions);
187187
if (defaultInfo && defaultInfo.name === symbolName && skipAlias(defaultInfo.symbol, checker) === exportedSymbol) {
188-
result.push({ moduleSymbol, importKind: defaultInfo.kind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(defaultInfo.symbol) });
188+
result.push({ moduleSymbol, importKind: defaultInfo.kind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(defaultInfo.symbol, checker) });
189189
}
190190

191191
for (const exported of checker.getExportsOfModule(moduleSymbol)) {
192192
if (exported.name === symbolName && skipAlias(exported, checker) === exportedSymbol) {
193-
result.push({ moduleSymbol, importKind: ImportKind.Named, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exported) });
193+
result.push({ moduleSymbol, importKind: ImportKind.Named, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exported, checker) });
194194
}
195195
}
196196
});
197197
return result;
198198
}
199199

200-
function isTypeOnlySymbol(s: Symbol): boolean {
201-
return !(s.flags & SymbolFlags.Value);
200+
function isTypeOnlySymbol(s: Symbol, checker: TypeChecker): boolean {
201+
return !(skipAlias(s, checker).flags & SymbolFlags.Value);
202202
}
203203

204204
function getFixForImport(
@@ -398,7 +398,7 @@ namespace ts.codefix {
398398
// Maps symbol id to info for modules providing that symbol (original export + re-exports).
399399
const originalSymbolToExportInfos = createMultiMap<SymbolExportInfo>();
400400
function addSymbol(moduleSymbol: Symbol, exportedSymbol: Symbol, importKind: ImportKind): void {
401-
originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { moduleSymbol, importKind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exportedSymbol) });
401+
originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { moduleSymbol, importKind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exportedSymbol, checker) });
402402
}
403403
forEachExternalModuleToImportFrom(checker, sourceFile, program.getSourceFiles(), moduleSymbol => {
404404
cancellationToken.throwIfCancellationRequested();
@@ -424,7 +424,7 @@ namespace ts.codefix {
424424
if (!exported) return undefined;
425425
const { symbol, kind } = exported;
426426
const info = getDefaultExportInfoWorker(symbol, moduleSymbol, checker, compilerOptions);
427-
return info && { symbol, symbolForMeaning: info.symbolForMeaning, name: info.name, kind };
427+
return info && { symbol, kind, ...info };
428428
}
429429

430430
function getDefaultLikeExportWorker(moduleSymbol: Symbol, checker: TypeChecker): { readonly symbol: Symbol, readonly kind: ImportKind.Default | ImportKind.Equals } | undefined {

src/testRunner/unittests/tsbuild.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,10 @@ export class cNew {}`);
337337
assert.deepEqual(host.traces, [
338338
"TSFILE: /src/core/anotherModule.js",
339339
"TSFILE: /src/core/anotherModule.d.ts",
340+
"TSFILE: /src/core/anotherModule.d.ts.map",
340341
"TSFILE: /src/core/index.js",
341342
"TSFILE: /src/core/index.d.ts",
343+
"TSFILE: /src/core/index.d.ts.map",
342344
"TSFILE: /src/logic/index.js",
343345
"TSFILE: /src/logic/index.js.map",
344346
"TSFILE: /src/logic/index.d.ts",

src/testRunner/unittests/tsbuildWatchMode.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,58 @@ function myFunc() { return 100; }`);
368368
}
369369
});
370370

371+
it("when referenced project change introduces error in the down stream project and then fixes it", () => {
372+
const subProjectLibrary = `${projectsLocation}/${project}/Library`;
373+
const libraryTs: File = {
374+
path: `${subProjectLibrary}/library.ts`,
375+
content: `
376+
interface SomeObject
377+
{
378+
message: string;
379+
}
380+
381+
export function createSomeObject(): SomeObject
382+
{
383+
return {
384+
message: "new Object"
385+
};
386+
}`
387+
};
388+
const libraryTsconfig: File = {
389+
path: `${subProjectLibrary}/tsconfig.json`,
390+
content: JSON.stringify({ compilerOptions: { composite: true } })
391+
};
392+
const subProjectApp = `${projectsLocation}/${project}/App`;
393+
const appTs: File = {
394+
path: `${subProjectApp}/app.ts`,
395+
content: `import { createSomeObject } from "../Library/library";
396+
createSomeObject().message;`
397+
};
398+
const appTsconfig: File = {
399+
path: `${subProjectApp}/tsconfig.json`,
400+
content: JSON.stringify({ references: [{ path: "../Library" }] })
401+
};
402+
403+
const files = [libFile, libraryTs, libraryTsconfig, appTs, appTsconfig];
404+
const host = createWatchedSystem(files, { currentDirectory: `${projectsLocation}/${project}` });
405+
createSolutionBuilderWithWatch(host, ["App"]);
406+
checkOutputErrorsInitial(host, emptyArray);
407+
408+
// Change message in library to message2
409+
host.writeFile(libraryTs.path, libraryTs.content.replace(/message/g, "message2"));
410+
host.checkTimeoutQueueLengthAndRun(1); // Build library
411+
host.checkTimeoutQueueLengthAndRun(1); // Build App
412+
checkOutputErrorsIncremental(host, [
413+
"App/app.ts(2,20): error TS2551: Property 'message' does not exist on type 'SomeObject'. Did you mean 'message2'?\n"
414+
]);
415+
416+
// Revert library changes
417+
host.writeFile(libraryTs.path, libraryTs.content);
418+
host.checkTimeoutQueueLengthAndRun(1); // Build library
419+
host.checkTimeoutQueueLengthAndRun(1); // Build App
420+
checkOutputErrorsIncremental(host, emptyArray);
421+
});
422+
371423
describe("reports errors in all projects on incremental compile", () => {
372424
function verifyIncrementalErrors(defaultBuildOptions?: BuildOptions, disabledConsoleClear?: boolean) {
373425
const host = createSolutionInWatchMode(allFiles, defaultBuildOptions, disabledConsoleClear);

0 commit comments

Comments
 (0)