Skip to main content

nargo_script_analyzer/
lib.rs

1#![warn(missing_docs)]
2
3use nargo_ir::{JsExpr, JsProgram, JsStmt};
4use nargo_types::{NargoValue, Result, Span};
5use std::{
6    collections::{HashMap, HashSet},
7    fs::File,
8    io::Write,
9    path::Path,
10};
11
12#[derive(Debug, Clone, Default)]
13pub struct ScriptMetadata {
14    pub signals: HashSet<String>,
15    pub computed: HashSet<String>,
16    pub props: HashSet<String>,
17    pub emits: HashSet<String>,
18    pub actions: HashSet<String>,
19    pub dependencies: HashMap<String, HashSet<String>>, // variable -> variables it depends on
20}
21
22impl ScriptMetadata {
23    pub fn to_nargo_value(&self) -> NargoValue {
24        let mut map = HashMap::new();
25
26        let signals_arr = self.signals.iter().map(|s| NargoValue::String(s.clone())).collect();
27        map.insert("signals".to_string(), NargoValue::Array(signals_arr));
28
29        let computed_arr = self.computed.iter().map(|s| NargoValue::String(s.clone())).collect();
30        map.insert("computed".to_string(), NargoValue::Array(computed_arr));
31
32        let props_arr = self.props.iter().map(|s| NargoValue::String(s.clone())).collect();
33        map.insert("props".to_string(), NargoValue::Array(props_arr));
34
35        let emits_arr = self.emits.iter().map(|s| NargoValue::String(s.clone())).collect();
36        map.insert("emits".to_string(), NargoValue::Array(emits_arr));
37
38        let actions_arr = self.actions.iter().map(|s| NargoValue::String(s.clone())).collect();
39        map.insert("actions".to_string(), NargoValue::Array(actions_arr));
40
41        let mut deps_map = HashMap::new();
42        for (k, v) in &self.dependencies {
43            let deps_arr = v.iter().map(|s| NargoValue::String(s.clone())).collect();
44            deps_map.insert(k.clone(), NargoValue::Array(deps_arr));
45        }
46        map.insert("dependencies".to_string(), NargoValue::Object(deps_map));
47
48        NargoValue::Object(map)
49    }
50}
51
52/// 分析报告中的问题级别
53#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
54pub enum IssueLevel {
55    /// 错误
56    Error,
57    /// 警告
58    Warning,
59    /// 提示
60    Info,
61}
62
63/// 分析报告中的问题
64#[derive(Debug, Clone)]
65pub struct AnalysisIssue {
66    /// 问题级别
67    pub level: IssueLevel,
68    /// 问题代码
69    pub code: String,
70    /// 问题描述
71    pub message: String,
72    /// 问题位置
73    pub span: Option<Span>,
74}
75
76/// 分析报告
77#[derive(Debug, Clone, Default)]
78pub struct AnalysisReport {
79    /// 分析的文件路径
80    pub file_path: Option<String>,
81    /// 发现的问题
82    pub issues: Vec<AnalysisIssue>,
83    /// 分析耗时(毫秒)
84    pub duration_ms: u64,
85}
86
87impl AnalysisReport {
88    /// 创建新的分析报告
89    pub fn new(file_path: Option<String>) -> Self {
90        Self { file_path, issues: Vec::new(), duration_ms: 0 }
91    }
92
93    /// 添加问题
94    pub fn add_issue(&mut self, level: IssueLevel, code: String, message: String, span: Option<Span>) {
95        self.issues.push(AnalysisIssue { level, code, message, span });
96    }
97
98    /// 按级别排序问题
99    pub fn sort_issues(&mut self) {
100        self.issues.sort_by(|a, b| a.level.cmp(&b.level));
101    }
102
103    /// 生成控制台报告
104    pub fn generate_console_report(&self) -> String {
105        let mut report = String::new();
106
107        if let Some(file_path) = &self.file_path {
108            report.push_str(&format!(
109                "Analysis report for: {}
110",
111                file_path
112            ));
113        }
114        else {
115            report.push_str(
116                "Analysis report
117",
118            );
119        }
120
121        report.push_str(&format!(
122            "Duration: {}ms
123",
124            self.duration_ms
125        ));
126        report.push_str(&format!(
127            "Total issues: {}
128",
129            self.issues.len()
130        ));
131
132        let error_count = self.issues.iter().filter(|i| i.level == IssueLevel::Error).count();
133        let warning_count = self.issues.iter().filter(|i| i.level == IssueLevel::Warning).count();
134        let info_count = self.issues.iter().filter(|i| i.level == IssueLevel::Info).count();
135
136        report.push_str(&format!(
137            "Errors: {}, Warnings: {}, Info: {}
138
139",
140            error_count, warning_count, info_count
141        ));
142
143        for (i, issue) in self.issues.iter().enumerate() {
144            let level_str = match issue.level {
145                IssueLevel::Error => "ERROR",
146                IssueLevel::Warning => "WARNING",
147                IssueLevel::Info => "INFO",
148            };
149
150            report.push_str(&format!(
151                "{}. [{}] [{}] {}
152",
153                i + 1,
154                level_str,
155                issue.code,
156                issue.message
157            ));
158
159            if let Some(span) = &issue.span {
160                report.push_str(&format!(
161                    "   Location: line {}, column {}
162",
163                    span.start.line, span.start.column
164                ));
165            }
166
167            report.push_str("\n");
168        }
169
170        report
171    }
172
173    /// 生成 JSON 报告
174    pub fn generate_json_report(&self) -> Result<NargoValue> {
175        let mut issues = Vec::new();
176
177        for issue in &self.issues {
178            let mut issue_map = HashMap::new();
179            issue_map.insert(
180                "level".to_string(),
181                NargoValue::String(match issue.level {
182                    IssueLevel::Error => "error".to_string(),
183                    IssueLevel::Warning => "warning".to_string(),
184                    IssueLevel::Info => "info".to_string(),
185                }),
186            );
187            issue_map.insert("code".to_string(), NargoValue::String(issue.code.clone()));
188            issue_map.insert("message".to_string(), NargoValue::String(issue.message.clone()));
189
190            if let Some(span) = &issue.span {
191                let mut span_map = HashMap::new();
192                span_map.insert("start_line".to_string(), NargoValue::Number(span.start.line as f64));
193                span_map.insert("start_column".to_string(), NargoValue::Number(span.start.column as f64));
194                span_map.insert("end_line".to_string(), NargoValue::Number(span.end.line as f64));
195                span_map.insert("end_column".to_string(), NargoValue::Number(span.end.column as f64));
196                issue_map.insert("span".to_string(), NargoValue::Object(span_map));
197            }
198
199            issues.push(NargoValue::Object(issue_map));
200        }
201
202        let mut report_map = HashMap::new();
203        if let Some(file_path) = &self.file_path {
204            report_map.insert("file_path".to_string(), NargoValue::String(file_path.clone()));
205        }
206        report_map.insert("issues".to_string(), NargoValue::Array(issues));
207        report_map.insert("duration_ms".to_string(), NargoValue::Number(self.duration_ms as f64));
208        report_map.insert("total_issues".to_string(), NargoValue::Number(self.issues.len() as f64));
209
210        Ok(NargoValue::Object(report_map))
211    }
212
213    /// 保存报告到文件
214    pub fn save_to_file(&self, output_path: &str) -> Result<()> {
215        let json_report = self.generate_json_report()?;
216        let report_str = serde_json::to_string_pretty(&json_report).map_err(|e| nargo_types::Error::external_error("serde_json".to_string(), e.to_string(), Span::default()))?;
217
218        let path = Path::new(output_path);
219        let mut file = File::create(path)?;
220        write!(file, "{}", report_str)?;
221
222        Ok(())
223    }
224}
225
226/// 规则接口
227pub trait Rule {
228    /// 获取规则代码
229    fn code(&self) -> String;
230    /// 获取规则描述
231    fn description(&self) -> String;
232    /// 执行规则检查
233    fn check(&self, program: &JsProgram, meta: &ScriptMetadata, report: &mut AnalysisReport);
234}
235
236/// 规则引擎
237#[derive(Default)]
238pub struct RuleEngine {
239    rules: Vec<Box<dyn Rule>>,
240}
241
242impl RuleEngine {
243    /// 创建新的规则引擎
244    pub fn new() -> Self {
245        Self { rules: Vec::new() }
246    }
247
248    /// 添加规则
249    pub fn add_rule(&mut self, rule: Box<dyn Rule>) {
250        self.rules.push(rule);
251    }
252
253    /// 执行所有规则检查
254    pub fn run(&self, program: &JsProgram, meta: &ScriptMetadata, report: &mut AnalysisReport) {
255        for rule in &self.rules {
256            rule.check(program, meta, report);
257        }
258    }
259}
260
261/// 未使用变量检查规则
262pub struct UnusedVariableRule;
263
264impl Rule for UnusedVariableRule {
265    fn code(&self) -> String {
266        "unused-variable".to_string()
267    }
268
269    fn description(&self) -> String {
270        "检查未使用的变量".to_string()
271    }
272
273    fn check(&self, program: &JsProgram, meta: &ScriptMetadata, report: &mut AnalysisReport) {
274        let mut declared_vars = HashSet::new();
275        let mut used_vars = HashSet::new();
276
277        // 收集所有声明的变量
278        for stmt in &program.body {
279            match stmt {
280                JsStmt::VariableDecl { id, .. } => {
281                    if id.starts_with('[') && id.ends_with(']') {
282                        let content = &id[1..id.len() - 1];
283                        for part in content.split(',') {
284                            let trimmed = part.trim();
285                            if !trimmed.is_empty() {
286                                declared_vars.insert(trimmed.to_string());
287                            }
288                        }
289                    }
290                    else {
291                        declared_vars.insert(id.to_string());
292                    }
293                }
294                _ => {}
295            }
296        }
297
298        // 收集所有使用的变量
299        fn collect_used_vars(expr: &JsExpr, used_vars: &mut HashSet<String>) {
300            match expr {
301                JsExpr::Identifier(name, _, _) => {
302                    used_vars.insert(name.clone());
303                }
304                JsExpr::Binary { left, right, .. } => {
305                    collect_used_vars(left, used_vars);
306                    collect_used_vars(right, used_vars);
307                }
308                JsExpr::Unary { argument, .. } => {
309                    collect_used_vars(argument, used_vars);
310                }
311                JsExpr::Call { callee, args, .. } => {
312                    collect_used_vars(callee, used_vars);
313                    for arg in args {
314                        collect_used_vars(arg, used_vars);
315                    }
316                }
317                JsExpr::Member { object, property, computed, .. } => {
318                    collect_used_vars(object, used_vars);
319                    if *computed {
320                        collect_used_vars(property, used_vars);
321                    }
322                }
323                JsExpr::Array(elements, _, _) => {
324                    for el in elements {
325                        collect_used_vars(el, used_vars);
326                    }
327                }
328                JsExpr::Object(properties, _, _) => {
329                    for value in properties.values() {
330                        collect_used_vars(value, used_vars);
331                    }
332                }
333                JsExpr::ArrowFunction { body, .. } => {
334                    collect_used_vars(body, used_vars);
335                }
336                JsExpr::Conditional { test, consequent, alternate, .. } => {
337                    collect_used_vars(test, used_vars);
338                    collect_used_vars(consequent, used_vars);
339                    collect_used_vars(alternate, used_vars);
340                }
341                JsExpr::TemplateLiteral { expressions, .. } => {
342                    for e in expressions {
343                        collect_used_vars(e, used_vars);
344                    }
345                }
346                _ => {}
347            }
348        }
349
350        fn collect_used_vars_in_stmt(stmt: &JsStmt, used_vars: &mut HashSet<String>) {
351            match stmt {
352                JsStmt::Expr(expr, _, _) => collect_used_vars(expr, used_vars),
353                JsStmt::VariableDecl { init, .. } => {
354                    if let Some(e) = init {
355                        collect_used_vars(e, used_vars);
356                    }
357                }
358                JsStmt::Return(expr, _, _) => {
359                    if let Some(e) = expr {
360                        collect_used_vars(e, used_vars);
361                    }
362                }
363                JsStmt::If { test, consequent, alternate, .. } => {
364                    collect_used_vars(test, used_vars);
365                    collect_used_vars_in_stmt(consequent, used_vars);
366                    if let Some(alt) = alternate {
367                        collect_used_vars_in_stmt(alt, used_vars);
368                    }
369                }
370                JsStmt::While { test, body, .. } => {
371                    collect_used_vars(test, used_vars);
372                    collect_used_vars_in_stmt(body, used_vars);
373                }
374                JsStmt::For { init, test, update, body, .. } => {
375                    if let Some(i) = init {
376                        collect_used_vars_in_stmt(i, used_vars);
377                    }
378                    if let Some(t) = test {
379                        collect_used_vars(t, used_vars);
380                    }
381                    if let Some(u) = update {
382                        collect_used_vars(u, used_vars);
383                    }
384                    collect_used_vars_in_stmt(body, used_vars);
385                }
386                JsStmt::Block(stmts, _, _) => {
387                    for s in stmts {
388                        collect_used_vars_in_stmt(s, used_vars);
389                    }
390                }
391                _ => {}
392            }
393        }
394
395        for stmt in &program.body {
396            collect_used_vars_in_stmt(stmt, &mut used_vars);
397        }
398
399        // 检查未使用的变量
400        for var in &declared_vars {
401            if !used_vars.contains(var) && !meta.signals.contains(var) && !meta.computed.contains(var) && !meta.actions.contains(var) {
402                report.add_issue(IssueLevel::Warning, self.code(), format!("Unused variable: {}", var), None);
403            }
404        }
405    }
406}
407
408/// 未定义变量使用检查规则
409pub struct UndefinedVariableRule;
410
411impl Rule for UndefinedVariableRule {
412    fn code(&self) -> String {
413        "undefined-variable".to_string()
414    }
415
416    fn description(&self) -> String {
417        "检查使用未定义的变量".to_string()
418    }
419
420    fn check(&self, program: &JsProgram, meta: &ScriptMetadata, report: &mut AnalysisReport) {
421        let mut declared_vars = HashSet::new();
422
423        // 收集所有声明的变量
424        for stmt in &program.body {
425            match stmt {
426                JsStmt::VariableDecl { id, .. } => {
427                    if id.starts_with('[') && id.ends_with(']') {
428                        let content = &id[1..id.len() - 1];
429                        for part in content.split(',') {
430                            let trimmed = part.trim();
431                            if !trimmed.is_empty() {
432                                declared_vars.insert(trimmed.to_string());
433                            }
434                        }
435                    }
436                    else {
437                        declared_vars.insert(id.to_string());
438                    }
439                }
440                JsStmt::FunctionDecl { id, .. } => {
441                    declared_vars.insert(id.clone());
442                }
443                _ => {}
444            }
445        }
446
447        // 检查未定义变量的使用
448        fn check_undefined_vars(expr: &JsExpr, declared_vars: &HashSet<String>, meta: &ScriptMetadata, report: &mut AnalysisReport) {
449            match expr {
450                JsExpr::Identifier(name, span, _) => {
451                    if !declared_vars.contains(name) && !meta.signals.contains(name) && !meta.computed.contains(name) && !meta.props.contains(name) && name != "props" && name != "emit" && name != "emits" && !name.starts_with('$') {
452                        report.add_issue(IssueLevel::Error, "undefined-variable".to_string(), format!("Undefined variable: {}", name), Some(*span));
453                    }
454                }
455                JsExpr::Binary { left, right, .. } => {
456                    check_undefined_vars(left, declared_vars, meta, report);
457                    check_undefined_vars(right, declared_vars, meta, report);
458                }
459                JsExpr::Unary { argument, .. } => {
460                    check_undefined_vars(argument, declared_vars, meta, report);
461                }
462                JsExpr::Call { callee, args, .. } => {
463                    check_undefined_vars(callee, declared_vars, meta, report);
464                    for arg in args {
465                        check_undefined_vars(arg, declared_vars, meta, report);
466                    }
467                }
468                JsExpr::Member { object, property, computed, .. } => {
469                    check_undefined_vars(object, declared_vars, meta, report);
470                    if *computed {
471                        check_undefined_vars(property, declared_vars, meta, report);
472                    }
473                }
474                JsExpr::Array(elements, _, _) => {
475                    for el in elements {
476                        check_undefined_vars(el, declared_vars, meta, report);
477                    }
478                }
479                JsExpr::Object(properties, _, _) => {
480                    for value in properties.values() {
481                        check_undefined_vars(value, declared_vars, meta, report);
482                    }
483                }
484                JsExpr::ArrowFunction { body, .. } => {
485                    check_undefined_vars(body, declared_vars, meta, report);
486                }
487                JsExpr::Conditional { test, consequent, alternate, .. } => {
488                    check_undefined_vars(test, declared_vars, meta, report);
489                    check_undefined_vars(consequent, declared_vars, meta, report);
490                    check_undefined_vars(alternate, declared_vars, meta, report);
491                }
492                JsExpr::TemplateLiteral { expressions, .. } => {
493                    for e in expressions {
494                        check_undefined_vars(e, declared_vars, meta, report);
495                    }
496                }
497                _ => {}
498            }
499        }
500
501        fn check_undefined_vars_in_stmt(stmt: &JsStmt, declared_vars: &HashSet<String>, meta: &ScriptMetadata, report: &mut AnalysisReport) {
502            match stmt {
503                JsStmt::Expr(expr, _, _) => check_undefined_vars(expr, declared_vars, meta, report),
504                JsStmt::VariableDecl { init, .. } => {
505                    if let Some(e) = init {
506                        check_undefined_vars(e, declared_vars, meta, report);
507                    }
508                }
509                JsStmt::Return(expr, _, _) => {
510                    if let Some(e) = expr {
511                        check_undefined_vars(e, declared_vars, meta, report);
512                    }
513                }
514                JsStmt::If { test, consequent, alternate, .. } => {
515                    check_undefined_vars(test, declared_vars, meta, report);
516                    check_undefined_vars_in_stmt(consequent, declared_vars, meta, report);
517                    if let Some(alt) = alternate {
518                        check_undefined_vars_in_stmt(alt, declared_vars, meta, report);
519                    }
520                }
521                JsStmt::While { test, body, .. } => {
522                    check_undefined_vars(test, declared_vars, meta, report);
523                    check_undefined_vars_in_stmt(body, declared_vars, meta, report);
524                }
525                JsStmt::For { init, test, update, body, .. } => {
526                    if let Some(i) = init {
527                        check_undefined_vars_in_stmt(i, declared_vars, meta, report);
528                    }
529                    if let Some(t) = test {
530                        check_undefined_vars(t, declared_vars, meta, report);
531                    }
532                    if let Some(u) = update {
533                        check_undefined_vars(u, declared_vars, meta, report);
534                    }
535                    check_undefined_vars_in_stmt(body, declared_vars, meta, report);
536                }
537                JsStmt::Block(stmts, _, _) => {
538                    for s in stmts {
539                        check_undefined_vars_in_stmt(s, declared_vars, meta, report);
540                    }
541                }
542                _ => {}
543            }
544        }
545
546        for stmt in &program.body {
547            check_undefined_vars_in_stmt(stmt, &declared_vars, meta, report);
548        }
549    }
550}
551
552/// 不安全操作检查规则
553pub struct UnsafeOperationRule;
554
555impl Rule for UnsafeOperationRule {
556    fn code(&self) -> String {
557        "unsafe-operation".to_string()
558    }
559
560    fn description(&self) -> String {
561        "检查不安全的操作".to_string()
562    }
563
564    fn check(&self, program: &JsProgram, _meta: &ScriptMetadata, report: &mut AnalysisReport) {
565        // 检查不安全操作
566        fn check_unsafe_operations(expr: &JsExpr, report: &mut AnalysisReport) {
567            match expr {
568                JsExpr::Binary { left, right, op, span, .. } => {
569                    // 检查除以零
570                    if *op == "/" {
571                        if let JsExpr::Literal(NargoValue::Number(0.0), _, _) = &**right {
572                            report.add_issue(IssueLevel::Error, "unsafe-operation".to_string(), "Division by zero".to_string(), Some(*span));
573                        }
574                    }
575                    // 检查类型不安全的比较
576                    if *op == "==" || *op == "!=" {
577                        report.add_issue(IssueLevel::Warning, "unsafe-operation".to_string(), "Use of loose equality operator (==/!=) may cause type coercion issues, consider using strict equality (===/!==)".to_string(), Some(*span));
578                    }
579                    check_unsafe_operations(left, report);
580                    check_unsafe_operations(right, report);
581                }
582                JsExpr::Call { callee, args, span, .. } => {
583                    // 检查 eval 调用
584                    if let JsExpr::Identifier(name, _, _) = &**callee {
585                        if name == "eval" {
586                            report.add_issue(IssueLevel::Warning, "unsafe-operation".to_string(), "Use of eval is potentially unsafe".to_string(), Some(*span));
587                        }
588                        // 检查 Function 构造函数
589                        else if name == "Function" {
590                            report.add_issue(IssueLevel::Warning, "unsafe-operation".to_string(), "Use of Function constructor is potentially unsafe".to_string(), Some(*span));
591                        }
592                    }
593                    check_unsafe_operations(callee, report);
594                    for arg in args {
595                        check_unsafe_operations(arg, report);
596                    }
597                }
598                JsExpr::Member { object, property, computed, .. } => {
599                    check_unsafe_operations(object, report);
600                    if *computed {
601                        check_unsafe_operations(property, report);
602                    }
603                }
604                JsExpr::Array(elements, _, _) => {
605                    for el in elements {
606                        check_unsafe_operations(el, report);
607                    }
608                }
609                JsExpr::Object(properties, _, _) => {
610                    for value in properties.values() {
611                        check_unsafe_operations(value, report);
612                    }
613                }
614                JsExpr::ArrowFunction { body, .. } => {
615                    check_unsafe_operations(body, report);
616                }
617                JsExpr::Conditional { test, consequent, alternate, .. } => {
618                    check_unsafe_operations(test, report);
619                    check_unsafe_operations(consequent, report);
620                    check_unsafe_operations(alternate, report);
621                }
622                JsExpr::TemplateLiteral { expressions, .. } => {
623                    for e in expressions {
624                        check_unsafe_operations(e, report);
625                    }
626                }
627                _ => {}
628            }
629        }
630
631        fn check_unsafe_operations_in_stmt(stmt: &JsStmt, report: &mut AnalysisReport) {
632            match stmt {
633                JsStmt::Expr(expr, _, _) => check_unsafe_operations(expr, report),
634                JsStmt::VariableDecl { init, .. } => {
635                    if let Some(e) = init {
636                        check_unsafe_operations(e, report);
637                    }
638                }
639                JsStmt::Return(expr, _, _) => {
640                    if let Some(e) = expr {
641                        check_unsafe_operations(e, report);
642                    }
643                }
644                JsStmt::If { test, consequent, alternate, .. } => {
645                    check_unsafe_operations(test, report);
646                    check_unsafe_operations_in_stmt(consequent, report);
647                    if let Some(alt) = alternate {
648                        check_unsafe_operations_in_stmt(alt, report);
649                    }
650                }
651                JsStmt::While { test, body, .. } => {
652                    check_unsafe_operations(test, report);
653                    check_unsafe_operations_in_stmt(body, report);
654                }
655                JsStmt::For { init, test, update, body, .. } => {
656                    if let Some(i) = init {
657                        check_unsafe_operations_in_stmt(i, report);
658                    }
659                    if let Some(t) = test {
660                        check_unsafe_operations(t, report);
661                    }
662                    if let Some(u) = update {
663                        check_unsafe_operations(u, report);
664                    }
665                    check_unsafe_operations_in_stmt(body, report);
666                }
667                JsStmt::Block(stmts, _, _) => {
668                    for s in stmts {
669                        check_unsafe_operations_in_stmt(s, report);
670                    }
671                }
672                _ => {}
673            }
674        }
675
676        for stmt in &program.body {
677            check_unsafe_operations_in_stmt(stmt, report);
678        }
679    }
680}
681
682/// 未使用的导入检查规则
683pub struct UnusedImportRule;
684
685impl Rule for UnusedImportRule {
686    fn code(&self) -> String {
687        "unused-import".to_string()
688    }
689
690    fn description(&self) -> String {
691        "检查未使用的导入".to_string()
692    }
693
694    fn check(&self, program: &JsProgram, _meta: &ScriptMetadata, report: &mut AnalysisReport) {
695        let mut imports = HashSet::new();
696        let mut used_imports = HashSet::new();
697
698        // 收集所有导入
699        for stmt in &program.body {
700            match stmt {
701                JsStmt::Import { specifiers, source: _, span: _, trivia: _ } => {
702                    for specifier in specifiers {
703                        imports.insert(specifier.clone());
704                    }
705                }
706                _ => {}
707            }
708        }
709
710        // 收集所有使用的标识符
711        fn collect_used_identifiers(expr: &JsExpr, used: &mut HashSet<String>) {
712            match expr {
713                JsExpr::Identifier(name, _, _) => {
714                    used.insert(name.clone());
715                }
716                JsExpr::Binary { left, right, .. } => {
717                    collect_used_identifiers(left, used);
718                    collect_used_identifiers(right, used);
719                }
720                JsExpr::Unary { argument, .. } => {
721                    collect_used_identifiers(argument, used);
722                }
723                JsExpr::Call { callee, args, .. } => {
724                    collect_used_identifiers(callee, used);
725                    for arg in args {
726                        collect_used_identifiers(arg, used);
727                    }
728                }
729                JsExpr::Member { object, property, computed, .. } => {
730                    collect_used_identifiers(object, used);
731                    if *computed {
732                        collect_used_identifiers(property, used);
733                    }
734                }
735                JsExpr::Array(elements, _, _) => {
736                    for el in elements {
737                        collect_used_identifiers(el, used);
738                    }
739                }
740                JsExpr::Object(properties, _, _) => {
741                    for value in properties.values() {
742                        collect_used_identifiers(value, used);
743                    }
744                }
745                JsExpr::ArrowFunction { body, .. } => {
746                    collect_used_identifiers(body, used);
747                }
748                JsExpr::Conditional { test, consequent, alternate, .. } => {
749                    collect_used_identifiers(test, used);
750                    collect_used_identifiers(consequent, used);
751                    collect_used_identifiers(alternate, used);
752                }
753                JsExpr::TemplateLiteral { expressions, .. } => {
754                    for e in expressions {
755                        collect_used_identifiers(e, used);
756                    }
757                }
758                _ => {}
759            }
760        }
761
762        fn collect_used_identifiers_in_stmt(stmt: &JsStmt, used: &mut HashSet<String>) {
763            match stmt {
764                JsStmt::Expr(expr, _, _) => collect_used_identifiers(expr, used),
765                JsStmt::VariableDecl { init, .. } => {
766                    if let Some(e) = init {
767                        collect_used_identifiers(e, used);
768                    }
769                }
770                JsStmt::Return(expr, _, _) => {
771                    if let Some(e) = expr {
772                        collect_used_identifiers(e, used);
773                    }
774                }
775                JsStmt::If { test, consequent, alternate, .. } => {
776                    collect_used_identifiers(test, used);
777                    collect_used_identifiers_in_stmt(consequent, used);
778                    if let Some(alt) = alternate {
779                        collect_used_identifiers_in_stmt(alt, used);
780                    }
781                }
782                JsStmt::While { test, body, .. } => {
783                    collect_used_identifiers(test, used);
784                    collect_used_identifiers_in_stmt(body, used);
785                }
786                JsStmt::For { init, test, update, body, .. } => {
787                    if let Some(i) = init {
788                        collect_used_identifiers_in_stmt(i, used);
789                    }
790                    if let Some(t) = test {
791                        collect_used_identifiers(t, used);
792                    }
793                    if let Some(u) = update {
794                        collect_used_identifiers(u, used);
795                    }
796                    collect_used_identifiers_in_stmt(body, used);
797                }
798                JsStmt::Block(stmts, _, _) => {
799                    for s in stmts {
800                        collect_used_identifiers_in_stmt(s, used);
801                    }
802                }
803                _ => {}
804            }
805        }
806
807        for stmt in &program.body {
808            collect_used_identifiers_in_stmt(stmt, &mut used_imports);
809        }
810
811        // 检查未使用的导入
812        for import in &imports {
813            if !used_imports.contains(import) {
814                report.add_issue(IssueLevel::Warning, self.code(), format!("Unused import: {}", import), None);
815            }
816        }
817    }
818}
819
820/// 内存泄漏检测规则
821pub struct MemoryLeakRule;
822
823impl Rule for MemoryLeakRule {
824    fn code(&self) -> String {
825        "memory-leak".to_string()
826    }
827
828    fn description(&self) -> String {
829        "检查可能的内存泄漏".to_string()
830    }
831
832    fn check(&self, program: &JsProgram, _meta: &ScriptMetadata, report: &mut AnalysisReport) {
833        let mut set_intervals = HashSet::new();
834        let mut set_timeouts = HashSet::new();
835        let mut event_listeners = HashSet::new();
836
837        // 检查定时器和事件监听器
838        fn check_memory_leaks(expr: &JsExpr, set_intervals: &mut HashSet<String>, set_timeouts: &mut HashSet<String>, event_listeners: &mut HashSet<String>, report: &mut AnalysisReport) {
839            match expr {
840                JsExpr::Call { callee, args, span, .. } => {
841                    // 检查 setInterval
842                    if let JsExpr::Identifier(name, _, _) = &**callee {
843                        if name == "setInterval" {
844                            if let Some(id_expr) = args.get(2) {
845                                if let JsExpr::Identifier(id, _, _) = id_expr {
846                                    set_intervals.insert(id.clone());
847                                }
848                            }
849                            else {
850                                report.add_issue(IssueLevel::Warning, "memory-leak".to_string(), "setInterval without an ID may cause memory leaks".to_string(), Some(*span));
851                            }
852                        }
853                        // 检查 setTimeout
854                        else if name == "setTimeout" {
855                            if let Some(id_expr) = args.get(2) {
856                                if let JsExpr::Identifier(id, _, _) = id_expr {
857                                    set_timeouts.insert(id.clone());
858                                }
859                            }
860                        }
861                    }
862                    // 检查事件监听器
863                    if let JsExpr::Member { object, property, computed: false, .. } = &**callee {
864                        if let JsExpr::Identifier(prop_name, _, _) = &**property {
865                            if prop_name == "addEventListener" {
866                                event_listeners.insert(format!("{:?}.addEventListener", object));
867                            }
868                        }
869                    }
870                    check_memory_leaks(callee, set_intervals, set_timeouts, event_listeners, report);
871                    for arg in args {
872                        check_memory_leaks(arg, set_intervals, set_timeouts, event_listeners, report);
873                    }
874                }
875                JsExpr::Member { object, property, computed, .. } => {
876                    check_memory_leaks(object, set_intervals, set_timeouts, event_listeners, report);
877                    if *computed {
878                        check_memory_leaks(property, set_intervals, set_timeouts, event_listeners, report);
879                    }
880                }
881                JsExpr::Binary { left, right, .. } => {
882                    check_memory_leaks(left, set_intervals, set_timeouts, event_listeners, report);
883                    check_memory_leaks(right, set_intervals, set_timeouts, event_listeners, report);
884                }
885                JsExpr::Unary { argument, .. } => {
886                    check_memory_leaks(argument, set_intervals, set_timeouts, event_listeners, report);
887                }
888                JsExpr::Array(elements, _, _) => {
889                    for el in elements {
890                        check_memory_leaks(el, set_intervals, set_timeouts, event_listeners, report);
891                    }
892                }
893                JsExpr::Object(properties, _, _) => {
894                    for value in properties.values() {
895                        check_memory_leaks(value, set_intervals, set_timeouts, event_listeners, report);
896                    }
897                }
898                JsExpr::ArrowFunction { body, .. } => {
899                    check_memory_leaks(body, set_intervals, set_timeouts, event_listeners, report);
900                }
901                JsExpr::Conditional { test, consequent, alternate, .. } => {
902                    check_memory_leaks(test, set_intervals, set_timeouts, event_listeners, report);
903                    check_memory_leaks(consequent, set_intervals, set_timeouts, event_listeners, report);
904                    check_memory_leaks(alternate, set_intervals, set_timeouts, event_listeners, report);
905                }
906                JsExpr::TemplateLiteral { expressions, .. } => {
907                    for e in expressions {
908                        check_memory_leaks(e, set_intervals, set_timeouts, event_listeners, report);
909                    }
910                }
911                _ => {}
912            }
913        }
914
915        fn check_memory_leaks_in_stmt(stmt: &JsStmt, set_intervals: &mut HashSet<String>, set_timeouts: &mut HashSet<String>, event_listeners: &mut HashSet<String>, report: &mut AnalysisReport) {
916            match stmt {
917                JsStmt::Expr(expr, _, _) => check_memory_leaks(expr, set_intervals, set_timeouts, event_listeners, report),
918                JsStmt::VariableDecl { init, .. } => {
919                    if let Some(e) = init {
920                        check_memory_leaks(e, set_intervals, set_timeouts, event_listeners, report);
921                    }
922                }
923                JsStmt::Return(expr, _, _) => {
924                    if let Some(e) = expr {
925                        check_memory_leaks(e, set_intervals, set_timeouts, event_listeners, report);
926                    }
927                }
928                JsStmt::If { test, consequent, alternate, .. } => {
929                    check_memory_leaks(test, set_intervals, set_timeouts, event_listeners, report);
930                    check_memory_leaks_in_stmt(consequent, set_intervals, set_timeouts, event_listeners, report);
931                    if let Some(alt) = alternate {
932                        check_memory_leaks_in_stmt(alt, set_intervals, set_timeouts, event_listeners, report);
933                    }
934                }
935                JsStmt::While { test, body, .. } => {
936                    check_memory_leaks(test, set_intervals, set_timeouts, event_listeners, report);
937                    check_memory_leaks_in_stmt(body, set_intervals, set_timeouts, event_listeners, report);
938                }
939                JsStmt::For { init, test, update, body, .. } => {
940                    if let Some(i) = init {
941                        check_memory_leaks_in_stmt(i, set_intervals, set_timeouts, event_listeners, report);
942                    }
943                    if let Some(t) = test {
944                        check_memory_leaks(t, set_intervals, set_timeouts, event_listeners, report);
945                    }
946                    if let Some(u) = update {
947                        check_memory_leaks(u, set_intervals, set_timeouts, event_listeners, report);
948                    }
949                    check_memory_leaks_in_stmt(body, set_intervals, set_timeouts, event_listeners, report);
950                }
951                JsStmt::Block(stmts, _, _) => {
952                    for s in stmts {
953                        check_memory_leaks_in_stmt(s, set_intervals, set_timeouts, event_listeners, report);
954                    }
955                }
956                _ => {}
957            }
958        }
959
960        for stmt in &program.body {
961            check_memory_leaks_in_stmt(stmt, &mut set_intervals, &mut set_timeouts, &mut event_listeners, report);
962        }
963
964        // 检查是否有对应的 clearInterval 调用
965        let mut clear_intervals = HashSet::new();
966        let mut clear_timeouts = HashSet::new();
967        let mut remove_event_listeners = HashSet::new();
968
969        fn check_clear_functions(expr: &JsExpr, clear_intervals: &mut HashSet<String>, clear_timeouts: &mut HashSet<String>, remove_event_listeners: &mut HashSet<String>) {
970            match expr {
971                JsExpr::Call { callee, args, .. } => {
972                    if let JsExpr::Identifier(name, _, _) = &**callee {
973                        if name == "clearInterval" {
974                            if let Some(id_expr) = args.get(0) {
975                                if let JsExpr::Identifier(id, _, _) = id_expr {
976                                    clear_intervals.insert(id.clone());
977                                }
978                            }
979                        }
980                        else if name == "clearTimeout" {
981                            if let Some(id_expr) = args.get(0) {
982                                if let JsExpr::Identifier(id, _, _) = id_expr {
983                                    clear_timeouts.insert(id.clone());
984                                }
985                            }
986                        }
987                    }
988                    if let JsExpr::Member { property, computed: false, .. } = &**callee {
989                        if let JsExpr::Identifier(prop_name, _, _) = &**property {
990                            if prop_name == "removeEventListener" {
991                                remove_event_listeners.insert("removeEventListener".to_string());
992                            }
993                        }
994                    }
995                    check_clear_functions(callee, clear_intervals, clear_timeouts, remove_event_listeners);
996                    for arg in args {
997                        check_clear_functions(arg, clear_intervals, clear_timeouts, remove_event_listeners);
998                    }
999                }
1000                JsExpr::Member { object, property, computed, .. } => {
1001                    check_clear_functions(object, clear_intervals, clear_timeouts, remove_event_listeners);
1002                    if *computed {
1003                        check_clear_functions(property, clear_intervals, clear_timeouts, remove_event_listeners);
1004                    }
1005                }
1006                JsExpr::Binary { left, right, .. } => {
1007                    check_clear_functions(left, clear_intervals, clear_timeouts, remove_event_listeners);
1008                    check_clear_functions(right, clear_intervals, clear_timeouts, remove_event_listeners);
1009                }
1010                JsExpr::Unary { argument, .. } => {
1011                    check_clear_functions(argument, clear_intervals, clear_timeouts, remove_event_listeners);
1012                }
1013                JsExpr::Array(elements, _, _) => {
1014                    for el in elements {
1015                        check_clear_functions(el, clear_intervals, clear_timeouts, remove_event_listeners);
1016                    }
1017                }
1018                JsExpr::Object(properties, _, _) => {
1019                    for value in properties.values() {
1020                        check_clear_functions(value, clear_intervals, clear_timeouts, remove_event_listeners);
1021                    }
1022                }
1023                JsExpr::ArrowFunction { body, .. } => {
1024                    check_clear_functions(body, clear_intervals, clear_timeouts, remove_event_listeners);
1025                }
1026                JsExpr::Conditional { test, consequent, alternate, .. } => {
1027                    check_clear_functions(test, clear_intervals, clear_timeouts, remove_event_listeners);
1028                    check_clear_functions(consequent, clear_intervals, clear_timeouts, remove_event_listeners);
1029                    check_clear_functions(alternate, clear_intervals, clear_timeouts, remove_event_listeners);
1030                }
1031                JsExpr::TemplateLiteral { expressions, .. } => {
1032                    for e in expressions {
1033                        check_clear_functions(e, clear_intervals, clear_timeouts, remove_event_listeners);
1034                    }
1035                }
1036                _ => {}
1037            }
1038        }
1039
1040        fn check_clear_functions_in_stmt(stmt: &JsStmt, clear_intervals: &mut HashSet<String>, clear_timeouts: &mut HashSet<String>, remove_event_listeners: &mut HashSet<String>) {
1041            match stmt {
1042                JsStmt::Expr(expr, _, _) => check_clear_functions(expr, clear_intervals, clear_timeouts, remove_event_listeners),
1043                JsStmt::VariableDecl { init, .. } => {
1044                    if let Some(e) = init {
1045                        check_clear_functions(e, clear_intervals, clear_timeouts, remove_event_listeners);
1046                    }
1047                }
1048                JsStmt::Return(expr, _, _) => {
1049                    if let Some(e) = expr {
1050                        check_clear_functions(e, clear_intervals, clear_timeouts, remove_event_listeners);
1051                    }
1052                }
1053                JsStmt::If { test, consequent, alternate, .. } => {
1054                    check_clear_functions(test, clear_intervals, clear_timeouts, remove_event_listeners);
1055                    check_clear_functions_in_stmt(consequent, clear_intervals, clear_timeouts, remove_event_listeners);
1056                    if let Some(alt) = alternate {
1057                        check_clear_functions_in_stmt(alt, clear_intervals, clear_timeouts, remove_event_listeners);
1058                    }
1059                }
1060                JsStmt::While { test, body, .. } => {
1061                    check_clear_functions(test, clear_intervals, clear_timeouts, remove_event_listeners);
1062                    check_clear_functions_in_stmt(body, clear_intervals, clear_timeouts, remove_event_listeners);
1063                }
1064                JsStmt::For { init, test, update, body, .. } => {
1065                    if let Some(i) = init {
1066                        check_clear_functions_in_stmt(i, clear_intervals, clear_timeouts, remove_event_listeners);
1067                    }
1068                    if let Some(t) = test {
1069                        check_clear_functions(t, clear_intervals, clear_timeouts, remove_event_listeners);
1070                    }
1071                    if let Some(u) = update {
1072                        check_clear_functions(u, clear_intervals, clear_timeouts, remove_event_listeners);
1073                    }
1074                    check_clear_functions_in_stmt(body, clear_intervals, clear_timeouts, remove_event_listeners);
1075                }
1076                JsStmt::Block(stmts, _, _) => {
1077                    for s in stmts {
1078                        check_clear_functions_in_stmt(s, clear_intervals, clear_timeouts, remove_event_listeners);
1079                    }
1080                }
1081                _ => {}
1082            }
1083        }
1084
1085        for stmt in &program.body {
1086            check_clear_functions_in_stmt(stmt, &mut clear_intervals, &mut clear_timeouts, &mut remove_event_listeners);
1087        }
1088
1089        // 检查未清理的定时器
1090        for interval_id in &set_intervals {
1091            if !clear_intervals.contains(interval_id) {
1092                report.add_issue(IssueLevel::Warning, self.code(), format!("setInterval with ID '{}' may not be cleared, potential memory leak", interval_id), None);
1093            }
1094        }
1095
1096        // 检查事件监听器是否有对应的移除调用
1097        if !event_listeners.is_empty() && remove_event_listeners.is_empty() {
1098            report.add_issue(IssueLevel::Warning, self.code(), "Event listeners added but not removed, potential memory leak".to_string(), None);
1099        }
1100    }
1101}
1102
1103/// 性能瓶颈检测规则
1104pub struct PerformanceBottleneckRule;
1105
1106impl Rule for PerformanceBottleneckRule {
1107    fn code(&self) -> String {
1108        "performance-bottleneck".to_string()
1109    }
1110
1111    fn description(&self) -> String {
1112        "检查可能的性能瓶颈".to_string()
1113    }
1114
1115    fn check(&self, program: &JsProgram, _meta: &ScriptMetadata, report: &mut AnalysisReport) {
1116        // 检查嵌套循环
1117        fn check_nested_loops(stmt: &JsStmt, loop_depth: u32, report: &mut AnalysisReport) {
1118            match stmt {
1119                JsStmt::For { body, span, .. } => {
1120                    let new_depth = loop_depth + 1;
1121                    if new_depth >= 3 {
1122                        report.add_issue(IssueLevel::Warning, "performance-bottleneck".to_string(), "Deeply nested loops (3+ levels) may cause performance issues".to_string(), Some(*span));
1123                    }
1124                    check_nested_loops(body, new_depth, report);
1125                }
1126                JsStmt::While { body, span, .. } => {
1127                    let new_depth = loop_depth + 1;
1128                    if new_depth >= 3 {
1129                        report.add_issue(IssueLevel::Warning, "performance-bottleneck".to_string(), "Deeply nested loops (3+ levels) may cause performance issues".to_string(), Some(*span));
1130                    }
1131                    check_nested_loops(body, new_depth, report);
1132                }
1133                JsStmt::Block(stmts, _, _) => {
1134                    for s in stmts {
1135                        check_nested_loops(s, loop_depth, report);
1136                    }
1137                }
1138                JsStmt::If { consequent, alternate, .. } => {
1139                    check_nested_loops(consequent, loop_depth, report);
1140                    if let Some(alt) = alternate {
1141                        check_nested_loops(alt, loop_depth, report);
1142                    }
1143                }
1144                _ => {}
1145            }
1146        }
1147
1148        // 检查 DOM 操作
1149        fn check_dom_operations(expr: &JsExpr, report: &mut AnalysisReport) {
1150            match expr {
1151                JsExpr::Call { callee, args, span, .. } => {
1152                    // 检查频繁的 DOM 查询
1153                    if let JsExpr::Member { property, computed: false, .. } = &**callee {
1154                        if let JsExpr::Identifier(prop_name, _, _) = &**property {
1155                            if prop_name == "getElementById" || prop_name == "querySelector" || prop_name == "querySelectorAll" {
1156                                report.add_issue(IssueLevel::Info, "performance-bottleneck".to_string(), "Frequent DOM queries may cause performance issues, consider caching results".to_string(), Some(*span));
1157                            }
1158                        }
1159                    }
1160                    check_dom_operations(callee, report);
1161                    for arg in args {
1162                        check_dom_operations(arg, report);
1163                    }
1164                }
1165                JsExpr::Member { object, property, computed, .. } => {
1166                    check_dom_operations(object, report);
1167                    if *computed {
1168                        check_dom_operations(property, report);
1169                    }
1170                }
1171                JsExpr::Binary { left, right, .. } => {
1172                    check_dom_operations(left, report);
1173                    check_dom_operations(right, report);
1174                }
1175                JsExpr::Unary { argument, .. } => {
1176                    check_dom_operations(argument, report);
1177                }
1178                JsExpr::Array(elements, _, _) => {
1179                    for el in elements {
1180                        check_dom_operations(el, report);
1181                    }
1182                }
1183                JsExpr::Object(properties, _, _) => {
1184                    for value in properties.values() {
1185                        check_dom_operations(value, report);
1186                    }
1187                }
1188                JsExpr::ArrowFunction { body, .. } => {
1189                    check_dom_operations(body, report);
1190                }
1191                JsExpr::Conditional { test, consequent, alternate, .. } => {
1192                    check_dom_operations(test, report);
1193                    check_dom_operations(consequent, report);
1194                    check_dom_operations(alternate, report);
1195                }
1196                JsExpr::TemplateLiteral { expressions, .. } => {
1197                    for e in expressions {
1198                        check_dom_operations(e, report);
1199                    }
1200                }
1201                _ => {}
1202            }
1203        }
1204
1205        fn check_dom_operations_in_stmt(stmt: &JsStmt, report: &mut AnalysisReport) {
1206            match stmt {
1207                JsStmt::Expr(expr, _, _) => check_dom_operations(expr, report),
1208                JsStmt::VariableDecl { init, .. } => {
1209                    if let Some(e) = init {
1210                        check_dom_operations(e, report);
1211                    }
1212                }
1213                JsStmt::Return(expr, _, _) => {
1214                    if let Some(e) = expr {
1215                        check_dom_operations(e, report);
1216                    }
1217                }
1218                JsStmt::If { test, consequent, alternate, .. } => {
1219                    check_dom_operations(test, report);
1220                    check_dom_operations_in_stmt(consequent, report);
1221                    if let Some(alt) = alternate {
1222                        check_dom_operations_in_stmt(alt, report);
1223                    }
1224                }
1225                JsStmt::While { test, body, .. } => {
1226                    check_dom_operations(test, report);
1227                    check_dom_operations_in_stmt(body, report);
1228                }
1229                JsStmt::For { init, test, update, body, .. } => {
1230                    if let Some(i) = init {
1231                        check_dom_operations_in_stmt(i, report);
1232                    }
1233                    if let Some(t) = test {
1234                        check_dom_operations(t, report);
1235                    }
1236                    if let Some(u) = update {
1237                        check_dom_operations(u, report);
1238                    }
1239                    check_dom_operations_in_stmt(body, report);
1240                }
1241                JsStmt::Block(stmts, _, _) => {
1242                    for s in stmts {
1243                        check_dom_operations_in_stmt(s, report);
1244                    }
1245                }
1246                _ => {}
1247            }
1248        }
1249
1250        // 检查大数组操作
1251        fn check_large_array_operations(expr: &JsExpr, report: &mut AnalysisReport) {
1252            match expr {
1253                JsExpr::Call { callee, args, span, .. } => {
1254                    // 检查可能的大数组操作
1255                    if let JsExpr::Member { property, computed: false, .. } = &**callee {
1256                        if let JsExpr::Identifier(prop_name, _, _) = &**property {
1257                            if prop_name == "map" || prop_name == "filter" || prop_name == "reduce" || prop_name == "forEach" {
1258                                report.add_issue(IssueLevel::Info, "performance-bottleneck".to_string(), "Large array operations may cause performance issues, consider using more efficient methods".to_string(), Some(*span));
1259                            }
1260                        }
1261                    }
1262                    check_large_array_operations(callee, report);
1263                    for arg in args {
1264                        check_large_array_operations(arg, report);
1265                    }
1266                }
1267                JsExpr::Member { object, property, computed, .. } => {
1268                    check_large_array_operations(object, report);
1269                    if *computed {
1270                        check_large_array_operations(property, report);
1271                    }
1272                }
1273                JsExpr::Binary { left, right, .. } => {
1274                    check_large_array_operations(left, report);
1275                    check_large_array_operations(right, report);
1276                }
1277                JsExpr::Unary { argument, .. } => {
1278                    check_large_array_operations(argument, report);
1279                }
1280                JsExpr::Array(elements, span, _) => {
1281                    if elements.len() > 100 {
1282                        report.add_issue(IssueLevel::Warning, "performance-bottleneck".to_string(), "Large array literals may cause performance issues".to_string(), Some(*span));
1283                    }
1284                    for el in elements {
1285                        check_large_array_operations(el, report);
1286                    }
1287                }
1288                JsExpr::Object(properties, _, _) => {
1289                    for value in properties.values() {
1290                        check_large_array_operations(value, report);
1291                    }
1292                }
1293                JsExpr::ArrowFunction { body, .. } => {
1294                    check_large_array_operations(body, report);
1295                }
1296                JsExpr::Conditional { test, consequent, alternate, .. } => {
1297                    check_large_array_operations(test, report);
1298                    check_large_array_operations(consequent, report);
1299                    check_large_array_operations(alternate, report);
1300                }
1301                JsExpr::TemplateLiteral { expressions, .. } => {
1302                    for e in expressions {
1303                        check_large_array_operations(e, report);
1304                    }
1305                }
1306                _ => {}
1307            }
1308        }
1309
1310        fn check_large_array_operations_in_stmt(stmt: &JsStmt, report: &mut AnalysisReport) {
1311            match stmt {
1312                JsStmt::Expr(expr, _, _) => check_large_array_operations(expr, report),
1313                JsStmt::VariableDecl { init, .. } => {
1314                    if let Some(e) = init {
1315                        check_large_array_operations(e, report);
1316                    }
1317                }
1318                JsStmt::Return(expr, _, _) => {
1319                    if let Some(e) = expr {
1320                        check_large_array_operations(e, report);
1321                    }
1322                }
1323                JsStmt::If { test, consequent, alternate, .. } => {
1324                    check_large_array_operations(test, report);
1325                    check_large_array_operations_in_stmt(consequent, report);
1326                    if let Some(alt) = alternate {
1327                        check_large_array_operations_in_stmt(alt, report);
1328                    }
1329                }
1330                JsStmt::While { test, body, .. } => {
1331                    check_large_array_operations(test, report);
1332                    check_large_array_operations_in_stmt(body, report);
1333                }
1334                JsStmt::For { init, test, update, body, .. } => {
1335                    if let Some(i) = init {
1336                        check_large_array_operations_in_stmt(i, report);
1337                    }
1338                    if let Some(t) = test {
1339                        check_large_array_operations(t, report);
1340                    }
1341                    if let Some(u) = update {
1342                        check_large_array_operations(u, report);
1343                    }
1344                    check_large_array_operations_in_stmt(body, report);
1345                }
1346                JsStmt::Block(stmts, _, _) => {
1347                    for s in stmts {
1348                        check_large_array_operations_in_stmt(s, report);
1349                    }
1350                }
1351                _ => {}
1352            }
1353        }
1354
1355        // 执行检查
1356        for stmt in &program.body {
1357            check_nested_loops(stmt, 0, report);
1358            check_dom_operations_in_stmt(stmt, report);
1359            check_large_array_operations_in_stmt(stmt, report);
1360        }
1361    }
1362}
1363
1364/// 安全规则
1365pub struct SecurityRule;
1366
1367impl Rule for SecurityRule {
1368    fn code(&self) -> String {
1369        "security".to_string()
1370    }
1371
1372    fn description(&self) -> String {
1373        "检查安全问题".to_string()
1374    }
1375
1376    fn check(&self, program: &JsProgram, _meta: &ScriptMetadata, report: &mut AnalysisReport) {
1377        // 检查 SQL 注入
1378        fn check_sql_injection(expr: &JsExpr, report: &mut AnalysisReport) {
1379            match expr {
1380                JsExpr::Binary { left, right, op, span, .. } => {
1381                    // 检查字符串拼接可能导致的 SQL 注入
1382                    if *op == "+" {
1383                        // 检查是否有 SQL 相关的关键词
1384                        let mut has_sql_keyword = false;
1385                        let mut has_user_input = false;
1386
1387                        fn check_sql_keywords(expr: &JsExpr) -> bool {
1388                            match expr {
1389                                JsExpr::Literal(NargoValue::String(s), _, _) => {
1390                                    let s_lower = s.to_lowercase();
1391                                    s_lower.contains("select") || s_lower.contains("insert") || s_lower.contains("update") || s_lower.contains("delete") || s_lower.contains("from") || s_lower.contains("where")
1392                                }
1393                                JsExpr::Binary { left, right, .. } => check_sql_keywords(left) || check_sql_keywords(right),
1394                                JsExpr::Identifier(_, _, _) => true, // 可能是用户输入变量
1395                                _ => false,
1396                            }
1397                        }
1398
1399                        has_sql_keyword = check_sql_keywords(left) || check_sql_keywords(right);
1400                        has_user_input = matches!(&**left, JsExpr::Identifier(_, _, _)) || matches!(&**right, JsExpr::Identifier(_, _, _));
1401
1402                        if has_sql_keyword && has_user_input {
1403                            report.add_issue(IssueLevel::Error, "security".to_string(), "Potential SQL injection vulnerability: avoid string concatenation for SQL queries".to_string(), Some(*span));
1404                        }
1405                    }
1406                    check_sql_injection(left, report);
1407                    check_sql_injection(right, report);
1408                }
1409                JsExpr::Call { callee, args, .. } => {
1410                    check_sql_injection(callee, report);
1411                    for arg in args {
1412                        check_sql_injection(arg, report);
1413                    }
1414                }
1415                JsExpr::Member { object, property, computed, .. } => {
1416                    check_sql_injection(object, report);
1417                    if *computed {
1418                        check_sql_injection(property, report);
1419                    }
1420                }
1421                JsExpr::Array(elements, _, _) => {
1422                    for el in elements {
1423                        check_sql_injection(el, report);
1424                    }
1425                }
1426                JsExpr::Object(properties, _, _) => {
1427                    for value in properties.values() {
1428                        check_sql_injection(value, report);
1429                    }
1430                }
1431                JsExpr::ArrowFunction { body, .. } => {
1432                    check_sql_injection(body, report);
1433                }
1434                JsExpr::Conditional { test, consequent, alternate, .. } => {
1435                    check_sql_injection(test, report);
1436                    check_sql_injection(consequent, report);
1437                    check_sql_injection(alternate, report);
1438                }
1439                JsExpr::TemplateLiteral { expressions, .. } => {
1440                    for e in expressions {
1441                        check_sql_injection(e, report);
1442                    }
1443                }
1444                _ => {}
1445            }
1446        }
1447
1448        // 检查 XSS 攻击
1449        fn check_xss(expr: &JsExpr, report: &mut AnalysisReport) {
1450            match expr {
1451                JsExpr::Member { object, property, computed: false, span, .. } => {
1452                    if let JsExpr::Identifier(prop_name, _, _) = &**property {
1453                        // 检查 innerHTML 赋值
1454                        if prop_name == "innerHTML" {
1455                            report.add_issue(IssueLevel::Warning, "security".to_string(), "Potential XSS vulnerability: avoid using innerHTML with untrusted data".to_string(), Some(*span));
1456                        }
1457                    }
1458                    check_xss(object, report);
1459                }
1460                JsExpr::Call { callee, args, span, .. } => {
1461                    // 检查 document.write
1462                    if let JsExpr::Member { property, computed: false, .. } = &**callee {
1463                        if let JsExpr::Identifier(prop_name, _, _) = &**property {
1464                            if prop_name == "write" {
1465                                report.add_issue(IssueLevel::Warning, "security".to_string(), "Potential XSS vulnerability: avoid using document.write".to_string(), Some(*span));
1466                            }
1467                        }
1468                    }
1469                    check_xss(callee, report);
1470                    for arg in args {
1471                        check_xss(arg, report);
1472                    }
1473                }
1474                JsExpr::Binary { left, right, .. } => {
1475                    check_xss(left, report);
1476                    check_xss(right, report);
1477                }
1478                JsExpr::Unary { argument, .. } => {
1479                    check_xss(argument, report);
1480                }
1481                JsExpr::Array(elements, _, _) => {
1482                    for el in elements {
1483                        check_xss(el, report);
1484                    }
1485                }
1486                JsExpr::Object(properties, _, _) => {
1487                    for value in properties.values() {
1488                        check_xss(value, report);
1489                    }
1490                }
1491                JsExpr::ArrowFunction { body, .. } => {
1492                    check_xss(body, report);
1493                }
1494                JsExpr::Conditional { test, consequent, alternate, .. } => {
1495                    check_xss(test, report);
1496                    check_xss(consequent, report);
1497                    check_xss(alternate, report);
1498                }
1499                JsExpr::TemplateLiteral { expressions, .. } => {
1500                    for e in expressions {
1501                        check_xss(e, report);
1502                    }
1503                }
1504                _ => {}
1505            }
1506        }
1507
1508        // 检查不安全的密码存储
1509        fn check_password_storage(expr: &JsExpr, report: &mut AnalysisReport) {
1510            match expr {
1511                JsExpr::Call { callee, args, span, .. } => {
1512                    // 检查密码相关的存储
1513                    if let JsExpr::Identifier(name, _, _) = &**callee {
1514                        if name == "localStorage" || name == "sessionStorage" {
1515                            // 检查是否存储密码
1516                            for arg in args {
1517                                if let JsExpr::Literal(NargoValue::String(s), _, _) = arg {
1518                                    let s_lower = s.to_lowercase();
1519                                    if s_lower.contains("password") || s_lower.contains("pwd") {
1520                                        report.add_issue(IssueLevel::Error, "security".to_string(), "Insecure password storage: avoid storing passwords in localStorage/sessionStorage".to_string(), Some(*span));
1521                                    }
1522                                }
1523                            }
1524                        }
1525                    }
1526                    check_password_storage(callee, report);
1527                    for arg in args {
1528                        check_password_storage(arg, report);
1529                    }
1530                }
1531                JsExpr::Member { object, property, computed, .. } => {
1532                    check_password_storage(object, report);
1533                    if *computed {
1534                        check_password_storage(property, report);
1535                    }
1536                }
1537                JsExpr::Binary { left, right, .. } => {
1538                    check_password_storage(left, report);
1539                    check_password_storage(right, report);
1540                }
1541                JsExpr::Unary { argument, .. } => {
1542                    check_password_storage(argument, report);
1543                }
1544                JsExpr::Array(elements, _, _) => {
1545                    for el in elements {
1546                        check_password_storage(el, report);
1547                    }
1548                }
1549                JsExpr::Object(properties, _, _) => {
1550                    for value in properties.values() {
1551                        check_password_storage(value, report);
1552                    }
1553                }
1554                JsExpr::ArrowFunction { body, .. } => {
1555                    check_password_storage(body, report);
1556                }
1557                JsExpr::Conditional { test, consequent, alternate, .. } => {
1558                    check_password_storage(test, report);
1559                    check_password_storage(consequent, report);
1560                    check_password_storage(alternate, report);
1561                }
1562                JsExpr::TemplateLiteral { expressions, .. } => {
1563                    for e in expressions {
1564                        check_password_storage(e, report);
1565                    }
1566                }
1567                _ => {}
1568            }
1569        }
1570
1571        fn check_security_in_stmt(stmt: &JsStmt, report: &mut AnalysisReport) {
1572            match stmt {
1573                JsStmt::Expr(expr, _, _) => {
1574                    check_sql_injection(expr, report);
1575                    check_xss(expr, report);
1576                    check_password_storage(expr, report);
1577                }
1578                JsStmt::VariableDecl { init, .. } => {
1579                    if let Some(e) = init {
1580                        check_sql_injection(e, report);
1581                        check_xss(e, report);
1582                        check_password_storage(e, report);
1583                    }
1584                }
1585                JsStmt::Return(expr, _, _) => {
1586                    if let Some(e) = expr {
1587                        check_sql_injection(e, report);
1588                        check_xss(e, report);
1589                        check_password_storage(e, report);
1590                    }
1591                }
1592                JsStmt::If { test, consequent, alternate, .. } => {
1593                    check_sql_injection(test, report);
1594                    check_xss(test, report);
1595                    check_password_storage(test, report);
1596                    check_security_in_stmt(consequent, report);
1597                    if let Some(alt) = alternate {
1598                        check_security_in_stmt(alt, report);
1599                    }
1600                }
1601                JsStmt::While { test, body, .. } => {
1602                    check_sql_injection(test, report);
1603                    check_xss(test, report);
1604                    check_password_storage(test, report);
1605                    check_security_in_stmt(body, report);
1606                }
1607                JsStmt::For { init, test, update, body, .. } => {
1608                    if let Some(i) = init {
1609                        check_security_in_stmt(i, report);
1610                    }
1611                    if let Some(t) = test {
1612                        check_sql_injection(t, report);
1613                        check_xss(t, report);
1614                        check_password_storage(t, report);
1615                    }
1616                    if let Some(u) = update {
1617                        check_sql_injection(u, report);
1618                        check_xss(u, report);
1619                        check_password_storage(u, report);
1620                    }
1621                    check_security_in_stmt(body, report);
1622                }
1623                JsStmt::Block(stmts, _, _) => {
1624                    for s in stmts {
1625                        check_security_in_stmt(s, report);
1626                    }
1627                }
1628                _ => {}
1629            }
1630        }
1631
1632        // 执行安全检查
1633        for stmt in &program.body {
1634            check_security_in_stmt(stmt, report);
1635        }
1636    }
1637}
1638
1639/// 代码风格检查规则
1640pub struct CodeStyleRule;
1641
1642impl Rule for CodeStyleRule {
1643    fn code(&self) -> String {
1644        "code-style".to_string()
1645    }
1646
1647    fn description(&self) -> String {
1648        "检查代码风格问题".to_string()
1649    }
1650
1651    fn check(&self, program: &JsProgram, _meta: &ScriptMetadata, report: &mut AnalysisReport) {
1652        // 检查变量命名规范
1653        fn check_naming_conventions(stmt: &JsStmt, report: &mut AnalysisReport) {
1654            match stmt {
1655                JsStmt::VariableDecl { id, span, .. } => {
1656                    // 检查变量命名
1657                    if id.starts_with('[') && id.ends_with(']') {
1658                        let content = &id[1..id.len() - 1];
1659                        for part in content.split(',') {
1660                            let trimmed = part.trim();
1661                            if !trimmed.is_empty() {
1662                                check_variable_name(trimmed, *span, report);
1663                            }
1664                        }
1665                    }
1666                    else {
1667                        check_variable_name(id, *span, report);
1668                    }
1669                }
1670                JsStmt::FunctionDecl { id, span, .. } => {
1671                    // 检查函数命名
1672                    if !id.starts_with(|c: char| c.is_ascii_lowercase()) {
1673                        report.add_issue(IssueLevel::Warning, "code-style".to_string(), format!("Function name '{}' should use camelCase", id), Some(*span));
1674                    }
1675                }
1676                JsStmt::Block(stmts, _, _) => {
1677                    for s in stmts {
1678                        check_naming_conventions(s, report);
1679                    }
1680                }
1681                JsStmt::If { consequent, alternate, .. } => {
1682                    check_naming_conventions(consequent, report);
1683                    if let Some(alt) = alternate {
1684                        check_naming_conventions(alt, report);
1685                    }
1686                }
1687                JsStmt::While { body, .. } => {
1688                    check_naming_conventions(body, report);
1689                }
1690                JsStmt::For { body, .. } => {
1691                    check_naming_conventions(body, report);
1692                }
1693                _ => {}
1694            }
1695        }
1696
1697        fn check_variable_name(name: &str, span: Span, report: &mut AnalysisReport) {
1698            // 检查变量命名
1699            if name.starts_with(|c: char| c.is_ascii_uppercase()) {
1700                report.add_issue(IssueLevel::Warning, "code-style".to_string(), format!("Variable name '{}' should use camelCase", name), Some(span));
1701            }
1702            // 检查单字符变量名(除了 i, j, k 等循环变量)
1703            if name.len() == 1 && !"ijklmn".contains(name) {
1704                report.add_issue(IssueLevel::Info, "code-style".to_string(), format!("Variable name '{}' is too short, consider using a more descriptive name", name), Some(span));
1705            }
1706        }
1707
1708        // 检查代码长度
1709        fn check_code_length(stmt: &JsStmt, report: &mut AnalysisReport) {
1710            match stmt {
1711                JsStmt::FunctionDecl { body, span, .. } => {
1712                    let body_length = body.len();
1713                    if body_length > 50 {
1714                        report.add_issue(IssueLevel::Warning, "code-style".to_string(), "Function is too long (over 50 statements), consider refactoring".to_string(), Some(*span));
1715                    }
1716                }
1717                JsStmt::Block(stmts, span, ..) => {
1718                    let block_length = stmts.len();
1719                    if block_length > 30 {
1720                        report.add_issue(IssueLevel::Info, "code-style".to_string(), "Block is too long (over 30 statements), consider refactoring".to_string(), Some(*span));
1721                    }
1722                }
1723                JsStmt::If { consequent, alternate, .. } => {
1724                    check_code_length(consequent, report);
1725                    if let Some(alt) = alternate {
1726                        check_code_length(alt, report);
1727                    }
1728                }
1729                JsStmt::While { body, .. } => {
1730                    check_code_length(body, report);
1731                }
1732                JsStmt::For { body, .. } => {
1733                    check_code_length(body, report);
1734                }
1735                _ => {}
1736            }
1737        }
1738
1739        fn count_statements(stmt: &JsStmt) -> usize {
1740            match stmt {
1741                JsStmt::Block(stmts, _, _) => stmts.len(),
1742                _ => 1,
1743            }
1744        }
1745
1746        // 检查空格和缩进
1747        fn check_whitespace(stmt: &JsStmt, report: &mut AnalysisReport) {
1748            // 这里可以添加空格和缩进的检查逻辑
1749            // 由于 AST 中可能没有包含空格信息,这里暂时只做简单检查
1750        }
1751
1752        // 执行代码风格检查
1753        for stmt in &program.body {
1754            check_naming_conventions(stmt, report);
1755            check_code_length(stmt, report);
1756            check_whitespace(stmt, report);
1757        }
1758    }
1759}
1760
1761#[derive(Default)]
1762pub struct ScriptAnalyzer;
1763
1764impl ScriptAnalyzer {
1765    pub fn new() -> Self {
1766        Self
1767    }
1768
1769    pub fn analyze(&self, program: &JsProgram) -> Result<ScriptMetadata> {
1770        let mut meta = ScriptMetadata::default();
1771
1772        // 收集所有声明的变量和函数,用于依赖分析
1773        let mut declared_vars = HashSet::new();
1774        let mut declared_functions = HashSet::new();
1775
1776        // 第一次遍历:收集所有声明
1777        for stmt in &program.body {
1778            match stmt {
1779                JsStmt::VariableDecl { id, init, .. } => {
1780                    self.analyze_variable_decl(id, init.as_ref(), &mut meta);
1781                    // 收集声明的变量
1782                    if id.starts_with('[') && id.ends_with(']') {
1783                        let content = &id[1..id.len() - 1];
1784                        for part in content.split(',') {
1785                            let trimmed = part.trim();
1786                            if !trimmed.is_empty() {
1787                                declared_vars.insert(trimmed.to_string());
1788                            }
1789                        }
1790                    }
1791                    else {
1792                        declared_vars.insert(id.to_string());
1793                    }
1794                }
1795                JsStmt::FunctionDecl { id, params: _, body: _, .. } => {
1796                    meta.actions.insert(id.clone());
1797                    declared_functions.insert(id.clone());
1798                }
1799                JsStmt::Expr(expr, _, _) => {
1800                    self.analyze_expression(expr, &mut meta);
1801                }
1802                _ => {}
1803            }
1804        }
1805
1806        // 第二次遍历:分析依赖关系
1807        for stmt in &program.body {
1808            match stmt {
1809                JsStmt::VariableDecl { id, init, .. } => {
1810                    if let Some(init_expr) = init {
1811                        let mut deps = HashSet::new();
1812                        self.find_dependencies(init_expr, &mut deps, &meta);
1813                        if !deps.is_empty() {
1814                            meta.dependencies.insert(id.clone(), deps);
1815                        }
1816                    }
1817                }
1818                JsStmt::FunctionDecl { id, body, .. } => {
1819                    let mut deps = HashSet::new();
1820                    for s in body {
1821                        self.find_dependencies_in_stmt(s, &mut deps, &meta);
1822                    }
1823                    if !deps.is_empty() {
1824                        meta.dependencies.insert(id.clone(), deps);
1825                    }
1826                }
1827                JsStmt::Expr(expr, _, _) => {
1828                    // Track effect dependencies
1829                    if let JsExpr::Call { callee, args, .. } = expr {
1830                        if let JsExpr::Identifier(name, _, _) = &**callee {
1831                            if name == "$effect" || name == "watchEffect" {
1832                                if let Some(first_arg) = args.get(0) {
1833                                    let mut deps = HashSet::new();
1834                                    self.find_dependencies(first_arg, &mut deps, &meta);
1835                                    if !deps.is_empty() {
1836                                        // Store effect dependencies with a special key or handle separately
1837                                        meta.dependencies.insert(format!("$effect_{}", meta.dependencies.len()), deps);
1838                                    }
1839                                }
1840                            }
1841                        }
1842                    }
1843                }
1844                _ => {}
1845            }
1846        }
1847
1848        Ok(meta)
1849    }
1850
1851    /// 执行静态分析规则检查
1852    pub fn analyze_with_rules(&self, program: &JsProgram, file_path: Option<String>) -> Result<(ScriptMetadata, AnalysisReport)> {
1853        let start_time = std::time::Instant::now();
1854
1855        // 首先进行常规分析
1856        let meta = self.analyze(program)?;
1857
1858        // 创建分析报告
1859        let mut report = AnalysisReport::new(file_path);
1860
1861        // 使用默认规则引擎
1862        let rule_engine = default_rule_engine();
1863
1864        // 执行规则检查
1865        rule_engine.run(program, &meta, &mut report);
1866
1867        // 计算分析耗时
1868        let duration = start_time.elapsed();
1869        report.duration_ms = duration.as_millis() as u64;
1870
1871        // 排序问题
1872        report.sort_issues();
1873
1874        Ok((meta, report))
1875    }
1876
1877    fn analyze_variable_decl(&self, id: &str, init: Option<&JsExpr>, meta: &mut ScriptMetadata) {
1878        let mut ids = Vec::new();
1879        if id.starts_with('[') && id.ends_with(']') {
1880            // Simple array pattern parsing: [a, b, ...]
1881            let content = &id[1..id.len() - 1];
1882            for part in content.split(',') {
1883                let trimmed = part.trim();
1884                if !trimmed.is_empty() {
1885                    ids.push(trimmed.to_string());
1886                }
1887            }
1888        }
1889        else {
1890            ids.push(id.to_string());
1891        }
1892
1893        // Handle props: const props = defineProps(...)
1894        if id == "props" {
1895            if let Some(JsExpr::Call { callee, args, .. }) = init {
1896                if let JsExpr::Identifier(name, _, _) = &**callee {
1897                    if name == "defineProps" {
1898                        self.extract_keys_from_args(args, &mut meta.props);
1899                        return;
1900                    }
1901                }
1902            }
1903        }
1904
1905        // Handle emits: const emit = defineEmits(...)
1906        if id == "emit" || id == "emits" {
1907            if let Some(JsExpr::Call { callee, args, .. }) = init {
1908                if let JsExpr::Identifier(name, _, _) = &**callee {
1909                    if name == "defineEmits" {
1910                        self.extract_keys_from_args(args, &mut meta.emits);
1911                        return;
1912                    }
1913                }
1914            }
1915        }
1916
1917        // Handle signals/computed
1918        if let Some(init_expr) = init {
1919            match init_expr {
1920                JsExpr::Call { callee, .. } => {
1921                    if let JsExpr::Identifier(name, _, _) = &**callee {
1922                        if name == "signal" || name == "createSignal" || name == "ref" || name == "reactive" {
1923                            for (i, var_id) in ids.iter().enumerate() {
1924                                if i == 0 || name == "ref" || name == "reactive" {
1925                                    meta.signals.insert(var_id.clone());
1926                                }
1927                            }
1928                        }
1929                        else if name == "computed" || name == "$computed" || name == "createComputed" {
1930                            for var_id in &ids {
1931                                meta.computed.insert(var_id.clone());
1932                            }
1933                        }
1934                    }
1935                }
1936                JsExpr::ArrowFunction { .. } => {
1937                    for var_id in &ids {
1938                        meta.actions.insert(var_id.clone());
1939                    }
1940                }
1941                _ => {}
1942            }
1943        }
1944    }
1945
1946    fn analyze_expression(&self, expr: &JsExpr, meta: &mut ScriptMetadata) {
1947        if let JsExpr::Call { callee, args, .. } = expr {
1948            if let JsExpr::Identifier(name, _, _) = &**callee {
1949                match name.as_str() {
1950                    "defineProps" => {
1951                        self.extract_keys_from_args(args, &mut meta.props);
1952                    }
1953                    "defineEmits" => {
1954                        self.extract_keys_from_args(args, &mut meta.emits);
1955                    }
1956                    _ => {}
1957                }
1958            }
1959        }
1960    }
1961
1962    fn find_dependencies(&self, expr: &JsExpr, deps: &mut HashSet<String>, meta: &ScriptMetadata) {
1963        match expr {
1964            JsExpr::Identifier(name, _, _) => {
1965                if meta.signals.contains(name) || meta.computed.contains(name) || meta.props.contains(name) {
1966                    deps.insert(name.clone());
1967                }
1968            }
1969            JsExpr::Binary { left, right, .. } => {
1970                self.find_dependencies(left, deps, meta);
1971                self.find_dependencies(right, deps, meta);
1972            }
1973            JsExpr::Unary { argument, .. } => {
1974                self.find_dependencies(argument, deps, meta);
1975            }
1976            JsExpr::Call { callee, args, .. } => {
1977                self.find_dependencies(callee, deps, meta);
1978                for arg in args {
1979                    self.find_dependencies(arg, deps, meta);
1980                }
1981            }
1982            JsExpr::Member { object, property, computed, .. } => {
1983                self.find_dependencies(object, deps, meta);
1984                if *computed {
1985                    self.find_dependencies(property, deps, meta);
1986                }
1987            }
1988            JsExpr::Array(elements, _, _) => {
1989                for el in elements {
1990                    self.find_dependencies(el, deps, meta);
1991                }
1992            }
1993            JsExpr::Object(properties, _, _) => {
1994                for value in properties.values() {
1995                    self.find_dependencies(value, deps, meta);
1996                }
1997            }
1998            JsExpr::ArrowFunction { body, .. } => {
1999                self.find_dependencies(body, deps, meta);
2000            }
2001            JsExpr::Conditional { test, consequent, alternate, .. } => {
2002                self.find_dependencies(test, deps, meta);
2003                self.find_dependencies(consequent, deps, meta);
2004                self.find_dependencies(alternate, deps, meta);
2005            }
2006            JsExpr::TemplateLiteral { expressions, .. } => {
2007                for e in expressions {
2008                    self.find_dependencies(e, deps, meta);
2009                }
2010            }
2011            _ => {}
2012        }
2013    }
2014
2015    fn find_dependencies_in_stmt(&self, stmt: &JsStmt, deps: &mut HashSet<String>, meta: &ScriptMetadata) {
2016        match stmt {
2017            JsStmt::Expr(expr, _, _) => self.find_dependencies(expr, deps, meta),
2018            JsStmt::VariableDecl { init, .. } => {
2019                if let Some(e) = init {
2020                    self.find_dependencies(e, deps, meta);
2021                }
2022            }
2023            JsStmt::Return(expr, _, _) => {
2024                if let Some(e) = expr {
2025                    self.find_dependencies(e, deps, meta);
2026                }
2027            }
2028            JsStmt::If { test, consequent, alternate, .. } => {
2029                self.find_dependencies(test, deps, meta);
2030                self.find_dependencies_in_stmt(consequent, deps, meta);
2031                if let Some(alt) = alternate {
2032                    self.find_dependencies_in_stmt(alt, deps, meta);
2033                }
2034            }
2035            JsStmt::While { test, body, .. } => {
2036                self.find_dependencies(test, deps, meta);
2037                self.find_dependencies_in_stmt(body, deps, meta);
2038            }
2039            JsStmt::For { init, test, update, body, .. } => {
2040                if let Some(i) = init {
2041                    self.find_dependencies_in_stmt(i, deps, meta);
2042                }
2043                if let Some(t) = test {
2044                    self.find_dependencies(t, deps, meta);
2045                }
2046                if let Some(u) = update {
2047                    self.find_dependencies(u, deps, meta);
2048                }
2049                self.find_dependencies_in_stmt(body, deps, meta);
2050            }
2051            JsStmt::Block(stmts, _, _) => {
2052                for s in stmts {
2053                    self.find_dependencies_in_stmt(s, deps, meta);
2054                }
2055            }
2056            _ => {}
2057        }
2058    }
2059
2060    fn extract_keys_from_args(&self, args: &[JsExpr], set: &mut HashSet<String>) {
2061        if let Some(first_arg) = args.get(0) {
2062            match first_arg {
2063                JsExpr::Object(map, _, _) => {
2064                    for key in map.keys() {
2065                        set.insert(key.clone());
2066                    }
2067                }
2068                JsExpr::Array(arr, _, _) => {
2069                    for item in arr {
2070                        if let JsExpr::Literal(NargoValue::String(s), _, _) = item {
2071                            set.insert(s.clone());
2072                        }
2073                    }
2074                }
2075                _ => {}
2076            }
2077        }
2078    }
2079}
2080
2081/// 导出默认规则引擎配置
2082pub fn default_rule_engine() -> RuleEngine {
2083    let mut engine = RuleEngine::new();
2084    engine.add_rule(Box::new(UnusedVariableRule));
2085    engine.add_rule(Box::new(UndefinedVariableRule));
2086    engine.add_rule(Box::new(UnsafeOperationRule));
2087    engine.add_rule(Box::new(UnusedImportRule));
2088    engine.add_rule(Box::new(MemoryLeakRule));
2089    engine.add_rule(Box::new(PerformanceBottleneckRule));
2090    engine.add_rule(Box::new(SecurityRule));
2091    engine.add_rule(Box::new(CodeStyleRule));
2092    engine
2093}