Skip to content

Commit c6cf775

Browse files
fix(health): count TS object-literal shorthand properties as reads (#759)
* fix(health): count TS object-literal shorthand properties as reads The ts_js def/use dialect only knew shorthand_property_identifier_pattern (the destructuring binder) and never counted shorthand_property_identifier (an object-literal read like { days, limit }) as a use. Extract Method IN/OUT inference on TS/JS could therefore omit a variable a candidate span consumes via shorthand, producing a wrong parameter list, and params_unused reported false positives for such parameters. Grammar introspection across the typescript, tsx, and javascript packs confirms the non-pattern kind never appears in a write-target position, so extending identifier_kinds adds reads without risking false defs. * fix(upgrade): sync bundled changelog with docs
1 parent dbc92a6 commit c6cf775

2 files changed

Lines changed: 62 additions & 0 deletions

File tree

packages/core/src/repowise/core/analysis/health/dataflow/dialects/ts_js.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ class TsJsDefUseDialect(BaseDefUseDialect):
6060
language = "typescript"
6161
member_access_kinds = frozenset({"member_expression"})
6262
keyword_kinds = frozenset() # object-property keys are not variable reads.
63+
# ``shorthand_property_identifier`` is an object-literal read (``{ days }``
64+
# reads the local ``days``). Its ``_pattern`` twin is the destructuring
65+
# *binder*; the grammars (ts / tsx / js) never emit the non-pattern kind in
66+
# a write-target position, so counting it as an identifier only adds reads.
67+
identifier_kinds = frozenset({"identifier", "shorthand_property_identifier"})
6368

6469
def _is_scope_boundary(self, node: Node) -> bool:
6570
return node.type in _SCOPE_BOUNDARIES

tests/unit/health/test_dataflow_langs.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,63 @@ def test_js_def_use_destructuring():
300300
assert {"items", "factor"} <= defs
301301

302302

303+
def test_ts_shorthand_object_literal_reads():
304+
# ``{ days, limit }`` in a return reads both locals; ``shorthand_property_
305+
# identifier`` must count as a use even though its ``_pattern`` twin is a
306+
# destructuring def.
307+
fn = _first(
308+
"typescript",
309+
"""
310+
function build(days: number, limit: number) {
311+
const payload = compute(days);
312+
return { days, limit, payload };
313+
}
314+
""",
315+
)
316+
uses = _use_names(fn)
317+
assert {"days", "limit", "payload"} <= uses
318+
# Reads never become defs.
319+
defs = _def_names(fn)
320+
assert defs == {"days", "limit", "payload"} # params + the one declaration
321+
322+
323+
def test_ts_shorthand_mixed_with_destructuring_same_statement():
324+
# LHS shorthand is a destructuring def; RHS shorthand is a read -- both on
325+
# one statement.
326+
fn = _first(
327+
"typescript",
328+
"""
329+
function f(source: { a: number }, b: number) {
330+
const { a } = { ...source, b };
331+
return a;
332+
}
333+
""",
334+
)
335+
assert "a" in _def_names(fn)
336+
# On the destructuring line itself only ``a`` binds; the RHS shorthand
337+
# ``b`` stays a read (its sole def is the parameter seed).
338+
destructure_defs = {d.var for d in fn.def_use.definitions if d.line == 3}
339+
assert destructure_defs == {"a"}
340+
uses = _use_names(fn)
341+
assert {"source", "b"} <= uses
342+
343+
344+
def test_js_shorthand_and_spread_reads():
345+
# Plain JS pack: spread reads its identifier, shorthand reads its local.
346+
fn = _first(
347+
"javascript",
348+
"""
349+
function merge(base, extra) {
350+
const combined = { ...base, extra };
351+
return combined;
352+
}
353+
""",
354+
)
355+
uses = _use_names(fn)
356+
assert {"base", "extra"} <= uses
357+
assert "extra" not in {d.var for d in fn.def_use.definitions if d.line > fn.start_line}
358+
359+
303360
_TS_PROCESS = """
304361
function process(records: number[], threshold: number): [number, number] {
305362
const results: number[] = [];

0 commit comments

Comments
 (0)