gather_all_code_from_crates/
parse_function_item.rs1crate::ix!();
2
3pub fn extract_doc_comments(fn_def: &ast::Fn) -> Vec<String> {
4 let mut doc_comments = Vec::new();
5
6 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 let (raw_attrs, is_test) = extract_attributes(fn_def.attrs());
22 let doc_comments = extract_doc_comments(&fn_def);
23
24 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 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 let signature = extract_signature(&fn_def, remove_doc_comments);
43
44 let body = fn_def.body().map(|b| b.syntax().text().to_string());
52
53 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}