aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/code_model.rs2095
-rw-r--r--crates/hir/src/from_id.rs5
-rw-r--r--crates/hir/src/lib.rs2126
-rw-r--r--crates/hir/src/semantics.rs5
-rw-r--r--crates/hir/src/source_analyzer.rs5
-rw-r--r--crates/hir_def/Cargo.toml3
-rw-r--r--crates/hir_def/src/adt.rs21
-rw-r--r--crates/hir_def/src/attr.rs5
-rw-r--r--crates/hir_def/src/body.rs14
-rw-r--r--crates/hir_def/src/body/lower.rs160
-rw-r--r--crates/hir_def/src/body/scope.rs4
-rw-r--r--crates/hir_def/src/body/tests.rs3
-rw-r--r--crates/hir_def/src/body/tests/block.rs10
-rw-r--r--crates/hir_def/src/child_by_source.rs50
-rw-r--r--crates/hir_def/src/data.rs4
-rw-r--r--crates/hir_def/src/find_path.rs22
-rw-r--r--crates/hir_def/src/generics.rs4
-rw-r--r--crates/hir_def/src/import_map.rs101
-rw-r--r--crates/hir_def/src/item_scope.rs34
-rw-r--r--crates/hir_def/src/item_tree.rs13
-rw-r--r--crates/hir_def/src/item_tree/lower.rs2
-rw-r--r--crates/hir_def/src/lib.rs56
-rw-r--r--crates/hir_def/src/nameres/collector.rs46
-rw-r--r--crates/hir_def/src/nameres/mod_resolution.rs3
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs27
-rw-r--r--crates/hir_def/src/nameres/tests.rs9
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs5
-rw-r--r--crates/hir_def/src/nameres/tests/globs.rs8
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs10
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs4
-rw-r--r--crates/hir_def/src/path/lower/lower_use.rs3
-rw-r--r--crates/hir_def/src/resolver.rs29
-rw-r--r--crates/hir_def/src/test_db.rs9
-rw-r--r--crates/hir_expand/Cargo.toml6
-rw-r--r--crates/hir_expand/src/builtin_derive.rs66
-rw-r--r--crates/hir_expand/src/builtin_macro.rs92
-rw-r--r--crates/hir_expand/src/name.rs1
-rw-r--r--crates/hir_expand/src/quote.rs5
-rw-r--r--crates/hir_ty/Cargo.toml9
-rw-r--r--crates/hir_ty/src/diagnostics.rs29
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs22
-rw-r--r--crates/hir_ty/src/display.rs4
-rw-r--r--crates/hir_ty/src/infer/coerce.rs5
-rw-r--r--crates/hir_ty/src/infer/expr.rs3
-rw-r--r--crates/hir_ty/src/infer/pat.rs3
-rw-r--r--crates/hir_ty/src/infer/path.rs2
-rw-r--r--crates/hir_ty/src/infer/unify.rs8
-rw-r--r--crates/hir_ty/src/lower.rs40
-rw-r--r--crates/hir_ty/src/method_resolution.rs9
-rw-r--r--crates/hir_ty/src/tests.rs30
-rw-r--r--crates/hir_ty/src/tests/coercion.rs5
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs2
-rw-r--r--crates/hir_ty/src/tests/patterns.rs3
-rw-r--r--crates/hir_ty/src/tests/regression.rs7
-rw-r--r--crates/hir_ty/src/tests/simple.rs3
-rw-r--r--crates/hir_ty/src/tests/traits.rs38
-rw-r--r--crates/hir_ty/src/traits.rs23
-rw-r--r--crates/hir_ty/src/traits/chalk.rs23
-rw-r--r--crates/hir_ty/src/traits/chalk/mapping.rs38
-rw-r--r--crates/hir_ty/src/utils.rs2
-rw-r--r--crates/ide/Cargo.toml3
-rw-r--r--crates/ide/src/display/short_label.rs6
-rw-r--r--crates/ide/src/hover.rs71
-rw-r--r--crates/ide/src/join_lines.rs8
-rw-r--r--crates/ide/src/lib.rs12
-rw-r--r--crates/ide/src/matching_brace.rs5
-rw-r--r--crates/ide/src/parent_module.rs6
-rw-r--r--crates/ide/src/references/rename.rs113
-rw-r--r--crates/ide/src/runnables.rs8
-rw-r--r--crates/ide/src/ssr.rs259
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs5
-rw-r--r--crates/ide/src/typing/on_enter.rs8
-rw-r--r--crates/ide_assists/Cargo.toml3
-rw-r--r--crates/ide_assists/src/handlers/add_turbo_fish.rs55
-rw-r--r--crates/ide_assists/src/handlers/apply_demorgan.rs10
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs51
-rw-r--r--crates/ide_assists/src/handlers/change_visibility.rs7
-rw-r--r--crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs248
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs21
-rw-r--r--crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs6
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs15
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs6
-rw-r--r--crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs11
-rw-r--r--crates/ide_assists/src/handlers/generate_default_from_new.rs17
-rw-r--r--crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs7
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs73
-rw-r--r--crates/ide_assists/src/handlers/infer_function_return_type.rs37
-rw-r--r--crates/ide_assists/src/handlers/inline_function.rs5
-rw-r--r--crates/ide_assists/src/handlers/inline_local_variable.rs19
-rw-r--r--crates/ide_assists/src/handlers/move_module_to_file.rs5
-rw-r--r--crates/ide_assists/src/handlers/pull_assignment_up.rs13
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs57
-rw-r--r--crates/ide_assists/src/handlers/raw_string.rs7
-rw-r--r--crates/ide_assists/src/handlers/remove_unused_param.rs6
-rw-r--r--crates/ide_assists/src/handlers/reorder_fields.rs7
-rw-r--r--crates/ide_assists/src/handlers/reorder_impl.rs7
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs33
-rw-r--r--crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs5
-rw-r--r--crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs7
-rw-r--r--crates/ide_assists/src/handlers/unmerge_use.rs5
-rw-r--r--crates/ide_assists/src/handlers/wrap_return_type_in_result.rs5
-rw-r--r--crates/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests.rs1
-rw-r--r--crates/ide_assists/src/tests/generated.rs30
-rw-r--r--crates/ide_completion/Cargo.toml3
-rw-r--r--crates/ide_completion/src/completions/attribute.rs35
-rw-r--r--crates/ide_completion/src/completions/dot.rs6
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs280
-rw-r--r--crates/ide_completion/src/completions/fn_param.rs7
-rw-r--r--crates/ide_completion/src/completions/keyword.rs58
-rw-r--r--crates/ide_completion/src/completions/mod_.rs6
-rw-r--r--crates/ide_completion/src/completions/postfix.rs21
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs6
-rw-r--r--crates/ide_completion/src/completions/record.rs15
-rw-r--r--crates/ide_completion/src/completions/snippet.rs25
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs32
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs12
-rw-r--r--crates/ide_completion/src/context.rs4
-rw-r--r--crates/ide_completion/src/item.rs257
-rw-r--r--crates/ide_completion/src/lib.rs30
-rw-r--r--crates/ide_completion/src/render.rs271
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs15
-rw-r--r--crates/ide_completion/src/render/const_.rs10
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs28
-rw-r--r--crates/ide_completion/src/render/function.rs24
-rw-r--r--crates/ide_completion/src/render/macro_.rs33
-rw-r--r--crates/ide_completion/src/render/pattern.rs18
-rw-r--r--crates/ide_completion/src/render/type_alias.rs10
-rw-r--r--crates/ide_completion/src/test_utils.rs3
-rw-r--r--crates/ide_db/Cargo.toml3
-rw-r--r--crates/ide_db/src/call_info.rs5
-rw-r--r--crates/ide_db/src/call_info/tests.rs4
-rw-r--r--crates/ide_db/src/helpers.rs10
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs578
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs24
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs44
-rw-r--r--crates/ide_db/src/items_locator.rs (renamed from crates/ide_db/src/imports_locator.rs)79
-rw-r--r--crates/ide_db/src/lib.rs2
-rw-r--r--crates/ide_ssr/Cargo.toml3
-rw-r--r--crates/ide_ssr/src/from_comment.rs32
-rw-r--r--crates/ide_ssr/src/lib.rs11
-rw-r--r--crates/ide_ssr/src/matching.rs3
-rw-r--r--crates/ide_ssr/src/parsing.rs3
-rw-r--r--crates/ide_ssr/src/replacing.rs4
-rw-r--r--crates/ide_ssr/src/resolving.rs9
-rw-r--r--crates/ide_ssr/src/search.rs5
-rw-r--r--crates/ide_ssr/src/tests.rs20
-rw-r--r--crates/mbe/Cargo.toml3
-rw-r--r--crates/mbe/src/lib.rs5
-rw-r--r--crates/mbe/src/tests.rs6
-rw-r--r--crates/parser/src/grammar.rs2
-rw-r--r--crates/proc_macro_api/Cargo.toml3
-rw-r--r--crates/proc_macro_api/src/lib.rs22
-rw-r--r--crates/proc_macro_api/src/version.rs132
-rw-r--r--crates/proc_macro_srv/Cargo.toml2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/mod.rs2
-rw-r--r--crates/project_model/src/cargo_workspace.rs23
-rw-r--r--crates/project_model/src/workspace.rs138
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/bin/flags.rs15
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs6
-rw-r--r--crates/rust-analyzer/src/config.rs73
-rw-r--r--crates/rust-analyzer/src/handlers.rs43
-rw-r--r--crates/rust-analyzer/src/to_proto.rs100
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs97
-rw-r--r--crates/syntax/Cargo.toml5
-rw-r--r--crates/syntax/src/algo.rs26
-rw-r--r--crates/syntax/src/ast/make.rs7
-rw-r--r--crates/syntax/src/lib.rs1
-rw-r--r--crates/syntax/src/tests.rs6
-rw-r--r--crates/syntax/src/utils.rs43
-rw-r--r--crates/test_utils/Cargo.toml1
-rw-r--r--crates/test_utils/src/bench_fixture.rs6
-rw-r--r--crates/test_utils/src/lib.rs142
-rw-r--r--crates/test_utils/src/mark.rs78
175 files changed, 5516 insertions, 4179 deletions
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
deleted file mode 100644
index 9ee4b3059..000000000
--- a/crates/hir/src/code_model.rs
+++ /dev/null
@@ -1,2095 +0,0 @@
1//! FIXME: write short doc here
2use std::{iter, sync::Arc};
3
4use arrayvec::ArrayVec;
5use base_db::{CrateDisplayName, CrateId, Edition, FileId};
6use either::Either;
7use hir_def::{
8 adt::{ReprKind, StructKind, VariantData},
9 expr::{BindingAnnotation, LabelId, Pat, PatId},
10 import_map,
11 item_tree::ItemTreeNode,
12 lang_item::LangItemTarget,
13 path::ModPath,
14 per_ns::PerNs,
15 resolver::{HasResolver, Resolver},
16 src::HasSource as _,
17 type_ref::TypeRef,
18 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
19 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
20 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
21 TypeParamId, UnionId,
22};
23use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility};
24use hir_expand::{
25 diagnostics::DiagnosticSink,
26 name::{name, AsName},
27 MacroDefId, MacroDefKind,
28};
29use hir_ty::{
30 autoderef,
31 display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter},
32 method_resolution,
33 traits::{FnTrait, Solution, SolutionVariables},
34 AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate,
35 InEnvironment, Mutability, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs,
36 TraitEnvironment, Ty, TyDefId, TyVariableKind,
37};
38use rustc_hash::FxHashSet;
39use stdx::{format_to, impl_from};
40use syntax::{
41 ast::{self, AttrsOwner, NameOwner},
42 AstNode, SmolStr,
43};
44use tt::{Ident, Leaf, Literal, TokenTree};
45
46use crate::{
47 db::{DefDatabase, HirDatabase},
48 has_source::HasSource,
49 HirDisplay, InFile, Name,
50};
51
52/// hir::Crate describes a single crate. It's the main interface with which
53/// a crate's dependencies interact. Mostly, it should be just a proxy for the
54/// root module.
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub struct Crate {
57 pub(crate) id: CrateId,
58}
59
60#[derive(Debug)]
61pub struct CrateDependency {
62 pub krate: Crate,
63 pub name: Name,
64}
65
66impl Crate {
67 pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> {
68 db.crate_graph()[self.id]
69 .dependencies
70 .iter()
71 .map(|dep| {
72 let krate = Crate { id: dep.crate_id };
73 let name = dep.as_name();
74 CrateDependency { krate, name }
75 })
76 .collect()
77 }
78
79 // FIXME: add `transitive_reverse_dependencies`.
80 pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> {
81 let crate_graph = db.crate_graph();
82 crate_graph
83 .iter()
84 .filter(|&krate| {
85 crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id)
86 })
87 .map(|id| Crate { id })
88 .collect()
89 }
90
91 pub fn root_module(self, db: &dyn HirDatabase) -> Module {
92 let def_map = db.crate_def_map(self.id);
93 Module { id: def_map.module_id(def_map.root()) }
94 }
95
96 pub fn root_file(self, db: &dyn HirDatabase) -> FileId {
97 db.crate_graph()[self.id].root_file_id
98 }
99
100 pub fn edition(self, db: &dyn HirDatabase) -> Edition {
101 db.crate_graph()[self.id].edition
102 }
103
104 pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> {
105 db.crate_graph()[self.id].display_name.clone()
106 }
107
108 pub fn query_external_importables(
109 self,
110 db: &dyn DefDatabase,
111 query: import_map::Query,
112 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
113 import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item {
114 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
115 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
116 })
117 }
118
119 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
120 db.crate_graph().iter().map(|id| Crate { id }).collect()
121 }
122
123 /// Try to get the root URL of the documentation of a crate.
124 pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> {
125 // Look for #![doc(html_root_url = "...")]
126 let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into()));
127 let doc_attr_q = attrs.by_key("doc");
128
129 if !doc_attr_q.exists() {
130 return None;
131 }
132
133 let doc_url = doc_attr_q.tt_values().map(|tt| {
134 let name = tt.token_trees.iter()
135 .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url"))
136 .skip(2)
137 .next();
138
139 match name {
140 Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text),
141 _ => None
142 }
143 }).flat_map(|t| t).next();
144
145 doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
150pub struct Module {
151 pub(crate) id: ModuleId,
152}
153
154/// The defs which can be visible in the module.
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
156pub enum ModuleDef {
157 Module(Module),
158 Function(Function),
159 Adt(Adt),
160 // Can't be directly declared, but can be imported.
161 Variant(Variant),
162 Const(Const),
163 Static(Static),
164 Trait(Trait),
165 TypeAlias(TypeAlias),
166 BuiltinType(BuiltinType),
167}
168impl_from!(
169 Module,
170 Function,
171 Adt(Struct, Enum, Union),
172 Variant,
173 Const,
174 Static,
175 Trait,
176 TypeAlias,
177 BuiltinType
178 for ModuleDef
179);
180
181impl From<VariantDef> for ModuleDef {
182 fn from(var: VariantDef) -> Self {
183 match var {
184 VariantDef::Struct(t) => Adt::from(t).into(),
185 VariantDef::Union(t) => Adt::from(t).into(),
186 VariantDef::Variant(t) => t.into(),
187 }
188 }
189}
190
191impl ModuleDef {
192 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
193 match self {
194 ModuleDef::Module(it) => it.parent(db),
195 ModuleDef::Function(it) => Some(it.module(db)),
196 ModuleDef::Adt(it) => Some(it.module(db)),
197 ModuleDef::Variant(it) => Some(it.module(db)),
198 ModuleDef::Const(it) => Some(it.module(db)),
199 ModuleDef::Static(it) => Some(it.module(db)),
200 ModuleDef::Trait(it) => Some(it.module(db)),
201 ModuleDef::TypeAlias(it) => Some(it.module(db)),
202 ModuleDef::BuiltinType(_) => None,
203 }
204 }
205
206 pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> {
207 let mut segments = Vec::new();
208 segments.push(self.name(db)?.to_string());
209 for m in self.module(db)?.path_to_root(db) {
210 segments.extend(m.name(db).map(|it| it.to_string()))
211 }
212 segments.reverse();
213 Some(segments.join("::"))
214 }
215
216 pub fn definition_visibility(&self, db: &dyn HirDatabase) -> Option<Visibility> {
217 let module = match self {
218 ModuleDef::Module(it) => it.parent(db)?,
219 ModuleDef::Function(it) => return Some(it.visibility(db)),
220 ModuleDef::Adt(it) => it.module(db),
221 ModuleDef::Variant(it) => {
222 let parent = it.parent_enum(db);
223 let module = it.module(db);
224 return module.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent)));
225 }
226 ModuleDef::Const(it) => return Some(it.visibility(db)),
227 ModuleDef::Static(it) => it.module(db),
228 ModuleDef::Trait(it) => it.module(db),
229 ModuleDef::TypeAlias(it) => return Some(it.visibility(db)),
230 ModuleDef::BuiltinType(_) => return None,
231 };
232
233 module.visibility_of(db, self)
234 }
235
236 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
237 match self {
238 ModuleDef::Adt(it) => Some(it.name(db)),
239 ModuleDef::Trait(it) => Some(it.name(db)),
240 ModuleDef::Function(it) => Some(it.name(db)),
241 ModuleDef::Variant(it) => Some(it.name(db)),
242 ModuleDef::TypeAlias(it) => Some(it.name(db)),
243 ModuleDef::Module(it) => it.name(db),
244 ModuleDef::Const(it) => it.name(db),
245 ModuleDef::Static(it) => it.name(db),
246
247 ModuleDef::BuiltinType(it) => Some(it.name()),
248 }
249 }
250
251 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
252 let id = match self {
253 ModuleDef::Adt(it) => match it {
254 Adt::Struct(it) => it.id.into(),
255 Adt::Enum(it) => it.id.into(),
256 Adt::Union(it) => it.id.into(),
257 },
258 ModuleDef::Trait(it) => it.id.into(),
259 ModuleDef::Function(it) => it.id.into(),
260 ModuleDef::TypeAlias(it) => it.id.into(),
261 ModuleDef::Module(it) => it.id.into(),
262 ModuleDef::Const(it) => it.id.into(),
263 ModuleDef::Static(it) => it.id.into(),
264 _ => return,
265 };
266
267 let module = match self.module(db) {
268 Some(it) => it,
269 None => return,
270 };
271
272 hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink)
273 }
274}
275
276impl Module {
277 /// Name of this module.
278 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
279 let def_map = self.id.def_map(db.upcast());
280 let parent = def_map[self.id.local_id].parent?;
281 def_map[parent].children.iter().find_map(|(name, module_id)| {
282 if *module_id == self.id.local_id {
283 Some(name.clone())
284 } else {
285 None
286 }
287 })
288 }
289
290 /// Returns the crate this module is part of.
291 pub fn krate(self) -> Crate {
292 Crate { id: self.id.krate() }
293 }
294
295 /// Topmost parent of this module. Every module has a `crate_root`, but some
296 /// might be missing `krate`. This can happen if a module's file is not included
297 /// in the module tree of any target in `Cargo.toml`.
298 pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
299 let def_map = db.crate_def_map(self.id.krate());
300 Module { id: def_map.module_id(def_map.root()) }
301 }
302
303 /// Iterates over all child modules.
304 pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> {
305 let def_map = self.id.def_map(db.upcast());
306 let children = def_map[self.id.local_id]
307 .children
308 .iter()
309 .map(|(_, module_id)| Module { id: def_map.module_id(*module_id) })
310 .collect::<Vec<_>>();
311 children.into_iter()
312 }
313
314 /// Finds a parent module.
315 pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> {
316 // FIXME: handle block expressions as modules (their parent is in a different DefMap)
317 let def_map = self.id.def_map(db.upcast());
318 let parent_id = def_map[self.id.local_id].parent?;
319 Some(Module { id: def_map.module_id(parent_id) })
320 }
321
322 pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> {
323 let mut res = vec![self];
324 let mut curr = self;
325 while let Some(next) = curr.parent(db) {
326 res.push(next);
327 curr = next
328 }
329 res
330 }
331
332 /// Returns a `ModuleScope`: a set of items, visible in this module.
333 pub fn scope(
334 self,
335 db: &dyn HirDatabase,
336 visible_from: Option<Module>,
337 ) -> Vec<(Name, ScopeDef)> {
338 self.id.def_map(db.upcast())[self.id.local_id]
339 .scope
340 .entries()
341 .filter_map(|(name, def)| {
342 if let Some(m) = visible_from {
343 let filtered =
344 def.filter_visibility(|vis| vis.is_visible_from(db.upcast(), m.id));
345 if filtered.is_none() && !def.is_none() {
346 None
347 } else {
348 Some((name, filtered))
349 }
350 } else {
351 Some((name, def))
352 }
353 })
354 .flat_map(|(name, def)| {
355 ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item))
356 })
357 .collect()
358 }
359
360 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> {
361 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into())
362 }
363
364 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
365 let _p = profile::span("Module::diagnostics").detail(|| {
366 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
367 });
368 let def_map = self.id.def_map(db.upcast());
369 def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
370 for decl in self.declarations(db) {
371 match decl {
372 crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
373 crate::ModuleDef::Module(m) => {
374 // Only add diagnostics from inline modules
375 if def_map[m.id.local_id].origin.is_inline() {
376 m.diagnostics(db, sink)
377 }
378 }
379 _ => {
380 decl.diagnostics(db, sink);
381 }
382 }
383 }
384
385 for impl_def in self.impl_defs(db) {
386 for item in impl_def.items(db) {
387 if let AssocItem::Function(f) = item {
388 f.diagnostics(db, sink);
389 }
390 }
391 }
392 }
393
394 pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> {
395 let def_map = self.id.def_map(db.upcast());
396 def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect()
397 }
398
399 pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> {
400 let def_map = self.id.def_map(db.upcast());
401 def_map[self.id.local_id].scope.impls().map(Impl::from).collect()
402 }
403
404 /// Finds a path that can be used to refer to the given item from within
405 /// this module, if possible.
406 pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> {
407 hir_def::find_path::find_path(db, item.into(), self.into())
408 }
409
410 /// Finds a path that can be used to refer to the given item from within
411 /// this module, if possible. This is used for returning import paths for use-statements.
412 pub fn find_use_path_prefixed(
413 self,
414 db: &dyn DefDatabase,
415 item: impl Into<ItemInNs>,
416 prefix_kind: PrefixKind,
417 ) -> Option<ModPath> {
418 hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind)
419 }
420}
421
422#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
423pub struct Field {
424 pub(crate) parent: VariantDef,
425 pub(crate) id: LocalFieldId,
426}
427
428#[derive(Debug, PartialEq, Eq)]
429pub enum FieldSource {
430 Named(ast::RecordField),
431 Pos(ast::TupleField),
432}
433
434impl Field {
435 pub fn name(&self, db: &dyn HirDatabase) -> Name {
436 self.parent.variant_data(db).fields()[self.id].name.clone()
437 }
438
439 /// Returns the type as in the signature of the struct (i.e., with
440 /// placeholder types for type parameters). This is good for showing
441 /// signature help, but not so good to actually get the type of the field
442 /// when you actually have a variable of the struct.
443 pub fn signature_ty(&self, db: &dyn HirDatabase) -> Type {
444 let var_id = self.parent.into();
445 let generic_def_id: GenericDefId = match self.parent {
446 VariantDef::Struct(it) => it.id.into(),
447 VariantDef::Union(it) => it.id.into(),
448 VariantDef::Variant(it) => it.parent.id.into(),
449 };
450 let substs = Substs::type_params(db, generic_def_id);
451 let ty = db.field_types(var_id)[self.id].clone().subst(&substs);
452 Type::new(db, self.parent.module(db).id.krate(), var_id, ty)
453 }
454
455 pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
456 self.parent
457 }
458}
459
460impl HasVisibility for Field {
461 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
462 let variant_data = self.parent.variant_data(db);
463 let visibility = &variant_data.fields()[self.id].visibility;
464 let parent_id: hir_def::VariantId = self.parent.into();
465 visibility.resolve(db.upcast(), &parent_id.resolver(db.upcast()))
466 }
467}
468
469#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
470pub struct Struct {
471 pub(crate) id: StructId,
472}
473
474impl Struct {
475 pub fn module(self, db: &dyn HirDatabase) -> Module {
476 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
477 }
478
479 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
480 Some(self.module(db).krate())
481 }
482
483 pub fn name(self, db: &dyn HirDatabase) -> Name {
484 db.struct_data(self.id).name.clone()
485 }
486
487 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
488 db.struct_data(self.id)
489 .variant_data
490 .fields()
491 .iter()
492 .map(|(id, _)| Field { parent: self.into(), id })
493 .collect()
494 }
495
496 pub fn ty(self, db: &dyn HirDatabase) -> Type {
497 Type::from_def(
498 db,
499 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
500 self.id,
501 )
502 }
503
504 pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> {
505 db.struct_data(self.id).repr.clone()
506 }
507
508 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
509 self.variant_data(db).kind()
510 }
511
512 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
513 db.struct_data(self.id).variant_data.clone()
514 }
515}
516
517#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
518pub struct Union {
519 pub(crate) id: UnionId,
520}
521
522impl Union {
523 pub fn name(self, db: &dyn HirDatabase) -> Name {
524 db.union_data(self.id).name.clone()
525 }
526
527 pub fn module(self, db: &dyn HirDatabase) -> Module {
528 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
529 }
530
531 pub fn ty(self, db: &dyn HirDatabase) -> Type {
532 Type::from_def(
533 db,
534 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
535 self.id,
536 )
537 }
538
539 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
540 db.union_data(self.id)
541 .variant_data
542 .fields()
543 .iter()
544 .map(|(id, _)| Field { parent: self.into(), id })
545 .collect()
546 }
547
548 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
549 db.union_data(self.id).variant_data.clone()
550 }
551}
552
553#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
554pub struct Enum {
555 pub(crate) id: EnumId,
556}
557
558impl Enum {
559 pub fn module(self, db: &dyn HirDatabase) -> Module {
560 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
561 }
562
563 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
564 Some(self.module(db).krate())
565 }
566
567 pub fn name(self, db: &dyn HirDatabase) -> Name {
568 db.enum_data(self.id).name.clone()
569 }
570
571 pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> {
572 db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect()
573 }
574
575 pub fn ty(self, db: &dyn HirDatabase) -> Type {
576 Type::from_def(
577 db,
578 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
579 self.id,
580 )
581 }
582}
583
584#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
585pub struct Variant {
586 pub(crate) parent: Enum,
587 pub(crate) id: LocalEnumVariantId,
588}
589
590impl Variant {
591 pub fn module(self, db: &dyn HirDatabase) -> Module {
592 self.parent.module(db)
593 }
594 pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum {
595 self.parent
596 }
597
598 pub fn name(self, db: &dyn HirDatabase) -> Name {
599 db.enum_data(self.parent.id).variants[self.id].name.clone()
600 }
601
602 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
603 self.variant_data(db)
604 .fields()
605 .iter()
606 .map(|(id, _)| Field { parent: self.into(), id })
607 .collect()
608 }
609
610 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
611 self.variant_data(db).kind()
612 }
613
614 pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
615 db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
616 }
617}
618
619/// A Data Type
620#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
621pub enum Adt {
622 Struct(Struct),
623 Union(Union),
624 Enum(Enum),
625}
626impl_from!(Struct, Union, Enum for Adt);
627
628impl Adt {
629 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
630 let subst = db.generic_defaults(self.into());
631 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
632 }
633
634 /// Turns this ADT into a type. Any type parameters of the ADT will be
635 /// turned into unknown types, which is good for e.g. finding the most
636 /// general set of completions, but will not look very nice when printed.
637 pub fn ty(self, db: &dyn HirDatabase) -> Type {
638 let id = AdtId::from(self);
639 Type::from_def(db, id.module(db.upcast()).krate(), id)
640 }
641
642 pub fn module(self, db: &dyn HirDatabase) -> Module {
643 match self {
644 Adt::Struct(s) => s.module(db),
645 Adt::Union(s) => s.module(db),
646 Adt::Enum(e) => e.module(db),
647 }
648 }
649
650 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
651 Some(self.module(db).krate())
652 }
653
654 pub fn name(self, db: &dyn HirDatabase) -> Name {
655 match self {
656 Adt::Struct(s) => s.name(db),
657 Adt::Union(u) => u.name(db),
658 Adt::Enum(e) => e.name(db),
659 }
660 }
661}
662
663#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
664pub enum VariantDef {
665 Struct(Struct),
666 Union(Union),
667 Variant(Variant),
668}
669impl_from!(Struct, Union, Variant for VariantDef);
670
671impl VariantDef {
672 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
673 match self {
674 VariantDef::Struct(it) => it.fields(db),
675 VariantDef::Union(it) => it.fields(db),
676 VariantDef::Variant(it) => it.fields(db),
677 }
678 }
679
680 pub fn module(self, db: &dyn HirDatabase) -> Module {
681 match self {
682 VariantDef::Struct(it) => it.module(db),
683 VariantDef::Union(it) => it.module(db),
684 VariantDef::Variant(it) => it.module(db),
685 }
686 }
687
688 pub fn name(&self, db: &dyn HirDatabase) -> Name {
689 match self {
690 VariantDef::Struct(s) => s.name(db),
691 VariantDef::Union(u) => u.name(db),
692 VariantDef::Variant(e) => e.name(db),
693 }
694 }
695
696 pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
697 match self {
698 VariantDef::Struct(it) => it.variant_data(db),
699 VariantDef::Union(it) => it.variant_data(db),
700 VariantDef::Variant(it) => it.variant_data(db),
701 }
702 }
703}
704
705/// The defs which have a body.
706#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
707pub enum DefWithBody {
708 Function(Function),
709 Static(Static),
710 Const(Const),
711}
712impl_from!(Function, Const, Static for DefWithBody);
713
714impl DefWithBody {
715 pub fn module(self, db: &dyn HirDatabase) -> Module {
716 match self {
717 DefWithBody::Const(c) => c.module(db),
718 DefWithBody::Function(f) => f.module(db),
719 DefWithBody::Static(s) => s.module(db),
720 }
721 }
722
723 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
724 match self {
725 DefWithBody::Function(f) => Some(f.name(db)),
726 DefWithBody::Static(s) => s.name(db),
727 DefWithBody::Const(c) => c.name(db),
728 }
729 }
730}
731
732#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
733pub struct Function {
734 pub(crate) id: FunctionId,
735}
736
737impl Function {
738 pub fn module(self, db: &dyn HirDatabase) -> Module {
739 self.id.lookup(db.upcast()).module(db.upcast()).into()
740 }
741
742 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
743 Some(self.module(db).krate())
744 }
745
746 pub fn name(self, db: &dyn HirDatabase) -> Name {
747 db.function_data(self.id).name.clone()
748 }
749
750 /// Get this function's return type
751 pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
752 let resolver = self.id.resolver(db.upcast());
753 let ret_type = &db.function_data(self.id).ret_type;
754 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
755 let environment = TraitEnvironment::lower(db, &resolver);
756 Type {
757 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
758 ty: InEnvironment { value: Ty::from_hir_ext(&ctx, ret_type).0, environment },
759 }
760 }
761
762 pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
763 if !db.function_data(self.id).has_self_param {
764 return None;
765 }
766 Some(SelfParam { func: self.id })
767 }
768
769 pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
770 let resolver = self.id.resolver(db.upcast());
771 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
772 let environment = TraitEnvironment::lower(db, &resolver);
773 db.function_data(self.id)
774 .params
775 .iter()
776 .map(|type_ref| {
777 let ty = Type {
778 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
779 ty: InEnvironment {
780 value: Ty::from_hir_ext(&ctx, type_ref).0,
781 environment: environment.clone(),
782 },
783 };
784 Param { ty }
785 })
786 .collect()
787 }
788 pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
789 if self.self_param(db).is_none() {
790 return None;
791 }
792 let mut res = self.assoc_fn_params(db);
793 res.remove(0);
794 Some(res)
795 }
796
797 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
798 db.function_data(self.id).is_unsafe
799 }
800
801 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
802 let krate = self.module(db).id.krate();
803 hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink);
804 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
805 hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
806 }
807
808 /// Whether this function declaration has a definition.
809 ///
810 /// This is false in the case of required (not provided) trait methods.
811 pub fn has_body(self, db: &dyn HirDatabase) -> bool {
812 db.function_data(self.id).has_body
813 }
814
815 /// A textual representation of the HIR of this function for debugging purposes.
816 pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
817 let body = db.body(self.id.into());
818
819 let mut result = String::new();
820 format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db));
821 for (id, expr) in body.exprs.iter() {
822 format_to!(result, "{:?}: {:?}\n", id, expr);
823 }
824
825 result
826 }
827}
828
829// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
830pub enum Access {
831 Shared,
832 Exclusive,
833 Owned,
834}
835
836impl From<Mutability> for Access {
837 fn from(mutability: Mutability) -> Access {
838 match mutability {
839 Mutability::Not => Access::Shared,
840 Mutability::Mut => Access::Exclusive,
841 }
842 }
843}
844
845#[derive(Debug)]
846pub struct Param {
847 ty: Type,
848}
849
850impl Param {
851 pub fn ty(&self) -> &Type {
852 &self.ty
853 }
854}
855
856#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
857pub struct SelfParam {
858 func: FunctionId,
859}
860
861impl SelfParam {
862 pub fn access(self, db: &dyn HirDatabase) -> Access {
863 let func_data = db.function_data(self.func);
864 func_data
865 .params
866 .first()
867 .map(|param| match *param {
868 TypeRef::Reference(.., mutability) => match mutability {
869 hir_def::type_ref::Mutability::Shared => Access::Shared,
870 hir_def::type_ref::Mutability::Mut => Access::Exclusive,
871 },
872 _ => Access::Owned,
873 })
874 .unwrap_or(Access::Owned)
875 }
876}
877
878impl HasVisibility for Function {
879 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
880 let function_data = db.function_data(self.id);
881 let visibility = &function_data.visibility;
882 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
883 }
884}
885
886#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
887pub struct Const {
888 pub(crate) id: ConstId,
889}
890
891impl Const {
892 pub fn module(self, db: &dyn HirDatabase) -> Module {
893 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
894 }
895
896 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
897 Some(self.module(db).krate())
898 }
899
900 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
901 db.const_data(self.id).name.clone()
902 }
903}
904
905impl HasVisibility for Const {
906 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
907 let function_data = db.const_data(self.id);
908 let visibility = &function_data.visibility;
909 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
910 }
911}
912
913#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
914pub struct Static {
915 pub(crate) id: StaticId,
916}
917
918impl Static {
919 pub fn module(self, db: &dyn HirDatabase) -> Module {
920 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
921 }
922
923 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
924 Some(self.module(db).krate())
925 }
926
927 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
928 db.static_data(self.id).name.clone()
929 }
930
931 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
932 db.static_data(self.id).mutable
933 }
934}
935
936#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
937pub struct Trait {
938 pub(crate) id: TraitId,
939}
940
941impl Trait {
942 pub fn module(self, db: &dyn HirDatabase) -> Module {
943 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
944 }
945
946 pub fn name(self, db: &dyn HirDatabase) -> Name {
947 db.trait_data(self.id).name.clone()
948 }
949
950 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
951 db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
952 }
953
954 pub fn is_auto(self, db: &dyn HirDatabase) -> bool {
955 db.trait_data(self.id).auto
956 }
957}
958
959#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
960pub struct TypeAlias {
961 pub(crate) id: TypeAliasId,
962}
963
964impl TypeAlias {
965 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
966 let subst = db.generic_defaults(self.id.into());
967 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
968 }
969
970 pub fn module(self, db: &dyn HirDatabase) -> Module {
971 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
972 }
973
974 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
975 Some(self.module(db).krate())
976 }
977
978 pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
979 db.type_alias_data(self.id).type_ref.clone()
980 }
981
982 pub fn ty(self, db: &dyn HirDatabase) -> Type {
983 Type::from_def(db, self.id.lookup(db.upcast()).module(db.upcast()).krate(), self.id)
984 }
985
986 pub fn name(self, db: &dyn HirDatabase) -> Name {
987 db.type_alias_data(self.id).name.clone()
988 }
989}
990
991impl HasVisibility for TypeAlias {
992 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
993 let function_data = db.type_alias_data(self.id);
994 let visibility = &function_data.visibility;
995 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
996 }
997}
998
999#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1000pub struct BuiltinType {
1001 pub(crate) inner: hir_def::builtin_type::BuiltinType,
1002}
1003
1004impl BuiltinType {
1005 pub fn ty(self, db: &dyn HirDatabase, module: Module) -> Type {
1006 let resolver = module.id.resolver(db.upcast());
1007 Type::new_with_resolver(db, &resolver, Ty::builtin(self.inner))
1008 .expect("crate not present in resolver")
1009 }
1010
1011 pub fn name(self) -> Name {
1012 self.inner.as_name()
1013 }
1014}
1015
1016#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1017pub struct MacroDef {
1018 pub(crate) id: MacroDefId,
1019}
1020
1021impl MacroDef {
1022 /// FIXME: right now, this just returns the root module of the crate that
1023 /// defines this macro. The reasons for this is that macros are expanded
1024 /// early, in `hir_expand`, where modules simply do not exist yet.
1025 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
1026 let krate = self.id.krate;
1027 let def_map = db.crate_def_map(krate);
1028 let module_id = def_map.root();
1029 Some(Module { id: def_map.module_id(module_id) })
1030 }
1031
1032 /// XXX: this parses the file
1033 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1034 self.source(db)?.value.name().map(|it| it.as_name())
1035 }
1036
1037 /// Indicate it is a proc-macro
1038 pub fn is_proc_macro(&self) -> bool {
1039 matches!(self.id.kind, MacroDefKind::ProcMacro(_))
1040 }
1041
1042 /// Indicate it is a derive macro
1043 pub fn is_derive_macro(&self) -> bool {
1044 matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_))
1045 }
1046}
1047
1048/// Invariant: `inner.as_assoc_item(db).is_some()`
1049/// We do not actively enforce this invariant.
1050#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1051pub enum AssocItem {
1052 Function(Function),
1053 Const(Const),
1054 TypeAlias(TypeAlias),
1055}
1056pub enum AssocItemContainer {
1057 Trait(Trait),
1058 Impl(Impl),
1059}
1060pub trait AsAssocItem {
1061 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>;
1062}
1063
1064impl AsAssocItem for Function {
1065 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1066 as_assoc_item(db, AssocItem::Function, self.id)
1067 }
1068}
1069impl AsAssocItem for Const {
1070 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1071 as_assoc_item(db, AssocItem::Const, self.id)
1072 }
1073}
1074impl AsAssocItem for TypeAlias {
1075 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1076 as_assoc_item(db, AssocItem::TypeAlias, self.id)
1077 }
1078}
1079impl AsAssocItem for ModuleDef {
1080 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1081 match self {
1082 ModuleDef::Function(it) => it.as_assoc_item(db),
1083 ModuleDef::Const(it) => it.as_assoc_item(db),
1084 ModuleDef::TypeAlias(it) => it.as_assoc_item(db),
1085 _ => None,
1086 }
1087 }
1088}
1089fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem>
1090where
1091 ID: Lookup<Data = AssocItemLoc<AST>>,
1092 DEF: From<ID>,
1093 CTOR: FnOnce(DEF) -> AssocItem,
1094 AST: ItemTreeNode,
1095{
1096 match id.lookup(db.upcast()).container {
1097 AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
1098 AssocContainerId::ContainerId(_) => None,
1099 }
1100}
1101
1102impl AssocItem {
1103 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1104 match self {
1105 AssocItem::Function(it) => Some(it.name(db)),
1106 AssocItem::Const(it) => it.name(db),
1107 AssocItem::TypeAlias(it) => Some(it.name(db)),
1108 }
1109 }
1110 pub fn module(self, db: &dyn HirDatabase) -> Module {
1111 match self {
1112 AssocItem::Function(f) => f.module(db),
1113 AssocItem::Const(c) => c.module(db),
1114 AssocItem::TypeAlias(t) => t.module(db),
1115 }
1116 }
1117 pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer {
1118 let container = match self {
1119 AssocItem::Function(it) => it.id.lookup(db.upcast()).container,
1120 AssocItem::Const(it) => it.id.lookup(db.upcast()).container,
1121 AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container,
1122 };
1123 match container {
1124 AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()),
1125 AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()),
1126 AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"),
1127 }
1128 }
1129
1130 pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
1131 match self.container(db) {
1132 AssocItemContainer::Trait(t) => Some(t),
1133 _ => None,
1134 }
1135 }
1136}
1137
1138impl HasVisibility for AssocItem {
1139 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
1140 match self {
1141 AssocItem::Function(f) => f.visibility(db),
1142 AssocItem::Const(c) => c.visibility(db),
1143 AssocItem::TypeAlias(t) => t.visibility(db),
1144 }
1145 }
1146}
1147
1148#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1149pub enum GenericDef {
1150 Function(Function),
1151 Adt(Adt),
1152 Trait(Trait),
1153 TypeAlias(TypeAlias),
1154 Impl(Impl),
1155 // enum variants cannot have generics themselves, but their parent enums
1156 // can, and this makes some code easier to write
1157 Variant(Variant),
1158 // consts can have type parameters from their parents (i.e. associated consts of traits)
1159 Const(Const),
1160}
1161impl_from!(
1162 Function,
1163 Adt(Struct, Enum, Union),
1164 Trait,
1165 TypeAlias,
1166 Impl,
1167 Variant,
1168 Const
1169 for GenericDef
1170);
1171
1172impl GenericDef {
1173 pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> {
1174 let generics = db.generic_params(self.into());
1175 let ty_params = generics
1176 .types
1177 .iter()
1178 .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } })
1179 .map(GenericParam::TypeParam);
1180 let lt_params = generics
1181 .lifetimes
1182 .iter()
1183 .map(|(local_id, _)| LifetimeParam {
1184 id: LifetimeParamId { parent: self.into(), local_id },
1185 })
1186 .map(GenericParam::LifetimeParam);
1187 let const_params = generics
1188 .consts
1189 .iter()
1190 .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } })
1191 .map(GenericParam::ConstParam);
1192 ty_params.chain(lt_params).chain(const_params).collect()
1193 }
1194
1195 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
1196 let generics = db.generic_params(self.into());
1197 generics
1198 .types
1199 .iter()
1200 .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } })
1201 .collect()
1202 }
1203}
1204
1205#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1206pub struct Local {
1207 pub(crate) parent: DefWithBodyId,
1208 pub(crate) pat_id: PatId,
1209}
1210
1211impl Local {
1212 pub fn is_param(self, db: &dyn HirDatabase) -> bool {
1213 let src = self.source(db);
1214 match src.value {
1215 Either::Left(bind_pat) => {
1216 bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind()))
1217 }
1218 Either::Right(_self_param) => true,
1219 }
1220 }
1221
1222 // FIXME: why is this an option? It shouldn't be?
1223 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1224 let body = db.body(self.parent.into());
1225 match &body[self.pat_id] {
1226 Pat::Bind { name, .. } => Some(name.clone()),
1227 _ => None,
1228 }
1229 }
1230
1231 pub fn is_self(self, db: &dyn HirDatabase) -> bool {
1232 self.name(db) == Some(name![self])
1233 }
1234
1235 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
1236 let body = db.body(self.parent.into());
1237 match &body[self.pat_id] {
1238 Pat::Bind { mode, .. } => match mode {
1239 BindingAnnotation::Mutable | BindingAnnotation::RefMut => true,
1240 _ => false,
1241 },
1242 _ => false,
1243 }
1244 }
1245
1246 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
1247 self.parent.into()
1248 }
1249
1250 pub fn module(self, db: &dyn HirDatabase) -> Module {
1251 self.parent(db).module(db)
1252 }
1253
1254 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1255 let def = DefWithBodyId::from(self.parent);
1256 let infer = db.infer(def);
1257 let ty = infer[self.pat_id].clone();
1258 let krate = def.module(db.upcast()).krate();
1259 Type::new(db, krate, def, ty)
1260 }
1261
1262 pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> {
1263 let (_body, source_map) = db.body_with_source_map(self.parent.into());
1264 let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
1265 let root = src.file_syntax(db.upcast());
1266 src.map(|ast| {
1267 ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root))
1268 })
1269 }
1270}
1271
1272#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1273pub struct Label {
1274 pub(crate) parent: DefWithBodyId,
1275 pub(crate) label_id: LabelId,
1276}
1277
1278impl Label {
1279 pub fn module(self, db: &dyn HirDatabase) -> Module {
1280 self.parent(db).module(db)
1281 }
1282
1283 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
1284 self.parent.into()
1285 }
1286
1287 pub fn name(self, db: &dyn HirDatabase) -> Name {
1288 let body = db.body(self.parent.into());
1289 body[self.label_id].name.clone()
1290 }
1291
1292 pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> {
1293 let (_body, source_map) = db.body_with_source_map(self.parent.into());
1294 let src = source_map.label_syntax(self.label_id);
1295 let root = src.file_syntax(db.upcast());
1296 src.map(|ast| ast.to_node(&root))
1297 }
1298}
1299
1300#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1301pub enum GenericParam {
1302 TypeParam(TypeParam),
1303 LifetimeParam(LifetimeParam),
1304 ConstParam(ConstParam),
1305}
1306impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam);
1307
1308impl GenericParam {
1309 pub fn module(self, db: &dyn HirDatabase) -> Module {
1310 match self {
1311 GenericParam::TypeParam(it) => it.module(db),
1312 GenericParam::LifetimeParam(it) => it.module(db),
1313 GenericParam::ConstParam(it) => it.module(db),
1314 }
1315 }
1316
1317 pub fn name(self, db: &dyn HirDatabase) -> Name {
1318 match self {
1319 GenericParam::TypeParam(it) => it.name(db),
1320 GenericParam::LifetimeParam(it) => it.name(db),
1321 GenericParam::ConstParam(it) => it.name(db),
1322 }
1323 }
1324}
1325
1326#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1327pub struct TypeParam {
1328 pub(crate) id: TypeParamId,
1329}
1330
1331impl TypeParam {
1332 pub fn name(self, db: &dyn HirDatabase) -> Name {
1333 let params = db.generic_params(self.id.parent);
1334 params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing)
1335 }
1336
1337 pub fn module(self, db: &dyn HirDatabase) -> Module {
1338 self.id.parent.module(db.upcast()).into()
1339 }
1340
1341 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1342 let resolver = self.id.parent.resolver(db.upcast());
1343 let environment = TraitEnvironment::lower(db, &resolver);
1344 let ty = Ty::Placeholder(self.id);
1345 Type {
1346 krate: self.id.parent.module(db.upcast()).krate(),
1347 ty: InEnvironment { value: ty, environment },
1348 }
1349 }
1350
1351 pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
1352 db.generic_predicates_for_param(self.id)
1353 .into_iter()
1354 .filter_map(|pred| match &pred.value {
1355 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1356 Some(Trait::from(trait_ref.trait_))
1357 }
1358 _ => None,
1359 })
1360 .collect()
1361 }
1362
1363 pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
1364 let params = db.generic_defaults(self.id.parent);
1365 let local_idx = hir_ty::param_idx(db, self.id)?;
1366 let resolver = self.id.parent.resolver(db.upcast());
1367 let environment = TraitEnvironment::lower(db, &resolver);
1368 let ty = params.get(local_idx)?.clone();
1369 let subst = Substs::type_params(db, self.id.parent);
1370 let ty = ty.subst(&subst.prefix(local_idx));
1371 Some(Type {
1372 krate: self.id.parent.module(db.upcast()).krate(),
1373 ty: InEnvironment { value: ty, environment },
1374 })
1375 }
1376}
1377
1378impl HirDisplay for TypeParam {
1379 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
1380 write!(f, "{}", self.name(f.db))?;
1381 let bounds = f.db.generic_predicates_for_param(self.id);
1382 let substs = Substs::type_params(f.db, self.id.parent);
1383 let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>();
1384 if !(predicates.is_empty() || f.omit_verbose_types()) {
1385 write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?;
1386 }
1387 Ok(())
1388 }
1389}
1390
1391#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1392pub struct LifetimeParam {
1393 pub(crate) id: LifetimeParamId,
1394}
1395
1396impl LifetimeParam {
1397 pub fn name(self, db: &dyn HirDatabase) -> Name {
1398 let params = db.generic_params(self.id.parent);
1399 params.lifetimes[self.id.local_id].name.clone()
1400 }
1401
1402 pub fn module(self, db: &dyn HirDatabase) -> Module {
1403 self.id.parent.module(db.upcast()).into()
1404 }
1405
1406 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1407 self.id.parent.into()
1408 }
1409}
1410
1411#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1412pub struct ConstParam {
1413 pub(crate) id: ConstParamId,
1414}
1415
1416impl ConstParam {
1417 pub fn name(self, db: &dyn HirDatabase) -> Name {
1418 let params = db.generic_params(self.id.parent);
1419 params.consts[self.id.local_id].name.clone()
1420 }
1421
1422 pub fn module(self, db: &dyn HirDatabase) -> Module {
1423 self.id.parent.module(db.upcast()).into()
1424 }
1425
1426 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1427 self.id.parent.into()
1428 }
1429
1430 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1431 let def = self.id.parent;
1432 let krate = def.module(db.upcast()).krate();
1433 Type::new(db, krate, def, db.const_param_ty(self.id))
1434 }
1435}
1436
1437#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1438pub struct Impl {
1439 pub(crate) id: ImplId,
1440}
1441
1442impl Impl {
1443 pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> {
1444 let inherent = db.inherent_impls_in_crate(krate.id);
1445 let trait_ = db.trait_impls_in_crate(krate.id);
1446
1447 inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect()
1448 }
1449 pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> {
1450 let impls = db.trait_impls_in_crate(krate.id);
1451 impls.for_trait(trait_.id).map(Self::from).collect()
1452 }
1453
1454 // FIXME: the return type is wrong. This should be a hir version of
1455 // `TraitRef` (ie, resolved `TypeRef`).
1456 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> {
1457 db.impl_data(self.id).target_trait.clone()
1458 }
1459
1460 pub fn target_ty(self, db: &dyn HirDatabase) -> Type {
1461 let impl_data = db.impl_data(self.id);
1462 let resolver = self.id.resolver(db.upcast());
1463 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
1464 let environment = TraitEnvironment::lower(db, &resolver);
1465 let ty = Ty::from_hir(&ctx, &impl_data.target_type);
1466 Type {
1467 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
1468 ty: InEnvironment { value: ty, environment },
1469 }
1470 }
1471
1472 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
1473 db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect()
1474 }
1475
1476 pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
1477 db.impl_data(self.id).is_negative
1478 }
1479
1480 pub fn module(self, db: &dyn HirDatabase) -> Module {
1481 self.id.lookup(db.upcast()).container.module(db.upcast()).into()
1482 }
1483
1484 pub fn krate(self, db: &dyn HirDatabase) -> Crate {
1485 Crate { id: self.module(db).id.krate() }
1486 }
1487
1488 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
1489 let src = self.source(db)?;
1490 let item = src.file_id.is_builtin_derive(db.upcast())?;
1491 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id);
1492
1493 // FIXME: handle `cfg_attr`
1494 let attr = item
1495 .value
1496 .attrs()
1497 .filter_map(|it| {
1498 let path = ModPath::from_src(it.path()?, &hygenic)?;
1499 if path.as_ident()?.to_string() == "derive" {
1500 Some(it)
1501 } else {
1502 None
1503 }
1504 })
1505 .last()?;
1506
1507 Some(item.with_value(attr))
1508 }
1509}
1510
1511#[derive(Clone, PartialEq, Eq, Debug)]
1512pub struct Type {
1513 krate: CrateId,
1514 ty: InEnvironment<Ty>,
1515}
1516
1517impl Type {
1518 pub(crate) fn new_with_resolver(
1519 db: &dyn HirDatabase,
1520 resolver: &Resolver,
1521 ty: Ty,
1522 ) -> Option<Type> {
1523 let krate = resolver.krate()?;
1524 Some(Type::new_with_resolver_inner(db, krate, resolver, ty))
1525 }
1526 pub(crate) fn new_with_resolver_inner(
1527 db: &dyn HirDatabase,
1528 krate: CrateId,
1529 resolver: &Resolver,
1530 ty: Ty,
1531 ) -> Type {
1532 let environment = TraitEnvironment::lower(db, &resolver);
1533 Type { krate, ty: InEnvironment { value: ty, environment } }
1534 }
1535
1536 fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
1537 let resolver = lexical_env.resolver(db.upcast());
1538 let environment = TraitEnvironment::lower(db, &resolver);
1539 Type { krate, ty: InEnvironment { value: ty, environment } }
1540 }
1541
1542 fn from_def(
1543 db: &dyn HirDatabase,
1544 krate: CrateId,
1545 def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>,
1546 ) -> Type {
1547 let substs = Substs::build_for_def(db, def).fill_with_unknown().build();
1548 let ty = db.ty(def.into()).subst(&substs);
1549 Type::new(db, krate, def, ty)
1550 }
1551
1552 pub fn is_unit(&self) -> bool {
1553 matches!(self.ty.value, Ty::Tuple(0, ..))
1554 }
1555 pub fn is_bool(&self) -> bool {
1556 matches!(self.ty.value, Ty::Scalar(Scalar::Bool))
1557 }
1558
1559 pub fn is_mutable_reference(&self) -> bool {
1560 matches!(self.ty.value, Ty::Ref(Mutability::Mut, ..))
1561 }
1562
1563 pub fn remove_ref(&self) -> Option<Type> {
1564 if let Ty::Ref(.., substs) = &self.ty.value {
1565 Some(self.derived(substs[0].clone()))
1566 } else {
1567 None
1568 }
1569 }
1570
1571 pub fn is_unknown(&self) -> bool {
1572 matches!(self.ty.value, Ty::Unknown)
1573 }
1574
1575 /// Checks that particular type `ty` implements `std::future::Future`.
1576 /// This function is used in `.await` syntax completion.
1577 pub fn impls_future(&self, db: &dyn HirDatabase) -> bool {
1578 // No special case for the type of async block, since Chalk can figure it out.
1579
1580 let krate = self.krate;
1581
1582 let std_future_trait =
1583 db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait());
1584 let std_future_trait = match std_future_trait {
1585 Some(it) => it,
1586 None => return false,
1587 };
1588
1589 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1590 method_resolution::implements_trait(
1591 &canonical_ty,
1592 db,
1593 self.ty.environment.clone(),
1594 krate,
1595 std_future_trait,
1596 )
1597 }
1598
1599 /// Checks that particular type `ty` implements `std::ops::FnOnce`.
1600 ///
1601 /// This function can be used to check if a particular type is callable, since FnOnce is a
1602 /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce.
1603 pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool {
1604 let krate = self.krate;
1605
1606 let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) {
1607 Some(it) => it,
1608 None => return false,
1609 };
1610
1611 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1612 method_resolution::implements_trait_unique(
1613 &canonical_ty,
1614 db,
1615 self.ty.environment.clone(),
1616 krate,
1617 fnonce_trait,
1618 )
1619 }
1620
1621 pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
1622 let trait_ref = hir_ty::TraitRef {
1623 trait_: trait_.id,
1624 substs: Substs::build_for_def(db, trait_.id)
1625 .push(self.ty.value.clone())
1626 .fill(args.iter().map(|t| t.ty.value.clone()))
1627 .build(),
1628 };
1629
1630 let goal = Canonical {
1631 value: hir_ty::InEnvironment::new(
1632 self.ty.environment.clone(),
1633 hir_ty::Obligation::Trait(trait_ref),
1634 ),
1635 kinds: Arc::new([]),
1636 };
1637
1638 db.trait_solve(self.krate, goal).is_some()
1639 }
1640
1641 pub fn normalize_trait_assoc_type(
1642 &self,
1643 db: &dyn HirDatabase,
1644 trait_: Trait,
1645 args: &[Type],
1646 alias: TypeAlias,
1647 ) -> Option<Type> {
1648 let subst = Substs::build_for_def(db, trait_.id)
1649 .push(self.ty.value.clone())
1650 .fill(args.iter().map(|t| t.ty.value.clone()))
1651 .build();
1652 let predicate = ProjectionPredicate {
1653 projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst },
1654 ty: Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)),
1655 };
1656 let goal = Canonical {
1657 value: InEnvironment::new(
1658 self.ty.environment.clone(),
1659 Obligation::Projection(predicate),
1660 ),
1661 kinds: Arc::new([TyVariableKind::General]),
1662 };
1663
1664 match db.trait_solve(self.krate, goal)? {
1665 Solution::Unique(SolutionVariables(subst)) => subst.value.first().cloned(),
1666 Solution::Ambig(_) => None,
1667 }
1668 .map(|ty| Type {
1669 krate: self.krate,
1670 ty: InEnvironment { value: ty, environment: Arc::clone(&self.ty.environment) },
1671 })
1672 }
1673
1674 pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
1675 let lang_item = db.lang_item(self.krate, SmolStr::new("copy"));
1676 let copy_trait = match lang_item {
1677 Some(LangItemTarget::TraitId(it)) => it,
1678 _ => return false,
1679 };
1680 self.impls_trait(db, copy_trait.into(), &[])
1681 }
1682
1683 pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
1684 let def = match self.ty.value {
1685 Ty::FnDef(def, _) => Some(def),
1686 _ => None,
1687 };
1688
1689 let sig = self.ty.value.callable_sig(db)?;
1690 Some(Callable { ty: self.clone(), sig, def, is_bound_method: false })
1691 }
1692
1693 pub fn is_closure(&self) -> bool {
1694 matches!(&self.ty.value, Ty::Closure { .. })
1695 }
1696
1697 pub fn is_fn(&self) -> bool {
1698 matches!(&self.ty.value, Ty::FnDef(..) | Ty::Function { .. })
1699 }
1700
1701 pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
1702 let adt_id = match self.ty.value {
1703 Ty::Adt(hir_ty::AdtId(adt_id), ..) => adt_id,
1704 _ => return false,
1705 };
1706
1707 let adt = adt_id.into();
1708 match adt {
1709 Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)),
1710 _ => false,
1711 }
1712 }
1713
1714 pub fn is_raw_ptr(&self) -> bool {
1715 matches!(&self.ty.value, Ty::Raw(..))
1716 }
1717
1718 pub fn contains_unknown(&self) -> bool {
1719 return go(&self.ty.value);
1720
1721 fn go(ty: &Ty) -> bool {
1722 match ty {
1723 Ty::Unknown => true,
1724 _ => ty.substs().map_or(false, |substs| substs.iter().any(go)),
1725 }
1726 }
1727 }
1728
1729 pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
1730 let (variant_id, substs) = match self.ty.value {
1731 Ty::Adt(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs),
1732 Ty::Adt(hir_ty::AdtId(AdtId::UnionId(u)), ref substs) => (u.into(), substs),
1733 _ => return Vec::new(),
1734 };
1735
1736 db.field_types(variant_id)
1737 .iter()
1738 .map(|(local_id, ty)| {
1739 let def = Field { parent: variant_id.into(), id: local_id };
1740 let ty = ty.clone().subst(substs);
1741 (def, self.derived(ty))
1742 })
1743 .collect()
1744 }
1745
1746 pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> {
1747 if let Ty::Tuple(_, substs) = &self.ty.value {
1748 substs.iter().map(|ty| self.derived(ty.clone())).collect()
1749 } else {
1750 Vec::new()
1751 }
1752 }
1753
1754 pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
1755 // There should be no inference vars in types passed here
1756 // FIXME check that?
1757 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1758 let environment = self.ty.environment.clone();
1759 let ty = InEnvironment { value: canonical, environment };
1760 autoderef(db, Some(self.krate), ty)
1761 .map(|canonical| canonical.value)
1762 .map(move |ty| self.derived(ty))
1763 }
1764
1765 // This would be nicer if it just returned an iterator, but that runs into
1766 // lifetime problems, because we need to borrow temp `CrateImplDefs`.
1767 pub fn iterate_assoc_items<T>(
1768 self,
1769 db: &dyn HirDatabase,
1770 krate: Crate,
1771 mut callback: impl FnMut(AssocItem) -> Option<T>,
1772 ) -> Option<T> {
1773 for krate in self.ty.value.def_crates(db, krate.id)? {
1774 let impls = db.inherent_impls_in_crate(krate);
1775
1776 for impl_def in impls.for_self_ty(&self.ty.value) {
1777 for &item in db.impl_data(*impl_def).items.iter() {
1778 if let Some(result) = callback(item.into()) {
1779 return Some(result);
1780 }
1781 }
1782 }
1783 }
1784 None
1785 }
1786
1787 pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ {
1788 self.ty
1789 .value
1790 .strip_references()
1791 .substs()
1792 .into_iter()
1793 .flat_map(|substs| substs.iter())
1794 .map(move |ty| self.derived(ty.clone()))
1795 }
1796
1797 pub fn iterate_method_candidates<T>(
1798 &self,
1799 db: &dyn HirDatabase,
1800 krate: Crate,
1801 traits_in_scope: &FxHashSet<TraitId>,
1802 name: Option<&Name>,
1803 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
1804 ) -> Option<T> {
1805 // There should be no inference vars in types passed here
1806 // FIXME check that?
1807 // FIXME replace Unknown by bound vars here
1808 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1809
1810 let env = self.ty.environment.clone();
1811 let krate = krate.id;
1812
1813 method_resolution::iterate_method_candidates(
1814 &canonical,
1815 db,
1816 env,
1817 krate,
1818 traits_in_scope,
1819 name,
1820 method_resolution::LookupMode::MethodCall,
1821 |ty, it| match it {
1822 AssocItemId::FunctionId(f) => callback(ty, f.into()),
1823 _ => None,
1824 },
1825 )
1826 }
1827
1828 pub fn iterate_path_candidates<T>(
1829 &self,
1830 db: &dyn HirDatabase,
1831 krate: Crate,
1832 traits_in_scope: &FxHashSet<TraitId>,
1833 name: Option<&Name>,
1834 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
1835 ) -> Option<T> {
1836 // There should be no inference vars in types passed here
1837 // FIXME check that?
1838 // FIXME replace Unknown by bound vars here
1839 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1840
1841 let env = self.ty.environment.clone();
1842 let krate = krate.id;
1843
1844 method_resolution::iterate_method_candidates(
1845 &canonical,
1846 db,
1847 env,
1848 krate,
1849 traits_in_scope,
1850 name,
1851 method_resolution::LookupMode::Path,
1852 |ty, it| callback(ty, it.into()),
1853 )
1854 }
1855
1856 pub fn as_adt(&self) -> Option<Adt> {
1857 let (adt, _subst) = self.ty.value.as_adt()?;
1858 Some(adt.into())
1859 }
1860
1861 pub fn as_dyn_trait(&self) -> Option<Trait> {
1862 self.ty.value.dyn_trait().map(Into::into)
1863 }
1864
1865 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> {
1866 self.ty.value.impl_trait_bounds(db).map(|it| {
1867 it.into_iter()
1868 .filter_map(|pred| match pred {
1869 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1870 Some(Trait::from(trait_ref.trait_))
1871 }
1872 _ => None,
1873 })
1874 .collect()
1875 })
1876 }
1877
1878 pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> {
1879 self.ty.value.associated_type_parent_trait(db).map(Into::into)
1880 }
1881
1882 // FIXME: provide required accessors such that it becomes implementable from outside.
1883 pub fn is_equal_for_find_impls(&self, other: &Type) -> bool {
1884 let rref = other.remove_ref();
1885 self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value))
1886 }
1887
1888 fn derived(&self, ty: Ty) -> Type {
1889 Type {
1890 krate: self.krate,
1891 ty: InEnvironment { value: ty, environment: self.ty.environment.clone() },
1892 }
1893 }
1894
1895 pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) {
1896 // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself.
1897 // We need a different order here.
1898
1899 fn walk_substs(
1900 db: &dyn HirDatabase,
1901 type_: &Type,
1902 substs: &Substs,
1903 cb: &mut impl FnMut(Type),
1904 ) {
1905 for ty in substs.iter() {
1906 walk_type(db, &type_.derived(ty.clone()), cb);
1907 }
1908 }
1909
1910 fn walk_bounds(
1911 db: &dyn HirDatabase,
1912 type_: &Type,
1913 bounds: &[GenericPredicate],
1914 cb: &mut impl FnMut(Type),
1915 ) {
1916 for pred in bounds {
1917 match pred {
1918 GenericPredicate::Implemented(trait_ref) => {
1919 cb(type_.clone());
1920 walk_substs(db, type_, &trait_ref.substs, cb);
1921 }
1922 _ => (),
1923 }
1924 }
1925 }
1926
1927 fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
1928 let ty = type_.ty.value.strip_references();
1929 match ty {
1930 Ty::Adt(..) => {
1931 cb(type_.derived(ty.clone()));
1932 }
1933 Ty::AssociatedType(..) => {
1934 if let Some(_) = ty.associated_type_parent_trait(db) {
1935 cb(type_.derived(ty.clone()));
1936 }
1937 }
1938 Ty::OpaqueType(..) => {
1939 if let Some(bounds) = ty.impl_trait_bounds(db) {
1940 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1941 }
1942 }
1943 Ty::Alias(AliasTy::Opaque(opaque_ty)) => {
1944 if let Some(bounds) = ty.impl_trait_bounds(db) {
1945 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1946 }
1947
1948 walk_substs(db, type_, &opaque_ty.parameters, cb);
1949 }
1950 Ty::Placeholder(_) => {
1951 if let Some(bounds) = ty.impl_trait_bounds(db) {
1952 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1953 }
1954 }
1955 Ty::Dyn(bounds) => {
1956 walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb);
1957 }
1958
1959 _ => {}
1960 }
1961 if let Some(substs) = ty.substs() {
1962 walk_substs(db, type_, &substs, cb);
1963 }
1964 }
1965
1966 walk_type(db, self, &mut cb);
1967 }
1968}
1969
1970impl HirDisplay for Type {
1971 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
1972 self.ty.value.hir_fmt(f)
1973 }
1974}
1975
1976// FIXME: closures
1977#[derive(Debug)]
1978pub struct Callable {
1979 ty: Type,
1980 sig: CallableSig,
1981 def: Option<CallableDefId>,
1982 pub(crate) is_bound_method: bool,
1983}
1984
1985pub enum CallableKind {
1986 Function(Function),
1987 TupleStruct(Struct),
1988 TupleEnumVariant(Variant),
1989 Closure,
1990}
1991
1992impl Callable {
1993 pub fn kind(&self) -> CallableKind {
1994 match self.def {
1995 Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
1996 Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
1997 Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
1998 None => CallableKind::Closure,
1999 }
2000 }
2001 pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
2002 let func = match self.def {
2003 Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
2004 _ => return None,
2005 };
2006 let src = func.lookup(db.upcast()).source(db.upcast());
2007 let param_list = src.value.param_list()?;
2008 param_list.self_param()
2009 }
2010 pub fn n_params(&self) -> usize {
2011 self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
2012 }
2013 pub fn params(
2014 &self,
2015 db: &dyn HirDatabase,
2016 ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> {
2017 let types = self
2018 .sig
2019 .params()
2020 .iter()
2021 .skip(if self.is_bound_method { 1 } else { 0 })
2022 .map(|ty| self.ty.derived(ty.clone()));
2023 let patterns = match self.def {
2024 Some(CallableDefId::FunctionId(func)) => {
2025 let src = func.lookup(db.upcast()).source(db.upcast());
2026 src.value.param_list().map(|param_list| {
2027 param_list
2028 .self_param()
2029 .map(|it| Some(Either::Left(it)))
2030 .filter(|_| !self.is_bound_method)
2031 .into_iter()
2032 .chain(param_list.params().map(|it| it.pat().map(Either::Right)))
2033 })
2034 }
2035 _ => None,
2036 };
2037 patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
2038 }
2039 pub fn return_type(&self) -> Type {
2040 self.ty.derived(self.sig.ret().clone())
2041 }
2042}
2043
2044/// For IDE only
2045#[derive(Debug, PartialEq, Eq, Hash)]
2046pub enum ScopeDef {
2047 ModuleDef(ModuleDef),
2048 MacroDef(MacroDef),
2049 GenericParam(GenericParam),
2050 ImplSelfType(Impl),
2051 AdtSelfType(Adt),
2052 Local(Local),
2053 Unknown,
2054}
2055
2056impl ScopeDef {
2057 pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> {
2058 let mut items = ArrayVec::new();
2059
2060 match (def.take_types(), def.take_values()) {
2061 (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())),
2062 (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())),
2063 (Some(m1), Some(m2)) => {
2064 // Some items, like unit structs and enum variants, are
2065 // returned as both a type and a value. Here we want
2066 // to de-duplicate them.
2067 if m1 != m2 {
2068 items.push(ScopeDef::ModuleDef(m1.into()));
2069 items.push(ScopeDef::ModuleDef(m2.into()));
2070 } else {
2071 items.push(ScopeDef::ModuleDef(m1.into()));
2072 }
2073 }
2074 (None, None) => {}
2075 };
2076
2077 if let Some(macro_def_id) = def.take_macros() {
2078 items.push(ScopeDef::MacroDef(macro_def_id.into()));
2079 }
2080
2081 if items.is_empty() {
2082 items.push(ScopeDef::Unknown);
2083 }
2084
2085 items
2086 }
2087}
2088
2089pub trait HasVisibility {
2090 fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
2091 fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
2092 let vis = self.visibility(db);
2093 vis.is_visible_from(db.upcast(), module.id)
2094 }
2095}
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index b5814da11..179b9d51e 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -11,9 +11,8 @@ use hir_def::{
11}; 11};
12 12
13use crate::{ 13use crate::{
14 code_model::{BuiltinType, GenericParam}, 14 Adt, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam, Label, Local,
15 Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant, 15 MacroDef, ModuleDef, Variant, VariantDef,
16 VariantDef,
17}; 16};
18 17
19macro_rules! from_id { 18macro_rules! from_id {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 69fcdab07..58adc8fd3 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -20,49 +20,2117 @@
20#![recursion_limit = "512"] 20#![recursion_limit = "512"]
21 21
22mod semantics; 22mod semantics;
23pub mod db;
24mod source_analyzer; 23mod source_analyzer;
25 24
26pub mod diagnostics;
27
28mod from_id; 25mod from_id;
29mod code_model;
30mod attrs; 26mod attrs;
31mod has_source; 27mod has_source;
32 28
29pub mod diagnostics;
30pub mod db;
31
32use std::{iter, sync::Arc};
33
34use arrayvec::ArrayVec;
35use base_db::{CrateDisplayName, CrateId, Edition, FileId};
36use either::Either;
37use hir_def::{
38 adt::{ReprKind, VariantData},
39 expr::{BindingAnnotation, LabelId, Pat, PatId},
40 item_tree::ItemTreeNode,
41 lang_item::LangItemTarget,
42 per_ns::PerNs,
43 resolver::{HasResolver, Resolver},
44 src::HasSource as _,
45 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
46 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
47 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
48 TypeParamId, UnionId,
49};
50use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
51use hir_ty::{
52 autoderef,
53 display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter},
54 method_resolution,
55 traits::{FnTrait, Solution, SolutionVariables},
56 AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate,
57 InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs, TraitEnvironment,
58 Ty, TyDefId, TyVariableKind,
59};
60use rustc_hash::FxHashSet;
61use stdx::{format_to, impl_from};
62use syntax::{
63 ast::{self, AttrsOwner, NameOwner},
64 AstNode, SmolStr,
65};
66use tt::{Ident, Leaf, Literal, TokenTree};
67
68use crate::db::{DefDatabase, HirDatabase};
69
33pub use crate::{ 70pub use crate::{
34 attrs::{HasAttrs, Namespace}, 71 attrs::{HasAttrs, Namespace},
35 code_model::{
36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, BuiltinType, Callable,
37 CallableKind, Const, ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field,
38 FieldSource, Function, GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam,
39 Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias,
40 TypeParam, Union, Variant, VariantDef,
41 },
42 has_source::HasSource, 72 has_source::HasSource,
43 semantics::{PathResolution, Semantics, SemanticsScope}, 73 semantics::{PathResolution, Semantics, SemanticsScope},
44}; 74};
45 75
46pub use hir_def::{ 76// Be careful with these re-exports.
47 adt::StructKind, 77//
48 attr::{Attrs, Documentation}, 78// `hir` is the boundary between the compiler and the IDE. It should try hard to
49 body::scope::ExprScopes, 79// isolate the compiler from the ide, to allow the two to be refactored
50 find_path::PrefixKind, 80// independently. Re-exporting something from the compiler is the sure way to
51 import_map, 81// breach the boundary.
52 item_scope::ItemInNs, 82//
53 nameres::ModuleSource, 83// Generally, a refactoring which *removes* a name from this list is a good
54 path::{ModPath, PathKind}, 84// idea!
55 type_ref::{Mutability, TypeRef}, 85pub use {
56 visibility::Visibility, 86 hir_def::{
57}; 87 adt::StructKind,
58pub use hir_expand::{ 88 attr::{Attrs, Documentation},
59 name::{known, AsName, Name}, 89 body::scope::ExprScopes,
60 ExpandResult, HirFileId, InFile, MacroCallId, MacroCallLoc, /* FIXME */ MacroDefId, 90 find_path::PrefixKind,
61 MacroFile, Origin, 91 import_map,
92 item_scope::ItemInNs,
93 nameres::ModuleSource,
94 path::{ModPath, PathKind},
95 type_ref::{Mutability, TypeRef},
96 visibility::Visibility,
97 },
98 hir_expand::{
99 name::{known, Name},
100 ExpandResult, HirFileId, InFile, MacroCallId, MacroCallLoc, /* FIXME */ MacroDefId,
101 MacroFile, Origin,
102 },
103 hir_ty::display::HirDisplay,
62}; 104};
63pub use hir_ty::display::HirDisplay;
64 105
65// These are negative re-exports: pub using these names is forbidden, they 106// These are negative re-exports: pub using these names is forbidden, they
66// should remain private to hir internals. 107// should remain private to hir internals.
67#[allow(unused)] 108#[allow(unused)]
68use {hir_def::path::Path, hir_expand::hygiene::Hygiene}; 109use {
110 hir_def::path::Path,
111 hir_expand::{hygiene::Hygiene, name::AsName},
112};
113
114/// hir::Crate describes a single crate. It's the main interface with which
115/// a crate's dependencies interact. Mostly, it should be just a proxy for the
116/// root module.
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118pub struct Crate {
119 pub(crate) id: CrateId,
120}
121
122#[derive(Debug)]
123pub struct CrateDependency {
124 pub krate: Crate,
125 pub name: Name,
126}
127
128impl Crate {
129 pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> {
130 db.crate_graph()[self.id]
131 .dependencies
132 .iter()
133 .map(|dep| {
134 let krate = Crate { id: dep.crate_id };
135 let name = dep.as_name();
136 CrateDependency { krate, name }
137 })
138 .collect()
139 }
140
141 // FIXME: add `transitive_reverse_dependencies`.
142 pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> {
143 let crate_graph = db.crate_graph();
144 crate_graph
145 .iter()
146 .filter(|&krate| {
147 crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id)
148 })
149 .map(|id| Crate { id })
150 .collect()
151 }
152
153 pub fn root_module(self, db: &dyn HirDatabase) -> Module {
154 let def_map = db.crate_def_map(self.id);
155 Module { id: def_map.module_id(def_map.root()) }
156 }
157
158 pub fn root_file(self, db: &dyn HirDatabase) -> FileId {
159 db.crate_graph()[self.id].root_file_id
160 }
161
162 pub fn edition(self, db: &dyn HirDatabase) -> Edition {
163 db.crate_graph()[self.id].edition
164 }
165
166 pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> {
167 db.crate_graph()[self.id].display_name.clone()
168 }
169
170 pub fn query_external_importables(
171 self,
172 db: &dyn DefDatabase,
173 query: import_map::Query,
174 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
175 import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item {
176 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
177 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
178 })
179 }
180
181 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
182 db.crate_graph().iter().map(|id| Crate { id }).collect()
183 }
184
185 /// Try to get the root URL of the documentation of a crate.
186 pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> {
187 // Look for #![doc(html_root_url = "...")]
188 let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into()));
189 let doc_attr_q = attrs.by_key("doc");
190
191 if !doc_attr_q.exists() {
192 return None;
193 }
194
195 let doc_url = doc_attr_q.tt_values().map(|tt| {
196 let name = tt.token_trees.iter()
197 .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url"))
198 .skip(2)
199 .next();
200
201 match name {
202 Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text),
203 _ => None
204 }
205 }).flat_map(|t| t).next();
206
207 doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
212pub struct Module {
213 pub(crate) id: ModuleId,
214}
215
216/// The defs which can be visible in the module.
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
218pub enum ModuleDef {
219 Module(Module),
220 Function(Function),
221 Adt(Adt),
222 // Can't be directly declared, but can be imported.
223 Variant(Variant),
224 Const(Const),
225 Static(Static),
226 Trait(Trait),
227 TypeAlias(TypeAlias),
228 BuiltinType(BuiltinType),
229}
230impl_from!(
231 Module,
232 Function,
233 Adt(Struct, Enum, Union),
234 Variant,
235 Const,
236 Static,
237 Trait,
238 TypeAlias,
239 BuiltinType
240 for ModuleDef
241);
242
243impl From<VariantDef> for ModuleDef {
244 fn from(var: VariantDef) -> Self {
245 match var {
246 VariantDef::Struct(t) => Adt::from(t).into(),
247 VariantDef::Union(t) => Adt::from(t).into(),
248 VariantDef::Variant(t) => t.into(),
249 }
250 }
251}
252
253impl ModuleDef {
254 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
255 match self {
256 ModuleDef::Module(it) => it.parent(db),
257 ModuleDef::Function(it) => Some(it.module(db)),
258 ModuleDef::Adt(it) => Some(it.module(db)),
259 ModuleDef::Variant(it) => Some(it.module(db)),
260 ModuleDef::Const(it) => Some(it.module(db)),
261 ModuleDef::Static(it) => Some(it.module(db)),
262 ModuleDef::Trait(it) => Some(it.module(db)),
263 ModuleDef::TypeAlias(it) => Some(it.module(db)),
264 ModuleDef::BuiltinType(_) => None,
265 }
266 }
267
268 pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> {
269 let mut segments = Vec::new();
270 segments.push(self.name(db)?.to_string());
271 for m in self.module(db)?.path_to_root(db) {
272 segments.extend(m.name(db).map(|it| it.to_string()))
273 }
274 segments.reverse();
275 Some(segments.join("::"))
276 }
277
278 pub fn definition_visibility(&self, db: &dyn HirDatabase) -> Option<Visibility> {
279 let module = match self {
280 ModuleDef::Module(it) => it.parent(db)?,
281 ModuleDef::Function(it) => return Some(it.visibility(db)),
282 ModuleDef::Adt(it) => it.module(db),
283 ModuleDef::Variant(it) => {
284 let parent = it.parent_enum(db);
285 let module = it.module(db);
286 return module.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent)));
287 }
288 ModuleDef::Const(it) => return Some(it.visibility(db)),
289 ModuleDef::Static(it) => it.module(db),
290 ModuleDef::Trait(it) => it.module(db),
291 ModuleDef::TypeAlias(it) => return Some(it.visibility(db)),
292 ModuleDef::BuiltinType(_) => return None,
293 };
294
295 module.visibility_of(db, self)
296 }
297
298 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
299 match self {
300 ModuleDef::Adt(it) => Some(it.name(db)),
301 ModuleDef::Trait(it) => Some(it.name(db)),
302 ModuleDef::Function(it) => Some(it.name(db)),
303 ModuleDef::Variant(it) => Some(it.name(db)),
304 ModuleDef::TypeAlias(it) => Some(it.name(db)),
305 ModuleDef::Module(it) => it.name(db),
306 ModuleDef::Const(it) => it.name(db),
307 ModuleDef::Static(it) => it.name(db),
308 ModuleDef::BuiltinType(it) => Some(it.name()),
309 }
310 }
311
312 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
313 let id = match self {
314 ModuleDef::Adt(it) => match it {
315 Adt::Struct(it) => it.id.into(),
316 Adt::Enum(it) => it.id.into(),
317 Adt::Union(it) => it.id.into(),
318 },
319 ModuleDef::Trait(it) => it.id.into(),
320 ModuleDef::Function(it) => it.id.into(),
321 ModuleDef::TypeAlias(it) => it.id.into(),
322 ModuleDef::Module(it) => it.id.into(),
323 ModuleDef::Const(it) => it.id.into(),
324 ModuleDef::Static(it) => it.id.into(),
325 _ => return,
326 };
327
328 let module = match self.module(db) {
329 Some(it) => it,
330 None => return,
331 };
332
333 hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink)
334 }
335}
336
337impl Module {
338 /// Name of this module.
339 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
340 let def_map = self.id.def_map(db.upcast());
341 let parent = def_map[self.id.local_id].parent?;
342 def_map[parent].children.iter().find_map(|(name, module_id)| {
343 if *module_id == self.id.local_id {
344 Some(name.clone())
345 } else {
346 None
347 }
348 })
349 }
350
351 /// Returns the crate this module is part of.
352 pub fn krate(self) -> Crate {
353 Crate { id: self.id.krate() }
354 }
355
356 /// Topmost parent of this module. Every module has a `crate_root`, but some
357 /// might be missing `krate`. This can happen if a module's file is not included
358 /// in the module tree of any target in `Cargo.toml`.
359 pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
360 let def_map = db.crate_def_map(self.id.krate());
361 Module { id: def_map.module_id(def_map.root()) }
362 }
363
364 /// Iterates over all child modules.
365 pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> {
366 let def_map = self.id.def_map(db.upcast());
367 let children = def_map[self.id.local_id]
368 .children
369 .iter()
370 .map(|(_, module_id)| Module { id: def_map.module_id(*module_id) })
371 .collect::<Vec<_>>();
372 children.into_iter()
373 }
374
375 /// Finds a parent module.
376 pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> {
377 // FIXME: handle block expressions as modules (their parent is in a different DefMap)
378 let def_map = self.id.def_map(db.upcast());
379 let parent_id = def_map[self.id.local_id].parent?;
380 Some(Module { id: def_map.module_id(parent_id) })
381 }
382
383 pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> {
384 let mut res = vec![self];
385 let mut curr = self;
386 while let Some(next) = curr.parent(db) {
387 res.push(next);
388 curr = next
389 }
390 res
391 }
392
393 /// Returns a `ModuleScope`: a set of items, visible in this module.
394 pub fn scope(
395 self,
396 db: &dyn HirDatabase,
397 visible_from: Option<Module>,
398 ) -> Vec<(Name, ScopeDef)> {
399 self.id.def_map(db.upcast())[self.id.local_id]
400 .scope
401 .entries()
402 .filter_map(|(name, def)| {
403 if let Some(m) = visible_from {
404 let filtered =
405 def.filter_visibility(|vis| vis.is_visible_from(db.upcast(), m.id));
406 if filtered.is_none() && !def.is_none() {
407 None
408 } else {
409 Some((name, filtered))
410 }
411 } else {
412 Some((name, def))
413 }
414 })
415 .flat_map(|(name, def)| {
416 ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item))
417 })
418 .collect()
419 }
420
421 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> {
422 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into())
423 }
424
425 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
426 let _p = profile::span("Module::diagnostics").detail(|| {
427 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
428 });
429 let def_map = self.id.def_map(db.upcast());
430 def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
431 for decl in self.declarations(db) {
432 match decl {
433 crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
434 crate::ModuleDef::Module(m) => {
435 // Only add diagnostics from inline modules
436 if def_map[m.id.local_id].origin.is_inline() {
437 m.diagnostics(db, sink)
438 }
439 }
440 _ => {
441 decl.diagnostics(db, sink);
442 }
443 }
444 }
445
446 for impl_def in self.impl_defs(db) {
447 for item in impl_def.items(db) {
448 if let AssocItem::Function(f) = item {
449 f.diagnostics(db, sink);
450 }
451 }
452 }
453 }
454
455 pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> {
456 let def_map = self.id.def_map(db.upcast());
457 def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect()
458 }
459
460 pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> {
461 let def_map = self.id.def_map(db.upcast());
462 def_map[self.id.local_id].scope.impls().map(Impl::from).collect()
463 }
464
465 /// Finds a path that can be used to refer to the given item from within
466 /// this module, if possible.
467 pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> {
468 hir_def::find_path::find_path(db, item.into(), self.into())
469 }
470
471 /// Finds a path that can be used to refer to the given item from within
472 /// this module, if possible. This is used for returning import paths for use-statements.
473 pub fn find_use_path_prefixed(
474 self,
475 db: &dyn DefDatabase,
476 item: impl Into<ItemInNs>,
477 prefix_kind: PrefixKind,
478 ) -> Option<ModPath> {
479 hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind)
480 }
481}
482
483#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
484pub struct Field {
485 pub(crate) parent: VariantDef,
486 pub(crate) id: LocalFieldId,
487}
488
489#[derive(Debug, PartialEq, Eq)]
490pub enum FieldSource {
491 Named(ast::RecordField),
492 Pos(ast::TupleField),
493}
494
495impl Field {
496 pub fn name(&self, db: &dyn HirDatabase) -> Name {
497 self.parent.variant_data(db).fields()[self.id].name.clone()
498 }
499
500 /// Returns the type as in the signature of the struct (i.e., with
501 /// placeholder types for type parameters). This is good for showing
502 /// signature help, but not so good to actually get the type of the field
503 /// when you actually have a variable of the struct.
504 pub fn signature_ty(&self, db: &dyn HirDatabase) -> Type {
505 let var_id = self.parent.into();
506 let generic_def_id: GenericDefId = match self.parent {
507 VariantDef::Struct(it) => it.id.into(),
508 VariantDef::Union(it) => it.id.into(),
509 VariantDef::Variant(it) => it.parent.id.into(),
510 };
511 let substs = Substs::type_params(db, generic_def_id);
512 let ty = db.field_types(var_id)[self.id].clone().subst(&substs);
513 Type::new(db, self.parent.module(db).id.krate(), var_id, ty)
514 }
515
516 pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
517 self.parent
518 }
519}
520
521impl HasVisibility for Field {
522 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
523 let variant_data = self.parent.variant_data(db);
524 let visibility = &variant_data.fields()[self.id].visibility;
525 let parent_id: hir_def::VariantId = self.parent.into();
526 visibility.resolve(db.upcast(), &parent_id.resolver(db.upcast()))
527 }
528}
529
530#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
531pub struct Struct {
532 pub(crate) id: StructId,
533}
534
535impl Struct {
536 pub fn module(self, db: &dyn HirDatabase) -> Module {
537 Module { id: self.id.lookup(db.upcast()).container }
538 }
539
540 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
541 Some(self.module(db).krate())
542 }
543
544 pub fn name(self, db: &dyn HirDatabase) -> Name {
545 db.struct_data(self.id).name.clone()
546 }
547
548 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
549 db.struct_data(self.id)
550 .variant_data
551 .fields()
552 .iter()
553 .map(|(id, _)| Field { parent: self.into(), id })
554 .collect()
555 }
556
557 pub fn ty(self, db: &dyn HirDatabase) -> Type {
558 Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id)
559 }
560
561 pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> {
562 db.struct_data(self.id).repr.clone()
563 }
564
565 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
566 self.variant_data(db).kind()
567 }
568
569 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
570 db.struct_data(self.id).variant_data.clone()
571 }
572}
573
574#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
575pub struct Union {
576 pub(crate) id: UnionId,
577}
578
579impl Union {
580 pub fn name(self, db: &dyn HirDatabase) -> Name {
581 db.union_data(self.id).name.clone()
582 }
583
584 pub fn module(self, db: &dyn HirDatabase) -> Module {
585 Module { id: self.id.lookup(db.upcast()).container }
586 }
587
588 pub fn ty(self, db: &dyn HirDatabase) -> Type {
589 Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id)
590 }
591
592 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
593 db.union_data(self.id)
594 .variant_data
595 .fields()
596 .iter()
597 .map(|(id, _)| Field { parent: self.into(), id })
598 .collect()
599 }
600
601 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
602 db.union_data(self.id).variant_data.clone()
603 }
604}
605
606#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
607pub struct Enum {
608 pub(crate) id: EnumId,
609}
610
611impl Enum {
612 pub fn module(self, db: &dyn HirDatabase) -> Module {
613 Module { id: self.id.lookup(db.upcast()).container }
614 }
615
616 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
617 Some(self.module(db).krate())
618 }
619
620 pub fn name(self, db: &dyn HirDatabase) -> Name {
621 db.enum_data(self.id).name.clone()
622 }
623
624 pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> {
625 db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect()
626 }
627
628 pub fn ty(self, db: &dyn HirDatabase) -> Type {
629 Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id)
630 }
631}
632
633#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
634pub struct Variant {
635 pub(crate) parent: Enum,
636 pub(crate) id: LocalEnumVariantId,
637}
638
639impl Variant {
640 pub fn module(self, db: &dyn HirDatabase) -> Module {
641 self.parent.module(db)
642 }
643 pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum {
644 self.parent
645 }
646
647 pub fn name(self, db: &dyn HirDatabase) -> Name {
648 db.enum_data(self.parent.id).variants[self.id].name.clone()
649 }
650
651 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
652 self.variant_data(db)
653 .fields()
654 .iter()
655 .map(|(id, _)| Field { parent: self.into(), id })
656 .collect()
657 }
658
659 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
660 self.variant_data(db).kind()
661 }
662
663 pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
664 db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
665 }
666}
667
668/// A Data Type
669#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
670pub enum Adt {
671 Struct(Struct),
672 Union(Union),
673 Enum(Enum),
674}
675impl_from!(Struct, Union, Enum for Adt);
676
677impl Adt {
678 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
679 let subst = db.generic_defaults(self.into());
680 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
681 }
682
683 /// Turns this ADT into a type. Any type parameters of the ADT will be
684 /// turned into unknown types, which is good for e.g. finding the most
685 /// general set of completions, but will not look very nice when printed.
686 pub fn ty(self, db: &dyn HirDatabase) -> Type {
687 let id = AdtId::from(self);
688 Type::from_def(db, id.module(db.upcast()).krate(), id)
689 }
690
691 pub fn module(self, db: &dyn HirDatabase) -> Module {
692 match self {
693 Adt::Struct(s) => s.module(db),
694 Adt::Union(s) => s.module(db),
695 Adt::Enum(e) => e.module(db),
696 }
697 }
698
699 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
700 Some(self.module(db).krate())
701 }
702
703 pub fn name(self, db: &dyn HirDatabase) -> Name {
704 match self {
705 Adt::Struct(s) => s.name(db),
706 Adt::Union(u) => u.name(db),
707 Adt::Enum(e) => e.name(db),
708 }
709 }
710}
711
712#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
713pub enum VariantDef {
714 Struct(Struct),
715 Union(Union),
716 Variant(Variant),
717}
718impl_from!(Struct, Union, Variant for VariantDef);
719
720impl VariantDef {
721 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
722 match self {
723 VariantDef::Struct(it) => it.fields(db),
724 VariantDef::Union(it) => it.fields(db),
725 VariantDef::Variant(it) => it.fields(db),
726 }
727 }
728
729 pub fn module(self, db: &dyn HirDatabase) -> Module {
730 match self {
731 VariantDef::Struct(it) => it.module(db),
732 VariantDef::Union(it) => it.module(db),
733 VariantDef::Variant(it) => it.module(db),
734 }
735 }
736
737 pub fn name(&self, db: &dyn HirDatabase) -> Name {
738 match self {
739 VariantDef::Struct(s) => s.name(db),
740 VariantDef::Union(u) => u.name(db),
741 VariantDef::Variant(e) => e.name(db),
742 }
743 }
744
745 pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
746 match self {
747 VariantDef::Struct(it) => it.variant_data(db),
748 VariantDef::Union(it) => it.variant_data(db),
749 VariantDef::Variant(it) => it.variant_data(db),
750 }
751 }
752}
753
754/// The defs which have a body.
755#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
756pub enum DefWithBody {
757 Function(Function),
758 Static(Static),
759 Const(Const),
760}
761impl_from!(Function, Const, Static for DefWithBody);
762
763impl DefWithBody {
764 pub fn module(self, db: &dyn HirDatabase) -> Module {
765 match self {
766 DefWithBody::Const(c) => c.module(db),
767 DefWithBody::Function(f) => f.module(db),
768 DefWithBody::Static(s) => s.module(db),
769 }
770 }
771
772 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
773 match self {
774 DefWithBody::Function(f) => Some(f.name(db)),
775 DefWithBody::Static(s) => s.name(db),
776 DefWithBody::Const(c) => c.name(db),
777 }
778 }
779}
780
781#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
782pub struct Function {
783 pub(crate) id: FunctionId,
784}
785
786impl Function {
787 pub fn module(self, db: &dyn HirDatabase) -> Module {
788 self.id.lookup(db.upcast()).module(db.upcast()).into()
789 }
790
791 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
792 Some(self.module(db).krate())
793 }
794
795 pub fn name(self, db: &dyn HirDatabase) -> Name {
796 db.function_data(self.id).name.clone()
797 }
798
799 /// Get this function's return type
800 pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
801 let resolver = self.id.resolver(db.upcast());
802 let krate = self.id.lookup(db.upcast()).container.module(db.upcast()).krate();
803 let ret_type = &db.function_data(self.id).ret_type;
804 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
805 let ty = Ty::from_hir_ext(&ctx, ret_type).0;
806 Type::new_with_resolver_inner(db, krate, &resolver, ty)
807 }
808
809 pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
810 if !db.function_data(self.id).has_self_param {
811 return None;
812 }
813 Some(SelfParam { func: self.id })
814 }
815
816 pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
817 let resolver = self.id.resolver(db.upcast());
818 let krate = self.id.lookup(db.upcast()).container.module(db.upcast()).krate();
819 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
820 let environment = TraitEnvironment::lower(db, &resolver);
821 db.function_data(self.id)
822 .params
823 .iter()
824 .map(|type_ref| {
825 let ty = Type {
826 krate,
827 ty: InEnvironment {
828 value: Ty::from_hir_ext(&ctx, type_ref).0,
829 environment: environment.clone(),
830 },
831 };
832 Param { ty }
833 })
834 .collect()
835 }
836 pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
837 if self.self_param(db).is_none() {
838 return None;
839 }
840 let mut res = self.assoc_fn_params(db);
841 res.remove(0);
842 Some(res)
843 }
844
845 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
846 db.function_data(self.id).is_unsafe
847 }
848
849 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
850 let krate = self.module(db).id.krate();
851 hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink);
852 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
853 hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
854 }
855
856 /// Whether this function declaration has a definition.
857 ///
858 /// This is false in the case of required (not provided) trait methods.
859 pub fn has_body(self, db: &dyn HirDatabase) -> bool {
860 db.function_data(self.id).has_body
861 }
862
863 /// A textual representation of the HIR of this function for debugging purposes.
864 pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
865 let body = db.body(self.id.into());
866
867 let mut result = String::new();
868 format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db));
869 for (id, expr) in body.exprs.iter() {
870 format_to!(result, "{:?}: {:?}\n", id, expr);
871 }
872
873 result
874 }
875}
876
877// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
878pub enum Access {
879 Shared,
880 Exclusive,
881 Owned,
882}
883
884impl From<hir_ty::Mutability> for Access {
885 fn from(mutability: hir_ty::Mutability) -> Access {
886 match mutability {
887 hir_ty::Mutability::Not => Access::Shared,
888 hir_ty::Mutability::Mut => Access::Exclusive,
889 }
890 }
891}
892
893#[derive(Debug)]
894pub struct Param {
895 ty: Type,
896}
897
898impl Param {
899 pub fn ty(&self) -> &Type {
900 &self.ty
901 }
902}
903
904#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
905pub struct SelfParam {
906 func: FunctionId,
907}
908
909impl SelfParam {
910 pub fn access(self, db: &dyn HirDatabase) -> Access {
911 let func_data = db.function_data(self.func);
912 func_data
913 .params
914 .first()
915 .map(|param| match *param {
916 TypeRef::Reference(.., mutability) => match mutability {
917 hir_def::type_ref::Mutability::Shared => Access::Shared,
918 hir_def::type_ref::Mutability::Mut => Access::Exclusive,
919 },
920 _ => Access::Owned,
921 })
922 .unwrap_or(Access::Owned)
923 }
924}
925
926impl HasVisibility for Function {
927 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
928 let function_data = db.function_data(self.id);
929 let visibility = &function_data.visibility;
930 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
931 }
932}
933
934#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
935pub struct Const {
936 pub(crate) id: ConstId,
937}
938
939impl Const {
940 pub fn module(self, db: &dyn HirDatabase) -> Module {
941 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
942 }
943
944 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
945 Some(self.module(db).krate())
946 }
947
948 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
949 db.const_data(self.id).name.clone()
950 }
951}
952
953impl HasVisibility for Const {
954 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
955 let function_data = db.const_data(self.id);
956 let visibility = &function_data.visibility;
957 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
958 }
959}
960
961#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
962pub struct Static {
963 pub(crate) id: StaticId,
964}
965
966impl Static {
967 pub fn module(self, db: &dyn HirDatabase) -> Module {
968 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
969 }
970
971 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
972 Some(self.module(db).krate())
973 }
974
975 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
976 db.static_data(self.id).name.clone()
977 }
978
979 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
980 db.static_data(self.id).mutable
981 }
982}
983
984#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
985pub struct Trait {
986 pub(crate) id: TraitId,
987}
988
989impl Trait {
990 pub fn module(self, db: &dyn HirDatabase) -> Module {
991 Module { id: self.id.lookup(db.upcast()).container }
992 }
993
994 pub fn name(self, db: &dyn HirDatabase) -> Name {
995 db.trait_data(self.id).name.clone()
996 }
997
998 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
999 db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
1000 }
1001
1002 pub fn is_auto(self, db: &dyn HirDatabase) -> bool {
1003 db.trait_data(self.id).auto
1004 }
1005}
1006
1007#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1008pub struct TypeAlias {
1009 pub(crate) id: TypeAliasId,
1010}
1011
1012impl TypeAlias {
1013 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
1014 let subst = db.generic_defaults(self.id.into());
1015 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
1016 }
1017
1018 pub fn module(self, db: &dyn HirDatabase) -> Module {
1019 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
1020 }
1021
1022 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
1023 Some(self.module(db).krate())
1024 }
1025
1026 pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
1027 db.type_alias_data(self.id).type_ref.clone()
1028 }
1029
1030 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1031 Type::from_def(db, self.id.lookup(db.upcast()).module(db.upcast()).krate(), self.id)
1032 }
1033
1034 pub fn name(self, db: &dyn HirDatabase) -> Name {
1035 db.type_alias_data(self.id).name.clone()
1036 }
1037}
1038
1039impl HasVisibility for TypeAlias {
1040 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
1041 let function_data = db.type_alias_data(self.id);
1042 let visibility = &function_data.visibility;
1043 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
1044 }
1045}
1046
1047#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1048pub struct BuiltinType {
1049 pub(crate) inner: hir_def::builtin_type::BuiltinType,
1050}
1051
1052impl BuiltinType {
1053 pub fn ty(self, db: &dyn HirDatabase, module: Module) -> Type {
1054 let resolver = module.id.resolver(db.upcast());
1055 Type::new_with_resolver(db, &resolver, Ty::builtin(self.inner))
1056 .expect("crate not present in resolver")
1057 }
1058
1059 pub fn name(self) -> Name {
1060 self.inner.as_name()
1061 }
1062}
1063
1064#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1065pub struct MacroDef {
1066 pub(crate) id: MacroDefId,
1067}
1068
1069impl MacroDef {
1070 /// FIXME: right now, this just returns the root module of the crate that
1071 /// defines this macro. The reasons for this is that macros are expanded
1072 /// early, in `hir_expand`, where modules simply do not exist yet.
1073 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
1074 let krate = self.id.krate;
1075 let def_map = db.crate_def_map(krate);
1076 let module_id = def_map.root();
1077 Some(Module { id: def_map.module_id(module_id) })
1078 }
1079
1080 /// XXX: this parses the file
1081 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1082 self.source(db)?.value.name().map(|it| it.as_name())
1083 }
1084
1085 /// Indicate it is a proc-macro
1086 pub fn is_proc_macro(&self) -> bool {
1087 matches!(self.id.kind, MacroDefKind::ProcMacro(_))
1088 }
1089
1090 /// Indicate it is a derive macro
1091 pub fn is_derive_macro(&self) -> bool {
1092 matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_))
1093 }
1094}
1095
1096/// Invariant: `inner.as_assoc_item(db).is_some()`
1097/// We do not actively enforce this invariant.
1098#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1099pub enum AssocItem {
1100 Function(Function),
1101 Const(Const),
1102 TypeAlias(TypeAlias),
1103}
1104#[derive(Debug)]
1105pub enum AssocItemContainer {
1106 Trait(Trait),
1107 Impl(Impl),
1108}
1109pub trait AsAssocItem {
1110 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>;
1111}
1112
1113impl AsAssocItem for Function {
1114 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1115 as_assoc_item(db, AssocItem::Function, self.id)
1116 }
1117}
1118impl AsAssocItem for Const {
1119 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1120 as_assoc_item(db, AssocItem::Const, self.id)
1121 }
1122}
1123impl AsAssocItem for TypeAlias {
1124 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1125 as_assoc_item(db, AssocItem::TypeAlias, self.id)
1126 }
1127}
1128impl AsAssocItem for ModuleDef {
1129 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1130 match self {
1131 ModuleDef::Function(it) => it.as_assoc_item(db),
1132 ModuleDef::Const(it) => it.as_assoc_item(db),
1133 ModuleDef::TypeAlias(it) => it.as_assoc_item(db),
1134 _ => None,
1135 }
1136 }
1137}
1138fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem>
1139where
1140 ID: Lookup<Data = AssocItemLoc<AST>>,
1141 DEF: From<ID>,
1142 CTOR: FnOnce(DEF) -> AssocItem,
1143 AST: ItemTreeNode,
1144{
1145 match id.lookup(db.upcast()).container {
1146 AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
1147 AssocContainerId::ModuleId(_) => None,
1148 }
1149}
1150
1151impl AssocItem {
1152 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1153 match self {
1154 AssocItem::Function(it) => Some(it.name(db)),
1155 AssocItem::Const(it) => it.name(db),
1156 AssocItem::TypeAlias(it) => Some(it.name(db)),
1157 }
1158 }
1159 pub fn module(self, db: &dyn HirDatabase) -> Module {
1160 match self {
1161 AssocItem::Function(f) => f.module(db),
1162 AssocItem::Const(c) => c.module(db),
1163 AssocItem::TypeAlias(t) => t.module(db),
1164 }
1165 }
1166 pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer {
1167 let container = match self {
1168 AssocItem::Function(it) => it.id.lookup(db.upcast()).container,
1169 AssocItem::Const(it) => it.id.lookup(db.upcast()).container,
1170 AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container,
1171 };
1172 match container {
1173 AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()),
1174 AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()),
1175 AssocContainerId::ModuleId(_) => panic!("invalid AssocItem"),
1176 }
1177 }
1178
1179 pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
1180 match self.container(db) {
1181 AssocItemContainer::Trait(t) => Some(t),
1182 _ => None,
1183 }
1184 }
1185}
1186
1187impl HasVisibility for AssocItem {
1188 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
1189 match self {
1190 AssocItem::Function(f) => f.visibility(db),
1191 AssocItem::Const(c) => c.visibility(db),
1192 AssocItem::TypeAlias(t) => t.visibility(db),
1193 }
1194 }
1195}
1196
1197#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1198pub enum GenericDef {
1199 Function(Function),
1200 Adt(Adt),
1201 Trait(Trait),
1202 TypeAlias(TypeAlias),
1203 Impl(Impl),
1204 // enum variants cannot have generics themselves, but their parent enums
1205 // can, and this makes some code easier to write
1206 Variant(Variant),
1207 // consts can have type parameters from their parents (i.e. associated consts of traits)
1208 Const(Const),
1209}
1210impl_from!(
1211 Function,
1212 Adt(Struct, Enum, Union),
1213 Trait,
1214 TypeAlias,
1215 Impl,
1216 Variant,
1217 Const
1218 for GenericDef
1219);
1220
1221impl GenericDef {
1222 pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> {
1223 let generics = db.generic_params(self.into());
1224 let ty_params = generics
1225 .types
1226 .iter()
1227 .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } })
1228 .map(GenericParam::TypeParam);
1229 let lt_params = generics
1230 .lifetimes
1231 .iter()
1232 .map(|(local_id, _)| LifetimeParam {
1233 id: LifetimeParamId { parent: self.into(), local_id },
1234 })
1235 .map(GenericParam::LifetimeParam);
1236 let const_params = generics
1237 .consts
1238 .iter()
1239 .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } })
1240 .map(GenericParam::ConstParam);
1241 ty_params.chain(lt_params).chain(const_params).collect()
1242 }
1243
1244 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
1245 let generics = db.generic_params(self.into());
1246 generics
1247 .types
1248 .iter()
1249 .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } })
1250 .collect()
1251 }
1252}
1253
1254#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1255pub struct Local {
1256 pub(crate) parent: DefWithBodyId,
1257 pub(crate) pat_id: PatId,
1258}
1259
1260impl Local {
1261 pub fn is_param(self, db: &dyn HirDatabase) -> bool {
1262 let src = self.source(db);
1263 match src.value {
1264 Either::Left(bind_pat) => {
1265 bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind()))
1266 }
1267 Either::Right(_self_param) => true,
1268 }
1269 }
1270
1271 // FIXME: why is this an option? It shouldn't be?
1272 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1273 let body = db.body(self.parent.into());
1274 match &body[self.pat_id] {
1275 Pat::Bind { name, .. } => Some(name.clone()),
1276 _ => None,
1277 }
1278 }
1279
1280 pub fn is_self(self, db: &dyn HirDatabase) -> bool {
1281 self.name(db) == Some(name![self])
1282 }
1283
1284 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
1285 let body = db.body(self.parent.into());
1286 matches!(&body[self.pat_id], Pat::Bind { mode: BindingAnnotation::Mutable, .. })
1287 }
1288
1289 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
1290 self.parent.into()
1291 }
1292
1293 pub fn module(self, db: &dyn HirDatabase) -> Module {
1294 self.parent(db).module(db)
1295 }
1296
1297 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1298 let def = DefWithBodyId::from(self.parent);
1299 let infer = db.infer(def);
1300 let ty = infer[self.pat_id].clone();
1301 let krate = def.module(db.upcast()).krate();
1302 Type::new(db, krate, def, ty)
1303 }
1304
1305 pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> {
1306 let (_body, source_map) = db.body_with_source_map(self.parent.into());
1307 let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
1308 let root = src.file_syntax(db.upcast());
1309 src.map(|ast| {
1310 ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root))
1311 })
1312 }
1313}
1314
1315#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1316pub struct Label {
1317 pub(crate) parent: DefWithBodyId,
1318 pub(crate) label_id: LabelId,
1319}
1320
1321impl Label {
1322 pub fn module(self, db: &dyn HirDatabase) -> Module {
1323 self.parent(db).module(db)
1324 }
1325
1326 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
1327 self.parent.into()
1328 }
1329
1330 pub fn name(self, db: &dyn HirDatabase) -> Name {
1331 let body = db.body(self.parent.into());
1332 body[self.label_id].name.clone()
1333 }
1334
1335 pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> {
1336 let (_body, source_map) = db.body_with_source_map(self.parent.into());
1337 let src = source_map.label_syntax(self.label_id);
1338 let root = src.file_syntax(db.upcast());
1339 src.map(|ast| ast.to_node(&root))
1340 }
1341}
1342
1343#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1344pub enum GenericParam {
1345 TypeParam(TypeParam),
1346 LifetimeParam(LifetimeParam),
1347 ConstParam(ConstParam),
1348}
1349impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam);
1350
1351impl GenericParam {
1352 pub fn module(self, db: &dyn HirDatabase) -> Module {
1353 match self {
1354 GenericParam::TypeParam(it) => it.module(db),
1355 GenericParam::LifetimeParam(it) => it.module(db),
1356 GenericParam::ConstParam(it) => it.module(db),
1357 }
1358 }
1359
1360 pub fn name(self, db: &dyn HirDatabase) -> Name {
1361 match self {
1362 GenericParam::TypeParam(it) => it.name(db),
1363 GenericParam::LifetimeParam(it) => it.name(db),
1364 GenericParam::ConstParam(it) => it.name(db),
1365 }
1366 }
1367}
1368
1369#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1370pub struct TypeParam {
1371 pub(crate) id: TypeParamId,
1372}
1373
1374impl TypeParam {
1375 pub fn name(self, db: &dyn HirDatabase) -> Name {
1376 let params = db.generic_params(self.id.parent);
1377 params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing)
1378 }
1379
1380 pub fn module(self, db: &dyn HirDatabase) -> Module {
1381 self.id.parent.module(db.upcast()).into()
1382 }
1383
1384 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1385 let resolver = self.id.parent.resolver(db.upcast());
1386 let krate = self.id.parent.module(db.upcast()).krate();
1387 let ty = Ty::Placeholder(self.id);
1388 Type::new_with_resolver_inner(db, krate, &resolver, ty)
1389 }
1390
1391 pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
1392 db.generic_predicates_for_param(self.id)
1393 .into_iter()
1394 .filter_map(|pred| match &pred.value {
1395 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1396 Some(Trait::from(trait_ref.trait_))
1397 }
1398 _ => None,
1399 })
1400 .collect()
1401 }
1402
1403 pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
1404 let params = db.generic_defaults(self.id.parent);
1405 let local_idx = hir_ty::param_idx(db, self.id)?;
1406 let resolver = self.id.parent.resolver(db.upcast());
1407 let krate = self.id.parent.module(db.upcast()).krate();
1408 let ty = params.get(local_idx)?.clone();
1409 let subst = Substs::type_params(db, self.id.parent);
1410 let ty = ty.subst(&subst.prefix(local_idx));
1411 Some(Type::new_with_resolver_inner(db, krate, &resolver, ty))
1412 }
1413}
1414
1415impl HirDisplay for TypeParam {
1416 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
1417 write!(f, "{}", self.name(f.db))?;
1418 let bounds = f.db.generic_predicates_for_param(self.id);
1419 let substs = Substs::type_params(f.db, self.id.parent);
1420 let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>();
1421 if !(predicates.is_empty() || f.omit_verbose_types()) {
1422 write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?;
1423 }
1424 Ok(())
1425 }
1426}
1427
1428#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1429pub struct LifetimeParam {
1430 pub(crate) id: LifetimeParamId,
1431}
1432
1433impl LifetimeParam {
1434 pub fn name(self, db: &dyn HirDatabase) -> Name {
1435 let params = db.generic_params(self.id.parent);
1436 params.lifetimes[self.id.local_id].name.clone()
1437 }
1438
1439 pub fn module(self, db: &dyn HirDatabase) -> Module {
1440 self.id.parent.module(db.upcast()).into()
1441 }
1442
1443 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1444 self.id.parent.into()
1445 }
1446}
1447
1448#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1449pub struct ConstParam {
1450 pub(crate) id: ConstParamId,
1451}
1452
1453impl ConstParam {
1454 pub fn name(self, db: &dyn HirDatabase) -> Name {
1455 let params = db.generic_params(self.id.parent);
1456 params.consts[self.id.local_id].name.clone()
1457 }
1458
1459 pub fn module(self, db: &dyn HirDatabase) -> Module {
1460 self.id.parent.module(db.upcast()).into()
1461 }
1462
1463 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1464 self.id.parent.into()
1465 }
1466
1467 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1468 let def = self.id.parent;
1469 let krate = def.module(db.upcast()).krate();
1470 Type::new(db, krate, def, db.const_param_ty(self.id))
1471 }
1472}
1473
1474#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1475pub struct Impl {
1476 pub(crate) id: ImplId,
1477}
1478
1479impl Impl {
1480 pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> {
1481 let inherent = db.inherent_impls_in_crate(krate.id);
1482 let trait_ = db.trait_impls_in_crate(krate.id);
1483
1484 inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect()
1485 }
1486 pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> {
1487 let impls = db.trait_impls_in_crate(krate.id);
1488 impls.for_trait(trait_.id).map(Self::from).collect()
1489 }
1490
1491 // FIXME: the return type is wrong. This should be a hir version of
1492 // `TraitRef` (ie, resolved `TypeRef`).
1493 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> {
1494 db.impl_data(self.id).target_trait.clone()
1495 }
1496
1497 pub fn target_ty(self, db: &dyn HirDatabase) -> Type {
1498 let impl_data = db.impl_data(self.id);
1499 let resolver = self.id.resolver(db.upcast());
1500 let krate = self.id.lookup(db.upcast()).container.krate();
1501 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
1502 let ty = Ty::from_hir(&ctx, &impl_data.target_type);
1503 Type::new_with_resolver_inner(db, krate, &resolver, ty)
1504 }
1505
1506 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
1507 db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect()
1508 }
1509
1510 pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
1511 db.impl_data(self.id).is_negative
1512 }
1513
1514 pub fn module(self, db: &dyn HirDatabase) -> Module {
1515 self.id.lookup(db.upcast()).container.into()
1516 }
1517
1518 pub fn krate(self, db: &dyn HirDatabase) -> Crate {
1519 Crate { id: self.module(db).id.krate() }
1520 }
1521
1522 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
1523 let src = self.source(db)?;
1524 let item = src.file_id.is_builtin_derive(db.upcast())?;
1525 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id);
1526
1527 // FIXME: handle `cfg_attr`
1528 let attr = item
1529 .value
1530 .attrs()
1531 .filter_map(|it| {
1532 let path = ModPath::from_src(it.path()?, &hygenic)?;
1533 if path.as_ident()?.to_string() == "derive" {
1534 Some(it)
1535 } else {
1536 None
1537 }
1538 })
1539 .last()?;
1540
1541 Some(item.with_value(attr))
1542 }
1543}
1544
1545#[derive(Clone, PartialEq, Eq, Debug)]
1546pub struct Type {
1547 krate: CrateId,
1548 ty: InEnvironment<Ty>,
1549}
1550
1551impl Type {
1552 pub(crate) fn new_with_resolver(
1553 db: &dyn HirDatabase,
1554 resolver: &Resolver,
1555 ty: Ty,
1556 ) -> Option<Type> {
1557 let krate = resolver.krate()?;
1558 Some(Type::new_with_resolver_inner(db, krate, resolver, ty))
1559 }
1560 pub(crate) fn new_with_resolver_inner(
1561 db: &dyn HirDatabase,
1562 krate: CrateId,
1563 resolver: &Resolver,
1564 ty: Ty,
1565 ) -> Type {
1566 let environment = TraitEnvironment::lower(db, &resolver);
1567 Type { krate, ty: InEnvironment { value: ty, environment } }
1568 }
1569
1570 fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
1571 let resolver = lexical_env.resolver(db.upcast());
1572 let environment = TraitEnvironment::lower(db, &resolver);
1573 Type { krate, ty: InEnvironment { value: ty, environment } }
1574 }
1575
1576 fn from_def(
1577 db: &dyn HirDatabase,
1578 krate: CrateId,
1579 def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>,
1580 ) -> Type {
1581 let substs = Substs::build_for_def(db, def).fill_with_unknown().build();
1582 let ty = db.ty(def.into()).subst(&substs);
1583 Type::new(db, krate, def, ty)
1584 }
1585
1586 pub fn is_unit(&self) -> bool {
1587 matches!(self.ty.value, Ty::Tuple(0, ..))
1588 }
1589 pub fn is_bool(&self) -> bool {
1590 matches!(self.ty.value, Ty::Scalar(Scalar::Bool))
1591 }
1592
1593 pub fn is_mutable_reference(&self) -> bool {
1594 matches!(self.ty.value, Ty::Ref(hir_ty::Mutability::Mut, ..))
1595 }
1596
1597 pub fn remove_ref(&self) -> Option<Type> {
1598 match &self.ty.value {
1599 Ty::Ref(.., substs) => Some(self.derived(substs[0].clone())),
1600 _ => None,
1601 }
1602 }
1603
1604 pub fn is_unknown(&self) -> bool {
1605 matches!(self.ty.value, Ty::Unknown)
1606 }
1607
1608 /// Checks that particular type `ty` implements `std::future::Future`.
1609 /// This function is used in `.await` syntax completion.
1610 pub fn impls_future(&self, db: &dyn HirDatabase) -> bool {
1611 // No special case for the type of async block, since Chalk can figure it out.
1612
1613 let krate = self.krate;
1614
1615 let std_future_trait =
1616 db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait());
1617 let std_future_trait = match std_future_trait {
1618 Some(it) => it,
1619 None => return false,
1620 };
1621
1622 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1623 method_resolution::implements_trait(
1624 &canonical_ty,
1625 db,
1626 self.ty.environment.clone(),
1627 krate,
1628 std_future_trait,
1629 )
1630 }
1631
1632 /// Checks that particular type `ty` implements `std::ops::FnOnce`.
1633 ///
1634 /// This function can be used to check if a particular type is callable, since FnOnce is a
1635 /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce.
1636 pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool {
1637 let krate = self.krate;
1638
1639 let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) {
1640 Some(it) => it,
1641 None => return false,
1642 };
1643
1644 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1645 method_resolution::implements_trait_unique(
1646 &canonical_ty,
1647 db,
1648 self.ty.environment.clone(),
1649 krate,
1650 fnonce_trait,
1651 )
1652 }
1653
1654 pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
1655 let trait_ref = hir_ty::TraitRef {
1656 trait_: trait_.id,
1657 substs: Substs::build_for_def(db, trait_.id)
1658 .push(self.ty.value.clone())
1659 .fill(args.iter().map(|t| t.ty.value.clone()))
1660 .build(),
1661 };
1662
1663 let goal = Canonical {
1664 value: hir_ty::InEnvironment::new(
1665 self.ty.environment.clone(),
1666 hir_ty::Obligation::Trait(trait_ref),
1667 ),
1668 kinds: Arc::new([]),
1669 };
1670
1671 db.trait_solve(self.krate, goal).is_some()
1672 }
1673
1674 pub fn normalize_trait_assoc_type(
1675 &self,
1676 db: &dyn HirDatabase,
1677 trait_: Trait,
1678 args: &[Type],
1679 alias: TypeAlias,
1680 ) -> Option<Type> {
1681 let subst = Substs::build_for_def(db, trait_.id)
1682 .push(self.ty.value.clone())
1683 .fill(args.iter().map(|t| t.ty.value.clone()))
1684 .build();
1685 let predicate = ProjectionPredicate {
1686 projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst },
1687 ty: Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)),
1688 };
1689 let goal = Canonical {
1690 value: InEnvironment::new(
1691 self.ty.environment.clone(),
1692 Obligation::Projection(predicate),
1693 ),
1694 kinds: Arc::new([TyVariableKind::General]),
1695 };
1696
1697 match db.trait_solve(self.krate, goal)? {
1698 Solution::Unique(SolutionVariables(subst)) => {
1699 subst.value.first().map(|ty| self.derived(ty.clone()))
1700 }
1701 Solution::Ambig(_) => None,
1702 }
1703 }
1704
1705 pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
1706 let lang_item = db.lang_item(self.krate, SmolStr::new("copy"));
1707 let copy_trait = match lang_item {
1708 Some(LangItemTarget::TraitId(it)) => it,
1709 _ => return false,
1710 };
1711 self.impls_trait(db, copy_trait.into(), &[])
1712 }
1713
1714 pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
1715 let def = match self.ty.value {
1716 Ty::FnDef(def, _) => Some(def),
1717 _ => None,
1718 };
1719
1720 let sig = self.ty.value.callable_sig(db)?;
1721 Some(Callable { ty: self.clone(), sig, def, is_bound_method: false })
1722 }
1723
1724 pub fn is_closure(&self) -> bool {
1725 matches!(&self.ty.value, Ty::Closure { .. })
1726 }
1727
1728 pub fn is_fn(&self) -> bool {
1729 matches!(&self.ty.value, Ty::FnDef(..) | Ty::Function { .. })
1730 }
1731
1732 pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
1733 let adt_id = match self.ty.value {
1734 Ty::Adt(hir_ty::AdtId(adt_id), ..) => adt_id,
1735 _ => return false,
1736 };
1737
1738 let adt = adt_id.into();
1739 match adt {
1740 Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)),
1741 _ => false,
1742 }
1743 }
1744
1745 pub fn is_raw_ptr(&self) -> bool {
1746 matches!(&self.ty.value, Ty::Raw(..))
1747 }
1748
1749 pub fn contains_unknown(&self) -> bool {
1750 return go(&self.ty.value);
1751
1752 fn go(ty: &Ty) -> bool {
1753 match ty {
1754 Ty::Unknown => true,
1755 _ => ty.substs().map_or(false, |substs| substs.iter().any(go)),
1756 }
1757 }
1758 }
1759
1760 pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
1761 let (variant_id, substs) = match self.ty.value {
1762 Ty::Adt(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs),
1763 Ty::Adt(hir_ty::AdtId(AdtId::UnionId(u)), ref substs) => (u.into(), substs),
1764 _ => return Vec::new(),
1765 };
1766
1767 db.field_types(variant_id)
1768 .iter()
1769 .map(|(local_id, ty)| {
1770 let def = Field { parent: variant_id.into(), id: local_id };
1771 let ty = ty.clone().subst(substs);
1772 (def, self.derived(ty))
1773 })
1774 .collect()
1775 }
1776
1777 pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> {
1778 if let Ty::Tuple(_, substs) = &self.ty.value {
1779 substs.iter().map(|ty| self.derived(ty.clone())).collect()
1780 } else {
1781 Vec::new()
1782 }
1783 }
1784
1785 pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
1786 // There should be no inference vars in types passed here
1787 // FIXME check that?
1788 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1789 let environment = self.ty.environment.clone();
1790 let ty = InEnvironment { value: canonical, environment };
1791 autoderef(db, Some(self.krate), ty)
1792 .map(|canonical| canonical.value)
1793 .map(move |ty| self.derived(ty))
1794 }
1795
1796 // This would be nicer if it just returned an iterator, but that runs into
1797 // lifetime problems, because we need to borrow temp `CrateImplDefs`.
1798 pub fn iterate_assoc_items<T>(
1799 self,
1800 db: &dyn HirDatabase,
1801 krate: Crate,
1802 mut callback: impl FnMut(AssocItem) -> Option<T>,
1803 ) -> Option<T> {
1804 for krate in self.ty.value.def_crates(db, krate.id)? {
1805 let impls = db.inherent_impls_in_crate(krate);
1806
1807 for impl_def in impls.for_self_ty(&self.ty.value) {
1808 for &item in db.impl_data(*impl_def).items.iter() {
1809 if let Some(result) = callback(item.into()) {
1810 return Some(result);
1811 }
1812 }
1813 }
1814 }
1815 None
1816 }
1817
1818 pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ {
1819 self.ty
1820 .value
1821 .strip_references()
1822 .substs()
1823 .into_iter()
1824 .flat_map(|substs| substs.iter())
1825 .map(move |ty| self.derived(ty.clone()))
1826 }
1827
1828 pub fn iterate_method_candidates<T>(
1829 &self,
1830 db: &dyn HirDatabase,
1831 krate: Crate,
1832 traits_in_scope: &FxHashSet<TraitId>,
1833 name: Option<&Name>,
1834 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
1835 ) -> Option<T> {
1836 // There should be no inference vars in types passed here
1837 // FIXME check that?
1838 // FIXME replace Unknown by bound vars here
1839 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1840
1841 let env = self.ty.environment.clone();
1842 let krate = krate.id;
1843
1844 method_resolution::iterate_method_candidates(
1845 &canonical,
1846 db,
1847 env,
1848 krate,
1849 traits_in_scope,
1850 name,
1851 method_resolution::LookupMode::MethodCall,
1852 |ty, it| match it {
1853 AssocItemId::FunctionId(f) => callback(ty, f.into()),
1854 _ => None,
1855 },
1856 )
1857 }
1858
1859 pub fn iterate_path_candidates<T>(
1860 &self,
1861 db: &dyn HirDatabase,
1862 krate: Crate,
1863 traits_in_scope: &FxHashSet<TraitId>,
1864 name: Option<&Name>,
1865 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
1866 ) -> Option<T> {
1867 // There should be no inference vars in types passed here
1868 // FIXME check that?
1869 // FIXME replace Unknown by bound vars here
1870 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1871
1872 let env = self.ty.environment.clone();
1873 let krate = krate.id;
1874
1875 method_resolution::iterate_method_candidates(
1876 &canonical,
1877 db,
1878 env,
1879 krate,
1880 traits_in_scope,
1881 name,
1882 method_resolution::LookupMode::Path,
1883 |ty, it| callback(ty, it.into()),
1884 )
1885 }
1886
1887 pub fn as_adt(&self) -> Option<Adt> {
1888 let (adt, _subst) = self.ty.value.as_adt()?;
1889 Some(adt.into())
1890 }
1891
1892 pub fn as_dyn_trait(&self) -> Option<Trait> {
1893 self.ty.value.dyn_trait().map(Into::into)
1894 }
1895
1896 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> {
1897 self.ty.value.impl_trait_bounds(db).map(|it| {
1898 it.into_iter()
1899 .filter_map(|pred| match pred {
1900 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1901 Some(Trait::from(trait_ref.trait_))
1902 }
1903 _ => None,
1904 })
1905 .collect()
1906 })
1907 }
1908
1909 pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> {
1910 self.ty.value.associated_type_parent_trait(db).map(Into::into)
1911 }
1912
1913 // FIXME: provide required accessors such that it becomes implementable from outside.
1914 pub fn is_equal_for_find_impls(&self, other: &Type) -> bool {
1915 let rref = other.remove_ref();
1916 self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value))
1917 }
1918
1919 fn derived(&self, ty: Ty) -> Type {
1920 Type {
1921 krate: self.krate,
1922 ty: InEnvironment { value: ty, environment: self.ty.environment.clone() },
1923 }
1924 }
1925
1926 pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) {
1927 // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself.
1928 // We need a different order here.
1929
1930 fn walk_substs(
1931 db: &dyn HirDatabase,
1932 type_: &Type,
1933 substs: &Substs,
1934 cb: &mut impl FnMut(Type),
1935 ) {
1936 for ty in substs.iter() {
1937 walk_type(db, &type_.derived(ty.clone()), cb);
1938 }
1939 }
1940
1941 fn walk_bounds(
1942 db: &dyn HirDatabase,
1943 type_: &Type,
1944 bounds: &[GenericPredicate],
1945 cb: &mut impl FnMut(Type),
1946 ) {
1947 for pred in bounds {
1948 match pred {
1949 GenericPredicate::Implemented(trait_ref) => {
1950 cb(type_.clone());
1951 walk_substs(db, type_, &trait_ref.substs, cb);
1952 }
1953 _ => (),
1954 }
1955 }
1956 }
1957
1958 fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
1959 let ty = type_.ty.value.strip_references();
1960 match ty {
1961 Ty::Adt(..) => {
1962 cb(type_.derived(ty.clone()));
1963 }
1964 Ty::AssociatedType(..) => {
1965 if let Some(_) = ty.associated_type_parent_trait(db) {
1966 cb(type_.derived(ty.clone()));
1967 }
1968 }
1969 Ty::OpaqueType(..) => {
1970 if let Some(bounds) = ty.impl_trait_bounds(db) {
1971 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1972 }
1973 }
1974 Ty::Alias(AliasTy::Opaque(opaque_ty)) => {
1975 if let Some(bounds) = ty.impl_trait_bounds(db) {
1976 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1977 }
1978
1979 walk_substs(db, type_, &opaque_ty.parameters, cb);
1980 }
1981 Ty::Placeholder(_) => {
1982 if let Some(bounds) = ty.impl_trait_bounds(db) {
1983 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1984 }
1985 }
1986 Ty::Dyn(bounds) => {
1987 walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb);
1988 }
1989
1990 _ => {}
1991 }
1992 if let Some(substs) = ty.substs() {
1993 walk_substs(db, type_, &substs, cb);
1994 }
1995 }
1996
1997 walk_type(db, self, &mut cb);
1998 }
1999}
2000
2001impl HirDisplay for Type {
2002 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
2003 self.ty.value.hir_fmt(f)
2004 }
2005}
2006
2007// FIXME: closures
2008#[derive(Debug)]
2009pub struct Callable {
2010 ty: Type,
2011 sig: CallableSig,
2012 def: Option<CallableDefId>,
2013 pub(crate) is_bound_method: bool,
2014}
2015
2016pub enum CallableKind {
2017 Function(Function),
2018 TupleStruct(Struct),
2019 TupleEnumVariant(Variant),
2020 Closure,
2021}
2022
2023impl Callable {
2024 pub fn kind(&self) -> CallableKind {
2025 match self.def {
2026 Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
2027 Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
2028 Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
2029 None => CallableKind::Closure,
2030 }
2031 }
2032 pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
2033 let func = match self.def {
2034 Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
2035 _ => return None,
2036 };
2037 let src = func.lookup(db.upcast()).source(db.upcast());
2038 let param_list = src.value.param_list()?;
2039 param_list.self_param()
2040 }
2041 pub fn n_params(&self) -> usize {
2042 self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
2043 }
2044 pub fn params(
2045 &self,
2046 db: &dyn HirDatabase,
2047 ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> {
2048 let types = self
2049 .sig
2050 .params()
2051 .iter()
2052 .skip(if self.is_bound_method { 1 } else { 0 })
2053 .map(|ty| self.ty.derived(ty.clone()));
2054 let patterns = match self.def {
2055 Some(CallableDefId::FunctionId(func)) => {
2056 let src = func.lookup(db.upcast()).source(db.upcast());
2057 src.value.param_list().map(|param_list| {
2058 param_list
2059 .self_param()
2060 .map(|it| Some(Either::Left(it)))
2061 .filter(|_| !self.is_bound_method)
2062 .into_iter()
2063 .chain(param_list.params().map(|it| it.pat().map(Either::Right)))
2064 })
2065 }
2066 _ => None,
2067 };
2068 patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
2069 }
2070 pub fn return_type(&self) -> Type {
2071 self.ty.derived(self.sig.ret().clone())
2072 }
2073}
2074
2075/// For IDE only
2076#[derive(Debug, PartialEq, Eq, Hash)]
2077pub enum ScopeDef {
2078 ModuleDef(ModuleDef),
2079 MacroDef(MacroDef),
2080 GenericParam(GenericParam),
2081 ImplSelfType(Impl),
2082 AdtSelfType(Adt),
2083 Local(Local),
2084 Unknown,
2085}
2086
2087impl ScopeDef {
2088 pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> {
2089 let mut items = ArrayVec::new();
2090
2091 match (def.take_types(), def.take_values()) {
2092 (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())),
2093 (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())),
2094 (Some(m1), Some(m2)) => {
2095 // Some items, like unit structs and enum variants, are
2096 // returned as both a type and a value. Here we want
2097 // to de-duplicate them.
2098 if m1 != m2 {
2099 items.push(ScopeDef::ModuleDef(m1.into()));
2100 items.push(ScopeDef::ModuleDef(m2.into()));
2101 } else {
2102 items.push(ScopeDef::ModuleDef(m1.into()));
2103 }
2104 }
2105 (None, None) => {}
2106 };
2107
2108 if let Some(macro_def_id) = def.take_macros() {
2109 items.push(ScopeDef::MacroDef(macro_def_id.into()));
2110 }
2111
2112 if items.is_empty() {
2113 items.push(ScopeDef::Unknown);
2114 }
2115
2116 items
2117 }
2118}
2119
2120impl From<ItemInNs> for ScopeDef {
2121 fn from(item: ItemInNs) -> Self {
2122 match item {
2123 ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
2124 ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
2125 ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
2126 }
2127 }
2128}
2129
2130pub trait HasVisibility {
2131 fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
2132 fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
2133 let vis = self.visibility(db);
2134 vis.is_visible_from(db.upcast(), module.id)
2135 }
2136}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 144851f83..945638cc5 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -20,12 +20,11 @@ use syntax::{
20}; 20};
21 21
22use crate::{ 22use crate::{
23 code_model::Access,
24 db::HirDatabase, 23 db::HirDatabase,
25 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, 24 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
26 source_analyzer::{resolve_hir_path, SourceAnalyzer}, 25 source_analyzer::{resolve_hir_path, SourceAnalyzer},
27 AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, Label, 26 Access, AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile,
28 LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, 27 Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type,
29 TypeAlias, TypeParam, VariantDef, 28 TypeAlias, TypeParam, VariantDef,
30}; 29};
31 30
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 64ce4add1..d546512cb 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -28,9 +28,8 @@ use syntax::{
28}; 28};
29 29
30use crate::{ 30use crate::{
31 code_model::BuiltinType, db::HirDatabase, semantics::PathResolution, Adt, Const, Field, 31 db::HirDatabase, semantics::PathResolution, Adt, BuiltinType, Const, Field, Function, Local,
32 Function, Local, MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, 32 MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Variant,
33 Variant,
34}; 33};
35use base_db::CrateId; 34use base_db::CrateId;
36 35
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index 535221294..c2d99280f 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13log = "0.4.8" 14log = "0.4.8"
14once_cell = "1.3.1" 15once_cell = "1.3.1"
15rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
@@ -27,10 +28,10 @@ base_db = { path = "../base_db", version = "0.0.0" }
27syntax = { path = "../syntax", version = "0.0.0" } 28syntax = { path = "../syntax", version = "0.0.0" }
28profile = { path = "../profile", version = "0.0.0" } 29profile = { path = "../profile", version = "0.0.0" }
29hir_expand = { path = "../hir_expand", version = "0.0.0" } 30hir_expand = { path = "../hir_expand", version = "0.0.0" }
30test_utils = { path = "../test_utils", version = "0.0.0" }
31mbe = { path = "../mbe", version = "0.0.0" } 31mbe = { path = "../mbe", version = "0.0.0" }
32cfg = { path = "../cfg", version = "0.0.0" } 32cfg = { path = "../cfg", version = "0.0.0" }
33tt = { path = "../tt", version = "0.0.0" } 33tt = { path = "../tt", version = "0.0.0" }
34 34
35[dev-dependencies] 35[dev-dependencies]
36test_utils = { path = "../test_utils" }
36expect-test = "1.1" 37expect-test = "1.1"
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs
index ed36c3109..efbde17d8 100644
--- a/crates/hir_def/src/adt.rs
+++ b/crates/hir_def/src/adt.rs
@@ -21,8 +21,7 @@ use crate::{
21 trace::Trace, 21 trace::Trace,
22 type_ref::TypeRef, 22 type_ref::TypeRef,
23 visibility::RawVisibility, 23 visibility::RawVisibility,
24 EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, 24 EnumId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
25 VariantId,
26}; 25};
27use cfg::CfgOptions; 26use cfg::CfgOptions;
28 27
@@ -92,10 +91,10 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
92impl StructData { 91impl StructData {
93 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { 92 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
94 let loc = id.lookup(db); 93 let loc = id.lookup(db);
95 let krate = loc.container.module(db).krate; 94 let krate = loc.container.krate;
96 let item_tree = db.item_tree(loc.id.file_id); 95 let item_tree = db.item_tree(loc.id.file_id);
97 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); 96 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
98 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); 97 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
99 98
100 let strukt = &item_tree[loc.id.value]; 99 let strukt = &item_tree[loc.id.value];
101 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None); 100 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None);
@@ -107,10 +106,10 @@ impl StructData {
107 } 106 }
108 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { 107 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
109 let loc = id.lookup(db); 108 let loc = id.lookup(db);
110 let krate = loc.container.module(db).krate; 109 let krate = loc.container.krate;
111 let item_tree = db.item_tree(loc.id.file_id); 110 let item_tree = db.item_tree(loc.id.file_id);
112 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); 111 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
113 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); 112 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
114 113
115 let union = &item_tree[loc.id.value]; 114 let union = &item_tree[loc.id.value];
116 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None); 115 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None);
@@ -126,7 +125,7 @@ impl StructData {
126impl EnumData { 125impl EnumData {
127 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { 126 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
128 let loc = e.lookup(db); 127 let loc = e.lookup(db);
129 let krate = loc.container.module(db).krate; 128 let krate = loc.container.krate;
130 let item_tree = db.item_tree(loc.id.file_id); 129 let item_tree = db.item_tree(loc.id.file_id);
131 let cfg_options = db.crate_graph()[krate].cfg_options.clone(); 130 let cfg_options = db.crate_graph()[krate].cfg_options.clone();
132 131
@@ -168,7 +167,7 @@ impl HasChildSource<LocalEnumVariantId> for EnumId {
168 ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> { 167 ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
169 let src = self.lookup(db).source(db); 168 let src = self.lookup(db).source(db);
170 let mut trace = Trace::new_for_map(); 169 let mut trace = Trace::new_for_map();
171 lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db)); 170 lower_enum(db, &mut trace, &src, self.lookup(db).container);
172 src.with_value(trace.into_map()) 171 src.with_value(trace.into_map())
173 } 172 }
174} 173}
@@ -238,10 +237,10 @@ impl HasChildSource<LocalFieldId> for VariantId {
238 // I don't really like the fact that we call into parent source 237 // I don't really like the fact that we call into parent source
239 // here, this might add to more queries then necessary. 238 // here, this might add to more queries then necessary.
240 let src = it.parent.child_source(db); 239 let src = it.parent.child_source(db);
241 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container.module(db)) 240 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container)
242 } 241 }
243 VariantId::StructId(it) => { 242 VariantId::StructId(it) => {
244 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container.module(db)) 243 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
245 } 244 }
246 VariantId::UnionId(it) => ( 245 VariantId::UnionId(it) => (
247 it.lookup(db).source(db).map(|it| { 246 it.lookup(db).source(db).map(|it| {
@@ -249,7 +248,7 @@ impl HasChildSource<LocalFieldId> for VariantId {
249 .map(ast::StructKind::Record) 248 .map(ast::StructKind::Record)
250 .unwrap_or(ast::StructKind::Unit) 249 .unwrap_or(ast::StructKind::Unit)
251 }), 250 }),
252 it.lookup(db).container.module(db), 251 it.lookup(db).container,
253 ), 252 ),
254 }; 253 };
255 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate); 254 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 24ffa6c3a..97cdbbb9e 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -13,7 +13,6 @@ use syntax::{
13 ast::{self, AstNode, AttrsOwner}, 13 ast::{self, AstNode, AttrsOwner},
14 match_ast, AstToken, SmolStr, SyntaxNode, 14 match_ast, AstToken, SmolStr, SyntaxNode,
15}; 15};
16use test_utils::mark;
17use tt::Subtree; 16use tt::Subtree;
18 17
19use crate::{ 18use crate::{
@@ -177,7 +176,7 @@ impl RawAttrs {
177 if cfg_options.check(&cfg) == Some(false) { 176 if cfg_options.check(&cfg) == Some(false) {
178 None 177 None
179 } else { 178 } else {
180 mark::hit!(cfg_attr_active); 179 cov_mark::hit!(cfg_attr_active);
181 180
182 let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?; 181 let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?;
183 let hygiene = Hygiene::new_unhygienic(); // FIXME 182 let hygiene = Hygiene::new_unhygienic(); // FIXME
@@ -268,7 +267,7 @@ impl Attrs {
268 db: &dyn DefDatabase, 267 db: &dyn DefDatabase,
269 e: EnumId, 268 e: EnumId,
270 ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> { 269 ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
271 let krate = e.lookup(db).container.module(db).krate; 270 let krate = e.lookup(db).container.krate;
272 let src = e.child_source(db); 271 let src = e.child_source(db);
273 let mut res = ArenaMap::default(); 272 let mut res = ArenaMap::default();
274 273
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 16e1bac40..19c4eb521 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -20,7 +20,6 @@ use la_arena::{Arena, ArenaMap};
20use profile::Count; 20use profile::Count;
21use rustc_hash::FxHashMap; 21use rustc_hash::FxHashMap;
22use syntax::{ast, AstNode, AstPtr}; 22use syntax::{ast, AstNode, AstPtr};
23use test_utils::mark;
24 23
25pub(crate) use lower::LowerCtx; 24pub(crate) use lower::LowerCtx;
26 25
@@ -29,11 +28,10 @@ use crate::{
29 db::DefDatabase, 28 db::DefDatabase,
30 expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, 29 expr::{Expr, ExprId, Label, LabelId, Pat, PatId},
31 item_scope::BuiltinShadowMode, 30 item_scope::BuiltinShadowMode,
32 item_scope::ItemScope,
33 nameres::DefMap, 31 nameres::DefMap,
34 path::{ModPath, Path}, 32 path::{ModPath, Path},
35 src::HasSource, 33 src::HasSource,
36 AsMacroCall, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId, 34 AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId,
37}; 35};
38 36
39/// A subset of Expander that only deals with cfg attributes. We only need it to 37/// A subset of Expander that only deals with cfg attributes. We only need it to
@@ -105,7 +103,7 @@ impl Expander {
105 macro_call: ast::MacroCall, 103 macro_call: ast::MacroCall,
106 ) -> ExpandResult<Option<(Mark, T)>> { 104 ) -> ExpandResult<Option<(Mark, T)>> {
107 if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { 105 if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT {
108 mark::hit!(your_stack_belongs_to_me); 106 cov_mark::hit!(your_stack_belongs_to_me);
109 return ExpandResult::str_err("reached recursion limit during macro expansion".into()); 107 return ExpandResult::str_err("reached recursion limit during macro expansion".into());
110 } 108 }
111 109
@@ -227,7 +225,8 @@ pub struct Body {
227 pub params: Vec<PatId>, 225 pub params: Vec<PatId>,
228 /// The `ExprId` of the actual body expression. 226 /// The `ExprId` of the actual body expression.
229 pub body_expr: ExprId, 227 pub body_expr: ExprId,
230 pub item_scope: ItemScope, 228 /// Block expressions in this body that may contain inner items.
229 pub block_scopes: Vec<BlockId>,
231 _c: Count<Self>, 230 _c: Count<Self>,
232} 231}
233 232
@@ -296,7 +295,7 @@ impl Body {
296 } 295 }
297 }; 296 };
298 let expander = Expander::new(db, file_id, module); 297 let expander = Expander::new(db, file_id, module);
299 let (body, source_map) = Body::new(db, def, expander, params, body); 298 let (body, source_map) = Body::new(db, expander, params, body);
300 (Arc::new(body), Arc::new(source_map)) 299 (Arc::new(body), Arc::new(source_map))
301 } 300 }
302 301
@@ -306,12 +305,11 @@ impl Body {
306 305
307 fn new( 306 fn new(
308 db: &dyn DefDatabase, 307 db: &dyn DefDatabase,
309 def: DefWithBodyId,
310 expander: Expander, 308 expander: Expander,
311 params: Option<ast::ParamList>, 309 params: Option<ast::ParamList>,
312 body: Option<ast::Expr>, 310 body: Option<ast::Expr>,
313 ) -> (Body, BodySourceMap) { 311 ) -> (Body, BodySourceMap) {
314 lower::lower(db, def, expander, params, body) 312 lower::lower(db, expander, params, body)
315 } 313 }
316} 314}
317 315
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 40beb2f7a..8c8eb8007 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -1,17 +1,16 @@
1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` 1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
2//! representation. 2//! representation.
3 3
4use std::{any::type_name, mem, sync::Arc}; 4use std::mem;
5 5
6use either::Either; 6use either::Either;
7use hir_expand::{ 7use hir_expand::{
8 hygiene::Hygiene, 8 hygiene::Hygiene,
9 name::{name, AsName, Name}, 9 name::{name, AsName, Name},
10 ExpandError, HirFileId, MacroDefId, MacroDefKind, 10 ExpandError, HirFileId,
11}; 11};
12use la_arena::Arena; 12use la_arena::Arena;
13use profile::Count; 13use profile::Count;
14use rustc_hash::FxHashMap;
15use syntax::{ 14use syntax::{
16 ast::{ 15 ast::{
17 self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner, 16 self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner,
@@ -19,7 +18,6 @@ use syntax::{
19 }, 18 },
20 AstNode, AstPtr, SyntaxNodePtr, 19 AstNode, AstPtr, SyntaxNodePtr,
21}; 20};
22use test_utils::mark;
23 21
24use crate::{ 22use crate::{
25 adt::StructKind, 23 adt::StructKind,
@@ -33,11 +31,9 @@ use crate::{
33 Statement, 31 Statement,
34 }, 32 },
35 item_scope::BuiltinShadowMode, 33 item_scope::BuiltinShadowMode,
36 item_tree::{ItemTree, ItemTreeId, ItemTreeNode},
37 path::{GenericArgs, Path}, 34 path::{GenericArgs, Path},
38 type_ref::{Mutability, Rawness, TypeRef}, 35 type_ref::{Mutability, Rawness, TypeRef},
39 AdtId, BlockLoc, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, 36 AdtId, BlockLoc, ModuleDefId,
40 ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
41}; 37};
42 38
43use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; 39use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
@@ -61,15 +57,12 @@ impl LowerCtx {
61 57
62pub(super) fn lower( 58pub(super) fn lower(
63 db: &dyn DefDatabase, 59 db: &dyn DefDatabase,
64 def: DefWithBodyId,
65 expander: Expander, 60 expander: Expander,
66 params: Option<ast::ParamList>, 61 params: Option<ast::ParamList>,
67 body: Option<ast::Expr>, 62 body: Option<ast::Expr>,
68) -> (Body, BodySourceMap) { 63) -> (Body, BodySourceMap) {
69 let item_tree = db.item_tree(expander.current_file_id);
70 ExprCollector { 64 ExprCollector {
71 db, 65 db,
72 def,
73 source_map: BodySourceMap::default(), 66 source_map: BodySourceMap::default(),
74 body: Body { 67 body: Body {
75 exprs: Arena::default(), 68 exprs: Arena::default(),
@@ -77,14 +70,9 @@ pub(super) fn lower(
77 labels: Arena::default(), 70 labels: Arena::default(),
78 params: Vec::new(), 71 params: Vec::new(),
79 body_expr: dummy_expr_id(), 72 body_expr: dummy_expr_id(),
80 item_scope: Default::default(), 73 block_scopes: Vec::new(),
81 _c: Count::new(), 74 _c: Count::new(),
82 }, 75 },
83 item_trees: {
84 let mut map = FxHashMap::default();
85 map.insert(expander.current_file_id, item_tree);
86 map
87 },
88 expander, 76 expander,
89 } 77 }
90 .collect(params, body) 78 .collect(params, body)
@@ -92,12 +80,9 @@ pub(super) fn lower(
92 80
93struct ExprCollector<'a> { 81struct ExprCollector<'a> {
94 db: &'a dyn DefDatabase, 82 db: &'a dyn DefDatabase,
95 def: DefWithBodyId,
96 expander: Expander, 83 expander: Expander,
97 body: Body, 84 body: Body,
98 source_map: BodySourceMap, 85 source_map: BodySourceMap,
99
100 item_trees: FxHashMap<HirFileId, Arc<ItemTree>>,
101} 86}
102 87
103impl ExprCollector<'_> { 88impl ExprCollector<'_> {
@@ -286,7 +271,7 @@ impl ExprCollector<'_> {
286 None => self.collect_expr_opt(condition.expr()), 271 None => self.collect_expr_opt(condition.expr()),
287 // if let -- desugar to match 272 // if let -- desugar to match
288 Some(pat) => { 273 Some(pat) => {
289 mark::hit!(infer_resolve_while_let); 274 cov_mark::hit!(infer_resolve_while_let);
290 let pat = self.collect_pat(pat); 275 let pat = self.collect_pat(pat);
291 let match_expr = self.collect_expr_opt(condition.expr()); 276 let match_expr = self.collect_expr_opt(condition.expr());
292 let placeholder_pat = self.missing_pat(); 277 let placeholder_pat = self.missing_pat();
@@ -594,9 +579,6 @@ impl ExprCollector<'_> {
594 } else { 579 } else {
595 self.source_map.expansions.insert(macro_call, self.expander.current_file_id); 580 self.source_map.expansions.insert(macro_call, self.expander.current_file_id);
596 581
597 let item_tree = self.db.item_tree(self.expander.current_file_id);
598 self.item_trees.insert(self.expander.current_file_id, item_tree);
599
600 let id = collector(self, Some(expansion)); 582 let id = collector(self, Some(expansion));
601 self.expander.exit(self.db, mark); 583 self.expander.exit(self.db, mark);
602 id 584 id
@@ -606,32 +588,6 @@ impl ExprCollector<'_> {
606 } 588 }
607 } 589 }
608 590
609 fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> {
610 let id = self.expander.ast_id(ast);
611 let tree = &self.item_trees[&id.file_id];
612
613 // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
614
615 // Root file (non-macro).
616 let item_tree_id = tree
617 .all_inner_items()
618 .chain(tree.top_level_items().iter().copied())
619 .filter_map(|mod_item| mod_item.downcast::<N>())
620 .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast())
621 .or_else(|| {
622 log::debug!(
623 "couldn't find inner {} item for {:?} (AST: `{}` - {:?})",
624 type_name::<N>(),
625 id,
626 ast.syntax(),
627 ast.syntax(),
628 );
629 None
630 })?;
631
632 Some(ItemTreeId::new(id.file_id, item_tree_id))
633 }
634
635 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { 591 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
636 if let Some(expr) = expr { 592 if let Some(expr) = expr {
637 self.collect_expr(expr) 593 self.collect_expr(expr)
@@ -663,7 +619,6 @@ impl ExprCollector<'_> {
663 match expansion { 619 match expansion {
664 Some(expansion) => { 620 Some(expansion) => {
665 let statements: ast::MacroStmts = expansion; 621 let statements: ast::MacroStmts = expansion;
666 this.collect_stmts_items(statements.statements());
667 622
668 statements.statements().for_each(|stmt| { 623 statements.statements().for_each(|stmt| {
669 if let Some(mut r) = this.collect_stmt(stmt) { 624 if let Some(mut r) = this.collect_stmt(stmt) {
@@ -701,6 +656,8 @@ impl ExprCollector<'_> {
701 let block_loc = 656 let block_loc =
702 BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) }; 657 BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) };
703 let block_id = self.db.intern_block(block_loc); 658 let block_id = self.db.intern_block(block_loc);
659 self.body.block_scopes.push(block_id);
660
704 let opt_def_map = self.db.block_def_map(block_id); 661 let opt_def_map = self.db.block_def_map(block_id);
705 let has_def_map = opt_def_map.is_some(); 662 let has_def_map = opt_def_map.is_some();
706 let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone()); 663 let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone());
@@ -708,7 +665,6 @@ impl ExprCollector<'_> {
708 let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); 665 let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
709 let prev_local_module = mem::replace(&mut self.expander.module, module); 666 let prev_local_module = mem::replace(&mut self.expander.module, module);
710 667
711 self.collect_stmts_items(block.statements());
712 let statements = 668 let statements =
713 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); 669 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect();
714 let tail = block.tail_expr().map(|e| self.collect_expr(e)); 670 let tail = block.tail_expr().map(|e| self.collect_expr(e));
@@ -723,108 +679,6 @@ impl ExprCollector<'_> {
723 expr_id 679 expr_id
724 } 680 }
725 681
726 fn collect_stmts_items(&mut self, stmts: ast::AstChildren<ast::Stmt>) {
727 let container = ContainerId::DefWithBodyId(self.def);
728
729 let items = stmts
730 .filter_map(|stmt| match stmt {
731 ast::Stmt::Item(it) => Some(it),
732 ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None,
733 })
734 .filter_map(|item| {
735 let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
736 ast::Item::Fn(def) => {
737 let id = self.find_inner_item(&def)?;
738 (
739 FunctionLoc { container: container.into(), id }.intern(self.db).into(),
740 def.name(),
741 )
742 }
743 ast::Item::TypeAlias(def) => {
744 let id = self.find_inner_item(&def)?;
745 (
746 TypeAliasLoc { container: container.into(), id }.intern(self.db).into(),
747 def.name(),
748 )
749 }
750 ast::Item::Const(def) => {
751 let id = self.find_inner_item(&def)?;
752 (
753 ConstLoc { container: container.into(), id }.intern(self.db).into(),
754 def.name(),
755 )
756 }
757 ast::Item::Static(def) => {
758 let id = self.find_inner_item(&def)?;
759 (StaticLoc { container, id }.intern(self.db).into(), def.name())
760 }
761 ast::Item::Struct(def) => {
762 let id = self.find_inner_item(&def)?;
763 (StructLoc { container, id }.intern(self.db).into(), def.name())
764 }
765 ast::Item::Enum(def) => {
766 let id = self.find_inner_item(&def)?;
767 (EnumLoc { container, id }.intern(self.db).into(), def.name())
768 }
769 ast::Item::Union(def) => {
770 let id = self.find_inner_item(&def)?;
771 (UnionLoc { container, id }.intern(self.db).into(), def.name())
772 }
773 ast::Item::Trait(def) => {
774 let id = self.find_inner_item(&def)?;
775 (TraitLoc { container, id }.intern(self.db).into(), def.name())
776 }
777 ast::Item::ExternBlock(_) => return None, // FIXME: collect from extern blocks
778 ast::Item::Impl(_)
779 | ast::Item::Use(_)
780 | ast::Item::ExternCrate(_)
781 | ast::Item::Module(_)
782 | ast::Item::MacroCall(_) => return None,
783 ast::Item::MacroRules(def) => {
784 return Some(Either::Right(ast::Macro::from(def)));
785 }
786 ast::Item::MacroDef(def) => {
787 return Some(Either::Right(ast::Macro::from(def)));
788 }
789 };
790
791 Some(Either::Left((def, name)))
792 })
793 .collect::<Vec<_>>();
794
795 for either in items {
796 match either {
797 Either::Left((def, name)) => {
798 self.body.item_scope.define_def(def);
799 if let Some(name) = name {
800 let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
801 let has_constructor = match def {
802 ModuleDefId::AdtId(AdtId::StructId(s)) => {
803 self.db.struct_data(s).variant_data.kind() != StructKind::Record
804 }
805 _ => true,
806 };
807 self.body.item_scope.push_res(
808 name.as_name(),
809 crate::per_ns::PerNs::from_def(def, vis, has_constructor),
810 );
811 }
812 }
813 Either::Right(e) => {
814 let mac = MacroDefId {
815 krate: self.expander.def_map.krate(),
816 ast_id: Some(self.expander.ast_id(&e)),
817 kind: MacroDefKind::Declarative,
818 local_inner: false,
819 };
820 if let Some(name) = e.name() {
821 self.body.item_scope.define_legacy_macro(name.as_name(), mac);
822 }
823 }
824 }
825 }
826 }
827
828 fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId { 682 fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId {
829 if let Some(block) = expr { 683 if let Some(block) = expr {
830 self.collect_block(block) 684 self.collect_block(block)
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index 210b4a617..1bbb54fc6 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -186,7 +186,7 @@ mod tests {
186 use base_db::{fixture::WithFixture, FileId, SourceDatabase}; 186 use base_db::{fixture::WithFixture, FileId, SourceDatabase};
187 use hir_expand::{name::AsName, InFile}; 187 use hir_expand::{name::AsName, InFile};
188 use syntax::{algo::find_node_at_offset, ast, AstNode}; 188 use syntax::{algo::find_node_at_offset, ast, AstNode};
189 use test_utils::{assert_eq_text, extract_offset, mark}; 189 use test_utils::{assert_eq_text, extract_offset};
190 190
191 use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; 191 use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId};
192 192
@@ -454,7 +454,7 @@ fn foo() {
454 454
455 #[test] 455 #[test]
456 fn while_let_desugaring() { 456 fn while_let_desugaring() {
457 mark::check!(infer_resolve_while_let); 457 cov_mark::check!(infer_resolve_while_let);
458 do_check_local_name( 458 do_check_local_name(
459 r#" 459 r#"
460fn test() { 460fn test() {
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index bb43569d7..991a32b15 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -2,7 +2,6 @@ mod block;
2 2
3use base_db::{fixture::WithFixture, SourceDatabase}; 3use base_db::{fixture::WithFixture, SourceDatabase};
4use expect_test::Expect; 4use expect_test::Expect;
5use test_utils::mark;
6 5
7use crate::{test_db::TestDB, ModuleDefId}; 6use crate::{test_db::TestDB, ModuleDefId};
8 7
@@ -48,7 +47,7 @@ fn check_at(ra_fixture: &str, expect: Expect) {
48 47
49#[test] 48#[test]
50fn your_stack_belongs_to_me() { 49fn your_stack_belongs_to_me() {
51 mark::check!(your_stack_belongs_to_me); 50 cov_mark::check!(your_stack_belongs_to_me);
52 lower( 51 lower(
53 " 52 "
54macro_rules! n_nuple { 53macro_rules! n_nuple {
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs
index 8bca72a17..3b6ba4cde 100644
--- a/crates/hir_def/src/body/tests/block.rs
+++ b/crates/hir_def/src/body/tests/block.rs
@@ -165,16 +165,16 @@ fn macro_resolve() {
165 check_at( 165 check_at(
166 r#" 166 r#"
167//- /lib.rs crate:lib deps:core 167//- /lib.rs crate:lib deps:core
168use core::mark; 168use core::cov_mark;
169 169
170fn f() { 170fn f() {
171 fn nested() { 171 fn nested() {
172 mark::hit!(Hit); 172 cov_mark::hit!(Hit);
173 $0 173 $0
174 } 174 }
175} 175}
176//- /core.rs crate:core 176//- /core.rs crate:core
177pub mod mark { 177pub mod cov_mark {
178 #[macro_export] 178 #[macro_export]
179 macro_rules! _hit { 179 macro_rules! _hit {
180 ($name:ident) => { 180 ($name:ident) => {
@@ -193,8 +193,8 @@ pub mod mark {
193 nested: v 193 nested: v
194 194
195 crate 195 crate
196 cov_mark: t
196 f: v 197 f: v
197 mark: t
198 "#]], 198 "#]],
199 ); 199 );
200} 200}
@@ -264,7 +264,7 @@ fn main() {
264fn underscore_import() { 264fn underscore_import() {
265 // This used to panic, because the default (private) visibility inside block expressions would 265 // This used to panic, because the default (private) visibility inside block expressions would
266 // point into the containing `DefMap`, which visibilities should never be able to do. 266 // point into the containing `DefMap`, which visibilities should never be able to do.
267 mark::check!(adjust_vis_in_block_def_map); 267 cov_mark::check!(adjust_vis_in_block_def_map);
268 check_at( 268 check_at(
269 r#" 269 r#"
270mod m { 270mod m {
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
index 75c2d756b..2a331dcaf 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -17,13 +17,16 @@ use crate::{
17}; 17};
18 18
19pub trait ChildBySource { 19pub trait ChildBySource {
20 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap;
21}
22
23impl ChildBySource for TraitId {
24 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 20 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
25 let mut res = DynMap::default(); 21 let mut res = DynMap::default();
22 self.child_by_source_to(db, &mut res);
23 res
24 }
25 fn child_by_source_to(&self, db: &dyn DefDatabase, map: &mut DynMap);
26}
26 27
28impl ChildBySource for TraitId {
29 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
27 let data = db.trait_data(*self); 30 let data = db.trait_data(*self);
28 for (_name, item) in data.items.iter() { 31 for (_name, item) in data.items.iter() {
29 match *item { 32 match *item {
@@ -41,15 +44,11 @@ impl ChildBySource for TraitId {
41 } 44 }
42 } 45 }
43 } 46 }
44
45 res
46 } 47 }
47} 48}
48 49
49impl ChildBySource for ImplId { 50impl ChildBySource for ImplId {
50 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 51 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
51 let mut res = DynMap::default();
52
53 let data = db.impl_data(*self); 52 let data = db.impl_data(*self);
54 for &item in data.items.iter() { 53 for &item in data.items.iter() {
55 match item { 54 match item {
@@ -67,25 +66,21 @@ impl ChildBySource for ImplId {
67 } 66 }
68 } 67 }
69 } 68 }
70
71 res
72 } 69 }
73} 70}
74 71
75impl ChildBySource for ModuleId { 72impl ChildBySource for ModuleId {
76 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 73 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
77 let def_map = self.def_map(db); 74 let def_map = self.def_map(db);
78 let module_data = &def_map[self.local_id]; 75 let module_data = &def_map[self.local_id];
79 module_data.scope.child_by_source(db) 76 module_data.scope.child_by_source_to(db, res);
80 } 77 }
81} 78}
82 79
83impl ChildBySource for ItemScope { 80impl ChildBySource for ItemScope {
84 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 81 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
85 let mut res = DynMap::default(); 82 self.declarations().for_each(|item| add_module_def(db, res, item));
86 self.declarations().for_each(|item| add_module_def(db, &mut res, item)); 83 self.impls().for_each(|imp| add_impl(db, res, imp));
87 self.impls().for_each(|imp| add_impl(db, &mut res, imp));
88 return res;
89 84
90 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { 85 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) {
91 match item { 86 match item {
@@ -134,9 +129,7 @@ impl ChildBySource for ItemScope {
134} 129}
135 130
136impl ChildBySource for VariantId { 131impl ChildBySource for VariantId {
137 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 132 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
138 let mut res = DynMap::default();
139
140 let arena_map = self.child_source(db); 133 let arena_map = self.child_source(db);
141 let arena_map = arena_map.as_ref(); 134 let arena_map = arena_map.as_ref();
142 for (local_id, source) in arena_map.value.iter() { 135 for (local_id, source) in arena_map.value.iter() {
@@ -150,28 +143,27 @@ impl ChildBySource for VariantId {
150 } 143 }
151 } 144 }
152 } 145 }
153 res
154 } 146 }
155} 147}
156 148
157impl ChildBySource for EnumId { 149impl ChildBySource for EnumId {
158 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 150 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
159 let mut res = DynMap::default();
160
161 let arena_map = self.child_source(db); 151 let arena_map = self.child_source(db);
162 let arena_map = arena_map.as_ref(); 152 let arena_map = arena_map.as_ref();
163 for (local_id, source) in arena_map.value.iter() { 153 for (local_id, source) in arena_map.value.iter() {
164 let id = EnumVariantId { parent: *self, local_id }; 154 let id = EnumVariantId { parent: *self, local_id };
165 res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id) 155 res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id)
166 } 156 }
167
168 res
169 } 157 }
170} 158}
171 159
172impl ChildBySource for DefWithBodyId { 160impl ChildBySource for DefWithBodyId {
173 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 161 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
174 let body = db.body(*self); 162 let body = db.body(*self);
175 body.item_scope.child_by_source(db) 163 for def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) {
164 // All block expressions are merged into the same map, because they logically all add
165 // inner items to the containing `DefWithBodyId`.
166 def_map[def_map.root()].scope.child_by_source_to(db, res);
167 }
176 } 168 }
177} 169}
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index d3380e0f4..aea53d527 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -97,7 +97,7 @@ impl TraitData {
97 let tr_def = &item_tree[tr_loc.id.value]; 97 let tr_def = &item_tree[tr_loc.id.value];
98 let name = tr_def.name.clone(); 98 let name = tr_def.name.clone();
99 let auto = tr_def.auto; 99 let auto = tr_def.auto;
100 let module_id = tr_loc.container.module(db); 100 let module_id = tr_loc.container;
101 let container = AssocContainerId::TraitId(tr); 101 let container = AssocContainerId::TraitId(tr);
102 let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); 102 let mut expander = Expander::new(db, tr_loc.id.file_id, module_id);
103 103
@@ -147,7 +147,7 @@ impl ImplData {
147 let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); 147 let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone());
148 let target_type = item_tree[impl_def.target_type].clone(); 148 let target_type = item_tree[impl_def.target_type].clone();
149 let is_negative = impl_def.is_negative; 149 let is_negative = impl_def.is_negative;
150 let module_id = impl_loc.container.module(db); 150 let module_id = impl_loc.container;
151 let container = AssocContainerId::ImplId(id); 151 let container = AssocContainerId::ImplId(id);
152 let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); 152 let mut expander = Expander::new(db, impl_loc.id.file_id, module_id);
153 153
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 3a98ffbaa..de08e2737 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -4,7 +4,6 @@ use std::iter;
4 4
5use hir_expand::name::{known, AsName, Name}; 5use hir_expand::name::{known, AsName, Name};
6use rustc_hash::FxHashSet; 6use rustc_hash::FxHashSet;
7use test_utils::mark;
8 7
9use crate::nameres::DefMap; 8use crate::nameres::DefMap;
10use crate::{ 9use crate::{
@@ -215,7 +214,7 @@ fn find_path_inner(
215 best_path_len - 1, 214 best_path_len - 1,
216 prefixed, 215 prefixed,
217 )?; 216 )?;
218 mark::hit!(partially_imported); 217 cov_mark::hit!(partially_imported);
219 path.push_segment(info.path.segments.last().unwrap().clone()); 218 path.push_segment(info.path.segments.last().unwrap().clone());
220 Some(path) 219 Some(path)
221 }) 220 })
@@ -235,7 +234,7 @@ fn find_path_inner(
235 // that correctly (FIXME). 234 // that correctly (FIXME).
236 if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) { 235 if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) {
237 if item_module.def_map(db).block_id().is_some() && prefixed.is_some() { 236 if item_module.def_map(db).block_id().is_some() && prefixed.is_some() {
238 mark::hit!(prefixed_in_block_expression); 237 cov_mark::hit!(prefixed_in_block_expression);
239 prefixed = Some(PrefixKind::Plain); 238 prefixed = Some(PrefixKind::Plain);
240 } 239 }
241 } 240 }
@@ -252,18 +251,18 @@ fn find_path_inner(
252fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { 251fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
253 if old_path.starts_with_std() && new_path.can_start_with_std() { 252 if old_path.starts_with_std() && new_path.can_start_with_std() {
254 if prefer_no_std { 253 if prefer_no_std {
255 mark::hit!(prefer_no_std_paths); 254 cov_mark::hit!(prefer_no_std_paths);
256 new_path 255 new_path
257 } else { 256 } else {
258 mark::hit!(prefer_std_paths); 257 cov_mark::hit!(prefer_std_paths);
259 old_path 258 old_path
260 } 259 }
261 } else if new_path.starts_with_std() && old_path.can_start_with_std() { 260 } else if new_path.starts_with_std() && old_path.can_start_with_std() {
262 if prefer_no_std { 261 if prefer_no_std {
263 mark::hit!(prefer_no_std_paths); 262 cov_mark::hit!(prefer_no_std_paths);
264 old_path 263 old_path
265 } else { 264 } else {
266 mark::hit!(prefer_std_paths); 265 cov_mark::hit!(prefer_std_paths);
267 new_path 266 new_path
268 } 267 }
269 } else if new_path.len() < old_path.len() { 268 } else if new_path.len() < old_path.len() {
@@ -364,7 +363,6 @@ mod tests {
364 use base_db::fixture::WithFixture; 363 use base_db::fixture::WithFixture;
365 use hir_expand::hygiene::Hygiene; 364 use hir_expand::hygiene::Hygiene;
366 use syntax::ast::AstNode; 365 use syntax::ast::AstNode;
367 use test_utils::mark;
368 366
369 use crate::test_db::TestDB; 367 use crate::test_db::TestDB;
370 368
@@ -522,7 +520,7 @@ mod tests {
522 520
523 #[test] 521 #[test]
524 fn partially_imported() { 522 fn partially_imported() {
525 mark::check!(partially_imported); 523 cov_mark::check!(partially_imported);
526 // Tests that short paths are used even for external items, when parts of the path are 524 // Tests that short paths are used even for external items, when parts of the path are
527 // already in scope. 525 // already in scope.
528 let code = r#" 526 let code = r#"
@@ -686,7 +684,7 @@ mod tests {
686 684
687 #[test] 685 #[test]
688 fn prefer_std_paths_over_alloc() { 686 fn prefer_std_paths_over_alloc() {
689 mark::check!(prefer_std_paths); 687 cov_mark::check!(prefer_std_paths);
690 let code = r#" 688 let code = r#"
691 //- /main.rs crate:main deps:alloc,std 689 //- /main.rs crate:main deps:alloc,std
692 $0 690 $0
@@ -712,7 +710,7 @@ mod tests {
712 710
713 #[test] 711 #[test]
714 fn prefer_core_paths_over_std() { 712 fn prefer_core_paths_over_std() {
715 mark::check!(prefer_no_std_paths); 713 cov_mark::check!(prefer_no_std_paths);
716 let code = r#" 714 let code = r#"
717 //- /main.rs crate:main deps:core,std 715 //- /main.rs crate:main deps:core,std
718 #![no_std] 716 #![no_std]
@@ -842,7 +840,7 @@ mod tests {
842 840
843 #[test] 841 #[test]
844 fn inner_items_from_inner_module() { 842 fn inner_items_from_inner_module() {
845 mark::check!(prefixed_in_block_expression); 843 cov_mark::check!(prefixed_in_block_expression);
846 check_found_path( 844 check_found_path(
847 r#" 845 r#"
848 fn main() { 846 fn main() {
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 3ace3be1f..a056ab797 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -421,8 +421,7 @@ impl HasChildSource<LocalConstParamId> for GenericDefId {
421} 421}
422 422
423impl ChildBySource for GenericDefId { 423impl ChildBySource for GenericDefId {
424 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 424 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
425 let mut res = DynMap::default();
426 let (_, sm) = GenericParams::new(db, *self); 425 let (_, sm) = GenericParams::new(db, *self);
427 426
428 let sm = sm.as_ref(); 427 let sm = sm.as_ref();
@@ -440,6 +439,5 @@ impl ChildBySource for GenericDefId {
440 let id = ConstParamId { parent: *self, local_id }; 439 let id = ConstParamId { parent: *self, local_id };
441 res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id); 440 res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id);
442 } 441 }
443 res
444 } 442 }
445} 443}
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index 0a3dc7956..369bc3350 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -8,7 +8,6 @@ use hir_expand::name::Name;
8use indexmap::{map::Entry, IndexMap}; 8use indexmap::{map::Entry, IndexMap};
9use itertools::Itertools; 9use itertools::Itertools;
10use rustc_hash::{FxHashSet, FxHasher}; 10use rustc_hash::{FxHashSet, FxHasher};
11use test_utils::mark;
12 11
13use crate::{ 12use crate::{
14 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, 13 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
@@ -193,7 +192,7 @@ impl ImportMap {
193 // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias` 192 // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
194 // qualifier, ergo no need to store it for imports in import_map 193 // qualifier, ergo no need to store it for imports in import_map
195 AssocItemId::TypeAliasId(_) => { 194 AssocItemId::TypeAliasId(_) => {
196 mark::hit!(type_aliases_ignored); 195 cov_mark::hit!(type_aliases_ignored);
197 continue; 196 continue;
198 } 197 }
199 }; 198 };
@@ -388,7 +387,7 @@ pub fn search_dependencies<'a>(
388 db: &'a dyn DefDatabase, 387 db: &'a dyn DefDatabase,
389 krate: CrateId, 388 krate: CrateId,
390 query: Query, 389 query: Query,
391) -> Vec<ItemInNs> { 390) -> FxHashSet<ItemInNs> {
392 let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query)); 391 let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query));
393 392
394 let graph = db.crate_graph(); 393 let graph = db.crate_graph();
@@ -403,41 +402,42 @@ pub fn search_dependencies<'a>(
403 } 402 }
404 403
405 let mut stream = op.union(); 404 let mut stream = op.union();
406 let mut res = Vec::new(); 405
406 let mut all_indexed_values = FxHashSet::default();
407 while let Some((_, indexed_values)) = stream.next() { 407 while let Some((_, indexed_values)) = stream.next() {
408 for indexed_value in indexed_values { 408 all_indexed_values.extend(indexed_values.iter().copied());
409 let import_map = &import_maps[indexed_value.index]; 409 }
410 let importables = &import_map.importables[indexed_value.value as usize..];
411 410
412 let common_importable_data = &import_map.map[&importables[0]]; 411 let mut res = FxHashSet::default();
413 if !query.import_matches(common_importable_data, true) { 412 for indexed_value in all_indexed_values {
414 continue; 413 let import_map = &import_maps[indexed_value.index];
415 } 414 let importables = &import_map.importables[indexed_value.value as usize..];
416 415
417 // Path shared by the importable items in this group. 416 let common_importable_data = &import_map.map[&importables[0]];
418 let common_importables_path_fst = fst_path(&common_importable_data.path); 417 if !query.import_matches(common_importable_data, true) {
419 // Add the items from this `ModPath` group. Those are all subsequent items in 418 continue;
420 // `importables` whose paths match `path`. 419 }
421 let iter = importables 420
422 .iter() 421 // Path shared by the importable items in this group.
423 .copied() 422 let common_importables_path_fst = fst_path(&common_importable_data.path);
424 .take_while(|item| { 423 // Add the items from this `ModPath` group. Those are all subsequent items in
425 common_importables_path_fst == fst_path(&import_map.map[item].path) 424 // `importables` whose paths match `path`.
426 }) 425 let iter = importables
427 .filter(|&item| match item_import_kind(item) { 426 .iter()
428 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), 427 .copied()
429 None => true, 428 .take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path))
430 }) 429 .filter(|&item| match item_import_kind(item) {
431 .filter(|item| { 430 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
432 !query.case_sensitive // we've already checked the common importables path case-insensitively 431 None => true,
432 })
433 .filter(|item| {
434 !query.case_sensitive // we've already checked the common importables path case-insensitively
433 || query.import_matches(&import_map.map[item], false) 435 || query.import_matches(&import_map.map[item], false)
434 }); 436 });
435 res.extend(iter); 437 res.extend(iter);
436 438
437 if res.len() >= query.limit { 439 if res.len() >= query.limit {
438 res.truncate(query.limit); 440 return res;
439 return res;
440 }
441 } 441 }
442 } 442 }
443 443
@@ -462,7 +462,6 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
462mod tests { 462mod tests {
463 use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; 463 use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
464 use expect_test::{expect, Expect}; 464 use expect_test::{expect, Expect};
465 use test_utils::mark;
466 465
467 use crate::{test_db::TestDB, AssocContainerId, Lookup}; 466 use crate::{test_db::TestDB, AssocContainerId, Lookup};
468 467
@@ -800,7 +799,7 @@ mod tests {
800 799
801 #[test] 800 #[test]
802 fn fuzzy_import_trait_and_assoc_items() { 801 fn fuzzy_import_trait_and_assoc_items() {
803 mark::check!(type_aliases_ignored); 802 cov_mark::check!(type_aliases_ignored);
804 let ra_fixture = r#" 803 let ra_fixture = r#"
805 //- /main.rs crate:main deps:dep 804 //- /main.rs crate:main deps:dep
806 //- /dep.rs crate:dep 805 //- /dep.rs crate:dep
@@ -821,10 +820,10 @@ mod tests {
821 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), 820 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
822 expect![[r#" 821 expect![[r#"
823 dep::fmt (t) 822 dep::fmt (t)
823 dep::fmt::Display::format_method (a)
824 dep::fmt::Display (t) 824 dep::fmt::Display (t)
825 dep::fmt::Display::FMT_CONST (a) 825 dep::fmt::Display::FMT_CONST (a)
826 dep::fmt::Display::format_function (a) 826 dep::fmt::Display::format_function (a)
827 dep::fmt::Display::format_method (a)
828 "#]], 827 "#]],
829 ); 828 );
830 } 829 }
@@ -850,9 +849,9 @@ mod tests {
850 "main", 849 "main",
851 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(), 850 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(),
852 expect![[r#" 851 expect![[r#"
852 dep::fmt::Display::format_method (a)
853 dep::fmt::Display::FMT_CONST (a) 853 dep::fmt::Display::FMT_CONST (a)
854 dep::fmt::Display::format_function (a) 854 dep::fmt::Display::format_function (a)
855 dep::fmt::Display::format_method (a)
856 "#]], 855 "#]],
857 ); 856 );
858 857
@@ -911,12 +910,12 @@ mod tests {
911 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), 910 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
912 expect![[r#" 911 expect![[r#"
913 dep::fmt (t) 912 dep::fmt (t)
914 dep::Fmt (t) 913 dep::format (f)
915 dep::Fmt (v) 914 dep::Fmt (v)
916 dep::Fmt (m)
917 dep::fmt::Display (t) 915 dep::fmt::Display (t)
916 dep::Fmt (t)
918 dep::fmt::Display::fmt (a) 917 dep::fmt::Display::fmt (a)
919 dep::format (f) 918 dep::Fmt (m)
920 "#]], 919 "#]],
921 ); 920 );
922 921
@@ -926,10 +925,10 @@ mod tests {
926 Query::new("fmt".to_string()).search_mode(SearchMode::Equals), 925 Query::new("fmt".to_string()).search_mode(SearchMode::Equals),
927 expect![[r#" 926 expect![[r#"
928 dep::fmt (t) 927 dep::fmt (t)
929 dep::Fmt (t)
930 dep::Fmt (v) 928 dep::Fmt (v)
931 dep::Fmt (m) 929 dep::Fmt (t)
932 dep::fmt::Display::fmt (a) 930 dep::fmt::Display::fmt (a)
931 dep::Fmt (m)
933 "#]], 932 "#]],
934 ); 933 );
935 934
@@ -939,11 +938,11 @@ mod tests {
939 Query::new("fmt".to_string()).search_mode(SearchMode::Contains), 938 Query::new("fmt".to_string()).search_mode(SearchMode::Contains),
940 expect![[r#" 939 expect![[r#"
941 dep::fmt (t) 940 dep::fmt (t)
942 dep::Fmt (t)
943 dep::Fmt (v) 941 dep::Fmt (v)
944 dep::Fmt (m)
945 dep::fmt::Display (t) 942 dep::fmt::Display (t)
943 dep::Fmt (t)
946 dep::fmt::Display::fmt (a) 944 dep::fmt::Display::fmt (a)
945 dep::Fmt (m)
947 "#]], 946 "#]],
948 ); 947 );
949 } 948 }
@@ -980,11 +979,11 @@ mod tests {
980 Query::new("fmt".to_string()), 979 Query::new("fmt".to_string()),
981 expect![[r#" 980 expect![[r#"
982 dep::fmt (t) 981 dep::fmt (t)
983 dep::Fmt (t)
984 dep::Fmt (v) 982 dep::Fmt (v)
985 dep::Fmt (m)
986 dep::fmt::Display (t) 983 dep::fmt::Display (t)
984 dep::Fmt (t)
987 dep::fmt::Display::fmt (a) 985 dep::fmt::Display::fmt (a)
986 dep::Fmt (m)
988 "#]], 987 "#]],
989 ); 988 );
990 989
@@ -994,10 +993,10 @@ mod tests {
994 Query::new("fmt".to_string()).name_only(), 993 Query::new("fmt".to_string()).name_only(),
995 expect![[r#" 994 expect![[r#"
996 dep::fmt (t) 995 dep::fmt (t)
997 dep::Fmt (t)
998 dep::Fmt (v) 996 dep::Fmt (v)
999 dep::Fmt (m) 997 dep::Fmt (t)
1000 dep::fmt::Display::fmt (a) 998 dep::fmt::Display::fmt (a)
999 dep::Fmt (m)
1001 "#]], 1000 "#]],
1002 ); 1001 );
1003 } 1002 }
@@ -1018,9 +1017,9 @@ mod tests {
1018 Query::new("FMT".to_string()), 1017 Query::new("FMT".to_string()),
1019 expect![[r#" 1018 expect![[r#"
1020 dep::fmt (t) 1019 dep::fmt (t)
1020 dep::FMT (v)
1021 dep::fmt (v) 1021 dep::fmt (v)
1022 dep::FMT (t) 1022 dep::FMT (t)
1023 dep::FMT (v)
1024 "#]], 1023 "#]],
1025 ); 1024 );
1026 1025
@@ -1060,6 +1059,8 @@ mod tests {
1060 expect![[r#" 1059 expect![[r#"
1061 dep::fmt (t) 1060 dep::fmt (t)
1062 dep::Fmt (t) 1061 dep::Fmt (t)
1062 dep::Fmt (m)
1063 dep::Fmt (v)
1063 "#]], 1064 "#]],
1064 ); 1065 );
1065 } 1066 }
@@ -1080,9 +1081,9 @@ mod tests {
1080 Query::new("FMT".to_string()), 1081 Query::new("FMT".to_string()),
1081 expect![[r#" 1082 expect![[r#"
1082 dep::fmt (t) 1083 dep::fmt (t)
1084 dep::FMT (v)
1083 dep::fmt (v) 1085 dep::fmt (v)
1084 dep::FMT (t) 1086 dep::FMT (t)
1085 dep::FMT (v)
1086 "#]], 1087 "#]],
1087 ); 1088 );
1088 1089
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index 4e5daa2ff..aafd73b60 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -9,7 +9,6 @@ use hir_expand::MacroDefKind;
9use once_cell::sync::Lazy; 9use once_cell::sync::Lazy;
10use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
11use stdx::format_to; 11use stdx::format_to;
12use test_utils::mark;
13 12
14use crate::{ 13use crate::{
15 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, 14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId,
@@ -169,37 +168,6 @@ impl ItemScope {
169 self.unnamed_trait_imports.insert(tr, vis); 168 self.unnamed_trait_imports.insert(tr, vis);
170 } 169 }
171 170
172 pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool {
173 let mut changed = false;
174
175 if let Some(types) = def.types {
176 self.types.entry(name.clone()).or_insert_with(|| {
177 changed = true;
178 types
179 });
180 }
181 if let Some(values) = def.values {
182 self.values.entry(name.clone()).or_insert_with(|| {
183 changed = true;
184 values
185 });
186 }
187 if let Some(macros) = def.macros {
188 self.macros.entry(name.clone()).or_insert_with(|| {
189 changed = true;
190 macros
191 });
192 }
193
194 if def.is_none() {
195 if self.unresolved.insert(name) {
196 changed = true;
197 }
198 }
199
200 changed
201 }
202
203 pub(crate) fn push_res_with_import( 171 pub(crate) fn push_res_with_import(
204 &mut self, 172 &mut self,
205 glob_imports: &mut PerNsGlobImports, 173 glob_imports: &mut PerNsGlobImports,
@@ -237,7 +205,7 @@ impl ItemScope {
237 if $glob_imports.$field.contains(&$lookup) 205 if $glob_imports.$field.contains(&$lookup)
238 && matches!($def_import_type, ImportType::Named) => 206 && matches!($def_import_type, ImportType::Named) =>
239 { 207 {
240 mark::hit!(import_shadowed); 208 cov_mark::hit!(import_shadowed);
241 $glob_imports.$field.remove(&$lookup); 209 $glob_imports.$field.remove(&$lookup);
242 if let Some(fld) = $def.$field { 210 if let Some(fld) = $def.$field {
243 entry.insert(fld); 211 entry.insert(fld);
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 3233b1957..09bcb10dc 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -25,7 +25,6 @@ use profile::Count;
25use rustc_hash::FxHashMap; 25use rustc_hash::FxHashMap;
26use smallvec::SmallVec; 26use smallvec::SmallVec;
27use syntax::{ast, match_ast, SyntaxKind}; 27use syntax::{ast, match_ast, SyntaxKind};
28use test_utils::mark;
29 28
30use crate::{ 29use crate::{
31 attr::{Attrs, RawAttrs}, 30 attr::{Attrs, RawAttrs},
@@ -210,18 +209,6 @@ impl ItemTree {
210 } 209 }
211 } 210 }
212 211
213 pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source {
214 // This unwrap cannot fail, since it has either succeeded above, or resulted in an empty
215 // ItemTree (in which case there is no valid `FileItemTreeId` to call this method with).
216 let root =
217 db.parse_or_expand(of.file_id).expect("parse_or_expand failed on constructed ItemTree");
218
219 let id = self[of.value].ast_id();
220 let map = db.ast_id_map(of.file_id);
221 let ptr = map.get(id);
222 ptr.to_node(&root)
223 }
224
225 fn data(&self) -> &ItemTreeData { 212 fn data(&self) -> &ItemTreeData {
226 self.data.as_ref().expect("attempted to access data of empty ItemTree") 213 self.data.as_ref().expect("attempted to access data of empty ItemTree")
227 } 214 }
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 8f2f0b340..240fdacf9 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -466,7 +466,7 @@ impl Ctx {
466 .collect() 466 .collect()
467 }) 467 })
468 .unwrap_or_else(|| { 468 .unwrap_or_else(|| {
469 mark::hit!(name_res_works_for_broken_modules); 469 cov_mark::hit!(name_res_works_for_broken_modules);
470 Box::new([]) as Box<[_]> 470 Box::new([]) as Box<[_]>
471 }), 471 }),
472 } 472 }
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 4498d94bb..6d11c5be4 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -108,7 +108,7 @@ pub type LocalModuleId = Idx<nameres::ModuleData>;
108 108
109#[derive(Debug)] 109#[derive(Debug)]
110pub struct ItemLoc<N: ItemTreeNode> { 110pub struct ItemLoc<N: ItemTreeNode> {
111 pub container: ContainerId, 111 pub container: ModuleId,
112 pub id: ItemTreeId<N>, 112 pub id: ItemTreeId<N>,
113} 113}
114 114
@@ -279,18 +279,12 @@ pub struct ConstParamId {
279pub type LocalConstParamId = Idx<generics::ConstParamData>; 279pub type LocalConstParamId = Idx<generics::ConstParamData>;
280 280
281#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 281#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
282pub enum ContainerId {
283 ModuleId(ModuleId),
284 DefWithBodyId(DefWithBodyId),
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
288pub enum AssocContainerId { 282pub enum AssocContainerId {
289 ContainerId(ContainerId), 283 ModuleId(ModuleId),
290 ImplId(ImplId), 284 ImplId(ImplId),
291 TraitId(TraitId), 285 TraitId(TraitId),
292} 286}
293impl_from!(ContainerId for AssocContainerId); 287impl_from!(ModuleId for AssocContainerId);
294 288
295/// A Data Type 289/// A Data Type
296#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 290#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -447,21 +441,12 @@ pub trait HasModule {
447 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId; 441 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId;
448} 442}
449 443
450impl HasModule for ContainerId {
451 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
452 match *self {
453 ContainerId::ModuleId(it) => it,
454 ContainerId::DefWithBodyId(it) => it.module(db),
455 }
456 }
457}
458
459impl HasModule for AssocContainerId { 444impl HasModule for AssocContainerId {
460 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 445 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
461 match *self { 446 match *self {
462 AssocContainerId::ContainerId(it) => it.module(db), 447 AssocContainerId::ModuleId(it) => it,
463 AssocContainerId::ImplId(it) => it.lookup(db).container.module(db), 448 AssocContainerId::ImplId(it) => it.lookup(db).container,
464 AssocContainerId::TraitId(it) => it.lookup(db).container.module(db), 449 AssocContainerId::TraitId(it) => it.lookup(db).container,
465 } 450 }
466 } 451 }
467} 452}
@@ -479,16 +464,15 @@ impl HasModule for AdtId {
479 AdtId::UnionId(it) => it.lookup(db).container, 464 AdtId::UnionId(it) => it.lookup(db).container,
480 AdtId::EnumId(it) => it.lookup(db).container, 465 AdtId::EnumId(it) => it.lookup(db).container,
481 } 466 }
482 .module(db)
483 } 467 }
484} 468}
485 469
486impl HasModule for VariantId { 470impl HasModule for VariantId {
487 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 471 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
488 match self { 472 match self {
489 VariantId::EnumVariantId(it) => it.parent.lookup(db).container.module(db), 473 VariantId::EnumVariantId(it) => it.parent.lookup(db).container,
490 VariantId::StructId(it) => it.lookup(db).container.module(db), 474 VariantId::StructId(it) => it.lookup(db).container,
491 VariantId::UnionId(it) => it.lookup(db).container.module(db), 475 VariantId::UnionId(it) => it.lookup(db).container,
492 } 476 }
493 } 477 }
494} 478}
@@ -518,18 +502,18 @@ impl HasModule for GenericDefId {
518 match self { 502 match self {
519 GenericDefId::FunctionId(it) => it.lookup(db).module(db), 503 GenericDefId::FunctionId(it) => it.lookup(db).module(db),
520 GenericDefId::AdtId(it) => it.module(db), 504 GenericDefId::AdtId(it) => it.module(db),
521 GenericDefId::TraitId(it) => it.lookup(db).container.module(db), 505 GenericDefId::TraitId(it) => it.lookup(db).container,
522 GenericDefId::TypeAliasId(it) => it.lookup(db).module(db), 506 GenericDefId::TypeAliasId(it) => it.lookup(db).module(db),
523 GenericDefId::ImplId(it) => it.lookup(db).container.module(db), 507 GenericDefId::ImplId(it) => it.lookup(db).container,
524 GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db), 508 GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container,
525 GenericDefId::ConstId(it) => it.lookup(db).module(db), 509 GenericDefId::ConstId(it) => it.lookup(db).module(db),
526 } 510 }
527 } 511 }
528} 512}
529 513
530impl HasModule for StaticLoc { 514impl HasModule for StaticLoc {
531 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 515 fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId {
532 self.container.module(db) 516 self.container
533 } 517 }
534} 518}
535 519
@@ -542,10 +526,10 @@ impl ModuleDefId {
542 ModuleDefId::ModuleId(id) => *id, 526 ModuleDefId::ModuleId(id) => *id,
543 ModuleDefId::FunctionId(id) => id.lookup(db).module(db), 527 ModuleDefId::FunctionId(id) => id.lookup(db).module(db),
544 ModuleDefId::AdtId(id) => id.module(db), 528 ModuleDefId::AdtId(id) => id.module(db),
545 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db), 529 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container,
546 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db), 530 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db),
547 ModuleDefId::StaticId(id) => id.lookup(db).container.module(db), 531 ModuleDefId::StaticId(id) => id.lookup(db).container,
548 ModuleDefId::TraitId(id) => id.lookup(db).container.module(db), 532 ModuleDefId::TraitId(id) => id.lookup(db).container,
549 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db), 533 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db),
550 ModuleDefId::BuiltinType(_) => return None, 534 ModuleDefId::BuiltinType(_) => return None,
551 }) 535 })
@@ -559,12 +543,12 @@ impl AttrDefId {
559 AttrDefId::FieldId(it) => it.parent.module(db).krate, 543 AttrDefId::FieldId(it) => it.parent.module(db).krate,
560 AttrDefId::AdtId(it) => it.module(db).krate, 544 AttrDefId::AdtId(it) => it.module(db).krate,
561 AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate, 545 AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate,
562 AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db).krate, 546 AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.krate,
563 AttrDefId::StaticId(it) => it.lookup(db).module(db).krate, 547 AttrDefId::StaticId(it) => it.lookup(db).module(db).krate,
564 AttrDefId::ConstId(it) => it.lookup(db).module(db).krate, 548 AttrDefId::ConstId(it) => it.lookup(db).module(db).krate,
565 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate, 549 AttrDefId::TraitId(it) => it.lookup(db).container.krate,
566 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, 550 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate,
567 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate, 551 AttrDefId::ImplId(it) => it.lookup(db).container.krate,
568 AttrDefId::GenericParamId(it) => { 552 AttrDefId::GenericParamId(it) => {
569 match it { 553 match it {
570 GenericParamId::TypeParamId(it) => it.parent, 554 GenericParamId::TypeParamId(it) => it.parent,
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index e51d89b43..9ed48c506 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -18,7 +18,6 @@ use hir_expand::{
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 20use syntax::ast;
21use test_utils::mark;
22use tt::{Leaf, TokenTree}; 21use tt::{Leaf, TokenTree};
23 22
24use crate::{ 23use crate::{
@@ -38,9 +37,9 @@ use crate::{
38 path::{ImportAlias, ModPath, PathKind}, 37 path::{ImportAlias, ModPath, PathKind},
39 per_ns::PerNs, 38 per_ns::PerNs,
40 visibility::{RawVisibility, Visibility}, 39 visibility::{RawVisibility, Visibility},
41 AdtId, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, 40 AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern,
42 ImplLoc, Intern, LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, 41 LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
43 UnionLoc, UnresolvedMacro, 42 UnresolvedMacro,
44}; 43};
45 44
46const GLOB_RECURSION_LIMIT: usize = 100; 45const GLOB_RECURSION_LIMIT: usize = 100;
@@ -462,7 +461,7 @@ impl DefCollector<'_> {
462 let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name); 461 let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name);
463 462
464 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { 463 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
465 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); 464 cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
466 self.import_all_macros_exported(current_module_id, m.krate); 465 self.import_all_macros_exported(current_module_id, m.krate);
467 } 466 }
468 } 467 }
@@ -571,10 +570,10 @@ impl DefCollector<'_> {
571 match def.take_types() { 570 match def.take_types() {
572 Some(ModuleDefId::ModuleId(m)) => { 571 Some(ModuleDefId::ModuleId(m)) => {
573 if import.is_prelude { 572 if import.is_prelude {
574 mark::hit!(std_prelude); 573 cov_mark::hit!(std_prelude);
575 self.def_map.prelude = Some(m); 574 self.def_map.prelude = Some(m);
576 } else if m.krate != self.def_map.krate { 575 } else if m.krate != self.def_map.krate {
577 mark::hit!(glob_across_crates); 576 cov_mark::hit!(glob_across_crates);
578 // glob import from other crate => we can just import everything once 577 // glob import from other crate => we can just import everything once
579 let item_map = m.def_map(self.db); 578 let item_map = m.def_map(self.db);
580 let scope = &item_map[m.local_id].scope; 579 let scope = &item_map[m.local_id].scope;
@@ -626,7 +625,7 @@ impl DefCollector<'_> {
626 } 625 }
627 } 626 }
628 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { 627 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
629 mark::hit!(glob_enum); 628 cov_mark::hit!(glob_enum);
630 // glob import from enum => just import all the variants 629 // glob import from enum => just import all the variants
631 630
632 // XXX: urgh, so this works by accident! Here, we look at 631 // XXX: urgh, so this works by accident! Here, we look at
@@ -675,7 +674,7 @@ impl DefCollector<'_> {
675 674
676 self.update(module_id, &[(name, def)], vis, ImportType::Named); 675 self.update(module_id, &[(name, def)], vis, ImportType::Named);
677 } 676 }
678 None => mark::hit!(bogus_paths), 677 None => cov_mark::hit!(bogus_paths),
679 } 678 }
680 } 679 }
681 } 680 }
@@ -738,7 +737,7 @@ impl DefCollector<'_> {
738 if max_vis == old_vis { 737 if max_vis == old_vis {
739 false 738 false
740 } else { 739 } else {
741 mark::hit!(upgrade_underscore_visibility); 740 cov_mark::hit!(upgrade_underscore_visibility);
742 true 741 true
743 } 742 }
744 } 743 }
@@ -866,7 +865,7 @@ impl DefCollector<'_> {
866 depth: usize, 865 depth: usize,
867 ) { 866 ) {
868 if depth > EXPANSION_DEPTH_LIMIT { 867 if depth > EXPANSION_DEPTH_LIMIT {
869 mark::hit!(macro_expansion_overflow); 868 cov_mark::hit!(macro_expansion_overflow);
870 log::warn!("macro expansion is too deep"); 869 log::warn!("macro expansion is too deep");
871 return; 870 return;
872 } 871 }
@@ -1009,7 +1008,7 @@ impl ModCollector<'_, '_> {
1009 // Prelude module is always considered to be `#[macro_use]`. 1008 // Prelude module is always considered to be `#[macro_use]`.
1010 if let Some(prelude_module) = self.def_collector.def_map.prelude { 1009 if let Some(prelude_module) = self.def_collector.def_map.prelude {
1011 if prelude_module.krate != self.def_collector.def_map.krate { 1010 if prelude_module.krate != self.def_collector.def_map.krate {
1012 mark::hit!(prelude_is_macro_use); 1011 cov_mark::hit!(prelude_is_macro_use);
1013 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); 1012 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
1014 } 1013 }
1015 } 1014 }
@@ -1043,7 +1042,6 @@ impl ModCollector<'_, '_> {
1043 } 1042 }
1044 } 1043 }
1045 let module = self.def_collector.def_map.module_id(self.module_id); 1044 let module = self.def_collector.def_map.module_id(self.module_id);
1046 let container = ContainerId::ModuleId(module);
1047 1045
1048 let mut def = None; 1046 let mut def = None;
1049 match item { 1047 match item {
@@ -1110,9 +1108,9 @@ impl ModCollector<'_, '_> {
1110 } 1108 }
1111 ModItem::Impl(imp) => { 1109 ModItem::Impl(imp) => {
1112 let module = self.def_collector.def_map.module_id(self.module_id); 1110 let module = self.def_collector.def_map.module_id(self.module_id);
1113 let container = ContainerId::ModuleId(module); 1111 let impl_id =
1114 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } 1112 ImplLoc { container: module, id: ItemTreeId::new(self.file_id, imp) }
1115 .intern(self.def_collector.db); 1113 .intern(self.def_collector.db);
1116 self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id) 1114 self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id)
1117 } 1115 }
1118 ModItem::Function(id) => { 1116 ModItem::Function(id) => {
@@ -1122,7 +1120,7 @@ impl ModCollector<'_, '_> {
1122 1120
1123 def = Some(DefData { 1121 def = Some(DefData {
1124 id: FunctionLoc { 1122 id: FunctionLoc {
1125 container: container.into(), 1123 container: module.into(),
1126 id: ItemTreeId::new(self.file_id, id), 1124 id: ItemTreeId::new(self.file_id, id),
1127 } 1125 }
1128 .intern(self.def_collector.db) 1126 .intern(self.def_collector.db)
@@ -1141,7 +1139,7 @@ impl ModCollector<'_, '_> {
1141 self.collect_derives(&attrs, it.ast_id.upcast()); 1139 self.collect_derives(&attrs, it.ast_id.upcast());
1142 1140
1143 def = Some(DefData { 1141 def = Some(DefData {
1144 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } 1142 id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1145 .intern(self.def_collector.db) 1143 .intern(self.def_collector.db)
1146 .into(), 1144 .into(),
1147 name: &it.name, 1145 name: &it.name,
@@ -1158,7 +1156,7 @@ impl ModCollector<'_, '_> {
1158 self.collect_derives(&attrs, it.ast_id.upcast()); 1156 self.collect_derives(&attrs, it.ast_id.upcast());
1159 1157
1160 def = Some(DefData { 1158 def = Some(DefData {
1161 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } 1159 id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1162 .intern(self.def_collector.db) 1160 .intern(self.def_collector.db)
1163 .into(), 1161 .into(),
1164 name: &it.name, 1162 name: &it.name,
@@ -1175,7 +1173,7 @@ impl ModCollector<'_, '_> {
1175 self.collect_derives(&attrs, it.ast_id.upcast()); 1173 self.collect_derives(&attrs, it.ast_id.upcast());
1176 1174
1177 def = Some(DefData { 1175 def = Some(DefData {
1178 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } 1176 id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1179 .intern(self.def_collector.db) 1177 .intern(self.def_collector.db)
1180 .into(), 1178 .into(),
1181 name: &it.name, 1179 name: &it.name,
@@ -1189,7 +1187,7 @@ impl ModCollector<'_, '_> {
1189 if let Some(name) = &it.name { 1187 if let Some(name) = &it.name {
1190 def = Some(DefData { 1188 def = Some(DefData {
1191 id: ConstLoc { 1189 id: ConstLoc {
1192 container: container.into(), 1190 container: module.into(),
1193 id: ItemTreeId::new(self.file_id, id), 1191 id: ItemTreeId::new(self.file_id, id),
1194 } 1192 }
1195 .intern(self.def_collector.db) 1193 .intern(self.def_collector.db)
@@ -1204,7 +1202,7 @@ impl ModCollector<'_, '_> {
1204 let it = &self.item_tree[id]; 1202 let it = &self.item_tree[id];
1205 1203
1206 def = Some(DefData { 1204 def = Some(DefData {
1207 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } 1205 id: StaticLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1208 .intern(self.def_collector.db) 1206 .intern(self.def_collector.db)
1209 .into(), 1207 .into(),
1210 name: &it.name, 1208 name: &it.name,
@@ -1216,7 +1214,7 @@ impl ModCollector<'_, '_> {
1216 let it = &self.item_tree[id]; 1214 let it = &self.item_tree[id];
1217 1215
1218 def = Some(DefData { 1216 def = Some(DefData {
1219 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } 1217 id: TraitLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1220 .intern(self.def_collector.db) 1218 .intern(self.def_collector.db)
1221 .into(), 1219 .into(),
1222 name: &it.name, 1220 name: &it.name,
@@ -1229,7 +1227,7 @@ impl ModCollector<'_, '_> {
1229 1227
1230 def = Some(DefData { 1228 def = Some(DefData {
1231 id: TypeAliasLoc { 1229 id: TypeAliasLoc {
1232 container: container.into(), 1230 container: module.into(),
1233 id: ItemTreeId::new(self.file_id, id), 1231 id: ItemTreeId::new(self.file_id, id),
1234 } 1232 }
1235 .intern(self.def_collector.db) 1233 .intern(self.def_collector.db)
diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs
index af3262439..d5de9899c 100644
--- a/crates/hir_def/src/nameres/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/mod_resolution.rs
@@ -2,7 +2,6 @@
2use base_db::{AnchoredPath, FileId}; 2use base_db::{AnchoredPath, FileId};
3use hir_expand::name::Name; 3use hir_expand::name::Name;
4use syntax::SmolStr; 4use syntax::SmolStr;
5use test_utils::mark;
6 5
7use crate::{db::DefDatabase, HirFileId}; 6use crate::{db::DefDatabase, HirFileId};
8 7
@@ -28,7 +27,7 @@ impl ModDir {
28 let depth = self.depth + 1; 27 let depth = self.depth + 1;
29 if depth > MOD_DEPTH_LIMIT { 28 if depth > MOD_DEPTH_LIMIT {
30 log::error!("MOD_DEPTH_LIMIT exceeded"); 29 log::error!("MOD_DEPTH_LIMIT exceeded");
31 mark::hit!(circular_mods); 30 cov_mark::hit!(circular_mods);
32 return None; 31 return None;
33 } 32 }
34 Some(ModDir { dir_path, root_non_dir_owner, depth }) 33 Some(ModDir { dir_path, root_non_dir_owner, depth })
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index dd1db0094..db459b1ed 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -13,7 +13,6 @@
13use base_db::Edition; 13use base_db::Edition;
14use hir_expand::name; 14use hir_expand::name;
15use hir_expand::name::Name; 15use hir_expand::name::Name;
16use test_utils::mark;
17 16
18use crate::{ 17use crate::{
19 db::DefDatabase, 18 db::DefDatabase,
@@ -63,7 +62,7 @@ impl ResolvePathResult {
63impl DefMap { 62impl DefMap {
64 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { 63 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
65 if name == &name!(self) { 64 if name == &name!(self) {
66 mark::hit!(extern_crate_self_as); 65 cov_mark::hit!(extern_crate_self_as);
67 return PerNs::types(self.module_id(self.root).into(), Visibility::Public); 66 return PerNs::types(self.module_id(self.root).into(), Visibility::Public);
68 } 67 }
69 self.extern_prelude 68 self.extern_prelude
@@ -101,7 +100,7 @@ impl DefMap {
101 // DefMap they're written in, so we restrict them when that happens. 100 // DefMap they're written in, so we restrict them when that happens.
102 if let Visibility::Module(m) = vis { 101 if let Visibility::Module(m) = vis {
103 if self.block_id() != m.block { 102 if self.block_id() != m.block {
104 mark::hit!(adjust_vis_in_block_def_map); 103 cov_mark::hit!(adjust_vis_in_block_def_map);
105 vis = Visibility::Module(self.module_id(self.root())); 104 vis = Visibility::Module(self.module_id(self.root()));
106 log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); 105 log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
107 } 106 }
@@ -157,7 +156,7 @@ impl DefMap {
157 } 156 }
158 } 157 }
159 158
160 pub(super) fn resolve_path_fp_with_macro_single( 159 fn resolve_path_fp_with_macro_single(
161 &self, 160 &self,
162 db: &dyn DefDatabase, 161 db: &dyn DefDatabase,
163 mode: ResolveMode, 162 mode: ResolveMode,
@@ -169,12 +168,12 @@ impl DefMap {
169 let mut curr_per_ns: PerNs = match path.kind { 168 let mut curr_per_ns: PerNs = match path.kind {
170 PathKind::DollarCrate(krate) => { 169 PathKind::DollarCrate(krate) => {
171 if krate == self.krate { 170 if krate == self.krate {
172 mark::hit!(macro_dollar_crate_self); 171 cov_mark::hit!(macro_dollar_crate_self);
173 PerNs::types(self.crate_root(db).into(), Visibility::Public) 172 PerNs::types(self.crate_root(db).into(), Visibility::Public)
174 } else { 173 } else {
175 let def_map = db.crate_def_map(krate); 174 let def_map = db.crate_def_map(krate);
176 let module = def_map.module_id(def_map.root); 175 let module = def_map.module_id(def_map.root);
177 mark::hit!(macro_dollar_crate_other); 176 cov_mark::hit!(macro_dollar_crate_other);
178 PerNs::types(module.into(), Visibility::Public) 177 PerNs::types(module.into(), Visibility::Public)
179 } 178 }
180 } 179 }
@@ -310,7 +309,7 @@ impl DefMap {
310 } 309 }
311 ModuleDefId::AdtId(AdtId::EnumId(e)) => { 310 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
312 // enum variant 311 // enum variant
313 mark::hit!(can_import_enum_variant); 312 cov_mark::hit!(can_import_enum_variant);
314 let enum_data = db.enum_data(e); 313 let enum_data = db.enum_data(e);
315 match enum_data.variant(&segment) { 314 match enum_data.variant(&segment) {
316 Some(local_id) => { 315 Some(local_id) => {
@@ -385,10 +384,16 @@ impl DefMap {
385 } 384 }
386 } 385 }
387 }; 386 };
388 let from_extern_prelude = self 387 // Give precedence to names in outer `DefMap`s over the extern prelude; only check prelude
389 .extern_prelude 388 // from the crate DefMap.
390 .get(name) 389 let from_extern_prelude = match self.block {
391 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)); 390 Some(_) => PerNs::none(),
391 None => self
392 .extern_prelude
393 .get(name)
394 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)),
395 };
396
392 let from_prelude = self.resolve_in_prelude(db, name); 397 let from_prelude = self.resolve_in_prelude(db, name);
393 398
394 from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) 399 from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude)
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index bd3e2701b..de3aa4f9a 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -9,7 +9,6 @@ use std::sync::Arc;
9 9
10use base_db::{fixture::WithFixture, SourceDatabase}; 10use base_db::{fixture::WithFixture, SourceDatabase};
11use expect_test::{expect, Expect}; 11use expect_test::{expect, Expect};
12use test_utils::mark;
13 12
14use crate::{db::DefDatabase, test_db::TestDB}; 13use crate::{db::DefDatabase, test_db::TestDB};
15 14
@@ -136,7 +135,7 @@ mod m {
136 135
137#[test] 136#[test]
138fn bogus_paths() { 137fn bogus_paths() {
139 mark::check!(bogus_paths); 138 cov_mark::check!(bogus_paths);
140 check( 139 check(
141 r#" 140 r#"
142//- /lib.rs 141//- /lib.rs
@@ -243,7 +242,7 @@ pub struct Baz;
243 242
244#[test] 243#[test]
245fn std_prelude() { 244fn std_prelude() {
246 mark::check!(std_prelude); 245 cov_mark::check!(std_prelude);
247 check( 246 check(
248 r#" 247 r#"
249//- /main.rs crate:main deps:test_crate 248//- /main.rs crate:main deps:test_crate
@@ -267,7 +266,7 @@ pub enum Foo { Bar, Baz };
267 266
268#[test] 267#[test]
269fn can_import_enum_variant() { 268fn can_import_enum_variant() {
270 mark::check!(can_import_enum_variant); 269 cov_mark::check!(can_import_enum_variant);
271 check( 270 check(
272 r#" 271 r#"
273enum E { V } 272enum E { V }
@@ -628,7 +627,7 @@ use crate::reex::*;
628 627
629#[test] 628#[test]
630fn underscore_pub_crate_reexport() { 629fn underscore_pub_crate_reexport() {
631 mark::check!(upgrade_underscore_visibility); 630 cov_mark::check!(upgrade_underscore_visibility);
632 check( 631 check(
633 r#" 632 r#"
634//- /main.rs crate:main deps:lib 633//- /main.rs crate:main deps:lib
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index e8e72e5ef..d5ef8ceb5 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -1,5 +1,4 @@
1use base_db::fixture::WithFixture; 1use base_db::fixture::WithFixture;
2use test_utils::mark;
3 2
4use crate::test_db::TestDB; 3use crate::test_db::TestDB;
5 4
@@ -63,7 +62,7 @@ fn unresolved_extern_crate() {
63 62
64#[test] 63#[test]
65fn extern_crate_self_as() { 64fn extern_crate_self_as() {
66 mark::check!(extern_crate_self_as); 65 cov_mark::check!(extern_crate_self_as);
67 check_diagnostics( 66 check_diagnostics(
68 r" 67 r"
69 //- /lib.rs 68 //- /lib.rs
@@ -140,7 +139,7 @@ fn inactive_item() {
140/// Tests that `cfg` attributes behind `cfg_attr` is handled properly. 139/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
141#[test] 140#[test]
142fn inactive_via_cfg_attr() { 141fn inactive_via_cfg_attr() {
143 mark::check!(cfg_attr_active); 142 cov_mark::check!(cfg_attr_active);
144 check_diagnostics( 143 check_diagnostics(
145 r#" 144 r#"
146 //- /lib.rs 145 //- /lib.rs
diff --git a/crates/hir_def/src/nameres/tests/globs.rs b/crates/hir_def/src/nameres/tests/globs.rs
index 2ae836e3c..17426d54d 100644
--- a/crates/hir_def/src/nameres/tests/globs.rs
+++ b/crates/hir_def/src/nameres/tests/globs.rs
@@ -148,7 +148,7 @@ pub(crate) struct PubCrateStruct;
148 148
149#[test] 149#[test]
150fn glob_across_crates() { 150fn glob_across_crates() {
151 mark::check!(glob_across_crates); 151 cov_mark::check!(glob_across_crates);
152 check( 152 check(
153 r#" 153 r#"
154//- /main.rs crate:main deps:test_crate 154//- /main.rs crate:main deps:test_crate
@@ -184,7 +184,7 @@ struct Foo;
184 184
185#[test] 185#[test]
186fn glob_enum() { 186fn glob_enum() {
187 mark::check!(glob_enum); 187 cov_mark::check!(glob_enum);
188 check( 188 check(
189 r#" 189 r#"
190enum Foo { Bar, Baz } 190enum Foo { Bar, Baz }
@@ -201,7 +201,7 @@ use self::Foo::*;
201 201
202#[test] 202#[test]
203fn glob_enum_group() { 203fn glob_enum_group() {
204 mark::check!(glob_enum_group); 204 cov_mark::check!(glob_enum_group);
205 check( 205 check(
206 r#" 206 r#"
207enum Foo { Bar, Baz } 207enum Foo { Bar, Baz }
@@ -218,7 +218,7 @@ use self::Foo::{*};
218 218
219#[test] 219#[test]
220fn glob_shadowed_def() { 220fn glob_shadowed_def() {
221 mark::check!(import_shadowed); 221 cov_mark::check!(import_shadowed);
222 check( 222 check(
223 r#" 223 r#"
224//- /lib.rs 224//- /lib.rs
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 36ed5e8ce..f65a655bf 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -210,7 +210,7 @@ macro_rules! bar {
210 210
211#[test] 211#[test]
212fn macro_rules_from_other_crates_are_visible_with_macro_use() { 212fn macro_rules_from_other_crates_are_visible_with_macro_use() {
213 mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use); 213 cov_mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use);
214 check( 214 check(
215 r#" 215 r#"
216//- /main.rs crate:main deps:foo 216//- /main.rs crate:main deps:foo
@@ -260,7 +260,7 @@ mod priv_mod {
260 260
261#[test] 261#[test]
262fn prelude_is_macro_use() { 262fn prelude_is_macro_use() {
263 mark::check!(prelude_is_macro_use); 263 cov_mark::check!(prelude_is_macro_use);
264 check( 264 check(
265 r#" 265 r#"
266//- /main.rs crate:main deps:foo 266//- /main.rs crate:main deps:foo
@@ -550,7 +550,7 @@ mod m {
550 550
551#[test] 551#[test]
552fn macro_dollar_crate_is_correct_in_item() { 552fn macro_dollar_crate_is_correct_in_item() {
553 mark::check!(macro_dollar_crate_self); 553 cov_mark::check!(macro_dollar_crate_self);
554 check( 554 check(
555 r#" 555 r#"
556//- /main.rs crate:main deps:foo 556//- /main.rs crate:main deps:foo
@@ -608,7 +608,7 @@ struct Baz;
608 608
609#[test] 609#[test]
610fn macro_dollar_crate_is_correct_in_indirect_deps() { 610fn macro_dollar_crate_is_correct_in_indirect_deps() {
611 mark::check!(macro_dollar_crate_other); 611 cov_mark::check!(macro_dollar_crate_other);
612 // From std 612 // From std
613 check( 613 check(
614 r#" 614 r#"
@@ -686,7 +686,7 @@ pub trait Clone {}
686 686
687#[test] 687#[test]
688fn macro_expansion_overflow() { 688fn macro_expansion_overflow() {
689 mark::check!(macro_expansion_overflow); 689 cov_mark::check!(macro_expansion_overflow);
690 check( 690 check(
691 r#" 691 r#"
692macro_rules! a { 692macro_rules! a {
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs
index e80b593aa..dfbbad1f9 100644
--- a/crates/hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs
@@ -2,7 +2,7 @@ use super::*;
2 2
3#[test] 3#[test]
4fn name_res_works_for_broken_modules() { 4fn name_res_works_for_broken_modules() {
5 mark::check!(name_res_works_for_broken_modules); 5 cov_mark::check!(name_res_works_for_broken_modules);
6 check( 6 check(
7 r" 7 r"
8//- /lib.rs 8//- /lib.rs
@@ -774,7 +774,7 @@ struct X;
774 774
775#[test] 775#[test]
776fn circular_mods() { 776fn circular_mods() {
777 mark::check!(circular_mods); 777 cov_mark::check!(circular_mods);
778 compute_crate_def_map( 778 compute_crate_def_map(
779 r#" 779 r#"
780//- /lib.rs 780//- /lib.rs
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs
index d584b0b70..e2965b033 100644
--- a/crates/hir_def/src/path/lower/lower_use.rs
+++ b/crates/hir_def/src/path/lower/lower_use.rs
@@ -6,7 +6,6 @@ use std::iter;
6use either::Either; 6use either::Either;
7use hir_expand::{hygiene::Hygiene, name::AsName}; 7use hir_expand::{hygiene::Hygiene, name::AsName};
8use syntax::ast::{self, NameOwner}; 8use syntax::ast::{self, NameOwner};
9use test_utils::mark;
10 9
11use crate::path::{ImportAlias, ModPath, PathKind}; 10use crate::path::{ImportAlias, ModPath, PathKind};
12 11
@@ -54,7 +53,7 @@ pub(crate) fn lower_use_tree(
54 // FIXME: report errors somewhere 53 // FIXME: report errors somewhere
55 // We get here if we do 54 // We get here if we do
56 } else if is_glob { 55 } else if is_glob {
57 mark::hit!(glob_enum_group); 56 cov_mark::hit!(glob_enum_group);
58 if let Some(prefix) = prefix { 57 if let Some(prefix) = prefix {
59 cb(prefix, &tree, is_glob, None) 58 cb(prefix, &tree, is_glob, None)
60 } 59 }
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 77ff21739..42736171e 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -19,10 +19,10 @@ use crate::{
19 path::{ModPath, PathKind}, 19 path::{ModPath, PathKind},
20 per_ns::PerNs, 20 per_ns::PerNs,
21 visibility::{RawVisibility, Visibility}, 21 visibility::{RawVisibility, Visibility},
22 AdtId, AssocContainerId, ConstId, ConstParamId, ContainerId, DefWithBodyId, EnumId, 22 AdtId, AssocContainerId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
23 EnumVariantId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId, 23 FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId, LocalModuleId,
24 LocalModuleId, Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, 24 Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
25 TypeParamId, VariantId, 25 VariantId,
26}; 26};
27 27
28#[derive(Debug, Clone, Default)] 28#[derive(Debug, Clone, Default)]
@@ -342,6 +342,16 @@ impl Resolver {
342 traits.extend(prelude_def_map[prelude.local_id].scope.traits()); 342 traits.extend(prelude_def_map[prelude.local_id].scope.traits());
343 } 343 }
344 traits.extend(m.def_map[m.module_id].scope.traits()); 344 traits.extend(m.def_map[m.module_id].scope.traits());
345
346 // Add all traits that are in scope because of the containing DefMaps
347 m.def_map.with_ancestor_maps(db, m.module_id, &mut |def_map, module| {
348 if let Some(prelude) = def_map.prelude() {
349 let prelude_def_map = prelude.def_map(db);
350 traits.extend(prelude_def_map[prelude.local_id].scope.traits());
351 }
352 traits.extend(def_map[module].scope.traits());
353 None::<()>
354 });
345 } 355 }
346 } 356 }
347 traits 357 traits
@@ -678,19 +688,10 @@ impl HasResolver for DefWithBodyId {
678 } 688 }
679} 689}
680 690
681impl HasResolver for ContainerId {
682 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
683 match self {
684 ContainerId::ModuleId(it) => it.resolver(db),
685 ContainerId::DefWithBodyId(it) => it.module(db).resolver(db),
686 }
687 }
688}
689
690impl HasResolver for AssocContainerId { 691impl HasResolver for AssocContainerId {
691 fn resolver(self, db: &dyn DefDatabase) -> Resolver { 692 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
692 match self { 693 match self {
693 AssocContainerId::ContainerId(it) => it.resolver(db), 694 AssocContainerId::ModuleId(it) => it.resolver(db),
694 AssocContainerId::TraitId(it) => it.resolver(db), 695 AssocContainerId::TraitId(it) => it.resolver(db),
695 AssocContainerId::ImplId(it) => it.resolver(db), 696 AssocContainerId::ImplId(it) => it.resolver(db),
696 } 697 }
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index eda982c85..10977761c 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -15,7 +15,7 @@ use rustc_hash::FxHashSet;
15use syntax::{algo, ast, AstNode, TextRange, TextSize}; 15use syntax::{algo, ast, AstNode, TextRange, TextSize};
16use test_utils::extract_annotations; 16use test_utils::extract_annotations;
17 17
18use crate::{db::DefDatabase, nameres::DefMap, Lookup, ModuleDefId, ModuleId}; 18use crate::{db::DefDatabase, nameres::DefMap, src::HasSource, Lookup, ModuleDefId, ModuleId};
19 19
20#[salsa::database( 20#[salsa::database(
21 base_db::SourceDatabaseExtStorage, 21 base_db::SourceDatabaseExtStorage,
@@ -115,14 +115,9 @@ impl TestDB {
115 if file_id != position.file_id.into() { 115 if file_id != position.file_id.into() {
116 continue; 116 continue;
117 } 117 }
118 let root = self.parse_or_expand(file_id).unwrap();
119 let ast_map = self.ast_id_map(file_id);
120 let item_tree = self.item_tree(file_id);
121 for decl in module.scope.declarations() { 118 for decl in module.scope.declarations() {
122 if let ModuleDefId::FunctionId(it) = decl { 119 if let ModuleDefId::FunctionId(it) = decl {
123 let ast = 120 let range = it.lookup(self).source(self).value.syntax().text_range();
124 ast_map.get(item_tree[it.lookup(self).id.value].ast_id).to_node(&root);
125 let range = ast.syntax().text_range();
126 121
127 if !range.contains(position.offset) { 122 if !range.contains(position.offset) {
128 continue; 123 continue;
diff --git a/crates/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml
index 5271110d2..f649ab925 100644
--- a/crates/hir_expand/Cargo.toml
+++ b/crates/hir_expand/Cargo.toml
@@ -16,9 +16,13 @@ rustc-hash = "1.0.0"
16la-arena = { version = "0.2.0", path = "../../lib/arena" } 16la-arena = { version = "0.2.0", path = "../../lib/arena" }
17 17
18base_db = { path = "../base_db", version = "0.0.0" } 18base_db = { path = "../base_db", version = "0.0.0" }
19cfg = { path = "../cfg", version = "0.0.0" }
19syntax = { path = "../syntax", version = "0.0.0" } 20syntax = { path = "../syntax", version = "0.0.0" }
20parser = { path = "../parser", version = "0.0.0" } 21parser = { path = "../parser", version = "0.0.0" }
21profile = { path = "../profile", version = "0.0.0" } 22profile = { path = "../profile", version = "0.0.0" }
22tt = { path = "../tt", version = "0.0.0" } 23tt = { path = "../tt", version = "0.0.0" }
23mbe = { path = "../mbe", version = "0.0.0" } 24mbe = { path = "../mbe", version = "0.0.0" }
24test_utils = { path = "../test_utils", version = "0.0.0" } 25
26[dev-dependencies]
27test_utils = { path = "../test_utils" }
28expect-test = "1.1"
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index b7f1aae8f..dfdb9cf59 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -267,13 +267,14 @@ fn partial_ord_expand(
267#[cfg(test)] 267#[cfg(test)]
268mod tests { 268mod tests {
269 use base_db::{fixture::WithFixture, CrateId, SourceDatabase}; 269 use base_db::{fixture::WithFixture, CrateId, SourceDatabase};
270 use expect_test::{expect, Expect};
270 use name::{known, Name}; 271 use name::{known, Name};
271 272
272 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; 273 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
273 274
274 use super::*; 275 use super::*;
275 276
276 fn expand_builtin_derive(s: &str, name: Name) -> String { 277 fn expand_builtin_derive(ra_fixture: &str, name: Name) -> String {
277 let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); 278 let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap();
278 let fixture = format!( 279 let fixture = format!(
279 r#"//- /main.rs crate:main deps:core 280 r#"//- /main.rs crate:main deps:core
@@ -282,7 +283,7 @@ $0
282//- /lib.rs crate:core 283//- /lib.rs crate:core
283// empty 284// empty
284"#, 285"#,
285 s 286 ra_fixture
286 ); 287 );
287 288
288 let (db, file_pos) = TestDB::with_position(&fixture); 289 let (db, file_pos) = TestDB::with_position(&fixture);
@@ -314,66 +315,57 @@ $0
314 parsed.text().to_string() 315 parsed.text().to_string()
315 } 316 }
316 317
318 fn check_derive(ra_fixture: &str, name: Name, expected: Expect) {
319 let expanded = expand_builtin_derive(ra_fixture, name);
320 expected.assert_eq(&expanded);
321 }
322
317 #[test] 323 #[test]
318 fn test_copy_expand_simple() { 324 fn test_copy_expand_simple() {
319 let expanded = expand_builtin_derive( 325 check_derive(
320 r#" 326 r#"
321 #[derive(Copy)] 327 #[derive(Copy)]
322 struct Foo; 328 struct Foo;
323"#, 329 "#,
324 known::Copy, 330 known::Copy,
331 expect![["impl< >core::marker::CopyforFoo< >{}"]],
325 ); 332 );
326
327 assert_eq!(expanded, "impl< >core::marker::CopyforFoo< >{}");
328 } 333 }
329 334
330 #[test] 335 #[test]
331 fn test_copy_expand_with_type_params() { 336 fn test_copy_expand_with_type_params() {
332 let expanded = expand_builtin_derive( 337 check_derive(
333 r#" 338 r#"
334 #[derive(Copy)] 339 #[derive(Copy)]
335 struct Foo<A, B>; 340 struct Foo<A, B>;
336"#, 341 "#,
337 known::Copy, 342 known::Copy,
338 ); 343 expect![["impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"]],
339
340 assert_eq!(
341 expanded,
342 "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
343 ); 344 );
344 } 345 }
345 346
346 #[test] 347 #[test]
347 fn test_copy_expand_with_lifetimes() { 348 fn test_copy_expand_with_lifetimes() {
348 let expanded = expand_builtin_derive( 349 check_derive(
349 r#" 350 r#"
350 #[derive(Copy)] 351 #[derive(Copy)]
351 struct Foo<A, B, 'a, 'b>; 352 struct Foo<A, B, 'a, 'b>;
352"#, 353 "#,
353 known::Copy, 354 known::Copy,
354 ); 355 // We currently just ignore lifetimes
355 356 expect![["impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"]],
356 // We currently just ignore lifetimes
357
358 assert_eq!(
359 expanded,
360 "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
361 ); 357 );
362 } 358 }
363 359
364 #[test] 360 #[test]
365 fn test_clone_expand() { 361 fn test_clone_expand() {
366 let expanded = expand_builtin_derive( 362 check_derive(
367 r#" 363 r#"
368 #[derive(Clone)] 364 #[derive(Clone)]
369 struct Foo<A, B>; 365 struct Foo<A, B>;
370"#, 366 "#,
371 known::Clone, 367 known::Clone,
372 ); 368 expect![["impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"]],
373
374 assert_eq!(
375 expanded,
376 "impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"
377 ); 369 );
378 } 370 }
379} 371}
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index eb57ea7d6..2a79c892b 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -5,6 +5,7 @@ use crate::{
5}; 5};
6 6
7use base_db::{AnchoredPath, FileId}; 7use base_db::{AnchoredPath, FileId};
8use cfg::CfgExpr;
8use either::Either; 9use either::Either;
9use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; 10use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult};
10use parser::FragmentKind; 11use parser::FragmentKind;
@@ -97,6 +98,7 @@ register_builtin! {
97 (format_args_nl, FormatArgsNl) => format_args_expand, 98 (format_args_nl, FormatArgsNl) => format_args_expand,
98 (llvm_asm, LlvmAsm) => asm_expand, 99 (llvm_asm, LlvmAsm) => asm_expand,
99 (asm, Asm) => asm_expand, 100 (asm, Asm) => asm_expand,
101 (cfg, Cfg) => cfg_expand,
100 102
101 EAGER: 103 EAGER:
102 (compile_error, CompileError) => compile_error_expand, 104 (compile_error, CompileError) => compile_error_expand,
@@ -258,6 +260,18 @@ fn asm_expand(
258 ExpandResult::ok(expanded) 260 ExpandResult::ok(expanded)
259} 261}
260 262
263fn cfg_expand(
264 db: &dyn AstDatabase,
265 id: LazyMacroId,
266 tt: &tt::Subtree,
267) -> ExpandResult<tt::Subtree> {
268 let loc = db.lookup_intern_macro(id);
269 let expr = CfgExpr::parse(tt);
270 let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false);
271 let expanded = if enabled { quote!(true) } else { quote!(false) };
272 ExpandResult::ok(expanded)
273}
274
261fn unquote_str(lit: &tt::Literal) -> Option<String> { 275fn unquote_str(lit: &tt::Literal) -> Option<String> {
262 let lit = ast::make::tokens::literal(&lit.to_string()); 276 let lit = ast::make::tokens::literal(&lit.to_string());
263 let token = ast::String::cast(lit)?; 277 let token = ast::String::cast(lit)?;
@@ -477,6 +491,7 @@ mod tests {
477 MacroCallLoc, 491 MacroCallLoc,
478 }; 492 };
479 use base_db::{fixture::WithFixture, SourceDatabase}; 493 use base_db::{fixture::WithFixture, SourceDatabase};
494 use expect_test::{expect, Expect};
480 use std::sync::Arc; 495 use std::sync::Arc;
481 use syntax::ast::NameOwner; 496 use syntax::ast::NameOwner;
482 497
@@ -560,87 +575,86 @@ mod tests {
560 db.parse_or_expand(file_id).unwrap().to_string() 575 db.parse_or_expand(file_id).unwrap().to_string()
561 } 576 }
562 577
578 fn check_expansion(ra_fixture: &str, expect: Expect) {
579 let expansion = expand_builtin_macro(ra_fixture);
580 expect.assert_eq(&expansion);
581 }
582
563 #[test] 583 #[test]
564 fn test_column_expand() { 584 fn test_column_expand() {
565 let expanded = expand_builtin_macro( 585 check_expansion(
566 r#" 586 r#"
567 #[rustc_builtin_macro] 587 #[rustc_builtin_macro]
568 macro_rules! column {() => {}} 588 macro_rules! column {() => {}}
569 column!() 589 column!()
570 "#, 590 "#,
591 expect![["0"]],
571 ); 592 );
572
573 assert_eq!(expanded, "0");
574 } 593 }
575 594
576 #[test] 595 #[test]
577 fn test_line_expand() { 596 fn test_line_expand() {
578 let expanded = expand_builtin_macro( 597 check_expansion(
579 r#" 598 r#"
580 #[rustc_builtin_macro] 599 #[rustc_builtin_macro]
581 macro_rules! line {() => {}} 600 macro_rules! line {() => {}}
582 line!() 601 line!()
583 "#, 602 "#,
603 expect![["0"]],
584 ); 604 );
585
586 assert_eq!(expanded, "0");
587 } 605 }
588 606
589 #[test] 607 #[test]
590 fn test_stringify_expand() { 608 fn test_stringify_expand() {
591 let expanded = expand_builtin_macro( 609 check_expansion(
592 r#" 610 r#"
593 #[rustc_builtin_macro] 611 #[rustc_builtin_macro]
594 macro_rules! stringify {() => {}} 612 macro_rules! stringify {() => {}}
595 stringify!(a b c) 613 stringify!(a b c)
596 "#, 614 "#,
615 expect![["\"a b c\""]],
597 ); 616 );
598
599 assert_eq!(expanded, "\"a b c\"");
600 } 617 }
601 618
602 #[test] 619 #[test]
603 fn test_env_expand() { 620 fn test_env_expand() {
604 let expanded = expand_builtin_macro( 621 check_expansion(
605 r#" 622 r#"
606 #[rustc_builtin_macro] 623 #[rustc_builtin_macro]
607 macro_rules! env {() => {}} 624 macro_rules! env {() => {}}
608 env!("TEST_ENV_VAR") 625 env!("TEST_ENV_VAR")
609 "#, 626 "#,
627 expect![["\"__RA_UNIMPLEMENTED__\""]],
610 ); 628 );
611
612 assert_eq!(expanded, "\"__RA_UNIMPLEMENTED__\"");
613 } 629 }
614 630
615 #[test] 631 #[test]
616 fn test_option_env_expand() { 632 fn test_option_env_expand() {
617 let expanded = expand_builtin_macro( 633 check_expansion(
618 r#" 634 r#"
619 #[rustc_builtin_macro] 635 #[rustc_builtin_macro]
620 macro_rules! option_env {() => {}} 636 macro_rules! option_env {() => {}}
621 option_env!("TEST_ENV_VAR") 637 option_env!("TEST_ENV_VAR")
622 "#, 638 "#,
639 expect![["std::option::Option::None:: < &str>"]],
623 ); 640 );
624
625 assert_eq!(expanded, "std::option::Option::None:: < &str>");
626 } 641 }
627 642
628 #[test] 643 #[test]
629 fn test_file_expand() { 644 fn test_file_expand() {
630 let expanded = expand_builtin_macro( 645 check_expansion(
631 r#" 646 r#"
632 #[rustc_builtin_macro] 647 #[rustc_builtin_macro]
633 macro_rules! file {() => {}} 648 macro_rules! file {() => {}}
634 file!() 649 file!()
635 "#, 650 "#,
651 expect![[r#""""#]],
636 ); 652 );
637
638 assert_eq!(expanded, "\"\"");
639 } 653 }
640 654
641 #[test] 655 #[test]
642 fn test_assert_expand() { 656 fn test_assert_expand() {
643 let expanded = expand_builtin_macro( 657 check_expansion(
644 r#" 658 r#"
645 #[rustc_builtin_macro] 659 #[rustc_builtin_macro]
646 macro_rules! assert { 660 macro_rules! assert {
@@ -649,14 +663,13 @@ mod tests {
649 } 663 }
650 assert!(true, "{} {:?}", arg1(a, b, c), arg2); 664 assert!(true, "{} {:?}", arg1(a, b, c), arg2);
651 "#, 665 "#,
666 expect![["{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}"]],
652 ); 667 );
653
654 assert_eq!(expanded, "{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}");
655 } 668 }
656 669
657 #[test] 670 #[test]
658 fn test_compile_error_expand() { 671 fn test_compile_error_expand() {
659 let expanded = expand_builtin_macro( 672 check_expansion(
660 r#" 673 r#"
661 #[rustc_builtin_macro] 674 #[rustc_builtin_macro]
662 macro_rules! compile_error { 675 macro_rules! compile_error {
@@ -665,15 +678,14 @@ mod tests {
665 } 678 }
666 compile_error!("error!"); 679 compile_error!("error!");
667 "#, 680 "#,
681 // This expands to nothing (since it's in item position), but emits an error.
682 expect![[""]],
668 ); 683 );
669
670 // This expands to nothing (since it's in item position), but emits an error.
671 assert_eq!(expanded, "");
672 } 684 }
673 685
674 #[test] 686 #[test]
675 fn test_format_args_expand() { 687 fn test_format_args_expand() {
676 let expanded = expand_builtin_macro( 688 check_expansion(
677 r#" 689 r#"
678 #[rustc_builtin_macro] 690 #[rustc_builtin_macro]
679 macro_rules! format_args { 691 macro_rules! format_args {
@@ -682,17 +694,15 @@ mod tests {
682 } 694 }
683 format_args!("{} {:?}", arg1(a, b, c), arg2); 695 format_args!("{} {:?}", arg1(a, b, c), arg2);
684 "#, 696 "#,
685 ); 697 expect![[
686 698 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"#
687 assert_eq!( 699 ]],
688 expanded,
689 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"#
690 ); 700 );
691 } 701 }
692 702
693 #[test] 703 #[test]
694 fn test_format_args_expand_with_comma_exprs() { 704 fn test_format_args_expand_with_comma_exprs() {
695 let expanded = expand_builtin_macro( 705 check_expansion(
696 r#" 706 r#"
697 #[rustc_builtin_macro] 707 #[rustc_builtin_macro]
698 macro_rules! format_args { 708 macro_rules! format_args {
@@ -701,17 +711,15 @@ mod tests {
701 } 711 }
702 format_args!("{} {:?}", a::<A,B>(), b); 712 format_args!("{} {:?}", a::<A,B>(), b);
703 "#, 713 "#,
704 ); 714 expect![[
705 715 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A,B>()),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(b),std::fmt::Display::fmt),])"#
706 assert_eq!( 716 ]],
707 expanded,
708 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A,B>()),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(b),std::fmt::Display::fmt),])"#
709 ); 717 );
710 } 718 }
711 719
712 #[test] 720 #[test]
713 fn test_include_bytes_expand() { 721 fn test_include_bytes_expand() {
714 let expanded = expand_builtin_macro( 722 check_expansion(
715 r#" 723 r#"
716 #[rustc_builtin_macro] 724 #[rustc_builtin_macro]
717 macro_rules! include_bytes { 725 macro_rules! include_bytes {
@@ -720,21 +728,19 @@ mod tests {
720 } 728 }
721 include_bytes("foo"); 729 include_bytes("foo");
722 "#, 730 "#,
731 expect![[r#"b"""#]],
723 ); 732 );
724
725 assert_eq!(expanded, r#"b"""#);
726 } 733 }
727 734
728 #[test] 735 #[test]
729 fn test_concat_expand() { 736 fn test_concat_expand() {
730 let expanded = expand_builtin_macro( 737 check_expansion(
731 r##" 738 r##"
732 #[rustc_builtin_macro] 739 #[rustc_builtin_macro]
733 macro_rules! concat {} 740 macro_rules! concat {}
734 concat!("foo", "r", 0, r#"bar"#, false); 741 concat!("foo", "r", 0, r#"bar"#, false);
735 "##, 742 "##,
743 expect![[r#""foor0barfalse""#]],
736 ); 744 );
737
738 assert_eq!(expanded, r#""foor0barfalse""#);
739 } 745 }
740} 746}
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index c94fb580a..e833e032c 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -154,6 +154,7 @@ pub mod known {
154 macro_rules, 154 macro_rules,
155 derive, 155 derive,
156 doc, 156 doc,
157 cfg,
157 cfg_attr, 158 cfg_attr,
158 // Components of known path (value or mod name) 159 // Components of known path (value or mod name)
159 std, 160 std,
diff --git a/crates/hir_expand/src/quote.rs b/crates/hir_expand/src/quote.rs
index 219bc2097..08bc5aa49 100644
--- a/crates/hir_expand/src/quote.rs
+++ b/crates/hir_expand/src/quote.rs
@@ -188,8 +188,9 @@ macro_rules! impl_to_to_tokentrees {
188 188
189impl_to_to_tokentrees! { 189impl_to_to_tokentrees! {
190 u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} }; 190 u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
191 usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}}; 191 usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
192 i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}}; 192 i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
193 bool => self { tt::Ident{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
193 tt::Leaf => self { self }; 194 tt::Leaf => self { self };
194 tt::Literal => self { self }; 195 tt::Literal => self { self };
195 tt::Ident => self { self }; 196 tt::Ident => self { self };
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index d1302d749..b9c93f56f 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13itertools = "0.10.0" 14itertools = "0.10.0"
14arrayvec = "0.5.1" 15arrayvec = "0.5.1"
15smallvec = "1.2.0" 16smallvec = "1.2.0"
@@ -17,9 +18,9 @@ ena = "0.14.0"
17log = "0.4.8" 18log = "0.4.8"
18rustc-hash = "1.1.0" 19rustc-hash = "1.1.0"
19scoped-tls = "1" 20scoped-tls = "1"
20chalk-solve = { version = "0.59", default-features = false } 21chalk-solve = { version = "0.60", default-features = false }
21chalk-ir = "0.59" 22chalk-ir = "0.60"
22chalk-recursive = "0.59" 23chalk-recursive = "0.60"
23la-arena = { version = "0.2.0", path = "../../lib/arena" } 24la-arena = { version = "0.2.0", path = "../../lib/arena" }
24 25
25stdx = { path = "../stdx", version = "0.0.0" } 26stdx = { path = "../stdx", version = "0.0.0" }
@@ -28,9 +29,9 @@ hir_expand = { path = "../hir_expand", version = "0.0.0" }
28base_db = { path = "../base_db", version = "0.0.0" } 29base_db = { path = "../base_db", version = "0.0.0" }
29profile = { path = "../profile", version = "0.0.0" } 30profile = { path = "../profile", version = "0.0.0" }
30syntax = { path = "../syntax", version = "0.0.0" } 31syntax = { path = "../syntax", version = "0.0.0" }
31test_utils = { path = "../test_utils", version = "0.0.0" }
32 32
33[dev-dependencies] 33[dev-dependencies]
34test_utils = { path = "../test_utils" }
34expect-test = "1.1" 35expect-test = "1.1"
35tracing = "0.1" 36tracing = "0.1"
36tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } 37tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 6bca7aa0d..86f937e1d 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -706,6 +706,35 @@ fn x(a: S) {
706 } 706 }
707 707
708 #[test] 708 #[test]
709 fn import_extern_crate_clash_with_inner_item() {
710 // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
711
712 check_diagnostics(
713 r#"
714//- /lib.rs crate:lib deps:jwt
715mod permissions;
716
717use permissions::jwt;
718
719fn f() {
720 fn inner() {}
721 jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
722}
723
724//- /permissions.rs
725pub mod jwt {
726 pub struct Claims {}
727}
728
729//- /jwt/lib.rs crate:jwt
730pub struct Claims {
731 field: u8,
732}
733 "#,
734 );
735 }
736
737 #[test]
709 fn break_outside_of_loop() { 738 fn break_outside_of_loop() {
710 check_diagnostics( 739 check_diagnostics(
711 r#" 740 r#"
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index 6773ddea3..3605ca581 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -28,7 +28,6 @@ use syntax::{
28 ast::{self, NameOwner}, 28 ast::{self, NameOwner},
29 AstNode, AstPtr, 29 AstNode, AstPtr,
30}; 30};
31use test_utils::mark;
32 31
33use crate::{ 32use crate::{
34 db::HirDatabase, 33 db::HirDatabase,
@@ -93,16 +92,21 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
93 fn validate_func(&mut self, func: FunctionId) { 92 fn validate_func(&mut self, func: FunctionId) {
94 let data = self.db.function_data(func); 93 let data = self.db.function_data(func);
95 if data.is_extern { 94 if data.is_extern {
96 mark::hit!(extern_func_incorrect_case_ignored); 95 cov_mark::hit!(extern_func_incorrect_case_ignored);
97 return; 96 return;
98 } 97 }
99 98
100 let body = self.db.body(func.into()); 99 let body = self.db.body(func.into());
101 100
102 // Recursively validate inner scope items, such as static variables and constants. 101 // Recursively validate inner scope items, such as static variables and constants.
103 for (item_id, _) in body.item_scope.values() { 102 let db = self.db;
104 let mut validator = DeclValidator::new(self.db, self.krate, self.sink); 103 for block_def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) {
105 validator.validate_item(item_id); 104 for (_, module) in block_def_map.modules() {
105 for (def_id, _) in module.scope.values() {
106 let mut validator = DeclValidator::new(self.db, self.krate, self.sink);
107 validator.validate_item(def_id);
108 }
109 }
106 } 110 }
107 111
108 // Check whether non-snake case identifiers are allowed for this function. 112 // Check whether non-snake case identifiers are allowed for this function.
@@ -625,7 +629,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
625 fn validate_static(&mut self, static_id: StaticId) { 629 fn validate_static(&mut self, static_id: StaticId) {
626 let data = self.db.static_data(static_id); 630 let data = self.db.static_data(static_id);
627 if data.is_extern { 631 if data.is_extern {
628 mark::hit!(extern_static_incorrect_case_ignored); 632 cov_mark::hit!(extern_static_incorrect_case_ignored);
629 return; 633 return;
630 } 634 }
631 635
@@ -673,8 +677,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
673 677
674#[cfg(test)] 678#[cfg(test)]
675mod tests { 679mod tests {
676 use test_utils::mark;
677
678 use crate::diagnostics::tests::check_diagnostics; 680 use crate::diagnostics::tests::check_diagnostics;
679 681
680 #[test] 682 #[test]
@@ -889,8 +891,8 @@ fn main() {
889 891
890 #[test] 892 #[test]
891 fn ignores_extern_items() { 893 fn ignores_extern_items() {
892 mark::check!(extern_func_incorrect_case_ignored); 894 cov_mark::check!(extern_func_incorrect_case_ignored);
893 mark::check!(extern_static_incorrect_case_ignored); 895 cov_mark::check!(extern_static_incorrect_case_ignored);
894 check_diagnostics( 896 check_diagnostics(
895 r#" 897 r#"
896extern { 898extern {
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index a0882a2a1..ab51cb0a6 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -6,7 +6,7 @@ use arrayvec::ArrayVec;
6use chalk_ir::Mutability; 6use chalk_ir::Mutability;
7use hir_def::{ 7use hir_def::{
8 db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, 8 db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs,
9 AssocContainerId, HasModule, Lookup, ModuleId, TraitId, 9 AssocContainerId, Lookup, ModuleId, TraitId,
10}; 10};
11use hir_expand::name::Name; 11use hir_expand::name::Name;
12 12
@@ -611,7 +611,7 @@ impl HirDisplay for CallableSig {
611} 611}
612 612
613fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> { 613fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
614 let krate = trait_.lookup(db).container.module(db).krate(); 614 let krate = trait_.lookup(db).container.krate();
615 let fn_traits = [ 615 let fn_traits = [
616 db.lang_item(krate, "fn".into()), 616 db.lang_item(krate, "fn".into()),
617 db.lang_item(krate, "fn_mut".into()), 617 db.lang_item(krate, "fn_mut".into()),
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index cf0a3add4..7e8846f27 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -6,7 +6,6 @@
6 6
7use chalk_ir::{Mutability, TyVariableKind}; 7use chalk_ir::{Mutability, TyVariableKind};
8use hir_def::lang_item::LangItemTarget; 8use hir_def::lang_item::LangItemTarget;
9use test_utils::mark;
10 9
11use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty}; 10use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty};
12 11
@@ -35,7 +34,7 @@ impl<'a> InferenceContext<'a> {
35 ty1.clone() 34 ty1.clone()
36 } else { 35 } else {
37 if let (Ty::FnDef(..), Ty::FnDef(..)) = (ty1, ty2) { 36 if let (Ty::FnDef(..), Ty::FnDef(..)) = (ty1, ty2) {
38 mark::hit!(coerce_fn_reification); 37 cov_mark::hit!(coerce_fn_reification);
39 // Special case: two function types. Try to coerce both to 38 // Special case: two function types. Try to coerce both to
40 // pointers to have a chance at getting a match. See 39 // pointers to have a chance at getting a match. See
41 // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 40 // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
@@ -45,7 +44,7 @@ impl<'a> InferenceContext<'a> {
45 let ptr_ty2 = Ty::fn_ptr(sig2); 44 let ptr_ty2 = Ty::fn_ptr(sig2);
46 self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) 45 self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
47 } else { 46 } else {
48 mark::hit!(coerce_merge_fail_fallback); 47 cov_mark::hit!(coerce_merge_fail_fallback);
49 ty1.clone() 48 ty1.clone()
50 } 49 }
51 } 50 }
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index ec2c13154..262177ffb 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -12,7 +12,6 @@ use hir_def::{
12}; 12};
13use hir_expand::name::{name, Name}; 13use hir_expand::name::{name, Name};
14use syntax::ast::RangeOp; 14use syntax::ast::RangeOp;
15use test_utils::mark;
16 15
17use crate::{ 16use crate::{
18 autoderef, 17 autoderef,
@@ -565,7 +564,7 @@ impl<'a> InferenceContext<'a> {
565 let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone()); 564 let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone());
566 565
567 if ret == Ty::Unknown { 566 if ret == Ty::Unknown {
568 mark::hit!(infer_expr_inner_binary_operator_overload); 567 cov_mark::hit!(infer_expr_inner_binary_operator_overload);
569 568
570 self.resolve_associated_type_with_params( 569 self.resolve_associated_type_with_params(
571 lhs_ty, 570 lhs_ty,
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index 987793e2e..a0ac8d80f 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -10,7 +10,6 @@ use hir_def::{
10 FieldId, 10 FieldId,
11}; 11};
12use hir_expand::name::Name; 12use hir_expand::name::Name;
13use test_utils::mark;
14 13
15use super::{BindingMode, Expectation, InferenceContext}; 14use super::{BindingMode, Expectation, InferenceContext};
16use crate::{lower::lower_to_chalk_mutability, utils::variant_data, Substs, Ty}; 15use crate::{lower::lower_to_chalk_mutability, utils::variant_data, Substs, Ty};
@@ -108,7 +107,7 @@ impl<'a> InferenceContext<'a> {
108 } 107 }
109 } 108 }
110 } else if let Pat::Ref { .. } = &body[pat] { 109 } else if let Pat::Ref { .. } = &body[pat] {
111 mark::hit!(match_ergonomics_ref); 110 cov_mark::hit!(match_ergonomics_ref);
112 // When you encounter a `&pat` pattern, reset to Move. 111 // When you encounter a `&pat` pattern, reset to Move.
113 // This is so that `w` is by value: `let (_, &w) = &(1, &2);` 112 // This is so that `w` is by value: `let (_, &w) = &(1, &2);`
114 default_bm = BindingMode::Move; 113 default_bm = BindingMode::Move;
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 5d541104e..ae3554bac 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -260,7 +260,7 @@ impl<'a> InferenceContext<'a> {
260 })); 260 }));
261 Some(trait_substs) 261 Some(trait_substs)
262 } 262 }
263 AssocContainerId::ContainerId(_) => None, 263 AssocContainerId::ModuleId(_) => None,
264 }; 264 };
265 265
266 self.write_assoc_resolution(id, item); 266 self.write_assoc_resolution(id, item);
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index 99a89a7f3..54fcfed10 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -5,8 +5,6 @@ use std::borrow::Cow;
5use chalk_ir::{FloatTy, IntTy, TyVariableKind}; 5use chalk_ir::{FloatTy, IntTy, TyVariableKind};
6use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; 6use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
7 7
8use test_utils::mark;
9
10use super::{InferenceContext, Obligation}; 8use super::{InferenceContext, Obligation};
11use crate::{ 9use crate::{
12 BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferenceVar, Scalar, 10 BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferenceVar, Scalar,
@@ -387,7 +385,7 @@ impl InferenceTable {
387 // more than once 385 // more than once
388 for i in 0..3 { 386 for i in 0..3 {
389 if i > 0 { 387 if i > 0 {
390 mark::hit!(type_var_resolves_to_int_var); 388 cov_mark::hit!(type_var_resolves_to_int_var);
391 } 389 }
392 match &*ty { 390 match &*ty {
393 Ty::InferenceVar(tv, _) => { 391 Ty::InferenceVar(tv, _) => {
@@ -416,7 +414,7 @@ impl InferenceTable {
416 Ty::InferenceVar(tv, kind) => { 414 Ty::InferenceVar(tv, kind) => {
417 let inner = tv.to_inner(); 415 let inner = tv.to_inner();
418 if tv_stack.contains(&inner) { 416 if tv_stack.contains(&inner) {
419 mark::hit!(type_var_cycles_resolve_as_possible); 417 cov_mark::hit!(type_var_cycles_resolve_as_possible);
420 // recursive type 418 // recursive type
421 return self.type_variable_table.fallback_value(tv, kind); 419 return self.type_variable_table.fallback_value(tv, kind);
422 } 420 }
@@ -443,7 +441,7 @@ impl InferenceTable {
443 Ty::InferenceVar(tv, kind) => { 441 Ty::InferenceVar(tv, kind) => {
444 let inner = tv.to_inner(); 442 let inner = tv.to_inner();
445 if tv_stack.contains(&inner) { 443 if tv_stack.contains(&inner) {
446 mark::hit!(type_var_cycles_resolve_completely); 444 cov_mark::hit!(type_var_cycles_resolve_completely);
447 // recursive type 445 // recursive type
448 return self.type_variable_table.fallback_value(tv, kind); 446 return self.type_variable_table.fallback_value(tv, kind);
449 } 447 }
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 5fe5b8ad1..d84ec9b7a 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -8,7 +8,7 @@
8use std::{iter, sync::Arc}; 8use std::{iter, sync::Arc};
9 9
10use base_db::CrateId; 10use base_db::CrateId;
11use chalk_ir::Mutability; 11use chalk_ir::{cast::Cast, Mutability};
12use hir_def::{ 12use hir_def::{
13 adt::StructKind, 13 adt::StructKind,
14 builtin_type::BuiltinType, 14 builtin_type::BuiltinType,
@@ -24,10 +24,10 @@ use hir_expand::name::Name;
24use la_arena::ArenaMap; 24use la_arena::ArenaMap;
25use smallvec::SmallVec; 25use smallvec::SmallVec;
26use stdx::impl_from; 26use stdx::impl_from;
27use test_utils::mark;
28 27
29use crate::{ 28use crate::{
30 db::HirDatabase, 29 db::HirDatabase,
30 traits::chalk::{Interner, ToChalk},
31 utils::{ 31 utils::{
32 all_super_trait_refs, associated_type_by_name_including_super_traits, generics, 32 all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
33 make_mut_slice, variant_data, 33 make_mut_slice, variant_data,
@@ -760,7 +760,7 @@ fn assoc_type_bindings_from_type_bound<'a>(
760 760
761impl ReturnTypeImplTrait { 761impl ReturnTypeImplTrait {
762 fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self { 762 fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self {
763 mark::hit!(lower_rpit); 763 cov_mark::hit!(lower_rpit);
764 let self_ty = Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)); 764 let self_ty = Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0));
765 let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { 765 let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
766 bounds 766 bounds
@@ -915,10 +915,21 @@ impl TraitEnvironment {
915 pub fn lower(db: &dyn HirDatabase, resolver: &Resolver) -> Arc<TraitEnvironment> { 915 pub fn lower(db: &dyn HirDatabase, resolver: &Resolver) -> Arc<TraitEnvironment> {
916 let ctx = TyLoweringContext::new(db, &resolver) 916 let ctx = TyLoweringContext::new(db, &resolver)
917 .with_type_param_mode(TypeParamLoweringMode::Placeholder); 917 .with_type_param_mode(TypeParamLoweringMode::Placeholder);
918 let mut predicates = resolver 918 let mut traits_in_scope = Vec::new();
919 .where_predicates_in_scope() 919 let mut clauses = Vec::new();
920 .flat_map(|pred| GenericPredicate::from_where_predicate(&ctx, pred)) 920 for pred in resolver.where_predicates_in_scope() {
921 .collect::<Vec<_>>(); 921 for pred in GenericPredicate::from_where_predicate(&ctx, pred) {
922 if pred.is_error() {
923 continue;
924 }
925 if let GenericPredicate::Implemented(tr) = &pred {
926 traits_in_scope.push((tr.self_ty().clone(), tr.trait_));
927 }
928 let program_clause: chalk_ir::ProgramClause<Interner> =
929 pred.clone().to_chalk(db).cast(&Interner);
930 clauses.push(program_clause.into_from_env_clause(&Interner));
931 }
932 }
922 933
923 if let Some(def) = resolver.generic_def() { 934 if let Some(def) = resolver.generic_def() {
924 let container: Option<AssocContainerId> = match def { 935 let container: Option<AssocContainerId> = match def {
@@ -935,16 +946,19 @@ impl TraitEnvironment {
935 // add `Self: Trait<T1, T2, ...>` to the environment in trait 946 // add `Self: Trait<T1, T2, ...>` to the environment in trait
936 // function default implementations (and hypothetical code 947 // function default implementations (and hypothetical code
937 // inside consts or type aliases) 948 // inside consts or type aliases)
938 test_utils::mark::hit!(trait_self_implements_self); 949 cov_mark::hit!(trait_self_implements_self);
939 let substs = Substs::type_params(db, trait_id); 950 let substs = Substs::type_params(db, trait_id);
940 let trait_ref = TraitRef { trait_: trait_id, substs }; 951 let trait_ref = TraitRef { trait_: trait_id, substs };
941 let pred = GenericPredicate::Implemented(trait_ref); 952 let pred = GenericPredicate::Implemented(trait_ref);
942 953 let program_clause: chalk_ir::ProgramClause<Interner> =
943 predicates.push(pred); 954 pred.clone().to_chalk(db).cast(&Interner);
955 clauses.push(program_clause.into_from_env_clause(&Interner));
944 } 956 }
945 } 957 }
946 958
947 Arc::new(TraitEnvironment { predicates }) 959 let env = chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses);
960
961 Arc::new(TraitEnvironment { traits_from_clauses: traits_in_scope, env })
948 } 962 }
949} 963}
950 964
@@ -1131,8 +1145,8 @@ impl CallableDefId {
1131 let db = db.upcast(); 1145 let db = db.upcast();
1132 match self { 1146 match self {
1133 CallableDefId::FunctionId(f) => f.lookup(db).module(db), 1147 CallableDefId::FunctionId(f) => f.lookup(db).module(db),
1134 CallableDefId::StructId(s) => s.lookup(db).container.module(db), 1148 CallableDefId::StructId(s) => s.lookup(db).container,
1135 CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container.module(db), 1149 CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container,
1136 } 1150 }
1137 .krate() 1151 .krate()
1138 } 1152 }
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index dfcf346fb..d57c6de70 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -267,7 +267,7 @@ impl Ty {
267 LangItemTarget::ImplDefId(it) => Some(it), 267 LangItemTarget::ImplDefId(it) => Some(it),
268 _ => None, 268 _ => None,
269 }) 269 })
270 .map(|it| it.lookup(db.upcast()).container.module(db.upcast()).krate()) 270 .map(|it| it.lookup(db.upcast()).container.krate())
271 .collect(); 271 .collect();
272 Some(res) 272 Some(res)
273 } 273 }
@@ -528,8 +528,7 @@ fn iterate_trait_method_candidates(
528 self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); 528 self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
529 let env_traits = if let Ty::Placeholder(_) = self_ty.value { 529 let env_traits = if let Ty::Placeholder(_) = self_ty.value {
530 // if we have `T: Trait` in the param env, the trait doesn't need to be in scope 530 // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
531 env.trait_predicates_for_self_ty(&self_ty.value) 531 env.traits_in_scope_from_clauses(&self_ty.value)
532 .map(|tr| tr.trait_)
533 .flat_map(|t| all_super_traits(db.upcast(), t)) 532 .flat_map(|t| all_super_traits(db.upcast(), t))
534 .collect() 533 .collect()
535 } else { 534 } else {
@@ -588,7 +587,7 @@ fn iterate_inherent_methods(
588 // already happens in `is_valid_candidate` above; if not, we 587 // already happens in `is_valid_candidate` above; if not, we
589 // check it here 588 // check it here
590 if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { 589 if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() {
591 test_utils::mark::hit!(impl_self_type_match_without_receiver); 590 cov_mark::hit!(impl_self_type_match_without_receiver);
592 continue; 591 continue;
593 } 592 }
594 if callback(&self_ty.value, item) { 593 if callback(&self_ty.value, item) {
@@ -715,7 +714,7 @@ fn transform_receiver_ty(
715 .fill_with_unknown() 714 .fill_with_unknown()
716 .build() 715 .build()
717 } 716 }
718 AssocContainerId::ContainerId(_) => unreachable!(), 717 AssocContainerId::ModuleId(_) => unreachable!(),
719 }; 718 };
720 let sig = db.callable_item_signature(function_id.into()); 719 let sig = db.callable_item_signature(function_id.into());
721 Some(sig.value.params()[0].clone().subst_bound_vars(&substs)) 720 Some(sig.value.params()[0].clone().subst_bound_vars(&substs))
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 7386a4e7b..0a4141e69 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -13,12 +13,13 @@ use std::{env, sync::Arc};
13use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; 13use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt};
14use expect_test::Expect; 14use expect_test::Expect;
15use hir_def::{ 15use hir_def::{
16 body::{BodySourceMap, SyntheticSyntax}, 16 body::{Body, BodySourceMap, SyntheticSyntax},
17 child_by_source::ChildBySource, 17 child_by_source::ChildBySource,
18 db::DefDatabase, 18 db::DefDatabase,
19 item_scope::ItemScope, 19 item_scope::ItemScope,
20 keys, 20 keys,
21 nameres::DefMap, 21 nameres::DefMap,
22 src::HasSource,
22 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 23 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
23}; 24};
24use hir_expand::{db::AstDatabase, InFile}; 25use hir_expand::{db::AstDatabase, InFile};
@@ -195,18 +196,15 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
195 defs.sort_by_key(|def| match def { 196 defs.sort_by_key(|def| match def {
196 DefWithBodyId::FunctionId(it) => { 197 DefWithBodyId::FunctionId(it) => {
197 let loc = it.lookup(&db); 198 let loc = it.lookup(&db);
198 let tree = db.item_tree(loc.id.file_id); 199 loc.source(&db).value.syntax().text_range().start()
199 tree.source(&db, loc.id).syntax().text_range().start()
200 } 200 }
201 DefWithBodyId::ConstId(it) => { 201 DefWithBodyId::ConstId(it) => {
202 let loc = it.lookup(&db); 202 let loc = it.lookup(&db);
203 let tree = db.item_tree(loc.id.file_id); 203 loc.source(&db).value.syntax().text_range().start()
204 tree.source(&db, loc.id).syntax().text_range().start()
205 } 204 }
206 DefWithBodyId::StaticId(it) => { 205 DefWithBodyId::StaticId(it) => {
207 let loc = it.lookup(&db); 206 let loc = it.lookup(&db);
208 let tree = db.item_tree(loc.id.file_id); 207 loc.source(&db).value.syntax().text_range().start()
209 tree.source(&db, loc.id).syntax().text_range().start()
210 } 208 }
211 }); 209 });
212 for def in defs { 210 for def in defs {
@@ -234,13 +232,13 @@ fn visit_module(
234 let def = it.into(); 232 let def = it.into();
235 cb(def); 233 cb(def);
236 let body = db.body(def); 234 let body = db.body(def);
237 visit_scope(db, crate_def_map, &body.item_scope, cb); 235 visit_body(db, &body, cb);
238 } 236 }
239 AssocItemId::ConstId(it) => { 237 AssocItemId::ConstId(it) => {
240 let def = it.into(); 238 let def = it.into();
241 cb(def); 239 cb(def);
242 let body = db.body(def); 240 let body = db.body(def);
243 visit_scope(db, crate_def_map, &body.item_scope, cb); 241 visit_body(db, &body, cb);
244 } 242 }
245 AssocItemId::TypeAliasId(_) => (), 243 AssocItemId::TypeAliasId(_) => (),
246 } 244 }
@@ -259,19 +257,19 @@ fn visit_module(
259 let def = it.into(); 257 let def = it.into();
260 cb(def); 258 cb(def);
261 let body = db.body(def); 259 let body = db.body(def);
262 visit_scope(db, crate_def_map, &body.item_scope, cb); 260 visit_body(db, &body, cb);
263 } 261 }
264 ModuleDefId::ConstId(it) => { 262 ModuleDefId::ConstId(it) => {
265 let def = it.into(); 263 let def = it.into();
266 cb(def); 264 cb(def);
267 let body = db.body(def); 265 let body = db.body(def);
268 visit_scope(db, crate_def_map, &body.item_scope, cb); 266 visit_body(db, &body, cb);
269 } 267 }
270 ModuleDefId::StaticId(it) => { 268 ModuleDefId::StaticId(it) => {
271 let def = it.into(); 269 let def = it.into();
272 cb(def); 270 cb(def);
273 let body = db.body(def); 271 let body = db.body(def);
274 visit_scope(db, crate_def_map, &body.item_scope, cb); 272 visit_body(db, &body, cb);
275 } 273 }
276 ModuleDefId::TraitId(it) => { 274 ModuleDefId::TraitId(it) => {
277 let trait_data = db.trait_data(it); 275 let trait_data = db.trait_data(it);
@@ -288,6 +286,14 @@ fn visit_module(
288 } 286 }
289 } 287 }
290 } 288 }
289
290 fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(DefWithBodyId)) {
291 for def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) {
292 for (mod_id, _) in def_map.modules() {
293 visit_module(db, &def_map, mod_id, cb);
294 }
295 }
296 }
291} 297}
292 298
293fn ellipsize(mut text: String, max_len: usize) -> String { 299fn ellipsize(mut text: String, max_len: usize) -> String {
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index 7bc6c79f3..63d9d4e0b 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -1,5 +1,4 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
3 2
4use super::{check_infer, check_infer_with_mismatches}; 3use super::{check_infer, check_infer_with_mismatches};
5 4
@@ -381,7 +380,7 @@ fn infer_match_second_coerce() {
381 380
382#[test] 381#[test]
383fn coerce_merge_one_by_one1() { 382fn coerce_merge_one_by_one1() {
384 mark::check!(coerce_merge_fail_fallback); 383 cov_mark::check!(coerce_merge_fail_fallback);
385 384
386 check_infer( 385 check_infer(
387 r" 386 r"
@@ -589,7 +588,7 @@ fn coerce_fn_item_to_fn_ptr() {
589 588
590#[test] 589#[test]
591fn coerce_fn_items_in_match_arms() { 590fn coerce_fn_items_in_match_arms() {
592 mark::check!(coerce_fn_reification); 591 cov_mark::check!(coerce_fn_reification);
593 592
594 check_infer_with_mismatches( 593 check_infer_with_mismatches(
595 r" 594 r"
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index a9901d7b8..4e3f9a9b6 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -913,7 +913,7 @@ fn test() { S2.into(); }
913 913
914#[test] 914#[test]
915fn method_resolution_overloaded_method() { 915fn method_resolution_overloaded_method() {
916 test_utils::mark::check!(impl_self_type_match_without_receiver); 916 cov_mark::check!(impl_self_type_match_without_receiver);
917 check_types( 917 check_types(
918 r#" 918 r#"
919struct Wrapper<T>(T); 919struct Wrapper<T>(T);
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 2053d8f56..5da19ba5f 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -1,5 +1,4 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
3 2
4use super::{check_infer, check_infer_with_mismatches}; 3use super::{check_infer, check_infer_with_mismatches};
5 4
@@ -197,7 +196,7 @@ fn infer_pattern_match_ergonomics() {
197 196
198#[test] 197#[test]
199fn infer_pattern_match_ergonomics_ref() { 198fn infer_pattern_match_ergonomics_ref() {
200 mark::check!(match_ergonomics_ref); 199 cov_mark::check!(match_ergonomics_ref);
201 check_infer( 200 check_infer(
202 r#" 201 r#"
203 fn test() { 202 fn test() {
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index cffe8630b..69314e245 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -1,5 +1,4 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
3 2
4use super::{check_infer, check_types}; 3use super::{check_infer, check_types};
5 4
@@ -87,8 +86,8 @@ fn bug_651() {
87 86
88#[test] 87#[test]
89fn recursive_vars() { 88fn recursive_vars() {
90 mark::check!(type_var_cycles_resolve_completely); 89 cov_mark::check!(type_var_cycles_resolve_completely);
91 mark::check!(type_var_cycles_resolve_as_possible); 90 cov_mark::check!(type_var_cycles_resolve_as_possible);
92 check_infer( 91 check_infer(
93 r#" 92 r#"
94 fn test() { 93 fn test() {
@@ -166,7 +165,7 @@ fn infer_std_crash_1() {
166 165
167#[test] 166#[test]
168fn infer_std_crash_2() { 167fn infer_std_crash_2() {
169 mark::check!(type_var_resolves_to_int_var); 168 cov_mark::check!(type_var_resolves_to_int_var);
170 // caused "equating two type variables, ...", taken from std 169 // caused "equating two type variables, ...", taken from std
171 check_infer( 170 check_infer(
172 r#" 171 r#"
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 2947857a5..f5069eba5 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1,5 +1,4 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
3 2
4use super::{check_infer, check_types}; 3use super::{check_infer, check_types};
5 4
@@ -2314,7 +2313,7 @@ fn generic_default_depending_on_other_type_arg_forward() {
2314 2313
2315#[test] 2314#[test]
2316fn infer_operator_overload() { 2315fn infer_operator_overload() {
2317 mark::check!(infer_expr_inner_binary_operator_overload); 2316 cov_mark::check!(infer_expr_inner_binary_operator_overload);
2318 2317
2319 check_infer( 2318 check_infer(
2320 r#" 2319 r#"
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 1298e5a88..e185b1c0a 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -1,5 +1,4 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
3 2
4use super::{check_infer, check_infer_with_mismatches, check_types}; 3use super::{check_infer, check_infer_with_mismatches, check_types};
5 4
@@ -319,7 +318,7 @@ fn infer_from_bound_2() {
319 318
320#[test] 319#[test]
321fn trait_default_method_self_bound_implements_trait() { 320fn trait_default_method_self_bound_implements_trait() {
322 mark::check!(trait_self_implements_self); 321 cov_mark::check!(trait_self_implements_self);
323 check_infer( 322 check_infer(
324 r#" 323 r#"
325 trait Trait { 324 trait Trait {
@@ -1189,7 +1188,7 @@ fn impl_trait() {
1189 1188
1190#[test] 1189#[test]
1191fn simple_return_pos_impl_trait() { 1190fn simple_return_pos_impl_trait() {
1192 mark::check!(lower_rpit); 1191 cov_mark::check!(lower_rpit);
1193 check_infer( 1192 check_infer(
1194 r#" 1193 r#"
1195 trait Trait<T> { 1194 trait Trait<T> {
@@ -3175,6 +3174,39 @@ fn f() {
3175} 3174}
3176 3175
3177#[test] 3176#[test]
3177fn trait_in_scope_with_inner_item() {
3178 check_infer(
3179 r#"
3180mod m {
3181 pub trait Tr {
3182 fn method(&self) -> u8 { 0 }
3183 }
3184
3185 impl Tr for () {}
3186}
3187
3188use m::Tr;
3189
3190fn f() {
3191 fn inner() {
3192 ().method();
3193 //^^^^^^^^^^^ u8
3194 }
3195}
3196 "#,
3197 expect![[r#"
3198 46..50 'self': &Self
3199 58..63 '{ 0 }': u8
3200 60..61 '0': u8
3201 115..185 '{ ... } }': ()
3202 132..183 '{ ... }': ()
3203 142..144 '()': ()
3204 142..153 '().method()': u8
3205 "#]],
3206 );
3207}
3208
3209#[test]
3178fn inner_use_in_block() { 3210fn inner_use_in_block() {
3179 check_types( 3211 check_types(
3180 r#" 3212 r#"
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs
index e4cdb6d53..27f350f70 100644
--- a/crates/hir_ty/src/traits.rs
+++ b/crates/hir_ty/src/traits.rs
@@ -38,22 +38,25 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
38/// fn foo<T: Default>(t: T) {} 38/// fn foo<T: Default>(t: T) {}
39/// ``` 39/// ```
40/// we assume that `T: Default`. 40/// we assume that `T: Default`.
41#[derive(Clone, Debug, PartialEq, Eq, Hash)] 41#[derive(Debug, Clone, PartialEq, Eq, Hash)]
42pub struct TraitEnvironment { 42pub struct TraitEnvironment {
43 pub predicates: Vec<GenericPredicate>, 43 // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord,
44 // but for now it's too annoying...
45 pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>,
46 pub(crate) env: chalk_ir::Environment<Interner>,
44} 47}
45 48
46impl TraitEnvironment { 49impl TraitEnvironment {
47 /// Returns trait refs with the given self type which are supposed to hold 50 pub(crate) fn traits_in_scope_from_clauses<'a>(
48 /// in this trait env. E.g. if we are in `foo<T: SomeTrait>()`, this will
49 /// find that `T: SomeTrait` if we call it for `T`.
50 pub(crate) fn trait_predicates_for_self_ty<'a>(
51 &'a self, 51 &'a self,
52 ty: &'a Ty, 52 ty: &'a Ty,
53 ) -> impl Iterator<Item = &'a TraitRef> + 'a { 53 ) -> impl Iterator<Item = TraitId> + 'a {
54 self.predicates.iter().filter_map(move |pred| match pred { 54 self.traits_from_clauses.iter().filter_map(move |(self_ty, trait_id)| {
55 GenericPredicate::Implemented(tr) if tr.self_ty() == ty => Some(tr), 55 if self_ty == ty {
56 _ => None, 56 Some(*trait_id)
57 } else {
58 None
59 }
57 }) 60 })
58 } 61 }
59} 62}
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 4378a9723..1a2a3a8c7 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -33,13 +33,13 @@ pub(super) mod tls;
33mod interner; 33mod interner;
34mod mapping; 34mod mapping;
35 35
36pub(super) trait ToChalk { 36pub(crate) trait ToChalk {
37 type Chalk; 37 type Chalk;
38 fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk; 38 fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk;
39 fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self; 39 fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self;
40} 40}
41 41
42pub(super) fn from_chalk<T, ChalkT>(db: &dyn HirDatabase, chalk: ChalkT) -> T 42pub(crate) fn from_chalk<T, ChalkT>(db: &dyn HirDatabase, chalk: ChalkT) -> T
43where 43where
44 T: ToChalk<Chalk = ChalkT>, 44 T: ToChalk<Chalk = ChalkT>,
45{ 45{
@@ -424,7 +424,7 @@ pub(crate) fn trait_datum_query(
424 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); 424 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
425 let flags = rust_ir::TraitFlags { 425 let flags = rust_ir::TraitFlags {
426 auto: trait_data.auto, 426 auto: trait_data.auto,
427 upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate() != krate, 427 upstream: trait_.lookup(db.upcast()).container.krate() != krate,
428 non_enumerable: true, 428 non_enumerable: true,
429 coinductive: false, // only relevant for Chalk testing 429 coinductive: false, // only relevant for Chalk testing
430 // FIXME: set these flags correctly 430 // FIXME: set these flags correctly
@@ -487,19 +487,14 @@ pub(crate) fn struct_datum_query(
487 struct_id: AdtId, 487 struct_id: AdtId,
488) -> Arc<StructDatum> { 488) -> Arc<StructDatum> {
489 debug!("struct_datum {:?}", struct_id); 489 debug!("struct_datum {:?}", struct_id);
490 let type_ctor = Ty::Adt(struct_id, Substs::empty());
491 let chalk_ir::AdtId(adt_id) = struct_id; 490 let chalk_ir::AdtId(adt_id) = struct_id;
492 debug!("struct {:?} = {:?}", struct_id, type_ctor);
493 let num_params = generics(db.upcast(), adt_id.into()).len(); 491 let num_params = generics(db.upcast(), adt_id.into()).len();
494 let upstream = adt_id.module(db.upcast()).krate() != krate; 492 let upstream = adt_id.module(db.upcast()).krate() != krate;
495 let where_clauses = type_ctor 493 let where_clauses = {
496 .as_generic_def() 494 let generic_params = generics(db.upcast(), adt_id.into());
497 .map(|generic_def| { 495 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
498 let generic_params = generics(db.upcast(), generic_def); 496 convert_where_clauses(db, adt_id.into(), &bound_vars)
499 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); 497 };
500 convert_where_clauses(db, generic_def, &bound_vars)
501 })
502 .unwrap_or_else(Vec::new);
503 let flags = rust_ir::AdtFlags { 498 let flags = rust_ir::AdtFlags {
504 upstream, 499 upstream,
505 // FIXME set fundamental and phantom_data flags correctly 500 // FIXME set fundamental and phantom_data flags correctly
@@ -548,7 +543,7 @@ fn impl_def_datum(
548 let generic_params = generics(db.upcast(), impl_id.into()); 543 let generic_params = generics(db.upcast(), impl_id.into());
549 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); 544 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
550 let trait_ = trait_ref.trait_; 545 let trait_ = trait_ref.trait_;
551 let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate() == krate { 546 let impl_type = if impl_id.lookup(db.upcast()).container.krate() == krate {
552 rust_ir::ImplType::Local 547 rust_ir::ImplType::Local
553 } else { 548 } else {
554 rust_ir::ImplType::External 549 rust_ir::ImplType::External
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs
index 3a08b67e9..b0415e8b0 100644
--- a/crates/hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/hir_ty/src/traits/chalk/mapping.rs
@@ -17,7 +17,7 @@ use crate::{
17 primitive::UintTy, 17 primitive::UintTy,
18 traits::{Canonical, Obligation}, 18 traits::{Canonical, Obligation},
19 AliasTy, CallableDefId, FnPointer, FnSig, GenericPredicate, InEnvironment, OpaqueTy, 19 AliasTy, CallableDefId, FnPointer, FnSig, GenericPredicate, InEnvironment, OpaqueTy,
20 OpaqueTyId, ProjectionPredicate, ProjectionTy, Scalar, Substs, TraitEnvironment, TraitRef, Ty, 20 OpaqueTyId, ProjectionPredicate, ProjectionTy, Scalar, Substs, TraitRef, Ty,
21}; 21};
22 22
23use super::interner::*; 23use super::interner::*;
@@ -536,31 +536,6 @@ where
536 } 536 }
537} 537}
538 538
539impl ToChalk for Arc<TraitEnvironment> {
540 type Chalk = chalk_ir::Environment<Interner>;
541
542 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Environment<Interner> {
543 let mut clauses = Vec::new();
544 for pred in &self.predicates {
545 if pred.is_error() {
546 // for env, we just ignore errors
547 continue;
548 }
549 let program_clause: chalk_ir::ProgramClause<Interner> =
550 pred.clone().to_chalk(db).cast(&Interner);
551 clauses.push(program_clause.into_from_env_clause(&Interner));
552 }
553 chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses)
554 }
555
556 fn from_chalk(
557 _db: &dyn HirDatabase,
558 _env: chalk_ir::Environment<Interner>,
559 ) -> Arc<TraitEnvironment> {
560 unimplemented!()
561 }
562}
563
564impl<T: ToChalk> ToChalk for InEnvironment<T> 539impl<T: ToChalk> ToChalk for InEnvironment<T>
565where 540where
566 T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>, 541 T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>,
@@ -569,19 +544,16 @@ where
569 544
570 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> { 545 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> {
571 chalk_ir::InEnvironment { 546 chalk_ir::InEnvironment {
572 environment: self.environment.to_chalk(db), 547 environment: self.environment.env.clone(),
573 goal: self.value.to_chalk(db), 548 goal: self.value.to_chalk(db),
574 } 549 }
575 } 550 }
576 551
577 fn from_chalk( 552 fn from_chalk(
578 db: &dyn HirDatabase, 553 _db: &dyn HirDatabase,
579 in_env: chalk_ir::InEnvironment<T::Chalk>, 554 _in_env: chalk_ir::InEnvironment<T::Chalk>,
580 ) -> InEnvironment<T> { 555 ) -> InEnvironment<T> {
581 InEnvironment { 556 unimplemented!()
582 environment: from_chalk(db, in_env.environment),
583 value: from_chalk(db, in_env.goal),
584 }
585 } 557 }
586} 558}
587 559
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index 65b79df0d..7351e4e54 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -259,6 +259,6 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic
259 match container { 259 match container {
260 AssocContainerId::ImplId(it) => Some(it.into()), 260 AssocContainerId::ImplId(it) => Some(it.into()),
261 AssocContainerId::TraitId(it) => Some(it.into()), 261 AssocContainerId::TraitId(it) => Some(it.into()),
262 AssocContainerId::ContainerId(_) => None, 262 AssocContainerId::ModuleId(_) => None,
263 } 263 }
264} 264}
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f6aaaeda4..107bd8432 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13either = "1.5.3" 14either = "1.5.3"
14indexmap = "1.4.0" 15indexmap = "1.4.0"
15itertools = "0.10.0" 16itertools = "0.10.0"
@@ -26,7 +27,6 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
26ide_db = { path = "../ide_db", version = "0.0.0" } 27ide_db = { path = "../ide_db", version = "0.0.0" }
27cfg = { path = "../cfg", version = "0.0.0" } 28cfg = { path = "../cfg", version = "0.0.0" }
28profile = { path = "../profile", version = "0.0.0" } 29profile = { path = "../profile", version = "0.0.0" }
29test_utils = { path = "../test_utils", version = "0.0.0" }
30ide_assists = { path = "../ide_assists", version = "0.0.0" } 30ide_assists = { path = "../ide_assists", version = "0.0.0" }
31ide_ssr = { path = "../ide_ssr", version = "0.0.0" } 31ide_ssr = { path = "../ide_ssr", version = "0.0.0" }
32ide_completion = { path = "../ide_completion", version = "0.0.0" } 32ide_completion = { path = "../ide_completion", version = "0.0.0" }
@@ -36,4 +36,5 @@ ide_completion = { path = "../ide_completion", version = "0.0.0" }
36hir = { path = "../hir", version = "0.0.0" } 36hir = { path = "../hir", version = "0.0.0" }
37 37
38[dev-dependencies] 38[dev-dependencies]
39test_utils = { path = "../test_utils" }
39expect-test = "1.1" 40expect-test = "1.1"
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs
index 84b8883de..2df9266b4 100644
--- a/crates/ide/src/display/short_label.rs
+++ b/crates/ide/src/display/short_label.rs
@@ -71,11 +71,7 @@ impl ShortLabel for ast::TypeAlias {
71 71
72impl ShortLabel for ast::Const { 72impl ShortLabel for ast::Const {
73 fn short_label(&self) -> Option<String> { 73 fn short_label(&self) -> Option<String> {
74 let mut new_buf = short_label_from_ty(self, self.ty(), "const ")?; 74 short_label_from_ty(self, self.ty(), "const ")
75 if let Some(expr) = self.body() {
76 format_to!(new_buf, " = {}", expr.syntax());
77 }
78 Some(new_buf)
79 } 75 }
80} 76}
81 77
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index a9454cfa3..ea45086ce 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1,3 +1,4 @@
1use either::Either;
1use hir::{ 2use hir::{
2 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, 3 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource,
3 HirDisplay, Module, ModuleDef, ModuleSource, Semantics, 4 HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
@@ -11,7 +12,6 @@ use ide_db::{
11use itertools::Itertools; 12use itertools::Itertools;
12use stdx::format_to; 13use stdx::format_to;
13use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 14use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
14use test_utils::mark;
15 15
16use crate::{ 16use crate::{
17 display::{macro_label, ShortLabel, TryToNav}, 17 display::{macro_label, ShortLabel, TryToNav},
@@ -193,8 +193,8 @@ fn runnable_action(
193 ModuleDef::Function(func) => { 193 ModuleDef::Function(func) => {
194 let src = func.source(sema.db)?; 194 let src = func.source(sema.db)?;
195 if src.file_id != file_id.into() { 195 if src.file_id != file_id.into() {
196 mark::hit!(hover_macro_generated_struct_fn_doc_comment); 196 cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
197 mark::hit!(hover_macro_generated_struct_fn_doc_attr); 197 cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr);
198 return None; 198 return None;
199 } 199 }
200 200
@@ -367,7 +367,7 @@ fn hover_for_definition(
367 .and_then(|fd| hover_for_builtin(fd, it)) 367 .and_then(|fd| hover_for_builtin(fd, it))
368 .or_else(|| Some(Markup::fenced_block(&it.name()))), 368 .or_else(|| Some(Markup::fenced_block(&it.name()))),
369 }, 369 },
370 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), 370 Definition::Local(it) => hover_for_local(it, db),
371 Definition::SelfType(impl_def) => { 371 Definition::SelfType(impl_def) => {
372 impl_def.target_ty(db).as_adt().and_then(|adt| match adt { 372 impl_def.target_ty(db).as_adt().and_then(|adt| match adt {
373 Adt::Struct(it) => from_def_source(db, it, mod_path), 373 Adt::Struct(it) => from_def_source(db, it, mod_path),
@@ -406,6 +406,29 @@ fn hover_for_definition(
406 } 406 }
407} 407}
408 408
409fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> {
410 let ty = it.ty(db);
411 let ty = ty.display(db);
412 let is_mut = if it.is_mut(db) { "mut " } else { "" };
413 let desc = match it.source(db).value {
414 Either::Left(ident) => {
415 let name = it.name(db).unwrap();
416 let let_kw = if ident
417 .syntax()
418 .parent()
419 .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION)
420 {
421 "let "
422 } else {
423 ""
424 };
425 format!("{}{}{}: {}", let_kw, is_mut, name, ty)
426 }
427 Either::Right(_) => format!("{}self: {}", is_mut, ty),
428 };
429 hover_markup(None, Some(desc), None)
430}
431
409fn hover_for_keyword( 432fn hover_for_keyword(
410 sema: &Semantics<RootDatabase>, 433 sema: &Semantics<RootDatabase>,
411 links_in_hover: bool, 434 links_in_hover: bool,
@@ -575,7 +598,7 @@ fn main() {
575 *iter* 598 *iter*
576 599
577 ```rust 600 ```rust
578 Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> 601 let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
579 ``` 602 ```
580 "#]], 603 "#]],
581 ); 604 );
@@ -799,7 +822,7 @@ fn main() {
799 ``` 822 ```
800 823
801 ```rust 824 ```rust
802 const foo: u32 = 123 825 const foo: u32
803 ``` 826 ```
804 "#]], 827 "#]],
805 ); 828 );
@@ -832,7 +855,7 @@ fn main() {
832 *zz* 855 *zz*
833 856
834 ```rust 857 ```rust
835 Test<i32, u8> 858 let zz: Test<i32, u8>
836 ``` 859 ```
837 "#]], 860 "#]],
838 ); 861 );
@@ -871,7 +894,7 @@ fn main() { let b$0ar = Some(12); }
871 *bar* 894 *bar*
872 895
873 ```rust 896 ```rust
874 Option<i32> 897 let bar: Option<i32>
875 ``` 898 ```
876 "#]], 899 "#]],
877 ); 900 );
@@ -939,7 +962,7 @@ fn main() {
939 *foo* 962 *foo*
940 963
941 ```rust 964 ```rust
942 i32 965 foo: i32
943 ``` 966 ```
944 "#]], 967 "#]],
945 ) 968 )
@@ -953,7 +976,7 @@ fn main() {
953 *foo* 976 *foo*
954 977
955 ```rust 978 ```rust
956 i32 979 foo: i32
957 ``` 980 ```
958 "#]], 981 "#]],
959 ) 982 )
@@ -967,7 +990,7 @@ fn main() {
967 *foo* 990 *foo*
968 991
969 ```rust 992 ```rust
970 i32 993 foo: i32
971 ``` 994 ```
972 "#]], 995 "#]],
973 ) 996 )
@@ -981,7 +1004,7 @@ fn main() {
981 *foo* 1004 *foo*
982 1005
983 ```rust 1006 ```rust
984 i32 1007 foo: i32
985 ``` 1008 ```
986 "#]], 1009 "#]],
987 ) 1010 )
@@ -1001,7 +1024,7 @@ fn main() {
1001 *_x* 1024 *_x*
1002 1025
1003 ```rust 1026 ```rust
1004 impl Deref<Target = u8> + DerefMut<Target = u8> 1027 _x: impl Deref<Target = u8> + DerefMut<Target = u8>
1005 ``` 1028 ```
1006 "#]], 1029 "#]],
1007 ) 1030 )
@@ -1023,7 +1046,7 @@ fn main() { let foo_$0test = Thing::new(); }
1023 *foo_test* 1046 *foo_test*
1024 1047
1025 ```rust 1048 ```rust
1026 Thing 1049 let foo_test: Thing
1027 ``` 1050 ```
1028 "#]], 1051 "#]],
1029 ) 1052 )
@@ -1082,7 +1105,7 @@ fn main() {
1082 ``` 1105 ```
1083 1106
1084 ```rust 1107 ```rust
1085 const C: u32 = 1 1108 const C: u32
1086 ``` 1109 ```
1087 "#]], 1110 "#]],
1088 ) 1111 )
@@ -1183,7 +1206,7 @@ fn y() {
1183 *x* 1206 *x*
1184 1207
1185 ```rust 1208 ```rust
1186 i32 1209 let x: i32
1187 ``` 1210 ```
1188 "#]], 1211 "#]],
1189 ) 1212 )
@@ -1260,7 +1283,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
1260 *bar* 1283 *bar*
1261 1284
1262 ```rust 1285 ```rust
1263 u32 1286 bar: u32
1264 ``` 1287 ```
1265 "#]], 1288 "#]],
1266 ); 1289 );
@@ -1278,7 +1301,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
1278 *bar* 1301 *bar*
1279 1302
1280 ```rust 1303 ```rust
1281 u32 1304 bar: u32
1282 ``` 1305 ```
1283 "#]], 1306 "#]],
1284 ); 1307 );
@@ -2101,7 +2124,7 @@ pub fn fo$0o() {}
2101 2124
2102 #[test] 2125 #[test]
2103 fn test_hover_macro_generated_struct_fn_doc_comment() { 2126 fn test_hover_macro_generated_struct_fn_doc_comment() {
2104 mark::check!(hover_macro_generated_struct_fn_doc_comment); 2127 cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
2105 2128
2106 check( 2129 check(
2107 r#" 2130 r#"
@@ -2139,7 +2162,7 @@ fn foo() { let bar = Bar; bar.fo$0o(); }
2139 2162
2140 #[test] 2163 #[test]
2141 fn test_hover_macro_generated_struct_fn_doc_attr() { 2164 fn test_hover_macro_generated_struct_fn_doc_attr() {
2142 mark::check!(hover_macro_generated_struct_fn_doc_attr); 2165 cov_mark::check!(hover_macro_generated_struct_fn_doc_attr);
2143 2166
2144 check( 2167 check(
2145 r#" 2168 r#"
@@ -3303,7 +3326,7 @@ fn main() {
3303 *f* 3326 *f*
3304 3327
3305 ```rust 3328 ```rust
3306 &i32 3329 f: &i32
3307 ``` 3330 ```
3308 "#]], 3331 "#]],
3309 ); 3332 );
@@ -3322,7 +3345,7 @@ impl Foo {
3322 *self* 3345 *self*
3323 3346
3324 ```rust 3347 ```rust
3325 &Foo 3348 self: &Foo
3326 ``` 3349 ```
3327 "#]], 3350 "#]],
3328 ); 3351 );
@@ -3342,7 +3365,7 @@ impl Foo {
3342 *self* 3365 *self*
3343 3366
3344 ```rust 3367 ```rust
3345 Arc<Foo> 3368 self: Arc<Foo>
3346 ``` 3369 ```
3347 "#]], 3370 "#]],
3348 ); 3371 );
@@ -3538,7 +3561,7 @@ fn foo() {
3538 ``` 3561 ```
3539 3562
3540 ```rust 3563 ```rust
3541 const FOO: usize = 3 3564 const FOO: usize
3542 ``` 3565 ```
3543 3566
3544 --- 3567 ---
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 7fcae13e0..20a920ddb 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -7,7 +7,7 @@ use syntax::{
7 SyntaxKind::{self, USE_TREE, WHITESPACE}, 7 SyntaxKind::{self, USE_TREE, WHITESPACE},
8 SyntaxNode, SyntaxToken, TextRange, TextSize, T, 8 SyntaxNode, SyntaxToken, TextRange, TextSize, T,
9}; 9};
10use test_utils::mark; 10
11use text_edit::{TextEdit, TextEditBuilder}; 11use text_edit::{TextEdit, TextEditBuilder};
12 12
13// Feature: Join Lines 13// Feature: Join Lines
@@ -60,7 +60,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
60 let mut string_open_quote = false; 60 let mut string_open_quote = false;
61 if let Some(string) = ast::String::cast(token.clone()) { 61 if let Some(string) = ast::String::cast(token.clone()) {
62 if let Some(range) = string.open_quote_text_range() { 62 if let Some(range) = string.open_quote_text_range() {
63 mark::hit!(join_string_literal); 63 cov_mark::hit!(join_string_literal);
64 string_open_quote = range.end() == offset; 64 string_open_quote = range.end() == offset;
65 } 65 }
66 } 66 }
@@ -206,7 +206,7 @@ fn compute_ws(left: SyntaxKind, right: SyntaxKind) -> &'static str {
206#[cfg(test)] 206#[cfg(test)]
207mod tests { 207mod tests {
208 use syntax::SourceFile; 208 use syntax::SourceFile;
209 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range, mark}; 209 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
210 210
211 use super::*; 211 use super::*;
212 212
@@ -786,7 +786,7 @@ fn foo() {
786 786
787 #[test] 787 #[test]
788 fn join_string_literal() { 788 fn join_string_literal() {
789 mark::check!(join_string_literal); 789 cov_mark::check!(join_string_literal);
790 check_join_lines( 790 check_join_lines(
791 r#" 791 r#"
792fn main() { 792fn main() {
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index b600178ee..0a493d2f3 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -41,6 +41,7 @@ mod parent_module;
41mod references; 41mod references;
42mod fn_references; 42mod fn_references;
43mod runnables; 43mod runnables;
44mod ssr;
44mod status; 45mod status;
45mod syntax_highlighting; 46mod syntax_highlighting;
46mod syntax_tree; 47mod syntax_tree;
@@ -51,6 +52,7 @@ mod doc_links;
51use std::sync::Arc; 52use std::sync::Arc;
52 53
53use cfg::CfgOptions; 54use cfg::CfgOptions;
55
54use ide_db::base_db::{ 56use ide_db::base_db::{
55 salsa::{self, ParallelDatabase}, 57 salsa::{self, ParallelDatabase},
56 CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath, 58 CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
@@ -85,7 +87,7 @@ pub use crate::{
85pub use hir::{Documentation, Semantics}; 87pub use hir::{Documentation, Semantics};
86pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; 88pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind};
87pub use ide_completion::{ 89pub use ide_completion::{
88 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, 90 CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit,
89 InsertTextFormat, 91 InsertTextFormat,
90}; 92};
91pub use ide_db::{ 93pub use ide_db::{
@@ -478,7 +480,6 @@ impl Analysis {
478 position: FilePosition, 480 position: FilePosition,
479 full_import_path: &str, 481 full_import_path: &str,
480 imported_name: String, 482 imported_name: String,
481 import_for_trait_assoc_item: bool,
482 ) -> Cancelable<Vec<TextEdit>> { 483 ) -> Cancelable<Vec<TextEdit>> {
483 Ok(self 484 Ok(self
484 .with_db(|db| { 485 .with_db(|db| {
@@ -488,7 +489,6 @@ impl Analysis {
488 position, 489 position,
489 full_import_path, 490 full_import_path,
490 imported_name, 491 imported_name,
491 import_for_trait_assoc_item,
492 ) 492 )
493 })? 493 })?
494 .unwrap_or_default()) 494 .unwrap_or_default())
@@ -504,7 +504,11 @@ impl Analysis {
504 resolve: bool, 504 resolve: bool,
505 frange: FileRange, 505 frange: FileRange,
506 ) -> Cancelable<Vec<Assist>> { 506 ) -> Cancelable<Vec<Assist>> {
507 self.with_db(|db| Assist::get(db, config, resolve, frange)) 507 self.with_db(|db| {
508 let mut acc = Assist::get(db, config, resolve, frange);
509 ssr::add_ssr_assist(db, &mut acc, resolve, frange);
510 acc
511 })
508 } 512 }
509 513
510 /// Computes the set of diagnostics for the given file. 514 /// Computes the set of diagnostics for the given file.
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs
index 1bfa1439d..000c412d9 100644
--- a/crates/ide/src/matching_brace.rs
+++ b/crates/ide/src/matching_brace.rs
@@ -2,7 +2,6 @@ use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 SourceFile, SyntaxKind, TextSize, T, 3 SourceFile, SyntaxKind, TextSize, T,
4}; 4};
5use test_utils::mark;
6 5
7// Feature: Matching Brace 6// Feature: Matching Brace
8// 7//
@@ -28,7 +27,7 @@ pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<Text
28 .next()?; 27 .next()?;
29 let parent = brace_token.parent(); 28 let parent = brace_token.parent();
30 if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { 29 if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
31 mark::hit!(pipes_not_braces); 30 cov_mark::hit!(pipes_not_braces);
32 return None; 31 return None;
33 } 32 }
34 let matching_kind = BRACES[brace_idx ^ 1]; 33 let matching_kind = BRACES[brace_idx ^ 1];
@@ -63,7 +62,7 @@ mod tests {
63 do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}"); 62 do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}");
64 63
65 { 64 {
66 mark::check!(pipes_not_braces); 65 cov_mark::check!(pipes_not_braces);
67 do_check( 66 do_check(
68 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", 67 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }",
69 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", 68 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }",
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index ddbaf22b7..03d71b380 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -5,7 +5,6 @@ use syntax::{
5 algo::find_node_at_offset, 5 algo::find_node_at_offset,
6 ast::{self, AstNode}, 6 ast::{self, AstNode},
7}; 7};
8use test_utils::mark;
9 8
10use crate::NavigationTarget; 9use crate::NavigationTarget;
11 10
@@ -33,7 +32,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
33 .item_list() 32 .item_list()
34 .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) 33 .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset))
35 { 34 {
36 mark::hit!(test_resolve_parent_module_on_module_decl); 35 cov_mark::hit!(test_resolve_parent_module_on_module_decl);
37 module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); 36 module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast);
38 } 37 }
39 } 38 }
@@ -64,7 +63,6 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
64#[cfg(test)] 63#[cfg(test)]
65mod tests { 64mod tests {
66 use ide_db::base_db::FileRange; 65 use ide_db::base_db::FileRange;
67 use test_utils::mark;
68 66
69 use crate::fixture; 67 use crate::fixture;
70 68
@@ -92,7 +90,7 @@ $0// empty
92 90
93 #[test] 91 #[test]
94 fn test_resolve_parent_module_on_module_decl() { 92 fn test_resolve_parent_module_on_module_decl() {
95 mark::check!(test_resolve_parent_module_on_module_decl); 93 cov_mark::check!(test_resolve_parent_module_on_module_decl);
96 check( 94 check(
97 r#" 95 r#"
98//- /lib.rs 96//- /lib.rs
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 1919639a3..1e378279d 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -14,14 +14,14 @@ use syntax::{
14 ast::{self, NameOwner}, 14 ast::{self, NameOwner},
15 lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, 15 lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T,
16}; 16};
17use test_utils::mark; 17
18use text_edit::TextEdit; 18use text_edit::TextEdit;
19 19
20use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; 20use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange};
21 21
22type RenameResult<T> = Result<T, RenameError>; 22type RenameResult<T> = Result<T, RenameError>;
23#[derive(Debug)] 23#[derive(Debug)]
24pub struct RenameError(pub(crate) String); 24pub struct RenameError(String);
25 25
26impl fmt::Display for RenameError { 26impl fmt::Display for RenameError {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -47,16 +47,15 @@ pub(crate) fn prepare_rename(
47 let sema = Semantics::new(db); 47 let sema = Semantics::new(db);
48 let source_file = sema.parse(position.file_id); 48 let source_file = sema.parse(position.file_id);
49 let syntax = source_file.syntax(); 49 let syntax = source_file.syntax();
50 let range = match &sema 50 let name_like = sema
51 .find_node_at_offset_with_descend(&syntax, position.offset) 51 .find_node_at_offset_with_descend(&syntax, position.offset)
52 .ok_or_else(|| format_err!("No references found at position"))? 52 .ok_or_else(|| format_err!("No references found at position"))?;
53 { 53 let node = match &name_like {
54 ast::NameLike::Name(it) => it.syntax(), 54 ast::NameLike::Name(it) => it.syntax(),
55 ast::NameLike::NameRef(it) => it.syntax(), 55 ast::NameLike::NameRef(it) => it.syntax(),
56 ast::NameLike::Lifetime(it) => it.syntax(), 56 ast::NameLike::Lifetime(it) => it.syntax(),
57 } 57 };
58 .text_range(); 58 Ok(RangeInfo::new(sema.original_range(node).range, ()))
59 Ok(RangeInfo::new(range, ()))
60} 59}
61 60
62// Feature: Rename 61// Feature: Rename
@@ -94,6 +93,7 @@ pub(crate) fn rename_with_semantics(
94 } 93 }
95} 94}
96 95
96/// Called by the client when it is about to rename a file.
97pub(crate) fn will_rename_file( 97pub(crate) fn will_rename_file(
98 db: &RootDatabase, 98 db: &RootDatabase,
99 file_id: FileId, 99 file_id: FileId,
@@ -226,34 +226,36 @@ fn rename_reference(
226 | (IdentifierKind::Ident, _) 226 | (IdentifierKind::Ident, _)
227 if def_is_lbl_or_lt => 227 if def_is_lbl_or_lt =>
228 { 228 {
229 mark::hit!(rename_not_a_lifetime_ident_ref); 229 cov_mark::hit!(rename_not_a_lifetime_ident_ref);
230 bail!("Invalid name `{}`: not a lifetime identifier", new_name) 230 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
231 } 231 }
232 (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime), 232 (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => cov_mark::hit!(rename_lifetime),
233 (IdentifierKind::Lifetime, _) => { 233 (IdentifierKind::Lifetime, _) => {
234 mark::hit!(rename_not_an_ident_ref); 234 cov_mark::hit!(rename_not_an_ident_ref);
235 bail!("Invalid name `{}`: not an identifier", new_name) 235 bail!("Invalid name `{}`: not an identifier", new_name)
236 } 236 }
237 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { 237 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => {
238 // no-op 238 // no-op
239 mark::hit!(rename_self_to_self); 239 cov_mark::hit!(rename_self_to_self);
240 return Ok(SourceChange::default()); 240 return Ok(SourceChange::default());
241 } 241 }
242 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { 242 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => {
243 mark::hit!(rename_self_to_param); 243 cov_mark::hit!(rename_self_to_param);
244 return rename_self_to_param(sema, local, new_name, ident_kind); 244 return rename_self_to_param(sema, local, new_name, ident_kind);
245 } 245 }
246 (IdentifierKind::ToSelf, Definition::Local(local)) => { 246 (IdentifierKind::ToSelf, Definition::Local(local)) => {
247 mark::hit!(rename_to_self); 247 cov_mark::hit!(rename_to_self);
248 return rename_to_self(sema, local); 248 return rename_to_self(sema, local);
249 } 249 }
250 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), 250 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name),
251 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), 251 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => {
252 cov_mark::hit!(rename_ident)
253 }
252 } 254 }
253 255
254 let usages = def.usages(sema).all(); 256 let usages = def.usages(sema).all();
255 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { 257 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
256 mark::hit!(rename_underscore_multiple); 258 cov_mark::hit!(rename_underscore_multiple);
257 bail!("Cannot rename reference to `_` as it is being referenced multiple times"); 259 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
258 } 260 }
259 let mut source_change = SourceChange::default(); 261 let mut source_change = SourceChange::default();
@@ -444,7 +446,7 @@ fn source_edit_from_name_ref(
444 (Some(field_name), Some(init)) => { 446 (Some(field_name), Some(init)) => {
445 if field_name == *name_ref { 447 if field_name == *name_ref {
446 if init.text() == new_name { 448 if init.text() == new_name {
447 mark::hit!(test_rename_field_put_init_shorthand); 449 cov_mark::hit!(test_rename_field_put_init_shorthand);
448 // same names, we can use a shorthand here instead. 450 // same names, we can use a shorthand here instead.
449 // we do not want to erase attributes hence this range start 451 // we do not want to erase attributes hence this range start
450 let s = field_name.syntax().text_range().start(); 452 let s = field_name.syntax().text_range().start();
@@ -453,7 +455,7 @@ fn source_edit_from_name_ref(
453 } 455 }
454 } else if init == *name_ref { 456 } else if init == *name_ref {
455 if field_name.text() == new_name { 457 if field_name.text() == new_name {
456 mark::hit!(test_rename_local_put_init_shorthand); 458 cov_mark::hit!(test_rename_local_put_init_shorthand);
457 // same names, we can use a shorthand here instead. 459 // same names, we can use a shorthand here instead.
458 // we do not want to erase attributes hence this range start 460 // we do not want to erase attributes hence this range start
459 let s = field_name.syntax().text_range().start(); 461 let s = field_name.syntax().text_range().start();
@@ -467,12 +469,12 @@ fn source_edit_from_name_ref(
467 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the 469 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
468 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 470 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
469 (None, Some(_)) if matches!(def, Definition::Field(_)) => { 471 (None, Some(_)) if matches!(def, Definition::Field(_)) => {
470 mark::hit!(test_rename_field_in_field_shorthand); 472 cov_mark::hit!(test_rename_field_in_field_shorthand);
471 let s = name_ref.syntax().text_range().start(); 473 let s = name_ref.syntax().text_range().start();
472 Some((TextRange::empty(s), format!("{}: ", new_name))) 474 Some((TextRange::empty(s), format!("{}: ", new_name)))
473 } 475 }
474 (None, Some(_)) if matches!(def, Definition::Local(_)) => { 476 (None, Some(_)) if matches!(def, Definition::Local(_)) => {
475 mark::hit!(test_rename_local_in_field_shorthand); 477 cov_mark::hit!(test_rename_local_in_field_shorthand);
476 let s = name_ref.syntax().text_range().end(); 478 let s = name_ref.syntax().text_range().end();
477 Some((TextRange::empty(s), format!(": {}", new_name))) 479 Some((TextRange::empty(s), format!(": {}", new_name)))
478 } 480 }
@@ -486,7 +488,7 @@ fn source_edit_from_name_ref(
486 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { 488 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => {
487 // field name is being renamed 489 // field name is being renamed
488 if pat.name().map_or(false, |it| it.text() == new_name) { 490 if pat.name().map_or(false, |it| it.text() == new_name) {
489 mark::hit!(test_rename_field_put_init_shorthand_pat); 491 cov_mark::hit!(test_rename_field_put_init_shorthand_pat);
490 // same names, we can use a shorthand here instead/ 492 // same names, we can use a shorthand here instead/
491 // we do not want to erase attributes hence this range start 493 // we do not want to erase attributes hence this range start
492 let s = field_name.syntax().text_range().start(); 494 let s = field_name.syntax().text_range().start();
@@ -538,11 +540,13 @@ fn source_edit_from_def(
538mod tests { 540mod tests {
539 use expect_test::{expect, Expect}; 541 use expect_test::{expect, Expect};
540 use stdx::trim_indent; 542 use stdx::trim_indent;
541 use test_utils::{assert_eq_text, mark}; 543 use test_utils::assert_eq_text;
542 use text_edit::TextEdit; 544 use text_edit::TextEdit;
543 545
544 use crate::{fixture, FileId}; 546 use crate::{fixture, FileId};
545 547
548 use super::{RangeInfo, RenameError};
549
546 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 550 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
547 let ra_fixture_after = &trim_indent(ra_fixture_after); 551 let ra_fixture_after = &trim_indent(ra_fixture_after);
548 let (analysis, position) = fixture::position(ra_fixture_before); 552 let (analysis, position) = fixture::position(ra_fixture_before);
@@ -588,6 +592,45 @@ mod tests {
588 expect.assert_debug_eq(&source_change) 592 expect.assert_debug_eq(&source_change)
589 } 593 }
590 594
595 fn check_prepare(ra_fixture: &str, expect: Expect) {
596 let (analysis, position) = fixture::position(ra_fixture);
597 let result = analysis
598 .prepare_rename(position)
599 .unwrap_or_else(|err| panic!("PrepareRename was cancelled: {}", err));
600 match result {
601 Ok(RangeInfo { range, info: () }) => {
602 let source = analysis.file_text(position.file_id).unwrap();
603 expect.assert_eq(&format!("{:?}: {}", range, &source[range]))
604 }
605 Err(RenameError(err)) => expect.assert_eq(&err),
606 };
607 }
608
609 #[test]
610 fn test_prepare_rename_namelikes() {
611 check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]);
612 check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]);
613 check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]);
614 }
615
616 #[test]
617 fn test_prepare_rename_in_macro() {
618 check_prepare(
619 r"macro_rules! foo {
620 ($ident:ident) => {
621 pub struct $ident;
622 }
623}
624foo!(Foo$0);",
625 expect![[r#"83..86: Foo"#]],
626 );
627 }
628
629 #[test]
630 fn test_prepare_rename_keyword() {
631 check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]);
632 }
633
591 #[test] 634 #[test]
592 fn test_rename_to_underscore() { 635 fn test_rename_to_underscore() {
593 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); 636 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
@@ -627,7 +670,7 @@ mod tests {
627 670
628 #[test] 671 #[test]
629 fn test_rename_to_invalid_identifier_lifetime() { 672 fn test_rename_to_invalid_identifier_lifetime() {
630 mark::check!(rename_not_an_ident_ref); 673 cov_mark::check!(rename_not_an_ident_ref);
631 check( 674 check(
632 "'foo", 675 "'foo",
633 r#"fn main() { let i$0 = 1; }"#, 676 r#"fn main() { let i$0 = 1; }"#,
@@ -637,7 +680,7 @@ mod tests {
637 680
638 #[test] 681 #[test]
639 fn test_rename_to_invalid_identifier_lifetime2() { 682 fn test_rename_to_invalid_identifier_lifetime2() {
640 mark::check!(rename_not_a_lifetime_ident_ref); 683 cov_mark::check!(rename_not_a_lifetime_ident_ref);
641 check( 684 check(
642 "foo", 685 "foo",
643 r#"fn main<'a>(_: &'a$0 ()) {}"#, 686 r#"fn main<'a>(_: &'a$0 ()) {}"#,
@@ -647,7 +690,7 @@ mod tests {
647 690
648 #[test] 691 #[test]
649 fn test_rename_to_underscore_invalid() { 692 fn test_rename_to_underscore_invalid() {
650 mark::check!(rename_underscore_multiple); 693 cov_mark::check!(rename_underscore_multiple);
651 check( 694 check(
652 "_", 695 "_",
653 r#"fn main(foo$0: ()) {foo;}"#, 696 r#"fn main(foo$0: ()) {foo;}"#,
@@ -666,7 +709,7 @@ mod tests {
666 709
667 #[test] 710 #[test]
668 fn test_rename_for_local() { 711 fn test_rename_for_local() {
669 mark::check!(rename_ident); 712 cov_mark::check!(rename_ident);
670 check( 713 check(
671 "k", 714 "k",
672 r#" 715 r#"
@@ -829,7 +872,7 @@ impl Foo {
829 872
830 #[test] 873 #[test]
831 fn test_rename_field_in_field_shorthand() { 874 fn test_rename_field_in_field_shorthand() {
832 mark::check!(test_rename_field_in_field_shorthand); 875 cov_mark::check!(test_rename_field_in_field_shorthand);
833 check( 876 check(
834 "j", 877 "j",
835 r#" 878 r#"
@@ -855,7 +898,7 @@ impl Foo {
855 898
856 #[test] 899 #[test]
857 fn test_rename_local_in_field_shorthand() { 900 fn test_rename_local_in_field_shorthand() {
858 mark::check!(test_rename_local_in_field_shorthand); 901 cov_mark::check!(test_rename_local_in_field_shorthand);
859 check( 902 check(
860 "j", 903 "j",
861 r#" 904 r#"
@@ -1261,7 +1304,7 @@ fn foo(f: foo::Foo) {
1261 1304
1262 #[test] 1305 #[test]
1263 fn test_parameter_to_self() { 1306 fn test_parameter_to_self() {
1264 mark::check!(rename_to_self); 1307 cov_mark::check!(rename_to_self);
1265 check( 1308 check(
1266 "self", 1309 "self",
1267 r#" 1310 r#"
@@ -1401,7 +1444,7 @@ impl Foo {
1401 1444
1402 #[test] 1445 #[test]
1403 fn test_owned_self_to_parameter() { 1446 fn test_owned_self_to_parameter() {
1404 mark::check!(rename_self_to_param); 1447 cov_mark::check!(rename_self_to_param);
1405 check( 1448 check(
1406 "foo", 1449 "foo",
1407 r#" 1450 r#"
@@ -1454,7 +1497,7 @@ impl Foo {
1454 1497
1455 #[test] 1498 #[test]
1456 fn test_rename_field_put_init_shorthand() { 1499 fn test_rename_field_put_init_shorthand() {
1457 mark::check!(test_rename_field_put_init_shorthand); 1500 cov_mark::check!(test_rename_field_put_init_shorthand);
1458 check( 1501 check(
1459 "bar", 1502 "bar",
1460 r#" 1503 r#"
@@ -1476,7 +1519,7 @@ fn foo(bar: i32) -> Foo {
1476 1519
1477 #[test] 1520 #[test]
1478 fn test_rename_local_put_init_shorthand() { 1521 fn test_rename_local_put_init_shorthand() {
1479 mark::check!(test_rename_local_put_init_shorthand); 1522 cov_mark::check!(test_rename_local_put_init_shorthand);
1480 check( 1523 check(
1481 "i", 1524 "i",
1482 r#" 1525 r#"
@@ -1498,7 +1541,7 @@ fn foo(i: i32) -> Foo {
1498 1541
1499 #[test] 1542 #[test]
1500 fn test_struct_field_pat_into_shorthand() { 1543 fn test_struct_field_pat_into_shorthand() {
1501 mark::check!(test_rename_field_put_init_shorthand_pat); 1544 cov_mark::check!(test_rename_field_put_init_shorthand_pat);
1502 check( 1545 check(
1503 "baz", 1546 "baz",
1504 r#" 1547 r#"
@@ -1610,7 +1653,7 @@ fn foo(foo: Foo) {
1610 1653
1611 #[test] 1654 #[test]
1612 fn test_rename_lifetimes() { 1655 fn test_rename_lifetimes() {
1613 mark::check!(rename_lifetime); 1656 cov_mark::check!(rename_lifetime);
1614 check( 1657 check(
1615 "'yeeee", 1658 "'yeeee",
1616 r#" 1659 r#"
@@ -1698,7 +1741,7 @@ fn foo<'a>() -> &'a () {
1698 1741
1699 #[test] 1742 #[test]
1700 fn test_self_to_self() { 1743 fn test_self_to_self() {
1701 mark::check!(rename_self_to_self); 1744 cov_mark::check!(rename_self_to_self);
1702 check( 1745 check(
1703 "self", 1746 "self",
1704 r#" 1747 r#"
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 65f60891e..280565563 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -9,7 +9,6 @@ use syntax::{
9 ast::{self, AstNode, AttrsOwner}, 9 ast::{self, AstNode, AttrsOwner},
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12use test_utils::mark;
13 12
14use crate::{ 13use crate::{
15 display::{ToNav, TryToNav}, 14 display::{ToNav, TryToNav},
@@ -130,7 +129,9 @@ fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module
130 if let hir::ModuleDef::Module(submodule) = def { 129 if let hir::ModuleDef::Module(submodule) = def {
131 match submodule.definition_source(sema.db).value { 130 match submodule.definition_source(sema.db).value {
132 hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule), 131 hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule),
133 hir::ModuleSource::SourceFile(_) => mark::hit!(dont_recurse_in_outline_submodules), 132 hir::ModuleSource::SourceFile(_) => {
133 cov_mark::hit!(dont_recurse_in_outline_submodules)
134 }
134 hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable 135 hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable
135 } 136 }
136 } 137 }
@@ -328,7 +329,6 @@ fn has_test_function_or_multiple_test_submodules(
328#[cfg(test)] 329#[cfg(test)]
329mod tests { 330mod tests {
330 use expect_test::{expect, Expect}; 331 use expect_test::{expect, Expect};
331 use test_utils::mark;
332 332
333 use crate::fixture; 333 use crate::fixture;
334 334
@@ -1056,7 +1056,7 @@ mod tests {
1056 1056
1057 #[test] 1057 #[test]
1058 fn dont_recurse_in_outline_submodules() { 1058 fn dont_recurse_in_outline_submodules() {
1059 mark::check!(dont_recurse_in_outline_submodules); 1059 cov_mark::check!(dont_recurse_in_outline_submodules);
1060 check( 1060 check(
1061 r#" 1061 r#"
1062//- /lib.rs 1062//- /lib.rs
diff --git a/crates/ide/src/ssr.rs b/crates/ide/src/ssr.rs
new file mode 100644
index 000000000..f3638d928
--- /dev/null
+++ b/crates/ide/src/ssr.rs
@@ -0,0 +1,259 @@
1//! This module provides an SSR assist. It is not desirable to include this
2//! assist in ide_assists because that would require the ide_assists crate
3//! depend on the ide_ssr crate.
4
5use ide_assists::{Assist, AssistId, AssistKind, GroupLabel};
6use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase};
7
8pub(crate) fn add_ssr_assist(
9 db: &RootDatabase,
10 base: &mut Vec<Assist>,
11 resolve: bool,
12 frange: FileRange,
13) -> Option<()> {
14 let (match_finder, comment_range) = ide_ssr::ssr_from_comment(db, frange)?;
15
16 let (source_change_for_file, source_change_for_workspace) = if resolve {
17 let edits = match_finder.edits();
18
19 let source_change_for_file = {
20 let text_edit_for_file = edits.get(&frange.file_id).cloned().unwrap_or_default();
21 SourceChange::from_text_edit(frange.file_id, text_edit_for_file)
22 };
23
24 let source_change_for_workspace = SourceChange::from(match_finder.edits());
25
26 (Some(source_change_for_file), Some(source_change_for_workspace))
27 } else {
28 (None, None)
29 };
30
31 let assists = vec![
32 ("Apply SSR in file", source_change_for_file),
33 ("Apply SSR in workspace", source_change_for_workspace),
34 ];
35
36 for (label, source_change) in assists.into_iter() {
37 let assist = Assist {
38 id: AssistId("ssr", AssistKind::RefactorRewrite),
39 label: Label::new(label),
40 group: Some(GroupLabel("Apply SSR".into())),
41 target: comment_range,
42 source_change,
43 };
44
45 base.push(assist);
46 }
47 Some(())
48}
49
50#[cfg(test)]
51mod tests {
52 use std::sync::Arc;
53
54 use expect_test::expect;
55 use ide_assists::Assist;
56 use ide_db::{
57 base_db::{fixture::WithFixture, salsa::Durability, FileRange},
58 symbol_index::SymbolsDatabase,
59 RootDatabase,
60 };
61 use rustc_hash::FxHashSet;
62
63 use super::add_ssr_assist;
64
65 fn get_assists(ra_fixture: &str, resolve: bool) -> Vec<Assist> {
66 let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
67 let mut local_roots = FxHashSet::default();
68 local_roots.insert(ide_db::base_db::fixture::WORKSPACE);
69 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
70
71 let mut assists = vec![];
72
73 add_ssr_assist(
74 &db,
75 &mut assists,
76 resolve,
77 FileRange { file_id, range: range_or_offset.into() },
78 );
79
80 assists
81 }
82
83 #[test]
84 fn not_applicable_comment_not_ssr() {
85 let ra_fixture = r#"
86 //- /lib.rs
87
88 // This is foo $0
89 fn foo() {}
90 "#;
91 let resolve = true;
92
93 let assists = get_assists(ra_fixture, resolve);
94
95 assert_eq!(0, assists.len());
96 }
97
98 #[test]
99 fn resolve_edits_true() {
100 let resolve = true;
101 let assists = get_assists(
102 r#"
103 //- /lib.rs
104 mod bar;
105
106 // 2 ==>> 3$0
107 fn foo() { 2 }
108
109 //- /bar.rs
110 fn bar() { 2 }
111 "#,
112 resolve,
113 );
114
115 assert_eq!(2, assists.len());
116 let mut assists = assists.into_iter();
117
118 let apply_in_file_assist = assists.next().unwrap();
119 expect![[r#"
120 Assist {
121 id: AssistId(
122 "ssr",
123 RefactorRewrite,
124 ),
125 label: "Apply SSR in file",
126 group: Some(
127 GroupLabel(
128 "Apply SSR",
129 ),
130 ),
131 target: 10..21,
132 source_change: Some(
133 SourceChange {
134 source_file_edits: {
135 FileId(
136 0,
137 ): TextEdit {
138 indels: [
139 Indel {
140 insert: "3",
141 delete: 33..34,
142 },
143 ],
144 },
145 },
146 file_system_edits: [],
147 is_snippet: false,
148 },
149 ),
150 }
151 "#]]
152 .assert_debug_eq(&apply_in_file_assist);
153
154 let apply_in_workspace_assist = assists.next().unwrap();
155 expect![[r#"
156 Assist {
157 id: AssistId(
158 "ssr",
159 RefactorRewrite,
160 ),
161 label: "Apply SSR in workspace",
162 group: Some(
163 GroupLabel(
164 "Apply SSR",
165 ),
166 ),
167 target: 10..21,
168 source_change: Some(
169 SourceChange {
170 source_file_edits: {
171 FileId(
172 0,
173 ): TextEdit {
174 indels: [
175 Indel {
176 insert: "3",
177 delete: 33..34,
178 },
179 ],
180 },
181 FileId(
182 1,
183 ): TextEdit {
184 indels: [
185 Indel {
186 insert: "3",
187 delete: 11..12,
188 },
189 ],
190 },
191 },
192 file_system_edits: [],
193 is_snippet: false,
194 },
195 ),
196 }
197 "#]]
198 .assert_debug_eq(&apply_in_workspace_assist);
199 }
200
201 #[test]
202 fn resolve_edits_false() {
203 let resolve = false;
204 let assists = get_assists(
205 r#"
206 //- /lib.rs
207 mod bar;
208
209 // 2 ==>> 3$0
210 fn foo() { 2 }
211
212 //- /bar.rs
213 fn bar() { 2 }
214 "#,
215 resolve,
216 );
217
218 assert_eq!(2, assists.len());
219 let mut assists = assists.into_iter();
220
221 let apply_in_file_assist = assists.next().unwrap();
222 expect![[r#"
223 Assist {
224 id: AssistId(
225 "ssr",
226 RefactorRewrite,
227 ),
228 label: "Apply SSR in file",
229 group: Some(
230 GroupLabel(
231 "Apply SSR",
232 ),
233 ),
234 target: 10..21,
235 source_change: None,
236 }
237 "#]]
238 .assert_debug_eq(&apply_in_file_assist);
239
240 let apply_in_workspace_assist = assists.next().unwrap();
241 expect![[r#"
242 Assist {
243 id: AssistId(
244 "ssr",
245 RefactorRewrite,
246 ),
247 label: "Apply SSR in workspace",
248 group: Some(
249 GroupLabel(
250 "Apply SSR",
251 ),
252 ),
253 target: 10..21,
254 source_change: None,
255 }
256 "#]]
257 .assert_debug_eq(&apply_in_workspace_assist);
258 }
259}
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 24fcbb584..b0cfdd8b7 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -330,10 +330,11 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
330 HlTag::Symbol(SymbolKind::Local) 330 HlTag::Symbol(SymbolKind::Local)
331 }; 331 };
332 let mut h = Highlight::new(tag); 332 let mut h = Highlight::new(tag);
333 if local.is_mut(db) || local.ty(db).is_mutable_reference() { 333 let ty = local.ty(db);
334 if local.is_mut(db) || ty.is_mutable_reference() {
334 h |= HlMod::Mutable; 335 h |= HlMod::Mutable;
335 } 336 }
336 if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) { 337 if ty.as_callable(db).is_some() || ty.impls_fnonce(db) {
337 h |= HlMod::Callable; 338 h |= HlMod::Callable;
338 } 339 }
339 return h; 340 return h;
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 63cd51b69..978c479de 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -9,7 +9,7 @@ use syntax::{
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxToken, TextRange, TextSize, TokenAtOffset, 10 SyntaxToken, TextRange, TextSize, TokenAtOffset,
11}; 11};
12use test_utils::mark; 12
13use text_edit::TextEdit; 13use text_edit::TextEdit;
14 14
15// Feature: On Enter 15// Feature: On Enter
@@ -55,7 +55,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
55 // Continuing single-line non-doc comments (like this one :) ) is annoying 55 // Continuing single-line non-doc comments (like this one :) ) is annoying
56 if prefix == "//" && comment_range.end() == position.offset { 56 if prefix == "//" && comment_range.end() == position.offset {
57 if comment.text().ends_with(' ') { 57 if comment.text().ends_with(' ') {
58 mark::hit!(continues_end_of_line_comment_with_space); 58 cov_mark::hit!(continues_end_of_line_comment_with_space);
59 remove_trailing_whitespace = true; 59 remove_trailing_whitespace = true;
60 } else if !followed_by_comment(&comment) { 60 } else if !followed_by_comment(&comment) {
61 return None; 61 return None;
@@ -109,7 +109,7 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
109#[cfg(test)] 109#[cfg(test)]
110mod tests { 110mod tests {
111 use stdx::trim_indent; 111 use stdx::trim_indent;
112 use test_utils::{assert_eq_text, mark}; 112 use test_utils::assert_eq_text;
113 113
114 use crate::fixture; 114 use crate::fixture;
115 115
@@ -238,7 +238,7 @@ fn main() {
238 238
239 #[test] 239 #[test]
240 fn continues_end_of_line_comment_with_space() { 240 fn continues_end_of_line_comment_with_space() {
241 mark::check!(continues_end_of_line_comment_with_space); 241 cov_mark::check!(continues_end_of_line_comment_with_space);
242 do_check( 242 do_check(
243 r#" 243 r#"
244fn main() { 244fn main() {
diff --git a/crates/ide_assists/Cargo.toml b/crates/ide_assists/Cargo.toml
index a34bdd6c3..dd9aa27c6 100644
--- a/crates/ide_assists/Cargo.toml
+++ b/crates/ide_assists/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
14itertools = "0.10.0" 15itertools = "0.10.0"
15either = "1.6.1" 16either = "1.6.1"
@@ -20,7 +21,7 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
20profile = { path = "../profile", version = "0.0.0" } 21profile = { path = "../profile", version = "0.0.0" }
21ide_db = { path = "../ide_db", version = "0.0.0" } 22ide_db = { path = "../ide_db", version = "0.0.0" }
22hir = { path = "../hir", version = "0.0.0" } 23hir = { path = "../hir", version = "0.0.0" }
23test_utils = { path = "../test_utils", version = "0.0.0" }
24 24
25[dev-dependencies] 25[dev-dependencies]
26test_utils = { path = "../test_utils" }
26expect-test = "1.1" 27expect-test = "1.1"
diff --git a/crates/ide_assists/src/handlers/add_turbo_fish.rs b/crates/ide_assists/src/handlers/add_turbo_fish.rs
index a08b55ebb..ee879c151 100644
--- a/crates/ide_assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ide_assists/src/handlers/add_turbo_fish.rs
@@ -1,6 +1,5 @@
1use ide_db::defs::{Definition, NameRefClass}; 1use ide_db::defs::{Definition, NameRefClass};
2use syntax::{ast, AstNode, SyntaxKind, T}; 2use syntax::{ast, AstNode, SyntaxKind, T};
3use test_utils::mark;
4 3
5use crate::{ 4use crate::{
6 assist_context::{AssistContext, Assists}, 5 assist_context::{AssistContext, Assists},
@@ -30,13 +29,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
30 if arg_list.args().count() > 0 { 29 if arg_list.args().count() > 0 {
31 return None; 30 return None;
32 } 31 }
33 mark::hit!(add_turbo_fish_after_call); 32 cov_mark::hit!(add_turbo_fish_after_call);
34 mark::hit!(add_type_ascription_after_call); 33 cov_mark::hit!(add_type_ascription_after_call);
35 arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) 34 arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT)
36 })?; 35 })?;
37 let next_token = ident.next_token()?; 36 let next_token = ident.next_token()?;
38 if next_token.kind() == T![::] { 37 if next_token.kind() == T![::] {
39 mark::hit!(add_turbo_fish_one_fish_is_enough); 38 cov_mark::hit!(add_turbo_fish_one_fish_is_enough);
40 return None; 39 return None;
41 } 40 }
42 let name_ref = ast::NameRef::cast(ident.parent())?; 41 let name_ref = ast::NameRef::cast(ident.parent())?;
@@ -50,24 +49,31 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
50 }; 49 };
51 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); 50 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
52 if generics.is_empty() { 51 if generics.is_empty() {
53 mark::hit!(add_turbo_fish_non_generic); 52 cov_mark::hit!(add_turbo_fish_non_generic);
54 return None; 53 return None;
55 } 54 }
56 55
57 if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() { 56 if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() {
58 if let_stmt.colon_token().is_none() { 57 if let_stmt.colon_token().is_none() {
59 let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end(); 58 let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end();
59 let semi_pos = let_stmt.syntax().last_token()?.text_range().end();
60
60 acc.add( 61 acc.add(
61 AssistId("add_type_ascription", AssistKind::RefactorRewrite), 62 AssistId("add_type_ascription", AssistKind::RefactorRewrite),
62 "Add `: _` before assignment operator", 63 "Add `: _` before assignment operator",
63 ident.text_range(), 64 ident.text_range(),
64 |builder| match ctx.config.snippet_cap { 65 |builder| {
65 Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"), 66 if let_stmt.semicolon_token().is_none() {
66 None => builder.insert(type_pos, ": _"), 67 builder.insert(semi_pos, ";");
68 }
69 match ctx.config.snippet_cap {
70 Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"),
71 None => builder.insert(type_pos, ": _"),
72 }
67 }, 73 },
68 )? 74 )?
69 } else { 75 } else {
70 mark::hit!(add_type_ascription_already_typed); 76 cov_mark::hit!(add_type_ascription_already_typed);
71 } 77 }
72 } 78 }
73 79
@@ -87,7 +93,6 @@ mod tests {
87 use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; 93 use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable};
88 94
89 use super::*; 95 use super::*;
90 use test_utils::mark;
91 96
92 #[test] 97 #[test]
93 fn add_turbo_fish_function() { 98 fn add_turbo_fish_function() {
@@ -110,7 +115,7 @@ fn main() {
110 115
111 #[test] 116 #[test]
112 fn add_turbo_fish_after_call() { 117 fn add_turbo_fish_after_call() {
113 mark::check!(add_turbo_fish_after_call); 118 cov_mark::check!(add_turbo_fish_after_call);
114 check_assist( 119 check_assist(
115 add_turbo_fish, 120 add_turbo_fish,
116 r#" 121 r#"
@@ -155,7 +160,7 @@ fn main() {
155 160
156 #[test] 161 #[test]
157 fn add_turbo_fish_one_fish_is_enough() { 162 fn add_turbo_fish_one_fish_is_enough() {
158 mark::check!(add_turbo_fish_one_fish_is_enough); 163 cov_mark::check!(add_turbo_fish_one_fish_is_enough);
159 check_assist_not_applicable( 164 check_assist_not_applicable(
160 add_turbo_fish, 165 add_turbo_fish,
161 r#" 166 r#"
@@ -169,7 +174,7 @@ fn main() {
169 174
170 #[test] 175 #[test]
171 fn add_turbo_fish_non_generic() { 176 fn add_turbo_fish_non_generic() {
172 mark::check!(add_turbo_fish_non_generic); 177 cov_mark::check!(add_turbo_fish_non_generic);
173 check_assist_not_applicable( 178 check_assist_not_applicable(
174 add_turbo_fish, 179 add_turbo_fish,
175 r#" 180 r#"
@@ -203,7 +208,7 @@ fn main() {
203 208
204 #[test] 209 #[test]
205 fn add_type_ascription_after_call() { 210 fn add_type_ascription_after_call() {
206 mark::check!(add_type_ascription_after_call); 211 cov_mark::check!(add_type_ascription_after_call);
207 check_assist_by_label( 212 check_assist_by_label(
208 add_turbo_fish, 213 add_turbo_fish,
209 r#" 214 r#"
@@ -250,7 +255,7 @@ fn main() {
250 255
251 #[test] 256 #[test]
252 fn add_type_ascription_already_typed() { 257 fn add_type_ascription_already_typed() {
253 mark::check!(add_type_ascription_already_typed); 258 cov_mark::check!(add_type_ascription_already_typed);
254 check_assist( 259 check_assist(
255 add_turbo_fish, 260 add_turbo_fish,
256 r#" 261 r#"
@@ -267,4 +272,24 @@ fn main() {
267"#, 272"#,
268 ); 273 );
269 } 274 }
275
276 #[test]
277 fn add_type_ascription_append_semicolon() {
278 check_assist_by_label(
279 add_turbo_fish,
280 r#"
281fn make<T>() -> T {}
282fn main() {
283 let x = make$0()
284}
285"#,
286 r#"
287fn make<T>() -> T {}
288fn main() {
289 let x: ${0:_} = make();
290}
291"#,
292 "Add `: _` before assignment operator",
293 );
294 }
270} 295}
diff --git a/crates/ide_assists/src/handlers/apply_demorgan.rs b/crates/ide_assists/src/handlers/apply_demorgan.rs
index 128b1eb56..a1c339603 100644
--- a/crates/ide_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide_assists/src/handlers/apply_demorgan.rs
@@ -1,5 +1,4 @@
1use syntax::ast::{self, AstNode}; 1use syntax::ast::{self, AstNode};
2use test_utils::mark;
3 2
4use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; 3use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
5 4
@@ -64,10 +63,10 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
64 edit.replace(lhs_range, not_lhs.syntax().text()); 63 edit.replace(lhs_range, not_lhs.syntax().text());
65 edit.replace(rhs_range, not_rhs.syntax().text()); 64 edit.replace(rhs_range, not_rhs.syntax().text());
66 if let Some(neg_expr) = neg_expr { 65 if let Some(neg_expr) = neg_expr {
67 mark::hit!(demorgan_double_negation); 66 cov_mark::hit!(demorgan_double_negation);
68 edit.replace(neg_expr.op_token().unwrap().text_range(), ""); 67 edit.replace(neg_expr.op_token().unwrap().text_range(), "");
69 } else { 68 } else {
70 mark::hit!(demorgan_double_parens); 69 cov_mark::hit!(demorgan_double_parens);
71 edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!("); 70 edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!(");
72 } 71 }
73 } else { 72 } else {
@@ -90,7 +89,6 @@ fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> {
90#[cfg(test)] 89#[cfg(test)]
91mod tests { 90mod tests {
92 use ide_db::helpers::FamousDefs; 91 use ide_db::helpers::FamousDefs;
93 use test_utils::mark;
94 92
95 use super::*; 93 use super::*;
96 94
@@ -188,13 +186,13 @@ fn f() {
188 186
189 #[test] 187 #[test]
190 fn demorgan_doesnt_double_negation() { 188 fn demorgan_doesnt_double_negation() {
191 mark::check!(demorgan_double_negation); 189 cov_mark::check!(demorgan_double_negation);
192 check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }") 190 check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }")
193 } 191 }
194 192
195 #[test] 193 #[test]
196 fn demorgan_doesnt_double_parens() { 194 fn demorgan_doesnt_double_parens() {
197 mark::check!(demorgan_double_parens); 195 cov_mark::check!(demorgan_double_parens);
198 check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") 196 check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
199 } 197 }
200} 198}
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index dc38f90e9..7019039b9 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -90,17 +90,17 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
90 } 90 }
91 91
92 let range = ctx.sema.original_range(&syntax_under_caret).range; 92 let range = ctx.sema.original_range(&syntax_under_caret).range;
93 let group = import_group_message(import_assets.import_candidate()); 93 let group_label = group_label(import_assets.import_candidate());
94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?; 94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?;
95 for (import, _) in proposed_imports { 95 for import in proposed_imports {
96 acc.add_group( 96 acc.add_group(
97 &group, 97 &group_label,
98 AssistId("auto_import", AssistKind::QuickFix), 98 AssistId("auto_import", AssistKind::QuickFix),
99 format!("Import `{}`", &import), 99 format!("Import `{}`", import.import_path),
100 range, 100 range,
101 |builder| { 101 |builder| {
102 let rewriter = 102 let rewriter =
103 insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge); 103 insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use);
104 builder.rewrite(rewriter); 104 builder.rewrite(rewriter);
105 }, 105 },
106 ); 106 );
@@ -122,14 +122,14 @@ pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets,
122 } 122 }
123} 123}
124 124
125fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { 125fn group_label(import_candidate: &ImportCandidate) -> GroupLabel {
126 let name = match import_candidate { 126 let name = match import_candidate {
127 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), 127 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
128 ImportCandidate::TraitAssocItem(candidate) => { 128 ImportCandidate::TraitAssocItem(candidate) => {
129 format!("Import a trait for item {}", candidate.name.text()) 129 format!("Import a trait for item {}", candidate.assoc_item_name.text())
130 } 130 }
131 ImportCandidate::TraitMethod(candidate) => { 131 ImportCandidate::TraitMethod(candidate) => {
132 format!("Import a trait for method {}", candidate.name.text()) 132 format!("Import a trait for method {}", candidate.assoc_item_name.text())
133 } 133 }
134 }; 134 };
135 GroupLabel(name) 135 GroupLabel(name)
@@ -222,41 +222,6 @@ mod tests {
222 } 222 }
223 223
224 #[test] 224 #[test]
225 fn auto_imports_are_merged() {
226 check_assist(
227 auto_import,
228 r"
229 use PubMod::PubStruct1;
230
231 struct Test {
232 test: Pub$0Struct2<u8>,
233 }
234
235 pub mod PubMod {
236 pub struct PubStruct1;
237 pub struct PubStruct2<T> {
238 _t: T,
239 }
240 }
241 ",
242 r"
243 use PubMod::{PubStruct1, PubStruct2};
244
245 struct Test {
246 test: PubStruct2<u8>,
247 }
248
249 pub mod PubMod {
250 pub struct PubStruct1;
251 pub struct PubStruct2<T> {
252 _t: T,
253 }
254 }
255 ",
256 );
257 }
258
259 #[test]
260 fn applicable_when_found_multiple_imports() { 225 fn applicable_when_found_multiple_imports() {
261 check_assist( 226 check_assist(
262 auto_import, 227 auto_import,
diff --git a/crates/ide_assists/src/handlers/change_visibility.rs b/crates/ide_assists/src/handlers/change_visibility.rs
index ac8c44124..ec99a5505 100644
--- a/crates/ide_assists/src/handlers/change_visibility.rs
+++ b/crates/ide_assists/src/handlers/change_visibility.rs
@@ -4,7 +4,6 @@ use syntax::{
4 SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY}, 4 SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY},
5 T, 5 T,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; 8use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
10 9
@@ -56,7 +55,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
56 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { 55 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() {
57 let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?; 56 let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?;
58 if field.name()? != field_name { 57 if field.name()? != field_name {
59 mark::hit!(change_visibility_field_false_positive); 58 cov_mark::hit!(change_visibility_field_false_positive);
60 return None; 59 return None;
61 } 60 }
62 if field.visibility().is_some() { 61 if field.visibility().is_some() {
@@ -110,8 +109,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
110 109
111#[cfg(test)] 110#[cfg(test)]
112mod tests { 111mod tests {
113 use test_utils::mark;
114
115 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 112 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
116 113
117 use super::*; 114 use super::*;
@@ -139,7 +136,7 @@ mod tests {
139 136
140 #[test] 137 #[test]
141 fn change_visibility_field_false_positive() { 138 fn change_visibility_field_false_positive() {
142 mark::check!(change_visibility_field_false_positive); 139 cov_mark::check!(change_visibility_field_false_positive);
143 check_assist_not_applicable( 140 check_assist_not_applicable(
144 change_visibility, 141 change_visibility,
145 r"struct S { field: [(); { let $0x = ();}] }", 142 r"struct S { field: [(); { let $0x = ();}] }",
diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
new file mode 100644
index 000000000..4e75a7b14
--- /dev/null
+++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -0,0 +1,248 @@
1use ide_db::helpers::FamousDefs;
2use syntax::{
3 ast::{self, edit::AstNodeEdit, make, ArgListOwner},
4 AstNode,
5};
6
7use crate::{AssistContext, AssistId, AssistKind, Assists};
8
9// Assist: convert_iter_for_each_to_for
10//
11// Converts an Iterator::for_each function into a for loop.
12//
13// ```
14// # //- /lib.rs crate:core
15// # pub mod iter { pub mod traits { pub mod iterator { pub trait Iterator {} } } }
16// # pub struct SomeIter;
17// # impl self::iter::traits::iterator::Iterator for SomeIter {}
18// # //- /lib.rs crate:main deps:core
19// # use core::SomeIter;
20// fn main() {
21// let iter = SomeIter;
22// iter.for_each$0(|(x, y)| {
23// println!("x: {}, y: {}", x, y);
24// });
25// }
26// ```
27// ->
28// ```
29// # use core::SomeIter;
30// fn main() {
31// let iter = SomeIter;
32// for (x, y) in iter {
33// println!("x: {}, y: {}", x, y);
34// }
35// }
36// ```
37
38pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 let method = ctx.find_node_at_offset::<ast::MethodCallExpr>()?;
40
41 let closure = match method.arg_list()?.args().next()? {
42 ast::Expr::ClosureExpr(expr) => expr,
43 _ => return None,
44 };
45
46 let (method, receiver) = validate_method_call_expr(ctx, method)?;
47
48 let param_list = closure.param_list()?;
49 let param = param_list.params().next()?.pat()?;
50 let body = closure.body()?;
51
52 let stmt = method.syntax().parent().and_then(ast::ExprStmt::cast);
53 let syntax = stmt.as_ref().map_or(method.syntax(), |stmt| stmt.syntax());
54
55 acc.add(
56 AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
57 "Replace this `Iterator::for_each` with a for loop",
58 syntax.text_range(),
59 |builder| {
60 let indent = stmt.as_ref().map_or(method.indent_level(), |stmt| stmt.indent_level());
61
62 let block = match body {
63 ast::Expr::BlockExpr(block) => block,
64 _ => make::block_expr(Vec::new(), Some(body)),
65 }
66 .reset_indent()
67 .indent(indent);
68
69 let expr_for_loop = make::expr_for_loop(param, receiver, block);
70 builder.replace(syntax.text_range(), expr_for_loop.syntax().text())
71 },
72 )
73}
74
75fn validate_method_call_expr(
76 ctx: &AssistContext,
77 expr: ast::MethodCallExpr,
78) -> Option<(ast::Expr, ast::Expr)> {
79 let name_ref = expr.name_ref()?;
80 if name_ref.syntax().text_range().intersect(ctx.frange.range).is_none()
81 || name_ref.text() != "for_each"
82 {
83 return None;
84 }
85
86 let sema = &ctx.sema;
87
88 let receiver = expr.receiver()?;
89 let expr = ast::Expr::MethodCallExpr(expr);
90
91 let it_type = sema.type_of_expr(&receiver)?;
92 let module = sema.scope(receiver.syntax()).module()?;
93 let krate = module.krate();
94
95 let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
96 it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, receiver))
97}
98
99#[cfg(test)]
100mod tests {
101 use crate::tests::{self, check_assist};
102
103 use super::*;
104
105 const EMPTY_ITER_FIXTURE: &'static str = r"
106//- /lib.rs deps:core crate:empty_iter
107pub struct EmptyIter;
108impl Iterator for EmptyIter {
109 type Item = usize;
110 fn next(&mut self) -> Option<Self::Item> { None }
111}
112pub struct Empty;
113impl Empty {
114 pub fn iter(&self) -> EmptyIter { EmptyIter }
115}
116";
117
118 fn check_assist_with_fixtures(before: &str, after: &str) {
119 let before = &format!(
120 "//- /main.rs crate:main deps:core,empty_iter{}{}{}",
121 before,
122 EMPTY_ITER_FIXTURE,
123 FamousDefs::FIXTURE,
124 );
125 check_assist(convert_iter_for_each_to_for, before, after);
126 }
127
128 fn check_assist_not_applicable(before: &str) {
129 let before = &format!(
130 "//- /main.rs crate:main deps:core,empty_iter{}{}{}",
131 before,
132 EMPTY_ITER_FIXTURE,
133 FamousDefs::FIXTURE,
134 );
135 tests::check_assist_not_applicable(convert_iter_for_each_to_for, before);
136 }
137
138 #[test]
139 fn test_for_each_in_method_stmt() {
140 check_assist_with_fixtures(
141 r#"
142use empty_iter::*;
143fn main() {
144 let x = Empty;
145 x.iter().$0for_each(|(x, y)| {
146 println!("x: {}, y: {}", x, y);
147 });
148}"#,
149 r#"
150use empty_iter::*;
151fn main() {
152 let x = Empty;
153 for (x, y) in x.iter() {
154 println!("x: {}, y: {}", x, y);
155 }
156}
157"#,
158 )
159 }
160
161 #[test]
162 fn test_for_each_in_method() {
163 check_assist_with_fixtures(
164 r#"
165use empty_iter::*;
166fn main() {
167 let x = Empty;
168 x.iter().$0for_each(|(x, y)| {
169 println!("x: {}, y: {}", x, y);
170 })
171}"#,
172 r#"
173use empty_iter::*;
174fn main() {
175 let x = Empty;
176 for (x, y) in x.iter() {
177 println!("x: {}, y: {}", x, y);
178 }
179}
180"#,
181 )
182 }
183
184 #[test]
185 fn test_for_each_in_iter_stmt() {
186 check_assist_with_fixtures(
187 r#"
188use empty_iter::*;
189fn main() {
190 let x = Empty.iter();
191 x.$0for_each(|(x, y)| {
192 println!("x: {}, y: {}", x, y);
193 });
194}"#,
195 r#"
196use empty_iter::*;
197fn main() {
198 let x = Empty.iter();
199 for (x, y) in x {
200 println!("x: {}, y: {}", x, y);
201 }
202}
203"#,
204 )
205 }
206
207 #[test]
208 fn test_for_each_without_braces_stmt() {
209 check_assist_with_fixtures(
210 r#"
211use empty_iter::*;
212fn main() {
213 let x = Empty;
214 x.iter().$0for_each(|(x, y)| println!("x: {}, y: {}", x, y));
215}"#,
216 r#"
217use empty_iter::*;
218fn main() {
219 let x = Empty;
220 for (x, y) in x.iter() {
221 println!("x: {}, y: {}", x, y)
222 }
223}
224"#,
225 )
226 }
227
228 #[test]
229 fn test_for_each_not_applicable() {
230 check_assist_not_applicable(
231 r#"
232fn main() {
233 ().$0for_each(|x| println!("{}", x));
234}"#,
235 )
236 }
237
238 #[test]
239 fn test_for_each_not_applicable_invalid_cursor_pos() {
240 check_assist_not_applicable(
241 r#"
242use empty_iter::*;
243fn main() {
244 Empty.iter().for_each(|(x, y)| $0println!("x: {}, y: {}", x, y));
245}"#,
246 )
247 }
248}
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 8779d8bd1..dd4501709 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -20,7 +20,6 @@ use syntax::{
20 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, 20 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR},
21 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, 21 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
22}; 22};
23use test_utils::mark;
24 23
25use crate::{ 24use crate::{
26 assist_context::{AssistContext, Assists}, 25 assist_context::{AssistContext, Assists},
@@ -59,7 +58,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
59 58
60 let node = ctx.covering_element(); 59 let node = ctx.covering_element();
61 if node.kind() == COMMENT { 60 if node.kind() == COMMENT {
62 mark::hit!(extract_function_in_comment_is_not_applicable); 61 cov_mark::hit!(extract_function_in_comment_is_not_applicable);
63 return None; 62 return None;
64 } 63 }
65 64
@@ -197,14 +196,14 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con
197 if let Some(kind) = expr_err_kind(&expr, ctx) { 196 if let Some(kind) = expr_err_kind(&expr, ctx) {
198 Some(FlowKind::TryReturn { expr, kind }) 197 Some(FlowKind::TryReturn { expr, kind })
199 } else { 198 } else {
200 mark::hit!(external_control_flow_try_and_return_non_err); 199 cov_mark::hit!(external_control_flow_try_and_return_non_err);
201 return None; 200 return None;
202 } 201 }
203 } 202 }
204 None => return None, 203 None => return None,
205 }, 204 },
206 (Some(_), _, _, _) => { 205 (Some(_), _, _, _) => {
207 mark::hit!(external_control_flow_try_and_bc); 206 cov_mark::hit!(external_control_flow_try_and_bc);
208 return None; 207 return None;
209 } 208 }
210 (None, Some(r), None, None) => match r.expr() { 209 (None, Some(r), None, None) => match r.expr() {
@@ -212,11 +211,11 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con
212 None => Some(FlowKind::Return), 211 None => Some(FlowKind::Return),
213 }, 212 },
214 (None, Some(_), _, _) => { 213 (None, Some(_), _, _) => {
215 mark::hit!(external_control_flow_return_and_bc); 214 cov_mark::hit!(external_control_flow_return_and_bc);
216 return None; 215 return None;
217 } 216 }
218 (None, None, Some(_), Some(_)) => { 217 (None, None, Some(_), Some(_)) => {
219 mark::hit!(external_control_flow_break_and_continue); 218 cov_mark::hit!(external_control_flow_break_and_continue);
220 return None; 219 return None;
221 } 220 }
222 (None, None, Some(b), None) => match b.expr() { 221 (None, None, Some(b), None) => match b.expr() {
@@ -1837,7 +1836,7 @@ fn $0fun_name(n: u32) -> u32 {
1837 1836
1838 #[test] 1837 #[test]
1839 fn in_comment_is_not_applicable() { 1838 fn in_comment_is_not_applicable() {
1840 mark::check!(extract_function_in_comment_is_not_applicable); 1839 cov_mark::check!(extract_function_in_comment_is_not_applicable);
1841 check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }"); 1840 check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }");
1842 } 1841 }
1843 1842
@@ -2822,7 +2821,7 @@ fn $0fun_name(n: i32) -> Result<i32, i64> {
2822 2821
2823 #[test] 2822 #[test]
2824 fn break_and_continue() { 2823 fn break_and_continue() {
2825 mark::check!(external_control_flow_break_and_continue); 2824 cov_mark::check!(external_control_flow_break_and_continue);
2826 check_assist_not_applicable( 2825 check_assist_not_applicable(
2827 extract_function, 2826 extract_function,
2828 r##" 2827 r##"
@@ -2842,7 +2841,7 @@ fn foo() {
2842 2841
2843 #[test] 2842 #[test]
2844 fn return_and_break() { 2843 fn return_and_break() {
2845 mark::check!(external_control_flow_return_and_bc); 2844 cov_mark::check!(external_control_flow_return_and_bc);
2846 check_assist_not_applicable( 2845 check_assist_not_applicable(
2847 extract_function, 2846 extract_function,
2848 r##" 2847 r##"
@@ -3341,7 +3340,7 @@ fn $0fun_name() -> Result<i32, i64> {
3341 3340
3342 #[test] 3341 #[test]
3343 fn try_and_break() { 3342 fn try_and_break() {
3344 mark::check!(external_control_flow_try_and_bc); 3343 cov_mark::check!(external_control_flow_try_and_bc);
3345 check_assist_not_applicable( 3344 check_assist_not_applicable(
3346 extract_function, 3345 extract_function,
3347 r##" 3346 r##"
@@ -3363,7 +3362,7 @@ fn foo() -> Option<()> {
3363 3362
3364 #[test] 3363 #[test]
3365 fn try_and_return_ok() { 3364 fn try_and_return_ok() {
3366 mark::check!(external_control_flow_try_and_return_non_err); 3365 cov_mark::check!(external_control_flow_try_and_return_non_err);
3367 check_assist_not_applicable( 3366 check_assist_not_applicable(
3368 extract_function, 3367 extract_function,
3369 r##" 3368 r##"
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
index 5c7678b53..335e0ed95 100644
--- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -1,7 +1,7 @@
1use std::iter; 1use std::iter;
2 2
3use either::Either; 3use either::Either;
4use hir::{AsName, Module, ModuleDef, Name, Variant}; 4use hir::{Module, ModuleDef, Name, Variant};
5use ide_db::{ 5use ide_db::{
6 defs::Definition, 6 defs::Definition,
7 helpers::{ 7 helpers::{
@@ -133,7 +133,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
133 ), 133 ),
134 _ => false, 134 _ => false,
135 }) 135 })
136 .any(|(name, _)| name == variant_name.as_name()) 136 .any(|(name, _)| name.to_string() == variant_name.to_string())
137} 137}
138 138
139fn insert_import( 139fn insert_import(
@@ -154,7 +154,7 @@ fn insert_import(
154 mod_path.pop_segment(); 154 mod_path.pop_segment();
155 mod_path.push_segment(variant_hir_name.clone()); 155 mod_path.push_segment(variant_hir_name.clone());
156 let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; 156 let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?;
157 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); 157 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use);
158 } 158 }
159 Some(()) 159 Some(())
160} 160}
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index 312ac7ac4..7a32483dc 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -6,7 +6,6 @@ use syntax::{
6 }, 6 },
7 SyntaxNode, 7 SyntaxNode,
8}; 8};
9use test_utils::mark;
10 9
11use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; 10use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
12 11
@@ -32,7 +31,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
32 } 31 }
33 let node = ctx.covering_element(); 32 let node = ctx.covering_element();
34 if node.kind() == COMMENT { 33 if node.kind() == COMMENT {
35 mark::hit!(extract_var_in_comment_is_not_applicable); 34 cov_mark::hit!(extract_var_in_comment_is_not_applicable);
36 return None; 35 return None;
37 } 36 }
38 let to_extract = node.ancestors().find_map(valid_target_expr)?; 37 let to_extract = node.ancestors().find_map(valid_target_expr)?;
@@ -69,7 +68,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
69 format_to!(buf, "{}", to_extract.syntax()); 68 format_to!(buf, "{}", to_extract.syntax());
70 69
71 if let Anchor::Replace(stmt) = anchor { 70 if let Anchor::Replace(stmt) = anchor {
72 mark::hit!(test_extract_var_expr_stmt); 71 cov_mark::hit!(test_extract_var_expr_stmt);
73 if stmt.semicolon_token().is_none() { 72 if stmt.semicolon_token().is_none() {
74 buf.push_str(";"); 73 buf.push_str(";");
75 } 74 }
@@ -142,7 +141,7 @@ impl Anchor {
142 node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr()) 141 node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr())
143 { 142 {
144 if expr.syntax() == &node { 143 if expr.syntax() == &node {
145 mark::hit!(test_extract_var_last_expr); 144 cov_mark::hit!(test_extract_var_last_expr);
146 return Some(Anchor::Before(node)); 145 return Some(Anchor::Before(node));
147 } 146 }
148 } 147 }
@@ -175,8 +174,6 @@ impl Anchor {
175 174
176#[cfg(test)] 175#[cfg(test)]
177mod tests { 176mod tests {
178 use test_utils::mark;
179
180 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 177 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
181 178
182 use super::*; 179 use super::*;
@@ -199,13 +196,13 @@ fn foo() {
199 196
200 #[test] 197 #[test]
201 fn extract_var_in_comment_is_not_applicable() { 198 fn extract_var_in_comment_is_not_applicable() {
202 mark::check!(extract_var_in_comment_is_not_applicable); 199 cov_mark::check!(extract_var_in_comment_is_not_applicable);
203 check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }"); 200 check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }");
204 } 201 }
205 202
206 #[test] 203 #[test]
207 fn test_extract_var_expr_stmt() { 204 fn test_extract_var_expr_stmt() {
208 mark::check!(test_extract_var_expr_stmt); 205 cov_mark::check!(test_extract_var_expr_stmt);
209 check_assist( 206 check_assist(
210 extract_variable, 207 extract_variable,
211 r#" 208 r#"
@@ -250,7 +247,7 @@ fn foo() {
250 247
251 #[test] 248 #[test]
252 fn test_extract_var_last_expr() { 249 fn test_extract_var_last_expr() {
253 mark::check!(test_extract_var_last_expr); 250 cov_mark::check!(test_extract_var_last_expr);
254 check_assist( 251 check_assist(
255 extract_variable, 252 extract_variable,
256 r#" 253 r#"
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 7086e47d2..878b3a3fa 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -5,7 +5,6 @@ use ide_db::helpers::{mod_path_to_ast, FamousDefs};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use itertools::Itertools; 6use itertools::Itertools;
7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; 7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
8use test_utils::mark;
9 8
10use crate::{ 9use crate::{
11 utils::{does_pat_match_variant, render_snippet, Cursor}, 10 utils::{does_pat_match_variant, render_snippet, Cursor},
@@ -62,7 +61,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
62 .collect::<Vec<_>>(); 61 .collect::<Vec<_>>();
63 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { 62 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() {
64 // Match `Some` variant first. 63 // Match `Some` variant first.
65 mark::hit!(option_order); 64 cov_mark::hit!(option_order);
66 variants.reverse() 65 variants.reverse()
67 } 66 }
68 variants 67 variants
@@ -195,7 +194,6 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Optio
195#[cfg(test)] 194#[cfg(test)]
196mod tests { 195mod tests {
197 use ide_db::helpers::FamousDefs; 196 use ide_db::helpers::FamousDefs;
198 use test_utils::mark;
199 197
200 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 198 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
201 199
@@ -730,7 +728,7 @@ fn main() {
730 728
731 #[test] 729 #[test]
732 fn option_order() { 730 fn option_order() {
733 mark::check!(option_order); 731 cov_mark::check!(option_order);
734 let before = r#" 732 let before = r#"
735fn foo(opt: Option<i32>) { 733fn foo(opt: Option<i32>) {
736 match opt$0 { 734 match opt$0 {
diff --git a/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs
index 6a2ab9596..588ee1350 100644
--- a/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs
@@ -1,7 +1,6 @@
1use ide_db::helpers::FamousDefs; 1use ide_db::helpers::FamousDefs;
2use ide_db::RootDatabase; 2use ide_db::RootDatabase;
3use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark;
5 4
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 5use crate::{AssistContext, AssistId, AssistKind, Assists};
7 6
@@ -38,12 +37,12 @@ pub(crate) fn generate_default_from_enum_variant(
38 let variant_name = variant.name()?; 37 let variant_name = variant.name()?;
39 let enum_name = variant.parent_enum().name()?; 38 let enum_name = variant.parent_enum().name()?;
40 if !matches!(variant.kind(), ast::StructKind::Unit) { 39 if !matches!(variant.kind(), ast::StructKind::Unit) {
41 mark::hit!(test_gen_default_on_non_unit_variant_not_implemented); 40 cov_mark::hit!(test_gen_default_on_non_unit_variant_not_implemented);
42 return None; 41 return None;
43 } 42 }
44 43
45 if existing_default_impl(&ctx.sema, &variant).is_some() { 44 if existing_default_impl(&ctx.sema, &variant).is_some() {
46 mark::hit!(test_gen_default_impl_already_exists); 45 cov_mark::hit!(test_gen_default_impl_already_exists);
47 return None; 46 return None;
48 } 47 }
49 48
@@ -89,8 +88,6 @@ fn existing_default_impl(
89 88
90#[cfg(test)] 89#[cfg(test)]
91mod tests { 90mod tests {
92 use test_utils::mark;
93
94 use crate::tests::{check_assist, check_assist_not_applicable}; 91 use crate::tests::{check_assist, check_assist_not_applicable};
95 92
96 use super::*; 93 use super::*;
@@ -127,7 +124,7 @@ impl Default for Variant {
127 124
128 #[test] 125 #[test]
129 fn test_generate_default_already_implemented() { 126 fn test_generate_default_already_implemented() {
130 mark::check!(test_gen_default_impl_already_exists); 127 cov_mark::check!(test_gen_default_impl_already_exists);
131 check_not_applicable( 128 check_not_applicable(
132 r#" 129 r#"
133enum Variant { 130enum Variant {
@@ -146,7 +143,7 @@ impl Default for Variant {
146 143
147 #[test] 144 #[test]
148 fn test_add_from_impl_no_element() { 145 fn test_add_from_impl_no_element() {
149 mark::check!(test_gen_default_on_non_unit_variant_not_implemented); 146 cov_mark::check!(test_gen_default_on_non_unit_variant_not_implemented);
150 check_not_applicable( 147 check_not_applicable(
151 r#" 148 r#"
152enum Variant { 149enum Variant {
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs
index fa1254579..81c54ba3e 100644
--- a/crates/ide_assists/src/handlers/generate_default_from_new.rs
+++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs
@@ -7,7 +7,6 @@ use syntax::{
7 ast::{self, Impl, NameOwner}, 7 ast::{self, Impl, NameOwner},
8 AstNode, 8 AstNode,
9}; 9};
10use test_utils::mark;
11 10
12// Assist: generate_default_from_new 11// Assist: generate_default_from_new
13// 12//
@@ -43,19 +42,19 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext)
43 let fn_name = fn_node.name()?; 42 let fn_name = fn_node.name()?;
44 43
45 if fn_name.text() != "new" { 44 if fn_name.text() != "new" {
46 mark::hit!(other_function_than_new); 45 cov_mark::hit!(other_function_than_new);
47 return None; 46 return None;
48 } 47 }
49 48
50 if fn_node.param_list()?.params().next().is_some() { 49 if fn_node.param_list()?.params().next().is_some() {
51 mark::hit!(new_function_with_parameters); 50 cov_mark::hit!(new_function_with_parameters);
52 return None; 51 return None;
53 } 52 }
54 53
55 let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?; 54 let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?;
56 if is_default_implemented(ctx, &impl_) { 55 if is_default_implemented(ctx, &impl_) {
57 mark::hit!(default_block_is_already_present); 56 cov_mark::hit!(default_block_is_already_present);
58 mark::hit!(struct_in_module_with_default); 57 cov_mark::hit!(struct_in_module_with_default);
59 return None; 58 return None;
60 } 59 }
61 60
@@ -178,7 +177,7 @@ impl Default for Test {
178 177
179 #[test] 178 #[test]
180 fn new_function_with_parameters() { 179 fn new_function_with_parameters() {
181 mark::check!(new_function_with_parameters); 180 cov_mark::check!(new_function_with_parameters);
182 check_not_applicable( 181 check_not_applicable(
183 r#" 182 r#"
184struct Example { _inner: () } 183struct Example { _inner: () }
@@ -194,7 +193,7 @@ impl Example {
194 193
195 #[test] 194 #[test]
196 fn other_function_than_new() { 195 fn other_function_than_new() {
197 mark::check!(other_function_than_new); 196 cov_mark::check!(other_function_than_new);
198 check_not_applicable( 197 check_not_applicable(
199 r#" 198 r#"
200struct Example { _inner: () } 199struct Example { _inner: () }
@@ -211,7 +210,7 @@ impl Example {
211 210
212 #[test] 211 #[test]
213 fn default_block_is_already_present() { 212 fn default_block_is_already_present() {
214 mark::check!(default_block_is_already_present); 213 cov_mark::check!(default_block_is_already_present);
215 check_not_applicable( 214 check_not_applicable(
216 r#" 215 r#"
217struct Example { _inner: () } 216struct Example { _inner: () }
@@ -340,7 +339,7 @@ impl Default for Example {
340 339
341 #[test] 340 #[test]
342 fn struct_in_module_with_default() { 341 fn struct_in_module_with_default() {
343 mark::check!(struct_in_module_with_default); 342 cov_mark::check!(struct_in_module_with_default);
344 check_not_applicable( 343 check_not_applicable(
345 r#" 344 r#"
346mod test { 345mod test {
diff --git a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
index d9388a737..c13c6eebe 100644
--- a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
@@ -1,7 +1,6 @@
1use ide_db::helpers::FamousDefs; 1use ide_db::helpers::FamousDefs;
2use ide_db::RootDatabase; 2use ide_db::RootDatabase;
3use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark;
5 4
6use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists}; 5use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists};
7 6
@@ -44,7 +43,7 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext
44 }; 43 };
45 44
46 if existing_from_impl(&ctx.sema, &variant).is_some() { 45 if existing_from_impl(&ctx.sema, &variant).is_some() {
47 mark::hit!(test_add_from_impl_already_exists); 46 cov_mark::hit!(test_add_from_impl_already_exists);
48 return None; 47 return None;
49 } 48 }
50 49
@@ -103,8 +102,6 @@ fn existing_from_impl(
103 102
104#[cfg(test)] 103#[cfg(test)]
105mod tests { 104mod tests {
106 use test_utils::mark;
107
108 use crate::tests::{check_assist, check_assist_not_applicable}; 105 use crate::tests::{check_assist, check_assist_not_applicable};
109 106
110 use super::*; 107 use super::*;
@@ -172,7 +169,7 @@ impl From<u32> for A {
172 169
173 #[test] 170 #[test]
174 fn test_add_from_impl_already_exists() { 171 fn test_add_from_impl_already_exists() {
175 mark::check!(test_add_from_impl_already_exists); 172 cov_mark::check!(test_add_from_impl_already_exists);
176 check_not_applicable( 173 check_not_applicable(
177 r#" 174 r#"
178enum A { $0One(u32), } 175enum A { $0One(u32), }
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index 3870b7e75..6f95b1a07 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -83,17 +83,18 @@ struct FunctionTemplate {
83 leading_ws: String, 83 leading_ws: String,
84 fn_def: ast::Fn, 84 fn_def: ast::Fn,
85 ret_type: ast::RetType, 85 ret_type: ast::RetType,
86 should_render_snippet: bool,
86 trailing_ws: String, 87 trailing_ws: String,
87 file: FileId, 88 file: FileId,
88} 89}
89 90
90impl FunctionTemplate { 91impl FunctionTemplate {
91 fn to_string(&self, cap: Option<SnippetCap>) -> String { 92 fn to_string(&self, cap: Option<SnippetCap>) -> String {
92 let f = match cap { 93 let f = match (cap, self.should_render_snippet) {
93 Some(cap) => { 94 (Some(cap), true) => {
94 render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) 95 render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax()))
95 } 96 }
96 None => self.fn_def.to_string(), 97 _ => self.fn_def.to_string(),
97 }; 98 };
98 format!("{}{}{}", self.leading_ws, f, self.trailing_ws) 99 format!("{}{}{}", self.leading_ws, f, self.trailing_ws)
99 } 100 }
@@ -104,6 +105,8 @@ struct FunctionBuilder {
104 fn_name: ast::Name, 105 fn_name: ast::Name,
105 type_params: Option<ast::GenericParamList>, 106 type_params: Option<ast::GenericParamList>,
106 params: ast::ParamList, 107 params: ast::ParamList,
108 ret_type: ast::RetType,
109 should_render_snippet: bool,
107 file: FileId, 110 file: FileId,
108 needs_pub: bool, 111 needs_pub: bool,
109} 112}
@@ -132,7 +135,43 @@ impl FunctionBuilder {
132 let fn_name = fn_name(&path)?; 135 let fn_name = fn_name(&path)?;
133 let (type_params, params) = fn_args(ctx, target_module, &call)?; 136 let (type_params, params) = fn_args(ctx, target_module, &call)?;
134 137
135 Some(Self { target, fn_name, type_params, params, file, needs_pub }) 138 // should_render_snippet intends to express a rough level of confidence about
139 // the correctness of the return type.
140 //
141 // If we are able to infer some return type, and that return type is not unit, we
142 // don't want to render the snippet. The assumption here is in this situation the
143 // return type is just as likely to be correct as any other part of the generated
144 // function.
145 //
146 // In the case where the return type is inferred as unit it is likely that the
147 // user does in fact intend for this generated function to return some non unit
148 // type, but that the current state of their code doesn't allow that return type
149 // to be accurately inferred.
150 let (ret_ty, should_render_snippet) = {
151 match ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone())) {
152 Some(ty) if ty.is_unknown() || ty.is_unit() => (make::ty_unit(), true),
153 Some(ty) => {
154 let rendered = ty.display_source_code(ctx.db(), target_module.into());
155 match rendered {
156 Ok(rendered) => (make::ty(&rendered), false),
157 Err(_) => (make::ty_unit(), true),
158 }
159 }
160 None => (make::ty_unit(), true),
161 }
162 };
163 let ret_type = make::ret_type(ret_ty);
164
165 Some(Self {
166 target,
167 fn_name,
168 type_params,
169 params,
170 ret_type,
171 should_render_snippet,
172 file,
173 needs_pub,
174 })
136 } 175 }
137 176
138 fn render(self) -> FunctionTemplate { 177 fn render(self) -> FunctionTemplate {
@@ -145,7 +184,7 @@ impl FunctionBuilder {
145 self.type_params, 184 self.type_params,
146 self.params, 185 self.params,
147 fn_body, 186 fn_body,
148 Some(make::ret_type(make::ty_unit())), 187 Some(self.ret_type),
149 ); 188 );
150 let leading_ws; 189 let leading_ws;
151 let trailing_ws; 190 let trailing_ws;
@@ -171,6 +210,7 @@ impl FunctionBuilder {
171 insert_offset, 210 insert_offset,
172 leading_ws, 211 leading_ws,
173 ret_type: fn_def.ret_type().unwrap(), 212 ret_type: fn_def.ret_type().unwrap(),
213 should_render_snippet: self.should_render_snippet,
174 fn_def, 214 fn_def,
175 trailing_ws, 215 trailing_ws,
176 file: self.file, 216 file: self.file,
@@ -546,7 +586,7 @@ impl Baz {
546 } 586 }
547} 587}
548 588
549fn bar(baz: Baz) ${0:-> ()} { 589fn bar(baz: Baz) -> Baz {
550 todo!() 590 todo!()
551} 591}
552", 592",
@@ -1060,6 +1100,27 @@ pub(crate) fn bar() ${0:-> ()} {
1060 } 1100 }
1061 1101
1062 #[test] 1102 #[test]
1103 fn add_function_with_return_type() {
1104 check_assist(
1105 generate_function,
1106 r"
1107fn main() {
1108 let x: u32 = foo$0();
1109}
1110",
1111 r"
1112fn main() {
1113 let x: u32 = foo();
1114}
1115
1116fn foo() -> u32 {
1117 todo!()
1118}
1119",
1120 )
1121 }
1122
1123 #[test]
1063 fn add_function_not_applicable_if_function_already_exists() { 1124 fn add_function_not_applicable_if_function_already_exists() {
1064 check_assist_not_applicable( 1125 check_assist_not_applicable(
1065 generate_function, 1126 generate_function,
diff --git a/crates/ide_assists/src/handlers/infer_function_return_type.rs b/crates/ide_assists/src/handlers/infer_function_return_type.rs
index 5279af1f3..66113751c 100644
--- a/crates/ide_assists/src/handlers/infer_function_return_type.rs
+++ b/crates/ide_assists/src/handlers/infer_function_return_type.rs
@@ -1,6 +1,5 @@
1use hir::HirDisplay; 1use hir::HirDisplay;
2use syntax::{ast, AstNode, TextRange, TextSize}; 2use syntax::{ast, AstNode, TextRange, TextSize};
3use test_utils::mark;
4 3
5use crate::{AssistContext, AssistId, AssistKind, Assists}; 4use crate::{AssistContext, AssistId, AssistKind, Assists};
6 5
@@ -42,7 +41,7 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext)
42 } 41 }
43 } 42 }
44 if let FnType::Closure { wrap_expr: true } = fn_type { 43 if let FnType::Closure { wrap_expr: true } = fn_type {
45 mark::hit!(wrap_closure_non_block_expr); 44 cov_mark::hit!(wrap_closure_non_block_expr);
46 // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block 45 // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
47 builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); 46 builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr));
48 } 47 }
@@ -61,13 +60,13 @@ fn ret_ty_to_action(ret_ty: Option<ast::RetType>, insert_pos: TextSize) -> Optio
61 match ret_ty { 60 match ret_ty {
62 Some(ret_ty) => match ret_ty.ty() { 61 Some(ret_ty) => match ret_ty.ty() {
63 Some(ast::Type::InferType(_)) | None => { 62 Some(ast::Type::InferType(_)) | None => {
64 mark::hit!(existing_infer_ret_type); 63 cov_mark::hit!(existing_infer_ret_type);
65 mark::hit!(existing_infer_ret_type_closure); 64 cov_mark::hit!(existing_infer_ret_type_closure);
66 Some(InsertOrReplace::Replace(ret_ty.syntax().text_range())) 65 Some(InsertOrReplace::Replace(ret_ty.syntax().text_range()))
67 } 66 }
68 _ => { 67 _ => {
69 mark::hit!(existing_ret_type); 68 cov_mark::hit!(existing_ret_type);
70 mark::hit!(existing_ret_type_closure); 69 cov_mark::hit!(existing_ret_type_closure);
71 None 70 None
72 } 71 }
73 }, 72 },
@@ -109,11 +108,11 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla
109 }; 108 };
110 let frange = ctx.frange.range; 109 let frange = ctx.frange.range;
111 if return_type_range.contains_range(frange) { 110 if return_type_range.contains_range(frange) {
112 mark::hit!(cursor_in_ret_position); 111 cov_mark::hit!(cursor_in_ret_position);
113 mark::hit!(cursor_in_ret_position_closure); 112 cov_mark::hit!(cursor_in_ret_position_closure);
114 } else if tail_expr.syntax().text_range().contains_range(frange) { 113 } else if tail_expr.syntax().text_range().contains_range(frange) {
115 mark::hit!(cursor_on_tail); 114 cov_mark::hit!(cursor_on_tail);
116 mark::hit!(cursor_on_tail_closure); 115 cov_mark::hit!(cursor_on_tail_closure);
117 } else { 116 } else {
118 return None; 117 return None;
119 } 118 }
@@ -128,7 +127,7 @@ mod tests {
128 127
129 #[test] 128 #[test]
130 fn infer_return_type_specified_inferred() { 129 fn infer_return_type_specified_inferred() {
131 mark::check!(existing_infer_ret_type); 130 cov_mark::check!(existing_infer_ret_type);
132 check_assist( 131 check_assist(
133 infer_function_return_type, 132 infer_function_return_type,
134 r#"fn foo() -> $0_ { 133 r#"fn foo() -> $0_ {
@@ -142,7 +141,7 @@ mod tests {
142 141
143 #[test] 142 #[test]
144 fn infer_return_type_specified_inferred_closure() { 143 fn infer_return_type_specified_inferred_closure() {
145 mark::check!(existing_infer_ret_type_closure); 144 cov_mark::check!(existing_infer_ret_type_closure);
146 check_assist( 145 check_assist(
147 infer_function_return_type, 146 infer_function_return_type,
148 r#"fn foo() { 147 r#"fn foo() {
@@ -156,7 +155,7 @@ mod tests {
156 155
157 #[test] 156 #[test]
158 fn infer_return_type_cursor_at_return_type_pos() { 157 fn infer_return_type_cursor_at_return_type_pos() {
159 mark::check!(cursor_in_ret_position); 158 cov_mark::check!(cursor_in_ret_position);
160 check_assist( 159 check_assist(
161 infer_function_return_type, 160 infer_function_return_type,
162 r#"fn foo() $0{ 161 r#"fn foo() $0{
@@ -170,7 +169,7 @@ mod tests {
170 169
171 #[test] 170 #[test]
172 fn infer_return_type_cursor_at_return_type_pos_closure() { 171 fn infer_return_type_cursor_at_return_type_pos_closure() {
173 mark::check!(cursor_in_ret_position_closure); 172 cov_mark::check!(cursor_in_ret_position_closure);
174 check_assist( 173 check_assist(
175 infer_function_return_type, 174 infer_function_return_type,
176 r#"fn foo() { 175 r#"fn foo() {
@@ -184,7 +183,7 @@ mod tests {
184 183
185 #[test] 184 #[test]
186 fn infer_return_type() { 185 fn infer_return_type() {
187 mark::check!(cursor_on_tail); 186 cov_mark::check!(cursor_on_tail);
188 check_assist( 187 check_assist(
189 infer_function_return_type, 188 infer_function_return_type,
190 r#"fn foo() { 189 r#"fn foo() {
@@ -219,7 +218,7 @@ mod tests {
219 218
220 #[test] 219 #[test]
221 fn not_applicable_ret_type_specified() { 220 fn not_applicable_ret_type_specified() {
222 mark::check!(existing_ret_type); 221 cov_mark::check!(existing_ret_type);
223 check_assist_not_applicable( 222 check_assist_not_applicable(
224 infer_function_return_type, 223 infer_function_return_type,
225 r#"fn foo() -> i32 { 224 r#"fn foo() -> i32 {
@@ -251,7 +250,7 @@ mod tests {
251 250
252 #[test] 251 #[test]
253 fn infer_return_type_closure_block() { 252 fn infer_return_type_closure_block() {
254 mark::check!(cursor_on_tail_closure); 253 cov_mark::check!(cursor_on_tail_closure);
255 check_assist( 254 check_assist(
256 infer_function_return_type, 255 infer_function_return_type,
257 r#"fn foo() { 256 r#"fn foo() {
@@ -282,7 +281,7 @@ mod tests {
282 281
283 #[test] 282 #[test]
284 fn infer_return_type_closure_wrap() { 283 fn infer_return_type_closure_wrap() {
285 mark::check!(wrap_closure_non_block_expr); 284 cov_mark::check!(wrap_closure_non_block_expr);
286 check_assist( 285 check_assist(
287 infer_function_return_type, 286 infer_function_return_type,
288 r#"fn foo() { 287 r#"fn foo() {
@@ -321,7 +320,7 @@ mod tests {
321 320
322 #[test] 321 #[test]
323 fn not_applicable_ret_type_specified_closure() { 322 fn not_applicable_ret_type_specified_closure() {
324 mark::check!(existing_ret_type_closure); 323 cov_mark::check!(existing_ret_type_closure);
325 check_assist_not_applicable( 324 check_assist_not_applicable(
326 infer_function_return_type, 325 infer_function_return_type,
327 r#"fn foo() { 326 r#"fn foo() {
diff --git a/crates/ide_assists/src/handlers/inline_function.rs b/crates/ide_assists/src/handlers/inline_function.rs
index 6ec99b09b..8e56029cb 100644
--- a/crates/ide_assists/src/handlers/inline_function.rs
+++ b/crates/ide_assists/src/handlers/inline_function.rs
@@ -4,7 +4,6 @@ use syntax::{
4 ast::{self, edit::AstNodeEdit, ArgListOwner}, 4 ast::{self, edit::AstNodeEdit, ArgListOwner},
5 AstNode, 5 AstNode,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{ 8use crate::{
10 assist_context::{AssistContext, Assists}, 9 assist_context::{AssistContext, Assists},
@@ -49,7 +48,7 @@ pub(crate) fn inline_function(acc: &mut Assists, ctx: &AssistContext) -> Option<
49 if arguments.len() != parameters.len() { 48 if arguments.len() != parameters.len() {
50 // Can't inline the function because they've passed the wrong number of 49 // Can't inline the function because they've passed the wrong number of
51 // arguments to this function 50 // arguments to this function
52 mark::hit!(inline_function_incorrect_number_of_arguments); 51 cov_mark::hit!(inline_function_incorrect_number_of_arguments);
53 return None; 52 return None;
54 } 53 }
55 54
@@ -155,7 +154,7 @@ fn main() { Foo.bar$0(); }
155 154
156 #[test] 155 #[test]
157 fn not_applicable_when_incorrect_number_of_parameters_are_provided() { 156 fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
158 mark::check!(inline_function_incorrect_number_of_arguments); 157 cov_mark::check!(inline_function_incorrect_number_of_arguments);
159 check_assist_not_applicable( 158 check_assist_not_applicable(
160 inline_function, 159 inline_function,
161 r#" 160 r#"
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs
index da5522670..ea1466dc8 100644
--- a/crates/ide_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ide_assists/src/handlers/inline_local_variable.rs
@@ -4,7 +4,6 @@ use syntax::{
4 ast::{self, AstNode, AstToken}, 4 ast::{self, AstNode, AstToken},
5 TextRange, 5 TextRange,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{ 8use crate::{
10 assist_context::{AssistContext, Assists}, 9 assist_context::{AssistContext, Assists},
@@ -34,11 +33,11 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
34 _ => return None, 33 _ => return None,
35 }; 34 };
36 if bind_pat.mut_token().is_some() { 35 if bind_pat.mut_token().is_some() {
37 mark::hit!(test_not_inline_mut_variable); 36 cov_mark::hit!(test_not_inline_mut_variable);
38 return None; 37 return None;
39 } 38 }
40 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { 39 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) {
41 mark::hit!(not_applicable_outside_of_bind_pat); 40 cov_mark::hit!(not_applicable_outside_of_bind_pat);
42 return None; 41 return None;
43 } 42 }
44 let initializer_expr = let_stmt.initializer()?; 43 let initializer_expr = let_stmt.initializer()?;
@@ -47,7 +46,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
47 let def = Definition::Local(def); 46 let def = Definition::Local(def);
48 let usages = def.usages(&ctx.sema).all(); 47 let usages = def.usages(&ctx.sema).all();
49 if usages.is_empty() { 48 if usages.is_empty() {
50 mark::hit!(test_not_applicable_if_variable_unused); 49 cov_mark::hit!(test_not_applicable_if_variable_unused);
51 return None; 50 return None;
52 }; 51 };
53 52
@@ -130,7 +129,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
130 Some(name_ref) 129 Some(name_ref)
131 if ast::RecordExprField::for_field_name(name_ref).is_some() => 130 if ast::RecordExprField::for_field_name(name_ref).is_some() =>
132 { 131 {
133 mark::hit!(inline_field_shorthand); 132 cov_mark::hit!(inline_field_shorthand);
134 builder.insert(reference.range.end(), format!(": {}", replacement)); 133 builder.insert(reference.range.end(), format!(": {}", replacement));
135 } 134 }
136 _ => builder.replace(reference.range, replacement), 135 _ => builder.replace(reference.range, replacement),
@@ -143,8 +142,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
143 142
144#[cfg(test)] 143#[cfg(test)]
145mod tests { 144mod tests {
146 use test_utils::mark;
147
148 use crate::tests::{check_assist, check_assist_not_applicable}; 145 use crate::tests::{check_assist, check_assist_not_applicable};
149 146
150 use super::*; 147 use super::*;
@@ -351,7 +348,7 @@ fn foo() {
351 348
352 #[test] 349 #[test]
353 fn test_not_inline_mut_variable() { 350 fn test_not_inline_mut_variable() {
354 mark::check!(test_not_inline_mut_variable); 351 cov_mark::check!(test_not_inline_mut_variable);
355 check_assist_not_applicable( 352 check_assist_not_applicable(
356 inline_local_variable, 353 inline_local_variable,
357 r" 354 r"
@@ -684,7 +681,7 @@ fn foo() {
684 681
685 #[test] 682 #[test]
686 fn inline_field_shorthand() { 683 fn inline_field_shorthand() {
687 mark::check!(inline_field_shorthand); 684 cov_mark::check!(inline_field_shorthand);
688 check_assist( 685 check_assist(
689 inline_local_variable, 686 inline_local_variable,
690 r" 687 r"
@@ -705,7 +702,7 @@ fn main() {
705 702
706 #[test] 703 #[test]
707 fn test_not_applicable_if_variable_unused() { 704 fn test_not_applicable_if_variable_unused() {
708 mark::check!(test_not_applicable_if_variable_unused); 705 cov_mark::check!(test_not_applicable_if_variable_unused);
709 check_assist_not_applicable( 706 check_assist_not_applicable(
710 inline_local_variable, 707 inline_local_variable,
711 r" 708 r"
@@ -718,7 +715,7 @@ fn foo() {
718 715
719 #[test] 716 #[test]
720 fn not_applicable_outside_of_bind_pat() { 717 fn not_applicable_outside_of_bind_pat() {
721 mark::check!(not_applicable_outside_of_bind_pat); 718 cov_mark::check!(not_applicable_outside_of_bind_pat);
722 check_assist_not_applicable( 719 check_assist_not_applicable(
723 inline_local_variable, 720 inline_local_variable,
724 r" 721 r"
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs
index 91c395c1b..6e685b4b2 100644
--- a/crates/ide_assists/src/handlers/move_module_to_file.rs
+++ b/crates/ide_assists/src/handlers/move_module_to_file.rs
@@ -5,7 +5,6 @@ use syntax::{
5 ast::{self, edit::AstNodeEdit, NameOwner}, 5 ast::{self, edit::AstNodeEdit, NameOwner},
6 AstNode, TextRange, 6 AstNode, TextRange,
7}; 7};
8use test_utils::mark;
9 8
10use crate::{AssistContext, AssistId, AssistKind, Assists}; 9use crate::{AssistContext, AssistId, AssistKind, Assists};
11 10
@@ -28,7 +27,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt
28 27
29 let l_curly_offset = module_items.syntax().text_range().start(); 28 let l_curly_offset = module_items.syntax().text_range().start();
30 if l_curly_offset <= ctx.offset() { 29 if l_curly_offset <= ctx.offset() {
31 mark::hit!(available_before_curly); 30 cov_mark::hit!(available_before_curly);
32 return None; 31 return None;
33 } 32 }
34 let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset); 33 let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset);
@@ -182,7 +181,7 @@ pub(crate) mod tests;
182 181
183 #[test] 182 #[test]
184 fn available_before_curly() { 183 fn available_before_curly() {
185 mark::check!(available_before_curly); 184 cov_mark::check!(available_before_curly);
186 check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); 185 check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#);
187 } 186 }
188} 187}
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs
index 377ed4f2f..04bae4e58 100644
--- a/crates/ide_assists/src/handlers/pull_assignment_up.rs
+++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs
@@ -2,7 +2,6 @@ use syntax::{
2 ast::{self, edit::AstNodeEdit, make}, 2 ast::{self, edit::AstNodeEdit, make},
3 AstNode, 3 AstNode,
4}; 4};
5use test_utils::mark;
6 5
7use crate::{ 6use crate::{
8 assist_context::{AssistContext, Assists}, 7 assist_context::{AssistContext, Assists},
@@ -104,7 +103,7 @@ fn exprify_if(
104 ast::ElseBranch::Block(exprify_block(block, sema, name)?) 103 ast::ElseBranch::Block(exprify_block(block, sema, name)?)
105 } 104 }
106 ast::ElseBranch::IfExpr(expr) => { 105 ast::ElseBranch::IfExpr(expr) => {
107 mark::hit!(test_pull_assignment_up_chained_if); 106 cov_mark::hit!(test_pull_assignment_up_chained_if);
108 ast::ElseBranch::IfExpr(ast::IfExpr::cast( 107 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
109 exprify_if(&expr, sema, name)?.syntax().to_owned(), 108 exprify_if(&expr, sema, name)?.syntax().to_owned(),
110 )?) 109 )?)
@@ -144,7 +143,7 @@ fn is_equivalent(
144) -> bool { 143) -> bool {
145 match (expr0, expr1) { 144 match (expr0, expr1) {
146 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => { 145 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
147 mark::hit!(test_pull_assignment_up_field_assignment); 146 cov_mark::hit!(test_pull_assignment_up_field_assignment);
148 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1) 147 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
149 } 148 }
150 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => { 149 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
@@ -160,7 +159,7 @@ fn is_equivalent(
160 if prefix0.op_kind() == Some(ast::PrefixOp::Deref) 159 if prefix0.op_kind() == Some(ast::PrefixOp::Deref)
161 && prefix1.op_kind() == Some(ast::PrefixOp::Deref) => 160 && prefix1.op_kind() == Some(ast::PrefixOp::Deref) =>
162 { 161 {
163 mark::hit!(test_pull_assignment_up_deref); 162 cov_mark::hit!(test_pull_assignment_up_deref);
164 if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) { 163 if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) {
165 is_equivalent(sema, &prefix0, &prefix1) 164 is_equivalent(sema, &prefix0, &prefix1)
166 } else { 165 } else {
@@ -263,7 +262,7 @@ fn foo() {
263 262
264 #[test] 263 #[test]
265 fn test_pull_assignment_up_chained_if() { 264 fn test_pull_assignment_up_chained_if() {
266 mark::check!(test_pull_assignment_up_chained_if); 265 cov_mark::check!(test_pull_assignment_up_chained_if);
267 check_assist( 266 check_assist(
268 pull_assignment_up, 267 pull_assignment_up,
269 r#" 268 r#"
@@ -379,7 +378,7 @@ fn foo() {
379 378
380 #[test] 379 #[test]
381 fn test_pull_assignment_up_field_assignment() { 380 fn test_pull_assignment_up_field_assignment() {
382 mark::check!(test_pull_assignment_up_field_assignment); 381 cov_mark::check!(test_pull_assignment_up_field_assignment);
383 check_assist( 382 check_assist(
384 pull_assignment_up, 383 pull_assignment_up,
385 r#" 384 r#"
@@ -411,7 +410,7 @@ fn foo() {
411 410
412 #[test] 411 #[test]
413 fn test_pull_assignment_up_deref() { 412 fn test_pull_assignment_up_deref() {
414 mark::check!(test_pull_assignment_up_deref); 413 cov_mark::check!(test_pull_assignment_up_deref);
415 check_assist( 414 check_assist(
416 pull_assignment_up, 415 pull_assignment_up,
417 r#" 416 r#"
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs
index b0b0d31b4..30b23da6c 100644
--- a/crates/ide_assists/src/handlers/qualify_path.rs
+++ b/crates/ide_assists/src/handlers/qualify_path.rs
@@ -1,14 +1,16 @@
1use std::iter; 1use std::iter;
2 2
3use hir::{AsAssocItem, AsName}; 3use hir::AsAssocItem;
4use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast}; 4use ide_db::helpers::{
5 import_assets::{ImportCandidate, LocatedImport},
6 mod_path_to_ast,
7};
5use ide_db::RootDatabase; 8use ide_db::RootDatabase;
6use syntax::{ 9use syntax::{
7 ast, 10 ast,
8 ast::{make, ArgListOwner}, 11 ast::{make, ArgListOwner},
9 AstNode, 12 AstNode,
10}; 13};
11use test_utils::mark;
12 14
13use crate::{ 15use crate::{
14 assist_context::{AssistContext, Assists}, 16 assist_context::{AssistContext, Assists},
@@ -47,32 +49,32 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
47 let qualify_candidate = match candidate { 49 let qualify_candidate = match candidate {
48 ImportCandidate::Path(candidate) => { 50 ImportCandidate::Path(candidate) => {
49 if candidate.qualifier.is_some() { 51 if candidate.qualifier.is_some() {
50 mark::hit!(qualify_path_qualifier_start); 52 cov_mark::hit!(qualify_path_qualifier_start);
51 let path = ast::Path::cast(syntax_under_caret)?; 53 let path = ast::Path::cast(syntax_under_caret)?;
52 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); 54 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
53 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) 55 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
54 } else { 56 } else {
55 mark::hit!(qualify_path_unqualified_name); 57 cov_mark::hit!(qualify_path_unqualified_name);
56 let path = ast::Path::cast(syntax_under_caret)?; 58 let path = ast::Path::cast(syntax_under_caret)?;
57 let generics = path.segment()?.generic_arg_list(); 59 let generics = path.segment()?.generic_arg_list();
58 QualifyCandidate::UnqualifiedName(generics) 60 QualifyCandidate::UnqualifiedName(generics)
59 } 61 }
60 } 62 }
61 ImportCandidate::TraitAssocItem(_) => { 63 ImportCandidate::TraitAssocItem(_) => {
62 mark::hit!(qualify_path_trait_assoc_item); 64 cov_mark::hit!(qualify_path_trait_assoc_item);
63 let path = ast::Path::cast(syntax_under_caret)?; 65 let path = ast::Path::cast(syntax_under_caret)?;
64 let (qualifier, segment) = (path.qualifier()?, path.segment()?); 66 let (qualifier, segment) = (path.qualifier()?, path.segment()?);
65 QualifyCandidate::TraitAssocItem(qualifier, segment) 67 QualifyCandidate::TraitAssocItem(qualifier, segment)
66 } 68 }
67 ImportCandidate::TraitMethod(_) => { 69 ImportCandidate::TraitMethod(_) => {
68 mark::hit!(qualify_path_trait_method); 70 cov_mark::hit!(qualify_path_trait_method);
69 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; 71 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
70 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) 72 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
71 } 73 }
72 }; 74 };
73 75
74 let group_label = group_label(candidate); 76 let group_label = group_label(candidate);
75 for (import, item) in proposed_imports { 77 for import in proposed_imports {
76 acc.add_group( 78 acc.add_group(
77 &group_label, 79 &group_label,
78 AssistId("qualify_path", AssistKind::QuickFix), 80 AssistId("qualify_path", AssistKind::QuickFix),
@@ -81,8 +83,8 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
81 |builder| { 83 |builder| {
82 qualify_candidate.qualify( 84 qualify_candidate.qualify(
83 |replace_with: String| builder.replace(range, replace_with), 85 |replace_with: String| builder.replace(range, replace_with),
84 import, 86 &import.import_path,
85 item, 87 import.item_to_import,
86 ) 88 )
87 }, 89 },
88 ); 90 );
@@ -98,8 +100,13 @@ enum QualifyCandidate<'db> {
98} 100}
99 101
100impl QualifyCandidate<'_> { 102impl QualifyCandidate<'_> {
101 fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) { 103 fn qualify(
102 let import = mod_path_to_ast(&import); 104 &self,
105 mut replacer: impl FnMut(String),
106 import: &hir::ModPath,
107 item: hir::ItemInNs,
108 ) {
109 let import = mod_path_to_ast(import);
103 match self { 110 match self {
104 QualifyCandidate::QualifierStart(segment, generics) => { 111 QualifyCandidate::QualifierStart(segment, generics) => {
105 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); 112 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
@@ -160,7 +167,9 @@ fn find_trait_method(
160) -> Option<hir::Function> { 167) -> Option<hir::Function> {
161 if let Some(hir::AssocItem::Function(method)) = 168 if let Some(hir::AssocItem::Function(method)) =
162 trait_.items(db).into_iter().find(|item: &hir::AssocItem| { 169 trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
163 item.name(db).map(|name| name == trait_method_name.as_name()).unwrap_or(false) 170 item.name(db)
171 .map(|name| name.to_string() == trait_method_name.to_string())
172 .unwrap_or(false)
164 }) 173 })
165 { 174 {
166 Some(method) 175 Some(method)
@@ -182,23 +191,25 @@ fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
182fn group_label(candidate: &ImportCandidate) -> GroupLabel { 191fn group_label(candidate: &ImportCandidate) -> GroupLabel {
183 let name = match candidate { 192 let name = match candidate {
184 ImportCandidate::Path(it) => &it.name, 193 ImportCandidate::Path(it) => &it.name,
185 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, 194 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => {
195 &it.assoc_item_name
196 }
186 } 197 }
187 .text(); 198 .text();
188 GroupLabel(format!("Qualify {}", name)) 199 GroupLabel(format!("Qualify {}", name))
189} 200}
190 201
191fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { 202fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String {
192 match candidate { 203 match candidate {
193 ImportCandidate::Path(candidate) => { 204 ImportCandidate::Path(candidate) => {
194 if candidate.qualifier.is_some() { 205 if candidate.qualifier.is_some() {
195 format!("Qualify with `{}`", &import) 206 format!("Qualify with `{}`", import.import_path)
196 } else { 207 } else {
197 format!("Qualify as `{}`", &import) 208 format!("Qualify as `{}`", import.import_path)
198 } 209 }
199 } 210 }
200 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import), 211 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", import.import_path),
201 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import), 212 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", import.import_path),
202 } 213 }
203} 214}
204 215
@@ -210,7 +221,7 @@ mod tests {
210 221
211 #[test] 222 #[test]
212 fn applicable_when_found_an_import_partial() { 223 fn applicable_when_found_an_import_partial() {
213 mark::check!(qualify_path_unqualified_name); 224 cov_mark::check!(qualify_path_unqualified_name);
214 check_assist( 225 check_assist(
215 qualify_path, 226 qualify_path,
216 r" 227 r"
@@ -502,7 +513,7 @@ fn main() {
502 513
503 #[test] 514 #[test]
504 fn associated_struct_const() { 515 fn associated_struct_const() {
505 mark::check!(qualify_path_qualifier_start); 516 cov_mark::check!(qualify_path_qualifier_start);
506 check_assist( 517 check_assist(
507 qualify_path, 518 qualify_path,
508 r" 519 r"
@@ -603,7 +614,7 @@ fn main() {
603 614
604 #[test] 615 #[test]
605 fn associated_trait_const() { 616 fn associated_trait_const() {
606 mark::check!(qualify_path_trait_assoc_item); 617 cov_mark::check!(qualify_path_trait_assoc_item);
607 check_assist( 618 check_assist(
608 qualify_path, 619 qualify_path,
609 r" 620 r"
@@ -673,7 +684,7 @@ fn main() {
673 684
674 #[test] 685 #[test]
675 fn trait_method() { 686 fn trait_method() {
676 mark::check!(qualify_path_trait_method); 687 cov_mark::check!(qualify_path_trait_method);
677 check_assist( 688 check_assist(
678 qualify_path, 689 qualify_path,
679 r" 690 r"
diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs
index d95267607..d0f1613f3 100644
--- a/crates/ide_assists/src/handlers/raw_string.rs
+++ b/crates/ide_assists/src/handlers/raw_string.rs
@@ -1,7 +1,6 @@
1use std::borrow::Cow; 1use std::borrow::Cow;
2 2
3use syntax::{ast, AstToken, TextRange, TextSize}; 3use syntax::{ast, AstToken, TextRange, TextSize};
4use test_utils::mark;
5 4
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 5use crate::{AssistContext, AssistId, AssistKind, Assists};
7 6
@@ -149,7 +148,7 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
149 let internal_text = &text[token.text_range_between_quotes()? - text_range.start()]; 148 let internal_text = &text[token.text_range_between_quotes()? - text_range.start()];
150 149
151 if existing_hashes == required_hashes(internal_text) { 150 if existing_hashes == required_hashes(internal_text) {
152 mark::hit!(cant_remove_required_hash); 151 cov_mark::hit!(cant_remove_required_hash);
153 return None; 152 return None;
154 } 153 }
155 154
@@ -182,8 +181,6 @@ fn test_required_hashes() {
182 181
183#[cfg(test)] 182#[cfg(test)]
184mod tests { 183mod tests {
185 use test_utils::mark;
186
187 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 184 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
188 185
189 use super::*; 186 use super::*;
@@ -396,7 +393,7 @@ string"###;
396 393
397 #[test] 394 #[test]
398 fn cant_remove_required_hash() { 395 fn cant_remove_required_hash() {
399 mark::check!(cant_remove_required_hash); 396 cov_mark::check!(cant_remove_required_hash);
400 check_assist_not_applicable( 397 check_assist_not_applicable(
401 remove_hash, 398 remove_hash,
402 r##" 399 r##"
diff --git a/crates/ide_assists/src/handlers/remove_unused_param.rs b/crates/ide_assists/src/handlers/remove_unused_param.rs
index c961680e2..2699d2861 100644
--- a/crates/ide_assists/src/handlers/remove_unused_param.rs
+++ b/crates/ide_assists/src/handlers/remove_unused_param.rs
@@ -4,7 +4,7 @@ use syntax::{
4 ast::{self, ArgListOwner}, 4 ast::{self, ArgListOwner},
5 AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T, 5 AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
6}; 6};
7use test_utils::mark; 7
8use SyntaxKind::WHITESPACE; 8use SyntaxKind::WHITESPACE;
9 9
10use crate::{ 10use crate::{
@@ -49,7 +49,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
49 Definition::Local(local) 49 Definition::Local(local)
50 }; 50 };
51 if param_def.usages(&ctx.sema).at_least_one() { 51 if param_def.usages(&ctx.sema).at_least_one() {
52 mark::hit!(keep_used); 52 cov_mark::hit!(keep_used);
53 return None; 53 return None;
54 } 54 }
55 acc.add( 55 acc.add(
@@ -243,7 +243,7 @@ fn b2() { foo(9) }
243 243
244 #[test] 244 #[test]
245 fn keep_used() { 245 fn keep_used() {
246 mark::check!(keep_used); 246 cov_mark::check!(keep_used);
247 check_assist_not_applicable( 247 check_assist_not_applicable(
248 remove_unused_param, 248 remove_unused_param,
249 r#" 249 r#"
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs
index fba7d6ddb..794c89323 100644
--- a/crates/ide_assists/src/handlers/reorder_fields.rs
+++ b/crates/ide_assists/src/handlers/reorder_fields.rs
@@ -4,7 +4,6 @@ use rustc_hash::FxHashMap;
4use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; 4use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; 6use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
7use test_utils::mark;
8 7
9use crate::{AssistContext, AssistId, AssistKind, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
10 9
@@ -39,7 +38,7 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 }); 38 });
40 39
41 if sorted_fields == fields { 40 if sorted_fields == fields {
42 mark::hit!(reorder_sorted_fields); 41 cov_mark::hit!(reorder_sorted_fields);
43 return None; 42 return None;
44 } 43 }
45 44
@@ -109,15 +108,13 @@ fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashM
109 108
110#[cfg(test)] 109#[cfg(test)]
111mod tests { 110mod tests {
112 use test_utils::mark;
113
114 use crate::tests::{check_assist, check_assist_not_applicable}; 111 use crate::tests::{check_assist, check_assist_not_applicable};
115 112
116 use super::*; 113 use super::*;
117 114
118 #[test] 115 #[test]
119 fn reorder_sorted_fields() { 116 fn reorder_sorted_fields() {
120 mark::check!(reorder_sorted_fields); 117 cov_mark::check!(reorder_sorted_fields);
121 check_assist_not_applicable( 118 check_assist_not_applicable(
122 reorder_fields, 119 reorder_fields,
123 r#" 120 r#"
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs
index 309f291c8..edf4b0bfe 100644
--- a/crates/ide_assists/src/handlers/reorder_impl.rs
+++ b/crates/ide_assists/src/handlers/reorder_impl.rs
@@ -8,7 +8,6 @@ use syntax::{
8 ast::{self, NameOwner}, 8 ast::{self, NameOwner},
9 AstNode, 9 AstNode,
10}; 10};
11use test_utils::mark;
12 11
13use crate::{AssistContext, AssistId, AssistKind, Assists}; 12use crate::{AssistContext, AssistId, AssistKind, Assists};
14 13
@@ -71,7 +70,7 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
71 70
72 // Don't edit already sorted methods: 71 // Don't edit already sorted methods:
73 if methods == sorted { 72 if methods == sorted {
74 mark::hit!(not_applicable_if_sorted); 73 cov_mark::hit!(not_applicable_if_sorted);
75 return None; 74 return None;
76 } 75 }
77 76
@@ -121,15 +120,13 @@ fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
121 120
122#[cfg(test)] 121#[cfg(test)]
123mod tests { 122mod tests {
124 use test_utils::mark;
125
126 use crate::tests::{check_assist, check_assist_not_applicable}; 123 use crate::tests::{check_assist, check_assist_not_applicable};
127 124
128 use super::*; 125 use super::*;
129 126
130 #[test] 127 #[test]
131 fn not_applicable_if_sorted() { 128 fn not_applicable_if_sorted() {
132 mark::check!(not_applicable_if_sorted); 129 cov_mark::check!(not_applicable_if_sorted);
133 check_assist_not_applicable( 130 check_assist_not_applicable(
134 reorder_impl, 131 reorder_impl,
135 r#" 132 r#"
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index c69bc5cac..88fe2fe90 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,5 +1,6 @@
1use hir::ModuleDef;
1use ide_db::helpers::mod_path_to_ast; 2use ide_db::helpers::mod_path_to_ast;
2use ide_db::imports_locator; 3use ide_db::items_locator;
3use itertools::Itertools; 4use itertools::Itertools;
4use syntax::{ 5use syntax::{
5 ast::{self, make, AstNode, NameOwner}, 6 ast::{self, make, AstNode, NameOwner},
@@ -64,22 +65,20 @@ pub(crate) fn replace_derive_with_manual_impl(
64 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; 65 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
65 let current_crate = current_module.krate(); 66 let current_crate = current_module.krate();
66 67
67 let found_traits = imports_locator::find_exact_imports( 68 let found_traits =
68 &ctx.sema, 69 items_locator::with_exact_name(&ctx.sema, current_crate, trait_token.text().to_string())
69 current_crate, 70 .into_iter()
70 trait_token.text().to_string(), 71 .filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) {
71 ) 72 ModuleDef::Trait(trait_) => Some(trait_),
72 .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { 73 _ => None,
73 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), 74 })
74 _ => None, 75 .flat_map(|trait_| {
75 }) 76 current_module
76 .flat_map(|trait_| { 77 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
77 current_module 78 .as_ref()
78 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) 79 .map(mod_path_to_ast)
79 .as_ref() 80 .zip(Some(trait_))
80 .map(mod_path_to_ast) 81 });
81 .zip(Some(trait_))
82 });
83 82
84 let mut no_traits_found = true; 83 let mut no_traits_found = true;
85 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 84 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
diff --git a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
index 27da28bc0..50b05ab0b 100644
--- a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
+++ b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
@@ -3,7 +3,6 @@ use hir::known;
3use ide_db::helpers::FamousDefs; 3use ide_db::helpers::FamousDefs;
4use stdx::format_to; 4use stdx::format_to;
5use syntax::{ast, AstNode}; 5use syntax::{ast, AstNode};
6use test_utils::mark;
7 6
8use crate::{AssistContext, AssistId, AssistKind, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
9 8
@@ -34,7 +33,7 @@ pub(crate) fn replace_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistCont
34 let pat = for_loop.pat()?; 33 let pat = for_loop.pat()?;
35 let body = for_loop.loop_body()?; 34 let body = for_loop.loop_body()?;
36 if body.syntax().text_range().start() < ctx.offset() { 35 if body.syntax().text_range().start() < ctx.offset() {
37 mark::hit!(not_available_in_body); 36 cov_mark::hit!(not_available_in_body);
38 return None; 37 return None;
39 } 38 }
40 39
@@ -187,7 +186,7 @@ fn main() {
187 186
188 #[test] 187 #[test]
189 fn not_available_in_body() { 188 fn not_available_in_body() {
190 mark::check!(not_available_in_body); 189 cov_mark::check!(not_available_in_body);
191 check_assist_not_applicable( 190 check_assist_not_applicable(
192 replace_for_loop_with_for_each, 191 replace_for_loop_with_for_each,
193 r" 192 r"
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
index f3bc6cf39..36d2e0331 100644
--- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -1,6 +1,5 @@
1use ide_db::helpers::insert_use::{insert_use, ImportScope}; 1use ide_db::helpers::insert_use::{insert_use, ImportScope};
2use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; 2use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode};
3use test_utils::mark;
4 3
5use crate::{AssistContext, AssistId, AssistKind, Assists}; 4use crate::{AssistContext, AssistId, AssistKind, Assists};
6 5
@@ -27,7 +26,7 @@ pub(crate) fn replace_qualified_name_with_use(
27 return None; 26 return None;
28 } 27 }
29 if path.qualifier().is_none() { 28 if path.qualifier().is_none() {
30 mark::hit!(dont_import_trivial_paths); 29 cov_mark::hit!(dont_import_trivial_paths);
31 return None; 30 return None;
32 } 31 }
33 32
@@ -44,7 +43,7 @@ pub(crate) fn replace_qualified_name_with_use(
44 let mut rewriter = SyntaxRewriter::default(); 43 let mut rewriter = SyntaxRewriter::default();
45 shorten_paths(&mut rewriter, syntax.clone(), &path); 44 shorten_paths(&mut rewriter, syntax.clone(), &path);
46 if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { 45 if let Some(ref import_scope) = ImportScope::from(syntax.clone()) {
47 rewriter += insert_use(import_scope, path, ctx.config.insert_use.merge); 46 rewriter += insert_use(import_scope, path, ctx.config.insert_use);
48 builder.rewrite(rewriter); 47 builder.rewrite(rewriter);
49 } 48 }
50 }, 49 },
@@ -458,7 +457,7 @@ impl Debug for Foo {
458 457
459 #[test] 458 #[test]
460 fn dont_import_trivial_paths() { 459 fn dont_import_trivial_paths() {
461 mark::check!(dont_import_trivial_paths); 460 cov_mark::check!(dont_import_trivial_paths);
462 check_assist_not_applicable( 461 check_assist_not_applicable(
463 replace_qualified_name_with_use, 462 replace_qualified_name_with_use,
464 r" 463 r"
diff --git a/crates/ide_assists/src/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs
index 3dbef8e51..616af7c2e 100644
--- a/crates/ide_assists/src/handlers/unmerge_use.rs
+++ b/crates/ide_assists/src/handlers/unmerge_use.rs
@@ -3,7 +3,6 @@ use syntax::{
3 ast::{self, edit::AstNodeEdit, VisibilityOwner}, 3 ast::{self, edit::AstNodeEdit, VisibilityOwner},
4 AstNode, SyntaxKind, 4 AstNode, SyntaxKind,
5}; 5};
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 assist_context::{AssistContext, Assists}, 8 assist_context::{AssistContext, Assists},
@@ -27,7 +26,7 @@ pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
27 26
28 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?; 27 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
29 if tree_list.use_trees().count() < 2 { 28 if tree_list.use_trees().count() < 2 {
30 mark::hit!(skip_single_use_item); 29 cov_mark::hit!(skip_single_use_item);
31 return None; 30 return None;
32 } 31 }
33 32
@@ -89,7 +88,7 @@ mod tests {
89 88
90 #[test] 89 #[test]
91 fn skip_single_use_item() { 90 fn skip_single_use_item() {
92 mark::check!(skip_single_use_item); 91 cov_mark::check!(skip_single_use_item);
93 check_assist_not_applicable( 92 check_assist_not_applicable(
94 unmerge_use, 93 unmerge_use,
95 r" 94 r"
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
index fec16fc49..e838630ea 100644
--- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
+++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
@@ -4,7 +4,6 @@ use syntax::{
4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, 4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
5 match_ast, AstNode, SyntaxNode, 5 match_ast, AstNode, SyntaxNode,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{AssistContext, AssistId, AssistKind, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
10 9
@@ -39,7 +38,7 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext)
39 let first_part_ret_type = ret_type_str.splitn(2, '<').next(); 38 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
40 if let Some(ret_type_first_part) = first_part_ret_type { 39 if let Some(ret_type_first_part) = first_part_ret_type {
41 if ret_type_first_part.ends_with("Result") { 40 if ret_type_first_part.ends_with("Result") {
42 mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); 41 cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result);
43 return None; 42 return None;
44 } 43 }
45 } 44 }
@@ -367,7 +366,7 @@ fn foo() -> std::result::Result<i32$0, String> {
367 366
368 #[test] 367 #[test]
369 fn wrap_return_type_in_result_simple_return_type_already_result() { 368 fn wrap_return_type_in_result_simple_return_type_already_result() {
370 mark::check!(wrap_return_type_in_result_simple_return_type_already_result); 369 cov_mark::check!(wrap_return_type_in_result_simple_return_type_already_result);
371 check_assist_not_applicable( 370 check_assist_not_applicable(
372 wrap_return_type_in_result, 371 wrap_return_type_in_result,
373 r#" 372 r#"
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index ea62d5f5d..f1aab74d4 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -116,6 +116,7 @@ mod handlers {
116 mod change_visibility; 116 mod change_visibility;
117 mod convert_integer_literal; 117 mod convert_integer_literal;
118 mod convert_comment_block; 118 mod convert_comment_block;
119 mod convert_iter_for_each_to_for;
119 mod early_return; 120 mod early_return;
120 mod expand_glob_import; 121 mod expand_glob_import;
121 mod extract_function; 122 mod extract_function;
@@ -181,6 +182,7 @@ mod handlers {
181 change_visibility::change_visibility, 182 change_visibility::change_visibility,
182 convert_integer_literal::convert_integer_literal, 183 convert_integer_literal::convert_integer_literal,
183 convert_comment_block::convert_comment_block, 184 convert_comment_block::convert_comment_block,
185 convert_iter_for_each_to_for::convert_iter_for_each_to_for,
184 early_return::convert_to_guarded_return, 186 early_return::convert_to_guarded_return,
185 expand_glob_import::expand_glob_import, 187 expand_glob_import::expand_glob_import,
186 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 188 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index b7f616760..a7a923beb 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -23,6 +23,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
23 insert_use: InsertUseConfig { 23 insert_use: InsertUseConfig {
24 merge: Some(MergeBehavior::Full), 24 merge: Some(MergeBehavior::Full),
25 prefix_kind: hir::PrefixKind::Plain, 25 prefix_kind: hir::PrefixKind::Plain,
26 group: true,
26 }, 27 },
27}; 28};
28 29
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 304b5798f..3f77edd8d 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -206,6 +206,36 @@ const _: i32 = 0b1010;
206} 206}
207 207
208#[test] 208#[test]
209fn doctest_convert_iter_for_each_to_for() {
210 check_doc_test(
211 "convert_iter_for_each_to_for",
212 r#####"
213//- /lib.rs crate:core
214pub mod iter { pub mod traits { pub mod iterator { pub trait Iterator {} } } }
215pub struct SomeIter;
216impl self::iter::traits::iterator::Iterator for SomeIter {}
217//- /lib.rs crate:main deps:core
218use core::SomeIter;
219fn main() {
220 let iter = SomeIter;
221 iter.for_each$0(|(x, y)| {
222 println!("x: {}, y: {}", x, y);
223 });
224}
225"#####,
226 r#####"
227use core::SomeIter;
228fn main() {
229 let iter = SomeIter;
230 for (x, y) in iter {
231 println!("x: {}, y: {}", x, y);
232 }
233}
234"#####,
235 )
236}
237
238#[test]
209fn doctest_convert_to_guarded_return() { 239fn doctest_convert_to_guarded_return() {
210 check_doc_test( 240 check_doc_test(
211 "convert_to_guarded_return", 241 "convert_to_guarded_return",
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index c09101ccb..585ecca50 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13itertools = "0.10.0" 14itertools = "0.10.0"
14log = "0.4.8" 15log = "0.4.8"
15rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
@@ -21,11 +22,11 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
21base_db = { path = "../base_db", version = "0.0.0" } 22base_db = { path = "../base_db", version = "0.0.0" }
22ide_db = { path = "../ide_db", version = "0.0.0" } 23ide_db = { path = "../ide_db", version = "0.0.0" }
23profile = { path = "../profile", version = "0.0.0" } 24profile = { path = "../profile", version = "0.0.0" }
24test_utils = { path = "../test_utils", version = "0.0.0" }
25 25
26# completions crate should depend only on the top-level `hir` package. if you need 26# completions crate should depend only on the top-level `hir` package. if you need
27# something from some `hir_xxx` subpackage, reexport the API via `hir`. 27# something from some `hir_xxx` subpackage, reexport the API via `hir`.
28hir = { path = "../hir", version = "0.0.0" } 28hir = { path = "../hir", version = "0.0.0" }
29 29
30[dev-dependencies] 30[dev-dependencies]
31test_utils = { path = "../test_utils" }
31expect-test = "1.1" 32expect-test = "1.1"
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index cb05e85fc..e846678b4 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -45,15 +45,15 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
45 CompletionKind::Attribute, 45 CompletionKind::Attribute,
46 ctx.source_range(), 46 ctx.source_range(),
47 attr_completion.label, 47 attr_completion.label,
48 ) 48 );
49 .kind(CompletionItemKind::Attribute); 49 item.kind(CompletionItemKind::Attribute);
50 50
51 if let Some(lookup) = attr_completion.lookup { 51 if let Some(lookup) = attr_completion.lookup {
52 item = item.lookup_by(lookup); 52 item.lookup_by(lookup);
53 } 53 }
54 54
55 if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) { 55 if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) {
56 item = item.insert_snippet(cap, snippet); 56 item.insert_snippet(cap, snippet);
57 } 57 }
58 58
59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { 59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
@@ -168,16 +168,20 @@ fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input:
168 ); 168 );
169 let lookup = components.join(", "); 169 let lookup = components.join(", ");
170 let label = components.iter().rev().join(", "); 170 let label = components.iter().rev().join(", ");
171 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label) 171 let mut item =
172 .lookup_by(lookup) 172 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
173 .kind(CompletionItemKind::Attribute) 173 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
174 .add_to(acc) 174 item.add_to(acc);
175 } 175 }
176 176
177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { 177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
178 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), custom_derive_name) 178 let mut item = CompletionItem::new(
179 .kind(CompletionItemKind::Attribute) 179 CompletionKind::Attribute,
180 .add_to(acc) 180 ctx.source_range(),
181 custom_derive_name,
182 );
183 item.kind(CompletionItemKind::Attribute);
184 item.add_to(acc);
181 } 185 }
182 } 186 }
183} 187}
@@ -193,14 +197,13 @@ fn complete_lint(
193 .into_iter() 197 .into_iter()
194 .filter(|completion| !existing_lints.contains(completion.label)) 198 .filter(|completion| !existing_lints.contains(completion.label))
195 { 199 {
196 CompletionItem::new( 200 let mut item = CompletionItem::new(
197 CompletionKind::Attribute, 201 CompletionKind::Attribute,
198 ctx.source_range(), 202 ctx.source_range(),
199 lint_completion.label, 203 lint_completion.label,
200 ) 204 );
201 .kind(CompletionItemKind::Attribute) 205 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
202 .detail(lint_completion.description) 206 item.add_to(acc)
203 .add_to(acc)
204 } 207 }
205 } 208 }
206} 209}
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index 084d7721d..5ee9a9f07 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -2,7 +2,6 @@
2 2
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, Type};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark;
6 5
7use crate::{context::CompletionContext, Completions}; 6use crate::{context::CompletionContext, Completions};
8 7
@@ -19,7 +18,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
19 }; 18 };
20 19
21 if ctx.is_call { 20 if ctx.is_call {
22 mark::hit!(test_no_struct_field_completion_for_method_call); 21 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else { 22 } else {
24 complete_fields(acc, ctx, &receiver_ty); 23 complete_fields(acc, ctx, &receiver_ty);
25 } 24 }
@@ -62,7 +61,6 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
62#[cfg(test)] 61#[cfg(test)]
63mod tests { 62mod tests {
64 use expect_test::{expect, Expect}; 63 use expect_test::{expect, Expect};
65 use test_utils::mark;
66 64
67 use crate::{test_utils::completion_list, CompletionKind}; 65 use crate::{test_utils::completion_list, CompletionKind};
68 66
@@ -122,7 +120,7 @@ impl A {
122 120
123 #[test] 121 #[test]
124 fn test_no_struct_field_completion_for_method_call() { 122 fn test_no_struct_field_completion_for_method_call() {
125 mark::check!(test_no_struct_field_completion_for_method_call); 123 cov_mark::check!(test_no_struct_field_completion_for_method_call);
126 check( 124 check(
127 r#" 125 r#"
128struct A { the_field: u32 } 126struct A { the_field: u32 }
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index da8375af9..391a11c91 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -21,6 +21,46 @@
21//! ``` 21//! ```
22//! 22//!
23//! Also completes associated items, that require trait imports. 23//! Also completes associated items, that require trait imports.
24//! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account.
25//! Currently, only the imports with their import path ending with the whole qialifier will be proposed
26//! (no fuzzy matching for qualifier).
27//!
28//! ```
29//! mod foo {
30//! pub mod bar {
31//! pub struct Item;
32//!
33//! impl Item {
34//! pub const TEST_ASSOC: usize = 3;
35//! }
36//! }
37//! }
38//!
39//! fn main() {
40//! bar::Item::TEST_A$0
41//! }
42//! ```
43//! ->
44//! ```
45//! use foo::bar;
46//!
47//! mod foo {
48//! pub mod bar {
49//! pub struct Item;
50//!
51//! impl Item {
52//! pub const TEST_ASSOC: usize = 3;
53//! }
54//! }
55//! }
56//!
57//! fn main() {
58//! bar::Item::TEST_ASSOC
59//! }
60//! ```
61//!
62//! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path,
63//! no imports will be proposed.
24//! 64//!
25//! .Fuzzy search details 65//! .Fuzzy search details
26//! 66//!
@@ -48,14 +88,13 @@
48//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 88//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
49//! capability enabled. 89//! capability enabled.
50 90
51use hir::{AsAssocItem, ModPath, ScopeDef}; 91use hir::ModPath;
52use ide_db::helpers::{ 92use ide_db::helpers::{
53 import_assets::{ImportAssets, ImportCandidate}, 93 import_assets::{ImportAssets, ImportCandidate},
54 insert_use::ImportScope, 94 insert_use::ImportScope,
55}; 95};
56use rustc_hash::FxHashSet; 96use itertools::Itertools;
57use syntax::{AstNode, SyntaxNode, T}; 97use syntax::{AstNode, SyntaxNode, T};
58use test_utils::mark;
59 98
60use crate::{ 99use crate::{
61 context::CompletionContext, 100 context::CompletionContext,
@@ -93,50 +132,26 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
93 &ctx.sema, 132 &ctx.sema,
94 )?; 133 )?;
95 134
96 let scope_definitions = scope_definitions(ctx); 135 acc.add_all(
97 let mut all_mod_paths = import_assets 136 import_assets
98 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) 137 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
99 .into_iter() 138 .into_iter()
100 .map(|(mod_path, item_in_ns)| { 139 .sorted_by_key(|located_import| {
101 let scope_item = match item_in_ns { 140 compute_fuzzy_completion_order_key(
102 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), 141 &located_import.import_path,
103 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), 142 &user_input_lowercased,
104 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), 143 )
105 }; 144 })
106 (mod_path, scope_item) 145 .filter_map(|import| {
107 }) 146 render_resolution_with_import(
108 .filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def)) 147 RenderContext::new(ctx),
109 .collect::<Vec<_>>(); 148 ImportEdit { import, scope: import_scope.clone() },
110 all_mod_paths.sort_by_cached_key(|(mod_path, _)| { 149 )
111 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) 150 }),
112 }); 151 );
113
114 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
115 let import_for_trait_assoc_item = match definition {
116 ScopeDef::ModuleDef(module_def) => module_def
117 .as_assoc_item(ctx.db)
118 .and_then(|assoc| assoc.containing_trait(ctx.db))
119 .is_some(),
120 _ => false,
121 };
122 let import_edit = ImportEdit {
123 import_path,
124 import_scope: import_scope.clone(),
125 import_for_trait_assoc_item,
126 };
127 render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
128 }));
129 Some(()) 152 Some(())
130} 153}
131 154
132fn scope_definitions(ctx: &CompletionContext) -> FxHashSet<ScopeDef> {
133 let mut scope_definitions = FxHashSet::default();
134 ctx.scope.process_all_names(&mut |_, scope_def| {
135 scope_definitions.insert(scope_def);
136 });
137 scope_definitions
138}
139
140pub(crate) fn position_for_import<'a>( 155pub(crate) fn position_for_import<'a>(
141 ctx: &'a CompletionContext, 156 ctx: &'a CompletionContext,
142 import_candidate: Option<&ImportCandidate>, 157 import_candidate: Option<&ImportCandidate>,
@@ -161,23 +176,30 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
161 current_module, 176 current_module,
162 ctx.sema.type_of_expr(dot_receiver)?, 177 ctx.sema.type_of_expr(dot_receiver)?,
163 fuzzy_name, 178 fuzzy_name,
179 dot_receiver.syntax().clone(),
164 ) 180 )
165 } else { 181 } else {
166 let fuzzy_name_length = fuzzy_name.len(); 182 let fuzzy_name_length = fuzzy_name.len();
183 let approximate_node = match current_module.definition_source(ctx.db).value {
184 hir::ModuleSource::SourceFile(s) => s.syntax().clone(),
185 hir::ModuleSource::Module(m) => m.syntax().clone(),
186 hir::ModuleSource::BlockExpr(b) => b.syntax().clone(),
187 };
167 let assets_for_path = ImportAssets::for_fuzzy_path( 188 let assets_for_path = ImportAssets::for_fuzzy_path(
168 current_module, 189 current_module,
169 ctx.path_qual.clone(), 190 ctx.path_qual.clone(),
170 fuzzy_name, 191 fuzzy_name,
171 &ctx.sema, 192 &ctx.sema,
172 ); 193 approximate_node,
194 )?;
173 195
174 if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_)) 196 if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
175 && fuzzy_name_length < 2 197 && fuzzy_name_length < 2
176 { 198 {
177 mark::hit!(ignore_short_input_for_path); 199 cov_mark::hit!(ignore_short_input_for_path);
178 None 200 None
179 } else { 201 } else {
180 assets_for_path 202 Some(assets_for_path)
181 } 203 }
182 } 204 }
183} 205}
@@ -186,12 +208,12 @@ fn compute_fuzzy_completion_order_key(
186 proposed_mod_path: &ModPath, 208 proposed_mod_path: &ModPath,
187 user_input_lowercased: &str, 209 user_input_lowercased: &str,
188) -> usize { 210) -> usize {
189 mark::hit!(certain_fuzzy_order_test); 211 cov_mark::hit!(certain_fuzzy_order_test);
190 let proposed_import_name = match proposed_mod_path.segments().last() { 212 let import_name = match proposed_mod_path.segments().last() {
191 Some(name) => name.to_string().to_lowercase(), 213 Some(name) => name.to_string().to_lowercase(),
192 None => return usize::MAX, 214 None => return usize::MAX,
193 }; 215 };
194 match proposed_import_name.match_indices(user_input_lowercased).next() { 216 match import_name.match_indices(user_input_lowercased).next() {
195 Some((first_matching_index, _)) => first_matching_index, 217 Some((first_matching_index, _)) => first_matching_index,
196 None => usize::MAX, 218 None => usize::MAX,
197 } 219 }
@@ -200,7 +222,6 @@ fn compute_fuzzy_completion_order_key(
200#[cfg(test)] 222#[cfg(test)]
201mod tests { 223mod tests {
202 use expect_test::{expect, Expect}; 224 use expect_test::{expect, Expect};
203 use test_utils::mark;
204 225
205 use crate::{ 226 use crate::{
206 item::CompletionKind, 227 item::CompletionKind,
@@ -295,7 +316,7 @@ fn main() {
295 316
296 #[test] 317 #[test]
297 fn short_paths_are_ignored() { 318 fn short_paths_are_ignored() {
298 mark::check!(ignore_short_input_for_path); 319 cov_mark::check!(ignore_short_input_for_path);
299 320
300 check( 321 check(
301 r#" 322 r#"
@@ -319,7 +340,7 @@ fn main() {
319 340
320 #[test] 341 #[test]
321 fn fuzzy_completions_come_in_specific_order() { 342 fn fuzzy_completions_come_in_specific_order() {
322 mark::check!(certain_fuzzy_order_test); 343 cov_mark::check!(certain_fuzzy_order_test);
323 check( 344 check(
324 r#" 345 r#"
325//- /lib.rs crate:dep 346//- /lib.rs crate:dep
@@ -775,4 +796,155 @@ fn main() {
775}"#, 796}"#,
776 ); 797 );
777 } 798 }
799
800 #[test]
801 fn unresolved_qualifier() {
802 let fixture = r#"
803mod foo {
804 pub mod bar {
805 pub mod baz {
806 pub struct Item;
807 }
808 }
809}
810
811fn main() {
812 bar::baz::Ite$0
813}"#;
814
815 check(
816 fixture,
817 expect![[r#"
818 st foo::bar::baz::Item
819 "#]],
820 );
821
822 check_edit(
823 "Item",
824 fixture,
825 r#"
826 use foo::bar;
827
828 mod foo {
829 pub mod bar {
830 pub mod baz {
831 pub struct Item;
832 }
833 }
834 }
835
836 fn main() {
837 bar::baz::Item
838 }"#,
839 );
840 }
841
842 #[test]
843 fn unresolved_assoc_item_container() {
844 let fixture = r#"
845mod foo {
846 pub struct Item;
847
848 impl Item {
849 pub const TEST_ASSOC: usize = 3;
850 }
851}
852
853fn main() {
854 Item::TEST_A$0
855}"#;
856
857 check(
858 fixture,
859 expect![[r#"
860 ct TEST_ASSOC (foo::Item)
861 "#]],
862 );
863
864 check_edit(
865 "TEST_ASSOC",
866 fixture,
867 r#"
868use foo::Item;
869
870mod foo {
871 pub struct Item;
872
873 impl Item {
874 pub const TEST_ASSOC: usize = 3;
875 }
876}
877
878fn main() {
879 Item::TEST_ASSOC
880}"#,
881 );
882 }
883
884 #[test]
885 fn unresolved_assoc_item_container_with_path() {
886 let fixture = r#"
887mod foo {
888 pub mod bar {
889 pub struct Item;
890
891 impl Item {
892 pub const TEST_ASSOC: usize = 3;
893 }
894 }
895}
896
897fn main() {
898 bar::Item::TEST_A$0
899}"#;
900
901 check(
902 fixture,
903 expect![[r#"
904 ct TEST_ASSOC (foo::bar::Item)
905 "#]],
906 );
907
908 check_edit(
909 "TEST_ASSOC",
910 fixture,
911 r#"
912use foo::bar;
913
914mod foo {
915 pub mod bar {
916 pub struct Item;
917
918 impl Item {
919 pub const TEST_ASSOC: usize = 3;
920 }
921 }
922}
923
924fn main() {
925 bar::Item::TEST_ASSOC
926}"#,
927 );
928 }
929
930 #[test]
931 fn fuzzy_unresolved_path() {
932 check(
933 r#"
934mod foo {
935 pub mod bar {
936 pub struct Item;
937
938 impl Item {
939 pub const TEST_ASSOC: usize = 3;
940 }
941 }
942}
943
944fn main() {
945 bar::Ass$0
946}"#,
947 expect![[]],
948 )
949 }
778} 950}
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index 1bcc8727f..0243dce56 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -54,10 +54,9 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
54 } 54 }
55 55
56 params.into_iter().for_each(|(label, lookup)| { 56 params.into_iter().for_each(|(label, lookup)| {
57 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 57 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
58 .kind(CompletionItemKind::Binding) 58 item.kind(CompletionItemKind::Binding).lookup_by(lookup);
59 .lookup_by(lookup) 59 item.add_to(acc)
60 .add_to(acc)
61 }); 60 });
62} 61}
63 62
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 03c6dd454..b635e0ca3 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -3,7 +3,6 @@
3use std::iter; 3use std::iter;
4 4
5use syntax::SyntaxKind; 5use syntax::SyntaxKind;
6use test_utils::mark;
7 6
8use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; 7use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
9 8
@@ -13,21 +12,19 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
13 12
14 if ctx.use_item_syntax.is_some() { 13 if ctx.use_item_syntax.is_some() {
15 if ctx.path_qual.is_none() { 14 if ctx.path_qual.is_none() {
16 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::") 15 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "crate::");
17 .kind(CompletionItemKind::Keyword) 16 item.kind(CompletionItemKind::Keyword).insert_text("crate::");
18 .insert_text("crate::") 17 item.add_to(acc);
19 .add_to(acc);
20 } 18 }
21 CompletionItem::new(CompletionKind::Keyword, source_range, "self") 19 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "self");
22 .kind(CompletionItemKind::Keyword) 20 item.kind(CompletionItemKind::Keyword);
23 .add_to(acc); 21 item.add_to(acc);
24 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) 22 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier())
25 .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) 23 .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
26 { 24 {
27 CompletionItem::new(CompletionKind::Keyword, source_range, "super::") 25 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "super::");
28 .kind(CompletionItemKind::Keyword) 26 item.kind(CompletionItemKind::Keyword).insert_text("super::");
29 .insert_text("super::") 27 item.add_to(acc);
30 .add_to(acc);
31 } 28 }
32 } 29 }
33 30
@@ -35,11 +32,10 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
35 if let Some(receiver) = &ctx.dot_receiver { 32 if let Some(receiver) = &ctx.dot_receiver {
36 if let Some(ty) = ctx.sema.type_of_expr(receiver) { 33 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
37 if ty.impls_future(ctx.db) { 34 if ty.impls_future(ctx.db) {
38 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") 35 let mut item =
39 .kind(CompletionItemKind::Keyword) 36 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await");
40 .detail("expr.await") 37 item.kind(CompletionItemKind::Keyword).detail("expr.await").insert_text("await");
41 .insert_text("await") 38 item.add_to(acc);
42 .add_to(acc);
43 } 39 }
44 }; 40 };
45 } 41 }
@@ -47,11 +43,11 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
47 43
48pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 44pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
49 if ctx.token.kind() == SyntaxKind::COMMENT { 45 if ctx.token.kind() == SyntaxKind::COMMENT {
50 mark::hit!(no_keyword_completion_in_comments); 46 cov_mark::hit!(no_keyword_completion_in_comments);
51 return; 47 return;
52 } 48 }
53 if ctx.record_lit_syntax.is_some() { 49 if ctx.record_lit_syntax.is_some() {
54 mark::hit!(no_keyword_completion_in_record_lit); 50 cov_mark::hit!(no_keyword_completion_in_record_lit);
55 return; 51 return;
56 } 52 }
57 53
@@ -166,29 +162,31 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
166} 162}
167 163
168fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { 164fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
169 let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 165 let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw);
170 .kind(CompletionItemKind::Keyword); 166 item.kind(CompletionItemKind::Keyword);
171 let builder = match ctx.config.snippet_cap { 167
168 match ctx.config.snippet_cap {
172 Some(cap) => { 169 Some(cap) => {
173 let tmp; 170 let tmp;
174 let snippet = if snippet.ends_with('}') && ctx.incomplete_let { 171 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
175 mark::hit!(let_semi); 172 cov_mark::hit!(let_semi);
176 tmp = format!("{};", snippet); 173 tmp = format!("{};", snippet);
177 &tmp 174 &tmp
178 } else { 175 } else {
179 snippet 176 snippet
180 }; 177 };
181 builder.insert_snippet(cap, snippet) 178 item.insert_snippet(cap, snippet);
179 }
180 None => {
181 item.insert_text(if snippet.contains('$') { kw } else { snippet });
182 } 182 }
183 None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
184 }; 183 };
185 acc.add(builder.build()); 184 item.add_to(acc);
186} 185}
187 186
188#[cfg(test)] 187#[cfg(test)]
189mod tests { 188mod tests {
190 use expect_test::{expect, Expect}; 189 use expect_test::{expect, Expect};
191 use test_utils::mark;
192 190
193 use crate::{ 191 use crate::{
194 test_utils::{check_edit, completion_list}, 192 test_utils::{check_edit, completion_list},
@@ -494,7 +492,7 @@ fn quux() -> i32 {
494 492
495 #[test] 493 #[test]
496 fn no_keyword_completion_in_comments() { 494 fn no_keyword_completion_in_comments() {
497 mark::check!(no_keyword_completion_in_comments); 495 cov_mark::check!(no_keyword_completion_in_comments);
498 check( 496 check(
499 r#" 497 r#"
500fn test() { 498fn test() {
@@ -599,7 +597,7 @@ struct Foo {
599 597
600 #[test] 598 #[test]
601 fn skip_struct_initializer() { 599 fn skip_struct_initializer() {
602 mark::check!(no_keyword_completion_in_record_lit); 600 cov_mark::check!(no_keyword_completion_in_record_lit);
603 check( 601 check(
604 r#" 602 r#"
605struct Foo { 603struct Foo {
@@ -643,7 +641,7 @@ fn foo() {
643 641
644 #[test] 642 #[test]
645 fn let_semi() { 643 fn let_semi() {
646 mark::check!(let_semi); 644 cov_mark::check!(let_semi);
647 check_edit( 645 check_edit(
648 "match", 646 "match",
649 r#" 647 r#"
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
index 352fc7c77..4f9415736 100644
--- a/crates/ide_completion/src/completions/mod_.rs
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -80,9 +80,9 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op
80 if mod_under_caret.semicolon_token().is_none() { 80 if mod_under_caret.semicolon_token().is_none() {
81 label.push(';'); 81 label.push(';');
82 } 82 }
83 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) 83 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label);
84 .kind(SymbolKind::Module) 84 item.kind(SymbolKind::Module);
85 .add_to(acc) 85 item.add_to(acc)
86 }); 86 });
87 87
88 Some(()) 88 Some(())
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 9c34ed0b6..ac69b720a 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -187,6 +187,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
187 ctx, 187 ctx,
188 cap, 188 cap,
189 &dot_receiver, 189 &dot_receiver,
190 "err",
191 "Err(expr)",
192 &format!("Err({})", receiver_text),
193 )
194 .add_to(acc);
195
196 postfix_snippet(
197 ctx,
198 cap,
199 &dot_receiver,
190 "some", 200 "some",
191 "Some(expr)", 201 "Some(expr)",
192 &format!("Some({})", receiver_text), 202 &format!("Some({})", receiver_text),
@@ -287,10 +297,9 @@ fn postfix_snippet(
287 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end()); 297 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
288 TextEdit::replace(delete_range, snippet.to_string()) 298 TextEdit::replace(delete_range, snippet.to_string())
289 }; 299 };
290 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 300 let mut item = CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label);
291 .detail(detail) 301 item.detail(detail).kind(CompletionItemKind::Snippet).snippet_edit(cap, edit);
292 .kind(CompletionItemKind::Snippet) 302 item
293 .snippet_edit(cap, edit)
294} 303}
295 304
296#[cfg(test)] 305#[cfg(test)]
@@ -325,6 +334,7 @@ fn main() {
325 sn match match expr {} 334 sn match match expr {}
326 sn box Box::new(expr) 335 sn box Box::new(expr)
327 sn ok Ok(expr) 336 sn ok Ok(expr)
337 sn err Err(expr)
328 sn some Some(expr) 338 sn some Some(expr)
329 sn dbg dbg!(expr) 339 sn dbg dbg!(expr)
330 sn dbgr dbg!(&expr) 340 sn dbgr dbg!(&expr)
@@ -357,6 +367,7 @@ fn main() {
357 sn match match expr {} 367 sn match match expr {}
358 sn box Box::new(expr) 368 sn box Box::new(expr)
359 sn ok Ok(expr) 369 sn ok Ok(expr)
370 sn err Err(expr)
360 sn some Some(expr) 371 sn some Some(expr)
361 sn dbg dbg!(expr) 372 sn dbg dbg!(expr)
362 sn dbgr dbg!(&expr) 373 sn dbgr dbg!(&expr)
@@ -380,6 +391,7 @@ fn main() {
380 sn match match expr {} 391 sn match match expr {}
381 sn box Box::new(expr) 392 sn box Box::new(expr)
382 sn ok Ok(expr) 393 sn ok Ok(expr)
394 sn err Err(expr)
383 sn some Some(expr) 395 sn some Some(expr)
384 sn dbg dbg!(expr) 396 sn dbg dbg!(expr)
385 sn dbgr dbg!(&expr) 397 sn dbgr dbg!(&expr)
@@ -408,6 +420,7 @@ fn main() {
408 sn match match expr {} 420 sn match match expr {}
409 sn box Box::new(expr) 421 sn box Box::new(expr)
410 sn ok Ok(expr) 422 sn ok Ok(expr)
423 sn err Err(expr)
411 sn some Some(expr) 424 sn some Some(expr)
412 sn dbg dbg!(expr) 425 sn dbg dbg!(expr)
413 sn dbgr dbg!(&expr) 426 sn dbgr dbg!(&expr)
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 72fb757b1..df74b739e 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -3,7 +3,6 @@
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; 3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use syntax::AstNode; 5use syntax::AstNode;
6use test_utils::mark;
7 6
8use crate::{CompletionContext, Completions}; 7use crate::{CompletionContext, Completions};
9 8
@@ -39,7 +38,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
39 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { 38 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
40 if name_ref.syntax().text() == name.to_string().as_str() { 39 if name_ref.syntax().text() == name.to_string().as_str() {
41 // for `use self::foo$0`, don't suggest `foo` as a completion 40 // for `use self::foo$0`, don't suggest `foo` as a completion
42 mark::hit!(dont_complete_current_use); 41 cov_mark::hit!(dont_complete_current_use);
43 continue; 42 continue;
44 } 43 }
45 } 44 }
@@ -155,7 +154,6 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
155#[cfg(test)] 154#[cfg(test)]
156mod tests { 155mod tests {
157 use expect_test::{expect, Expect}; 156 use expect_test::{expect, Expect};
158 use test_utils::mark;
159 157
160 use crate::{ 158 use crate::{
161 test_utils::{check_edit, completion_list}, 159 test_utils::{check_edit, completion_list},
@@ -174,7 +172,7 @@ mod tests {
174 172
175 #[test] 173 #[test]
176 fn dont_complete_current_use() { 174 fn dont_complete_current_use() {
177 mark::check!(dont_complete_current_use); 175 cov_mark::check!(dont_complete_current_use);
178 check(r#"use self::foo$0;"#, expect![[""]]); 176 check(r#"use self::foo$0;"#, expect![[""]]);
179 } 177 }
180 178
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index 0a7927eb8..2f95b8687 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -22,16 +22,13 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
22 let completion_text = completion_text 22 let completion_text = completion_text
23 .strip_prefix(ctx.token.to_string().as_str()) 23 .strip_prefix(ctx.token.to_string().as_str())
24 .unwrap_or(completion_text); 24 .unwrap_or(completion_text);
25 acc.add( 25 let mut item = CompletionItem::new(
26 CompletionItem::new( 26 CompletionKind::Snippet,
27 CompletionKind::Snippet, 27 ctx.source_range(),
28 ctx.source_range(), 28 "..Default::default()",
29 "..Default::default()",
30 )
31 .insert_text(completion_text)
32 .kind(SymbolKind::Field)
33 .build(),
34 ); 29 );
30 item.insert_text(completion_text).kind(SymbolKind::Field);
31 item.add_to(acc);
35 } 32 }
36 33
37 missing_fields 34 missing_fields
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index df17a15c5..7f7830976 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -8,9 +8,9 @@ use crate::{
8}; 8};
9 9
10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { 10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
11 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) 11 let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label);
12 .insert_snippet(cap, snippet) 12 item.insert_snippet(cap, snippet).kind(CompletionItemKind::Snippet);
13 .kind(CompletionItemKind::Snippet) 13 item
14} 14}
15 15
16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { 16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
@@ -35,7 +35,7 @@ pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte
35 None => return, 35 None => return,
36 }; 36 };
37 37
38 snippet( 38 let mut item = snippet(
39 ctx, 39 ctx,
40 cap, 40 cap,
41 "tmod (Test module)", 41 "tmod (Test module)",
@@ -49,11 +49,11 @@ mod tests {
49 $0 49 $0
50 } 50 }
51}", 51}",
52 ) 52 );
53 .lookup_by("tmod") 53 item.lookup_by("tmod");
54 .add_to(acc); 54 item.add_to(acc);
55 55
56 snippet( 56 let mut item = snippet(
57 ctx, 57 ctx,
58 cap, 58 cap,
59 "tfn (Test function)", 59 "tfn (Test function)",
@@ -62,11 +62,12 @@ mod tests {
62fn ${1:feature}() { 62fn ${1:feature}() {
63 $0 63 $0
64}", 64}",
65 ) 65 );
66 .lookup_by("tfn") 66 item.lookup_by("tfn");
67 .add_to(acc); 67 item.add_to(acc);
68 68
69 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); 69 let item = snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}");
70 item.add_to(acc);
70} 71}
71 72
72#[cfg(test)] 73#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index b999540b8..5a7361f8e 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -145,9 +145,8 @@ fn add_function_impl(
145 format!("fn {}(..)", fn_name) 145 format!("fn {}(..)", fn_name)
146 }; 146 };
147 147
148 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 148 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
149 .lookup_by(fn_name) 149 item.lookup_by(fn_name).set_documentation(func.docs(ctx.db));
150 .set_documentation(func.docs(ctx.db));
151 150
152 let completion_kind = if func.self_param(ctx.db).is_some() { 151 let completion_kind = if func.self_param(ctx.db).is_some() {
153 CompletionItemKind::Method 152 CompletionItemKind::Method
@@ -161,15 +160,15 @@ fn add_function_impl(
161 match ctx.config.snippet_cap { 160 match ctx.config.snippet_cap {
162 Some(cap) => { 161 Some(cap) => {
163 let snippet = format!("{} {{\n $0\n}}", function_decl); 162 let snippet = format!("{} {{\n $0\n}}", function_decl);
164 builder.snippet_edit(cap, TextEdit::replace(range, snippet)) 163 item.snippet_edit(cap, TextEdit::replace(range, snippet));
165 } 164 }
166 None => { 165 None => {
167 let header = format!("{} {{", function_decl); 166 let header = format!("{} {{", function_decl);
168 builder.text_edit(TextEdit::replace(range, header)) 167 item.text_edit(TextEdit::replace(range, header));
169 } 168 }
170 } 169 };
171 .kind(completion_kind) 170 item.kind(completion_kind);
172 .add_to(acc); 171 item.add_to(acc);
173 } 172 }
174} 173}
175 174
@@ -185,12 +184,12 @@ fn add_type_alias_impl(
185 184
186 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); 185 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
187 186
188 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 187 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
189 .text_edit(TextEdit::replace(range, snippet)) 188 item.text_edit(TextEdit::replace(range, snippet))
190 .lookup_by(alias_name) 189 .lookup_by(alias_name)
191 .kind(SymbolKind::TypeAlias) 190 .kind(SymbolKind::TypeAlias)
192 .set_documentation(type_alias.docs(ctx.db)) 191 .set_documentation(type_alias.docs(ctx.db));
193 .add_to(acc); 192 item.add_to(acc);
194} 193}
195 194
196fn add_const_impl( 195fn add_const_impl(
@@ -208,12 +207,13 @@ fn add_const_impl(
208 let range = 207 let range =
209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); 208 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
210 209
211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 210 let mut item =
212 .text_edit(TextEdit::replace(range, snippet)) 211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
212 item.text_edit(TextEdit::replace(range, snippet))
213 .lookup_by(const_name) 213 .lookup_by(const_name)
214 .kind(SymbolKind::Const) 214 .kind(SymbolKind::Const)
215 .set_documentation(const_.docs(ctx.db)) 215 .set_documentation(const_.docs(ctx.db));
216 .add_to(acc); 216 item.add_to(acc);
217 } 217 }
218 } 218 }
219} 219}
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index e9d0ff665..044dfd160 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -2,7 +2,6 @@
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use syntax::AstNode; 4use syntax::AstNode;
5use test_utils::mark;
6 5
7use crate::{CompletionContext, Completions}; 6use crate::{CompletionContext, Completions};
8 7
@@ -30,13 +29,13 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
30 29
31 ctx.scope.process_all_names(&mut |name, res| { 30 ctx.scope.process_all_names(&mut |name, res| {
32 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { 31 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
33 mark::hit!(skip_lifetime_completion); 32 cov_mark::hit!(skip_lifetime_completion);
34 return; 33 return;
35 } 34 }
36 if ctx.use_item_syntax.is_some() { 35 if ctx.use_item_syntax.is_some() {
37 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 36 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
38 if name_ref.syntax().text() == name.to_string().as_str() { 37 if name_ref.syntax().text() == name.to_string().as_str() {
39 mark::hit!(self_fulfilling_completion); 38 cov_mark::hit!(self_fulfilling_completion);
40 return; 39 return;
41 } 40 }
42 } 41 }
@@ -48,7 +47,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
48#[cfg(test)] 47#[cfg(test)]
49mod tests { 48mod tests {
50 use expect_test::{expect, Expect}; 49 use expect_test::{expect, Expect};
51 use test_utils::mark;
52 50
53 use crate::{ 51 use crate::{
54 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG}, 52 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
@@ -66,7 +64,7 @@ mod tests {
66 64
67 #[test] 65 #[test]
68 fn self_fulfilling_completion() { 66 fn self_fulfilling_completion() {
69 mark::check!(self_fulfilling_completion); 67 cov_mark::check!(self_fulfilling_completion);
70 check( 68 check(
71 r#" 69 r#"
72use foo$0 70use foo$0
@@ -185,7 +183,7 @@ fn quux() {
185 183
186 #[test] 184 #[test]
187 fn completes_if_prefix_is_keyword() { 185 fn completes_if_prefix_is_keyword() {
188 mark::check!(completes_if_prefix_is_keyword); 186 cov_mark::check!(completes_if_prefix_is_keyword);
189 check_edit( 187 check_edit(
190 "wherewolf", 188 "wherewolf",
191 r#" 189 r#"
@@ -223,7 +221,7 @@ fn main() {
223 221
224 #[test] 222 #[test]
225 fn does_not_complete_lifetimes() { 223 fn does_not_complete_lifetimes() {
226 mark::check!(skip_lifetime_completion); 224 cov_mark::check!(skip_lifetime_completion);
227 check( 225 check(
228 r#"fn quux<'a>() { $0 }"#, 226 r#"fn quux<'a>() { $0 }"#,
229 expect![[r#" 227 expect![[r#"
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 3db357855..17d9a3adf 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -7,7 +7,7 @@ use syntax::{
7 algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode, 7 algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode,
8 SyntaxToken, TextRange, TextSize, 8 SyntaxToken, TextRange, TextSize,
9}; 9};
10use test_utils::mark; 10
11use text_edit::Indel; 11use text_edit::Indel;
12 12
13use crate::{ 13use crate::{
@@ -240,7 +240,7 @@ impl<'a> CompletionContext<'a> {
240 // check kind of macro-expanded token, but use range of original token 240 // check kind of macro-expanded token, but use range of original token
241 let kind = self.token.kind(); 241 let kind = self.token.kind();
242 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { 242 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() {
243 mark::hit!(completes_if_prefix_is_keyword); 243 cov_mark::hit!(completes_if_prefix_is_keyword);
244 self.original_token.text_range() 244 self.original_token.text_range()
245 } else { 245 } else {
246 TextRange::empty(self.position.offset) 246 TextRange::empty(self.position.offset)
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 884711f11..3febab32b 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -2,15 +2,16 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use hir::{Documentation, ModPath, Mutability}; 5use hir::{Documentation, Mutability};
6use ide_db::{ 6use ide_db::{
7 helpers::{ 7 helpers::{
8 insert_use::{self, ImportScope, MergeBehavior}, 8 import_assets::LocatedImport,
9 insert_use::{self, ImportScope, InsertUseConfig},
9 mod_path_to_ast, SnippetCap, 10 mod_path_to_ast, SnippetCap,
10 }, 11 },
11 SymbolKind, 12 SymbolKind,
12}; 13};
13use stdx::{impl_from, never}; 14use stdx::{format_to, impl_from, never};
14use syntax::{algo, TextRange}; 15use syntax::{algo, TextRange};
15use text_edit::TextEdit; 16use text_edit::TextEdit;
16 17
@@ -62,12 +63,18 @@ pub struct CompletionItem {
62 /// after completion. 63 /// after completion.
63 trigger_call_info: bool, 64 trigger_call_info: bool,
64 65
65 /// Score is useful to pre select or display in better order completion items 66 /// We use this to sort completion. Relevance records facts like "do the
66 score: Option<CompletionScore>, 67 /// types align precisely?". We can't sort by relevances directly, they are
68 /// only partially ordered.
69 ///
70 /// Note that Relevance ignores fuzzy match score. We compute Relevance for
71 /// all possible items, and then separately build an ordered completion list
72 /// based on relevance and fuzzy matching with the already typed identifier.
73 relevance: CompletionRelevance,
67 74
68 /// Indicates that a reference or mutable reference to this variable is a 75 /// Indicates that a reference or mutable reference to this variable is a
69 /// possible match. 76 /// possible match.
70 ref_match: Option<(Mutability, CompletionScore)>, 77 ref_match: Option<Mutability>,
71 78
72 /// The import data to add to completion's edits. 79 /// The import data to add to completion's edits.
73 import_to_add: Option<ImportEdit>, 80 import_to_add: Option<ImportEdit>,
@@ -100,8 +107,13 @@ impl fmt::Debug for CompletionItem {
100 if self.deprecated { 107 if self.deprecated {
101 s.field("deprecated", &true); 108 s.field("deprecated", &true);
102 } 109 }
103 if let Some(score) = &self.score { 110
104 s.field("score", score); 111 if self.relevance != CompletionRelevance::default() {
112 s.field("relevance", &self.relevance);
113 }
114
115 if let Some(mutability) = &self.ref_match {
116 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
105 } 117 }
106 if self.trigger_call_info { 118 if self.trigger_call_info {
107 s.field("trigger_call_info", &true); 119 s.field("trigger_call_info", &true);
@@ -110,12 +122,59 @@ impl fmt::Debug for CompletionItem {
110 } 122 }
111} 123}
112 124
113#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)] 125#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
114pub enum CompletionScore { 126pub struct CompletionRelevance {
115 /// If only type match 127 /// This is set in cases like these:
116 TypeMatch, 128 ///
117 /// If type and name match 129 /// ```
118 TypeAndNameMatch, 130 /// fn f(spam: String) {}
131 /// fn main {
132 /// let spam = 92;
133 /// f($0) // name of local matches the name of param
134 /// }
135 /// ```
136 pub exact_name_match: bool,
137 /// This is set in cases like these:
138 ///
139 /// ```
140 /// fn f(spam: String) {}
141 /// fn main {
142 /// let foo = String::new();
143 /// f($0) // type of local matches the type of param
144 /// }
145 /// ```
146 pub exact_type_match: bool,
147}
148
149impl CompletionRelevance {
150 /// Provides a relevance score. Higher values are more relevant.
151 ///
152 /// The absolute value of the relevance score is not meaningful, for
153 /// example a value of 0 doesn't mean "not relevant", rather
154 /// it means "least relevant". The score value should only be used
155 /// for relative ordering.
156 ///
157 /// See is_relevant if you need to make some judgement about score
158 /// in an absolute sense.
159 pub fn score(&self) -> u32 {
160 let mut score = 0;
161
162 if self.exact_name_match {
163 score += 1;
164 }
165 if self.exact_type_match {
166 score += 1;
167 }
168
169 score
170 }
171
172 /// Returns true when the score for this threshold is above
173 /// some threshold such that we think it is especially likely
174 /// to be relevant.
175 pub fn is_relevant(&self) -> bool {
176 self.score() > 0
177 }
119} 178}
120 179
121#[derive(Debug, Clone, Copy, PartialEq, Eq)] 180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -207,9 +266,9 @@ impl CompletionItem {
207 lookup: None, 266 lookup: None,
208 kind: None, 267 kind: None,
209 text_edit: None, 268 text_edit: None,
210 deprecated: None, 269 deprecated: false,
211 trigger_call_info: None, 270 trigger_call_info: None,
212 score: None, 271 relevance: CompletionRelevance::default(),
213 ref_match: None, 272 ref_match: None,
214 import_to_add: None, 273 import_to_add: None,
215 } 274 }
@@ -252,16 +311,22 @@ impl CompletionItem {
252 self.deprecated 311 self.deprecated
253 } 312 }
254 313
255 pub fn score(&self) -> Option<CompletionScore> { 314 pub fn relevance(&self) -> CompletionRelevance {
256 self.score 315 self.relevance
257 } 316 }
258 317
259 pub fn trigger_call_info(&self) -> bool { 318 pub fn trigger_call_info(&self) -> bool {
260 self.trigger_call_info 319 self.trigger_call_info
261 } 320 }
262 321
263 pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { 322 pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> {
264 self.ref_match 323 // Relevance of the ref match should be the same as the original
324 // match, but with exact type match set because self.ref_match
325 // is only set if there is an exact type match.
326 let mut relevance = self.relevance;
327 relevance.exact_type_match = true;
328
329 self.ref_match.map(|mutability| (mutability, relevance))
265 } 330 }
266 331
267 pub fn import_to_add(&self) -> Option<&ImportEdit> { 332 pub fn import_to_add(&self) -> Option<&ImportEdit> {
@@ -272,22 +337,18 @@ impl CompletionItem {
272/// An extra import to add after the completion is applied. 337/// An extra import to add after the completion is applied.
273#[derive(Debug, Clone)] 338#[derive(Debug, Clone)]
274pub struct ImportEdit { 339pub struct ImportEdit {
275 pub import_path: ModPath, 340 pub import: LocatedImport,
276 pub import_scope: ImportScope, 341 pub scope: ImportScope,
277 pub import_for_trait_assoc_item: bool,
278} 342}
279 343
280impl ImportEdit { 344impl ImportEdit {
281 /// Attempts to insert the import to the given scope, producing a text edit. 345 /// Attempts to insert the import to the given scope, producing a text edit.
282 /// May return no edit in edge cases, such as scope already containing the import. 346 /// May return no edit in edge cases, such as scope already containing the import.
283 pub fn to_text_edit(&self, merge_behavior: Option<MergeBehavior>) -> Option<TextEdit> { 347 pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> {
284 let _p = profile::span("ImportEdit::to_text_edit"); 348 let _p = profile::span("ImportEdit::to_text_edit");
285 349
286 let rewriter = insert_use::insert_use( 350 let rewriter =
287 &self.import_scope, 351 insert_use::insert_use(&self.scope, mod_path_to_ast(&self.import.import_path), cfg);
288 mod_path_to_ast(&self.import_path),
289 merge_behavior,
290 );
291 let old_ast = rewriter.rewrite_root()?; 352 let old_ast = rewriter.rewrite_root()?;
292 let mut import_insert = TextEdit::builder(); 353 let mut import_insert = TextEdit::builder();
293 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); 354 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
@@ -311,10 +372,10 @@ pub(crate) struct Builder {
311 lookup: Option<String>, 372 lookup: Option<String>,
312 kind: Option<CompletionItemKind>, 373 kind: Option<CompletionItemKind>,
313 text_edit: Option<TextEdit>, 374 text_edit: Option<TextEdit>,
314 deprecated: Option<bool>, 375 deprecated: bool,
315 trigger_call_info: Option<bool>, 376 trigger_call_info: Option<bool>,
316 score: Option<CompletionScore>, 377 relevance: CompletionRelevance,
317 ref_match: Option<(Mutability, CompletionScore)>, 378 ref_match: Option<Mutability>,
318} 379}
319 380
320impl Builder { 381impl Builder {
@@ -325,20 +386,19 @@ impl Builder {
325 let mut lookup = self.lookup; 386 let mut lookup = self.lookup;
326 let mut insert_text = self.insert_text; 387 let mut insert_text = self.insert_text;
327 388
328 if let Some(import_to_add) = self.import_to_add.as_ref() { 389 if let Some(original_path) = self
329 if import_to_add.import_for_trait_assoc_item { 390 .import_to_add
330 lookup = lookup.or_else(|| Some(label.clone())); 391 .as_ref()
331 insert_text = insert_text.or_else(|| Some(label.clone())); 392 .and_then(|import_edit| import_edit.import.original_path.as_ref())
332 label = format!("{} ({})", label, import_to_add.import_path); 393 {
394 lookup = lookup.or_else(|| Some(label.clone()));
395 insert_text = insert_text.or_else(|| Some(label.clone()));
396
397 let original_path_label = original_path.to_string();
398 if original_path_label.ends_with(&label) {
399 label = original_path_label;
333 } else { 400 } else {
334 let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); 401 format_to!(label, " ({})", original_path)
335 let _ = import_path_without_last_segment.pop_segment();
336
337 if !import_path_without_last_segment.segments().is_empty() {
338 lookup = lookup.or_else(|| Some(label.clone()));
339 insert_text = insert_text.or_else(|| Some(label.clone()));
340 label = format!("{}::{}", import_path_without_last_segment, label);
341 }
342 } 402 }
343 } 403 }
344 404
@@ -359,49 +419,49 @@ impl Builder {
359 lookup, 419 lookup,
360 kind: self.kind, 420 kind: self.kind,
361 completion_kind: self.completion_kind, 421 completion_kind: self.completion_kind,
362 deprecated: self.deprecated.unwrap_or(false), 422 deprecated: self.deprecated,
363 trigger_call_info: self.trigger_call_info.unwrap_or(false), 423 trigger_call_info: self.trigger_call_info.unwrap_or(false),
364 score: self.score, 424 relevance: self.relevance,
365 ref_match: self.ref_match, 425 ref_match: self.ref_match,
366 import_to_add: self.import_to_add, 426 import_to_add: self.import_to_add,
367 } 427 }
368 } 428 }
369 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 429 pub(crate) fn lookup_by(&mut self, lookup: impl Into<String>) -> &mut Builder {
370 self.lookup = Some(lookup.into()); 430 self.lookup = Some(lookup.into());
371 self 431 self
372 } 432 }
373 pub(crate) fn label(mut self, label: impl Into<String>) -> Builder { 433 pub(crate) fn label(&mut self, label: impl Into<String>) -> &mut Builder {
374 self.label = label.into(); 434 self.label = label.into();
375 self 435 self
376 } 436 }
377 pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { 437 pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
378 self.insert_text = Some(insert_text.into()); 438 self.insert_text = Some(insert_text.into());
379 self 439 self
380 } 440 }
381 pub(crate) fn insert_snippet( 441 pub(crate) fn insert_snippet(
382 mut self, 442 &mut self,
383 _cap: SnippetCap, 443 _cap: SnippetCap,
384 snippet: impl Into<String>, 444 snippet: impl Into<String>,
385 ) -> Builder { 445 ) -> &mut Builder {
386 self.insert_text_format = InsertTextFormat::Snippet; 446 self.insert_text_format = InsertTextFormat::Snippet;
387 self.insert_text(snippet) 447 self.insert_text(snippet)
388 } 448 }
389 pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder { 449 pub(crate) fn kind(&mut self, kind: impl Into<CompletionItemKind>) -> &mut Builder {
390 self.kind = Some(kind.into()); 450 self.kind = Some(kind.into());
391 self 451 self
392 } 452 }
393 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { 453 pub(crate) fn text_edit(&mut self, edit: TextEdit) -> &mut Builder {
394 self.text_edit = Some(edit); 454 self.text_edit = Some(edit);
395 self 455 self
396 } 456 }
397 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder { 457 pub(crate) fn snippet_edit(&mut self, _cap: SnippetCap, edit: TextEdit) -> &mut Builder {
398 self.insert_text_format = InsertTextFormat::Snippet; 458 self.insert_text_format = InsertTextFormat::Snippet;
399 self.text_edit(edit) 459 self.text_edit(edit)
400 } 460 }
401 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { 461 pub(crate) fn detail(&mut self, detail: impl Into<String>) -> &mut Builder {
402 self.set_detail(Some(detail)) 462 self.set_detail(Some(detail))
403 } 463 }
404 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 464 pub(crate) fn set_detail(&mut self, detail: Option<impl Into<String>>) -> &mut Builder {
405 self.detail = detail.map(Into::into); 465 self.detail = detail.map(Into::into);
406 if let Some(detail) = &self.detail { 466 if let Some(detail) = &self.detail {
407 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { 467 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
@@ -411,40 +471,91 @@ impl Builder {
411 self 471 self
412 } 472 }
413 #[allow(unused)] 473 #[allow(unused)]
414 pub(crate) fn documentation(self, docs: Documentation) -> Builder { 474 pub(crate) fn documentation(&mut self, docs: Documentation) -> &mut Builder {
415 self.set_documentation(Some(docs)) 475 self.set_documentation(Some(docs))
416 } 476 }
417 pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder { 477 pub(crate) fn set_documentation(&mut self, docs: Option<Documentation>) -> &mut Builder {
418 self.documentation = docs.map(Into::into); 478 self.documentation = docs.map(Into::into);
419 self 479 self
420 } 480 }
421 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { 481 pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder {
422 self.deprecated = Some(deprecated); 482 self.deprecated = deprecated;
423 self 483 self
424 } 484 }
425 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { 485 pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder {
426 self.score = Some(score); 486 self.relevance = relevance;
427 self 487 self
428 } 488 }
429 pub(crate) fn trigger_call_info(mut self) -> Builder { 489 pub(crate) fn trigger_call_info(&mut self) -> &mut Builder {
430 self.trigger_call_info = Some(true); 490 self.trigger_call_info = Some(true);
431 self 491 self
432 } 492 }
433 pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder { 493 pub(crate) fn add_import(&mut self, import_to_add: Option<ImportEdit>) -> &mut Builder {
434 self.import_to_add = import_to_add; 494 self.import_to_add = import_to_add;
435 self 495 self
436 } 496 }
437 pub(crate) fn set_ref_match( 497 pub(crate) fn ref_match(&mut self, mutability: Mutability) -> &mut Builder {
438 mut self, 498 self.ref_match = Some(mutability);
439 ref_match: Option<(Mutability, CompletionScore)>,
440 ) -> Builder {
441 self.ref_match = ref_match;
442 self 499 self
443 } 500 }
444} 501}
445 502
446impl<'a> Into<CompletionItem> for Builder { 503#[cfg(test)]
447 fn into(self) -> CompletionItem { 504mod tests {
448 self.build() 505 use itertools::Itertools;
506 use test_utils::assert_eq_text;
507
508 use super::CompletionRelevance;
509
510 /// Check that these are CompletionRelevance are sorted in ascending order
511 /// by their relevance score.
512 ///
513 /// We want to avoid making assertions about the absolute score of any
514 /// item, but we do want to assert whether each is >, <, or == to the
515 /// others.
516 ///
517 /// If provided vec![vec![a], vec![b, c], vec![d]], then this will assert:
518 /// a.score < b.score == c.score < d.score
519 fn check_relevance_score_ordered(expected_relevance_order: Vec<Vec<CompletionRelevance>>) {
520 let expected = format!("{:#?}", &expected_relevance_order);
521
522 let actual_relevance_order = expected_relevance_order
523 .into_iter()
524 .flatten()
525 .map(|r| (r.score(), r))
526 .sorted_by_key(|(score, _r)| *score)
527 .fold(
528 (u32::MIN, vec![vec![]]),
529 |(mut currently_collecting_score, mut out), (score, r)| {
530 if currently_collecting_score == score {
531 out.last_mut().unwrap().push(r);
532 } else {
533 currently_collecting_score = score;
534 out.push(vec![r]);
535 }
536 (currently_collecting_score, out)
537 },
538 )
539 .1;
540
541 let actual = format!("{:#?}", &actual_relevance_order);
542
543 assert_eq_text!(&expected, &actual);
544 }
545
546 #[test]
547 fn relevance_score() {
548 // This test asserts that the relevance score for these items is ascending, and
549 // that any items in the same vec have the same score.
550 let expected_relevance_order = vec![
551 vec![CompletionRelevance::default()],
552 vec![
553 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },
554 CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() },
555 ],
556 vec![CompletionRelevance { exact_name_match: true, exact_type_match: true }],
557 ];
558
559 check_relevance_score_ordered(expected_relevance_order);
449 } 560 }
450} 561}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 76f31de9e..263554ecf 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -13,7 +13,9 @@ mod completions;
13 13
14use completions::flyimport::position_for_import; 14use completions::flyimport::position_for_import;
15use ide_db::{ 15use ide_db::{
16 base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, 16 base_db::FilePosition,
17 helpers::{import_assets::LocatedImport, insert_use::ImportScope},
18 items_locator, RootDatabase,
17}; 19};
18use text_edit::TextEdit; 20use text_edit::TextEdit;
19 21
@@ -21,7 +23,7 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi
21 23
22pub use crate::{ 24pub use crate::{
23 config::CompletionConfig, 25 config::CompletionConfig,
24 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, 26 item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, InsertTextFormat},
25}; 27};
26 28
27//FIXME: split the following feature into fine-grained features. 29//FIXME: split the following feature into fine-grained features.
@@ -139,25 +141,27 @@ pub fn resolve_completion_edits(
139 position: FilePosition, 141 position: FilePosition,
140 full_import_path: &str, 142 full_import_path: &str,
141 imported_name: String, 143 imported_name: String,
142 import_for_trait_assoc_item: bool,
143) -> Option<Vec<TextEdit>> { 144) -> Option<Vec<TextEdit>> {
144 let ctx = CompletionContext::new(db, position, config)?; 145 let ctx = CompletionContext::new(db, position, config)?;
145 let position_for_import = position_for_import(&ctx, None)?; 146 let position_for_import = position_for_import(&ctx, None)?;
146 let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; 147 let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
147 148
148 let current_module = ctx.sema.scope(position_for_import).module()?; 149 let current_module = ctx.sema.scope(position_for_import).module()?;
149 let current_crate = current_module.krate(); 150 let current_crate = current_module.krate();
150 151
151 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) 152 let (import_path, item_to_import) =
152 .filter_map(|candidate| { 153 items_locator::with_exact_name(&ctx.sema, current_crate, imported_name)
153 let item: hir::ItemInNs = candidate.either(Into::into, Into::into); 154 .into_iter()
154 current_module.find_use_path_prefixed(db, item, config.insert_use.prefix_kind) 155 .filter_map(|candidate| {
155 }) 156 current_module
156 .find(|mod_path| mod_path.to_string() == full_import_path)?; 157 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
158 .zip(Some(candidate))
159 })
160 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
161 let import =
162 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path));
157 163
158 ImportEdit { import_path, import_scope, import_for_trait_assoc_item } 164 ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
159 .to_text_edit(config.insert_use.merge)
160 .map(|edit| vec![edit])
161} 165}
162 166
163#[cfg(test)] 167#[cfg(test)]
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index eddaaa6f3..db31896e5 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -13,13 +13,15 @@ mod builder_ext;
13use hir::{ 13use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, 14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
15}; 15};
16use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind}; 16use ide_db::{
17 helpers::{item_name, SnippetCap},
18 RootDatabase, SymbolKind,
19};
17use syntax::TextRange; 20use syntax::TextRange;
18use test_utils::mark;
19 21
20use crate::{ 22use crate::{
21 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 23 item::{CompletionRelevance, ImportEdit},
22 CompletionScore, 24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
23}; 25};
24 26
25use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; 27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
@@ -51,18 +53,20 @@ pub(crate) fn render_resolution<'a>(
51pub(crate) fn render_resolution_with_import<'a>( 53pub(crate) fn render_resolution_with_import<'a>(
52 ctx: RenderContext<'a>, 54 ctx: RenderContext<'a>,
53 import_edit: ImportEdit, 55 import_edit: ImportEdit,
54 resolution: &ScopeDef,
55) -> Option<CompletionItem> { 56) -> Option<CompletionItem> {
57 let resolution = ScopeDef::from(import_edit.import.original_item);
56 let local_name = match resolution { 58 let local_name = match resolution {
57 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), 59 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
58 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), 60 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
59 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), 61 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
60 _ => import_edit.import_path.segments().last()?.to_string(), 62 _ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(),
61 }; 63 };
62 Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { 64 Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map(
63 item.completion_kind = CompletionKind::Magic; 65 |mut item| {
64 item 66 item.completion_kind = CompletionKind::Magic;
65 }) 67 item
68 },
69 )
66} 70}
67 71
68/// Interface for data and methods required for items rendering. 72/// Interface for data and methods required for items rendering.
@@ -113,13 +117,13 @@ impl<'a> RenderContext<'a> {
113 node.docs(self.db()) 117 node.docs(self.db())
114 } 118 }
115 119
116 fn active_name_and_type(&self) -> Option<(String, Type)> { 120 fn expected_name_and_type(&self) -> Option<(String, Type)> {
117 if let Some(record_field) = &self.completion.record_field_syntax { 121 if let Some(record_field) = &self.completion.record_field_syntax {
118 mark::hit!(record_field_type_match); 122 cov_mark::hit!(record_field_type_match);
119 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; 123 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?;
120 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) 124 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db())))
121 } else if let Some(active_parameter) = &self.completion.active_parameter { 125 } else if let Some(active_parameter) = &self.completion.active_parameter {
122 mark::hit!(active_param_type_match); 126 cov_mark::hit!(active_param_type_match);
123 Some((active_parameter.name.clone(), active_parameter.ty.clone())) 127 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
124 } else { 128 } else {
125 None 129 None
@@ -145,24 +149,29 @@ impl<'a> Render<'a> {
145 CompletionKind::Reference, 149 CompletionKind::Reference,
146 self.ctx.source_range(), 150 self.ctx.source_range(),
147 name.to_string(), 151 name.to_string(),
148 ) 152 );
149 .kind(SymbolKind::Field) 153 item.kind(SymbolKind::Field)
150 .detail(ty.display(self.ctx.db()).to_string()) 154 .detail(ty.display(self.ctx.db()).to_string())
151 .set_documentation(field.docs(self.ctx.db())) 155 .set_documentation(field.docs(self.ctx.db()))
152 .set_deprecated(is_deprecated); 156 .set_deprecated(is_deprecated);
153 157
154 if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { 158 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) {
155 item = item.set_score(score); 159 item.set_relevance(relevance);
156 } 160 }
157 161
158 item.build() 162 item.build()
159 } 163 }
160 164
161 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { 165 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
162 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) 166 let mut item = CompletionItem::new(
163 .kind(SymbolKind::Field) 167 CompletionKind::Reference,
164 .detail(ty.display(self.ctx.db()).to_string()) 168 self.ctx.source_range(),
165 .build() 169 field.to_string(),
170 );
171
172 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string());
173
174 item.build()
166 } 175 }
167 176
168 fn render_resolution( 177 fn render_resolution(
@@ -221,15 +230,13 @@ impl<'a> Render<'a> {
221 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 230 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
222 } 231 }
223 ScopeDef::Unknown => { 232 ScopeDef::Unknown => {
224 let item = CompletionItem::new( 233 let mut item = CompletionItem::new(
225 CompletionKind::Reference, 234 CompletionKind::Reference,
226 self.ctx.source_range(), 235 self.ctx.source_range(),
227 local_name, 236 local_name,
228 ) 237 );
229 .kind(CompletionItemKind::UnresolvedReference) 238 item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add);
230 .add_import(import_to_add) 239 return Some(item.build());
231 .build();
232 return Some(item);
233 } 240 }
234 }; 241 };
235 242
@@ -238,20 +245,27 @@ impl<'a> Render<'a> {
238 if let ScopeDef::Local(local) = resolution { 245 if let ScopeDef::Local(local) = resolution {
239 let ty = local.ty(self.ctx.db()); 246 let ty = local.ty(self.ctx.db());
240 if !ty.is_unknown() { 247 if !ty.is_unknown() {
241 item = item.detail(ty.display(self.ctx.db()).to_string()); 248 item.detail(ty.display(self.ctx.db()).to_string());
242 } 249 }
243 }; 250 };
244 251
245 let mut ref_match = None;
246 if let ScopeDef::Local(local) = resolution { 252 if let ScopeDef::Local(local) = resolution {
247 if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { 253 let ty = local.ty(self.ctx.db());
248 let ty = local.ty(self.ctx.db()); 254 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) {
249 if let Some(score) = 255 item.set_relevance(relevance);
250 compute_score_from_active(&active_type, &active_name, &ty, &local_name) 256 }
251 { 257 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() {
252 item = item.set_score(score); 258 if let Some(ty_without_ref) = expected_type.remove_ref() {
259 if ty_without_ref == ty {
260 cov_mark::hit!(suggest_ref);
261 let mutability = if expected_type.is_mutable_reference() {
262 Mutability::Mut
263 } else {
264 Mutability::Shared
265 };
266 item.ref_match(mutability);
267 }
253 } 268 }
254 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
255 } 269 }
256 } 270 }
257 271
@@ -269,23 +283,18 @@ impl<'a> Render<'a> {
269 _ => false, 283 _ => false,
270 }; 284 };
271 if has_non_default_type_params { 285 if has_non_default_type_params {
272 mark::hit!(inserts_angle_brackets_for_generics); 286 cov_mark::hit!(inserts_angle_brackets_for_generics);
273 item = item 287 item.lookup_by(local_name.clone())
274 .lookup_by(local_name.clone())
275 .label(format!("{}<…>", local_name)) 288 .label(format!("{}<…>", local_name))
276 .insert_snippet(cap, format!("{}<$0>", local_name)); 289 .insert_snippet(cap, format!("{}<$0>", local_name));
277 } 290 }
278 } 291 }
279 } 292 }
280 293 item.kind(kind)
281 Some( 294 .add_import(import_to_add)
282 item.kind(kind) 295 .set_documentation(self.docs(resolution))
283 .add_import(import_to_add) 296 .set_deprecated(self.is_deprecated(resolution));
284 .set_ref_match(ref_match) 297 Some(item.build())
285 .set_documentation(self.docs(resolution))
286 .set_deprecated(self.is_deprecated(resolution))
287 .build(),
288 )
289 } 298 }
290 299
291 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { 300 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
@@ -313,56 +322,23 @@ impl<'a> Render<'a> {
313 } 322 }
314} 323}
315 324
316fn compute_score_from_active( 325fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> {
317 active_type: &Type, 326 let (expected_name, expected_type) = ctx.expected_name_and_type()?;
318 active_name: &str, 327 let mut res = CompletionRelevance::default();
319 ty: &Type, 328 res.exact_type_match = ty == &expected_type;
320 name: &str, 329 res.exact_name_match = name == &expected_name;
321) -> Option<CompletionScore> {
322 // Compute score
323 // For the same type
324 if active_type != ty {
325 return None;
326 }
327
328 let mut res = CompletionScore::TypeMatch;
329
330 // If same type + same name then go top position
331 if active_name == name {
332 res = CompletionScore::TypeAndNameMatch
333 }
334
335 Some(res) 330 Some(res)
336} 331}
337fn refed_type_matches(
338 active_type: &Type,
339 active_name: &str,
340 ty: &Type,
341 name: &str,
342) -> Option<(Mutability, CompletionScore)> {
343 let derefed_active = active_type.remove_ref()?;
344 let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
345 Some((
346 if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
347 score,
348 ))
349}
350
351fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> {
352 let (active_name, active_type) = ctx.active_name_and_type()?;
353 compute_score_from_active(&active_type, &active_name, ty, name)
354}
355 332
356#[cfg(test)] 333#[cfg(test)]
357mod tests { 334mod tests {
358 use std::cmp::Reverse; 335 use std::cmp::Reverse;
359 336
360 use expect_test::{expect, Expect}; 337 use expect_test::{expect, Expect};
361 use test_utils::mark;
362 338
363 use crate::{ 339 use crate::{
364 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 340 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
365 CompletionKind, CompletionScore, 341 CompletionKind, CompletionRelevance,
366 }; 342 };
367 343
368 fn check(ra_fixture: &str, expect: Expect) { 344 fn check(ra_fixture: &str, expect: Expect) {
@@ -370,24 +346,27 @@ mod tests {
370 expect.assert_debug_eq(&actual); 346 expect.assert_debug_eq(&actual);
371 } 347 }
372 348
373 fn check_scores(ra_fixture: &str, expect: Expect) { 349 fn check_relevance(ra_fixture: &str, expect: Expect) {
374 fn display_score(score: Option<CompletionScore>) -> &'static str { 350 fn display_relevance(relevance: CompletionRelevance) -> &'static str {
375 match score { 351 match relevance {
376 Some(CompletionScore::TypeMatch) => "[type]", 352 CompletionRelevance { exact_type_match: true, exact_name_match: true } => {
377 Some(CompletionScore::TypeAndNameMatch) => "[type+name]", 353 "[type+name]"
378 None => "[]".into(), 354 }
355 CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]",
356 CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]",
357 CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]",
379 } 358 }
380 } 359 }
381 360
382 let mut completions = get_all_items(TEST_CONFIG, ra_fixture); 361 let mut completions = get_all_items(TEST_CONFIG, ra_fixture);
383 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); 362 completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string()));
384 let actual = completions 363 let actual = completions
385 .into_iter() 364 .into_iter()
386 .filter(|it| it.completion_kind == CompletionKind::Reference) 365 .filter(|it| it.completion_kind == CompletionKind::Reference)
387 .map(|it| { 366 .map(|it| {
388 let tag = it.kind().unwrap().tag(); 367 let tag = it.kind().unwrap().tag();
389 let score = display_score(it.score()); 368 let relevance = display_relevance(it.relevance());
390 format!("{} {} {}\n", tag, it.label(), score) 369 format!("{} {} {}\n", tag, it.label(), relevance)
391 }) 370 })
392 .collect::<String>(); 371 .collect::<String>();
393 expect.assert_eq(&actual); 372 expect.assert_eq(&actual);
@@ -734,7 +713,7 @@ fn foo(s: S) { s.$0 }
734 713
735 #[test] 714 #[test]
736 fn no_call_parens_if_fn_ptr_needed() { 715 fn no_call_parens_if_fn_ptr_needed() {
737 mark::check!(no_call_parens_if_fn_ptr_needed); 716 cov_mark::check!(no_call_parens_if_fn_ptr_needed);
738 check_edit( 717 check_edit(
739 "foo", 718 "foo",
740 r#" 719 r#"
@@ -758,7 +737,7 @@ fn main() -> ManualVtable {
758 737
759 #[test] 738 #[test]
760 fn no_parens_in_use_item() { 739 fn no_parens_in_use_item() {
761 mark::check!(no_parens_in_use_item); 740 cov_mark::check!(no_parens_in_use_item);
762 check_edit( 741 check_edit(
763 "foo", 742 "foo",
764 r#" 743 r#"
@@ -802,7 +781,7 @@ fn f(foo: &Foo) { foo.foo(); }
802 781
803 #[test] 782 #[test]
804 fn inserts_angle_brackets_for_generics() { 783 fn inserts_angle_brackets_for_generics() {
805 mark::check!(inserts_angle_brackets_for_generics); 784 cov_mark::check!(inserts_angle_brackets_for_generics);
806 check_edit( 785 check_edit(
807 "Vec", 786 "Vec",
808 r#" 787 r#"
@@ -850,9 +829,9 @@ fn foo(xs: Vec<i128>)
850 } 829 }
851 830
852 #[test] 831 #[test]
853 fn active_param_score() { 832 fn active_param_relevance() {
854 mark::check!(active_param_type_match); 833 cov_mark::check!(active_param_type_match);
855 check_scores( 834 check_relevance(
856 r#" 835 r#"
857struct S { foo: i64, bar: u32, baz: u32 } 836struct S { foo: i64, bar: u32, baz: u32 }
858fn test(bar: u32) { } 837fn test(bar: u32) { }
@@ -867,9 +846,9 @@ fn foo(s: S) { test(s.$0) }
867 } 846 }
868 847
869 #[test] 848 #[test]
870 fn record_field_scores() { 849 fn record_field_relevances() {
871 mark::check!(record_field_type_match); 850 cov_mark::check!(record_field_type_match);
872 check_scores( 851 check_relevance(
873 r#" 852 r#"
874struct A { foo: i64, bar: u32, baz: u32 } 853struct A { foo: i64, bar: u32, baz: u32 }
875struct B { x: (), y: f32, bar: u32 } 854struct B { x: (), y: f32, bar: u32 }
@@ -884,8 +863,8 @@ fn foo(a: A) { B { bar: a.$0 }; }
884 } 863 }
885 864
886 #[test] 865 #[test]
887 fn record_field_and_call_scores() { 866 fn record_field_and_call_relevances() {
888 check_scores( 867 check_relevance(
889 r#" 868 r#"
890struct A { foo: i64, bar: u32, baz: u32 } 869struct A { foo: i64, bar: u32, baz: u32 }
891struct B { x: (), y: f32, bar: u32 } 870struct B { x: (), y: f32, bar: u32 }
@@ -898,7 +877,7 @@ fn foo(a: A) { B { bar: f(a.$0) }; }
898 fd baz [] 877 fd baz []
899 "#]], 878 "#]],
900 ); 879 );
901 check_scores( 880 check_relevance(
902 r#" 881 r#"
903struct A { foo: i64, bar: u32, baz: u32 } 882struct A { foo: i64, bar: u32, baz: u32 }
904struct B { x: (), y: f32, bar: u32 } 883struct B { x: (), y: f32, bar: u32 }
@@ -915,7 +894,7 @@ fn foo(a: A) { f(B { bar: a.$0 }); }
915 894
916 #[test] 895 #[test]
917 fn prioritize_exact_ref_match() { 896 fn prioritize_exact_ref_match() {
918 check_scores( 897 check_relevance(
919 r#" 898 r#"
920struct WorldSnapshot { _f: () }; 899struct WorldSnapshot { _f: () };
921fn go(world: &WorldSnapshot) { go(w$0) } 900fn go(world: &WorldSnapshot) { go(w$0) }
@@ -930,7 +909,7 @@ fn go(world: &WorldSnapshot) { go(w$0) }
930 909
931 #[test] 910 #[test]
932 fn too_many_arguments() { 911 fn too_many_arguments() {
933 check_scores( 912 check_relevance(
934 r#" 913 r#"
935struct Foo; 914struct Foo;
936fn f(foo: &Foo) { f(foo, w$0) } 915fn f(foo: &Foo) { f(foo, w$0) }
@@ -942,4 +921,70 @@ fn f(foo: &Foo) { f(foo, w$0) }
942 "#]], 921 "#]],
943 ); 922 );
944 } 923 }
924
925 #[test]
926 fn suggest_ref_mut() {
927 cov_mark::check!(suggest_ref);
928 check(
929 r#"
930struct S;
931fn foo(s: &mut S) {}
932fn main() {
933 let mut s = S;
934 foo($0);
935}
936 "#,
937 expect![[r#"
938 [
939 CompletionItem {
940 label: "S",
941 source_range: 70..70,
942 delete: 70..70,
943 insert: "S",
944 kind: SymbolKind(
945 Struct,
946 ),
947 },
948 CompletionItem {
949 label: "foo(…)",
950 source_range: 70..70,
951 delete: 70..70,
952 insert: "foo(${1:&mut s})$0",
953 kind: SymbolKind(
954 Function,
955 ),
956 lookup: "foo",
957 detail: "-> ()",
958 trigger_call_info: true,
959 },
960 CompletionItem {
961 label: "main()",
962 source_range: 70..70,
963 delete: 70..70,
964 insert: "main()$0",
965 kind: SymbolKind(
966 Function,
967 ),
968 lookup: "main",
969 detail: "-> ()",
970 },
971 CompletionItem {
972 label: "s",
973 source_range: 70..70,
974 delete: 70..70,
975 insert: "s",
976 kind: SymbolKind(
977 Local,
978 ),
979 detail: "S",
980 relevance: CompletionRelevance {
981 exact_name_match: true,
982 exact_type_match: false,
983 },
984 ref_match: "&mut ",
985 },
986 ]
987 "#]],
988 )
989 }
945} 990}
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
index d053a988b..6d062b3b9 100644
--- a/crates/ide_completion/src/render/builder_ext.rs
+++ b/crates/ide_completion/src/render/builder_ext.rs
@@ -1,7 +1,6 @@
1//! Extensions for `Builder` structure required for item rendering. 1//! Extensions for `Builder` structure required for item rendering.
2 2
3use itertools::Itertools; 3use itertools::Itertools;
4use test_utils::mark;
5 4
6use crate::{item::Builder, CompletionContext}; 5use crate::{item::Builder, CompletionContext};
7 6
@@ -30,7 +29,7 @@ impl Builder {
30 return false; 29 return false;
31 } 30 }
32 if ctx.use_item_syntax.is_some() { 31 if ctx.use_item_syntax.is_some() {
33 mark::hit!(no_parens_in_use_item); 32 cov_mark::hit!(no_parens_in_use_item);
34 return false; 33 return false;
35 } 34 }
36 if ctx.is_pattern_call { 35 if ctx.is_pattern_call {
@@ -43,7 +42,7 @@ impl Builder {
43 // Don't add parentheses if the expected type is some function reference. 42 // Don't add parentheses if the expected type is some function reference.
44 if let Some(ty) = &ctx.expected_type { 43 if let Some(ty) = &ctx.expected_type {
45 if ty.is_fn() { 44 if ty.is_fn() {
46 mark::hit!(no_call_parens_if_fn_ptr_needed); 45 cov_mark::hit!(no_call_parens_if_fn_ptr_needed);
47 return false; 46 return false;
48 } 47 }
49 } 48 }
@@ -53,11 +52,11 @@ impl Builder {
53 } 52 }
54 53
55 pub(super) fn add_call_parens( 54 pub(super) fn add_call_parens(
56 mut self, 55 &mut self,
57 ctx: &CompletionContext, 56 ctx: &CompletionContext,
58 name: String, 57 name: String,
59 params: Params, 58 params: Params,
60 ) -> Builder { 59 ) -> &mut Builder {
61 if !self.should_add_parens(ctx) { 60 if !self.should_add_parens(ctx) {
62 return self; 61 return self;
63 } 62 }
@@ -67,12 +66,12 @@ impl Builder {
67 None => return self, 66 None => return self,
68 }; 67 };
69 // If not an import, add parenthesis automatically. 68 // If not an import, add parenthesis automatically.
70 mark::hit!(inserts_parens_for_function_calls); 69 cov_mark::hit!(inserts_parens_for_function_calls);
71 70
72 let (snippet, label) = if params.is_empty() { 71 let (snippet, label) = if params.is_empty() {
73 (format!("{}()$0", name), format!("{}()", name)) 72 (format!("{}()$0", name), format!("{}()", name))
74 } else { 73 } else {
75 self = self.trigger_call_info(); 74 self.trigger_call_info();
76 let snippet = match (ctx.config.add_call_argument_snippets, params) { 75 let snippet = match (ctx.config.add_call_argument_snippets, params) {
77 (true, Params::Named(params)) => { 76 (true, Params::Named(params)) => {
78 let function_params_snippet = 77 let function_params_snippet =
@@ -82,7 +81,7 @@ impl Builder {
82 format!("{}({})$0", name, function_params_snippet) 81 format!("{}({})$0", name, function_params_snippet)
83 } 82 }
84 _ => { 83 _ => {
85 mark::hit!(suppress_arg_snippets); 84 cov_mark::hit!(suppress_arg_snippets);
86 format!("{}($0)", name) 85 format!("{}($0)", name)
87 } 86 }
88 }; 87 };
diff --git a/crates/ide_completion/src/render/const_.rs b/crates/ide_completion/src/render/const_.rs
index 5010b642a..8add369e4 100644
--- a/crates/ide_completion/src/render/const_.rs
+++ b/crates/ide_completion/src/render/const_.rs
@@ -36,17 +36,17 @@ impl<'a> ConstRender<'a> {
36 let name = self.name()?; 36 let name = self.name()?;
37 let detail = self.detail(); 37 let detail = self.detail();
38 38
39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 39 let mut item =
40 .kind(SymbolKind::Const) 40 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name);
41 item.kind(SymbolKind::Const)
41 .set_documentation(self.ctx.docs(self.const_)) 42 .set_documentation(self.ctx.docs(self.const_))
42 .set_deprecated( 43 .set_deprecated(
43 self.ctx.is_deprecated(self.const_) 44 self.ctx.is_deprecated(self.const_)
44 || self.ctx.is_deprecated_assoc_item(self.const_), 45 || self.ctx.is_deprecated_assoc_item(self.const_),
45 ) 46 )
46 .detail(detail) 47 .detail(detail);
47 .build();
48 48
49 Some(item) 49 Some(item.build())
50 } 50 }
51 51
52 fn name(&self) -> Option<String> { 52 fn name(&self) -> Option<String> {
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index 9214193b4..e8cfcc0c7 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -3,7 +3,6 @@
3use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; 3use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use itertools::Itertools; 5use itertools::Itertools;
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionKind, ImportEdit},
@@ -56,27 +55,26 @@ impl<'a> EnumRender<'a> {
56 } 55 }
57 56
58 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 57 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
59 let mut builder = CompletionItem::new( 58 let mut item = CompletionItem::new(
60 CompletionKind::Reference, 59 CompletionKind::Reference,
61 self.ctx.source_range(), 60 self.ctx.source_range(),
62 self.qualified_name.clone(), 61 self.qualified_name.clone(),
63 ) 62 );
64 .kind(SymbolKind::Variant) 63 item.kind(SymbolKind::Variant)
65 .set_documentation(self.variant.docs(self.ctx.db())) 64 .set_documentation(self.variant.docs(self.ctx.db()))
66 .set_deprecated(self.ctx.is_deprecated(self.variant)) 65 .set_deprecated(self.ctx.is_deprecated(self.variant))
67 .add_import(import_to_add) 66 .add_import(import_to_add)
68 .detail(self.detail()); 67 .detail(self.detail());
69 68
70 if self.variant_kind == StructKind::Tuple { 69 if self.variant_kind == StructKind::Tuple {
71 mark::hit!(inserts_parens_for_tuple_enums); 70 cov_mark::hit!(inserts_parens_for_tuple_enums);
72 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); 71 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
73 builder = 72 item.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
74 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
75 } else if self.path.is_some() { 73 } else if self.path.is_some() {
76 builder = builder.lookup_by(self.short_qualified_name); 74 item.lookup_by(self.short_qualified_name);
77 } 75 }
78 76
79 builder.build() 77 item.build()
80 } 78 }
81 79
82 fn detail(&self) -> String { 80 fn detail(&self) -> String {
@@ -103,13 +101,11 @@ impl<'a> EnumRender<'a> {
103 101
104#[cfg(test)] 102#[cfg(test)]
105mod tests { 103mod tests {
106 use test_utils::mark;
107
108 use crate::test_utils::check_edit; 104 use crate::test_utils::check_edit;
109 105
110 #[test] 106 #[test]
111 fn inserts_parens_for_tuple_enums() { 107 fn inserts_parens_for_tuple_enums() {
112 mark::check!(inserts_parens_for_tuple_enums); 108 cov_mark::check!(inserts_parens_for_tuple_enums);
113 check_edit( 109 check_edit(
114 "Some", 110 "Some",
115 r#" 111 r#"
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index e46e21d24..f4dabe3d1 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -3,7 +3,6 @@
3use hir::{HasSource, HirDisplay, Type}; 3use hir::{HasSource, HirDisplay, Type};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use syntax::ast::Fn; 5use syntax::ast::Fn;
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
@@ -42,16 +41,21 @@ impl<'a> FunctionRender<'a> {
42 41
43 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
44 let params = self.params(); 43 let params = self.params();
45 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 44 let mut item = CompletionItem::new(
46 .kind(self.kind()) 45 CompletionKind::Reference,
46 self.ctx.source_range(),
47 self.name.clone(),
48 );
49 item.kind(self.kind())
47 .set_documentation(self.ctx.docs(self.func)) 50 .set_documentation(self.ctx.docs(self.func))
48 .set_deprecated( 51 .set_deprecated(
49 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), 52 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
50 ) 53 )
51 .detail(self.detail()) 54 .detail(self.detail())
52 .add_call_parens(self.ctx.completion, self.name, params) 55 .add_call_parens(self.ctx.completion, self.name, params)
53 .add_import(import_to_add) 56 .add_import(import_to_add);
54 .build() 57
58 item.build()
55 } 59 }
56 60
57 fn detail(&self) -> String { 61 fn detail(&self) -> String {
@@ -82,7 +86,7 @@ impl<'a> FunctionRender<'a> {
82 self.func.method_params(self.ctx.db()).unwrap_or_default() 86 self.func.method_params(self.ctx.db()).unwrap_or_default()
83 } else { 87 } else {
84 if let Some(s) = ast_params.self_param() { 88 if let Some(s) = ast_params.self_param() {
85 mark::hit!(parens_for_method_call_as_assoc_fn); 89 cov_mark::hit!(parens_for_method_call_as_assoc_fn);
86 params_pats.push(Some(s.to_string())); 90 params_pats.push(Some(s.to_string()));
87 } 91 }
88 self.func.assoc_fn_params(self.ctx.db()) 92 self.func.assoc_fn_params(self.ctx.db())
@@ -114,8 +118,6 @@ impl<'a> FunctionRender<'a> {
114 118
115#[cfg(test)] 119#[cfg(test)]
116mod tests { 120mod tests {
117 use test_utils::mark;
118
119 use crate::{ 121 use crate::{
120 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG}, 122 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG},
121 CompletionConfig, 123 CompletionConfig,
@@ -123,7 +125,7 @@ mod tests {
123 125
124 #[test] 126 #[test]
125 fn inserts_parens_for_function_calls() { 127 fn inserts_parens_for_function_calls() {
126 mark::check!(inserts_parens_for_function_calls); 128 cov_mark::check!(inserts_parens_for_function_calls);
127 check_edit( 129 check_edit(
128 "no_args", 130 "no_args",
129 r#" 131 r#"
@@ -191,7 +193,7 @@ fn bar(s: &S) {
191 193
192 #[test] 194 #[test]
193 fn parens_for_method_call_as_assoc_fn() { 195 fn parens_for_method_call_as_assoc_fn() {
194 mark::check!(parens_for_method_call_as_assoc_fn); 196 cov_mark::check!(parens_for_method_call_as_assoc_fn);
195 check_edit( 197 check_edit(
196 "foo", 198 "foo",
197 r#" 199 r#"
@@ -213,7 +215,7 @@ fn main() { S::foo(${1:&self})$0 }
213 215
214 #[test] 216 #[test]
215 fn suppress_arg_snippets() { 217 fn suppress_arg_snippets() {
216 mark::check!(suppress_arg_snippets); 218 cov_mark::check!(suppress_arg_snippets);
217 check_edit_with_config( 219 check_edit_with_config(
218 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG }, 220 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
219 "with_args", 221 "with_args",
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index a4535786f..3fa21ba7c 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -3,7 +3,6 @@
3use hir::{Documentation, HasSource}; 3use hir::{Documentation, HasSource};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use syntax::display::macro_label; 5use syntax::display::macro_label;
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionKind, ImportEdit},
@@ -40,29 +39,31 @@ impl<'a> MacroRender<'a> {
40 } 39 }
41 40
42 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> { 41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
43 let mut builder = 42 let mut item =
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label()) 43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label());
45 .kind(SymbolKind::Macro) 44 item.kind(SymbolKind::Macro)
46 .set_documentation(self.docs.clone()) 45 .set_documentation(self.docs.clone())
47 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 46 .set_deprecated(self.ctx.is_deprecated(self.macro_))
48 .add_import(import_to_add) 47 .add_import(import_to_add)
49 .set_detail(self.detail()); 48 .set_detail(self.detail());
50 49
51 let needs_bang = self.needs_bang(); 50 let needs_bang = self.needs_bang();
52 builder = match self.ctx.snippet_cap() { 51 match self.ctx.snippet_cap() {
53 Some(cap) if needs_bang => { 52 Some(cap) if needs_bang => {
54 let snippet = self.snippet(); 53 let snippet = self.snippet();
55 let lookup = self.lookup(); 54 let lookup = self.lookup();
56 builder.insert_snippet(cap, snippet).lookup_by(lookup) 55 item.insert_snippet(cap, snippet).lookup_by(lookup);
56 }
57 None if needs_bang => {
58 item.insert_text(self.banged_name());
57 } 59 }
58 None if needs_bang => builder.insert_text(self.banged_name()),
59 _ => { 60 _ => {
60 mark::hit!(dont_insert_macro_call_parens_unncessary); 61 cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
61 builder.insert_text(&self.name) 62 item.insert_text(&self.name);
62 } 63 }
63 }; 64 };
64 65
65 Some(builder.build()) 66 Some(item.build())
66 } 67 }
67 68
68 fn needs_bang(&self) -> bool { 69 fn needs_bang(&self) -> bool {
@@ -125,13 +126,11 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
125 126
126#[cfg(test)] 127#[cfg(test)]
127mod tests { 128mod tests {
128 use test_utils::mark;
129
130 use crate::test_utils::check_edit; 129 use crate::test_utils::check_edit;
131 130
132 #[test] 131 #[test]
133 fn dont_insert_macro_call_parens_unncessary() { 132 fn dont_insert_macro_call_parens_unncessary() {
134 mark::check!(dont_insert_macro_call_parens_unncessary); 133 cov_mark::check!(dont_insert_macro_call_parens_unncessary);
135 check_edit( 134 check_edit(
136 "frobnicate!", 135 "frobnicate!",
137 r#" 136 r#"
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index 465dfe00c..ca2926125 100644
--- a/crates/ide_completion/src/render/pattern.rs
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -69,19 +69,19 @@ fn build_completion(
69 ctx: RenderContext<'_>, 69 ctx: RenderContext<'_>,
70 name: String, 70 name: String,
71 pat: String, 71 pat: String,
72 item: impl HasAttrs + Copy, 72 def: impl HasAttrs + Copy,
73) -> CompletionItem { 73) -> CompletionItem {
74 let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) 74 let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name);
75 .kind(CompletionItemKind::Binding) 75 item.kind(CompletionItemKind::Binding)
76 .set_documentation(ctx.docs(item)) 76 .set_documentation(ctx.docs(def))
77 .set_deprecated(ctx.is_deprecated(item)) 77 .set_deprecated(ctx.is_deprecated(def))
78 .detail(&pat); 78 .detail(&pat);
79 let completion = if let Some(snippet_cap) = ctx.snippet_cap() { 79 if let Some(snippet_cap) = ctx.snippet_cap() {
80 completion.insert_snippet(snippet_cap, pat) 80 item.insert_snippet(snippet_cap, pat);
81 } else { 81 } else {
82 completion.insert_text(pat) 82 item.insert_text(pat);
83 }; 83 };
84 completion.build() 84 item.build()
85} 85}
86 86
87fn render_pat( 87fn render_pat(
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs
index bd97c3692..e47b4c745 100644
--- a/crates/ide_completion/src/render/type_alias.rs
+++ b/crates/ide_completion/src/render/type_alias.rs
@@ -36,17 +36,17 @@ impl<'a> TypeAliasRender<'a> {
36 let name = self.name()?; 36 let name = self.name()?;
37 let detail = self.detail(); 37 let detail = self.detail();
38 38
39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 39 let mut item =
40 .kind(SymbolKind::TypeAlias) 40 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name);
41 item.kind(SymbolKind::TypeAlias)
41 .set_documentation(self.ctx.docs(self.type_alias)) 42 .set_documentation(self.ctx.docs(self.type_alias))
42 .set_deprecated( 43 .set_deprecated(
43 self.ctx.is_deprecated(self.type_alias) 44 self.ctx.is_deprecated(self.type_alias)
44 || self.ctx.is_deprecated_assoc_item(self.type_alias), 45 || self.ctx.is_deprecated_assoc_item(self.type_alias),
45 ) 46 )
46 .detail(detail) 47 .detail(detail);
47 .build();
48 48
49 Some(item) 49 Some(item.build())
50 } 50 }
51 51
52 fn name(&self) -> Option<String> { 52 fn name(&self) -> Option<String> {
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index baff83305..9da844031 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -25,6 +25,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
25 insert_use: InsertUseConfig { 25 insert_use: InsertUseConfig {
26 merge: Some(MergeBehavior::Full), 26 merge: Some(MergeBehavior::Full),
27 prefix_kind: PrefixKind::Plain, 27 prefix_kind: PrefixKind::Plain,
28 group: true,
28 }, 29 },
29}; 30};
30 31
@@ -119,7 +120,7 @@ pub(crate) fn check_edit_with_config(
119 120
120 let mut combined_edit = completion.text_edit().to_owned(); 121 let mut combined_edit = completion.text_edit().to_owned();
121 if let Some(import_text_edit) = 122 if let Some(import_text_edit) =
122 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use.merge)) 123 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use))
123 { 124 {
124 combined_edit.union(import_text_edit).expect( 125 combined_edit.union(import_text_edit).expect(
125 "Failed to apply completion resolve changes: change ranges overlap, but should not", 126 "Failed to apply completion resolve changes: change ranges overlap, but should not",
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index d4612a75e..1f7a90d20 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13log = "0.4.8" 14log = "0.4.8"
14rayon = "1.5.0" 15rayon = "1.5.0"
15fst = { version = "0.4", default-features = false } 16fst = { version = "0.4", default-features = false }
@@ -23,10 +24,10 @@ syntax = { path = "../syntax", version = "0.0.0" }
23text_edit = { path = "../text_edit", version = "0.0.0" } 24text_edit = { path = "../text_edit", version = "0.0.0" }
24base_db = { path = "../base_db", version = "0.0.0" } 25base_db = { path = "../base_db", version = "0.0.0" }
25profile = { path = "../profile", version = "0.0.0" } 26profile = { path = "../profile", version = "0.0.0" }
26test_utils = { path = "../test_utils", version = "0.0.0" }
27# ide should depend only on the top-level `hir` package. if you need 27# ide should depend only on the top-level `hir` package. if you need
28# something from some `hir_xxx` subpackage, reexport the API via `hir`. 28# something from some `hir_xxx` subpackage, reexport the API via `hir`.
29hir = { path = "../hir", version = "0.0.0" } 29hir = { path = "../hir", version = "0.0.0" }
30 30
31[dev-dependencies] 31[dev-dependencies]
32test_utils = { path = "../test_utils" }
32expect-test = "1.1" 33expect-test = "1.1"
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index 615fa7b0e..d8878aa91 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -7,7 +7,6 @@ use syntax::{
7 ast::{self, ArgListOwner}, 7 ast::{self, ArgListOwner},
8 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, 8 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
9}; 9};
10use test_utils::mark;
11 10
12use crate::RootDatabase; 11use crate::RootDatabase;
13 12
@@ -122,7 +121,7 @@ fn call_info_impl(
122 121
123 let arg_list_range = arg_list.syntax().text_range(); 122 let arg_list_range = arg_list.syntax().text_range();
124 if !arg_list_range.contains_inclusive(token.text_range().start()) { 123 if !arg_list_range.contains_inclusive(token.text_range().start()) {
125 mark::hit!(call_info_bad_offset); 124 cov_mark::hit!(call_info_bad_offset);
126 return None; 125 return None;
127 } 126 }
128 let param = std::cmp::min( 127 let param = std::cmp::min(
@@ -162,7 +161,7 @@ impl ActiveParameter {
162 let idx = active_parameter?; 161 let idx = active_parameter?;
163 let mut params = signature.params(sema.db); 162 let mut params = signature.params(sema.db);
164 if !(idx < params.len()) { 163 if !(idx < params.len()) {
165 mark::hit!(too_many_arguments); 164 cov_mark::hit!(too_many_arguments);
166 return None; 165 return None;
167 } 166 }
168 let (pat, ty) = params.swap_remove(idx); 167 let (pat, ty) = params.swap_remove(idx);
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index c714cf280..9f84c253c 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -1,7 +1,7 @@
1use crate::RootDatabase; 1use crate::RootDatabase;
2use base_db::{fixture::ChangeFixture, FilePosition}; 2use base_db::{fixture::ChangeFixture, FilePosition};
3use expect_test::{expect, Expect}; 3use expect_test::{expect, Expect};
4use test_utils::{mark, RangeOrOffset}; 4use test_utils::RangeOrOffset;
5 5
6/// Creates analysis from a multi-file fixture, returns positions marked with $0. 6/// Creates analysis from a multi-file fixture, returns positions marked with $0.
7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { 7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
@@ -347,7 +347,7 @@ pub fn foo(mut r: WriteHandler<()>) {
347 347
348#[test] 348#[test]
349fn call_info_bad_offset() { 349fn call_info_bad_offset() {
350 mark::check!(call_info_bad_offset); 350 cov_mark::check!(call_info_bad_offset);
351 check( 351 check(
352 r#" 352 r#"
353fn foo(x: u32, y: u32) -> u32 {x + y} 353fn foo(x: u32, y: u32) -> u32 {x + y}
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 3ff77400b..3c95d3cff 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -2,11 +2,19 @@
2pub mod insert_use; 2pub mod insert_use;
3pub mod import_assets; 3pub mod import_assets;
4 4
5use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; 5use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
6use syntax::ast::{self, make}; 6use syntax::ast::{self, make};
7 7
8use crate::RootDatabase; 8use crate::RootDatabase;
9 9
10pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
11 match item {
12 ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).name(db),
13 ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).name(db),
14 ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db),
15 }
16}
17
10/// Converts the mod path struct into its ast representation. 18/// Converts the mod path struct into its ast representation.
11pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { 19pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
12 let _p = profile::span("mod_path_to_ast"); 20 let _p = profile::span("mod_path_to_ast");
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 517abbb4b..e03ccd351 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,19 +1,29 @@
1//! Look up accessible paths for items. 1//! Look up accessible paths for items.
2use either::Either; 2use hir::{
3use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics}; 3 AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module,
4 ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type,
5};
6use itertools::Itertools;
4use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
5use syntax::{ast, AstNode}; 8use syntax::{ast, utils::path_to_string_stripping_turbo_fish, AstNode, SyntaxNode};
6 9
7use crate::{ 10use crate::{
8 imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, 11 items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
9 RootDatabase, 12 RootDatabase,
10}; 13};
11 14
15use super::item_name;
16
17/// A candidate for import, derived during various IDE activities:
18/// * completion with imports on the fly proposals
19/// * completion edit resolve requests
20/// * assists
21/// * etc.
12#[derive(Debug)] 22#[derive(Debug)]
13pub enum ImportCandidate { 23pub enum ImportCandidate {
14 // A path, qualified (`std::collections::HashMap`) or not (`HashMap`). 24 /// A path, qualified (`std::collections::HashMap`) or not (`HashMap`).
15 Path(PathImportCandidate), 25 Path(PathImportCandidate),
16 /// A trait associated function (with no self parameter) or associated constant. 26 /// A trait associated function (with no self parameter) or an associated constant.
17 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type 27 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
18 /// and `name` is the `test_function` 28 /// and `name` is the `test_function`
19 TraitAssocItem(TraitImportCandidate), 29 TraitAssocItem(TraitImportCandidate),
@@ -23,21 +33,40 @@ pub enum ImportCandidate {
23 TraitMethod(TraitImportCandidate), 33 TraitMethod(TraitImportCandidate),
24} 34}
25 35
36/// A trait import needed for a given associated item access.
37/// For `some::path::SomeStruct::ASSOC_`, contains the
38/// type of `some::path::SomeStruct` and `ASSOC_` as the item name.
26#[derive(Debug)] 39#[derive(Debug)]
27pub struct TraitImportCandidate { 40pub struct TraitImportCandidate {
28 pub receiver_ty: hir::Type, 41 /// A type of the item that has the associated item accessed at.
29 pub name: NameToImport, 42 pub receiver_ty: Type,
43 /// The associated item name that the trait to import should contain.
44 pub assoc_item_name: NameToImport,
30} 45}
31 46
47/// Path import for a given name, qualified or not.
32#[derive(Debug)] 48#[derive(Debug)]
33pub struct PathImportCandidate { 49pub struct PathImportCandidate {
34 pub qualifier: Option<ast::Path>, 50 /// Optional qualifier before name.
51 pub qualifier: Option<FirstSegmentUnresolved>,
52 /// The name the item (struct, trait, enum, etc.) should have.
35 pub name: NameToImport, 53 pub name: NameToImport,
36} 54}
37 55
56/// A qualifier that has a first segment and it's unresolved.
57#[derive(Debug)]
58pub struct FirstSegmentUnresolved {
59 fist_segment: ast::NameRef,
60 full_qualifier: ast::Path,
61}
62
63/// A name that will be used during item lookups.
38#[derive(Debug)] 64#[derive(Debug)]
39pub enum NameToImport { 65pub enum NameToImport {
66 /// Requires items with names that exactly match the given string, case-sensitive.
40 Exact(String), 67 Exact(String),
68 /// Requires items with names that case-insensitively contain all letters from the string,
69 /// in the same order, but not necessary adjacent.
41 Fuzzy(String), 70 Fuzzy(String),
42} 71}
43 72
@@ -50,10 +79,12 @@ impl NameToImport {
50 } 79 }
51} 80}
52 81
82/// A struct to find imports in the project, given a certain name (or its part) and the context.
53#[derive(Debug)] 83#[derive(Debug)]
54pub struct ImportAssets { 84pub struct ImportAssets {
55 import_candidate: ImportCandidate, 85 import_candidate: ImportCandidate,
56 module_with_candidate: hir::Module, 86 candidate_node: SyntaxNode,
87 module_with_candidate: Module,
57} 88}
58 89
59impl ImportAssets { 90impl ImportAssets {
@@ -61,9 +92,11 @@ impl ImportAssets {
61 method_call: &ast::MethodCallExpr, 92 method_call: &ast::MethodCallExpr,
62 sema: &Semantics<RootDatabase>, 93 sema: &Semantics<RootDatabase>,
63 ) -> Option<Self> { 94 ) -> Option<Self> {
95 let candidate_node = method_call.syntax().clone();
64 Some(Self { 96 Some(Self {
65 import_candidate: ImportCandidate::for_method_call(sema, method_call)?, 97 import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
66 module_with_candidate: sema.scope(method_call.syntax()).module()?, 98 module_with_candidate: sema.scope(&candidate_node).module()?,
99 candidate_node,
67 }) 100 })
68 } 101 }
69 102
@@ -71,94 +104,94 @@ impl ImportAssets {
71 fully_qualified_path: &ast::Path, 104 fully_qualified_path: &ast::Path,
72 sema: &Semantics<RootDatabase>, 105 sema: &Semantics<RootDatabase>,
73 ) -> Option<Self> { 106 ) -> Option<Self> {
74 let syntax_under_caret = fully_qualified_path.syntax(); 107 let candidate_node = fully_qualified_path.syntax().clone();
75 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { 108 if candidate_node.ancestors().find_map(ast::Use::cast).is_some() {
76 return None; 109 return None;
77 } 110 }
78 Some(Self { 111 Some(Self {
79 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, 112 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
80 module_with_candidate: sema.scope(syntax_under_caret).module()?, 113 module_with_candidate: sema.scope(&candidate_node).module()?,
114 candidate_node,
81 }) 115 })
82 } 116 }
83 117
84 pub fn for_fuzzy_path( 118 pub fn for_fuzzy_path(
85 module_with_path: Module, 119 module_with_candidate: Module,
86 qualifier: Option<ast::Path>, 120 qualifier: Option<ast::Path>,
87 fuzzy_name: String, 121 fuzzy_name: String,
88 sema: &Semantics<RootDatabase>, 122 sema: &Semantics<RootDatabase>,
123 candidate_node: SyntaxNode,
89 ) -> Option<Self> { 124 ) -> Option<Self> {
90 Some(match qualifier { 125 Some(Self {
91 Some(qualifier) => { 126 import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?,
92 let qualifier_resolution = sema.resolve_path(&qualifier)?; 127 module_with_candidate,
93 match qualifier_resolution { 128 candidate_node,
94 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self {
95 import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate {
96 receiver_ty: assoc_item_path.ty(sema.db),
97 name: NameToImport::Fuzzy(fuzzy_name),
98 }),
99 module_with_candidate: module_with_path,
100 },
101 _ => Self {
102 import_candidate: ImportCandidate::Path(PathImportCandidate {
103 qualifier: Some(qualifier),
104 name: NameToImport::Fuzzy(fuzzy_name),
105 }),
106 module_with_candidate: module_with_path,
107 },
108 }
109 }
110 None => Self {
111 import_candidate: ImportCandidate::Path(PathImportCandidate {
112 qualifier: None,
113 name: NameToImport::Fuzzy(fuzzy_name),
114 }),
115 module_with_candidate: module_with_path,
116 },
117 }) 129 })
118 } 130 }
119 131
120 pub fn for_fuzzy_method_call( 132 pub fn for_fuzzy_method_call(
121 module_with_method_call: Module, 133 module_with_method_call: Module,
122 receiver_ty: hir::Type, 134 receiver_ty: Type,
123 fuzzy_method_name: String, 135 fuzzy_method_name: String,
136 candidate_node: SyntaxNode,
124 ) -> Option<Self> { 137 ) -> Option<Self> {
125 Some(Self { 138 Some(Self {
126 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { 139 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
127 receiver_ty, 140 receiver_ty,
128 name: NameToImport::Fuzzy(fuzzy_method_name), 141 assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name),
129 }), 142 }),
130 module_with_candidate: module_with_method_call, 143 module_with_candidate: module_with_method_call,
144 candidate_node,
131 }) 145 })
132 } 146 }
133} 147}
134 148
149/// An import (not necessary the only one) that corresponds a certain given [`PathImportCandidate`].
150/// (the structure is not entirely correct, since there can be situations requiring two imports, see FIXME below for the details)
151#[derive(Debug, Clone, PartialEq, Eq, Hash)]
152pub struct LocatedImport {
153 /// The path to use in the `use` statement for a given candidate to be imported.
154 pub import_path: ModPath,
155 /// An item that will be imported with the import path given.
156 pub item_to_import: ItemInNs,
157 /// The path import candidate, resolved.
158 ///
159 /// Not necessary matches the import:
160 /// For any associated constant from the trait, we try to access as `some::path::SomeStruct::ASSOC_`
161 /// the original item is the associated constant, but the import has to be a trait that
162 /// defines this constant.
163 pub original_item: ItemInNs,
164 /// A path of the original item.
165 pub original_path: Option<ModPath>,
166}
167
168impl LocatedImport {
169 pub fn new(
170 import_path: ModPath,
171 item_to_import: ItemInNs,
172 original_item: ItemInNs,
173 original_path: Option<ModPath>,
174 ) -> Self {
175 Self { import_path, item_to_import, original_item, original_path }
176 }
177}
178
135impl ImportAssets { 179impl ImportAssets {
136 pub fn import_candidate(&self) -> &ImportCandidate { 180 pub fn import_candidate(&self) -> &ImportCandidate {
137 &self.import_candidate 181 &self.import_candidate
138 } 182 }
139 183
140 fn name_to_import(&self) -> &NameToImport {
141 match &self.import_candidate {
142 ImportCandidate::Path(candidate) => &candidate.name,
143 ImportCandidate::TraitAssocItem(candidate)
144 | ImportCandidate::TraitMethod(candidate) => &candidate.name,
145 }
146 }
147
148 pub fn search_for_imports( 184 pub fn search_for_imports(
149 &self, 185 &self,
150 sema: &Semantics<RootDatabase>, 186 sema: &Semantics<RootDatabase>,
151 prefix_kind: PrefixKind, 187 prefix_kind: PrefixKind,
152 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 188 ) -> Vec<LocatedImport> {
153 let _p = profile::span("import_assets::search_for_imports"); 189 let _p = profile::span("import_assets::search_for_imports");
154 self.search_for(sema, Some(prefix_kind)) 190 self.search_for(sema, Some(prefix_kind))
155 } 191 }
156 192
157 /// This may return non-absolute paths if a part of the returned path is already imported into scope. 193 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
158 pub fn search_for_relative_paths( 194 pub fn search_for_relative_paths(&self, sema: &Semantics<RootDatabase>) -> Vec<LocatedImport> {
159 &self,
160 sema: &Semantics<RootDatabase>,
161 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
162 let _p = profile::span("import_assets::search_for_relative_paths"); 195 let _p = profile::span("import_assets::search_for_relative_paths");
163 self.search_for(sema, None) 196 self.search_for(sema, None)
164 } 197 }
@@ -166,29 +199,29 @@ impl ImportAssets {
166 fn search_for( 199 fn search_for(
167 &self, 200 &self,
168 sema: &Semantics<RootDatabase>, 201 sema: &Semantics<RootDatabase>,
169 prefixed: Option<hir::PrefixKind>, 202 prefixed: Option<PrefixKind>,
170 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 203 ) -> Vec<LocatedImport> {
171 let current_crate = self.module_with_candidate.krate(); 204 let items_with_candidate_name = match self.name_to_import() {
172 205 NameToImport::Exact(exact_name) => items_locator::with_exact_name(
173 let unfiltered_imports = match self.name_to_import() { 206 sema,
174 NameToImport::Exact(exact_name) => { 207 self.module_with_candidate.krate(),
175 imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) 208 exact_name.clone(),
176 } 209 ),
177 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: 210 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
178 // instead, we need to look up all trait impls for a certain struct and search through them only 211 // instead, we need to look up all trait impls for a certain struct and search through them only
179 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 212 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
180 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup 213 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
181 // for the details 214 // for the details
182 NameToImport::Fuzzy(fuzzy_name) => { 215 NameToImport::Fuzzy(fuzzy_name) => {
183 let (assoc_item_search, limit) = match self.import_candidate { 216 let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() {
184 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { 217 (AssocItemSearch::AssocItemsOnly, None)
185 (AssocItemSearch::AssocItemsOnly, None) 218 } else {
186 } 219 (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT))
187 _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)),
188 }; 220 };
189 imports_locator::find_similar_imports( 221
222 items_locator::with_similar_name(
190 sema, 223 sema,
191 current_crate, 224 self.module_with_candidate.krate(),
192 fuzzy_name.clone(), 225 fuzzy_name.clone(),
193 assoc_item_search, 226 assoc_item_search,
194 limit, 227 limit,
@@ -196,63 +229,224 @@ impl ImportAssets {
196 } 229 }
197 }; 230 };
198 231
199 let db = sema.db; 232 let scope_definitions = self.scope_definitions(sema);
200 let mut res = 233 self.applicable_defs(sema.db, prefixed, items_with_candidate_name)
201 applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports) 234 .into_iter()
202 .filter_map(|candidate| { 235 .filter(|import| import.import_path.len() > 1)
203 let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into); 236 .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
204 237 .sorted_by_key(|import| import.import_path.clone())
205 let item_to_search = match self.import_candidate { 238 .collect()
206 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { 239 }
207 let canidate_trait = match candidate { 240
208 Either::Left(module_def) => { 241 fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> {
209 module_def.as_assoc_item(db)?.containing_trait(db) 242 let mut scope_definitions = FxHashSet::default();
210 } 243 sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| {
211 _ => None, 244 scope_definitions.insert(scope_def);
212 }?; 245 });
213 ModuleDef::from(canidate_trait).into() 246 scope_definitions
214 } 247 }
215 _ => item, 248
216 }; 249 fn name_to_import(&self) -> &NameToImport {
217 250 match &self.import_candidate {
218 if let Some(prefix_kind) = prefixed { 251 ImportCandidate::Path(candidate) => &candidate.name,
219 self.module_with_candidate.find_use_path_prefixed( 252 ImportCandidate::TraitAssocItem(candidate)
220 db, 253 | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name,
221 item_to_search, 254 }
222 prefix_kind, 255 }
223 ) 256
224 } else { 257 fn applicable_defs(
225 self.module_with_candidate.find_use_path(db, item_to_search) 258 &self,
226 } 259 db: &RootDatabase,
227 .map(|path| (path, item)) 260 prefixed: Option<PrefixKind>,
228 }) 261 items_with_candidate_name: FxHashSet<ItemInNs>,
229 .filter(|(use_path, _)| use_path.len() > 1) 262 ) -> FxHashSet<LocatedImport> {
230 .collect::<Vec<_>>(); 263 let _p = profile::span("import_assets::applicable_defs");
231 res.sort_by_cached_key(|(path, _)| path.clone()); 264 let current_crate = self.module_with_candidate.krate();
232 res 265
266 let mod_path = |item| {
267 get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed)
268 };
269
270 match &self.import_candidate {
271 ImportCandidate::Path(path_candidate) => {
272 path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name)
273 }
274 ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items(
275 db,
276 current_crate,
277 trait_candidate,
278 true,
279 mod_path,
280 items_with_candidate_name,
281 ),
282 ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
283 db,
284 current_crate,
285 trait_candidate,
286 false,
287 mod_path,
288 items_with_candidate_name,
289 ),
290 }
233 } 291 }
234} 292}
235 293
236fn applicable_defs<'a>( 294fn path_applicable_imports(
237 import_candidate: &ImportCandidate,
238 current_crate: Crate,
239 db: &RootDatabase, 295 db: &RootDatabase,
240 unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, 296 path_candidate: &PathImportCandidate,
241) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { 297 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
242 let receiver_ty = match import_candidate { 298 items_with_candidate_name: FxHashSet<ItemInNs>,
243 ImportCandidate::Path(_) => return unfiltered_imports, 299) -> FxHashSet<LocatedImport> {
244 ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => { 300 let _p = profile::span("import_assets::path_applicable_imports");
245 &candidate.receiver_ty 301
302 let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier {
303 None => {
304 return items_with_candidate_name
305 .into_iter()
306 .filter_map(|item| {
307 Some(LocatedImport::new(mod_path(item)?, item, item, mod_path(item)))
308 })
309 .collect();
246 } 310 }
311 Some(first_segment_unresolved) => (
312 first_segment_unresolved.fist_segment.to_string(),
313 path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier),
314 ),
247 }; 315 };
248 316
317 items_with_candidate_name
318 .into_iter()
319 .filter_map(|item| {
320 import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item)
321 })
322 .collect()
323}
324
325fn import_for_item(
326 db: &RootDatabase,
327 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
328 unresolved_first_segment: &str,
329 unresolved_qualifier: &str,
330 original_item: ItemInNs,
331) -> Option<LocatedImport> {
332 let _p = profile::span("import_assets::import_for_item");
333
334 let original_item_candidate = item_for_path_search(db, original_item)?;
335 let import_path_candidate = mod_path(original_item_candidate)?;
336 let import_path_string = import_path_candidate.to_string();
337
338 let expected_import_end = if item_as_assoc(db, original_item).is_some() {
339 unresolved_qualifier.to_string()
340 } else {
341 format!("{}::{}", unresolved_qualifier, item_name(db, original_item)?)
342 };
343 if !import_path_string.contains(unresolved_first_segment)
344 || !import_path_string.ends_with(&expected_import_end)
345 {
346 return None;
347 }
348
349 let segment_import =
350 find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?;
351 let trait_item_to_import = item_as_assoc(db, original_item)
352 .and_then(|assoc| assoc.containing_trait(db))
353 .map(|trait_| ItemInNs::from(ModuleDef::from(trait_)));
354 Some(match (segment_import == original_item_candidate, trait_item_to_import) {
355 (true, Some(_)) => {
356 // FIXME we should be able to import both the trait and the segment,
357 // but it's unclear what to do with overlapping edits (merge imports?)
358 // especially in case of lazy completion edit resolutions.
359 return None;
360 }
361 (false, Some(trait_to_import)) => LocatedImport::new(
362 mod_path(trait_to_import)?,
363 trait_to_import,
364 original_item,
365 mod_path(original_item),
366 ),
367 (true, None) => LocatedImport::new(
368 import_path_candidate,
369 original_item_candidate,
370 original_item,
371 mod_path(original_item),
372 ),
373 (false, None) => LocatedImport::new(
374 mod_path(segment_import)?,
375 segment_import,
376 original_item,
377 mod_path(original_item),
378 ),
379 })
380}
381
382fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
383 Some(match item {
384 ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
385 Some(assoc_item) => match assoc_item.container(db) {
386 AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
387 AssocItemContainer::Impl(impl_) => {
388 ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?))
389 }
390 },
391 None => item,
392 },
393 ItemInNs::Macros(_) => item,
394 })
395}
396
397fn find_import_for_segment(
398 db: &RootDatabase,
399 original_item: ItemInNs,
400 unresolved_first_segment: &str,
401) -> Option<ItemInNs> {
402 let segment_is_name = item_name(db, original_item)
403 .map(|name| name.to_string() == unresolved_first_segment)
404 .unwrap_or(false);
405
406 Some(if segment_is_name {
407 original_item
408 } else {
409 let matching_module =
410 module_with_segment_name(db, &unresolved_first_segment, original_item)?;
411 ItemInNs::from(ModuleDef::from(matching_module))
412 })
413}
414
415fn module_with_segment_name(
416 db: &RootDatabase,
417 segment_name: &str,
418 candidate: ItemInNs,
419) -> Option<Module> {
420 let mut current_module = match candidate {
421 ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).module(db),
422 ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).module(db),
423 ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).module(db),
424 };
425 while let Some(module) = current_module {
426 if let Some(module_name) = module.name(db) {
427 if module_name.to_string() == segment_name {
428 return Some(module);
429 }
430 }
431 current_module = module.parent(db);
432 }
433 None
434}
435
436fn trait_applicable_items(
437 db: &RootDatabase,
438 current_crate: Crate,
439 trait_candidate: &TraitImportCandidate,
440 trait_assoc_item: bool,
441 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
442 items_with_candidate_name: FxHashSet<ItemInNs>,
443) -> FxHashSet<LocatedImport> {
444 let _p = profile::span("import_assets::trait_applicable_items");
249 let mut required_assoc_items = FxHashSet::default(); 445 let mut required_assoc_items = FxHashSet::default();
250 446
251 let trait_candidates = unfiltered_imports 447 let trait_candidates = items_with_candidate_name
252 .filter_map(|input| match input { 448 .into_iter()
253 Either::Left(module_def) => module_def.as_assoc_item(db), 449 .filter_map(|input| item_as_assoc(db, input))
254 _ => None,
255 })
256 .filter_map(|assoc| { 450 .filter_map(|assoc| {
257 let assoc_item_trait = assoc.containing_trait(db)?; 451 let assoc_item_trait = assoc.containing_trait(db)?;
258 required_assoc_items.insert(assoc); 452 required_assoc_items.insert(assoc);
@@ -260,11 +454,10 @@ fn applicable_defs<'a>(
260 }) 454 })
261 .collect(); 455 .collect();
262 456
263 let mut applicable_defs = FxHashSet::default(); 457 let mut located_imports = FxHashSet::default();
264 458
265 match import_candidate { 459 if trait_assoc_item {
266 ImportCandidate::Path(_) => unreachable!(), 460 trait_candidate.receiver_ty.iterate_path_candidates(
267 ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates(
268 db, 461 db,
269 current_crate, 462 current_crate,
270 &trait_candidates, 463 &trait_candidates,
@@ -276,12 +469,21 @@ fn applicable_defs<'a>(
276 return None; 469 return None;
277 } 470 }
278 } 471 }
279 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); 472
473 let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
474 let original_item = assoc_to_item(assoc);
475 located_imports.insert(LocatedImport::new(
476 mod_path(item)?,
477 item,
478 original_item,
479 mod_path(original_item),
480 ));
280 } 481 }
281 None::<()> 482 None::<()>
282 }, 483 },
283 ), 484 )
284 ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates( 485 } else {
486 trait_candidate.receiver_ty.iterate_method_candidates(
285 db, 487 db,
286 current_crate, 488 current_crate,
287 &trait_candidates, 489 &trait_candidates,
@@ -289,21 +491,41 @@ fn applicable_defs<'a>(
289 |_, function| { 491 |_, function| {
290 let assoc = function.as_assoc_item(db)?; 492 let assoc = function.as_assoc_item(db)?;
291 if required_assoc_items.contains(&assoc) { 493 if required_assoc_items.contains(&assoc) {
292 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); 494 let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
495 let original_item = assoc_to_item(assoc);
496 located_imports.insert(LocatedImport::new(
497 mod_path(item)?,
498 item,
499 original_item,
500 mod_path(original_item),
501 ));
293 } 502 }
294 None::<()> 503 None::<()>
295 }, 504 },
296 ), 505 )
297 }; 506 };
298 507
299 Box::new(applicable_defs.into_iter()) 508 located_imports
300} 509}
301 510
302fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef { 511fn assoc_to_item(assoc: AssocItem) -> ItemInNs {
303 match assoc { 512 match assoc {
304 AssocItem::Function(f) => f.into(), 513 AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)),
305 AssocItem::Const(c) => c.into(), 514 AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)),
306 AssocItem::TypeAlias(t) => t.into(), 515 AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)),
516 }
517}
518
519fn get_mod_path(
520 db: &RootDatabase,
521 item_to_search: ItemInNs,
522 module_with_candidate: &Module,
523 prefixed: Option<PrefixKind>,
524) -> Option<ModPath> {
525 if let Some(prefix_kind) = prefixed {
526 module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind)
527 } else {
528 module_with_candidate.find_use_path(db, item_to_search)
307 } 529 }
308} 530}
309 531
@@ -316,7 +538,7 @@ impl ImportCandidate {
316 Some(_) => None, 538 Some(_) => None,
317 None => Some(Self::TraitMethod(TraitImportCandidate { 539 None => Some(Self::TraitMethod(TraitImportCandidate {
318 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, 540 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?,
319 name: NameToImport::Exact(method_call.name_ref()?.to_string()), 541 assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()),
320 })), 542 })),
321 } 543 }
322 } 544 }
@@ -325,41 +547,63 @@ impl ImportCandidate {
325 if sema.resolve_path(path).is_some() { 547 if sema.resolve_path(path).is_some() {
326 return None; 548 return None;
327 } 549 }
550 path_import_candidate(
551 sema,
552 path.qualifier(),
553 NameToImport::Exact(path.segment()?.name_ref()?.to_string()),
554 )
555 }
328 556
329 let segment = path.segment()?; 557 fn for_fuzzy_path(
330 let candidate = if let Some(qualifier) = path.qualifier() { 558 qualifier: Option<ast::Path>,
331 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; 559 fuzzy_name: String,
332 let qualifier_start_path = 560 sema: &Semantics<RootDatabase>,
333 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; 561 ) -> Option<Self> {
334 if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) { 562 path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name))
335 let qualifier_resolution = if qualifier_start_path == qualifier { 563 }
336 qualifier_start_resolution 564
565 fn is_trait_candidate(&self) -> bool {
566 matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_))
567 }
568}
569
570fn path_import_candidate(
571 sema: &Semantics<RootDatabase>,
572 qualifier: Option<ast::Path>,
573 name: NameToImport,
574) -> Option<ImportCandidate> {
575 Some(match qualifier {
576 Some(qualifier) => match sema.resolve_path(&qualifier) {
577 None => {
578 let qualifier_start =
579 qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
580 let qualifier_start_path =
581 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
582 if sema.resolve_path(&qualifier_start_path).is_none() {
583 ImportCandidate::Path(PathImportCandidate {
584 qualifier: Some(FirstSegmentUnresolved {
585 fist_segment: qualifier_start,
586 full_qualifier: qualifier,
587 }),
588 name,
589 })
337 } else { 590 } else {
338 sema.resolve_path(&qualifier)? 591 return None;
339 };
340 match qualifier_resolution {
341 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
342 ImportCandidate::TraitAssocItem(TraitImportCandidate {
343 receiver_ty: assoc_item_path.ty(sema.db),
344 name: NameToImport::Exact(segment.name_ref()?.to_string()),
345 })
346 }
347 _ => return None,
348 } 592 }
349 } else { 593 }
350 ImportCandidate::Path(PathImportCandidate { 594 Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => {
351 qualifier: Some(qualifier), 595 ImportCandidate::TraitAssocItem(TraitImportCandidate {
352 name: NameToImport::Exact(qualifier_start.to_string()), 596 receiver_ty: assoc_item_path.ty(sema.db),
597 assoc_item_name: name,
353 }) 598 })
354 } 599 }
355 } else { 600 Some(_) => return None,
356 ImportCandidate::Path(PathImportCandidate { 601 },
357 qualifier: None, 602 None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }),
358 name: NameToImport::Exact( 603 })
359 segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(), 604}
360 ), 605
361 }) 606fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> {
362 }; 607 item.as_module_def_id()
363 Some(candidate) 608 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
364 }
365} 609}
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index fd4035198..df66d8ea0 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -13,12 +13,12 @@ use syntax::{
13 }, 13 },
14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, 14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
15}; 15};
16use test_utils::mark;
17 16
18#[derive(Clone, Copy, Debug, PartialEq, Eq)] 17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct InsertUseConfig { 18pub struct InsertUseConfig {
20 pub merge: Option<MergeBehavior>, 19 pub merge: Option<MergeBehavior>,
21 pub prefix_kind: hir::PrefixKind, 20 pub prefix_kind: hir::PrefixKind,
21 pub group: bool,
22} 22}
23 23
24#[derive(Debug, Clone)] 24#[derive(Debug, Clone)]
@@ -99,13 +99,13 @@ fn is_inner_comment(token: SyntaxToken) -> bool {
99pub fn insert_use<'a>( 99pub fn insert_use<'a>(
100 scope: &ImportScope, 100 scope: &ImportScope,
101 path: ast::Path, 101 path: ast::Path,
102 merge: Option<MergeBehavior>, 102 cfg: InsertUseConfig,
103) -> SyntaxRewriter<'a> { 103) -> SyntaxRewriter<'a> {
104 let _p = profile::span("insert_use"); 104 let _p = profile::span("insert_use");
105 let mut rewriter = SyntaxRewriter::default(); 105 let mut rewriter = SyntaxRewriter::default();
106 let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false)); 106 let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false));
107 // merge into existing imports if possible 107 // merge into existing imports if possible
108 if let Some(mb) = merge { 108 if let Some(mb) = cfg.merge {
109 for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { 109 for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
110 if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { 110 if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) {
111 rewriter.replace(existing_use.syntax(), merged.syntax()); 111 rewriter.replace(existing_use.syntax(), merged.syntax());
@@ -116,7 +116,7 @@ pub fn insert_use<'a>(
116 116
117 // either we weren't allowed to merge or there is no import that fits the merge conditions 117 // either we weren't allowed to merge or there is no import that fits the merge conditions
118 // so look for the place we have to insert to 118 // so look for the place we have to insert to
119 let (insert_position, add_blank) = find_insert_position(scope, path); 119 let (insert_position, add_blank) = find_insert_position(scope, path, cfg.group);
120 120
121 let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { 121 let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize {
122 Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()) 122 Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into())
@@ -137,7 +137,7 @@ pub fn insert_use<'a>(
137 137
138 if add_blank.has_before() { 138 if add_blank.has_before() {
139 if let Some(indent) = indent.clone() { 139 if let Some(indent) = indent.clone() {
140 mark::hit!(insert_use_indent_before); 140 cov_mark::hit!(insert_use_indent_before);
141 buf.push(indent); 141 buf.push(indent);
142 } 142 }
143 } 143 }
@@ -155,11 +155,11 @@ pub fn insert_use<'a>(
155 // only add indentation *after* our stuff if there's another node directly after it 155 // only add indentation *after* our stuff if there's another node directly after it
156 if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) { 156 if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) {
157 if let Some(indent) = indent { 157 if let Some(indent) = indent {
158 mark::hit!(insert_use_indent_after); 158 cov_mark::hit!(insert_use_indent_after);
159 buf.push(indent); 159 buf.push(indent);
160 } 160 }
161 } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) { 161 } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) {
162 mark::hit!(insert_use_no_indent_after); 162 cov_mark::hit!(insert_use_no_indent_after);
163 } 163 }
164 164
165 buf 165 buf
@@ -538,6 +538,7 @@ impl AddBlankLine {
538fn find_insert_position( 538fn find_insert_position(
539 scope: &ImportScope, 539 scope: &ImportScope,
540 insert_path: ast::Path, 540 insert_path: ast::Path,
541 group_imports: bool,
541) -> (InsertPosition<SyntaxElement>, AddBlankLine) { 542) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
542 let group = ImportGroup::new(&insert_path); 543 let group = ImportGroup::new(&insert_path);
543 let path_node_iter = scope 544 let path_node_iter = scope
@@ -550,6 +551,14 @@ fn find_insert_position(
550 let has_tl = tree.use_tree_list().is_some(); 551 let has_tl = tree.use_tree_list().is_some();
551 Some((path, has_tl, node)) 552 Some((path, has_tl, node))
552 }); 553 });
554
555 if !group_imports {
556 if let Some((_, _, node)) = path_node_iter.last() {
557 return (InsertPosition::After(node.into()), AddBlankLine::Before);
558 }
559 return (InsertPosition::First, AddBlankLine::AfterTwice);
560 }
561
553 // Iterator that discards anything thats not in the required grouping 562 // Iterator that discards anything thats not in the required grouping
554 // This implementation allows the user to rearrange their import groups as this only takes the first group that fits 563 // This implementation allows the user to rearrange their import groups as this only takes the first group that fits
555 let group_iter = path_node_iter 564 let group_iter = path_node_iter
@@ -565,6 +574,7 @@ fn find_insert_position(
565 use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater 574 use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater
566 }, 575 },
567 ); 576 );
577
568 match post_insert { 578 match post_insert {
569 // insert our import before that element 579 // insert our import before that element
570 Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After), 580 Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After),
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index 4bbe66f1f..3d151e629 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -1,8 +1,32 @@
1use super::*; 1use super::*;
2 2
3use hir::PrefixKind;
3use test_utils::assert_eq_text; 4use test_utils::assert_eq_text;
4 5
5#[test] 6#[test]
7fn insert_not_group() {
8 check(
9 "use external_crate2::bar::A",
10 r"
11use std::bar::B;
12use external_crate::bar::A;
13use crate::bar::A;
14use self::bar::A;
15use super::bar::A;",
16 r"
17use std::bar::B;
18use external_crate::bar::A;
19use crate::bar::A;
20use self::bar::A;
21use super::bar::A;
22use external_crate2::bar::A;",
23 None,
24 false,
25 false,
26 );
27}
28
29#[test]
6fn insert_existing() { 30fn insert_existing() {
7 check_full("std::fs", "use std::fs;", "use std::fs;") 31 check_full("std::fs", "use std::fs;", "use std::fs;")
8} 32}
@@ -27,7 +51,7 @@ use std::bar::G;",
27 51
28#[test] 52#[test]
29fn insert_start_indent() { 53fn insert_start_indent() {
30 mark::check!(insert_use_indent_after); 54 cov_mark::check!(insert_use_indent_after);
31 check_none( 55 check_none(
32 "std::bar::AA", 56 "std::bar::AA",
33 r" 57 r"
@@ -96,7 +120,7 @@ use std::bar::ZZ;",
96 120
97#[test] 121#[test]
98fn insert_end_indent() { 122fn insert_end_indent() {
99 mark::check!(insert_use_indent_before); 123 cov_mark::check!(insert_use_indent_before);
100 check_none( 124 check_none(
101 "std::bar::ZZ", 125 "std::bar::ZZ",
102 r" 126 r"
@@ -231,7 +255,7 @@ fn insert_empty_file() {
231 255
232#[test] 256#[test]
233fn insert_empty_module() { 257fn insert_empty_module() {
234 mark::check!(insert_use_no_indent_after); 258 cov_mark::check!(insert_use_no_indent_after);
235 check( 259 check(
236 "foo::bar", 260 "foo::bar",
237 "mod x {}", 261 "mod x {}",
@@ -240,6 +264,7 @@ fn insert_empty_module() {
240}", 264}",
241 None, 265 None,
242 true, 266 true,
267 true,
243 ) 268 )
244} 269}
245 270
@@ -584,6 +609,7 @@ fn check(
584 ra_fixture_after: &str, 609 ra_fixture_after: &str,
585 mb: Option<MergeBehavior>, 610 mb: Option<MergeBehavior>,
586 module: bool, 611 module: bool,
612 group: bool,
587) { 613) {
588 let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); 614 let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone();
589 if module { 615 if module {
@@ -597,21 +623,25 @@ fn check(
597 .find_map(ast::Path::cast) 623 .find_map(ast::Path::cast)
598 .unwrap(); 624 .unwrap();
599 625
600 let rewriter = insert_use(&file, path, mb); 626 let rewriter = insert_use(
627 &file,
628 path,
629 InsertUseConfig { merge: mb, prefix_kind: PrefixKind::Plain, group },
630 );
601 let result = rewriter.rewrite(file.as_syntax_node()).to_string(); 631 let result = rewriter.rewrite(file.as_syntax_node()).to_string();
602 assert_eq_text!(ra_fixture_after, &result); 632 assert_eq_text!(ra_fixture_after, &result);
603} 633}
604 634
605fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 635fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
606 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false) 636 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false, true)
607} 637}
608 638
609fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 639fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
610 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false) 640 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false, true)
611} 641}
612 642
613fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 643fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
614 check(path, ra_fixture_before, ra_fixture_after, None, false) 644 check(path, ra_fixture_before, ra_fixture_after, None, false, true)
615} 645}
616 646
617fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) { 647fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) {
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/items_locator.rs
index 502e8281a..8a7f02935 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/items_locator.rs
@@ -1,9 +1,10 @@
1//! This module contains an import search functionality that is provided to the assists module. 1//! This module contains an import search functionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use either::Either;
4use hir::{ 5use hir::{
5 import_map::{self, ImportKind}, 6 import_map::{self, ImportKind},
6 AsAssocItem, Crate, MacroDef, ModuleDef, Semantics, 7 AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics,
7}; 8};
8use syntax::{ast, AstNode, SyntaxKind::NAME}; 9use syntax::{ast, AstNode, SyntaxKind::NAME};
9 10
@@ -12,47 +13,47 @@ use crate::{
12 symbol_index::{self, FileSymbol}, 13 symbol_index::{self, FileSymbol},
13 RootDatabase, 14 RootDatabase,
14}; 15};
15use either::Either;
16use rustc_hash::FxHashSet; 16use rustc_hash::FxHashSet;
17 17
18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; 18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
19 19
20pub fn find_exact_imports<'a>( 20pub fn with_exact_name(
21 sema: &Semantics<'a, RootDatabase>, 21 sema: &Semantics<'_, RootDatabase>,
22 krate: Crate, 22 krate: Crate,
23 name_to_import: String, 23 exact_name: String,
24) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> { 24) -> FxHashSet<ItemInNs> {
25 let _p = profile::span("find_exact_imports"); 25 let _p = profile::span("find_exact_imports");
26 Box::new(find_imports( 26 find_items(
27 sema, 27 sema,
28 krate, 28 krate,
29 { 29 {
30 let mut local_query = symbol_index::Query::new(name_to_import.clone()); 30 let mut local_query = symbol_index::Query::new(exact_name.clone());
31 local_query.exact(); 31 local_query.exact();
32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); 32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
33 local_query 33 local_query
34 }, 34 },
35 import_map::Query::new(name_to_import) 35 import_map::Query::new(exact_name)
36 .limit(DEFAULT_QUERY_SEARCH_LIMIT) 36 .limit(DEFAULT_QUERY_SEARCH_LIMIT)
37 .name_only() 37 .name_only()
38 .search_mode(import_map::SearchMode::Equals) 38 .search_mode(import_map::SearchMode::Equals)
39 .case_sensitive(), 39 .case_sensitive(),
40 )) 40 )
41} 41}
42 42
43#[derive(Debug)]
43pub enum AssocItemSearch { 44pub enum AssocItemSearch {
44 Include, 45 Include,
45 Exclude, 46 Exclude,
46 AssocItemsOnly, 47 AssocItemsOnly,
47} 48}
48 49
49pub fn find_similar_imports<'a>( 50pub fn with_similar_name(
50 sema: &Semantics<'a, RootDatabase>, 51 sema: &Semantics<'_, RootDatabase>,
51 krate: Crate, 52 krate: Crate,
52 fuzzy_search_string: String, 53 fuzzy_search_string: String,
53 assoc_item_search: AssocItemSearch, 54 assoc_item_search: AssocItemSearch,
54 limit: Option<usize>, 55 limit: Option<usize>,
55) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { 56) -> FxHashSet<ItemInNs> {
56 let _p = profile::span("find_similar_imports"); 57 let _p = profile::span("find_similar_imports");
57 58
58 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 59 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
@@ -76,37 +77,39 @@ pub fn find_similar_imports<'a>(
76 local_query.limit(limit); 77 local_query.limit(limit);
77 } 78 }
78 79
79 let db = sema.db; 80 find_items(sema, krate, local_query, external_query)
80 Box::new(find_imports(sema, krate, local_query, external_query).filter( 81 .into_iter()
81 move |import_candidate| match assoc_item_search { 82 .filter(move |&item| match assoc_item_search {
82 AssocItemSearch::Include => true, 83 AssocItemSearch::Include => true,
83 AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db), 84 AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
84 AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db), 85 AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
85 }, 86 })
86 )) 87 .collect()
87} 88}
88 89
89fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool { 90fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
90 match import_candidate { 91 item.as_module_def_id()
91 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(), 92 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
92 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(), 93 .is_some()
93 Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(),
94 _ => false,
95 }
96} 94}
97 95
98fn find_imports<'a>( 96fn find_items(
99 sema: &Semantics<'a, RootDatabase>, 97 sema: &Semantics<'_, RootDatabase>,
100 krate: Crate, 98 krate: Crate,
101 local_query: symbol_index::Query, 99 local_query: symbol_index::Query,
102 external_query: import_map::Query, 100 external_query: import_map::Query,
103) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 101) -> FxHashSet<ItemInNs> {
104 let _p = profile::span("find_similar_imports"); 102 let _p = profile::span("find_similar_imports");
105 let db = sema.db; 103 let db = sema.db;
106 104
107 // Query dependencies first. 105 // Query dependencies first.
108 let mut candidates: FxHashSet<_> = 106 let mut candidates = krate
109 krate.query_external_importables(db, external_query).collect(); 107 .query_external_importables(db, external_query)
108 .map(|external_importable| match external_importable {
109 Either::Left(module_def) => ItemInNs::from(module_def),
110 Either::Right(macro_def) => ItemInNs::from(macro_def),
111 })
112 .collect::<FxHashSet<_>>();
110 113
111 // Query the local crate using the symbol index. 114 // Query the local crate using the symbol index.
112 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); 115 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
@@ -114,19 +117,19 @@ fn find_imports<'a>(
114 candidates.extend( 117 candidates.extend(
115 local_results 118 local_results
116 .into_iter() 119 .into_iter()
117 .filter_map(|import_candidate| get_name_definition(sema, &import_candidate)) 120 .filter_map(|local_candidate| get_name_definition(sema, &local_candidate))
118 .filter_map(|name_definition_to_import| match name_definition_to_import { 121 .filter_map(|name_definition_to_import| match name_definition_to_import {
119 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), 122 Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)),
120 Definition::Macro(macro_def) => Some(Either::Right(macro_def)), 123 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
121 _ => None, 124 _ => None,
122 }), 125 }),
123 ); 126 );
124 127
125 candidates.into_iter() 128 candidates
126} 129}
127 130
128fn get_name_definition<'a>( 131fn get_name_definition(
129 sema: &Semantics<'a, RootDatabase>, 132 sema: &Semantics<'_, RootDatabase>,
130 import_candidate: &FileSymbol, 133 import_candidate: &FileSymbol,
131) -> Option<Definition> { 134) -> Option<Definition> {
132 let _p = profile::span("get_name_definition"); 135 let _p = profile::span("get_name_definition");
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 6eb34b06b..88ee4a87d 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -8,7 +8,7 @@ pub mod line_index;
8pub mod symbol_index; 8pub mod symbol_index;
9pub mod defs; 9pub mod defs;
10pub mod search; 10pub mod search;
11pub mod imports_locator; 11pub mod items_locator;
12pub mod source_change; 12pub mod source_change;
13pub mod ty_filter; 13pub mod ty_filter;
14pub mod traits; 14pub mod traits;
diff --git a/crates/ide_ssr/Cargo.toml b/crates/ide_ssr/Cargo.toml
index edbc1846b..8c31df13a 100644
--- a/crates/ide_ssr/Cargo.toml
+++ b/crates/ide_ssr/Cargo.toml
@@ -11,6 +11,7 @@ edition = "2018"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14cov-mark = "1.1"
14rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
15itertools = "0.10.0" 16itertools = "0.10.0"
16 17
@@ -18,7 +19,7 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
18syntax = { path = "../syntax", version = "0.0.0" } 19syntax = { path = "../syntax", version = "0.0.0" }
19ide_db = { path = "../ide_db", version = "0.0.0" } 20ide_db = { path = "../ide_db", version = "0.0.0" }
20hir = { path = "../hir", version = "0.0.0" } 21hir = { path = "../hir", version = "0.0.0" }
21test_utils = { path = "../test_utils", version = "0.0.0" }
22 22
23[dev-dependencies] 23[dev-dependencies]
24test_utils = { path = "../test_utils" }
24expect-test = "1.1" 25expect-test = "1.1"
diff --git a/crates/ide_ssr/src/from_comment.rs b/crates/ide_ssr/src/from_comment.rs
new file mode 100644
index 000000000..f1b312284
--- /dev/null
+++ b/crates/ide_ssr/src/from_comment.rs
@@ -0,0 +1,32 @@
1//! This module allows building an SSR MatchFinder by parsing the SSR rule
2//! from a comment.
3
4use ide_db::{
5 base_db::{FilePosition, FileRange, SourceDatabase},
6 RootDatabase,
7};
8use syntax::{
9 ast::{self, AstNode, AstToken},
10 TextRange,
11};
12
13use crate::MatchFinder;
14
15/// Attempts to build an SSR MatchFinder from a comment at the given file
16/// range. If successful, returns the MatchFinder and a TextRange covering
17/// comment.
18pub fn ssr_from_comment(db: &RootDatabase, frange: FileRange) -> Option<(MatchFinder, TextRange)> {
19 let comment = {
20 let file = db.parse(frange.file_id);
21 file.tree().syntax().token_at_offset(frange.range.start()).find_map(ast::Comment::cast)
22 }?;
23 let comment_text_without_prefix = comment.text().strip_prefix(comment.prefix()).unwrap();
24 let ssr_rule = comment_text_without_prefix.parse().ok()?;
25
26 let lookup_context = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
27
28 let mut match_finder = MatchFinder::in_context(db, lookup_context, vec![]);
29 match_finder.add_rule(ssr_rule).ok()?;
30
31 Some((match_finder, comment.syntax().text_range()))
32}
diff --git a/crates/ide_ssr/src/lib.rs b/crates/ide_ssr/src/lib.rs
index a97fc8bca..00585f448 100644
--- a/crates/ide_ssr/src/lib.rs
+++ b/crates/ide_ssr/src/lib.rs
@@ -57,7 +57,17 @@
57// 57//
58// | VS Code | **Rust Analyzer: Structural Search Replace** 58// | VS Code | **Rust Analyzer: Structural Search Replace**
59// |=== 59// |===
60//
61// Also available as an assist, by writing a comment containing the structural
62// search and replace rule. You will only see the assist if the comment can
63// be parsed as a valid structural search and replace rule.
64//
65// ```rust
66// // Place the cursor on the line below to see the assist 💡.
67// // foo($a, $b) ==>> ($a).foo($b)
68// ```
60 69
70mod from_comment;
61mod matching; 71mod matching;
62mod nester; 72mod nester;
63mod parsing; 73mod parsing;
@@ -71,6 +81,7 @@ mod tests;
71 81
72use crate::errors::bail; 82use crate::errors::bail;
73pub use crate::errors::SsrError; 83pub use crate::errors::SsrError;
84pub use crate::from_comment::ssr_from_comment;
74pub use crate::matching::Match; 85pub use crate::matching::Match;
75use crate::matching::MatchFailureReason; 86use crate::matching::MatchFailureReason;
76use hir::Semantics; 87use hir::Semantics;
diff --git a/crates/ide_ssr/src/matching.rs b/crates/ide_ssr/src/matching.rs
index df013bae9..e1adb381e 100644
--- a/crates/ide_ssr/src/matching.rs
+++ b/crates/ide_ssr/src/matching.rs
@@ -15,7 +15,6 @@ use syntax::{
15 ast::{AstNode, AstToken}, 15 ast::{AstNode, AstToken},
16 SmolStr, 16 SmolStr,
17}; 17};
18use test_utils::mark;
19 18
20// Creates a match error. If we're currently attempting to match some code that we thought we were 19// Creates a match error. If we're currently attempting to match some code that we thought we were
21// going to match, as indicated by the --debug-snippet flag, then populate the reason field. 20// going to match, as indicated by the --debug-snippet flag, then populate the reason field.
@@ -731,7 +730,7 @@ impl NodeKind {
731 fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> { 730 fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> {
732 let ok = match self { 731 let ok = match self {
733 Self::Literal => { 732 Self::Literal => {
734 mark::hit!(literal_constraint); 733 cov_mark::hit!(literal_constraint);
735 ast::Literal::can_cast(node.kind()) 734 ast::Literal::can_cast(node.kind())
736 } 735 }
737 }; 736 };
diff --git a/crates/ide_ssr/src/parsing.rs b/crates/ide_ssr/src/parsing.rs
index 3d5e4feb7..5ff25cb6d 100644
--- a/crates/ide_ssr/src/parsing.rs
+++ b/crates/ide_ssr/src/parsing.rs
@@ -10,7 +10,6 @@ use crate::{SsrError, SsrPattern, SsrRule};
10use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
11use std::{fmt::Display, str::FromStr}; 11use std::{fmt::Display, str::FromStr};
12use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T}; 12use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T};
13use test_utils::mark;
14 13
15#[derive(Debug)] 14#[derive(Debug)]
16pub(crate) struct ParsedRule { 15pub(crate) struct ParsedRule {
@@ -131,7 +130,7 @@ impl RuleBuilder {
131 let old_len = self.rules.len(); 130 let old_len = self.rules.len();
132 self.rules.retain(|rule| contains_path(&rule.pattern)); 131 self.rules.retain(|rule| contains_path(&rule.pattern));
133 if self.rules.len() < old_len { 132 if self.rules.len() < old_len {
134 mark::hit!(pattern_is_a_single_segment_path); 133 cov_mark::hit!(pattern_is_a_single_segment_path);
135 } 134 }
136 } 135 }
137 Ok(self.rules) 136 Ok(self.rules)
diff --git a/crates/ide_ssr/src/replacing.rs b/crates/ide_ssr/src/replacing.rs
index 06a94a46c..c9ccc1961 100644
--- a/crates/ide_ssr/src/replacing.rs
+++ b/crates/ide_ssr/src/replacing.rs
@@ -5,7 +5,7 @@ use itertools::Itertools;
5use rustc_hash::{FxHashMap, FxHashSet}; 5use rustc_hash::{FxHashMap, FxHashSet};
6use syntax::ast::{self, AstNode, AstToken}; 6use syntax::ast::{self, AstNode, AstToken};
7use syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize}; 7use syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize};
8use test_utils::mark; 8
9use text_edit::TextEdit; 9use text_edit::TextEdit;
10 10
11/// Returns a text edit that will replace each match in `matches` with its corresponding replacement 11/// Returns a text edit that will replace each match in `matches` with its corresponding replacement
@@ -128,7 +128,7 @@ impl ReplacementRenderer<'_> {
128 && (placeholder_value.autoderef_count > 0 128 && (placeholder_value.autoderef_count > 0
129 || placeholder_value.autoref_kind != ast::SelfParamKind::Owned) 129 || placeholder_value.autoref_kind != ast::SelfParamKind::Owned)
130 { 130 {
131 mark::hit!(replace_autoref_autoderef_capture); 131 cov_mark::hit!(replace_autoref_autoderef_capture);
132 let ref_kind = match placeholder_value.autoref_kind { 132 let ref_kind = match placeholder_value.autoref_kind {
133 ast::SelfParamKind::Owned => "", 133 ast::SelfParamKind::Owned => "",
134 ast::SelfParamKind::Ref => "&", 134 ast::SelfParamKind::Ref => "&",
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs
index 14e5a3b69..af94c7bb1 100644
--- a/crates/ide_ssr/src/resolving.rs
+++ b/crates/ide_ssr/src/resolving.rs
@@ -6,7 +6,6 @@ use ide_db::base_db::FilePosition;
6use parsing::Placeholder; 6use parsing::Placeholder;
7use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
8use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; 8use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken};
9use test_utils::mark;
10 9
11pub(crate) struct ResolutionScope<'db> { 10pub(crate) struct ResolutionScope<'db> {
12 scope: hir::SemanticsScope<'db>, 11 scope: hir::SemanticsScope<'db>,
@@ -170,13 +169,13 @@ impl Resolver<'_, '_> {
170 // calls. e.g. `Foo::bar($s)` should match `x.bar()`. 169 // calls. e.g. `Foo::bar($s)` should match `x.bar()`.
171 true 170 true
172 } else { 171 } else {
173 mark::hit!(replace_associated_trait_default_function_call); 172 cov_mark::hit!(replace_associated_trait_default_function_call);
174 false 173 false
175 } 174 }
176 } 175 }
177 hir::PathResolution::AssocItem(_) => { 176 hir::PathResolution::AssocItem(_) => {
178 // Not a function. Could be a constant or an associated type. 177 // Not a function. Could be a constant or an associated type.
179 mark::hit!(replace_associated_trait_constant); 178 cov_mark::hit!(replace_associated_trait_constant);
180 false 179 false
181 } 180 }
182 _ => true, 181 _ => true,
@@ -267,7 +266,7 @@ fn pick_node_for_resolution(node: SyntaxNode) -> SyntaxNode {
267 match node.kind() { 266 match node.kind() {
268 SyntaxKind::EXPR_STMT => { 267 SyntaxKind::EXPR_STMT => {
269 if let Some(n) = node.first_child() { 268 if let Some(n) = node.first_child() {
270 mark::hit!(cursor_after_semicolon); 269 cov_mark::hit!(cursor_after_semicolon);
271 return n; 270 return n;
272 } 271 }
273 } 272 }
@@ -291,7 +290,7 @@ fn path_contains_type_arguments(path: Option<ast::Path>) -> bool {
291 if let Some(path) = path { 290 if let Some(path) = path {
292 if let Some(segment) = path.segment() { 291 if let Some(segment) = path.segment() {
293 if segment.generic_arg_list().is_some() { 292 if segment.generic_arg_list().is_some() {
294 mark::hit!(type_arguments_within_path); 293 cov_mark::hit!(type_arguments_within_path);
295 return true; 294 return true;
296 } 295 }
297 } 296 }
diff --git a/crates/ide_ssr/src/search.rs b/crates/ide_ssr/src/search.rs
index 836eb94b2..28cef742c 100644
--- a/crates/ide_ssr/src/search.rs
+++ b/crates/ide_ssr/src/search.rs
@@ -12,7 +12,6 @@ use ide_db::{
12}; 12};
13use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
14use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; 14use syntax::{ast, AstNode, SyntaxKind, SyntaxNode};
15use test_utils::mark;
16 15
17/// A cache for the results of find_usages. This is for when we have multiple patterns that have the 16/// A cache for the results of find_usages. This is for when we have multiple patterns that have the
18/// same path. e.g. if the pattern was `foo::Bar` that can parse as a path, an expression, a type 17/// same path. e.g. if the pattern was `foo::Bar` that can parse as a path, an expression, a type
@@ -61,7 +60,7 @@ impl<'db> MatchFinder<'db> {
61 for file_range in self.find_usages(usage_cache, definition).file_ranges() { 60 for file_range in self.find_usages(usage_cache, definition).file_ranges() {
62 if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) { 61 if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) {
63 if !is_search_permitted_ancestors(&node_to_match) { 62 if !is_search_permitted_ancestors(&node_to_match) {
64 mark::hit!(use_declaration_with_braces); 63 cov_mark::hit!(use_declaration_with_braces);
65 continue; 64 continue;
66 } 65 }
67 self.try_add_match(rule, &node_to_match, &None, matches_out); 66 self.try_add_match(rule, &node_to_match, &None, matches_out);
@@ -205,7 +204,7 @@ impl<'db> MatchFinder<'db> {
205 matches_out: &mut Vec<Match>, 204 matches_out: &mut Vec<Match>,
206 ) { 205 ) {
207 if !self.within_range_restrictions(code) { 206 if !self.within_range_restrictions(code) {
208 mark::hit!(replace_nonpath_within_selection); 207 cov_mark::hit!(replace_nonpath_within_selection);
209 return; 208 return;
210 } 209 }
211 if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) { 210 if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) {
diff --git a/crates/ide_ssr/src/tests.rs b/crates/ide_ssr/src/tests.rs
index a3ea44f23..1d8565dc0 100644
--- a/crates/ide_ssr/src/tests.rs
+++ b/crates/ide_ssr/src/tests.rs
@@ -3,7 +3,7 @@ use expect_test::{expect, Expect};
3use ide_db::base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt}; 3use ide_db::base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use std::sync::Arc; 5use std::sync::Arc;
6use test_utils::{mark, RangeOrOffset}; 6use test_utils::RangeOrOffset;
7 7
8fn parse_error_text(query: &str) -> String { 8fn parse_error_text(query: &str) -> String {
9 format!("{}", query.parse::<SsrRule>().unwrap_err()) 9 format!("{}", query.parse::<SsrRule>().unwrap_err())
@@ -492,7 +492,7 @@ fn match_resolved_type_name() {
492 492
493#[test] 493#[test]
494fn type_arguments_within_path() { 494fn type_arguments_within_path() {
495 mark::check!(type_arguments_within_path); 495 cov_mark::check!(type_arguments_within_path);
496 let code = r#" 496 let code = r#"
497 mod foo { 497 mod foo {
498 pub struct Bar<T> {t: T} 498 pub struct Bar<T> {t: T}
@@ -508,7 +508,7 @@ fn type_arguments_within_path() {
508 508
509#[test] 509#[test]
510fn literal_constraint() { 510fn literal_constraint() {
511 mark::check!(literal_constraint); 511 cov_mark::check!(literal_constraint);
512 let code = r#" 512 let code = r#"
513 enum Option<T> { Some(T), None } 513 enum Option<T> { Some(T), None }
514 use Option::Some; 514 use Option::Some;
@@ -641,7 +641,7 @@ fn replace_associated_function_call() {
641 641
642#[test] 642#[test]
643fn replace_associated_trait_default_function_call() { 643fn replace_associated_trait_default_function_call() {
644 mark::check!(replace_associated_trait_default_function_call); 644 cov_mark::check!(replace_associated_trait_default_function_call);
645 assert_ssr_transform( 645 assert_ssr_transform(
646 "Bar2::foo() ==>> Bar2::foo2()", 646 "Bar2::foo() ==>> Bar2::foo2()",
647 r#" 647 r#"
@@ -673,7 +673,7 @@ fn replace_associated_trait_default_function_call() {
673 673
674#[test] 674#[test]
675fn replace_associated_trait_constant() { 675fn replace_associated_trait_constant() {
676 mark::check!(replace_associated_trait_constant); 676 cov_mark::check!(replace_associated_trait_constant);
677 assert_ssr_transform( 677 assert_ssr_transform(
678 "Bar2::VALUE ==>> Bar2::VALUE_2222", 678 "Bar2::VALUE ==>> Bar2::VALUE_2222",
679 r#" 679 r#"
@@ -998,7 +998,7 @@ fn use_declaration_with_braces() {
998 // It would be OK for a path rule to match and alter a use declaration. We shouldn't mess it up 998 // It would be OK for a path rule to match and alter a use declaration. We shouldn't mess it up
999 // though. In particular, we must not change `use foo::{baz, bar}` to `use foo::{baz, 999 // though. In particular, we must not change `use foo::{baz, bar}` to `use foo::{baz,
1000 // foo2::bar2}`. 1000 // foo2::bar2}`.
1001 mark::check!(use_declaration_with_braces); 1001 cov_mark::check!(use_declaration_with_braces);
1002 assert_ssr_transform( 1002 assert_ssr_transform(
1003 "foo::bar ==>> foo2::bar2", 1003 "foo::bar ==>> foo2::bar2",
1004 r#" 1004 r#"
@@ -1076,7 +1076,7 @@ fn ufcs_matches_method_call() {
1076 1076
1077#[test] 1077#[test]
1078fn pattern_is_a_single_segment_path() { 1078fn pattern_is_a_single_segment_path() {
1079 mark::check!(pattern_is_a_single_segment_path); 1079 cov_mark::check!(pattern_is_a_single_segment_path);
1080 // The first function should not be altered because the `foo` in scope at the cursor position is 1080 // The first function should not be altered because the `foo` in scope at the cursor position is
1081 // a different `foo`. This case is special because "foo" can be parsed as a pattern (IDENT_PAT -> 1081 // a different `foo`. This case is special because "foo" can be parsed as a pattern (IDENT_PAT ->
1082 // NAME -> IDENT), which contains no path. If we're not careful we'll end up matching the `foo` 1082 // NAME -> IDENT), which contains no path. If we're not careful we'll end up matching the `foo`
@@ -1118,7 +1118,7 @@ fn replace_local_variable_reference() {
1118 // The pattern references a local variable `foo` in the block containing the cursor. We should 1118 // The pattern references a local variable `foo` in the block containing the cursor. We should
1119 // only replace references to this variable `foo`, not other variables that just happen to have 1119 // only replace references to this variable `foo`, not other variables that just happen to have
1120 // the same name. 1120 // the same name.
1121 mark::check!(cursor_after_semicolon); 1121 cov_mark::check!(cursor_after_semicolon);
1122 assert_ssr_transform( 1122 assert_ssr_transform(
1123 "foo + $a ==>> $a - foo", 1123 "foo + $a ==>> $a - foo",
1124 r#" 1124 r#"
@@ -1179,7 +1179,7 @@ fn replace_path_within_selection() {
1179 1179
1180#[test] 1180#[test]
1181fn replace_nonpath_within_selection() { 1181fn replace_nonpath_within_selection() {
1182 mark::check!(replace_nonpath_within_selection); 1182 cov_mark::check!(replace_nonpath_within_selection);
1183 assert_ssr_transform( 1183 assert_ssr_transform(
1184 "$a + $b ==>> $b * $a", 1184 "$a + $b ==>> $b * $a",
1185 r#" 1185 r#"
@@ -1269,7 +1269,7 @@ fn replace_autoref_autoderef_capture() {
1269 // second, we already have a reference, so it isn't. When $a is used in a context where autoref 1269 // second, we already have a reference, so it isn't. When $a is used in a context where autoref
1270 // doesn't apply, we need to prefix it with `&`. Finally, we have some cases where autoderef 1270 // doesn't apply, we need to prefix it with `&`. Finally, we have some cases where autoderef
1271 // needs to be applied. 1271 // needs to be applied.
1272 mark::check!(replace_autoref_autoderef_capture); 1272 cov_mark::check!(replace_autoref_autoderef_capture);
1273 let code = r#" 1273 let code = r#"
1274 struct Foo {} 1274 struct Foo {}
1275 impl Foo { 1275 impl Foo {
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index bb2656a80..139214207 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
14smallvec = "1.2.0" 15smallvec = "1.2.0"
15log = "0.4.8" 16log = "0.4.8"
@@ -17,8 +18,8 @@ log = "0.4.8"
17syntax = { path = "../syntax", version = "0.0.0" } 18syntax = { path = "../syntax", version = "0.0.0" }
18parser = { path = "../parser", version = "0.0.0" } 19parser = { path = "../parser", version = "0.0.0" }
19tt = { path = "../tt", version = "0.0.0" } 20tt = { path = "../tt", version = "0.0.0" }
20test_utils = { path = "../test_utils", version = "0.0.0" }
21stdx = { path = "../stdx", version = "0.0.0" } 21stdx = { path = "../stdx", version = "0.0.0" }
22 22
23[dev-dependencies] 23[dev-dependencies]
24profile = { path = "../profile" } 24profile = { path = "../profile" }
25test_utils = { path = "../test_utils" }
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index f3d2da55a..33b85e23d 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -17,7 +17,6 @@ mod benchmark;
17 17
18use std::fmt; 18use std::fmt;
19 19
20use test_utils::mark;
21pub use tt::{Delimiter, DelimiterKind, Punct}; 20pub use tt::{Delimiter, DelimiterKind, Punct};
22 21
23use crate::{ 22use crate::{
@@ -217,7 +216,7 @@ impl MacroDef {
217 let mut rules = Vec::new(); 216 let mut rules = Vec::new();
218 217
219 if Some(tt::DelimiterKind::Brace) == tt.delimiter_kind() { 218 if Some(tt::DelimiterKind::Brace) == tt.delimiter_kind() {
220 mark::hit!(parse_macro_def_rules); 219 cov_mark::hit!(parse_macro_def_rules);
221 while src.len() > 0 { 220 while src.len() > 0 {
222 let rule = Rule::parse(&mut src, true)?; 221 let rule = Rule::parse(&mut src, true)?;
223 rules.push(rule); 222 rules.push(rule);
@@ -229,7 +228,7 @@ impl MacroDef {
229 } 228 }
230 } 229 }
231 } else { 230 } else {
232 mark::hit!(parse_macro_def_simple); 231 cov_mark::hit!(parse_macro_def_simple);
233 let rule = Rule::parse(&mut src, false)?; 232 let rule = Rule::parse(&mut src, false)?;
234 if src.len() != 0 { 233 if src.len() != 0 {
235 return Err(ParseError::Expected("remain tokens in macro def".to_string())); 234 return Err(ParseError::Expected("remain tokens in macro def".to_string()));
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 08acd4ac2..3a168bb4b 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -6,7 +6,7 @@ use syntax::{
6 SyntaxKind::{ERROR, IDENT}, 6 SyntaxKind::{ERROR, IDENT},
7 SyntaxNode, WalkEvent, T, 7 SyntaxNode, WalkEvent, T,
8}; 8};
9use test_utils::{assert_eq_text, mark}; 9use test_utils::assert_eq_text;
10 10
11use super::*; 11use super::*;
12 12
@@ -687,7 +687,7 @@ fn test_match_literal() {
687 687
688#[test] 688#[test]
689fn test_parse_macro_def_simple() { 689fn test_parse_macro_def_simple() {
690 mark::check!(parse_macro_def_simple); 690 cov_mark::check!(parse_macro_def_simple);
691 691
692 parse_macro2( 692 parse_macro2(
693 r#" 693 r#"
@@ -701,7 +701,7 @@ macro foo($id:ident) {
701 701
702#[test] 702#[test]
703fn test_parse_macro_def_rules() { 703fn test_parse_macro_def_rules() {
704 mark::check!(parse_macro_def_rules); 704 cov_mark::check!(parse_macro_def_rules);
705 705
706 parse_macro2( 706 parse_macro2(
707 r#" 707 r#"
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 6159d064c..6c0e22722 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -18,7 +18,7 @@
18//! // fn foo() {} 18//! // fn foo() {}
19//! ``` 19//! ```
20//! 20//!
21//! After adding a new inline-test, run `cargo xtask codegen` to 21//! After adding a new inline-test, run `cargo test -p xtask` to
22//! extract it as a standalone text-fixture into 22//! extract it as a standalone text-fixture into
23//! `crates/syntax/test_data/parser/`, and run `cargo test` once to 23//! `crates/syntax/test_data/parser/`, and run `cargo test` once to
24//! create the "gold" value. 24//! create the "gold" value.
diff --git a/crates/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml
index f09726223..16fd56c7e 100644
--- a/crates/proc_macro_api/Cargo.toml
+++ b/crates/proc_macro_api/Cargo.toml
@@ -19,3 +19,6 @@ jod-thread = "0.1.1"
19tt = { path = "../tt", version = "0.0.0" } 19tt = { path = "../tt", version = "0.0.0" }
20base_db = { path = "../base_db", version = "0.0.0" } 20base_db = { path = "../base_db", version = "0.0.0" }
21stdx = { path = "../stdx", version = "0.0.0" } 21stdx = { path = "../stdx", version = "0.0.0" }
22snap = "1"
23object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] }
24memmap = "0.7.0"
diff --git a/crates/proc_macro_api/src/lib.rs b/crates/proc_macro_api/src/lib.rs
index 2ea456fb0..941d0fe9e 100644
--- a/crates/proc_macro_api/src/lib.rs
+++ b/crates/proc_macro_api/src/lib.rs
@@ -5,10 +5,12 @@
5//! is used to provide basic infrastructure for communication between two 5//! is used to provide basic infrastructure for communication between two
6//! processes: Client (RA itself), Server (the external program) 6//! processes: Client (RA itself), Server (the external program)
7 7
8mod rpc;
9mod process;
10pub mod msg; 8pub mod msg;
9mod process;
10mod rpc;
11mod version;
11 12
13use base_db::{Env, ProcMacro};
12use std::{ 14use std::{
13 ffi::OsStr, 15 ffi::OsStr,
14 io, 16 io,
@@ -16,7 +18,6 @@ use std::{
16 sync::Arc, 18 sync::Arc,
17}; 19};
18 20
19use base_db::{Env, ProcMacro};
20use tt::{SmolStr, Subtree}; 21use tt::{SmolStr, Subtree};
21 22
22use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; 23use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};
@@ -75,6 +76,21 @@ impl ProcMacroClient {
75 } 76 }
76 77
77 pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> { 78 pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> {
79 match version::read_info(dylib_path) {
80 Ok(info) => {
81 if info.version.0 < 1 || info.version.1 < 47 {
82 eprintln!("proc-macro {} built by {:#?} is not supported by Rust Analyzer, please update your rust version.", dylib_path.to_string_lossy(), info);
83 }
84 }
85 Err(err) => {
86 eprintln!(
87 "proc-macro {} failed to find the given version. Reason: {}",
88 dylib_path.to_string_lossy(),
89 err
90 );
91 }
92 }
93
78 let macros = match self.process.find_proc_macros(dylib_path) { 94 let macros = match self.process.find_proc_macros(dylib_path) {
79 Err(err) => { 95 Err(err) => {
80 eprintln!("Failed to find proc macros. Error: {:#?}", err); 96 eprintln!("Failed to find proc macros. Error: {:#?}", err);
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs
new file mode 100644
index 000000000..11a7fb59a
--- /dev/null
+++ b/crates/proc_macro_api/src/version.rs
@@ -0,0 +1,132 @@
1//! Reading proc-macro rustc version information from binary data
2
3use std::{
4 fs::File,
5 io::{self, Read},
6 path::Path,
7};
8
9use memmap::Mmap;
10use object::read::{File as BinaryFile, Object, ObjectSection};
11use snap::read::FrameDecoder as SnapDecoder;
12
13#[derive(Debug)]
14pub(crate) struct RustCInfo {
15 pub(crate) version: (usize, usize, usize),
16 pub(crate) channel: String,
17 pub(crate) commit: String,
18 pub(crate) date: String,
19}
20
21pub(crate) fn read_info(dylib_path: &Path) -> io::Result<RustCInfo> {
22 macro_rules! err {
23 ($e:literal) => {
24 io::Error::new(io::ErrorKind::InvalidData, $e)
25 };
26 }
27
28 let ver_str = read_version(dylib_path)?;
29 let mut items = ver_str.split_whitespace();
30 let tag = items.next().ok_or(err!("version format error"))?;
31 if tag != "rustc" {
32 return Err(err!("version format error (No rustc tag)"));
33 }
34
35 let version_part = items.next().ok_or(err!("no version string"))?;
36 let mut version_parts = version_part.split("-");
37 let version = version_parts.next().ok_or(err!("no version"))?;
38 let channel = version_parts.next().unwrap_or_default().to_string();
39
40 let commit = items.next().ok_or(err!("no commit info"))?;
41 // remove (
42 if commit.len() == 0 {
43 return Err(err!("commit format error"));
44 }
45 let commit = commit[1..].to_string();
46 let date = items.next().ok_or(err!("no date info"))?;
47 // remove )
48 if date.len() == 0 {
49 return Err(err!("date format error"));
50 }
51 let date = date[0..date.len() - 2].to_string();
52
53 let version_numbers = version
54 .split(".")
55 .map(|it| it.parse::<usize>())
56 .collect::<Result<Vec<_>, _>>()
57 .map_err(|_| err!("version number error"))?;
58
59 if version_numbers.len() != 3 {
60 return Err(err!("version number format error"));
61 }
62 let version = (version_numbers[0], version_numbers[1], version_numbers[2]);
63
64 Ok(RustCInfo { version, channel, commit, date })
65}
66
67/// This is used inside read_version() to locate the ".rustc" section
68/// from a proc macro crate's binary file.
69fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'a [u8]> {
70 BinaryFile::parse(dylib_binary)
71 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
72 .section_by_name(section_name)
73 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "section read error"))?
74 .data()
75 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
76}
77
78/// Check the version of rustc that was used to compile a proc macro crate's
79///
80/// binary file.
81/// A proc macro crate binary's ".rustc" section has following byte layout:
82/// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes
83/// * ff060000 734e6150 is followed, it's the snappy format magic bytes,
84/// means bytes from here(including this sequence) are compressed in
85/// snappy compression format. Version info is inside here, so decompress
86/// this.
87/// The bytes you get after decompressing the snappy format portion has
88/// following layout:
89/// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes(again)
90/// * [crate root bytes] next 4 bytes is to store crate root position,
91/// according to rustc's source code comment
92/// * [length byte] next 1 byte tells us how many bytes we should read next
93/// for the version string's utf8 bytes
94/// * [version string bytes encoded in utf8] <- GET THIS BOI
95/// * [some more bytes that we don really care but still there] :-)
96/// Check this issue for more about the bytes layout:
97/// https://github.com/rust-analyzer/rust-analyzer/issues/6174
98fn read_version(dylib_path: &Path) -> io::Result<String> {
99 let dylib_file = File::open(dylib_path)?;
100 let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?;
101
102 let dot_rustc = read_section(&dylib_mmaped, ".rustc")?;
103
104 let header = &dot_rustc[..8];
105 const EXPECTED_HEADER: [u8; 8] = [b'r', b'u', b's', b't', 0, 0, 0, 5];
106 // check if header is valid
107 if header != EXPECTED_HEADER {
108 return Err(io::Error::new(
109 io::ErrorKind::InvalidData,
110 format!("only metadata version 5 is supported, section header was: {:?}", header),
111 ));
112 }
113
114 let snappy_portion = &dot_rustc[8..];
115
116 let mut snappy_decoder = SnapDecoder::new(snappy_portion);
117
118 // the bytes before version string bytes, so this basically is:
119 // 8 bytes for [b'r',b'u',b's',b't',0,0,0,5]
120 // 4 bytes for [crate root bytes]
121 // 1 byte for length of version string
122 // so 13 bytes in total, and we should check the 13th byte
123 // to know the length
124 let mut bytes_before_version = [0u8; 13];
125 snappy_decoder.read_exact(&mut bytes_before_version)?;
126 let length = bytes_before_version[12]; // what? can't use -1 indexing?
127
128 let mut version_string_utf8 = vec![0u8; length as usize];
129 snappy_decoder.read_exact(&mut version_string_utf8)?;
130 let version_string = String::from_utf8(version_string_utf8);
131 version_string.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
132}
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml
index 4c1b3036a..63b3f1532 100644
--- a/crates/proc_macro_srv/Cargo.toml
+++ b/crates/proc_macro_srv/Cargo.toml
@@ -17,9 +17,9 @@ memmap2 = "0.2.0"
17tt = { path = "../tt", version = "0.0.0" } 17tt = { path = "../tt", version = "0.0.0" }
18mbe = { path = "../mbe", version = "0.0.0" } 18mbe = { path = "../mbe", version = "0.0.0" }
19proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } 19proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" }
20test_utils = { path = "../test_utils", version = "0.0.0" }
21 20
22[dev-dependencies] 21[dev-dependencies]
22test_utils = { path = "../test_utils" }
23cargo_metadata = "0.13" 23cargo_metadata = "0.13"
24 24
25# used as proc macro test targets 25# used as proc macro test targets
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
index e4006a5ab..e67902682 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
@@ -268,7 +268,7 @@ trait Mark {
268 fn mark(unmarked: Self::Unmarked) -> Self; 268 fn mark(unmarked: Self::Unmarked) -> Self;
269} 269}
270 270
271/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). 271/// Unwrap types wrapped by `cov_mark::mark` (see `Mark` for details).
272trait Unmark { 272trait Unmark {
273 type Unmarked; 273 type Unmarked;
274 fn unmark(self) -> Self::Unmarked; 274 fn unmark(self) -> Self::Unmarked;
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index f7241b711..bc6e20341 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -9,6 +9,8 @@ use cargo_metadata::{CargoOpt, MetadataCommand};
9use la_arena::{Arena, Idx}; 9use la_arena::{Arena, Idx};
10use paths::{AbsPath, AbsPathBuf}; 10use paths::{AbsPath, AbsPathBuf};
11use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
12use serde::Deserialize;
13use serde_json::from_value;
12 14
13use crate::build_data::BuildDataConfig; 15use crate::build_data::BuildDataConfig;
14use crate::utf8_stdout; 16use crate::utf8_stdout;
@@ -104,6 +106,13 @@ pub struct PackageData {
104 pub active_features: Vec<String>, 106 pub active_features: Vec<String>,
105 // String representation of package id 107 // String representation of package id
106 pub id: String, 108 pub id: String,
109 // The contents of [package.metadata.rust-analyzer]
110 pub metadata: RustAnalyzerPackageMetaData,
111}
112
113#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
114pub struct RustAnalyzerPackageMetaData {
115 pub rustc_private: bool,
107} 116}
108 117
109#[derive(Debug, Clone, Eq, PartialEq)] 118#[derive(Debug, Clone, Eq, PartialEq)]
@@ -161,6 +170,13 @@ impl PackageData {
161 } 170 }
162} 171}
163 172
173#[derive(Deserialize, Default)]
174// Deserialise helper for the cargo metadata
175struct PackageMetadata {
176 #[serde(rename = "rust-analyzer")]
177 rust_analyzer: Option<RustAnalyzerPackageMetaData>,
178}
179
164impl CargoWorkspace { 180impl CargoWorkspace {
165 pub fn from_cargo_metadata( 181 pub fn from_cargo_metadata(
166 cargo_toml: &AbsPath, 182 cargo_toml: &AbsPath,
@@ -244,8 +260,10 @@ impl CargoWorkspace {
244 260
245 meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); 261 meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
246 for meta_pkg in &meta.packages { 262 for meta_pkg in &meta.packages {
247 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = 263 let cargo_metadata::Package {
248 meta_pkg; 264 id, edition, name, manifest_path, version, metadata, ..
265 } = meta_pkg;
266 let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default();
249 let is_member = ws_members.contains(&id); 267 let is_member = ws_members.contains(&id);
250 let edition = edition 268 let edition = edition
251 .parse::<Edition>() 269 .parse::<Edition>()
@@ -262,6 +280,7 @@ impl CargoWorkspace {
262 dependencies: Vec::new(), 280 dependencies: Vec::new(),
263 features: meta_pkg.features.clone().into_iter().collect(), 281 features: meta_pkg.features.clone().into_iter().collect(),
264 active_features: Vec::new(), 282 active_features: Vec::new(),
283 metadata: meta.rust_analyzer.unwrap_or_default(),
265 }); 284 });
266 let pkg_data = &mut packages[pkg]; 285 let pkg_data = &mut packages[pkg];
267 pkg_by_id.insert(id, pkg); 286 pkg_by_id.insert(id, pkg);
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 0220efdb4..1b53fcc30 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -2,11 +2,7 @@
2//! metadata` or `rust-project.json`) into representation stored in the salsa 2//! metadata` or `rust-project.json`) into representation stored in the salsa
3//! database -- `CrateGraph`. 3//! database -- `CrateGraph`.
4 4
5use std::{ 5use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
6 fmt, fs,
7 path::{Component, Path},
8 process::Command,
9};
10 6
11use anyhow::{Context, Result}; 7use anyhow::{Context, Result};
12use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; 8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
@@ -60,6 +56,7 @@ impl fmt::Debug for ProjectWorkspace {
60 match self { 56 match self {
61 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f 57 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f
62 .debug_struct("Cargo") 58 .debug_struct("Cargo")
59 .field("root", &cargo.workspace_root().file_name())
63 .field("n_packages", &cargo.packages().len()) 60 .field("n_packages", &cargo.packages().len())
64 .field("n_sysroot_crates", &sysroot.crates().len()) 61 .field("n_sysroot_crates", &sysroot.crates().len())
65 .field( 62 .field(
@@ -279,11 +276,8 @@ impl ProjectWorkspace {
279 276
280 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { 277 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
281 match self { 278 match self {
282 ProjectWorkspace::Cargo { cargo, rustc, .. } => { 279 ProjectWorkspace::Cargo { cargo, .. } => {
283 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); 280 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone());
284 if let Some(rustc) = rustc {
285 collector.add_config(rustc.workspace_root(), rustc.build_data_config().clone());
286 }
287 } 281 }
288 _ => {} 282 _ => {}
289 } 283 }
@@ -380,9 +374,11 @@ fn cargo_to_crate_graph(
380 cfg_options.insert_atom("debug_assertions".into()); 374 cfg_options.insert_atom("debug_assertions".into());
381 375
382 let mut pkg_crates = FxHashMap::default(); 376 let mut pkg_crates = FxHashMap::default();
383 377 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
378 let mut has_private = false;
384 // Next, create crates for each package, target pair 379 // Next, create crates for each package, target pair
385 for pkg in cargo.packages() { 380 for pkg in cargo.packages() {
381 has_private |= cargo[pkg].metadata.rustc_private;
386 let mut lib_tgt = None; 382 let mut lib_tgt = None;
387 for &tgt in cargo[pkg].targets.iter() { 383 for &tgt in cargo[pkg].targets.iter() {
388 if let Some(file_id) = load(&cargo[tgt].root) { 384 if let Some(file_id) = load(&cargo[tgt].root) {
@@ -443,28 +439,66 @@ fn cargo_to_crate_graph(
443 } 439 }
444 } 440 }
445 441
446 let mut rustc_pkg_crates = FxHashMap::default(); 442 if has_private {
443 // If the user provided a path to rustc sources, we add all the rustc_private crates
444 // and create dependencies on them for the crates which opt-in to that
445 if let Some(rustc_workspace) = rustc {
446 handle_rustc_crates(
447 rustc_workspace,
448 load,
449 &mut crate_graph,
450 rustc_build_data_map,
451 &cfg_options,
452 proc_macro_loader,
453 &mut pkg_to_lib_crate,
454 &public_deps,
455 cargo,
456 &pkg_crates,
457 );
458 }
459 }
460 crate_graph
461}
447 462
448 // If the user provided a path to rustc sources, we add all the rustc_private crates 463fn handle_rustc_crates(
449 // and create dependencies on them for the crates in the current workspace 464 rustc_workspace: &CargoWorkspace,
450 if let Some(rustc_workspace) = rustc { 465 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
451 for pkg in rustc_workspace.packages() { 466 crate_graph: &mut CrateGraph,
467 rustc_build_data_map: Option<&FxHashMap<String, BuildData>>,
468 cfg_options: &CfgOptions,
469 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
470 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
471 public_deps: &[(CrateName, CrateId)],
472 cargo: &CargoWorkspace,
473 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<CrateId>>,
474) {
475 let mut rustc_pkg_crates = FxHashMap::default();
476 // The root package of the rustc-dev component is rustc_driver, so we match that
477 let root_pkg =
478 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
479 // The rustc workspace might be incomplete (such as if rustc-dev is not
480 // installed for the current toolchain) and `rustcSource` is set to discover.
481 if let Some(root_pkg) = root_pkg {
482 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
483 let mut queue = VecDeque::new();
484 queue.push_back(root_pkg);
485 while let Some(pkg) = queue.pop_front() {
486 // Don't duplicate packages if they are dependended on a diamond pattern
487 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
488 // which is not ideal
489 if rustc_pkg_crates.contains_key(&pkg) {
490 continue;
491 }
492 for dep in &rustc_workspace[pkg].dependencies {
493 queue.push_back(dep.pkg);
494 }
452 for &tgt in rustc_workspace[pkg].targets.iter() { 495 for &tgt in rustc_workspace[pkg].targets.iter() {
453 if rustc_workspace[tgt].kind != TargetKind::Lib { 496 if rustc_workspace[tgt].kind != TargetKind::Lib {
454 continue; 497 continue;
455 } 498 }
456 // Exclude alloc / core / std
457 if rustc_workspace[tgt]
458 .root
459 .components()
460 .any(|c| c == Component::Normal("library".as_ref()))
461 {
462 continue;
463 }
464
465 if let Some(file_id) = load(&rustc_workspace[tgt].root) { 499 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
466 let crate_id = add_target_crate_root( 500 let crate_id = add_target_crate_root(
467 &mut crate_graph, 501 crate_graph,
468 &rustc_workspace[pkg], 502 &rustc_workspace[pkg],
469 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), 503 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
470 &cfg_options, 504 &cfg_options,
@@ -472,44 +506,50 @@ fn cargo_to_crate_graph(
472 file_id, 506 file_id,
473 ); 507 );
474 pkg_to_lib_crate.insert(pkg, crate_id); 508 pkg_to_lib_crate.insert(pkg, crate_id);
475 // Add dependencies on the core / std / alloc for rustc 509 // Add dependencies on core / std / alloc for this crate
476 for (name, krate) in public_deps.iter() { 510 for (name, krate) in public_deps.iter() {
477 add_dep(&mut crate_graph, crate_id, name.clone(), *krate); 511 add_dep(crate_graph, crate_id, name.clone(), *krate);
478 } 512 }
479 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); 513 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
480 } 514 }
481 } 515 }
482 } 516 }
483 // Now add a dep edge from all targets of upstream to the lib 517 }
484 // target of downstream. 518 // Now add a dep edge from all targets of upstream to the lib
485 for pkg in rustc_workspace.packages() { 519 // target of downstream.
486 for dep in rustc_workspace[pkg].dependencies.iter() { 520 for pkg in rustc_pkg_crates.keys().copied() {
487 let name = CrateName::new(&dep.name).unwrap(); 521 for dep in rustc_workspace[pkg].dependencies.iter() {
488 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { 522 let name = CrateName::new(&dep.name).unwrap();
489 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { 523 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
490 add_dep(&mut crate_graph, from, name.clone(), to); 524 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
491 } 525 add_dep(crate_graph, from, name.clone(), to);
492 } 526 }
493 } 527 }
494 } 528 }
495 529 }
496 // Add dependencies for all the crates of the current workspace to rustc_private libraries 530 // Add a dependency on the rustc_private crates for all targets of each package
497 for dep in rustc_workspace.packages() { 531 // which opts in
498 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); 532 for dep in rustc_workspace.packages() {
499 533 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
500 if let Some(&to) = pkg_to_lib_crate.get(&dep) { 534
501 for pkg in cargo.packages() { 535 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
502 if !cargo[pkg].is_member { 536 for pkg in cargo.packages() {
503 continue; 537 let package = &cargo[pkg];
504 } 538 if !package.metadata.rustc_private {
505 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 539 continue;
506 add_dep(&mut crate_graph, from, name.clone(), to); 540 }
541 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
542 // Avoid creating duplicate dependencies
543 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
544 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
545 // instead of the one from `crates.io`
546 if !crate_graph[from].dependencies.iter().any(|d| d.name == name) {
547 add_dep(crate_graph, from, name.clone(), to);
507 } 548 }
508 } 549 }
509 } 550 }
510 } 551 }
511 } 552 }
512 crate_graph
513} 553}
514 554
515fn add_target_crate_root( 555fn add_target_crate_root(
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 8789f0852..3130785cc 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -24,7 +24,7 @@ jod-thread = "0.1.0"
24log = "0.4.8" 24log = "0.4.8"
25lsp-types = { version = "0.88.0", features = ["proposed"] } 25lsp-types = { version = "0.88.0", features = ["proposed"] }
26parking_lot = "0.11.0" 26parking_lot = "0.11.0"
27xflags = "0.1.2" 27xflags = "0.2.1"
28oorandom = "11.1.2" 28oorandom = "11.1.2"
29rustc-hash = "1.1.0" 29rustc-hash = "1.1.0"
30serde = { version = "1.0.106", features = ["derive"] } 30serde = { version = "1.0.106", features = ["derive"] }
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs
index 244912d26..3a7caaf3f 100644
--- a/crates/rust-analyzer/src/bin/flags.rs
+++ b/crates/rust-analyzer/src/bin/flags.rs
@@ -6,7 +6,9 @@ use ide_ssr::{SsrPattern, SsrRule};
6use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; 6use rust_analyzer::cli::{BenchWhat, Position, Verbosity};
7use vfs::AbsPathBuf; 7use vfs::AbsPathBuf;
8 8
9xflags::args_parser! { 9xflags::xflags! {
10 src "./src/bin/flags.rs"
11
10 /// LSP server for the Rust programming language. 12 /// LSP server for the Rust programming language.
11 cmd rust-analyzer { 13 cmd rust-analyzer {
12 /// Verbosity level, can be repeated multiple times. 14 /// Verbosity level, can be repeated multiple times.
@@ -120,7 +122,7 @@ xflags::args_parser! {
120 122
121// generated start 123// generated start
122// The following code is generated by `xflags` macro. 124// The following code is generated by `xflags` macro.
123// Run `env XFLAGS_DUMP= cargo build` to regenerate. 125// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
124#[derive(Debug)] 126#[derive(Debug)]
125pub struct RustAnalyzer { 127pub struct RustAnalyzer {
126 pub verbose: u32, 128 pub verbose: u32,
@@ -158,7 +160,7 @@ pub struct Parse {
158} 160}
159 161
160#[derive(Debug)] 162#[derive(Debug)]
161pub struct Symbols {} 163pub struct Symbols;
162 164
163#[derive(Debug)] 165#[derive(Debug)]
164pub struct Highlight { 166pub struct Highlight {
@@ -211,14 +213,13 @@ pub struct Search {
211} 213}
212 214
213#[derive(Debug)] 215#[derive(Debug)]
214pub struct ProcMacro {} 216pub struct ProcMacro;
215 217
216impl RustAnalyzer { 218impl RustAnalyzer {
217 pub const HELP: &'static str = Self::_HELP; 219 pub const HELP: &'static str = Self::HELP_;
218 220
219 pub fn from_env() -> xflags::Result<Self> { 221 pub fn from_env() -> xflags::Result<Self> {
220 let mut p = xflags::rt::Parser::new_from_env(); 222 Self::from_env_()
221 Self::_parse(&mut p)
222 } 223 }
223} 224}
224// generated end 225// generated end
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 3bd7e678d..49994824f 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -108,7 +108,11 @@ impl BenchCmd {
108 add_call_parenthesis: true, 108 add_call_parenthesis: true,
109 add_call_argument_snippets: true, 109 add_call_argument_snippets: true,
110 snippet_cap: SnippetCap::new(true), 110 snippet_cap: SnippetCap::new(true),
111 insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain }, 111 insert_use: InsertUseConfig {
112 merge: None,
113 prefix_kind: PrefixKind::Plain,
114 group: true,
115 },
112 }; 116 };
113 let res = do_work(&mut host, file_id, |analysis| { 117 let res = do_work(&mut host, file_id, |analysis| {
114 analysis.completions(&options, file_position) 118 analysis.completions(&options, file_position)
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 367136702..8af7871ac 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -16,7 +16,6 @@ use ide_db::helpers::{
16 insert_use::{InsertUseConfig, MergeBehavior}, 16 insert_use::{InsertUseConfig, MergeBehavior},
17 SnippetCap, 17 SnippetCap,
18}; 18};
19use itertools::Itertools;
20use lsp_types::{ClientCapabilities, MarkupKind}; 19use lsp_types::{ClientCapabilities, MarkupKind};
21use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; 20use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
22use rustc_hash::FxHashSet; 21use rustc_hash::FxHashSet;
@@ -35,7 +34,8 @@ config_data! {
35 assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", 34 assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"",
36 /// The path structure for newly inserted paths to use. 35 /// The path structure for newly inserted paths to use.
37 assist_importPrefix: ImportPrefixDef = "\"plain\"", 36 assist_importPrefix: ImportPrefixDef = "\"plain\"",
38 37 /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
38 assist_importGroup: bool = "true",
39 /// Show function name and docs in parameter hints. 39 /// Show function name and docs in parameter hints.
40 callInfo_full: bool = "true", 40 callInfo_full: bool = "true",
41 41
@@ -48,7 +48,7 @@ config_data! {
48 cargo_features: Vec<String> = "[]", 48 cargo_features: Vec<String> = "[]",
49 /// Run build scripts (`build.rs`) for more precise code analysis. 49 /// Run build scripts (`build.rs`) for more precise code analysis.
50 cargo_runBuildScripts | 50 cargo_runBuildScripts |
51 cargo_loadOutDirsFromCheck: bool = "false", 51 cargo_loadOutDirsFromCheck: bool = "true",
52 /// Do not activate the `default` feature. 52 /// Do not activate the `default` feature.
53 cargo_noDefaultFeatures: bool = "false", 53 cargo_noDefaultFeatures: bool = "false",
54 /// Compilation target (target triple). 54 /// Compilation target (target triple).
@@ -97,13 +97,15 @@ config_data! {
97 diagnostics_enableExperimental: bool = "true", 97 diagnostics_enableExperimental: bool = "true",
98 /// List of rust-analyzer diagnostics to disable. 98 /// List of rust-analyzer diagnostics to disable.
99 diagnostics_disabled: FxHashSet<String> = "[]", 99 diagnostics_disabled: FxHashSet<String> = "[]",
100 /// List of warnings that should be displayed with info severity.\n\nThe 100 /// List of warnings that should be displayed with info severity.
101 /// warnings will be indicated by a blue squiggly underline in code and 101 ///
102 /// a blue icon in the `Problems Panel`. 102 /// The warnings will be indicated by a blue squiggly underline in code
103 /// and a blue icon in the `Problems Panel`.
103 diagnostics_warningsAsHint: Vec<String> = "[]", 104 diagnostics_warningsAsHint: Vec<String> = "[]",
104 /// List of warnings that should be displayed with hint severity.\n\nThe 105 /// List of warnings that should be displayed with hint severity.
105 /// warnings will be indicated by faded text or three dots in code and 106 ///
106 /// will not show up in the `Problems Panel`. 107 /// The warnings will be indicated by faded text or three dots in code
108 /// and will not show up in the `Problems Panel`.
107 diagnostics_warningsAsInfo: Vec<String> = "[]", 109 diagnostics_warningsAsInfo: Vec<String> = "[]",
108 110
109 /// Controls file watching implementation. 111 /// Controls file watching implementation.
@@ -157,7 +159,9 @@ config_data! {
157 lens_references: bool = "false", 159 lens_references: bool = "false",
158 160
159 /// Disable project auto-discovery in favor of explicitly specified set 161 /// Disable project auto-discovery in favor of explicitly specified set
160 /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, 162 /// of projects.
163 ///
164 /// Elements must be paths pointing to `Cargo.toml`,
161 /// `rust-project.json`, or JSON objects in `rust-project.json` format. 165 /// `rust-project.json`, or JSON objects in `rust-project.json` format.
162 linkedProjects: Vec<ManifestOrProjectJson> = "[]", 166 linkedProjects: Vec<ManifestOrProjectJson> = "[]",
163 167
@@ -176,12 +180,17 @@ config_data! {
176 /// Command to be executed instead of 'cargo' for runnables. 180 /// Command to be executed instead of 'cargo' for runnables.
177 runnables_overrideCargo: Option<String> = "null", 181 runnables_overrideCargo: Option<String> = "null",
178 /// Additional arguments to be passed to cargo for runnables such as 182 /// Additional arguments to be passed to cargo for runnables such as
179 /// tests or binaries.\nFor example, it may be `--release`. 183 /// tests or binaries. For example, it may be `--release`.
180 runnables_cargoExtraArgs: Vec<String> = "[]", 184 runnables_cargoExtraArgs: Vec<String> = "[]",
181 185
182 /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover" 186 /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
183 /// to try to automatically find it. 187 /// projects, or "discover" to try to automatically find it.
184 rustcSource : Option<String> = "null", 188 ///
189 /// Any project which uses rust-analyzer with the rustcPrivate
190 /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
191 ///
192 /// This option is not reloaded automatically; you must restart rust-analyzer for it to take effect.
193 rustcSource: Option<String> = "null",
185 194
186 /// Additional arguments to `rustfmt`. 195 /// Additional arguments to `rustfmt`.
187 rustfmt_extraArgs: Vec<String> = "[]", 196 rustfmt_extraArgs: Vec<String> = "[]",
@@ -386,6 +395,9 @@ impl Config {
386 pub fn work_done_progress(&self) -> bool { 395 pub fn work_done_progress(&self) -> bool {
387 try_or!(self.caps.window.as_ref()?.work_done_progress?, false) 396 try_or!(self.caps.window.as_ref()?.work_done_progress?, false)
388 } 397 }
398 pub fn will_rename(&self) -> bool {
399 try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false)
400 }
389 pub fn code_action_resolve(&self) -> bool { 401 pub fn code_action_resolve(&self) -> bool {
390 try_or!( 402 try_or!(
391 self.caps 403 self.caps
@@ -574,6 +586,7 @@ impl Config {
574 ImportPrefixDef::ByCrate => PrefixKind::ByCrate, 586 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
575 ImportPrefixDef::BySelf => PrefixKind::BySelf, 587 ImportPrefixDef::BySelf => PrefixKind::BySelf,
576 }, 588 },
589 group: self.data.assist_importGroup,
577 } 590 }
578 } 591 }
579 pub fn completion(&self) -> CompletionConfig { 592 pub fn completion(&self) -> CompletionConfig {
@@ -758,7 +771,8 @@ fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json:
758} 771}
759 772
760fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value { 773fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value {
761 let doc = doc.iter().map(|it| it.trim()).join(" "); 774 let doc = doc_comment_to_string(doc);
775 let doc = doc.trim_end_matches('\n');
762 assert!( 776 assert!(
763 doc.ends_with('.') && doc.starts_with(char::is_uppercase), 777 doc.ends_with('.') && doc.starts_with(char::is_uppercase),
764 "bad docs for {}: {:?}", 778 "bad docs for {}: {:?}",
@@ -847,21 +861,26 @@ fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
847 .iter() 861 .iter()
848 .map(|(field, _ty, doc, default)| { 862 .map(|(field, _ty, doc, default)| {
849 let name = format!("rust-analyzer.{}", field.replace("_", ".")); 863 let name = format!("rust-analyzer.{}", field.replace("_", "."));
850 format!("[[{}]]{} (default: `{}`)::\n{}\n", name, name, default, doc.join(" ")) 864 let doc = doc_comment_to_string(*doc);
865 format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc)
851 }) 866 })
852 .collect::<String>() 867 .collect::<String>()
853} 868}
854 869
870fn doc_comment_to_string(doc: &[&str]) -> String {
871 doc.iter().map(|it| it.strip_prefix(' ').unwrap_or(it)).map(|it| format!("{}\n", it)).collect()
872}
873
855#[cfg(test)] 874#[cfg(test)]
856mod tests { 875mod tests {
857 use std::fs; 876 use std::fs;
858 877
859 use test_utils::project_dir; 878 use test_utils::{ensure_file_contents, project_root};
860 879
861 use super::*; 880 use super::*;
862 881
863 #[test] 882 #[test]
864 fn schema_in_sync_with_package_json() { 883 fn generate_package_json_config() {
865 let s = Config::json_schema(); 884 let s = Config::json_schema();
866 let schema = format!("{:#}", s); 885 let schema = format!("{:#}", s);
867 let mut schema = schema 886 let mut schema = schema
@@ -874,7 +893,7 @@ mod tests {
874 .to_string(); 893 .to_string();
875 schema.push_str(",\n"); 894 schema.push_str(",\n");
876 895
877 let package_json_path = project_dir().join("editors/code/package.json"); 896 let package_json_path = project_root().join("editors/code/package.json");
878 let mut package_json = fs::read_to_string(&package_json_path).unwrap(); 897 let mut package_json = fs::read_to_string(&package_json_path).unwrap();
879 898
880 let start_marker = " \"$generated-start\": false,\n"; 899 let start_marker = " \"$generated-start\": false,\n";
@@ -882,26 +901,20 @@ mod tests {
882 901
883 let start = package_json.find(start_marker).unwrap() + start_marker.len(); 902 let start = package_json.find(start_marker).unwrap() + start_marker.len();
884 let end = package_json.find(end_marker).unwrap(); 903 let end = package_json.find(end_marker).unwrap();
904
885 let p = remove_ws(&package_json[start..end]); 905 let p = remove_ws(&package_json[start..end]);
886 let s = remove_ws(&schema); 906 let s = remove_ws(&schema);
887
888 if !p.contains(&s) { 907 if !p.contains(&s) {
889 package_json.replace_range(start..end, &schema); 908 package_json.replace_range(start..end, &schema);
890 fs::write(&package_json_path, &mut package_json).unwrap(); 909 ensure_file_contents(&package_json_path, &package_json)
891 panic!("new config, updating package.json")
892 } 910 }
893 } 911 }
894 912
895 #[test] 913 #[test]
896 fn schema_in_sync_with_docs() { 914 fn generate_config_documentation() {
897 let docs_path = project_dir().join("docs/user/generated_config.adoc"); 915 let docs_path = project_root().join("docs/user/generated_config.adoc");
898 let current = fs::read_to_string(&docs_path).unwrap();
899 let expected = ConfigData::manual(); 916 let expected = ConfigData::manual();
900 917 ensure_file_contents(&docs_path, &expected);
901 if remove_ws(&current) != remove_ws(&expected) {
902 fs::write(&docs_path, expected).unwrap();
903 panic!("updated config manual");
904 }
905 } 918 }
906 919
907 fn remove_ws(text: &str) -> String { 920 fn remove_ws(text: &str) -> String {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 4f6f250d6..6cc433cb8 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -697,7 +697,6 @@ pub(crate) fn handle_completion_resolve(
697 FilePosition { file_id, offset }, 697 FilePosition { file_id, offset },
698 &resolve_data.full_import_path, 698 &resolve_data.full_import_path,
699 resolve_data.imported_name, 699 resolve_data.imported_name,
700 resolve_data.import_for_trait_assoc_item,
701 )? 700 )?
702 .into_iter() 701 .into_iter()
703 .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) 702 .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
@@ -800,8 +799,18 @@ pub(crate) fn handle_rename(
800 let _p = profile::span("handle_rename"); 799 let _p = profile::span("handle_rename");
801 let position = from_proto::file_position(&snap, params.text_document_position)?; 800 let position = from_proto::file_position(&snap, params.text_document_position)?;
802 801
803 let change = 802 let mut change =
804 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?; 803 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
804
805 // this is kind of a hack to prevent double edits from happening when moving files
806 // When a module gets renamed by renaming the mod declaration this causes the file to move
807 // which in turn will trigger a WillRenameFiles request to the server for which we reply with a
808 // a second identical set of renames, the client will then apply both edits causing incorrect edits
809 // with this we only emit source_file_edits in the WillRenameFiles response which will do the rename instead
810 // See https://github.com/microsoft/vscode-languageserver-node/issues/752 for more info
811 if !change.file_system_edits.is_empty() && snap.config.will_rename() {
812 change.source_file_edits.clear();
813 }
805 let workspace_edit = to_proto::workspace_edit(&snap, change)?; 814 let workspace_edit = to_proto::workspace_edit(&snap, change)?;
806 Ok(Some(workspace_edit)) 815 Ok(Some(workspace_edit))
807} 816}
@@ -1135,20 +1144,13 @@ pub(crate) fn handle_document_highlight(
1135 None 1144 None
1136 }; 1145 };
1137 1146
1138 let res = refs 1147 let file_refs = refs.references.get(&position.file_id).map_or(&[][..], Vec::as_slice);
1139 .references 1148 let mut res = Vec::with_capacity(file_refs.len() + 1);
1140 .get(&position.file_id) 1149 res.extend(decl);
1141 .map(|file_refs| { 1150 res.extend(file_refs.iter().map(|&(range, access)| DocumentHighlight {
1142 file_refs 1151 range: to_proto::range(&line_index, range),
1143 .into_iter() 1152 kind: access.map(to_proto::document_highlight_kind),
1144 .map(|&(range, access)| DocumentHighlight { 1153 }));
1145 range: to_proto::range(&line_index, range),
1146 kind: access.map(to_proto::document_highlight_kind),
1147 })
1148 .chain(decl)
1149 .collect()
1150 })
1151 .unwrap_or_default();
1152 Ok(Some(res)) 1154 Ok(Some(res))
1153} 1155}
1154 1156
@@ -1525,7 +1527,6 @@ struct CompletionResolveData {
1525 position: lsp_types::TextDocumentPositionParams, 1527 position: lsp_types::TextDocumentPositionParams,
1526 full_import_path: String, 1528 full_import_path: String,
1527 imported_name: String, 1529 imported_name: String,
1528 import_for_trait_assoc_item: bool,
1529} 1530}
1530 1531
1531fn fill_resolve_data( 1532fn fill_resolve_data(
@@ -1534,15 +1535,13 @@ fn fill_resolve_data(
1534 position: &TextDocumentPositionParams, 1535 position: &TextDocumentPositionParams,
1535) -> Option<()> { 1536) -> Option<()> {
1536 let import_edit = item.import_to_add()?; 1537 let import_edit = item.import_to_add()?;
1537 let full_import_path = import_edit.import_path.to_string(); 1538 let import_path = &import_edit.import.import_path;
1538 let imported_name = import_edit.import_path.segments().last()?.to_string();
1539 1539
1540 *resolve_data = Some( 1540 *resolve_data = Some(
1541 to_value(CompletionResolveData { 1541 to_value(CompletionResolveData {
1542 position: position.to_owned(), 1542 position: position.to_owned(),
1543 full_import_path, 1543 full_import_path: import_path.to_string(),
1544 imported_name, 1544 imported_name: import_path.segments().last()?.to_string(),
1545 import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,
1546 }) 1545 })
1547 .unwrap(), 1546 .unwrap(),
1548 ); 1547 );
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index c1ca88df6..1a8cdadad 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -6,9 +6,10 @@ use std::{
6 6
7use ide::{ 7use ide::{
8 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, 8 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind,
9 Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, 9 CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind,
10 HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, Markup, NavigationTarget, 10 Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat,
11 ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize, 11 Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange,
12 TextEdit, TextRange, TextSize,
12}; 13};
13use ide_db::SymbolKind; 14use ide_db::SymbolKind;
14use itertools::Itertools; 15use itertools::Itertools;
@@ -173,20 +174,14 @@ pub(crate) fn snippet_text_edit_vec(
173 174
174pub(crate) fn completion_item( 175pub(crate) fn completion_item(
175 line_index: &LineIndex, 176 line_index: &LineIndex,
176 completion_item: CompletionItem, 177 item: CompletionItem,
177) -> Vec<lsp_types::CompletionItem> { 178) -> Vec<lsp_types::CompletionItem> {
178 fn set_score(res: &mut lsp_types::CompletionItem, label: &str) {
179 res.preselect = Some(true);
180 // HACK: sort preselect items first
181 res.sort_text = Some(format!(" {}", label));
182 }
183
184 let mut additional_text_edits = Vec::new(); 179 let mut additional_text_edits = Vec::new();
185 let mut text_edit = None; 180 let mut text_edit = None;
186 // LSP does not allow arbitrary edits in completion, so we have to do a 181 // LSP does not allow arbitrary edits in completion, so we have to do a
187 // non-trivial mapping here. 182 // non-trivial mapping here.
188 let source_range = completion_item.source_range(); 183 let source_range = item.source_range();
189 for indel in completion_item.text_edit().iter() { 184 for indel in item.text_edit().iter() {
190 if indel.delete.contains_range(source_range) { 185 if indel.delete.contains_range(source_range) {
191 text_edit = Some(if indel.delete == source_range { 186 text_edit = Some(if indel.delete == source_range {
192 self::text_edit(line_index, indel.clone()) 187 self::text_edit(line_index, indel.clone())
@@ -207,46 +202,61 @@ pub(crate) fn completion_item(
207 } 202 }
208 let text_edit = text_edit.unwrap(); 203 let text_edit = text_edit.unwrap();
209 204
210 let mut res = lsp_types::CompletionItem { 205 let mut lsp_item = lsp_types::CompletionItem {
211 label: completion_item.label().to_string(), 206 label: item.label().to_string(),
212 detail: completion_item.detail().map(|it| it.to_string()), 207 detail: item.detail().map(|it| it.to_string()),
213 filter_text: Some(completion_item.lookup().to_string()), 208 filter_text: Some(item.lookup().to_string()),
214 kind: completion_item.kind().map(completion_item_kind), 209 kind: item.kind().map(completion_item_kind),
215 text_edit: Some(text_edit.into()), 210 text_edit: Some(text_edit.into()),
216 additional_text_edits: Some(additional_text_edits), 211 additional_text_edits: Some(additional_text_edits),
217 documentation: completion_item.documentation().map(documentation), 212 documentation: item.documentation().map(documentation),
218 deprecated: Some(completion_item.deprecated()), 213 deprecated: Some(item.deprecated()),
219 ..Default::default() 214 ..Default::default()
220 }; 215 };
221 216
222 if completion_item.score().is_some() { 217 fn set_score(res: &mut lsp_types::CompletionItem, relevance: CompletionRelevance) {
223 set_score(&mut res, completion_item.label()); 218 if relevance.is_relevant() {
219 res.preselect = Some(true);
220 }
221 // The relevance needs to be inverted to come up with a sort score
222 // because the client will sort ascending.
223 let sort_score = relevance.score() ^ 0xFF_FF_FF_FF;
224 // Zero pad the string to ensure values can be properly sorted
225 // by the client. Hex format is used because it is easier to
226 // visually compare very large values, which the sort text
227 // tends to be since it is the opposite of the score.
228 res.sort_text = Some(format!("{:08x}", sort_score));
224 } 229 }
225 230
226 if completion_item.deprecated() { 231 set_score(&mut lsp_item, item.relevance());
227 res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) 232
233 if item.deprecated() {
234 lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
228 } 235 }
229 236
230 if completion_item.trigger_call_info() { 237 if item.trigger_call_info() {
231 res.command = Some(command::trigger_parameter_hints()); 238 lsp_item.command = Some(command::trigger_parameter_hints());
232 } 239 }
233 240
234 let mut all_results = match completion_item.ref_match() { 241 let mut res = match item.ref_match() {
235 Some(ref_match) => { 242 Some((mutability, relevance)) => {
236 let mut refed = res.clone(); 243 let mut lsp_item_with_ref = lsp_item.clone();
237 let (mutability, _score) = ref_match; 244 set_score(&mut lsp_item_with_ref, relevance);
238 let label = format!("&{}{}", mutability.as_keyword_for_ref(), refed.label); 245 lsp_item_with_ref.label =
239 set_score(&mut refed, &label); 246 format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
240 refed.label = label; 247 if let Some(lsp_types::CompletionTextEdit::Edit(it)) = &mut lsp_item_with_ref.text_edit
241 vec![res, refed] 248 {
249 it.new_text = format!("&{}{}", mutability.as_keyword_for_ref(), it.new_text);
250 }
251 vec![lsp_item_with_ref, lsp_item]
242 } 252 }
243 None => vec![res], 253 None => vec![lsp_item],
244 }; 254 };
245 255
246 for mut r in all_results.iter_mut() { 256 for lsp_item in res.iter_mut() {
247 r.insert_text_format = Some(insert_text_format(completion_item.insert_text_format())); 257 lsp_item.insert_text_format = Some(insert_text_format(item.insert_text_format()));
248 } 258 }
249 all_results 259 res
250} 260}
251 261
252pub(crate) fn signature_help( 262pub(crate) fn signature_help(
@@ -1087,7 +1097,11 @@ mod tests {
1087 add_call_parenthesis: true, 1097 add_call_parenthesis: true,
1088 add_call_argument_snippets: true, 1098 add_call_argument_snippets: true,
1089 snippet_cap: SnippetCap::new(true), 1099 snippet_cap: SnippetCap::new(true),
1090 insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain }, 1100 insert_use: InsertUseConfig {
1101 merge: None,
1102 prefix_kind: PrefixKind::Plain,
1103 group: true,
1104 },
1091 }, 1105 },
1092 ide_db::base_db::FilePosition { file_id, offset }, 1106 ide_db::base_db::FilePosition { file_id, offset },
1093 ) 1107 )
@@ -1101,13 +1115,15 @@ mod tests {
1101 expect_test::expect![[r#" 1115 expect_test::expect![[r#"
1102 [ 1116 [
1103 ( 1117 (
1104 "arg", 1118 "&arg",
1105 None, 1119 Some(
1120 "fffffffd",
1121 ),
1106 ), 1122 ),
1107 ( 1123 (
1108 "&arg", 1124 "arg",
1109 Some( 1125 Some(
1110 " &arg", 1126 "fffffffe",
1111 ), 1127 ),
1112 ), 1128 ),
1113 ] 1129 ]
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index 6b774073d..cd0c91481 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -13,7 +13,7 @@ use project_model::ProjectManifest;
13use rust_analyzer::{config::Config, lsp_ext, main_loop}; 13use rust_analyzer::{config::Config, lsp_ext, main_loop};
14use serde::Serialize; 14use serde::Serialize;
15use serde_json::{json, to_string_pretty, Value}; 15use serde_json::{json, to_string_pretty, Value};
16use test_utils::{find_mismatch, Fixture}; 16use test_utils::Fixture;
17use vfs::AbsPathBuf; 17use vfs::AbsPathBuf;
18 18
19use crate::testdir::TestDir; 19use crate::testdir::TestDir;
@@ -279,3 +279,98 @@ fn recv_timeout(receiver: &Receiver<Message>) -> Result<Option<Message>, Timeout
279 recv(after(timeout)) -> _ => Err(Timeout), 279 recv(after(timeout)) -> _ => Err(Timeout),
280 } 280 }
281} 281}
282
283// Comparison functionality borrowed from cargo:
284
285/// Compares JSON object for approximate equality.
286/// You can use `[..]` wildcard in strings (useful for OS dependent things such
287/// as paths). You can use a `"{...}"` string literal as a wildcard for
288/// arbitrary nested JSON. Arrays are sorted before comparison.
289fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> {
290 match (expected, actual) {
291 (Value::Number(l), Value::Number(r)) if l == r => None,
292 (Value::Bool(l), Value::Bool(r)) if l == r => None,
293 (Value::String(l), Value::String(r)) if lines_match(l, r) => None,
294 (Value::Array(l), Value::Array(r)) => {
295 if l.len() != r.len() {
296 return Some((expected, actual));
297 }
298
299 let mut l = l.iter().collect::<Vec<_>>();
300 let mut r = r.iter().collect::<Vec<_>>();
301
302 l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) {
303 Some(i) => {
304 r.remove(i);
305 false
306 }
307 None => true,
308 });
309
310 if !l.is_empty() {
311 assert!(!r.is_empty());
312 Some((&l[0], &r[0]))
313 } else {
314 assert_eq!(r.len(), 0);
315 None
316 }
317 }
318 (Value::Object(l), Value::Object(r)) => {
319 fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> {
320 let mut entries = obj.iter().collect::<Vec<_>>();
321 entries.sort_by_key(|it| it.0);
322 entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>()
323 }
324
325 let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k));
326 if !same_keys {
327 return Some((expected, actual));
328 }
329
330 let l = sorted_values(l);
331 let r = sorted_values(r);
332
333 l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next()
334 }
335 (Value::Null, Value::Null) => None,
336 // magic string literal "{...}" acts as wildcard for any sub-JSON
337 (Value::String(l), _) if l == "{...}" => None,
338 _ => Some((expected, actual)),
339 }
340}
341
342/// Compare a line with an expected pattern.
343/// - Use `[..]` as a wildcard to match 0 or more characters on the same line
344/// (similar to `.*` in a regex).
345fn lines_match(expected: &str, actual: &str) -> bool {
346 // Let's not deal with / vs \ (windows...)
347 // First replace backslash-escaped backslashes with forward slashes
348 // which can occur in, for example, JSON output
349 let expected = expected.replace(r"\\", "/").replace(r"\", "/");
350 let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/");
351 for (i, part) in expected.split("[..]").enumerate() {
352 match actual.find(part) {
353 Some(j) => {
354 if i == 0 && j != 0 {
355 return false;
356 }
357 actual = &actual[j + part.len()..];
358 }
359 None => return false,
360 }
361 }
362 actual.is_empty() || expected.ends_with("[..]")
363}
364
365#[test]
366fn lines_match_works() {
367 assert!(lines_match("a b", "a b"));
368 assert!(lines_match("a[..]b", "a b"));
369 assert!(lines_match("a[..]", "a b"));
370 assert!(lines_match("[..]", "a b"));
371 assert!(lines_match("[..]b", "a b"));
372
373 assert!(!lines_match("[..]b", "c"));
374 assert!(!lines_match("b", "c"));
375 assert!(!lines_match("b", "cb"));
376}
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 9ee3a8586..05fca5dc4 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -11,9 +11,10 @@ edition = "2018"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14cov-mark = "1.1"
14itertools = "0.10.0" 15itertools = "0.10.0"
15rowan = "0.12.2" 16rowan = "0.12.2"
16rustc_lexer = { version = "709.0.0", package = "rustc-ap-rustc_lexer" } 17rustc_lexer = { version = "710.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 19arrayvec = "0.5.1"
19once_cell = "1.3.1" 20once_cell = "1.3.1"
@@ -24,10 +25,10 @@ serde = { version = "1.0.106", features = ["derive"] }
24stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { path = "../stdx", version = "0.0.0" }
25text_edit = { path = "../text_edit", version = "0.0.0" } 26text_edit = { path = "../text_edit", version = "0.0.0" }
26parser = { path = "../parser", version = "0.0.0" } 27parser = { path = "../parser", version = "0.0.0" }
27test_utils = { path = "../test_utils", version = "0.0.0" }
28profile = { path = "../profile", version = "0.0.0" } 28profile = { path = "../profile", version = "0.0.0" }
29 29
30[dev-dependencies] 30[dev-dependencies]
31test_utils = { path = "../test_utils" }
31walkdir = "2.3.1" 32walkdir = "2.3.1"
32rayon = "1" 33rayon = "1"
33expect-test = "1.1" 34expect-test = "1.1"
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 2ff92f9f6..b13252eec 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -10,7 +10,6 @@ use std::{
10use indexmap::IndexMap; 10use indexmap::IndexMap;
11use itertools::Itertools; 11use itertools::Itertools;
12use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
13use test_utils::mark;
14use text_edit::TextEditBuilder; 13use text_edit::TextEditBuilder;
15 14
16use crate::{ 15use crate::{
@@ -184,7 +183,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
184 let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) { 183 let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) {
185 Some((lhs, rhs)) => (lhs, rhs), 184 Some((lhs, rhs)) => (lhs, rhs),
186 _ => { 185 _ => {
187 mark::hit!(diff_node_token_replace); 186 cov_mark::hit!(diff_node_token_replace);
188 diff.replacements.insert(lhs, rhs); 187 diff.replacements.insert(lhs, rhs);
189 return; 188 return;
190 } 189 }
@@ -202,19 +201,19 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
202 (None, Some(element)) => { 201 (None, Some(element)) => {
203 let insert_pos = match last_lhs.clone() { 202 let insert_pos = match last_lhs.clone() {
204 Some(prev) => { 203 Some(prev) => {
205 mark::hit!(diff_insert); 204 cov_mark::hit!(diff_insert);
206 TreeDiffInsertPos::After(prev) 205 TreeDiffInsertPos::After(prev)
207 } 206 }
208 // first iteration, insert into out parent as the first child 207 // first iteration, insert into out parent as the first child
209 None => { 208 None => {
210 mark::hit!(diff_insert_as_first_child); 209 cov_mark::hit!(diff_insert_as_first_child);
211 TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) 210 TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
212 } 211 }
213 }; 212 };
214 diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element); 213 diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element);
215 } 214 }
216 (Some(element), None) => { 215 (Some(element), None) => {
217 mark::hit!(diff_delete); 216 cov_mark::hit!(diff_delete);
218 diff.deletions.push(element); 217 diff.deletions.push(element);
219 } 218 }
220 (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} 219 (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {}
@@ -228,7 +227,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
228 let mut insert = false; 227 let mut insert = false;
229 while let Some(rhs_child) = rhs_children_clone.next() { 228 while let Some(rhs_child) = rhs_children_clone.next() {
230 if syntax_element_eq(&lhs_ele, &rhs_child) { 229 if syntax_element_eq(&lhs_ele, &rhs_child) {
231 mark::hit!(diff_insertions); 230 cov_mark::hit!(diff_insertions);
232 insert = true; 231 insert = true;
233 break; 232 break;
234 } else { 233 } else {
@@ -240,7 +239,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
240 let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) { 239 let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) {
241 TreeDiffInsertPos::After(prev) 240 TreeDiffInsertPos::After(prev)
242 } else { 241 } else {
243 mark::hit!(insert_first_child); 242 cov_mark::hit!(insert_first_child);
244 TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) 243 TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
245 }; 244 };
246 245
@@ -635,14 +634,13 @@ mod tests {
635 use expect_test::{expect, Expect}; 634 use expect_test::{expect, Expect};
636 use itertools::Itertools; 635 use itertools::Itertools;
637 use parser::SyntaxKind; 636 use parser::SyntaxKind;
638 use test_utils::mark;
639 use text_edit::TextEdit; 637 use text_edit::TextEdit;
640 638
641 use crate::{AstNode, SyntaxElement}; 639 use crate::{AstNode, SyntaxElement};
642 640
643 #[test] 641 #[test]
644 fn replace_node_token() { 642 fn replace_node_token() {
645 mark::check!(diff_node_token_replace); 643 cov_mark::check!(diff_node_token_replace);
646 check_diff( 644 check_diff(
647 r#"use node;"#, 645 r#"use node;"#,
648 r#"ident"#, 646 r#"ident"#,
@@ -666,7 +664,7 @@ mod tests {
666 664
667 #[test] 665 #[test]
668 fn replace_parent() { 666 fn replace_parent() {
669 mark::check!(diff_insert_as_first_child); 667 cov_mark::check!(diff_insert_as_first_child);
670 check_diff( 668 check_diff(
671 r#""#, 669 r#""#,
672 r#"use foo::bar;"#, 670 r#"use foo::bar;"#,
@@ -689,7 +687,7 @@ mod tests {
689 687
690 #[test] 688 #[test]
691 fn insert_last() { 689 fn insert_last() {
692 mark::check!(diff_insert); 690 cov_mark::check!(diff_insert);
693 check_diff( 691 check_diff(
694 r#" 692 r#"
695use foo; 693use foo;
@@ -774,7 +772,7 @@ use baz;"#,
774 772
775 #[test] 773 #[test]
776 fn first_child_insertion() { 774 fn first_child_insertion() {
777 mark::check!(insert_first_child); 775 cov_mark::check!(insert_first_child);
778 check_diff( 776 check_diff(
779 r#"fn main() { 777 r#"fn main() {
780 stdi 778 stdi
@@ -804,7 +802,7 @@ use baz;"#,
804 802
805 #[test] 803 #[test]
806 fn delete_last() { 804 fn delete_last() {
807 mark::check!(diff_delete); 805 cov_mark::check!(diff_delete);
808 check_diff( 806 check_diff(
809 r#"use foo; 807 r#"use foo;
810 use bar;"#, 808 use bar;"#,
@@ -828,7 +826,7 @@ use baz;"#,
828 826
829 #[test] 827 #[test]
830 fn delete_middle() { 828 fn delete_middle() {
831 mark::check!(diff_insertions); 829 cov_mark::check!(diff_insertions);
832 check_diff( 830 check_diff(
833 r#" 831 r#"
834use expect_test::{expect, Expect}; 832use expect_test::{expect, Expect};
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index b6c5de658..05a6b0b25 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -91,6 +91,10 @@ pub fn path_from_segments(
91 }) 91 })
92} 92}
93 93
94pub fn path_from_text(text: &str) -> ast::Path {
95 ast_from_text(&format!("fn main() {{ let test = {}; }}", text))
96}
97
94pub fn glob_use_tree() -> ast::UseTree { 98pub fn glob_use_tree() -> ast::UseTree {
95 ast_from_text("use *;") 99 ast_from_text("use *;")
96} 100}
@@ -218,6 +222,9 @@ pub fn expr_if(
218 }; 222 };
219 expr_from_text(&format!("if {} {} {}", condition, then_branch, else_branch)) 223 expr_from_text(&format!("if {} {} {}", condition, then_branch, else_branch))
220} 224}
225pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::Expr {
226 expr_from_text(&format!("for {} in {} {}", pat, expr, block))
227}
221pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr { 228pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
222 let token = token(op); 229 let token = token(op);
223 expr_from_text(&format!("{}{}", token, expr)) 230 expr_from_text(&format!("{}{}", token, expr))
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 11294c5b2..09e212e8c 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -37,6 +37,7 @@ pub mod algo;
37pub mod ast; 37pub mod ast;
38#[doc(hidden)] 38#[doc(hidden)]
39pub mod fuzz; 39pub mod fuzz;
40pub mod utils;
40 41
41use std::{marker::PhantomData, sync::Arc}; 42use std::{marker::PhantomData, sync::Arc};
42 43
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs
index b2c06e24f..ba0ccfaed 100644
--- a/crates/syntax/src/tests.rs
+++ b/crates/syntax/src/tests.rs
@@ -7,7 +7,7 @@ use std::{
7use ast::NameOwner; 7use ast::NameOwner;
8use expect_test::expect_file; 8use expect_test::expect_file;
9use rayon::prelude::*; 9use rayon::prelude::*;
10use test_utils::{bench, bench_fixture, project_dir, skip_slow_tests}; 10use test_utils::{bench, bench_fixture, project_root, skip_slow_tests};
11 11
12use crate::{ast, fuzz, tokenize, AstNode, SourceFile, SyntaxError, TextRange, TextSize, Token}; 12use crate::{ast, fuzz, tokenize, AstNode, SourceFile, SyntaxError, TextRange, TextSize, Token};
13 13
@@ -153,7 +153,7 @@ fn reparse_fuzz_tests() {
153/// Test that Rust-analyzer can parse and validate the rust-analyzer 153/// Test that Rust-analyzer can parse and validate the rust-analyzer
154#[test] 154#[test]
155fn self_hosting_parsing() { 155fn self_hosting_parsing() {
156 let dir = project_dir().join("crates"); 156 let dir = project_root().join("crates");
157 let files = walkdir::WalkDir::new(dir) 157 let files = walkdir::WalkDir::new(dir)
158 .into_iter() 158 .into_iter()
159 .filter_entry(|entry| { 159 .filter_entry(|entry| {
@@ -193,7 +193,7 @@ fn self_hosting_parsing() {
193} 193}
194 194
195fn test_data_dir() -> PathBuf { 195fn test_data_dir() -> PathBuf {
196 project_dir().join("crates/syntax/test_data") 196 project_root().join("crates/syntax/test_data")
197} 197}
198 198
199fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) { 199fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) {
diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs
new file mode 100644
index 000000000..f4c02518b
--- /dev/null
+++ b/crates/syntax/src/utils.rs
@@ -0,0 +1,43 @@
1//! A set of utils methods to reuse on other abstraction levels
2
3use itertools::Itertools;
4
5use crate::{ast, match_ast, AstNode};
6
7pub fn path_to_string_stripping_turbo_fish(path: &ast::Path) -> String {
8 path.syntax()
9 .children()
10 .filter_map(|node| {
11 match_ast! {
12 match node {
13 ast::PathSegment(it) => {
14 Some(it.name_ref()?.to_string())
15 },
16 ast::Path(it) => {
17 Some(path_to_string_stripping_turbo_fish(&it))
18 },
19 _ => None,
20 }
21 }
22 })
23 .join("::")
24}
25
26#[cfg(test)]
27mod tests {
28 use super::path_to_string_stripping_turbo_fish;
29 use crate::ast::make;
30
31 #[test]
32 fn turbofishes_are_stripped() {
33 assert_eq!("Vec", path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>")),);
34 assert_eq!(
35 "Vec::new",
36 path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>::new")),
37 );
38 assert_eq!(
39 "Vec::new",
40 path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::new()")),
41 );
42 }
43}
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 2a65000b8..87bab7a08 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -13,7 +13,6 @@ doctest = false
13# Avoid adding deps here, this crate is widely used in tests it should compile fast! 13# Avoid adding deps here, this crate is widely used in tests it should compile fast!
14dissimilar = "1.0.2" 14dissimilar = "1.0.2"
15text-size = "1.0.0" 15text-size = "1.0.0"
16serde_json = "1.0.48"
17rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
18 17
19stdx = { path = "../stdx", version = "0.0.0" } 18stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/test_utils/src/bench_fixture.rs b/crates/test_utils/src/bench_fixture.rs
index d775e2cc9..3a37c4473 100644
--- a/crates/test_utils/src/bench_fixture.rs
+++ b/crates/test_utils/src/bench_fixture.rs
@@ -4,7 +4,7 @@ use std::fs;
4 4
5use stdx::format_to; 5use stdx::format_to;
6 6
7use crate::project_dir; 7use crate::project_root;
8 8
9pub fn big_struct() -> String { 9pub fn big_struct() -> String {
10 let n = 1_000; 10 let n = 1_000;
@@ -32,11 +32,11 @@ struct S{} {{
32} 32}
33 33
34pub fn glorious_old_parser() -> String { 34pub fn glorious_old_parser() -> String {
35 let path = project_dir().join("bench_data/glorious_old_parser"); 35 let path = project_root().join("bench_data/glorious_old_parser");
36 fs::read_to_string(&path).unwrap() 36 fs::read_to_string(&path).unwrap()
37} 37}
38 38
39pub fn numerous_macro_rules() -> String { 39pub fn numerous_macro_rules() -> String {
40 let path = project_dir().join("bench_data/numerous_macro_rules"); 40 let path = project_root().join("bench_data/numerous_macro_rules");
41 fs::read_to_string(&path).unwrap() 41 fs::read_to_string(&path).unwrap()
42} 42}
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 5be4a64fc..c5f859790 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -6,20 +6,17 @@
6//! * Extracting markup (mainly, `$0` markers) out of fixture strings. 6//! * Extracting markup (mainly, `$0` markers) out of fixture strings.
7//! * marks (see the eponymous module). 7//! * marks (see the eponymous module).
8 8
9#[macro_use]
10pub mod mark;
11pub mod bench_fixture; 9pub mod bench_fixture;
12mod fixture; 10mod fixture;
13 11
14use std::{ 12use std::{
15 convert::{TryFrom, TryInto}, 13 convert::{TryFrom, TryInto},
16 env, fs, 14 env, fs,
17 path::PathBuf, 15 path::{Path, PathBuf},
18}; 16};
19 17
20use profile::StopWatch; 18use profile::StopWatch;
21use serde_json::Value; 19use stdx::{is_ci, lines_with_ends};
22use stdx::lines_with_ends;
23use text_size::{TextRange, TextSize}; 20use text_size::{TextRange, TextSize};
24 21
25pub use dissimilar::diff as __diff; 22pub use dissimilar::diff as __diff;
@@ -281,101 +278,6 @@ fn main() {
281 ); 278 );
282} 279}
283 280
284// Comparison functionality borrowed from cargo:
285
286/// Compare a line with an expected pattern.
287/// - Use `[..]` as a wildcard to match 0 or more characters on the same line
288/// (similar to `.*` in a regex).
289pub fn lines_match(expected: &str, actual: &str) -> bool {
290 // Let's not deal with / vs \ (windows...)
291 // First replace backslash-escaped backslashes with forward slashes
292 // which can occur in, for example, JSON output
293 let expected = expected.replace(r"\\", "/").replace(r"\", "/");
294 let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/");
295 for (i, part) in expected.split("[..]").enumerate() {
296 match actual.find(part) {
297 Some(j) => {
298 if i == 0 && j != 0 {
299 return false;
300 }
301 actual = &actual[j + part.len()..];
302 }
303 None => return false,
304 }
305 }
306 actual.is_empty() || expected.ends_with("[..]")
307}
308
309#[test]
310fn lines_match_works() {
311 assert!(lines_match("a b", "a b"));
312 assert!(lines_match("a[..]b", "a b"));
313 assert!(lines_match("a[..]", "a b"));
314 assert!(lines_match("[..]", "a b"));
315 assert!(lines_match("[..]b", "a b"));
316
317 assert!(!lines_match("[..]b", "c"));
318 assert!(!lines_match("b", "c"));
319 assert!(!lines_match("b", "cb"));
320}
321
322/// Compares JSON object for approximate equality.
323/// You can use `[..]` wildcard in strings (useful for OS dependent things such
324/// as paths). You can use a `"{...}"` string literal as a wildcard for
325/// arbitrary nested JSON. Arrays are sorted before comparison.
326pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> {
327 match (expected, actual) {
328 (Value::Number(l), Value::Number(r)) if l == r => None,
329 (Value::Bool(l), Value::Bool(r)) if l == r => None,
330 (Value::String(l), Value::String(r)) if lines_match(l, r) => None,
331 (Value::Array(l), Value::Array(r)) => {
332 if l.len() != r.len() {
333 return Some((expected, actual));
334 }
335
336 let mut l = l.iter().collect::<Vec<_>>();
337 let mut r = r.iter().collect::<Vec<_>>();
338
339 l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) {
340 Some(i) => {
341 r.remove(i);
342 false
343 }
344 None => true,
345 });
346
347 if !l.is_empty() {
348 assert!(!r.is_empty());
349 Some((&l[0], &r[0]))
350 } else {
351 assert_eq!(r.len(), 0);
352 None
353 }
354 }
355 (Value::Object(l), Value::Object(r)) => {
356 fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> {
357 let mut entries = obj.iter().collect::<Vec<_>>();
358 entries.sort_by_key(|it| it.0);
359 entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>()
360 }
361
362 let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k));
363 if !same_keys {
364 return Some((expected, actual));
365 }
366
367 let l = sorted_values(l);
368 let r = sorted_values(r);
369
370 l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next()
371 }
372 (Value::Null, Value::Null) => None,
373 // magic string literal "{...}" acts as wildcard for any sub-JSON
374 (Value::String(l), _) if l == "{...}" => None,
375 _ => Some((expected, actual)),
376 }
377}
378
379/// Returns `false` if slow tests should not run, otherwise returns `true` and 281/// Returns `false` if slow tests should not run, otherwise returns `true` and
380/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag 282/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag
381/// that slow tests did run. 283/// that slow tests did run.
@@ -384,14 +286,14 @@ pub fn skip_slow_tests() -> bool {
384 if should_skip { 286 if should_skip {
385 eprintln!("ignoring slow test") 287 eprintln!("ignoring slow test")
386 } else { 288 } else {
387 let path = project_dir().join("./target/.slow_tests_cookie"); 289 let path = project_root().join("./target/.slow_tests_cookie");
388 fs::write(&path, ".").unwrap(); 290 fs::write(&path, ".").unwrap();
389 } 291 }
390 should_skip 292 should_skip
391} 293}
392 294
393/// Returns the path to the root directory of `rust-analyzer` project. 295/// Returns the path to the root directory of `rust-analyzer` project.
394pub fn project_dir() -> PathBuf { 296pub fn project_root() -> PathBuf {
395 let dir = env!("CARGO_MANIFEST_DIR"); 297 let dir = env!("CARGO_MANIFEST_DIR");
396 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() 298 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned()
397} 299}
@@ -449,3 +351,39 @@ pub fn bench(label: &'static str) -> impl Drop {
449 351
450 Bencher { sw: StopWatch::start(), label } 352 Bencher { sw: StopWatch::start(), label }
451} 353}
354
355/// Checks that the `file` has the specified `contents`. If that is not the
356/// case, updates the file and then fails the test.
357pub fn ensure_file_contents(file: &Path, contents: &str) {
358 if let Err(()) = try_ensure_file_contents(file, contents) {
359 panic!("Some files were not up-to-date");
360 }
361}
362
363/// Checks that the `file` has the specified `contents`. If that is not the
364/// case, updates the file and return an Error.
365pub fn try_ensure_file_contents(file: &Path, contents: &str) -> Result<(), ()> {
366 match std::fs::read_to_string(file) {
367 Ok(old_contents) if normalize_newlines(&old_contents) == normalize_newlines(contents) => {
368 return Ok(())
369 }
370 _ => (),
371 }
372 let display_path = file.strip_prefix(&project_root()).unwrap_or(file);
373 eprintln!(
374 "\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n",
375 display_path.display()
376 );
377 if is_ci() {
378 eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n");
379 }
380 if let Some(parent) = file.parent() {
381 let _ = std::fs::create_dir_all(parent);
382 }
383 std::fs::write(file, contents).unwrap();
384 Err(())
385}
386
387fn normalize_newlines(s: &str) -> String {
388 s.replace("\r\n", "\n")
389}
diff --git a/crates/test_utils/src/mark.rs b/crates/test_utils/src/mark.rs
deleted file mode 100644
index 97f5a93ad..000000000
--- a/crates/test_utils/src/mark.rs
+++ /dev/null
@@ -1,78 +0,0 @@
1//! This module implements manually tracked test coverage, which is useful for
2//! quickly finding a test responsible for testing a particular bit of code.
3//!
4//! See <https://matklad.github.io/2018/06/18/a-trick-for-test-maintenance.html>
5//! for details, but the TL;DR is that you write your test as
6//!
7//! ```
8//! #[test]
9//! fn test_foo() {
10//! mark::check!(test_foo);
11//! }
12//! ```
13//!
14//! and in the code under test you write
15//!
16//! ```
17//! # use test_utils::mark;
18//! # fn some_condition() -> bool { true }
19//! fn foo() {
20//! if some_condition() {
21//! mark::hit!(test_foo);
22//! }
23//! }
24//! ```
25//!
26//! This module then checks that executing the test indeed covers the specified
27//! function. This is useful if you come back to the `foo` function ten years
28//! later and wonder where the test are: now you can grep for `test_foo`.
29use std::sync::atomic::{AtomicUsize, Ordering};
30
31#[macro_export]
32macro_rules! _hit {
33 ($ident:ident) => {{
34 #[cfg(test)]
35 {
36 extern "C" {
37 #[no_mangle]
38 static $ident: std::sync::atomic::AtomicUsize;
39 }
40 unsafe {
41 $ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
42 }
43 }
44 }};
45}
46pub use _hit as hit;
47
48#[macro_export]
49macro_rules! _check {
50 ($ident:ident) => {
51 #[no_mangle]
52 static $ident: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
53 let _checker = $crate::mark::MarkChecker::new(&$ident);
54 };
55}
56pub use _check as check;
57
58pub struct MarkChecker {
59 mark: &'static AtomicUsize,
60 value_on_entry: usize,
61}
62
63impl MarkChecker {
64 pub fn new(mark: &'static AtomicUsize) -> MarkChecker {
65 let value_on_entry = mark.load(Ordering::Relaxed);
66 MarkChecker { mark, value_on_entry }
67 }
68}
69
70impl Drop for MarkChecker {
71 fn drop(&mut self) {
72 if std::thread::panicking() {
73 return;
74 }
75 let value_on_exit = self.mark.load(Ordering::Relaxed);
76 assert!(value_on_exit > self.value_on_entry, "mark was not hit")
77 }
78}