Skip to content

Commit c3d151a

Browse files
author
Côme ALLART
committed
fix: check correctly if function is exported
1 parent dc4e4c7 commit c3d151a

File tree

1 file changed

+30
-20
lines changed

1 file changed

+30
-20
lines changed

crates/ide_assists/src/handlers/generate_documentation_template.rs

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use hir::ModuleDef;
1+
use hir::{HasVisibility, ModuleDef, Visibility};
22
use ide_db::assists::{AssistId, AssistKind};
33
use stdx::to_lower_snake_case;
44
use syntax::{
5-
ast::{self, edit::IndentLevel, HasDocComments, HasName, HasVisibility},
5+
ast::{self, edit::IndentLevel, HasDocComments, HasName},
66
AstNode,
77
};
88

@@ -43,7 +43,7 @@ pub(crate) fn generate_documentation_template(
4343
let name = ctx.find_node_at_offset::<ast::Name>()?;
4444
let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
4545
if is_in_trait_impl(&ast_func)
46-
|| !is_public(&ast_func)
46+
|| !is_public(&ast_func, ctx)?
4747
|| ast_func.doc_comments().next().is_some()
4848
{
4949
return None;
@@ -187,7 +187,7 @@ struct ExHelper {
187187
self_name: Option<String>,
188188
}
189189

190-
/// Build the start of the example and transmit the useful intermediary results.
190+
/// Builds the start of the example and transmit the useful intermediary results.
191191
/// `None` if the function has a `self` parameter but is not in an `impl`.
192192
fn gen_ex_start_helper(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<(Vec<String>, ExHelper)> {
193193
let mut lines = Vec::new();
@@ -209,31 +209,41 @@ fn gen_ex_start_helper(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<(Vec<S
209209
Some((lines, ex_helper))
210210
}
211211

212-
/// Check if the function and all its parent modules are exactly `pub`
213-
fn is_public(ast_func: &ast::Fn) -> bool {
214-
has_pub(ast_func)
215-
&& ast_func
216-
.syntax()
217-
.ancestors()
218-
.filter_map(ast::Module::cast)
219-
.all(|module| has_pub(&module))
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+
)
220219
}
221220

222-
/// Get the name of the current crate
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
223237
fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<String> {
224238
let krate = ctx.sema.scope(&ast_func.syntax()).module()?.krate();
225239
Some(krate.display_name(ctx.db())?.to_string())
226240
}
227241

228-
/// Check if visibility is exactly `pub` (not `pub(crate)` for instance)
229-
fn has_pub<T: HasVisibility>(item: &T) -> bool {
230-
item.visibility().map(|v| v.path().is_none()).unwrap_or(false)
231-
}
232-
233242
/// `None` if function without a body; some bool to guess if function can panic
234243
fn can_panic(ast_func: &ast::Fn) -> Option<bool> {
235244
let body = ast_func.body()?.to_string();
236245
let can_panic = body.contains("panic!(")
246+
// FIXME it would be better to not match `debug_assert*!` macro invocations
237247
|| body.contains("assert!(")
238248
|| body.contains(".unwrap()")
239249
|| body.contains(".expect(");
@@ -387,8 +397,8 @@ fn build_path(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<String> {
387397
let leaf = self_partial_type(ast_func)
388398
.or_else(|| ast_func.name().map(|n| n.to_string()))
389399
.unwrap_or_else(|| "*".into());
390-
let func_module_def: ModuleDef = ctx.sema.to_def(ast_func)?.module(ctx.db()).into();
391-
match func_module_def.canonical_path(ctx.db()) {
400+
let module_def: ModuleDef = ctx.sema.to_def(ast_func)?.module(ctx.db()).into();
401+
match module_def.canonical_path(ctx.db()) {
392402
Some(path) => Some(format!("{}::{}::{}", crate_name, path, leaf)),
393403
None => Some(format!("{}::{}", crate_name, leaf)),
394404
}

0 commit comments

Comments
 (0)