Fix flyimport not filtering via stability of import path

This commit is contained in:
Lukas Wirth 2025-01-25 12:18:54 +01:00
parent e22bcfbf57
commit b31f53e0d6
21 changed files with 89 additions and 20 deletions

View file

@ -445,6 +445,10 @@ fn find_in_dep(
}; };
cov_mark::hit!(partially_imported); cov_mark::hit!(partially_imported);
if info.is_unstable { if info.is_unstable {
if !ctx.cfg.allow_unstable {
// the item is unstable and we are not allowed to use unstable items
continue;
}
choice.stability = Unstable; choice.stability = Unstable;
} }
@ -670,6 +674,7 @@ mod tests {
prefer_prelude: bool, prefer_prelude: bool,
prefer_absolute: bool, prefer_absolute: bool,
prefer_no_std: bool, prefer_no_std: bool,
allow_unstable: bool,
expect: Expect, expect: Expect,
) { ) {
let (db, pos) = TestDB::with_position(ra_fixture); let (db, pos) = TestDB::with_position(ra_fixture);
@ -711,7 +716,7 @@ mod tests {
module, module,
prefix, prefix,
ignore_local_imports, ignore_local_imports,
ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute }, ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable },
); );
format_to!( format_to!(
res, res,
@ -732,7 +737,7 @@ mod tests {
path: &str, path: &str,
expect: Expect, expect: Expect,
) { ) {
check_found_path_(ra_fixture, path, false, false, false, expect); check_found_path_(ra_fixture, path, false, false, false, false, expect);
} }
fn check_found_path_prelude( fn check_found_path_prelude(
@ -740,7 +745,7 @@ mod tests {
path: &str, path: &str,
expect: Expect, expect: Expect,
) { ) {
check_found_path_(ra_fixture, path, true, false, false, expect); check_found_path_(ra_fixture, path, true, false, false, false, expect);
} }
fn check_found_path_absolute( fn check_found_path_absolute(
@ -748,7 +753,7 @@ mod tests {
path: &str, path: &str,
expect: Expect, expect: Expect,
) { ) {
check_found_path_(ra_fixture, path, false, true, false, expect); check_found_path_(ra_fixture, path, false, true, false, false, expect);
} }
fn check_found_path_prefer_no_std( fn check_found_path_prefer_no_std(
@ -756,7 +761,15 @@ mod tests {
path: &str, path: &str,
expect: Expect, expect: Expect,
) { ) {
check_found_path_(ra_fixture, path, false, false, true, expect); check_found_path_(ra_fixture, path, false, false, true, false, expect);
}
fn check_found_path_prefer_no_std_allow_unstable(
#[rust_analyzer::rust_fixture] ra_fixture: &str,
path: &str,
expect: Expect,
) {
check_found_path_(ra_fixture, path, false, false, true, true, expect);
} }
#[test] #[test]
@ -1951,7 +1964,7 @@ pub mod ops {
#[test] #[test]
fn respect_unstable_modules() { fn respect_unstable_modules() {
check_found_path_prefer_no_std( check_found_path_prefer_no_std_allow_unstable(
r#" r#"
//- /main.rs crate:main deps:std,core //- /main.rs crate:main deps:std,core
extern crate std; extern crate std;

View file

@ -114,6 +114,9 @@ pub struct ImportPathConfig {
pub prefer_prelude: bool, pub prefer_prelude: bool,
/// If true, prefer abs path (starting with `::`) where it is available. /// If true, prefer abs path (starting with `::`) where it is available.
pub prefer_absolute: bool, pub prefer_absolute: bool,
/// If true, paths containing `#[unstable]` segments may be returned, but only if if there is no
/// stable path. This does not check, whether the item itself that is being imported is `#[unstable]`.
pub allow_unstable: bool,
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1159,6 +1159,7 @@ impl HirDisplay for Ty {
prefer_no_std: false, prefer_no_std: false,
prefer_prelude: true, prefer_prelude: true,
prefer_absolute: false, prefer_absolute: false,
allow_unstable: true,
}, },
) { ) {
write!(f, "{}", path.display(f.db.upcast(), f.edition()))?; write!(f, "{}", path.display(f.db.upcast(), f.edition()))?;

View file

@ -28,6 +28,7 @@ impl AssistConfig {
prefer_no_std: self.prefer_no_std, prefer_no_std: self.prefer_no_std,
prefer_prelude: self.prefer_prelude, prefer_prelude: self.prefer_prelude,
prefer_absolute: self.prefer_absolute, prefer_absolute: self.prefer_absolute,
allow_unstable: true,
} }
} }
} }

View file

@ -660,7 +660,7 @@ fn enum_variants_with_paths(
if let Some(path) = ctx.module.find_path( if let Some(path) = ctx.module.find_path(
ctx.db, ctx.db,
hir::ModuleDef::from(variant), hir::ModuleDef::from(variant),
ctx.config.import_path_config(), ctx.config.import_path_config(ctx.is_nightly),
) { ) {
// Variants with trivial paths are already added by the existing completion logic, // Variants with trivial paths are already added by the existing completion logic,
// so we should avoid adding these twice // so we should avoid adding these twice

View file

@ -247,7 +247,7 @@ pub(crate) fn complete_expr_path(
.find_path( .find_path(
ctx.db, ctx.db,
hir::ModuleDef::from(strukt), hir::ModuleDef::from(strukt),
ctx.config.import_path_config(), ctx.config.import_path_config(ctx.is_nightly),
) )
.filter(|it| it.len() > 1); .filter(|it| it.len() > 1);
@ -269,7 +269,7 @@ pub(crate) fn complete_expr_path(
.find_path( .find_path(
ctx.db, ctx.db,
hir::ModuleDef::from(un), hir::ModuleDef::from(un),
ctx.config.import_path_config(), ctx.config.import_path_config(ctx.is_nightly),
) )
.filter(|it| it.len() > 1); .filter(|it| it.len() > 1);

View file

@ -257,7 +257,7 @@ fn import_on_the_fly(
}; };
let user_input_lowercased = potential_import_name.to_lowercase(); let user_input_lowercased = potential_import_name.to_lowercase();
let import_cfg = ctx.config.import_path_config(); let import_cfg = ctx.config.import_path_config(ctx.is_nightly);
import_assets import_assets
.search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind) .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind)
@ -316,7 +316,7 @@ fn import_on_the_fly_pat_(
ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)), ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)),
}; };
let user_input_lowercased = potential_import_name.to_lowercase(); let user_input_lowercased = potential_import_name.to_lowercase();
let cfg = ctx.config.import_path_config(); let cfg = ctx.config.import_path_config(ctx.is_nightly);
import_assets import_assets
.search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)
@ -358,7 +358,7 @@ fn import_on_the_fly_method(
let user_input_lowercased = potential_import_name.to_lowercase(); let user_input_lowercased = potential_import_name.to_lowercase();
let cfg = ctx.config.import_path_config(); let cfg = ctx.config.import_path_config(ctx.is_nightly);
import_assets import_assets
.search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)

