Skip to content

Commit af3eb84

Browse files
authored
Merge pull request #25 from 100monkeys-ai/refactor/analyzer-check-expression-16546670221595898591
🧹 [Refactor check_expression for maintainability]
2 parents 176b278 + a714510 commit af3eb84

1 file changed

Lines changed: 125 additions & 100 deletions

File tree

foundry/client/src/migrate/analyzer.rs

Lines changed: 125 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -147,137 +147,162 @@ fn scan_statements(
147147
}
148148
}
149149
}
150-
151150
/// Check an expression tree for Node.js API patterns.
152151
fn check_expression(expr: &Expression<'_>, source: &str, detected: &mut Vec<DetectedApi>) {
153152
match expr {
154-
// require('fs'), require('child_process'), etc.
155153
Expression::CallExpression(call) => {
156-
if let Expression::Identifier(ident) = &call.callee {
157-
if ident.name == "require" {
158-
if let Some(Argument::StringLiteral(lit)) = call.arguments.first() {
159-
if let Some(api) = classify_import_specifier(lit.value.as_str()) {
160-
let line = line_number_at_offset(source, call.span.start);
161-
detected.push(api.with_line(line));
162-
}
163-
}
164-
}
165-
}
154+
check_call_expression(call, source, detected);
155+
}
156+
Expression::StaticMemberExpression(member) => {
157+
check_static_member_expression(member, source, detected);
158+
}
159+
Expression::Identifier(ident) => {
160+
check_identifier(ident, source, detected);
161+
}
162+
Expression::AssignmentExpression(assign) => {
163+
check_assignment_expression(assign, source, detected);
164+
}
165+
_ => {}
166+
}
167+
}
166168

