Stop offering private functions in completions

Before
Private functions have RawVisibility module, but were
missed because take_types returned None early. After resolve_visibility
returned None, Visibility::Public was set instead and private functions
ended up being offered in autocompletion.

Choosing such a function results in an immediate error diagnostic
about using a private function.

After
Pattern match of take_types that returns None and
query for Module-level visibility from the original_module

Fix #15134 - tested with a unit test and a manual end-to-end
test of building rust-analyzer from my branch and opening
the reproduction repository

REVIEW
Refactor to move scope_def_applicable and check function visibility
from a module

Please let me know what's the best way to add a unit tests to
nameres, which is where the root cause was
This commit is contained in:
petr-tik 2023-08-20 18:31:25 +01:00 committed by Lukas Wirth
parent bc9c952b6d
commit 2d879e0431
3 changed files with 53 additions and 14 deletions

View file

@ -93,11 +93,15 @@ impl DefMap {
if remaining.is_some() { if remaining.is_some() {
return None; return None;
} }
let types = result.take_types()?; let types = result.take_types();
match types {
ModuleDefId::ModuleId(m) => Visibility::Module(m), match (types, path.kind) {
_ => { (Some(ModuleDefId::ModuleId(m)), _) => Visibility::Module(m),
// error: visibility needs to refer to module // resolve_path doesn't find any values for a plan pathkind of a private function
(None, PathKind::Plain | PathKind::Crate) => {
Visibility::Module(self.module_id(original_module))
}
(_, _) => {
return None; return None;
} }
} }

View file

@ -1,6 +1,6 @@
//! Completion of names from the current scope in expression position. //! Completion of names from the current scope in expression position.
use hir::ScopeDef; use hir::{HasVisibility, Module, ScopeDef};
use syntax::ast; use syntax::ast;
use crate::{ use crate::{
@ -9,6 +9,23 @@ use crate::{
CompletionContext, Completions, CompletionContext, Completions,
}; };
fn scope_def_applicable(
def: ScopeDef,
ctx: &CompletionContext<'_>,
module: Option<&Module>,
) -> bool {
match (def, module) {
(ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_), _) => {
false
}
(ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)), _) => mac.is_fn_like(ctx.db),
(ScopeDef::ModuleDef(hir::ModuleDef::Function(f)), Some(m)) => {
f.is_visible_from(ctx.db, *m)
}
_ => true,
}
}
pub(crate) fn complete_expr_path( pub(crate) fn complete_expr_path(
acc: &mut Completions, acc: &mut Completions,
ctx: &CompletionContext<'_>, ctx: &CompletionContext<'_>,
@ -37,12 +54,6 @@ pub(crate) fn complete_expr_path(
let wants_mut_token = let wants_mut_token =
ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false); ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
let scope_def_applicable = |def| match def {
ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => false,
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
_ => true,
};
let add_assoc_item = |acc: &mut Completions, item| match item { let add_assoc_item = |acc: &mut Completions, item| match item {
hir::AssocItem::Function(func) => acc.add_function(ctx, path_ctx, func, None), hir::AssocItem::Function(func) => acc.add_function(ctx, path_ctx, func, None),
hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
@ -87,7 +98,7 @@ pub(crate) fn complete_expr_path(
hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
let module_scope = module.scope(ctx.db, Some(ctx.module)); let module_scope = module.scope(ctx.db, Some(ctx.module));
for (name, def) in module_scope { for (name, def) in module_scope {
if scope_def_applicable(def) { if scope_def_applicable(def, ctx, Some(module)) {
acc.add_path_resolution( acc.add_path_resolution(
ctx, ctx,
path_ctx, path_ctx,
@ -233,7 +244,7 @@ pub(crate) fn complete_expr_path(
[..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases), [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases),
} }
} }
_ if scope_def_applicable(def) => { _ if scope_def_applicable(def, ctx, None) => {
acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases)
} }
_ => (), _ => (),

View file

@ -1286,6 +1286,30 @@ fn here_we_go() {
); );
} }
#[test]
fn completes_only_public() {
check(
r#"
//- /e.rs
pub(self) fn i_should_be_hidden() {}
pub(in crate::krate) fn i_should_also_be_hidden() {}
pub fn i_am_public () {}
//- /lib.rs crate:krate
pub mod e;
//- /main.rs deps:krate crate:main
use krate::e;
fn main() {
e::$0
}"#,
expect![
"fn i_am_public() fn()
"
],
)
}
#[test] #[test]
fn completion_filtering_excludes_non_identifier_doc_aliases() { fn completion_filtering_excludes_non_identifier_doc_aliases() {
check_edit( check_edit(