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