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