@@ -169,35 +169,154 @@ export default function transform(root: SgRoot<Js>): string | null {
169
169
/**
170
170
* Helper function to determine if a parameter is likely a file descriptor
171
171
* rather than a file path string.
172
- * @todo (@AugustinMauroy): use more AST than regex for this function.
173
172
* @param param The parameter to check (e.g., 'fd').
174
173
* @param rootNode The root node of the AST to search within.
175
174
*/
176
175
function isLikelyFileDescriptor ( param : string , rootNode : SgNode < Js > ) : boolean {
177
176
// Check if it's a numeric literal
178
177
if ( / ^ \d + $ / . test ( param . trim ( ) ) ) return true ;
179
178
180
- // Simple check: if the parameter appears in an `openSync` assignment, it's likely a file descriptor
181
- const sourceText = rootNode . text ( ) ;
182
-
183
- // Look for patterns like "const fd = openSync(...)" or "const fd = fs.openSync(...)"
184
- const openSyncPattern = new RegExp ( `(?:const|let|var)\\s+${ param } \\s*=\\s*(?:fs\\.)?openSync\\s*\\(` , 'g' ) ;
185
-
186
- if ( openSyncPattern . test ( sourceText ) ) return true ;
179
+ // Search for variable declarations that might assign a file descriptor
180
+ // such as `const fd = fs.openSync(...)` or `open(..., (err, fd) => ...)`
181
+ const variableDeclarators = rootNode . findAll ( {
182
+ rule : {
183
+ kind : "variable_declarator" ,
184
+ all : [
185
+ {
186
+ has : {
187
+ field : "name" ,
188
+ kind : "identifier" ,
189
+ regex : `^${ param } $`
190
+ }
191
+ } ,
192
+ {
193
+ has : {
194
+ field : "value" ,
195
+ kind : "call_expression" ,
196
+ has : {
197
+ field : "function" ,
198
+ kind : "member_expression" ,
199
+ all : [
200
+ {
201
+ has : {
202
+ field : "object" ,
203
+ kind : "identifier" ,
204
+ regex : "^fs$"
205
+ }
206
+ } ,
207
+ {
208
+ has : {
209
+ field : "property" ,
210
+ kind : "property_identifier" ,
211
+ regex : "^(open|openSync)$"
212
+ }
213
+ }
214
+ ]
215
+ }
216
+ }
217
+ }
218
+ ]
219
+ }
220
+ } ) ;
187
221
188
- // Look for patterns where the parameter is used in an open callback
189
- // This handles cases like: open('file', (err, fd) => { truncate(fd, ...) })
190
- const callbackPattern = new RegExp ( `\\(\\s*(?:err|error)\\s*,\\s*${ param } \\s*\\)\\s*=>` , 'g' ) ;
222
+ if ( variableDeclarators . length > 0 ) return true ;
191
223
192
- if ( callbackPattern . test ( sourceText ) ) return true ;
224
+ // Check if the parameter appears as a callback parameter in fs.open calls
225
+ // Pattern: open(..., (err, fd) => ...)
226
+ const callbackParameters = rootNode . findAll ( {
227
+ rule : {
228
+ kind : "call_expression" ,
229
+ all : [
230
+ {
231
+ has : {
232
+ field : "function" ,
233
+ kind : "identifier" ,
234
+ regex : "^open$"
235
+ }
236
+ } ,
237
+ {
238
+ has : {
239
+ field : "arguments" ,
240
+ kind : "arguments" ,
241
+ has : {
242
+ kind : "arrow_function" ,
243
+ has : {
244
+ field : "parameters" ,
245
+ kind : "formal_parameters" ,
246
+ has : {
247
+ // @ts -ignore - idk what happend here maybe a bug in infering `Js` type
248
+ kind : "required_parameter" ,
249
+ has : {
250
+ field : "pattern" ,
251
+ kind : "identifier" ,
252
+ regex : `^${ param } $`
253
+ }
254
+ }
255
+ }
256
+ }
257
+ }
258
+ }
259
+ ]
260
+ }
261
+ } ) ;
193
262
263
+ if ( callbackParameters . length > 0 ) return true ;
194
264
195
- // Look for function callback patterns
196
- const functionCallbackPattern = new RegExp ( `function\\s*\\(\\s*(?:err|error)\\s*,\\s*${ param } \\s*\\)` , 'g' ) ;
265
+ // Check for fs.open callback patterns as well
266
+ const fsOpenCallbacks = rootNode . findAll ( {
267
+ rule : {
268
+ kind : "call_expression" ,
269
+ all : [
270
+ {
271
+ has : {
272
+ field : "function" ,
273
+ kind : "member_expression" ,
274
+ all : [
275
+ {
276
+ has : {
277
+ field : "object" ,
278
+ kind : "identifier" ,
279
+ regex : "^fs$"
280
+ }
281
+ } ,
282
+ {
283
+ has : {
284
+ field : "property" ,
285
+ kind : "property_identifier" ,
286
+ regex : "^open$"
287
+ }
288
+ }
289
+ ]
290
+ }
291
+ } ,
292
+ {
293
+ has : {
294
+ field : "arguments" ,
295
+ kind : "arguments" ,
296
+ has : {
297
+ kind : "arrow_function" ,
298
+ has : {
299
+ field : "parameters" ,
300
+ kind : "formal_parameters" ,
301
+ has : {
302
+ // @ts -ignore - idk what happend here maybe a bug in infering `Js` type
303
+ kind : "required_parameter" ,
304
+ has : {
305
+ field : "pattern" ,
306
+ kind : "identifier" ,
307
+ regex : `^${ param } $`
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+ }
314
+ ]
315
+ }
316
+ } ) ;
197
317
198
- if ( functionCallbackPattern . test ( sourceText ) ) return true ;
318
+ if ( fsOpenCallbacks . length > 0 ) return true ;
199
319
200
- // Conservative approach: if we can't determine it's a file descriptor,
201
- // assume it's a file path to avoid breaking valid path-based truncate calls
320
+ // If we didn't find any indicators, assume it's not a file descriptor
202
321
return false ;
203
322
}
0 commit comments