Skip to content

Commit 7df76b6

Browse files
committed
Merge branch 'master' into fix35414-2
2 parents e60d7ff + 16aae96 commit 7df76b6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+467
-211
lines changed

src/compiler/builder.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -882,36 +882,36 @@ namespace ts {
882882
oldProgram = undefined;
883883
oldState = undefined;
884884

885-
const result = createRedirectedBuilderProgram(state, configFileParsingDiagnostics);
886-
result.getState = () => state;
887-
result.backupState = () => {
885+
const builderProgram = createRedirectedBuilderProgram(state, configFileParsingDiagnostics);
886+
builderProgram.getState = () => state;
887+
builderProgram.backupState = () => {
888888
Debug.assert(backupState === undefined);
889889
backupState = cloneBuilderProgramState(state);
890890
};
891-
result.restoreState = () => {
891+
builderProgram.restoreState = () => {
892892
state = Debug.assertDefined(backupState);
893893
backupState = undefined;
894894
};
895-
result.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.assertDefined(state.program), sourceFile);
896-
result.getSemanticDiagnostics = getSemanticDiagnostics;
897-
result.emit = emit;
898-
result.releaseProgram = () => {
895+
builderProgram.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.assertDefined(state.program), sourceFile);
896+
builderProgram.getSemanticDiagnostics = getSemanticDiagnostics;
897+
builderProgram.emit = emit;
898+
builderProgram.releaseProgram = () => {
899899
releaseCache(state);
900900
backupState = undefined;
901901
};
902902

903903
if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
904-
(result as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
904+
(builderProgram as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
905905
}
906906
else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
907-
(result as EmitAndSemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
908-
(result as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile;
907+
(builderProgram as EmitAndSemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
908+
(builderProgram as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile;
909909
}
910910
else {
911911
notImplemented();
912912
}
913913

914-
return result;
914+
return builderProgram;
915915

916916
/**
917917
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
@@ -987,6 +987,8 @@ namespace ts {
987987
function emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult {
988988
if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
989989
assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
990+
const result = handleNoEmitOptions(builderProgram, targetSourceFile, cancellationToken);
991+
if (result) return result;
990992
if (!targetSourceFile) {
991993
// Emit and report any errors we ran into.
992994
let sourceMaps: SourceMapEmitResult[] = [];

src/compiler/checker.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2881,7 +2881,11 @@ namespace ts {
28812881
}
28822882

28832883
const type = getTypeOfSymbol(exportEquals);
2884-
return type.flags & TypeFlags.Primitive ? undefined : getPropertyOfType(type, memberName);
2884+
return type.flags & TypeFlags.Primitive ||
2885+
getObjectFlags(type) & ObjectFlags.Class ||
2886+
isArrayOrTupleLikeType(type)
2887+
? undefined
2888+
: getPropertyOfType(type, memberName);
28852889
}
28862890

28872891
function getExportsOfSymbol(symbol: Symbol): SymbolTable {

src/compiler/program.ts

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,37 +1570,9 @@ namespace ts {
15701570
}
15711571

15721572
function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
1573-
let declarationDiagnostics: readonly Diagnostic[] = [];
1574-
15751573
if (!forceDtsEmit) {
1576-
if (options.noEmit) {
1577-
return { diagnostics: declarationDiagnostics, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
1578-
}
1579-
1580-
// If the noEmitOnError flag is set, then check if we have any errors so far. If so,
1581-
// immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we
1582-
// get any preEmit diagnostics, not just the ones
1583-
if (options.noEmitOnError) {
1584-
const diagnostics = [
1585-
...program.getOptionsDiagnostics(cancellationToken),
1586-
...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
1587-
...program.getGlobalDiagnostics(cancellationToken),
1588-
...program.getSemanticDiagnostics(sourceFile, cancellationToken)
1589-
];
1590-
1591-
if (diagnostics.length === 0 && getEmitDeclarations(program.getCompilerOptions())) {
1592-
declarationDiagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken);
1593-
}
1594-
1595-
if (diagnostics.length > 0 || declarationDiagnostics.length > 0) {
1596-
return {
1597-
diagnostics: concatenate(diagnostics, declarationDiagnostics),
1598-
sourceMaps: undefined,
1599-
emittedFiles: undefined,
1600-
emitSkipped: true
1601-
};
1602-
}
1603-
}
1574+
const result = handleNoEmitOptions(program, sourceFile, cancellationToken);
1575+
if (result) return result;
16041576
}
16051577

16061578
// Create the emit resolver outside of the "emitTime" tracking code below. That way
@@ -3442,6 +3414,33 @@ namespace ts {
34423414
}
34433415
}
34443416

3417+
/*@internal*/
3418+
export function handleNoEmitOptions(program: ProgramToEmitFilesAndReportErrors, sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): EmitResult | undefined {
3419+
const options = program.getCompilerOptions();
3420+
if (options.noEmit) {
3421+
return { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
3422+
}
3423+
3424+
// If the noEmitOnError flag is set, then check if we have any errors so far. If so,
3425+
// immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we
3426+
// get any preEmit diagnostics, not just the ones
3427+
if (!options.noEmitOnError) return undefined;
3428+
let diagnostics: readonly Diagnostic[] = [
3429+
...program.getOptionsDiagnostics(cancellationToken),
3430+
...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
3431+
...program.getGlobalDiagnostics(cancellationToken),
3432+
...program.getSemanticDiagnostics(sourceFile, cancellationToken)
3433+
];
3434+
3435+
if (diagnostics.length === 0 && getEmitDeclarations(program.getCompilerOptions())) {
3436+
diagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken);
3437+
}
3438+
3439+
return diagnostics.length > 0 ?
3440+
{ diagnostics, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true } :
3441+
undefined;
3442+
}
3443+
34453444
/*@internal*/
34463445
interface CompilerHostLike {
34473446
useCaseSensitiveFileNames(): boolean;

src/compiler/watch.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ namespace ts {
124124
getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
125125
getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
126126
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
127+
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[];
127128
getConfigFileParsingDiagnostics(): readonly Diagnostic[];
128129
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
129130
}
@@ -152,20 +153,20 @@ namespace ts {
152153
const isListFilesOnly = !!program.getCompilerOptions().listFilesOnly;
153154

154155
// First get and report any syntactic errors.
155-
const diagnostics = program.getConfigFileParsingDiagnostics().slice();
156-
const configFileParsingDiagnosticsLength = diagnostics.length;
157-
addRange(diagnostics, program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
156+
const allDiagnostics = program.getConfigFileParsingDiagnostics().slice();
157+
const configFileParsingDiagnosticsLength = allDiagnostics.length;
158+
addRange(allDiagnostics, program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
158159

159160
// If we didn't have any syntactic errors, then also try getting the global and
160161
// semantic errors.
161-
if (diagnostics.length === configFileParsingDiagnosticsLength) {
162-
addRange(diagnostics, program.getOptionsDiagnostics(cancellationToken));
162+
if (allDiagnostics.length === configFileParsingDiagnosticsLength) {
163+
addRange(allDiagnostics, program.getOptionsDiagnostics(cancellationToken));
163164

164165
if (!isListFilesOnly) {
165-
addRange(diagnostics, program.getGlobalDiagnostics(cancellationToken));
166+
addRange(allDiagnostics, program.getGlobalDiagnostics(cancellationToken));
166167

167-
if (diagnostics.length === configFileParsingDiagnosticsLength) {
168-
addRange(diagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
168+
if (allDiagnostics.length === configFileParsingDiagnosticsLength) {
169+
addRange(allDiagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
169170
}
170171
}
171172
}
@@ -175,9 +176,10 @@ namespace ts {
175176
? { emitSkipped: true, diagnostics: emptyArray }
176177
: program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
177178
const { emittedFiles, diagnostics: emitDiagnostics } = emitResult;
178-
addRange(diagnostics, emitDiagnostics);
179+
addRange(allDiagnostics, emitDiagnostics);
179180

180-
sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic);
181+
const diagnostics = sortAndDeduplicateDiagnostics(allDiagnostics);
182+
diagnostics.forEach(reportDiagnostic);
181183
if (writeFileName) {
182184
const currentDir = program.getCurrentDirectory();
183185
forEach(emittedFiles, file => {

src/harness/fourslashImpl.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,7 +2553,7 @@ namespace FourSlash {
25532553
* Rerieves a codefix satisfying the parameters, or undefined if no such codefix is found.
25542554
* @param fileName Path to file where error should be retrieved from.
25552555
*/
2556-
private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.emptyOptions): readonly ts.CodeFixAction[] {
2556+
private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.emptyOptions, position?: number): readonly ts.CodeFixAction[] {
25572557
const diagnosticsForCodeFix = this.getDiagnostics(fileName, /*includeSuggestions*/ true).map(diagnostic => ({
25582558
start: diagnostic.start,
25592559
length: diagnostic.length,
@@ -2564,7 +2564,12 @@ namespace FourSlash {
25642564
if (errorCode !== undefined && errorCode !== diagnostic.code) {
25652565
return;
25662566
}
2567-
2567+
if (position !== undefined && diagnostic.start !== undefined && diagnostic.length !== undefined) {
2568+
const span = ts.createTextRangeFromSpan({ start: diagnostic.start, length: diagnostic.length });
2569+
if (!ts.textRangeContainsPositionInclusive(span, position)) {
2570+
return;
2571+
}
2572+
}
25682573
return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start!, diagnostic.start! + diagnostic.length!, [diagnostic.code], this.formatCodeSettings, preferences);
25692574
});
25702575
}
@@ -2614,6 +2619,23 @@ namespace FourSlash {
26142619
});
26152620
}
26162621

2622+
public verifyImportFixModuleSpecifiers(markerName: string, moduleSpecifiers: string[]) {
2623+
const marker = this.getMarkerByName(markerName);
2624+
const codeFixes = this.getCodeFixes(marker.fileName, ts.Diagnostics.Cannot_find_name_0.code, {
2625+
includeCompletionsForModuleExports: true,
2626+
includeCompletionsWithInsertText: true
2627+
}, marker.position).filter(f => f.fixId === ts.codefix.importFixId);
2628+
2629+
const actualModuleSpecifiers = ts.mapDefined(codeFixes, fix => {
2630+
return ts.forEach(ts.flatMap(fix.changes, c => c.textChanges), c => {
2631+
const match = /(?:from |require\()(['"])((?:(?!\1).)*)\1/.exec(c.newText);
2632+
return match?.[2];
2633+
});
2634+
});
2635+
2636+
assert.deepEqual(actualModuleSpecifiers, moduleSpecifiers);
2637+
}
2638+
26172639
public verifyDocCommentTemplate(expected: ts.TextInsertion | undefined) {
26182640
const name = "verifyDocCommentTemplate";
26192641
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition)!;

src/harness/fourslashInterfaceImpl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,10 @@ namespace FourSlashInterface {
434434
this.state.verifyImportFixAtPosition(expectedTextArray, errorCode, preferences);
435435
}
436436

437+
public importFixModuleSpecifiers(marker: string, moduleSpecifiers: string[]) {
438+
this.state.verifyImportFixModuleSpecifiers(marker, moduleSpecifiers);
439+
}
440+
437441
public navigationBar(json: any, options?: { checkSpans?: boolean }) {
438442
this.state.verifyNavigationBar(json, options);
439443
}

src/testRunner/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
"unittests/tsbuild/lateBoundSymbol.ts",
119119
"unittests/tsbuild/missingExtendedFile.ts",
120120
"unittests/tsbuild/moduleSpecifiers.ts",
121+
"unittests/tsbuild/noEmitOnError.ts",
121122
"unittests/tsbuild/outFile.ts",
122123
"unittests/tsbuild/referencesWithRootDirInParent.ts",
123124
"unittests/tsbuild/resolveJsonModule.ts",

src/testRunner/unittests/tsbuild/exitCodeOnBogusFile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace ts {
44
verifyTsc({
55
scenario: "exitCodeOnBogusFile",
66
subScenario: `test exit code`,
7-
fs: () => loadProjectFromFiles({}, symbolLibContent),
7+
fs: () => loadProjectFromFiles({}),
88
commandLineArgs: ["-b", "bogus.json"]
99
});
1010
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace ts {
2+
describe("unittests:: tsbuild - with noEmitOnError", () => {
3+
let projFs: vfs.FileSystem;
4+
before(() => {
5+
projFs = loadProjectFromDisk("tests/projects/noEmitOnError");
6+
});
7+
after(() => {
8+
projFs = undefined!;
9+
});
10+
verifyTsc({
11+
scenario: "noEmitOnError",
12+
subScenario: "has empty files diagnostic when files is empty and no references are provided",
13+
fs: () => projFs,
14+
commandLineArgs: ["--b", "/src/tsconfig.json"],
15+
});
16+
});
17+
}

src/testRunner/unittests/tsbuild/watchMode.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,4 +1389,45 @@ ${coreFiles[1].content}`);
13891389
);
13901390
}
13911391
});
1392+
1393+
describe("unittests:: tsbuild:: watchMode:: with noEmitOnError", () => {
1394+
it("does not emit any files on error", () => {
1395+
const projectLocation = `${projectsLocation}/noEmitOnError`;
1396+
const host = createTsBuildWatchSystem([
1397+
...["tsconfig.json", "shared/types/db.ts", "src/main.ts", "src/other.ts"]
1398+
.map(f => getFileFromProject("noEmitOnError", f)),
1399+
{ path: libFile.path, content: libContent }
1400+
], { currentDirectory: projectLocation });
1401+
createSolutionBuilderWithWatch(host, ["tsconfig.json"], { verbose: true, watch: true });
1402+
checkOutputErrorsInitial(host, [
1403+
`src/main.ts(4,1): error TS1005: ',' expected.\n`,
1404+
], /*disableConsoleClears*/ undefined, [
1405+
`Projects in this build: \r\n * tsconfig.json\n\n`,
1406+
`Project 'tsconfig.json' is out of date because output file 'dev-build/shared/types/db.js' does not exist\n\n`,
1407+
`Building project '/user/username/projects/noEmitOnError/tsconfig.json'...\n\n`,
1408+
]);
1409+
assert.equal(host.writtenFiles.size, 0, `Expected not to write any files: ${arrayFrom(host.writtenFiles.keys())}`);
1410+
1411+
// Make changes
1412+
host.writeFile(`${projectLocation}/src/main.ts`, `import { A } from "../shared/types/db";
1413+
const a = {
1414+
lastName: 'sdsd'
1415+
};`);
1416+
host.writtenFiles.clear();
1417+
host.checkTimeoutQueueLengthAndRun(1); // build project
1418+
host.checkTimeoutQueueLength(0);
1419+
checkOutputErrorsIncremental(host, emptyArray, /*disableConsoleClears*/ undefined, /*logsBeforeWatchDiagnostics*/ undefined, [
1420+
`Project 'tsconfig.json' is out of date because output file 'dev-build/shared/types/db.js' does not exist\n\n`,
1421+
`Building project '/user/username/projects/noEmitOnError/tsconfig.json'...\n\n`,
1422+
]);
1423+
assert.equal(host.writtenFiles.size, 3, `Expected to write 3 files: Actual:: ${arrayFrom(host.writtenFiles.keys())}`);
1424+
for (const f of [
1425+
`${projectLocation}/dev-build/shared/types/db.js`,
1426+
`${projectLocation}/dev-build/src/main.js`,
1427+
`${projectLocation}/dev-build/src/other.js`,
1428+
]) {
1429+
assert.isTrue(host.writtenFiles.has(f.toLowerCase()), `Expected to write file: ${f}:: Actual:: ${arrayFrom(host.writtenFiles.keys())}`);
1430+
}
1431+
});
1432+
});
13921433
}

src/testRunner/unittests/tsc/incremental.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,21 @@ namespace ts {
7272
commandLineArgs: ["--p", "src/project"],
7373
incrementalScenarios: [noChangeRun]
7474
});
75+
76+
verifyTscIncrementalEdits({
77+
scenario: "incremental",
78+
subScenario: "with noEmitOnError",
79+
fs: () => loadProjectFromDisk("tests/projects/noEmitOnError"),
80+
commandLineArgs: ["--incremental", "-p", "src"],
81+
incrementalScenarios: [
82+
{
83+
buildKind: BuildKind.IncrementalDtsUnchanged,
84+
modifyFs: fs => fs.writeFileSync("/src/src/main.ts", `import { A } from "../shared/types/db";
85+
const a = {
86+
lastName: 'sdsd'
87+
};`, "utf-8")
88+
}
89+
]
90+
});
7591
});
7692
}

0 commit comments

Comments
 (0)