View file

@ -60,7 +60,7 @@ pub(crate) fn complete_postfix(
None => return, None => return,
}; };
let cfg = ctx.config.import_path_config(); let cfg = ctx.config.import_path_config(ctx.is_nightly);
if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) {

View file

@ -59,11 +59,12 @@ impl CompletionConfig<'_> {
.flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip))) .flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip)))
} }
pub fn import_path_config(&self) -> ImportPathConfig { pub fn import_path_config(&self, allow_unstable: bool) -> ImportPathConfig {
ImportPathConfig { ImportPathConfig {
prefer_no_std: self.prefer_no_std, prefer_no_std: self.prefer_no_std,
prefer_prelude: self.prefer_prelude, prefer_prelude: self.prefer_prelude,
prefer_absolute: self.prefer_absolute, prefer_absolute: self.prefer_absolute,
allow_unstable,
} }
} }
} }

View file

@ -443,7 +443,9 @@ pub(crate) struct CompletionContext<'a> {
/// The module of the `scope`. /// The module of the `scope`.
pub(crate) module: hir::Module, pub(crate) module: hir::Module,
/// Whether nightly toolchain is used. Cached since this is looked up a lot. /// Whether nightly toolchain is used. Cached since this is looked up a lot.
is_nightly: bool, pub(crate) is_nightly: bool,
/// The edition of the current crate
// FIXME: This should probably be the crate of the current token?
pub(crate) edition: Edition, pub(crate) edition: Edition,
/// The expected name of what we are completing. /// The expected name of what we are completing.

View file

