Skip to main content

ferrous_forge/ai_analyzer/
context.rs

1//! Code context extraction utilities for AI analysis
2//!
3//! This module provides functions to extract contextual information from code,
4//! including surrounding code, function signatures, imports, and error handling patterns.
5
6use super::types::{CodeContext, ErrorHandlingStyle};
7
8/// Extract code context around a violation line
9pub fn extract_code_context(line_number: usize, content: &str) -> CodeContext {
10    let lines: Vec<&str> = content.lines().collect();
11    let context_start = line_number.saturating_sub(10);
12    let context_end = (line_number + 10).min(lines.len());
13
14    let surrounding_code = lines[context_start..context_end]
15        .iter()
16        .map(|s| s.to_string())
17        .collect();
18
19    let imports = extract_imports(content);
20    let (function_name, function_signature, return_type) =
21        extract_function_info(&lines, line_number);
22    let is_async = function_signature
23        .as_ref()
24        .map(|s| s.contains("async"))
25        .unwrap_or(false);
26    let is_generic = function_signature
27        .as_ref()
28        .map(|s| s.contains('<'))
29        .unwrap_or(false);
30    let trait_impl = detect_trait_impl(content, line_number);
31    let error_handling_style = detect_error_handling_style(&imports, content);
32
33    CodeContext {
34        function_name,
35        function_signature,
36        return_type,
37        is_async,
38        is_generic,
39        trait_impl,
40        surrounding_code,
41        imports,
42        error_handling_style,
43    }
44}
45
46/// Extract all import statements from the file content
47///
48/// Identifies lines starting with "use " and collects them as import statements.
49fn extract_imports(content: &str) -> Vec<String> {
50    content
51        .lines()
52        .filter(|line| line.trim().starts_with("use "))
53        .map(|s| s.to_string())
54        .collect()
55}
56
57/// Extract function information for the context of a given line
58///
59/// Searches backwards from the violation line to find the containing function,
60/// extracting its name, signature, and return type.
61///
62/// Returns a tuple of (`function_name`, `function_signature`, `return_type`)
63fn extract_function_info(
64    lines: &[&str],
65    line_number: usize,
66) -> (Option<String>, Option<String>, Option<String>) {
67    for i in (0..line_number).rev() {
68        if let Some(line) = lines.get(i)
69            && line.contains("fn ")
70            && !line.trim().starts_with("//")
71        {
72            let signature = line.trim().to_string();
73            let name = signature
74                .split("fn ")
75                .nth(1)
76                .and_then(|s| s.split('(').next())
77                .map(|s| s.trim().to_string());
78            let return_type = if signature.contains("->") {
79                signature.split("->").nth(1).map(|s| s.trim().to_string())
80            } else {
81                None
82            };
83            return (name, Some(signature), return_type);
84        }
85    }
86    (None, None, None)
87}
88
89/// Detect the error handling style used in the code
90pub fn detect_error_handling_style(imports: &[String], content: &str) -> ErrorHandlingStyle {
91    if imports.iter().any(|i| i.contains("anyhow")) || content.contains("anyhow::Result") {
92        ErrorHandlingStyle::AnyhowResult
93    } else if content.contains("Result<") && !content.contains("std::result::Result") {
94        ErrorHandlingStyle::CustomResult
95    } else if content.contains("Result<") {
96        ErrorHandlingStyle::StdResult
97    } else if content.contains("Option<") {
98        ErrorHandlingStyle::OptionBased
99    } else if content.contains("panic!") || content.contains(".unwrap()") {
100        ErrorHandlingStyle::Panic
101    } else {
102        ErrorHandlingStyle::Unknown
103    }
104}
105
106/// Detect if a line is within a trait implementation block
107///
108/// Searches backwards from the given line to find an "impl...for" statement,
109/// which indicates the code is part of a trait implementation.
110fn detect_trait_impl(content: &str, line: usize) -> Option<String> {
111    let lines: Vec<&str> = content.lines().collect();
112    for i in (0..line.min(lines.len())).rev() {
113        if lines[i].contains("impl") && lines[i].contains("for") {
114            return Some(lines[i].trim().to_string());
115        }
116    }
117    None
118}