Skip to content

Commit

Permalink
Report compiler options errors as well as part of configFileDiag event
Browse files Browse the repository at this point in the history
Fixes #25741
  • Loading branch information
sheetalkamat committed Jul 21, 2018
1 parent 7c512fb commit a9d497a
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 40 deletions.
19 changes: 5 additions & 14 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2703,12 +2703,12 @@ namespace ts {

function createDiagnosticForReference(index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
const referencesSyntax = getProjectReferencesSyntax();
if (referencesSyntax) {
if (createOptionDiagnosticInArrayLiteralSyntax(referencesSyntax, index, message, arg0, arg1)) {
return;
}
if (referencesSyntax && referencesSyntax.elements.length > index) {
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, referencesSyntax.elements[index], message, arg0, arg1));
}
else {
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
}
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
}

function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number) {
Expand Down Expand Up @@ -2761,15 +2761,6 @@ namespace ts {
return !!props.length;
}

function createOptionDiagnosticInArrayLiteralSyntax(arrayLiteral: ArrayLiteralExpression, index: number, message: DiagnosticMessage, arg0: string | number | undefined, arg1?: string | number, arg2?: string | number): boolean {
if (arrayLiteral.elements.length <= index) {
// Out-of-bounds
return false;
}
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, arrayLiteral.elements[index], message, arg0, arg1, arg2));
return false; // TODO: GH#18217 This function always returns `false`!`
}

