Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,13 @@ namespace ts {

function getSemanticDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
return runWithCancellationToken(() => {
// If skipLibCheck is enabled, skip reporting errors if file is a declaration file.
// If skipDefaultLibCheck is enabled, skip reporting errors if file contains a
// '/// <reference no-default-lib="true"/>' directive.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment isn't making much sense to me as written. If you have no-default-lib set to true, then I assume there is no default lib to check - in which case, what difference does skipDefaultLibCheck make?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-default-lib is just a triple-slash directive. it can exist any where.
We have two flags, for historic reasons:

  1. skipDefaultLibCheck, only skips the checking of files that have /// <reference no-default-lib="true"/> in them. This flag is now deprecated, but we still honor it.
  2. skipLibCheck, skips checking of all .d.ts files

if (options.skipLibCheck && sourceFile.isDeclarationFile || options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have this check in two places ? I see similar check in checkSourceFileWorker in checker.ts. Do we need to remove that check?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, will get that fixed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember now why i did not do that, we call TypeChecker.getDiagnostics from the tests directly.

return emptyArray;
}

const typeChecker = getDiagnosticsProducingTypeChecker();

Debug.assert(!!sourceFile.bindDiagnostics);
Expand Down
116 changes: 116 additions & 0 deletions src/harness/unittests/tsserverProjectSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3204,6 +3204,122 @@ namespace ts.projectSystem {
const errorResult = <protocol.Diagnostic[]>session.executeCommand(dTsFileGetErrRequest).response;
assert.isTrue(errorResult.length === 0);
});

it("should not report bind errors for declaration files with skipLibCheck=true", () => {
const jsconfigFile = {
path: "/a/jsconfig.json",
content: "{}"
};
const jsFile = {
path: "/a/jsFile.js",
content: "let x = 1;"
};
const dTsFile1 = {
path: "/a/dTsFile1.d.ts",
content: `
declare var x: number;`
};
const dTsFile2 = {
path: "/a/dTsFile2.d.ts",
content: `
declare var x: string;`
};
const host = createServerHost([jsconfigFile, jsFile, dTsFile1, dTsFile2]);
const session = createSession(host);
openFilesForSession([jsFile], session);

const dTsFile1GetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
CommandNames.SemanticDiagnosticsSync,
{ file: dTsFile1.path }
);
const error1Result = <protocol.Diagnostic[]>session.executeCommand(dTsFile1GetErrRequest).response;
assert.isTrue(error1Result.length === 0);

const dTsFile2GetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
CommandNames.SemanticDiagnosticsSync,
{ file: dTsFile2.path }
);
const error2Result = <protocol.Diagnostic[]>session.executeCommand(dTsFile2GetErrRequest).response;
assert.isTrue(error2Result.length === 0);
});

it("should report semanitc errors for loose JS files with '// @ts-check' and skipLibCheck=true", () => {
const jsFile = {
path: "/a/jsFile.js",
content: `
// @ts-check
let x = 1;
x === "string";`
};

const host = createServerHost([jsFile]);
const session = createSession(host);
openFilesForSession([jsFile], session);

const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
CommandNames.SemanticDiagnosticsSync,
{ file: jsFile.path }
);
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
assert.isTrue(errorResult.length === 1);
assert.equal(errorResult[0].code, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2.code);
});

it("should report semanitc errors for configured js project with '// @ts-check' and skipLibCheck=true", () => {
const jsconfigFile = {
path: "/a/jsconfig.json",
content: "{}"
};

const jsFile = {
path: "/a/jsFile.js",
content: `
// @ts-check
let x = 1;
x === "string";`
};

const host = createServerHost([jsconfigFile, jsFile]);
const session = createSession(host);
openFilesForSession([jsFile], session);

const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
CommandNames.SemanticDiagnosticsSync,
{ file: jsFile.path }
);
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
assert.isTrue(errorResult.length === 1);
assert.equal(errorResult[0].code, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2.code);
});

it("should report semanitc errors for configured js project with checkJs=true and skipLibCheck=true", () => {
const jsconfigFile = {
path: "/a/jsconfig.json",
content: JSON.stringify({
compilerOptions: {
checkJs: true,
skipLibCheck: true
},
})
};
const jsFile = {
path: "/a/jsFile.js",
content: `let x = 1;
x === "string";`
};

const host = createServerHost([jsconfigFile, jsFile]);
const session = createSession(host);
openFilesForSession([jsFile], session);

const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
CommandNames.SemanticDiagnosticsSync,
{ file: jsFile.path }
);
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
assert.isTrue(errorResult.length === 1);
assert.equal(errorResult[0].code, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2.code);
});
});

describe("non-existing directories listed in config file input array", () => {
Expand Down
31 changes: 21 additions & 10 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,26 @@ namespace ts.server {
return ((1e9 * seconds) + nanoseconds) / 1000000.0;
}

function shouldSkipSemanticCheck(project: Project) {
if (project.projectKind === ProjectKind.Inferred || project.projectKind === ProjectKind.External) {
return project.isJsOnlyProject();
}
else {
// For configured projects, require that skipLibCheck be set also
const options = project.getCompilerOptions();
return options.skipLibCheck && !options.checkJs && project.isJsOnlyProject();
function isDeclarationFileInJSOnlyNonConfiguredProject(project: Project, file: NormalizedPath) {
// Checking for semantic diagnostics is an expensive process. We want to avoid it if we
// know for sure it is not needed.
// For instance, .d.ts files injected by ATA automatically do not produce any relevant
// errors to a JS- only project.
//
// Note that configured projects can set skipLibCheck (on by default in jsconfig.json) to
// disable checking for declaration files. We only need to verify for inferred projects (e.g.
// miscellaneous context in VS) and external projects(e.g.VS.csproj project) with only JS
// files.
//
// We still want to check .js files in a JS-only inferred or external project (e.g. if the
// file has '// @ts-check').

if ((project.projectKind === ProjectKind.Inferred || project.projectKind === ProjectKind.External) &&
project.isJsOnlyProject()) {
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
return scriptInfo && !scriptInfo.isJavaScript();
}
return false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this always returns false for configured projects (as the above if is only entered for inferred/external projects)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. program.getSemanticDiagnostics should do the right thing for configured projects.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Again, maybe clarify in comments in the function then. It seems this only returns true for a narrow subset of files that may actually have semantic checking skipped eventually.

}

interface FileStart {
Expand Down Expand Up @@ -489,7 +500,7 @@ namespace ts.server {
private semanticCheck(file: NormalizedPath, project: Project) {
try {
let diags: Diagnostic[] = [];
if (!shouldSkipSemanticCheck(project)) {
if (!isDeclarationFileInJSOnlyNonConfiguredProject(project, file)) {
diags = project.getLanguageService().getSemanticDiagnostics(file);
}

Expand Down Expand Up @@ -597,7 +608,7 @@ namespace ts.server {

private getDiagnosticsWorker(args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => Diagnostic[], includeLinePosition: boolean) {
const { project, file } = this.getFileAndProject(args);
if (isSemantic && shouldSkipSemanticCheck(project)) {
if (isSemantic && isDeclarationFileInJSOnlyNonConfiguredProject(project, file)) {
return [];
}
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
Expand Down
1 change: 1 addition & 0 deletions tests/cases/compiler/noDefaultLib.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @skipDefaultLibCheck: false
/// <reference no-default-lib="true"/>
var x;

Expand Down
1 change: 1 addition & 0 deletions tests/cases/compiler/variableDeclarationInStrictMode1.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// @skipDefaultLibCheck: false
"use strict";
var eval;