From b31f53e0d6c7b3730fde89a00336b2d9603c5275 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Jan 2025 12:18:54 +0100 Subject: [PATCH] Fix flyimport not filtering via stability of import path --- crates/hir-def/src/find_path.rs | 25 +++++++++---- crates/hir-def/src/lib.rs | 3 ++ crates/hir-ty/src/display.rs | 1 + crates/ide-assists/src/assist_config.rs | 1 + crates/ide-completion/src/completions.rs | 2 +- crates/ide-completion/src/completions/expr.rs | 4 +-- .../src/completions/flyimport.rs | 6 ++-- .../ide-completion/src/completions/postfix.rs | 2 +- crates/ide-completion/src/config.rs | 3 +- crates/ide-completion/src/context.rs | 4 ++- crates/ide-completion/src/lib.rs | 2 +- crates/ide-completion/src/render.rs | 2 +- crates/ide-completion/src/snippet.rs | 2 +- crates/ide-completion/src/tests/flyimport.rs | 35 +++++++++++++++++++ crates/ide-db/src/path_transform.rs | 3 ++ .../src/handlers/json_is_not_rust.rs | 1 + .../src/handlers/missing_fields.rs | 1 + .../src/handlers/typed_hole.rs | 1 + crates/ide-diagnostics/src/lib.rs | 9 +++-- crates/ide-ssr/src/matching.rs | 1 + .../rust-analyzer/src/cli/analysis_stats.rs | 1 + 21 files changed, 89 insertions(+), 20 deletions(-) diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index 5d67902c8a..c30ad0163b 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -445,6 +445,10 @@ fn find_in_dep( }; cov_mark::hit!(partially_imported); 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; } @@ -670,6 +674,7 @@ mod tests { prefer_prelude: bool, prefer_absolute: bool, prefer_no_std: bool, + allow_unstable: bool, expect: Expect, ) { let (db, pos) = TestDB::with_position(ra_fixture); @@ -711,7 +716,7 @@ mod tests { module, prefix, ignore_local_imports, - ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute }, + ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable }, ); format_to!( res, @@ -732,7 +737,7 @@ mod tests { path: &str, 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( @@ -740,7 +745,7 @@ mod tests { path: &str, 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( @@ -748,7 +753,7 @@ mod tests { path: &str, 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( @@ -756,7 +761,15 @@ mod tests { path: &str, 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] @@ -1951,7 +1964,7 @@ pub mod ops { #[test] fn respect_unstable_modules() { - check_found_path_prefer_no_std( + check_found_path_prefer_no_std_allow_unstable( r#" //- /main.rs crate:main deps:std,core extern crate std; diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index da9ffae8aa..c78818c642 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -114,6 +114,9 @@ pub struct ImportPathConfig { pub prefer_prelude: bool, /// If true, prefer abs path (starting with `::`) where it is available. 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)] diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 3545bf7677..d960aaf99f 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1159,6 +1159,7 @@ impl HirDisplay for Ty { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }, ) { write!(f, "{}", path.display(f.db.upcast(), f.edition()))?; diff --git a/crates/ide-assists/src/assist_config.rs b/crates/ide-assists/src/assist_config.rs index 82d8db4258..fb533077d9 100644 --- a/crates/ide-assists/src/assist_config.rs +++ b/crates/ide-assists/src/assist_config.rs @@ -28,6 +28,7 @@ impl AssistConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, + allow_unstable: true, } } } diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 40669c65c5..88f893e42a 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -660,7 +660,7 @@ fn enum_variants_with_paths( if let Some(path) = ctx.module.find_path( ctx.db, 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, // so we should avoid adding these twice diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index db18b531d7..e710175170 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -247,7 +247,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(strukt), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); @@ -269,7 +269,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(un), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 435b88de4a..24243f57b4 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -257,7 +257,7 @@ fn import_on_the_fly( }; 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 .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(_)), }; 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 .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 cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 67ea05e002..2c39a8fdfe 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -60,7 +60,7 @@ pub(crate) fn complete_postfix( 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 receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs index c641df38ff..45aab38e8e 100644 --- a/crates/ide-completion/src/config.rs +++ b/crates/ide-completion/src/config.rs @@ -59,11 +59,12 @@ impl CompletionConfig<'_> { .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 { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, + allow_unstable, } } } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 3a2a4a23a1..366e79cddf 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -443,7 +443,9 @@ pub(crate) struct CompletionContext<'a> { /// The module of the `scope`. pub(crate) module: hir::Module, /// 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, /// The expected name of what we are completing. diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 56d7eeaf8e..ac6b1207f2 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -289,7 +289,7 @@ pub fn resolve_completion_edits( let new_ast = scope.clone_for_update(); 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)| { let items_with_name = items_locator::items_with_name( diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 1b7adf1adb..dc7eacbfba 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -301,7 +301,7 @@ pub(crate) fn render_expr( .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()?; diff --git a/crates/ide-completion/src/snippet.rs b/crates/ide-completion/src/snippet.rs index 04bb178c65..866b83a614 100644 --- a/crates/ide-completion/src/snippet.rs +++ b/crates/ide-completion/src/snippet.rs @@ -164,7 +164,7 @@ impl Snippet { } fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { - let import_cfg = ctx.config.import_path_config(); + let import_cfg = ctx.config.import_path_config(ctx.is_nightly); let resolve = |import| { let item = ctx.scope.resolve_mod_path(import).next()?; diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index d491e438fe..2e7c53def7 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -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] fn flyimport_pattern_unstable_item_on_nightly() { check( diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index a045c22c2d..f045e44dd3 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -319,6 +319,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path( self.source_scope.db.upcast(), @@ -378,6 +379,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?; @@ -417,6 +419,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path( self.source_scope.db.upcast(), diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index dca889d1a8..f22041ebe2 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -147,6 +147,7 @@ pub(crate) fn json_in_items( prefer_no_std: config.prefer_no_std, prefer_prelude: config.prefer_prelude, prefer_absolute: config.prefer_absolute, + allow_unstable: true, }; if !scope_has("Serialize") { diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index fd1044e51b..938b7182bc 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -128,6 +128,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option, d: &hir::TypedHole) -> Option prefer_no_std: ctx.config.prefer_no_std, prefer_prelude: ctx.config.prefer_prelude, prefer_absolute: ctx.config.prefer_absolute, + allow_unstable: ctx.is_nightly, }, ctx.edition, ) diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 1e99d7ad6e..50c91a6960 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -83,7 +83,7 @@ use either::Either; use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, - base_db::SourceDatabase, + base_db::{ReleaseChannel, SourceDatabase}, generated::lints::{Lint, LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINTS, DEFAULT_LINT_GROUPS}, imports::insert_use::InsertUseConfig, label::Label, @@ -276,6 +276,7 @@ struct DiagnosticsContext<'a> { sema: Semantics<'a, RootDatabase>, resolve: &'a AssistResolveStrategy, edition: Edition, + is_nightly: bool, } impl DiagnosticsContext<'_> { @@ -368,7 +369,11 @@ pub fn semantic_diagnostics( 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(); match module { diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs index 4edc3633fb..4bead14e31 100644 --- a/crates/ide-ssr/src/matching.rs +++ b/crates/ide-ssr/src/matching.rs @@ -673,6 +673,7 @@ impl Match { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let mod_path = module.find_path(sema.db, module_def, cfg).ok_or_else(|| { match_error!("Failed to render template path `{}` at match location") diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index bcaec52019..18c27c8449 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -465,6 +465,7 @@ impl flags::AnalysisStats { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }, Edition::LATEST, )