acp/ast/
mod.rs

1//! @acp:module "AST Parsing"
2//! @acp:summary "Tree-sitter based AST parsing for symbol extraction"
3//! @acp:domain cli
4//! @acp:layer parsing
5//!
6//! # AST Parsing
7//!
8//! Provides tree-sitter based AST parsing for extracting symbols from source code:
9//! - Functions, methods, classes, structs
10//! - Interfaces, traits, enums, type aliases
11//! - Import/export statements
12//! - Function calls for call graph analysis
13
14pub mod languages;
15pub mod parser;
16
17pub use languages::{get_extractor, LanguageExtractor};
18pub use parser::AstParser;
19
20use serde::{Deserialize, Serialize};
21
22/// Symbol kinds extracted from AST
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "snake_case")]
25pub enum SymbolKind {
26    /// A function declaration
27    Function,
28    /// A method (function inside a class/struct/impl)
29    Method,
30    /// A class declaration
31    Class,
32    /// A struct declaration
33    Struct,
34    /// An interface declaration
35    Interface,
36    /// A trait declaration (Rust)
37    Trait,
38    /// An enum declaration
39    Enum,
40    /// An enum variant/member
41    EnumVariant,
42    /// A constant declaration
43    Constant,
44    /// A variable declaration
45    Variable,
46    /// A type alias
47    TypeAlias,
48    /// A module declaration
49    Module,
50    /// A namespace declaration
51    Namespace,
52    /// A property (in class/object)
53    Property,
54    /// A field (in struct)
55    Field,
56    /// An impl block (Rust)
57    Impl,
58}
59
60impl std::fmt::Display for SymbolKind {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        match self {
63            Self::Function => write!(f, "function"),
64            Self::Method => write!(f, "method"),
65            Self::Class => write!(f, "class"),
66            Self::Struct => write!(f, "struct"),
67            Self::Interface => write!(f, "interface"),
68            Self::Trait => write!(f, "trait"),
69            Self::Enum => write!(f, "enum"),
70            Self::EnumVariant => write!(f, "enum_variant"),
71            Self::Constant => write!(f, "constant"),
72            Self::Variable => write!(f, "variable"),
73            Self::TypeAlias => write!(f, "type_alias"),
74            Self::Module => write!(f, "module"),
75            Self::Namespace => write!(f, "namespace"),
76            Self::Property => write!(f, "property"),
77            Self::Field => write!(f, "field"),
78            Self::Impl => write!(f, "impl"),
79        }
80    }
81}
82
83/// Visibility of a symbol
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
85#[serde(rename_all = "snake_case")]
86pub enum Visibility {
87    /// Public visibility (default for most languages)
88    #[default]
89    Public,
90    /// Private visibility
91    Private,
92    /// Protected visibility (class inheritance)
93    Protected,
94    /// Internal visibility (package/module level)
95    Internal,
96    /// Crate-level visibility (Rust pub(crate))
97    Crate,
98}
99
100/// A function/method parameter
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct Parameter {
103    /// Parameter name
104    pub name: String,
105    /// Type annotation (if available)
106    pub type_info: Option<String>,
107    /// Default value (if available)
108    pub default_value: Option<String>,
109    /// Is this a rest/variadic parameter?
110    pub is_rest: bool,
111    /// Is this optional? (TypeScript)
112    pub is_optional: bool,
113}
114
115/// A symbol extracted from AST parsing
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct ExtractedSymbol {
118    /// Symbol name (e.g., "main", "UserService")
119    pub name: String,
120    /// Fully qualified name (e.g., "src/main.rs::main")
121    pub qualified_name: Option<String>,
122    /// Symbol kind
123    pub kind: SymbolKind,
124    /// Start line (1-indexed)
125    pub start_line: usize,
126    /// End line (1-indexed)
127    pub end_line: usize,
128    /// Start column (0-indexed)
129    pub start_col: usize,
130    /// End column (0-indexed)
131    pub end_col: usize,
132    /// Function/method signature (for display)
133    pub signature: Option<String>,
134    /// Visibility modifier
135    pub visibility: Visibility,
136    /// Doc comment (if present)
137    pub doc_comment: Option<String>,
138    /// Parent symbol name (for nested symbols)
139    pub parent: Option<String>,
140    /// Type information (return type or declared type)
141    pub type_info: Option<String>,
142    /// Function/method parameters
143    pub parameters: Vec<Parameter>,
144    /// Return type (if available)
145    pub return_type: Option<String>,
146    /// Is this exported/public?
147    pub exported: bool,
148    /// Is this async?
149    pub is_async: bool,
150    /// Is this static?
151    pub is_static: bool,
152    /// Generic type parameters
153    pub generics: Vec<String>,
154    /// Line where annotation should be inserted (1-indexed)
155    /// For decorated/attributed symbols, this is BEFORE the decorators/attributes
156    /// For plain symbols, this equals start_line
157    pub definition_start_line: Option<usize>,
158}
159
160impl ExtractedSymbol {
161    /// Create a new ExtractedSymbol with required fields
162    pub fn new(name: String, kind: SymbolKind, start_line: usize, end_line: usize) -> Self {
163        Self {
164            name,
165            qualified_name: None,
166            kind,
167            start_line,
168            end_line,
169            start_col: 0,
170            end_col: 0,
171            signature: None,
172            visibility: Visibility::default(),
173            doc_comment: None,
174            parent: None,
175            type_info: None,
176            parameters: Vec::new(),
177            return_type: None,
178            exported: false,
179            is_async: false,
180            is_static: false,
181            generics: Vec::new(),
182            definition_start_line: None,
183        }
184    }
185
186    /// Set the qualified name
187    pub fn with_qualified_name(mut self, name: impl Into<String>) -> Self {
188        self.qualified_name = Some(name.into());
189        self
190    }
191
192    /// Set column positions
193    pub fn with_columns(mut self, start_col: usize, end_col: usize) -> Self {
194        self.start_col = start_col;
195        self.end_col = end_col;
196        self
197    }
198
199    /// Set the signature
200    pub fn with_signature(mut self, sig: impl Into<String>) -> Self {
201        self.signature = Some(sig.into());
202        self
203    }
204
205    /// Set visibility
206    pub fn with_visibility(mut self, vis: Visibility) -> Self {
207        self.visibility = vis;
208        self
209    }
210
211    /// Set doc comment
212    pub fn with_doc_comment(mut self, doc: impl Into<String>) -> Self {
213        self.doc_comment = Some(doc.into());
214        self
215    }
216
217    /// Set parent
218    pub fn with_parent(mut self, parent: impl Into<String>) -> Self {
219        self.parent = Some(parent.into());
220        self
221    }
222
223    /// Set return type
224    pub fn with_return_type(mut self, ret: impl Into<String>) -> Self {
225        self.return_type = Some(ret.into());
226        self
227    }
228
229    /// Mark as exported
230    pub fn exported(mut self) -> Self {
231        self.exported = true;
232        self
233    }
234
235    /// Mark as async
236    pub fn async_fn(mut self) -> Self {
237        self.is_async = true;
238        self
239    }
240
241    /// Mark as static
242    pub fn static_fn(mut self) -> Self {
243        self.is_static = true;
244        self
245    }
246
247    /// Add a parameter
248    pub fn add_parameter(&mut self, param: Parameter) {
249        self.parameters.push(param);
250    }
251
252    /// Add a generic type parameter
253    pub fn add_generic(&mut self, generic: impl Into<String>) {
254        self.generics.push(generic.into());
255    }
256
257    /// Set the definition start line (where annotation should be inserted)
258    /// This should be before any decorators/attributes
259    pub fn with_definition_start_line(mut self, line: usize) -> Self {
260        self.definition_start_line = Some(line);
261        self
262    }
263}
264
265/// An import statement extracted from AST
266#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct Import {
268    /// Source module path
269    pub source: String,
270    /// Imported names (empty for default/namespace imports)
271    pub names: Vec<ImportedName>,
272    /// Is this a default import?
273    pub is_default: bool,
274    /// Is this a namespace import (import * as x)?
275    pub is_namespace: bool,
276    /// Line number
277    pub line: usize,
278}
279
280/// A single imported name
281#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct ImportedName {
283    /// Original name
284    pub name: String,
285    /// Alias (if renamed)
286    pub alias: Option<String>,
287}
288
289/// A function call for call graph analysis
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct FunctionCall {
292    /// Name of the function containing this call
293    pub caller: String,
294    /// Name of the function being called
295    pub callee: String,
296    /// Line number of the call
297    pub line: usize,
298    /// Is this a method call (x.foo())?
299    pub is_method: bool,
300    /// Receiver expression (for method calls)
301    pub receiver: Option<String>,
302}