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