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
102 changes: 56 additions & 46 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3098,7 +3098,7 @@
"category": "Message",
"code": 6132
},
"'{0}' is declared but never used.": {
"'{0}' is declared but its value is never read.": {
"category": "Error",
"code": 6133
},
Expand All @@ -3118,7 +3118,7 @@
"category": "Error",
"code": 6137
},
"Property '{0}' is declared but never used.": {
"Property '{0}' is declared but its value is never read.": {
"category": "Error",
"code": 6138
},
Expand Down
7 changes: 0 additions & 7 deletions src/compiler/transformers/es2017.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ namespace ts {
const compilerOptions = context.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);

// These variables contain state that changes as we descend into the tree.
let currentSourceFile: SourceFile;

/**
* Keeps track of whether expression substitution has been enabled for specific edge cases.
* They are persisted between each SourceFile transformation and should not be reset.
Expand Down Expand Up @@ -51,12 +48,8 @@ namespace ts {
return node;
}

currentSourceFile = node;

const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());

currentSourceFile = undefined;
return visited;
}

Expand Down
4 changes: 0 additions & 4 deletions src/compiler/transformers/generators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ namespace ts {
const previousOnSubstituteNode = context.onSubstituteNode;
context.onSubstituteNode = onSubstituteNode;

let currentSourceFile: SourceFile;
let renamedCatchVariables: Map<boolean>;
let renamedCatchVariableDeclarations: Identifier[];

Expand Down Expand Up @@ -300,12 +299,9 @@ namespace ts {
return node;
}

currentSourceFile = node;

const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());

currentSourceFile = undefined;
return visited;
}

Expand Down
3 changes: 1 addition & 2 deletions src/compiler/tsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ namespace ts {
const commandLine = parseCommandLine(args);
let configFileName: string; // Configuration file name (if any)
let cachedConfigFileText: string; // Cached configuration file text, used for reparsing (if any)
let configFileWatcher: FileWatcher; // Configuration file watcher
let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal
let cachedProgram: Program; // Program cached from last compilation
let rootFileNames: string[]; // Root fileNames for compilation
Expand Down Expand Up @@ -189,7 +188,7 @@ namespace ts {
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
if (configFileName) {
configFileWatcher = sys.watchFile(configFileName, configFileChanged);
sys.watchFile(configFileName, configFileChanged);
}
if (sys.watchDirectory && configFileName) {
const directory = ts.getDirectoryPath(configFileName);
Expand Down
40 changes: 40 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3504,6 +3504,46 @@ namespace ts {
export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags {
return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags;
}

export function isWriteOnlyAccess(node: Node) {
return accessKind(node) === AccessKind.Write;
}

export function isWriteAccess(node: Node) {
return accessKind(node) !== AccessKind.Read;
}

const enum AccessKind {
/** Only reads from a variable. */
Read,
/** Only writes to a variable without using the result. E.g.: `x++;`. */
Write,
/** Writes to a variable and uses the result as an expression. E.g.: `f(x++);`. */
ReadWrite
}
function accessKind(node: Node): AccessKind {
const { parent } = node;
if (!parent) return AccessKind.Read;

switch (parent.kind) {
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.PrefixUnaryExpression:
const { operator } = parent as PrefixUnaryExpression | PostfixUnaryExpression;
return operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken ? writeOrReadWrite() : AccessKind.Read;
case SyntaxKind.BinaryExpression:
const { left, operatorToken } = parent as BinaryExpression;
return left === node && isAssignmentOperator(operatorToken.kind) ? writeOrReadWrite() : AccessKind.Read;
case SyntaxKind.PropertyAccessExpression:
return (parent as PropertyAccessExpression).name !== node ? AccessKind.Read : accessKind(parent);
default:
return AccessKind.Read;
}

function writeOrReadWrite(): AccessKind {
// If grandparent is not an ExpressionStatement, this is used as an expression in addition to having a side effect.
return parent.parent && parent.parent.kind === SyntaxKind.ExpressionStatement ? AccessKind.Write : AccessKind.ReadWrite;
}
}
}