167-
// Check for shimmable patterns: Buffer.from(), Buffer.alloc(), crypto.randomBytes()
168-
if let Expression::StaticMemberExpression(member) = &call.callee {
169-
let prop = member.property.name.as_str();
170-
if let Expression::Identifier(obj) = &member.object {
171-
let obj_name = obj.name.as_str();
172-
if obj_name == "Buffer" && (prop == "from" || prop == "alloc") {
173-
let line = line_number_at_offset(source, call.span.start);
174-
detected.push(DetectedApi {
175-
pattern: format!("Buffer.{prop}()"),
176-
line,
177-
compatibility: Compatibility::Shimmable,
178-
});
179-
}
180-
if obj_name == "crypto" && prop == "randomBytes" {
181-
let line = line_number_at_offset(source, call.span.start);
182-
detected.push(DetectedApi {
183-
pattern: "crypto.randomBytes()".into(),
184-
line,
185-
compatibility: Compatibility::Shimmable,
186-
});
187-
}
169+
fn check_call_expression(
170+
call: &oxc_ast::ast::CallExpression<'_>,
171+
source: &str,
172+
detected: &mut Vec<DetectedApi>,
173+
) {
174+
// require('fs'), require('child_process'), etc.
175+
if let Expression::Identifier(ident) = &call.callee {
176+
if ident.name == "require" {
177+
if let Some(Argument::StringLiteral(lit)) = call.arguments.first() {
178+
if let Some(api) = classify_import_specifier(lit.value.as_str()) {
179+
let line = line_number_at_offset(source, call.span.start);
180+
detected.push(api.with_line(line));
188181
}
189182
}
183+
}
184+
}
190185

191-
// process.exit()
192-
if let Expression::StaticMemberExpression(member) = &call.callee {
193-
if member.property.name == "exit" {
194-
if let Expression::Identifier(obj) = &member.object {
195-
if obj.name == "process" {
196-
let line = line_number_at_offset(source, call.span.start);
197-
detected.push(DetectedApi {
198-
pattern: "process.exit()".into(),
199-
line,
200-
compatibility: Compatibility::NeedsManualAttention,
201-
});
202-
}
203-
}
204-
}
186+
// Check for shimmable patterns: Buffer.from(), Buffer.alloc(), crypto.randomBytes()
187+
if let Expression::StaticMemberExpression(member) = &call.callee {
188+
let prop = member.property.name.as_str();
189+
if let Expression::Identifier(obj) = &member.object {
190+
let obj_name = obj.name.as_str();
191+
if obj_name == "Buffer" && (prop == "from" || prop == "alloc") {
192+
let line = line_number_at_offset(source, call.span.start);
193+
detected.push(DetectedApi {
194+
pattern: format!("Buffer.{prop}()"),
195+
line,
196+
compatibility: Compatibility::Shimmable,
197+
});
205198
}
206-
207-
// Recurse into call arguments
208-
for arg in &call.arguments {
209-
if let Argument::FunctionExpression(fn_expr) = arg {
210-
if let Some(body) = &fn_expr.body {
211-
scan_statements(&body.statements, source, detected);
212-
}
213-
}
214-
if let Argument::ArrowFunctionExpression(arrow) = arg {
215-
scan_statements(&arrow.body.statements, source, detected);
216-
}
199+
if obj_name == "crypto" && prop == "randomBytes" {
200+
let line = line_number_at_offset(source, call.span.start);
201+
detected.push(DetectedApi {
202+
pattern: "crypto.randomBytes()".into(),
203+
line,
204+
compatibility: Compatibility::Shimmable,
205+
});
217206
}
218207
}
208+
}
219209

220-
// process.env access (shimmable → Forge.env())
221-
Expression::StaticMemberExpression(member) if member.property.name == "env" => {
210+
// process.exit()
211+
if let Expression::StaticMemberExpression(member) = &call.callee {
212+
if member.property.name == "exit" {
222213
if let Expression::Identifier(obj) = &member.object {
223214
if obj.name == "process" {
224-
let line = line_number_at_offset(source, member.span.start);
215+
let line = line_number_at_offset(source, call.span.start);
225216
detected.push(DetectedApi {
226-
pattern: "process.env".into(),
217+
pattern: "process.exit()".into(),
227218
line,
228-
compatibility: Compatibility::Shimmable,
219+
compatibility: Compatibility::NeedsManualAttention,
229220
});
230221
}
231222
}
232223
}
233-
Expression::StaticMemberExpression(_) => {
234-
// __dirname, __filename as member access targets are covered below
235-
}
236-
Expression::StaticMemberExpression(_) => {}
224+
}
237225

238-
// Standalone identifiers: __dirname, __filename, Buffer (as global)
239-
Expression::Identifier(ident) => {
240-
let name = ident.name.as_str();
241-
match name {
242-
"__dirname" | "__filename" => {
243-
let line = line_number_at_offset(source, ident.span.start);
244-
detected.push(DetectedApi {
245-
pattern: name.to_string(),
246-
line,
247-
compatibility: Compatibility::NeedsManualAttention,
248-
});
249-
}
250-
"Buffer" => {
251-
let line = line_number_at_offset(source, ident.span.start);
252-
detected.push(DetectedApi {
253-
pattern: "Buffer (global)".into(),
254-
line,
255-
compatibility: Compatibility::NeedsManualAttention,
256-
});
257-
}
258-
_ => {}
226+
// Recurse into call arguments
227+
for arg in &call.arguments {
228+
if let Argument::FunctionExpression(fn_expr) = arg {
229+
if let Some(body) = &fn_expr.body {
230+
scan_statements(&body.statements, source, detected);
259231
}
260232
}
233+
if let Argument::ArrowFunctionExpression(arrow) = arg {
234+
scan_statements(&arrow.body.statements, source, detected);
235+
}
236+
}
237+
}
261238

262-
// module.exports (CJS export pattern)
263-
Expression::AssignmentExpression(assign) => {
264-
if let Some(member) = extract_member_pattern(&assign.left) {
265-
if member == "module.exports" {
266-
let line = line_number_at_offset(source, assign.span.start);
267-
detected.push(DetectedApi {
268-
pattern: "module.exports".into(),
269-
line,
270-
compatibility: Compatibility::NeedsManualAttention,
271-
});
272-
}
239+
fn check_static_member_expression(
240+
member: &oxc_ast::ast::StaticMemberExpression<'_>,
241+
source: &str,
242+
detected: &mut Vec<DetectedApi>,
243+
) {
244+
// process.env access (shimmable → Forge.env())
245+
if member.property.name == "env" {
246+
if let Expression::Identifier(obj) = &member.object {
247+
if obj.name == "process" {
248+
let line = line_number_at_offset(source, member.span.start);
249+
detected.push(DetectedApi {
250+
pattern: "process.env".into(),
251+
line,
252+
compatibility: Compatibility::Shimmable,
253+
});
273254
}
274-
check_expression(&assign.right, source, detected);
275255
}
256+
}
257+
// __dirname, __filename as member access targets are covered below
258+
}
276259

260+
fn check_identifier(
261+
ident: &oxc_ast::ast::IdentifierReference<'_>,
262+
source: &str,
263+
detected: &mut Vec<DetectedApi>,
264+
) {
265+
// Standalone identifiers: __dirname, __filename, Buffer (as global)
266+
let name = ident.name.as_str();
267+
match name {
268+
"__dirname" | "__filename" => {
269+
let line = line_number_at_offset(source, ident.span.start);
270+
detected.push(DetectedApi {
271+
pattern: name.to_string(),
272+
line,
273+
compatibility: Compatibility::NeedsManualAttention,
274+
});
275+
}
276+
"Buffer" => {
277+
let line = line_number_at_offset(source, ident.span.start);
278+
detected.push(DetectedApi {
279+
pattern: "Buffer (global)".into(),
280+
line,
281+
compatibility: Compatibility::NeedsManualAttention,
282+
});
283+
}
277284
_ => {}
278285
}
279286
}
280287

288+
fn check_assignment_expression(
289+
assign: &oxc_ast::ast::AssignmentExpression<'_>,
290+
source: &str,
291+
detected: &mut Vec<DetectedApi>,
292+
) {
293+
// module.exports (CJS export pattern)
294+
if let Some(member) = extract_member_pattern(&assign.left) {
295+
if member == "module.exports" {
296+
let line = line_number_at_offset(source, assign.span.start);
297+
detected.push(DetectedApi {
298+
pattern: "module.exports".into(),
299+
line,
300+
compatibility: Compatibility::NeedsManualAttention,
301+
});
302+
}
303+
}
304+
check_expression(&assign.right, source, detected);
305+
}
281306
/// Try to extract a `obj.prop` pattern string from an assignment target.
282307
fn extract_member_pattern(target: &oxc_ast::ast::AssignmentTarget<'_>) -> Option<String> {
283308
if let oxc_ast::ast::AssignmentTarget::StaticMemberExpression(member) = target {

0 commit comments

Comments
 (0)