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