use ide_db::assists::{AssistId, AssistKind};
use stdx::to_lower_snake_case;
use syntax::{
- ast::{self, edit::IndentLevel, HasDocComments, HasName},
+ ast::{self, edit::IndentLevel, HasDocComments, HasName, HasVisibility},
AstNode,
};
// Adds a documentation template above a function definition / declaration.
//
// ```
-// fn my_$0func(a: i32, b: i32) -> Result<(), std::io::Error> {
+// pub fn my_$0func(a: i32, b: i32) -> Result<(), std::io::Error> {
// unimplemented!()
// }
// ```
// /// # Errors
// ///
// /// This function will return an error if .
-// fn my_func(a: i32, b: i32) -> Result<(), std::io::Error> {
+// pub fn my_func(a: i32, b: i32) -> Result<(), std::io::Error> {
// unimplemented!()
// }
// ```
ctx: &AssistContext,
) -> Option<()> {
let name = ctx.find_node_at_offset::<ast::Name>()?;
- let ast_func = ast::Fn::cast(name.syntax().parent()?)?;
- if is_in_trait_impl(&ast_func) {
+ let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
+ if is_in_trait_impl(&ast_func) || !is_public(&ast_func) {
return None;
}
- // TODO disable at least examples if function not public, as the example will fail to build on
- // `cargo test`. What is the exact criteria of `pub`ness? All parent modules must be `pub`, for
- // `impl { fn }` both `fn` and `struct`* must be public.
- //
- // What about `pub(crate)`?
- //
- // *: Seems complex but maybe ignoring this criteria can be ignored.
let parent_syntax = ast_func.syntax();
let text_range = parent_syntax.text_range();
Some((lines, ex_helper))
}
+/// Check if the function and all its parent modules are exactly `pub`
+fn is_public(ast_func: &ast::Fn) -> bool {
+ has_pub(ast_func)
+ && ast_func
+ .syntax()
+ .ancestors()
+ .filter_map(ast::Module::cast)
+ .all(|module| has_pub(&module))
+}
+
+/// Check if visibility is exactly `pub` (not `pub(crate)` for instance)
+fn has_pub<T: HasVisibility>(item: &T) -> bool {
+ item.visibility().map(|v| v.path().is_none()).unwrap_or(false)
+}
+
/// `None` if function without a body; some bool to guess if function can panic
fn can_panic(ast_func: &ast::Fn) -> Option<bool> {
let body = ast_func.body()?.to_string();
)
}
+ #[test]
+ fn not_applicable_if_function_is_private() {
+ check_assist_not_applicable(generate_documentation_template, r#"fn priv$0ate() {}"#);
+ }
+
+ #[test]
+ fn not_applicable_if_function_is_pub_crate() {
+ check_assist_not_applicable(
+ generate_documentation_template,
+ r#"pub(crate) fn pri$0vate() {}"#,
+ );
+ }
+
+ #[test]
+ fn not_applicable_if_function_is_in_private_mod() {
+ check_assist_not_applicable(
+ generate_documentation_template,
+ r#"
+mod PrivateModule {
+ pub fn pri$0vate() {}
+}"#,
+ );
+ }
+
+ #[test]
+ fn not_applicable_if_function_is_in_pub_crate_mod() {
+ check_assist_not_applicable(
+ generate_documentation_template,
+ r#"
+pub(crate) mod PrivateModule {
+ pub fn pr$0ivate() {}
+}"#,
+ );
+ }
+
+ #[test]
+ fn not_applicable_if_function_is_in_non_public_mod_is_recursive() {
+ check_assist_not_applicable(
+ generate_documentation_template,
+ r#"
+mod ParentPrivateModule {
+ pub mod PrivateModule {
+ pub fn pr$0ivate() {}
+ }
+}"#,
+ );
+ }
+
#[test]
fn supports_noop_function() {
check_assist(
generate_documentation_template,
r#"
-fn no$0op() {}
+pub fn no$0op() {}
"#,
r#"
/// .
///
/// noop();
/// ```
-fn noop() {}
+pub fn noop() {}
"#,
);
}
check_assist(
generate_documentation_template,
r#"
-fn no$0op_with_param(_a: i32) {}
+pub fn no$0op_with_param(_a: i32) {}
"#,
r#"
/// .
///
/// noop_with_param(_a);
/// ```
-fn noop_with_param(_a: i32) {}
+pub fn noop_with_param(_a: i32) {}
"#,
);
}
check_assist(
generate_documentation_template,
r#"
-unsafe fn no$0op_unsafe() {}
+pub unsafe fn no$0op_unsafe() {}
"#,
r#"
/// .
/// # Safety
///
/// .
-unsafe fn noop_unsafe() {}
+pub unsafe fn noop_unsafe() {}
"#,
);
}
check_assist(
generate_documentation_template,
r#"
-fn panic$0s_if(a: bool) {
+pub fn panic$0s_if(a: bool) {
if a {
panic!();
}
/// # Panics
///
/// Panics if .
-fn panics_if(a: bool) {
+pub fn panics_if(a: bool) {
if a {
panic!();
}
check_assist(
generate_documentation_template,
r#"
-fn $0panics_if_not(a: bool) {
+pub fn $0panics_if_not(a: bool) {
assert!(a == true);
}
"#,
/// # Panics
///
/// Panics if .
-fn panics_if_not(a: bool) {
+pub fn panics_if_not(a: bool) {
assert!(a == true);
}
"#,
check_assist(
generate_documentation_template,
r#"
-fn $0panics_if_none(a: Option<()>) {
+pub fn $0panics_if_none(a: Option<()>) {
a.unwrap();
}
"#,
/// # Panics
///
/// Panics if .
-fn panics_if_none(a: Option<()>) {
+pub fn panics_if_none(a: Option<()>) {
a.unwrap();
}
"#,
check_assist(
generate_documentation_template,
r#"
-fn $0panics_if_none2(a: Option<()>) {
+pub fn $0panics_if_none2(a: Option<()>) {
a.expect("Bouh!");
}
"#,
/// # Panics
///
/// Panics if .
-fn panics_if_none2(a: Option<()>) {
+pub fn panics_if_none2(a: Option<()>) {
a.expect("Bouh!");
}
"#,
check_assist(
generate_documentation_template,
r#"
-fn returns_a_value$0() -> i32 {
+pub fn returns_a_value$0() -> i32 {
0
}
"#,
///
/// assert_eq!(returns_a_value(), );
/// ```
-fn returns_a_value() -> i32 {
+pub fn returns_a_value() -> i32 {
0
}
"#,
check_assist(
generate_documentation_template,
r#"
-fn returns_a_result$0() -> Result<i32, std::io::Error> {
+pub fn returns_a_result$0() -> Result<i32, std::io::Error> {
Ok(0)
}
"#,
/// # Errors
///
/// This function will return an error if .
-fn returns_a_result() -> Result<i32, std::io::Error> {
+pub fn returns_a_result() -> Result<i32, std::io::Error> {
Ok(0)
}
"#,
check_assist(
generate_documentation_template,
r#"
-fn modifies_a_value$0(a: &mut i32) {
+pub fn modifies_a_value$0(a: &mut i32) {
*a = 0;
}
"#,
/// modifies_a_value(&mut a);
/// assert_eq!(a, );
/// ```
-fn modifies_a_value(a: &mut i32) {
+pub fn modifies_a_value(a: &mut i32) {
*a = 0;
}
"#,
check_assist(
generate_documentation_template,
r#"
-fn sum3$0(a: i32, b: i32, c: i32) -> i32 {
+pub fn sum3$0(a: i32, b: i32, c: i32) -> i32 {
a + b + c
}
"#,
/// let result = sum3(a, b, c);
/// assert_eq!(result, );
/// ```
-fn sum3(a: i32, b: i32, c: i32) -> i32 {
+pub fn sum3(a: i32, b: i32, c: i32) -> i32 {
a + b + c
}
"#,
check_assist(
generate_documentation_template,
r#"
-mod a {
- mod b {
- fn no$0op() {}
+pub mod a {
+ pub mod b {
+ pub fn no$0op() {}
}
}
"#,
r#"
-mod a {
- mod b {
+pub mod a {
+ pub mod b {
/// .
///
/// # Examples
///
/// noop();
/// ```
- fn noop() {}
+ pub fn noop() {}
}
}
"#,
check_assist(
generate_documentation_template,
r#"
-struct MyStruct;
+pub struct MyStruct;
impl MyStruct {
- fn no$0op() {}
+ pub fn no$0op() {}
}
"#,
r#"
-struct MyStruct;
+pub struct MyStruct;
impl MyStruct {
/// .
///
///
/// MyStruct::noop();
/// ```
- fn noop() {}
+ pub fn noop() {}
}
"#,
);