From 487ec28b1c3a24c91ef3f29edceb080e933a8b75 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilev Date: Fri, 27 Oct 2023 12:39:25 +0800 Subject: [PATCH] fixed name declared twice --- src/find_definitions.js | 35 +++++++++++++++++++++++---------- src/utils.js | 7 +++++++ test/test.js | 43 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/find_definitions.js b/src/find_definitions.js index ed0ea12..f6989ae 100644 --- a/src/find_definitions.js +++ b/src/find_definitions.js @@ -1,7 +1,9 @@ // TODO rename to analyze.js -import {set_push, set_diff, set_union, map_object, map_find, uniq} from './utils.js' -import {collect_destructuring_identifiers, collect_imports, ancestry, find_leaf} from './ast_utils.js' +import {set_push, set_diff, set_union, map_object, map_find, uniq, uniq_by} + from './utils.js' +import {collect_destructuring_identifiers, collect_imports, ancestry, find_leaf} + from './ast_utils.js' const map_find_definitions = (nodes, mapper) => { const result = nodes.map(mapper) @@ -305,9 +307,27 @@ const analyze_await = (node, is_async_context = true) => { return result ?? [] } +const find_duplicates = names => { + const duplicates = names.filter((n, i) => + names.find((name, j) => name.value == n.value && j < i) != null + ) + const problems = duplicates.map(d => ({ + index: d.index, + length: d.length, + message: `Identifier '${d.value}' has already been declared`, + })) + return problems +} + const named_declared_once = node => { return collect_problems(node, null, (node, cxt) => { - if(node.type == 'do') { + if(node.type == 'function_expr') { + const names = collect_destructuring_identifiers(node.function_args) + return { + context: uniq_by(names, n => n.value), + problems: find_duplicates(names), + } + } else if(node.type == 'do') { const names = node .children .map(c => { @@ -327,14 +347,9 @@ const named_declared_once = node => { }) .flat() .filter(n => n != null) - const duplicates = names.filter((n, i) => - names.find((name, j) => name.value == n.value && j < i) != null + const problems = find_duplicates( + [...(cxt ?? []), ...names] ) - const problems = duplicates.map(d => ({ - index: d.index, - length: d.length, - message: `Identifier '${d.value}' has already been declared`, - })) return {context: null, problems} } else { return {context: null, problems: null} diff --git a/src/utils.js b/src/utils.js index 1ec6bf0..2ff47aa 100644 --- a/src/utils.js +++ b/src/utils.js @@ -53,6 +53,13 @@ export const zip = (x,y) => { export const uniq = arr => [...new Set(arr)] +export const uniq_by = (arr, mapper) => [ + ...new Map( + arr.map(e => [mapper(e), e]) + ) + .values() +] + export const collect_nodes_with_parents = new Function('node', 'pred', ` const result = [] diff --git a/test/test.js b/test/test.js index 4613a20..2fa3a86 100644 --- a/test/test.js +++ b/test/test.js @@ -1269,6 +1269,49 @@ export const tests = [ ] ) }), + + test('identifier has already been declared in fn arg', () => { + const code = ` + function foo(x) { + const x = 1 + } + + ` + const i = test_initial_state(code) + assert_equal(i.parse_result.ok, false) + assert_equal( + i.parse_result.problems, + [ + { + index: code.indexOf('x = 1'), + length: 1, + message: "Identifier 'x' has already been declared", + module: '', + } + ] + ) + }), + + test('identifier has been declared twice in args', () => { + const code = ` + function foo({x,x}) { + } + + ` + const i = test_initial_state(code) + assert_equal(i.parse_result.ok, false) + assert_equal( + i.parse_result.problems, + [ + { + index: code.indexOf('x}'), + length: 1, + message: "Identifier 'x' has already been declared", + module: '', + } + ] + ) + }), test('identifier has already been declared fn decl', () => { const code = `