function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) {
hasEmitBlockingDiagnostics.set(toPath(emitFileName), true);
programDiagnostics.add(diag);
Expand Down
4 changes: 3 additions & 1 deletion src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1674,10 +1674,12 @@ namespace ts.server {
if (!this.eventHandler || this.suppressDiagnosticEvents) {
return;
}
const diagnostics = project.getLanguageService().getCompilerOptionsDiagnostics();
diagnostics.push(...project.getAllProjectErrors());

this.eventHandler(<ConfigFileDiagEvent>{
eventName: ConfigFileDiagEvent,
data: { configFileName: project.getConfigFilePath(), diagnostics: project.getAllProjectErrors(), triggerFile }
data: { configFileName: project.getConfigFilePath(), diagnostics, triggerFile }
});
}

Expand Down
121 changes: 96 additions & 25 deletions src/testRunner/unittests/tsserverProjectSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,18 @@ namespace ts.projectSystem {
return { ts: 0, tsx: 0, dts: 0, js: 0, jsx: 0, deferred: 0, ...nonZeroStats };
}

export interface ConfigFileDiagnostic {
fileName: string | undefined;
start: number | undefined;
length: number | undefined;
messageText: string;
category: DiagnosticCategory;
code: number;
reportsUnnecessary?: {};
source?: string;
relatedInformation?: DiagnosticRelatedInformation[];
}

export class TestServerEventManager {
private events: server.ProjectServiceEvent[] = [];
readonly session: TestSession;
Expand Down Expand Up @@ -216,10 +228,14 @@ namespace ts.projectSystem {
this.events.forEach(event => assert.notEqual(event.eventName, eventName));
}

checkSingleConfigFileDiagEvent(configFileName: string, triggerFile: string) {
checkSingleConfigFileDiagEvent(configFileName: string, triggerFile: string, errors: ReadonlyArray<ConfigFileDiagnostic>) {
const eventData = this.getEvent<server.ConfigFileDiagEvent>(server.ConfigFileDiagEvent);
assert.equal(eventData.configFileName, configFileName);
assert.equal(eventData.triggerFile, triggerFile);
const actual = eventData.diagnostics.map(({ file, messageText, ...rest }) => ({ fileName: file && file.fileName, messageText: isString(messageText) ? messageText : "", ...rest }));
if (errors) {
assert.deepEqual(actual, errors);
}
}

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

describe("tsserverProjectSystem Configure file diagnostics events", () => {
function getUnknownCompilerOptionDiagnostic(configFile: File, prop: string): ConfigFileDiagnostic {
const d = Diagnostics.Unknown_compiler_option_0;
const start = configFile.content.indexOf(prop) - 1; // start at "prop"
return {
fileName: configFile.path,
start,
length: prop.length + 2,
messageText: formatStringFromArgs(d.message, [prop]),
category: d.category,
code: d.code,
reportsUnnecessary: undefined
};
}

function getFileNotFoundDiagnostic(configFile: File, relativeFileName: string): ConfigFileDiagnostic {
const findString = `{"path":"./${relativeFileName}"}`;
const d = Diagnostics.File_0_does_not_exist;
const start = configFile.content.indexOf(findString);
return {
fileName: configFile.path,
start,
length: findString.length,
messageText: formatStringFromArgs(d.message, [`${getDirectoryPath(configFile.path)}/${relativeFileName}`]),
category: d.category,
code: d.code,
reportsUnnecessary: undefined
};
}

it("are generated when the config file has errors", () => {
const file = {
const file: File = {
path: "/a/b/app.ts",
content: "let x = 10"
};
const configFile = {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
Expand All @@ -4713,29 +4757,32 @@ namespace ts.projectSystem {
}
}`
};
const serverEventManager = new TestServerEventManager([file, configFile]);
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
openFilesForSession([file], serverEventManager.session);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, [
getUnknownCompilerOptionDiagnostic(configFile, "foo"),
getUnknownCompilerOptionDiagnostic(configFile, "allowJS")
]);
});

it("are generated when the config file doesn't have errors", () => {
const file = {
const file: File = {
path: "/a/b/app.ts",
content: "let x = 10"
};
const configFile = {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {}
}`
};
const serverEventManager = new TestServerEventManager([file, configFile]);
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
openFilesForSession([file], serverEventManager.session);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, emptyArray);
});

it("are generated when the config file changes", () => {
const file = {
const file: File = {
path: "/a/b/app.ts",
content: "let x = 10"
};
Expand All @@ -4746,37 +4793,40 @@ namespace ts.projectSystem {
}`
};

const serverEventManager = new TestServerEventManager([file, configFile]);
const files = [file, libFile, configFile];
const serverEventManager = new TestServerEventManager(files);
openFilesForSession([file], serverEventManager.session);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, emptyArray);

configFile.content = `{
"compilerOptions": {
"haha": 123
}
}`;
serverEventManager.host.reloadFS([file, configFile]);
serverEventManager.host.reloadFS(files);
serverEventManager.host.runQueuedTimeoutCallbacks();
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path, [
getUnknownCompilerOptionDiagnostic(configFile, "haha")
]);

configFile.content = `{
"compilerOptions": {}
}`;
serverEventManager.host.reloadFS([file, configFile]);
serverEventManager.host.reloadFS(files);
serverEventManager.host.runQueuedTimeoutCallbacks();
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path, emptyArray);
});

it("are not generated when the config file does not include file opened and config file has errors", () => {
const file = {
const file: File = {
path: "/a/b/app.ts",
content: "let x = 10"
};
const file2 = {
const file2: File = {
path: "/a/b/test.ts",
content: "let x = 10"
};
const configFile = {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
Expand All @@ -4792,11 +4842,11 @@ namespace ts.projectSystem {
});

it("are not generated when the config file has errors but suppressDiagnosticEvents is true", () => {
const file = {
const file: File = {
path: "/a/b/app.ts",
content: "let x = 10"
};
const configFile = {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
Expand All @@ -4805,21 +4855,21 @@ namespace ts.projectSystem {
}
}`
};
const serverEventManager = new TestServerEventManager([file, configFile], /*suppressDiagnosticEvents*/ true);
const serverEventManager = new TestServerEventManager([file, libFile, configFile], /*suppressDiagnosticEvents*/ true);
openFilesForSession([file], serverEventManager.session);
serverEventManager.hasZeroEvent("configFileDiag");
});

it("are not generated when the config file does not include file opened and doesnt contain any errors", () => {
const file = {
const file: File = {
path: "/a/b/app.ts",
content: "let x = 10"
};
const file2 = {
const file2: File = {
path: "/a/b/test.ts",
content: "let x = 10"
};
const configFile = {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"files": ["app.ts"]
Expand All @@ -4830,6 +4880,27 @@ namespace ts.projectSystem {
openFilesForSession([file2], serverEventManager.session);
serverEventManager.hasZeroEvent("configFileDiag");
});

it("contains the project reference errors", () => {
const file: File = {
path: "/a/b/app.ts",
content: "let x = 10"
};
const noSuchTsconfig = "no-such-tsconfig.json";
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"files": ["app.ts"],
"references": [{"path":"./${noSuchTsconfig}"}]
}`
};

const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
openFilesForSession([file], serverEventManager.session);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, [
getFileNotFoundDiagnostic(configFile, noSuchTsconfig)
]);
});
});

describe("tsserverProjectSystem skipLibCheck", () => {
Expand Down

0 comments on commit a9d497a

Please sign in to comment.