]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/generate_documentation_template.rs
Merge #11194
[rust.git] / crates / ide_assists / src / handlers / generate_documentation_template.rs
1 use hir::{AsAssocItem, HasVisibility, ModuleDef, Visibility};
2 use ide_db::assists::{AssistId, AssistKind};
3 use itertools::Itertools;
4 use stdx::to_lower_snake_case;
5 use syntax::{
6     ast::{self, edit::IndentLevel, HasDocComments, HasName},
7     AstNode,
8 };
9
10 use crate::assist_context::{AssistContext, Assists};
11
12 // Assist: generate_documentation_template
13 //
14 // Adds a documentation template above a function definition / declaration.
15 //
16 // ```
17 // pub fn my_$0func(a: i32, b: i32) -> Result<(), std::io::Error> {
18 //     unimplemented!()
19 // }
20 // ```
21 // ->
22 // ```
23 // /// .
24 // ///
25 // /// # Examples
26 // ///
27 // /// ```
28 // /// use test::my_func;
29 // ///
30 // /// assert_eq!(my_func(a, b), );
31 // /// ```
32 // ///
33 // /// # Errors
34 // ///
35 // /// This function will return an error if .
36 // pub fn my_func(a: i32, b: i32) -> Result<(), std::io::Error> {
37 //     unimplemented!()
38 // }
39 // ```
40 pub(crate) fn generate_documentation_template(
41     acc: &mut Assists,
42     ctx: &AssistContext,
43 ) -> Option<()> {
44     let name = ctx.find_node_at_offset::<ast::Name>()?;
45     let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
46     if is_in_trait_impl(&ast_func, ctx)
47         || !is_public(&ast_func, ctx)?
48         || ast_func.doc_comments().next().is_some()
49     {
50         return None;
51     }
52
53     let parent_syntax = ast_func.syntax();
54     let text_range = parent_syntax.text_range();
55     let indent_level = IndentLevel::from_node(&parent_syntax);
56
57     acc.add(
58         AssistId("generate_documentation_template", AssistKind::Generate),
59         "Generate a documentation template",
60         text_range,
61         |builder| {
62             // Introduction / short function description before the sections
63             let mut doc_lines = vec![introduction_builder(&ast_func, ctx)];
64             // Then come the sections
65             if let Some(mut lines) = examples_builder(&ast_func, ctx) {
66                 doc_lines.push("".into());
67                 doc_lines.append(&mut lines);
68             }
69             for section_builder in [panics_builder, errors_builder, safety_builder] {
70                 if let Some(mut lines) = section_builder(&ast_func) {
71                     doc_lines.push("".into());
72                     doc_lines.append(&mut lines);
73                 }
74             }
75             builder.insert(text_range.start(), documentation_from_lines(doc_lines, indent_level));
76         },
77     )
78 }
79
80 /// Builds an introduction, trying to be smart if the function is `::new()`
81 fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext) -> String {
82     || -> Option<String> {
83         let hir_func = ctx.sema.to_def(ast_func)?;
84         let container = hir_func.as_assoc_item(ctx.db())?.container(ctx.db());
85         if let hir::AssocItemContainer::Impl(implementation) = container {
86             let ret_ty = hir_func.ret_type(ctx.db());
87             let self_ty = implementation.self_ty(ctx.db());
88
89             let is_new = ast_func.name()?.to_string() == "new";
90             match is_new && ret_ty == self_ty {
91                 true => {
92                     Some(format!("Creates a new [`{}`].", self_type_without_lifetimes(ast_func)?))
93                 }
94                 false => None,
95             }
96         } else {
97             None
98         }
99     }()
100     .unwrap_or_else(|| ".".into())
101 }
102
103 /// Builds an `# Examples` section. An option is returned to be able to manage an error in the AST.
104 fn examples_builder(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<Vec<String>> {
105     let mut lines = string_vec_from(&["# Examples", "", "```"]);
106     if is_in_trait_def(ast_func, ctx) {
107         lines.push("// Example template not implemented for trait functions".into());
108     } else {
109         lines.append(&mut gen_ex_template(ast_func, ctx)?)
110     };
111
112     lines.push("```".into());
113     Some(lines)
114 }
115
116 /// Builds an optional `# Panics` section
117 fn panics_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
118     match can_panic(ast_func) {
119         Some(true) => Some(string_vec_from(&["# Panics", "", "Panics if ."])),
120         _ => None,
121     }
122 }
123
124 /// Builds an optional `# Errors` section
125 fn errors_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
126     match return_type(ast_func)?.to_string().contains("Result") {
127         true => Some(string_vec_from(&["# Errors", "", "This function will return an error if ."])),
128         false => None,
129     }
130 }
131
132 /// Builds an optional `# Safety` section
133 fn safety_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
134     let is_unsafe = ast_func.unsafe_token().is_some();
135     match is_unsafe {
136         true => Some(string_vec_from(&["# Safety", "", "."])),
137         false => None,
138     }
139 }
140
141 /// Generates an example template
142 fn gen_ex_template(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<Vec<String>> {
143     let mut lines = Vec::new();
144     let is_unsafe = ast_func.unsafe_token().is_some();
145     let param_list = ast_func.param_list()?;
146     let ref_mut_params = ref_mut_params(&param_list);
147     let self_name: Option<String> = self_name(ast_func);
148
149     lines.push(format!("use {};", build_path(ast_func, ctx)?));
150     lines.push("".into());
151     if let Some(self_definition) = self_definition(ast_func, self_name.as_deref()) {
152         lines.push(self_definition);
153     }
154     for param_name in &ref_mut_params {
155         lines.push(format!("let mut {} = ;", param_name))
156     }
157     // Call the function, check result
158     let function_call = function_call(ast_func, &param_list, self_name.as_deref(), is_unsafe)?;
159     if returns_a_value(ast_func, ctx) {
160         if count_parameters(&param_list) < 3 {
161             lines.push(format!("assert_eq!({}, );", function_call));
162         } else {
163             lines.push(format!("let result = {};", function_call));
164             lines.push("assert_eq!(result, );".into());
165         }
166     } else {
167         lines.push(format!("{};", function_call));
168     }
169     // Check the mutated values
170     if is_ref_mut_self(ast_func) == Some(true) {
171         lines.push(format!("assert_eq!({}, );", self_name?));
172     }
173     for param_name in &ref_mut_params {
174         lines.push(format!("assert_eq!({}, );", param_name));
175     }
176     Some(lines)
177 }
178
179 /// Checks if the function is public / exported
180 fn is_public(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<bool> {
181     let hir_func = ctx.sema.to_def(ast_func)?;
182     Some(
183         hir_func.visibility(ctx.db()) == Visibility::Public
184             && all_parent_mods_public(&hir_func, ctx),
185     )
186 }
187
188 /// Checks that all parent modules of the function are public / exported
189 fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext) -> bool {
190     let mut module = hir_func.module(ctx.db());
191     loop {
192         if let Some(parent) = module.parent(ctx.db()) {
193             match ModuleDef::from(module).visibility(ctx.db()) {
194                 Visibility::Public => module = parent,
195                 _ => break false,
196             }
197         } else {
198             break true;
199         }
200     }
201 }
202
203 /// Returns the name of the current crate
204 fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<String> {
205     let krate = ctx.sema.scope(&ast_func.syntax()).module()?.krate();
206     Some(krate.display_name(ctx.db())?.to_string())
207 }
208
209 /// `None` if function without a body; some bool to guess if function can panic
210 fn can_panic(ast_func: &ast::Fn) -> Option<bool> {
211     let body = ast_func.body()?.to_string();
212     let can_panic = body.contains("panic!(")
213         // FIXME it would be better to not match `debug_assert*!` macro invocations
214         || body.contains("assert!(")
215         || body.contains(".unwrap()")
216         || body.contains(".expect(");
217     Some(can_panic)
218 }
219
220 /// Helper function to get the name that should be given to `self` arguments
221 fn self_name(ast_func: &ast::Fn) -> Option<String> {
222     self_partial_type(ast_func).map(|name| to_lower_snake_case(&name))
223 }
224
225 /// Heper function to get the name of the type of `self`
226 fn self_type(ast_func: &ast::Fn) -> Option<ast::Type> {
227     ast_func.syntax().ancestors().find_map(ast::Impl::cast).and_then(|i| i.self_ty())
228 }
229
230 /// Output the real name of `Self` like `MyType<T>`, without the lifetimes.
231 fn self_type_without_lifetimes(ast_func: &ast::Fn) -> Option<String> {
232     let path_segment = match self_type(ast_func)? {
233         ast::Type::PathType(path_type) => path_type.path()?.segment()?,
234         _ => return None,
235     };
236     let mut name = path_segment.name_ref()?.to_string();
237     let generics = path_segment
238         .generic_arg_list()?
239         .generic_args()
240         .filter(|generic| matches!(generic, ast::GenericArg::TypeArg(_)))
241         .map(|generic| generic.to_string());
242     let generics: String = generics.format(", ").to_string();
243     if !generics.is_empty() {
244         name.push('<');
245         name.push_str(&generics);
246         name.push('>');
247     }
248     Some(name)
249 }
250
251 /// Heper function to get the name of the type of `self` without generic arguments
252 fn self_partial_type(ast_func: &ast::Fn) -> Option<String> {
253     let mut self_type = self_type(ast_func)?.to_string();
254     if let Some(idx) = self_type.find(|c| ['<', ' '].contains(&c)) {
255         self_type.truncate(idx);
256     }
257     Some(self_type)
258 }
259
260 /// Helper function to determine if the function is in a trait implementation
261 fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
262     ctx.sema
263         .to_def(ast_func)
264         .and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
265         .and_then(|assoc_item| assoc_item.containing_trait_impl(ctx.db()))
266         .is_some()
267 }
268
269 /// Helper function to determine if the function definition is in a trait definition
270 fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
271     ctx.sema
272         .to_def(ast_func)
273         .and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
274         .and_then(|assoc_item| assoc_item.containing_trait(ctx.db()))
275         .is_some()
276 }
277
278 /// Returns `None` if no `self` at all, `Some(true)` if there is `&mut self` else `Some(false)`
279 fn is_ref_mut_self(ast_func: &ast::Fn) -> Option<bool> {
280     let self_param = ast_func.param_list()?.self_param()?;
281     Some(self_param.mut_token().is_some() && self_param.amp_token().is_some())
282 }
283
284 /// Helper function to define an variable to be the `self` argument
285 fn self_definition(ast_func: &ast::Fn, self_name: Option<&str>) -> Option<String> {
286     let definition = match is_ref_mut_self(ast_func)? {
287         true => format!("let mut {} = ;", self_name?),
288         false => format!("let {} = ;", self_name?),
289     };
290     Some(definition)
291 }
292
293 /// Helper function to determine if a parameter is `&mut`
294 fn is_a_ref_mut_param(param: &ast::Param) -> bool {
295     match param.ty() {
296         Some(ast::Type::RefType(param_ref)) => param_ref.mut_token().is_some(),
297         _ => false,
298     }
299 }
300
301 /// Helper function to build the list of `&mut` parameters
302 fn ref_mut_params(param_list: &ast::ParamList) -> Vec<String> {
303     param_list
304         .params()
305         .filter_map(|param| match is_a_ref_mut_param(&param) {
306             // Maybe better filter the param name (to do this maybe extract a function from
307             // `arguments_from_params`?) in case of a `mut a: &mut T`. Anyway managing most (not
308             // all) cases might be enough, the goal is just to produce a template.
309             true => Some(param.pat()?.to_string()),
310             false => None,
311         })
312         .collect()
313 }
314
315 /// Helper function to build the comma-separated list of arguments of the function
316 fn arguments_from_params(param_list: &ast::ParamList) -> String {
317     let args_iter = param_list.params().map(|param| match param.pat() {
318         // To avoid `mut` in the function call (which would be a nonsense), `Pat` should not be
319         // written as is so its variants must be managed independently. Other variants (for
320         // instance `TuplePat`) could be managed later.
321         Some(ast::Pat::IdentPat(ident_pat)) => match ident_pat.name() {
322             Some(name) => match is_a_ref_mut_param(&param) {
323                 true => format!("&mut {}", name),
324                 false => name.to_string(),
325             },
326             None => "_".to_string(),
327         },
328         _ => "_".to_string(),
329     });
330     args_iter.format(", ").to_string()
331 }
332
333 /// Helper function to build a function call. `None` if expected `self_name` was not provided
334 fn function_call(
335     ast_func: &ast::Fn,
336     param_list: &ast::ParamList,
337     self_name: Option<&str>,
338     is_unsafe: bool,
339 ) -> Option<String> {
340     let name = ast_func.name()?;
341     let arguments = arguments_from_params(&param_list);
342     let function_call = if param_list.self_param().is_some() {
343         format!("{}.{}({})", self_name?, name, arguments)
344     } else if let Some(implementation) = self_partial_type(ast_func) {
345         format!("{}::{}({})", implementation, name, arguments)
346     } else {
347         format!("{}({})", name, arguments)
348     };
349     match is_unsafe {
350         true => Some(format!("unsafe {{ {} }}", function_call)),
351         false => Some(function_call),
352     }
353 }
354
355 /// Helper function to count the parameters including `self`
356 fn count_parameters(param_list: &ast::ParamList) -> usize {
357     param_list.params().count() + if param_list.self_param().is_some() { 1 } else { 0 }
358 }
359
360 /// Helper function to transform lines of documentation into a Rust code documentation
361 fn documentation_from_lines(doc_lines: Vec<String>, indent_level: IndentLevel) -> String {
362     let mut result = String::new();
363     for doc_line in doc_lines {
364         result.push_str("///");
365         if !doc_line.is_empty() {
366             result.push(' ');
367             result.push_str(&doc_line);
368         }
369         result.push('\n');
370         result.push_str(&indent_level.to_string());
371     }
372     result
373 }
374
375 /// Helper function to transform an array of borrowed strings to an owned `Vec<String>`
376 fn string_vec_from(string_array: &[&str]) -> Vec<String> {
377     string_array.iter().map(|&s| s.to_owned()).collect()
378 }
379
380 /// Helper function to build the path of the module in the which is the node
381 fn build_path(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<String> {
382     let crate_name = crate_name(ast_func, ctx)?;
383     let leaf = self_partial_type(ast_func)
384         .or_else(|| ast_func.name().map(|n| n.to_string()))
385         .unwrap_or_else(|| "*".into());
386     let module_def: ModuleDef = ctx.sema.to_def(ast_func)?.module(ctx.db()).into();
387     match module_def.canonical_path(ctx.db()) {
388         Some(path) => Some(format!("{}::{}::{}", crate_name, path, leaf)),
389         None => Some(format!("{}::{}", crate_name, leaf)),
390     }
391 }
392
393 /// Helper function to get the return type of a function
394 fn return_type(ast_func: &ast::Fn) -> Option<ast::Type> {
395     ast_func.ret_type()?.ty()
396 }
397
398 /// Helper function to determine if the function returns some data
399 fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
400     ctx.sema
401         .to_def(ast_func)
402         .map(|hir_func| hir_func.ret_type(ctx.db()))
403         .map(|ret_ty| !ret_ty.is_unit() && !ret_ty.is_never())
404         .unwrap_or(false)
405 }
406
407 #[cfg(test)]
408 mod tests {
409     use crate::tests::{check_assist, check_assist_not_applicable};
410
411     use super::*;
412
413     #[test]
414     fn not_applicable_on_function_calls() {
415         check_assist_not_applicable(
416             generate_documentation_template,
417             r#"
418 fn hello_world() {}
419 fn calls_hello_world() {
420     hello_world$0();
421 }
422 "#,
423         )
424     }
425
426     #[test]
427     fn not_applicable_in_trait_impl() {
428         check_assist_not_applicable(
429             generate_documentation_template,
430             r#"
431 trait MyTrait {}
432 struct MyStruct;
433 impl MyTrait for MyStruct {
434     fn hello_world$0();
435 }
436 "#,
437         )
438     }
439
440     #[test]
441     fn not_applicable_if_function_is_private() {
442         check_assist_not_applicable(generate_documentation_template, r#"fn priv$0ate() {}"#);
443     }
444
445     #[test]
446     fn not_applicable_if_function_is_pub_crate() {
447         check_assist_not_applicable(
448             generate_documentation_template,
449             r#"pub(crate) fn pri$0vate() {}"#,
450         );
451     }
452
453     #[test]
454     fn not_applicable_if_function_is_in_private_mod() {
455         check_assist_not_applicable(
456             generate_documentation_template,
457             r#"
458 mod PrivateModule {
459     pub fn pri$0vate() {}
460 }"#,
461         );
462     }
463
464     #[test]
465     fn not_applicable_if_function_is_in_pub_crate_mod() {
466         check_assist_not_applicable(
467             generate_documentation_template,
468             r#"
469 pub(crate) mod PrivateModule {
470     pub fn pr$0ivate() {}
471 }"#,
472         );
473     }
474
475     #[test]
476     fn not_applicable_if_function_is_in_non_public_mod_is_recursive() {
477         check_assist_not_applicable(
478             generate_documentation_template,
479             r#"
480 mod ParentPrivateModule {
481     pub mod PrivateModule {
482         pub fn pr$0ivate() {}
483     }
484 }"#,
485         );
486     }
487
488     #[test]
489     fn not_applicable_if_function_already_documented() {
490         check_assist_not_applicable(
491             generate_documentation_template,
492             r#"
493 /// Some documentation here
494 pub fn $0documented_function() {}
495 "#,
496         );
497     }
498
499     #[test]
500     fn supports_noop_function() {
501         check_assist(
502             generate_documentation_template,
503             r#"
504 pub fn no$0op() {}
505 "#,
506             r#"
507 /// .
508 ///
509 /// # Examples
510 ///
511 /// ```
512 /// use test::noop;
513 ///
514 /// noop();
515 /// ```
516 pub fn noop() {}
517 "#,
518         );
519     }
520
521     #[test]
522     fn supports_a_parameter() {
523         check_assist(
524             generate_documentation_template,
525             r#"
526 pub fn no$0op_with_param(_a: i32) {}
527 "#,
528             r#"
529 /// .
530 ///
531 /// # Examples
532 ///
533 /// ```
534 /// use test::noop_with_param;
535 ///
536 /// noop_with_param(_a);
537 /// ```
538 pub fn noop_with_param(_a: i32) {}
539 "#,
540         );
541     }
542
543     #[test]
544     fn detects_unsafe_function() {
545         check_assist(
546             generate_documentation_template,
547             r#"
548 pub unsafe fn no$0op_unsafe() {}
549 "#,
550             r#"
551 /// .
552 ///
553 /// # Examples
554 ///
555 /// ```
556 /// use test::noop_unsafe;
557 ///
558 /// unsafe { noop_unsafe() };
559 /// ```
560 ///
561 /// # Safety
562 ///
563 /// .
564 pub unsafe fn noop_unsafe() {}
565 "#,
566         );
567     }
568
569     #[test]
570     fn guesses_panic_macro_can_panic() {
571         check_assist(
572             generate_documentation_template,
573             r#"
574 pub fn panic$0s_if(a: bool) {
575     if a {
576         panic!();
577     }
578 }
579 "#,
580             r#"
581 /// .
582 ///
583 /// # Examples
584 ///
585 /// ```
586 /// use test::panics_if;
587 ///
588 /// panics_if(a);
589 /// ```
590 ///
591 /// # Panics
592 ///
593 /// Panics if .
594 pub fn panics_if(a: bool) {
595     if a {
596         panic!();
597     }
598 }
599 "#,
600         );
601     }
602
603     #[test]
604     fn guesses_assert_macro_can_panic() {
605         check_assist(
606             generate_documentation_template,
607             r#"
608 pub fn $0panics_if_not(a: bool) {
609     assert!(a == true);
610 }
611 "#,
612             r#"
613 /// .
614 ///
615 /// # Examples
616 ///
617 /// ```
618 /// use test::panics_if_not;
619 ///
620 /// panics_if_not(a);
621 /// ```
622 ///
623 /// # Panics
624 ///
625 /// Panics if .
626 pub fn panics_if_not(a: bool) {
627     assert!(a == true);
628 }
629 "#,
630         );
631     }
632
633     #[test]
634     fn guesses_unwrap_can_panic() {
635         check_assist(
636             generate_documentation_template,
637             r#"
638 pub fn $0panics_if_none(a: Option<()>) {
639     a.unwrap();
640 }
641 "#,
642             r#"
643 /// .
644 ///
645 /// # Examples
646 ///
647 /// ```
648 /// use test::panics_if_none;
649 ///
650 /// panics_if_none(a);
651 /// ```
652 ///
653 /// # Panics
654 ///
655 /// Panics if .
656 pub fn panics_if_none(a: Option<()>) {
657     a.unwrap();
658 }
659 "#,
660         );
661     }
662
663     #[test]
664     fn guesses_expect_can_panic() {
665         check_assist(
666             generate_documentation_template,
667             r#"
668 pub fn $0panics_if_none2(a: Option<()>) {
669     a.expect("Bouh!");
670 }
671 "#,
672             r#"
673 /// .
674 ///
675 /// # Examples
676 ///
677 /// ```
678 /// use test::panics_if_none2;
679 ///
680 /// panics_if_none2(a);
681 /// ```
682 ///
683 /// # Panics
684 ///
685 /// Panics if .
686 pub fn panics_if_none2(a: Option<()>) {
687     a.expect("Bouh!");
688 }
689 "#,
690         );
691     }
692
693     #[test]
694     fn checks_output_in_example() {
695         check_assist(
696             generate_documentation_template,
697             r#"
698 pub fn returns_a_value$0() -> i32 {
699     0
700 }
701 "#,
702             r#"
703 /// .
704 ///
705 /// # Examples
706 ///
707 /// ```
708 /// use test::returns_a_value;
709 ///
710 /// assert_eq!(returns_a_value(), );
711 /// ```
712 pub fn returns_a_value() -> i32 {
713     0
714 }
715 "#,
716         );
717     }
718
719     #[test]
720     fn detects_result_output() {
721         check_assist(
722             generate_documentation_template,
723             r#"
724 pub fn returns_a_result$0() -> Result<i32, std::io::Error> {
725     Ok(0)
726 }
727 "#,
728             r#"
729 /// .
730 ///
731 /// # Examples
732 ///
733 /// ```
734 /// use test::returns_a_result;
735 ///
736 /// assert_eq!(returns_a_result(), );
737 /// ```
738 ///
739 /// # Errors
740 ///
741 /// This function will return an error if .
742 pub fn returns_a_result() -> Result<i32, std::io::Error> {
743     Ok(0)
744 }
745 "#,
746         );
747     }
748
749     #[test]
750     fn checks_ref_mut_in_example() {
751         check_assist(
752             generate_documentation_template,
753             r#"
754 pub fn modifies_a_value$0(a: &mut i32) {
755     *a = 0;
756 }
757 "#,
758             r#"
759 /// .
760 ///
761 /// # Examples
762 ///
763 /// ```
764 /// use test::modifies_a_value;
765 ///
766 /// let mut a = ;
767 /// modifies_a_value(&mut a);
768 /// assert_eq!(a, );
769 /// ```
770 pub fn modifies_a_value(a: &mut i32) {
771     *a = 0;
772 }
773 "#,
774         );
775     }
776
777     #[test]
778     fn stores_result_if_at_least_3_params() {
779         check_assist(
780             generate_documentation_template,
781             r#"
782 pub fn sum3$0(a: i32, b: i32, c: i32) -> i32 {
783     a + b + c
784 }
785 "#,
786             r#"
787 /// .
788 ///
789 /// # Examples
790 ///
791 /// ```
792 /// use test::sum3;
793 ///
794 /// let result = sum3(a, b, c);
795 /// assert_eq!(result, );
796 /// ```
797 pub fn sum3(a: i32, b: i32, c: i32) -> i32 {
798     a + b + c
799 }
800 "#,
801         );
802     }
803
804     #[test]
805     fn supports_fn_in_mods() {
806         check_assist(
807             generate_documentation_template,
808             r#"
809 pub mod a {
810     pub mod b {
811         pub fn no$0op() {}
812     }
813 }
814 "#,
815             r#"
816 pub mod a {
817     pub mod b {
818         /// .
819         ///
820         /// # Examples
821         ///
822         /// ```
823         /// use test::a::b::noop;
824         ///
825         /// noop();
826         /// ```
827         pub fn noop() {}
828     }
829 }
830 "#,
831         );
832     }
833
834     #[test]
835     fn supports_fn_in_impl() {
836         check_assist(
837             generate_documentation_template,
838             r#"
839 pub struct MyStruct;
840 impl MyStruct {
841     pub fn no$0op() {}
842 }
843 "#,
844             r#"
845 pub struct MyStruct;
846 impl MyStruct {
847     /// .
848     ///
849     /// # Examples
850     ///
851     /// ```
852     /// use test::MyStruct;
853     ///
854     /// MyStruct::noop();
855     /// ```
856     pub fn noop() {}
857 }
858 "#,
859         );
860     }
861
862     #[test]
863     fn supports_fn_in_trait() {
864         check_assist(
865             generate_documentation_template,
866             r#"
867 pub trait MyTrait {
868     fn fun$0ction_trait();
869 }
870 "#,
871             r#"
872 pub trait MyTrait {
873     /// .
874     ///
875     /// # Examples
876     ///
877     /// ```
878     /// // Example template not implemented for trait functions
879     /// ```
880     fn function_trait();
881 }
882 "#,
883         );
884     }
885
886     #[test]
887     fn supports_unsafe_fn_in_trait() {
888         check_assist(
889             generate_documentation_template,
890             r#"
891 pub trait MyTrait {
892     unsafe fn unsafe_funct$0ion_trait();
893 }
894 "#,
895             r#"
896 pub trait MyTrait {
897     /// .
898     ///
899     /// # Examples
900     ///
901     /// ```
902     /// // Example template not implemented for trait functions
903     /// ```
904     ///
905     /// # Safety
906     ///
907     /// .
908     unsafe fn unsafe_function_trait();
909 }
910 "#,
911         );
912     }
913
914     #[test]
915     fn supports_fn_in_trait_with_default_panicking() {
916         check_assist(
917             generate_documentation_template,
918             r#"
919 pub trait MyTrait {
920     fn function_trait_with_$0default_panicking() {
921         panic!()
922     }
923 }
924 "#,
925             r#"
926 pub trait MyTrait {
927     /// .
928     ///
929     /// # Examples
930     ///
931     /// ```
932     /// // Example template not implemented for trait functions
933     /// ```
934     ///
935     /// # Panics
936     ///
937     /// Panics if .
938     fn function_trait_with_default_panicking() {
939         panic!()
940     }
941 }
942 "#,
943         );
944     }
945
946     #[test]
947     fn supports_fn_in_trait_returning_result() {
948         check_assist(
949             generate_documentation_template,
950             r#"
951 pub trait MyTrait {
952     fn function_tr$0ait_returning_result() -> Result<(), std::io::Error>;
953 }
954 "#,
955             r#"
956 pub trait MyTrait {
957     /// .
958     ///
959     /// # Examples
960     ///
961     /// ```
962     /// // Example template not implemented for trait functions
963     /// ```
964     ///
965     /// # Errors
966     ///
967     /// This function will return an error if .
968     fn function_trait_returning_result() -> Result<(), std::io::Error>;
969 }
970 "#,
971         );
972     }
973
974     #[test]
975     fn detects_new() {
976         check_assist(
977             generate_documentation_template,
978             r#"
979 #[derive(Debug, PartialEq)]
980 pub struct MyGenericStruct<T> {
981     pub x: T,
982 }
983 impl<T> MyGenericStruct<T> {
984     pub fn new$0(x: T) -> MyGenericStruct<T> {
985         MyGenericStruct { x }
986     }
987 }
988 "#,
989             r#"
990 #[derive(Debug, PartialEq)]
991 pub struct MyGenericStruct<T> {
992     pub x: T,
993 }
994 impl<T> MyGenericStruct<T> {
995     /// Creates a new [`MyGenericStruct<T>`].
996     ///
997     /// # Examples
998     ///
999     /// ```
1000     /// use test::MyGenericStruct;
1001     ///
1002     /// assert_eq!(MyGenericStruct::new(x), );
1003     /// ```
1004     pub fn new(x: T) -> MyGenericStruct<T> {
1005         MyGenericStruct { x }
1006     }
1007 }
1008 "#,
1009         );
1010     }
1011
1012     #[test]
1013     fn removes_one_lifetime_from_description() {
1014         check_assist(
1015             generate_documentation_template,
1016             r#"
1017 #[derive(Debug, PartialEq)]
1018 pub struct MyGenericStruct<'a, T> {
1019     pub x: &'a T,
1020 }
1021 impl<'a, T> MyGenericStruct<'a, T> {
1022     pub fn new$0(x: &'a T) -> Self {
1023         MyGenericStruct { x }
1024     }
1025 }
1026 "#,
1027             r#"
1028 #[derive(Debug, PartialEq)]
1029 pub struct MyGenericStruct<'a, T> {
1030     pub x: &'a T,
1031 }
1032 impl<'a, T> MyGenericStruct<'a, T> {
1033     /// Creates a new [`MyGenericStruct<T>`].
1034     ///
1035     /// # Examples
1036     ///
1037     /// ```
1038     /// use test::MyGenericStruct;
1039     ///
1040     /// assert_eq!(MyGenericStruct::new(x), );
1041     /// ```
1042     pub fn new(x: &'a T) -> Self {
1043         MyGenericStruct { x }
1044     }
1045 }
1046 "#,
1047         );
1048     }
1049
1050     #[test]
1051     fn removes_all_lifetimes_from_description() {
1052         check_assist(
1053             generate_documentation_template,
1054             r#"
1055 #[derive(Debug, PartialEq)]
1056 pub struct MyGenericStruct<'a, 'b, T> {
1057     pub x: &'a T,
1058     pub y: &'b T,
1059 }
1060 impl<'a, 'b, T> MyGenericStruct<'a, 'b, T> {
1061     pub fn new$0(x: &'a T, y: &'b T) -> Self {
1062         MyGenericStruct { x, y }
1063     }
1064 }
1065 "#,
1066             r#"
1067 #[derive(Debug, PartialEq)]
1068 pub struct MyGenericStruct<'a, 'b, T> {
1069     pub x: &'a T,
1070     pub y: &'b T,
1071 }
1072 impl<'a, 'b, T> MyGenericStruct<'a, 'b, T> {
1073     /// Creates a new [`MyGenericStruct<T>`].
1074     ///
1075     /// # Examples
1076     ///
1077     /// ```
1078     /// use test::MyGenericStruct;
1079     ///
1080     /// assert_eq!(MyGenericStruct::new(x, y), );
1081     /// ```
1082     pub fn new(x: &'a T, y: &'b T) -> Self {
1083         MyGenericStruct { x, y }
1084     }
1085 }
1086 "#,
1087         );
1088     }
1089
1090     #[test]
1091     fn removes_all_lifetimes_and_brackets_from_description() {
1092         check_assist(
1093             generate_documentation_template,
1094             r#"
1095 #[derive(Debug, PartialEq)]
1096 pub struct MyGenericStruct<'a, 'b> {
1097     pub x: &'a usize,
1098     pub y: &'b usize,
1099 }
1100 impl<'a, 'b> MyGenericStruct<'a, 'b> {
1101     pub fn new$0(x: &'a usize, y: &'b usize) -> Self {
1102         MyGenericStruct { x, y }
1103     }
1104 }
1105 "#,
1106             r#"
1107 #[derive(Debug, PartialEq)]
1108 pub struct MyGenericStruct<'a, 'b> {
1109     pub x: &'a usize,
1110     pub y: &'b usize,
1111 }
1112 impl<'a, 'b> MyGenericStruct<'a, 'b> {
1113     /// Creates a new [`MyGenericStruct`].
1114     ///
1115     /// # Examples
1116     ///
1117     /// ```
1118     /// use test::MyGenericStruct;
1119     ///
1120     /// assert_eq!(MyGenericStruct::new(x, y), );
1121     /// ```
1122     pub fn new(x: &'a usize, y: &'b usize) -> Self {
1123         MyGenericStruct { x, y }
1124     }
1125 }
1126 "#,
1127         );
1128     }
1129
1130     #[test]
1131     fn detects_new_with_self() {
1132         check_assist(
1133             generate_documentation_template,
1134             r#"
1135 #[derive(Debug, PartialEq)]
1136 pub struct MyGenericStruct2<T> {
1137     pub x: T,
1138 }
1139 impl<T> MyGenericStruct2<T> {
1140     pub fn new$0(x: T) -> Self {
1141         MyGenericStruct2 { x }
1142     }
1143 }
1144 "#,
1145             r#"
1146 #[derive(Debug, PartialEq)]
1147 pub struct MyGenericStruct2<T> {
1148     pub x: T,
1149 }
1150 impl<T> MyGenericStruct2<T> {
1151     /// Creates a new [`MyGenericStruct2<T>`].
1152     ///
1153     /// # Examples
1154     ///
1155     /// ```
1156     /// use test::MyGenericStruct2;
1157     ///
1158     /// assert_eq!(MyGenericStruct2::new(x), );
1159     /// ```
1160     pub fn new(x: T) -> Self {
1161         MyGenericStruct2 { x }
1162     }
1163 }
1164 "#,
1165         );
1166     }
1167
1168     #[test]
1169     fn supports_method_call() {
1170         check_assist(
1171             generate_documentation_template,
1172             r#"
1173 impl<T> MyGenericStruct<T> {
1174     pub fn co$0nsume(self) {}
1175 }
1176 "#,
1177             r#"
1178 impl<T> MyGenericStruct<T> {
1179     /// .
1180     ///
1181     /// # Examples
1182     ///
1183     /// ```
1184     /// use test::MyGenericStruct;
1185     ///
1186     /// let my_generic_struct = ;
1187     /// my_generic_struct.consume();
1188     /// ```
1189     pub fn consume(self) {}
1190 }
1191 "#,
1192         );
1193     }
1194
1195     #[test]
1196     fn checks_modified_self_param() {
1197         check_assist(
1198             generate_documentation_template,
1199             r#"
1200 impl<T> MyGenericStruct<T> {
1201     pub fn modi$0fy(&mut self, new_value: T) {
1202         self.x = new_value;
1203     }
1204 }
1205 "#,
1206             r#"
1207 impl<T> MyGenericStruct<T> {
1208     /// .
1209     ///
1210     /// # Examples
1211     ///
1212     /// ```
1213     /// use test::MyGenericStruct;
1214     ///
1215     /// let mut my_generic_struct = ;
1216     /// my_generic_struct.modify(new_value);
1217     /// assert_eq!(my_generic_struct, );
1218     /// ```
1219     pub fn modify(&mut self, new_value: T) {
1220         self.x = new_value;
1221     }
1222 }
1223 "#,
1224         );
1225     }
1226 }