1use hir::{AsAssocItem, HasVisibility, ModuleDef, Visibility};
2use ide_db::assists::AssistId;
3use itertools::Itertools;
4use stdx::{format_to, to_lower_snake_case};
5use syntax::{
6 AstNode, AstToken, Edition,
7 algo::skip_whitespace_token,
8 ast::{self, HasDocComments, HasGenericArgs, HasName, edit::IndentLevel},
9 match_ast,
10};
11
12use crate::assist_context::{AssistContext, Assists};
13
14pub(crate) fn generate_documentation_template(
45 acc: &mut Assists,
46 ctx: &AssistContext<'_>,
47) -> Option<()> {
48 let name = ctx.find_node_at_offset::<ast::Name>()?;
49 let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
50 if is_in_trait_impl(&ast_func, ctx) || ast_func.doc_comments().next().is_some() {
51 return None;
52 }
53
54 let parent_syntax = ast_func.syntax();
55 let text_range = parent_syntax.text_range();
56 let indent_level = IndentLevel::from_node(parent_syntax);
57
58 acc.add(
59 AssistId::generate("generate_documentation_template"),
60 "Generate a documentation template",
61 text_range,
62 |builder| {
63 let mut doc_lines = vec![introduction_builder(&ast_func, ctx).unwrap_or(".".into())];
65 for section_builder in [panics_builder, errors_builder, safety_builder] {
67 if let Some(mut lines) = section_builder(&ast_func) {
68 doc_lines.push("".into());
69 doc_lines.append(&mut lines);
70 }
71 }
72 builder.insert(text_range.start(), documentation_from_lines(doc_lines, indent_level));
73 },
74 )
75}
76
77pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
99 let tok: ast::Comment = ctx.find_token_at_offset()?;
100 let node = tok.syntax().parent()?;
101 let last_doc_token =
102 ast::AnyHasDocComments::cast(node.clone())?.doc_comments().last()?.syntax().clone();
103 let next_token = skip_whitespace_token(last_doc_token.next_token()?, syntax::Direction::Next)?;
104
105 let example = match_ast! {
106 match node {
107 ast::Fn(it) => make_example_for_fn(&it, ctx)?,
108 _ => return None,
109 }
110 };
111
112 let mut lines = string_vec_from(&["", "# Examples", "", "```"]);
113 lines.extend(example.lines().map(String::from));
114 lines.push("```".into());
115 let indent_level = IndentLevel::from_node(&node);
116
117 acc.add(
118 AssistId::generate("generate_doc_example"),
119 "Generate a documentation example",
120 node.text_range(),
121 |builder| {
122 builder.insert(
123 next_token.text_range().start(),
124 documentation_from_lines(lines, indent_level),
125 );
126 },
127 )
128}
129
130fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> {
131 if !is_public(ast_func, ctx)? {
132 return None;
134 }
135
136 if is_in_trait_def(ast_func, ctx) {
137 return None;
139 }
140
141 let mut example = String::new();
142
143 let edition = ctx.sema.scope(ast_func.syntax())?.krate().edition(ctx.db());
144 let use_path = build_path(ast_func, ctx, edition)?;
145 let is_unsafe = ast_func.unsafe_token().is_some();
146 let param_list = ast_func.param_list()?;
147 let ref_mut_params = ref_mut_params(¶m_list);
148 let self_name = self_name(ast_func);
149
150 format_to!(example, "use {use_path};\n\n");
151 if let Some(self_name) = &self_name
152 && let Some(mut_) = is_ref_mut_self(ast_func)
153 {
154 let mut_ = if mut_ { "mut " } else { "" };
155 format_to!(example, "let {mut_}{self_name} = ;\n");
156 }
157 for param_name in &ref_mut_params {
158 format_to!(example, "let mut {param_name} = ;\n");
159 }
160 let function_call = function_call(ast_func, ¶m_list, self_name.as_deref(), is_unsafe)?;
162 if returns_a_value(ast_func, ctx) {
163 if count_parameters(¶m_list) < 3 {
164 format_to!(example, "assert_eq!({function_call}, );\n");
165 } else {
166 format_to!(example, "let result = {function_call};\n");
167 example.push_str("assert_eq!(result, );\n");
168 }
169 } else {
170 format_to!(example, "{function_call};\n");
171 }
172 if let Some(self_name) = &self_name
174 && is_ref_mut_self(ast_func) == Some(true)
175 {
176 format_to!(example, "assert_eq!({self_name}, );");
177 }
178 for param_name in &ref_mut_params {
179 format_to!(example, "assert_eq!({param_name}, );");
180 }
181
182 Some(example)
183}
184
185fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> {
186 let hir_func = ctx.sema.to_def(ast_func)?;
187 let container = hir_func.as_assoc_item(ctx.db())?.container(ctx.db());
188 if let hir::AssocItemContainer::Impl(imp) = container {
189 let ret_ty = hir_func.ret_type(ctx.db());
190 let self_ty = imp.self_ty(ctx.db());
191 let name = ast_func.name()?.to_string();
192 let linkable_self_ty = self_type_without_lifetimes(ast_func);
193 let linkable_self_ty = linkable_self_ty.as_deref();
194
195 let intro_for_new = || {
196 let is_new = name == "new";
197 if is_new && ret_ty == self_ty {
198 let self_ty = linkable_self_ty?;
199 Some(format!("Creates a new [`{self_ty}`]."))
200 } else {
201 None
202 }
203 };
204
205 let intro_for_getter = || match (
206 hir_func.self_param(ctx.sema.db),
207 &*hir_func.params_without_self(ctx.sema.db),
208 ) {
209 (Some(self_param), []) if self_param.access(ctx.sema.db) != hir::Access::Owned => {
210 if name.starts_with("as_") || name.starts_with("to_") || name == "get" {
211 return None;
212 }
213 let mut what = name.trim_end_matches("_mut").replace('_', " ");
214 if what == "len" {
215 what = "length".into()
216 }
217 let reference = if ret_ty.is_mutable_reference() {
218 " a mutable reference to"
219 } else if ret_ty.is_reference() {
220 " a reference to"
221 } else {
222 ""
223 };
224
225 let self_ty = linkable_self_ty?;
226 Some(format!("Returns{reference} the {what} of this [`{self_ty}`]."))
227 }
228 _ => None,
229 };
230
231 let intro_for_setter = || {
232 if !name.starts_with("set_") {
233 return None;
234 }
235
236 let mut what = name.trim_start_matches("set_").replace('_', " ");
237 if what == "len" {
238 what = "length".into()
239 };
240
241 let self_ty = linkable_self_ty?;
242 Some(format!("Sets the {what} of this [`{self_ty}`]."))
243 };
244
245 if let Some(intro) = intro_for_new() {
246 return Some(intro);
247 }
248 if let Some(intro) = intro_for_getter() {
249 return Some(intro);
250 }
251 if let Some(intro) = intro_for_setter() {
252 return Some(intro);
253 }
254 }
255 None
256}
257
258fn panics_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
260 match can_panic(ast_func) {
261 Some(true) => Some(string_vec_from(&["# Panics", "", "Panics if ."])),
262 _ => None,
263 }
264}
265
266fn errors_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
268 match return_type(ast_func)?.to_string().contains("Result") {
269 true => Some(string_vec_from(&["# Errors", "", "This function will return an error if ."])),
270 false => None,
271 }
272}
273
274fn safety_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
276 let is_unsafe = ast_func.unsafe_token().is_some();
277 match is_unsafe {
278 true => Some(string_vec_from(&["# Safety", "", "."])),
279 false => None,
280 }
281}
282
283fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<bool> {
285 let hir_func = ctx.sema.to_def(ast_func)?;
286 Some(
287 hir_func.visibility(ctx.db()) == Visibility::Public
288 && all_parent_mods_public(&hir_func, ctx),
289 )
290}
291
292fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext<'_>) -> bool {
294 let mut module = hir_func.module(ctx.db());
295 loop {
296 if let Some(parent) = module.parent(ctx.db()) {
297 match ModuleDef::from(module).visibility(ctx.db()) {
298 Visibility::Public => module = parent,
299 _ => break false,
300 }
301 } else {
302 break true;
303 }
304 }
305}
306
307fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> {
309 let krate = ctx.sema.scope(ast_func.syntax())?.krate();
310 Some(krate.display_name(ctx.db())?.to_string())
311}
312
313fn can_panic(ast_func: &ast::Fn) -> Option<bool> {
315 let body = ast_func.body()?.to_string();
316 let mut iter = body.chars();
317 let assert_postfix = |s| {
318 ["!(", "_eq!(", "_ne!(", "_matches!("].iter().any(|postfix| str::starts_with(s, postfix))
319 };
320
321 while !iter.as_str().is_empty() {
322 let s = iter.as_str();
323 iter.next();
324 if s.strip_prefix("debug_assert").is_some_and(assert_postfix) {
325 iter.nth(10);
326 continue;
327 }
328 if s.strip_prefix("assert").is_some_and(assert_postfix)
329 || s.starts_with("panic!(")
330 || s.starts_with(".unwrap()")
331 || s.starts_with(".expect(")
332 {
333 return Some(true);
334 }
335 }
336
337 Some(false)
338}
339
340fn self_name(ast_func: &ast::Fn) -> Option<String> {
342 self_partial_type(ast_func).map(|name| to_lower_snake_case(&name))
343}
344
345fn self_type(ast_func: &ast::Fn) -> Option<ast::Type> {
347 ast_func.syntax().ancestors().find_map(ast::Impl::cast).and_then(|i| i.self_ty())
348}
349
350fn self_type_without_lifetimes(ast_func: &ast::Fn) -> Option<String> {
352 let path_segment = match self_type(ast_func)? {
353 ast::Type::PathType(path_type) => path_type.path()?.segment()?,
354 _ => return None,
355 };
356 let mut name = path_segment.name_ref()?.to_string();
357 let generics = path_segment.generic_arg_list().into_iter().flat_map(|list| {
358 list.generic_args()
359 .filter(|generic| matches!(generic, ast::GenericArg::TypeArg(_)))
360 .map(|generic| generic.to_string())
361 });
362 let generics: String = generics.format(", ").to_string();
363 if !generics.is_empty() {
364 name.push('<');
365 name.push_str(&generics);
366 name.push('>');
367 }
368 Some(name)
369}
370
371fn self_partial_type(ast_func: &ast::Fn) -> Option<String> {
373 let mut self_type = self_type(ast_func)?.to_string();
374 if let Some(idx) = self_type.find(|c| ['<', ' '].contains(&c)) {
375 self_type.truncate(idx);
376 }
377 Some(self_type)
378}
379
380fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool {
382 ctx.sema
383 .to_def(ast_func)
384 .and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
385 .and_then(|assoc_item| assoc_item.implemented_trait(ctx.db()))
386 .is_some()
387}
388
389fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool {
391 ctx.sema
392 .to_def(ast_func)
393 .and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
394 .and_then(|assoc_item| assoc_item.container_trait(ctx.db()))
395 .is_some()
396}
397
398fn is_ref_mut_self(ast_func: &ast::Fn) -> Option<bool> {
400 let self_param = ast_func.param_list()?.self_param()?;
401 Some(self_param.mut_token().is_some() && self_param.amp_token().is_some())
402}
403
404fn is_a_ref_mut_param(param: &ast::Param) -> bool {
406 match param.ty() {
407 Some(ast::Type::RefType(param_ref)) => param_ref.mut_token().is_some(),
408 _ => false,
409 }
410}
411
412fn ref_mut_params(param_list: &ast::ParamList) -> Vec<String> {
414 param_list
415 .params()
416 .filter_map(|param| match is_a_ref_mut_param(¶m) {
417 true => Some(param.pat()?.to_string()),
421 false => None,
422 })
423 .collect()
424}
425
426fn arguments_from_params(param_list: &ast::ParamList) -> String {
428 let args_iter = param_list.params().map(|param| match param.pat() {
429 Some(ast::Pat::IdentPat(ident_pat)) => match ident_pat.name() {
433 Some(name) => match is_a_ref_mut_param(¶m) {
434 true => format!("&mut {name}"),
435 false => name.to_string(),
436 },
437 None => "_".to_owned(),
438 },
439 _ => "_".to_owned(),
440 });
441 args_iter.format(", ").to_string()
442}
443
444fn function_call(
446 ast_func: &ast::Fn,
447 param_list: &ast::ParamList,
448 self_name: Option<&str>,
449 is_unsafe: bool,
450) -> Option<String> {
451 let name = ast_func.name()?;
452 let arguments = arguments_from_params(param_list);
453 let function_call = if param_list.self_param().is_some() {
454 let self_ = self_name?;
455 format!("{self_}.{name}({arguments})")
456 } else if let Some(implementation) = self_partial_type(ast_func) {
457 format!("{implementation}::{name}({arguments})")
458 } else {
459 format!("{name}({arguments})")
460 };
461 match is_unsafe {
462 true => Some(format!("unsafe {{ {function_call} }}")),
463 false => Some(function_call),
464 }
465}
466
467fn count_parameters(param_list: &ast::ParamList) -> usize {
469 param_list.params().count() + if param_list.self_param().is_some() { 1 } else { 0 }
470}
471
472fn documentation_from_lines(doc_lines: Vec<String>, indent_level: IndentLevel) -> String {
474 let mut result = String::new();
475 for doc_line in doc_lines {
476 result.push_str("///");
477 if !doc_line.is_empty() {
478 result.push(' ');
479 result.push_str(&doc_line);
480 }
481 result.push('\n');
482 result.push_str(&indent_level.to_string());
483 }
484 result
485}
486
487fn string_vec_from(string_array: &[&str]) -> Vec<String> {
489 string_array.iter().map(|&s| s.to_owned()).collect()
490}
491
492fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_>, edition: Edition) -> Option<String> {
494 let crate_name = crate_name(ast_func, ctx)?;
495 let leaf = self_partial_type(ast_func)
496 .or_else(|| ast_func.name().map(|n| n.to_string()))
497 .unwrap_or_else(|| "*".into());
498 let module_def: ModuleDef = ctx.sema.to_def(ast_func)?.module(ctx.db()).into();
499 match module_def.canonical_path(ctx.db(), edition) {
500 Some(path) => Some(format!("{crate_name}::{path}::{leaf}")),
501 None => Some(format!("{crate_name}::{leaf}")),
502 }
503}
504
505fn return_type(ast_func: &ast::Fn) -> Option<ast::Type> {
507 ast_func.ret_type()?.ty()
508}
509
510fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool {
512 ctx.sema
513 .to_def(ast_func)
514 .map(|hir_func| hir_func.ret_type(ctx.db()))
515 .map(|ret_ty| !ret_ty.is_unit() && !ret_ty.is_never())
516 .unwrap_or(false)
517}
518
519#[cfg(test)]
520mod tests {
521 use crate::tests::{check_assist, check_assist_not_applicable};
522
523 use super::*;
524
525 #[test]
526 fn not_applicable_on_function_calls() {
527 check_assist_not_applicable(
528 generate_documentation_template,
529 r#"
530fn hello_world() {}
531fn calls_hello_world() {
532 hello_world$0();
533}
534"#,
535 )
536 }
537
538 #[test]
539 fn not_applicable_in_trait_impl() {
540 check_assist_not_applicable(
541 generate_documentation_template,
542 r#"
543trait MyTrait {}
544struct MyStruct;
545impl MyTrait for MyStruct {
546 fn hello_world$0();
547}
548"#,
549 )
550 }
551
552 #[test]
553 fn not_applicable_if_function_already_documented() {
554 check_assist_not_applicable(
555 generate_documentation_template,
556 r#"
557/// Some documentation here
558pub fn $0documented_function() {}
559"#,
560 );
561 }
562
563 #[test]
564 fn supports_noop_function() {
565 check_assist(
566 generate_documentation_template,
567 r#"
568pub fn no$0op() {}
569"#,
570 r#"
571/// .
572pub fn noop() {}
573"#,
574 );
575 }
576
577 #[test]
578 fn is_applicable_if_function_is_private() {
579 check_assist(
580 generate_documentation_template,
581 r#"
582fn priv$0ate() {}
583"#,
584 r#"
585/// .
586fn private() {}
587"#,
588 );
589 }
590
591 #[test]
592 fn no_doc_example_for_private_fn() {
593 check_assist_not_applicable(
594 generate_doc_example,
595 r#"
596///$0
597fn private() {}
598"#,
599 );
600 }
601
602 #[test]
603 fn supports_a_parameter() {
604 check_assist(
605 generate_doc_example,
606 r#"
607/// $0.
608pub fn noop_with_param(_a: i32) {}
609"#,
610 r#"
611/// .
612///
613/// # Examples
614///
615/// ```
616/// use ra_test_fixture::noop_with_param;
617///
618/// noop_with_param(_a);
619/// ```
620pub fn noop_with_param(_a: i32) {}
621"#,
622 );
623 }
624
625 #[test]
626 fn detects_unsafe_function() {
627 check_assist(
628 generate_documentation_template,
629 r#"
630pub unsafe fn no$0op_unsafe() {}
631"#,
632 r#"
633/// .
634///
635/// # Safety
636///
637/// .
638pub unsafe fn noop_unsafe() {}
639"#,
640 );
641 check_assist(
642 generate_doc_example,
643 r#"
644/// .
645///
646/// # Safety$0
647///
648/// .
649pub unsafe fn noop_unsafe() {}
650"#,
651 r#"
652/// .
653///
654/// # Safety
655///
656/// .
657///
658/// # Examples
659///
660/// ```
661/// use ra_test_fixture::noop_unsafe;
662///
663/// unsafe { noop_unsafe() };
664/// ```
665pub unsafe fn noop_unsafe() {}
666"#,
667 );
668 }
669
670 #[test]
671 fn guesses_panic_macro_can_panic() {
672 check_assist(
673 generate_documentation_template,
674 r#"
675pub fn panic$0s_if(a: bool) {
676 if a {
677 panic!();
678 }
679}
680"#,
681 r#"
682/// .
683///
684/// # Panics
685///
686/// Panics if .
687pub fn panics_if(a: bool) {
688 if a {
689 panic!();
690 }
691}
692"#,
693 );
694 }
695
696 #[test]
697 fn guesses_debug_assert_macro_cannot_panic() {
698 check_assist(
699 generate_documentation_template,
700 r#"
701pub fn $0debug_panics_if_not(a: bool) {
702 debug_assert!(a == true);
703}
704"#,
705 r#"
706/// .
707pub fn debug_panics_if_not(a: bool) {
708 debug_assert!(a == true);
709}
710"#,
711 );
712 }
713
714 #[test]
715 fn guesses_assert_macro_can_panic() {
716 check_assist(
717 generate_documentation_template,
718 r#"
719pub fn $0panics_if_not(a: bool) {
720 assert!(a == true);
721}
722"#,
723 r#"
724/// .
725///
726/// # Panics
727///
728/// Panics if .
729pub fn panics_if_not(a: bool) {
730 assert!(a == true);
731}
732"#,
733 );
734 }
735
736 #[test]
737 fn guesses_assert_eq_macro_can_panic() {
738 check_assist(
739 generate_documentation_template,
740 r#"
741pub fn $0panics_if_not(a: bool) {
742 assert_eq!(a, true);
743}
744"#,
745 r#"
746/// .
747///
748/// # Panics
749///
750/// Panics if .
751pub fn panics_if_not(a: bool) {
752 assert_eq!(a, true);
753}
754"#,
755 );
756 }
757
758 #[test]
759 fn guesses_unwrap_can_panic() {
760 check_assist(
761 generate_documentation_template,
762 r#"
763pub fn $0panics_if_none(a: Option<()>) {
764 a.unwrap();
765}
766"#,
767 r#"
768/// .
769///
770/// # Panics
771///
772/// Panics if .
773pub fn panics_if_none(a: Option<()>) {
774 a.unwrap();
775}
776"#,
777 );
778 }
779
780 #[test]
781 fn guesses_expect_can_panic() {
782 check_assist(
783 generate_documentation_template,
784 r#"
785pub fn $0panics_if_none2(a: Option<()>) {
786 a.expect("Bouh!");
787}
788"#,
789 r#"
790/// .
791///
792/// # Panics
793///
794/// Panics if .
795pub fn panics_if_none2(a: Option<()>) {
796 a.expect("Bouh!");
797}
798"#,
799 );
800 }
801
802 #[test]
803 fn checks_output_in_example() {
804 check_assist(
805 generate_doc_example,
806 r#"
807///$0
808pub fn returns_a_value$0() -> i32 {
809 0
810}
811"#,
812 r#"
813///
814///
815/// # Examples
816///
817/// ```
818/// use ra_test_fixture::returns_a_value;
819///
820/// assert_eq!(returns_a_value(), );
821/// ```
822pub fn returns_a_value() -> i32 {
823 0
824}
825"#,
826 );
827 }
828
829 #[test]
830 fn detects_result_output() {
831 check_assist(
832 generate_documentation_template,
833 r#"
834pub fn returns_a_result$0() -> Result<i32, std::io::Error> {
835 Ok(0)
836}
837"#,
838 r#"
839/// .
840///
841/// # Errors
842///
843/// This function will return an error if .
844pub fn returns_a_result() -> Result<i32, std::io::Error> {
845 Ok(0)
846}
847"#,
848 );
849 }
850
851 #[test]
852 fn checks_ref_mut_in_example() {
853 check_assist(
854 generate_doc_example,
855 r#"
856///$0
857pub fn modifies_a_value$0(a: &mut i32) {
858 *a = 0;
859}
860"#,
861 r#"
862///
863///
864/// # Examples
865///
866/// ```
867/// use ra_test_fixture::modifies_a_value;
868///
869/// let mut a = ;
870/// modifies_a_value(&mut a);
871/// assert_eq!(a, );
872/// ```
873pub fn modifies_a_value(a: &mut i32) {
874 *a = 0;
875}
876"#,
877 );
878 }
879
880 #[test]
881 fn stores_result_if_at_least_3_params() {
882 check_assist(
883 generate_doc_example,
884 r#"
885///$0
886pub fn sum3$0(a: i32, b: i32, c: i32) -> i32 {
887 a + b + c
888}
889"#,
890 r#"
891///
892///
893/// # Examples
894///
895/// ```
896/// use ra_test_fixture::sum3;
897///
898/// let result = sum3(a, b, c);
899/// assert_eq!(result, );
900/// ```
901pub fn sum3(a: i32, b: i32, c: i32) -> i32 {
902 a + b + c
903}
904"#,
905 );
906 }
907
908 #[test]
909 fn supports_fn_in_mods() {
910 check_assist(
911 generate_doc_example,
912 r#"
913pub mod a {
914 pub mod b {
915 ///$0
916 pub fn noop() {}
917 }
918}
919"#,
920 r#"
921pub mod a {
922 pub mod b {
923 ///
924 ///
925 /// # Examples
926 ///
927 /// ```
928 /// use ra_test_fixture::a::b::noop;
929 ///
930 /// noop();
931 /// ```
932 pub fn noop() {}
933 }
934}
935"#,
936 );
937 }
938
939 #[test]
940 fn supports_fn_in_impl() {
941 check_assist(
942 generate_doc_example,
943 r#"
944pub struct MyStruct;
945impl MyStruct {
946 ///$0
947 pub fn noop() {}
948}
949"#,
950 r#"
951pub struct MyStruct;
952impl MyStruct {
953 ///
954 ///
955 /// # Examples
956 ///
957 /// ```
958 /// use ra_test_fixture::MyStruct;
959 ///
960 /// MyStruct::noop();
961 /// ```
962 pub fn noop() {}
963}
964"#,
965 );
966 }
967
968 #[test]
969 fn supports_unsafe_fn_in_trait() {
970 check_assist(
971 generate_documentation_template,
972 r#"
973pub trait MyTrait {
974 unsafe fn unsafe_funct$0ion_trait();
975}
976"#,
977 r#"
978pub trait MyTrait {
979 /// .
980 ///
981 /// # Safety
982 ///
983 /// .
984 unsafe fn unsafe_function_trait();
985}
986"#,
987 );
988 }
989
990 #[test]
991 fn supports_fn_in_trait_with_default_panicking() {
992 check_assist(
993 generate_documentation_template,
994 r#"
995pub trait MyTrait {
996 fn function_trait_with_$0default_panicking() {
997 panic!()
998 }
999}
1000"#,
1001 r#"
1002pub trait MyTrait {
1003 /// .
1004 ///
1005 /// # Panics
1006 ///
1007 /// Panics if .
1008 fn function_trait_with_default_panicking() {
1009 panic!()
1010 }
1011}
1012"#,
1013 );
1014 }
1015
1016 #[test]
1017 fn supports_fn_in_trait_returning_result() {
1018 check_assist(
1019 generate_documentation_template,
1020 r#"
1021pub trait MyTrait {
1022 fn function_tr$0ait_returning_result() -> Result<(), std::io::Error>;
1023}
1024"#,
1025 r#"
1026pub trait MyTrait {
1027 /// .
1028 ///
1029 /// # Errors
1030 ///
1031 /// This function will return an error if .
1032 fn function_trait_returning_result() -> Result<(), std::io::Error>;
1033}
1034"#,
1035 );
1036 }
1037
1038 #[test]
1039 fn detects_new() {
1040 check_assist(
1041 generate_documentation_template,
1042 r#"
1043pub struct String(u8);
1044impl String {
1045 pub fn new$0(x: u8) -> String {
1046 String(x)
1047 }
1048}
1049"#,
1050 r#"
1051pub struct String(u8);
1052impl String {
1053 /// Creates a new [`String`].
1054 pub fn new(x: u8) -> String {
1055 String(x)
1056 }
1057}
1058"#,
1059 );
1060 check_assist(
1061 generate_documentation_template,
1062 r#"
1063#[derive(Debug, PartialEq)]
1064pub struct MyGenericStruct<T> {
1065 pub x: T,
1066}
1067impl<T> MyGenericStruct<T> {
1068 pub fn new$0(x: T) -> MyGenericStruct<T> {
1069 MyGenericStruct { x }
1070 }
1071}
1072"#,
1073 r#"
1074#[derive(Debug, PartialEq)]
1075pub struct MyGenericStruct<T> {
1076 pub x: T,
1077}
1078impl<T> MyGenericStruct<T> {
1079 /// Creates a new [`MyGenericStruct<T>`].
1080 pub fn new(x: T) -> MyGenericStruct<T> {
1081 MyGenericStruct { x }
1082 }
1083}
1084"#,
1085 );
1086 }
1087
1088 #[test]
1089 fn removes_one_lifetime_from_description() {
1090 check_assist(
1091 generate_documentation_template,
1092 r#"
1093#[derive(Debug, PartialEq)]
1094pub struct MyGenericStruct<'a, T> {
1095 pub x: &'a T,
1096}
1097impl<'a, T> MyGenericStruct<'a, T> {
1098 pub fn new$0(x: &'a T) -> Self {
1099 MyGenericStruct { x }
1100 }
1101}
1102"#,
1103 r#"
1104#[derive(Debug, PartialEq)]
1105pub struct MyGenericStruct<'a, T> {
1106 pub x: &'a T,
1107}
1108impl<'a, T> MyGenericStruct<'a, T> {
1109 /// Creates a new [`MyGenericStruct<T>`].
1110 pub fn new(x: &'a T) -> Self {
1111 MyGenericStruct { x }
1112 }
1113}
1114"#,
1115 );
1116 }
1117
1118 #[test]
1119 fn removes_all_lifetimes_from_description() {
1120 check_assist(
1121 generate_documentation_template,
1122 r#"
1123#[derive(Debug, PartialEq)]
1124pub struct MyGenericStruct<'a, 'b, T> {
1125 pub x: &'a T,
1126 pub y: &'b T,
1127}
1128impl<'a, 'b, T> MyGenericStruct<'a, 'b, T> {
1129 pub fn new$0(x: &'a T, y: &'b T) -> Self {
1130 MyGenericStruct { x, y }
1131 }
1132}
1133"#,
1134 r#"
1135#[derive(Debug, PartialEq)]
1136pub struct MyGenericStruct<'a, 'b, T> {
1137 pub x: &'a T,
1138 pub y: &'b T,
1139}
1140impl<'a, 'b, T> MyGenericStruct<'a, 'b, T> {
1141 /// Creates a new [`MyGenericStruct<T>`].
1142 pub fn new(x: &'a T, y: &'b T) -> Self {
1143 MyGenericStruct { x, y }
1144 }
1145}
1146"#,
1147 );
1148 }
1149
1150 #[test]
1151 fn removes_all_lifetimes_and_brackets_from_description() {
1152 check_assist(
1153 generate_documentation_template,
1154 r#"
1155#[derive(Debug, PartialEq)]
1156pub struct MyGenericStruct<'a, 'b> {
1157 pub x: &'a usize,
1158 pub y: &'b usize,
1159}
1160impl<'a, 'b> MyGenericStruct<'a, 'b> {
1161 pub fn new$0(x: &'a usize, y: &'b usize) -> Self {
1162 MyGenericStruct { x, y }
1163 }
1164}
1165"#,
1166 r#"
1167#[derive(Debug, PartialEq)]
1168pub struct MyGenericStruct<'a, 'b> {
1169 pub x: &'a usize,
1170 pub y: &'b usize,
1171}
1172impl<'a, 'b> MyGenericStruct<'a, 'b> {
1173 /// Creates a new [`MyGenericStruct`].
1174 pub fn new(x: &'a usize, y: &'b usize) -> Self {
1175 MyGenericStruct { x, y }
1176 }
1177}
1178"#,
1179 );
1180 }
1181
1182 #[test]
1183 fn detects_new_with_self() {
1184 check_assist(
1185 generate_documentation_template,
1186 r#"
1187#[derive(Debug, PartialEq)]
1188pub struct MyGenericStruct2<T> {
1189 pub x: T,
1190}
1191impl<T> MyGenericStruct2<T> {
1192 pub fn new$0(x: T) -> Self {
1193 MyGenericStruct2 { x }
1194 }
1195}
1196"#,
1197 r#"
1198#[derive(Debug, PartialEq)]
1199pub struct MyGenericStruct2<T> {
1200 pub x: T,
1201}
1202impl<T> MyGenericStruct2<T> {
1203 /// Creates a new [`MyGenericStruct2<T>`].
1204 pub fn new(x: T) -> Self {
1205 MyGenericStruct2 { x }
1206 }
1207}
1208"#,
1209 );
1210 }
1211
1212 #[test]
1213 fn supports_method_call() {
1214 check_assist(
1215 generate_doc_example,
1216 r#"
1217impl<T> MyGenericStruct<T> {
1218 ///$0
1219 pub fn consume(self) {}
1220}
1221"#,
1222 r#"
1223impl<T> MyGenericStruct<T> {
1224 ///
1225 ///
1226 /// # Examples
1227 ///
1228 /// ```
1229 /// use ra_test_fixture::MyGenericStruct;
1230 ///
1231 /// let my_generic_struct = ;
1232 /// my_generic_struct.consume();
1233 /// ```
1234 pub fn consume(self) {}
1235}
1236"#,
1237 );
1238 }
1239
1240 #[test]
1241 fn checks_modified_self_param() {
1242 check_assist(
1243 generate_doc_example,
1244 r#"
1245impl<T> MyGenericStruct<T> {
1246 ///$0
1247 pub fn modify(&mut self, new_value: T) {
1248 self.x = new_value;
1249 }
1250}
1251"#,
1252 r#"
1253impl<T> MyGenericStruct<T> {
1254 ///
1255 ///
1256 /// # Examples
1257 ///
1258 /// ```
1259 /// use ra_test_fixture::MyGenericStruct;
1260 ///
1261 /// let mut my_generic_struct = ;
1262 /// my_generic_struct.modify(new_value);
1263 /// assert_eq!(my_generic_struct, );
1264 /// ```
1265 pub fn modify(&mut self, new_value: T) {
1266 self.x = new_value;
1267 }
1268}
1269"#,
1270 );
1271 }
1272
1273 #[test]
1274 fn generates_intro_for_getters() {
1275 check_assist(
1276 generate_documentation_template,
1277 r#"
1278pub struct S;
1279impl S {
1280 pub fn speed$0(&self) -> f32 { 0.0 }
1281}
1282"#,
1283 r#"
1284pub struct S;
1285impl S {
1286 /// Returns the speed of this [`S`].
1287 pub fn speed(&self) -> f32 { 0.0 }
1288}
1289"#,
1290 );
1291 check_assist(
1292 generate_documentation_template,
1293 r#"
1294pub struct S;
1295impl S {
1296 pub fn data$0(&self) -> &[u8] { &[] }
1297}
1298"#,
1299 r#"
1300pub struct S;
1301impl S {
1302 /// Returns a reference to the data of this [`S`].
1303 pub fn data(&self) -> &[u8] { &[] }
1304}
1305"#,
1306 );
1307 check_assist(
1308 generate_documentation_template,
1309 r#"
1310pub struct S;
1311impl S {
1312 pub fn data$0(&mut self) -> &mut [u8] { &mut [] }
1313}
1314"#,
1315 r#"
1316pub struct S;
1317impl S {
1318 /// Returns a mutable reference to the data of this [`S`].
1319 pub fn data(&mut self) -> &mut [u8] { &mut [] }
1320}
1321"#,
1322 );
1323 check_assist(
1324 generate_documentation_template,
1325 r#"
1326pub struct S;
1327impl S {
1328 pub fn data_mut$0(&mut self) -> &mut [u8] { &mut [] }
1329}
1330"#,
1331 r#"
1332pub struct S;
1333impl S {
1334 /// Returns a mutable reference to the data of this [`S`].
1335 pub fn data_mut(&mut self) -> &mut [u8] { &mut [] }
1336}
1337"#,
1338 );
1339 }
1340
1341 #[test]
1342 fn no_getter_intro_for_prefixed_methods() {
1343 check_assist(
1344 generate_documentation_template,
1345 r#"
1346pub struct S;
1347impl S {
1348 pub fn as_bytes$0(&self) -> &[u8] { &[] }
1349}
1350"#,
1351 r#"
1352pub struct S;
1353impl S {
1354 /// .
1355 pub fn as_bytes(&self) -> &[u8] { &[] }
1356}
1357"#,
1358 );
1359 }
1360
1361 #[test]
1362 fn generates_intro_for_setters() {
1363 check_assist(
1364 generate_documentation_template,
1365 r#"
1366pub struct S;
1367impl S {
1368 pub fn set_data$0(&mut self, data: Vec<u8>) {}
1369}
1370"#,
1371 r#"
1372pub struct S;
1373impl S {
1374 /// Sets the data of this [`S`].
1375 pub fn set_data(&mut self, data: Vec<u8>) {}
1376}
1377"#,
1378 );
1379 check_assist(
1380 generate_documentation_template,
1381 r#"
1382pub struct S;
1383impl S {
1384 pub fn set_domain_name$0(&mut self, name: String) {}
1385}
1386"#,
1387 r#"
1388pub struct S;
1389impl S {
1390 /// Sets the domain name of this [`S`].
1391 pub fn set_domain_name(&mut self, name: String) {}
1392}
1393"#,
1394 );
1395 }
1396}