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