From 0365c96e37bbdf8cb3cdce2b3ff5cef98c79dd05 Mon Sep 17 00:00:00 2001 From: Dom Chen Date: Thu, 20 Oct 2016 04:07:49 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20#11660:=20wrong=20reports=20that=20block-?= =?UTF-8?q?scoped=20variable=20used=20before=20its=20=E2=80=A6=20(#11692)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix #11660: wrong reports that block-scoped variable used before its declaration * Fix code style in checker.ts * Add unit test for #11660 * Fix the unit test for #11660 --- src/compiler/checker.ts | 12 ++++-- .../reference/useBeforeDeclaration.js | 38 ++++++++++++++++++ .../reference/useBeforeDeclaration.symbols | 34 ++++++++++++++++ .../reference/useBeforeDeclaration.types | 39 +++++++++++++++++++ tests/cases/compiler/useBeforeDeclaration.ts | 20 ++++++++++ 5 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/useBeforeDeclaration.js create mode 100644 tests/baselines/reference/useBeforeDeclaration.symbols create mode 100644 tests/baselines/reference/useBeforeDeclaration.types create mode 100644 tests/cases/compiler/useBeforeDeclaration.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3c7ff7112e0..a0a2fa19f19 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -591,7 +591,11 @@ namespace ts { // nodes are in different files and order cannot be determines return true; } - + // declaration is after usage + // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) + if (isUsedInFunctionOrNonStaticProperty(usage)) { + return true; + } const sourceFiles = host.getSourceFiles(); return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile); } @@ -605,7 +609,8 @@ namespace ts { // declaration is after usage // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) - return isUsedInFunctionOrNonStaticProperty(declaration, usage); + const container = getEnclosingBlockScopeContainer(declaration); + return isUsedInFunctionOrNonStaticProperty(usage, container); function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean { const container = getEnclosingBlockScopeContainer(declaration); @@ -634,8 +639,7 @@ namespace ts { return false; } - function isUsedInFunctionOrNonStaticProperty(declaration: Declaration, usage: Node): boolean { - const container = getEnclosingBlockScopeContainer(declaration); + function isUsedInFunctionOrNonStaticProperty(usage: Node, container?: Node): boolean { let current = usage; while (current) { if (current === container) { diff --git a/tests/baselines/reference/useBeforeDeclaration.js b/tests/baselines/reference/useBeforeDeclaration.js new file mode 100644 index 00000000000..6eab8683aec --- /dev/null +++ b/tests/baselines/reference/useBeforeDeclaration.js @@ -0,0 +1,38 @@ +//// [tests/cases/compiler/useBeforeDeclaration.ts] //// + +//// [A.ts] + +namespace ts { + export function printVersion():void { + log("Version: " + sys.version); // the call of sys.version is deferred, should not report an error. + } + + export function log(info:string):void { + + } +} + +//// [B.ts] +namespace ts { + + export let sys:{version:string} = {version: "2.0.5"}; + +} + + + +//// [test.js] +var ts; +(function (ts) { + function printVersion() { + log("Version: " + ts.sys.version); // the call of sys.version is deferred, should not report an error. + } + ts.printVersion = printVersion; + function log(info) { + } + ts.log = log; +})(ts || (ts = {})); +var ts; +(function (ts) { + ts.sys = { version: "2.0.5" }; +})(ts || (ts = {})); diff --git a/tests/baselines/reference/useBeforeDeclaration.symbols b/tests/baselines/reference/useBeforeDeclaration.symbols new file mode 100644 index 00000000000..484255c0c7d --- /dev/null +++ b/tests/baselines/reference/useBeforeDeclaration.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/A.ts === + +namespace ts { +>ts : Symbol(ts, Decl(A.ts, 0, 0), Decl(B.ts, 0, 0)) + + export function printVersion():void { +>printVersion : Symbol(printVersion, Decl(A.ts, 1, 14)) + + log("Version: " + sys.version); // the call of sys.version is deferred, should not report an error. +>log : Symbol(log, Decl(A.ts, 4, 5)) +>sys.version : Symbol(version, Decl(B.ts, 2, 20)) +>sys : Symbol(sys, Decl(B.ts, 2, 14)) +>version : Symbol(version, Decl(B.ts, 2, 20)) + } + + export function log(info:string):void { +>log : Symbol(log, Decl(A.ts, 4, 5)) +>info : Symbol(info, Decl(A.ts, 6, 24)) + + } +} + +=== tests/cases/compiler/B.ts === +namespace ts { +>ts : Symbol(ts, Decl(A.ts, 0, 0), Decl(B.ts, 0, 0)) + + export let sys:{version:string} = {version: "2.0.5"}; +>sys : Symbol(sys, Decl(B.ts, 2, 14)) +>version : Symbol(version, Decl(B.ts, 2, 20)) +>version : Symbol(version, Decl(B.ts, 2, 39)) + +} + + diff --git a/tests/baselines/reference/useBeforeDeclaration.types b/tests/baselines/reference/useBeforeDeclaration.types new file mode 100644 index 00000000000..141826723cf --- /dev/null +++ b/tests/baselines/reference/useBeforeDeclaration.types @@ -0,0 +1,39 @@ +=== tests/cases/compiler/A.ts === + +namespace ts { +>ts : typeof ts + + export function printVersion():void { +>printVersion : () => void + + log("Version: " + sys.version); // the call of sys.version is deferred, should not report an error. +>log("Version: " + sys.version) : void +>log : (info: string) => void +>"Version: " + sys.version : string +>"Version: " : "Version: " +>sys.version : string +>sys : { version: string; } +>version : string + } + + export function log(info:string):void { +>log : (info: string) => void +>info : string + + } +} + +=== tests/cases/compiler/B.ts === +namespace ts { +>ts : typeof ts + + export let sys:{version:string} = {version: "2.0.5"}; +>sys : { version: string; } +>version : string +>{version: "2.0.5"} : { version: string; } +>version : string +>"2.0.5" : "2.0.5" + +} + + diff --git a/tests/cases/compiler/useBeforeDeclaration.ts b/tests/cases/compiler/useBeforeDeclaration.ts new file mode 100644 index 00000000000..6dc3b026d13 --- /dev/null +++ b/tests/cases/compiler/useBeforeDeclaration.ts @@ -0,0 +1,20 @@ +// @outFile: test.js + +// @fileName: A.ts +namespace ts { + export function printVersion():void { + log("Version: " + sys.version); // the call of sys.version is deferred, should not report an error. + } + + export function log(info:string):void { + + } +} + +// @fileName: B.ts +namespace ts { + + export let sys:{version:string} = {version: "2.0.5"}; + +} +