Skip to content

Commit 15b68a9

Browse files
committed
Skip typechecking of source of project reference redirect
1 parent b5737fc commit 15b68a9

File tree

9 files changed

+460
-126
lines changed

9 files changed

+460
-126
lines changed

src/compiler/builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ namespace ts {
407407
const options = program.getCompilerOptions();
408408
forEach(program.getSourceFiles(), f =>
409409
program.isSourceFileDefaultLibrary(f) &&
410-
!skipTypeChecking(f, options) &&
410+
!skipTypeChecking(f, options, program) &&
411411
removeSemanticDiagnosticsOf(state, f.path)
412412
);
413413
}

src/compiler/checker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ namespace ts {
338338
return node && getTypeArgumentConstraint(node);
339339
},
340340
getSuggestionDiagnostics: (file, ct) => {
341-
if (skipTypeChecking(file, compilerOptions)) {
341+
if (skipTypeChecking(file, compilerOptions, host)) {
342342
return emptyArray;
343343
}
344344

@@ -29657,7 +29657,7 @@ namespace ts {
2965729657
function checkSourceFileWorker(node: SourceFile) {
2965829658
const links = getNodeLinks(node);
2965929659
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
29660-
if (skipTypeChecking(node, compilerOptions)) {
29660+
if (skipTypeChecking(node, compilerOptions, host)) {
2966129661
return;
2966229662
}
2966329663

src/compiler/program.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1701,7 +1701,7 @@ namespace ts {
17011701

17021702
function getSemanticDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] | undefined {
17031703
return runWithCancellationToken(() => {
1704-
if (skipTypeChecking(sourceFile, options)) {
1704+
if (skipTypeChecking(sourceFile, options, program)) {
17051705
return emptyArray;
17061706
}
17071707

src/compiler/utilities.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8573,11 +8573,16 @@ namespace ts {
85738573
return { pos: typeParameters.pos - 1, end: typeParameters.end + 1 };
85748574
}
85758575

8576-
export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOptions) {
8576+
export interface HostWithIsSourceOfProjectReferenceRedirect {
8577+
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
8578+
}
8579+
export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOptions, host: HostWithIsSourceOfProjectReferenceRedirect) {
85778580
// If skipLibCheck is enabled, skip reporting errors if file is a declaration file.
85788581
// If skipDefaultLibCheck is enabled, skip reporting errors if file contains a
85798582
// '/// <reference no-default-lib="true"/>' directive.
8580-
return options.skipLibCheck && sourceFile.isDeclarationFile || options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib;
8583+
return (options.skipLibCheck && sourceFile.isDeclarationFile ||
8584+
options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib) ||
8585+
host.isSourceOfProjectReferenceRedirect(sourceFile.fileName);
85818586
}
85828587

85838588
export function isJsonEqual(a: unknown, b: unknown): boolean {

src/server/scriptInfo.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,15 +495,17 @@ namespace ts.server {
495495
// the default project; if no configured projects, the first external project should
496496
// be the default project; otherwise the first inferred project should be the default.
497497
let firstExternalProject;
498+
let firstConfiguredProject;
498499
for (const project of this.containingProjects) {
499500
if (project.projectKind === ProjectKind.Configured) {
500-
return project;
501+
if (!project.isSourceOfProjectReferenceRedirect(this.fileName)) return project;
502+
if (!firstConfiguredProject) firstConfiguredProject = project;
501503
}
502504
else if (project.projectKind === ProjectKind.External && !firstExternalProject) {
503505
firstExternalProject = project;
504506
}
505507
}
506-
return firstExternalProject || this.containingProjects[0];
508+
return firstConfiguredProject || firstExternalProject || this.containingProjects[0];
507509
}
508510
}
509511

src/server/session.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,10 +1658,10 @@ namespace ts.server {
16581658
}
16591659
}
16601660

1661-
private createCheckList(fileNames: string[], defaultProject?: Project): PendingErrorCheck[] {
1661+
private createCheckList(fileNames: string[]): PendingErrorCheck[] {
16621662
return mapDefined<string, PendingErrorCheck>(fileNames, uncheckedFileName => {
16631663
const fileName = toNormalizedPath(uncheckedFileName);
1664-
const project = defaultProject || this.projectService.tryGetDefaultProjectForFile(fileName);
1664+
const project = this.projectService.tryGetDefaultProjectForFile(fileName);
16651665
return project && { fileName, project };
16661666
});
16671667
}

src/testRunner/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
"unittests/tsserver/occurences.ts",
138138
"unittests/tsserver/openFile.ts",
139139
"unittests/tsserver/projectErrors.ts",
140+
"unittests/tsserver/projectReferenceErrors.ts",
140141
"unittests/tsserver/projectReferences.ts",
141142
"unittests/tsserver/projects.ts",
142143
"unittests/tsserver/refactors.ts",
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
namespace ts.projectSystem {
2+
describe("unittests:: tsserver:: with project references and error reporting", () => {
3+
const projectLocation = "/user/username/projects/myproject";
4+
const dependecyLocation = `${projectLocation}/dependency`;
5+
const usageLocation = `${projectLocation}/usage`;
6+
const dependencyTs: File = {
7+
path: `${dependecyLocation}/fns.ts`,
8+
content: `export function fn1() { }
9+
export function fn2() { }
10+
// Introduce error for fnErr import in main
11+
// export function fnErr() { }
12+
// Error in dependency ts file
13+
export let x: string = 10;`
14+
};
15+
const dependencyConfig: File = {
16+
path: `${dependecyLocation}/tsconfig.json`,
17+
content: JSON.stringify({ compilerOptions: { composite: true, declarationDir: "../decls" } })
18+
};
19+
const usageTs: File = {
20+
path: `${usageLocation}/usage.ts`,
21+
content: `import {
22+
fn1,
23+
fn2,
24+
fnErr
25+
} from '../decls/fns'
26+
fn1();
27+
fn2();
28+
fnErr();
29+
`
30+
};
31+
const usageConfig: File = {
32+
path: `${usageLocation}/tsconfig.json`,
33+
content: JSON.stringify({
34+
references: [{ path: "../dependency" }]
35+
})
36+
};
37+
38+
interface CheckErrorsInFile {
39+
session: TestSession;
40+
host: TestServerHost;
41+
expected: GetErrDiagnostics;
42+
expectedSequenceId?: number;
43+
}
44+
function checkErrorsInFile({ session, host, expected: { file, syntax, semantic, suggestion }, expectedSequenceId }: CheckErrorsInFile) {
45+
host.checkTimeoutQueueLengthAndRun(1);
46+
checkErrorMessage(session, "syntaxDiag", { file: file.path, diagnostics: syntax });
47+
session.clearMessages();
48+
49+
host.runQueuedImmediateCallbacks(1);
50+
checkErrorMessage(session, "semanticDiag", { file: file.path, diagnostics: semantic });
51+
session.clearMessages();
52+
53+
host.runQueuedImmediateCallbacks(1);
54+
checkErrorMessage(session, "suggestionDiag", { file: file.path, diagnostics: suggestion });
55+
if (expectedSequenceId !== undefined) {
56+
checkCompleteEvent(session, 2, expectedSequenceId);
57+
}
58+
session.clearMessages();
59+
}
60+
61+
interface CheckAllErrors {
62+
session: TestSession;
63+
host: TestServerHost;
64+
expected: ReadonlyArray<GetErrDiagnostics>;
65+
expectedSequenceId: number;
66+
}
67+
function checkAllErrors({ session, host, expected, expectedSequenceId }: CheckAllErrors) {
68+
for (let i = 0; i < expected.length; i++) {
69+
checkErrorsInFile({
70+
session,
71+
host,
72+
expected: expected[i],
73+
expectedSequenceId: i === expected.length - 1 ? expectedSequenceId : undefined
74+
});
75+
}
76+
}
77+
78+
function verifyErrorsUsingGeterr({ openFiles, expectedGetErr }: VerifyScenario) {
79+
it("verifies the errors in open file", () => {
80+
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
81+
const session = createSession(host, { canUseEvents: true, });
82+
openFilesForSession(openFiles(), session);
83+
84+
session.clearMessages();
85+
const expectedSequenceId = session.getNextSeq();
86+
const expected = expectedGetErr();
87+
session.executeCommandSeq<protocol.GeterrRequest>({
88+
command: protocol.CommandTypes.Geterr,
89+
arguments: {
90+
delay: 0,
91+
files: expected.map(f => f.file.path)
92+
}
93+
});
94+
95+
checkAllErrors({ session, host, expected, expectedSequenceId });
96+
});
97+
}
98+
99+
function verifyErrorsUsingGeterrForProject({ openFiles, expectedGetErrForProject }: VerifyScenario) {
100+
it("verifies the errors in projects", () => {
101+
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
102+
const session = createSession(host, { canUseEvents: true, });
103+
openFilesForSession(openFiles(), session);
104+
105+
session.clearMessages();
106+
for (const expected of expectedGetErrForProject()) {
107+
const expectedSequenceId = session.getNextSeq();
108+
session.executeCommandSeq<protocol.GeterrForProjectRequest>({
109+
command: protocol.CommandTypes.GeterrForProject,
110+
arguments: {
111+
delay: 0,
112+
file: expected.project
113+
}
114+
});
115+
116+
checkAllErrors({ session, host, expected: expected.errors, expectedSequenceId });
117+
}
118+
});
119+
}
120+
121+
interface GetErrDiagnostics {
122+
file: File;
123+
syntax: protocol.Diagnostic[];
124+
semantic: protocol.Diagnostic[];
125+
suggestion: protocol.Diagnostic[];
126+
}
127+
interface GetErrForProjectDiagnostics {
128+
project: string;
129+
errors: ReadonlyArray<GetErrDiagnostics>;
130+
}
131+
interface VerifyScenario {
132+
openFiles: () => ReadonlyArray<File>;
133+
expectedGetErr: () => ReadonlyArray<GetErrDiagnostics>;
134+
expectedGetErrForProject: () => ReadonlyArray<GetErrForProjectDiagnostics>;
135+
}
136+
function verifyScenario(scenario: VerifyScenario) {
137+
verifyErrorsUsingGeterr(scenario);
138+
verifyErrorsUsingGeterrForProject(scenario);
139+
}
140+
141+
function emptyDiagnostics(file: File): GetErrDiagnostics {
142+
return {
143+
file,
144+
syntax: emptyArray,
145+
semantic: emptyArray,
146+
suggestion: emptyArray
147+
};
148+
}
149+
150+
function usageDiagnostics(): GetErrDiagnostics {
151+
return {
152+
file: usageTs,
153+
syntax: emptyArray,
154+
semantic: [
155+
createDiagnostic(
156+
{ line: 4, offset: 5 },
157+
{ line: 4, offset: 10 },
158+
Diagnostics.Module_0_has_no_exported_member_1,
159+
[`"../dependency/fns"`, "fnErr"],
160+
"error",
161+
)
162+
],
163+
suggestion: emptyArray
164+
};
165+
}
166+
167+
function dependencyDiagnostics(): GetErrDiagnostics {
168+
return {
169+
file: dependencyTs,
170+
syntax: emptyArray,
171+
semantic: [
172+
createDiagnostic(
173+
{ line: 6, offset: 12 },
174+
{ line: 6, offset: 13 },
175+
Diagnostics.Type_0_is_not_assignable_to_type_1,
176+
["10", "string"],
177+
"error",
178+
)
179+
],
180+
suggestion: emptyArray
181+
};
182+
}
183+
184+
function usageProjectDiagnostics(): GetErrForProjectDiagnostics {
185+
return {
186+
project: usageTs.path,
187+
errors: [
188+
usageDiagnostics(),
189+
emptyDiagnostics(dependencyTs)
190+
]
191+
};
192+
}
193+
194+
function dependencyProjectDiagnostics(): GetErrForProjectDiagnostics {
195+
return {
196+
project: dependencyTs.path,
197+
errors: [
198+
dependencyDiagnostics()
199+
]
200+
};
201+
}
202+
203+
describe("when dependency project is not open", () => {
204+
verifyScenario({
205+
openFiles: () => [usageTs],
206+
expectedGetErr: () => [
207+
usageDiagnostics()
208+
],
209+
expectedGetErrForProject: () => [
210+
usageProjectDiagnostics(),
211+
{
212+
project: dependencyTs.path,
213+
errors: [
214+
emptyDiagnostics(dependencyTs),
215+
usageDiagnostics()
216+
]
217+
}
218+
]
219+
});
220+
});
221+
222+
describe("when the depedency file is open", () => {
223+
verifyScenario({
224+
openFiles: () => [usageTs, dependencyTs],
225+
expectedGetErr: () => [
226+
usageDiagnostics(),
227+
dependencyDiagnostics(),
228+
],
229+
expectedGetErrForProject: () => [
230+
usageProjectDiagnostics(),
231+
dependencyProjectDiagnostics()
232+
]
233+
});
234+
});
235+
});
236+
}

0 commit comments

Comments
 (0)