diff --git a/.quality-baseline.json b/.quality-baseline.json index dda4559..73e24e9 100644 --- a/.quality-baseline.json +++ b/.quality-baseline.json @@ -381,12 +381,6 @@ "singleUseVariable" ] }, - "src/core/services/variable-name-validator.ts": { - "lastCommitId": "4d7170d18e23cea4dc86b7c500ac66e148a95683", - "violations": [ - "singleUseVariable" - ] - }, "src/core/services/variable-reference-request.ts": { "lastCommitId": "5add9ade67733f16f53716881aab85e467d3c68f", "violations": [ @@ -609,13 +603,6 @@ "singleUseVariable" ] }, - "tests/unit/services/variable-name-validator.test.ts": { - "lastCommitId": "0f4e7736995c00491695acae2238fe55da1bd0a0", - "violations": [ - "fileSize", - "singleUseVariable" - ] - }, "tests/utils/file-operations.ts": { "lastCommitId": "89ea54e72e552715acec38f728fc0face807d2ad", "violations": [ @@ -631,24 +618,6 @@ "singleUseVariable" ] }, - "tests/unit/cli-location-handling.test.ts": { - "lastCommitId": "1690de88db858021fcf493912253e06dab9dd7b4", - "violations": [ - "singleUseVariable" - ] - }, - "tests/unit/file-validator.test.ts": { - "lastCommitId": "5add9ade67733f16f53716881aab85e467d3c68f", - "violations": [ - "singleUseVariable" - ] - }, - "tests/unit/usage-tracker.test.ts": { - "lastCommitId": "608b51cb41bc489a35141052b3269596700e71f3", - "violations": [ - "singleUseVariable" - ] - }, "tests/utils/fixture-location.ts": { "lastCommitId": "c419bc00c74e093452bf8eb2aa0e61e34598225d", "violations": [ @@ -709,8 +678,20 @@ "singleUseVariable" ] }, - "tests/integration/fixture.test.ts": { - "lastCommitId": "f7d3433d1b8e57e08257e50af05bf009f175e2d0", + "tests/unit/cli-location-handling.test.ts": { + "lastCommitId": "1690de88db858021fcf493912253e06dab9dd7b4", + "violations": [ + "singleUseVariable" + ] + }, + "tests/unit/file-validator.test.ts": { + "lastCommitId": "5add9ade67733f16f53716881aab85e467d3c68f", + "violations": [ + "singleUseVariable" + ] + }, + "tests/unit/usage-tracker.test.ts": { + "lastCommitId": "608b51cb41bc489a35141052b3269596700e71f3", "violations": [ "singleUseVariable" ] @@ -733,6 +714,24 @@ "singleUseVariable" ] }, + "tests/integration/fixture.test.ts": { + "lastCommitId": "f7d3433d1b8e57e08257e50af05bf009f175e2d0", + "violations": [ + "singleUseVariable" + ] + }, + "tests/utils/parsers/location-parser.ts": { + "lastCommitId": "d9034ceebf77dd46366b48298985149e2e7922bb", + "violations": [ + "singleUseVariable" + ] + }, + "tests/utils/parsers/meta-parser.ts": { + "lastCommitId": "fb39b25c30bad5bbfce825da839abba185c7a1d4", + "violations": [ + "singleUseVariable" + ] + }, "tests/unit/core/location-parser.test.ts": { "lastCommitId": "d9034ceebf77dd46366b48298985149e2e7922bb", "violations": [ @@ -913,18 +912,6 @@ "singleUseVariable" ] }, - "tests/utils/parsers/location-parser.ts": { - "lastCommitId": "d9034ceebf77dd46366b48298985149e2e7922bb", - "violations": [ - "singleUseVariable" - ] - }, - "tests/utils/parsers/meta-parser.ts": { - "lastCommitId": "fb39b25c30bad5bbfce825da839abba185c7a1d4", - "violations": [ - "singleUseVariable" - ] - }, "tests/unit/locators/services/node-assignment-analyzer.test.ts": { "lastCommitId": "e37f849566f5affaa17254ceaebb6b99b12da3d9", "violations": [ diff --git a/guides/REFACTORING_STYLE.md b/guides/REFACTORING_STYLE.md index db057a1..ba009bc 100644 --- a/guides/REFACTORING_STYLE.md +++ b/guides/REFACTORING_STYLE.md @@ -130,4 +130,10 @@ const lineCount = this.getLineCount(func); ### Naming Conventions - **Intention-revealing names** that describe the business purpose - **Avoid technical jargon** in favor of domain language -- **Use verbs for methods, nouns for classes and properties** \ No newline at end of file +- **Use verbs for methods, nouns for classes and properties** + +### Systematic Refactoring + +#### Line-Based Operations +- **Work bottom-up for coordinate-based edits** - Start with highest line numbers to avoid coordinate shifts +- **Example**: When inlining multiple variables, process line 167 before line 94 to maintain stable targets \ No newline at end of file diff --git a/src/core/services/variable-name-validator.ts b/src/core/services/variable-name-validator.ts index 2ddf2f9..7bf0114 100644 --- a/src/core/services/variable-name-validator.ts +++ b/src/core/services/variable-name-validator.ts @@ -1,12 +1,11 @@ -import { Node, ParameterDeclaration, FunctionDeclaration, MethodDeclaration, ArrowFunction, FunctionExpression, BindingElement, OmittedExpression, ConstructorDeclaration, ObjectBindingPattern } from 'ts-morph'; +import { Node, ParameterDeclaration, FunctionDeclaration, MethodDeclaration, ArrowFunction, FunctionExpression, BindingElement, OmittedExpression, ConstructorDeclaration } from 'ts-morph'; +import { ScopeAnalyzer } from './scope-analyzer'; type FunctionLikeNode = FunctionDeclaration | MethodDeclaration | ArrowFunction | FunctionExpression | ConstructorDeclaration; export class VariableNameValidator { generateUniqueName(baseName: string, scope: Node): string { - const existingNames = this.getExistingVariableNames(scope); - - if (!existingNames.has(baseName)) { + if (!this.getExistingVariableNames(scope).has(baseName)) { return baseName; } @@ -18,6 +17,7 @@ export class VariableNameValidator { this.addFunctionParametersIfInBlock(scope, names); this.addDescendantVariableNames(scope, names); + this.addParentScopeVariableNames(scope, names); return names; } @@ -44,21 +44,48 @@ export class VariableNameValidator { }); } + private addParentScopeVariableNames(scope: Node, names: Set): void { + let parentScope = ScopeAnalyzer.getParentScope(scope); + while (parentScope) { + this.addDirectVariableNames(parentScope, names); + this.addFunctionParametersIfInBlock(parentScope, names); + parentScope = ScopeAnalyzer.getParentScope(parentScope); + } + } + + private addDirectVariableNames(scope: Node, names: Set): void { + scope.forEachChild((child) => { + this.addChildVariableNames(child, names); + }); + } + + private addChildVariableNames(child: Node, names: Set): void { + if (Node.isVariableStatement(child)) { + child.getDeclarations().forEach((declaration) => { + this.addVariableNameIfExists(declaration, names); + }); + } else if (Node.isVariableDeclaration(child)) { + this.addVariableNameIfExists(child, names); + } else if (Node.isParameterDeclaration(child)) { + this.addParameterNameIfExists(child, names); + } + } + + private addNameIfIdentifier(node: Node, names: Set): void { + if (Node.isIdentifier(node)) { + names.add(node.getText()); + } + } + private addVariableNameIfExists(node: Node, names: Set): void { if (Node.isVariableDeclaration(node)) { - const nameNode = node.getNameNode(); - if (Node.isIdentifier(nameNode)) { - names.add(nameNode.getText()); - } + this.addNameIfIdentifier(node.getNameNode(), names); } } private addParameterNameIfExists(node: Node, names: Set): void { if (Node.isParameterDeclaration(node)) { - const nameNode = node.getNameNode(); - if (Node.isIdentifier(nameNode)) { - names.add(nameNode.getText()); - } + this.addNameIfIdentifier(node.getNameNode(), names); } } @@ -69,16 +96,13 @@ export class VariableNameValidator { } private processParameters(functionNode: Node, names: Set): void { - const parameters = this.getParametersFromFunction(functionNode); - for (const param of parameters) { + for (const param of this.getParametersFromFunction(functionNode)) { this.processParameter(param, names); } } private getParametersFromFunction(functionNode: Node): ParameterDeclaration[] { - if (Node.isFunctionDeclaration(functionNode) || Node.isMethodDeclaration(functionNode) || - Node.isArrowFunction(functionNode) || Node.isFunctionExpression(functionNode) || - Node.isConstructorDeclaration(functionNode)) { + if (this.isFunctionNode(functionNode)) { return (functionNode as FunctionLikeNode).getParameters(); } return []; @@ -87,54 +111,27 @@ export class VariableNameValidator { private processParameter(param: ParameterDeclaration, names: Set): void { const nameNode = param.getNameNode(); if (Node.isIdentifier(nameNode)) { - names.add(nameNode.getText()); + this.addNameIfIdentifier(nameNode, names); } else { - this.addDestructuredParameterNames(nameNode, names); - } - } - - private addDestructuredParameterNames(nameNode: Node, names: Set): void { - if (Node.isObjectBindingPattern(nameNode)) { - this.addObjectBindingNames(nameNode, names); - } else if (Node.isArrayBindingPattern(nameNode)) { - this.addArrayBindingNames(nameNode, names); + this.addBindingPatternNames(nameNode, names); } } - private addObjectBindingNames(nameNode: Node, names: Set): void { - if (Node.isObjectBindingPattern(nameNode)) { - this.processBindingElements(nameNode, names); - } - } - - private processBindingElements(nameNode: ObjectBindingPattern, names: Set): void { - nameNode.getElements().forEach((element: BindingElement | OmittedExpression) => { - this.processBindingElement(element, names); - }); - } - - private processBindingElement(element: BindingElement | OmittedExpression, names: Set): void { - if (!Node.isBindingElement(element)) return; - const elementName = element.getNameNode(); - if (Node.isIdentifier(elementName)) { - names.add(elementName.getText()); - } - } - - private addArrayBindingNames(nameNode: Node, names: Set): void { - if (Node.isArrayBindingPattern(nameNode)) { - nameNode.getElements().forEach((element: BindingElement | OmittedExpression) => { - this.processArrayBindingElement(element, names); + private addBindingPatternNames(pattern: Node, names: Set): void { + if (Node.isObjectBindingPattern(pattern)) { + pattern.getElements().forEach(element => { + this.addBindingElementName(element, names); + }); + } else if (Node.isArrayBindingPattern(pattern)) { + pattern.getElements().forEach(element => { + this.addBindingElementName(element, names); }); } } - private processArrayBindingElement(element: BindingElement | OmittedExpression, names: Set): void { + private addBindingElementName(element: BindingElement | OmittedExpression, names: Set): void { if (element && Node.isBindingElement(element)) { - const elementName = element.getNameNode(); - if (Node.isIdentifier(elementName)) { - names.add(elementName.getText()); - } + this.addNameIfIdentifier(element.getNameNode(), names); } } } \ No newline at end of file diff --git a/tests/fixtures/commands/rename/child-scope-conflict.expected.err b/tests/fixtures/commands/rename/child-scope-conflict.expected.err new file mode 100644 index 0000000..487c3d9 --- /dev/null +++ b/tests/fixtures/commands/rename/child-scope-conflict.expected.err @@ -0,0 +1 @@ +Variable name 'x' already exists in this scope. Please choose a different name. \ No newline at end of file diff --git a/tests/fixtures/commands/rename/child-scope-conflict.expected.ts b/tests/fixtures/commands/rename/child-scope-conflict.expected.ts new file mode 100644 index 0000000..d25b755 --- /dev/null +++ b/tests/fixtures/commands/rename/child-scope-conflict.expected.ts @@ -0,0 +1,14 @@ +/** + * @description Test rename with child scope conflict - renaming y to x should fail + * @command refakts rename "[child-scope-conflict.input.ts 9:15-9:16]" --to "x" + * @expect-error true + */ +function f() { + const x = 1; + { + const y = 2; + { + return x; + } + } +} \ No newline at end of file diff --git a/tests/fixtures/commands/rename/child-scope-conflict.input.ts b/tests/fixtures/commands/rename/child-scope-conflict.input.ts new file mode 100644 index 0000000..d25b755 --- /dev/null +++ b/tests/fixtures/commands/rename/child-scope-conflict.input.ts @@ -0,0 +1,14 @@ +/** + * @description Test rename with child scope conflict - renaming y to x should fail + * @command refakts rename "[child-scope-conflict.input.ts 9:15-9:16]" --to "x" + * @expect-error true + */ +function f() { + const x = 1; + { + const y = 2; + { + return x; + } + } +} \ No newline at end of file diff --git a/tests/unit/services/variable-name-validator.test.ts b/tests/unit/services/variable-name-validator.test.ts index b938f86..e0f2a47 100644 --- a/tests/unit/services/variable-name-validator.test.ts +++ b/tests/unit/services/variable-name-validator.test.ts @@ -13,166 +13,114 @@ describe('VariableNameValidator', () => { describe('generateUniqueName', () => { it('should return base name when no conflict exists', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.simpleFunction); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const uniqueName = validator.generateUniqueName('newVar', functionBlock); - - expect(uniqueName).toBe('newVar'); + expect(validator.generateUniqueName('newVar', + project.createSourceFile('test.ts', fixtures.simpleFunction) + .getDescendantsOfKind(SyntaxKind.Block)[0] + )).toBe('newVar'); }); it('should throw error when variable name already exists', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.simpleFunction); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - expect(() => { - validator.generateUniqueName('result', functionBlock); + validator.generateUniqueName('result', + project.createSourceFile('test.ts', fixtures.simpleFunction) + .getDescendantsOfKind(SyntaxKind.Block)[0] + ); }).toThrow("Variable name 'result' already exists in this scope. Please choose a different name."); }); it('should throw error when parameter name conflicts', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.functionWithParameters); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - expect(() => { - validator.generateUniqueName('data', functionBlock); + validator.generateUniqueName('data', project.createSourceFile('test1.ts', fixtures.functionWithParameters) + .getDescendantsOfKind(SyntaxKind.Block)[0]); }).toThrow("Variable name 'data' already exists in this scope. Please choose a different name."); expect(() => { - validator.generateUniqueName('options', functionBlock); + validator.generateUniqueName('options', project.createSourceFile('test2.ts', fixtures.functionWithParameters) + .getDescendantsOfKind(SyntaxKind.Block)[0]); }).toThrow("Variable name 'options' already exists in this scope. Please choose a different name."); }); it('should detect conflicts in nested scopes', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.nestedScopes); - const outerBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; // Outer function block - expect(() => { - validator.generateUniqueName('innerVar', outerBlock); + validator.generateUniqueName('innerVar', + project.createSourceFile('test.ts', fixtures.nestedScopes) + .getDescendantsOfKind(SyntaxKind.Block)[0] + ); }).toThrow("Variable name 'innerVar' already exists in this scope. Please choose a different name."); }); it('should detect conflicts in class method scope', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.classScope); - const methodBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[1]; // Calculate method block - expect(() => { - validator.generateUniqueName('input', methodBlock); + validator.generateUniqueName('input', project.createSourceFile('test3.ts', fixtures.classScope) + .getDescendantsOfKind(SyntaxKind.Block)[1]); }).toThrow("Variable name 'input' already exists in this scope. Please choose a different name."); expect(() => { - validator.generateUniqueName('result', methodBlock); + validator.generateUniqueName('result', project.createSourceFile('test4.ts', fixtures.classScope) + .getDescendantsOfKind(SyntaxKind.Block)[1]); }).toThrow("Variable name 'result' already exists in this scope. Please choose a different name."); }); it('should allow names that do not conflict in empty scope', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.emptyScope); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const uniqueName1 = validator.generateUniqueName('anyName', functionBlock); - const uniqueName2 = validator.generateUniqueName('anotherName', functionBlock); - - expect(uniqueName1).toBe('anyName'); - expect(uniqueName2).toBe('anotherName'); + expect(validator.generateUniqueName('anyName', project.createSourceFile('test5.ts', fixtures.emptyScope) + .getDescendantsOfKind(SyntaxKind.Block)[0])).toBe('anyName'); + expect(validator.generateUniqueName('anotherName', project.createSourceFile('test6.ts', fixtures.emptyScope) + .getDescendantsOfKind(SyntaxKind.Block)[0])).toBe('anotherName'); }); }); describe('getExistingVariableNames', () => { it('should collect all variable names in simple function', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.simpleFunction); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const existingNames = validator.getExistingVariableNames(functionBlock); - - expect(existingNames).toEqual(new Set(['input', 'result', 'output'])); + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', fixtures.simpleFunction).getDescendantsOfKind(SyntaxKind.Block)[0])).toEqual(new Set(['input', 'result', 'output'])); }); it('should collect parameter names and variable names', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.functionWithParameters); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const existingNames = validator.getExistingVariableNames(functionBlock); - - expect(existingNames).toEqual(new Set(['data', 'options', 'callback', 'processed', 'config', 'item'])); + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', fixtures.functionWithParameters).getDescendantsOfKind(SyntaxKind.Block)[0])).toEqual(new Set(['data', 'options', 'callback', 'processed', 'config', 'item'])); }); it('should collect names from nested scopes', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.nestedScopes); - const outerBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const existingNames = validator.getExistingVariableNames(outerBlock); - - expect(existingNames).toEqual(new Set([ + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', fixtures.nestedScopes).getDescendantsOfKind(SyntaxKind.Block)[0])).toEqual(new Set([ 'param1', 'outerVar', 'param2', 'innerVar', 'anotherVar' ])); }); it('should handle destructuring parameters', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.destructuringParameters); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const existingNames = validator.getExistingVariableNames(functionBlock); - - expect(existingNames).toEqual(new Set([ + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', fixtures.destructuringParameters).getDescendantsOfKind(SyntaxKind.Block)[0])).toEqual(new Set([ 'url', 'method', 'options', 'normalizedUrl', 'upperMethod' ])); }); it('should collect names from arrow functions', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.arrowFunctions); - const outerBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const existingNames = validator.getExistingVariableNames(outerBlock); - - expect(existingNames).toEqual(new Set([ - 'items', 'filtered', 'transformed', 'item', 'converted' + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', fixtures.arrowFunctions).getDescendantsOfKind(SyntaxKind.Block)[0])).toEqual(new Set([ + 'items', 'filtered', 'transformed', 'item', 'converted', 'processor' ])); }); it('should return empty set for scope with no variables', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.emptyScope); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const existingNames = validator.getExistingVariableNames(functionBlock); - - expect(existingNames).toEqual(new Set()); + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', fixtures.emptyScope).getDescendantsOfKind(SyntaxKind.Block)[0])).toEqual(new Set()); }); it('should collect module-level variables', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.moduleLevel); - - const existingNames = validator.getExistingVariableNames(sourceFile); - - expect(existingNames).toEqual(new Set(['globalVar', 'anotherGlobal', 'mutableGlobal'])); + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', fixtures.moduleLevel))).toEqual(new Set(['globalVar', 'anotherGlobal', 'mutableGlobal'])); }); }); describe('edge cases and error conditions', () => { it('should handle class constructor parameters', () => { - const sourceFile = project.createSourceFile('test.ts', fixtures.classScope); - const constructorBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const existingNames = validator.getExistingVariableNames(constructorBlock); - - expect(existingNames).toEqual(new Set(['initialValue'])); + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', fixtures.classScope).getDescendantsOfKind(SyntaxKind.Block)[0])).toEqual(new Set(['initialValue'])); }); it('should handle function expressions', () => { - const sourceFile = project.createSourceFile('test.ts', ` + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', ` const fn = function(param: string) { const local = param.toUpperCase(); return local; }; - `); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const existingNames = validator.getExistingVariableNames(functionBlock); - - expect(existingNames).toEqual(new Set(['param', 'local'])); + `).getDescendantsOfKind(SyntaxKind.Block)[0])).toEqual(new Set(['param', 'local', 'fn'])); }); it('should handle complex nested structures', () => { - const sourceFile = project.createSourceFile('test.ts', ` + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', ` function complex(a: number) { const b = a + 1; @@ -188,28 +136,18 @@ describe('VariableNameValidator', () => { const e = b - 1; return e; } - `); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const existingNames = validator.getExistingVariableNames(functionBlock); - - expect(existingNames).toEqual(new Set(['a', 'b', 'c', 'i', 'd', 'e'])); + `).getDescendantsOfKind(SyntaxKind.Block)[0])).toEqual(new Set(['a', 'b', 'c', 'i', 'd', 'e'])); }); it('should handle variable naming edge cases', () => { - const sourceFile = project.createSourceFile('test.ts', ` + expect(validator.getExistingVariableNames(project.createSourceFile('test.ts', ` function test() { const _private = 1; const $jquery = 2; const var123 = 3; - const αβγ = 4; // Unicode identifiers + const αβγ = 4; } - `); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - - const existingNames = validator.getExistingVariableNames(functionBlock); - - expect(existingNames).toEqual(new Set(['_private', '$jquery', 'var123', 'αβγ'])); + `).getDescendantsOfKind(SyntaxKind.Block)[0])).toEqual(new Set(['_private', '$jquery', 'var123', 'αβγ'])); }); it('should throw error for case-sensitive conflicts', () => { @@ -219,18 +157,14 @@ describe('VariableNameValidator', () => { const MYVAR = 2; } `); - const functionBlock = sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]; - expect(() => { - validator.generateUniqueName('myVar', functionBlock); + validator.generateUniqueName('myVar', sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]); }).toThrow("Variable name 'myVar' already exists in this scope. Please choose a different name."); expect(() => { - validator.generateUniqueName('MYVAR', functionBlock); + validator.generateUniqueName('MYVAR', sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0]); }).toThrow("Variable name 'MYVAR' already exists in this scope. Please choose a different name."); - - const uniqueName = validator.generateUniqueName('myvar', functionBlock); - expect(uniqueName).toBe('myvar'); + expect(validator.generateUniqueName('myvar', sourceFile.getDescendantsOfKind(SyntaxKind.Block)[0])).toBe('myvar'); }); }); }); \ No newline at end of file