]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/generate_documentation_template.rs
fix: remove brackets if no generic types
[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 =
233         ast::PathType::cast(self_type(ast_func)?.syntax().clone())?.path()?.segment()?;
234     let mut name = path_segment.name_ref()?.to_string();
235     let generics = path_segment
236         .generic_arg_list()?
237         .generic_args()
238         .filter(|generic| matches!(generic, ast::GenericArg::TypeArg(_)))
239         .map(|generic| generic.to_string());
240     let generics: String = Itertools::intersperse(generics, ", ".to_string()).collect();
241     if !generics.is_empty() {
242         name.push('<');
243         name.push_str(&generics);
244         name.push('>');
245     }
246     Some(name)
247 }
248
249 /// Heper function to get the name of the type of `self` without generic arguments
250 fn self_partial_type(ast_func: &ast::Fn) -> Option<String> {
251     let mut self_type = self_type(ast_func)?.to_string();
252     if let Some(idx) = self_type.find(|c| ['<', ' '].contains(&c)) {
253         self_type.truncate(idx);
254     }
255     Some(self_type)
256 }
257
258 /// Helper function to determine if the function is in a trait implementation
259 fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
260     ctx.sema
261         .to_def(ast_func)
262         .and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
263         .and_then(|assoc_item| assoc_item.containing_trait_impl(ctx.db()))
264         .is_some()
265 }
266
267 /// Helper function to determine if the function definition is in a trait definition
268 fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
269     ctx.sema
270         .to_def(ast_func)
271         .and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
272         .and_then(|assoc_item| assoc_item.containing_trait(ctx.db()))
273         .is_some()
274 }
275
276 /// Returns `None` if no `self` at all, `Some(true)` if there is `&mut self` else `Some(false)`
277 fn is_ref_mut_self(ast_func: &ast::Fn) -> Option<bool> {
278     let self_param = ast_func.param_list()?.self_param()?;
279     Some(self_param.mut_token().is_some() && self_param.amp_token().is_some())
280 }
281
282 /// Helper function to define an variable to be the `self` argument
283 fn self_definition(ast_func: &ast::Fn, self_name: Option<&str>) -> Option<String> {
284     let definition = match is_ref_mut_self(ast_func)? {
285         true => format!("let mut {} = ;", self_name?),
286         false => format!("let {} = ;", self_name?),
287     };
288     Some(definition)
289 }
290
291 /// Helper function to determine if a parameter is `&mut`
292 fn is_a_ref_mut_param(param: &ast::Param) -> bool {
293     match param.ty() {
294         Some(ast::Type::RefType(param_ref)) => param_ref.mut_token().is_some(),
295         _ => false,
296     }
297 }
298
299 /// Helper function to build the list of `&mut` parameters
300 fn ref_mut_params(param_list: &ast::ParamList) -> Vec<String> {
301     param_list
302         .params()
303         .filter_map(|param| match is_a_ref_mut_param(&param) {
304             // Maybe better filter the param name (to do this maybe extract a function from
305             // `arguments_from_params`?) in case of a `mut a: &mut T`. Anyway managing most (not
306             // all) cases might be enough, the goal is just to produce a template.
307             true => Some(param.pat()?.to_string()),
308             false => None,
309         })
310         .collect()
311 }
312
313 /// Helper function to build the comma-separated list of arguments of the function
314 fn arguments_from_params(param_list: &ast::ParamList) -> String {
315     let args_iter = param_list.params().map(|param| match param.pat() {
316         // To avoid `mut` in the function call (which would be a nonsense), `Pat` should not be
317         // written as is so its variants must be managed independently. Other variants (for
318         // instance `TuplePat`) could be managed later.
319         Some(ast::Pat::IdentPat(ident_pat)) => match ident_pat.name() {
320             Some(name) => match is_a_ref_mut_param(&param) {
321                 true => format!("&mut {}", name),
322                 false => name.to_string(),
323             },
324             None => "_".to_string(),
325         },
326         _ => "_".to_string(),
327     });
328     Itertools::intersperse(args_iter, ", ".to_string()).collect()
329 }
330
331 /// Helper function to build a function call. `None` if expected `self_name` was not provided
332 fn function_call(
333     ast_func: &ast::Fn,
334     param_list: &ast::ParamList,
335     self_name: Option<&str>,
336     is_unsafe: bool,
337 ) -> Option<String> {
338     let name = ast_func.name()?;
339     let arguments = arguments_from_params(&param_list);
340     let function_call = if param_list.self_param().is_some() {
341         format!("{}.{}({})", self_name?, name, arguments)
342     } else if let Some(implementation) = self_partial_type(ast_func) {
343         format!("{}::{}({})", implementation, name, arguments)
344     } else {
345         format!("{}({})", name, arguments)
346     };
347     match is_unsafe {
348         true => Some(format!("unsafe {{ {} }}", function_call)),
349         false => Some(function_call),
350     }
351 }
352
353 /// Helper function to count the parameters including `self`
354 fn count_parameters(param_list: &ast::ParamList) -> usize {
355     param_list.params().count() + if param_list.self_param().is_some() { 1 } else { 0 }
356 }
357
358 /// Helper function to transform lines of documentation into a Rust code documentation
359 fn documentation_from_lines(doc_lines: Vec<String>, indent_level: IndentLevel) -> String {
360     let mut result = String::new();
361     for doc_line in doc_lines {
362         result.push_str("///");
363         if !doc_line.is_empty() {
364             result.push(' ');
365             result.push_str(&doc_line);
366         }
367         result.push('\n');
368         result.push_str(&indent_level.to_string());
369     }
370     result
371 }
372
373 /// Helper function to transform an array of borrowed strings to an owned `Vec<String>`
374 fn string_vec_from(string_array: &[&str]) -> Vec<String> {
375     string_array.iter().map(|&s| s.to_owned()).collect()
376 }
377
378 /// Helper function to build the path of the module in the which is the node
379 fn build_path(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<String> {
380     let crate_name = crate_name(ast_func, ctx)?;
381     let leaf = self_partial_type(ast_func)
382         .or_else(|| ast_func.name().map(|n| n.to_string()))
383         .unwrap_or_else(|| "*".into());
384     let module_def: ModuleDef = ctx.sema.to_def(ast_func)?.module(ctx.db()).into();
385     match module_def.canonical_path(ctx.db()) {
386         Some(path) => Some(format!("{}::{}::{}", crate_name, path, leaf)),
387         None => Some(format!("{}::{}", crate_name, leaf)),
388     }
389 }
390
391 /// Helper function to get the return type of a function
392 fn return_type(ast_func: &ast::Fn) -> Option<ast::Type> {
393     ast_func.ret_type()?.ty()
394 }
395
396 /// Helper function to determine if the function returns some data
397 fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
398     ctx.sema
399         .to_def(ast_func)
400         .map(|hir_func| hir_func.ret_type(ctx.db()))
401         .map(|ret_ty| !ret_ty.is_unit() && !ret_ty.is_never())
402         .unwrap_or(false)
403 }
404
405 #[cfg(test)]
406 mod tests {
407     use crate::tests::{check_assist, check_assist_not_applicable};
408
409     use super::*;
410
411     #[test]
412     fn not_applicable_on_function_calls() {
413         check_assist_not_applicable(
414             generate_documentation_template,
415             r#"
416 fn hello_world() {}
417 fn calls_hello_world() {
418     hello_world$0();
419 }
420 "#,
421         )
422     }
423
424     #[test]
425     fn not_applicable_in_trait_impl() {
426         check_assist_not_applicable(
427             generate_documentation_template,
428             r#"
429 trait MyTrait {}
430 struct MyStruct;
431 impl MyTrait for MyStruct {
432     fn hello_world$0();
433 }
434 "#,
435         )
436     }
437
438     #[test]
439     fn not_applicable_if_function_is_private() {
440         check_assist_not_applicable(generate_documentation_template, r#"fn priv$0ate() {}"#);
441     }
442
443     #[test]
444     fn not_applicable_if_function_is_pub_crate() {
445         check_assist_not_applicable(
446             generate_documentation_template,
447             r#"pub(crate) fn pri$0vate() {}"#,
448         );
449     }
450
451     #[test]
452     fn not_applicable_if_function_is_in_private_mod() {
453         check_assist_not_applicable(
454             generate_documentation_template,
455             r#"
456 mod PrivateModule {
457     pub fn pri$0vate() {}
458 }"#,
459         );
460     }
461
462     #[test]
463     fn not_applicable_if_function_is_in_pub_crate_mod() {
464         check_assist_not_applicable(
465             generate_documentation_template,
466             r#"
467 pub(crate) mod PrivateModule {
468     pub fn pr$0ivate() {}
469 }"#,
470         );
471     }
472
473     #[test]
474     fn not_applicable_if_function_is_in_non_public_mod_is_recursive() {
475         check_assist_not_applicable(
476             generate_documentation_template,
477             r#"
478 mod ParentPrivateModule {
479     pub mod PrivateModule {
480         pub fn pr$0ivate() {}
481     }
482 }"#,
483         );
484     }
485
486     #[test]
487     fn not_applicable_if_function_already_documented() {
488         check_assist_not_applicable(
489             generate_documentation_template,
490             r#"
491 /// Some documentation here
492 pub fn $0documented_function() {}
493 "#,
494         );
495     }
496
497     #[test]
498     fn supports_noop_function() {
499         check_assist(
500             generate_documentation_template,
501             r#"
502 pub fn no$0op() {}
503 "#,
504             r#"
505 /// .
506 ///
507 /// # Examples
508 ///
509 /// ```
510 /// use test::noop;
511 ///
512 /// noop();
513 /// ```
514 pub fn noop() {}
515 "#,
516         );
517     }
518
519     #[test]
520     fn supports_a_parameter() {
521         check_assist(
522             generate_documentation_template,
523             r#"
524 pub fn no$0op_with_param(_a: i32) {}
525 "#,
526             r#"
527 /// .
528 ///
529 /// # Examples
530 ///
531 /// ```
532 /// use test::noop_with_param;
533 ///
534 /// noop_with_param(_a);
535 /// ```
536 pub fn noop_with_param(_a: i32) {}
537 "#,
538         );
539     }
540
541     #[test]
542     fn detects_unsafe_function() {
543         check_assist(
544             generate_documentation_template,
545             r#"
546 pub unsafe fn no$0op_unsafe() {}
547 "#,
548             r#"
549 /// .
550 ///
551 /// # Examples
552 ///
553 /// ```
554 /// use test::noop_unsafe;
555 ///
556 /// unsafe { noop_unsafe() };
557 /// ```
558 ///
559 /// # Safety
560 ///
561 /// .
562 pub unsafe fn noop_unsafe() {}
563 "#,
564         );
565     }
566
567     #[test]
568     fn guesses_panic_macro_can_panic() {
569         check_assist(
570             generate_documentation_template,
571             r#"
572 pub fn panic$0s_if(a: bool) {
573     if a {
574         panic!();
575     }
576 }
577 "#,
578             r#"
579 /// .
580 ///
581 /// # Examples
582 ///
583 /// ```
584 /// use test::panics_if;
585 ///
586 /// panics_if(a);
587 /// ```
588 ///
589 /// # Panics
590 ///
591 /// Panics if .
592 pub fn panics_if(a: bool) {
593     if a {
594         panic!();
595     }
596 }
597 "#,
598         );
599     }
600
601     #[test]
602     fn guesses_assert_macro_can_panic() {
603         check_assist(
604             generate_documentation_template,
605             r#"
606 pub fn $0panics_if_not(a: bool) {
607     assert!(a == true);
608 }
609 "#,
610             r#"
611 /// .
612 ///
613 /// # Examples
614 ///
615 /// ```
616 /// use test::panics_if_not;
617 ///
618 /// panics_if_not(a);
619 /// ```
620 ///
621 /// # Panics
622 ///
623 /// Panics if .
624 pub fn panics_if_not(a: bool) {
625     assert!(a == true);
626 }
627 "#,
628         );
629     }
630
631     #[test]
632     fn guesses_unwrap_can_panic() {
633         check_assist(
634             generate_documentation_template,
635             r#"
636 pub fn $0panics_if_none(a: Option<()>) {
637     a.unwrap();
638 }
639 "#,
640             r#"
641 /// .
642 ///
643 /// # Examples
644 ///
645 /// ```
646 /// use test::panics_if_none;
647 ///
648 /// panics_if_none(a);
649 /// ```
650 ///
651 /// # Panics
652 ///
653 /// Panics if .
654 pub fn panics_if_none(a: Option<()>) {
655     a.unwrap();
656 }
657 "#,
658         );
659     }
660
661     #[test]
662     fn guesses_expect_can_panic() {
663         check_assist(
664             generate_documentation_template,
665             r#"
666 pub fn $0panics_if_none2(a: Option<()>) {
667     a.expect("Bouh!");
668 }
669 "#,
670             r#"
671 /// .
672 ///
673 /// # Examples
674 ///
675 /// ```
676 /// use test::panics_if_none2;
677 ///
678 /// panics_if_none2(a);
679 /// ```
680 ///
681 /// # Panics
682 ///
683 /// Panics if .
684 pub fn panics_if_none2(a: Option<()>) {
685     a.expect("Bouh!");
686 }
687 "#,
688         );
689     }
690
691     #[test]
692     fn checks_output_in_example() {
693         check_assist(
694             generate_documentation_template,
695             r#"
696 pub fn returns_a_value$0() -> i32 {
697     0
698 }
699 "#,
700             r#"
701 /// .
702 ///
703 /// # Examples
704 ///
705 /// ```
706 /// use test::returns_a_value;
707 ///
708 /// assert_eq!(returns_a_value(), );
709 /// ```
710 pub fn returns_a_value() -> i32 {
711     0
712 }
713 "#,
714         );
715     }
716
717     #[test]
718     fn detects_result_output() {
719         check_assist(
720             generate_documentation_template,
721             r#"
722 pub fn returns_a_result$0() -> Result<i32, std::io::Error> {
723     Ok(0)
724 }
725 "#,
726             r#"
727 /// .
728 ///
729 /// # Examples
730 ///
731 /// ```
732 /// use test::returns_a_result;
733 ///
734 /// assert_eq!(returns_a_result(), );
735 /// ```
736 ///
737 /// # Errors
738 ///
739 /// This function will return an error if .
740 pub fn returns_a_result() -> Result<i32, std::io::Error> {
741     Ok(0)
742 }
743 "#,
744         );
745     }
746
747     #[test]
748     fn checks_ref_mut_in_example() {
749         check_assist(
750             generate_documentation_template,
751             r#"
752 pub fn modifies_a_value$0(a: &mut i32) {
753     *a = 0;
754 }
755 "#,
756             r#"
757 /// .
758 ///
759 /// # Examples
760 ///
761 /// ```
762 /// use test::modifies_a_value;
763 ///
764 /// let mut a = ;
765 /// modifies_a_value(&mut a);
766 /// assert_eq!(a, );
767 /// ```
768 pub fn modifies_a_value(a: &mut i32) {
769     *a = 0;
770 }
771 "#,
772         );
773     }
774
775     #[test]
776     fn stores_result_if_at_least_3_params() {
777         check_assist(
778             generate_documentation_template,
779             r#"
780 pub fn sum3$0(a: i32, b: i32, c: i32) -> i32 {
781     a + b + c
782 }
783 "#,
784             r#"
785 /// .
786 ///
787 /// # Examples
788 ///
789 /// ```
790 /// use test::sum3;
791 ///
792 /// let result = sum3(a, b, c);
793 /// assert_eq!(result, );
794 /// ```
795 pub fn sum3(a: i32, b: i32, c: i32) -> i32 {
796     a + b + c
797 }
798 "#,
799         );
800     }
801
802     #[test]
803     fn supports_fn_in_mods() {
804         check_assist(
805             generate_documentation_template,
806             r#"
807 pub mod a {
808     pub mod b {
809         pub fn no$0op() {}
810     }
811 }
812 "#,
813             r#"
814 pub mod a {
815     pub mod b {
816         /// .
817         ///
818         /// # Examples
819         ///
820         /// ```
821         /// use test::a::b::noop;
822         ///
823         /// noop();
824         /// ```
825         pub fn noop() {}
826     }
827 }
828 "#,
829         );
830     }
831
832     #[test]
833     fn supports_fn_in_impl() {
834         check_assist(
835             generate_documentation_template,
836             r#"
837 pub struct MyStruct;
838 impl MyStruct {
839     pub fn no$0op() {}
840 }
841 "#,
842             r#"
843 pub struct MyStruct;
844 impl MyStruct {
845     /// .
846     ///
847     /// # Examples
848     ///
849     /// ```
850     /// use test::MyStruct;
851     ///
852     /// MyStruct::noop();
853     /// ```
854     pub fn noop() {}
855 }
856 "#,
857         );
858     }
859
860     #[test]
861     fn supports_fn_in_trait() {
862         check_assist(
863             generate_documentation_template,
864             r#"
865 pub trait MyTrait {
866     fn fun$0ction_trait();
867 }
868 "#,
869             r#"
870 pub trait MyTrait {
871     /// .
872     ///
873     /// # Examples
874     ///
875     /// ```
876     /// // Example template not implemented for trait functions
877     /// ```
878     fn function_trait();
879 }
880 "#,
881         );
882     }
883
884     #[test]
885     fn supports_unsafe_fn_in_trait() {
886         check_assist(
887             generate_documentation_template,
888             r#"
889 pub trait MyTrait {
890     unsafe fn unsafe_funct$0ion_trait();
891 }
892 "#,
893             r#"
894 pub trait MyTrait {
895     /// .
896     ///
897     /// # Examples
898     ///
899     /// ```
900     /// // Example template not implemented for trait functions
901     /// ```
902     ///
903     /// # Safety
904     ///
905     /// .
906     unsafe fn unsafe_function_trait();
907 }
908 "#,
909         );
910     }
911
912     #[test]
913     fn supports_fn_in_trait_with_default_panicking() {
914         check_assist(
915             generate_documentation_template,
916             r#"
917 pub trait MyTrait {
918     fn function_trait_with_$0default_panicking() {
919         panic!()
920     }
921 }
922 "#,
923             r#"
924 pub trait MyTrait {
925     /// .
926     ///
927     /// # Examples
928     ///
929     /// ```
930     /// // Example template not implemented for trait functions
931     /// ```
932     ///
933     /// # Panics
934     ///
935     /// Panics if .
936     fn function_trait_with_default_panicking() {
937         panic!()
938     }
939 }
940 "#,
941         );
942     }
943
944     #[test]
945     fn supports_fn_in_trait_returning_result() {
946         check_assist(
947             generate_documentation_template,
948             r#"
949 pub trait MyTrait {
950     fn function_tr$0ait_returning_result() -> Result<(), std::io::Error>;
951 }
952 "#,
953             r#"
954 pub trait MyTrait {
955     /// .
956     ///
957     /// # Examples
958     ///
959     /// ```
960     /// // Example template not implemented for trait functions
961     /// ```
962     ///
963     /// # Errors
964     ///
965     /// This function will return an error if .
966     fn function_trait_returning_result() -> Result<(), std::io::Error>;
967 }
968 "#,
969         );
970     }
971
972     #[test]
973     fn detects_new() {
974         check_assist(
975             generate_documentation_template,
976             r#"
977 #[derive(Debug, PartialEq)]
978 pub struct MyGenericStruct<T> {
979     pub x: T,
980 }
981 impl<T> MyGenericStruct<T> {
982     pub fn new$0(x: T) -> MyGenericStruct<T> {
983         MyGenericStruct { x }
984     }
985 }
986 "#,
987             r#"
988 #[derive(Debug, PartialEq)]
989 pub struct MyGenericStruct<T> {
990     pub x: T,
991 }
992 impl<T> MyGenericStruct<T> {
993     /// Creates a new [`MyGenericStruct<T>`].
994     ///
995     /// # Examples
996     ///
997     /// ```
998     /// use test::MyGenericStruct;
999     ///
1000     /// assert_eq!(MyGenericStruct::new(x), );
1001     /// ```
1002     pub fn new(x: T) -> MyGenericStruct<T> {
1003         MyGenericStruct { x }
1004     }
1005 }
1006 "#,
1007         );
1008     }
1009
1010     #[test]
1011     fn removes_one_lifetime_from_description() {
1012         check_assist(
1013             generate_documentation_template,
1014             r#"
1015 #[derive(Debug, PartialEq)]
1016 pub struct MyGenericStruct<'a, T> {
1017     pub x: &'a T,
1018 }
1019 impl<'a, T> MyGenericStruct<'a, T> {
1020     pub fn new$0(x: &'a T) -> Self {
1021         MyGenericStruct { x }
1022     }
1023 }
1024 "#,
1025             r#"
1026 #[derive(Debug, PartialEq)]
1027 pub struct MyGenericStruct<'a, T> {
1028     pub x: &'a T,
1029 }
1030 impl<'a, T> MyGenericStruct<'a, T> {
1031     /// Creates a new [`MyGenericStruct<T>`].
1032     ///
1033     /// # Examples
1034     ///
1035     /// ```
1036     /// use test::MyGenericStruct;
1037     ///
1038     /// assert_eq!(MyGenericStruct::new(x), );
1039     /// ```
1040     pub fn new(x: &'a T) -> Self {
1041         MyGenericStruct { x }
1042     }
1043 }
1044 "#,
1045         );
1046     }
1047
1048     #[test]
1049     fn removes_all_lifetimes_from_description() {
1050         check_assist(
1051             generate_documentation_template,
1052             r#"
1053 #[derive(Debug, PartialEq)]
1054 pub struct MyGenericStruct<'a, 'b, T> {
1055     pub x: &'a T,
1056     pub y: &'b T,
1057 }
1058 impl<'a, 'b, T> MyGenericStruct<'a, 'b, T> {
1059     pub fn new$0(x: &'a T, y: &'b T) -> Self {
1060         MyGenericStruct { x, y }
1061     }
1062 }
1063 "#,
1064             r#"
1065 #[derive(Debug, PartialEq)]
1066 pub struct MyGenericStruct<'a, 'b, T> {
1067     pub x: &'a T,
1068     pub y: &'b T,
1069 }
1070 impl<'a, 'b, T> MyGenericStruct<'a, 'b, T> {
1071     /// Creates a new [`MyGenericStruct<T>`].
1072     ///
1073     /// # Examples
1074     ///
1075     /// ```
1076     /// use test::MyGenericStruct;
1077     ///
1078     /// assert_eq!(MyGenericStruct::new(x, y), );
1079     /// ```
1080     pub fn new(x: &'a T, y: &'b T) -> Self {
1081         MyGenericStruct { x, y }
1082     }
1083 }
1084 "#,
1085         );
1086     }
1087
1088     #[test]
1089     fn removes_all_lifetimes_and_brackets_from_description() {
1090         check_assist(
1091             generate_documentation_template,
1092             r#"
1093 #[derive(Debug, PartialEq)]
1094 pub struct MyGenericStruct<'a, 'b> {
1095     pub x: &'a usize,
1096     pub y: &'b usize,
1097 }
1098 impl<'a, 'b> MyGenericStruct<'a, 'b> {
1099     pub fn new$0(x: &'a usize, y: &'b usize) -> Self {
1100         MyGenericStruct { x, y }
1101     }
1102 }
1103 "#,
1104             r#"
1105 #[derive(Debug, PartialEq)]
1106 pub struct MyGenericStruct<'a, 'b> {
1107     pub x: &'a usize,
1108     pub y: &'b usize,
1109 }
1110 impl<'a, 'b> MyGenericStruct<'a, 'b> {
1111     /// Creates a new [`MyGenericStruct`].
1112     ///
1113     /// # Examples
1114     ///
1115     /// ```
1116     /// use test::MyGenericStruct;
1117     ///
1118     /// assert_eq!(MyGenericStruct::new(x, y), );
1119     /// ```
1120     pub fn new(x: &'a usize, y: &'b usize) -> Self {
1121         MyGenericStruct { x, y }
1122     }
1123 }
1124 "#,
1125         );
1126     }
1127
1128     #[test]
1129     fn detects_new_with_self() {
1130         check_assist(
1131             generate_documentation_template,
1132             r#"
1133 #[derive(Debug, PartialEq)]
1134 pub struct MyGenericStruct2<T> {
1135     pub x: T,
1136 }
1137 impl<T> MyGenericStruct2<T> {
1138     pub fn new$0(x: T) -> Self {
1139         MyGenericStruct2 { x }
1140     }
1141 }
1142 "#,
1143             r#"
1144 #[derive(Debug, PartialEq)]
1145 pub struct MyGenericStruct2<T> {
1146     pub x: T,
1147 }
1148 impl<T> MyGenericStruct2<T> {
1149     /// Creates a new [`MyGenericStruct2<T>`].
1150     ///
1151     /// # Examples
1152     ///
1153     /// ```
1154     /// use test::MyGenericStruct2;
1155     ///
1156     /// assert_eq!(MyGenericStruct2::new(x), );
1157     /// ```
1158     pub fn new(x: T) -> Self {
1159         MyGenericStruct2 { x }
1160     }
1161 }
1162 "#,
1163         );
1164     }
1165
1166     #[test]
1167     fn supports_method_call() {
1168         check_assist(
1169             generate_documentation_template,
1170             r#"
1171 impl<T> MyGenericStruct<T> {
1172     pub fn co$0nsume(self) {}
1173 }
1174 "#,
1175             r#"
1176 impl<T> MyGenericStruct<T> {
1177     /// .
1178     ///
1179     /// # Examples
1180     ///
1181     /// ```
1182     /// use test::MyGenericStruct;
1183     ///
1184     /// let my_generic_struct = ;
1185     /// my_generic_struct.consume();
1186     /// ```
1187     pub fn consume(self) {}
1188 }
1189 "#,
1190         );
1191     }
1192
1193     #[test]
1194     fn checks_modified_self_param() {
1195         check_assist(
1196             generate_documentation_template,
1197             r#"
1198 impl<T> MyGenericStruct<T> {
1199     pub fn modi$0fy(&mut self, new_value: T) {
1200         self.x = new_value;
1201     }
1202 }
1203 "#,
1204             r#"
1205 impl<T> MyGenericStruct<T> {
1206     /// .
1207     ///
1208     /// # Examples
1209     ///
1210     /// ```
1211     /// use test::MyGenericStruct;
1212     ///
1213     /// let mut my_generic_struct = ;
1214     /// my_generic_struct.modify(new_value);
1215     /// assert_eq!(my_generic_struct, );
1216     /// ```
1217     pub fn modify(&mut self, new_value: T) {
1218         self.x = new_value;
1219     }
1220 }
1221 "#,
1222         );
1223     }
1224 }