Skip to content

Commit 1ddc27e

Browse files
authored
fix: do not stack overflow when namespace exports itself or an ancestor (#706)
1 parent ceea694 commit 1ddc27e

File tree

2 files changed

+311
-5
lines changed

2 files changed

+311
-5
lines changed

src/parser.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -733,11 +733,26 @@ impl<'a> DocParser<'a> {
733733
fn get_doc_for_ts_namespace(
734734
&self,
735735
module_info: &EsModuleInfo,
736-
symbol: &Symbol,
736+
ns_symbol: &Symbol,
737737
ts_module: &TsModuleDecl,
738738
full_range: &SourceRange,
739739
) -> Option<DocNode> {
740-
let first_ns_decl = symbol
740+
fn symbol_in_ancestors(
741+
id: UniqueSymbolId,
742+
parent: &Symbol,
743+
module_info: &EsModuleInfo,
744+
) -> bool {
745+
let mut current_symbol = Some(parent);
746+
while let Some(symbol) = current_symbol.take() {
747+
if symbol.unique_id() == id {
748+
return true;
749+
}
750+
current_symbol = symbol.parent_id().and_then(|s| module_info.symbol(s));
751+
}
752+
false
753+
}
754+
755+
let first_ns_decl = ns_symbol
741756
.decls()
742757
.iter()
743758
.filter_map(|d| {
@@ -766,7 +781,7 @@ impl<'a> DocParser<'a> {
766781
let mut elements = Vec::new();
767782
let mut handled_symbols = HashSet::new();
768783

769-
for (export_name, export_symbol_id) in symbol.exports() {
784+
for (export_name, export_symbol_id) in ns_symbol.exports() {
770785
handled_symbols.insert(UniqueSymbolId::new(
771786
module_info.module_id(),
772787
*export_symbol_id,
@@ -778,7 +793,12 @@ impl<'a> DocParser<'a> {
778793
let original_range = &export_symbol.decls().first().unwrap().range;
779794

780795
for definition in definitions {
781-
handled_symbols.insert(definition.symbol.unique_id());
796+
let definition_id = definition.symbol.unique_id();
797+
if symbol_in_ancestors(definition_id, ns_symbol, module_info) {
798+
continue;
799+
}
800+
801+
handled_symbols.insert(definition_id);
782802

783803
let maybe_docs = self.docs_for_maybe_node(
784804
definition.module,
@@ -799,7 +819,7 @@ impl<'a> DocParser<'a> {
799819
}
800820

801821
let is_ambient = elements.is_empty() && !module_has_import(module_info);
802-
for child_id in symbol.child_ids() {
822+
for child_id in ns_symbol.child_ids() {
803823
let unique_id = UniqueSymbolId::new(module_info.module_id(), child_id);
804824
if !handled_symbols.insert(unique_id) {
805825
continue; // already handled
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
# mod.ts
2+
/** Description. */
3+
export declare namespace a {
4+
/** Description. */
5+
const a: string;
6+
export { a };
7+
}
8+
9+
/** Description. */
10+
export declare namespace b {
11+
/** Description. */
12+
export class b {}
13+
}
14+
15+
/** Description. */
16+
export namespace c {
17+
export { c };
18+
}
19+
20+
/** Description. */
21+
export namespace d {
22+
/** Description. */
23+
export namespace e {
24+
export { d };
25+
}
26+
}
27+
28+
/** Description. */
29+
export namespace f {
30+
/** Description. */
31+
export namespace g {
32+
/** Description. */
33+
const value: f;
34+
export { value };
35+
}
36+
}
37+
38+
# output.txt
39+
Defined in file:///mod.ts:2:1
40+
41+
namespace a
42+
Description.
43+
44+
const a: string
45+
Description.
46+
47+
Defined in file:///mod.ts:9:1
48+
49+
namespace b
50+
Description.
51+
52+
class b
53+
Description.
54+
55+
Defined in file:///mod.ts:15:1
56+
57+
namespace c
58+
Description.
59+
60+
61+
Defined in file:///mod.ts:20:1
62+
63+
namespace d
64+
Description.
65+
66+
namespace e
67+
Description.
68+
69+
Defined in file:///mod.ts:28:1
70+
71+
namespace f
72+
Description.
73+
74+
namespace g
75+
Description.
76+
77+
78+
# output.json
79+
[
80+
{
81+
"name": "a",
82+
"isDefault": false,
83+
"location": {
84+
"filename": "file:///mod.ts",
85+
"line": 2,
86+
"col": 0,
87+
"byteIndex": 20
88+
},
89+
"declarationKind": "export",
90+
"jsDoc": {
91+
"doc": "Description."
92+
},
93+
"kind": "namespace",
94+
"namespaceDef": {
95+
"elements": [
96+
{
97+
"name": "a",
98+
"isDefault": false,
99+
"location": {
100+
"filename": "file:///mod.ts",
101+
"line": 4,
102+
"col": 8,
103+
"byteIndex": 79
104+
},
105+
"declarationKind": "export",
106+
"jsDoc": {
107+
"doc": "Description."
108+
},
109+
"kind": "variable",
110+
"variableDef": {
111+
"tsType": {
112+
"repr": "string",
113+
"kind": "keyword",
114+
"keyword": "string"
115+
},
116+
"kind": "const"
117+
}
118+
}
119+
]
120+
}
121+
},
122+
{
123+
"name": "b",
124+
"isDefault": false,
125+
"location": {
126+
"filename": "file:///mod.ts",
127+
"line": 9,
128+
"col": 0,
129+
"byteIndex": 129
130+
},
131+
"declarationKind": "export",
132+
"jsDoc": {
133+
"doc": "Description."
134+
},
135+
"kind": "namespace",
136+
"namespaceDef": {
137+
"elements": [
138+
{
139+
"name": "b",
140+
"isDefault": false,
141+
"location": {
142+
"filename": "file:///mod.ts",
143+
"line": 11,
144+
"col": 2,
145+
"byteIndex": 182
146+
},
147+
"declarationKind": "export",
148+
"jsDoc": {
149+
"doc": "Description."
150+
},
151+
"kind": "class",
152+
"classDef": {
153+
"isAbstract": false,
154+
"constructors": [],
155+
"properties": [],
156+
"indexSignatures": [],
157+
"methods": [],
158+
"extends": null,
159+
"implements": [],
160+
"typeParams": [],
161+
"superTypeParams": []
162+
}
163+
}
164+
]
165+
}
166+
},
167+
{
168+
"name": "c",
169+
"isDefault": false,
170+
"location": {
171+
"filename": "file:///mod.ts",
172+
"line": 15,
173+
"col": 0,
174+
"byteIndex": 223
175+
},
176+
"declarationKind": "export",
177+
"jsDoc": {
178+
"doc": "Description."
179+
},
180+
"kind": "namespace",
181+
"namespaceDef": {
182+
"elements": []
183+
}
184+
},
185+
{
186+
"name": "d",
187+
"isDefault": false,
188+
"location": {
189+
"filename": "file:///mod.ts",
190+
"line": 20,
191+
"col": 0,
192+
"byteIndex": 285
193+
},
194+
"declarationKind": "export",
195+
"jsDoc": {
196+
"doc": "Description."
197+
},
198+
"kind": "namespace",
199+
"namespaceDef": {
200+
"elements": [
201+
{
202+
"name": "e",
203+
"isDefault": false,
204+
"location": {
205+
"filename": "file:///mod.ts",
206+
"line": 22,
207+
"col": 2,
208+
"byteIndex": 330
209+
},
210+
"declarationKind": "export",
211+
"jsDoc": {
212+
"doc": "Description."
213+
},
214+
"kind": "namespace",
215+
"namespaceDef": {
216+
"elements": []
217+
}
218+
}
219+
]
220+
}
221+
},
222+
{
223+
"name": "f",
224+
"isDefault": false,
225+
"location": {
226+
"filename": "file:///mod.ts",
227+
"line": 28,
228+
"col": 0,
229+
"byteIndex": 396
230+
},
231+
"declarationKind": "export",
232+
"jsDoc": {
233+
"doc": "Description."
234+
},
235+
"kind": "namespace",
236+
"namespaceDef": {
237+
"elements": [
238+
{
239+
"name": "g",
240+
"isDefault": false,
241+
"location": {
242+
"filename": "file:///mod.ts",
243+
"line": 30,
244+
"col": 2,
245+
"byteIndex": 441
246+
},
247+
"declarationKind": "export",
248+
"jsDoc": {
249+
"doc": "Description."
250+
},
251+
"kind": "namespace",
252+
"namespaceDef": {
253+
"elements": [
254+
{
255+
"name": "value",
256+
"isDefault": false,
257+
"location": {
258+
"filename": "file:///mod.ts",
259+
"line": 32,
260+
"col": 10,
261+
"byteIndex": 496
262+
},
263+
"declarationKind": "export",
264+
"jsDoc": {
265+
"doc": "Description."
266+
},
267+
"kind": "variable",
268+
"variableDef": {
269+
"tsType": {
270+
"repr": "f",
271+
"kind": "typeRef",
272+
"typeRef": {
273+
"typeParams": null,
274+
"typeName": "f"
275+
}
276+
},
277+
"kind": "const"
278+
}
279+
}
280+
]
281+
}
282+
}
283+
]
284+
}
285+
}
286+
]

0 commit comments

Comments
 (0)