gather_all_code_from_crates/
parse_function_item.rs

1crate::ix!();
2
3pub fn extract_doc_comments(fn_def: &ast::Fn) -> Vec<String> {
4    let mut doc_comments = Vec::new();
5
6    // Iterate over leading trivia (e.g., comments and attributes)
7    for element in fn_def.syntax().children_with_tokens() {
8        if let Some(token) = element.as_token() {
9            let text = token.text();
10            if text.starts_with("///") || text.starts_with("//!") {
11                doc_comments.push(text.trim().to_string());
12            }
13        }
14    }
15
16    doc_comments
17}
18
19pub fn parse_function_item(fn_def: ast::Fn, remove_doc_comments: bool) -> ItemInfo {
20    // Extract raw attributes and test marker
21    let (raw_attrs, is_test) = extract_attributes(fn_def.attrs());
22    let doc_comments = extract_doc_comments(&fn_def);
23
24    // Determine visibility and name
25    let is_public = fn_def.visibility()
26        .map_or(false, |v| v.syntax().text().to_string().contains("pub"));
27    let name = fn_def.name().map(|n| n.text().to_string()).unwrap_or_default();
28
29    // Filter attributes if needed
30    let attributes: Vec<String> = if remove_doc_comments {
31        raw_attrs
32            .into_iter()
33            .filter(|a| !a.trim_start().starts_with("///") && !a.trim_start().starts_with("//!"))
34            .collect()
35    } else {
36        let mut all_attrs = raw_attrs;
37        all_attrs.extend(doc_comments);
38        all_attrs
39    };
40
41    // Build a clean signature from AST
42    let signature = extract_signature(&fn_def, remove_doc_comments);
43
44    // Debugging output for verification
45    //println!("fn_def: {:#?}", fn_def.syntax().text().to_string());
46    //println!("remove_doc_comments: {:#?}", remove_doc_comments);
47    //println!("attributes: {:#?}", attributes);
48    //println!("sig: {:#?}", signature);
49
50    // Extract body text if needed
51    let body = fn_def.body().map(|b| b.syntax().text().to_string());
52
53    // Construct the `FunctionInfo` object
54    let fi = FunctionInfoBuilder::default()
55        .name(name)
56        .is_public(is_public)
57        .is_test(is_test)
58        .attributes(attributes)
59        .signature(signature)
60        .body(body)
61        .build()
62        .expect("expected to build FunctionInfo");
63
64    ItemInfo::Function(fi)
65}
66
67#[cfg(test)]
68mod parse_function_item_tests {
69    use super::*;
70
71    #[test]
72    fn test_parse_function_item() {
73        let code = r#"
74#[inline]
75#[test]
76pub fn myfunc() {
77    // body
78}
79"#;
80        let syntax = parse_source(code);
81        let fn_node = syntax.descendants().find_map(ast::Fn::cast).unwrap();
82        let item = parse_function_item(fn_node, false);
83        if let ItemInfo::Function(f) = item {
84            assert_eq!(f.name(), "myfunc");
85            assert!(f.is_public());
86            assert!(f.is_test());
87            assert!(f.attributes().iter().any(|a| a.contains("#[inline]")));
88            assert!(f.signature().contains("pub fn myfunc() {"));
89        } else {
90            panic!("Expected a function item");
91        }
92    }
93
94    #[test]
95    fn test_parse_function_item_basic() {
96        let code = r#"
97#[inline]
98#[test]
99pub fn myfunc() {
100    // body
101}
102"#;
103        let syntax = parse_source(code);
104        let fn_node = syntax.descendants().find_map(ast::Fn::cast).unwrap();
105        let item = parse_function_item(fn_node, false);
106        if let ItemInfo::Function(f) = item {
107            assert_eq!(f.name(), "myfunc");
108            assert!(f.is_public());
109            assert!(f.is_test());
110            assert!(f.attributes().iter().any(|a| a.contains("#[inline]")));
111            assert!(f.signature().contains("pub fn myfunc() {"));
112        } else {
113            panic!("Expected a function item");
114        }
115    }
116
117    #[test]
118    fn test_parse_function_item_non_pub_non_test() {
119        let code = r#"
120fn private_func() {}
121"#;
122        let syntax = parse_source(code);
123        let fn_node = syntax.descendants().find_map(ast::Fn::cast).unwrap();
124        let item = parse_function_item(fn_node, false);
125        if let ItemInfo::Function(f) = item {
126            assert_eq!(f.name(), "private_func");
127            assert!(!f.is_public());
128            assert!(!f.is_test());
129            assert!(f.signature().contains("fn private_func() {}"));
130        } else {
131            panic!("Expected a function item");
132        }
133    }
134
135    #[test]
136    fn test_parse_function_item_remove_doc_comments() {
137        let code = r#"
138/// doc comment
139fn with_docs() {}
140"#;
141        let syntax = parse_source(code);
142        let fn_node = syntax.descendants().find_map(ast::Fn::cast).unwrap();
143
144        let item = parse_function_item(fn_node, true);
145        if let ItemInfo::Function(f) = item {
146            assert!(!f.signature().contains("/// doc comment"));
147        } else {
148            panic!("Expected a function item");
149        }
150    }
151
152    #[test]
153    fn test_parse_function_item_no_body() {
154        let code = r#"
155extern "C" {
156    fn extern_func();
157}
158"#;
159        let syntax = parse_source(code);
160        let fn_node = syntax.descendants().find_map(ast::Fn::cast).unwrap();
161        let item = parse_function_item(fn_node, false);
162        if let ItemInfo::Function(f) = item {
163            assert_eq!(f.name(), "extern_func");
164            assert!(f.signature().contains("fn extern_func();"));
165            assert!(f.body().is_none());
166        } else {
167            panic!("Expected a function item");
168        }
169    }
170}