Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #115048 - lnicola:sync-from-ra, r=lnicola
:arrow_up: `rust-analyzer`
bors 2023-08-21
parent 084baa5 · parent c9a98f9 · commit f0e632d
-rw-r--r--Cargo.lock12
-rw-r--r--Cargo.toml5
-rw-r--r--crates/base-db/src/fixture.rs4
-rw-r--r--crates/base-db/src/input.rs6
-rw-r--r--crates/hir-def/src/attr.rs10
-rw-r--r--crates/hir-def/src/body/lower.rs7
-rw-r--r--crates/hir-def/src/body/tests/block.rs20
-rw-r--r--crates/hir-def/src/data.rs2
-rw-r--r--crates/hir-def/src/db.rs3
-rw-r--r--crates/hir-def/src/find_path.rs81
-rw-r--r--crates/hir-def/src/generics.rs85
-rw-r--r--crates/hir-def/src/hir/type_ref.rs11
-rw-r--r--crates/hir-def/src/import_map.rs42
-rw-r--r--crates/hir-def/src/item_scope.rs477
-rw-r--r--crates/hir-def/src/item_tree.rs51
-rw-r--r--crates/hir-def/src/item_tree/lower.rs21
-rw-r--r--crates/hir-def/src/item_tree/pretty.rs27
-rw-r--r--crates/hir-def/src/item_tree/tests.rs12
-rw-r--r--crates/hir-def/src/lib.rs44
-rw-r--r--crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs2
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe/regression.rs65
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs2
-rw-r--r--crates/hir-def/src/nameres.rs72
-rw-r--r--crates/hir-def/src/nameres/collector.rs434
-rw-r--r--crates/hir-def/src/nameres/path_resolution.rs85
-rw-r--r--crates/hir-def/src/nameres/tests.rs80
-rw-r--r--crates/hir-def/src/nameres/tests/globs.rs18
-rw-r--r--crates/hir-def/src/nameres/tests/incremental.rs2
-rw-r--r--crates/hir-def/src/nameres/tests/macros.rs52
-rw-r--r--crates/hir-def/src/nameres/tests/mod_resolution.rs32
-rw-r--r--crates/hir-def/src/nameres/tests/primitives.rs4
-rw-r--r--crates/hir-def/src/per_ns.rs82
-rw-r--r--crates/hir-def/src/resolver.rs189
-rw-r--r--crates/hir-def/src/src.rs19
-rw-r--r--crates/hir-expand/src/attrs.rs9
-rw-r--r--crates/hir-expand/src/lib.rs19
-rw-r--r--crates/hir-ty/src/builder.rs13
-rw-r--r--crates/hir-ty/src/consteval.rs4
-rw-r--r--crates/hir-ty/src/consteval/tests.rs53
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs2
-rw-r--r--crates/hir-ty/src/display.rs42
-rw-r--r--crates/hir-ty/src/infer.rs8
-rw-r--r--crates/hir-ty/src/infer/closure.rs2
-rw-r--r--crates/hir-ty/src/infer/expr.rs21
-rw-r--r--crates/hir-ty/src/infer/path.rs4
-rw-r--r--crates/hir-ty/src/infer/unify.rs48
-rw-r--r--crates/hir-ty/src/lib.rs17
-rw-r--r--crates/hir-ty/src/lower.rs121
-rw-r--r--crates/hir-ty/src/mir/eval.rs31
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs102
-rw-r--r--crates/hir-ty/src/mir/eval/tests.rs42
-rw-r--r--crates/hir-ty/src/mir/lower.rs52
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs2
-rw-r--r--crates/hir/src/attrs.rs183
-rw-r--r--crates/hir/src/display.rs68
-rw-r--r--crates/hir/src/lib.rs68
-rw-r--r--crates/hir/src/semantics.rs91
-rw-r--r--crates/hir/src/source_analyzer.rs9
-rw-r--r--crates/hir/src/symbols.rs40
-rw-r--r--crates/ide-assists/src/handlers/add_missing_impl_members.rs105
-rw-r--r--crates/ide-assists/src/handlers/apply_demorgan.rs190
-rw-r--r--crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs4
-rw-r--r--crates/ide-assists/src/handlers/convert_to_guarded_return.rs21
-rw-r--r--crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs2
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs123
-rw-r--r--crates/ide-assists/src/handlers/generate_delegate_methods.rs9
-rw-r--r--crates/ide-assists/src/handlers/generate_derive.rs37
-rw-r--r--crates/ide-assists/src/handlers/remove_dbg.rs5
-rw-r--r--crates/ide-assists/src/handlers/remove_unused_imports.rs2
-rw-r--r--crates/ide-completion/src/completions.rs6
-rw-r--r--crates/ide-completion/src/completions/extern_crate.rs71
-rw-r--r--crates/ide-completion/src/completions/type.rs87
-rw-r--r--crates/ide-completion/src/context.rs53
-rw-r--r--crates/ide-completion/src/context/analysis.rs145
-rw-r--r--crates/ide-completion/src/tests/flyimport.rs54
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs294
-rw-r--r--crates/ide-db/src/defs.rs12
-rw-r--r--crates/ide-db/src/helpers.rs2
-rw-r--r--crates/ide-db/src/imports/import_assets.rs4
-rw-r--r--crates/ide-db/src/lib.rs3
-rw-r--r--crates/ide-db/src/path_transform.rs44
-rw-r--r--crates/ide-db/src/search.rs4
-rw-r--r--crates/ide-db/src/symbol_index.rs11
-rw-r--r--crates/ide-db/src/test_data/test_symbol_index_collection.txt203
-rw-r--r--crates/ide-db/src/use_trivial_constructor.rs16
-rw-r--r--crates/ide-ssr/src/matching.rs6
-rw-r--r--crates/ide/src/call_hierarchy.rs10
-rw-r--r--crates/ide/src/doc_links.rs10
-rw-r--r--crates/ide/src/doc_links/tests.rs56
-rw-r--r--crates/ide/src/expand_macro.rs47
-rw-r--r--crates/ide/src/extend_selection.rs29
-rw-r--r--crates/ide/src/goto_declaration.rs8
-rw-r--r--crates/ide/src/goto_definition.rs54
-rw-r--r--crates/ide/src/goto_implementation.rs15
-rw-r--r--crates/ide/src/goto_type_definition.rs8
-rw-r--r--crates/ide/src/highlight_related.rs17
-rw-r--r--crates/ide/src/hover.rs4
-rw-r--r--crates/ide/src/hover/tests.rs107
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/ide/src/moniker.rs2
-rw-r--r--crates/ide/src/navigation_target.rs8
-rw-r--r--crates/ide/src/references.rs2
-rw-r--r--crates/ide/src/signature_help.rs32
-rw-r--r--crates/ide/src/syntax_highlighting.rs4
-rw-r--r--crates/mbe/src/syntax_bridge.rs1
-rw-r--r--crates/parser/src/grammar.rs4
-rw-r--r--crates/parser/src/grammar/generic_params.rs2
-rw-r--r--crates/parser/src/shortcuts.rs13
-rw-r--r--crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast6
-rw-r--r--crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast15
-rw-r--r--crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast17
-rw-r--r--crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast9
-rw-r--r--crates/profile/src/stop_watch.rs20
-rw-r--r--crates/project-model/src/workspace.rs3
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs8
-rw-r--r--crates/rust-analyzer/src/cli/flags.rs3
-rw-r--r--crates/rust-analyzer/src/config.rs13
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs2
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs7
-rw-r--r--crates/syntax/rust.ungram2
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs2
-rw-r--r--crates/syntax/src/ast/make.rs7
-rw-r--r--crates/syntax/src/ast/node_ext.rs8
-rw-r--r--crates/syntax/src/lib.rs12
-rw-r--r--docs/user/generated_config.adoc15
-rw-r--r--editors/code/package.json18
-rw-r--r--editors/code/src/bootstrap.ts2
-rw-r--r--editors/code/src/commands.ts7
-rw-r--r--editors/code/src/config.ts33
-rw-r--r--editors/code/src/ctx.ts18
-rw-r--r--editors/code/src/main.ts1
-rw-r--r--editors/code/src/util.ts9
-rw-r--r--lib/lsp-server/Cargo.toml5
-rw-r--r--lib/lsp-server/src/stdio.rs5
-rw-r--r--xtask/src/metrics.rs4
136 files changed, 3862 insertions, 1448 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a2b263cf2d..49e96236c5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -999,23 +999,23 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "lsp-server"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3711e4d6f491dc9edc0f1df80e204f38206775ac92c1241e89b79229a850bc00"
+version = "0.7.3"
dependencies = [
"crossbeam-channel",
"log",
+ "lsp-types",
"serde",
"serde_json",
]
[[package]]
name = "lsp-server"
-version = "0.7.2"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72417faa455bfb4e5bf14b157d8e2ca2ed74b4e89b8cf42ea2d864825ae5c8a2"
dependencies = [
"crossbeam-channel",
"log",
- "lsp-types",
"serde",
"serde_json",
]
@@ -1555,7 +1555,7 @@ dependencies = [
"ide-ssr",
"itertools",
"load-cargo",
- "lsp-server 0.7.1",
+ "lsp-server 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lsp-types",
"mbe",
"mimalloc",
diff --git a/Cargo.toml b/Cargo.toml
index f6a50bfa6b..5eb59d6db1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -86,7 +86,7 @@ proc-macro-test = { path = "./crates/proc-macro-test" }
# In-tree crates that are published separately and follow semver. See lib/README.md
line-index = { version = "0.1.0-pre.1" }
la-arena = { version = "0.3.1" }
-lsp-server = { version = "0.7.1" }
+lsp-server = { version = "0.7.3" }
# non-local crates
smallvec = { version = "1.10.0", features = [
@@ -97,7 +97,8 @@ smallvec = { version = "1.10.0", features = [
smol_str = "0.2.0"
nohash-hasher = "0.2.0"
text-size = "1.1.0"
-serde = { version = "1.0.156", features = ["derive"] }
+# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde
+serde = { version = "1.0.156, < 1.0.172", features = ["derive"] }
serde_json = "1.0.96"
triomphe = { version = "0.1.8", default-features = false, features = ["std"] }
# can't upgrade due to dashmap depending on 0.12.3 currently
diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs
index 323ee4260e..aaac0fc379 100644
--- a/crates/base-db/src/fixture.rs
+++ b/crates/base-db/src/fixture.rs
@@ -130,6 +130,7 @@ impl ChangeFixture {
let mut default_crate_root: Option<FileId> = None;
let mut default_target_data_layout: Option<String> = None;
let mut default_cfg = CfgOptions::default();
+ let mut default_env = Env::new_for_test_fixture();
let mut file_set = FileSet::default();
let mut current_source_root_kind = SourceRootKind::Local;
@@ -200,6 +201,7 @@ impl ChangeFixture {
assert!(default_crate_root.is_none());
default_crate_root = Some(file_id);
default_cfg = meta.cfg;
+ default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
default_target_data_layout = meta.target_data_layout;
}
@@ -220,7 +222,7 @@ impl ChangeFixture {
None,
default_cfg,
Default::default(),
- Env::new_for_test_fixture(),
+ default_env,
false,
CrateOrigin::Local { repo: None, name: None },
default_target_data_layout
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index c47799f132..b75c7079be 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -686,6 +686,12 @@ impl fmt::Display for Edition {
}
}
+impl Extend<(String, String)> for Env {
+ fn extend<T: IntoIterator<Item = (String, String)>>(&mut self, iter: T) {
+ self.entries.extend(iter);
+ }
+}
+
impl FromIterator<(String, String)> for Env {
fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
Env { entries: FromIterator::from_iter(iter) }
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index fae0711180..a5db75a91e 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -431,12 +431,10 @@ impl AttrsWithOwner {
.item_tree(db)
.raw_attrs(AttrOwner::ModItem(definition_tree_id.value.into()))
.clone(),
- ModuleOrigin::BlockExpr { block } => RawAttrs::from_attrs_owner(
- db.upcast(),
- InFile::new(block.file_id, block.to_node(db.upcast()))
- .as_ref()
- .map(|it| it as &dyn ast::HasAttrs),
- ),
+ ModuleOrigin::BlockExpr { id, .. } => {
+ let tree = db.block_item_tree_query(id);
+ tree.raw_attrs(AttrOwner::TopLevel).clone()
+ }
}
}
AttrDefId::FieldId(it) => {
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 3853a6ab3a..7071fcb939 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -505,6 +505,9 @@ impl ExprCollector<'_> {
let mut args = Vec::new();
let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() {
+ let num_params = pl.params().count();
+ args.reserve_exact(num_params);
+ arg_types.reserve_exact(num_params);
for param in pl.params() {
let pat = this.collect_pat_top(param.pat());
let type_ref =
@@ -1100,7 +1103,9 @@ impl ExprCollector<'_> {
ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
_ => false,
});
- statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
+ statement_has_item
+ || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
+ || (block.may_carry_attributes() && block.attrs().next().is_some())
};
let block_id = if block_has_items {
diff --git a/crates/hir-def/src/body/tests/block.rs b/crates/hir-def/src/body/tests/block.rs
index 4e015a7fbb..44eeed9e3f 100644
--- a/crates/hir-def/src/body/tests/block.rs
+++ b/crates/hir-def/src/body/tests/block.rs
@@ -38,9 +38,9 @@ fn outer() {
"#,
expect![[r#"
block scope
- CrateStruct: t
- PlainStruct: t v
- SelfStruct: t
+ CrateStruct: ti
+ PlainStruct: ti vi
+ SelfStruct: ti
Struct: v
SuperStruct: _
@@ -66,7 +66,7 @@ fn outer() {
"#,
expect![[r#"
block scope
- imported: t v
+ imported: ti vi
name: v
crate
@@ -92,9 +92,9 @@ fn outer() {
"#,
expect![[r#"
block scope
- inner1: t
+ inner1: ti
inner2: v
- outer: v
+ outer: vi
block scope
inner: v
@@ -121,7 +121,7 @@ struct Struct {}
"#,
expect![[r#"
block scope
- Struct: t
+ Struct: ti
crate
Struct: t
@@ -153,7 +153,7 @@ fn outer() {
"#,
expect![[r#"
block scope
- ResolveMe: t
+ ResolveMe: ti
block scope
m2: t
@@ -214,7 +214,7 @@ fn f() {
"#,
expect![[r#"
block scope
- ResolveMe: t
+ ResolveMe: ti
block scope
h: v
@@ -292,7 +292,7 @@ pub mod cov_mark {
nested: v
crate
- cov_mark: t
+ cov_mark: ti
f: v
"#]],
);
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 91db68058b..68defa3858 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -487,7 +487,7 @@ impl ExternCrateDeclData {
db.crate_def_map(loc.container.krate())
.extern_prelude()
.find(|&(prelude_name, ..)| *prelude_name == name)
- .map(|(_, root)| root.krate())
+ .map(|(_, (root, _))| root.krate())
};
Arc::new(Self {
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index e34a6768f2..31c1a71303 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -82,6 +82,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(ItemTree::file_item_tree_query)]
fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
+ #[salsa::invoke(ItemTree::block_item_tree_query)]
+ fn block_item_tree_query(&self, block_id: BlockId) -> Arc<ItemTree>;
+
#[salsa::invoke(crate_def_map_wait)]
#[salsa::transparent]
fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>;
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs
index df2af4c89b..b60e790910 100644
--- a/crates/hir-def/src/find_path.rs
+++ b/crates/hir-def/src/find_path.rs
@@ -11,7 +11,7 @@ use crate::{
nameres::DefMap,
path::{ModPath, PathKind},
visibility::Visibility,
- ModuleDefId, ModuleId,
+ CrateRootModuleId, ModuleDefId, ModuleId,
};
/// Find a path that can be used to refer to a certain item. This can depend on
@@ -81,7 +81,7 @@ fn find_path_inner(
}
let def_map = from.def_map(db);
- let crate_root = def_map.crate_root().into();
+ let crate_root = def_map.crate_root();
// - if the item is a module, jump straight to module search
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
let mut visited_modules = FxHashSet::default();
@@ -149,7 +149,7 @@ fn find_path_for_module(
db: &dyn DefDatabase,
def_map: &DefMap,
visited_modules: &mut FxHashSet<ModuleId>,
- crate_root: ModuleId,
+ crate_root: CrateRootModuleId,
from: ModuleId,
module_id: ModuleId,
max_len: usize,
@@ -183,7 +183,7 @@ fn find_path_for_module(
// - if the item is the crate root of a dependency crate, return the name from the extern prelude
let root_def_map = crate_root.def_map(db);
- for (name, def_id) in root_def_map.extern_prelude() {
+ for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude() {
if module_id == def_id {
let name = scope_name.unwrap_or_else(|| name.clone());
@@ -192,7 +192,7 @@ fn find_path_for_module(
def_map[local_id]
.scope
.type_(&name)
- .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id))
+ .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id.into()))
})
.is_some();
let kind = if name_already_occupied_in_type_ns {
@@ -224,6 +224,7 @@ fn find_path_for_module(
)
}
+// FIXME: Do we still need this now that we record import origins, and hence aliases?
fn find_in_scope(
db: &dyn DefDatabase,
def_map: &DefMap,
@@ -244,7 +245,7 @@ fn find_in_prelude(
item: ItemInNs,
from: ModuleId,
) -> Option<ModPath> {
- let prelude_module = root_def_map.prelude()?;
+ let (prelude_module, _) = root_def_map.prelude()?;
// Preludes in block DefMaps are ignored, only the crate DefMap is searched
let prelude_def_map = prelude_module.def_map(db);
let prelude_scope = &prelude_def_map[prelude_module.local_id].scope;
@@ -293,7 +294,7 @@ fn calculate_best_path(
db: &dyn DefDatabase,
def_map: &DefMap,
visited_modules: &mut FxHashSet<ModuleId>,
- crate_root: ModuleId,
+ crate_root: CrateRootModuleId,
max_len: usize,
item: ItemInNs,
from: ModuleId,
@@ -346,6 +347,11 @@ fn calculate_best_path(
let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
let import_map = db.import_map(dep.crate_id);
import_map.import_info_for(item).and_then(|info| {
+ if info.is_doc_hidden {
+ // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate
+ return None;
+ }
+
// Determine best path for containing module and append last segment from `info`.
// FIXME: we should guide this to look up the path locally, or from the same crate again?
let mut path = find_path_for_module(
@@ -1293,4 +1299,65 @@ pub mod prelude {
"None",
);
}
+
+ #[test]
+ fn different_crate_renamed_through_dep() {
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:intermediate
+$0
+//- /intermediate.rs crate:intermediate deps:std
+pub extern crate std as std_renamed;
+//- /std.rs crate:std
+pub struct S;
+ "#,
+ "intermediate::std_renamed::S",
+ "intermediate::std_renamed::S",
+ "intermediate::std_renamed::S",
+ "intermediate::std_renamed::S",
+ );
+ }
+
+ #[test]
+ fn different_crate_doc_hidden() {
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:intermediate
+$0
+//- /intermediate.rs crate:intermediate deps:std
+#[doc(hidden)]
+pub extern crate std;
+pub extern crate std as longer;
+//- /std.rs crate:std
+pub struct S;
+ "#,
+ "intermediate::longer::S",
+ "intermediate::longer::S",
+ "intermediate::longer::S",
+ "intermediate::longer::S",
+ );
+ }
+
+ #[test]
+ fn respect_doc_hidden() {
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:std,lazy_static
+$0
+//- /lazy_static.rs crate:lazy_static deps:core
+#[doc(hidden)]
+pub use core::ops::Deref as __Deref;
+//- /std.rs crate:std deps:core
+pub use core::ops;
+//- /core.rs crate:core
+pub mod ops {
+ pub trait Deref {}
+}
+ "#,
+ "std::ops::Deref",
+ "std::ops::Deref",
+ "std::ops::Deref",
+ "std::ops::Deref",
+ );
+ }
}
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index d7d44e4138..1e2535a8a9 100644
--- a/crates/hir-def/src/generics.rs
+++ b/crates/hir-def/src/generics.rs
@@ -21,10 +21,11 @@ use crate::{
db::DefDatabase,
dyn_map::{keys, DynMap},
expander::Expander,
+ item_tree::{AttrOwner, ItemTree},
lower::LowerCtx,
nameres::{DefMap, MacroSubNs},
src::{HasChildSource, HasSource},
- type_ref::{LifetimeRef, TypeBound, TypeRef},
+ type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
};
@@ -48,7 +49,7 @@ pub struct LifetimeParamData {
pub struct ConstParamData {
pub name: Name,
pub ty: Interned<TypeRef>,
- pub has_default: bool,
+ pub default: Option<ConstRef>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
@@ -75,7 +76,7 @@ impl TypeOrConstParamData {
pub fn has_default(&self) -> bool {
match self {
TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
- TypeOrConstParamData::ConstParamData(it) => it.has_default,
+ TypeOrConstParamData::ConstParamData(it) => it.default.is_some(),
}
}
@@ -154,12 +155,58 @@ impl GenericParams {
def: GenericDefId,
) -> Interned<GenericParams> {
let _p = profile::span("generic_params_query");
+
+ let krate = def.module(db).krate;
+ let cfg_options = db.crate_graph();
+ let cfg_options = &cfg_options[krate].cfg_options;
+
+ // Returns the generic parameters that are enabled under the current `#[cfg]` options
+ let enabled_params = |params: &Interned<GenericParams>, item_tree: &ItemTree| {
+ let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options);
+
+ // In the common case, no parameters will by disabled by `#[cfg]` attributes.
+ // Therefore, make a first pass to check if all parameters are enabled and, if so,
+ // clone the `Interned<GenericParams>` instead of recreating an identical copy.
+ let all_type_or_consts_enabled =
+ params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into()));
+ let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into()));
+
+ if all_type_or_consts_enabled && all_lifetimes_enabled {
+ params.clone()
+ } else {
+ Interned::new(GenericParams {
+ type_or_consts: all_type_or_consts_enabled
+ .then(|| params.type_or_consts.clone())
+ .unwrap_or_else(|| {
+ params
+ .type_or_consts
+ .iter()
+ .filter_map(|(idx, param)| {
+ enabled(idx.into()).then(|| param.clone())
+ })
+ .collect()
+ }),
+ lifetimes: all_lifetimes_enabled
+ .then(|| params.lifetimes.clone())
+ .unwrap_or_else(|| {
+ params
+ .lifetimes
+ .iter()
+ .filter_map(|(idx, param)| {
+ enabled(idx.into()).then(|| param.clone())
+ })
+ .collect()
+ }),
+ where_predicates: params.where_predicates.clone(),
+ })
+ }
+ };
macro_rules! id_to_generics {
($id:ident) => {{
let id = $id.lookup(db).id;
let tree = id.item_tree(db);
let item = &tree[id.value];
- item.generic_params.clone()
+ enabled_params(&item.generic_params, &tree)
}};
}
@@ -169,7 +216,8 @@ impl GenericParams {
let tree = loc.id.item_tree(db);
let item = &tree[loc.id.value];
- let mut generic_params = GenericParams::clone(&item.explicit_generic_params);
+ let enabled_params = enabled_params(&item.explicit_generic_params, &tree);
+ let mut generic_params = GenericParams::clone(&enabled_params);
let module = loc.container.module(db);
let func_data = db.function_data(id);
@@ -198,9 +246,14 @@ impl GenericParams {
}
}
- pub(crate) fn fill(&mut self, lower_ctx: &LowerCtx<'_>, node: &dyn HasGenericParams) {
+ pub(crate) fn fill(
+ &mut self,
+ lower_ctx: &LowerCtx<'_>,
+ node: &dyn HasGenericParams,
+ add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam),
+ ) {
if let Some(params) = node.generic_param_list() {
- self.fill_params(lower_ctx, params)
+ self.fill_params(lower_ctx, params, add_param_attrs)
}
if let Some(where_clause) = node.where_clause() {
self.fill_where_predicates(lower_ctx, where_clause);
@@ -218,7 +271,12 @@ impl GenericParams {
}
}
- fn fill_params(&mut self, lower_ctx: &LowerCtx<'_>, params: ast::GenericParamList) {
+ fn fill_params(
+ &mut self,
+ lower_ctx: &LowerCtx<'_>,
+ params: ast::GenericParamList,
+ mut add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam),
+ ) {
for type_or_const_param in params.type_or_const_params() {
match type_or_const_param {
ast::TypeOrConstParam::Type(type_param) => {
@@ -232,13 +290,14 @@ impl GenericParams {
default,
provenance: TypeParamProvenance::TypeParamList,
};
- self.type_or_consts.alloc(param.into());
+ let idx = self.type_or_consts.alloc(param.into());
let type_ref = TypeRef::Path(name.into());
self.fill_bounds(
lower_ctx,
type_param.type_bound_list(),
Either::Left(type_ref),
);
+ add_param_attrs(idx.into(), ast::GenericParam::TypeParam(type_param));
}
ast::TypeOrConstParam::Const(const_param) => {
let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
@@ -248,9 +307,10 @@ impl GenericParams {
let param = ConstParamData {
name,
ty: Interned::new(ty),
- has_default: const_param.default_val().is_some(),
+ default: ConstRef::from_const_param(lower_ctx, &const_param),
};
- self.type_or_consts.alloc(param.into());
+ let idx = self.type_or_consts.alloc(param.into());
+ add_param_attrs(idx.into(), ast::GenericParam::ConstParam(const_param));
}
}
}
@@ -258,13 +318,14 @@ impl GenericParams {
let name =
lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(&lt));
let param = LifetimeParamData { name: name.clone() };
- self.lifetimes.alloc(param);
+ let idx = self.lifetimes.alloc(param);
let lifetime_ref = LifetimeRef::new_name(name);
self.fill_bounds(
lower_ctx,
lifetime_param.type_bound_list(),
Either::Right(lifetime_ref),
);
+ add_param_attrs(idx.into(), ast::GenericParam::LifetimeParam(lifetime_param));
}
}
diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs
index 57f023ef35..75adf21abd 100644
--- a/crates/hir-def/src/hir/type_ref.rs
+++ b/crates/hir-def/src/hir/type_ref.rs
@@ -393,6 +393,17 @@ impl ConstRef {
Self::Scalar(LiteralConstRef::Unknown)
}
+ pub(crate) fn from_const_param(
+ lower_ctx: &LowerCtx<'_>,
+ param: &ast::ConstParam,
+ ) -> Option<Self> {
+ let default = param.default_val();
+ match default {
+ Some(_) => Some(Self::from_const_arg(lower_ctx, default)),
+ None => None,
+ }
+ }
+
pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
impl fmt::Display for Display<'_> {
diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs
index 4b2e5041a1..6038caaf8f 100644
--- a/crates/hir-def/src/import_map.rs
+++ b/crates/hir-def/src/import_map.rs
@@ -11,6 +11,7 @@ use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
use triomphe::Arc;
+use crate::item_scope::ImportOrExternCrate;
use crate::{
db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId,
ModuleDefId, ModuleId, TraitId,
@@ -29,6 +30,8 @@ pub struct ImportInfo {
pub container: ModuleId,
/// Whether the import is a trait associated item or not.
pub is_trait_assoc_item: bool,
+ /// Whether this item is annotated with `#[doc(hidden)]`.
+ pub is_doc_hidden: bool,
}
/// A map from publicly exported items to its name.
@@ -109,23 +112,41 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
});
for (name, per_ns) in visible_items {
- for item in per_ns.iter_items() {
+ for (item, import) in per_ns.iter_items() {
+ // FIXME: Not yet used, but will be once we handle doc(hidden) import sources
+ let attr_id = if let Some(import) = import {
+ match import {
+ ImportOrExternCrate::ExternCrate(id) => Some(id.into()),
+ ImportOrExternCrate::Import(id) => Some(id.import.into()),
+ }
+ } else {
+ match item {
+ ItemInNs::Types(id) | ItemInNs::Values(id) => id.try_into().ok(),
+ ItemInNs::Macros(id) => Some(id.into()),
+ }
+ };
+ let is_doc_hidden =
+ attr_id.map_or(false, |attr_id| db.attrs(attr_id).has_doc_hidden());
+
let import_info = ImportInfo {
name: name.clone(),
container: module,
is_trait_assoc_item: false,
+ is_doc_hidden,
};
match depth_map.entry(item) {
- Entry::Vacant(entry) => {
- entry.insert(depth);
- }
+ Entry::Vacant(entry) => _ = entry.insert((depth, is_doc_hidden)),
Entry::Occupied(mut entry) => {
- if depth < *entry.get() {
- entry.insert(depth);
- } else {
+ let &(occ_depth, occ_is_doc_hidden) = entry.get();
+ // Prefer the one that is not doc(hidden),
+ // Otherwise, if both have the same doc(hidden)-ness and the new path is shorter, prefer that one.
+ let overwrite_entry = occ_is_doc_hidden && !is_doc_hidden
+ || occ_is_doc_hidden == is_doc_hidden && depth < occ_depth;
+ if !overwrite_entry {
continue;
}
+ entry.insert((depth, is_doc_hidden));
}
}
@@ -162,10 +183,10 @@ fn collect_trait_assoc_items(
trait_import_info: &ImportInfo,
) {
let _p = profile::span("collect_trait_assoc_items");
- for (assoc_item_name, item) in &db.trait_data(tr).items {
+ for &(ref assoc_item_name, item) in &db.trait_data(tr).items {
let module_def_id = match item {
- AssocItemId::FunctionId(f) => ModuleDefId::from(*f),
- AssocItemId::ConstId(c) => ModuleDefId::from(*c),
+ AssocItemId::FunctionId(f) => ModuleDefId::from(f),
+ AssocItemId::ConstId(c) => ModuleDefId::from(c),
// cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
// qualifier, ergo no need to store it for imports in import_map
AssocItemId::TypeAliasId(_) => {
@@ -183,6 +204,7 @@ fn collect_trait_assoc_items(
container: trait_import_info.container,
name: assoc_item_name.clone(),
is_trait_assoc_item: true,
+ is_doc_hidden: db.attrs(item.into()).has_doc_hidden(),
};
map.insert(assoc_item, assoc_item_info);
}
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index 873accafb4..7c11fb9d13 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -6,6 +6,7 @@ use std::collections::hash_map::Entry;
use base_db::CrateId;
use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
use itertools::Itertools;
+use la_arena::Idx;
use once_cell::sync::Lazy;
use profile::Count;
use rustc_hash::{FxHashMap, FxHashSet};
@@ -15,16 +16,10 @@ use syntax::ast;
use crate::{
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId,
- ExternCrateId, HasModule, ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId,
- UseId,
+ ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId,
+ TraitId, UseId,
};
-#[derive(Copy, Clone, Debug)]
-pub(crate) enum ImportType {
- Glob,
- Named,
-}
-
#[derive(Debug, Default)]
pub struct PerNsGlobImports {
types: FxHashSet<(LocalModuleId, Name)>,
@@ -32,15 +27,50 @@ pub struct PerNsGlobImports {
macros: FxHashSet<(LocalModuleId, Name)>,
}
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ImportOrExternCrate {
+ Import(ImportId),
+ ExternCrate(ExternCrateId),
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub(crate) enum ImportType {
+ Import(ImportId),
+ Glob(UseId),
+ ExternCrate(ExternCrateId),
+}
+
+impl ImportOrExternCrate {
+ pub fn into_import(self) -> Option<ImportId> {
+ match self {
+ ImportOrExternCrate::Import(it) => Some(it),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ImportOrDef {
+ Import(ImportId),
+ ExternCrate(ExternCrateId),
+ Def(ModuleDefId),
+}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub struct ImportId {
+ pub import: UseId,
+ pub idx: Idx<ast::UseTree>,
+}
+
#[derive(Debug, Default, PartialEq, Eq)]
pub struct ItemScope {
_c: Count<Self>,
/// Defs visible in this scope. This includes `declarations`, but also
- /// imports.
- types: FxHashMap<Name, (ModuleDefId, Visibility)>,
- values: FxHashMap<Name, (ModuleDefId, Visibility)>,
- macros: FxHashMap<Name, (MacroId, Visibility)>,
+ /// imports. The imports belong to this module and can be resolved by using them on
+ /// the `use_imports_*` fields.
+ types: FxHashMap<Name, (ModuleDefId, Visibility, Option<ImportOrExternCrate>)>,
+ values: FxHashMap<Name, (ModuleDefId, Visibility, Option<ImportId>)>,
+ macros: FxHashMap<Name, (MacroId, Visibility, Option<ImportId>)>,
unresolved: FxHashSet<Name>,
/// The defs declared in this scope. Each def has a single scope where it is
@@ -50,7 +80,14 @@ pub struct ItemScope {
impls: Vec<ImplId>,
unnamed_consts: Vec<ConstId>,
/// Traits imported via `use Trait as _;`.
- unnamed_trait_imports: FxHashMap<TraitId, Visibility>,
+ unnamed_trait_imports: FxHashMap<TraitId, (Visibility, Option<ImportId>)>,
+
+ // the resolutions of the imports of this scope
+ use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>,
+ use_imports_values: FxHashMap<ImportId, ImportOrDef>,
+ use_imports_macros: FxHashMap<ImportId, ImportOrDef>,
+
+ use_decls: Vec<UseId>,
extern_crate_decls: Vec<ExternCrateId>,
/// Macros visible in current module in legacy textual scope
///
@@ -82,7 +119,7 @@ struct DeriveMacroInvocation {
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
BuiltinType::ALL
.iter()
- .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public)))
+ .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public, None)))
.collect()
});
@@ -105,11 +142,77 @@ impl ItemScope {
.chain(self.values.keys())
.chain(self.macros.keys())
.chain(self.unresolved.iter())
- .sorted()
.unique()
+ .sorted()
.map(move |name| (name, self.get(name)))
}
+ pub fn imports(&self) -> impl Iterator<Item = ImportId> + '_ {
+ self.use_imports_types
+ .keys()
+ .copied()
+ .filter_map(ImportOrExternCrate::into_import)
+ .chain(self.use_imports_values.keys().copied())
+ .chain(self.use_imports_macros.keys().copied())
+ .unique()
+ .sorted()
+ }
+
+ pub fn fully_resolve_import(&self, db: &dyn DefDatabase, mut import: ImportId) -> PerNs {
+ let mut res = PerNs::none();
+
+ let mut def_map;
+ let mut scope = self;
+ while let Some(&m) = scope.use_imports_macros.get(&import) {
+ match m {
+ ImportOrDef::Import(i) => {
+ let module_id = i.import.lookup(db).container;
+ def_map = module_id.def_map(db);
+ scope = &def_map[module_id.local_id].scope;
+ import = i;
+ }
+ ImportOrDef::Def(ModuleDefId::MacroId(def)) => {
+ res.macros = Some((def, Visibility::Public, None));
+ break;
+ }
+ _ => break,
+ }
+ }
+ let mut scope = self;
+ while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) {
+ match m {
+ ImportOrDef::Import(i) => {
+ let module_id = i.import.lookup(db).container;
+ def_map = module_id.def_map(db);
+ scope = &def_map[module_id.local_id].scope;
+ import = i;
+ }
+ ImportOrDef::Def(def) => {
+ res.types = Some((def, Visibility::Public, None));
+ break;
+ }
+ _ => break,
+ }
+ }
+ let mut scope = self;
+ while let Some(&m) = scope.use_imports_values.get(&import) {
+ match m {
+ ImportOrDef::Import(i) => {
+ let module_id = i.import.lookup(db).container;
+ def_map = module_id.def_map(db);
+ scope = &def_map[module_id.local_id].scope;
+ import = i;
+ }
+ ImportOrDef::Def(def) => {
+ res.values = Some((def, Visibility::Public, None));
+ break;
+ }
+ _ => break,
+ }
+ }
+ res
+ }
+
pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
self.declarations.iter().copied()
}
@@ -121,8 +224,7 @@ impl ItemScope {
}
pub fn use_decls(&self) -> impl Iterator<Item = UseId> + ExactSizeIterator + '_ {
- // FIXME: to be implemented
- std::iter::empty()
+ self.use_decls.iter().copied()
}
pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ {
@@ -132,13 +234,13 @@ impl ItemScope {
pub fn values(
&self,
) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ {
- self.values.values().copied()
+ self.values.values().copied().map(|(a, b, _)| (a, b))
}
- pub fn types(
+ pub(crate) fn types(
&self,
) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ {
- self.types.values().copied()
+ self.types.values().copied().map(|(def, vis, _)| (def, vis))
}
pub fn unnamed_consts(&self) -> impl Iterator<Item = ConstId> + '_ {
@@ -165,33 +267,55 @@ impl ItemScope {
}
pub(crate) fn type_(&self, name: &Name) -> Option<(ModuleDefId, Visibility)> {
- self.types.get(name).copied()
+ self.types.get(name).copied().map(|(a, b, _)| (a, b))
}
/// XXX: this is O(N) rather than O(1), try to not introduce new usages.
pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> {
- let (def, mut iter) = match item {
- ItemInNs::Macros(def) => {
- return self.macros.iter().find_map(|(name, &(other_def, vis))| {
- (other_def == def).then_some((name, vis))
- });
- }
- ItemInNs::Types(def) => (def, self.types.iter()),
- ItemInNs::Values(def) => (def, self.values.iter()),
- };
- iter.find_map(|(name, &(other_def, vis))| (other_def == def).then_some((name, vis)))
+ match item {
+ ItemInNs::Macros(def) => self
+ .macros
+ .iter()
+ .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))),
+ ItemInNs::Types(def) => self
+ .types
+ .iter()
+ .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))),
+
+ ItemInNs::Values(def) => self
+ .values
+ .iter()
+ .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))),
+ }
}
pub(crate) fn traits(&self) -> impl Iterator<Item = TraitId> + '_ {
self.types
.values()
- .filter_map(|&(def, _)| match def {
+ .filter_map(|&(def, _, _)| match def {
ModuleDefId::TraitId(t) => Some(t),
_ => None,
})
.chain(self.unnamed_trait_imports.keys().copied())
}
+ pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ {
+ self.entries().map(|(name, res)| (Some(name.clone()), res)).chain(
+ self.unnamed_trait_imports.iter().map(|(tr, (vis, i))| {
+ (
+ None,
+ PerNs::types(
+ ModuleDefId::TraitId(*tr),
+ *vis,
+ i.map(ImportOrExternCrate::Import),
+ ),
+ )
+ }),
+ )
+ }
+}
+
+impl ItemScope {
pub(crate) fn declare(&mut self, def: ModuleDefId) {
self.declarations.push(def)
}
@@ -277,12 +401,14 @@ impl ItemScope {
})
}
+ // FIXME: This is only used in collection, we should move the relevant parts of it out of ItemScope
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
- self.unnamed_trait_imports.get(&tr).copied()
+ self.unnamed_trait_imports.get(&tr).copied().map(|(a, _)| a)
}
pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) {
- self.unnamed_trait_imports.insert(tr, vis);
+ // FIXME: import
+ self.unnamed_trait_imports.insert(tr, (vis, None));
}
pub(crate) fn push_res_with_import(
@@ -290,51 +416,187 @@ impl ItemScope {
glob_imports: &mut PerNsGlobImports,
lookup: (LocalModuleId, Name),
def: PerNs,
- def_import_type: ImportType,
+ import: Option<ImportType>,
) -> bool {
let mut changed = false;
- macro_rules! check_changed {
- (
- $changed:ident,
- ( $this:ident / $def:ident ) . $field:ident,
- $glob_imports:ident [ $lookup:ident ],
- $def_import_type:ident
- ) => {{
- if let Some(fld) = $def.$field {
- let existing = $this.$field.entry($lookup.1.clone());
- match existing {
- Entry::Vacant(entry) => {
- match $def_import_type {
- ImportType::Glob => {
- $glob_imports.$field.insert($lookup.clone());
+ // FIXME: Document and simplify this
+
+ if let Some(mut fld) = def.types {
+ let existing = self.types.entry(lookup.1.clone());
+ match existing {
+ Entry::Vacant(entry) => {
+ match import {
+ Some(ImportType::Glob(_)) => {
+ glob_imports.types.insert(lookup.clone());
+ }
+ _ => _ = glob_imports.types.remove(&lookup),
+ }
+ let import = match import {
+ Some(ImportType::ExternCrate(extern_crate)) => {
+ Some(ImportOrExternCrate::ExternCrate(extern_crate))
+ }
+ Some(ImportType::Import(import)) => {
+ Some(ImportOrExternCrate::Import(import))
+ }
+ None | Some(ImportType::Glob(_)) => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_types.insert(
+ import,
+ match prev {
+ Some(ImportOrExternCrate::Import(import)) => {
+ ImportOrDef::Import(import)
}
- ImportType::Named => {
- $glob_imports.$field.remove(&$lookup);
+ Some(ImportOrExternCrate::ExternCrate(import)) => {
+ ImportOrDef::ExternCrate(import)
}
+ None => ImportOrDef::Def(fld.0),
+ },
+ );
+ }
+ entry.insert(fld);
+ changed = true;
+ }
+ Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
+ if glob_imports.types.remove(&lookup) {
+ let import = match import {
+ Some(ImportType::ExternCrate(extern_crate)) => {
+ Some(ImportOrExternCrate::ExternCrate(extern_crate))
+ }
+ Some(ImportType::Import(import)) => {
+ Some(ImportOrExternCrate::Import(import))
}
+ None | Some(ImportType::Glob(_)) => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_types.insert(
+ import,
+ match prev {
+ Some(ImportOrExternCrate::Import(import)) => {
+ ImportOrDef::Import(import)
+ }
+ Some(ImportOrExternCrate::ExternCrate(import)) => {
+ ImportOrDef::ExternCrate(import)
+ }
+ None => ImportOrDef::Def(fld.0),
+ },
+ );
+ }
+ cov_mark::hit!(import_shadowed);
+ entry.insert(fld);
+ changed = true;
+ }
+ }
+ _ => {}
+ }
+ }
- entry.insert(fld);
- $changed = true;
+ if let Some(mut fld) = def.values {
+ let existing = self.values.entry(lookup.1.clone());
+ match existing {
+ Entry::Vacant(entry) => {
+ match import {
+ Some(ImportType::Glob(_)) => {
+ glob_imports.values.insert(lookup.clone());
}
- Entry::Occupied(mut entry)
- if matches!($def_import_type, ImportType::Named) =>
- {
- if $glob_imports.$field.remove(&$lookup) {
- cov_mark::hit!(import_shadowed);
- entry.insert(fld);
- $changed = true;
- }
+ _ => _ = glob_imports.values.remove(&lookup),
+ }
+ let import = match import {
+ Some(ImportType::Import(import)) => Some(import),
+ _ => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_values.insert(
+ import,
+ match prev {
+ Some(import) => ImportOrDef::Import(import),
+ None => ImportOrDef::Def(fld.0),
+ },
+ );
+ }
+ entry.insert(fld);
+ changed = true;
+ }
+ Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
+ if glob_imports.values.remove(&lookup) {
+ cov_mark::hit!(import_shadowed);
+ let import = match import {
+ Some(ImportType::Import(import)) => Some(import),
+ _ => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_values.insert(
+ import,
+ match prev {
+ Some(import) => ImportOrDef::Import(import),
+ None => ImportOrDef::Def(fld.0),
+ },
+ );
}
- _ => {}
+ entry.insert(fld);
+ changed = true;
}
}
- }};
+ _ => {}
+ }
}
- check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type);
- check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type);
- check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type);
+ if let Some(mut fld) = def.macros {
+ let existing = self.macros.entry(lookup.1.clone());
+ match existing {
+ Entry::Vacant(entry) => {
+ match import {
+ Some(ImportType::Glob(_)) => {
+ glob_imports.macros.insert(lookup.clone());
+ }
+ _ => _ = glob_imports.macros.remove(&lookup),
+ }
+ let import = match import {
+ Some(ImportType::Import(import)) => Some(import),
+ _ => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_macros.insert(
+ import,
+ match prev {
+ Some(import) => ImportOrDef::Import(import),
+ None => ImportOrDef::Def(fld.0.into()),
+ },
+ );
+ }
+ entry.insert(fld);
+ changed = true;
+ }
+ Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
+ if glob_imports.macros.remove(&lookup) {
+ cov_mark::hit!(import_shadowed);
+ let import = match import {
+ Some(ImportType::Import(import)) => Some(import),
+ _ => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_macros.insert(
+ import,
+ match prev {
+ Some(import) => ImportOrDef::Import(import),
+ None => ImportOrDef::Def(fld.0.into()),
+ },
+ );
+ }
+ entry.insert(fld);
+ changed = true;
+ }
+ }
+ _ => {}
+ }
+ }
if def.is_none() && self.unresolved.insert(lookup.1) {
changed = true;
@@ -343,27 +605,18 @@ impl ItemScope {
changed
}
- pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ {
- self.entries().map(|(name, res)| (Some(name.clone()), res)).chain(
- self.unnamed_trait_imports
- .iter()
- .map(|(tr, vis)| (None, PerNs::types(ModuleDefId::TraitId(*tr), *vis))),
- )
- }
-
/// Marks everything that is not a procedural macro as private to `this_module`.
pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
self.types
.values_mut()
- .chain(self.values.values_mut())
+ .map(|(def, vis, _)| (def, vis))
+ .chain(self.values.values_mut().map(|(def, vis, _)| (def, vis)))
.map(|(_, v)| v)
- .chain(self.unnamed_trait_imports.values_mut())
+ .chain(self.unnamed_trait_imports.values_mut().map(|(vis, _)| vis))
.for_each(|vis| *vis = Visibility::Module(this_module));
- for (mac, vis) in self.macros.values_mut() {
- if let MacroId::ProcMacroId(_) = mac {
- // FIXME: Technically this is insufficient since reexports of proc macros are also
- // forbidden. Practically nobody does that.
+ for (mac, vis, import) in self.macros.values_mut() {
+ if matches!(mac, MacroId::ProcMacroId(_) if import.is_none()) {
continue;
}
@@ -382,14 +635,25 @@ impl ItemScope {
name.map_or("_".to_string(), |name| name.display(db).to_string())
);
- if def.types.is_some() {
+ if let Some((.., i)) = def.types {
buf.push_str(" t");
+ match i {
+ Some(ImportOrExternCrate::Import(_)) => buf.push('i'),
+ Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'),
+ None => (),
+ }
}
- if def.values.is_some() {
+ if let Some((.., i)) = def.values {
buf.push_str(" v");
+ if i.is_some() {
+ buf.push('i');
+ }
}
- if def.macros.is_some() {
+ if let Some((.., i)) = def.macros {
buf.push_str(" m");
+ if i.is_some() {
+ buf.push('i');
+ }
}
if def.is_none() {
buf.push_str(" _");
@@ -415,10 +679,17 @@ impl ItemScope {
attr_macros,
derive_macros,
extern_crate_decls,
+ use_decls,
+ use_imports_values,
+ use_imports_types,
+ use_imports_macros,
} = self;
types.shrink_to_fit();
values.shrink_to_fit();
macros.shrink_to_fit();
+ use_imports_types.shrink_to_fit();
+ use_imports_values.shrink_to_fit();
+ use_imports_macros.shrink_to_fit();
unresolved.shrink_to_fit();
declarations.shrink_to_fit();
impls.shrink_to_fit();
@@ -428,32 +699,44 @@ impl ItemScope {
attr_macros.shrink_to_fit();
derive_macros.shrink_to_fit();
extern_crate_decls.shrink_to_fit();
+ use_decls.shrink_to_fit();
}
}
impl PerNs {
- pub(crate) fn from_def(def: ModuleDefId, v: Visibility, has_constructor: bool) -> PerNs {
+ pub(crate) fn from_def(
+ def: ModuleDefId,
+ v: Visibility,
+ has_constructor: bool,
+ import: Option<ImportOrExternCrate>,
+ ) -> PerNs {
match def {
- ModuleDefId::ModuleId(_) => PerNs::types(def, v),
- ModuleDefId::FunctionId(_) => PerNs::values(def, v),
+ ModuleDefId::ModuleId(_) => PerNs::types(def, v, import),
+ ModuleDefId::FunctionId(_) => {
+ PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import))
+ }
ModuleDefId::AdtId(adt) => match adt {
- AdtId::UnionId(_) => PerNs::types(def, v),
- AdtId::EnumId(_) => PerNs::types(def, v),
+ AdtId::UnionId(_) => PerNs::types(def, v, import),
+ AdtId::EnumId(_) => PerNs::types(def, v, import),
AdtId::StructId(_) => {
if has_constructor {
- PerNs::both(def, def, v)
+ PerNs::both(def, def, v, import)
} else {
- PerNs::types(def, v)
+ PerNs::types(def, v, import)
}
}
},
- ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v),
- ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v),
- ModuleDefId::TraitId(_) => PerNs::types(def, v),
- ModuleDefId::TraitAliasId(_) => PerNs::types(def, v),
- ModuleDefId::TypeAliasId(_) => PerNs::types(def, v),
- ModuleDefId::BuiltinType(_) => PerNs::types(def, v),
- ModuleDefId::MacroId(mac) => PerNs::macros(mac, v),
+ ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import),
+ ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => {
+ PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import))
+ }
+ ModuleDefId::TraitId(_) => PerNs::types(def, v, import),
+ ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import),
+ ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import),
+ ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import),
+ ModuleDefId::MacroId(mac) => {
+ PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import))
+ }
}
}
}
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index c9b0f75f1a..3e1922750b 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -64,11 +64,11 @@ use triomphe::Arc;
use crate::{
attr::Attrs,
db::DefDatabase,
- generics::GenericParams,
+ generics::{GenericParams, LifetimeParamData, TypeOrConstParamData},
path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
visibility::RawVisibility,
- BlockId,
+ BlockId, Lookup,
};
#[derive(Copy, Clone, Eq, PartialEq)]
@@ -143,6 +143,16 @@ impl ItemTree {
Arc::new(item_tree)
}
+ pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
+ let loc = block.lookup(db);
+ let block = loc.ast_id.to_node(db.upcast());
+
+ let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
+ let mut item_tree = ctx.lower_block(&block);
+ item_tree.shrink_to_fit();
+ Arc::new(item_tree)
+ }
+
/// Returns an iterator over all items located at the top level of the `HirFileId` this
/// `ItemTree` was created from.
pub fn top_level_items(&self) -> &[ModItem] {
@@ -178,13 +188,6 @@ impl ItemTree {
self.data.get_or_insert_with(Box::default)
}
- fn block_item_tree(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
- let loc = db.lookup_intern_block(block);
- let block = loc.ast_id.to_node(db.upcast());
- let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
- Arc::new(ctx.lower_block(&block))
- }
-
fn shrink_to_fit(&mut self) {
if let Some(data) = &mut self.data {
let ItemTreeData {
@@ -296,10 +299,12 @@ pub enum AttrOwner {
Variant(Idx<Variant>),
Field(Idx<Field>),
Param(Idx<Param>),
+ TypeOrConstParamData(Idx<TypeOrConstParamData>),
+ LifetimeParamData(Idx<LifetimeParamData>),
}
macro_rules! from_attrs {
- ( $( $var:ident($t:ty) ),+ ) => {
+ ( $( $var:ident($t:ty) ),+ $(,)? ) => {
$(
impl From<$t> for AttrOwner {
fn from(t: $t) -> AttrOwner {
@@ -310,7 +315,14 @@ macro_rules! from_attrs {
};
}
-from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>), Param(Idx<Param>));
+from_attrs!(
+ ModItem(ModItem),
+ Variant(Idx<Variant>),
+ Field(Idx<Field>),
+ Param(Idx<Param>),
+ TypeOrConstParamData(Idx<TypeOrConstParamData>),
+ LifetimeParamData(Idx<LifetimeParamData>),
+);
/// Trait implemented by all item nodes in the item tree.
pub trait ItemTreeNode: Clone {
@@ -373,7 +385,7 @@ impl TreeId {
pub(crate) fn item_tree(&self, db: &dyn DefDatabase) -> Arc<ItemTree> {
match self.block {
- Some(block) => ItemTree::block_item_tree(db, block),
+ Some(block) => db.block_item_tree_query(block),
None => db.file_item_tree(self.file),
}
}
@@ -761,6 +773,19 @@ impl Use {
lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree");
source_map[index].clone()
}
+ /// Maps a `UseTree` contained in this import back to its AST node.
+ pub fn use_tree_source_map(
+ &self,
+ db: &dyn DefDatabase,
+ file_id: HirFileId,
+ ) -> Arena<ast::UseTree> {
+ // Re-lower the AST item and get the source map.
+ // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
+ let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
+ let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
+ let hygiene = Hygiene::new(db.upcast(), file_id);
+ lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree").1
+ }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -785,7 +810,7 @@ impl UseTree {
fn expand_impl(
&self,
prefix: Option<ModPath>,
- cb: &mut dyn FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
+ cb: &mut impl FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
) {
fn concat_mod_paths(
prefix: Option<ModPath>,
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index 7b898e62db..e4702c113b 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -77,6 +77,9 @@ impl<'a> Ctx<'a> {
}
pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
+ self.tree
+ .attrs
+ .insert(AttrOwner::TopLevel, RawAttrs::new(self.db.upcast(), block, self.hygiene()));
self.tree.top_level = block
.statements()
.filter_map(|stmt| match stmt {
@@ -602,7 +605,21 @@ impl<'a> Ctx<'a> {
generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param));
}
- generics.fill(&self.body_ctx, node);
+ let add_param_attrs = |item, param| {
+ let attrs = RawAttrs::new(self.db.upcast(), &param, self.body_ctx.hygiene());
+ // This is identical to the body of `Ctx::add_attrs()` but we can't call that here
+ // because it requires `&mut self` and the call to `generics.fill()` below also
+ // references `self`.
+ match self.tree.attrs.entry(item) {
+ Entry::Occupied(mut entry) => {
+ *entry.get_mut() = entry.get().merge(attrs);
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(attrs);
+ }
+ }
+ };
+ generics.fill(&self.body_ctx, node, add_param_attrs);
generics.shrink_to_fit();
Interned::new(generics)
@@ -763,7 +780,7 @@ impl UseTreeLowering<'_> {
}
}
-pub(super) fn lower_use_tree(
+pub(crate) fn lower_use_tree(
db: &dyn DefDatabase,
hygiene: &Hygiene,
tree: ast::UseTree,
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index da30830fe4..4b852dd613 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -16,7 +16,7 @@ pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> Strin
let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
- p.print_attrs(attrs, true);
+ p.print_attrs(attrs, true, "\n");
}
p.blank();
@@ -84,22 +84,23 @@ impl Printer<'_> {
}
}
- fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) {
+ fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool, separated_by: &str) {
let inner = if inner { "!" } else { "" };
for attr in &**attrs {
- wln!(
+ w!(
self,
- "#{}[{}{}]",
+ "#{}[{}{}]{}",
inner,
attr.path.display(self.db),
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
+ separated_by,
);
}
}
- fn print_attrs_of(&mut self, of: impl Into<AttrOwner>) {
+ fn print_attrs_of(&mut self, of: impl Into<AttrOwner>, separated_by: &str) {
if let Some(attrs) = self.tree.attrs.get(&of.into()) {
- self.print_attrs(attrs, false);
+ self.print_attrs(attrs, false, separated_by);
}
}
@@ -118,7 +119,7 @@ impl Printer<'_> {
self.indented(|this| {
for field in fields.clone() {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
- this.print_attrs_of(field);
+ this.print_attrs_of(field, "\n");
this.print_visibility(*visibility);
w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref);
@@ -132,7 +133,7 @@ impl Printer<'_> {
self.indented(|this| {
for field in fields.clone() {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
- this.print_attrs_of(field);
+ this.print_attrs_of(field, "\n");
this.print_visibility(*visibility);
w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref);
@@ -195,7 +196,7 @@ impl Printer<'_> {
}
fn print_mod_item(&mut self, item: ModItem) {
- self.print_attrs_of(item);
+ self.print_attrs_of(item, "\n");
match item {
ModItem::Use(it) => {
@@ -261,7 +262,7 @@ impl Printer<'_> {
if !params.is_empty() {
self.indented(|this| {
for param in params.clone() {
- this.print_attrs_of(param);
+ this.print_attrs_of(param, "\n");
match &this.tree[param] {
Param::Normal(ty) => {
if flags.contains(FnFlags::HAS_SELF_PARAM) {
@@ -319,7 +320,7 @@ impl Printer<'_> {
self.indented(|this| {
for variant in variants.clone() {
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
- this.print_attrs_of(variant);
+ this.print_attrs_of(variant, "\n");
w!(this, "{}", name.display(self.db));
this.print_fields(fields);
wln!(this, ",");
@@ -484,11 +485,12 @@ impl Printer<'_> {
w!(self, "<");
let mut first = true;
- for (_, lt) in params.lifetimes.iter() {
+ for (idx, lt) in params.lifetimes.iter() {
if !first {
w!(self, ", ");
}
first = false;
+ self.print_attrs_of(idx, " ");
w!(self, "{}", lt.name.display(self.db));
}
for (idx, x) in params.type_or_consts.iter() {
@@ -496,6 +498,7 @@ impl Printer<'_> {
w!(self, ", ");
}
first = false;
+ self.print_attrs_of(idx, " ");
match x {
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
Some(name) => w!(self, "{}", name.display(self.db)),
diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index 5ded4b6b27..4180f81720 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -358,3 +358,15 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
"#]],
)
}
+
+#[test]
+fn generics_with_attributes() {
+ check(
+ r#"
+struct S<#[cfg(never)] T>;
+ "#,
+ expect![[r#"
+ pub(self) struct S<#[cfg(never)] T>;
+ "#]],
+ )
+}
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 1901db8a0f..3f87fe62b8 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -109,6 +109,17 @@ impl CrateRootModuleId {
}
}
+impl PartialEq<ModuleId> for CrateRootModuleId {
+ fn eq(&self, other: &ModuleId) -> bool {
+ other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate
+ }
+}
+impl PartialEq<CrateRootModuleId> for ModuleId {
+ fn eq(&self, other: &CrateRootModuleId) -> bool {
+ other == self
+ }
+}
+
impl From<CrateRootModuleId> for ModuleId {
fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
ModuleId { krate, block: None, local_id: DefMap::ROOT }
@@ -854,14 +865,36 @@ impl_from!(
ConstId,
FunctionId,
TraitId,
+ TraitAliasId,
TypeAliasId,
MacroId(Macro2Id, MacroRulesId, ProcMacroId),
ImplId,
GenericParamId,
- ExternCrateId
+ ExternCrateId,
+ UseId
for AttrDefId
);
+impl TryFrom<ModuleDefId> for AttrDefId {
+ type Error = ();
+
+ fn try_from(value: ModuleDefId) -> Result<Self, Self::Error> {
+ match value {
+ ModuleDefId::ModuleId(it) => Ok(it.into()),
+ ModuleDefId::FunctionId(it) => Ok(it.into()),
+ ModuleDefId::AdtId(it) => Ok(it.into()),
+ ModuleDefId::EnumVariantId(it) => Ok(it.into()),
+ ModuleDefId::ConstId(it) => Ok(it.into()),
+ ModuleDefId::StaticId(it) => Ok(it.into()),
+ ModuleDefId::TraitId(it) => Ok(it.into()),
+ ModuleDefId::TypeAliasId(it) => Ok(it.into()),
+ ModuleDefId::TraitAliasId(id) => Ok(id.into()),
+ ModuleDefId::MacroId(id) => Ok(id.into()),
+ ModuleDefId::BuiltinType(_) => Err(()),
+ }
+ }
+}
+
impl From<ItemContainerId> for AttrDefId {
fn from(acid: ItemContainerId) -> Self {
match acid {
@@ -872,6 +905,15 @@ impl From<ItemContainerId> for AttrDefId {
}
}
}
+impl From<AssocItemId> for AttrDefId {
+ fn from(assoc: AssocItemId) -> Self {
+ match assoc {
+ AssocItemId::FunctionId(it) => AttrDefId::FunctionId(it),
+ AssocItemId::ConstId(it) => AttrDefId::ConstId(it),
+ AssocItemId::TypeAliasId(it) => AttrDefId::TypeAliasId(it),
+ }
+ }
+}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VariantId {
diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 1250cbb742..b232651db9 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -238,7 +238,7 @@ fn main() {
/* error: expected expression */;
/* error: expected expression, expected COMMA */;
/* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]);
- /* error: expected expression, expected expression */;
+ /* error: expected expression, expected R_PAREN */;
::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]);
}
"##]],
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index d8e4a4dcc7..97554f93f1 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -909,3 +909,68 @@ macro_rules! with_std {
"##]],
)
}
+
+#[test]
+fn eager_regression_15403() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+ format_args /* +errors */ !("{}", line.1.);
+}
+
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+ /* error: expected field name or number *//* parse error: expected field name or number */
+::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(line.1.), ::core::fmt::Display::fmt), ]);
+}
+
+"##]],
+ );
+}
+
+#[test]
+fn eager_regression_154032() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+ format_args /* +errors */ !("{}", &[0 2]);
+}
+
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+ /* error: expected COMMA, expected R_BRACK, expected COMMA, expected COMMA, expected expression, expected R_PAREN *//* parse error: expected COMMA */
+/* parse error: expected R_BRACK */
+/* parse error: expected COMMA */
+/* parse error: expected COMMA */
+/* parse error: expected expression */
+/* parse error: expected R_PAREN */
+/* parse error: expected R_PAREN */
+/* parse error: expected expression, item or let statement */
+/* parse error: expected expression, item or let statement */
+/* parse error: expected expression, item or let statement */
+/* parse error: expected expression, item or let statement */
+/* parse error: expected expression, item or let statement */
+::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(&[0 2]), ::core::fmt::Display::fmt), ]);
+}
+
+"##]],
+ );
+}
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 7a87e61c69..8adced4e08 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -131,7 +131,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
.as_call_id_with_errors(&db, krate, |path| {
resolver
.resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang))
- .map(|it| macro_id_to_def_id(&db, it))
+ .map(|(it, _)| macro_id_to_def_id(&db, it))
})
.unwrap();
let macro_call_id = res.value.unwrap();
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index 86818ce26d..f211041098 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -60,7 +60,7 @@ mod tests;
use std::{cmp::Ord, ops::Deref};
use base_db::{CrateId, Edition, FileId, ProcMacroKind};
-use hir_expand::{name::Name, HirFileId, InFile, MacroCallId, MacroDefId};
+use hir_expand::{ast_id_map::FileAstId, name::Name, HirFileId, InFile, MacroCallId, MacroDefId};
use itertools::Itertools;
use la_arena::Arena;
use profile::Count;
@@ -77,8 +77,8 @@ use crate::{
path::ModPath,
per_ns::PerNs,
visibility::Visibility,
- AstId, BlockId, BlockLoc, CrateRootModuleId, FunctionId, LocalModuleId, Lookup, MacroExpander,
- MacroId, ModuleId, ProcMacroId,
+ AstId, BlockId, BlockLoc, CrateRootModuleId, ExternCrateId, FunctionId, LocalModuleId, Lookup,
+ MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
};
/// Contains the results of (early) name resolution.
@@ -105,10 +105,11 @@ pub struct DefMap {
/// The prelude is empty for non-block DefMaps (unless `#[prelude_import]` was used,
/// but that attribute is nightly and when used in a block, it affects resolution globally
/// so we aren't handling this correctly anyways).
- prelude: Option<ModuleId>,
+ prelude: Option<(ModuleId, Option<UseId>)>,
/// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that
/// this contains all kinds of macro, not just `macro_rules!` macro.
- macro_use_prelude: FxHashMap<Name, MacroId>,
+ /// ExternCrateId being None implies it being imported from the general prelude import.
+ macro_use_prelude: FxHashMap<Name, (MacroId, Option<ExternCrateId>)>,
/// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
/// attributes.
@@ -125,7 +126,7 @@ pub struct DefMap {
#[derive(Clone, Debug, PartialEq, Eq)]
struct DefMapCrateData {
/// The extern prelude which contains all root modules of external crates that are in scope.
- extern_prelude: FxHashMap<Name, CrateRootModuleId>,
+ extern_prelude: FxHashMap<Name, (CrateRootModuleId, Option<ExternCrateId>)>,
/// Side table for resolving derive helpers.
exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
@@ -217,16 +218,17 @@ pub enum ModuleOrigin {
/// Note that non-inline modules, by definition, live inside non-macro file.
File {
is_mod_rs: bool,
- declaration: AstId<ast::Module>,
+ declaration: FileAstId<ast::Module>,
declaration_tree_id: ItemTreeId<Mod>,
definition: FileId,
},
Inline {
definition_tree_id: ItemTreeId<Mod>,
- definition: AstId<ast::Module>,
+ definition: FileAstId<ast::Module>,
},
/// Pseudo-module introduced by a block scope (contains only inner items).
BlockExpr {
+ id: BlockId,
block: AstId<ast::BlockExpr>,
},
}
@@ -234,8 +236,12 @@ pub enum ModuleOrigin {
impl ModuleOrigin {
pub fn declaration(&self) -> Option<AstId<ast::Module>> {
match self {
- ModuleOrigin::File { declaration: module, .. }
- | ModuleOrigin::Inline { definition: module, .. } => Some(*module),
+ &ModuleOrigin::File { declaration, declaration_tree_id, .. } => {
+ Some(AstId::new(declaration_tree_id.file_id(), declaration))
+ }
+ &ModuleOrigin::Inline { definition, definition_tree_id } => {
+ Some(AstId::new(definition_tree_id.file_id(), definition))
+ }
ModuleOrigin::CrateRoot { .. } | ModuleOrigin::BlockExpr { .. } => None,
}
}
@@ -260,16 +266,17 @@ impl ModuleOrigin {
/// That is, a file or a `mod foo {}` with items.
fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> {
match self {
- ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
- let file_id = *definition;
- let sf = db.parse(file_id).tree();
- InFile::new(file_id.into(), ModuleSource::SourceFile(sf))
+ &ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => {
+ let sf = db.parse(definition).tree();
+ InFile::new(definition.into(), ModuleSource::SourceFile(sf))
}
- ModuleOrigin::Inline { definition, .. } => InFile::new(
- definition.file_id,
- ModuleSource::Module(definition.to_node(db.upcast())),
+ &ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new(
+ definition_tree_id.file_id(),
+ ModuleSource::Module(
+ AstId::new(definition_tree_id.file_id(), definition).to_node(db.upcast()),
+ ),
),
- ModuleOrigin::BlockExpr { block } => {
+ ModuleOrigin::BlockExpr { block, .. } => {
InFile::new(block.file_id, ModuleSource::BlockExpr(block.to_node(db.upcast())))
}
}
@@ -314,9 +321,7 @@ impl DefMap {
}
pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> {
- let block: BlockLoc = db.lookup_intern_block(block_id);
-
- let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id));
+ let block: BlockLoc = block_id.lookup(db);
let parent_map = block.module.def_map(db);
let krate = block.module.krate;
@@ -325,8 +330,10 @@ impl DefMap {
// modules declared by blocks with items. At the moment, we don't use
// this visibility for anything outside IDE, so that's probably OK.
let visibility = Visibility::Module(ModuleId { krate, local_id, block: None });
- let module_data =
- ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility);
+ let module_data = ModuleData::new(
+ ModuleOrigin::BlockExpr { block: block.ast_id, id: block_id },
+ visibility,
+ );
let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data);
def_map.data = parent_map.data.clone();
@@ -338,7 +345,8 @@ impl DefMap {
},
});
- let def_map = collector::collect_defs(db, def_map, tree_id);
+ let def_map =
+ collector::collect_defs(db, def_map, TreeId::new(block.ast_id.file_id, Some(block_id)));
Arc::new(def_map)
}
@@ -427,15 +435,19 @@ impl DefMap {
self.block.map(|block| block.block)
}
- pub(crate) fn prelude(&self) -> Option<ModuleId> {
+ pub(crate) fn prelude(&self) -> Option<(ModuleId, Option<UseId>)> {
self.prelude
}
- pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
- self.data.extern_prelude.iter().map(|(name, &def)| (name, def.into()))
+ pub(crate) fn extern_prelude(
+ &self,
+ ) -> impl Iterator<Item = (&Name, (CrateRootModuleId, Option<ExternCrateId>))> + '_ {
+ self.data.extern_prelude.iter().map(|(name, &def)| (name, def))
}
- pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
+ pub(crate) fn macro_use_prelude(
+ &self,
+ ) -> impl Iterator<Item = (&Name, (MacroId, Option<ExternCrateId>))> + '_ {
self.macro_use_prelude.iter().map(|(name, &def)| (name, def))
}
@@ -638,8 +650,8 @@ impl ModuleData {
ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
definition.into()
}
- ModuleOrigin::Inline { definition, .. } => definition.file_id,
- ModuleOrigin::BlockExpr { block } => block.file_id,
+ ModuleOrigin::Inline { definition_tree_id, .. } => definition_tree_id.file_id(),
+ ModuleOrigin::BlockExpr { block, .. } => block.file_id,
}
}
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index eef54fc492..e9e71a8747 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -33,7 +33,7 @@ use crate::{
attr_macro_as_call_id,
db::DefDatabase,
derive_macro_as_call_id,
- item_scope::{ImportType, PerNsGlobImports},
+ item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
item_tree::{
self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
@@ -52,10 +52,10 @@ use crate::{
tt,
visibility::{RawVisibility, Visibility},
AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId,
- ExternBlockLoc, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId,
- LocalModuleId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc,
- ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc,
- TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseLoc,
+ ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern,
+ ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
+ MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc,
+ StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc,
};
static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
@@ -146,8 +146,8 @@ impl PartialResolvedImport {
#[derive(Clone, Debug, Eq, PartialEq)]
enum ImportSource {
- Use { id: ItemTreeId<item_tree::Use>, use_tree: Idx<ast::UseTree> },
- ExternCrate(ItemTreeId<item_tree::ExternCrate>),
+ Use { use_tree: Idx<ast::UseTree>, id: UseId, is_prelude: bool, kind: ImportKind },
+ ExternCrate { id: ExternCrateId },
}
#[derive(Debug, Eq, PartialEq)]
@@ -155,54 +155,41 @@ struct Import {
path: ModPath,
alias: Option<ImportAlias>,
visibility: RawVisibility,
- kind: ImportKind,
source: ImportSource,
- is_prelude: bool,
- is_macro_use: bool,
}
impl Import {
fn from_use(
- db: &dyn DefDatabase,
- krate: CrateId,
tree: &ItemTree,
- id: ItemTreeId<item_tree::Use>,
+ item_tree_id: ItemTreeId<item_tree::Use>,
+ id: UseId,
+ is_prelude: bool,
mut cb: impl FnMut(Self),
) {
- let it = &tree[id.value];
- let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
+ let it = &tree[item_tree_id.value];
let visibility = &tree[it.visibility];
- let is_prelude = attrs.by_key("prelude_import").exists();
it.use_tree.expand(|idx, path, kind, alias| {
cb(Self {
path,
alias,
visibility: visibility.clone(),
- kind,
- is_prelude,
- is_macro_use: false,
- source: ImportSource::Use { id, use_tree: idx },
+ source: ImportSource::Use { use_tree: idx, id, is_prelude, kind },
});
});
}
fn from_extern_crate(
- db: &dyn DefDatabase,
- krate: CrateId,
tree: &ItemTree,
- id: ItemTreeId<item_tree::ExternCrate>,
+ item_tree_id: ItemTreeId<item_tree::ExternCrate>,
+ id: ExternCrateId,
) -> Self {
- let it = &tree[id.value];
- let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
+ let it = &tree[item_tree_id.value];
let visibility = &tree[it.visibility];
Self {
path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
alias: it.alias.clone(),
visibility: visibility.clone(),
- kind: ImportKind::Plain,
- is_prelude: false,
- is_macro_use: attrs.by_key("macro_use").exists(),
- source: ImportSource::ExternCrate(id),
+ source: ImportSource::ExternCrate { id },
}
}
}
@@ -235,7 +222,7 @@ struct DefCollector<'a> {
db: &'a dyn DefDatabase,
def_map: DefMap,
deps: FxHashMap<Name, Dependency>,
- glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
+ glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>,
unresolved_imports: Vec<ImportDirective>,
indeterminate_imports: Vec<ImportDirective>,
unresolved_macros: Vec<MacroDirective>,
@@ -280,7 +267,7 @@ impl DefCollector<'_> {
if dep.is_prelude() {
crate_data
.extern_prelude
- .insert(name.clone(), CrateRootModuleId { krate: dep.crate_id });
+ .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None));
}
}
@@ -556,8 +543,12 @@ impl DefCollector<'_> {
self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
match per_ns.types {
- Some((ModuleDefId::ModuleId(m), _)) => {
- self.def_map.prelude = Some(m);
+ Some((ModuleDefId::ModuleId(m), _, import)) => {
+ // FIXME: This should specifically look for a glob import somehow and record that here
+ self.def_map.prelude = Some((
+ m,
+ import.and_then(ImportOrExternCrate::into_import).map(|it| it.import),
+ ));
}
types => {
tracing::debug!(
@@ -657,9 +648,9 @@ impl DefCollector<'_> {
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
- &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))],
+ &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))],
Visibility::Public,
- ImportType::Named,
+ None,
);
}
}
@@ -693,9 +684,9 @@ impl DefCollector<'_> {
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
- &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))],
+ &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))],
vis,
- ImportType::Named,
+ None,
);
}
@@ -708,9 +699,9 @@ impl DefCollector<'_> {
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
- &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))],
+ &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))],
Visibility::Public,
- ImportType::Named,
+ None,
);
}
@@ -720,21 +711,29 @@ impl DefCollector<'_> {
/// Exported macros are just all macros in the root module scope.
/// Note that it contains not only all `#[macro_export]` macros, but also all aliases
/// created by `use` in the root module, ignoring the visibility of `use`.
- fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
+ fn import_macros_from_extern_crate(
+ &mut self,
+ krate: CrateId,
+ names: Option<Vec<Name>>,
+ extern_crate: Option<ExternCrateId>,
+ ) {
let def_map = self.db.crate_def_map(krate);
// `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
// macros.
let root_scope = &def_map[DefMap::ROOT].scope;
- if let Some(names) = names {
- for name in names {
- // FIXME: Report diagnostic on 404.
- if let Some(def) = root_scope.get(&name).take_macros() {
- self.def_map.macro_use_prelude.insert(name, def);
+ match names {
+ Some(names) => {
+ for name in names {
+ // FIXME: Report diagnostic on 404.
+ if let Some(def) = root_scope.get(&name).take_macros() {
+ self.def_map.macro_use_prelude.insert(name, (def, extern_crate));
+ }
}
}
- } else {
- for (name, def) in root_scope.macros() {
- self.def_map.macro_use_prelude.insert(name.clone(), def);
+ None => {
+ for (name, def) in root_scope.macros() {
+ self.def_map.macro_use_prelude.insert(name.clone(), (def, extern_crate));
+ }
}
}
}
@@ -771,48 +770,53 @@ impl DefCollector<'_> {
let _p = profile::span("resolve_import")
.detail(|| format!("{}", import.path.display(self.db.upcast())));
tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
- if matches!(import.source, ImportSource::ExternCrate { .. }) {
- let name = import
- .path
- .as_ident()
- .expect("extern crate should have been desugared to one-element path");
-
- let res = self.resolve_extern_crate(name);
-
- match res {
- Some(res) => {
- PartialResolvedImport::Resolved(PerNs::types(res.into(), Visibility::Public))
+ match import.source {
+ ImportSource::ExternCrate { .. } => {
+ let name = import
+ .path
+ .as_ident()
+ .expect("extern crate should have been desugared to one-element path");
+
+ let res = self.resolve_extern_crate(name);
+
+ match res {
+ Some(res) => PartialResolvedImport::Resolved(PerNs::types(
+ res.into(),
+ Visibility::Public,
+ None,
+ )),
+ None => PartialResolvedImport::Unresolved,
}
- None => PartialResolvedImport::Unresolved,
}
- } else {
- let res = self.def_map.resolve_path_fp_with_macro(
- self.db,
- ResolveMode::Import,
- module_id,
- &import.path,
- BuiltinShadowMode::Module,
- None, // An import may resolve to any kind of macro.
- );
+ ImportSource::Use { .. } => {
+ let res = self.def_map.resolve_path_fp_with_macro(
+ self.db,
+ ResolveMode::Import,
+ module_id,
+ &import.path,
+ BuiltinShadowMode::Module,
+ None, // An import may resolve to any kind of macro.
+ );
- let def = res.resolved_def;
- if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
- return PartialResolvedImport::Unresolved;
- }
+ let def = res.resolved_def;
+ if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
+ return PartialResolvedImport::Unresolved;
+ }
- if let Some(krate) = res.krate {
- if krate != self.def_map.krate {
- return PartialResolvedImport::Resolved(
- def.filter_visibility(|v| matches!(v, Visibility::Public)),
- );
+ if let Some(krate) = res.krate {
+ if krate != self.def_map.krate {
+ return PartialResolvedImport::Resolved(
+ def.filter_visibility(|v| matches!(v, Visibility::Public)),
+ );
+ }
}
- }
- // Check whether all namespaces are resolved.
- if def.is_full() {
- PartialResolvedImport::Resolved(def)
- } else {
- PartialResolvedImport::Indeterminate(def)
+ // Check whether all namespaces are resolved.
+ if def.is_full() {
+ PartialResolvedImport::Resolved(def)
+ } else {
+ PartialResolvedImport::Indeterminate(def)
+ }
}
}
}
@@ -837,8 +841,9 @@ impl DefCollector<'_> {
.resolve_visibility(self.db, module_id, &directive.import.visibility, false)
.unwrap_or(Visibility::Public);
- match import.kind {
- ImportKind::Plain | ImportKind::TypeOnly => {
+ match import.source {
+ ImportSource::ExternCrate { .. }
+ | ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => {
let name = match &import.alias {
Some(ImportAlias::Alias(name)) => Some(name),
Some(ImportAlias::Underscore) => None,
@@ -851,40 +856,44 @@ impl DefCollector<'_> {
},
};
- if import.kind == ImportKind::TypeOnly {
- def.values = None;
- def.macros = None;
- }
-
- tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
-
- // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
- if matches!(import.source, ImportSource::ExternCrate { .. })
- && self.def_map.block.is_none()
- && module_id == DefMap::ROOT
- {
- if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
- {
- if let Ok(def) = def.try_into() {
- Arc::get_mut(&mut self.def_map.data)
- .unwrap()
- .extern_prelude
- .insert(name.clone(), def);
+ let imp = match import.source {
+ // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
+ ImportSource::ExternCrate { id, .. } => {
+ if self.def_map.block.is_none() && module_id == DefMap::ROOT {
+ if let (Some(ModuleDefId::ModuleId(def)), Some(name)) =
+ (def.take_types(), name)
+ {
+ if let Ok(def) = def.try_into() {
+ Arc::get_mut(&mut self.def_map.data)
+ .unwrap()
+ .extern_prelude
+ .insert(name.clone(), (def, Some(id)));
+ }
+ }
}
+ ImportType::ExternCrate(id)
}
- }
+ ImportSource::Use { kind, id, use_tree, .. } => {
+ if kind == ImportKind::TypeOnly {
+ def.values = None;
+ def.macros = None;
+ }
+ ImportType::Import(ImportId { import: id, idx: use_tree })
+ }
+ };
+ tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
- self.update(module_id, &[(name.cloned(), def)], vis, ImportType::Named);
+ self.update(module_id, &[(name.cloned(), def)], vis, Some(imp));
}
- ImportKind::Glob => {
+ ImportSource::Use { kind: ImportKind::Glob, id, .. } => {
tracing::debug!("glob import: {:?}", import);
match def.take_types() {
Some(ModuleDefId::ModuleId(m)) => {
- if import.is_prelude {
+ if let ImportSource::Use { id, is_prelude: true, .. } = import.source {
// Note: This dodgily overrides the injected prelude. The rustc
// implementation seems to work the same though.
cov_mark::hit!(std_prelude);
- self.def_map.prelude = Some(m);
+ self.def_map.prelude = Some((m, Some(id)));
} else if m.krate != self.def_map.krate {
cov_mark::hit!(glob_across_crates);
// glob import from other crate => we can just import everything once
@@ -901,7 +910,7 @@ impl DefCollector<'_> {
.filter(|(_, res)| !res.is_none())
.collect::<Vec<_>>();
- self.update(module_id, &items, vis, ImportType::Glob);
+ self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
} else {
// glob import from same crate => we do an initial
// import, and then need to propagate any further
@@ -933,11 +942,11 @@ impl DefCollector<'_> {
.filter(|(_, res)| !res.is_none())
.collect::<Vec<_>>();
- self.update(module_id, &items, vis, ImportType::Glob);
+ self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
// record the glob import in case we add further items
let glob = self.glob_imports.entry(m.local_id).or_default();
- if !glob.iter().any(|(mid, _)| *mid == module_id) {
- glob.push((module_id, vis));
+ if !glob.iter().any(|(mid, _, _)| *mid == module_id) {
+ glob.push((module_id, vis, id));
}
}
}
@@ -959,11 +968,11 @@ impl DefCollector<'_> {
.map(|(local_id, variant_data)| {
let name = variant_data.name.clone();
let variant = EnumVariantId { parent: e, local_id };
- let res = PerNs::both(variant.into(), variant.into(), vis);
+ let res = PerNs::both(variant.into(), variant.into(), vis, None);
(Some(name), res)
})
.collect::<Vec<_>>();
- self.update(module_id, &resolutions, vis, ImportType::Glob);
+ self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id)));
}
Some(d) => {
tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d);
@@ -983,10 +992,10 @@ impl DefCollector<'_> {
resolutions: &[(Option<Name>, PerNs)],
// Visibility this import will have
vis: Visibility,
- import_type: ImportType,
+ import: Option<ImportType>,
) {
self.db.unwind_if_cancelled();
- self.update_recursive(module_id, resolutions, vis, import_type, 0)
+ self.update_recursive(module_id, resolutions, vis, import, 0)
}
fn update_recursive(
@@ -997,7 +1006,7 @@ impl DefCollector<'_> {
// All resolutions are imported with this visibility; the visibilities in
// the `PerNs` values are ignored and overwritten
vis: Visibility,
- import_type: ImportType,
+ import: Option<ImportType>,
depth: usize,
) {
if GLOB_RECURSION_LIMIT.check(depth).is_err() {
@@ -1014,7 +1023,7 @@ impl DefCollector<'_> {
&mut self.from_glob_import,
(module_id, name.clone()),
res.with_visibility(vis),
- import_type,
+ import,
);
}
None => {
@@ -1059,7 +1068,7 @@ impl DefCollector<'_> {
.get(&module_id)
.into_iter()
.flatten()
- .filter(|(glob_importing_module, _)| {
+ .filter(|(glob_importing_module, _, _)| {
// we know all resolutions have the same visibility (`vis`), so we
// just need to check that once
vis.is_visible_from_def_map(self.db, &self.def_map, *glob_importing_module)
@@ -1067,12 +1076,12 @@ impl DefCollector<'_> {
.cloned()
.collect::<Vec<_>>();
- for (glob_importing_module, glob_import_vis) in glob_imports {
+ for (glob_importing_module, glob_import_vis, use_) in glob_imports {
self.update_recursive(
glob_importing_module,
resolutions,
glob_import_vis,
- ImportType::Glob,
+ Some(ImportType::Glob(use_)),
depth + 1,
);
}
@@ -1460,31 +1469,34 @@ impl DefCollector<'_> {
// heuristic, but it works in practice.
let mut diagnosed_extern_crates = FxHashSet::default();
for directive in &self.unresolved_imports {
- if let ImportSource::ExternCrate(krate) = directive.import.source {
- let item_tree = krate.item_tree(self.db);
- let extern_crate = &item_tree[krate.value];
+ if let ImportSource::ExternCrate { id } = directive.import.source {
+ let item_tree_id = id.lookup(self.db).id;
+ let item_tree = item_tree_id.item_tree(self.db);
+ let extern_crate = &item_tree[item_tree_id.value];
diagnosed_extern_crates.insert(extern_crate.name.clone());
self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
directive.module_id,
- InFile::new(krate.file_id(), extern_crate.ast_id),
+ InFile::new(item_tree_id.file_id(), extern_crate.ast_id),
));
}
}
for directive in &self.unresolved_imports {
- if let ImportSource::Use { id: import, use_tree } = directive.import.source {
+ if let ImportSource::Use { use_tree, id, is_prelude: _, kind: _ } =
+ directive.import.source
+ {
if matches!(
(directive.import.path.segments().first(), &directive.import.path.kind),
(Some(krate), PathKind::Plain | PathKind::Abs) if diagnosed_extern_crates.contains(krate)
) {
continue;
}
-
+ let item_tree_id = id.lookup(self.db).id;
self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
directive.module_id,
- import,
+ item_tree_id,
use_tree,
));
}
@@ -1519,72 +1531,66 @@ impl ModCollector<'_, '_> {
self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
// Prelude module is always considered to be `#[macro_use]`.
- if let Some(prelude_module) = self.def_collector.def_map.prelude {
+ if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude {
if prelude_module.krate != krate && is_crate_root {
cov_mark::hit!(prelude_is_macro_use);
- self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
- }
- }
-
- // This should be processed eagerly instead of deferred to resolving.
- // `#[macro_use] extern crate` is hoisted to imports macros before collecting
- // any other items.
- //
- // If we're not at the crate root, `macro_use`d extern crates are an error so let's just
- // ignore them.
- if is_crate_root {
- for &item in items {
- if let ModItem::ExternCrate(id) = item {
- self.process_macro_use_extern_crate(id);
- }
+ self.def_collector.import_macros_from_extern_crate(
+ prelude_module.krate,
+ None,
+ None,
+ );
}
}
+ let db = self.def_collector.db;
+ let module_id = self.module_id;
+ let update_def =
+ |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
+ def_collector.def_map.modules[module_id].scope.declare(id);
+ def_collector.update(
+ module_id,
+ &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor, None))],
+ vis,
+ None,
+ )
+ };
+ let resolve_vis = |def_map: &DefMap, visibility| {
+ def_map
+ .resolve_visibility(db, module_id, visibility, false)
+ .unwrap_or(Visibility::Public)
+ };
- for &item in items {
- let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
+ let mut process_mod_item = |item: ModItem| {
+ let attrs = self.item_tree.attrs(db, krate, item.into());
if let Some(cfg) = attrs.cfg() {
if !self.is_cfg_enabled(&cfg) {
self.emit_unconfigured_diagnostic(item, &cfg);
- continue;
+ return;
}
}
if let Err(()) = self.resolve_attributes(&attrs, item, container) {
// Do not process the item. It has at least one non-builtin attribute, so the
// fixed-point algorithm is required to resolve the rest of them.
- continue;
+ return;
}
- let db = self.def_collector.db;
- let module = self.def_collector.def_map.module_id(self.module_id);
+ let module = self.def_collector.def_map.module_id(module_id);
let def_map = &mut self.def_collector.def_map;
- let update_def =
- |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
- def_collector.def_map.modules[self.module_id].scope.declare(id);
- def_collector.update(
- self.module_id,
- &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
- vis,
- ImportType::Named,
- )
- };
- let resolve_vis = |def_map: &DefMap, visibility| {
- def_map
- .resolve_visibility(db, self.module_id, visibility, false)
- .unwrap_or(Visibility::Public)
- };
match item {
ModItem::Mod(m) => self.collect_module(m, &attrs),
- ModItem::Use(import_id) => {
- let _import_id =
- UseLoc { container: module, id: ItemTreeId::new(self.tree_id, import_id) }
- .intern(db);
+ ModItem::Use(item_tree_id) => {
+ let id = UseLoc {
+ container: module,
+ id: ItemTreeId::new(self.tree_id, item_tree_id),
+ }
+ .intern(db);
+ let is_prelude = attrs.by_key("prelude_import").exists();
Import::from_use(
- db,
- krate,
self.item_tree,
- ItemTreeId::new(self.tree_id, import_id),
+ ItemTreeId::new(self.tree_id, item_tree_id),
+ id,
+ is_prelude,
|import| {
self.def_collector.unresolved_imports.push(ImportDirective {
module_id: self.module_id,
@@ -1594,22 +1600,29 @@ impl ModCollector<'_, '_> {
},
)
}
- ModItem::ExternCrate(import_id) => {
- let extern_crate_id = ExternCrateLoc {
+ ModItem::ExternCrate(item_tree_id) => {
+ let id = ExternCrateLoc {
container: module,
- id: ItemTreeId::new(self.tree_id, import_id),
+ id: ItemTreeId::new(self.tree_id, item_tree_id),
}
.intern(db);
+ if is_crate_root {
+ self.process_macro_use_extern_crate(
+ item_tree_id,
+ id,
+ attrs.by_key("macro_use").attrs(),
+ );
+ }
+
self.def_collector.def_map.modules[self.module_id]
.scope
- .define_extern_crate_decl(extern_crate_id);
+ .define_extern_crate_decl(id);
self.def_collector.unresolved_imports.push(ImportDirective {
module_id: self.module_id,
import: Import::from_extern_crate(
- db,
- krate,
self.item_tree,
- ItemTreeId::new(self.tree_id, import_id),
+ ItemTreeId::new(self.tree_id, item_tree_id),
+ id,
),
status: PartialResolvedImport::Unresolved,
})
@@ -1768,21 +1781,34 @@ impl ModCollector<'_, '_> {
);
}
}
+ };
+
+ // extern crates should be processed eagerly instead of deferred to resolving.
+ // `#[macro_use] extern crate` is hoisted to imports macros before collecting
+ // any other items.
+ if is_crate_root {
+ items
+ .iter()
+ .filter(|it| matches!(it, ModItem::ExternCrate(..)))
+ .copied()
+ .for_each(&mut process_mod_item);
+ items
+ .iter()
+ .filter(|it| !matches!(it, ModItem::ExternCrate(..)))
+ .copied()
+ .for_each(process_mod_item);
+ } else {
+ items.iter().copied().for_each(process_mod_item);
}
}
- fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
+ fn process_macro_use_extern_crate<'a>(
+ &mut self,
+ extern_crate: FileItemTreeId<ExternCrate>,
+ extern_crate_id: ExternCrateId,
+ macro_use_attrs: impl Iterator<Item = &'a Attr>,
+ ) {
let db = self.def_collector.db;
- let attrs = self.item_tree.attrs(
- db,
- self.def_collector.def_map.krate,
- ModItem::from(extern_crate).into(),
- );
- if let Some(cfg) = attrs.cfg() {
- if !self.is_cfg_enabled(&cfg) {
- return;
- }
- }
let target_crate =
match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
@@ -1798,11 +1824,11 @@ impl ModCollector<'_, '_> {
let mut single_imports = Vec::new();
let hygiene = Hygiene::new_unhygienic();
- for attr in attrs.by_key("macro_use").attrs() {
+ for attr in macro_use_attrs {
let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
// `#[macro_use]` (without any paths) found, forget collected names and just import
// all visible macros.
- self.def_collector.import_macros_from_extern_crate(target_crate, None);
+ self.def_collector.import_macros_from_extern_crate(target_crate, None, Some(extern_crate_id));
return;
};
for path in paths {
@@ -1812,7 +1838,11 @@ impl ModCollector<'_, '_> {
}
}
- self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
+ self.def_collector.import_macros_from_extern_crate(
+ target_crate,
+ Some(single_imports),
+ Some(extern_crate_id),
+ );
}
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
@@ -1824,7 +1854,7 @@ impl ModCollector<'_, '_> {
ModKind::Inline { items } => {
let module_id = self.push_child_module(
module.name.clone(),
- AstId::new(self.file_id(), module.ast_id),
+ module.ast_id,
None,
&self.item_tree[module.visibility],
module_id,
@@ -1862,7 +1892,7 @@ impl ModCollector<'_, '_> {
if is_enabled {
let module_id = self.push_child_module(
module.name.clone(),
- ast_id,
+ ast_id.value,
Some((file_id, is_mod_rs)),
&self.item_tree[module.visibility],
module_id,
@@ -1889,7 +1919,7 @@ impl ModCollector<'_, '_> {
Err(candidates) => {
self.push_child_module(
module.name.clone(),
- ast_id,
+ ast_id.value,
None,
&self.item_tree[module.visibility],
module_id,
@@ -1906,7 +1936,7 @@ impl ModCollector<'_, '_> {
fn push_child_module(
&mut self,
name: Name,
- declaration: AstId<ast::Module>,
+ declaration: FileAstId<ast::Module>,
definition: Option<(FileId, bool)>,
visibility: &crate::visibility::RawVisibility,
mod_tree_id: FileItemTreeId<Mod>,
@@ -1948,9 +1978,9 @@ impl ModCollector<'_, '_> {
def_map.modules[self.module_id].scope.declare(def);
self.def_collector.update(
self.module_id,
- &[(Some(name), PerNs::from_def(def, vis, false))],
+ &[(Some(name), PerNs::from_def(def, vis, false, None))],
vis,
- ImportType::Named,
+ None,
);
res
}
@@ -2198,7 +2228,7 @@ impl ModCollector<'_, '_> {
map[module].scope.get_legacy_macro(name)?.last().copied()
})
.or_else(|| def_map[self.module_id].scope.get(name).take_macros())
- .or_else(|| def_map.macro_use_prelude.get(name).copied())
+ .or_else(|| Some(def_map.macro_use_prelude.get(name).copied()?.0))
.filter(|&id| {
sub_namespace_match(
Some(MacroSubNs::from_id(db, id)),
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index de22ea1014..460a908b6d 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -15,8 +15,9 @@ use hir_expand::name::Name;
use triomphe::Arc;
use crate::{
+ data::adt::VariantData,
db::DefDatabase,
- item_scope::BUILTIN_SCOPE,
+ item_scope::{ImportOrExternCrate, BUILTIN_SCOPE},
nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
path::{ModPath, PathKind},
per_ns::PerNs,
@@ -65,7 +66,7 @@ impl PerNs {
db: &dyn DefDatabase,
expected: Option<MacroSubNs>,
) -> Self {
- self.macros = self.macros.filter(|&(id, _)| {
+ self.macros = self.macros.filter(|&(id, _, _)| {
let this = MacroSubNs::from_id(db, id);
sub_namespace_match(Some(this), expected)
});
@@ -196,15 +197,15 @@ impl DefMap {
PathKind::DollarCrate(krate) => {
if krate == self.krate {
cov_mark::hit!(macro_dollar_crate_self);
- PerNs::types(self.crate_root().into(), Visibility::Public)
+ PerNs::types(self.crate_root().into(), Visibility::Public, None)
} else {
let def_map = db.crate_def_map(krate);
let module = def_map.module_id(Self::ROOT);
cov_mark::hit!(macro_dollar_crate_other);
- PerNs::types(module.into(), Visibility::Public)
+ PerNs::types(module.into(), Visibility::Public, None)
}
}
- PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public),
+ PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public, None),
// plain import or absolute path in 2015: crate-relative with
// fallback to extern prelude (with the simplification in
// rust-lang/rust#57745)
@@ -291,7 +292,7 @@ impl DefMap {
);
}
- PerNs::types(module.into(), Visibility::Public)
+ PerNs::types(module.into(), Visibility::Public, None)
}
PathKind::Abs => {
// 2018-style absolute path -- only extern prelude
@@ -299,9 +300,13 @@ impl DefMap {
Some((_, segment)) => segment,
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
};
- if let Some(&def) = self.data.extern_prelude.get(segment) {
+ if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) {
tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
- PerNs::types(def.into(), Visibility::Public)
+ PerNs::types(
+ def.into(),
+ Visibility::Public,
+ extern_crate.map(ImportOrExternCrate::ExternCrate),
+ )
} else {
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
}
@@ -309,7 +314,7 @@ impl DefMap {
};
for (i, segment) in segments {
- let (curr, vis) = match curr_per_ns.take_types_vis() {
+ let (curr, vis, imp) = match curr_per_ns.take_types_full() {
Some(r) => r,
None => {
// we still have path segments left, but the path so far
@@ -364,18 +369,20 @@ impl DefMap {
Some(local_id) => {
let variant = EnumVariantId { parent: e, local_id };
match &*enum_data.variants[local_id].variant_data {
- crate::data::adt::VariantData::Record(_) => {
- PerNs::types(variant.into(), Visibility::Public)
- }
- crate::data::adt::VariantData::Tuple(_)
- | crate::data::adt::VariantData::Unit => {
- PerNs::both(variant.into(), variant.into(), Visibility::Public)
+ VariantData::Record(_) => {
+ PerNs::types(variant.into(), Visibility::Public, None)
}
+ VariantData::Tuple(_) | VariantData::Unit => PerNs::both(
+ variant.into(),
+ variant.into(),
+ Visibility::Public,
+ None,
+ ),
}
}
None => {
return ResolvePathResult::with(
- PerNs::types(e.into(), vis),
+ PerNs::types(e.into(), vis, imp),
ReachedFixedPoint::Yes,
Some(i),
Some(self.krate),
@@ -393,7 +400,7 @@ impl DefMap {
);
return ResolvePathResult::with(
- PerNs::types(s, vis),
+ PerNs::types(s, vis, imp),
ReachedFixedPoint::Yes,
Some(i),
Some(self.krate),
@@ -430,7 +437,7 @@ impl DefMap {
.filter(|&id| {
sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns)
})
- .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
+ .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public, None));
let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns);
let from_builtin = match self.block {
Some(_) => {
@@ -449,18 +456,27 @@ impl DefMap {
let extern_prelude = || {
if self.block.is_some() {
- // Don't resolve extern prelude in block `DefMap`s.
+ // Don't resolve extern prelude in block `DefMap`s, defer it to the crate def map so
+ // that blocks can properly shadow them
return PerNs::none();
}
- self.data
- .extern_prelude
- .get(name)
- .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
+ self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
+ PerNs::types(
+ it.into(),
+ Visibility::Public,
+ extern_crate.map(ImportOrExternCrate::ExternCrate),
+ )
+ })
};
let macro_use_prelude = || {
- self.macro_use_prelude
- .get(name)
- .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
+ self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| {
+ PerNs::macros(
+ it.into(),
+ Visibility::Public,
+ // FIXME?
+ None, // extern_crate.map(ImportOrExternCrate::ExternCrate),
+ )
+ })
};
let prelude = || self.resolve_in_prelude(db, name);
@@ -488,18 +504,23 @@ impl DefMap {
// Don't resolve extern prelude in block `DefMap`s.
return PerNs::none();
}
- self.data
- .extern_prelude
- .get(name)
- .copied()
- .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
+ self.data.extern_prelude.get(name).copied().map_or(
+ PerNs::none(),
+ |(it, extern_crate)| {
+ PerNs::types(
+ it.into(),
+ Visibility::Public,
+ extern_crate.map(ImportOrExternCrate::ExternCrate),
+ )
+ },
+ )
};
from_crate_root.or_else(from_extern_prelude)
}
fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
- if let Some(prelude) = self.prelude {
+ if let Some((prelude, _use)) = self.prelude {
let keep;
let def_map = if prelude.krate == self.krate {
self
diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs
index dd7c3c3630..e7cc44b04d 100644
--- a/crates/hir-def/src/nameres/tests.rs
+++ b/crates/hir-def/src/nameres/tests.rs
@@ -168,7 +168,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Foo: t v
+ Foo: ti vi
foo: t
crate::foo
@@ -194,8 +194,8 @@ pub enum Quux {};
"#,
expect![[r#"
crate
- Baz: t v
- Quux: t
+ Baz: ti vi
+ Quux: ti
foo: t
crate::foo
@@ -225,11 +225,11 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Baz: t v
+ Baz: ti vi
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -274,7 +274,7 @@ use self::E::V;
expect![[r#"
crate
E: t
- V: t v
+ V: ti vi
"#]],
);
}
@@ -307,7 +307,7 @@ pub struct FromLib;
crate::foo
Bar: _
- FromLib: t v
+ FromLib: ti vi
"#]],
);
}
@@ -328,7 +328,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Baz: t
+ Baz: ti
foo: t
crate::foo
@@ -352,7 +352,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Baz: t v
+ Baz: ti vi
"#]],
);
}
@@ -375,13 +375,13 @@ pub struct Arc;
expect![[r#"
crate
alloc: t
- alloc_crate: t
+ alloc_crate: te
sync: t
crate::alloc
crate::sync
- Arc: t v
+ Arc: ti vi
"#]],
);
}
@@ -404,13 +404,13 @@ pub struct Arc;
expect![[r#"
crate
alloc: t
- alloc_crate: t
+ alloc_crate: te
sync: t
crate::alloc
crate::sync
- Arc: t v
+ Arc: ti vi
"#]],
);
}
@@ -426,7 +426,7 @@ extern crate self as bla;
"#,
expect![[r#"
crate
- bla: t
+ bla: te
"#]],
);
}
@@ -447,7 +447,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Baz: t v
+ Baz: ti vi
"#]],
);
}
@@ -465,7 +465,7 @@ pub struct Bar;
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
foo: v
"#]],
);
@@ -492,9 +492,9 @@ fn no_std_prelude() {
}
"#,
expect![[r#"
- crate
- Rust: t v
- "#]],
+ crate
+ Rust: ti vi
+ "#]],
);
}
@@ -516,9 +516,9 @@ fn edition_specific_preludes() {
}
"#,
expect![[r#"
- crate
- Rust2018: t v
- "#]],
+ crate
+ Rust2018: ti vi
+ "#]],
);
check(
r#"
@@ -533,9 +533,9 @@ fn edition_specific_preludes() {
}
"#,
expect![[r#"
- crate
- Rust2021: t v
- "#]],
+ crate
+ Rust2021: ti vi
+ "#]],
);
}
@@ -563,8 +563,8 @@ pub mod prelude {
"#,
expect![[r#"
crate
- Bar: t v
- Foo: t v
+ Bar: ti vi
+ Foo: ti vi
"#]],
);
}
@@ -590,7 +590,7 @@ pub mod prelude {
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
Baz: _
Foo: _
"#]],
@@ -619,8 +619,8 @@ pub mod prelude {
expect![[r#"
crate
Bar: _
- Baz: t v
- Foo: t v
+ Baz: ti vi
+ Foo: ti vi
"#]],
);
}
@@ -643,7 +643,7 @@ mod b {
"#,
expect![[r#"
crate
- T: t v
+ T: ti vi
a: t
b: t
@@ -816,8 +816,8 @@ fn bar() {}
expect![[r#"
crate
bar: v
- baz: v
- foo: t
+ baz: vi
+ foo: ti
"#]],
);
}
@@ -836,7 +836,7 @@ use self::m::S::{self};
"#,
expect![[r#"
crate
- S: t
+ S: ti
m: t
crate::m
@@ -860,8 +860,8 @@ pub const settings: () = ();
"#,
expect![[r#"
crate
- Settings: t v
- settings: v
+ Settings: ti vi
+ settings: vi
"#]],
)
}
@@ -890,8 +890,8 @@ pub struct Struct;
"#,
expect![[r#"
crate
- Struct: t v
- dep: t
+ Struct: ti vi
+ dep: te
"#]],
);
}
@@ -917,13 +917,13 @@ use some_module::unknown_func;
crate
other_module: t
some_module: t
- unknown_func: v
+ unknown_func: vi
crate::other_module
some_submodule: t
crate::other_module::some_submodule
- unknown_func: v
+ unknown_func: vi
crate::some_module
unknown_func: v
diff --git a/crates/hir-def/src/nameres/tests/globs.rs b/crates/hir-def/src/nameres/tests/globs.rs
index 88a3c76393..1ca74b5da6 100644
--- a/crates/hir-def/src/nameres/tests/globs.rs
+++ b/crates/hir-def/src/nameres/tests/globs.rs
@@ -24,7 +24,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
Foo: t v
bar: t
@@ -237,9 +237,9 @@ pub mod baz { pub struct Bar; }
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
bar: t
- baz: t
+ baz: ti
foo: t
crate::bar
@@ -276,9 +276,9 @@ pub mod baz { pub struct Bar; }
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
bar: t
- baz: t
+ baz: ti
foo: t
crate::bar
@@ -323,7 +323,7 @@ mod d {
X: t v
crate::b
- foo: t
+ foo: ti
crate::c
foo: t
@@ -332,8 +332,8 @@ mod d {
Y: t v
crate::d
- Y: t v
- foo: t
+ Y: ti vi
+ foo: ti
"#]],
);
}
@@ -355,7 +355,7 @@ use event::Event;
"#,
expect![[r#"
crate
- Event: t
+ Event: ti
event: t
crate::event
diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs
index 40d3a16540..4a86f88e57 100644
--- a/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -212,7 +212,7 @@ pub type Ty = ();
}
for (_, res) in module_data.scope.resolutions() {
- match res.values.or(res.types).unwrap().0 {
+ match res.values.map(|(a, _, _)| a).or(res.types.map(|(a, _, _)| a)).unwrap() {
ModuleDefId::FunctionId(f) => _ = db.function_data(f),
ModuleDefId::AdtId(adt) => match adt {
AdtId::StructId(it) => _ = db.struct_data(it),
diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs
index f4cca8d68d..e64fa0b46f 100644
--- a/crates/hir-def/src/nameres/tests/macros.rs
+++ b/crates/hir-def/src/nameres/tests/macros.rs
@@ -203,8 +203,8 @@ macro_rules! bar {
expect![[r#"
crate
Foo: t
- bar: m
- foo: m
+ bar: mi
+ foo: mi
"#]],
);
}
@@ -251,7 +251,7 @@ mod priv_mod {
Bar: t v
Foo: t v
bar: t
- foo: t
+ foo: te
crate::bar
Baz: t v
@@ -318,9 +318,9 @@ macro_rules! baz3 { () => { struct OkBaz3; } }
OkBaz1: t v
OkBaz2: t v
OkBaz3: t v
- all: t
- empty: t
- multiple: t
+ all: te
+ empty: te
+ multiple: te
"#]],
);
}
@@ -551,8 +551,8 @@ fn baz() {}
"#,
expect![[r#"
crate
- bar: t m
- baz: t v m
+ bar: ti mi
+ baz: ti v mi
foo: t m
"#]],
);
@@ -583,7 +583,7 @@ mod m {
crate
Alias: t v
Direct: t v
- foo: t
+ foo: te
"#]],
);
}
@@ -628,9 +628,9 @@ mod m {
m: t
crate::m
- alias1: m
- alias2: m
- alias3: m
+ alias1: mi
+ alias2: mi
+ alias3: mi
not_found: _
"#]],
);
@@ -682,11 +682,11 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Bar: t v
- Baz: t v
+ Bar: ti vi
+ Baz: ti vi
Foo: t v
- FooSelf: t v
- foo: t
+ FooSelf: ti vi
+ foo: te
m: t
crate::m
@@ -725,7 +725,7 @@ pub struct bar;
"#,
expect![[r#"
crate
- bar: t v
+ bar: ti vi
"#]],
);
}
@@ -1340,7 +1340,7 @@ pub mod prelude {
crate
Ok: t v
bar: m
- dep: t
+ dep: te
foo: m
ok: v
"#]],
@@ -1370,13 +1370,13 @@ macro_rules! mk_foo {
}
"#,
expect![[r#"
- crate
- a: t
- lib: t
+ crate
+ a: t
+ lib: te
- crate::a
- Ok: t v
- "#]],
+ crate::a
+ Ok: t v
+ "#]],
);
}
@@ -1427,8 +1427,8 @@ pub mod prelude {
expect![[r#"
crate
Ok: t v
- bar: m
- foo: m
+ bar: mi
+ foo: mi
ok: v
"#]],
);
diff --git a/crates/hir-def/src/nameres/tests/mod_resolution.rs b/crates/hir-def/src/nameres/tests/mod_resolution.rs
index 81bc0ff91e..1327d9aa62 100644
--- a/crates/hir-def/src/nameres/tests/mod_resolution.rs
+++ b/crates/hir-def/src/nameres/tests/mod_resolution.rs
@@ -80,18 +80,18 @@ pub trait Iterator;
prelude: t
crate::iter
- Iterator: t
+ Iterator: ti
traits: t
crate::iter::traits
- Iterator: t
+ Iterator: ti
iterator: t
crate::iter::traits::iterator
Iterator: t
crate::prelude
- Iterator: t
+ Iterator: ti
"#]],
);
}
@@ -109,7 +109,7 @@ pub struct Bar;
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
foo: t
crate::foo
@@ -139,7 +139,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
r#async: t
crate::r#async
@@ -176,8 +176,8 @@ pub struct Bar;
"#,
expect![[r#"
crate
- Bar: t v
- Foo: t v
+ Bar: ti vi
+ Foo: ti vi
r#async: t
crate::r#async
@@ -207,7 +207,7 @@ pub struct Bar;
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
foo: t
crate::foo
@@ -236,7 +236,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -265,7 +265,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -292,7 +292,7 @@ use super::Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
"#]],
);
}
@@ -626,7 +626,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Baz: t v
+ Baz: ti vi
foo: t
crate::foo
@@ -660,7 +660,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -694,7 +694,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -728,7 +728,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -868,7 +868,7 @@ pub mod hash { pub trait Hash {} }
"#,
expect![[r#"
crate
- Hash: t
+ Hash: ti
core: t
crate::core
diff --git a/crates/hir-def/src/nameres/tests/primitives.rs b/crates/hir-def/src/nameres/tests/primitives.rs
index 215e8952d9..271eb1c79b 100644
--- a/crates/hir-def/src/nameres/tests/primitives.rs
+++ b/crates/hir-def/src/nameres/tests/primitives.rs
@@ -14,10 +14,10 @@ pub use i32 as int;
expect![[r#"
crate
foo: t
- int: t
+ int: ti
crate::foo
- int: t
+ int: ti
"#]],
);
}
diff --git a/crates/hir-def/src/per_ns.rs b/crates/hir-def/src/per_ns.rs
index 2bc1f8e926..14890364d0 100644
--- a/crates/hir-def/src/per_ns.rs
+++ b/crates/hir-def/src/per_ns.rs
@@ -3,13 +3,24 @@
//!
//! `PerNs` (per namespace) captures this.
-use crate::{item_scope::ItemInNs, visibility::Visibility, MacroId, ModuleDefId};
+use crate::{
+ item_scope::{ImportId, ImportOrExternCrate, ItemInNs},
+ visibility::Visibility,
+ MacroId, ModuleDefId,
+};
+
+#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
+pub enum Namespace {
+ Types,
+ Values,
+ Macros,
+}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct PerNs {
- pub types: Option<(ModuleDefId, Visibility)>,
- pub values: Option<(ModuleDefId, Visibility)>,
- pub macros: Option<(MacroId, Visibility)>,
+ pub types: Option<(ModuleDefId, Visibility, Option<ImportOrExternCrate>)>,
+ pub values: Option<(ModuleDefId, Visibility, Option<ImportId>)>,
+ pub macros: Option<(MacroId, Visibility, Option<ImportId>)>,
}
impl Default for PerNs {
@@ -23,20 +34,29 @@ impl PerNs {
PerNs { types: None, values: None, macros: None }
}
- pub fn values(t: ModuleDefId, v: Visibility) -> PerNs {
- PerNs { types: None, values: Some((t, v)), macros: None }
+ pub fn values(t: ModuleDefId, v: Visibility, i: Option<ImportId>) -> PerNs {
+ PerNs { types: None, values: Some((t, v, i)), macros: None }
}
- pub fn types(t: ModuleDefId, v: Visibility) -> PerNs {
- PerNs { types: Some((t, v)), values: None, macros: None }
+ pub fn types(t: ModuleDefId, v: Visibility, i: Option<ImportOrExternCrate>) -> PerNs {
+ PerNs { types: Some((t, v, i)), values: None, macros: None }
}
- pub fn both(types: ModuleDefId, values: ModuleDefId, v: Visibility) -> PerNs {
- PerNs { types: Some((types, v)), values: Some((values, v)), macros: None }
+ pub fn both(
+ types: ModuleDefId,
+ values: ModuleDefId,
+ v: Visibility,
+ i: Option<ImportOrExternCrate>,
+ ) -> PerNs {
+ PerNs {
+ types: Some((types, v, i)),
+ values: Some((values, v, i.and_then(ImportOrExternCrate::into_import))),
+ macros: None,
+ }
}
- pub fn macros(macro_: MacroId, v: Visibility) -> PerNs {
- PerNs { types: None, values: None, macros: Some((macro_, v)) }
+ pub fn macros(macro_: MacroId, v: Visibility, i: Option<ImportId>) -> PerNs {
+ PerNs { types: None, values: None, macros: Some((macro_, v, i)) }
}
pub fn is_none(&self) -> bool {
@@ -51,7 +71,7 @@ impl PerNs {
self.types.map(|it| it.0)
}
- pub fn take_types_vis(self) -> Option<(ModuleDefId, Visibility)> {
+ pub fn take_types_full(self) -> Option<(ModuleDefId, Visibility, Option<ImportOrExternCrate>)> {
self.types
}
@@ -59,24 +79,32 @@ impl PerNs {
self.values.map(|it| it.0)
}
+ pub fn take_values_import(self) -> Option<(ModuleDefId, Option<ImportId>)> {
+ self.values.map(|it| (it.0, it.2))
+ }
+
pub fn take_macros(self) -> Option<MacroId> {
self.macros.map(|it| it.0)
}
+ pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportId>)> {
+ self.macros.map(|it| (it.0, it.2))
+ }
+
pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs {
let _p = profile::span("PerNs::filter_visibility");
PerNs {
- types: self.types.filter(|(_, v)| f(*v)),
- values: self.values.filter(|(_, v)| f(*v)),
- macros: self.macros.filter(|(_, v)| f(*v)),
+ types: self.types.filter(|&(_, v, _)| f(v)),
+ values: self.values.filter(|&(_, v, _)| f(v)),
+ macros: self.macros.filter(|&(_, v, _)| f(v)),
}
}
pub fn with_visibility(self, vis: Visibility) -> PerNs {
PerNs {
- types: self.types.map(|(it, _)| (it, vis)),
- values: self.values.map(|(it, _)| (it, vis)),
- macros: self.macros.map(|(it, _)| (it, vis)),
+ types: self.types.map(|(it, _, c)| (it, vis, c)),
+ values: self.values.map(|(it, _, c)| (it, vis, c)),
+ macros: self.macros.map(|(it, _, import)| (it, vis, import)),
}
}
@@ -96,12 +124,20 @@ impl PerNs {
}
}
- pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
+ pub fn iter_items(self) -> impl Iterator<Item = (ItemInNs, Option<ImportOrExternCrate>)> {
let _p = profile::span("PerNs::iter_items");
self.types
- .map(|it| ItemInNs::Types(it.0))
+ .map(|it| (ItemInNs::Types(it.0), it.2))
.into_iter()
- .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter())
- .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter())
+ .chain(
+ self.values
+ .map(|it| (ItemInNs::Values(it.0), it.2.map(ImportOrExternCrate::Import)))
+ .into_iter(),
+ )
+ .chain(
+ self.macros
+ .map(|it| (ItemInNs::Macros(it.0), it.2.map(ImportOrExternCrate::Import)))
+ .into_iter(),
+ )
}
}
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index b112c1070d..2f9187009e 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -12,20 +12,21 @@ use triomphe::Arc;
use crate::{
body::scope::{ExprScopes, ScopeId},
builtin_type::BuiltinType,
+ data::ExternCrateDeclData,
db::DefDatabase,
generics::{GenericParams, TypeOrConstParamData},
hir::{BindingId, ExprId, LabelId},
- item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
+ item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE},
lang_item::LangItemTarget,
nameres::{DefMap, MacroSubNs},
path::{ModPath, Path, PathKind},
per_ns::PerNs,
visibility::{RawVisibility, Visibility},
- AdtId, AssocItemId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId,
- EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId,
- HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId,
- MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId,
- TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId,
+ AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
+ ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
+ ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId,
+ ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
+ TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId,
};
#[derive(Debug, Clone)]
@@ -100,8 +101,8 @@ pub enum TypeNs {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ResolveValueResult {
- ValueNs(ValueNs),
- Partial(TypeNs, usize),
+ ValueNs(ValueNs, Option<ImportId>),
+ Partial(TypeNs, usize, Option<ImportOrExternCrate>),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -148,39 +149,11 @@ impl Resolver {
self.resolve_module_path(db, path, BuiltinShadowMode::Module)
}
- // FIXME: This shouldn't exist
- pub fn resolve_module_path_in_trait_assoc_items(
- &self,
- db: &dyn DefDatabase,
- path: &ModPath,
- ) -> Option<PerNs> {
- let (item_map, module) = self.item_scope();
- let (module_res, idx) =
- item_map.resolve_path(db, module, path, BuiltinShadowMode::Module, None);
- match module_res.take_types()? {
- ModuleDefId::TraitId(it) => {
- let idx = idx?;
- let unresolved = &path.segments()[idx..];
- let assoc = match unresolved {
- [it] => it,
- _ => return None,
- };
- let &(_, assoc) = db.trait_data(it).items.iter().find(|(n, _)| n == assoc)?;
- Some(match assoc {
- AssocItemId::FunctionId(it) => PerNs::values(it.into(), Visibility::Public),
- AssocItemId::ConstId(it) => PerNs::values(it.into(), Visibility::Public),
- AssocItemId::TypeAliasId(it) => PerNs::types(it.into(), Visibility::Public),
- })
- }
- _ => None,
- }
- }
-
pub fn resolve_path_in_type_ns(
&self,
db: &dyn DefDatabase,
path: &Path,
- ) -> Option<(TypeNs, Option<usize>)> {
+ ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
let path = match path {
Path::Normal { mod_path, .. } => mod_path,
Path::LangItem(l) => {
@@ -197,6 +170,7 @@ impl Resolver {
| LangItemTarget::Static(_) => return None,
},
None,
+ None,
))
}
};
@@ -213,17 +187,17 @@ impl Resolver {
Scope::ExprScope(_) => continue,
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_type_by_name(first_name, *def) {
- return Some((TypeNs::GenericParam(id), remaining_idx()));
+ return Some((TypeNs::GenericParam(id), remaining_idx(), None));
}
}
&Scope::ImplDefScope(impl_) => {
if first_name == &name![Self] {
- return Some((TypeNs::SelfType(impl_), remaining_idx()));
+ return Some((TypeNs::SelfType(impl_), remaining_idx(), None));
}
}
&Scope::AdtScope(adt) => {
if first_name == &name![Self] {
- return Some((TypeNs::AdtSelfType(adt), remaining_idx()));
+ return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None));
}
}
Scope::BlockScope(m) => {
@@ -236,12 +210,24 @@ impl Resolver {
self.module_scope.resolve_path_in_type_ns(db, path)
}
+ pub fn resolve_path_in_type_ns_fully_with_imports(
+ &self,
+ db: &dyn DefDatabase,
+ path: &Path,
+ ) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
+ let (res, unresolved, imp) = self.resolve_path_in_type_ns(db, path)?;
+ if unresolved.is_some() {
+ return None;
+ }
+ Some((res, imp))
+ }
+
pub fn resolve_path_in_type_ns_fully(
&self,
db: &dyn DefDatabase,
path: &Path,
) -> Option<TypeNs> {
- let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
+ let (res, unresolved, _) = self.resolve_path_in_type_ns(db, path)?;
if unresolved.is_some() {
return None;
}
@@ -263,7 +249,6 @@ impl Resolver {
RawVisibility::Public => Some(Visibility::Public),
}
}
-
pub fn resolve_path_in_value_ns(
&self,
db: &dyn DefDatabase,
@@ -272,17 +257,20 @@ impl Resolver {
let path = match path {
Path::Normal { mod_path, .. } => mod_path,
Path::LangItem(l) => {
- return Some(ResolveValueResult::ValueNs(match *l {
- LangItemTarget::Function(it) => ValueNs::FunctionId(it),
- LangItemTarget::Static(it) => ValueNs::StaticId(it),
- LangItemTarget::Struct(it) => ValueNs::StructId(it),
- LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
- LangItemTarget::Union(_)
- | LangItemTarget::ImplDef(_)
- | LangItemTarget::TypeAlias(_)
- | LangItemTarget::Trait(_)
- | LangItemTarget::EnumId(_) => return None,
- }))
+ return Some(ResolveValueResult::ValueNs(
+ match *l {
+ LangItemTarget::Function(it) => ValueNs::FunctionId(it),
+ LangItemTarget::Static(it) => ValueNs::StaticId(it),
+ LangItemTarget::Struct(it) => ValueNs::StructId(it),
+ LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
+ LangItemTarget::Union(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::TypeAlias(_)
+ | LangItemTarget::Trait(_)
+ | LangItemTarget::EnumId(_) => return None,
+ },
+ None,
+ ))
}
};
let n_segments = path.segments().len();
@@ -304,20 +292,24 @@ impl Resolver {
.find(|entry| entry.name() == first_name);
if let Some(e) = entry {
- return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(
- e.binding(),
- )));
+ return Some(ResolveValueResult::ValueNs(
+ ValueNs::LocalBinding(e.binding()),
+ None,
+ ));
}
}
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_const_by_name(first_name, *def) {
let val = ValueNs::GenericParam(id);
- return Some(ResolveValueResult::ValueNs(val));
+ return Some(ResolveValueResult::ValueNs(val, None));
}
}
&Scope::ImplDefScope(impl_) => {
if first_name == &name![Self] {
- return Some(ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_)));
+ return Some(ResolveValueResult::ValueNs(
+ ValueNs::ImplSelf(impl_),
+ None,
+ ));
}
}
// bare `Self` doesn't work in the value namespace in a struct/enum definition
@@ -336,18 +328,22 @@ impl Resolver {
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_type_by_name(first_name, *def) {
let ty = TypeNs::GenericParam(id);
- return Some(ResolveValueResult::Partial(ty, 1));
+ return Some(ResolveValueResult::Partial(ty, 1, None));
}
}
&Scope::ImplDefScope(impl_) => {
if first_name == &name![Self] {
- return Some(ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1));
+ return Some(ResolveValueResult::Partial(
+ TypeNs::SelfType(impl_),
+ 1,
+ None,
+ ));
}
}
Scope::AdtScope(adt) => {
if first_name == &name![Self] {
let ty = TypeNs::AdtSelfType(*adt);
- return Some(ResolveValueResult::Partial(ty, 1));
+ return Some(ResolveValueResult::Partial(ty, 1, None));
}
}
Scope::BlockScope(m) => {
@@ -368,7 +364,7 @@ impl Resolver {
// `use core::u16;`.
if path.kind == PathKind::Plain && n_segments > 1 {
if let Some(builtin) = BuiltinType::by_name(first_name) {
- return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1));
+ return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None));
}
}
@@ -381,7 +377,7 @@ impl Resolver {
path: &Path,
) -> Option<ValueNs> {
match self.resolve_path_in_value_ns(db, path)? {
- ResolveValueResult::ValueNs(it) => Some(it),
+ ResolveValueResult::ValueNs(it, _) => Some(it),
ResolveValueResult::Partial(..) => None,
}
}
@@ -391,12 +387,12 @@ impl Resolver {
db: &dyn DefDatabase,
path: &ModPath,
expected_macro_kind: Option<MacroSubNs>,
- ) -> Option<MacroId> {
+ ) -> Option<(MacroId, Option<ImportId>)> {
let (item_map, module) = self.item_scope();
item_map
.resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
.0
- .take_macros()
+ .take_macros_import()
}
/// Returns a set of names available in the current scope.
@@ -456,21 +452,22 @@ impl Resolver {
def_map[module_id].scope.entries().for_each(|(name, def)| {
res.add_per_ns(name, def);
});
+
def_map[module_id].scope.legacy_macros().for_each(|(name, macs)| {
macs.iter().for_each(|&mac| {
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
})
});
- def_map.macro_use_prelude().for_each(|(name, def)| {
+ def_map.macro_use_prelude().for_each(|(name, (def, _extern_crate))| {
res.add(name, ScopeDef::ModuleDef(def.into()));
});
- def_map.extern_prelude().for_each(|(name, def)| {
- res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
+ def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| {
+ res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into())));
});
BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
res.add_per_ns(name, def);
});
- if let Some(prelude) = def_map.prelude() {
+ if let Some((prelude, _use)) = def_map.prelude() {
let prelude_def_map = prelude.def_map(db);
for (name, def) in prelude_def_map[prelude.local_id].scope.entries() {
res.add_per_ns(name, def)
@@ -479,6 +476,23 @@ impl Resolver {
res.map
}
+ pub fn extern_crate_decls_in_scope<'a>(
+ &'a self,
+ db: &'a dyn DefDatabase,
+ ) -> impl Iterator<Item = Name> + 'a {
+ self.module_scope.def_map[self.module_scope.module_id]
+ .scope
+ .extern_crate_decls()
+ .map(|id| ExternCrateDeclData::extern_crate_decl_data_query(db, id).name.clone())
+ }
+
+ pub fn extern_crates_in_scope<'a>(&'a self) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
+ self.module_scope
+ .def_map
+ .extern_prelude()
+ .map(|(name, module_id)| (name.clone(), module_id.0.into()))
+ }
+
pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
// FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of
// aliased traits are NOT brought in scope (unless also aliased).
@@ -501,7 +515,7 @@ impl Resolver {
}
// Fill in the prelude traits
- if let Some(prelude) = self.module_scope.def_map.prelude() {
+ if let Some((prelude, _use)) = self.module_scope.def_map.prelude() {
let prelude_def_map = prelude.def_map(db);
traits.extend(prelude_def_map[prelude.local_id].scope.traits());
}
@@ -804,11 +818,12 @@ impl ModuleItemMap {
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
match idx {
None => {
- let value = to_value_ns(module_def)?;
- Some(ResolveValueResult::ValueNs(value))
+ let (value, import) = to_value_ns(module_def)?;
+ Some(ResolveValueResult::ValueNs(value, import))
}
Some(idx) => {
- let ty = match module_def.take_types()? {
+ let (def, _, import) = module_def.take_types_full()?;
+ let ty = match def {
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
@@ -822,7 +837,7 @@ impl ModuleItemMap {
| ModuleDefId::MacroId(_)
| ModuleDefId::StaticId(_) => return None,
};
- Some(ResolveValueResult::Partial(ty, idx))
+ Some(ResolveValueResult::Partial(ty, idx, import))
}
}
}
@@ -831,16 +846,17 @@ impl ModuleItemMap {
&self,
db: &dyn DefDatabase,
path: &ModPath,
- ) -> Option<(TypeNs, Option<usize>)> {
+ ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
let (module_def, idx) =
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
- let res = to_type_ns(module_def)?;
- Some((res, idx))
+ let (res, import) = to_type_ns(module_def)?;
+ Some((res, idx, import))
}
}
-fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
- let res = match per_ns.take_values()? {
+fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportId>)> {
+ let (def, import) = per_ns.take_values_import()?;
+ let res = match def {
ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it),
ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it),
@@ -855,11 +871,12 @@ fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
| ModuleDefId::MacroId(_)
| ModuleDefId::ModuleId(_) => return None,
};
- Some(res)
+ Some((res, import))
}
-fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
- let res = match per_ns.take_types()? {
+fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
+ let (def, _, import) = per_ns.take_types_full()?;
+ let res = match def {
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it),
@@ -875,7 +892,7 @@ fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
| ModuleDefId::StaticId(_)
| ModuleDefId::ModuleId(_) => return None,
};
- Some(res)
+ Some((res, import))
}
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>;
@@ -892,13 +909,13 @@ impl ScopeNames {
}
}
fn add_per_ns(&mut self, name: &Name, def: PerNs) {
- if let &Some((ty, _)) = &def.types {
+ if let &Some((ty, _, _)) = &def.types {
self.add(name, ScopeDef::ModuleDef(ty))
}
- if let &Some((def, _)) = &def.values {
+ if let &Some((def, _, _)) = &def.values {
self.add(name, ScopeDef::ModuleDef(def))
}
- if let &Some((mac, _)) = &def.macros {
+ if let &Some((mac, _, _)) = &def.macros {
self.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)))
}
if def.is_none() {
diff --git a/crates/hir-def/src/src.rs b/crates/hir-def/src/src.rs
index 6047f770d4..3770103cda 100644
--- a/crates/hir-def/src/src.rs
+++ b/crates/hir-def/src/src.rs
@@ -5,8 +5,8 @@ use la_arena::ArenaMap;
use syntax::ast;
use crate::{
- db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Macro2Loc, MacroRulesLoc,
- ProcMacroLoc,
+ db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Lookup, Macro2Loc,
+ MacroRulesLoc, ProcMacroLoc, UseId,
};
pub trait HasSource {
@@ -83,3 +83,18 @@ pub trait HasChildSource<ChildId> {
type Value;
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<ChildId, Self::Value>>;
}
+
+impl HasChildSource<la_arena::Idx<ast::UseTree>> for UseId {
+ type Value = ast::UseTree;
+ fn child_source(
+ &self,
+ db: &dyn DefDatabase,
+ ) -> InFile<ArenaMap<la_arena::Idx<ast::UseTree>, Self::Value>> {
+ let loc = &self.lookup(db);
+ let use_ = &loc.id.item_tree(db)[loc.id.value];
+ InFile::new(
+ loc.id.file_id(),
+ use_.use_tree_source_map(db, loc.id.file_id()).into_iter().collect(),
+ )
+ }
+}
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index 4c918e55b9..0ec2422b30 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -342,14 +342,7 @@ fn inner_attributes(
ast::Impl(it) => it.assoc_item_list()?.syntax().clone(),
ast::Module(it) => it.item_list()?.syntax().clone(),
ast::BlockExpr(it) => {
- use syntax::SyntaxKind::{BLOCK_EXPR , EXPR_STMT};
- // Block expressions accept outer and inner attributes, but only when they are the outer
- // expression of an expression statement or the final expression of another block expression.
- let may_carry_attributes = matches!(
- it.syntax().parent().map(|it| it.kind()),
- Some(BLOCK_EXPR | EXPR_STMT)
- );
- if !may_carry_attributes {
+ if !it.may_carry_attributes() {
return None
}
syntax.clone()
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 1f1e20f49e..4be55126b8 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -37,7 +37,7 @@ use either::Either;
use syntax::{
algo::{self, skip_trivia_token},
ast::{self, AstNode, HasDocComments},
- AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken,
+ AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize,
};
use crate::{
@@ -544,7 +544,7 @@ impl MacroCallKind {
};
let range = match kind {
- MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
+ MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(),
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
// FIXME: should be the range of the macro name, not the whole derive
// FIXME: handle `cfg_attr`
@@ -642,6 +642,8 @@ impl ExpansionInfo {
db: &dyn db::ExpandDatabase,
item: Option<ast::Item>,
token: InFile<&SyntaxToken>,
+ // FIXME: use this for range mapping, so that we can resolve inline format args
+ _relative_token_offset: Option<TextSize>,
) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> {
assert_eq!(token.file_id, self.arg.file_id);
let token_id_in_attr_input = if let Some(item) = item {
@@ -840,9 +842,6 @@ impl<N: AstIdNode> AstId<N> {
pub type ErasedAstId = InFile<ErasedFileAstId>;
impl ErasedAstId {
- pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
- self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
- }
pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr {
db.ast_id_map(self.file_id).get_raw(self.value)
}
@@ -1054,16 +1053,6 @@ impl InFile<SyntaxToken> {
}
}
}
-
- pub fn ancestors_with_macros(
- self,
- db: &dyn db::ExpandDatabase,
- ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
- self.value.parent().into_iter().flat_map({
- let file_id = self.file_id;
- move |parent| InFile::new(file_id, &parent).ancestors_with_macros(db)
- })
- }
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs
index eec57ba3f8..967e028bfb 100644
--- a/crates/hir-ty/src/builder.rs
+++ b/crates/hir-ty/src/builder.rs
@@ -17,7 +17,8 @@ use smallvec::SmallVec;
use crate::{
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
- GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
+ GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt,
+ TyKind,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -79,9 +80,9 @@ impl<D> TyBuilder<D> {
let expected_kind = &self.param_kinds[self.vec.len()];
let arg_kind = match arg.data(Interner) {
- chalk_ir::GenericArgData::Ty(_) => ParamKind::Type,
- chalk_ir::GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"),
- chalk_ir::GenericArgData::Const(c) => {
+ GenericArgData::Ty(_) => ParamKind::Type,
+ GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"),
+ GenericArgData::Const(c) => {
let c = c.data(Interner);
ParamKind::Const(c.ty.clone())
}
@@ -139,8 +140,8 @@ impl<D> TyBuilder<D> {
fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) {
match (a.data(Interner), e) {
- (chalk_ir::GenericArgData::Ty(_), ParamKind::Type)
- | (chalk_ir::GenericArgData::Const(_), ParamKind::Const(_)) => (),
+ (GenericArgData::Ty(_), ParamKind::Type)
+ | (GenericArgData::Const(_), ParamKind::Const(_)) => (),
_ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds),
}
}
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 1c0f7b08da..0348680e5d 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -1,7 +1,7 @@
//! Constant evaluation details
use base_db::CrateId;
-use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
+use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
use hir_def::{
hir::Expr,
path::Path,
@@ -120,7 +120,7 @@ pub fn unknown_const(ty: Ty) -> Const {
}
pub fn unknown_const_as_generic(ty: Ty) -> GenericArg {
- GenericArgData::Const(unknown_const(ty)).intern(Interner)
+ unknown_const(ty).cast(Interner)
}
/// Interns a constant scalar with the given type
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 666955fa1c..7ad3659a4f 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1203,6 +1203,27 @@ fn destructing_assignment() {
"#,
5,
);
+ check_number(
+ r#"
+ const GOAL: u8 = {
+ let (mut a, mut b) = (2, 5);
+ (a, b) = (b, a);
+ a * 10 + b
+ };
+ "#,
+ 52,
+ );
+ check_number(
+ r#"
+ struct Point { x: i32, y: i32 }
+ const GOAL: i32 = {
+ let mut p = Point { x: 5, y: 6 };
+ (p.x, _) = (p.y, p.x);
+ p.x * 10 + p.y
+ };
+ "#,
+ 66,
+ );
}
#[test]
@@ -1433,6 +1454,30 @@ fn from_trait() {
}
#[test]
+fn closure_clone() {
+ check_number(
+ r#"
+//- minicore: clone, fn
+struct S(u8);
+
+impl Clone for S(u8) {
+ fn clone(&self) -> S {
+ S(self.0 + 5)
+ }
+}
+
+const GOAL: u8 = {
+ let s = S(3);
+ let cl = move || s;
+ let cl = cl.clone();
+ cl().0
+}
+ "#,
+ 8,
+ );
+}
+
+#[test]
fn builtin_derive_macro() {
check_number(
r#"
@@ -2396,14 +2441,14 @@ fn const_loop() {
fn const_transfer_memory() {
check_number(
r#"
- //- minicore: slice, index, coerce_unsized
+ //- minicore: slice, index, coerce_unsized, option
const A1: &i32 = &1;
const A2: &i32 = &10;
const A3: [&i32; 3] = [&1, &2, &100];
- const A4: (i32, &i32) = (1, &1000);
- const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1;
+ const A4: (i32, &i32, Option<&i32>) = (1, &1000, Some(&10000));
+ const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1 + *A4.2.unwrap_or(&5);
"#,
- 1111,
+ 11111,
);
}
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 9f9a56ffab..cbca0e801d 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -75,7 +75,7 @@ fn walk_unsafe(
Expr::Path(path) => {
let resolver = resolver_for_expr(db.upcast(), def, current);
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
- if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
+ if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
if db.static_data(id).mutable {
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
}
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 1b4ee4613d..f6d6b00d74 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -1809,6 +1809,25 @@ impl HirDisplay for Path {
}
}
+ // Convert trait's `Self` bound back to the surface syntax. Note there is no associated
+ // trait, so there can only be one path segment that `has_self_type`. The `Self` type
+ // itself can contain further qualified path through, which will be handled by recursive
+ // `hir_fmt`s.
+ //
+ // `trait_mod::Trait<Self = type_mod::Type, Args>::Assoc`
+ // =>
+ // `<type_mod::Type as trait_mod::Trait<Args>>::Assoc`
+ let trait_self_ty = self.segments().iter().find_map(|seg| {
+ let generic_args = seg.args_and_bindings?;
+ generic_args.has_self_type.then(|| &generic_args.args[0])
+ });
+ if let Some(ty) = trait_self_ty {
+ write!(f, "<")?;
+ ty.hir_fmt(f)?;
+ write!(f, " as ")?;
+ // Now format the path of the trait...
+ }
+
for (seg_idx, segment) in self.segments().iter().enumerate() {
if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 {
write!(f, "::")?;
@@ -1840,15 +1859,12 @@ impl HirDisplay for Path {
return Ok(());
}
- write!(f, "<")?;
let mut first = true;
- for arg in generic_args.args.iter() {
+ // Skip the `Self` bound if exists. It's handled outside the loop.
+ for arg in &generic_args.args[generic_args.has_self_type as usize..] {
if first {
first = false;
- if generic_args.has_self_type {
- // FIXME: Convert to `<Ty as Trait>` form.
- write!(f, "Self = ")?;
- }
+ write!(f, "<")?;
} else {
write!(f, ", ")?;
}
@@ -1857,6 +1873,7 @@ impl HirDisplay for Path {
for binding in generic_args.bindings.iter() {
if first {
first = false;
+ write!(f, "<")?;
} else {
write!(f, ", ")?;
}
@@ -1872,9 +1889,20 @@ impl HirDisplay for Path {
}
}
}
- write!(f, ">")?;
+
+ // There may be no generic arguments to print, in case of a trait having only a
+ // single `Self` bound which is converted to `<Ty as Trait>::Assoc`.
+ if !first {
+ write!(f, ">")?;
+ }
+
+ // Current position: `<Ty as Trait<Args>|`
+ if generic_args.has_self_type {
+ write!(f, ">")?;
+ }
}
}
+
Ok(())
}
}
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index b4915dbf0f..0fb4934444 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -1017,7 +1017,7 @@ impl<'a> InferenceContext<'a> {
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
let (resolution, unresolved) = if value_ns {
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
- Some(ResolveValueResult::ValueNs(value)) => match value {
+ Some(ResolveValueResult::ValueNs(value, _)) => match value {
ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
let ty = self.db.ty(var.parent.into());
@@ -1033,12 +1033,14 @@ impl<'a> InferenceContext<'a> {
ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
_ => return (self.err_ty(), None),
},
- Some(ResolveValueResult::Partial(typens, unresolved)) => (typens, Some(unresolved)),
+ Some(ResolveValueResult::Partial(typens, unresolved, _)) => {
+ (typens, Some(unresolved))
+ }
None => return (self.err_ty(), None),
}
} else {
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
- Some(it) => it,
+ Some((it, idx, _)) => (it, idx),
None => return (self.err_ty(), None),
}
};
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 1781f6c58f..23efe616f4 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -322,7 +322,7 @@ impl InferenceContext<'_> {
Expr::Path(p) => {
let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) {
- if let ResolveValueResult::ValueNs(v) = r {
+ if let ResolveValueResult::ValueNs(v, _) = r {
if let ValueNs::LocalBinding(b) = v {
return Some(HirPlace { local: b, projections: vec![] });
}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 8cbdae6252..8b35214108 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -5,9 +5,7 @@ use std::{
mem,
};
-use chalk_ir::{
- cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
-};
+use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind};
use hir_def::{
generics::TypeOrConstParamData,
hir::{
@@ -750,7 +748,7 @@ impl InferenceContext<'_> {
self.resolve_associated_type_with_params(
self_ty,
self.resolve_ops_index_output(),
- &[GenericArgData::Ty(index_ty).intern(Interner)],
+ &[index_ty.cast(Interner)],
)
} else {
self.err_ty()
@@ -1721,16 +1719,13 @@ impl InferenceContext<'_> {
for (id, data) in def_generics.iter().skip(substs.len()) {
match data {
TypeOrConstParamData::TypeParamData(_) => {
- substs.push(GenericArgData::Ty(self.table.new_type_var()).intern(Interner))
- }
- TypeOrConstParamData::ConstParamData(_) => {
- substs.push(
- GenericArgData::Const(self.table.new_const_var(
- self.db.const_param_ty(ConstParamId::from_unchecked(id)),
- ))
- .intern(Interner),
- )
+ substs.push(self.table.new_type_var().cast(Interner))
}
+ TypeOrConstParamData::ConstParamData(_) => substs.push(
+ self.table
+ .new_const_var(self.db.const_param_ty(ConstParamId::from_unchecked(id)))
+ .cast(Interner),
+ ),
}
}
assert_eq!(substs.len(), total_len);
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 79d9e21e79..2a51c84db3 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -61,8 +61,8 @@ impl InferenceContext<'_> {
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
match value_or_partial {
- ResolveValueResult::ValueNs(it) => (it, None),
- ResolveValueResult::Partial(def, remaining_index) => self
+ ResolveValueResult::ValueNs(it, _) => (it, None),
+ ResolveValueResult::Partial(def, remaining_index, _) => self
.resolve_assoc_item(def, path, remaining_index, id)
.map(|(it, substs)| (it, Some(substs)))?,
}
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 0fb71135b4..0a68a9f3b5 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -10,7 +10,6 @@ use chalk_solve::infer::ParameterEnaVariableExt;
use either::Either;
use ena::unify::UnifyKey;
use hir_expand::name;
-use stdx::never;
use triomphe::Arc;
use super::{InferOk, InferResult, InferenceContext, TypeError};
@@ -92,15 +91,10 @@ pub(crate) fn unify(
let vars = Substitution::from_iter(
Interner,
tys.binders.iter(Interner).map(|it| match &it.kind {
- chalk_ir::VariableKind::Ty(_) => {
- GenericArgData::Ty(table.new_type_var()).intern(Interner)
- }
- chalk_ir::VariableKind::Lifetime => {
- GenericArgData::Ty(table.new_type_var()).intern(Interner)
- } // FIXME: maybe wrong?
- chalk_ir::VariableKind::Const(ty) => {
- GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
- }
+ chalk_ir::VariableKind::Ty(_) => table.new_type_var().cast(Interner),
+ // FIXME: maybe wrong?
+ chalk_ir::VariableKind::Lifetime => table.new_type_var().cast(Interner),
+ chalk_ir::VariableKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner),
}),
);
let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
@@ -111,10 +105,10 @@ pub(crate) fn unify(
// default any type vars that weren't unified back to their original bound vars
// (kind of hacky)
let find_var = |iv| {
- vars.iter(Interner).position(|v| match v.interned() {
- chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner),
- chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
- chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
+ vars.iter(Interner).position(|v| match v.data(Interner) {
+ GenericArgData::Ty(ty) => ty.inference_var(Interner),
+ GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
+ GenericArgData::Const(c) => c.inference_var(Interner),
} == Some(iv))
};
let fallback = |iv, kind, default, binder| match kind {
@@ -149,6 +143,9 @@ pub(crate) struct InferenceTable<'a> {
var_unification_table: ChalkInferenceTable,
type_variable_table: Vec<TypeVariableFlags>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
+ /// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on
+ /// temporary allocations.
+ resolve_obligations_buffer: Vec<Canonicalized<InEnvironment<Goal>>>,
}
pub(crate) struct InferenceTableSnapshot {
@@ -165,6 +162,7 @@ impl<'a> InferenceTable<'a> {
var_unification_table: ChalkInferenceTable::new(),
type_variable_table: Vec::new(),
pending_obligations: Vec::new(),
+ resolve_obligations_buffer: Vec::new(),
}
}
@@ -516,10 +514,10 @@ impl<'a> InferenceTable<'a> {
pub(crate) fn resolve_obligations_as_possible(&mut self) {
let _span = profile::span("resolve_obligations_as_possible");
let mut changed = true;
- let mut obligations = Vec::new();
- while changed {
- changed = false;
+ let mut obligations = mem::take(&mut self.resolve_obligations_buffer);
+ while mem::take(&mut changed) {
mem::swap(&mut self.pending_obligations, &mut obligations);
+
for canonicalized in obligations.drain(..) {
if !self.check_changed(&canonicalized) {
self.pending_obligations.push(canonicalized);
@@ -534,6 +532,8 @@ impl<'a> InferenceTable<'a> {
self.register_obligation_in_env(uncanonical);
}
}
+ self.resolve_obligations_buffer = obligations;
+ self.resolve_obligations_buffer.clear();
}
pub(crate) fn fudge_inference<T: TypeFoldable<Interner>>(
@@ -611,9 +611,9 @@ impl<'a> InferenceTable<'a> {
fn check_changed(&mut self, canonicalized: &Canonicalized<InEnvironment<Goal>>) -> bool {
canonicalized.free_vars.iter().any(|var| {
let iv = match var.data(Interner) {
- chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner),
- chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
- chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
+ GenericArgData::Ty(ty) => ty.inference_var(Interner),
+ GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
+ GenericArgData::Const(c) => c.inference_var(Interner),
}
.expect("free var is not inference var");
if self.var_unification_table.probe_var(iv).is_some() {
@@ -690,14 +690,10 @@ impl<'a> InferenceTable<'a> {
.fill(|it| {
let arg = match it {
ParamKind::Type => self.new_type_var(),
- ParamKind::Const(ty) => {
- never!("Tuple with const parameter");
- return GenericArgData::Const(self.new_const_var(ty.clone()))
- .intern(Interner);
- }
+ ParamKind::Const(_) => unreachable!("Tuple with const parameter"),
};
arg_tys.push(arg.clone());
- GenericArgData::Ty(arg).intern(Interner)
+ arg.cast(Interner)
})
.build();
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index b3ca2a2225..405bb001b5 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -52,12 +52,14 @@ use hir_expand::name;
use la_arena::{Arena, Idx};
use mir::{MirEvalError, VTableMap};
use rustc_hash::FxHashSet;
+use syntax::ast::{make, ConstArg};
use traits::FnTrait;
use triomphe::Arc;
use utils::Generics;
use crate::{
- consteval::unknown_const, db::HirDatabase, infer::unify::InferenceTable, utils::generics,
+ consteval::unknown_const, db::HirDatabase, display::HirDisplay, infer::unify::InferenceTable,
+ utils::generics,
};
pub use autoderef::autoderef;
@@ -719,3 +721,16 @@ where
value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
collector.placeholders.into_iter().collect()
}
+
+pub fn known_const_to_ast(konst: &Const, db: &dyn HirDatabase) -> Option<ConstArg> {
+ if let ConstValue::Concrete(c) = &konst.interned().value {
+ match c.interned {
+ ConstScalar::UnevaluatedConst(GeneralConstId::InTypeConstId(cid), _) => {
+ return Some(cid.source(db.upcast()));
+ }
+ ConstScalar::Unknown => return None,
+ _ => (),
+ }
+ }
+ Some(make::expr_const_value(konst.display(db).to_string().as_str()))
+}
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 2837f400bc..9a61f15359 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -58,10 +58,9 @@ use crate::{
InTypeConstIdMetadata,
},
AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
- FnPointer, FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig,
- ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait,
- ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
- TyKind, WhereClause,
+ FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
+ QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits,
+ Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
};
#[derive(Debug)]
@@ -213,6 +212,19 @@ impl<'a> TyLoweringContext<'a> {
self.lower_ty_ext(type_ref).0
}
+ pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const {
+ const_or_path_to_chalk(
+ self.db,
+ self.resolver,
+ self.owner,
+ const_type,
+ const_ref,
+ self.type_param_mode,
+ || self.generics(),
+ self.in_binders,
+ )
+ }
+
fn generics(&self) -> Generics {
generics(
self.db.upcast(),
@@ -242,17 +254,7 @@ impl<'a> TyLoweringContext<'a> {
}
TypeRef::Array(inner, len) => {
let inner_ty = self.lower_ty(inner);
- let const_len = const_or_path_to_chalk(
- self.db,
- self.resolver,
- self.owner,
- TyBuilder::usize(),
- len,
- self.type_param_mode,
- || self.generics(),
- self.in_binders,
- );
-
+ let const_len = self.lower_const(len, TyBuilder::usize());
TyKind::Array(inner_ty, const_len).intern(Interner)
}
TypeRef::Slice(inner) => {
@@ -391,11 +393,9 @@ impl<'a> TyLoweringContext<'a> {
let ty = {
let macro_call = macro_call.to_node(self.db.upcast());
let resolver = |path| {
- self.resolver.resolve_path_as_macro(
- self.db.upcast(),
- &path,
- Some(MacroSubNs::Bang),
- )
+ self.resolver
+ .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang))
+ .map(|(it, _)| it)
};
match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call, resolver)
{
@@ -447,7 +447,7 @@ impl<'a> TyLoweringContext<'a> {
return None;
}
let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
- Some((it, None)) => it,
+ Some((it, None, _)) => it,
_ => return None,
};
match resolution {
@@ -627,7 +627,7 @@ impl<'a> TyLoweringContext<'a> {
return self.lower_ty_relative_path(ty, res, path.segments());
}
- let (resolution, remaining_index) =
+ let (resolution, remaining_index, _) =
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some(it) => it,
None => return (TyKind::Error.intern(Interner), None),
@@ -847,18 +847,7 @@ impl<'a> TyLoweringContext<'a> {
arg,
&mut (),
|_, type_ref| self.lower_ty(type_ref),
- |_, c, ty| {
- const_or_path_to_chalk(
- self.db,
- self.resolver,
- self.owner,
- ty,
- c,
- self.type_param_mode,
- || self.generics(),
- self.in_binders,
- )
- },
+ |_, const_ref, ty| self.lower_const(const_ref, ty),
) {
had_explicit_args = true;
substs.push(x);
@@ -1604,24 +1593,35 @@ pub(crate) fn generic_defaults_query(
.iter()
.enumerate()
.map(|(idx, (id, p))| {
- let p = match p {
- TypeOrConstParamData::TypeParamData(p) => p,
- TypeOrConstParamData::ConstParamData(_) => {
- // FIXME: implement const generic defaults
- let val = unknown_const_as_generic(
- db.const_param_ty(ConstParamId::from_unchecked(id)),
+ match p {
+ TypeOrConstParamData::TypeParamData(p) => {
+ let mut ty = p
+ .default
+ .as_ref()
+ .map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
+ // Each default can only refer to previous parameters.
+ // Type variable default referring to parameter coming
+ // after it is forbidden (FIXME: report diagnostic)
+ ty = fallback_bound_vars(ty, idx, parent_start_idx);
+ crate::make_binders(db, &generic_params, ty.cast(Interner))
+ }
+ TypeOrConstParamData::ConstParamData(p) => {
+ let mut val = p.default.as_ref().map_or_else(
+ || {
+ unknown_const_as_generic(
+ db.const_param_ty(ConstParamId::from_unchecked(id)),
+ )
+ },
+ |c| {
+ let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
+ c.cast(Interner)
+ },
);
- return make_binders(db, &generic_params, val);
+ // Each default can only refer to previous parameters, see above.
+ val = fallback_bound_vars(val, idx, parent_start_idx);
+ make_binders(db, &generic_params, val)
}
- };
- let mut ty =
- p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
-
- // Each default can only refer to previous parameters.
- // Type variable default referring to parameter coming
- // after it is forbidden (FIXME: report diagnostic)
- ty = fallback_bound_vars(ty, idx, parent_start_idx);
- crate::make_binders(db, &generic_params, ty.cast(Interner))
+ }
})
// FIXME: use `Arc::from_iter` when it becomes available
.collect::<Vec<_>>(),
@@ -1643,9 +1643,7 @@ pub(crate) fn generic_defaults_recover(
.iter_id()
.map(|id| {
let val = match id {
- Either::Left(_) => {
- GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
- }
+ Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
};
crate::make_binders(db, &generic_params, val)
@@ -1991,16 +1989,9 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
}
};
Some(match (arg, kind) {
- (GenericArg::Type(type_ref), ParamKind::Type) => {
- let ty = for_type(this, type_ref);
- GenericArgData::Ty(ty).intern(Interner)
- }
- (GenericArg::Const(c), ParamKind::Const(c_ty)) => {
- GenericArgData::Const(for_const(this, c, c_ty)).intern(Interner)
- }
- (GenericArg::Const(_), ParamKind::Type) => {
- GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
- }
+ (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner),
+ (GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner),
+ (GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
(GenericArg::Type(t), ParamKind::Const(c_ty)) => {
// We want to recover simple idents, which parser detects them
// as types. Maybe here is not the best place to do it, but
@@ -2010,9 +2001,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
if p.kind == PathKind::Plain {
if let [n] = p.segments() {
let c = ConstRef::Path(n.clone());
- return Some(
- GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner),
- );
+ return Some(for_const(this, &c, c_ty).cast(Interner));
}
}
}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 9e30eed56f..3944feb128 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -10,7 +10,7 @@ use std::{
};
use base_db::{CrateId, FileId};
-use chalk_ir::Mutability;
+use chalk_ir::{cast::Cast, Mutability};
use either::Either;
use hir_def::{
builtin_type::BuiltinType,
@@ -40,8 +40,8 @@ use crate::{
name, static_lifetime,
traits::FnTrait,
utils::{detect_variant_from_bytes, ClosureSubst},
- CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
- Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
+ CallableDefId, ClosureId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution,
+ TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
};
use super::{
@@ -2007,7 +2007,28 @@ impl Evaluator<'_> {
}
}
AdtId::UnionId(_) => (),
- AdtId::EnumId(_) => (),
+ AdtId::EnumId(e) => {
+ if let Some((variant, layout)) = detect_variant_from_bytes(
+ &layout,
+ self.db,
+ self.trait_env.clone(),
+ self.read_memory(addr, layout.size.bytes_usize())?,
+ e,
+ ) {
+ let ev = EnumVariantId { parent: e, local_id: variant };
+ for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() {
+ let offset = layout.fields.offset(i).bytes_usize();
+ let ty = ty.clone().substitute(Interner, subst);
+ self.patch_addresses(
+ patch_map,
+ old_vtable,
+ addr.offset(offset),
+ &ty,
+ locals,
+ )?;
+ }
+ }
+ }
},
TyKind::Tuple(_, subst) => {
for (id, ty) in subst.iter(Interner).enumerate() {
@@ -2248,7 +2269,7 @@ impl Evaluator<'_> {
interval: args_for_target[0].interval.slice(0..self.ptr_size()),
ty: ty.clone(),
};
- let ty = GenericArgData::Ty(ty.clone()).intern(Interner);
+ let ty = ty.clone().cast(Interner);
let generics_for_target = Substitution::from_iter(
Interner,
generic_args.iter(Interner).enumerate().map(|(i, it)| {
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index b2e29fd34b..52943e97ac 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -136,7 +136,10 @@ impl Evaluator<'_> {
not_supported!("wrong generic arg kind for clone");
};
// Clone has special impls for tuples and function pointers
- if matches!(self_ty.kind(Interner), TyKind::Function(_) | TyKind::Tuple(..)) {
+ if matches!(
+ self_ty.kind(Interner),
+ TyKind::Function(_) | TyKind::Tuple(..) | TyKind::Closure(..)
+ ) {
self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?;
return Ok(true);
}
@@ -167,32 +170,26 @@ impl Evaluator<'_> {
return destination
.write_from_interval(self, Interval { addr, size: destination.size });
}
+ TyKind::Closure(id, subst) => {
+ let [arg] = args else {
+ not_supported!("wrong arg count for clone");
+ };
+ let addr = Address::from_bytes(arg.get(self)?)?;
+ let (closure_owner, _) = self.db.lookup_intern_closure((*id).into());
+ let infer = self.db.infer(closure_owner);
+ let (captures, _) = infer.closure_info(id);
+ let layout = self.layout(&self_ty)?;
+ let ty_iter = captures.iter().map(|c| c.ty(subst));
+ self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
+ }
TyKind::Tuple(_, subst) => {
let [arg] = args else {
not_supported!("wrong arg count for clone");
};
let addr = Address::from_bytes(arg.get(self)?)?;
let layout = self.layout(&self_ty)?;
- for (i, ty) in subst.iter(Interner).enumerate() {
- let ty = ty.assert_ty_ref(Interner);
- let size = self.layout(ty)?.size.bytes_usize();
- let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
- let arg = IntervalAndTy {
- interval: Interval { addr: tmp, size: self.ptr_size() },
- ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone())
- .intern(Interner),
- };
- let offset = layout.fields.offset(i).bytes_usize();
- self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
- self.exec_clone(
- def,
- &[arg],
- ty.clone(),
- locals,
- destination.slice(offset..offset + size),
- span,
- )?;
- }
+ let ty_iter = subst.iter(Interner).map(|ga| ga.assert_ty_ref(Interner).clone());
+ self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
}
_ => {
self.exec_fn_with_args(
@@ -209,6 +206,37 @@ impl Evaluator<'_> {
Ok(())
}
+ fn exec_clone_for_fields(
+ &mut self,
+ ty_iter: impl Iterator<Item = Ty>,
+ layout: Arc<Layout>,
+ addr: Address,
+ def: FunctionId,
+ locals: &Locals,
+ destination: Interval,
+ span: MirSpan,
+ ) -> Result<()> {
+ for (i, ty) in ty_iter.enumerate() {
+ let size = self.layout(&ty)?.size.bytes_usize();
+ let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
+ let arg = IntervalAndTy {
+ interval: Interval { addr: tmp, size: self.ptr_size() },
+ ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()).intern(Interner),
+ };
+ let offset = layout.fields.offset(i).bytes_usize();
+ self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
+ self.exec_clone(
+ def,
+ &[arg],
+ ty,
+ locals,
+ destination.slice(offset..offset + size),
+ span,
+ )?;
+ }
+ Ok(())
+ }
+
fn exec_alloc_fn(
&mut self,
alloc_fn: &str,
@@ -473,6 +501,38 @@ impl Evaluator<'_> {
self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
Ok(())
}
+ "getenv" => {
+ let [name] = args else {
+ return Err(MirEvalError::TypeError("libc::write args are not provided"));
+ };
+ let mut name_buf = vec![];
+ let name = {
+ let mut index = Address::from_bytes(name.get(self)?)?;
+ loop {
+ let byte = self.read_memory(index, 1)?[0];
+ index = index.offset(1);
+ if byte == 0 {
+ break;
+ }
+ name_buf.push(byte);
+ }
+ String::from_utf8_lossy(&name_buf)
+ };
+ let value = self.db.crate_graph()[self.crate_id].env.get(&name);
+ match value {
+ None => {
+ // Write null as fail
+ self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
+ }
+ Some(mut value) => {
+ value.push('\0');
+ let addr = self.heap_allocate(value.len(), 1)?;
+ self.write_memory(addr, value.as_bytes())?;
+ self.write_memory(destination.addr, &addr.to_bytes())?;
+ }
+ }
+ Ok(())
+ }
_ => not_supported!("unknown external function {as_str}"),
}
}
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index 46165cf3d6..ff30dc6dad 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -730,6 +730,48 @@ fn main() {
}
#[test]
+fn posix_getenv() {
+ check_pass(
+ r#"
+//- /main.rs env:foo=bar
+
+type c_char = u8;
+
+extern "C" {
+ pub fn getenv(s: *const c_char) -> *mut c_char;
+}
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ let result = getenv(b"foo\0" as *const _);
+ if *result != b'b' {
+ should_not_reach();
+ }
+ let result = (result as usize + 1) as *const c_char;
+ if *result != b'a' {
+ should_not_reach();
+ }
+ let result = (result as usize + 1) as *const c_char;
+ if *result != b'r' {
+ should_not_reach();
+ }
+ let result = (result as usize + 1) as *const c_char;
+ if *result != 0 {
+ should_not_reach();
+ }
+ let result = getenv(b"not found\0" as *const _);
+ if result as usize != 0 {
+ should_not_reach();
+ }
+}
+"#,
+ );
+}
+
+#[test]
fn posix_tls() {
check_pass(
r#"
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 718df8331e..51cf882d05 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -15,7 +15,7 @@ use hir_def::{
path::Path,
resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs},
AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
- TraitId, TypeOrConstParamId,
+ Lookup, TraitId, TypeOrConstParamId,
};
use hir_expand::name::Name;
use la_arena::ArenaMap;
@@ -372,7 +372,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
match &self.body.exprs[expr_id] {
Expr::Missing => {
if let DefWithBodyId::FunctionId(f) = self.owner {
- let assoc = self.db.lookup_intern_function(f);
+ let assoc = f.lookup(self.db.upcast());
if let ItemContainerId::TraitId(t) = assoc.container {
let name = &self.db.function_data(f).name;
return Err(MirLowerError::TraitFunctionDefinition(t, name.clone()));
@@ -1244,6 +1244,41 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
}
+ fn lower_destructing_assignment(
+ &mut self,
+ mut current: BasicBlockId,
+ lhs: ExprId,
+ rhs: Place,
+ span: MirSpan,
+ ) -> Result<Option<BasicBlockId>> {
+ match &self.body.exprs[lhs] {
+ Expr::Tuple { exprs, is_assignee_expr: _ } => {
+ for (i, expr) in exprs.iter().enumerate() {
+ let Some(c) = self.lower_destructing_assignment(
+ current,
+ *expr,
+ rhs.project(ProjectionElem::TupleOrClosureField(i)),
+ span,
+ )? else {
+ return Ok(None);
+ };
+ current = c;
+ }
+ Ok(Some(current))
+ }
+ Expr::Underscore => Ok(Some(current)),
+ _ => {
+ let Some((lhs_place, current)) =
+ self.lower_expr_as_place(current, lhs, false)?
+ else {
+ return Ok(None);
+ };
+ self.push_assignment(current, lhs_place, Operand::Copy(rhs).into(), span);
+ Ok(Some(current))
+ }
+ }
+ }
+
fn lower_assignment(
&mut self,
current: BasicBlockId,
@@ -1259,6 +1294,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
if matches!(&self.body.exprs[lhs], Expr::Underscore) {
return Ok(Some(current));
}
+ if matches!(
+ &self.body.exprs[lhs],
+ Expr::Tuple { .. } | Expr::RecordLit { .. } | Expr::Call { .. }
+ ) {
+ let temp = self.temp(self.expr_ty_after_adjustments(rhs), current, rhs.into())?;
+ let temp = Place::from(temp);
+ self.push_assignment(current, temp.clone(), rhs_op.into(), span);
+ return self.lower_destructing_assignment(current, lhs, temp, span);
+ }
let Some((lhs_place, current)) =
self.lower_expr_as_place(current, lhs, false)?
else {
@@ -1308,14 +1352,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
.resolve_path_in_value_ns(self.db.upcast(), c)
.ok_or_else(unresolved_name)?;
match pr {
- ResolveValueResult::ValueNs(v) => {
+ ResolveValueResult::ValueNs(v, _) => {
if let ValueNs::ConstId(c) = v {
self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty)
} else {
not_supported!("bad path in range pattern");
}
}
- ResolveValueResult::Partial(_, _) => {
+ ResolveValueResult::Partial(_, _, _) => {
not_supported!("associated constants in range pattern")
}
}
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 3354cbd76a..1cdfd91974 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -323,7 +323,7 @@ impl MirLowerCtx<'_> {
break 'b (c, x.1);
}
}
- if let ResolveValueResult::ValueNs(v) = pr {
+ if let ResolveValueResult::ValueNs(v, _) = pr {
if let ValueNs::ConstId(c) = v {
break 'b (c, Substitution::empty(Interner));
}
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 0f2fb2c811..121e5a0a24 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -3,18 +3,19 @@
use hir_def::{
attr::{AttrsWithOwner, Documentation},
item_scope::ItemInNs,
- path::ModPath,
- resolver::HasResolver,
- AttrDefId, GenericParamId, ModuleDefId,
+ path::{ModPath, Path},
+ per_ns::Namespace,
+ resolver::{HasResolver, Resolver, TypeNs},
+ AssocItemId, AttrDefId, GenericParamId, ModuleDefId,
};
-use hir_expand::hygiene::Hygiene;
+use hir_expand::{hygiene::Hygiene, name::Name};
use hir_ty::db::HirDatabase;
use syntax::{ast, AstNode};
use crate::{
- Adt, AssocItem, Const, ConstParam, Enum, ExternCrateDecl, Field, Function, GenericParam, Impl,
- LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias,
- TypeParam, Union, Variant,
+ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, Enum, ExternCrateDecl, Field,
+ Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait,
+ TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef,
};
pub trait HasAttrs {
@@ -25,14 +26,14 @@ pub trait HasAttrs {
db: &dyn HirDatabase,
link: &str,
ns: Option<Namespace>,
- ) -> Option<ModuleDef>;
+ ) -> Option<DocLinkDef>;
}
-#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
-pub enum Namespace {
- Types,
- Values,
- Macros,
+/// Subset of `ide_db::Definition` that doc links can resolve to.
+pub enum DocLinkDef {
+ ModuleDef(ModuleDef),
+ Field(Field),
+ SelfType(Trait),
}
macro_rules! impl_has_attrs {
@@ -46,9 +47,14 @@ macro_rules! impl_has_attrs {
let def = AttrDefId::$def_id(self.into());
db.attrs(def).docs()
}
- fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<Namespace>
+ ) -> Option<DocLinkDef> {
let def = AttrDefId::$def_id(self.into());
- resolve_doc_path(db, def, link, ns).map(ModuleDef::from)
+ resolve_doc_path(db, def, link, ns)
}
}
)*};
@@ -79,7 +85,12 @@ macro_rules! impl_has_attrs_enum {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
$enum::$variant(self).docs(db)
}
- fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<Namespace>
+ ) -> Option<DocLinkDef> {
$enum::$variant(self).resolve_doc_path(db, link, ns)
}
}
@@ -111,7 +122,7 @@ impl HasAttrs for AssocItem {
db: &dyn HirDatabase,
link: &str,
ns: Option<Namespace>,
- ) -> Option<ModuleDef> {
+ ) -> Option<DocLinkDef> {
match self {
AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
@@ -147,9 +158,9 @@ impl HasAttrs for ExternCrateDecl {
db: &dyn HirDatabase,
link: &str,
ns: Option<Namespace>,
- ) -> Option<ModuleDef> {
+ ) -> Option<DocLinkDef> {
let def = AttrDefId::ExternCrateId(self.into());
- resolve_doc_path(db, def, link, ns).map(ModuleDef::from)
+ resolve_doc_path(db, def, link, ns)
}
}
@@ -159,7 +170,7 @@ fn resolve_doc_path(
def: AttrDefId,
link: &str,
ns: Option<Namespace>,
-) -> Option<ModuleDefId> {
+) -> Option<DocLinkDef> {
let resolver = match def {
AttrDefId::ModuleId(it) => it.resolver(db.upcast()),
AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
@@ -184,8 +195,107 @@ fn resolve_doc_path(
.resolver(db.upcast()),
};
- let modpath = {
- // FIXME: this is not how we should get a mod path here
+ let mut modpath = modpath_from_str(db, link)?;
+
+ let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
+ if resolved.is_none() {
+ let last_name = modpath.pop_segment()?;
+ resolve_assoc_or_field(db, resolver, modpath, last_name, ns)
+ } else {
+ let def = match ns {
+ Some(Namespace::Types) => resolved.take_types(),
+ Some(Namespace::Values) => resolved.take_values(),
+ Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId),
+ None => resolved.iter_items().next().map(|(it, _)| match it {
+ ItemInNs::Types(it) => it,
+ ItemInNs::Values(it) => it,
+ ItemInNs::Macros(it) => ModuleDefId::MacroId(it),
+ }),
+ };
+ Some(DocLinkDef::ModuleDef(def?.into()))
+ }
+}
+
+fn resolve_assoc_or_field(
+ db: &dyn HirDatabase,
+ resolver: Resolver,
+ path: ModPath,
+ name: Name,
+ ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+ let path = Path::from_known_path_with_no_generic(path);
+ // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the
+ // trait itself.
+ let base_def = resolver.resolve_path_in_type_ns_fully(db.upcast(), &path)?;
+
+ let ty = match base_def {
+ TypeNs::SelfType(id) => Impl::from(id).self_ty(db),
+ TypeNs::GenericParam(_) => {
+ // Even if this generic parameter has some trait bounds, rustdoc doesn't
+ // resolve `name` to trait items.
+ return None;
+ }
+ TypeNs::AdtId(id) | TypeNs::AdtSelfType(id) => Adt::from(id).ty(db),
+ TypeNs::EnumVariantId(id) => {
+ // Enum variants don't have path candidates.
+ let variant = Variant::from(id);
+ return resolve_field(db, variant.into(), name, ns);
+ }
+ TypeNs::TypeAliasId(id) => {
+ let alias = TypeAlias::from(id);
+ if alias.as_assoc_item(db).is_some() {
+ // We don't normalize associated type aliases, so we have nothing to
+ // resolve `name` to.
+ return None;
+ }
+ alias.ty(db)
+ }
+ TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db),
+ TypeNs::TraitId(id) => {
+ // Doc paths in this context may only resolve to an item of this trait
+ // (i.e. no items of its supertraits), so we need to handle them here
+ // independently of others.
+ return db.trait_data(id).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| {
+ let def = match *assoc_id {
+ AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()),
+ AssocItemId::ConstId(it) => ModuleDef::Const(it.into()),
+ AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
+ };
+ DocLinkDef::ModuleDef(def)
+ });
+ }
+ TypeNs::TraitAliasId(_) => {
+ // XXX: Do these get resolved?
+ return None;
+ }
+ };
+
+ // FIXME: Resolve associated items here, e.g. `Option::map`. Note that associated items take
+ // precedence over fields.
+
+ let variant_def = match ty.as_adt()? {
+ Adt::Struct(it) => it.into(),
+ Adt::Union(it) => it.into(),
+ Adt::Enum(_) => return None,
+ };
+ resolve_field(db, variant_def, name, ns)
+}
+
+fn resolve_field(
+ db: &dyn HirDatabase,
+ def: VariantDef,
+ name: Name,
+ ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+ if let Some(Namespace::Types | Namespace::Macros) = ns {
+ return None;
+ }
+ def.fields(db).into_iter().find(|f| f.name(db) == name).map(DocLinkDef::Field)
+}
+
+fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option<ModPath> {
+ // FIXME: this is not how we should get a mod path here.
+ let try_get_modpath = |link: &str| {
let ast_path = ast::SourceFile::parse(&format!("type T = {link};"))
.syntax_node()
.descendants()
@@ -193,23 +303,20 @@ fn resolve_doc_path(
if ast_path.syntax().text() != link {
return None;
}
- ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())?
+ ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())
};
- let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
- let resolved = if resolved.is_none() {
- resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)?
- } else {
- resolved
- };
- match ns {
- Some(Namespace::Types) => resolved.take_types(),
- Some(Namespace::Values) => resolved.take_values(),
- Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId),
- None => resolved.iter_items().next().map(|it| match it {
- ItemInNs::Types(it) => it,
- ItemInNs::Values(it) => it,
- ItemInNs::Macros(it) => ModuleDefId::MacroId(it),
- }),
+ let full = try_get_modpath(link);
+ if full.is_some() {
+ return full;
}
+
+ // Tuple field names cannot be a part of `ModPath` usually, but rustdoc can
+ // resolve doc paths like `TupleStruct::0`.
+ // FIXME: Find a better way to handle these.
+ let (base, maybe_tuple_field) = link.rsplit_once("::")?;
+ let tuple_field = Name::new_tuple_field(maybe_tuple_field.parse().ok()?);
+ let mut modpath = try_get_modpath(base)?;
+ modpath.push_segment(tuple_field);
+ Some(modpath)
}
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 9dfb98e459..ac171026d5 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -8,7 +8,6 @@ use hir_def::{
type_ref::{TypeBound, TypeRef},
AdtId, GenericDefId,
};
-use hir_expand::name;
use hir_ty::{
display::{
write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
@@ -19,8 +18,9 @@ use hir_ty::{
use crate::{
Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, Field,
- Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, Static, Struct,
- Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant,
+ Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, SelfParam,
+ Static, Struct, Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam,
+ Union, Variant,
};
impl HirDisplay for Function {
@@ -57,37 +57,21 @@ impl HirDisplay for Function {
f.write_char('(')?;
- let write_self_param = |ty: &TypeRef, f: &mut HirFormatter<'_>| match ty {
- TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
- TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) =>
- {
- f.write_char('&')?;
- if let Some(lifetime) = lifetime {
- write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
- }
- if let hir_def::type_ref::Mutability::Mut = mut_ {
- f.write_str("mut ")?;
- }
- f.write_str("self")
- }
- _ => {
- f.write_str("self: ")?;
- ty.hir_fmt(f)
- }
- };
-
let mut first = true;
+ let mut skip_self = 0;
+ if let Some(self_param) = self.self_param(db) {
+ self_param.hir_fmt(f)?;
+ first = false;
+ skip_self = 1;
+ }
+
// FIXME: Use resolved `param.ty` once we no longer discard lifetimes
- for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) {
+ for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) {
let local = param.as_local(db).map(|it| it.name(db));
if !first {
f.write_str(", ")?;
} else {
first = false;
- if local == Some(name!(self)) {
- write_self_param(type_ref, f)?;
- continue;
- }
}
match local {
Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?,
@@ -137,6 +121,31 @@ impl HirDisplay for Function {
}
}
+impl HirDisplay for SelfParam {
+ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+ let data = f.db.function_data(self.func);
+ let param = data.params.first().unwrap();
+ match &**param {
+ TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
+ TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) =>
+ {
+ f.write_char('&')?;
+ if let Some(lifetime) = lifetime {
+ write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
+ }
+ if let hir_def::type_ref::Mutability::Mut = mut_ {
+ f.write_str("mut ")?;
+ }
+ f.write_str("self")
+ }
+ ty => {
+ f.write_str("self: ")?;
+ ty.hir_fmt(f)
+ }
+ }
+ }
+}
+
impl HirDisplay for Adt {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
match self {
@@ -357,6 +366,11 @@ fn write_generic_params(
delim(f)?;
write!(f, "const {}: ", name.display(f.db.upcast()))?;
c.ty.hir_fmt(f)?;
+
+ if let Some(default) = &c.default {
+ f.write_str(" = ")?;
+ write!(f, "{}", default.display(f.db.upcast()))?;
+ }
}
}
}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index bf041b61f2..512fe7e042 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -63,12 +63,13 @@ use hir_ty::{
all_super_traits, autoderef,
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
diagnostics::BodyValidationDiagnostic,
+ known_const_to_ast,
layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding},
method_resolution::{self, TyFingerprint},
mir::{self, interpret_mir},
primitive::UintTy,
traits::FnTrait,
- AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
+ AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
WhereClause,
@@ -87,7 +88,7 @@ use triomphe::Arc;
use crate::db::{DefDatabase, HirDatabase};
pub use crate::{
- attrs::{HasAttrs, Namespace},
+ attrs::{DocLinkDef, HasAttrs},
diagnostics::{
AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode,
IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError,
@@ -121,6 +122,7 @@ pub use {
lang_item::LangItem,
nameres::{DefMap, ModuleSource},
path::{ModPath, PathKind},
+ per_ns::Namespace,
type_ref::{Mutability, TypeRef},
visibility::Visibility,
// FIXME: This is here since some queries take it as input that are used
@@ -719,20 +721,18 @@ fn emit_def_diagnostic_(
) {
match diag {
DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
- let decl = declaration.to_node(db.upcast());
+ let decl = declaration.to_ptr(db.upcast());
acc.push(
UnresolvedModule {
- decl: InFile::new(declaration.file_id, AstPtr::new(&decl)),
+ decl: InFile::new(declaration.file_id, decl),
candidates: candidates.clone(),
}
.into(),
)
}
DefDiagnosticKind::UnresolvedExternCrate { ast } => {
- let item = ast.to_node(db.upcast());
- acc.push(
- UnresolvedExternCrate { decl: InFile::new(ast.file_id, AstPtr::new(&item)) }.into(),
- );
+ let item = ast.to_ptr(db.upcast());
+ acc.push(UnresolvedExternCrate { decl: InFile::new(ast.file_id, item) }.into());
}
DefDiagnosticKind::UnresolvedImport { id, index } => {
@@ -747,14 +747,10 @@ fn emit_def_diagnostic_(
}
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
- let item = ast.to_node(db.upcast());
+ let item = ast.to_ptr(db.upcast());
acc.push(
- InactiveCode {
- node: ast.with_value(SyntaxNodePtr::new(&item).into()),
- cfg: cfg.clone(),
- opts: opts.clone(),
- }
- .into(),
+ InactiveCode { node: ast.with_value(item), cfg: cfg.clone(), opts: opts.clone() }
+ .into(),
);
}
DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => {
@@ -1273,7 +1269,7 @@ impl Adt {
.fill(|x| {
let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x {
- ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
+ ParamKind::Type => r.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
})
@@ -2096,14 +2092,6 @@ impl SelfParam {
.unwrap_or(Access::Owned)
}
- pub fn display(self, db: &dyn HirDatabase) -> &'static str {
- match self.access(db) {
- Access::Shared => "&self",
- Access::Exclusive => "&mut self",
- Access::Owned => "self",
- }
- }
-
pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::SelfParam>> {
let InFile { file_id, value } = Function::from(self.func).source(db)?;
value
@@ -3142,12 +3130,8 @@ impl TypeParam {
}
pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
- let params = db.generic_defaults(self.id.parent());
- let local_idx = hir_ty::param_idx(db, self.id.into())?;
+ let ty = generic_arg_from_param(db, self.id.into())?;
let resolver = self.id.parent().resolver(db.upcast());
- let ty = params.get(local_idx)?.clone();
- let subst = TyBuilder::placeholder_subst(db, self.id.parent());
- let ty = ty.substitute(Interner, &subst);
match ty.data(Interner) {
GenericArgData::Ty(it) => {
Some(Type::new_with_resolver_inner(db, &resolver, it.clone()))
@@ -3209,6 +3193,19 @@ impl ConstParam {
pub fn ty(self, db: &dyn HirDatabase) -> Type {
Type::new(db, self.id.parent(), db.const_param_ty(self.id))
}
+
+ pub fn default(self, db: &dyn HirDatabase) -> Option<ast::ConstArg> {
+ let arg = generic_arg_from_param(db, self.id.into())?;
+ known_const_to_ast(arg.constant(Interner)?, db)
+ }
+}
+
+fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<GenericArg> {
+ let params = db.generic_defaults(id.parent);
+ let local_idx = hir_ty::param_idx(db, id)?;
+ let ty = params.get(local_idx)?.clone();
+ let subst = TyBuilder::placeholder_subst(db, id.parent);
+ Some(ty.substitute(Interner, &subst))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -3716,7 +3713,7 @@ impl Type {
.fill(|x| {
let r = it.next().unwrap();
match x {
- ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
+ ParamKind::Type => r.cast(Interner),
ParamKind::Const(ty) => {
// FIXME: this code is not covered in tests.
unknown_const_as_generic(ty.clone())
@@ -3749,9 +3746,7 @@ impl Type {
.fill(|it| {
// FIXME: this code is not covered in tests.
match it {
- ParamKind::Type => {
- GenericArgData::Ty(args.next().unwrap().ty.clone()).intern(Interner)
- }
+ ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
})
@@ -4414,14 +4409,13 @@ impl Callable {
Other => CallableKind::Other,
}
}
- pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(ast::SelfParam, Type)> {
+ pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(SelfParam, Type)> {
let func = match self.callee {
Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
_ => return None,
};
- let src = func.lookup(db.upcast()).source(db.upcast());
- let param_list = src.value.param_list()?;
- Some((param_list.self_param()?, self.ty.derived(self.sig.params()[0].clone())))
+ let func = Function { id: func };
+ Some((func.self_param(db)?, self.ty.derived(self.sig.params()[0].clone())))
}
pub fn n_params(&self) -> usize {
self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index e99d2984c3..b8d4ecd441 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -170,6 +170,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.is_derive_annotated(item)
}
+ /// Expand the macro call with a different token tree, mapping the `token_to_map` down into the
+ /// expansion. `token_to_map` should be a token from the `speculative args` node.
pub fn speculative_expand(
&self,
actual_macro_call: &ast::MacroCall,
@@ -179,6 +181,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map)
}
+ /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the
+ /// expansion. `token_to_map` should be a token from the `speculative args` node.
pub fn speculative_expand_attr_macro(
&self,
actual_macro_call: &ast::Item,
@@ -201,14 +205,22 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
)
}
- /// Descend the token into macrocalls to its first mapped counterpart.
- pub fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken {
- self.imp.descend_into_macros_single(token)
+ /// Descend the token into its macro call if it is part of one, returning the token in the
+ /// expansion that it is associated with. If `offset` points into the token's range, it will
+ /// be considered for the mapping in case of inline format args.
+ pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken {
+ self.imp.descend_into_macros_single(token, offset)
}
- /// Descend the token into macrocalls to all its mapped counterparts.
- pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
- self.imp.descend_into_macros(token)
+ /// Descend the token into its macro call if it is part of one, returning the tokens in the
+ /// expansion that it is associated with. If `offset` points into the token's range, it will
+ /// be considered for the mapping in case of inline format args.
+ pub fn descend_into_macros(
+ &self,
+ token: SyntaxToken,
+ offset: TextSize,
+ ) -> SmallVec<[SyntaxToken; 1]> {
+ self.imp.descend_into_macros(token, offset)
}
/// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token.
@@ -217,12 +229,17 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
pub fn descend_into_macros_with_same_text(
&self,
token: SyntaxToken,
+ offset: TextSize,
) -> SmallVec<[SyntaxToken; 1]> {
- self.imp.descend_into_macros_with_same_text(token)
+ self.imp.descend_into_macros_with_same_text(token, offset)
}
- pub fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken {
- self.imp.descend_into_macros_with_kind_preference(token)
+ pub fn descend_into_macros_with_kind_preference(
+ &self,
+ token: SyntaxToken,
+ offset: TextSize,
+ ) -> SyntaxToken {
+ self.imp.descend_into_macros_with_kind_preference(token, offset)
}
/// Maps a node down by mapping its first and last token down.
@@ -606,7 +623,7 @@ impl<'db> SemanticsImpl<'db> {
let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
resolver
.resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang))
- .map(|it| macro_id_to_def_id(self.db.upcast(), it))
+ .map(|(it, _)| macro_id_to_def_id(self.db.upcast(), it))
})?;
hir_expand::db::expand_speculative(
self.db.upcast(),
@@ -665,7 +682,7 @@ impl<'db> SemanticsImpl<'db> {
};
if first == last {
- self.descend_into_macros_impl(first, &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(first, 0.into(), &mut |InFile { value, .. }| {
if let Some(node) = value.parent_ancestors().find_map(N::cast) {
res.push(node)
}
@@ -674,7 +691,7 @@ impl<'db> SemanticsImpl<'db> {
} else {
// Descend first and last token, then zip them to look for the node they belong to
let mut scratch: SmallVec<[_; 1]> = smallvec![];
- self.descend_into_macros_impl(first, &mut |token| {
+ self.descend_into_macros_impl(first, 0.into(), &mut |token| {
scratch.push(token);
false
});
@@ -682,6 +699,7 @@ impl<'db> SemanticsImpl<'db> {
let mut scratch = scratch.into_iter();
self.descend_into_macros_impl(
last,
+ 0.into(),
&mut |InFile { value: last, file_id: last_fid }| {
if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
if first_fid == last_fid {
@@ -705,19 +723,27 @@ impl<'db> SemanticsImpl<'db> {
res
}
- fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
+ fn descend_into_macros(
+ &self,
+ token: SyntaxToken,
+ offset: TextSize,
+ ) -> SmallVec<[SyntaxToken; 1]> {
let mut res = smallvec![];
- self.descend_into_macros_impl(token, &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| {
res.push(value);
false
});
res
}
- fn descend_into_macros_with_same_text(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
+ fn descend_into_macros_with_same_text(
+ &self,
+ token: SyntaxToken,
+ offset: TextSize,
+ ) -> SmallVec<[SyntaxToken; 1]> {
let text = token.text();
let mut res = smallvec![];
- self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| {
if value.text() == text {
res.push(value);
}
@@ -729,7 +755,11 @@ impl<'db> SemanticsImpl<'db> {
res
}
- fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken {
+ fn descend_into_macros_with_kind_preference(
+ &self,
+ token: SyntaxToken,
+ offset: TextSize,
+ ) -> SyntaxToken {
let fetch_kind = |token: &SyntaxToken| match token.parent() {
Some(node) => match node.kind() {
kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => {
@@ -741,7 +771,7 @@ impl<'db> SemanticsImpl<'db> {
};
let preferred_kind = fetch_kind(&token);
let mut res = None;
- self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| {
if fetch_kind(&value) == preferred_kind {
res = Some(value);
true
@@ -755,9 +785,9 @@ impl<'db> SemanticsImpl<'db> {
res.unwrap_or(token)
}
- fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken {
+ fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken {
let mut res = token.clone();
- self.descend_into_macros_impl(token, &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| {
res = value;
true
});
@@ -767,9 +797,13 @@ impl<'db> SemanticsImpl<'db> {
fn descend_into_macros_impl(
&self,
token: SyntaxToken,
+ // FIXME: We might want this to be Option<TextSize> to be able to opt out of subrange
+ // mapping, specifically for node downmapping
+ offset: TextSize,
f: &mut dyn FnMut(InFile<SyntaxToken>) -> bool,
) {
let _p = profile::span("descend_into_macros");
+ let relative_token_offset = token.text_range().start().checked_sub(offset);
let parent = match token.parent() {
Some(it) => it,
None => return,
@@ -796,7 +830,12 @@ impl<'db> SemanticsImpl<'db> {
self.cache(value, file_id);
}
- let mapped_tokens = expansion_info.map_token_down(self.db.upcast(), item, token)?;
+ let mapped_tokens = expansion_info.map_token_down(
+ self.db.upcast(),
+ item,
+ token,
+ relative_token_offset,
+ )?;
let len = stack.len();
// requeue the tokens we got from mapping our current token down
@@ -943,7 +982,7 @@ impl<'db> SemanticsImpl<'db> {
offset: TextSize,
) -> impl Iterator<Item = impl Iterator<Item = SyntaxNode> + '_> + '_ {
node.token_at_offset(offset)
- .map(move |token| self.descend_into_macros(token))
+ .map(move |token| self.descend_into_macros(token, offset))
.map(|descendants| {
descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it))
})
@@ -1683,6 +1722,14 @@ impl SemanticsScope<'_> {
|name, id| cb(name, id.into()),
)
}
+
+ pub fn extern_crates(&self) -> impl Iterator<Item = (Name, Module)> + '_ {
+ self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id }))
+ }
+
+ pub fn extern_crate_decls(&self) -> impl Iterator<Item = Name> + '_ {
+ self.resolver.extern_crate_decls_in_scope(self.db.upcast())
+ }
}
#[derive(Debug)]
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 3499daf114..f29fb1edf0 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -487,7 +487,7 @@ impl SourceAnalyzer {
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
self.resolver
.resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Bang))
- .map(|it| it.into())
+ .map(|(it, _)| it.into())
}
pub(crate) fn resolve_bind_pat_to_const(
@@ -760,7 +760,7 @@ impl SourceAnalyzer {
let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
self.resolver
.resolve_path_as_macro(db.upcast(), &path, Some(MacroSubNs::Bang))
- .map(|it| macro_id_to_def_id(db.upcast(), it))
+ .map(|(it, _)| macro_id_to_def_id(db.upcast(), it))
})?;
Some(macro_call_id.as_file()).filter(|it| it.expansion_level(db.upcast()) < 64)
}
@@ -966,6 +966,7 @@ pub(crate) fn resolve_hir_path_as_attr_macro(
) -> Option<Macro> {
resolver
.resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Attr))
+ .map(|(it, _)| it)
.map(Into::into)
}
@@ -983,7 +984,7 @@ fn resolve_hir_path_(
res.map(|ty_ns| (ty_ns, path.segments().first()))
}
None => {
- let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
+ let (ty, remaining_idx, _) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
match remaining_idx {
Some(remaining_idx) => {
if remaining_idx + 1 == path.segments().len() {
@@ -1067,7 +1068,7 @@ fn resolve_hir_path_(
let macros = || {
resolver
.resolve_path_as_macro(db.upcast(), path.mod_path()?, None)
- .map(|def| PathResolution::Def(ModuleDef::Macro(def.into())))
+ .map(|(def, _)| PathResolution::Def(ModuleDef::Macro(def.into())))
};
if prefer_value_ns { values().or_else(types) } else { types().or_else(values) }
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index 43d957412b..ca7874c368 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -2,8 +2,10 @@
use base_db::FileRange;
use hir_def::{
- src::HasSource, AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId,
- ModuleDefId, ModuleId, TraitId,
+ item_scope::ItemInNs,
+ src::{HasChildSource, HasSource},
+ AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId,
+ TraitId,
};
use hir_expand::{HirFileId, InFile};
use hir_ty::db::HirDatabase;
@@ -167,6 +169,40 @@ impl<'a> SymbolCollector<'a> {
self.collect_from_impl(impl_id);
}
+ // Record renamed imports.
+ // In case it imports multiple items under different namespaces we just pick one arbitrarily
+ // for now.
+ for id in scope.imports() {
+ let loc = id.import.lookup(self.db.upcast());
+ loc.id.item_tree(self.db.upcast());
+ let source = id.import.child_source(self.db.upcast());
+ let Some(use_tree_src) = source.value.get(id.idx) else { continue };
+ let Some(rename) = use_tree_src.rename() else { continue };
+ let Some(name) = rename.name() else { continue };
+
+ let res = scope.fully_resolve_import(self.db.upcast(), id);
+ res.iter_items().for_each(|(item, _)| {
+ let def = match item {
+ ItemInNs::Types(def) | ItemInNs::Values(def) => def,
+ ItemInNs::Macros(def) => ModuleDefId::from(def),
+ }
+ .into();
+ let dec_loc = DeclarationLocation {
+ hir_file_id: source.file_id,
+ ptr: SyntaxNodePtr::new(use_tree_src.syntax()),
+ name_ptr: SyntaxNodePtr::new(name.syntax()),
+ };
+
+ self.symbols.push(FileSymbol {
+ name: name.text().into(),
+ def,
+ container_name: self.current_container_name.clone(),
+ loc: dec_loc,
+ is_alias: false,
+ });
+ });
+ }
+
for const_id in scope.unnamed_consts() {
self.collect_from_body(const_id);
}
diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 6aca716bb6..c0e5429a22 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -422,7 +422,7 @@ impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
check_assist(
add_missing_default_members,
r#"
-struct Bar<const: N: bool> {
+struct Bar<const N: usize> {
bar: [i32, N]
}
@@ -439,7 +439,7 @@ impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
$0
}"#,
r#"
-struct Bar<const: N: bool> {
+struct Bar<const N: usize> {
bar: [i32, N]
}
@@ -484,6 +484,107 @@ impl<X> Foo<42, {20 + 22}, X> for () {
}
#[test]
+ fn test_const_substitution_with_defaults() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> {
+ fn get_n(&self) -> usize { N }
+ fn get_m(&self) -> bool { M }
+ fn get_p(&self) -> char { P }
+ fn get_array(&self, arg: &T) -> [bool; N] { [M; N] }
+}
+
+impl<X> Foo<X> for () {
+ $0
+}"#,
+ r#"
+trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> {
+ fn get_n(&self) -> usize { N }
+ fn get_m(&self) -> bool { M }
+ fn get_p(&self) -> char { P }
+ fn get_array(&self, arg: &T) -> [bool; N] { [M; N] }
+}
+
+impl<X> Foo<X> for () {
+ $0fn get_n(&self) -> usize { 42 }
+
+ fn get_m(&self) -> bool { false }
+
+ fn get_p(&self) -> char { 'a' }
+
+ fn get_array(&self, arg: &X) -> [bool; 42] { [false; 42] }
+}"#,
+ );
+ }
+
+ #[test]
+ fn test_const_substitution_with_defaults_2() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ pub const LEN: usize = 42;
+ pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> {
+ fn get_t(&self) -> T;
+ }
+}
+
+impl m::Foo for () {
+ $0
+}"#,
+ r#"
+mod m {
+ pub const LEN: usize = 42;
+ pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> {
+ fn get_t(&self) -> T;
+ }
+}
+
+impl m::Foo for () {
+ fn get_t(&self) -> [bool; m::LEN] {
+ ${0:todo!()}
+ }
+}"#,
+ )
+ }
+
+ #[test]
+ fn test_const_substitution_with_defaults_3() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+mod m {
+ pub const VAL: usize = 0;
+
+ pub trait Foo<const N: usize = {40 + 2}, const M: usize = {VAL + 1}> {
+ fn get_n(&self) -> usize { N }
+ fn get_m(&self) -> usize { M }
+ }
+}
+
+impl m::Foo for () {
+ $0
+}"#,
+ r#"
+mod m {
+ pub const VAL: usize = 0;
+
+ pub trait Foo<const N: usize = {40 + 2}, const M: usize = {VAL + 1}> {
+ fn get_n(&self) -> usize { N }
+ fn get_m(&self) -> usize { M }
+ }
+}
+
+impl m::Foo for () {
+ $0fn get_n(&self) -> usize { {40 + 2} }
+
+ fn get_m(&self) -> usize { {m::VAL + 1} }
+}"#,
+ )
+ }
+
+ #[test]
fn test_cursor_after_empty_impl_def() {
check_assist(
add_missing_impl_members,
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs
index 57cfa17cc8..66bc2f6dad 100644
--- a/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -1,6 +1,10 @@
use std::collections::VecDeque;
-use syntax::ast::{self, AstNode};
+use syntax::{
+ ast::{self, AstNode, Expr::BinExpr},
+ ted::{self, Position},
+ SyntaxKind,
+};
use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
@@ -23,121 +27,117 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin
// }
// ```
pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
- let op = expr.op_kind()?;
- let op_range = expr.op_token()?.text_range();
+ let mut bin_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
+ let op = bin_expr.op_kind()?;
+ let op_range = bin_expr.op_token()?.text_range();
- let opposite_op = match op {
- ast::BinaryOp::LogicOp(ast::LogicOp::And) => "||",
- ast::BinaryOp::LogicOp(ast::LogicOp::Or) => "&&",
- _ => return None,
- };
-
- let cursor_in_range = op_range.contains_range(ctx.selection_trimmed());
- if !cursor_in_range {
+ // Is the cursor on the expression's logical operator?
+ if !op_range.contains_range(ctx.selection_trimmed()) {
return None;
}
- let mut expr = expr;
-
// Walk up the tree while we have the same binary operator
- while let Some(parent_expr) = expr.syntax().parent().and_then(ast::BinExpr::cast) {
- match expr.op_kind() {
+ while let Some(parent_expr) = bin_expr.syntax().parent().and_then(ast::BinExpr::cast) {
+ match parent_expr.op_kind() {
Some(parent_op) if parent_op == op => {
- expr = parent_expr;
+ bin_expr = parent_expr;
}
_ => break,
}
}
- let mut expr_stack = vec![expr.clone()];
- let mut terms = Vec::new();
- let mut op_ranges = Vec::new();
-
- // Find all the children with the same binary operator
- while let Some(expr) = expr_stack.pop() {
- let mut traverse_bin_expr_arm = |expr| {
- if let ast::Expr::BinExpr(bin_expr) = expr {
- if let Some(expr_op) = bin_expr.op_kind() {
- if expr_op == op {
- expr_stack.push(bin_expr);
- } else {
- terms.push(ast::Expr::BinExpr(bin_expr));
- }
+ let op = bin_expr.op_kind()?;
+ let inv_token = match op {
+ ast::BinaryOp::LogicOp(ast::LogicOp::And) => SyntaxKind::PIPE2,
+ ast::BinaryOp::LogicOp(ast::LogicOp::Or) => SyntaxKind::AMP2,
+ _ => return None,
+ };
+
+ let demorganed = bin_expr.clone_subtree().clone_for_update();
+
+ ted::replace(demorganed.op_token()?, ast::make::token(inv_token));
+ let mut exprs = VecDeque::from(vec![
+ (bin_expr.lhs()?, demorganed.lhs()?),
+ (bin_expr.rhs()?, demorganed.rhs()?),
+ ]);
+
+ while let Some((expr, dm)) = exprs.pop_front() {
+ if let BinExpr(bin_expr) = &expr {
+ if let BinExpr(cbin_expr) = &dm {
+ if op == bin_expr.op_kind()? {
+ ted::replace(cbin_expr.op_token()?, ast::make::token(inv_token));
+ exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?));
+ exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?));
} else {
- terms.push(ast::Expr::BinExpr(bin_expr));
+ let mut inv = invert_boolean_expression(expr);
+ if inv.needs_parens_in(dm.syntax().parent()?) {
+ inv = ast::make::expr_paren(inv).clone_for_update();
+ }
+ ted::replace(dm.syntax(), inv.syntax());
}
} else {
- terms.push(expr);
+ return None;
}
- };
-
- op_ranges.extend(expr.op_token().map(|t| t.text_range()));
- traverse_bin_expr_arm(expr.lhs()?);
- traverse_bin_expr_arm(expr.rhs()?);
+ } else {
+ let mut inv = invert_boolean_expression(dm.clone_subtree()).clone_for_update();
+ if inv.needs_parens_in(dm.syntax().parent()?) {
+ inv = ast::make::expr_paren(inv).clone_for_update();
+ }
+ ted::replace(dm.syntax(), inv.syntax());
+ }
}
+ let dm_lhs = demorganed.lhs()?;
+
acc.add(
AssistId("apply_demorgan", AssistKind::RefactorRewrite),
"Apply De Morgan's law",
op_range,
|edit| {
- terms.sort_by_key(|t| t.syntax().text_range().start());
- let mut terms = VecDeque::from(terms);
-
- let paren_expr = expr.syntax().parent().and_then(ast::ParenExpr::cast);
-
+ let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast);
let neg_expr = paren_expr
.clone()
.and_then(|paren_expr| paren_expr.syntax().parent())
.and_then(ast::PrefixExpr::cast)
.and_then(|prefix_expr| {
- if prefix_expr.op_kind().unwrap() == ast::UnaryOp::Not {
+ if prefix_expr.op_kind()? == ast::UnaryOp::Not {
Some(prefix_expr)
} else {
None
}
});
- for op_range in op_ranges {
- edit.replace(op_range, opposite_op);
- }
-
if let Some(paren_expr) = paren_expr {
- for term in terms {
- let range = term.syntax().text_range();
- let not_term = invert_boolean_expression(term);
-
- edit.replace(range, not_term.syntax().text());
- }
-
if let Some(neg_expr) = neg_expr {
cov_mark::hit!(demorgan_double_negation);
- edit.replace(neg_expr.op_token().unwrap().text_range(), "");
+ edit.replace_ast(ast::Expr::PrefixExpr(neg_expr), demorganed.into());
} else {
cov_mark::hit!(demorgan_double_parens);
- edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!(");
+ ted::insert_all_raw(
+ Position::before(dm_lhs.syntax()),
+ vec![
+ syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)),
+ syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)),
+ ],
+ );
+
+ ted::append_child_raw(
+ demorganed.syntax(),
+ syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::R_PAREN)),
+ );
+
+ edit.replace_ast(ast::Expr::ParenExpr(paren_expr), demorganed.into());
}
} else {
- if let Some(lhs) = terms.pop_front() {
- let lhs_range = lhs.syntax().text_range();
- let not_lhs = invert_boolean_expression(lhs);
-
- edit.replace(lhs_range, format!("!({not_lhs}"));
- }
-
- if let Some(rhs) = terms.pop_back() {
- let rhs_range = rhs.syntax().text_range();
- let not_rhs = invert_boolean_expression(rhs);
-
- edit.replace(rhs_range, format!("{not_rhs})"));
- }
-
- for term in terms {
- let term_range = term.syntax().text_range();
- let not_term = invert_boolean_expression(term);
- edit.replace(term_range, not_term.to_string());
- }
+ ted::insert_all_raw(
+ Position::before(dm_lhs.syntax()),
+ vec![
+ syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)),
+ syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)),
+ ],
+ );
+ ted::append_child_raw(demorganed.syntax(), ast::make::token(SyntaxKind::R_PAREN));
+ edit.replace_ast(bin_expr, demorganed);
}
},
)
@@ -145,9 +145,8 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
#[cfg(test)]
mod tests {
- use crate::tests::{check_assist, check_assist_not_applicable};
-
use super::*;
+ use crate::tests::{check_assist, check_assist_not_applicable};
#[test]
fn demorgan_handles_leq() {
@@ -213,7 +212,7 @@ fn f() { !(S <= S || S < S) }
#[test]
fn demorgan_doesnt_double_negation() {
cov_mark::check!(demorgan_double_negation);
- check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }")
+ check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { !x && !x }")
}
#[test]
@@ -222,13 +221,38 @@ fn f() { !(S <= S || S < S) }
check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
}
- // https://github.com/rust-lang/rust-analyzer/issues/10963
+ // FIXME : This needs to go.
+ // // https://github.com/rust-lang/rust-analyzer/issues/10963
+ // #[test]
+ // fn demorgan_doesnt_hang() {
+ // check_assist(
+ // apply_demorgan,
+ // "fn f() { 1 || 3 &&$0 4 || 5 }",
+ // "fn f() { !(!1 || !3 || !4) || 5 }",
+ // )
+ // }
+
+ #[test]
+ fn demorgan_keep_pars_for_op_precedence() {
+ check_assist(
+ apply_demorgan,
+ "fn main() {
+ let _ = !(!a ||$0 !(b || c));
+}
+",
+ "fn main() {
+ let _ = a && (b || c);
+}
+",
+ );
+ }
+
#[test]
- fn demorgan_doesnt_hang() {
+ fn demorgan_removes_pars_in_eq_precedence() {
check_assist(
apply_demorgan,
- "fn f() { 1 || 3 &&$0 4 || 5 }",
- "fn f() { !(!1 || !3 || !4) || 5 }",
+ "fn() { let x = a && !(!b |$0| !c); }",
+ "fn() { let x = a && b && c; }",
)
}
}
diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
index fe1cb6fce3..76f021ed91 100644
--- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
+++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -161,9 +161,9 @@ fn process_struct_name_reference(
let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?;
// A `PathSegment` always belongs to a `Path`, so there's at least one `Path` at this point.
let full_path =
- path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last().unwrap();
+ path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last()?;
- if full_path.segment().unwrap().name_ref()? != *name_ref {
+ if full_path.segment()?.name_ref()? != *name_ref {
// `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the
// struct we want to edit.
return None;
diff --git a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
index dcb96ab8af..7d0e424769 100644
--- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -58,7 +58,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
return None;
}
- let bound_ident = pat.fields().next().unwrap();
+ let bound_ident = pat.fields().next()?;
if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
return None;
}
@@ -108,6 +108,15 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?;
+ let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();
+
+ let end_of_then = then_block_items.syntax().last_child_or_token()?;
+ let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
+ end_of_then.prev_sibling_or_token()?
+ } else {
+ end_of_then
+ };
+
let target = if_expr.syntax().text_range();
acc.add(
AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
@@ -141,16 +150,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
}
};
- let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();
-
- let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
- let end_of_then =
- if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
- end_of_then.prev_sibling_or_token().unwrap()
- } else {
- end_of_then
- };
-
let then_statements = replacement
.children_with_tokens()
.chain(
diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
index 4f3b6e0c28..c3d925cb26 100644
--- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
+++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
@@ -48,7 +48,7 @@ pub(crate) fn extract_expressions_from_format_string(
let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?;
let expanded_t = ast::String::cast(
- ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone()),
+ ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone(), 0.into()),
)?;
if !is_format_string(&expanded_t) {
return None;
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index b8b781ea48..ea7a21e77a 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -750,7 +750,7 @@ impl FunctionBody {
.descendants_with_tokens()
.filter_map(SyntaxElement::into_token)
.filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self]))
- .flat_map(|t| sema.descend_into_macros(t))
+ .flat_map(|t| sema.descend_into_macros(t, 0.into()))
.for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast)));
}
}
@@ -810,7 +810,7 @@ impl FunctionBody {
(true, konst.body(), Some(sema.to_def(&konst)?.ty(sema.db)))
},
ast::ConstParam(cp) => {
- (true, cp.default_val(), Some(sema.to_def(&cp)?.ty(sema.db)))
+ (true, cp.default_val()?.expr(), Some(sema.to_def(&cp)?.ty(sema.db)))
},
ast::ConstBlockPat(cbp) => {
let expr = cbp.block_expr().map(ast::Expr::BlockExpr);
@@ -1385,31 +1385,30 @@ enum FlowHandler {
impl FlowHandler {
fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler {
- match &fun.control_flow.kind {
- None => FlowHandler::None,
- Some(flow_kind) => {
- let action = flow_kind.clone();
- if let FunType::Unit = ret_ty {
- match flow_kind {
- FlowKind::Return(None)
- | FlowKind::Break(_, None)
- | FlowKind::Continue(_) => FlowHandler::If { action },
- FlowKind::Return(_) | FlowKind::Break(_, _) => {
- FlowHandler::IfOption { action }
- }
- FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
- }
- } else {
- match flow_kind {
- FlowKind::Return(None)
- | FlowKind::Break(_, None)
- | FlowKind::Continue(_) => FlowHandler::MatchOption { none: action },
- FlowKind::Return(_) | FlowKind::Break(_, _) => {
- FlowHandler::MatchResult { err: action }
- }
- FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
- }
+ if fun.contains_tail_expr {
+ return FlowHandler::None;
+ }
+ let Some(action) = fun.control_flow.kind.clone() else {
+ return FlowHandler::None;
+ };
+
+ if let FunType::Unit = ret_ty {
+ match action {
+ FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
+ FlowHandler::If { action }
+ }
+ FlowKind::Return(_) | FlowKind::Break(_, _) => FlowHandler::IfOption { action },
+ FlowKind::Try { kind } => FlowHandler::Try { kind },
+ }
+ } else {
+ match action {
+ FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
+ FlowHandler::MatchOption { none: action }
+ }
+ FlowKind::Return(_) | FlowKind::Break(_, _) => {
+ FlowHandler::MatchResult { err: action }
}
+ FlowKind::Try { kind } => FlowHandler::Try { kind },
}
}
}
@@ -1654,11 +1653,7 @@ impl Function {
fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> {
let fun_ty = self.return_type(ctx);
- let handler = if self.contains_tail_expr {
- FlowHandler::None
- } else {
- FlowHandler::from_ret_ty(self, &fun_ty)
- };
+ let handler = FlowHandler::from_ret_ty(self, &fun_ty);
let ret_ty = match &handler {
FlowHandler::None => {
if matches!(fun_ty, FunType::Unit) {
@@ -1728,11 +1723,7 @@ fn make_body(
fun: &Function,
) -> ast::BlockExpr {
let ret_ty = fun.return_type(ctx);
- let handler = if fun.contains_tail_expr {
- FlowHandler::None
- } else {
- FlowHandler::from_ret_ty(fun, &ret_ty)
- };
+ let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
let block = match &fun.body {
FunctionBody::Expr(expr) => {
@@ -4471,7 +4462,7 @@ async fn foo() -> Result<(), ()> {
"#,
r#"
async fn foo() -> Result<(), ()> {
- fun_name().await?
+ fun_name().await
}
async fn $0fun_name() -> Result<(), ()> {
@@ -4690,7 +4681,7 @@ fn $0fun_name() {
check_assist(
extract_function,
r#"
-//- minicore: result
+//- minicore: result, try
fn foo() -> Result<(), i64> {
$0Result::<i32, i64>::Ok(0)?;
Ok(())$0
@@ -4698,7 +4689,7 @@ fn foo() -> Result<(), i64> {
"#,
r#"
fn foo() -> Result<(), i64> {
- fun_name()?
+ fun_name()
}
fn $0fun_name() -> Result<(), i64> {
@@ -5754,6 +5745,34 @@ fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
}
#[test]
+ fn tail_expr_no_extra_control_flow() {
+ check_assist(
+ extract_function,
+ r#"
+//- minicore: result
+fn fallible() -> Result<(), ()> {
+ $0if true {
+ return Err(());
+ }
+ Ok(())$0
+}
+"#,
+ r#"
+fn fallible() -> Result<(), ()> {
+ fun_name()
+}
+
+fn $0fun_name() -> Result<(), ()> {
+ if true {
+ return Err(());
+ }
+ Ok(())
+}
+"#,
+ );
+ }
+
+ #[test]
fn non_tail_expr_of_tail_expr_loop() {
check_assist(
extract_function,
@@ -5800,12 +5819,6 @@ fn $0fun_name() -> ControlFlow<()> {
extract_function,
r#"
//- minicore: option, try
-impl<T> core::ops::Try for Option<T> {
- type Output = T;
- type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
fn f() -> Option<()> {
if true {
let a = $0if true {
@@ -5820,12 +5833,6 @@ fn f() -> Option<()> {
}
"#,
r#"
-impl<T> core::ops::Try for Option<T> {
- type Output = T;
- type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
fn f() -> Option<()> {
if true {
let a = fun_name()?;;
@@ -5852,12 +5859,6 @@ fn $0fun_name() -> Option<()> {
extract_function,
r#"
//- minicore: option, try
-impl<T> core::ops::Try for Option<T> {
- type Output = T;
- type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
fn f() -> Option<()> {
if true {
$0{
@@ -5874,15 +5875,9 @@ fn f() -> Option<()> {
}
"#,
r#"
-impl<T> core::ops::Try for Option<T> {
- type Output = T;
- type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
fn f() -> Option<()> {
if true {
- fun_name()?
+ fun_name()
} else {
None
}
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 31fc69562c..bbac0a26ea 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -95,6 +95,9 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else {
continue;
};
+
+ let field = make::ext::field_from_idents(["self", &field_name])?;
+
acc.add_group(
&GroupLabel("Generate delegate methods…".to_owned()),
AssistId("generate_delegate_methods", AssistKind::Generate),
@@ -115,11 +118,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
Some(list) => convert_param_list_to_arg_list(list),
None => make::arg_list([]),
};
- let tail_expr = make::expr_method_call(
- make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list
- make::name_ref(&name),
- arg_list,
- );
+ let tail_expr = make::expr_method_call(field, make::name_ref(&name), arg_list);
let ret_type = method_source.ret_type();
let is_async = method_source.async_token().is_some();
let is_const = method_source.const_token().is_some();
diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs
index 747f70f9f6..53ba144ba9 100644
--- a/crates/ide-assists/src/handlers/generate_derive.rs
+++ b/crates/ide-assists/src/handlers/generate_derive.rs
@@ -27,13 +27,19 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
let cap = ctx.config.snippet_cap?;
let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
let target = nominal.syntax().text_range();
+ let derive_attr = nominal
+ .attrs()
+ .filter_map(|x| x.as_simple_call())
+ .filter(|(name, _arg)| name == "derive")
+ .map(|(_name, arg)| arg)
+ .next();
+
+ let delimiter = match &derive_attr {
+ None => None,
+ Some(tt) => Some(tt.right_delimiter_token()?),
+ };
+
acc.add(AssistId("generate_derive", AssistKind::Generate), "Add `#[derive]`", target, |edit| {
- let derive_attr = nominal
- .attrs()
- .filter_map(|x| x.as_simple_call())
- .filter(|(name, _arg)| name == "derive")
- .map(|(_name, arg)| arg)
- .next();
match derive_attr {
None => {
let derive = make::attr_outer(make::meta_token_tree(
@@ -45,16 +51,23 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
let nominal = edit.make_mut(nominal);
nominal.add_attr(derive.clone());
+ let delimiter = derive
+ .meta()
+ .expect("make::attr_outer was expected to have Meta")
+ .token_tree()
+ .expect("failed to get token tree out of Meta")
+ .r_paren_token()
+ .expect("make::attr_outer was expected to have a R_PAREN");
+
+ edit.add_tabstop_before_token(cap, delimiter);
+ }
+ Some(_) => {
+ // Just move the cursor.
edit.add_tabstop_before_token(
cap,
- derive.meta().unwrap().token_tree().unwrap().r_paren_token().unwrap(),
+ delimiter.expect("Right delim token could not be found."),
);
}
- Some(tt) => {
- // Just move the cursor.
- let tt = edit.make_mut(tt);
- edit.add_tabstop_before_token(cap, tt.right_delimiter_token().unwrap());
- }
};
})
}
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs
index a403d5bc67..e2b8222328 100644
--- a/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -39,14 +39,11 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
let replacements =
macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
- if replacements.is_empty() {
- return None;
- }
acc.add(
AssistId("remove_dbg", AssistKind::Refactor),
"Remove dbg!()",
- replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range)).unwrap(),
+ replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?,
|builder| {
for (range, expr) in replacements {
if let Some(expr) = expr {
diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs
index dd4839351f..5fcab8c02b 100644
--- a/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -67,7 +67,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
// This case maps to the situation where the * token is braced.
// In this case, the parent use tree's path is the one we should use to resolve the glob.
match u.syntax().ancestors().skip(1).find_map(ast::UseTree::cast) {
- Some(parent_u) if parent_u.path().is_some() => parent_u.path().unwrap(),
+ Some(parent_u) if parent_u.path().is_some() => parent_u.path()?,
_ => return None,
}
} else {
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 480cb77b4f..f60ac15016 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -20,6 +20,7 @@ pub(crate) mod r#type;
pub(crate) mod use_;
pub(crate) mod vis;
pub(crate) mod env_vars;
+pub(crate) mod extern_crate;
use std::iter;
@@ -703,7 +704,9 @@ pub(super) fn complete_name_ref(
TypeLocation::TypeAscription(ascription) => {
r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription);
}
- TypeLocation::GenericArgList(_)
+ TypeLocation::GenericArg { .. }
+ | TypeLocation::AssocConstEq
+ | TypeLocation::AssocTypeEq
| TypeLocation::TypeBound
| TypeLocation::ImplTarget
| TypeLocation::ImplTrait
@@ -737,6 +740,7 @@ pub(super) fn complete_name_ref(
}
}
}
+ NameRefKind::ExternCrate => extern_crate::complete_extern_crate(acc, ctx),
NameRefKind::DotAccess(dot_access) => {
flyimport::import_on_the_fly_dot(acc, ctx, dot_access);
dot::complete_dot(acc, ctx, dot_access);
diff --git a/crates/ide-completion/src/completions/extern_crate.rs b/crates/ide-completion/src/completions/extern_crate.rs
new file mode 100644
index 0000000000..0d0e143f5f
--- /dev/null
+++ b/crates/ide-completion/src/completions/extern_crate.rs
@@ -0,0 +1,71 @@
+//! Completion for extern crates
+
+use hir::{HasAttrs, Name};
+use ide_db::SymbolKind;
+
+use crate::{context::CompletionContext, CompletionItem, CompletionItemKind};
+
+use super::Completions;
+
+pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_>) {
+ let imported_extern_crates: Vec<Name> = ctx.scope.extern_crate_decls().collect();
+
+ for (name, module) in ctx.scope.extern_crates() {
+ if imported_extern_crates.contains(&name) {
+ continue;
+ }
+
+ let mut item = CompletionItem::new(
+ CompletionItemKind::SymbolKind(SymbolKind::Module),
+ ctx.source_range(),
+ name.to_smol_str(),
+ );
+ item.set_documentation(module.docs(ctx.db));
+
+ item.add_to(acc, ctx.db);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use crate::tests::completion_list_no_kw;
+
+ #[test]
+ fn can_complete_extern_crate() {
+ let case = r#"
+//- /lib.rs crate:other_crate_a
+// nothing here
+//- /other_crate_b.rs crate:other_crate_b
+pub mod good_mod{}
+//- /lib.rs crate:crate_c
+// nothing here
+//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a
+extern crate oth$0
+mod other_mod {}
+"#;
+
+ let completion_list = completion_list_no_kw(case);
+
+ assert_eq!("md other_crate_a\n".to_string(), completion_list);
+ }
+
+ #[test]
+ fn will_not_complete_existing_import() {
+ let case = r#"
+//- /lib.rs crate:other_crate_a
+// nothing here
+//- /lib.rs crate:crate_c
+// nothing here
+//- /lib.rs crate:other_crate_b
+//
+//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a,other_crate_b
+extern crate other_crate_b;
+extern crate oth$0
+mod other_mod {}
+"#;
+
+ let completion_list = completion_list_no_kw(case);
+
+ assert_eq!("md other_crate_a\n".to_string(), completion_list);
+ }
+}
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index e470547563..a30fd13b1d 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -1,7 +1,7 @@
//! Completion of names from the current scope in type position.
use hir::{HirDisplay, ScopeDef};
-use syntax::{ast, AstNode, SyntaxKind};
+use syntax::{ast, AstNode};
use crate::{
context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
@@ -20,16 +20,15 @@ pub(crate) fn complete_type_path(
let scope_def_applicable = |def| {
use hir::{GenericParam::*, ModuleDef::*};
match def {
- ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false,
+ ScopeDef::GenericParam(LifetimeParam(_)) => location.complete_lifetimes(),
+ ScopeDef::Label(_) => false,
// no values in type places
ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
// unless its a constant in a generic arg list position
ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
- matches!(location, TypeLocation::GenericArgList(_))
- }
- ScopeDef::ImplSelfType(_) => {
- !matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
+ location.complete_consts()
}
+ ScopeDef::ImplSelfType(_) => location.complete_self_type(),
// Don't suggest attribute macros and derives.
ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
// Type things are fine
@@ -38,12 +37,12 @@ pub(crate) fn complete_type_path(
)
| ScopeDef::AdtSelfType(_)
| ScopeDef::Unknown
- | ScopeDef::GenericParam(TypeParam(_)) => true,
+ | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(),
}
};
let add_assoc_item = |acc: &mut Completions, item| match item {
- hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => {
+ hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg { .. }) => {
acc.add_const(ctx, ct)
}
hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
@@ -157,56 +156,30 @@ pub(crate) fn complete_type_path(
});
return;
}
- TypeLocation::GenericArgList(Some(arg_list)) => {
- let in_assoc_type_arg = ctx
- .original_token
- .parent_ancestors()
- .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG);
-
- if !in_assoc_type_arg {
- if let Some(path_seg) =
- arg_list.syntax().parent().and_then(ast::PathSegment::cast)
- {
- if path_seg
- .syntax()
- .ancestors()
- .find_map(ast::TypeBound::cast)
- .is_some()
- {
- if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(
- trait_,
- ))) = ctx.sema.resolve_path(&path_seg.parent_path())
- {
- let arg_idx = arg_list
- .generic_args()
- .filter(|arg| {
- arg.syntax().text_range().end()
- < ctx.original_token.text_range().start()
- })
- .count();
-
- let n_required_params =
- trait_.type_or_const_param_count(ctx.sema.db, true);
- if arg_idx >= n_required_params {
- trait_
- .items_with_supertraits(ctx.sema.db)
- .into_iter()
- .for_each(|it| {
- if let hir::AssocItem::TypeAlias(alias) = it {
- cov_mark::hit!(
- complete_assoc_type_in_generics_list
- );
- acc.add_type_alias_with_eq(ctx, alias);
- }
- });
-
- let n_params =
- trait_.type_or_const_param_count(ctx.sema.db, false);
- if arg_idx >= n_params {
- return; // only show assoc types
- }
- }
+ TypeLocation::GenericArg {
+ args: Some(arg_list), of_trait: Some(trait_), ..
+ } => {
+ if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() {
+ let arg_idx = arg_list
+ .generic_args()
+ .filter(|arg| {
+ arg.syntax().text_range().end()
+ < ctx.original_token.text_range().start()
+ })
+ .count();
+
+ let n_required_params = trait_.type_or_const_param_count(ctx.sema.db, true);
+ if arg_idx >= n_required_params {
+ trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| {
+ if let hir::AssocItem::TypeAlias(alias) = it {
+ cov_mark::hit!(complete_assoc_type_in_generics_list);
+ acc.add_type_alias_with_eq(ctx, alias);
}
+ });
+
+ let n_params = trait_.type_or_const_param_count(ctx.sema.db, false);
+ if arg_idx >= n_params {
+ return; // only show assoc types
}
}
}
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 3cb65b2729..0da7ba6d00 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -155,13 +155,63 @@ pub(crate) struct ExprCtx {
pub(crate) enum TypeLocation {
TupleField,
TypeAscription(TypeAscriptionTarget),
- GenericArgList(Option<ast::GenericArgList>),
+ /// Generic argument position e.g. `Foo<$0>`
+ GenericArg {
+ /// The generic argument list containing the generic arg
+ args: Option<ast::GenericArgList>,
+ /// `Some(trait_)` if `trait_` is being instantiated with `args`
+ of_trait: Option<hir::Trait>,
+ /// The generic parameter being filled in by the generic arg
+ corresponding_param: Option<ast::GenericParam>,
+ },
+ /// Associated type equality constraint e.g. `Foo<Bar = $0>`
+ AssocTypeEq,
+ /// Associated constant equality constraint e.g. `Foo<X = $0>`
+ AssocConstEq,
TypeBound,
ImplTarget,
ImplTrait,
Other,
}
+impl TypeLocation {
+ pub(crate) fn complete_lifetimes(&self) -> bool {
+ matches!(
+ self,
+ TypeLocation::GenericArg {
+ corresponding_param: Some(ast::GenericParam::LifetimeParam(_)),
+ ..
+ }
+ )
+ }
+
+ pub(crate) fn complete_consts(&self) -> bool {
+ match self {
+ TypeLocation::GenericArg {
+ corresponding_param: Some(ast::GenericParam::ConstParam(_)),
+ ..
+ } => true,
+ TypeLocation::AssocConstEq => true,
+ _ => false,
+ }
+ }
+
+ pub(crate) fn complete_types(&self) -> bool {
+ match self {
+ TypeLocation::GenericArg { corresponding_param: Some(param), .. } => {
+ matches!(param, ast::GenericParam::TypeParam(_))
+ }
+ TypeLocation::AssocConstEq => false,
+ TypeLocation::AssocTypeEq => true,
+ _ => true,
+ }
+ }
+
+ pub(crate) fn complete_self_type(&self) -> bool {
+ self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
+ }
+}
+
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum TypeAscriptionTarget {
Let(Option<ast::Pat>),
@@ -301,6 +351,7 @@ pub(super) enum NameRefKind {
expr: ast::RecordExpr,
},
Pattern(PatternContext),
+ ExternCrate,
}
/// The identifier we are currently completing.
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 3ea5065903..1e6b2f319a 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -1,11 +1,11 @@
//! Module responsible for analyzing the code surrounding the cursor for completion.
use std::iter;
-use hir::{Semantics, Type, TypeInfo, Variant};
+use hir::{HasSource, Semantics, Type, TypeInfo, Variant};
use ide_db::{active_parameter::ActiveParameter, RootDatabase};
use syntax::{
algo::{find_node_at_offset, non_trivia_sibling},
- ast::{self, AttrKind, HasArgList, HasLoopBody, HasName, NameOrNameRef},
+ ast::{self, AttrKind, HasArgList, HasGenericParams, HasLoopBody, HasName, NameOrNameRef},
match_ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
SyntaxToken, TextRange, TextSize, T,
};
@@ -624,6 +624,10 @@ fn classify_name_ref(
});
return Some(make_res(kind));
},
+ ast::ExternCrate(_) => {
+ let kind = NameRefKind::ExternCrate;
+ return Some(make_res(kind));
+ },
ast::MethodCallExpr(method) => {
let receiver = find_opt_node_in_file(original_file, method.receiver());
let kind = NameRefKind::DotAccess(DotAccess {
@@ -719,6 +723,136 @@ fn classify_name_ref(
None
};
+ let generic_arg_location = |arg: ast::GenericArg| {
+ let mut override_location = None;
+ let location = find_opt_node_in_file_compensated(
+ sema,
+ original_file,
+ arg.syntax().parent().and_then(ast::GenericArgList::cast),
+ )
+ .map(|args| {
+ let mut in_trait = None;
+ let param = (|| {
+ let parent = args.syntax().parent()?;
+ let params = match_ast! {
+ match parent {
+ ast::PathSegment(segment) => {
+ match sema.resolve_path(&segment.parent_path().top_path())? {
+ hir::PathResolution::Def(def) => match def {
+ hir::ModuleDef::Function(func) => {
+ func.source(sema.db)?.value.generic_param_list()
+ }
+ hir::ModuleDef::Adt(adt) => {
+ adt.source(sema.db)?.value.generic_param_list()
+ }
+ hir::ModuleDef::Variant(variant) => {
+ variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list()
+ }
+ hir::ModuleDef::Trait(trait_) => {
+ if let ast::GenericArg::AssocTypeArg(arg) = &arg {
+ let arg_name = arg.name_ref()?;
+ let arg_name = arg_name.text();
+ for item in trait_.items_with_supertraits(sema.db) {
+ match item {
+ hir::AssocItem::TypeAlias(assoc_ty) => {
+ if assoc_ty.name(sema.db).as_str()? == arg_name {
+ override_location = Some(TypeLocation::AssocTypeEq);
+ return None;
+ }
+ },
+ hir::AssocItem::Const(const_) => {
+ if const_.name(sema.db)?.as_str()? == arg_name {
+ override_location = Some(TypeLocation::AssocConstEq);
+ return None;
+ }
+ },
+ _ => (),
+ }
+ }
+ return None;
+ } else {
+ in_trait = Some(trait_);
+ trait_.source(sema.db)?.value.generic_param_list()
+ }
+ }
+ hir::ModuleDef::TraitAlias(trait_) => {
+ trait_.source(sema.db)?.value.generic_param_list()
+ }
+ hir::ModuleDef::TypeAlias(ty_) => {
+ ty_.source(sema.db)?.value.generic_param_list()
+ }
+ _ => None,
+ },
+ _ => None,
+ }
+ },
+ ast::MethodCallExpr(call) => {
+ let func = sema.resolve_method_call(&call)?;
+ func.source(sema.db)?.value.generic_param_list()
+ },
+ ast::AssocTypeArg(arg) => {
+ let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?;
+ match sema.resolve_path(&trait_.parent_path().top_path())? {
+ hir::PathResolution::Def(def) => match def {
+ hir::ModuleDef::Trait(trait_) => {
+ let arg_name = arg.name_ref()?;
+ let arg_name = arg_name.text();
+ let trait_items = trait_.items_with_supertraits(sema.db);
+ let assoc_ty = trait_items.iter().find_map(|item| match item {
+ hir::AssocItem::TypeAlias(assoc_ty) => {
+ (assoc_ty.name(sema.db).as_str()? == arg_name)
+ .then_some(assoc_ty)
+ },
+ _ => None,
+ })?;
+ assoc_ty.source(sema.db)?.value.generic_param_list()
+ }
+ _ => None,
+ },
+ _ => None,
+ }
+ },
+ _ => None,
+ }
+ }?;
+ // Determine the index of the argument in the `GenericArgList` and match it with
+ // the corresponding parameter in the `GenericParamList`. Since lifetime parameters
+ // are often omitted, ignore them for the purposes of matching the argument with
+ // its parameter unless a lifetime argument is provided explicitly. That is, for
+ // `struct S<'a, 'b, T>`, match `S::<$0>` to `T` and `S::<'a, $0, _>` to `'b`.
+ // FIXME: This operates on the syntax tree and will produce incorrect results when
+ // generic parameters are disabled by `#[cfg]` directives. It should operate on the
+ // HIR, but the functionality necessary to do so is not exposed at the moment.
+ let mut explicit_lifetime_arg = false;
+ let arg_idx = arg
+ .syntax()
+ .siblings(Direction::Prev)
+ // Skip the node itself
+ .skip(1)
+ .map(|arg| if ast::LifetimeArg::can_cast(arg.kind()) { explicit_lifetime_arg = true })
+ .count();
+ let param_idx = if explicit_lifetime_arg {
+ arg_idx
+ } else {
+ // Lifetimes parameters always precede type and generic parameters,
+ // so offset the argument index by the total number of lifetime params
+ arg_idx + params.lifetime_params().count()
+ };
+ params.generic_params().nth(param_idx)
+ })();
+ (args, in_trait, param)
+ });
+ let (arg_list, of_trait, corresponding_param) = match location {
+ Some((arg_list, of_trait, param)) => (Some(arg_list), of_trait, param),
+ _ => (None, None, None),
+ };
+ override_location.unwrap_or(TypeLocation::GenericArg {
+ args: arg_list,
+ of_trait,
+ corresponding_param,
+ })
+ };
+
let type_location = |node: &SyntaxNode| {
let parent = node.parent()?;
let res = match_ast! {
@@ -774,9 +908,12 @@ fn classify_name_ref(
ast::TypeBound(_) => TypeLocation::TypeBound,
// is this case needed?
ast::TypeBoundList(_) => TypeLocation::TypeBound,
- ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))),
+ ast::GenericArg(it) => generic_arg_location(it),
// is this case needed?
- ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))),
+ ast::GenericArgList(it) => {
+ let args = find_opt_node_in_file_compensated(sema, original_file, Some(it));
+ TypeLocation::GenericArg { args, of_trait: None, corresponding_param: None }
+ },
ast::TupleField(_) => TypeLocation::TupleField,
_ => return None,
}
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index 8c038c0fba..4cdfd546f6 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -1286,3 +1286,57 @@ macro_rules! println {
expect![""],
)
}
+
+#[test]
+fn no_completions_for_external_doc_hidden_in_path() {
+ check(
+ r#"
+//- /main.rs crate:main deps:dep
+fn main() {
+ Span$0
+}
+//- /lib.rs crate:dep
+#[doc(hidden)]
+pub mod bridge {
+ pub mod server {
+ pub trait Span
+ }
+}
+pub mod bridge2 {
+ #[doc(hidden)]
+ pub mod server2 {
+ pub trait Span
+ }
+}
+"#,
+ expect![""],
+ );
+ // unless re-exported
+ check(
+ r#"
+//- /main.rs crate:main deps:dep
+fn main() {
+ Span$0
+}
+//- /lib.rs crate:dep
+#[doc(hidden)]
+pub mod bridge {
+ pub mod server {
+ pub trait Span
+ }
+}
+pub use bridge::server::Span;
+pub mod bridge2 {
+ #[doc(hidden)]
+ pub mod server2 {
+ pub trait Span2
+ }
+}
+pub use bridge2::server2::Span2;
+"#,
+ expect![[r#"
+ tt Span (use dep::Span)
+ tt Span2 (use dep::Span2)
+ "#]],
+ );
+}
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index 8cb1ff4a12..d518dd7641 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -384,10 +384,8 @@ trait Trait2<T>: Trait1 {
fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#,
expect![[r#"
- ct CONST
- cp CONST_PARAM
en Enum
- ma makro!(…) macro_rules! makro
+ ma makro!(…) macro_rules! makro
md module
st Record
st Tuple
@@ -404,14 +402,13 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
);
check(
r#"
-trait Trait2 {
+trait Trait2<T> {
type Foo;
}
fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -437,7 +434,6 @@ trait Tr<T> {
impl Tr<$0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -485,7 +481,6 @@ trait MyTrait<T, U> {
fn f(t: impl MyTrait<u$0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -511,7 +506,6 @@ trait MyTrait<T, U> {
fn f(t: impl MyTrait<u8, u$0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -555,7 +549,6 @@ trait MyTrait<T, U = u8> {
fn f(t: impl MyTrait<u$0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -581,7 +574,6 @@ trait MyTrait<T, U = u8> {
fn f(t: impl MyTrait<u8, u$0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -627,7 +619,6 @@ trait MyTrait {
fn f(t: impl MyTrait<Item1 = $0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -653,7 +644,6 @@ trait MyTrait {
fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -668,6 +658,22 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
kw self::
"#]],
);
+
+ check(
+ r#"
+trait MyTrait {
+ const C: usize;
+};
+
+fn f(t: impl MyTrait<C = $0
+"#,
+ expect![[r#"
+ ct CONST
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
}
#[test]
@@ -719,3 +725,267 @@ pub struct S;
"#]],
)
}
+
+#[test]
+fn completes_const_and_type_generics_separately() {
+ // Function generic params
+ check(
+ r#"
+ struct Foo;
+ const X: usize = 0;
+ fn foo<T, const N: usize>() {}
+ fn main() {
+ foo::<F$0, _>();
+ }
+ "#,
+ expect![[r#"
+ en Enum
+ ma makro!(…) macro_rules! makro
+ md module
+ st Foo
+ st Record
+ st Tuple
+ st Unit
+ tt Trait
+ un Union
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ );
+ // FIXME: This should probably also suggest completions for types, at least those that have
+ // associated constants usable in this position. For example, a user could be typing
+ // `foo::<_, { usize::MAX }>()`, but we currently don't suggest `usize` in constant position.
+ check(
+ r#"
+ struct Foo;
+ const X: usize = 0;
+ fn foo<T, const N: usize>() {}
+ fn main() {
+ foo::<_, $0>();
+ }
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Method generic params
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo;
+ impl Foo { fn bar<const N: usize, T>(self) {} }
+ fn main() {
+ Foo.bar::<_, $0>();
+ }
+ "#,
+ expect![[r#"
+ en Enum
+ ma makro!(…) macro_rules! makro
+ md module
+ st Foo
+ st Record
+ st Tuple
+ st Unit
+ tt Trait
+ un Union
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ );
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo;
+ impl Foo { fn bar<const N: usize, T>(self) {} }
+ fn main() {
+ Foo.bar::<X$0, _>();
+ }
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Associated type generic params
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo;
+ trait Bar {
+ type Baz<T, const X: usize>;
+ }
+ fn foo(_: impl Bar<Baz<F$0, 0> = ()>) {}
+ "#,
+ expect![[r#"
+ en Enum
+ ma makro!(…) macro_rules! makro
+ md module
+ st Foo
+ st Record
+ st Tuple
+ st Unit
+ tt Bar
+ tt Trait
+ un Union
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ );
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo;
+ trait Bar {
+ type Baz<T, const X: usize>;
+ }
+ fn foo<T: Bar<Baz<(), $0> = ()>>() {}
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Type generic params
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo<T, const N: usize>(T);
+ fn main() {
+ let _: Foo::<_, $0> = Foo(());
+ }
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Type alias generic params
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo<T, const N: usize>(T);
+ type Bar<const X: usize, U> = Foo<U, X>;
+ fn main() {
+ let _: Bar::<X$0, _> = Bar(());
+ }
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Enum variant params
+ check(
+ r#"
+ const X: usize = 0;
+ enum Foo<T, const N: usize> { A(T), B }
+ fn main() {
+ Foo::B::<(), $0>;
+ }
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Trait params
+ check(
+ r#"
+ const X: usize = 0;
+ trait Foo<T, const N: usize> {}
+ impl Foo<(), $0> for () {}
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Trait alias params
+ check(
+ r#"
+ #![feature(trait_alias)]
+ const X: usize = 0;
+ trait Foo<T, const N: usize> {}
+ trait Bar<const M: usize, U> = Foo<U, M>;
+ fn foo<T: Bar<X$0, ()>>() {}
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Omitted lifetime params
+ check(
+ r#"
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<F$0, _>; }
+ "#,
+ expect![[r#"
+ ct CONST
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+ // Explicit lifetime params
+ check(
+ r#"
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<'static, 'static, F$0, _>; }
+ "#,
+ expect![[r#"
+ ct CONST
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+ check(
+ r#"
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<'static, F$0, _, _>; }
+ "#,
+ expect![[r#"
+ lt 'a
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index 5e4562d9c5..4ce80532e8 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -7,7 +7,7 @@
use arrayvec::ArrayVec;
use hir::{
- Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper,
+ Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, DocLinkDef,
ExternCrateDecl, Field, Function, GenericParam, HasVisibility, Impl, Label, Local, Macro,
Module, ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias,
TypeAlias, Variant, Visibility,
@@ -649,3 +649,13 @@ impl From<ModuleDef> for Definition {
}
}
}
+
+impl From<DocLinkDef> for Definition {
+ fn from(def: DocLinkDef) -> Self {
+ match def {
+ DocLinkDef::ModuleDef(it) => it.into(),
+ DocLinkDef::Field(it) => it.into(),
+ DocLinkDef::SelfType(it) => it.into(),
+ }
+ }
+}
diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs
index 1eb8f00020..330af442f7 100644
--- a/crates/ide-db/src/helpers.rs
+++ b/crates/ide-db/src/helpers.rs
@@ -117,7 +117,7 @@ pub fn get_definition(
sema: &Semantics<'_, RootDatabase>,
token: SyntaxToken,
) -> Option<Definition> {
- for token in sema.descend_into_macros(token) {
+ for token in sema.descend_into_macros(token, 0.into()) {
let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
if let Some(&[x]) = def.as_deref() {
return Some(x);
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index e52dc35677..e475c5cd66 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -6,7 +6,7 @@ use hir::{
use itertools::Itertools;
use rustc_hash::FxHashSet;
use syntax::{
- ast::{self, HasName},
+ ast::{self, make, HasName},
utils::path_to_string_stripping_turbo_fish,
AstNode, SyntaxNode,
};
@@ -607,7 +607,7 @@ impl ImportCandidate {
fn for_name(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<Self> {
if sema
.scope(name.syntax())?
- .speculative_resolve(&ast::make::ext::ident_path(&name.text()))
+ .speculative_resolve(&make::ext::ident_path(&name.text()))
.is_some()
{
return None;
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index f27ed485d8..ac3511ba47 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -94,18 +94,21 @@ impl fmt::Debug for RootDatabase {
}
impl Upcast<dyn ExpandDatabase> for RootDatabase {
+ #[inline]
fn upcast(&self) -> &(dyn ExpandDatabase + 'static) {
&*self
}
}
impl Upcast<dyn DefDatabase> for RootDatabase {
+ #[inline]
fn upcast(&self) -> &(dyn DefDatabase + 'static) {
&*self
}
}
impl Upcast<dyn HirDatabase> for RootDatabase {
+ #[inline]
fn upcast(&self) -> &(dyn HirDatabase + 'static) {
&*self
}
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index 1d0cb426a5..fb75b5b458 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -5,7 +5,7 @@ use either::Either;
use hir::{AsAssocItem, HirDisplay, SemanticsScope};
use rustc_hash::FxHashMap;
use syntax::{
- ast::{self, AstNode},
+ ast::{self, make, AstNode},
ted, SyntaxNode,
};
@@ -21,6 +21,7 @@ enum TypeOrConst {
}
type LifetimeName = String;
+type DefaultedParam = Either<hir::TypeParam, hir::ConstParam>;
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
///
@@ -115,7 +116,7 @@ impl<'a> PathTransform<'a> {
};
let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
- let mut default_types: Vec<hir::TypeParam> = Default::default();
+ let mut defaulted_params: Vec<DefaultedParam> = Default::default();
self.generic_def
.into_iter()
.flat_map(|it| it.type_params(db))
@@ -138,8 +139,8 @@ impl<'a> PathTransform<'a> {
if let Some(default) =
&default.display_source_code(db, source_module.into(), false).ok()
{
- type_substs.insert(k, ast::make::ty(default).clone_for_update());
- default_types.push(k);
+ type_substs.insert(k, make::ty(default).clone_for_update());
+ defaulted_params.push(Either::Left(k));
}
}
}
@@ -155,11 +156,19 @@ impl<'a> PathTransform<'a> {
// is a standalone statement or a part of another expresson)
// and sometimes require slight modifications; see
// https://doc.rust-lang.org/reference/statements.html#expression-statements
+ // (default values in curly brackets can cause the same problem)
const_substs.insert(k, expr.syntax().clone());
}
}
- (Either::Left(_), None) => (), // FIXME: get default const value
- _ => (), // ignore mismatching params
+ (Either::Left(k), None) => {
+ if let Some(default) = k.default(db) {
+ if let Some(default) = default.expr() {
+ const_substs.insert(k, default.syntax().clone_for_update());
+ defaulted_params.push(Either::Right(k));
+ }
+ }
+ }
+ _ => (), // ignore mismatching params
});
let lifetime_substs: FxHashMap<_, _> = self
.generic_def
@@ -175,7 +184,7 @@ impl<'a> PathTransform<'a> {
target_module,
source_scope: self.source_scope,
};
- ctx.transform_default_type_substs(default_types);
+ ctx.transform_default_values(defaulted_params);
ctx
}
}
@@ -212,13 +221,19 @@ impl Ctx<'_> {
});
}
- fn transform_default_type_substs(&self, default_types: Vec<hir::TypeParam>) {
- for k in default_types {
- let v = self.type_substs.get(&k).unwrap();
+ fn transform_default_values(&self, defaulted_params: Vec<DefaultedParam>) {
+ // By now the default values are simply copied from where they are declared
+ // and should be transformed. As any value is allowed to refer to previous
+ // generic (both type and const) parameters, they should be all iterated left-to-right.
+ for param in defaulted_params {
+ let value = match param {
+ Either::Left(k) => self.type_substs.get(&k).unwrap().syntax(),
+ Either::Right(k) => self.const_substs.get(&k).unwrap(),
+ };
// `transform_path` may update a node's parent and that would break the
// tree traversal. Thus all paths in the tree are collected into a vec
// so that such operation is safe.
- let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>();
+ let paths = postorder(value).filter_map(ast::Path::cast).collect::<Vec<_>>();
for path in paths {
self.transform_path(path);
}
@@ -263,15 +278,14 @@ impl Ctx<'_> {
hir::ModuleDef::Trait(trait_ref),
false,
)?;
- match ast::make::ty_path(mod_path_to_ast(&found_path)) {
+ match make::ty_path(mod_path_to_ast(&found_path)) {
ast::Type::PathType(path_ty) => Some(path_ty),
_ => None,
}
});
- let segment = ast::make::path_segment_ty(subst.clone(), trait_ref);
- let qualified =
- ast::make::path_from_segments(std::iter::once(segment), false);
+ let segment = make::path_segment_ty(subst.clone(), trait_ref);
+ let qualified = make::path_from_segments(std::iter::once(segment), false);
ted::replace(path.syntax(), qualified.clone_for_update().syntax());
} else if let Some(path_ty) = ast::PathType::cast(parent) {
ted::replace(
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index d5abd09912..7e00d36865 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -456,14 +456,14 @@ impl<'a> FindUsages<'a> {
it.text().trim_start_matches("r#") == name
})
.into_iter()
- .flat_map(|token| {
+ .flat_map(move |token| {
// FIXME: There should be optimization potential here
// Currently we try to descend everything we find which
// means we call `Semantics::descend_into_macros` on
// every textual hit. That function is notoriously
// expensive even for things that do not get down mapped
// into macros.
- sema.descend_into_macros(token).into_iter().filter_map(|it| it.parent())
+ sema.descend_into_macros(token, offset).into_iter().filter_map(|it| it.parent())
})
};
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs
index b54c43b296..f699f999ba 100644
--- a/crates/ide-db/src/symbol_index.rs
+++ b/crates/ide-db/src/symbol_index.rs
@@ -323,6 +323,8 @@ impl Query {
hir::ModuleDef::Adt(..)
| hir::ModuleDef::TypeAlias(..)
| hir::ModuleDef::BuiltinType(..)
+ | hir::ModuleDef::TraitAlias(..)
+ | hir::ModuleDef::Trait(..)
)
{
continue;
@@ -417,9 +419,16 @@ const CONST_WITH_INNER: () = {
mod b_mod;
+
+use define_struct as really_define_struct;
+use Macro as ItemLikeMacro;
+use Macro as Trait; // overlay namespaces
//- /b_mod.rs
struct StructInModB;
- "#,
+use super::Macro as SuperItemLikeMacro;
+use crate::b_mod::StructInModB as ThisStruct;
+use crate::Trait as IsThisJustATrait;
+"#,
);
let symbols: Vec<_> = Crate::from(db.test_crate())
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 1a00e29384..87ad5844c6 100644
--- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -119,6 +119,35 @@
is_alias: false,
},
FileSymbol {
+ name: "ItemLikeMacro",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
+ ),
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 654..676,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 663..676,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
name: "Macro",
def: Macro(
Macro {
@@ -353,6 +382,35 @@
is_alias: false,
},
FileSymbol {
+ name: "Trait",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
+ ),
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 682..696,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 691..696,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
name: "Union",
def: Adt(
Union(
@@ -552,6 +610,35 @@
is_alias: false,
},
FileSymbol {
+ name: "really_define_struct",
+ def: Macro(
+ Macro {
+ id: MacroRulesId(
+ MacroRulesId(
+ 1,
+ ),
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 611..648,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 628..648,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
name: "trait_fn",
def: Function(
Function {
@@ -632,6 +719,35 @@
},
[
FileSymbol {
+ name: "IsThisJustATrait",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
+ ),
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 1,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 111..143,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 127..143,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
name: "StructInModB",
def: Adt(
Struct(
@@ -660,6 +776,93 @@
container_name: None,
is_alias: false,
},
+ FileSymbol {
+ name: "SuperItemLikeMacro",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
+ ),
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 1,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 25..59,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 41..59,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
+ name: "ThisStruct",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 3,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 1,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 65..105,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 95..105,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
+ name: "ThisStruct",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 3,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 1,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 65..105,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 95..105,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
],
),
]
diff --git a/crates/ide-db/src/use_trivial_constructor.rs b/crates/ide-db/src/use_trivial_constructor.rs
index f96ea29ae2..a915391ad9 100644
--- a/crates/ide-db/src/use_trivial_constructor.rs
+++ b/crates/ide-db/src/use_trivial_constructor.rs
@@ -1,31 +1,29 @@
//! Functionality for generating trivial constructors
use hir::StructKind;
-use syntax::ast;
+use syntax::ast::{make, Expr, Path};
/// given a type return the trivial constructor (if one exists)
pub fn use_trivial_constructor(
db: &crate::RootDatabase,
- path: ast::Path,
+ path: Path,
ty: &hir::Type,
-) -> Option<ast::Expr> {
+) -> Option<Expr> {
match ty.as_adt() {
Some(hir::Adt::Enum(x)) => {
if let &[variant] = &*x.variants(db) {
if variant.kind(db) == hir::StructKind::Unit {
- let path = ast::make::path_qualified(
+ let path = make::path_qualified(
path,
- syntax::ast::make::path_segment(ast::make::name_ref(
- &variant.name(db).to_smol_str(),
- )),
+ make::path_segment(make::name_ref(&variant.name(db).to_smol_str())),
);
- return Some(syntax::ast::make::expr_path(path));
+ return Some(make::expr_path(path));
}
}
}
Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => {
- return Some(syntax::ast::make::expr_path(path));
+ return Some(make::expr_path(path));
}
_ => {}
}
diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs
index a8e8836908..60fcbbbd39 100644
--- a/crates/ide-ssr/src/matching.rs
+++ b/crates/ide-ssr/src/matching.rs
@@ -560,8 +560,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
placeholder_value.autoref_kind = self
.sema
.resolve_method_call_as_callable(code)
- .and_then(|callable| callable.receiver_param(self.sema.db))
- .map(|(self_param, _)| self_param.kind())
+ .and_then(|callable| {
+ let (self_param, _) = callable.receiver_param(self.sema.db)?;
+ Some(self_param.source(self.sema.db)?.value.kind())
+ })
.unwrap_or(ast::SelfParamKind::Owned);
}
}
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index dd1d0d75c6..f834f2ce59 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -74,18 +74,20 @@ pub(crate) fn incoming_calls(
Some(calls.into_items())
}
-pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
+pub(crate) fn outgoing_calls(
+ db: &RootDatabase,
+ FilePosition { file_id, offset }: FilePosition,
+) -> Option<Vec<CallItem>> {
let sema = Semantics::new(db);
- let file_id = position.file_id;
let file = sema.parse(file_id);
let file = file.syntax();
- let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
+ let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
IDENT => 1,
_ => 0,
})?;
let mut calls = CallLocations::default();
- sema.descend_into_macros(token)
+ sema.descend_into_macros(token, offset)
.into_iter()
.filter_map(|it| it.parent_ancestors().nth(1).and_then(ast::Item::cast))
.filter_map(|item| match item {
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index d240127f37..901f7a2a79 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -131,19 +131,19 @@ pub(crate) fn remove_links(markdown: &str) -> String {
// |===
pub(crate) fn external_docs(
db: &RootDatabase,
- position: &FilePosition,
+ FilePosition { file_id, offset }: FilePosition,
target_dir: Option<&OsStr>,
sysroot: Option<&OsStr>,
) -> Option<DocumentationLinks> {
let sema = &Semantics::new(db);
- let file = sema.parse(position.file_id).syntax().clone();
- let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
+ let file = sema.parse(file_id).syntax().clone();
+ let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
IDENT | INT_NUMBER | T![self] => 3,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
})?;
- let token = sema.descend_into_macros_single(token);
+ let token = sema.descend_into_macros_single(token, offset);
let node = token.parent()?;
let definition = match_ast! {
@@ -285,7 +285,7 @@ impl DocCommentToken {
let original_start = doc_token.text_range().start();
let relative_comment_offset = offset - original_start - prefix_len;
- sema.descend_into_macros(doc_token).into_iter().find_map(|t| {
+ sema.descend_into_macros(doc_token, offset).into_iter().find_map(|t| {
let (node, descended_prefix_len) = match_ast! {
match t {
ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?),
diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs
index 05a64b33bf..8036c77072 100644
--- a/crates/ide/src/doc_links/tests.rs
+++ b/crates/ide/src/doc_links/tests.rs
@@ -518,6 +518,62 @@ fn function();
}
#[test]
+fn doc_links_field() {
+ check_doc_links(
+ r#"
+/// [`S::f`]
+/// [`S2::f`]
+/// [`T::0`]
+/// [`U::a`]
+/// [`E::A::f`]
+/// [`E::B::0`]
+struct S$0 {
+ f: i32,
+ //^ S::f
+ //^ S2::f
+}
+type S2 = S;
+struct T(i32);
+ //^^^ T::0
+union U {
+ a: i32,
+ //^ U::a
+}
+enum E {
+ A { f: i32 },
+ //^ E::A::f
+ B(i32),
+ //^^^ E::B::0
+}
+"#,
+ );
+}
+
+#[test]
+fn doc_links_field_via_self() {
+ check_doc_links(
+ r#"
+/// [`Self::f`]
+struct S$0 {
+ f: i32,
+ //^ Self::f
+}
+"#,
+ );
+}
+
+#[test]
+fn doc_links_tuple_field_via_self() {
+ check_doc_links(
+ r#"
+/// [`Self::0`]
+struct S$0(i32);
+ //^^^ Self::0
+"#,
+ );
+}
+
+#[test]
fn rewrite_html_root_url() {
check_rewrite(
r#"
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index d6bbf2bf79..119a9c7c3f 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -40,28 +40,33 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
// struct Bar;
// ```
- let derive = sema.descend_into_macros(tok.clone()).into_iter().find_map(|descended| {
- let hir_file = sema.hir_file_for(&descended.parent()?);
- if !hir_file.is_derive_attr_pseudo_expansion(db) {
- return None;
- }
+ let derive =
+ sema.descend_into_macros(tok.clone(), 0.into()).into_iter().find_map(|descended| {
+ let hir_file = sema.hir_file_for(&descended.parent()?);
+ if !hir_file.is_derive_attr_pseudo_expansion(db) {
+ return None;
+ }
- let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
- // up map out of the #[derive] expansion
- let token = hir::InFile::new(hir_file, descended).upmap(db)?.value;
- let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
- let expansions = sema.expand_derive_macro(&attr)?;
- let idx = attr
- .token_tree()?
- .token_trees_and_tokens()
- .filter_map(NodeOrToken::into_token)
- .take_while(|it| it != &token)
- .filter(|it| it.kind() == T![,])
- .count();
- let expansion =
- format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?);
- Some(ExpandedMacro { name, expansion })
- });
+ let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
+ // up map out of the #[derive] expansion
+ let token = hir::InFile::new(hir_file, descended).upmap(db)?.value;
+ let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
+ let expansions = sema.expand_derive_macro(&attr)?;
+ let idx = attr
+ .token_tree()?
+ .token_trees_and_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .take_while(|it| it != &token)
+ .filter(|it| it.kind() == T![,])
+ .count();
+ let expansion = format(
+ db,
+ SyntaxKind::MACRO_ITEMS,
+ position.file_id,
+ expansions.get(idx).cloned()?,
+ );
+ Some(ExpandedMacro { name, expansion })
+ });
if derive.is_some() {
return derive;
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index f906182224..3d89599c58 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -17,8 +17,6 @@ use crate::FileRange;
// Extends or shrinks the current selection to the encompassing syntactic construct
// (expression, statement, item, module, etc). It works with multiple cursors.
//
-// This is a standard LSP feature and not a protocol extension.
-//
// |===
// | Editor | Shortcut
//
@@ -142,8 +140,10 @@ fn extend_tokens_from_range(
// compute original mapped token range
let extended = {
- let fst_expanded = sema.descend_into_macros_single(first_token.clone());
- let lst_expanded = sema.descend_into_macros_single(last_token.clone());
+ let fst_expanded =
+ sema.descend_into_macros_single(first_token.clone(), original_range.start());
+ let lst_expanded =
+ sema.descend_into_macros_single(last_token.clone(), original_range.end());
let mut lca =
algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?;
lca = shallowest_node(&lca);
@@ -154,13 +154,16 @@ fn extend_tokens_from_range(
};
// Compute parent node range
- let validate = |token: &SyntaxToken| -> bool {
- let expanded = sema.descend_into_macros_single(token.clone());
- let parent = match expanded.parent() {
- Some(it) => it,
- None => return false,
- };
- algo::least_common_ancestor(&extended, &parent).as_ref() == Some(&extended)
+ let validate = |offset: TextSize| {
+ let extended = &extended;
+ move |token: &SyntaxToken| -> bool {
+ let expanded = sema.descend_into_macros_single(token.clone(), offset);
+ let parent = match expanded.parent() {
+ Some(it) => it,
+ None => return false,
+ };
+ algo::least_common_ancestor(extended, &parent).as_ref() == Some(extended)
+ }
};
// Find the first and last text range under expanded parent
@@ -168,14 +171,14 @@ fn extend_tokens_from_range(
let token = token.prev_token()?;
skip_trivia_token(token, Direction::Prev)
})
- .take_while(validate)
+ .take_while(validate(original_range.start()))
.last()?;
let last = successors(Some(last_token), |token| {
let token = token.next_token()?;
skip_trivia_token(token, Direction::Next)
})
- .take_while(validate)
+ .take_while(validate(original_range.end()))
.last()?;
let range = first.text_range().cover(last.text_range());
diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs
index c39c696cfd..7e0fab4260 100644
--- a/crates/ide/src/goto_declaration.rs
+++ b/crates/ide/src/goto_declaration.rs
@@ -20,16 +20,16 @@ use crate::{
// - fields in patterns will navigate to the field declaration of the struct, union or variant
pub(crate) fn goto_declaration(
db: &RootDatabase,
- position: FilePosition,
+ position @ FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = Semantics::new(db);
- let file = sema.parse(position.file_id).syntax().clone();
+ let file = sema.parse(file_id).syntax().clone();
let original_token = file
- .token_at_offset(position.offset)
+ .token_at_offset(offset)
.find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?;
let range = original_token.text_range();
let info: Vec<NavigationTarget> = sema
- .descend_into_macros(original_token)
+ .descend_into_macros(original_token, offset)
.iter()
.filter_map(|token| {
let parent = token.parent()?;
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 21471ab2a0..e09b9f3914 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -29,45 +29,39 @@ use syntax::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
// image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[]
pub(crate) fn goto_definition(
db: &RootDatabase,
- position: FilePosition,
+ FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = &Semantics::new(db);
- let file = sema.parse(position.file_id).syntax().clone();
- let original_token =
- pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
- IDENT
- | INT_NUMBER
- | LIFETIME_IDENT
- | T![self]
- | T![super]
- | T![crate]
- | T![Self]
- | COMMENT => 4,
- // index and prefix ops
- T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
- kind if kind.is_keyword() => 2,
- T!['('] | T![')'] => 2,
- kind if kind.is_trivia() => 0,
- _ => 1,
- })?;
+ let file = sema.parse(file_id).syntax().clone();
+ let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
+ IDENT
+ | INT_NUMBER
+ | LIFETIME_IDENT
+ | T![self]
+ | T![super]
+ | T![crate]
+ | T![Self]
+ | COMMENT => 4,
+ // index and prefix ops
+ T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
+ kind if kind.is_keyword() => 2,
+ T!['('] | T![')'] => 2,
+ kind if kind.is_trivia() => 0,
+ _ => 1,
+ })?;
if let Some(doc_comment) = token_as_doc_comment(&original_token) {
- return doc_comment.get_definition_with_descend_at(
- sema,
- position.offset,
- |def, _, link_range| {
- let nav = def.try_to_nav(db)?;
- Some(RangeInfo::new(link_range, vec![nav]))
- },
- );
+ return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| {
+ let nav = def.try_to_nav(db)?;
+ Some(RangeInfo::new(link_range, vec![nav]))
+ });
}
let navs = sema
- .descend_into_macros(original_token.clone())
+ .descend_into_macros(original_token.clone(), offset)
.into_iter()
.filter_map(|token| {
let parent = token.parent()?;
if let Some(tt) = ast::TokenTree::cast(parent) {
- if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), position.file_id)
- {
+ if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) {
return Some(vec![x]);
}
}
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 37166bdbd0..544c6b4231 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -22,20 +22,19 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
pub(crate) fn goto_implementation(
db: &RootDatabase,
- position: FilePosition,
+ FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = Semantics::new(db);
- let source_file = sema.parse(position.file_id);
+ let source_file = sema.parse(file_id);
let syntax = source_file.syntax().clone();
- let original_token =
- pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind {
- IDENT | T![self] | INT_NUMBER => 1,
- _ => 0,
- })?;
+ let original_token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
+ IDENT | T![self] | INT_NUMBER => 1,
+ _ => 0,
+ })?;
let range = original_token.text_range();
let navs =
- sema.descend_into_macros(original_token)
+ sema.descend_into_macros(original_token, offset)
.into_iter()
.filter_map(|token| token.parent().and_then(ast::NameLike::cast))
.filter_map(|node| match &node {
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index 6048990f74..955923d769 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -16,13 +16,13 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
pub(crate) fn goto_type_definition(
db: &RootDatabase,
- position: FilePosition,
+ FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = hir::Semantics::new(db);
- let file: ast::SourceFile = sema.parse(position.file_id);
+ let file: ast::SourceFile = sema.parse(file_id);
let token: SyntaxToken =
- pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
+ pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind {
IDENT | INT_NUMBER | T![self] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
@@ -37,7 +37,7 @@ pub(crate) fn goto_type_definition(
}
};
let range = token.text_range();
- sema.descend_into_macros(token)
+ sema.descend_into_macros(token, offset)
.into_iter()
.filter_map(|token| {
let ty = sema
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index 43e89a334b..46a0464e9e 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -15,6 +15,7 @@ use syntax::{
SyntaxKind::{self, IDENT, INT_NUMBER},
SyntaxNode, SyntaxToken, TextRange, T,
};
+use text_edit::TextSize;
use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav};
@@ -51,7 +52,7 @@ pub struct HighlightRelatedConfig {
pub(crate) fn highlight_related(
sema: &Semantics<'_, RootDatabase>,
config: HighlightRelatedConfig,
- FilePosition { offset, file_id }: FilePosition,
+ pos @ FilePosition { offset, file_id }: FilePosition,
) -> Option<Vec<HighlightedRange>> {
let _p = profile::span("highlight_related");
let syntax = sema.parse(file_id).syntax().clone();
@@ -79,7 +80,7 @@ pub(crate) fn highlight_related(
}
T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
- _ if config.references => highlight_references(sema, &syntax, token, file_id),
+ _ if config.references => highlight_references(sema, &syntax, token, pos),
_ => None,
}
}
@@ -129,9 +130,9 @@ fn highlight_references(
sema: &Semantics<'_, RootDatabase>,
node: &SyntaxNode,
token: SyntaxToken,
- file_id: FileId,
+ FilePosition { file_id, offset }: FilePosition,
) -> Option<Vec<HighlightedRange>> {
- let defs = find_defs(sema, token.clone());
+ let defs = find_defs(sema, token.clone(), offset);
let usages = defs
.iter()
.filter_map(|&d| {
@@ -455,8 +456,12 @@ fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange
}
}
-fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> {
- sema.descend_into_macros(token)
+fn find_defs(
+ sema: &Semantics<'_, RootDatabase>,
+ token: SyntaxToken,
+ offset: TextSize,
+) -> FxHashSet<Definition> {
+ sema.descend_into_macros(token, offset)
.into_iter()
.filter_map(|token| IdentClass::classify_token(sema, &token))
.map(IdentClass::definitions_no_ops)
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 40659e6c26..21934b9480 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -162,9 +162,9 @@ fn hover_simple(
// prefer descending the same token kind in attribute expansions, in normal macros text
// equivalency is more important
let descended = if in_attr {
- [sema.descend_into_macros_with_kind_preference(original_token.clone())].into()
+ [sema.descend_into_macros_with_kind_preference(original_token.clone(), offset)].into()
} else {
- sema.descend_into_macros_with_same_text(original_token.clone())
+ sema.descend_into_macros_with_same_text(original_token.clone(), offset)
};
let descended = || descended.iter();
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index ddc71dffa8..d0f9f7b0e1 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -1557,6 +1557,49 @@ fn test_hover_function_show_types() {
}
#[test]
+fn test_hover_function_associated_type_params() {
+ check(
+ r#"
+trait Foo { type Bar; }
+impl Foo for i32 { type Bar = i64; }
+fn foo(arg: <i32 as Foo>::Bar) {}
+fn main() { foo$0; }
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ fn foo(arg: <i32 as Foo>::Bar)
+ ```
+ "#]],
+ );
+
+ check(
+ r#"
+trait Foo<T> { type Bar<U>; }
+impl Foo<i64> for i32 { type Bar<U> = i32; }
+fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>) {}
+fn main() { foo$0; }
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>)
+ ```
+ "#]],
+ );
+}
+
+#[test]
fn test_hover_function_pointer_show_identifiers() {
check(
r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#,
@@ -3292,7 +3335,50 @@ struct S$0T<const C: usize = 1, T = Foo>(T);
```
```rust
- struct ST<const C: usize, T = Foo>
+ struct ST<const C: usize = 1, T = Foo>
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn const_generic_default_value() {
+ check(
+ r#"
+struct Foo;
+struct S$0T<const C: usize = {40 + 2}, T = Foo>(T);
+"#,
+ expect![[r#"
+ *ST*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ struct ST<const C: usize = {const}, T = Foo>
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn const_generic_default_value_2() {
+ check(
+ r#"
+struct Foo;
+const VAL = 1;
+struct S$0T<const C: usize = VAL, T = Foo>(T);
+"#,
+ expect![[r#"
+ *ST*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ struct ST<const C: usize = VAL, T = Foo>
```
"#]],
);
@@ -6469,3 +6555,22 @@ fn test() {
"#]],
);
}
+
+#[test]
+fn generic_params_disabled_by_cfg() {
+ check(
+ r#"
+struct S<#[cfg(never)] T>;
+fn test() {
+ let s$0: S = S;
+}
+"#,
+ expect![[r#"
+ *s*
+
+ ```rust
+ let s: S // size = 0, align = 1
+ ```
+ "#]],
+ );
+}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index bf77d55d58..c9cdbff7d7 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -484,7 +484,7 @@ impl Analysis {
sysroot: Option<&OsStr>,
) -> Cancellable<doc_links::DocumentationLinks> {
self.with_db(|db| {
- doc_links::external_docs(db, &position, target_dir, sysroot).unwrap_or_default()
+ doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default()
})
}
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index 17f3771b1a..2ca2b5b1d5 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -99,7 +99,7 @@ pub(crate) fn moniker(
});
}
let navs = sema
- .descend_into_macros(original_token.clone())
+ .descend_into_macros(original_token.clone(), offset)
.into_iter()
.filter_map(|token| {
IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| {
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index d1479dd1e5..0740bfbc7b 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -175,8 +175,12 @@ impl TryToNav for FileSymbol {
Some(NavigationTarget {
file_id: full_range.file_id,
- name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() },
- alias: if self.is_alias { Some(self.name.clone()) } else { None },
+ name: self
+ .is_alias
+ .then(|| self.def.name(db))
+ .flatten()
+ .map_or_else(|| self.name.clone(), |it| it.to_smol_str()),
+ alias: self.is_alias.then(|| self.name.clone()),
kind: Some(hir::ModuleDefId::from(self.def).into()),
full_range: full_range.range,
focus_range,
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 813f9ed943..2d0295692a 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -126,7 +126,7 @@ pub(crate) fn find_defs<'a>(
)
});
token.map(|token| {
- sema.descend_into_macros_with_same_text(token)
+ sema.descend_into_macros_with_same_text(token, offset)
.into_iter()
.filter_map(|it| ast::NameLike::cast(it.parent()?))
.filter_map(move |name_like| {
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 7795be54e2..847ab3d21a 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -67,17 +67,20 @@ impl SignatureHelp {
}
/// Computes parameter information for the given position.
-pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Option<SignatureHelp> {
+pub(crate) fn signature_help(
+ db: &RootDatabase,
+ FilePosition { file_id, offset }: FilePosition,
+) -> Option<SignatureHelp> {
let sema = Semantics::new(db);
- let file = sema.parse(position.file_id);
+ let file = sema.parse(file_id);
let file = file.syntax();
let token = file
- .token_at_offset(position.offset)
+ .token_at_offset(offset)
.left_biased()
// if the cursor is sandwiched between two space tokens and the call is unclosed
// this prevents us from leaving the CallExpression
.and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
- let token = sema.descend_into_macros_single(token);
+ let token = sema.descend_into_macros_single(token, offset);
for node in token.parent_ancestors() {
match_ast! {
@@ -202,7 +205,7 @@ fn signature_help_for_call(
res.signature.push('(');
{
if let Some((self_param, _)) = callable.receiver_param(db) {
- format_to!(res.signature, "{}", self_param)
+ format_to!(res.signature, "{}", self_param.display(db))
}
let mut buf = String::new();
for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() {
@@ -1315,6 +1318,25 @@ id! {
}
#[test]
+ fn fn_signature_for_method_call_defined_in_macro() {
+ check(
+ r#"
+macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
+struct S;
+id! {
+ impl S {
+ fn foo<'a>(&'a mut self) {}
+ }
+}
+fn test() { S.foo($0); }
+"#,
+ expect![[r#"
+ fn foo(&'a mut self)
+ "#]],
+ );
+ }
+
+ #[test]
fn call_info_for_lambdas() {
check(
r#"
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index ae97236409..bb01c81d66 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -395,10 +395,10 @@ fn traverse(
NodeOrToken::Token(token) if token.kind() != COMMENT => {
let token = match attr_or_derive_item {
Some(AttrOrDerive::Attr(_)) => {
- sema.descend_into_macros_with_kind_preference(token)
+ sema.descend_into_macros_with_kind_preference(token, 0.into())
}
Some(AttrOrDerive::Derive(_)) | None => {
- sema.descend_into_macros_single(token)
+ sema.descend_into_macros_single(token, 0.into())
}
};
match token.parent().and_then(ast::NameLike::cast) {
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 62b2accf5c..7b9bb61e69 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -961,6 +961,7 @@ impl TtTreeSink<'_> {
if has_pseudo_dot {
assert!(right.is_empty(), "{left}.{right}");
} else {
+ assert!(!right.is_empty(), "{left}.{right}");
self.inner.start_node(SyntaxKind::NAME_REF);
self.inner.token(SyntaxKind::INT_NUMBER, right);
self.inner.finish_node();
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 333318f53b..6a2a9adce1 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -184,7 +184,9 @@ pub(crate) mod entry {
};
p.bump_any();
while !p.at(EOF) && !p.at(closing_paren_kind) {
- expressions::expr(p);
+ if expressions::expr(p).is_none() {
+ break;
+ }
if !p.at(EOF) && !p.at(closing_paren_kind) {
p.expect(T![,]);
}
diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs
index 8ed1c84c4c..29d9b05d3f 100644
--- a/crates/parser/src/grammar/generic_params.rs
+++ b/crates/parser/src/grammar/generic_params.rs
@@ -88,7 +88,7 @@ fn const_param(p: &mut Parser<'_>, m: Marker) {
// test const_param_default_path
// struct A<const N: i32 = i32::MAX>;
- generic_args::const_arg_expr(p);
+ generic_args::const_arg(p);
}
m.complete(p, CONST_PARAM);
diff --git a/crates/parser/src/shortcuts.rs b/crates/parser/src/shortcuts.rs
index 53cdad6499..2c47e3d086 100644
--- a/crates/parser/src/shortcuts.rs
+++ b/crates/parser/src/shortcuts.rs
@@ -46,12 +46,16 @@ impl LexedStr<'_> {
// Tag the token as joint if it is float with a fractional part
// we use this jointness to inform the parser about what token split
// event to emit when we encounter a float literal in a field access
- if kind == SyntaxKind::FLOAT_NUMBER && !self.text(i).ends_with('.') {
- res.was_joint();
+ if kind == SyntaxKind::FLOAT_NUMBER {
+ if !self.text(i).ends_with('.') {
+ res.was_joint();
+ } else {
+ was_joint = false;
+ }
+ } else {
+ was_joint = true;
}
}
-
- was_joint = true;
}
}
res
@@ -204,6 +208,7 @@ impl Builder<'_, '_> {
assert!(right.is_empty(), "{left}.{right}");
self.state = State::Normal;
} else {
+ assert!(!right.is_empty(), "{left}.{right}");
(self.sink)(StrStep::Enter { kind: SyntaxKind::NAME_REF });
(self.sink)(StrStep::Token { kind: SyntaxKind::INT_NUMBER, text: right });
(self.sink)(StrStep::Exit);
diff --git a/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
index 809ad1b8d5..49f163b164 100644
--- a/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
+++ b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
@@ -20,7 +20,8 @@ SOURCE_FILE
IDENT "i32"
WHITESPACE " "
EQ "="
- WHITESPACE " "
+ WHITESPACE " "
+ CONST_ARG
COMMA ","
WHITESPACE " "
CONST_PARAM
@@ -37,8 +38,9 @@ SOURCE_FILE
IDENT "i32"
WHITESPACE " "
EQ "="
+ CONST_ARG
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
-error 23: expected a generic const argument
+error 24: expected a generic const argument
error 40: expected a generic const argument
diff --git a/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast b/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast
index 11002bf98d..3f5fb47d28 100644
--- a/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast
+++ b/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast
@@ -21,16 +21,17 @@ SOURCE_FILE
WHITESPACE " "
EQ "="
WHITESPACE " "
- PATH_EXPR
- PATH
+ CONST_ARG
+ PATH_EXPR
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "i32"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "MAX"
+ IDENT "MAX"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast b/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast
index 0607ff54fb..d650113749 100644
--- a/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast
+++ b/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast
@@ -21,14 +21,15 @@ SOURCE_FILE
WHITESPACE " "
EQ "="
WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "1"
- WHITESPACE " "
- R_CURLY "}"
+ CONST_ARG
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE " "
+ R_CURLY "}"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast b/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast
index 8e52313651..6de10353bf 100644
--- a/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast
+++ b/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast
@@ -21,10 +21,11 @@ SOURCE_FILE
WHITESPACE " "
EQ "="
WHITESPACE " "
- PREFIX_EXPR
- MINUS "-"
- LITERAL
- INT_NUMBER "1"
+ CONST_ARG
+ PREFIX_EXPR
+ MINUS "-"
+ LITERAL
+ INT_NUMBER "1"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
diff --git a/crates/profile/src/stop_watch.rs b/crates/profile/src/stop_watch.rs
index 71303d5a63..814a025740 100644
--- a/crates/profile/src/stop_watch.rs
+++ b/crates/profile/src/stop_watch.rs
@@ -10,13 +10,13 @@ pub struct StopWatch {
time: Instant,
#[cfg(target_os = "linux")]
counter: Option<perf_event::Counter>,
- memory: Option<MemoryUsage>,
+ memory: MemoryUsage,
}
pub struct StopWatchSpan {
pub time: Duration,
pub instructions: Option<u64>,
- pub memory: Option<MemoryUsage>,
+ pub memory: MemoryUsage,
}
impl StopWatch {
@@ -45,20 +45,16 @@ impl StopWatch {
None
}
};
+ let memory = MemoryUsage::now();
let time = Instant::now();
StopWatch {
time,
#[cfg(target_os = "linux")]
counter,
- memory: None,
+ memory,
}
}
- pub fn memory(mut self, yes: bool) -> StopWatch {
- if yes {
- self.memory = Some(MemoryUsage::now());
- }
- self
- }
+
pub fn elapsed(&mut self) -> StopWatchSpan {
let time = self.time.elapsed();
@@ -69,7 +65,7 @@ impl StopWatch {
#[cfg(not(target_os = "linux"))]
let instructions = None;
- let memory = self.memory.map(|it| MemoryUsage::now() - it);
+ let memory = MemoryUsage::now() - self.memory;
StopWatchSpan { time, instructions, memory }
}
}
@@ -93,9 +89,7 @@ impl fmt::Display for StopWatchSpan {
}
write!(f, ", {instructions}{prefix}instr")?;
}
- if let Some(memory) = self.memory {
- write!(f, ", {memory}")?;
- }
+ write!(f, ", {}", self.memory)?;
Ok(())
}
}
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index f51ea7eeb2..13463e9f72 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -167,7 +167,8 @@ impl ProjectWorkspace {
cmd.envs(&config.extra_env);
cmd.arg("--version").current_dir(current_dir);
cmd
- })?;
+ })
+ .with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?;
anyhow::Ok(
cargo_version
.get(prefix.len()..)
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 5bfac7ee45..1f9d6db931 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -24,7 +24,7 @@ crossbeam-channel = "0.5.5"
dissimilar = "1.0.4"
itertools = "0.10.5"
scip = "0.1.1"
-lsp-types = { version = "=0.94", features = ["proposed"] }
+lsp-types = { version = "=0.94.0", features = ["proposed"] }
parking_lot = "0.12.1"
xflags = "0.3.0"
oorandom = "11.1.3"
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index f446a7c059..4a03be1893 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -235,9 +235,7 @@ impl flags::AnalysisStats {
if let Some(instructions) = total_span.instructions {
report_metric("total instructions", instructions, "#instr");
}
- if let Some(memory) = total_span.memory {
- report_metric("total memory", memory.allocated.megabytes() as u64, "MB");
- }
+ report_metric("total memory", total_span.memory.allocated.megabytes() as u64, "MB");
if env::var("RA_COUNT").is_ok() {
eprintln!("{}", profile::countme::get_all());
@@ -257,7 +255,7 @@ impl flags::AnalysisStats {
eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}");
}
- if self.memory_usage && verbosity.is_verbose() {
+ if verbosity.is_verbose() {
print_memory_usage(host, vfs);
}
@@ -814,7 +812,7 @@ impl flags::AnalysisStats {
}
fn stop_watch(&self) -> StopWatch {
- StopWatch::start().memory(self.memory_usage)
+ StopWatch::start()
}
}
diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs
index 13b7f039bb..419440b6df 100644
--- a/crates/rust-analyzer/src/cli/flags.rs
+++ b/crates/rust-analyzer/src/cli/flags.rs
@@ -62,8 +62,6 @@ xflags::xflags! {
optional --randomize
/// Run type inference in parallel.
optional --parallel
- /// Collect memory usage statistics.
- optional --memory-usage
/// Print the total length of all source and macro files (whitespace is not counted).
optional --source-stats
@@ -191,7 +189,6 @@ pub struct AnalysisStats {
pub output: Option<OutputFormat>,
pub randomize: bool,
pub parallel: bool,
- pub memory_usage: bool,
pub source_stats: bool,
pub only: Option<String>,
pub with_deps: bool,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index fa20c796ec..40c50f6d17 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -150,18 +150,22 @@ config_data! {
///
/// Set to `"all"` to pass `--all-features` to Cargo.
check_features | checkOnSave_features: Option<CargoFeaturesDef> = "null",
+ /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.
+ ///
+ /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
+ check_ignore: FxHashSet<String> = "[]",
/// Specifies the working directory for running checks.
/// - "workspace": run checks for workspaces in the corresponding workspaces' root directories.
// FIXME: Ideally we would support this in some way
- /// This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.
+ /// This falls back to "root" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`.
/// - "root": run checks in the project's root directory.
- /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+ /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
/// is set.
check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"",
- /// Specifies the invocation strategy to use when running the checkOnSave command.
+ /// Specifies the invocation strategy to use when running the check command.
/// If `per_workspace` is set, the command will be executed for each workspace.
/// If `once` is set, the command will be executed once.
- /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+ /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
/// is set.
check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
/// Whether to pass `--no-default-features` to Cargo. Defaults to
@@ -1098,6 +1102,7 @@ impl Config {
remap_prefix: self.data.diagnostics_remapPrefix.clone(),
warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
+ check_ignore: self.data.check_ignore.clone(),
}
}
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 33422fd058..b65f38a0c7 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -6,6 +6,7 @@ use std::mem;
use ide::FileId;
use ide_db::FxHashMap;
use nohash_hasher::{IntMap, IntSet};
+use rustc_hash::FxHashSet;
use triomphe::Arc;
use crate::lsp_ext;
@@ -17,6 +18,7 @@ pub struct DiagnosticsMapConfig {
pub remap_prefix: FxHashMap<String, String>,
pub warnings_as_info: Vec<String>,
pub warnings_as_hint: Vec<String>,
+ pub check_ignore: FxHashSet<String>,
}
#[derive(Debug, Default, Clone)]
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index e1d1130ff1..06564578d8 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -292,6 +292,13 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
let mut source = String::from("rustc");
let mut code = rd.code.as_ref().map(|c| c.code.clone());
+
+ if let Some(code_val) = &code {
+ if config.check_ignore.contains(code_val) {
+ return Vec::new();
+ }
+ }
+
if let Some(code_val) = &code {
// See if this is an RFC #2103 scoped lint (e.g. from Clippy)
let scoped_code: Vec<&str> = code_val.split("::").collect();
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 138ddd2089..ea7ebd85b3 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -296,7 +296,7 @@ TypeParam =
ConstParam =
Attr* 'const' Name ':' Type
- ('=' default_val:Expr)?
+ ('=' default_val:ConstArg)?
LifetimeParam =
Attr* Lifetime (':' TypeBoundList?)?
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 0b27faa535..16448db04f 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -709,7 +709,7 @@ impl ConstParam {
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
- pub fn default_val(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn default_val(&self) -> Option<ConstArg> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 4c6db0ef06..17e311c0c5 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -503,11 +503,16 @@ pub fn hacky_block_expr(
pub fn expr_unit() -> ast::Expr {
expr_from_text("()")
}
+
pub fn expr_literal(text: &str) -> ast::Literal {
assert_eq!(text.trim(), text);
ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
}
+pub fn expr_const_value(text: &str) -> ast::ConstArg {
+ ast_from_text(&format!("trait Foo<const N: usize = {text}> {{}}"))
+}
+
pub fn expr_empty_block() -> ast::Expr {
expr_from_text("{}")
}
@@ -1100,7 +1105,7 @@ pub mod tokens {
pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
SourceFile::parse(
- "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n",
+ "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n",
)
});
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 3308077da5..691d0c618f 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -61,6 +61,14 @@ impl ast::BlockExpr {
pub fn tail_expr(&self) -> Option<ast::Expr> {
self.stmt_list()?.tail_expr()
}
+ /// Block expressions accept outer and inner attributes, but only when they are the outer
+ /// expression of an expression statement or the final expression of another block expression.
+ pub fn may_carry_attributes(&self) -> bool {
+ matches!(
+ self.syntax().parent().map(|it| it.kind()),
+ Some(SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT)
+ )
+ }
}
#[derive(Debug, PartialEq, Eq, Clone)]
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 4cd668a0cd..27c8a13e58 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -195,11 +195,16 @@ impl ast::TokenTree {
// Tag the token as joint if it is float with a fractional part
// we use this jointness to inform the parser about what token split
// event to emit when we encounter a float literal in a field access
- if kind == SyntaxKind::FLOAT_NUMBER && !t.text().ends_with('.') {
- parser_input.was_joint();
+ if kind == SyntaxKind::FLOAT_NUMBER {
+ if !t.text().ends_with('.') {
+ parser_input.was_joint();
+ } else {
+ was_joint = false;
+ }
+ } else {
+ was_joint = true;
}
}
- was_joint = true;
}
}
@@ -250,6 +255,7 @@ impl ast::TokenTree {
if has_pseudo_dot {
assert!(right.is_empty(), "{left}.{right}");
} else {
+ assert!(!right.is_empty(), "{left}.{right}");
builder.start_node(SyntaxKind::NAME_REF);
builder.token(SyntaxKind::INT_NUMBER, right);
builder.finish_node();
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index ea00c9540f..71feed0f72 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -161,23 +161,30 @@ List of features to activate. Defaults to
Set to `"all"` to pass `--all-features` to Cargo.
--
+[[rust-analyzer.check.ignore]]rust-analyzer.check.ignore (default: `[]`)::
++
+--
+List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.
+
+For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
+--
[[rust-analyzer.check.invocationLocation]]rust-analyzer.check.invocationLocation (default: `"workspace"`)::
+
--
Specifies the working directory for running checks.
- "workspace": run checks for workspaces in the corresponding workspaces' root directories.
- This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.
+ This falls back to "root" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`.
- "root": run checks in the project's root directory.
-This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
is set.
--
[[rust-analyzer.check.invocationStrategy]]rust-analyzer.check.invocationStrategy (default: `"per_workspace"`)::
+
--
-Specifies the invocation strategy to use when running the checkOnSave command.
+Specifies the invocation strategy to use when running the check command.
If `per_workspace` is set, the command will be executed for each workspace.
If `once` is set, the command will be executed once.
-This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
is set.
--
[[rust-analyzer.check.noDefaultFeatures]]rust-analyzer.check.noDefaultFeatures (default: `null`)::
diff --git a/editors/code/package.json b/editors/code/package.json
index 76d7e91f38..44f1b81675 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -292,6 +292,11 @@
"command": "rust-analyzer.viewMemoryLayout",
"title": "View Memory Layout",
"category": "rust-analyzer"
+ },
+ {
+ "command": "rust-analyzer.toggleCheckOnSave",
+ "title": "Toggle Check on Save",
+ "category": "rust-analyzer"
}
],
"keybindings": [
@@ -700,8 +705,17 @@
}
]
},
+ "rust-analyzer.check.ignore": {
+ "markdownDescription": "List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.\n\nFor example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...",
+ "default": [],
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "uniqueItems": true
+ },
"rust-analyzer.check.invocationLocation": {
- "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n This falls back to \"root\" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
+ "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n This falls back to \"root\" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`\nis set.",
"default": "workspace",
"type": "string",
"enum": [
@@ -714,7 +728,7 @@
]
},
"rust-analyzer.check.invocationStrategy": {
- "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
+ "markdownDescription": "Specifies the invocation strategy to use when running the check command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`\nis set.",
"default": "per_workspace",
"type": "string",
"enum": [
diff --git a/editors/code/src/bootstrap.ts b/editors/code/src/bootstrap.ts
index ef4dff095c..6cf399599d 100644
--- a/editors/code/src/bootstrap.ts
+++ b/editors/code/src/bootstrap.ts
@@ -20,7 +20,7 @@ export async function bootstrap(
log.info("Using server binary at", path);
- if (!isValidExecutable(path)) {
+ if (!isValidExecutable(path, config.serverExtraEnv)) {
if (config.serverPath) {
throw new Error(`Failed to execute ${path} --version. \`config.server.path\` or \`config.serverPath\` has been set explicitly.\
Consider removing this config or making a valid server binary available at that path.`);
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index e21f536f26..aba37bac27 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -1407,3 +1407,10 @@ locate()
ctx.pushExtCleanup(document);
};
}
+
+export function toggleCheckOnSave(ctx: Ctx): Cmd {
+ return async () => {
+ await ctx.config.toggleCheckOnSave();
+ ctx.refreshServerStatus();
+ };
+}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 0e64054c11..39e2f767c7 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -216,6 +216,39 @@ export class Config {
),
);
}
+ get checkOnSave() {
+ return this.get<boolean>("checkOnSave") ?? false;
+ }
+ async toggleCheckOnSave() {
+ const config = this.cfg.inspect<boolean>("checkOnSave") ?? { key: "checkOnSave" };
+ let overrideInLanguage;
+ let target;
+ let value;
+ if (
+ config.workspaceFolderValue !== undefined ||
+ config.workspaceFolderLanguageValue !== undefined
+ ) {
+ target = vscode.ConfigurationTarget.WorkspaceFolder;
+ overrideInLanguage = config.workspaceFolderLanguageValue;
+ value = config.workspaceFolderValue || config.workspaceFolderLanguageValue;
+ } else if (
+ config.workspaceValue !== undefined ||
+ config.workspaceLanguageValue !== undefined
+ ) {
+ target = vscode.ConfigurationTarget.Workspace;
+ overrideInLanguage = config.workspaceLanguageValue;
+ value = config.workspaceValue || config.workspaceLanguageValue;
+ } else if (config.globalValue !== undefined || config.globalLanguageValue !== undefined) {
+ target = vscode.ConfigurationTarget.Global;
+ overrideInLanguage = config.globalLanguageValue;
+ value = config.globalValue || config.globalLanguageValue;
+ } else if (config.defaultValue !== undefined || config.defaultLanguageValue !== undefined) {
+ overrideInLanguage = config.defaultLanguageValue;
+ value = config.defaultValue || config.defaultLanguageValue;
+ }
+ await this.cfg.update("checkOnSave", !(value || false), target || null, overrideInLanguage);
+ }
+
get traceExtension() {
return this.get<boolean>("trace.extension");
}
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 16c14ca54f..363a7a82e6 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -94,6 +94,7 @@ export class Ctx {
private unlinkedFiles: vscode.Uri[];
private _dependencies: RustDependenciesProvider | undefined;
private _treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId> | undefined;
+ private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" };
get client() {
return this._client;
@@ -404,7 +405,15 @@ export class Ctx {
}
setServerStatus(status: ServerStatusParams | { health: "stopped" }) {
+ this.lastStatus = status;
+ this.updateStatusBarItem();
+ }
+ refreshServerStatus() {
+ this.updateStatusBarItem();
+ }
+ private updateStatusBarItem() {
let icon = "";
+ const status = this.lastStatus;
const statusBar = this.statusBar;
statusBar.show();
statusBar.tooltip = new vscode.MarkdownString("", true);
@@ -447,13 +456,18 @@ export class Ctx {
"statusBarItem.warningBackground",
);
statusBar.command = "rust-analyzer.startServer";
- statusBar.text = `$(stop-circle) rust-analyzer`;
+ statusBar.text = "$(stop-circle) rust-analyzer";
return;
}
if (statusBar.tooltip.value) {
statusBar.tooltip.appendMarkdown("\n\n---\n\n");
}
- statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)");
+ statusBar.tooltip.appendMarkdown("\n\n[Open Logs](command:rust-analyzer.openLogs)");
+ statusBar.tooltip.appendMarkdown(
+ `\n\n[${
+ this.config.checkOnSave ? "Disable" : "Enable"
+ } Check on Save](command:rust-analyzer.toggleCheckOnSave)`,
+ );
statusBar.tooltip.appendMarkdown(
"\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)",
);
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 448150bac0..ee5e5b1b80 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -180,6 +180,7 @@ function createCommands(): Record<string, CommandFactory> {
ssr: { enabled: commands.ssr },
serverVersion: { enabled: commands.serverVersion },
viewMemoryLayout: { enabled: commands.viewMemoryLayout },
+ toggleCheckOnSave: { enabled: commands.toggleCheckOnSave },
// Internal commands which are invoked by the server.
applyActionGroup: { enabled: commands.applyActionGroup },
applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand },
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 38ce676157..0414ea0f80 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -2,6 +2,7 @@ import * as vscode from "vscode";
import { strict as nativeAssert } from "assert";
import { exec, type ExecOptions, spawnSync } from "child_process";
import { inspect } from "util";
+import type { Env } from "./client";
export function assert(condition: boolean, explanation: string): asserts condition {
try {
@@ -93,10 +94,13 @@ export function isDocumentInWorkspace(document: RustDocument): boolean {
return false;
}
-export function isValidExecutable(path: string): boolean {
+export function isValidExecutable(path: string, extraEnv: Env): boolean {
log.debug("Checking availability of a binary at", path);
- const res = spawnSync(path, ["--version"], { encoding: "utf8" });
+ const res = spawnSync(path, ["--version"], {
+ encoding: "utf8",
+ env: { ...process.env, ...extraEnv },
+ });
const printOutput = res.error ? log.warn : log.info;
printOutput(path, "--version:", res);
@@ -151,6 +155,7 @@ export function execute(command: string, options: ExecOptions): Promise<string>
}
export function executeDiscoverProject(command: string, options: ExecOptions): Promise<string> {
+ options = Object.assign({ maxBuffer: 10 * 1024 * 1024 }, options);
log.info(`running command: ${command}`);
return new Promise((resolve, reject) => {
exec(command, options, (err, stdout, _) => {
diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml
index 01707d3019..e1c49db39d 100644
--- a/lib/lsp-server/Cargo.toml
+++ b/lib/lsp-server/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "lsp-server"
-version = "0.7.2"
+version = "0.7.3"
description = "Generic LSP server scaffold."
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server"
@@ -9,7 +9,8 @@ edition = "2021"
[dependencies]
log = "0.4.17"
serde_json = "1.0.96"
-serde = { version = "1.0.156", features = ["derive"] }
+# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde
+serde = { version = "1.0.156, < 1.0.172", features = ["derive"] }
crossbeam-channel = "0.5.6"
[dev-dependencies]
diff --git a/lib/lsp-server/src/stdio.rs b/lib/lsp-server/src/stdio.rs
index 49a825e579..e487b9b462 100644
--- a/lib/lsp-server/src/stdio.rs
+++ b/lib/lsp-server/src/stdio.rs
@@ -3,6 +3,8 @@ use std::{
thread,
};
+use log::debug;
+
use crossbeam_channel::{bounded, Receiver, Sender};
use crate::Message;
@@ -23,7 +25,8 @@ pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThread
while let Some(msg) = Message::read(&mut stdin)? {
let is_exit = matches!(&msg, Message::Notification(n) if n.is_exit());
- reader_sender.send(msg).unwrap();
+ debug!("sending message {:#?}", msg);
+ reader_sender.send(msg).expect("receiver was dropped, failed to send a message");
if is_exit {
break;
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs
index 6853742319..e471026040 100644
--- a/xtask/src/metrics.rs
+++ b/xtask/src/metrics.rs
@@ -103,9 +103,7 @@ impl Metrics {
path: &str,
) -> anyhow::Result<()> {
eprintln!("\nMeasuring analysis-stats/{name}");
- let output =
- cmd!(sh, "./target/release/rust-analyzer -q analysis-stats --memory-usage {path}")
- .read()?;
+ let output = cmd!(sh, "./target/release/rust-analyzer -q analysis-stats {path}").read()?;
for (metric, value, unit) in parse_metrics(&output) {
self.report(&format!("analysis-stats/{name}/{metric}"), value, unit.into());
}