@ -289,7 +289,7 @@ pub fn resolve_completion_edits(
let new_ast = scope.clone_for_update(); let new_ast = scope.clone_for_update();
let mut import_insert = TextEdit::builder(); let mut import_insert = TextEdit::builder();
let cfg = config.import_path_config(); let cfg = config.import_path_config(true);
imports.into_iter().for_each(|(full_import_path, imported_name)| { imports.into_iter().for_each(|(full_import_path, imported_name)| {
let items_with_name = items_locator::items_with_name( let items_with_name = items_locator::items_with_name(

View file

@ -301,7 +301,7 @@ pub(crate) fn render_expr(
.unwrap_or_else(|| String::from("...")) .unwrap_or_else(|| String::from("..."))
}; };
let cfg = ctx.config.import_path_config(); let cfg = ctx.config.import_path_config(ctx.is_nightly);
let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.edition).ok()?; let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.edition).ok()?;

View file

@ -164,7 +164,7 @@ impl Snippet {
} }
fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option<Vec<LocatedImport>> { fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option<Vec<LocatedImport>> {
let import_cfg = ctx.config.import_path_config(); let import_cfg = ctx.config.import_path_config(ctx.is_nightly);
let resolve = |import| { let resolve = |import| {
let item = ctx.scope.resolve_mod_path(import).next()?; let item = ctx.scope.resolve_mod_path(import).next()?;

View file

@ -1390,6 +1390,41 @@ pub struct FooStruct {}
); );
} }
#[test]
fn flyimport_pattern_unstable_path() {
check(
r#"
//- /main.rs crate:main deps:std
fn function() {
let foo$0
}
//- /std.rs crate:std
#[unstable]
pub mod unstable {
pub struct FooStruct {}
}
"#,
expect![""],
);
check(
r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
fn function() {
let foo$0
}
//- /std.rs crate:std
#[unstable]
pub mod unstable {
pub struct FooStruct {}
}
"#,
expect![[r#"
st FooStruct (use std::unstable::FooStruct)
"#]],
);
}
#[test] #[test]
fn flyimport_pattern_unstable_item_on_nightly() { fn flyimport_pattern_unstable_item_on_nightly() {
check( check(

View file

@ -319,6 +319,7 @@ impl Ctx<'_> {
prefer_no_std: false, prefer_no_std: false,
prefer_prelude: true, prefer_prelude: true,
prefer_absolute: false, prefer_absolute: false,
allow_unstable: true,
}; };
let found_path = self.target_module.find_path( let found_path = self.target_module.find_path(
self.source_scope.db.upcast(), self.source_scope.db.upcast(),
@ -378,6 +379,7 @@ impl Ctx<'_> {
prefer_no_std: false, prefer_no_std: false,
prefer_prelude: true, prefer_prelude: true,
prefer_absolute: false, prefer_absolute: false,
allow_unstable: true,
}; };
let found_path = let found_path =
self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?; self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?;
@ -417,6 +419,7 @@ impl Ctx<'_> {
prefer_no_std: false, prefer_no_std: false,
prefer_prelude: true, prefer_prelude: true,
prefer_absolute: false, prefer_absolute: false,
allow_unstable: true,
}; };
let found_path = self.target_module.find_path( let found_path = self.target_module.find_path(
self.source_scope.db.upcast(), self.source_scope.db.upcast(),

View file

@ -147,6 +147,7 @@ pub(crate) fn json_in_items(
prefer_no_std: config.prefer_no_std, prefer_no_std: config.prefer_no_std,
prefer_prelude: config.prefer_prelude, prefer_prelude: config.prefer_prelude,
prefer_absolute: config.prefer_absolute, prefer_absolute: config.prefer_absolute,
allow_unstable: true,
}; };
if !scope_has("Serialize") { if !scope_has("Serialize") {

View file

@ -128,6 +128,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
prefer_no_std: ctx.config.prefer_no_std, prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude, prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute, prefer_absolute: ctx.config.prefer_absolute,
allow_unstable: ctx.is_nightly,
}, },
)?; )?;

View file

@ -70,6 +70,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
prefer_no_std: ctx.config.prefer_no_std, prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude, prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute, prefer_absolute: ctx.config.prefer_absolute,
allow_unstable: ctx.is_nightly,
}, },
ctx.edition, ctx.edition,
) )

View file

@ -83,7 +83,7 @@ use either::Either;
use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics}; use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics};
use ide_db::{ use ide_db::{
assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
base_db::SourceDatabase, base_db::{ReleaseChannel, SourceDatabase},
generated::lints::{Lint, LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINTS, DEFAULT_LINT_GROUPS}, generated::lints::{Lint, LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINTS, DEFAULT_LINT_GROUPS},
imports::insert_use::InsertUseConfig, imports::insert_use::InsertUseConfig,
label::Label, label::Label,
@ -276,6 +276,7 @@ struct DiagnosticsContext<'a> {
sema: Semantics<'a, RootDatabase>, sema: Semantics<'a, RootDatabase>,
resolve: &'a AssistResolveStrategy, resolve: &'a AssistResolveStrategy,
edition: Edition, edition: Edition,
is_nightly: bool,
} }
impl DiagnosticsContext<'_> { impl DiagnosticsContext<'_> {
@ -368,7 +369,11 @@ pub fn semantic_diagnostics(
let module = sema.file_to_module_def(file_id); let module = sema.file_to_module_def(file_id);
let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition() }; let is_nightly = matches!(
module.and_then(|m| db.toolchain_channel(m.krate().into())),
Some(ReleaseChannel::Nightly) | None
);
let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition(), is_nightly };
let mut diags = Vec::new(); let mut diags = Vec::new();
match module { match module {

View file

@ -673,6 +673,7 @@ impl Match {
prefer_no_std: false, prefer_no_std: false,
prefer_prelude: true, prefer_prelude: true,
prefer_absolute: false, prefer_absolute: false,
allow_unstable: true,
}; };
let mod_path = module.find_path(sema.db, module_def, cfg).ok_or_else(|| { let mod_path = module.find_path(sema.db, module_def, cfg).ok_or_else(|| {
match_error!("Failed to render template path `{}` at match location") match_error!("Failed to render template path `{}` at match location")

View file

@ -465,6 +465,7 @@ impl flags::AnalysisStats {
prefer_no_std: false, prefer_no_std: false,
prefer_prelude: true, prefer_prelude: true,
prefer_absolute: false, prefer_absolute: false,
allow_unstable: true,
}, },
Edition::LATEST, Edition::LATEST,
) )