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