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