1use crate::types::{Edge, Node, NodeKind, Span};
4use std::collections::HashMap;
5
6pub struct RustAnalyzer {
8 nodes: Vec<Node>,
9 edges: Vec<Edge>,
10 scope_map: HashMap<crate::types::NodeId, Vec<crate::types::NodeId>>,
12}
13
14impl RustAnalyzer {
15 pub fn new(nodes: Vec<Node>, edges: Vec<Edge>) -> Self {
17 let mut analyzer = Self {
18 nodes,
19 edges,
20 scope_map: HashMap::new(),
21 };
22 analyzer.build_scope_map();
23 analyzer
24 }
25
26 fn build_scope_map(&mut self) {
28 for edge in &self.edges {
29 if matches!(edge.kind, crate::types::EdgeKind::Contains) {
30 self.scope_map
31 .entry(edge.source)
32 .or_default()
33 .push(edge.target);
34 }
35 }
36 }
37
38 pub fn analyze_all(&self) -> RustAnalysisResult {
40 RustAnalysisResult {
41 ownership_patterns: self.analyze_ownership_patterns(),
42 performance_issues: self.analyze_performance_patterns(),
43 safety_issues: self.analyze_safety_patterns(),
44 concurrency_issues: self.analyze_concurrency_patterns(),
45 trait_implementations: self.analyze_trait_implementations(),
46 unsafe_usage: self.analyze_unsafe_usage(),
47 lifetime_usage: self.analyze_lifetime_usage(),
48 macro_usage: self.analyze_macro_usage(),
49 }
50 }
51
52 pub fn analyze_ownership_patterns(&self) -> Vec<OwnershipPattern> {
54 let mut patterns = Vec::new();
55
56 for node in &self.nodes {
57 match &node.kind {
58 NodeKind::Function | NodeKind::Method => {
59 patterns.extend(self.detect_ownership_antipatterns(node));
60 patterns.extend(self.detect_unnecessary_clones(node));
61 patterns.extend(self.detect_inefficient_borrowing(node));
62 patterns.extend(self.detect_move_semantics_issues(node));
63 }
64 NodeKind::Variable => {
65 patterns.extend(self.analyze_variable_ownership(node));
66 }
67 _ => {}
68 }
69 }
70
71 patterns
72 }
73
74 pub fn analyze_performance_patterns(&self) -> Vec<PerformanceIssue> {
76 let mut issues = Vec::new();
77
78 for node in &self.nodes {
79 match &node.kind {
80 NodeKind::Function | NodeKind::Method => {
81 issues.extend(self.detect_allocation_patterns(node));
82 issues.extend(self.detect_string_inefficiencies(node));
83 issues.extend(self.detect_iterator_inefficiencies(node));
84 issues.extend(self.detect_collection_inefficiencies(node));
85 }
86 NodeKind::Call => {
87 issues.extend(self.analyze_call_performance(node));
88 }
89 _ => {}
90 }
91 }
92
93 issues
94 }
95
96 pub fn analyze_safety_patterns(&self) -> Vec<SafetyIssue> {
98 let mut issues = Vec::new();
99
100 for node in &self.nodes {
101 match &node.kind {
102 NodeKind::Function | NodeKind::Method => {
103 if self.is_unsafe_function(node) {
104 issues.extend(self.analyze_unsafe_function(node));
105 }
106 issues.extend(self.detect_ffi_patterns(node));
107 issues.extend(self.detect_memory_safety_issues(node));
108 }
109 _ => {}
110 }
111 }
112
113 issues
114 }
115
116 pub fn analyze_concurrency_patterns(&self) -> Vec<ConcurrencyIssue> {
118 let mut issues = Vec::new();
119
120 for node in &self.nodes {
121 match &node.kind {
122 NodeKind::Function | NodeKind::Method => {
123 issues.extend(self.analyze_async_patterns(node));
124 issues.extend(self.detect_deadlock_potential(node));
125 issues.extend(self.analyze_thread_safety(node));
126 issues.extend(self.analyze_channel_usage(node));
127 }
128 NodeKind::Struct | NodeKind::Enum => {
129 issues.extend(self.analyze_send_sync_traits(node));
130 }
131 _ => {}
132 }
133 }
134
135 issues
136 }
137
138 fn detect_ownership_antipatterns(&self, function_node: &Node) -> Vec<OwnershipPattern> {
142 let mut patterns = Vec::new();
143
144 if let Some(signature) = &function_node.signature {
146 if signature.contains("String") && !signature.contains("&str") {
148 patterns.push(OwnershipPattern {
149 pattern_type: OwnershipPatternType::UnnecessaryOwned,
150 location: function_node.span.clone(),
151 description: "Function takes owned String when &str would suffice".to_string(),
152 suggestion: Some(
153 "Consider using &str parameter for read-only string access".to_string(),
154 ),
155 severity: Severity::Medium,
156 });
157 }
158
159 let mut_borrow_count = signature.matches("&mut").count();
161 if mut_borrow_count > 1 {
162 patterns.push(OwnershipPattern {
163 pattern_type: OwnershipPatternType::MultipleMutableBorrows,
164 location: function_node.span.clone(),
165 description: format!(
166 "Function has {} mutable borrows, potential for borrowing conflicts",
167 mut_borrow_count
168 ),
169 suggestion: Some(
170 "Consider refactoring to reduce mutable borrows or use interior mutability"
171 .to_string(),
172 ),
173 severity: Severity::High,
174 });
175 }
176 }
177
178 patterns
179 }
180
181 fn detect_unnecessary_clones(&self, function_node: &Node) -> Vec<OwnershipPattern> {
183 let mut patterns = Vec::new();
184
185 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
187 for &node_id in scope_nodes {
188 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
189 if matches!(call_node.kind, NodeKind::Call) && call_node.name.contains("clone")
190 {
191 patterns.push(OwnershipPattern {
192 pattern_type: OwnershipPatternType::UnnecessaryClone,
193 location: call_node.span.clone(),
194 description: "Potential unnecessary clone() call".to_string(),
195 suggestion: Some(
196 "Check if borrowing would work instead of cloning".to_string(),
197 ),
198 severity: Severity::Medium,
199 });
200 }
201 }
202 }
203 }
204
205 patterns
206 }
207
208 fn detect_inefficient_borrowing(&self, function_node: &Node) -> Vec<OwnershipPattern> {
210 let mut patterns = Vec::new();
211
212 if let Some(metadata) = function_node.metadata.as_object() {
214 if let Some(params) = metadata.get("parameters").and_then(|p| p.as_array()) {
215 for param in params {
216 if let Some(param_obj) = param.as_object() {
217 if let Some(ownership) = param_obj.get("ownership").and_then(|o| o.as_str())
218 {
219 if ownership == "borrowed" {
220 if let Some(usage) =
221 param_obj.get("usage_pattern").and_then(|u| u.as_str())
222 {
223 if usage == "stored" {
224 patterns.push(OwnershipPattern {
225 pattern_type: OwnershipPatternType::InefficientBorrowing,
226 location: function_node.span.clone(),
227 description: "Borrowed parameter is stored, consider taking ownership".to_string(),
228 suggestion: Some("Take ownership of the parameter if it needs to be stored".to_string()),
229 severity: Severity::Medium,
230 });
231 }
232 }
233 }
234 }
235 }
236 }
237 }
238 }
239
240 patterns
241 }
242
243 fn detect_move_semantics_issues(&self, function_node: &Node) -> Vec<OwnershipPattern> {
245 let mut patterns = Vec::new();
246
247 if let Some(signature) = &function_node.signature {
249 if signature.contains("self")
250 && !signature.contains("&self")
251 && !signature.contains("&mut self")
252 {
253 patterns.push(OwnershipPattern {
254 pattern_type: OwnershipPatternType::PotentialMoveError,
255 location: function_node.span.clone(),
256 description: "Method takes self by value, object will be moved".to_string(),
257 suggestion: Some(
258 "Consider if borrowing (&self or &mut self) would be more appropriate"
259 .to_string(),
260 ),
261 severity: Severity::Low,
262 });
263 }
264 }
265
266 patterns
267 }
268
269 fn analyze_variable_ownership(&self, variable_node: &Node) -> Vec<OwnershipPattern> {
271 let mut patterns = Vec::new();
272
273 if variable_node.name.ends_with("_ref") || variable_node.name.starts_with("ref_") {
275 patterns.push(OwnershipPattern {
276 pattern_type: OwnershipPatternType::InconsistentNaming,
277 location: variable_node.span.clone(),
278 description: "Variable name suggests reference but type might be owned".to_string(),
279 suggestion: Some(
280 "Ensure naming convention matches ownership semantics".to_string(),
281 ),
282 severity: Severity::Low,
283 });
284 }
285
286 patterns
287 }
288
289 fn detect_allocation_patterns(&self, function_node: &Node) -> Vec<PerformanceIssue> {
293 let mut issues = Vec::new();
294
295 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
296 for &node_id in scope_nodes {
297 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
298 if matches!(call_node.kind, NodeKind::Call) {
299 if call_node.name.contains("Vec::new") || call_node.name.contains("vec!") {
301 issues.push(PerformanceIssue {
302 issue_type: PerformanceIssueType::FrequentAllocations,
303 location: call_node.span.clone(),
304 description: "Vector allocation detected - ensure not in hot path"
305 .to_string(),
306 suggestion: Some(
307 "Consider pre-allocating with capacity or reusing allocations"
308 .to_string(),
309 ),
310 impact: PerformanceImpact::Medium,
311 });
312 }
313
314 if call_node.name.contains("HashMap::new") {
316 issues.push(PerformanceIssue {
317 issue_type: PerformanceIssueType::UnoptimizedCollections,
318 location: call_node.span.clone(),
319 description: "HashMap created without initial capacity".to_string(),
320 suggestion: Some(
321 "Use HashMap::with_capacity() if size is known".to_string(),
322 ),
323 impact: PerformanceImpact::Medium,
324 });
325 }
326 }
327 }
328 }
329 }
330
331 issues
332 }
333
334 fn detect_string_inefficiencies(&self, function_node: &Node) -> Vec<PerformanceIssue> {
336 let mut issues = Vec::new();
337
338 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
339 let mut string_pushes = 0;
340
341 for &node_id in scope_nodes {
342 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
343 if matches!(call_node.kind, NodeKind::Call) {
344 if call_node.name.contains("push_str") || call_node.name.contains("push") {
346 string_pushes += 1;
347 }
348 }
349 }
350 }
351
352 if string_pushes >= 3 {
354 issues.push(PerformanceIssue {
355 issue_type: PerformanceIssueType::StringConcatenation,
356 location: function_node.span.clone(),
357 description: format!(
358 "Multiple string operations detected: {} push operations",
359 string_pushes
360 ),
361 suggestion: Some(
362 "Consider using String::with_capacity() or format! macro".to_string(),
363 ),
364 impact: PerformanceImpact::Medium,
365 });
366 }
367 }
368
369 if !self.scope_map.contains_key(&function_node.id)
371 || self.scope_map.get(&function_node.id).unwrap().is_empty()
372 {
373 let mut push_str_count = 0;
374
375 for node in &self.nodes {
376 if matches!(node.kind, NodeKind::Call) && node.name.contains("push_str") {
377 push_str_count += 1;
378 }
379 }
380
381 if push_str_count >= 3 {
382 issues.push(PerformanceIssue {
383 issue_type: PerformanceIssueType::StringConcatenation,
384 location: function_node.span.clone(),
385 description: format!(
386 "Multiple string operations detected: {} push_str calls",
387 push_str_count
388 ),
389 suggestion: Some(
390 "Consider using String::with_capacity() or format! macro".to_string(),
391 ),
392 impact: PerformanceImpact::Medium,
393 });
394 }
395 }
396
397 issues
398 }
399
400 fn detect_iterator_inefficiencies(&self, function_node: &Node) -> Vec<PerformanceIssue> {
402 let mut issues = Vec::new();
403
404 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
405 for &node_id in scope_nodes {
406 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
407 if matches!(call_node.kind, NodeKind::Call) {
408 if call_node.name.contains("collect") {
410 issues.push(PerformanceIssue {
411 issue_type: PerformanceIssueType::IteratorChains,
412 location: call_node.span.clone(),
413 description:
414 "collect() call may be unnecessary if chaining iterators"
415 .to_string(),
416 suggestion: Some(
417 "Consider avoiding intermediate collection if possible"
418 .to_string(),
419 ),
420 impact: PerformanceImpact::Medium,
421 });
422 }
423 }
424 }
425 }
426 }
427
428 issues
429 }
430
431 fn detect_collection_inefficiencies(&self, function_node: &Node) -> Vec<PerformanceIssue> {
433 let mut issues = Vec::new();
434
435 if let Some(signature) = &function_node.signature {
436 if signature.contains("Vec<") && !signature.contains("&mut") {
438 issues.push(PerformanceIssue {
439 issue_type: PerformanceIssueType::UnoptimizedCollections,
440 location: function_node.span.clone(),
441 description: "Function takes Vec<T> when &[T] slice might suffice".to_string(),
442 suggestion: Some(
443 "Consider using slice (&[T]) for read-only access".to_string(),
444 ),
445 impact: PerformanceImpact::Low,
446 });
447 }
448 }
449
450 issues
451 }
452
453 fn analyze_call_performance(&self, call_node: &Node) -> Vec<PerformanceIssue> {
455 let mut issues = Vec::new();
456
457 if call_node.name.contains("sort") && !call_node.name.contains("sort_unstable") {
459 issues.push(PerformanceIssue {
460 issue_type: PerformanceIssueType::SuboptimalAlgorithms,
461 location: call_node.span.clone(),
462 description: "Using stable sort when unstable sort might be sufficient".to_string(),
463 suggestion: Some(
464 "Consider sort_unstable() for better performance if stability not required"
465 .to_string(),
466 ),
467 impact: PerformanceImpact::Low,
468 });
469 }
470
471 issues
472 }
473
474 fn is_unsafe_function(&self, function_node: &Node) -> bool {
478 if let Some(signature) = &function_node.signature {
480 if signature.contains("unsafe") {
481 return true;
482 }
483 }
484
485 if function_node.name.contains("unsafe") {
487 return true;
488 }
489
490 if matches!(function_node.kind, NodeKind::Method) {
492 if let Some(metadata) = function_node.metadata.as_object() {
493 if let Some(unsafe_flag) = metadata.get("unsafe").and_then(|u| u.as_bool()) {
494 if unsafe_flag {
495 return true;
496 }
497 }
498 if let Some(modifiers) = metadata.get("modifiers").and_then(|m| m.as_array()) {
499 for modifier in modifiers {
500 if let Some(mod_str) = modifier.as_str() {
501 if mod_str == "unsafe" {
502 return true;
503 }
504 }
505 }
506 }
507 }
508 }
509
510 false
511 }
512
513 fn analyze_unsafe_function(&self, function_node: &Node) -> Vec<SafetyIssue> {
515 let mut issues = Vec::new();
516
517 issues.push(SafetyIssue {
518 issue_type: SafetyIssueType::UnsafeFunction,
519 location: function_node.span.clone(),
520 description: "Unsafe function requires careful review".to_string(),
521 rationale: None,
522 mitigation: Some(
523 "Document safety invariants and ensure all callers uphold them".to_string(),
524 ),
525 risk_level: RiskLevel::High,
526 });
527
528 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
530 for &node_id in scope_nodes {
531 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
532 if matches!(call_node.kind, NodeKind::Call) {
533 if call_node.name.contains("*") && call_node.name.contains("raw") {
535 issues.push(SafetyIssue {
536 issue_type: SafetyIssueType::RawPointerDereference,
537 location: call_node.span.clone(),
538 description: "Raw pointer dereference detected".to_string(),
539 rationale: None,
540 mitigation: Some(
541 "Ensure pointer is valid and properly aligned".to_string(),
542 ),
543 risk_level: RiskLevel::Critical,
544 });
545 }
546 }
547 }
548 }
549 }
550
551 issues
552 }
553
554 fn detect_ffi_patterns(&self, function_node: &Node) -> Vec<SafetyIssue> {
556 let mut issues = Vec::new();
557
558 if let Some(signature) = &function_node.signature {
560 if signature.contains("extern") && signature.contains("\"C\"") {
562 issues.push(SafetyIssue {
563 issue_type: SafetyIssueType::FFIBoundary,
564 location: function_node.span.clone(),
565 description: "FFI function boundary requires careful handling".to_string(),
566 rationale: None,
567 mitigation: Some(
568 "Validate all parameters and handle C-style errors properly".to_string(),
569 ),
570 risk_level: RiskLevel::High,
571 });
572 }
573
574 if signature.contains("CString") || signature.contains("CStr") {
576 issues.push(SafetyIssue {
577 issue_type: SafetyIssueType::FFIBoundary,
578 location: function_node.span.clone(),
579 description: "C string handling requires null termination validation"
580 .to_string(),
581 rationale: None,
582 mitigation: Some(
583 "Ensure proper null termination and UTF-8 validation".to_string(),
584 ),
585 risk_level: RiskLevel::Medium,
586 });
587 }
588
589 if signature.contains("*const") || signature.contains("*mut") {
591 if signature.contains("i8")
593 || signature.contains("c_char")
594 || signature.contains("c_void")
595 {
596 issues.push(SafetyIssue {
597 issue_type: SafetyIssueType::FFIBoundary,
598 location: function_node.span.clone(),
599 description: "Function uses C-style raw pointers typical of FFI"
600 .to_string(),
601 rationale: None,
602 mitigation: Some(
603 "Validate pointer parameters and handle null pointers safely"
604 .to_string(),
605 ),
606 risk_level: RiskLevel::High,
607 });
608 }
609 }
610 }
611
612 if let Some(metadata) = function_node.metadata.as_object() {
614 if let Some(function_type) = metadata.get("function_type").and_then(|t| t.as_str()) {
615 if function_type == "extern" {
616 issues.push(SafetyIssue {
617 issue_type: SafetyIssueType::FFIBoundary,
618 location: function_node.span.clone(),
619 description: "Extern function requires FFI safety considerations"
620 .to_string(),
621 rationale: None,
622 mitigation: Some(
623 "Ensure proper parameter validation and error handling".to_string(),
624 ),
625 risk_level: RiskLevel::High,
626 });
627 }
628 }
629 }
630
631 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
633 for &node_id in scope_nodes {
634 if let Some(use_node) = self.nodes.iter().find(|n| n.id == node_id) {
635 if matches!(use_node.kind, NodeKind::Use)
636 && (use_node.name.contains("CString") || use_node.name.contains("CStr"))
637 {
638 issues.push(SafetyIssue {
639 issue_type: SafetyIssueType::FFIBoundary,
640 location: use_node.span.clone(),
641 description: "C string types used - typical of FFI code".to_string(),
642 rationale: None,
643 mitigation: Some(
644 "Ensure proper null termination and UTF-8 validation".to_string(),
645 ),
646 risk_level: RiskLevel::Medium,
647 });
648 }
649 }
650 }
651 }
652
653 issues
654 }
655
656 fn detect_memory_safety_issues(&self, function_node: &Node) -> Vec<SafetyIssue> {
658 let mut issues = Vec::new();
659
660 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
661 for &node_id in scope_nodes {
662 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
663 if matches!(call_node.kind, NodeKind::Call) {
664 if call_node.name.contains("alloc") || call_node.name.contains("dealloc") {
666 issues.push(SafetyIssue {
667 issue_type: SafetyIssueType::ManualMemoryManagement,
668 location: call_node.span.clone(),
669 description: "Manual memory management detected".to_string(),
670 rationale: None,
671 mitigation: Some("Ensure matching allocation/deallocation and consider RAII patterns".to_string()),
672 risk_level: RiskLevel::High,
673 });
674 }
675
676 if call_node.name.contains("transmute") {
678 issues.push(SafetyIssue {
679 issue_type: SafetyIssueType::TypeTransmutation,
680 location: call_node.span.clone(),
681 description: "Type transmutation is extremely dangerous".to_string(),
682 rationale: None,
683 mitigation: Some("Validate size and alignment requirements, consider safer alternatives".to_string()),
684 risk_level: RiskLevel::Critical,
685 });
686 }
687 }
688 }
689 }
690 }
691
692 if !self.scope_map.contains_key(&function_node.id)
694 || self.scope_map.get(&function_node.id).unwrap().is_empty()
695 {
696 for node in &self.nodes {
697 if matches!(node.kind, NodeKind::Call) && node.name.contains("transmute") {
698 issues.push(SafetyIssue {
699 issue_type: SafetyIssueType::TypeTransmutation,
700 location: function_node.span.clone(),
701 description: "Type transmutation is extremely dangerous".to_string(),
702 rationale: None,
703 mitigation: Some(
704 "Validate size and alignment requirements, consider safer alternatives"
705 .to_string(),
706 ),
707 risk_level: RiskLevel::Critical,
708 });
709 }
710 }
711 }
712
713 issues
714 }
715
716 fn analyze_async_patterns(&self, function_node: &Node) -> Vec<ConcurrencyIssue> {
720 let mut issues = Vec::new();
721
722 if let Some(signature) = &function_node.signature {
723 if signature.contains("async") {
724 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
726 for &node_id in scope_nodes {
727 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
728 if matches!(call_node.kind, NodeKind::Call) {
729 if call_node.name.contains("block_on")
731 || call_node.name.contains("wait")
732 {
733 issues.push(ConcurrencyIssue {
734 issue_type: ConcurrencyIssueType::AsyncAntipattern,
735 location: call_node.span.clone(),
736 description: "Blocking operation in async function".to_string(),
737 suggestion: Some("Use async alternatives or spawn_blocking for CPU-bound work".to_string()),
738 severity: ConcurrencySeverity::High,
739 });
740 }
741
742 if call_node.name.contains("async")
744 && !call_node.name.contains(".await")
745 {
746 issues.push(ConcurrencyIssue {
747 issue_type: ConcurrencyIssueType::MissingAwait,
748 location: call_node.span.clone(),
749 description: "Async call without .await".to_string(),
750 suggestion: Some(
751 "Add .await to async function call".to_string(),
752 ),
753 severity: ConcurrencySeverity::High,
754 });
755 }
756 }
757 }
758 }
759 }
760 }
761 }
762
763 issues
764 }
765
766 fn detect_deadlock_potential(&self, function_node: &Node) -> Vec<ConcurrencyIssue> {
768 let mut issues = Vec::new();
769
770 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
771 let mut mutex_calls = Vec::new();
772
773 for &node_id in scope_nodes {
774 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
775 if matches!(call_node.kind, NodeKind::Call) && call_node.name.contains("lock") {
776 mutex_calls.push(call_node);
777 }
778 }
779 }
780
781 if mutex_calls.len() > 1 {
783 issues.push(ConcurrencyIssue {
784 issue_type: ConcurrencyIssueType::DeadlockPotential,
785 location: function_node.span.clone(),
786 description: format!(
787 "Function acquires {} locks, potential deadlock risk",
788 mutex_calls.len()
789 ),
790 suggestion: Some(
791 "Ensure consistent lock ordering or use try_lock with timeouts".to_string(),
792 ),
793 severity: ConcurrencySeverity::High,
794 });
795 }
796 }
797
798 if !self.scope_map.contains_key(&function_node.id)
800 || self.scope_map.get(&function_node.id).unwrap().is_empty()
801 {
802 let lock_calls: Vec<&Node> = self
803 .nodes
804 .iter()
805 .filter(|n| matches!(n.kind, NodeKind::Call) && n.name.contains("lock"))
806 .collect();
807
808 if lock_calls.len() > 1 {
809 issues.push(ConcurrencyIssue {
810 issue_type: ConcurrencyIssueType::DeadlockPotential,
811 location: function_node.span.clone(),
812 description: format!(
813 "Multiple mutex locks detected ({}), potential deadlock risk",
814 lock_calls.len()
815 ),
816 suggestion: Some(
817 "Ensure consistent lock ordering or use try_lock with timeouts".to_string(),
818 ),
819 severity: ConcurrencySeverity::High,
820 });
821 }
822 }
823
824 issues
825 }
826
827 fn analyze_thread_safety(&self, function_node: &Node) -> Vec<ConcurrencyIssue> {
829 let mut issues = Vec::new();
830
831 if let Some(signature) = &function_node.signature {
833 if signature.contains("Rc<") || signature.contains("RefCell<") {
835 issues.push(ConcurrencyIssue {
836 issue_type: ConcurrencyIssueType::ThreadSafetyViolation,
837 location: function_node.span.clone(),
838 description: "Function uses non-thread-safe types (Rc, RefCell)".to_string(),
839 suggestion: Some(
840 "Consider Arc and Mutex for thread-safe alternatives".to_string(),
841 ),
842 severity: ConcurrencySeverity::Medium,
843 });
844 }
845 }
846
847 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
849 for &node_id in scope_nodes {
850 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
851 if matches!(call_node.kind, NodeKind::Call) {
852 if call_node.name.contains("Rc::new")
854 || call_node.name.contains("RefCell::new")
855 {
856 issues.push(ConcurrencyIssue {
857 issue_type: ConcurrencyIssueType::ThreadSafetyViolation,
858 location: call_node.span.clone(),
859 description: "Non-thread-safe type used (Rc/RefCell)".to_string(),
860 suggestion: Some(
861 "Consider Arc and Mutex for thread-safe alternatives"
862 .to_string(),
863 ),
864 severity: ConcurrencySeverity::Medium,
865 });
866 }
867 }
868 }
869 }
870 }
871
872 for use_node in &self.nodes {
874 if matches!(use_node.kind, NodeKind::Use)
875 && (use_node.name.contains("std::rc::Rc")
876 || use_node.name.contains("std::cell::RefCell"))
877 {
878 issues.push(ConcurrencyIssue {
880 issue_type: ConcurrencyIssueType::ThreadSafetyViolation,
881 location: function_node.span.clone(),
882 description: "Module imports non-thread-safe types (Rc, RefCell)".to_string(),
883 suggestion: Some(
884 "Consider Arc and Mutex for thread-safe alternatives".to_string(),
885 ),
886 severity: ConcurrencySeverity::Low,
887 });
888 }
889 }
890
891 issues
892 }
893
894 fn analyze_channel_usage(&self, function_node: &Node) -> Vec<ConcurrencyIssue> {
896 let mut issues = Vec::new();
897
898 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
899 for &node_id in scope_nodes {
900 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
901 if matches!(call_node.kind, NodeKind::Call) {
902 if call_node.name.contains("unbounded") {
904 issues.push(ConcurrencyIssue {
905 issue_type: ConcurrencyIssueType::UnboundedChannel,
906 location: call_node.span.clone(),
907 description: "Unbounded channel can lead to memory growth"
908 .to_string(),
909 suggestion: Some(
910 "Consider bounded channels with appropriate capacity"
911 .to_string(),
912 ),
913 severity: ConcurrencySeverity::Medium,
914 });
915 }
916 }
917 }
918 }
919 }
920
921 issues
922 }
923
924 fn analyze_send_sync_traits(&self, type_node: &Node) -> Vec<ConcurrencyIssue> {
926 let mut issues = Vec::new();
927
928 let type_name = &type_node.name;
930 for node in &self.nodes {
931 if matches!(node.kind, NodeKind::Impl)
932 && node.name.contains(type_name)
933 && (node.name.contains("Send") || node.name.contains("Sync"))
934 {
935 issues.push(ConcurrencyIssue {
936 issue_type: ConcurrencyIssueType::ManualSendSync,
937 location: node.span.clone(),
938 description: "Manual Send/Sync implementation requires careful review"
939 .to_string(),
940 suggestion: Some("Ensure thread safety guarantees are maintained".to_string()),
941 severity: ConcurrencySeverity::High,
942 });
943 }
944 }
945
946 issues
947 }
948
949 pub fn analyze_trait_implementations(&self) -> Vec<TraitImplementation> {
951 let mut implementations = Vec::new();
952
953 for node in &self.nodes {
954 if matches!(node.kind, NodeKind::Impl) {
955 if node.name.contains(" for ") {
957 let parts: Vec<&str> = node.name.split(" for ").collect();
958 if parts.len() == 2 {
959 implementations.push(TraitImplementation {
960 trait_name: parts[0].to_string(),
961 type_name: parts[1].to_string(),
962 impl_node: node.clone(),
963 });
964 }
965 }
966 }
967 }
968
969 implementations
970 }
971
972 pub fn analyze_unsafe_usage(&self) -> Vec<UnsafeUsage> {
974 let mut unsafe_usages = Vec::new();
975
976 for node in &self.nodes {
977 if matches!(node.kind, NodeKind::Function | NodeKind::Method) {
978 if let Some(signature) = &node.signature {
979 if signature.contains("unsafe") {
980 unsafe_usages.push(UnsafeUsage {
981 location: node.span.clone(),
982 usage_type: UnsafeType::Function,
983 description: format!("Unsafe function: {}", node.name),
984 });
985 }
986 }
987 }
988 }
989
990 unsafe_usages
991 }
992
993 pub fn analyze_lifetime_usage(&self) -> Vec<LifetimeUsage> {
995 let mut lifetime_usages = Vec::new();
996
997 for node in &self.nodes {
998 if matches!(node.kind, NodeKind::Lifetime) {
999 lifetime_usages.push(LifetimeUsage {
1000 name: node.name.clone(),
1001 location: node.span.clone(),
1002 scope: self.find_lifetime_scope(node),
1003 });
1004 }
1005 }
1006
1007 lifetime_usages
1008 }
1009
1010 pub fn analyze_macro_usage(&self) -> Vec<MacroUsage> {
1012 let mut macro_usages = Vec::new();
1013
1014 for node in &self.nodes {
1015 if matches!(node.kind, NodeKind::Call) && node.name.ends_with('!') {
1016 macro_usages.push(MacroUsage {
1017 name: node.name.clone(),
1018 location: node.span.clone(),
1019 macro_type: MacroType::Invocation,
1020 });
1021 }
1022 }
1023
1024 macro_usages
1025 }
1026
1027 fn find_lifetime_scope(&self, _lifetime_node: &Node) -> LifetimeScope {
1029 LifetimeScope::Function
1031 }
1032}
1033
1034#[derive(Debug, Clone)]
1038pub struct RustAnalysisResult {
1039 pub ownership_patterns: Vec<OwnershipPattern>,
1040 pub performance_issues: Vec<PerformanceIssue>,
1041 pub safety_issues: Vec<SafetyIssue>,
1042 pub concurrency_issues: Vec<ConcurrencyIssue>,
1043 pub trait_implementations: Vec<TraitImplementation>,
1044 pub unsafe_usage: Vec<UnsafeUsage>,
1045 pub lifetime_usage: Vec<LifetimeUsage>,
1046 pub macro_usage: Vec<MacroUsage>,
1047}
1048
1049#[derive(Debug, Clone)]
1051pub struct OwnershipPattern {
1052 pub pattern_type: OwnershipPatternType,
1053 pub location: Span,
1054 pub description: String,
1055 pub suggestion: Option<String>,
1056 pub severity: Severity,
1057}
1058
1059#[derive(Debug, Clone)]
1061pub enum OwnershipPatternType {
1062 UnnecessaryClone,
1063 InefficientBorrowing,
1064 PotentialMoveError,
1065 OptimalOwnership,
1066 UnnecessaryOwned,
1067 MultipleMutableBorrows,
1068 InconsistentNaming,
1069}
1070
1071#[derive(Debug, Clone)]
1073pub struct PerformanceIssue {
1074 pub issue_type: PerformanceIssueType,
1075 pub location: Span,
1076 pub description: String,
1077 pub suggestion: Option<String>,
1078 pub impact: PerformanceImpact,
1079}
1080
1081#[derive(Debug, Clone)]
1083pub enum PerformanceIssueType {
1084 FrequentAllocations,
1085 StringConcatenation,
1086 IteratorChains,
1087 UnoptimizedCollections,
1088 SuboptimalAlgorithms,
1089}
1090
1091#[derive(Debug, Clone)]
1093pub enum PerformanceImpact {
1094 Low,
1095 Medium,
1096 High,
1097 Critical,
1098}
1099
1100#[derive(Debug, Clone)]
1102pub struct SafetyIssue {
1103 pub issue_type: SafetyIssueType,
1104 pub location: Span,
1105 pub description: String,
1106 pub rationale: Option<String>,
1107 pub mitigation: Option<String>,
1108 pub risk_level: RiskLevel,
1109}
1110
1111#[derive(Debug, Clone)]
1113pub enum SafetyIssueType {
1114 UnsafeFunction,
1115 RawPointerDereference,
1116 FFIBoundary,
1117 ManualMemoryManagement,
1118 TypeTransmutation,
1119}
1120
1121#[derive(Debug, Clone)]
1123pub enum RiskLevel {
1124 Low,
1125 Medium,
1126 High,
1127 Critical,
1128}
1129
1130#[derive(Debug, Clone)]
1132pub struct ConcurrencyIssue {
1133 pub issue_type: ConcurrencyIssueType,
1134 pub location: Span,
1135 pub description: String,
1136 pub suggestion: Option<String>,
1137 pub severity: ConcurrencySeverity,
1138}
1139
1140#[derive(Debug, Clone)]
1142pub enum ConcurrencyIssueType {
1143 AsyncAntipattern,
1144 MissingAwait,
1145 DeadlockPotential,
1146 ThreadSafetyViolation,
1147 UnboundedChannel,
1148 ManualSendSync,
1149}
1150
1151#[derive(Debug, Clone)]
1153pub enum ConcurrencySeverity {
1154 Low,
1155 Medium,
1156 High,
1157 Critical,
1158}
1159
1160#[derive(Debug, Clone)]
1162pub enum Severity {
1163 Low,
1164 Medium,
1165 High,
1166 Critical,
1167}
1168
1169#[derive(Debug, Clone)]
1171pub struct TraitImplementation {
1172 pub trait_name: String,
1173 pub type_name: String,
1174 pub impl_node: Node,
1175}
1176
1177#[derive(Debug, Clone)]
1179pub struct UnsafeUsage {
1180 pub location: Span,
1181 pub usage_type: UnsafeType,
1182 pub description: String,
1183}
1184
1185#[derive(Debug, Clone)]
1187pub enum UnsafeType {
1188 Function,
1189 Block,
1190 Trait,
1191 Impl,
1192}
1193
1194#[derive(Debug, Clone)]
1196pub struct LifetimeUsage {
1197 pub name: String,
1198 pub location: Span,
1199 pub scope: LifetimeScope,
1200}
1201
1202#[derive(Debug, Clone)]
1204pub enum LifetimeScope {
1205 Function,
1206 Struct,
1207 Trait,
1208 Impl,
1209}
1210
1211#[derive(Debug, Clone)]
1213pub struct MacroUsage {
1214 pub name: String,
1215 pub location: Span,
1216 pub macro_type: MacroType,
1217}
1218
1219#[derive(Debug, Clone)]
1221pub enum MacroType {
1222 Definition,
1223 Invocation,
1224}