Skip to content

Commit 9196b7d

Browse files
authored
Merge pull request microsoft#25851 from Microsoft/configFileDiag
Report compiler options errors as well as part of configFileDiag event
2 parents 0923771 + a9d497a commit 9196b7d

File tree

3 files changed

+104
-40
lines changed

3 files changed

+104
-40
lines changed

src/compiler/program.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2711,12 +2711,12 @@ namespace ts {
27112711

27122712
function createDiagnosticForReference(index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
27132713
const referencesSyntax = getProjectReferencesSyntax();
2714-
if (referencesSyntax) {
2715-
if (createOptionDiagnosticInArrayLiteralSyntax(referencesSyntax, index, message, arg0, arg1)) {
2716-
return;
2717-
}
2714+
if (referencesSyntax && referencesSyntax.elements.length > index) {
2715+
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, referencesSyntax.elements[index], message, arg0, arg1));
2716+
}
2717+
else {
2718+
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
27182719
}
2719-
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
27202720
}
27212721

27222722
function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number) {
@@ -2769,15 +2769,6 @@ namespace ts {
27692769
return !!props.length;
27702770
}
27712771

2772-
function createOptionDiagnosticInArrayLiteralSyntax(arrayLiteral: ArrayLiteralExpression, index: number, message: DiagnosticMessage, arg0: string | number | undefined, arg1?: string | number, arg2?: string | number): boolean {
2773-
if (arrayLiteral.elements.length <= index) {
2774-
// Out-of-bounds
2775-
return false;
2776-
}
2777-
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, arrayLiteral.elements[index], message, arg0, arg1, arg2));
2778-
return false; // TODO: GH#18217 This function always returns `false`!`
2779-
}
2780-
27812772
function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) {
27822773
hasEmitBlockingDiagnostics.set(toPath(emitFileName), true);
27832774
programDiagnostics.add(diag);

src/server/editorServices.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1683,10 +1683,12 @@ namespace ts.server {
16831683
if (!this.eventHandler || this.suppressDiagnosticEvents) {
16841684
return;
16851685
}
1686+
const diagnostics = project.getLanguageService().getCompilerOptionsDiagnostics();
1687+
diagnostics.push(...project.getAllProjectErrors());
16861688

16871689
this.eventHandler(<ConfigFileDiagEvent>{
16881690
eventName: ConfigFileDiagEvent,
1689-
data: { configFileName: project.getConfigFilePath(), diagnostics: project.getAllProjectErrors(), triggerFile }
1691+
data: { configFileName: project.getConfigFilePath(), diagnostics, triggerFile }
16901692
});
16911693
}
16921694

src/testRunner/unittests/tsserverProjectSystem.ts

Lines changed: 96 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,18 @@ namespace ts.projectSystem {
176176
return { ts: 0, tsx: 0, dts: 0, js: 0, jsx: 0, deferred: 0, ...nonZeroStats };
177177
}
178178

179+
export interface ConfigFileDiagnostic {
180+
fileName: string | undefined;
181+
start: number | undefined;
182+
length: number | undefined;
183+
messageText: string;
184+
category: DiagnosticCategory;
185+
code: number;
186+
reportsUnnecessary?: {};
187+
source?: string;
188+
relatedInformation?: DiagnosticRelatedInformation[];
189+
}
190+
179191
export class TestServerEventManager {
180192
private events: server.ProjectServiceEvent[] = [];
181193
readonly session: TestSession;
@@ -216,10 +228,14 @@ namespace ts.projectSystem {
216228
this.events.forEach(event => assert.notEqual(event.eventName, eventName));
217229
}
218230

219-
checkSingleConfigFileDiagEvent(configFileName: string, triggerFile: string) {
231+
checkSingleConfigFileDiagEvent(configFileName: string, triggerFile: string, errors: ReadonlyArray<ConfigFileDiagnostic>) {
220232
const eventData = this.getEvent<server.ConfigFileDiagEvent>(server.ConfigFileDiagEvent);
221233
assert.equal(eventData.configFileName, configFileName);
222234
assert.equal(eventData.triggerFile, triggerFile);
235+
const actual = eventData.diagnostics.map(({ file, messageText, ...rest }) => ({ fileName: file && file.fileName, messageText: isString(messageText) ? messageText : "", ...rest }));
236+
if (errors) {
237+
assert.deepEqual(actual, errors);
238+
}
223239
}
224240

225241
assertProjectInfoTelemetryEvent(partial: Partial<server.ProjectInfoTelemetryEventData>, configFile = "/tsconfig.json"): void {
@@ -4698,13 +4714,41 @@ namespace ts.projectSystem {
46984714
});
46994715

47004716
describe("tsserverProjectSystem Configure file diagnostics events", () => {
4717+
function getUnknownCompilerOptionDiagnostic(configFile: File, prop: string): ConfigFileDiagnostic {
4718+
const d = Diagnostics.Unknown_compiler_option_0;
4719+
const start = configFile.content.indexOf(prop) - 1; // start at "prop"
4720+
return {
4721+
fileName: configFile.path,
4722+
start,
4723+
length: prop.length + 2,
4724+
messageText: formatStringFromArgs(d.message, [prop]),
4725+
category: d.category,
4726+
code: d.code,
4727+
reportsUnnecessary: undefined
4728+
};
4729+
}
4730+
4731+
function getFileNotFoundDiagnostic(configFile: File, relativeFileName: string): ConfigFileDiagnostic {
4732+
const findString = `{"path":"./${relativeFileName}"}`;
4733+
const d = Diagnostics.File_0_does_not_exist;
4734+
const start = configFile.content.indexOf(findString);
4735+
return {
4736+
fileName: configFile.path,
4737+
start,
4738+
length: findString.length,
4739+
messageText: formatStringFromArgs(d.message, [`${getDirectoryPath(configFile.path)}/${relativeFileName}`]),
4740+
category: d.category,
4741+
code: d.code,
4742+
reportsUnnecessary: undefined
4743+
};
4744+
}
47014745

47024746
it("are generated when the config file has errors", () => {
4703-
const file = {
4747+
const file: File = {
47044748
path: "/a/b/app.ts",
47054749
content: "let x = 10"
47064750
};
4707-
const configFile = {
4751+
const configFile: File = {
47084752
path: "/a/b/tsconfig.json",
47094753
content: `{
47104754
"compilerOptions": {
@@ -4713,29 +4757,32 @@ namespace ts.projectSystem {
47134757
}
47144758
}`
47154759
};
4716-
const serverEventManager = new TestServerEventManager([file, configFile]);
4760+
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
47174761
openFilesForSession([file], serverEventManager.session);
4718-
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
4762+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, [
4763+
getUnknownCompilerOptionDiagnostic(configFile, "foo"),
4764+
getUnknownCompilerOptionDiagnostic(configFile, "allowJS")
4765+
]);
47194766
});
47204767

47214768
it("are generated when the config file doesn't have errors", () => {
4722-
const file = {
4769+
const file: File = {
47234770
path: "/a/b/app.ts",
47244771
content: "let x = 10"
47254772
};
4726-
const configFile = {
4773+
const configFile: File = {
47274774
path: "/a/b/tsconfig.json",
47284775
content: `{
47294776
"compilerOptions": {}
47304777
}`
47314778
};
4732-
const serverEventManager = new TestServerEventManager([file, configFile]);
4779+
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
47334780
openFilesForSession([file], serverEventManager.session);
4734-
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
4781+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, emptyArray);
47354782
});
47364783

47374784
it("are generated when the config file changes", () => {
4738-
const file = {
4785+
const file: File = {
47394786
path: "/a/b/app.ts",
47404787
content: "let x = 10"
47414788
};
@@ -4746,37 +4793,40 @@ namespace ts.projectSystem {
47464793
}`
47474794
};
47484795

4749-
const serverEventManager = new TestServerEventManager([file, configFile]);
4796+
const files = [file, libFile, configFile];
4797+
const serverEventManager = new TestServerEventManager(files);
47504798
openFilesForSession([file], serverEventManager.session);
4751-
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
4799+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, emptyArray);
47524800

47534801
configFile.content = `{
47544802
"compilerOptions": {
47554803
"haha": 123
47564804
}
47574805
}`;
4758-
serverEventManager.host.reloadFS([file, configFile]);
4806+
serverEventManager.host.reloadFS(files);
47594807
serverEventManager.host.runQueuedTimeoutCallbacks();
4760-
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path);
4808+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path, [
4809+
getUnknownCompilerOptionDiagnostic(configFile, "haha")
4810+
]);
47614811

47624812
configFile.content = `{
47634813
"compilerOptions": {}
47644814
}`;
4765-
serverEventManager.host.reloadFS([file, configFile]);
4815+
serverEventManager.host.reloadFS(files);
47664816
serverEventManager.host.runQueuedTimeoutCallbacks();
4767-
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path);
4817+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path, emptyArray);
47684818
});
47694819

47704820
it("are not generated when the config file does not include file opened and config file has errors", () => {
4771-
const file = {
4821+
const file: File = {
47724822
path: "/a/b/app.ts",
47734823
content: "let x = 10"
47744824
};
4775-
const file2 = {
4825+
const file2: File = {
47764826
path: "/a/b/test.ts",
47774827
content: "let x = 10"
47784828
};
4779-
const configFile = {
4829+
const configFile: File = {
47804830
path: "/a/b/tsconfig.json",
47814831
content: `{
47824832
"compilerOptions": {
@@ -4792,11 +4842,11 @@ namespace ts.projectSystem {
47924842
});
47934843

47944844
it("are not generated when the config file has errors but suppressDiagnosticEvents is true", () => {
4795-
const file = {
4845+
const file: File = {
47964846
path: "/a/b/app.ts",
47974847
content: "let x = 10"
47984848
};
4799-
const configFile = {
4849+
const configFile: File = {
48004850
path: "/a/b/tsconfig.json",
48014851
content: `{
48024852
"compilerOptions": {
@@ -4805,21 +4855,21 @@ namespace ts.projectSystem {
48054855
}
48064856
}`
48074857
};
4808-
const serverEventManager = new TestServerEventManager([file, configFile], /*suppressDiagnosticEvents*/ true);
4858+
const serverEventManager = new TestServerEventManager([file, libFile, configFile], /*suppressDiagnosticEvents*/ true);
48094859
openFilesForSession([file], serverEventManager.session);
48104860
serverEventManager.hasZeroEvent("configFileDiag");
48114861
});
48124862

48134863
it("are not generated when the config file does not include file opened and doesnt contain any errors", () => {
4814-
const file = {
4864+
const file: File = {
48154865
path: "/a/b/app.ts",
48164866
content: "let x = 10"
48174867
};
4818-
const file2 = {
4868+
const file2: File = {
48194869
path: "/a/b/test.ts",
48204870
content: "let x = 10"
48214871
};
4822-
const configFile = {
4872+
const configFile: File = {
48234873
path: "/a/b/tsconfig.json",
48244874
content: `{
48254875
"files": ["app.ts"]
@@ -4830,6 +4880,27 @@ namespace ts.projectSystem {
48304880
openFilesForSession([file2], serverEventManager.session);
48314881
serverEventManager.hasZeroEvent("configFileDiag");
48324882
});
4883+
4884+
it("contains the project reference errors", () => {
4885+
const file: File = {
4886+
path: "/a/b/app.ts",
4887+
content: "let x = 10"
4888+
};
4889+
const noSuchTsconfig = "no-such-tsconfig.json";
4890+
const configFile: File = {
4891+
path: "/a/b/tsconfig.json",
4892+
content: `{
4893+
"files": ["app.ts"],
4894+
"references": [{"path":"./${noSuchTsconfig}"}]
4895+
}`
4896+
};
4897+
4898+
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
4899+
openFilesForSession([file], serverEventManager.session);
4900+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, [
4901+
getFileNotFoundDiagnostic(configFile, noSuchTsconfig)
4902+
]);
4903+
});
48334904
});
48344905

48354906
describe("tsserverProjectSystem skipLibCheck", () => {

0 commit comments

Comments
 (0)