diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 3d56796e37..d00e2b6e68 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -1905,46 +1905,64 @@ func (c *Checker) isBlockScopedNameDeclaredBeforeUse(declaration *ast.Node, usag } func (c *Checker) isUsedInFunctionOrInstanceProperty(usage *ast.Node, declaration *ast.Node, declContainer *ast.Node) bool { - for current := usage; current != nil && current != declContainer; current = current.Parent { + return ast.FindAncestorOrQuit(usage, func(current *ast.Node) ast.FindAncestorResult { + if current == declContainer { + return ast.FindAncestorQuit + } if ast.IsFunctionLike(current) { - return ast.GetImmediatelyInvokedFunctionExpression(current) == nil + return ast.ToFindAncestorResult(ast.GetImmediatelyInvokedFunctionExpression(current) == nil) } if ast.IsClassStaticBlockDeclaration(current) { - return declaration.Pos() < usage.Pos() + return ast.ToFindAncestorResult(declaration.Pos() < usage.Pos()) } - if current.Parent != nil && ast.IsPropertyDeclaration(current.Parent) && current.Parent.Initializer() == current { - if ast.IsStatic(current.Parent) { - if ast.IsMethodDeclaration(declaration) { - return true - } - if ast.IsPropertyDeclaration(declaration) && ast.GetContainingClass(usage) == ast.GetContainingClass(declaration) { - propName := declaration.Name() - if ast.IsIdentifier(propName) || ast.IsPrivateIdentifier(propName) { - t := c.getTypeOfSymbol(c.getSymbolOfDeclaration(declaration)) - staticBlocks := core.Filter(declaration.Parent.Members(), ast.IsClassStaticBlockDeclaration) - if c.isPropertyInitializedInStaticBlocks(propName, t, staticBlocks, declaration.Parent.Pos(), current.Pos()) { - return true + + if current.Parent != nil && ast.IsPropertyDeclaration(current.Parent) { + propertyDeclaration := current.Parent + initializerOfProperty := propertyDeclaration.Initializer() == current + if initializerOfProperty { + if ast.IsStatic(current.Parent) { + if ast.IsMethodDeclaration(declaration) { + return ast.FindAncestorTrue + } + if ast.IsPropertyDeclaration(declaration) && ast.GetContainingClass(usage) == ast.GetContainingClass(declaration) { + propName := declaration.Name() + if ast.IsIdentifier(propName) || ast.IsPrivateIdentifier(propName) { + t := c.getTypeOfSymbol(c.getSymbolOfDeclaration(declaration)) + staticBlocks := core.Filter(declaration.Parent.Members(), ast.IsClassStaticBlockDeclaration) + if c.isPropertyInitializedInStaticBlocks(propName, t, staticBlocks, declaration.Parent.Pos(), current.Pos()) { + return ast.FindAncestorTrue + } } } - } - } else { - isDeclarationInstanceProperty := ast.IsPropertyDeclaration(declaration) && !ast.IsStatic(declaration) - if !isDeclarationInstanceProperty || ast.GetContainingClass(usage) != ast.GetContainingClass(declaration) { - return true + } else { + isDeclarationInstanceProperty := ast.IsPropertyDeclaration(declaration) && !ast.IsStatic(declaration) + if !isDeclarationInstanceProperty || ast.GetContainingClass(usage) != ast.GetContainingClass(declaration) { + return ast.FindAncestorTrue + } } } } - if current.Parent != nil && ast.IsDecorator(current.Parent) && current.Parent.AsDecorator().Expression == current { + + if current.Parent != nil && ast.IsDecorator(current.Parent) { decorator := current.Parent.AsDecorator() - if ast.IsParameter(decorator.Parent) { - return c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent.Parent, declaration, declContainer) - } - if ast.IsMethodDeclaration(decorator.Parent) { - return c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent, declaration, declContainer) + if decorator.Expression == current { + if ast.IsParameter(decorator.Parent) { + if c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent.Parent, declaration, declContainer) { + return ast.FindAncestorTrue + } + return ast.FindAncestorQuit + } + if ast.IsMethodDeclaration(decorator.Parent) { + if c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent, declaration, declContainer) { + return ast.FindAncestorTrue + } + return ast.FindAncestorQuit + } } } - } - return false + + return ast.FindAncestorFalse + }) != nil } func isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration *ast.Node, usage *ast.Node, declContainer *ast.Node) bool { diff --git a/testdata/baselines/reference/compiler/blockedScopeVariableNotUnused1.js b/testdata/baselines/reference/compiler/blockedScopeVariableNotUnused1.js new file mode 100644 index 0000000000..54a3a527f9 --- /dev/null +++ b/testdata/baselines/reference/compiler/blockedScopeVariableNotUnused1.js @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/blockedScopeVariableNotUnused1.ts] //// + +//// [blockedScopeVariableNotUnused1.ts] +export function foo() { + const _fn = () => { + ;(() => numFilesSelected)() + } + + const numFilesSelected = 1 +} + + +//// [blockedScopeVariableNotUnused1.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.foo = foo; +function foo() { + const _fn = () => { + ; + (() => numFilesSelected)(); + }; + const numFilesSelected = 1; +} diff --git a/testdata/baselines/reference/compiler/blockedScopeVariableNotUnused1.symbols b/testdata/baselines/reference/compiler/blockedScopeVariableNotUnused1.symbols new file mode 100644 index 0000000000..7cb8b2fb75 --- /dev/null +++ b/testdata/baselines/reference/compiler/blockedScopeVariableNotUnused1.symbols @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/blockedScopeVariableNotUnused1.ts] //// + +=== blockedScopeVariableNotUnused1.ts === +export function foo() { +>foo : Symbol(foo, Decl(blockedScopeVariableNotUnused1.ts, 0, 0)) + + const _fn = () => { +>_fn : Symbol(_fn, Decl(blockedScopeVariableNotUnused1.ts, 1, 7)) + + ;(() => numFilesSelected)() +>numFilesSelected : Symbol(numFilesSelected, Decl(blockedScopeVariableNotUnused1.ts, 5, 7)) + } + + const numFilesSelected = 1 +>numFilesSelected : Symbol(numFilesSelected, Decl(blockedScopeVariableNotUnused1.ts, 5, 7)) +} + diff --git a/testdata/baselines/reference/compiler/blockedScopeVariableNotUnused1.types b/testdata/baselines/reference/compiler/blockedScopeVariableNotUnused1.types new file mode 100644 index 0000000000..51f64d7851 --- /dev/null +++ b/testdata/baselines/reference/compiler/blockedScopeVariableNotUnused1.types @@ -0,0 +1,22 @@ +//// [tests/cases/compiler/blockedScopeVariableNotUnused1.ts] //// + +=== blockedScopeVariableNotUnused1.ts === +export function foo() { +>foo : () => void + + const _fn = () => { +>_fn : () => void +>() => { ;(() => numFilesSelected)() } : () => void + + ;(() => numFilesSelected)() +>(() => numFilesSelected)() : number +>(() => numFilesSelected) : () => number +>() => numFilesSelected : () => number +>numFilesSelected : 1 + } + + const numFilesSelected = 1 +>numFilesSelected : 1 +>1 : 1 +} + diff --git a/testdata/tests/cases/compiler/blockedScopeVariableNotUnused1.ts b/testdata/tests/cases/compiler/blockedScopeVariableNotUnused1.ts new file mode 100644 index 0000000000..e0598e702f --- /dev/null +++ b/testdata/tests/cases/compiler/blockedScopeVariableNotUnused1.ts @@ -0,0 +1,9 @@ +// @strict: true + +export function foo() { + const _fn = () => { + ;(() => numFilesSelected)() + } + + const numFilesSelected = 1 +}