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