ludo-technologies / ludo-technologies/polyscan

[BUG][auto] js/js-nested-function-declaration-dropped-or-duplicated: nested function declarations are reported twice (name, name_<line>) or not at all

Open
#135 0 comments 0 reactions 0 assignees View on GitHub
auto-filed bug good first issue P1
Dominant language
Go
Stars
12
Forks
7
Avg merge
5h 46m
Merged PRs (30d)
49

Description

## Bug

In JavaScript, a named function declaration nested inside another function is reported wrongly by the complexity analysis:

- nested inside an **arrow function, function expression, method or IIFE** it is emitted **twice**, once as `name` and once as `name_`, with identical location and metrics;
- nested inside a **function declaration** it is **not emitted at all**.

## Repro

`b.js`:

```js
const arrow = () => {
function inArrow(x) { if (x) { return 1; } return 0; }
return inArrow(1);
};
class K {
m() {
function inMethod(x) { if (x) { return 1; } return 0; }
return inMethod(1);
}
}
function named() {
function inNamed(x) { if (x) { return 1; } return 0; }
return inNamed(1);
}
```

```
polyscan analyze --format json --select complexity . | jq -c '.complexity.functions[] | {name,start_line,end_line,cc:.metrics.complexity}'
```

## Expected

One entry each for `inArrow`, `inMethod` and `inNamed` (plus `anonymous_1`, `m`, `named`).

## Actual

```
{"name":"inArrow","start_line":2,"end_line":2,"cc":2}
{"name":"inArrow_2","start_line":2,"end_line":2,"cc":2}
{"name":"inMethod","start_line":7,"end_line":7,"cc":2}
{"name":"inMethod_7","start_line":7,"end_line":7,"cc":2}
{"name":"anonymous_1","start_line":1,"end_line":4,"cc":1}
{"name":"m","start_line":6,"end_line":9,"cc":1}
{"name":"named","start_line":11,"end_line":15,"cc":1}
```

`inNamed` is missing; `inArrow` and `inMethod` are duplicated. In the audited repo the duplicate shows up as `show` and `show_5` at `service/templates/analyze/report.js:5-13` (both `complexity: 3`), which also double-counts the function in `summary.total_functions`.

## Root cause (from reading `internal/js/analyzer/cfg_builder.go` and `internal/js/parser/ast.go`)

1. `CFGBuilder.processStatement` builds a nested function with a fresh `funcBuilder` and stores only `funcCFG` in `b.functionCFGs`; the nested builder's own `functionCFGs` (declarations inside that function) are discarded. The nested node is nevertheless appended to the block's `Statements`, so `BuildAll` marks its location as discovered and never rebuilds it. That drops `inNamed`.
2. `Node.Walk` visits the same child twice because `ast_builder.go` puts statements in both `Children` (via `AddChild`) and `Body` (e.g. lines 160-161). `BuildAll`'s discovery walk records `discoveredLocations` only before walking, so a function first found during the walk (nested in an expression) is visited again on the second pass, its name already exists in `allCFGs`, and it gets the `name_` suffix. That duplicates `inArrow` / `inMethod`.

## Priority

P1: wrong output (missing or duplicated function) on a common construct (nested function declarations), and it skews `summary.total_functions` and the complexity score.

- polyscan version: `polyscan version 0.3.3-1-g1b093ff`
- Found via the FP-audit skill in repo `ludo-technologies/pyscn@2c3d8ac4b3b6d8f1fb1790a47fcaab0079608017`

Contributor guide

Open the contributing guide

Research direction

Start with internal/js/analyzer/cfg_builder.go, especially processStatement and BuildAll, then inspect internal/js/parser/ast.go and ast_builder.go for Node.Walk behavior. Run the provided polyscan analyze command against the b.js reproduction. Done means one entry each for inArrow, inMethod, and inNamed, with summary.total_functions no longer double-counting nested functions.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, javascript
Domain
devtools
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.