rudg/parser/
ast_parser.rs

1use ra_ap_syntax::{ast::{self, AstNode, HasName, HasModuleItem}, match_ast, SourceFile, Parse};
2
3use crate::uml_entity::*;
4use super::StringParser;
5
6trait HasUMLEntity {
7    fn get_uml_entities(&self) -> Vec<UMLEntity>;
8}
9
10impl HasUMLEntity for ast::Struct {
11    fn get_uml_entities(&self) -> Vec<UMLEntity> {
12        let mut results = vec![];
13        let mut record_fields = vec![];
14        for node in self.syntax().descendants() {
15            match_ast! {
16                match node {
17                    ast::RecordField(rf) => {
18                        // get fields for UMLClass
19                        record_fields.push(rf.to_string());
20
21                        // get Aggregation and Composition Relations
22                        let rf_str = rf.to_string();
23                        if rf_str.contains(r"*mut") || rf_str.contains(r"*const") {
24                            get_paths_str_from_ast_node(rf)
25                                .iter()
26                                .for_each(|p| results.push(
27                                    UMLEntity::UMLRelation(UMLRelation::new(&self.name().unwrap().text().to_string(), &p, UMLRelationKind::UMLAggregation)))
28                                )
29                        } else if !rf_str.contains(r"*mut") && !rf_str.contains(r"*const") {
30                            get_paths_str_from_ast_node(rf)
31                                .iter()
32                                .for_each(|p| results.push(
33                                    UMLEntity::UMLRelation(UMLRelation::new(&self.name().unwrap().text().to_string(), &p, UMLRelationKind::UMLComposition)))
34                                )
35                        }
36                    },
37                    _ => ()
38                }
39            }
40            // println!("{:?}", node);
41            // println!("{}", node);
42        };
43        results.push(UMLEntity::UMLClass(UMLClass::new(&self.name().unwrap().text().to_string(), record_fields, vec![], UMLClassKind::UMLClass)));
44        results
45    }
46}
47
48impl HasUMLEntity for ast::Trait {
49    fn get_uml_entities(&self) -> Vec<UMLEntity> {
50        let mut results = vec![];
51        // add UMLClass
52        results.push(UMLEntity::UMLClass(UMLClass::new(&self.name().unwrap().text().to_string(), vec![], vec![], UMLClassKind::UMLTrait)));
53
54        for node in self.syntax().descendants() {
55            match_ast! {
56                match node {
57                    ast::RecordField(rf) => {
58                        // get Aggregation and Composition Relations
59                        let rf_str = rf.to_string();
60                        if rf_str.contains(r"*mut") || rf_str.contains(r"*const") {
61                            get_paths_str_from_ast_node(rf)
62                                .iter()
63                                .for_each(|p| results.push(
64                                    UMLEntity::UMLRelation(UMLRelation::new(&self.name().unwrap().text().to_string(), &p, UMLRelationKind::UMLAggregation)))
65                                )
66                        } else if !rf_str.contains(r"*mut") && !rf_str.contains(r"*const") {
67                            get_paths_str_from_ast_node(rf)
68                                .iter()
69                                .for_each(|p| results.push(
70                                    UMLEntity::UMLRelation(UMLRelation::new(&self.name().unwrap().text().to_string(), &p, UMLRelationKind::UMLComposition)))
71                                )
72                        }
73                    },
74                    _ => ()
75                }
76            }
77        };
78
79        results
80    }
81}
82
83impl HasUMLEntity for ast::Impl {
84    fn get_uml_entities(&self) -> Vec<UMLEntity> {
85        let mut results = vec![];
86
87        // get struct name
88        let mut impl_fn_names = vec![];
89        let struct_name: String = strip_trait_bound(&self.self_ty().unwrap().to_string());
90
91        let mut dep_list: Vec<String> = vec![];
92        let mut asct_list: Vec<String> = vec![];
93        for node in self.syntax().descendants() {
94            match_ast! {
95                match node {
96                    // get impl functions' names
97                    ast::Fn(f) => {
98                        impl_fn_names.push(get_fn_full_name(&f));
99                    },
100                    // get Dependency and Association Relations
101                    ast::ParamList(pl) => {
102                        dep_list.append(&mut get_paths_str_from_ast_node(pl));
103                    },
104                    ast::BlockExpr(ex) => {
105                        dep_list.append(&mut get_paths_str_from_ast_node(ex));
106                    },
107                    ast::RetType(rt) => {
108                        asct_list.append(&mut get_paths_str_from_ast_node(rt));
109                    },
110                    _ => ()
111                }
112            }
113        }
114
115        // first add Association Relation, then add dependency relation if the name not occured in assocaitions
116        results.extend(
117            asct_list.iter().map(|p| UMLEntity::UMLRelation(UMLRelation::new(&p, &struct_name, UMLRelationKind::UMLAssociationUni)))
118        );
119        let mut dep_set: Vec<&String> = dep_list.iter().filter(|p| !asct_list.contains(p)).collect();
120        dep_set.sort();
121        dep_set.dedup();
122        results.extend(
123            dep_set.iter().map(|p| UMLEntity::UMLRelation(UMLRelation::new(&p, &struct_name, UMLRelationKind::UMLDependency)))
124        );
125
126
127        // get trait if there is any
128        match self.trait_() {
129            Some(tt) => {
130                results.push(
131                    UMLEntity::UMLRelation(UMLRelation::new(&struct_name, &strip_trait_bound(&tt.to_string()), UMLRelationKind::UMLRealization))
132                );
133                // println!("trait: {}", tt.to_string());
134            },
135            None => {
136                results.push(UMLEntity::UMLClass(UMLClass::new(&struct_name, vec![], impl_fn_names, UMLClassKind::UMLClass)));
137            }
138        }
139        
140
141        results
142    }
143}
144
145fn strip_trait_bound(s: &str) -> String {
146    let class_name: Vec<&str> = s.split(r"<").collect();
147    String::from(class_name[0])
148}
149
150fn get_paths_str_from_ast_node(node: impl ast::AstNode) -> Vec<String> {
151    let mut results = vec![];
152    for node in node.syntax().descendants() {
153        match_ast! {
154            match node {
155                ast::Path(p) => {
156                    results.push(p.to_string())
157                },
158                _ => ()
159            }
160        }
161        // println!("{:?}", node);
162        // println!("{}", node);
163    };
164    results
165}
166
167impl HasUMLEntity for ast::Fn {
168    fn get_uml_entities(&self) -> Vec<UMLEntity> {
169        let mut results: Vec<UMLEntity> = vec![];
170        let f_name = self.name().unwrap().text().to_string();
171        let full_name: String = get_fn_full_name(self);
172
173        // visit all Fn descendants and process CallExpr
174        for node in self.syntax().descendants() {
175            match_ast! {
176                match node {
177                    ast::CallExpr(it) => {
178                        let call_name = get_call_expr_fn_names(it);
179                        results.push(UMLEntity::UMLRelation(UMLRelation::new(&f_name, &call_name, UMLRelationKind::UMLDependency)))
180                    },
181                    _ => {
182                        // println!("{:?}", node);
183                        // println!("{}", node)
184                    },
185                }
186            }
187        }
188        results.push(UMLEntity::UMLFn(UMLFn::new(&f_name, &full_name)));
189        results
190    }
191}
192
193fn get_fn_full_name(f: &ast::Fn) -> String {
194    let f_name = f.name().unwrap().text().to_string();
195    let mut full_name: String = f_name.clone();
196
197    // visit all Fn descendants and process CallExpr
198    for node in f.syntax().descendants() {
199        match_ast! {
200            match node {
201                ast::ParamList(pl) => {
202                    full_name.push_str(&pl.to_string());
203                },
204                ast::RetType(rt) => {
205                    full_name.push_str(" ");
206                    full_name.push_str(&rt.to_string());
207                },
208                _ => {
209                    // println!("{:?}", node);
210                    // println!("{}", node)
211                },
212            }
213        }
214    }
215    full_name
216}
217
218fn get_call_expr_fn_names(call_exp: ast::CallExpr) -> String {
219    let call_expr = call_exp.to_string();
220    let call_names: Vec<&str> = call_expr.split("(").collect();
221    String::from(call_names[0])
222}
223
224pub struct AstParser;
225
226impl StringParser for AstParser {
227    fn parse_string(input: &str) -> UMLGraph {
228        let parse: Parse<SourceFile> = SourceFile::parse(input);
229        let file: SourceFile = parse.tree();
230        let mut uml_graph = UMLGraph::new();
231        let mut uml_entities: Vec<UMLEntity> = vec![];
232
233        // visit all items in SourceFile and extract dot entities from every type of them
234        for item in file.items() {
235            match item {
236                ast::Item::Fn(f) => {
237                    uml_entities.append(&mut f.get_uml_entities());
238                },
239                ast::Item::Impl(ip) => {
240                    uml_entities.append(&mut ip.get_uml_entities());
241                },
242                ast::Item::Struct(st) => {
243                    uml_entities.append(&mut st.get_uml_entities());
244                },
245                ast::Item::Trait(tt) => {
246                    uml_entities.append(&mut tt.get_uml_entities());
247                },
248                _ => (),
249            }
250        }
251
252        // add relations last
253        let mut relations: Vec<UMLRelation> = vec![];
254        for e in uml_entities {
255            match e {
256                UMLEntity::UMLClass(c) => uml_graph.add_struct(c),
257                UMLEntity::UMLFn(f) => uml_graph.add_fn(f),
258                UMLEntity::UMLRelation(r) => {
259                    // uml_graph.add_relation(r);
260                    relations.push(r);
261                },
262            }
263        }
264        for rel in relations {
265            uml_graph.add_relation(rel);
266        }
267
268        uml_graph
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275
276    #[test]
277    fn test_parse_fn() {
278        let code: &str = r#"
279        fn main() {
280            println!("Hello, world!");
281        }
282        "#;
283        let parsed_graph = AstParser::parse_string(code);
284        let mut target_graph: UMLGraph = UMLGraph::new();
285        target_graph.add_fn(UMLFn::new("main", "main()"));
286        assert_eq!(parsed_graph, target_graph);
287    }
288
289    #[test]
290    fn test_parse_struct() {
291        let code: &str = r#"
292        pub struct Mock;
293            impl Mock {
294                pub fn mock_fn() {}
295            }
296        "#;
297        let parsed_graph = AstParser::parse_string(code);
298        let mut target_graph: UMLGraph = UMLGraph::new();
299        target_graph.add_struct(UMLClass::new("Mock", vec![], vec![String::from("mock_fn()")], UMLClassKind::UMLClass));
300        assert_eq!(parsed_graph, target_graph);
301    }
302
303    #[test]
304    fn test_fn_dependency() {
305        let code: &str = r#"
306            fn main() {
307                hello();
308            }
309            fn hello() {}
310        "#;
311        let parsed_graph = AstParser::parse_string(code);
312        let mut target_graph: UMLGraph = UMLGraph::new();
313
314        target_graph.add_fn(UMLFn::new("main", "main()"));
315        target_graph.add_fn(UMLFn::new("hello", "hello()"));
316        target_graph.add_relation(UMLRelation::new("main", "hello", UMLRelationKind::UMLDependency));
317        
318        assert_eq!(parsed_graph, target_graph);
319    }
320
321    #[test]
322    fn test_class_dependency() {
323        let code: &str = r#"
324        pub struct Mock;
325        impl Mock {
326            pub fn mock_fn() { f1(f2()) }    
327        }
328        fn f1(i: usize) {}
329        fn f2() -> usize { 0 }
330        "#;
331        let parsed_graph = AstParser::parse_string(code);
332        let mut target_graph: UMLGraph = UMLGraph::new();
333
334        target_graph.add_struct(UMLClass::new("Mock", vec![], vec![String::from("mock_fn()")], UMLClassKind::UMLClass));
335        target_graph.add_fn(UMLFn::new("f1", "f1(i: usize)"));
336        target_graph.add_fn(UMLFn::new("f2", "f2() -> usize"));
337        target_graph.add_relation(UMLRelation::new("f1", "Mock", UMLRelationKind::UMLDependency));
338        target_graph.add_relation(UMLRelation::new("f2", "Mock", UMLRelationKind::UMLDependency));
339        
340        assert_eq!(parsed_graph, target_graph);
341    }
342
343    #[test]
344    fn test_aggregation() {
345        let code: &str = r#"
346        struct Amut {
347            b: *mut B,
348        }
349        
350        struct Aconst {
351            b: *const B,
352        }
353        
354        struct B {
355        }
356        "#;
357        let parsed_graph = AstParser::parse_string(code);
358        let mut target_graph: UMLGraph = UMLGraph::new();
359
360        target_graph.add_struct(UMLClass::new("Amut", vec![String::from(r"b: *mut B")], vec![], UMLClassKind::UMLClass));
361        target_graph.add_struct(UMLClass::new("Aconst", vec![String::from(r"b: *const B")], vec![], UMLClassKind::UMLClass));
362        target_graph.add_struct(UMLClass::new("B", vec![], vec![], UMLClassKind::UMLClass));
363        target_graph.add_relation(UMLRelation::new("Amut", "B", UMLRelationKind::UMLAggregation));
364        target_graph.add_relation(UMLRelation::new("Aconst", "B", UMLRelationKind::UMLAggregation));
365        
366        assert_eq!(parsed_graph, target_graph);
367    }
368
369    #[test]
370    fn test_composition() {
371        let code: &str = r#"
372        struct A {
373            b: B,
374        }
375        
376        struct B {
377        }
378        "#;
379        let parsed_graph = AstParser::parse_string(code);
380        let mut target_graph: UMLGraph = UMLGraph::new();
381
382        target_graph.add_struct(UMLClass::new("A", vec![String::from(r"b: B")], vec![], UMLClassKind::UMLClass));
383        target_graph.add_struct(UMLClass::new("B", vec![], vec![], UMLClassKind::UMLClass));
384        target_graph.add_relation(UMLRelation::new("A", "B", UMLRelationKind::UMLComposition));
385        
386        assert_eq!(parsed_graph, target_graph);
387    }
388
389    
390    #[test]
391    fn test_realization() {
392        let code: &str = r#"
393        use std::fmt::Debug;
394
395        #[derive(Debug)]
396        struct A<T> where T: Debug {
397            a: T,
398        }
399
400        impl<T> A<T> where T: Debug {
401            fn a(a: T) -> Self {
402                A {
403                    a: a,
404                }
405            }
406        }
407
408        impl <T>B<T> for A<T> where T: Debug {
409            fn a(&self) -> Option<T> {
410                None
411            }
412        }
413
414        trait B<T> : Debug where T: Debug {
415            fn a(&self) -> Option<T>;
416        }
417
418        impl <T>B<T> {
419            fn a(&self) -> Option<T> {
420                None
421            }
422        }
423        "#;
424        let parsed_graph = AstParser::parse_string(code);
425        let mut target_graph: UMLGraph = UMLGraph::new();
426
427        target_graph.add_struct(UMLClass::new("A", vec![String::from(r"a: T")], vec![String::from(r"a(a: T) -> Self")], UMLClassKind::UMLClass));
428        target_graph.add_struct(UMLClass::new("B", vec![], vec![String::from(r"a(&self) -> Option<T>")], UMLClassKind::UMLTrait));
429        target_graph.add_relation(UMLRelation::new("A", "B", UMLRelationKind::UMLRealization));
430        
431        assert_eq!(parsed_graph, target_graph);
432    }
433
434    #[test]
435    fn test_association() {
436        let code: &str = r#"
437        struct A {
438        }
439        
440        impl A {
441            fn b() -> B {
442                B {
443                }
444            }
445        }
446        
447        struct Ab {
448        }
449        
450        impl Ab {
451            fn b() -> B {
452                B {
453                }
454            }
455        }
456        
457        struct B {
458        }
459        
460        impl B {
461            fn a() -> Ab {
462                Ab {
463                }
464            }
465        }
466        "#;
467        let parsed_graph = AstParser::parse_string(code);
468        let mut target_graph: UMLGraph = UMLGraph::new();
469
470        target_graph.add_struct(UMLClass::new("A", vec![], vec![String::from(r"b() -> B")], UMLClassKind::UMLClass));
471        target_graph.add_struct(UMLClass::new("Ab", vec![], vec![String::from(r"b() -> B")], UMLClassKind::UMLClass));
472        target_graph.add_struct(UMLClass::new("B", vec![], vec![String::from(r"a() -> Ab")], UMLClassKind::UMLClass));
473        target_graph.add_relation(UMLRelation::new("B", "A", UMLRelationKind::UMLAssociationUni));
474        target_graph.add_relation(UMLRelation::new("B", "Ab", UMLRelationKind::UMLAssociationBi));
475        
476        assert_eq!(parsed_graph, target_graph);
477    }
478
479}