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!("Function has {mut_borrow_count} mutable borrows, potential for borrowing conflicts"),
166 suggestion: Some(
167 "Consider refactoring to reduce mutable borrows or use interior mutability"
168 .to_string(),
169 ),
170 severity: Severity::High,
171 });
172 }
173 }
174
175 patterns
176 }
177
178 fn detect_unnecessary_clones(&self, function_node: &Node) -> Vec<OwnershipPattern> {
180 let mut patterns = Vec::new();
181
182 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
184 for &node_id in scope_nodes {
185 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
186 if matches!(call_node.kind, NodeKind::Call) && call_node.name.contains("clone")
187 {
188 patterns.push(OwnershipPattern {
189 pattern_type: OwnershipPatternType::UnnecessaryClone,
190 location: call_node.span.clone(),
191 description: "Potential unnecessary clone() call".to_string(),
192 suggestion: Some(
193 "Check if borrowing would work instead of cloning".to_string(),
194 ),
195 severity: Severity::Medium,
196 });
197 }
198 }
199 }
200 }
201
202 patterns
203 }
204
205 fn detect_inefficient_borrowing(&self, function_node: &Node) -> Vec<OwnershipPattern> {
207 let mut patterns = Vec::new();
208
209 if let Some(metadata) = function_node.metadata.as_object() {
211 if let Some(params) = metadata.get("parameters").and_then(|p| p.as_array()) {
212 for param in params {
213 if let Some(param_obj) = param.as_object() {
214 if let Some(ownership) = param_obj.get("ownership").and_then(|o| o.as_str())
215 {
216 if ownership == "borrowed" {
217 if let Some(usage) =
218 param_obj.get("usage_pattern").and_then(|u| u.as_str())
219 {
220 if usage == "stored" {
221 patterns.push(OwnershipPattern {
222 pattern_type: OwnershipPatternType::InefficientBorrowing,
223 location: function_node.span.clone(),
224 description: "Borrowed parameter is stored, consider taking ownership".to_string(),
225 suggestion: Some("Take ownership of the parameter if it needs to be stored".to_string()),
226 severity: Severity::Medium,
227 });
228 }
229 }
230 }
231 }
232 }
233 }
234 }
235 }
236
237 patterns
238 }
239
240 fn detect_move_semantics_issues(&self, function_node: &Node) -> Vec<OwnershipPattern> {
242 let mut patterns = Vec::new();
243
244 if let Some(signature) = &function_node.signature {
246 if signature.contains("self")
247 && !signature.contains("&self")
248 && !signature.contains("&mut self")
249 {
250 patterns.push(OwnershipPattern {
251 pattern_type: OwnershipPatternType::PotentialMoveError,
252 location: function_node.span.clone(),
253 description: "Method takes self by value, object will be moved".to_string(),
254 suggestion: Some(
255 "Consider if borrowing (&self or &mut self) would be more appropriate"
256 .to_string(),
257 ),
258 severity: Severity::Low,
259 });
260 }
261 }
262
263 patterns
264 }
265
266 fn analyze_variable_ownership(&self, variable_node: &Node) -> Vec<OwnershipPattern> {
268 let mut patterns = Vec::new();
269
270 if variable_node.name.ends_with("_ref") || variable_node.name.starts_with("ref_") {
272 patterns.push(OwnershipPattern {
273 pattern_type: OwnershipPatternType::InconsistentNaming,
274 location: variable_node.span.clone(),
275 description: "Variable name suggests reference but type might be owned".to_string(),
276 suggestion: Some(
277 "Ensure naming convention matches ownership semantics".to_string(),
278 ),
279 severity: Severity::Low,
280 });
281 }
282
283 patterns
284 }
285
286 fn detect_allocation_patterns(&self, function_node: &Node) -> Vec<PerformanceIssue> {
290 let mut issues = Vec::new();
291
292 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
293 for &node_id in scope_nodes {
294 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
295 if matches!(call_node.kind, NodeKind::Call) {
296 if call_node.name.contains("Vec::new") || call_node.name.contains("vec!") {
298 issues.push(PerformanceIssue {
299 issue_type: PerformanceIssueType::FrequentAllocations,
300 location: call_node.span.clone(),
301 description: "Vector allocation detected - ensure not in hot path"
302 .to_string(),
303 suggestion: Some(
304 "Consider pre-allocating with capacity or reusing allocations"
305 .to_string(),
306 ),
307 impact: PerformanceImpact::Medium,
308 });
309 }
310
311 if call_node.name.contains("HashMap::new") {
313 issues.push(PerformanceIssue {
314 issue_type: PerformanceIssueType::UnoptimizedCollections,
315 location: call_node.span.clone(),
316 description: "HashMap created without initial capacity".to_string(),
317 suggestion: Some(
318 "Use HashMap::with_capacity() if size is known".to_string(),
319 ),
320 impact: PerformanceImpact::Medium,
321 });
322 }
323 }
324 }
325 }
326 }
327
328 issues
329 }
330
331 fn detect_string_inefficiencies(&self, function_node: &Node) -> Vec<PerformanceIssue> {
333 let mut issues = Vec::new();
334
335 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
336 let mut string_pushes = 0;
337
338 for &node_id in scope_nodes {
339 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
340 if matches!(call_node.kind, NodeKind::Call) {
341 if call_node.name.contains("push_str") || call_node.name.contains("push") {
343 string_pushes += 1;
344 }
345 }
346 }
347 }
348
349 if string_pushes >= 3 {
351 issues.push(PerformanceIssue {
352 issue_type: PerformanceIssueType::StringConcatenation,
353 location: function_node.span.clone(),
354 description: format!(
355 "Multiple string operations detected: {string_pushes} push operations"
356 ),
357 suggestion: Some(
358 "Consider using String::with_capacity() or format! macro".to_string(),
359 ),
360 impact: PerformanceImpact::Medium,
361 });
362 }
363 }
364
365 if !self.scope_map.contains_key(&function_node.id)
367 || self.scope_map.get(&function_node.id).unwrap().is_empty()
368 {
369 let mut push_str_count = 0;
370
371 for node in &self.nodes {
372 if matches!(node.kind, NodeKind::Call) && node.name.contains("push_str") {
373 push_str_count += 1;
374 }
375 }
376
377 if push_str_count >= 3 {
378 issues.push(PerformanceIssue {
379 issue_type: PerformanceIssueType::StringConcatenation,
380 location: function_node.span.clone(),
381 description: format!(
382 "Multiple string operations detected: {push_str_count} push_str calls"
383 ),
384 suggestion: Some(
385 "Consider using String::with_capacity() or format! macro".to_string(),
386 ),
387 impact: PerformanceImpact::Medium,
388 });
389 }
390 }
391
392 issues
393 }
394
395 fn detect_iterator_inefficiencies(&self, function_node: &Node) -> Vec<PerformanceIssue> {
397 let mut issues = Vec::new();
398
399 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
400 for &node_id in scope_nodes {
401 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
402 if matches!(call_node.kind, NodeKind::Call) {
403 if call_node.name.contains("collect") {
405 issues.push(PerformanceIssue {
406 issue_type: PerformanceIssueType::IteratorChains,
407 location: call_node.span.clone(),
408 description:
409 "collect() call may be unnecessary if chaining iterators"
410 .to_string(),
411 suggestion: Some(
412 "Consider avoiding intermediate collection if possible"
413 .to_string(),
414 ),
415 impact: PerformanceImpact::Medium,
416 });
417 }
418 }
419 }
420 }
421 }
422
423 issues
424 }
425
426 fn detect_collection_inefficiencies(&self, function_node: &Node) -> Vec<PerformanceIssue> {
428 let mut issues = Vec::new();
429
430 if let Some(signature) = &function_node.signature {
431 if signature.contains("Vec<") && !signature.contains("&mut") {
433 issues.push(PerformanceIssue {
434 issue_type: PerformanceIssueType::UnoptimizedCollections,
435 location: function_node.span.clone(),
436 description: "Function takes Vec<T> when &[T] slice might suffice".to_string(),
437 suggestion: Some(
438 "Consider using slice (&[T]) for read-only access".to_string(),
439 ),
440 impact: PerformanceImpact::Low,
441 });
442 }
443 }
444
445 issues
446 }
447
448 fn analyze_call_performance(&self, call_node: &Node) -> Vec<PerformanceIssue> {
450 let mut issues = Vec::new();
451
452 if call_node.name.contains("sort") && !call_node.name.contains("sort_unstable") {
454 issues.push(PerformanceIssue {
455 issue_type: PerformanceIssueType::SuboptimalAlgorithms,
456 location: call_node.span.clone(),
457 description: "Using stable sort when unstable sort might be sufficient".to_string(),
458 suggestion: Some(
459 "Consider sort_unstable() for better performance if stability not required"
460 .to_string(),
461 ),
462 impact: PerformanceImpact::Low,
463 });
464 }
465
466 issues
467 }
468
469 fn is_unsafe_function(&self, function_node: &Node) -> bool {
473 if let Some(signature) = &function_node.signature {
475 if signature.contains("unsafe") {
476 return true;
477 }
478 }
479
480 if function_node.name.contains("unsafe") {
482 return true;
483 }
484
485 if matches!(function_node.kind, NodeKind::Method) {
487 if let Some(metadata) = function_node.metadata.as_object() {
488 if let Some(unsafe_flag) = metadata.get("unsafe").and_then(|u| u.as_bool()) {
489 if unsafe_flag {
490 return true;
491 }
492 }
493 if let Some(modifiers) = metadata.get("modifiers").and_then(|m| m.as_array()) {
494 for modifier in modifiers {
495 if let Some(mod_str) = modifier.as_str() {
496 if mod_str == "unsafe" {
497 return true;
498 }
499 }
500 }
501 }
502 }
503 }
504
505 false
506 }
507
508 fn analyze_unsafe_function(&self, function_node: &Node) -> Vec<SafetyIssue> {
510 let mut issues = Vec::new();
511
512 issues.push(SafetyIssue {
513 issue_type: SafetyIssueType::UnsafeFunction,
514 location: function_node.span.clone(),
515 description: "Unsafe function requires careful review".to_string(),
516 rationale: None,
517 mitigation: Some(
518 "Document safety invariants and ensure all callers uphold them".to_string(),
519 ),
520 risk_level: RiskLevel::High,
521 });
522
523 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
525 for &node_id in scope_nodes {
526 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
527 if matches!(call_node.kind, NodeKind::Call) {
528 if call_node.name.contains("*") && call_node.name.contains("raw") {
530 issues.push(SafetyIssue {
531 issue_type: SafetyIssueType::RawPointerDereference,
532 location: call_node.span.clone(),
533 description: "Raw pointer dereference detected".to_string(),
534 rationale: None,
535 mitigation: Some(
536 "Ensure pointer is valid and properly aligned".to_string(),
537 ),
538 risk_level: RiskLevel::Critical,
539 });
540 }
541 }
542 }
543 }
544 }
545
546 issues
547 }
548
549 fn detect_ffi_patterns(&self, function_node: &Node) -> Vec<SafetyIssue> {
551 let mut issues = Vec::new();
552
553 if let Some(signature) = &function_node.signature {
555 if signature.contains("extern") && signature.contains("\"C\"") {
557 issues.push(SafetyIssue {
558 issue_type: SafetyIssueType::FFIBoundary,
559 location: function_node.span.clone(),
560 description: "FFI function boundary requires careful handling".to_string(),
561 rationale: None,
562 mitigation: Some(
563 "Validate all parameters and handle C-style errors properly".to_string(),
564 ),
565 risk_level: RiskLevel::High,
566 });
567 }
568
569 if signature.contains("CString") || signature.contains("CStr") {
571 issues.push(SafetyIssue {
572 issue_type: SafetyIssueType::FFIBoundary,
573 location: function_node.span.clone(),
574 description: "C string handling requires null termination validation"
575 .to_string(),
576 rationale: None,
577 mitigation: Some(
578 "Ensure proper null termination and UTF-8 validation".to_string(),
579 ),
580 risk_level: RiskLevel::Medium,
581 });
582 }
583
584 if signature.contains("*const") || signature.contains("*mut") {
586 if signature.contains("i8")
588 || signature.contains("c_char")
589 || signature.contains("c_void")
590 {
591 issues.push(SafetyIssue {
592 issue_type: SafetyIssueType::FFIBoundary,
593 location: function_node.span.clone(),
594 description: "Function uses C-style raw pointers typical of FFI"
595 .to_string(),
596 rationale: None,
597 mitigation: Some(
598 "Validate pointer parameters and handle null pointers safely"
599 .to_string(),
600 ),
601 risk_level: RiskLevel::High,
602 });
603 }
604 }
605 }
606
607 if let Some(metadata) = function_node.metadata.as_object() {
609 if let Some(function_type) = metadata.get("function_type").and_then(|t| t.as_str()) {
610 if function_type == "extern" {
611 issues.push(SafetyIssue {
612 issue_type: SafetyIssueType::FFIBoundary,
613 location: function_node.span.clone(),
614 description: "Extern function requires FFI safety considerations"
615 .to_string(),
616 rationale: None,
617 mitigation: Some(
618 "Ensure proper parameter validation and error handling".to_string(),
619 ),
620 risk_level: RiskLevel::High,
621 });
622 }
623 }
624 }
625
626 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
628 for &node_id in scope_nodes {
629 if let Some(use_node) = self.nodes.iter().find(|n| n.id == node_id) {
630 if matches!(use_node.kind, NodeKind::Use)
631 && (use_node.name.contains("CString") || use_node.name.contains("CStr"))
632 {
633 issues.push(SafetyIssue {
634 issue_type: SafetyIssueType::FFIBoundary,
635 location: use_node.span.clone(),
636 description: "C string types used - typical of FFI code".to_string(),
637 rationale: None,
638 mitigation: Some(
639 "Ensure proper null termination and UTF-8 validation".to_string(),
640 ),
641 risk_level: RiskLevel::Medium,
642 });
643 }
644 }
645 }
646 }
647
648 issues
649 }
650
651 fn detect_memory_safety_issues(&self, function_node: &Node) -> Vec<SafetyIssue> {
653 let mut issues = Vec::new();
654
655 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
656 for &node_id in scope_nodes {
657 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
658 if matches!(call_node.kind, NodeKind::Call) {
659 if call_node.name.contains("alloc") || call_node.name.contains("dealloc") {
661 issues.push(SafetyIssue {
662 issue_type: SafetyIssueType::ManualMemoryManagement,
663 location: call_node.span.clone(),
664 description: "Manual memory management detected".to_string(),
665 rationale: None,
666 mitigation: Some("Ensure matching allocation/deallocation and consider RAII patterns".to_string()),
667 risk_level: RiskLevel::High,
668 });
669 }
670
671 if call_node.name.contains("transmute") {
673 issues.push(SafetyIssue {
674 issue_type: SafetyIssueType::TypeTransmutation,
675 location: call_node.span.clone(),
676 description: "Type transmutation is extremely dangerous".to_string(),
677 rationale: None,
678 mitigation: Some("Validate size and alignment requirements, consider safer alternatives".to_string()),
679 risk_level: RiskLevel::Critical,
680 });
681 }
682 }
683 }
684 }
685 }
686
687 if !self.scope_map.contains_key(&function_node.id)
689 || self.scope_map.get(&function_node.id).unwrap().is_empty()
690 {
691 for node in &self.nodes {
692 if matches!(node.kind, NodeKind::Call) && node.name.contains("transmute") {
693 issues.push(SafetyIssue {
694 issue_type: SafetyIssueType::TypeTransmutation,
695 location: function_node.span.clone(),
696 description: "Type transmutation is extremely dangerous".to_string(),
697 rationale: None,
698 mitigation: Some(
699 "Validate size and alignment requirements, consider safer alternatives"
700 .to_string(),
701 ),
702 risk_level: RiskLevel::Critical,
703 });
704 }
705 }
706 }
707
708 issues
709 }
710
711 fn analyze_async_patterns(&self, function_node: &Node) -> Vec<ConcurrencyIssue> {
715 let mut issues = Vec::new();
716
717 if let Some(signature) = &function_node.signature {
718 if signature.contains("async") {
719 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
721 for &node_id in scope_nodes {
722 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
723 if matches!(call_node.kind, NodeKind::Call) {
724 if call_node.name.contains("block_on")
726 || call_node.name.contains("wait")
727 {
728 issues.push(ConcurrencyIssue {
729 issue_type: ConcurrencyIssueType::AsyncAntipattern,
730 location: call_node.span.clone(),
731 description: "Blocking operation in async function".to_string(),
732 suggestion: Some("Use async alternatives or spawn_blocking for CPU-bound work".to_string()),
733 severity: ConcurrencySeverity::High,
734 });
735 }
736
737 if call_node.name.contains("async")
739 && !call_node.name.contains(".await")
740 {
741 issues.push(ConcurrencyIssue {
742 issue_type: ConcurrencyIssueType::MissingAwait,
743 location: call_node.span.clone(),
744 description: "Async call without .await".to_string(),
745 suggestion: Some(
746 "Add .await to async function call".to_string(),
747 ),
748 severity: ConcurrencySeverity::High,
749 });
750 }
751 }
752 }
753 }
754 }
755 }
756 }
757
758 issues
759 }
760
761 fn detect_deadlock_potential(&self, function_node: &Node) -> Vec<ConcurrencyIssue> {
763 let mut issues = Vec::new();
764
765 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
766 let mut mutex_calls = Vec::new();
767
768 for &node_id in scope_nodes {
769 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
770 if matches!(call_node.kind, NodeKind::Call) && call_node.name.contains("lock") {
771 mutex_calls.push(call_node);
772 }
773 }
774 }
775
776 if mutex_calls.len() > 1 {
778 issues.push(ConcurrencyIssue {
779 issue_type: ConcurrencyIssueType::DeadlockPotential,
780 location: function_node.span.clone(),
781 description: format!(
782 "Function acquires {} locks, potential deadlock risk",
783 mutex_calls.len()
784 ),
785 suggestion: Some(
786 "Ensure consistent lock ordering or use try_lock with timeouts".to_string(),
787 ),
788 severity: ConcurrencySeverity::High,
789 });
790 }
791 }
792
793 if !self.scope_map.contains_key(&function_node.id)
795 || self.scope_map.get(&function_node.id).unwrap().is_empty()
796 {
797 let lock_calls: Vec<&Node> = self
798 .nodes
799 .iter()
800 .filter(|n| matches!(n.kind, NodeKind::Call) && n.name.contains("lock"))
801 .collect();
802
803 if lock_calls.len() > 1 {
804 issues.push(ConcurrencyIssue {
805 issue_type: ConcurrencyIssueType::DeadlockPotential,
806 location: function_node.span.clone(),
807 description: format!(
808 "Multiple mutex locks detected ({}), potential deadlock risk",
809 lock_calls.len()
810 ),
811 suggestion: Some(
812 "Ensure consistent lock ordering or use try_lock with timeouts".to_string(),
813 ),
814 severity: ConcurrencySeverity::High,
815 });
816 }
817 }
818
819 issues
820 }
821
822 fn analyze_thread_safety(&self, function_node: &Node) -> Vec<ConcurrencyIssue> {
824 let mut issues = Vec::new();
825
826 if let Some(signature) = &function_node.signature {
828 if signature.contains("Rc<") || signature.contains("RefCell<") {
830 issues.push(ConcurrencyIssue {
831 issue_type: ConcurrencyIssueType::ThreadSafetyViolation,
832 location: function_node.span.clone(),
833 description: "Function uses non-thread-safe types (Rc, RefCell)".to_string(),
834 suggestion: Some(
835 "Consider Arc and Mutex for thread-safe alternatives".to_string(),
836 ),
837 severity: ConcurrencySeverity::Medium,
838 });
839 }
840 }
841
842 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
844 for &node_id in scope_nodes {
845 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
846 if matches!(call_node.kind, NodeKind::Call) {
847 if call_node.name.contains("Rc::new")
849 || call_node.name.contains("RefCell::new")
850 {
851 issues.push(ConcurrencyIssue {
852 issue_type: ConcurrencyIssueType::ThreadSafetyViolation,
853 location: call_node.span.clone(),
854 description: "Non-thread-safe type used (Rc/RefCell)".to_string(),
855 suggestion: Some(
856 "Consider Arc and Mutex for thread-safe alternatives"
857 .to_string(),
858 ),
859 severity: ConcurrencySeverity::Medium,
860 });
861 }
862 }
863 }
864 }
865 }
866
867 for use_node in &self.nodes {
869 if matches!(use_node.kind, NodeKind::Use)
870 && (use_node.name.contains("std::rc::Rc")
871 || use_node.name.contains("std::cell::RefCell"))
872 {
873 issues.push(ConcurrencyIssue {
875 issue_type: ConcurrencyIssueType::ThreadSafetyViolation,
876 location: function_node.span.clone(),
877 description: "Module imports non-thread-safe types (Rc, RefCell)".to_string(),
878 suggestion: Some(
879 "Consider Arc and Mutex for thread-safe alternatives".to_string(),
880 ),
881 severity: ConcurrencySeverity::Low,
882 });
883 }
884 }
885
886 issues
887 }
888
889 fn analyze_channel_usage(&self, function_node: &Node) -> Vec<ConcurrencyIssue> {
891 let mut issues = Vec::new();
892
893 if let Some(scope_nodes) = self.scope_map.get(&function_node.id) {
894 for &node_id in scope_nodes {
895 if let Some(call_node) = self.nodes.iter().find(|n| n.id == node_id) {
896 if matches!(call_node.kind, NodeKind::Call) {
897 if call_node.name.contains("unbounded") {
899 issues.push(ConcurrencyIssue {
900 issue_type: ConcurrencyIssueType::UnboundedChannel,
901 location: call_node.span.clone(),
902 description: "Unbounded channel can lead to memory growth"
903 .to_string(),
904 suggestion: Some(
905 "Consider bounded channels with appropriate capacity"
906 .to_string(),
907 ),
908 severity: ConcurrencySeverity::Medium,
909 });
910 }
911 }
912 }
913 }
914 }
915
916 issues
917 }
918
919 fn analyze_send_sync_traits(&self, type_node: &Node) -> Vec<ConcurrencyIssue> {
921 let mut issues = Vec::new();
922
923 let type_name = &type_node.name;
925 for node in &self.nodes {
926 if matches!(node.kind, NodeKind::Impl)
927 && node.name.contains(type_name)
928 && (node.name.contains("Send") || node.name.contains("Sync"))
929 {
930 issues.push(ConcurrencyIssue {
931 issue_type: ConcurrencyIssueType::ManualSendSync,
932 location: node.span.clone(),
933 description: "Manual Send/Sync implementation requires careful review"
934 .to_string(),
935 suggestion: Some("Ensure thread safety guarantees are maintained".to_string()),
936 severity: ConcurrencySeverity::High,
937 });
938 }
939 }
940
941 issues
942 }
943
944 pub fn analyze_trait_implementations(&self) -> Vec<TraitImplementation> {
946 let mut implementations = Vec::new();
947
948 for node in &self.nodes {
949 if matches!(node.kind, NodeKind::Impl) {
950 if node.name.contains(" for ") {
952 let parts: Vec<&str> = node.name.split(" for ").collect();
953 if parts.len() == 2 {
954 implementations.push(TraitImplementation {
955 trait_name: parts[0].to_string(),
956 type_name: parts[1].to_string(),
957 impl_node: node.clone(),
958 });
959 }
960 }
961 }
962 }
963
964 implementations
965 }
966
967 pub fn analyze_unsafe_usage(&self) -> Vec<UnsafeUsage> {
969 let mut unsafe_usages = Vec::new();
970
971 for node in &self.nodes {
972 if matches!(node.kind, NodeKind::Function | NodeKind::Method) {
973 if let Some(signature) = &node.signature {
974 if signature.contains("unsafe") {
975 unsafe_usages.push(UnsafeUsage {
976 location: node.span.clone(),
977 usage_type: UnsafeType::Function,
978 description: format!("Unsafe function: {}", node.name),
979 });
980 }
981 }
982 }
983 }
984
985 unsafe_usages
986 }
987
988 pub fn analyze_lifetime_usage(&self) -> Vec<LifetimeUsage> {
990 let mut lifetime_usages = Vec::new();
991
992 for node in &self.nodes {
993 if matches!(node.kind, NodeKind::Lifetime) {
994 lifetime_usages.push(LifetimeUsage {
995 name: node.name.clone(),
996 location: node.span.clone(),
997 scope: self.find_lifetime_scope(node),
998 });
999 }
1000 }
1001
1002 lifetime_usages
1003 }
1004
1005 pub fn analyze_macro_usage(&self) -> Vec<MacroUsage> {
1007 let mut macro_usages = Vec::new();
1008
1009 for node in &self.nodes {
1010 if matches!(node.kind, NodeKind::Call) && node.name.ends_with('!') {
1011 macro_usages.push(MacroUsage {
1012 name: node.name.clone(),
1013 location: node.span.clone(),
1014 macro_type: MacroType::Invocation,
1015 });
1016 }
1017 }
1018
1019 macro_usages
1020 }
1021
1022 fn find_lifetime_scope(&self, _lifetime_node: &Node) -> LifetimeScope {
1024 LifetimeScope::Function
1026 }
1027}
1028
1029#[derive(Debug, Clone)]
1033pub struct RustAnalysisResult {
1034 pub ownership_patterns: Vec<OwnershipPattern>,
1035 pub performance_issues: Vec<PerformanceIssue>,
1036 pub safety_issues: Vec<SafetyIssue>,
1037 pub concurrency_issues: Vec<ConcurrencyIssue>,
1038 pub trait_implementations: Vec<TraitImplementation>,
1039 pub unsafe_usage: Vec<UnsafeUsage>,
1040 pub lifetime_usage: Vec<LifetimeUsage>,
1041 pub macro_usage: Vec<MacroUsage>,
1042}
1043
1044#[derive(Debug, Clone)]
1046pub struct OwnershipPattern {
1047 pub pattern_type: OwnershipPatternType,
1048 pub location: Span,
1049 pub description: String,
1050 pub suggestion: Option<String>,
1051 pub severity: Severity,
1052}
1053
1054#[derive(Debug, Clone)]
1056pub enum OwnershipPatternType {
1057 UnnecessaryClone,
1058 InefficientBorrowing,
1059 PotentialMoveError,
1060 OptimalOwnership,
1061 UnnecessaryOwned,
1062 MultipleMutableBorrows,
1063 InconsistentNaming,
1064}
1065
1066#[derive(Debug, Clone)]
1068pub struct PerformanceIssue {
1069 pub issue_type: PerformanceIssueType,
1070 pub location: Span,
1071 pub description: String,
1072 pub suggestion: Option<String>,
1073 pub impact: PerformanceImpact,
1074}
1075
1076#[derive(Debug, Clone)]
1078pub enum PerformanceIssueType {
1079 FrequentAllocations,
1080 StringConcatenation,
1081 IteratorChains,
1082 UnoptimizedCollections,
1083 SuboptimalAlgorithms,
1084}
1085
1086#[derive(Debug, Clone)]
1088pub enum PerformanceImpact {
1089 Low,
1090 Medium,
1091 High,
1092 Critical,
1093}
1094
1095#[derive(Debug, Clone)]
1097pub struct SafetyIssue {
1098 pub issue_type: SafetyIssueType,
1099 pub location: Span,
1100 pub description: String,
1101 pub rationale: Option<String>,
1102 pub mitigation: Option<String>,
1103 pub risk_level: RiskLevel,
1104}
1105
1106#[derive(Debug, Clone)]
1108pub enum SafetyIssueType {
1109 UnsafeFunction,
1110 RawPointerDereference,
1111 FFIBoundary,
1112 ManualMemoryManagement,
1113 TypeTransmutation,
1114}
1115
1116#[derive(Debug, Clone)]
1118pub enum RiskLevel {
1119 Low,
1120 Medium,
1121 High,
1122 Critical,
1123}
1124
1125#[derive(Debug, Clone)]
1127pub struct ConcurrencyIssue {
1128 pub issue_type: ConcurrencyIssueType,
1129 pub location: Span,
1130 pub description: String,
1131 pub suggestion: Option<String>,
1132 pub severity: ConcurrencySeverity,
1133}
1134
1135#[derive(Debug, Clone)]
1137pub enum ConcurrencyIssueType {
1138 AsyncAntipattern,
1139 MissingAwait,
1140 DeadlockPotential,
1141 ThreadSafetyViolation,
1142 UnboundedChannel,
1143 ManualSendSync,
1144}
1145
1146#[derive(Debug, Clone)]
1148pub enum ConcurrencySeverity {
1149 Low,
1150 Medium,
1151 High,
1152 Critical,
1153}
1154
1155#[derive(Debug, Clone)]
1157pub enum Severity {
1158 Low,
1159 Medium,
1160 High,
1161 Critical,
1162}
1163
1164#[derive(Debug, Clone)]
1166pub struct TraitImplementation {
1167 pub trait_name: String,
1168 pub type_name: String,
1169 pub impl_node: Node,
1170}
1171
1172#[derive(Debug, Clone)]
1174pub struct UnsafeUsage {
1175 pub location: Span,
1176 pub usage_type: UnsafeType,
1177 pub description: String,
1178}
1179
1180#[derive(Debug, Clone)]
1182pub enum UnsafeType {
1183 Function,
1184 Block,
1185 Trait,
1186 Impl,
1187}
1188
1189#[derive(Debug, Clone)]
1191pub struct LifetimeUsage {
1192 pub name: String,
1193 pub location: Span,
1194 pub scope: LifetimeScope,
1195}
1196
1197#[derive(Debug, Clone)]
1199pub enum LifetimeScope {
1200 Function,
1201 Struct,
1202 Trait,
1203 Impl,
1204}
1205
1206#[derive(Debug, Clone)]
1208pub struct MacroUsage {
1209 pub name: String,
1210 pub location: Span,
1211 pub macro_type: MacroType,
1212}
1213
1214#[derive(Debug, Clone)]
1216pub enum MacroType {
1217 Definition,
1218 Invocation,
1219}