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