namespace ts {
Expand Down
18 changes: 0 additions & 18 deletions src/harness/compilerRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,13 @@ const enum CompilerTestType {
class CompilerBaselineRunner extends RunnerBase {
private basePath = "tests/cases";
private testSuiteName: TestRunnerKind;
private errors: boolean;
private emit: boolean;
private decl: boolean;
private output: boolean;

public options: string;

constructor(public testType: CompilerTestType) {
super();
this.errors = true;
this.emit = true;
this.decl = true;
this.output = true;
if (testType === CompilerTestType.Conformance) {
this.testSuiteName = "conformance";
}
Expand Down Expand Up @@ -214,26 +208,14 @@ class CompilerBaselineRunner extends RunnerBase {

private parseOptions() {
if (this.options && this.options.length > 0) {
this.errors = false;
this.emit = false;
this.decl = false;
this.output = false;

const opts = this.options.split(",");
for (let i = 0; i < opts.length; i++) {
switch (opts[i]) {
case "error":
this.errors = true;
break;
case "emit":
this.emit = true;
break;
case "decl":
this.decl = true;
break;
case "output":
this.output = true;
break;
default:
throw new Error("unsupported flag");
}
Expand Down
11 changes: 0 additions & 11 deletions src/harness/unittests/compileOnSave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ namespace ts.projectSystem {
let configFile: FileOrFolder;
let changeModuleFile1ShapeRequest1: server.protocol.Request;
let changeModuleFile1InternalRequest1: server.protocol.Request;
let changeModuleFile1ShapeRequest2: server.protocol.Request;
// A compile on save affected file request using file1
let moduleFile1FileListRequest: server.protocol.Request;

Expand Down Expand Up @@ -112,16 +111,6 @@ namespace ts.projectSystem {
insertString: `var T1: number;`
});

// Change the content of file1 to `export var T: number;export function Foo() { };`
changeModuleFile1ShapeRequest2 = makeSessionRequest<server.protocol.ChangeRequestArgs>(CommandNames.Change, {
file: moduleFile1.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: `export var T2: number;`
});

moduleFile1FileListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(CommandNames.CompileOnSaveAffectedFileList, { file: moduleFile1.path, projectFileName: configFile.path });
});

Expand Down
2 changes: 0 additions & 2 deletions src/harness/unittests/typingsInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,11 @@ namespace ts.projectSystem {
};

const host = createServerHost([file1, file2]);
let enqueueIsCalled = false;
const installer = new (class extends Installer {
constructor() {
super(host, { typesRegistry: createTypesRegistry("jquery") });
}
enqueueInstallTypingsRequest(project: server.Project, typeAcquisition: TypeAcquisition, unresolvedImports: server.SortedReadonlyArray<string>) {
enqueueIsCalled = true;
super.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
}
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
Expand Down
3 changes: 1 addition & 2 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,6 @@ namespace ts.server {
function createPollingWatchedFileSet(interval = 2500, chunkSize = 30) {
const watchedFiles: WatchedFile[] = [];
let nextFileToCheck = 0;
let watchTimer: any;
return { getModifiedTime, poll, startWatchTimer, addFile, removeFile };

function getModifiedTime(fileName: string): Date {
Expand Down Expand Up @@ -622,7 +621,7 @@ namespace ts.server {
// stat due to inconsistencies of fs.watch
// and efficiency of stat on modern filesystems
function startWatchTimer() {
watchTimer = setInterval(() => {
setInterval(() => {
let count = 0;
let nextToCheck = nextFileToCheck;
let firstCheck = -1;
Expand Down
4 changes: 2 additions & 2 deletions src/services/codefixes/fixUnusedIdentifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
namespace ts.codefix {
registerCodeFix({
errorCodes: [
Diagnostics._0_is_declared_but_never_used.code,
Diagnostics.Property_0_is_declared_but_never_used.code
Diagnostics._0_is_declared_but_its_value_is_never_read.code,
Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code
],
getCodeActions: (context: CodeFixContext) => {
const sourceFile = context.sourceFile;
Expand Down
21 changes: 4 additions & 17 deletions src/services/findAllReferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ namespace ts.FindAllReferences {
return {
fileName: node.getSourceFile().fileName,
textSpan: getTextSpan(node),
isWriteAccess: isWriteAccess(node),
isWriteAccess: isWriteAccessForReference(node),
isDefinition: node.kind === SyntaxKind.DefaultKeyword
|| isAnyDeclarationName(node)
|| isLiteralComputedPropertyDeclarationName(node),
Expand Down Expand Up @@ -224,7 +224,7 @@ namespace ts.FindAllReferences {

const { node, isInString } = entry;
const fileName = entry.node.getSourceFile().fileName;
const writeAccess = isWriteAccess(node);
const writeAccess = isWriteAccessForReference(node);
const span: HighlightSpan = {
textSpan: getTextSpan(node),
kind: writeAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference,
Expand All @@ -244,21 +244,8 @@ namespace ts.FindAllReferences {
}

/** A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment */
function isWriteAccess(node: Node): boolean {
if (node.kind === SyntaxKind.DefaultKeyword || isAnyDeclarationName(node)) {
return true;
}

const { parent } = node;
switch (parent && parent.kind) {
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.PrefixUnaryExpression:
return true;
case SyntaxKind.BinaryExpression:
return (<BinaryExpression>parent).left === node && isAssignmentOperator((<BinaryExpression>parent).operatorToken.kind);
default:
return false;
}
function isWriteAccessForReference(node: Node): boolean {
return node.kind === SyntaxKind.DefaultKeyword || isAnyDeclarationName(node) || isWriteAccess(node);
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/services/preProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,11 @@ namespace ts {

// skip open bracket
token = nextToken();
let i = 0;
// scan until ']' or EOF
while (token !== SyntaxKind.CloseBracketToken && token !== SyntaxKind.EndOfFileToken) {
// record string literals as module names
if (token === SyntaxKind.StringLiteral) {
recordModuleName();
i++;
}

token = nextToken();
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/extendsUntypedModule.errors.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/a.ts(2,8): error TS6133: 'Bar' is declared but never used.
/a.ts(2,8): error TS6133: 'Bar' is declared but its value is never read.


==== /a.ts (1 errors) ====
import Foo from "foo";
import Bar from "bar"; // error: unused
~~~
!!! error TS6133: 'Bar' is declared but never used.
!!! error TS6133: 'Bar' is declared but its value is never read.
export class A extends Foo { }

==== /node_modules/foo/index.js (0 errors) ====
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
tests/cases/compiler/noUnusedLocals_selfReference.ts(3,10): error TS6133: 'f' is declared but never used.
tests/cases/compiler/noUnusedLocals_selfReference.ts(4,7): error TS6133: 'C' is declared but never used.
tests/cases/compiler/noUnusedLocals_selfReference.ts(7,6): error TS6133: 'E' is declared but never used.
tests/cases/compiler/noUnusedLocals_selfReference.ts(3,10): error TS6133: 'f' is declared but its value is never read.
tests/cases/compiler/noUnusedLocals_selfReference.ts(4,7): error TS6133: 'C' is declared but its value is never read.
tests/cases/compiler/noUnusedLocals_selfReference.ts(7,6): error TS6133: 'E' is declared but its value is never read.


==== tests/cases/compiler/noUnusedLocals_selfReference.ts (3 errors) ====
export {}; // Make this a module scope, so these are local variables.

function f() { f; }
~
!!! error TS6133: 'f' is declared but never used.
!!! error TS6133: 'f' is declared but its value is never read.
class C {
~
!!! error TS6133: 'C' is declared but never used.
!!! error TS6133: 'C' is declared but its value is never read.
m() { C; }
}
enum E { A = 0, B = E.A }
~
!!! error TS6133: 'E' is declared but never used.
!!! error TS6133: 'E' is declared but its value is never read.

// Does not detect mutual recursion.
function g() { D; }
Expand Down
16 changes: 16 additions & 0 deletions tests/baselines/reference/noUnusedLocals_writeOnly.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
tests/cases/compiler/noUnusedLocals_writeOnly.ts(1,12): error TS6133: 'x' is declared but its value is never read.


==== tests/cases/compiler/noUnusedLocals_writeOnly.ts (1 errors) ====
function f(x = 0) {
~
!!! error TS6133: 'x' is declared but its value is never read.
x = 1;
x++;
x /= 2;

let y = 0;
// This is a write access to y, but not a write-*only* access.
f(y++);
}

22 changes: 22 additions & 0 deletions tests/baselines/reference/noUnusedLocals_writeOnly.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//// [noUnusedLocals_writeOnly.ts]
function f(x = 0) {
x = 1;
x++;
x /= 2;

let y = 0;
// This is a write access to y, but not a write-*only* access.
f(y++);
}


//// [noUnusedLocals_writeOnly.js]
function f(x) {
if (x === void 0) { x = 0; }
x = 1;
x++;
x /= 2;
var y = 0;
// This is a write access to y, but not a write-*only* access.
f(y++);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
tests/cases/compiler/noUnusedLocals_writeOnlyProperty.ts(2,13): error TS6133: 'x' is declared but its value is never read.


==== tests/cases/compiler/noUnusedLocals_writeOnlyProperty.ts (1 errors) ====
class C {
private x;
~
!!! error TS6133: 'x' is declared but its value is never read.
m() {
this.x = 0;
}
}

Loading