aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/cfg/src/lib.rs2
-rw-r--r--crates/hir/Cargo.toml1
-rw-r--r--crates/hir/src/attrs.rs2
-rw-r--r--crates/hir/src/lib.rs15
-rw-r--r--crates/hir/src/semantics.rs12
-rw-r--r--crates/hir/src/source_analyzer.rs2
-rw-r--r--crates/hir_def/src/attr.rs175
-rw-r--r--crates/hir_def/src/body.rs6
-rw-r--r--crates/hir_def/src/body/lower.rs28
-rw-r--r--crates/hir_def/src/find_path.rs2
-rw-r--r--crates/hir_def/src/item_tree.rs5
-rw-r--r--crates/hir_def/src/item_tree/lower.rs37
-rw-r--r--crates/hir_def/src/lib.rs18
-rw-r--r--crates/hir_def/src/nameres.rs3
-rw-r--r--crates/hir_def/src/nameres/collector.rs22
-rw-r--r--crates/hir_def/src/nameres/tests/incremental.rs58
-rw-r--r--crates/hir_def/src/path.rs9
-rw-r--r--crates/hir_def/src/path/lower.rs4
-rw-r--r--crates/hir_def/src/path/lower/lower_use.rs23
-rw-r--r--crates/hir_def/src/visibility.rs8
-rw-r--r--crates/hir_expand/src/builtin_derive.rs4
-rw-r--r--crates/hir_expand/src/builtin_macro.rs5
-rw-r--r--crates/hir_expand/src/db.rs469
-rw-r--r--crates/hir_expand/src/eager.rs7
-rw-r--r--crates/hir_expand/src/hygiene.rs73
-rw-r--r--crates/hir_expand/src/input.rs94
-rw-r--r--crates/hir_expand/src/lib.rs106
-rw-r--r--crates/hir_expand/src/proc_macro.rs102
-rw-r--r--crates/hir_expand/src/quote.rs4
-rw-r--r--crates/hir_ty/src/chalk_ext.rs36
-rw-r--r--crates/hir_ty/src/display.rs2
-rw-r--r--crates/ide/Cargo.toml1
-rw-r--r--crates/ide/src/diagnostics.rs63
-rw-r--r--crates/ide/src/diagnostics/fixes.rs51
-rw-r--r--crates/ide/src/diagnostics/unlinked_file.rs7
-rwxr-xr-x[-rw-r--r--]crates/ide/src/folding_ranges.rs45
-rw-r--r--crates/ide/src/goto_type_definition.rs12
-rw-r--r--crates/ide/src/inlay_hints.rs4
-rw-r--r--crates/ide/src/join_lines.rs69
-rw-r--r--crates/ide/src/lib.rs44
-rw-r--r--crates/ide/src/references.rs64
-rw-r--r--crates/ide/src/references/rename.rs17
-rw-r--r--crates/ide/src/ssr.rs54
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html2
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs2
-rw-r--r--crates/ide/src/typing.rs150
-rw-r--r--crates/ide/src/typing/on_enter.rs115
-rw-r--r--crates/ide/src/view_crate_graph.rs111
-rw-r--r--crates/ide_assists/src/assist_context.rs57
-rw-r--r--crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs2
-rw-r--r--crates/ide_assists/src/handlers/early_return.rs4
-rw-r--r--crates/ide_assists/src/handlers/expand_glob_import.rs89
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs197
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs2
-rw-r--r--crates/ide_assists/src/handlers/introduce_named_lifetime.rs46
-rw-r--r--crates/ide_assists/src/handlers/merge_imports.rs4
-rw-r--r--crates/ide_assists/src/handlers/move_bounds.rs6
-rw-r--r--crates/ide_assists/src/handlers/pull_assignment_up.rs212
-rw-r--r--crates/ide_assists/src/handlers/reorder_impl.rs40
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs8
-rw-r--r--crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs120
-rw-r--r--crates/ide_assists/src/handlers/replace_let_with_if_let.rs21
-rw-r--r--crates/ide_assists/src/handlers/replace_unwrap_with_match.rs21
-rw-r--r--crates/ide_assists/src/handlers/wrap_return_type_in_result.rs4
-rw-r--r--crates/ide_assists/src/lib.rs68
-rw-r--r--crates/ide_assists/src/tests.rs264
-rw-r--r--crates/ide_assists/src/tests/generated.rs2
-rw-r--r--crates/ide_assists/src/utils.rs4
-rw-r--r--crates/ide_assists/src/utils/suggest_name.rs15
-rw-r--r--crates/ide_completion/src/completions.rs56
-rw-r--r--crates/ide_completion/src/completions/pattern.rs6
-rw-r--r--crates/ide_completion/src/completions/postfix.rs5
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs73
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs69
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs6
-rw-r--r--crates/ide_completion/src/context.rs229
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_completion/src/test_utils.rs2
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs84
-rw-r--r--crates/ide_db/src/helpers/merge_imports.rs16
-rw-r--r--crates/ide_db/src/line_index.rs7
-rw-r--r--crates/ide_db/src/search.rs177
-rw-r--r--crates/ide_db/src/ty_filter.rs6
-rw-r--r--crates/parser/src/grammar/patterns.rs2
-rw-r--r--crates/proc_macro_api/Cargo.toml2
-rw-r--r--crates/proc_macro_api/src/version.rs2
-rw-r--r--crates/profile/Cargo.toml2
-rw-r--r--crates/project_model/src/cargo_workspace.rs67
-rw-r--r--crates/project_model/src/cfg_flag.rs3
-rw-r--r--crates/project_model/src/rustc_cfg.rs45
-rw-r--r--crates/project_model/src/workspace.rs7
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/build.rs19
-rw-r--r--crates/rust-analyzer/src/benchmarks.rs74
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs7
-rw-r--r--crates/rust-analyzer/src/config.rs20
-rw-r--r--crates/rust-analyzer/src/dispatch.rs21
-rw-r--r--crates/rust-analyzer/src/handlers.rs78
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs184
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs8
-rw-r--r--crates/rust-analyzer/src/main_loop.rs1
-rw-r--r--crates/rust-analyzer/src/to_proto.rs3
-rw-r--r--crates/stdx/src/lib.rs78
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/algo.rs124
-rw-r--r--crates/syntax/src/ast/edit.rs12
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs19
-rw-r--r--crates/syntax/src/ast/make.rs128
-rw-r--r--crates/syntax/src/ast/node_ext.rs25
-rw-r--r--crates/syntax/src/ted.rs5
-rw-r--r--crates/syntax/src/token_text.rs50
-rw-r--r--crates/test_utils/src/fixture.rs10
-rw-r--r--crates/test_utils/src/lib.rs4
115 files changed, 3253 insertions, 1824 deletions
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index d88ecf8b0..59fd38880 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -13,7 +13,7 @@ use tt::SmolStr;
13pub use cfg_expr::{CfgAtom, CfgExpr}; 13pub use cfg_expr::{CfgAtom, CfgExpr};
14pub use dnf::DnfExpr; 14pub use dnf::DnfExpr;
15 15
16/// Configuration options used for conditional compilition on items with `cfg` attributes. 16/// Configuration options used for conditional compilation on items with `cfg` attributes.
17/// We have two kind of options in different namespaces: atomic options like `unix`, and 17/// We have two kind of options in different namespaces: atomic options like `unix`, and
18/// key-value options like `target_arch="x86"`. 18/// key-value options like `target_arch="x86"`.
19/// 19///
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index 9e329656f..560b15238 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -25,3 +25,4 @@ hir_expand = { path = "../hir_expand", version = "0.0.0" }
25hir_def = { path = "../hir_def", version = "0.0.0" } 25hir_def = { path = "../hir_def", version = "0.0.0" }
26hir_ty = { path = "../hir_ty", version = "0.0.0" } 26hir_ty = { path = "../hir_ty", version = "0.0.0" }
27tt = { path = "../tt", version = "0.0.0" } 27tt = { path = "../tt", version = "0.0.0" }
28cfg = { path = "../cfg", version = "0.0.0" }
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 4a11622fc..e8fa3c56e 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -112,7 +112,7 @@ fn resolve_doc_path(
112 AttrDefId::MacroDefId(_) => return None, 112 AttrDefId::MacroDefId(_) => return None,
113 }; 113 };
114 let path = ast::Path::parse(link).ok()?; 114 let path = ast::Path::parse(link).ok()?;
115 let modpath = ModPath::from_src(path, &Hygiene::new_unhygienic()).unwrap(); 115 let modpath = ModPath::from_src(db.upcast(), path, &Hygiene::new_unhygienic()).unwrap();
116 let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); 116 let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
117 if resolved == PerNs::none() { 117 if resolved == PerNs::none() {
118 if let Some(trait_id) = resolver.resolve_module_path_in_trait_items(db.upcast(), &modpath) { 118 if let Some(trait_id) = resolver.resolve_module_path_in_trait_items(db.upcast(), &modpath) {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index d8ccfde0c..c9ef4b420 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -89,6 +89,7 @@ pub use crate::{
89// Generally, a refactoring which *removes* a name from this list is a good 89// Generally, a refactoring which *removes* a name from this list is a good
90// idea! 90// idea!
91pub use { 91pub use {
92 cfg::{CfgAtom, CfgExpr, CfgOptions},
92 hir_def::{ 93 hir_def::{
93 adt::StructKind, 94 adt::StructKind,
94 attr::{Attr, Attrs, AttrsWithOwner, Documentation}, 95 attr::{Attr, Attrs, AttrsWithOwner, Documentation},
@@ -215,6 +216,10 @@ impl Crate {
215 216
216 doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") 217 doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
217 } 218 }
219
220 pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions {
221 db.crate_graph()[self.id].cfg_options.clone()
222 }
218} 223}
219 224
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 225#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -1666,7 +1671,7 @@ impl Impl {
1666 .value 1671 .value
1667 .attrs() 1672 .attrs()
1668 .filter_map(|it| { 1673 .filter_map(|it| {
1669 let path = ModPath::from_src(it.path()?, &hygenic)?; 1674 let path = ModPath::from_src(db.upcast(), it.path()?, &hygenic)?;
1670 if path.as_ident()?.to_string() == "derive" { 1675 if path.as_ident()?.to_string() == "derive" {
1671 Some(it) 1676 Some(it)
1672 } else { 1677 } else {
@@ -1744,6 +1749,10 @@ impl Type {
1744 } 1749 }
1745 } 1750 }
1746 1751
1752 pub fn strip_references(&self) -> Type {
1753 self.derived(self.ty.strip_references().clone())
1754 }
1755
1747 pub fn is_unknown(&self) -> bool { 1756 pub fn is_unknown(&self) -> bool {
1748 self.ty.is_unknown() 1757 self.ty.is_unknown()
1749 } 1758 }
@@ -2062,6 +2071,10 @@ impl Type {
2062 Some(adt.into()) 2071 Some(adt.into())
2063 } 2072 }
2064 2073
2074 pub fn as_builtin(&self) -> Option<BuiltinType> {
2075 self.ty.as_builtin().map(|inner| BuiltinType { inner })
2076 }
2077
2065 pub fn as_dyn_trait(&self) -> Option<Trait> { 2078 pub fn as_dyn_trait(&self) -> Option<Trait> {
2066 self.ty.dyn_trait().map(Into::into) 2079 self.ty.dyn_trait().map(Into::into)
2067 } 2080 }
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 62500602a..38bd376bc 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -196,6 +196,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
196 self.imp.resolve_label(lifetime) 196 self.imp.resolve_label(lifetime)
197 } 197 }
198 198
199 pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
200 self.imp.resolve_type(ty)
201 }
202
199 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { 203 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
200 self.imp.type_of_expr(expr) 204 self.imp.type_of_expr(expr)
201 } 205 }
@@ -476,6 +480,14 @@ impl<'db> SemanticsImpl<'db> {
476 ToDef::to_def(self, src) 480 ToDef::to_def(self, src)
477 } 481 }
478 482
483 fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
484 let scope = self.scope(ty.syntax());
485 let ctx = body::LowerCtx::new(self.db.upcast(), scope.file_id);
486 let ty = hir_ty::TyLoweringContext::new(self.db, &scope.resolver)
487 .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
488 Type::new_with_resolver(self.db, &scope.resolver, ty)
489 }
490
479 fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { 491 fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
480 self.analyze(expr.syntax()).type_of_expr(self.db, expr) 492 self.analyze(expr.syntax()).type_of_expr(self.db, expr)
481 } 493 }
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 0895bd6f1..b5c65808e 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -283,7 +283,7 @@ impl SourceAnalyzer {
283 283
284 // This must be a normal source file rather than macro file. 284 // This must be a normal source file rather than macro file.
285 let hygiene = Hygiene::new(db.upcast(), self.file_id); 285 let hygiene = Hygiene::new(db.upcast(), self.file_id);
286 let ctx = body::LowerCtx::with_hygiene(&hygiene); 286 let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene);
287 let hir_path = Path::from_src(path.clone(), &ctx)?; 287 let hir_path = Path::from_src(path.clone(), &ctx)?;
288 288
289 // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we 289 // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index d9294d93a..aadd4e44a 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -9,7 +9,7 @@ use std::{
9use base_db::CrateId; 9use base_db::CrateId;
10use cfg::{CfgExpr, CfgOptions}; 10use cfg::{CfgExpr, CfgOptions};
11use either::Either; 11use either::Either;
12use hir_expand::{hygiene::Hygiene, name::AsName, AstId, AttrId, InFile}; 12use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
13use itertools::Itertools; 13use itertools::Itertools;
14use la_arena::ArenaMap; 14use la_arena::ArenaMap;
15use mbe::ast_to_token_tree; 15use mbe::ast_to_token_tree;
@@ -95,19 +95,19 @@ impl ops::Deref for AttrsWithOwner {
95impl RawAttrs { 95impl RawAttrs {
96 pub(crate) const EMPTY: Self = Self { entries: None }; 96 pub(crate) const EMPTY: Self = Self { entries: None };
97 97
98 pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self { 98 pub(crate) fn new(
99 db: &dyn DefDatabase,
100 owner: &dyn ast::AttrsOwner,
101 hygiene: &Hygiene,
102 ) -> Self {
99 let entries = collect_attrs(owner) 103 let entries = collect_attrs(owner)
100 .enumerate() 104 .flat_map(|(id, attr)| match attr {
101 .flat_map(|(i, attr)| { 105 Either::Left(attr) => Attr::from_src(db, attr, hygiene, id),
102 let index = AttrId(i as u32); 106 Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
103 match attr { 107 id,
104 Either::Left(attr) => Attr::from_src(attr, hygiene, index), 108 input: Some(AttrInput::Literal(SmolStr::new(doc))),
105 Either::Right(comment) => comment.doc_comment().map(|doc| Attr { 109 path: Interned::new(ModPath::from(hir_expand::name!(doc))),
106 id: index, 110 }),
107 input: Some(AttrInput::Literal(SmolStr::new(doc))),
108 path: Interned::new(ModPath::from(hir_expand::name!(doc))),
109 }),
110 }
111 }) 111 })
112 .collect::<Arc<_>>(); 112 .collect::<Arc<_>>();
113 113
@@ -116,10 +116,11 @@ impl RawAttrs {
116 116
117 fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self { 117 fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self {
118 let hygiene = Hygiene::new(db.upcast(), owner.file_id); 118 let hygiene = Hygiene::new(db.upcast(), owner.file_id);
119 Self::new(owner.value, &hygiene) 119 Self::new(db, owner.value, &hygiene)
120 } 120 }
121 121
122 pub(crate) fn merge(&self, other: Self) -> Self { 122 pub(crate) fn merge(&self, other: Self) -> Self {
123 // FIXME: This needs to fixup `AttrId`s
123 match (&self.entries, &other.entries) { 124 match (&self.entries, &other.entries) {
124 (None, None) => Self::EMPTY, 125 (None, None) => Self::EMPTY,
125 (Some(entries), None) | (None, Some(entries)) => { 126 (Some(entries), None) | (None, Some(entries)) => {
@@ -170,7 +171,7 @@ impl RawAttrs {
170 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; 171 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
171 // FIXME hygiene 172 // FIXME hygiene
172 let hygiene = Hygiene::new_unhygienic(); 173 let hygiene = Hygiene::new_unhygienic();
173 Attr::from_src(attr, &hygiene, index) 174 Attr::from_src(db, attr, &hygiene, index)
174 }); 175 });
175 176
176 let cfg_options = &crate_graph[krate].cfg_options; 177 let cfg_options = &crate_graph[krate].cfg_options;
@@ -371,39 +372,26 @@ impl AttrsWithOwner {
371 372
372 let def_map = module.def_map(db); 373 let def_map = module.def_map(db);
373 let mod_data = &def_map[module.local_id]; 374 let mod_data = &def_map[module.local_id];
374 let attrs = match mod_data.declaration_source(db) { 375 match mod_data.declaration_source(db) {
375 Some(it) => { 376 Some(it) => {
376 let mut attrs: Vec<_> = collect_attrs(&it.value as &dyn ast::AttrsOwner) 377 let mut map = AttrSourceMap::new(InFile::new(it.file_id, &it.value));
377 .map(|attr| InFile::new(it.file_id, attr))
378 .collect();
379 if let InFile { file_id, value: ModuleSource::SourceFile(file) } = 378 if let InFile { file_id, value: ModuleSource::SourceFile(file) } =
380 mod_data.definition_source(db) 379 mod_data.definition_source(db)
381 { 380 {
382 attrs.extend( 381 map.merge(AttrSourceMap::new(InFile::new(file_id, &file)));
383 collect_attrs(&file as &dyn ast::AttrsOwner)
384 .map(|attr| InFile::new(file_id, attr)),
385 )
386 } 382 }
387 attrs 383 return map;
388 } 384 }
389 None => { 385 None => {
390 let InFile { file_id, value } = mod_data.definition_source(db); 386 let InFile { file_id, value } = mod_data.definition_source(db);
391 match &value { 387 let attrs_owner = match &value {
392 ModuleSource::SourceFile(file) => { 388 ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner,
393 collect_attrs(file as &dyn ast::AttrsOwner) 389 ModuleSource::Module(module) => module as &dyn ast::AttrsOwner,
394 } 390 ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner,
395 ModuleSource::Module(module) => { 391 };
396 collect_attrs(module as &dyn ast::AttrsOwner) 392 return AttrSourceMap::new(InFile::new(file_id, attrs_owner));
397 }
398 ModuleSource::BlockExpr(block) => {
399 collect_attrs(block as &dyn ast::AttrsOwner)
400 }
401 }
402 .map(|attr| InFile::new(file_id, attr))
403 .collect()
404 } 393 }
405 }; 394 }
406 return AttrSourceMap { attrs };
407 } 395 }
408 AttrDefId::FieldId(id) => { 396 AttrDefId::FieldId(id) => {
409 let map = db.fields_attrs_source_map(id.parent); 397 let map = db.fields_attrs_source_map(id.parent);
@@ -458,11 +446,7 @@ impl AttrsWithOwner {
458 }, 446 },
459 }; 447 };
460 448
461 AttrSourceMap { 449 AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn AttrsOwner))
462 attrs: collect_attrs(&owner.value)
463 .map(|attr| InFile::new(owner.file_id, attr))
464 .collect(),
465 }
466 } 450 }
467 451
468 pub fn docs_with_rangemap( 452 pub fn docs_with_rangemap(
@@ -484,10 +468,10 @@ impl AttrsWithOwner {
484 let mut buf = String::new(); 468 let mut buf = String::new();
485 let mut mapping = Vec::new(); 469 let mut mapping = Vec::new();
486 for (doc, idx) in docs { 470 for (doc, idx) in docs {
487 // str::lines doesn't yield anything for the empty string
488 if !doc.is_empty() { 471 if !doc.is_empty() {
489 for line in doc.split('\n') { 472 let mut base_offset = 0;
490 let line = line.trim_end(); 473 for raw_line in doc.split('\n') {
474 let line = raw_line.trim_end();
491 let line_len = line.len(); 475 let line_len = line.len();
492 let (offset, line) = match line.char_indices().nth(indent) { 476 let (offset, line) = match line.char_indices().nth(indent) {
493 Some((offset, _)) => (offset, &line[offset..]), 477 Some((offset, _)) => (offset, &line[offset..]),
@@ -498,9 +482,13 @@ impl AttrsWithOwner {
498 mapping.push(( 482 mapping.push((
499 TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?), 483 TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?),
500 idx, 484 idx,
501 TextRange::new(offset.try_into().ok()?, line_len.try_into().ok()?), 485 TextRange::at(
486 (base_offset + offset).try_into().ok()?,
487 line_len.try_into().ok()?,
488 ),
502 )); 489 ));
503 buf.push('\n'); 490 buf.push('\n');
491 base_offset += raw_line.len() + 1;
504 } 492 }
505 } else { 493 } else {
506 buf.push('\n'); 494 buf.push('\n');
@@ -510,7 +498,7 @@ impl AttrsWithOwner {
510 if buf.is_empty() { 498 if buf.is_empty() {
511 None 499 None
512 } else { 500 } else {
513 Some((Documentation(buf), DocsRangeMap { mapping, source: self.source_map(db).attrs })) 501 Some((Documentation(buf), DocsRangeMap { mapping, source_map: self.source_map(db) }))
514 } 502 }
515 } 503 }
516} 504}
@@ -551,27 +539,59 @@ fn inner_attributes(
551} 539}
552 540
553pub struct AttrSourceMap { 541pub struct AttrSourceMap {
554 attrs: Vec<InFile<Either<ast::Attr, ast::Comment>>>, 542 attrs: Vec<InFile<ast::Attr>>,
543 doc_comments: Vec<InFile<ast::Comment>>,
555} 544}
556 545
557impl AttrSourceMap { 546impl AttrSourceMap {
547 fn new(owner: InFile<&dyn ast::AttrsOwner>) -> Self {
548 let mut attrs = Vec::new();
549 let mut doc_comments = Vec::new();
550 for (_, attr) in collect_attrs(owner.value) {
551 match attr {
552 Either::Left(attr) => attrs.push(owner.with_value(attr)),
553 Either::Right(comment) => doc_comments.push(owner.with_value(comment)),
554 }
555 }
556
557 Self { attrs, doc_comments }
558 }
559
560 fn merge(&mut self, other: Self) {
561 self.attrs.extend(other.attrs);
562 self.doc_comments.extend(other.doc_comments);
563 }
564
558 /// Maps the lowered `Attr` back to its original syntax node. 565 /// Maps the lowered `Attr` back to its original syntax node.
559 /// 566 ///
560 /// `attr` must come from the `owner` used for AttrSourceMap 567 /// `attr` must come from the `owner` used for AttrSourceMap
561 /// 568 ///
562 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of 569 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
563 /// the attribute represented by `Attr`. 570 /// the attribute represented by `Attr`.
564 pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> { 571 pub fn source_of(&self, attr: &Attr) -> InFile<Either<ast::Attr, ast::Comment>> {
565 self.attrs 572 self.source_of_id(attr.id)
566 .get(attr.id.0 as usize) 573 }
567 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", attr.id)) 574
568 .as_ref() 575 fn source_of_id(&self, id: AttrId) -> InFile<Either<ast::Attr, ast::Comment>> {
576 if id.is_doc_comment {
577 self.doc_comments
578 .get(id.ast_index as usize)
579 .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id))
580 .clone()
581 .map(|attr| Either::Right(attr))
582 } else {
583 self.attrs
584 .get(id.ast_index as usize)
585 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id))
586 .clone()
587 .map(|attr| Either::Left(attr))
588 }
569 } 589 }
570} 590}
571 591
572/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. 592/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
573pub struct DocsRangeMap { 593pub struct DocsRangeMap {
574 source: Vec<InFile<Either<ast::Attr, ast::Comment>>>, 594 source_map: AttrSourceMap,
575 // (docstring-line-range, attr_index, attr-string-range) 595 // (docstring-line-range, attr_index, attr-string-range)
576 // a mapping from the text range of a line of the [`Documentation`] to the attribute index and 596 // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
577 // the original (untrimmed) syntax doc line 597 // the original (untrimmed) syntax doc line
@@ -588,7 +608,7 @@ impl DocsRangeMap {
588 608
589 let relative_range = range - line_docs_range.start(); 609 let relative_range = range - line_docs_range.start();
590 610
591 let &InFile { file_id, value: ref source } = &self.source[idx.0 as usize]; 611 let &InFile { file_id, value: ref source } = &self.source_map.source_of_id(idx);
592 match source { 612 match source {
593 Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here 613 Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here
594 // as well as for whats done in syntax highlight doc injection 614 // as well as for whats done in syntax highlight doc injection
@@ -607,6 +627,12 @@ impl DocsRangeMap {
607 } 627 }
608} 628}
609 629
630#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
631pub(crate) struct AttrId {
632 is_doc_comment: bool,
633 pub(crate) ast_index: u32,
634}
635
610#[derive(Debug, Clone, PartialEq, Eq)] 636#[derive(Debug, Clone, PartialEq, Eq)]
611pub struct Attr { 637pub struct Attr {
612 pub(crate) id: AttrId, 638 pub(crate) id: AttrId,
@@ -623,8 +649,13 @@ pub enum AttrInput {
623} 649}
624 650
625impl Attr { 651impl Attr {
626 fn from_src(ast: ast::Attr, hygiene: &Hygiene, id: AttrId) -> Option<Attr> { 652 fn from_src(
627 let path = Interned::new(ModPath::from_src(ast.path()?, hygiene)?); 653 db: &dyn DefDatabase,
654 ast: ast::Attr,
655 hygiene: &Hygiene,
656 id: AttrId,
657 ) -> Option<Attr> {
658 let path = Interned::new(ModPath::from_src(db, ast.path()?, hygiene)?);
628 let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { 659 let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
629 let value = match lit.kind() { 660 let value = match lit.kind() {
630 ast::LiteralKind::String(string) => string.value()?.into(), 661 ast::LiteralKind::String(string) => string.value()?.into(),
@@ -736,22 +767,32 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase
736 767
737fn collect_attrs( 768fn collect_attrs(
738 owner: &dyn ast::AttrsOwner, 769 owner: &dyn ast::AttrsOwner,
739) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { 770) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
740 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) 771 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
741 .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); 772 .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs)));
742 773
743 let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer()); 774 let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer());
744 let attrs = outer_attrs 775 let attrs =
745 .chain(inner_attrs.into_iter().flatten()) 776 outer_attrs.chain(inner_attrs.into_iter().flatten()).enumerate().map(|(idx, attr)| {
746 .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr))); 777 (
778 AttrId { ast_index: idx as u32, is_doc_comment: false },
779 attr.syntax().text_range().start(),
780 Either::Left(attr),
781 )
782 });
747 783
748 let outer_docs = 784 let outer_docs =
749 ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); 785 ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
750 let docs = outer_docs 786 let docs =
751 .chain(inner_docs.into_iter().flatten()) 787 outer_docs.chain(inner_docs.into_iter().flatten()).enumerate().map(|(idx, docs_text)| {
752 .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text))); 788 (
789 AttrId { ast_index: idx as u32, is_doc_comment: true },
790 docs_text.syntax().text_range().start(),
791 Either::Right(docs_text),
792 )
793 });
753 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved 794 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
754 docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).map(|(_, attr)| attr) 795 docs.chain(attrs).sorted_by_key(|&(_, offset, _)| offset).map(|(id, _, attr)| (id, attr))
755} 796}
756 797
757pub(crate) fn variants_attrs_source_map( 798pub(crate) fn variants_attrs_source_map(
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 131f424cc..8360426f1 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -72,7 +72,7 @@ impl CfgExpander {
72 } 72 }
73 73
74 pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> Attrs { 74 pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> Attrs {
75 RawAttrs::new(owner, &self.hygiene).filter(db, self.krate) 75 RawAttrs::new(db, owner, &self.hygiene).filter(db, self.krate)
76 } 76 }
77 77
78 pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> bool { 78 pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> bool {
@@ -192,8 +192,8 @@ impl Expander {
192 self.current_file_id 192 self.current_file_id
193 } 193 }
194 194
195 fn parse_path(&mut self, path: ast::Path) -> Option<Path> { 195 fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
196 let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene); 196 let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene);
197 Path::from_src(path, &ctx) 197 Path::from_src(path, &ctx)
198 } 198 }
199 199
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 820d5c17e..9f278d35b 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -40,23 +40,25 @@ use crate::{
40 40
41use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; 41use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
42 42
43pub struct LowerCtx { 43pub struct LowerCtx<'a> {
44 pub db: &'a dyn DefDatabase,
44 hygiene: Hygiene, 45 hygiene: Hygiene,
45 file_id: Option<HirFileId>, 46 file_id: Option<HirFileId>,
46 source_ast_id_map: Option<Arc<AstIdMap>>, 47 source_ast_id_map: Option<Arc<AstIdMap>>,
47} 48}
48 49
49impl LowerCtx { 50impl<'a> LowerCtx<'a> {
50 pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self { 51 pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
51 LowerCtx { 52 LowerCtx {
53 db,
52 hygiene: Hygiene::new(db.upcast(), file_id), 54 hygiene: Hygiene::new(db.upcast(), file_id),
53 file_id: Some(file_id), 55 file_id: Some(file_id),
54 source_ast_id_map: Some(db.ast_id_map(file_id)), 56 source_ast_id_map: Some(db.ast_id_map(file_id)),
55 } 57 }
56 } 58 }
57 59
58 pub fn with_hygiene(hygiene: &Hygiene) -> Self { 60 pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
59 LowerCtx { hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None } 61 LowerCtx { db, hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None }
60 } 62 }
61 63
62 pub(crate) fn hygiene(&self) -> &Hygiene { 64 pub(crate) fn hygiene(&self) -> &Hygiene {
@@ -145,7 +147,7 @@ impl ExprCollector<'_> {
145 (self.body, self.source_map) 147 (self.body, self.source_map)
146 } 148 }
147 149
148 fn ctx(&self) -> LowerCtx { 150 fn ctx(&self) -> LowerCtx<'_> {
149 LowerCtx::new(self.db, self.expander.current_file_id) 151 LowerCtx::new(self.db, self.expander.current_file_id)
150 } 152 }
151 153
@@ -376,7 +378,7 @@ impl ExprCollector<'_> {
376 ast::Expr::PathExpr(e) => { 378 ast::Expr::PathExpr(e) => {
377 let path = e 379 let path = e
378 .path() 380 .path()
379 .and_then(|path| self.expander.parse_path(path)) 381 .and_then(|path| self.expander.parse_path(self.db, path))
380 .map(Expr::Path) 382 .map(Expr::Path)
381 .unwrap_or(Expr::Missing); 383 .unwrap_or(Expr::Missing);
382 self.alloc_expr(path, syntax_ptr) 384 self.alloc_expr(path, syntax_ptr)
@@ -408,7 +410,8 @@ impl ExprCollector<'_> {
408 self.alloc_expr(Expr::Yield { expr }, syntax_ptr) 410 self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
409 } 411 }
410 ast::Expr::RecordExpr(e) => { 412 ast::Expr::RecordExpr(e) => {
411 let path = e.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); 413 let path =
414 e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
412 let record_lit = if let Some(nfl) = e.record_expr_field_list() { 415 let record_lit = if let Some(nfl) = e.record_expr_field_list() {
413 let fields = nfl 416 let fields = nfl
414 .fields() 417 .fields()
@@ -801,7 +804,8 @@ impl ExprCollector<'_> {
801 } 804 }
802 } 805 }
803 ast::Pat::TupleStructPat(p) => { 806 ast::Pat::TupleStructPat(p) => {
804 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); 807 let path =
808 p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
805 let (args, ellipsis) = self.collect_tuple_pat(p.fields()); 809 let (args, ellipsis) = self.collect_tuple_pat(p.fields());
806 Pat::TupleStruct { path, args, ellipsis } 810 Pat::TupleStruct { path, args, ellipsis }
807 } 811 }
@@ -811,7 +815,8 @@ impl ExprCollector<'_> {
811 Pat::Ref { pat, mutability } 815 Pat::Ref { pat, mutability }
812 } 816 }
813 ast::Pat::PathPat(p) => { 817 ast::Pat::PathPat(p) => {
814 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); 818 let path =
819 p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
815 path.map(Pat::Path).unwrap_or(Pat::Missing) 820 path.map(Pat::Path).unwrap_or(Pat::Missing)
816 } 821 }
817 ast::Pat::OrPat(p) => { 822 ast::Pat::OrPat(p) => {
@@ -825,7 +830,8 @@ impl ExprCollector<'_> {
825 } 830 }
826 ast::Pat::WildcardPat(_) => Pat::Wild, 831 ast::Pat::WildcardPat(_) => Pat::Wild,
827 ast::Pat::RecordPat(p) => { 832 ast::Pat::RecordPat(p) => {
828 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); 833 let path =
834 p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
829 let args: Vec<_> = p 835 let args: Vec<_> = p
830 .record_pat_field_list() 836 .record_pat_field_list()
831 .expect("every struct should have a field list") 837 .expect("every struct should have a field list")
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index c06a37294..858e88038 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -386,7 +386,7 @@ mod tests {
386 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); 386 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
387 let ast_path = 387 let ast_path =
388 parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); 388 parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
389 let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap(); 389 let mod_path = ModPath::from_src(&db, ast_path, &Hygiene::new_unhygienic()).unwrap();
390 390
391 let def_map = module.def_map(&db); 391 let def_map = module.def_map(&db);
392 let resolved = def_map 392 let resolved = def_map
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index eaeca01bd..cad8a7479 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -18,7 +18,7 @@ use hir_expand::{
18 ast_id_map::FileAstId, 18 ast_id_map::FileAstId,
19 hygiene::Hygiene, 19 hygiene::Hygiene,
20 name::{name, AsName, Name}, 20 name::{name, AsName, Name},
21 HirFileId, InFile, 21 FragmentKind, HirFileId, InFile,
22}; 22};
23use la_arena::{Arena, Idx, RawIdx}; 23use la_arena::{Arena, Idx, RawIdx};
24use profile::Count; 24use profile::Count;
@@ -88,7 +88,7 @@ impl ItemTree {
88 let mut item_tree = match_ast! { 88 let mut item_tree = match_ast! {
89 match syntax { 89 match syntax {
90 ast::SourceFile(file) => { 90 ast::SourceFile(file) => {
91 top_attrs = Some(RawAttrs::new(&file, &hygiene)); 91 top_attrs = Some(RawAttrs::new(db, &file, &hygiene));
92 ctx.lower_module_items(&file) 92 ctx.lower_module_items(&file)
93 }, 93 },
94 ast::MacroItems(items) => { 94 ast::MacroItems(items) => {
@@ -656,6 +656,7 @@ pub struct MacroCall {
656 /// Path to the called macro. 656 /// Path to the called macro.
657 pub path: Interned<ModPath>, 657 pub path: Interned<ModPath>,
658 pub ast_id: FileAstId<ast::MacroCall>, 658 pub ast_id: FileAstId<ast::MacroCall>,
659 pub fragment: FragmentKind,
659} 660}
660 661
661#[derive(Debug, Clone, Eq, PartialEq)] 662#[derive(Debug, Clone, Eq, PartialEq)]
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 45b099cf3..fe348091d 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -31,18 +31,20 @@ where
31 } 31 }
32} 32}
33 33
34pub(super) struct Ctx { 34pub(super) struct Ctx<'a> {
35 db: &'a dyn DefDatabase,
35 tree: ItemTree, 36 tree: ItemTree,
36 hygiene: Hygiene, 37 hygiene: Hygiene,
37 file: HirFileId, 38 file: HirFileId,
38 source_ast_id_map: Arc<AstIdMap>, 39 source_ast_id_map: Arc<AstIdMap>,
39 body_ctx: crate::body::LowerCtx, 40 body_ctx: crate::body::LowerCtx<'a>,
40 forced_visibility: Option<RawVisibilityId>, 41 forced_visibility: Option<RawVisibilityId>,
41} 42}
42 43
43impl Ctx { 44impl<'a> Ctx<'a> {
44 pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self { 45 pub(super) fn new(db: &'a dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self {
45 Self { 46 Self {
47 db,
46 tree: ItemTree::default(), 48 tree: ItemTree::default(),
47 hygiene, 49 hygiene,
48 file, 50 file,
@@ -126,7 +128,7 @@ impl Ctx {
126 | ast::Item::MacroDef(_) => {} 128 | ast::Item::MacroDef(_) => {}
127 }; 129 };
128 130
129 let attrs = RawAttrs::new(item, &self.hygiene); 131 let attrs = RawAttrs::new(self.db, item, &self.hygiene);
130 let items = match item { 132 let items = match item {
131 ast::Item::Struct(ast) => self.lower_struct(ast).map(Into::into), 133 ast::Item::Struct(ast) => self.lower_struct(ast).map(Into::into),
132 ast::Item::Union(ast) => self.lower_union(ast).map(Into::into), 134 ast::Item::Union(ast) => self.lower_union(ast).map(Into::into),
@@ -256,7 +258,7 @@ impl Ctx {
256 for field in fields.fields() { 258 for field in fields.fields() {
257 if let Some(data) = self.lower_record_field(&field) { 259 if let Some(data) = self.lower_record_field(&field) {
258 let idx = self.data().fields.alloc(data); 260 let idx = self.data().fields.alloc(data);
259 self.add_attrs(idx.into(), RawAttrs::new(&field, &self.hygiene)); 261 self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, &self.hygiene));
260 } 262 }
261 } 263 }
262 let end = self.next_field_idx(); 264 let end = self.next_field_idx();
@@ -276,7 +278,7 @@ impl Ctx {
276 for (i, field) in fields.fields().enumerate() { 278 for (i, field) in fields.fields().enumerate() {
277 let data = self.lower_tuple_field(i, &field); 279 let data = self.lower_tuple_field(i, &field);
278 let idx = self.data().fields.alloc(data); 280 let idx = self.data().fields.alloc(data);
279 self.add_attrs(idx.into(), RawAttrs::new(&field, &self.hygiene)); 281 self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, &self.hygiene));
280 } 282 }
281 let end = self.next_field_idx(); 283 let end = self.next_field_idx();
282 IdRange::new(start..end) 284 IdRange::new(start..end)
@@ -321,7 +323,7 @@ impl Ctx {
321 for variant in variants.variants() { 323 for variant in variants.variants() {
322 if let Some(data) = self.lower_variant(&variant) { 324 if let Some(data) = self.lower_variant(&variant) {
323 let idx = self.data().variants.alloc(data); 325 let idx = self.data().variants.alloc(data);
324 self.add_attrs(idx.into(), RawAttrs::new(&variant, &self.hygiene)); 326 self.add_attrs(idx.into(), RawAttrs::new(self.db, &variant, &self.hygiene));
325 } 327 }
326 } 328 }
327 let end = self.next_variant_idx(); 329 let end = self.next_variant_idx();
@@ -364,7 +366,7 @@ impl Ctx {
364 }; 366 };
365 let ty = Interned::new(self_type); 367 let ty = Interned::new(self_type);
366 let idx = self.data().params.alloc(Param::Normal(ty)); 368 let idx = self.data().params.alloc(Param::Normal(ty));
367 self.add_attrs(idx.into(), RawAttrs::new(&self_param, &self.hygiene)); 369 self.add_attrs(idx.into(), RawAttrs::new(self.db, &self_param, &self.hygiene));
368 has_self_param = true; 370 has_self_param = true;
369 } 371 }
370 for param in param_list.params() { 372 for param in param_list.params() {
@@ -376,7 +378,7 @@ impl Ctx {
376 self.data().params.alloc(Param::Normal(ty)) 378 self.data().params.alloc(Param::Normal(ty))
377 } 379 }
378 }; 380 };
379 self.add_attrs(idx.into(), RawAttrs::new(&param, &self.hygiene)); 381 self.add_attrs(idx.into(), RawAttrs::new(self.db, &param, &self.hygiene));
380 } 382 }
381 } 383 }
382 let end_param = self.next_param_idx(); 384 let end_param = self.next_param_idx();
@@ -522,10 +524,11 @@ impl Ctx {
522 let is_unsafe = trait_def.unsafe_token().is_some(); 524 let is_unsafe = trait_def.unsafe_token().is_some();
523 let bounds = self.lower_type_bounds(trait_def); 525 let bounds = self.lower_type_bounds(trait_def);
524 let items = trait_def.assoc_item_list().map(|list| { 526 let items = trait_def.assoc_item_list().map(|list| {
527 let db = self.db;
525 self.with_inherited_visibility(visibility, |this| { 528 self.with_inherited_visibility(visibility, |this| {
526 list.assoc_items() 529 list.assoc_items()
527 .filter_map(|item| { 530 .filter_map(|item| {
528 let attrs = RawAttrs::new(&item, &this.hygiene); 531 let attrs = RawAttrs::new(db, &item, &this.hygiene);
529 this.collect_inner_items(item.syntax()); 532 this.collect_inner_items(item.syntax());
530 this.lower_assoc_item(&item).map(|item| { 533 this.lower_assoc_item(&item).map(|item| {
531 this.add_attrs(ModItem::from(item).into(), attrs); 534 this.add_attrs(ModItem::from(item).into(), attrs);
@@ -567,7 +570,7 @@ impl Ctx {
567 .filter_map(|item| { 570 .filter_map(|item| {
568 self.collect_inner_items(item.syntax()); 571 self.collect_inner_items(item.syntax());
569 let assoc = self.lower_assoc_item(&item)?; 572 let assoc = self.lower_assoc_item(&item)?;
570 let attrs = RawAttrs::new(&item, &self.hygiene); 573 let attrs = RawAttrs::new(self.db, &item, &self.hygiene);
571 self.add_attrs(ModItem::from(assoc).into(), attrs); 574 self.add_attrs(ModItem::from(assoc).into(), attrs);
572 Some(assoc) 575 Some(assoc)
573 }) 576 })
@@ -585,6 +588,7 @@ impl Ctx {
585 let mut imports = Vec::new(); 588 let mut imports = Vec::new();
586 let tree = self.tree.data_mut(); 589 let tree = self.tree.data_mut();
587 ModPath::expand_use_item( 590 ModPath::expand_use_item(
591 self.db,
588 InFile::new(self.file, use_item.clone()), 592 InFile::new(self.file, use_item.clone()),
589 &self.hygiene, 593 &self.hygiene,
590 |path, _use_tree, is_glob, alias| { 594 |path, _use_tree, is_glob, alias| {
@@ -618,9 +622,10 @@ impl Ctx {
618 } 622 }
619 623
620 fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> { 624 fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
621 let path = Interned::new(ModPath::from_src(m.path()?, &self.hygiene)?); 625 let path = Interned::new(ModPath::from_src(self.db, m.path()?, &self.hygiene)?);
622 let ast_id = self.source_ast_id_map.ast_id(m); 626 let ast_id = self.source_ast_id_map.ast_id(m);
623 let res = MacroCall { path, ast_id }; 627 let fragment = hir_expand::to_fragment_kind(m);
628 let res = MacroCall { path, ast_id, fragment };
624 Some(id(self.data().macro_calls.alloc(res))) 629 Some(id(self.data().macro_calls.alloc(res)))
625 } 630 }
626 631
@@ -647,7 +652,7 @@ impl Ctx {
647 list.extern_items() 652 list.extern_items()
648 .filter_map(|item| { 653 .filter_map(|item| {
649 self.collect_inner_items(item.syntax()); 654 self.collect_inner_items(item.syntax());
650 let attrs = RawAttrs::new(&item, &self.hygiene); 655 let attrs = RawAttrs::new(self.db, &item, &self.hygiene);
651 let id: ModItem = match item { 656 let id: ModItem = match item {
652 ast::ExternItem::Fn(ast) => { 657 ast::ExternItem::Fn(ast) => {
653 let func_id = self.lower_function(&ast)?; 658 let func_id = self.lower_function(&ast)?;
@@ -755,7 +760,7 @@ impl Ctx {
755 fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId { 760 fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId {
756 let vis = match self.forced_visibility { 761 let vis = match self.forced_visibility {
757 Some(vis) => return vis, 762 Some(vis) => return vis,
758 None => RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene), 763 None => RawVisibility::from_ast_with_hygiene(self.db, item.visibility(), &self.hygiene),
759 }; 764 };
760 765
761 self.data().vis.alloc(vis) 766 self.data().vis.alloc(vis)
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 25694f037..a82ea5957 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -62,13 +62,14 @@ use hir_expand::{
62 ast_id_map::FileAstId, 62 ast_id_map::FileAstId,
63 eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, 63 eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
64 hygiene::Hygiene, 64 hygiene::Hygiene,
65 AstId, AttrId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 65 AstId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
66}; 66};
67use la_arena::Idx; 67use la_arena::Idx;
68use nameres::DefMap; 68use nameres::DefMap;
69use path::ModPath; 69use path::ModPath;
70use syntax::ast; 70use syntax::ast;
71 71
72use crate::attr::AttrId;
72use crate::builtin_type::BuiltinType; 73use crate::builtin_type::BuiltinType;
73use item_tree::{ 74use item_tree::{
74 Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait, 75 Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
@@ -652,9 +653,10 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
652 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 653 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
653 mut error_sink: &mut dyn FnMut(mbe::ExpandError), 654 mut error_sink: &mut dyn FnMut(mbe::ExpandError),
654 ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { 655 ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
656 let fragment = hir_expand::to_fragment_kind(self.value);
655 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); 657 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
656 let h = Hygiene::new(db.upcast(), self.file_id); 658 let h = Hygiene::new(db.upcast(), self.file_id);
657 let path = self.value.path().and_then(|path| path::ModPath::from_src(path, &h)); 659 let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
658 660
659 let path = match error_sink 661 let path = match error_sink
660 .option(path, || mbe::ExpandError::Other("malformed macro invocation".into())) 662 .option(path, || mbe::ExpandError::Other("malformed macro invocation".into()))
@@ -667,6 +669,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
667 669
668 macro_call_as_call_id( 670 macro_call_as_call_id(
669 &AstIdWithPath::new(ast_id.file_id, ast_id.value, path), 671 &AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
672 fragment,
670 db, 673 db,
671 krate, 674 krate,
672 resolver, 675 resolver,
@@ -695,6 +698,7 @@ pub struct UnresolvedMacro {
695 698
696fn macro_call_as_call_id( 699fn macro_call_as_call_id(
697 call: &AstIdWithPath<ast::MacroCall>, 700 call: &AstIdWithPath<ast::MacroCall>,
701 fragment: FragmentKind,
698 db: &dyn db::DefDatabase, 702 db: &dyn db::DefDatabase,
699 krate: CrateId, 703 krate: CrateId,
700 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 704 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
@@ -712,13 +716,17 @@ fn macro_call_as_call_id(
712 krate, 716 krate,
713 macro_call, 717 macro_call,
714 def, 718 def,
715 &|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?), 719 &|path: ast::Path| resolver(path::ModPath::from_src(db, path, &hygiene)?),
716 error_sink, 720 error_sink,
717 ) 721 )
718 .map(MacroCallId::from) 722 .map(MacroCallId::from)
719 } else { 723 } else {
720 Ok(def 724 Ok(def
721 .as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike { ast_id: call.ast_id }) 725 .as_lazy_macro(
726 db.upcast(),
727 krate,
728 MacroCallKind::FnLike { ast_id: call.ast_id, fragment },
729 )
722 .into()) 730 .into())
723 }; 731 };
724 Ok(res) 732 Ok(res)
@@ -745,7 +753,7 @@ fn derive_macro_as_call_id(
745 MacroCallKind::Derive { 753 MacroCallKind::Derive {
746 ast_id: item_attr.ast_id, 754 ast_id: item_attr.ast_id,
747 derive_name: last_segment.to_string(), 755 derive_name: last_segment.to_string(),
748 derive_attr, 756 derive_attr_index: derive_attr.ast_index,
749 }, 757 },
750 ) 758 )
751 .into(); 759 .into();
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index ba027c44a..249af6fc8 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -599,6 +599,7 @@ mod diagnostics {
599 let mut cur = 0; 599 let mut cur = 0;
600 let mut tree = None; 600 let mut tree = None;
601 ModPath::expand_use_item( 601 ModPath::expand_use_item(
602 db,
602 InFile::new(ast.file_id, use_item), 603 InFile::new(ast.file_id, use_item),
603 &hygiene, 604 &hygiene,
604 |_mod_path, use_tree, _is_glob, _alias| { 605 |_mod_path, use_tree, _is_glob, _alias| {
@@ -628,7 +629,7 @@ mod diagnostics {
628 DiagnosticKind::UnresolvedProcMacro { ast } => { 629 DiagnosticKind::UnresolvedProcMacro { ast } => {
629 let mut precise_location = None; 630 let mut precise_location = None;
630 let (file, ast, name) = match ast { 631 let (file, ast, name) = match ast {
631 MacroCallKind::FnLike { ast_id } => { 632 MacroCallKind::FnLike { ast_id, .. } => {
632 let node = ast_id.to_node(db.upcast()); 633 let node = ast_id.to_node(db.upcast());
633 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) 634 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None)
634 } 635 }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 05ceb1efb..adfb78c94 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -13,14 +13,14 @@ use hir_expand::{
13 builtin_macro::find_builtin_macro, 13 builtin_macro::find_builtin_macro,
14 name::{AsName, Name}, 14 name::{AsName, Name},
15 proc_macro::ProcMacroExpander, 15 proc_macro::ProcMacroExpander,
16 AttrId, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 16 FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
17}; 17};
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;
21 21
22use crate::{ 22use crate::{
23 attr::Attrs, 23 attr::{AttrId, Attrs},
24 db::DefDatabase, 24 db::DefDatabase,
25 derive_macro_as_call_id, 25 derive_macro_as_call_id,
26 intern::Interned, 26 intern::Interned,
@@ -215,7 +215,7 @@ struct MacroDirective {
215 215
216#[derive(Clone, Debug, Eq, PartialEq)] 216#[derive(Clone, Debug, Eq, PartialEq)]
217enum MacroDirectiveKind { 217enum MacroDirectiveKind {
218 FnLike { ast_id: AstIdWithPath<ast::MacroCall> }, 218 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind },
219 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, 219 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
220} 220}
221 221
@@ -807,9 +807,10 @@ impl DefCollector<'_> {
807 let mut res = ReachedFixedPoint::Yes; 807 let mut res = ReachedFixedPoint::Yes;
808 macros.retain(|directive| { 808 macros.retain(|directive| {
809 match &directive.kind { 809 match &directive.kind {
810 MacroDirectiveKind::FnLike { ast_id } => { 810 MacroDirectiveKind::FnLike { ast_id, fragment } => {
811 match macro_call_as_call_id( 811 match macro_call_as_call_id(
812 ast_id, 812 ast_id,
813 *fragment,
813 self.db, 814 self.db,
814 self.def_map.krate, 815 self.def_map.krate,
815 |path| { 816 |path| {
@@ -926,8 +927,9 @@ impl DefCollector<'_> {
926 927
927 for directive in &self.unexpanded_macros { 928 for directive in &self.unexpanded_macros {
928 match &directive.kind { 929 match &directive.kind {
929 MacroDirectiveKind::FnLike { ast_id, .. } => match macro_call_as_call_id( 930 MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id(
930 ast_id, 931 ast_id,
932 *fragment,
931 self.db, 933 self.db,
932 self.def_map.krate, 934 self.def_map.krate,
933 |path| { 935 |path| {
@@ -1496,6 +1498,7 @@ impl ModCollector<'_, '_> {
1496 let mut error = None; 1498 let mut error = None;
1497 match macro_call_as_call_id( 1499 match macro_call_as_call_id(
1498 &ast_id, 1500 &ast_id,
1501 mac.fragment,
1499 self.def_collector.db, 1502 self.def_collector.db,
1500 self.def_collector.def_map.krate, 1503 self.def_collector.def_map.krate,
1501 |path| { 1504 |path| {
@@ -1524,9 +1527,14 @@ impl ModCollector<'_, '_> {
1524 } 1527 }
1525 Ok(Err(_)) => { 1528 Ok(Err(_)) => {
1526 // Built-in macro failed eager expansion. 1529 // Built-in macro failed eager expansion.
1530
1531 // FIXME: don't parse the file here
1532 let fragment = hir_expand::to_fragment_kind(
1533 &ast_id.ast_id.to_node(self.def_collector.db.upcast()),
1534 );
1527 self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( 1535 self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
1528 self.module_id, 1536 self.module_id,
1529 MacroCallKind::FnLike { ast_id: ast_id.ast_id }, 1537 MacroCallKind::FnLike { ast_id: ast_id.ast_id, fragment },
1530 error.unwrap().to_string(), 1538 error.unwrap().to_string(),
1531 )); 1539 ));
1532 return; 1540 return;
@@ -1543,7 +1551,7 @@ impl ModCollector<'_, '_> {
1543 self.def_collector.unexpanded_macros.push(MacroDirective { 1551 self.def_collector.unexpanded_macros.push(MacroDirective {
1544 module_id: self.module_id, 1552 module_id: self.module_id,
1545 depth: self.macro_depth + 1, 1553 depth: self.macro_depth + 1,
1546 kind: MacroDirectiveKind::FnLike { ast_id }, 1554 kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment },
1547 }); 1555 });
1548 } 1556 }
1549 1557
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs
index 509e1bbbc..d884a6eb4 100644
--- a/crates/hir_def/src/nameres/tests/incremental.rs
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -105,3 +105,61 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
105 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) 105 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
106 } 106 }
107} 107}
108
109#[test]
110fn typing_inside_a_function_should_not_invalidate_expansions() {
111 let (mut db, pos) = TestDB::with_position(
112 r#"
113//- /lib.rs
114macro_rules! m {
115 ($ident:ident) => {
116 fn $ident() { };
117 }
118}
119mod foo;
120
121//- /foo/mod.rs
122pub mod bar;
123
124//- /foo/bar.rs
125m!(X);
126fn quux() { 1$0 }
127m!(Y);
128m!(Z);
129"#,
130 );
131 let krate = db.test_crate();
132 {
133 let events = db.log_executed(|| {
134 let crate_def_map = db.crate_def_map(krate);
135 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
136 assert_eq!(module_data.scope.resolutions().count(), 4);
137 });
138 let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
139 assert_eq!(n_recalculated_item_trees, 6);
140 let n_reparsed_macros =
141 events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
142 assert_eq!(n_reparsed_macros, 3);
143 }
144
145 let new_text = r#"
146m!(X);
147fn quux() { 92 }
148m!(Y);
149m!(Z);
150"#;
151 db.set_file_text(pos.file_id, Arc::new(new_text.to_string()));
152
153 {
154 let events = db.log_executed(|| {
155 let crate_def_map = db.crate_def_map(krate);
156 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
157 assert_eq!(module_data.scope.resolutions().count(), 4);
158 });
159 let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
160 assert_eq!(n_recalculated_item_trees, 1);
161 let n_reparsed_macros =
162 events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
163 assert_eq!(n_reparsed_macros, 0);
164 }
165}
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index 509f77850..a43441b1c 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -7,7 +7,7 @@ use std::{
7 sync::Arc, 7 sync::Arc,
8}; 8};
9 9
10use crate::{body::LowerCtx, intern::Interned, type_ref::LifetimeRef}; 10use crate::{body::LowerCtx, db::DefDatabase, intern::Interned, type_ref::LifetimeRef};
11use base_db::CrateId; 11use base_db::CrateId;
12use hir_expand::{ 12use hir_expand::{
13 hygiene::Hygiene, 13 hygiene::Hygiene,
@@ -47,8 +47,8 @@ pub enum ImportAlias {
47} 47}
48 48
49impl ModPath { 49impl ModPath {
50 pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { 50 pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
51 let ctx = LowerCtx::with_hygiene(hygiene); 51 let ctx = LowerCtx::with_hygiene(db, hygiene);
52 lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone()) 52 lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone())
53 } 53 }
54 54
@@ -64,12 +64,13 @@ impl ModPath {
64 64
65 /// Calls `cb` with all paths, represented by this use item. 65 /// Calls `cb` with all paths, represented by this use item.
66 pub(crate) fn expand_use_item( 66 pub(crate) fn expand_use_item(
67 db: &dyn DefDatabase,
67 item_src: InFile<ast::Use>, 68 item_src: InFile<ast::Use>,
68 hygiene: &Hygiene, 69 hygiene: &Hygiene,
69 mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>), 70 mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>),
70 ) { 71 ) {
71 if let Some(tree) = item_src.value.use_tree() { 72 if let Some(tree) = item_src.value.use_tree() {
72 lower::lower_use_tree(None, tree, hygiene, &mut cb); 73 lower::lower_use_tree(db, None, tree, hygiene, &mut cb);
73 } 74 }
74 } 75 }
75 76
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 1df6db525..a873325b2 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -36,7 +36,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
36 match segment.kind()? { 36 match segment.kind()? {
37 ast::PathSegmentKind::Name(name_ref) => { 37 ast::PathSegmentKind::Name(name_ref) => {
38 // FIXME: this should just return name 38 // FIXME: this should just return name
39 match hygiene.name_ref_to_name(name_ref) { 39 match hygiene.name_ref_to_name(ctx.db.upcast(), name_ref) {
40 Either::Left(name) => { 40 Either::Left(name) => {
41 let args = segment 41 let args = segment
42 .generic_arg_list() 42 .generic_arg_list()
@@ -133,7 +133,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
133 // We follow what it did anyway :) 133 // We follow what it did anyway :)
134 if segments.len() == 1 && kind == PathKind::Plain { 134 if segments.len() == 1 && kind == PathKind::Plain {
135 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { 135 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
136 if let Some(crate_id) = hygiene.local_inner_macros(path) { 136 if let Some(crate_id) = hygiene.local_inner_macros(ctx.db.upcast(), path) {
137 kind = PathKind::DollarCrate(crate_id); 137 kind = PathKind::DollarCrate(crate_id);
138 } 138 }
139 } 139 }
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs
index e2965b033..ee80e3df3 100644
--- a/crates/hir_def/src/path/lower/lower_use.rs
+++ b/crates/hir_def/src/path/lower/lower_use.rs
@@ -7,9 +7,13 @@ use 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};
9 9
10use crate::path::{ImportAlias, ModPath, PathKind}; 10use crate::{
11 db::DefDatabase,
12 path::{ImportAlias, ModPath, PathKind},
13};
11 14
12pub(crate) fn lower_use_tree( 15pub(crate) fn lower_use_tree(
16 db: &dyn DefDatabase,
13 prefix: Option<ModPath>, 17 prefix: Option<ModPath>,
14 tree: ast::UseTree, 18 tree: ast::UseTree,
15 hygiene: &Hygiene, 19 hygiene: &Hygiene,
@@ -21,13 +25,13 @@ pub(crate) fn lower_use_tree(
21 None => prefix, 25 None => prefix,
22 // E.g. `use something::{inner}` (prefix is `None`, path is `something`) 26 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
23 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) 27 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
24 Some(path) => match convert_path(prefix, path, hygiene) { 28 Some(path) => match convert_path(db, prefix, path, hygiene) {
25 Some(it) => Some(it), 29 Some(it) => Some(it),
26 None => return, // FIXME: report errors somewhere 30 None => return, // FIXME: report errors somewhere
27 }, 31 },
28 }; 32 };
29 for child_tree in use_tree_list.use_trees() { 33 for child_tree in use_tree_list.use_trees() {
30 lower_use_tree(prefix.clone(), child_tree, hygiene, cb); 34 lower_use_tree(db, prefix.clone(), child_tree, hygiene, cb);
31 } 35 }
32 } else { 36 } else {
33 let alias = tree.rename().map(|a| { 37 let alias = tree.rename().map(|a| {
@@ -47,7 +51,7 @@ pub(crate) fn lower_use_tree(
47 } 51 }
48 } 52 }
49 } 53 }
50 if let Some(path) = convert_path(prefix, ast_path, hygiene) { 54 if let Some(path) = convert_path(db, prefix, ast_path, hygiene) {
51 cb(path, &tree, is_glob, alias) 55 cb(path, &tree, is_glob, alias)
52 } 56 }
53 // FIXME: report errors somewhere 57 // FIXME: report errors somewhere
@@ -61,9 +65,14 @@ pub(crate) fn lower_use_tree(
61 } 65 }
62} 66}
63 67
64fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { 68fn convert_path(
69 db: &dyn DefDatabase,
70 prefix: Option<ModPath>,
71 path: ast::Path,
72 hygiene: &Hygiene,
73) -> Option<ModPath> {
65 let prefix = if let Some(qual) = path.qualifier() { 74 let prefix = if let Some(qual) = path.qualifier() {
66 Some(convert_path(prefix, qual, hygiene)?) 75 Some(convert_path(db, prefix, qual, hygiene)?)
67 } else { 76 } else {
68 prefix 77 prefix
69 }; 78 };
@@ -71,7 +80,7 @@ fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) ->
71 let segment = path.segment()?; 80 let segment = path.segment()?;
72 let res = match segment.kind()? { 81 let res = match segment.kind()? {
73 ast::PathSegmentKind::Name(name_ref) => { 82 ast::PathSegmentKind::Name(name_ref) => {
74 match hygiene.name_ref_to_name(name_ref) { 83 match hygiene.name_ref_to_name(db.upcast(), name_ref) {
75 Either::Left(name) => { 84 Either::Left(name) => {
76 // no type args in use 85 // no type args in use
77 let mut res = prefix.unwrap_or_else(|| { 86 let mut res = prefix.unwrap_or_else(|| {
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
index d4b7c9970..83500f54e 100644
--- a/crates/hir_def/src/visibility.rs
+++ b/crates/hir_def/src/visibility.rs
@@ -33,17 +33,19 @@ impl RawVisibility {
33 db: &dyn DefDatabase, 33 db: &dyn DefDatabase,
34 node: InFile<Option<ast::Visibility>>, 34 node: InFile<Option<ast::Visibility>>,
35 ) -> RawVisibility { 35 ) -> RawVisibility {
36 Self::from_ast_with_hygiene(node.value, &Hygiene::new(db.upcast(), node.file_id)) 36 Self::from_ast_with_hygiene(db, node.value, &Hygiene::new(db.upcast(), node.file_id))
37 } 37 }
38 38
39 pub(crate) fn from_ast_with_hygiene( 39 pub(crate) fn from_ast_with_hygiene(
40 db: &dyn DefDatabase,
40 node: Option<ast::Visibility>, 41 node: Option<ast::Visibility>,
41 hygiene: &Hygiene, 42 hygiene: &Hygiene,
42 ) -> RawVisibility { 43 ) -> RawVisibility {
43 Self::from_ast_with_hygiene_and_default(node, RawVisibility::private(), hygiene) 44 Self::from_ast_with_hygiene_and_default(db, node, RawVisibility::private(), hygiene)
44 } 45 }
45 46
46 pub(crate) fn from_ast_with_hygiene_and_default( 47 pub(crate) fn from_ast_with_hygiene_and_default(
48 db: &dyn DefDatabase,
47 node: Option<ast::Visibility>, 49 node: Option<ast::Visibility>,
48 default: RawVisibility, 50 default: RawVisibility,
49 hygiene: &Hygiene, 51 hygiene: &Hygiene,
@@ -54,7 +56,7 @@ impl RawVisibility {
54 }; 56 };
55 match node.kind() { 57 match node.kind() {
56 ast::VisibilityKind::In(path) => { 58 ast::VisibilityKind::In(path) => {
57 let path = ModPath::from_src(path, hygiene); 59 let path = ModPath::from_src(db, path, hygiene);
58 let path = match path { 60 let path = match path {
59 None => return RawVisibility::private(), 61 None => return RawVisibility::private(),
60 Some(path) => path, 62 Some(path) => path,
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index 537c03028..b6a6d602f 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -269,7 +269,7 @@ mod tests {
269 use expect_test::{expect, Expect}; 269 use expect_test::{expect, Expect};
270 use name::AsName; 270 use name::AsName;
271 271
272 use crate::{test_db::TestDB, AstId, AttrId, MacroCallId, MacroCallKind, MacroCallLoc}; 272 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
273 273
274 use super::*; 274 use super::*;
275 275
@@ -320,7 +320,7 @@ $0
320 kind: MacroCallKind::Derive { 320 kind: MacroCallKind::Derive {
321 ast_id, 321 ast_id,
322 derive_name: name.to_string(), 322 derive_name: name.to_string(),
323 derive_attr: AttrId(0), 323 derive_attr_index: 0,
324 }, 324 },
325 }; 325 };
326 326
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 179de61f9..af9802144 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -578,6 +578,7 @@ mod tests {
578 krate, 578 krate,
579 kind: MacroCallKind::FnLike { 579 kind: MacroCallKind::FnLike {
580 ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(&macro_call)), 580 ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(&macro_call)),
581 fragment: FragmentKind::Expr,
581 }, 582 },
582 }; 583 };
583 584
@@ -788,9 +789,9 @@ mod tests {
788 r##" 789 r##"
789 #[rustc_builtin_macro] 790 #[rustc_builtin_macro]
790 macro_rules! concat {} 791 macro_rules! concat {}
791 concat!("foo", "r", 0, r#"bar"#, false); 792 concat!("foo", "r", 0, r#"bar"#, "\n", false);
792 "##, 793 "##,
793 expect![[r#""foor0barfalse""#]], 794 expect![[r#""foor0bar\nfalse""#]],
794 ); 795 );
795 } 796 }
796} 797}
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 1e4b0cc19..9fa419fcf 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -3,20 +3,18 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use base_db::{salsa, SourceDatabase}; 5use base_db::{salsa, SourceDatabase};
6use mbe::{ExpandError, ExpandResult, MacroDef, MacroRules}; 6use mbe::{ExpandError, ExpandResult};
7use parser::FragmentKind; 7use parser::FragmentKind;
8use syntax::{ 8use syntax::{
9 algo::diff, 9 algo::diff,
10 ast::{MacroStmts, NameOwner}, 10 ast::{self, NameOwner},
11 AstNode, GreenNode, Parse, 11 AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken,
12 SyntaxKind::*,
13 SyntaxNode,
14}; 12};
15 13
16use crate::{ 14use crate::{
17 ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander, 15 ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander,
18 EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, 16 BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId,
19 MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, 17 MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
20}; 18};
21 19
22/// Total limit on the number of tokens produced by any macro invocation. 20/// Total limit on the number of tokens produced by any macro invocation.
@@ -27,23 +25,28 @@ const TOKEN_LIMIT: usize = 524288;
27 25
28#[derive(Debug, Clone, Eq, PartialEq)] 26#[derive(Debug, Clone, Eq, PartialEq)]
29pub enum TokenExpander { 27pub enum TokenExpander {
30 MacroRules(mbe::MacroRules), 28 /// Old-style `macro_rules`.
31 MacroDef(mbe::MacroDef), 29 MacroRules { mac: mbe::MacroRules, def_site_token_map: mbe::TokenMap },
30 /// AKA macros 2.0.
31 MacroDef { mac: mbe::MacroDef, def_site_token_map: mbe::TokenMap },
32 /// Stuff like `line!` and `file!`.
32 Builtin(BuiltinFnLikeExpander), 33 Builtin(BuiltinFnLikeExpander),
34 /// `derive(Copy)` and such.
33 BuiltinDerive(BuiltinDeriveExpander), 35 BuiltinDerive(BuiltinDeriveExpander),
36 /// The thing we love the most here in rust-analyzer -- procedural macros.
34 ProcMacro(ProcMacroExpander), 37 ProcMacro(ProcMacroExpander),
35} 38}
36 39
37impl TokenExpander { 40impl TokenExpander {
38 pub fn expand( 41 fn expand(
39 &self, 42 &self,
40 db: &dyn AstDatabase, 43 db: &dyn AstDatabase,
41 id: LazyMacroId, 44 id: LazyMacroId,
42 tt: &tt::Subtree, 45 tt: &tt::Subtree,
43 ) -> mbe::ExpandResult<tt::Subtree> { 46 ) -> mbe::ExpandResult<tt::Subtree> {
44 match self { 47 match self {
45 TokenExpander::MacroRules(it) => it.expand(tt), 48 TokenExpander::MacroRules { mac, .. } => mac.expand(tt),
46 TokenExpander::MacroDef(it) => it.expand(tt), 49 TokenExpander::MacroDef { mac, .. } => mac.expand(tt),
47 TokenExpander::Builtin(it) => it.expand(db, id, tt), 50 TokenExpander::Builtin(it) => it.expand(db, id, tt),
48 // FIXME switch these to ExpandResult as well 51 // FIXME switch these to ExpandResult as well
49 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), 52 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
@@ -56,23 +59,23 @@ impl TokenExpander {
56 } 59 }
57 } 60 }
58 61
59 pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { 62 pub(crate) fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
60 match self { 63 match self {
61 TokenExpander::MacroRules(it) => it.map_id_down(id), 64 TokenExpander::MacroRules { mac, .. } => mac.map_id_down(id),
62 TokenExpander::MacroDef(it) => it.map_id_down(id), 65 TokenExpander::MacroDef { mac, .. } => mac.map_id_down(id),
63 TokenExpander::Builtin(..) => id, 66 TokenExpander::Builtin(..)
64 TokenExpander::BuiltinDerive(..) => id, 67 | TokenExpander::BuiltinDerive(..)
65 TokenExpander::ProcMacro(..) => id, 68 | TokenExpander::ProcMacro(..) => id,
66 } 69 }
67 } 70 }
68 71
69 pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { 72 pub(crate) fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
70 match self { 73 match self {
71 TokenExpander::MacroRules(it) => it.map_id_up(id), 74 TokenExpander::MacroRules { mac, .. } => mac.map_id_up(id),
72 TokenExpander::MacroDef(it) => it.map_id_up(id), 75 TokenExpander::MacroDef { mac, .. } => mac.map_id_up(id),
73 TokenExpander::Builtin(..) => (id, mbe::Origin::Call), 76 TokenExpander::Builtin(..)
74 TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), 77 | TokenExpander::BuiltinDerive(..)
75 TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), 78 | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
76 } 79 }
77 } 80 }
78} 81}
@@ -82,28 +85,48 @@ impl TokenExpander {
82pub trait AstDatabase: SourceDatabase { 85pub trait AstDatabase: SourceDatabase {
83 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; 86 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
84 87
88 /// Main public API -- parsis a hir file, not caring whether it's a real
89 /// file or a macro expansion.
85 #[salsa::transparent] 90 #[salsa::transparent]
86 fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; 91 fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
87 92 /// Implementation for the macro case.
88 #[salsa::interned]
89 fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId;
90 fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>;
91 #[salsa::transparent]
92 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
93 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
94 fn parse_macro_expansion( 93 fn parse_macro_expansion(
95 &self, 94 &self,
96 macro_file: MacroFile, 95 macro_file: MacroFile,
97 ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; 96 ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
98 fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
99
100 /// Firewall query that returns the error from the `macro_expand` query.
101 fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
102 97
98 /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
99 /// reason why we use salsa at all.
100 ///
101 /// We encode macro definitions into ids of macro calls, this what allows us
102 /// to be incremental.
103 #[salsa::interned]
104 fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId;
105 /// Certain built-in macros are eager (`format!(concat!("file: ", file!(), "{}"")), 92`).
106 /// For them, we actually want to encode the whole token tree as an argument.
103 #[salsa::interned] 107 #[salsa::interned]
104 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; 108 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
105 109
110 /// Lowers syntactic macro call to a token tree representation.
111 #[salsa::transparent]
112 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
113 /// Extracts syntax node, corresponding to a macro call. That's a firewall
114 /// query, only typing in the macro call itself changes the returned
115 /// subtree.
116 fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>;
117 /// Gets the expander for this macro. This compiles declarative macros, and
118 /// just fetches procedural ones.
119 fn macro_def(&self, id: MacroDefId) -> Option<Arc<TokenExpander>>;
120
121 /// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory)
122 fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
123 /// Special case of the previous query for procedural macros. We can't LRU
124 /// proc macros, since they are not deterministic in general, and
125 /// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng
126 /// heroically debugged this once!
106 fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; 127 fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>;
128 /// Firewall query that returns the error from the `macro_expand` query.
129 fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
107 130
108 fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>; 131 fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>;
109} 132}
@@ -115,36 +138,160 @@ pub trait AstDatabase: SourceDatabase {
115pub fn expand_hypothetical( 138pub fn expand_hypothetical(
116 db: &dyn AstDatabase, 139 db: &dyn AstDatabase,
117 actual_macro_call: MacroCallId, 140 actual_macro_call: MacroCallId,
118 hypothetical_args: &syntax::ast::TokenTree, 141 hypothetical_args: &ast::TokenTree,
119 token_to_map: syntax::SyntaxToken, 142 token_to_map: SyntaxToken,
120) -> Option<(SyntaxNode, syntax::SyntaxToken)> { 143) -> Option<(SyntaxNode, SyntaxToken)> {
121 let macro_file = MacroFile { macro_call_id: actual_macro_call };
122 let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax()); 144 let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax());
123 let range = 145 let range =
124 token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?; 146 token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?;
125 let token_id = tmap_1.token_by_range(range)?; 147 let token_id = tmap_1.token_by_range(range)?;
126 let macro_def = expander(db, actual_macro_call)?; 148
149 let lazy_id = match actual_macro_call {
150 MacroCallId::LazyMacro(id) => id,
151 MacroCallId::EagerMacro(_) => return None,
152 };
153
154 let macro_def = {
155 let loc = db.lookup_intern_macro(lazy_id);
156 db.macro_def(loc.def)?
157 };
158
159 let hypothetical_expansion = macro_def.expand(db, lazy_id, &tt);
160
161 let fragment_kind = macro_fragment_kind(db, actual_macro_call);
162
127 let (node, tmap_2) = 163 let (node, tmap_2) =
128 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?; 164 mbe::token_tree_to_syntax_node(&hypothetical_expansion.value, fragment_kind).ok()?;
129 let token_id = macro_def.0.map_id_down(token_id); 165
166 let token_id = macro_def.map_id_down(token_id);
130 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; 167 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?;
131 let token = node.syntax_node().covering_element(range).into_token()?; 168 let token = node.syntax_node().covering_element(range).into_token()?;
132 Some((node.syntax_node(), token)) 169 Some((node.syntax_node(), token))
133} 170}
134 171
135fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { 172fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
136 let map = 173 let map = db.parse_or_expand(file_id).map(|it| AstIdMap::from_source(&it)).unwrap_or_default();
137 db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it));
138 Arc::new(map) 174 Arc::new(map)
139} 175}
140 176
141fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { 177fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
178 match file_id.0 {
179 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
180 HirFileIdRepr::MacroFile(macro_file) => {
181 db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node())
182 }
183 }
184}
185
186fn parse_macro_expansion(
187 db: &dyn AstDatabase,
188 macro_file: MacroFile,
189) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
190 let _p = profile::span("parse_macro_expansion");
191 let result = db.macro_expand(macro_file.macro_call_id);
192
193 if let Some(err) = &result.err {
194 // Note:
195 // The final goal we would like to make all parse_macro success,
196 // such that the following log will not call anyway.
197 match macro_file.macro_call_id {
198 MacroCallId::LazyMacro(id) => {
199 let loc: MacroCallLoc = db.lookup_intern_macro(id);
200 let node = loc.kind.node(db);
201
202 // collect parent information for warning log
203 let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
204 it.file_id.call_node(db)
205 })
206 .map(|n| format!("{:#}", n.value))
207 .collect::<Vec<_>>()
208 .join("\n");
209
210 log::warn!(
211 "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
212 err,
213 node.value,
214 parents
215 );
216 }
217 _ => {
218 log::warn!("fail on macro_parse: (reason: {:?})", err);
219 }
220 }
221 }
222 let tt = match result.value {
223 Some(tt) => tt,
224 None => return ExpandResult { value: None, err: result.err },
225 };
226
227 let fragment_kind = macro_fragment_kind(db, macro_file.macro_call_id);
228
229 log::debug!("expanded = {}", tt.as_debug_string());
230 log::debug!("kind = {:?}", fragment_kind);
231
232 let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) {
233 Ok(it) => it,
234 Err(err) => {
235 log::debug!(
236 "failed to parse expanstion to {:?} = {}",
237 fragment_kind,
238 tt.as_debug_string()
239 );
240 return ExpandResult::only_err(err);
241 }
242 };
243
244 match result.err {
245 Some(err) => {
246 // Safety check for recursive identity macro.
247 let node = parse.syntax_node();
248 let file: HirFileId = macro_file.into();
249 let call_node = match file.call_node(db) {
250 Some(it) => it,
251 None => {
252 return ExpandResult::only_err(err);
253 }
254 };
255 if is_self_replicating(&node, &call_node.value) {
256 return ExpandResult::only_err(err);
257 } else {
258 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) }
259 }
260 }
261 None => {
262 log::debug!("parse = {:?}", parse.syntax_node().kind());
263 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None }
264 }
265 }
266}
267
268fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
269 let arg = db.macro_arg_text(id)?;
270 let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg));
271 Some(Arc::new((tt, tmap)))
272}
273
274fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
275 let id = match id {
276 MacroCallId::LazyMacro(id) => id,
277 MacroCallId::EagerMacro(_id) => {
278 // FIXME: support macro_arg for eager macro
279 return None;
280 }
281 };
282 let loc = db.lookup_intern_macro(id);
283 let arg = loc.kind.arg(db)?;
284 let arg = process_macro_input(db, arg, id);
285 Some(arg.green().into())
286}
287
288fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>> {
142 match id.kind { 289 match id.kind {
143 MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) { 290 MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) {
144 syntax::ast::Macro::MacroRules(macro_rules) => { 291 ast::Macro::MacroRules(macro_rules) => {
145 let arg = macro_rules.token_tree()?; 292 let arg = macro_rules.token_tree()?;
146 let (tt, tmap) = mbe::ast_to_token_tree(&arg); 293 let (tt, def_site_token_map) = mbe::ast_to_token_tree(&arg);
147 let rules = match MacroRules::parse(&tt) { 294 let mac = match mbe::MacroRules::parse(&tt) {
148 Ok(it) => it, 295 Ok(it) => it,
149 Err(err) => { 296 Err(err) => {
150 let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); 297 let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default();
@@ -152,12 +299,12 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander,
152 return None; 299 return None;
153 } 300 }
154 }; 301 };
155 Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) 302 Some(Arc::new(TokenExpander::MacroRules { mac, def_site_token_map }))
156 } 303 }
157 syntax::ast::Macro::MacroDef(macro_def) => { 304 ast::Macro::MacroDef(macro_def) => {
158 let arg = macro_def.body()?; 305 let arg = macro_def.body()?;
159 let (tt, tmap) = mbe::ast_to_token_tree(&arg); 306 let (tt, def_site_token_map) = mbe::ast_to_token_tree(&arg);
160 let rules = match MacroDef::parse(&tt) { 307 let mac = match mbe::MacroDef::parse(&tt) {
161 Ok(it) => it, 308 Ok(it) => it,
162 Err(err) => { 309 Err(err) => {
163 let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default(); 310 let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default();
@@ -165,41 +312,18 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander,
165 return None; 312 return None;
166 } 313 }
167 }; 314 };
168 Some(Arc::new((TokenExpander::MacroDef(rules), tmap))) 315 Some(Arc::new(TokenExpander::MacroDef { mac, def_site_token_map }))
169 } 316 }
170 }, 317 },
171 MacroDefKind::BuiltIn(expander, _) => { 318 MacroDefKind::BuiltIn(expander, _) => Some(Arc::new(TokenExpander::Builtin(expander))),
172 Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default())))
173 }
174 MacroDefKind::BuiltInDerive(expander, _) => { 319 MacroDefKind::BuiltInDerive(expander, _) => {
175 Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) 320 Some(Arc::new(TokenExpander::BuiltinDerive(expander)))
176 } 321 }
177 MacroDefKind::BuiltInEager(..) => None, 322 MacroDefKind::BuiltInEager(..) => None,
178 MacroDefKind::ProcMacro(expander, ..) => { 323 MacroDefKind::ProcMacro(expander, ..) => Some(Arc::new(TokenExpander::ProcMacro(expander))),
179 Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default())))
180 }
181 } 324 }
182} 325}
183 326
184fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
185 let id = match id {
186 MacroCallId::LazyMacro(id) => id,
187 MacroCallId::EagerMacro(_id) => {
188 // FIXME: support macro_arg for eager macro
189 return None;
190 }
191 };
192 let loc = db.lookup_intern_macro(id);
193 let arg = loc.kind.arg(db)?;
194 Some(arg.green())
195}
196
197fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
198 let arg = db.macro_arg_text(id)?;
199 let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg));
200 Some(Arc::new((tt, tmap)))
201}
202
203fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>> { 327fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>> {
204 macro_expand_with_arg(db, id, None) 328 macro_expand_with_arg(db, id, None)
205} 329}
@@ -208,19 +332,6 @@ fn macro_expand_error(db: &dyn AstDatabase, macro_call: MacroCallId) -> Option<E
208 db.macro_expand(macro_call).err 332 db.macro_expand(macro_call).err
209} 333}
210 334
211fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
212 let lazy_id = match id {
213 MacroCallId::LazyMacro(id) => id,
214 MacroCallId::EagerMacro(_id) => {
215 return None;
216 }
217 };
218
219 let loc = db.lookup_intern_macro(lazy_id);
220 let macro_rules = db.macro_def(loc.def)?;
221 Some(macro_rules)
222}
223
224fn macro_expand_with_arg( 335fn macro_expand_with_arg(
225 db: &dyn AstDatabase, 336 db: &dyn AstDatabase,
226 id: MacroCallId, 337 id: MacroCallId,
@@ -254,7 +365,7 @@ fn macro_expand_with_arg(
254 Some(it) => it, 365 Some(it) => it,
255 None => return ExpandResult::str_err("Fail to find macro definition".into()), 366 None => return ExpandResult::str_err("Fail to find macro definition".into()),
256 }; 367 };
257 let ExpandResult { value: tt, err } = macro_rules.0.expand(db, lazy_id, &macro_arg.0); 368 let ExpandResult { value: tt, err } = macro_rules.expand(db, lazy_id, &macro_arg.0);
258 // Set a hard limit for the expanded tt 369 // Set a hard limit for the expanded tt
259 let count = tt.count(); 370 let count = tt.count();
260 if count > TOKEN_LIMIT { 371 if count > TOKEN_LIMIT {
@@ -294,116 +405,11 @@ fn expand_proc_macro(
294 expander.expand(db, loc.krate, &macro_arg.0) 405 expander.expand(db, loc.krate, &macro_arg.0)
295} 406}
296 407
297fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
298 match file_id.0 {
299 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
300 HirFileIdRepr::MacroFile(macro_file) => {
301 db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node())
302 }
303 }
304}
305
306fn parse_macro_expansion(
307 db: &dyn AstDatabase,
308 macro_file: MacroFile,
309) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
310 parse_macro_with_arg(db, macro_file, None)
311}
312
313fn parse_macro_with_arg(
314 db: &dyn AstDatabase,
315 macro_file: MacroFile,
316 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
317) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
318 let macro_call_id = macro_file.macro_call_id;
319 let result = if let Some(arg) = arg {
320 macro_expand_with_arg(db, macro_call_id, Some(arg))
321 } else {
322 db.macro_expand(macro_call_id)
323 };
324
325 let _p = profile::span("parse_macro_expansion");
326
327 if let Some(err) = &result.err {
328 // Note:
329 // The final goal we would like to make all parse_macro success,
330 // such that the following log will not call anyway.
331 match macro_call_id {
332 MacroCallId::LazyMacro(id) => {
333 let loc: MacroCallLoc = db.lookup_intern_macro(id);
334 let node = loc.kind.node(db);
335
336 // collect parent information for warning log
337 let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
338 it.file_id.call_node(db)
339 })
340 .map(|n| format!("{:#}", n.value))
341 .collect::<Vec<_>>()
342 .join("\n");
343
344 log::warn!(
345 "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
346 err,
347 node.value,
348 parents
349 );
350 }
351 _ => {
352 log::warn!("fail on macro_parse: (reason: {:?})", err);
353 }
354 }
355 }
356 let tt = match result.value {
357 Some(tt) => tt,
358 None => return ExpandResult { value: None, err: result.err },
359 };
360
361 let fragment_kind = to_fragment_kind(db, macro_call_id);
362
363 log::debug!("expanded = {}", tt.as_debug_string());
364 log::debug!("kind = {:?}", fragment_kind);
365
366 let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) {
367 Ok(it) => it,
368 Err(err) => {
369 log::debug!(
370 "failed to parse expanstion to {:?} = {}",
371 fragment_kind,
372 tt.as_debug_string()
373 );
374 return ExpandResult::only_err(err);
375 }
376 };
377
378 match result.err {
379 Some(err) => {
380 // Safety check for recursive identity macro.
381 let node = parse.syntax_node();
382 let file: HirFileId = macro_file.into();
383 let call_node = match file.call_node(db) {
384 Some(it) => it,
385 None => {
386 return ExpandResult::only_err(err);
387 }
388 };
389 if is_self_replicating(&node, &call_node.value) {
390 return ExpandResult::only_err(err);
391 } else {
392 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) }
393 }
394 }
395 None => {
396 log::debug!("parse = {:?}", parse.syntax_node().kind());
397 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None }
398 }
399 }
400}
401
402fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { 408fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool {
403 if diff(from, to).is_empty() { 409 if diff(from, to).is_empty() {
404 return true; 410 return true;
405 } 411 }
406 if let Some(stmts) = MacroStmts::cast(from.clone()) { 412 if let Some(stmts) = ast::MacroStmts::cast(from.clone()) {
407 if stmts.statements().any(|stmt| diff(stmt.syntax(), to).is_empty()) { 413 if stmts.statements().any(|stmt| diff(stmt.syntax(), to).is_empty()) {
408 return true; 414 return true;
409 } 415 }
@@ -420,62 +426,15 @@ fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame>
420 Arc::new(HygieneFrame::new(db, file_id)) 426 Arc::new(HygieneFrame::new(db, file_id))
421} 427}
422 428
423/// Given a `MacroCallId`, return what `FragmentKind` it belongs to. 429fn macro_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
424/// FIXME: Not completed 430 match id {
425fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { 431 MacroCallId::LazyMacro(id) => {
426 let lazy_id = match id { 432 let loc: MacroCallLoc = db.lookup_intern_macro(id);
427 MacroCallId::LazyMacro(id) => id, 433 loc.kind.fragment_kind()
428 MacroCallId::EagerMacro(id) => {
429 return db.lookup_intern_eager_expansion(id).fragment;
430 }
431 };
432 let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value;
433
434 let parent = match syn.parent() {
435 Some(it) => it,
436 None => return FragmentKind::Statements,
437 };
438
439 match parent.kind() {
440 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
441 MACRO_STMTS => FragmentKind::Statements,
442 MACRO_PAT => FragmentKind::Pattern,
443 MACRO_TYPE => FragmentKind::Type,
444 ITEM_LIST => FragmentKind::Items,
445 LET_STMT => {
446 // FIXME: Handle LHS Pattern
447 FragmentKind::Expr
448 } 434 }
449 EXPR_STMT => FragmentKind::Statements, 435 MacroCallId::EagerMacro(id) => {
450 BLOCK_EXPR => FragmentKind::Statements, 436 let loc: EagerCallLoc = db.lookup_intern_eager_expansion(id);
451 ARG_LIST => FragmentKind::Expr, 437 loc.fragment
452 TRY_EXPR => FragmentKind::Expr,
453 TUPLE_EXPR => FragmentKind::Expr,
454 PAREN_EXPR => FragmentKind::Expr,
455 ARRAY_EXPR => FragmentKind::Expr,
456 FOR_EXPR => FragmentKind::Expr,
457 PATH_EXPR => FragmentKind::Expr,
458 CLOSURE_EXPR => FragmentKind::Expr,
459 CONDITION => FragmentKind::Expr,
460 BREAK_EXPR => FragmentKind::Expr,
461 RETURN_EXPR => FragmentKind::Expr,
462 MATCH_EXPR => FragmentKind::Expr,
463 MATCH_ARM => FragmentKind::Expr,
464 MATCH_GUARD => FragmentKind::Expr,
465 RECORD_EXPR_FIELD => FragmentKind::Expr,
466 CALL_EXPR => FragmentKind::Expr,
467 INDEX_EXPR => FragmentKind::Expr,
468 METHOD_CALL_EXPR => FragmentKind::Expr,
469 FIELD_EXPR => FragmentKind::Expr,
470 AWAIT_EXPR => FragmentKind::Expr,
471 CAST_EXPR => FragmentKind::Expr,
472 REF_EXPR => FragmentKind::Expr,
473 PREFIX_EXPR => FragmentKind::Expr,
474 RANGE_EXPR => FragmentKind::Expr,
475 BIN_EXPR => FragmentKind::Expr,
476 _ => {
477 // Unknown , Just guess it is `Items`
478 FragmentKind::Items
479 } 438 }
480 } 439 }
481} 440}
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index f12132f84..85491fe8b 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -175,8 +175,13 @@ fn lazy_expand(
175) -> ExpandResult<Option<InFile<SyntaxNode>>> { 175) -> ExpandResult<Option<InFile<SyntaxNode>>> {
176 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 176 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
177 177
178 let fragment = crate::to_fragment_kind(&macro_call.value);
178 let id: MacroCallId = def 179 let id: MacroCallId = def
179 .as_lazy_macro(db, krate, MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id) }) 180 .as_lazy_macro(
181 db,
182 krate,
183 MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment },
184 )
180 .into(); 185 .into();
181 186
182 let err = db.macro_expand_error(id); 187 let err = db.macro_expand_error(id);
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 779725629..aca69e35a 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -5,6 +5,7 @@
5use std::sync::Arc; 5use std::sync::Arc;
6 6
7use base_db::CrateId; 7use base_db::CrateId;
8use db::TokenExpander;
8use either::Either; 9use either::Either;
9use mbe::Origin; 10use mbe::Origin;
10use parser::SyntaxKind; 11use parser::SyntaxKind;
@@ -31,10 +32,14 @@ impl Hygiene {
31 } 32 }
32 33
33 // FIXME: this should just return name 34 // FIXME: this should just return name
34 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { 35 pub fn name_ref_to_name(
36 &self,
37 db: &dyn AstDatabase,
38 name_ref: ast::NameRef,
39 ) -> Either<Name, CrateId> {
35 if let Some(frames) = &self.frames { 40 if let Some(frames) = &self.frames {
36 if name_ref.text() == "$crate" { 41 if name_ref.text() == "$crate" {
37 if let Some(krate) = frames.root_crate(name_ref.syntax()) { 42 if let Some(krate) = frames.root_crate(db, name_ref.syntax()) {
38 return Either::Right(krate); 43 return Either::Right(krate);
39 } 44 }
40 } 45 }
@@ -43,15 +48,19 @@ impl Hygiene {
43 Either::Left(name_ref.as_name()) 48 Either::Left(name_ref.as_name())
44 } 49 }
45 50
46 pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> { 51 pub fn local_inner_macros(&self, db: &dyn AstDatabase, path: ast::Path) -> Option<CrateId> {
47 let mut token = path.syntax().first_token()?.text_range(); 52 let mut token = path.syntax().first_token()?.text_range();
48 let frames = self.frames.as_ref()?; 53 let frames = self.frames.as_ref()?;
49 let mut current = frames.0.clone(); 54 let mut current = frames.0.clone();
50 55
51 loop { 56 loop {
52 let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(token)?; 57 let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(db, token)?;
53 if origin == Origin::Def { 58 if origin == Origin::Def {
54 return if current.local_inner { frames.root_crate(path.syntax()) } else { None }; 59 return if current.local_inner {
60 frames.root_crate(db, path.syntax())
61 } else {
62 None
63 };
55 } 64 }
56 current = current.call_site.as_ref()?.clone(); 65 current = current.call_site.as_ref()?.clone();
57 token = mapped.value; 66 token = mapped.value;
@@ -81,13 +90,13 @@ impl HygieneFrames {
81 HygieneFrames(Arc::new(HygieneFrame::new(db, file_id))) 90 HygieneFrames(Arc::new(HygieneFrame::new(db, file_id)))
82 } 91 }
83 92
84 fn root_crate(&self, node: &SyntaxNode) -> Option<CrateId> { 93 fn root_crate(&self, db: &dyn AstDatabase, node: &SyntaxNode) -> Option<CrateId> {
85 let mut token = node.first_token()?.text_range(); 94 let mut token = node.first_token()?.text_range();
86 let mut result = self.0.krate; 95 let mut result = self.0.krate;
87 let mut current = self.0.clone(); 96 let mut current = self.0.clone();
88 97
89 while let Some((mapped, origin)) = 98 while let Some((mapped, origin)) =
90 current.expansion.as_ref().and_then(|it| it.map_ident_up(token)) 99 current.expansion.as_ref().and_then(|it| it.map_ident_up(db, token))
91 { 100 {
92 result = current.krate; 101 result = current.krate;
93 102
@@ -111,26 +120,41 @@ impl HygieneFrames {
111 120
112#[derive(Debug, Clone, PartialEq, Eq)] 121#[derive(Debug, Clone, PartialEq, Eq)]
113struct HygieneInfo { 122struct HygieneInfo {
114 arg_start: InFile<TextSize>, 123 file: MacroFile,
115 /// The `macro_rules!` arguments. 124 /// The `macro_rules!` arguments.
116 def_start: Option<InFile<TextSize>>, 125 def_start: Option<InFile<TextSize>>,
117 126
118 macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, 127 macro_def: Arc<TokenExpander>,
119 macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, 128 macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
120 exp_map: Arc<mbe::TokenMap>, 129 exp_map: Arc<mbe::TokenMap>,
121} 130}
122 131
123impl HygieneInfo { 132impl HygieneInfo {
124 fn map_ident_up(&self, token: TextRange) -> Option<(InFile<TextRange>, Origin)> { 133 fn map_ident_up(
134 &self,
135 db: &dyn AstDatabase,
136 token: TextRange,
137 ) -> Option<(InFile<TextRange>, Origin)> {
125 let token_id = self.exp_map.token_by_range(token)?; 138 let token_id = self.exp_map.token_by_range(token)?;
126 139
127 let (token_id, origin) = self.macro_def.0.map_id_up(token_id); 140 let (token_id, origin) = self.macro_def.map_id_up(token_id);
128 let (token_map, tt) = match origin { 141 let (token_map, tt) = match origin {
129 mbe::Origin::Call => (&self.macro_arg.1, self.arg_start), 142 mbe::Origin::Call => {
130 mbe::Origin::Def => ( 143 let call_id = match self.file.macro_call_id {
131 &self.macro_def.1, 144 MacroCallId::LazyMacro(lazy) => lazy,
132 *self.def_start.as_ref().expect("`Origin::Def` used with non-`macro_rules!` macro"), 145 MacroCallId::EagerMacro(_) => unreachable!(),
133 ), 146 };
147 let loc: MacroCallLoc = db.lookup_intern_macro(call_id);
148 let arg_start = loc.kind.arg(db)?.text_range().start();
149 (&self.macro_arg.1, InFile::new(loc.kind.file_id(), arg_start))
150 }
151 mbe::Origin::Def => match (&*self.macro_def, self.def_start) {
152 (TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt))
153 | (TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt)) => {
154 (def_site_token_map, tt)
155 }
156 _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"),
157 },
134 }; 158 };
135 159
136 let range = token_map.range_by_token(token_id)?.by_kind(SyntaxKind::IDENT)?; 160 let range = token_map.range_by_token(token_id)?.by_kind(SyntaxKind::IDENT)?;
@@ -143,8 +167,6 @@ fn make_hygiene_info(
143 macro_file: MacroFile, 167 macro_file: MacroFile,
144 loc: &MacroCallLoc, 168 loc: &MacroCallLoc,
145) -> Option<HygieneInfo> { 169) -> Option<HygieneInfo> {
146 let arg_tt = loc.kind.arg(db)?;
147
148 let def_offset = loc.def.ast_id().left().and_then(|id| { 170 let def_offset = loc.def.ast_id().left().and_then(|id| {
149 let def_tt = match id.to_node(db) { 171 let def_tt = match id.to_node(db) {
150 ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(), 172 ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(),
@@ -157,13 +179,7 @@ fn make_hygiene_info(
157 let (_, exp_map) = db.parse_macro_expansion(macro_file).value?; 179 let (_, exp_map) = db.parse_macro_expansion(macro_file).value?;
158 let macro_arg = db.macro_arg(macro_file.macro_call_id)?; 180 let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
159 181
160 Some(HygieneInfo { 182 Some(HygieneInfo { file: macro_file, def_start: def_offset, macro_arg, macro_def, exp_map })
161 arg_start: InFile::new(loc.kind.file_id(), arg_tt.text_range().start()),
162 def_start: def_offset,
163 macro_arg,
164 macro_def,
165 exp_map,
166 })
167} 183}
168 184
169impl HygieneFrame { 185impl HygieneFrame {
@@ -174,7 +190,8 @@ impl HygieneFrame {
174 MacroCallId::EagerMacro(_id) => (None, None, false), 190 MacroCallId::EagerMacro(_id) => (None, None, false),
175 MacroCallId::LazyMacro(id) => { 191 MacroCallId::LazyMacro(id) => {
176 let loc = db.lookup_intern_macro(id); 192 let loc = db.lookup_intern_macro(id);
177 let info = make_hygiene_info(db, macro_file, &loc); 193 let info = make_hygiene_info(db, macro_file, &loc)
194 .map(|info| (loc.kind.file_id(), info));
178 match loc.def.kind { 195 match loc.def.kind {
179 MacroDefKind::Declarative(_) => { 196 MacroDefKind::Declarative(_) => {
180 (info, Some(loc.def.krate), loc.def.local_inner) 197 (info, Some(loc.def.krate), loc.def.local_inner)
@@ -188,7 +205,7 @@ impl HygieneFrame {
188 }, 205 },
189 }; 206 };
190 207
191 let info = match info { 208 let (calling_file, info) = match info {
192 None => { 209 None => {
193 return HygieneFrame { 210 return HygieneFrame {
194 expansion: None, 211 expansion: None,
@@ -202,7 +219,7 @@ impl HygieneFrame {
202 }; 219 };
203 220
204 let def_site = info.def_start.map(|it| db.hygiene_frame(it.file_id)); 221 let def_site = info.def_start.map(|it| db.hygiene_frame(it.file_id));
205 let call_site = Some(db.hygiene_frame(info.arg_start.file_id)); 222 let call_site = Some(db.hygiene_frame(calling_file));
206 223
207 HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site } 224 HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site }
208 } 225 }
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs
new file mode 100644
index 000000000..112216859
--- /dev/null
+++ b/crates/hir_expand/src/input.rs
@@ -0,0 +1,94 @@
1//! Macro input conditioning.
2
3use syntax::{
4 ast::{self, AttrsOwner},
5 AstNode, SyntaxNode,
6};
7
8use crate::{
9 db::AstDatabase,
10 name::{name, AsName},
11 LazyMacroId, MacroCallKind, MacroCallLoc,
12};
13
14pub(crate) fn process_macro_input(
15 db: &dyn AstDatabase,
16 node: SyntaxNode,
17 id: LazyMacroId,
18) -> SyntaxNode {
19 let loc: MacroCallLoc = db.lookup_intern_macro(id);
20
21 match loc.kind {
22 MacroCallKind::FnLike { .. } => node,
23 MacroCallKind::Derive { derive_attr_index, .. } => {
24 let item = match ast::Item::cast(node.clone()) {
25 Some(item) => item,
26 None => return node,
27 };
28
29 remove_derives_up_to(item, derive_attr_index as usize).syntax().clone()
30 }
31 }
32}
33
34/// Removes `#[derive]` attributes from `item`, up to `attr_index`.
35fn remove_derives_up_to(item: ast::Item, attr_index: usize) -> ast::Item {
36 let item = item.clone_for_update();
37 for attr in item.attrs().take(attr_index + 1) {
38 if let Some(name) =
39 attr.path().and_then(|path| path.as_single_segment()).and_then(|seg| seg.name_ref())
40 {
41 if name.as_name() == name![derive] {
42 attr.syntax().detach();
43 }
44 }
45 }
46 item
47}
48
49#[cfg(test)]
50mod tests {
51 use base_db::fixture::WithFixture;
52 use base_db::SourceDatabase;
53 use expect_test::{expect, Expect};
54
55 use crate::test_db::TestDB;
56
57 use super::*;
58
59 fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) {
60 let (db, file_id) = TestDB::with_single_file(&ra_fixture);
61 let parsed = db.parse(file_id);
62
63 let mut items: Vec<_> =
64 parsed.syntax_node().descendants().filter_map(ast::Item::cast).collect();
65 assert_eq!(items.len(), 1);
66
67 let item = remove_derives_up_to(items.pop().unwrap(), attr);
68 expect.assert_eq(&item.to_string());
69 }
70
71 #[test]
72 fn remove_derive() {
73 test_remove_derives_up_to(
74 2,
75 r#"
76#[allow(unused)]
77#[derive(Copy)]
78#[derive(Hello)]
79#[derive(Clone)]
80struct A {
81 bar: u32
82}
83 "#,
84 expect![[r#"
85#[allow(unused)]
86
87
88#[derive(Clone)]
89struct A {
90 bar: u32
91}"#]],
92 );
93 }
94}
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index a0e6aec62..5df11856e 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -14,9 +14,12 @@ pub mod builtin_macro;
14pub mod proc_macro; 14pub mod proc_macro;
15pub mod quote; 15pub mod quote;
16pub mod eager; 16pub mod eager;
17mod input;
17 18
18use either::Either; 19use either::Either;
20
19pub use mbe::{ExpandError, ExpandResult}; 21pub use mbe::{ExpandError, ExpandResult};
22pub use parser::FragmentKind;
20 23
21use std::hash::Hash; 24use std::hash::Hash;
22use std::sync::Arc; 25use std::sync::Arc;
@@ -290,13 +293,21 @@ pub struct MacroCallLoc {
290 293
291#[derive(Debug, Clone, PartialEq, Eq, Hash)] 294#[derive(Debug, Clone, PartialEq, Eq, Hash)]
292pub enum MacroCallKind { 295pub enum MacroCallKind {
293 FnLike { ast_id: AstId<ast::MacroCall> }, 296 FnLike {
294 Derive { ast_id: AstId<ast::Item>, derive_name: String, derive_attr: AttrId }, 297 ast_id: AstId<ast::MacroCall>,
298 fragment: FragmentKind,
299 },
300 Derive {
301 ast_id: AstId<ast::Item>,
302 derive_name: String,
303 /// Syntactical index of the invoking `#[derive]` attribute.
304 ///
305 /// Outer attributes are counted first, then inner attributes. This does not support
306 /// out-of-line modules, which may have attributes spread across 2 files!
307 derive_attr_index: u32,
308 },
295} 309}
296 310
297#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
298pub struct AttrId(pub u32);
299
300impl MacroCallKind { 311impl MacroCallKind {
301 fn file_id(&self) -> HirFileId { 312 fn file_id(&self) -> HirFileId {
302 match self { 313 match self {
@@ -324,6 +335,13 @@ impl MacroCallKind {
324 MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), 335 MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
325 } 336 }
326 } 337 }
338
339 fn fragment_kind(&self) -> FragmentKind {
340 match self {
341 MacroCallKind::FnLike { fragment, .. } => *fragment,
342 MacroCallKind::Derive { .. } => FragmentKind::Items,
343 }
344 }
327} 345}
328 346
329impl MacroCallId { 347impl MacroCallId {
@@ -351,13 +369,12 @@ pub struct ExpansionInfo {
351 /// The `macro_rules!` arguments. 369 /// The `macro_rules!` arguments.
352 def: Option<InFile<ast::TokenTree>>, 370 def: Option<InFile<ast::TokenTree>>,
353 371
354 macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, 372 macro_def: Arc<db::TokenExpander>,
355 macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, 373 macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
356 exp_map: Arc<mbe::TokenMap>, 374 exp_map: Arc<mbe::TokenMap>,
357} 375}
358 376
359pub use mbe::Origin; 377pub use mbe::Origin;
360use parser::FragmentKind;
361 378
362impl ExpansionInfo { 379impl ExpansionInfo {
363 pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { 380 pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
@@ -368,7 +385,7 @@ impl ExpansionInfo {
368 assert_eq!(token.file_id, self.arg.file_id); 385 assert_eq!(token.file_id, self.arg.file_id);
369 let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; 386 let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
370 let token_id = self.macro_arg.1.token_by_range(range)?; 387 let token_id = self.macro_arg.1.token_by_range(range)?;
371 let token_id = self.macro_def.0.map_id_down(token_id); 388 let token_id = self.macro_def.map_id_down(token_id);
372 389
373 let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?; 390 let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
374 391
@@ -383,17 +400,16 @@ impl ExpansionInfo {
383 ) -> Option<(InFile<SyntaxToken>, Origin)> { 400 ) -> Option<(InFile<SyntaxToken>, Origin)> {
384 let token_id = self.exp_map.token_by_range(token.value.text_range())?; 401 let token_id = self.exp_map.token_by_range(token.value.text_range())?;
385 402
386 let (token_id, origin) = self.macro_def.0.map_id_up(token_id); 403 let (token_id, origin) = self.macro_def.map_id_up(token_id);
387 let (token_map, tt) = match origin { 404 let (token_map, tt) = match origin {
388 mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), 405 mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
389 mbe::Origin::Def => ( 406 mbe::Origin::Def => match (&*self.macro_def, self.def.as_ref()) {
390 &self.macro_def.1, 407 (db::TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt))
391 self.def 408 | (db::TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt)) => {
392 .as_ref() 409 (def_site_token_map, tt.as_ref().map(|tt| tt.syntax().clone()))
393 .expect("`Origin::Def` used with non-`macro_rules!` macro") 410 }
394 .as_ref() 411 _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"),
395 .map(|tt| tt.syntax().clone()), 412 },
396 ),
397 }; 413 };
398 414
399 let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; 415 let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
@@ -563,3 +579,59 @@ impl<N: AstNode> InFile<N> {
563 self.with_value(self.value.syntax()) 579 self.with_value(self.value.syntax())
564 } 580 }
565} 581}
582
583/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.
584/// FIXME: Not completed
585pub fn to_fragment_kind(call: &ast::MacroCall) -> FragmentKind {
586 use syntax::SyntaxKind::*;
587
588 let syn = call.syntax();
589
590 let parent = match syn.parent() {
591 Some(it) => it,
592 None => return FragmentKind::Statements,
593 };
594
595 match parent.kind() {
596 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
597 MACRO_STMTS => FragmentKind::Statements,
598 MACRO_PAT => FragmentKind::Pattern,
599 MACRO_TYPE => FragmentKind::Type,
600 ITEM_LIST => FragmentKind::Items,
601 LET_STMT => {
602 // FIXME: Handle LHS Pattern
603 FragmentKind::Expr
604 }
605 EXPR_STMT => FragmentKind::Statements,
606 BLOCK_EXPR => FragmentKind::Statements,
607 ARG_LIST => FragmentKind::Expr,
608 TRY_EXPR => FragmentKind::Expr,
609 TUPLE_EXPR => FragmentKind::Expr,
610 PAREN_EXPR => FragmentKind::Expr,
611 ARRAY_EXPR => FragmentKind::Expr,
612 FOR_EXPR => FragmentKind::Expr,
613 PATH_EXPR => FragmentKind::Expr,
614 CLOSURE_EXPR => FragmentKind::Expr,
615 CONDITION => FragmentKind::Expr,
616 BREAK_EXPR => FragmentKind::Expr,
617 RETURN_EXPR => FragmentKind::Expr,
618 MATCH_EXPR => FragmentKind::Expr,
619 MATCH_ARM => FragmentKind::Expr,
620 MATCH_GUARD => FragmentKind::Expr,
621 RECORD_EXPR_FIELD => FragmentKind::Expr,
622 CALL_EXPR => FragmentKind::Expr,
623 INDEX_EXPR => FragmentKind::Expr,
624 METHOD_CALL_EXPR => FragmentKind::Expr,
625 FIELD_EXPR => FragmentKind::Expr,
626 AWAIT_EXPR => FragmentKind::Expr,
627 CAST_EXPR => FragmentKind::Expr,
628 REF_EXPR => FragmentKind::Expr,
629 PREFIX_EXPR => FragmentKind::Expr,
630 RANGE_EXPR => FragmentKind::Expr,
631 BIN_EXPR => FragmentKind::Expr,
632 _ => {
633 // Unknown , Just guess it is `Items`
634 FragmentKind::Items
635 }
636 }
637}
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 75e950816..d5643393a 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -2,7 +2,6 @@
2 2
3use crate::db::AstDatabase; 3use crate::db::AstDatabase;
4use base_db::{CrateId, ProcMacroId}; 4use base_db::{CrateId, ProcMacroId};
5use tt::buffer::{Cursor, TokenBuffer};
6 5
7#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] 6#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
8pub struct ProcMacroExpander { 7pub struct ProcMacroExpander {
@@ -44,9 +43,6 @@ impl ProcMacroExpander {
44 .clone() 43 .clone()
45 .ok_or_else(|| err!("No derive macro found."))?; 44 .ok_or_else(|| err!("No derive macro found."))?;
46 45
47 let tt = remove_derive_attrs(tt)
48 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
49
50 // Proc macros have access to the environment variables of the invoking crate. 46 // Proc macros have access to the environment variables of the invoking crate.
51 let env = &krate_graph[calling_crate].env; 47 let env = &krate_graph[calling_crate].env;
52 48
@@ -56,101 +52,3 @@ impl ProcMacroExpander {
56 } 52 }
57 } 53 }
58} 54}
59
60fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
61 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = cursor.token_tree() {
62 if punct.char == c {
63 *cursor = cursor.bump();
64 return true;
65 }
66 }
67 false
68}
69
70fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
71 if let Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) = cursor.token_tree() {
72 if Some(kind) == subtree.delimiter_kind() {
73 *cursor = cursor.bump_subtree();
74 return true;
75 }
76 }
77 false
78}
79
80fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
81 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) = cursor.token_tree() {
82 if t == ident.text.as_str() {
83 *cursor = cursor.bump();
84 return true;
85 }
86 }
87 false
88}
89
90fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
91 let buffer = TokenBuffer::from_tokens(&tt.token_trees);
92 let mut p = buffer.begin();
93 let mut result = tt::Subtree::default();
94
95 while !p.eof() {
96 let curr = p;
97
98 if eat_punct(&mut p, '#') {
99 eat_punct(&mut p, '!');
100 let parent = p;
101 if eat_subtree(&mut p, tt::DelimiterKind::Bracket) {
102 if eat_ident(&mut p, "derive") {
103 p = parent.bump();
104 continue;
105 }
106 }
107 }
108
109 result.token_trees.push(curr.token_tree()?.cloned());
110 p = curr.bump();
111 }
112
113 Some(result)
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use test_utils::assert_eq_text;
120
121 #[test]
122 fn test_remove_derive_attrs() {
123 let tt = mbe::parse_to_token_tree(
124 r#"
125 #[allow(unused)]
126 #[derive(Copy)]
127 #[derive(Hello)]
128 struct A {
129 bar: u32
130 }
131"#,
132 )
133 .unwrap()
134 .0;
135 let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap());
136
137 assert_eq_text!(
138 r#"
139SUBTREE $
140 PUNCH # [alone] 0
141 SUBTREE [] 1
142 IDENT allow 2
143 SUBTREE () 3
144 IDENT unused 4
145 IDENT struct 15
146 IDENT A 16
147 SUBTREE {} 17
148 IDENT bar 18
149 PUNCH : [alone] 19
150 IDENT u32 20
151"#
152 .trim(),
153 &result
154 );
155 }
156}
diff --git a/crates/hir_expand/src/quote.rs b/crates/hir_expand/src/quote.rs
index c82487ef0..230a59964 100644
--- a/crates/hir_expand/src/quote.rs
+++ b/crates/hir_expand/src/quote.rs
@@ -196,8 +196,8 @@ impl_to_to_tokentrees! {
196 tt::Literal => self { self }; 196 tt::Literal => self { self };
197 tt::Ident => self { self }; 197 tt::Ident => self { self };
198 tt::Punct => self { self }; 198 tt::Punct => self { self };
199 &str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}}; 199 &str => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}};
200 String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}} 200 String => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}}
201} 201}
202 202
203#[cfg(test)] 203#[cfg(test)]
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs
index 8c4542956..5232a7d80 100644
--- a/crates/hir_ty/src/chalk_ext.rs
+++ b/crates/hir_ty/src/chalk_ext.rs
@@ -1,8 +1,10 @@
1//! Various extensions traits for Chalk types. 1//! Various extensions traits for Chalk types.
2 2
3use chalk_ir::Mutability; 3use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, UintTy};
4use hir_def::{ 4use hir_def::{
5 type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId, 5 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
6 type_ref::Rawness,
7 AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId,
6}; 8};
7 9
8use crate::{ 10use crate::{
@@ -18,6 +20,7 @@ pub trait TyExt {
18 fn is_unknown(&self) -> bool; 20 fn is_unknown(&self) -> bool;
19 21
20 fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; 22 fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
23 fn as_builtin(&self) -> Option<BuiltinType>;
21 fn as_tuple(&self) -> Option<&Substitution>; 24 fn as_tuple(&self) -> Option<&Substitution>;
22 fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>; 25 fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
23 fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; 26 fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
@@ -59,6 +62,35 @@ impl TyExt for Ty {
59 } 62 }
60 } 63 }
61 64
65 fn as_builtin(&self) -> Option<BuiltinType> {
66 match self.kind(&Interner) {
67 TyKind::Str => Some(BuiltinType::Str),
68 TyKind::Scalar(Scalar::Bool) => Some(BuiltinType::Bool),
69 TyKind::Scalar(Scalar::Char) => Some(BuiltinType::Char),
70 TyKind::Scalar(Scalar::Float(fty)) => Some(BuiltinType::Float(match fty {
71 FloatTy::F64 => BuiltinFloat::F64,
72 FloatTy::F32 => BuiltinFloat::F32,
73 })),
74 TyKind::Scalar(Scalar::Int(ity)) => Some(BuiltinType::Int(match ity {
75 IntTy::Isize => BuiltinInt::Isize,
76 IntTy::I8 => BuiltinInt::I8,
77 IntTy::I16 => BuiltinInt::I16,
78 IntTy::I32 => BuiltinInt::I32,
79 IntTy::I64 => BuiltinInt::I64,
80 IntTy::I128 => BuiltinInt::I128,
81 })),
82 TyKind::Scalar(Scalar::Uint(ity)) => Some(BuiltinType::Uint(match ity {
83 UintTy::Usize => BuiltinUint::Usize,
84 UintTy::U8 => BuiltinUint::U8,
85 UintTy::U16 => BuiltinUint::U16,
86 UintTy::U32 => BuiltinUint::U32,
87 UintTy::U64 => BuiltinUint::U64,
88 UintTy::U128 => BuiltinUint::U128,
89 })),
90 _ => None,
91 }
92 }
93
62 fn as_tuple(&self) -> Option<&Substitution> { 94 fn as_tuple(&self) -> Option<&Substitution> {
63 match self.kind(&Interner) { 95 match self.kind(&Interner) {
64 TyKind::Tuple(_, substs) => Some(substs), 96 TyKind::Tuple(_, substs) => Some(substs),
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 4fb7d9cf2..1f6edf7a2 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -1000,7 +1000,7 @@ impl HirDisplay for TypeRef {
1000 } 1000 }
1001 TypeRef::Macro(macro_call) => { 1001 TypeRef::Macro(macro_call) => {
1002 let macro_call = macro_call.to_node(f.db.upcast()); 1002 let macro_call = macro_call.to_node(f.db.upcast());
1003 let ctx = body::LowerCtx::with_hygiene(&Hygiene::new_unhygienic()); 1003 let ctx = body::LowerCtx::with_hygiene(f.db.upcast(), &Hygiene::new_unhygienic());
1004 match macro_call.path() { 1004 match macro_call.path() {
1005 Some(path) => match Path::from_src(path, &ctx) { 1005 Some(path) => match Path::from_src(path, &ctx) {
1006 Some(path) => path.hir_fmt(f)?, 1006 Some(path) => path.hir_fmt(f)?,
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f04bcf531..88f3d09d3 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -20,6 +20,7 @@ oorandom = "11.1.2"
20pulldown-cmark-to-cmark = "6.0.0" 20pulldown-cmark-to-cmark = "6.0.0"
21pulldown-cmark = { version = "0.8.0", default-features = false } 21pulldown-cmark = { version = "0.8.0", default-features = false }
22url = "2.1.1" 22url = "2.1.1"
23dot = "0.1.4"
23 24
24stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { path = "../stdx", version = "0.0.0" }
25syntax = { path = "../syntax", version = "0.0.0" } 26syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 1c911a8b2..273d8cfbb 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -15,6 +15,7 @@ use hir::{
15 diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, 15 diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
16 InFile, Semantics, 16 InFile, Semantics,
17}; 17};
18use ide_assists::AssistResolveStrategy;
18use ide_db::{base_db::SourceDatabase, RootDatabase}; 19use ide_db::{base_db::SourceDatabase, RootDatabase};
19use itertools::Itertools; 20use itertools::Itertools;
20use rustc_hash::FxHashSet; 21use rustc_hash::FxHashSet;
@@ -84,7 +85,7 @@ pub struct DiagnosticsConfig {
84pub(crate) fn diagnostics( 85pub(crate) fn diagnostics(
85 db: &RootDatabase, 86 db: &RootDatabase,
86 config: &DiagnosticsConfig, 87 config: &DiagnosticsConfig,
87 resolve: bool, 88 resolve: &AssistResolveStrategy,
88 file_id: FileId, 89 file_id: FileId,
89) -> Vec<Diagnostic> { 90) -> Vec<Diagnostic> {
90 let _p = profile::span("diagnostics"); 91 let _p = profile::span("diagnostics");
@@ -212,7 +213,7 @@ pub(crate) fn diagnostics(
212fn diagnostic_with_fix<D: DiagnosticWithFix>( 213fn diagnostic_with_fix<D: DiagnosticWithFix>(
213 d: &D, 214 d: &D,
214 sema: &Semantics<RootDatabase>, 215 sema: &Semantics<RootDatabase>,
215 resolve: bool, 216 resolve: &AssistResolveStrategy,
216) -> Diagnostic { 217) -> Diagnostic {
217 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) 218 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
218 .with_fix(d.fix(&sema, resolve)) 219 .with_fix(d.fix(&sema, resolve))
@@ -222,7 +223,7 @@ fn diagnostic_with_fix<D: DiagnosticWithFix>(
222fn warning_with_fix<D: DiagnosticWithFix>( 223fn warning_with_fix<D: DiagnosticWithFix>(
223 d: &D, 224 d: &D,
224 sema: &Semantics<RootDatabase>, 225 sema: &Semantics<RootDatabase>,
225 resolve: bool, 226 resolve: &AssistResolveStrategy,
226) -> Diagnostic { 227) -> Diagnostic {
227 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) 228 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
228 .with_fix(d.fix(&sema, resolve)) 229 .with_fix(d.fix(&sema, resolve))
@@ -299,6 +300,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
299#[cfg(test)] 300#[cfg(test)]
300mod tests { 301mod tests {
301 use expect_test::{expect, Expect}; 302 use expect_test::{expect, Expect};
303 use ide_assists::AssistResolveStrategy;
302 use stdx::trim_indent; 304 use stdx::trim_indent;
303 use test_utils::assert_eq_text; 305 use test_utils::assert_eq_text;
304 306
@@ -314,7 +316,11 @@ mod tests {
314 316
315 let (analysis, file_position) = fixture::position(ra_fixture_before); 317 let (analysis, file_position) = fixture::position(ra_fixture_before);
316 let diagnostic = analysis 318 let diagnostic = analysis
317 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) 319 .diagnostics(
320 &DiagnosticsConfig::default(),
321 AssistResolveStrategy::All,
322 file_position.file_id,
323 )
318 .unwrap() 324 .unwrap()
319 .pop() 325 .pop()
320 .unwrap(); 326 .unwrap();
@@ -343,7 +349,11 @@ mod tests {
343 fn check_no_fix(ra_fixture: &str) { 349 fn check_no_fix(ra_fixture: &str) {
344 let (analysis, file_position) = fixture::position(ra_fixture); 350 let (analysis, file_position) = fixture::position(ra_fixture);
345 let diagnostic = analysis 351 let diagnostic = analysis
346 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) 352 .diagnostics(
353 &DiagnosticsConfig::default(),
354 AssistResolveStrategy::All,
355 file_position.file_id,
356 )
347 .unwrap() 357 .unwrap()
348 .pop() 358 .pop()
349 .unwrap(); 359 .unwrap();
@@ -357,7 +367,9 @@ mod tests {
357 let diagnostics = files 367 let diagnostics = files
358 .into_iter() 368 .into_iter()
359 .flat_map(|file_id| { 369 .flat_map(|file_id| {
360 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap() 370 analysis
371 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
372 .unwrap()
361 }) 373 })
362 .collect::<Vec<_>>(); 374 .collect::<Vec<_>>();
363 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); 375 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
@@ -365,8 +377,9 @@ mod tests {
365 377
366 fn check_expect(ra_fixture: &str, expect: Expect) { 378 fn check_expect(ra_fixture: &str, expect: Expect) {
367 let (analysis, file_id) = fixture::file(ra_fixture); 379 let (analysis, file_id) = fixture::file(ra_fixture);
368 let diagnostics = 380 let diagnostics = analysis
369 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap(); 381 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
382 .unwrap();
370 expect.assert_debug_eq(&diagnostics) 383 expect.assert_debug_eq(&diagnostics)
371 } 384 }
372 385
@@ -641,6 +654,26 @@ fn test_fn() {
641 } 654 }
642 655
643 #[test] 656 #[test]
657 fn test_fill_struct_fields_raw_ident() {
658 check_fix(
659 r#"
660struct TestStruct { r#type: u8 }
661
662fn test_fn() {
663 TestStruct { $0 };
664}
665"#,
666 r"
667struct TestStruct { r#type: u8 }
668
669fn test_fn() {
670 TestStruct { r#type: () };
671}
672",
673 );
674 }
675
676 #[test]
644 fn test_fill_struct_fields_no_diagnostic() { 677 fn test_fill_struct_fields_no_diagnostic() {
645 check_no_diagnostics( 678 check_no_diagnostics(
646 r" 679 r"
@@ -911,11 +944,13 @@ struct Foo {
911 944
912 let (analysis, file_id) = fixture::file(r#"mod foo;"#); 945 let (analysis, file_id) = fixture::file(r#"mod foo;"#);
913 946
914 let diagnostics = analysis.diagnostics(&config, true, file_id).unwrap(); 947 let diagnostics =
948 analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap();
915 assert!(diagnostics.is_empty()); 949 assert!(diagnostics.is_empty());
916 950
917 let diagnostics = 951 let diagnostics = analysis
918 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap(); 952 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
953 .unwrap();
919 assert!(!diagnostics.is_empty()); 954 assert!(!diagnostics.is_empty());
920 } 955 }
921 956
@@ -1022,7 +1057,11 @@ impl TestStruct {
1022 1057
1023 let (analysis, file_position) = fixture::position(input); 1058 let (analysis, file_position) = fixture::position(input);
1024 let diagnostics = analysis 1059 let diagnostics = analysis
1025 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) 1060 .diagnostics(
1061 &DiagnosticsConfig::default(),
1062 AssistResolveStrategy::All,
1063 file_position.file_id,
1064 )
1026 .unwrap(); 1065 .unwrap();
1027 assert_eq!(diagnostics.len(), 1); 1066 assert_eq!(diagnostics.len(), 1);
1028 1067
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index 7be8b3459..15821500f 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -8,6 +8,7 @@ use hir::{
8 }, 8 },
9 HasSource, HirDisplay, InFile, Semantics, VariantDef, 9 HasSource, HirDisplay, InFile, Semantics, VariantDef,
10}; 10};
11use ide_assists::AssistResolveStrategy;
11use ide_db::{ 12use ide_db::{
12 base_db::{AnchoredPathBuf, FileId}, 13 base_db::{AnchoredPathBuf, FileId},
13 source_change::{FileSystemEdit, SourceChange}, 14 source_change::{FileSystemEdit, SourceChange},
@@ -35,11 +36,19 @@ pub(crate) trait DiagnosticWithFix: Diagnostic {
35 /// 36 ///
36 /// If `resolve` is false, the edit will be computed later, on demand, and 37 /// If `resolve` is false, the edit will be computed later, on demand, and
37 /// can be omitted. 38 /// can be omitted.
38 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist>; 39 fn fix(
40 &self,
41 sema: &Semantics<RootDatabase>,
42 _resolve: &AssistResolveStrategy,
43 ) -> Option<Assist>;
39} 44}
40 45
41impl DiagnosticWithFix for UnresolvedModule { 46impl DiagnosticWithFix for UnresolvedModule {
42 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 47 fn fix(
48 &self,
49 sema: &Semantics<RootDatabase>,
50 _resolve: &AssistResolveStrategy,
51 ) -> Option<Assist> {
43 let root = sema.db.parse_or_expand(self.file)?; 52 let root = sema.db.parse_or_expand(self.file)?;
44 let unresolved_module = self.decl.to_node(&root); 53 let unresolved_module = self.decl.to_node(&root);
45 Some(fix( 54 Some(fix(
@@ -59,7 +68,11 @@ impl DiagnosticWithFix for UnresolvedModule {
59} 68}
60 69
61impl DiagnosticWithFix for NoSuchField { 70impl DiagnosticWithFix for NoSuchField {
62 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 71 fn fix(
72 &self,
73 sema: &Semantics<RootDatabase>,
74 _resolve: &AssistResolveStrategy,
75 ) -> Option<Assist> {
63 let root = sema.db.parse_or_expand(self.file)?; 76 let root = sema.db.parse_or_expand(self.file)?;
64 missing_record_expr_field_fix( 77 missing_record_expr_field_fix(
65 &sema, 78 &sema,
@@ -70,7 +83,11 @@ impl DiagnosticWithFix for NoSuchField {
70} 83}
71 84
72impl DiagnosticWithFix for MissingFields { 85impl DiagnosticWithFix for MissingFields {
73 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 86 fn fix(
87 &self,
88 sema: &Semantics<RootDatabase>,
89 _resolve: &AssistResolveStrategy,
90 ) -> Option<Assist> {
74 // Note that although we could add a diagnostics to 91 // Note that although we could add a diagnostics to
75 // fill the missing tuple field, e.g : 92 // fill the missing tuple field, e.g :
76 // `struct A(usize);` 93 // `struct A(usize);`
@@ -106,7 +123,11 @@ impl DiagnosticWithFix for MissingFields {
106} 123}
107 124
108impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { 125impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
109 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 126 fn fix(
127 &self,
128 sema: &Semantics<RootDatabase>,
129 _resolve: &AssistResolveStrategy,
130 ) -> Option<Assist> {
110 let root = sema.db.parse_or_expand(self.file)?; 131 let root = sema.db.parse_or_expand(self.file)?;
111 let tail_expr = self.expr.to_node(&root); 132 let tail_expr = self.expr.to_node(&root);
112 let tail_expr_range = tail_expr.syntax().text_range(); 133 let tail_expr_range = tail_expr.syntax().text_range();
@@ -119,7 +140,11 @@ impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
119} 140}
120 141
121impl DiagnosticWithFix for RemoveThisSemicolon { 142impl DiagnosticWithFix for RemoveThisSemicolon {
122 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 143 fn fix(
144 &self,
145 sema: &Semantics<RootDatabase>,
146 _resolve: &AssistResolveStrategy,
147 ) -> Option<Assist> {
123 let root = sema.db.parse_or_expand(self.file)?; 148 let root = sema.db.parse_or_expand(self.file)?;
124 149
125 let semicolon = self 150 let semicolon = self
@@ -139,7 +164,11 @@ impl DiagnosticWithFix for RemoveThisSemicolon {
139} 164}
140 165
141impl DiagnosticWithFix for IncorrectCase { 166impl DiagnosticWithFix for IncorrectCase {
142 fn fix(&self, sema: &Semantics<RootDatabase>, resolve: bool) -> Option<Assist> { 167 fn fix(
168 &self,
169 sema: &Semantics<RootDatabase>,
170 resolve: &AssistResolveStrategy,
171 ) -> Option<Assist> {
143 let root = sema.db.parse_or_expand(self.file)?; 172 let root = sema.db.parse_or_expand(self.file)?;
144 let name_node = self.ident.to_node(&root); 173 let name_node = self.ident.to_node(&root);
145 174
@@ -149,7 +178,7 @@ impl DiagnosticWithFix for IncorrectCase {
149 178
150 let label = format!("Rename to {}", self.suggested_text); 179 let label = format!("Rename to {}", self.suggested_text);
151 let mut res = unresolved_fix("change_case", &label, frange.range); 180 let mut res = unresolved_fix("change_case", &label, frange.range);
152 if resolve { 181 if resolve.should_resolve(&res.id) {
153 let source_change = rename_with_semantics(sema, file_position, &self.suggested_text); 182 let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
154 res.source_change = Some(source_change.ok().unwrap_or_default()); 183 res.source_change = Some(source_change.ok().unwrap_or_default());
155 } 184 }
@@ -159,7 +188,11 @@ impl DiagnosticWithFix for IncorrectCase {
159} 188}
160 189
161impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { 190impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap {
162 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 191 fn fix(
192 &self,
193 sema: &Semantics<RootDatabase>,
194 _resolve: &AssistResolveStrategy,
195 ) -> Option<Assist> {
163 let root = sema.db.parse_or_expand(self.file)?; 196 let root = sema.db.parse_or_expand(self.file)?;
164 let next_expr = self.next_expr.to_node(&root); 197 let next_expr = self.next_expr.to_node(&root);
165 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; 198 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs
index 7d39f4fbe..93fd25dea 100644
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ b/crates/ide/src/diagnostics/unlinked_file.rs
@@ -5,6 +5,7 @@ use hir::{
5 diagnostics::{Diagnostic, DiagnosticCode}, 5 diagnostics::{Diagnostic, DiagnosticCode},
6 InFile, 6 InFile,
7}; 7};
8use ide_assists::AssistResolveStrategy;
8use ide_db::{ 9use ide_db::{
9 base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, 10 base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
10 source_change::SourceChange, 11 source_change::SourceChange,
@@ -50,7 +51,11 @@ impl Diagnostic for UnlinkedFile {
50} 51}
51 52
52impl DiagnosticWithFix for UnlinkedFile { 53impl DiagnosticWithFix for UnlinkedFile {
53 fn fix(&self, sema: &hir::Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 54 fn fix(
55 &self,
56 sema: &hir::Semantics<RootDatabase>,
57 _resolve: &AssistResolveStrategy,
58 ) -> Option<Assist> {
54 // If there's an existing module that could add a `mod` item to include the unlinked file, 59 // If there's an existing module that could add a `mod` item to include the unlinked file,
55 // suggest that as a fix. 60 // suggest that as a fix.
56 61
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index 2b9ed123c..a03988778 100644..100755
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -20,6 +20,7 @@ pub enum FoldKind {
20 Consts, 20 Consts,
21 Statics, 21 Statics,
22 Array, 22 Array,
23 WhereClause,
23} 24}
24 25
25#[derive(Debug)] 26#[derive(Debug)]
@@ -109,6 +110,13 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
109 res.push(Fold { range, kind: FoldKind::Statics }) 110 res.push(Fold { range, kind: FoldKind::Statics })
110 } 111 }
111 } 112 }
113
114 // Fold where clause
115 if node.kind() == WHERE_CLAUSE {
116 if let Some(range) = fold_range_for_where_clause(&node) {
117 res.push(Fold { range, kind: FoldKind::WhereClause })
118 }
119 }
112 } 120 }
113 } 121 }
114 } 122 }
@@ -241,6 +249,23 @@ fn contiguous_range_for_comment(
241 } 249 }
242} 250}
243 251
252fn fold_range_for_where_clause(node: &SyntaxNode) -> Option<TextRange> {
253 let first_where_pred = node.first_child();
254 let last_where_pred = node.last_child();
255
256 if first_where_pred != last_where_pred {
257 let mut it = node.descendants_with_tokens();
258 if let (Some(_where_clause), Some(where_kw), Some(last_comma)) =
259 (it.next(), it.next(), it.last())
260 {
261 let start = where_kw.text_range().end();
262 let end = last_comma.text_range().end();
263 return Some(TextRange::new(start, end));
264 }
265 }
266 None
267}
268
244#[cfg(test)] 269#[cfg(test)]
245mod tests { 270mod tests {
246 use test_utils::extract_tags; 271 use test_utils::extract_tags;
@@ -272,6 +297,7 @@ mod tests {
272 FoldKind::Consts => "consts", 297 FoldKind::Consts => "consts",
273 FoldKind::Statics => "statics", 298 FoldKind::Statics => "statics",
274 FoldKind::Array => "array", 299 FoldKind::Array => "array",
300 FoldKind::WhereClause => "whereclause",
275 }; 301 };
276 assert_eq!(kind, &attr.unwrap()); 302 assert_eq!(kind, &attr.unwrap());
277 } 303 }
@@ -513,4 +539,23 @@ static SECOND_STATIC: &str = "second";</fold>
513 "#, 539 "#,
514 ) 540 )
515 } 541 }
542
543 #[test]
544 fn fold_where_clause() {
545 // fold multi-line and don't fold single line.
546 check(
547 r#"
548fn foo()
549where<fold whereclause>
550 A: Foo,
551 B: Foo,
552 C: Foo,
553 D: Foo,</fold> {}
554
555fn bar()
556where
557 A: Bar, {}
558"#,
559 )
560 }
516} 561}
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index 9d34b109b..f3284bb96 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -30,6 +30,7 @@ pub(crate) fn goto_type_definition(
30 ast::Expr(it) => sema.type_of_expr(&it)?, 30 ast::Expr(it) => sema.type_of_expr(&it)?,
31 ast::Pat(it) => sema.type_of_pat(&it)?, 31 ast::Pat(it) => sema.type_of_pat(&it)?,
32 ast::SelfParam(it) => sema.type_of_self(&it)?, 32 ast::SelfParam(it) => sema.type_of_self(&it)?,
33 ast::Type(it) => sema.resolve_type(&it)?,
33 _ => return None, 34 _ => return None,
34 } 35 }
35 }; 36 };
@@ -149,4 +150,15 @@ impl Foo {
149"#, 150"#,
150 ) 151 )
151 } 152 }
153
154 #[test]
155 fn goto_def_for_type_fallback() {
156 check(
157 r#"
158struct Foo;
159 //^^^
160impl Foo$0 {}
161"#,
162 )
163 }
152} 164}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index d5ef054d8..e0bf660c4 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -218,9 +218,7 @@ fn hint_iterator(
218 ty: &hir::Type, 218 ty: &hir::Type,
219) -> Option<SmolStr> { 219) -> Option<SmolStr> {
220 let db = sema.db; 220 let db = sema.db;
221 let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref()) 221 let strukt = ty.strip_references().as_adt()?;
222 .last()
223 .and_then(|strukt| strukt.as_adt())?;
224 let krate = strukt.krate(db); 222 let krate = strukt.krate(db);
225 if krate != famous_defs.core()? { 223 if krate != famous_defs.core()? {
226 return None; 224 return None;
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index fe2a349e6..61dcbb399 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -1,3 +1,5 @@
1use std::convert::TryFrom;
2
1use ide_assists::utils::extract_trivial_expression; 3use ide_assists::utils::extract_trivial_expression;
2use itertools::Itertools; 4use itertools::Itertools;
3use syntax::{ 5use syntax::{
@@ -65,14 +67,6 @@ fn remove_newlines(edit: &mut TextEditBuilder, token: &SyntaxToken, range: TextR
65 67
66fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) { 68fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) {
67 if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 { 69 if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 {
68 let mut string_open_quote = false;
69 if let Some(string) = ast::String::cast(token.clone()) {
70 if let Some(range) = string.open_quote_text_range() {
71 cov_mark::hit!(join_string_literal);
72 string_open_quote = range.end() == offset;
73 }
74 }
75
76 let n_spaces_after_line_break = { 70 let n_spaces_after_line_break = {
77 let suff = &token.text()[TextRange::new( 71 let suff = &token.text()[TextRange::new(
78 offset - token.text_range().start() + TextSize::of('\n'), 72 offset - token.text_range().start() + TextSize::of('\n'),
@@ -81,8 +75,23 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
81 suff.bytes().take_while(|&b| b == b' ').count() 75 suff.bytes().take_while(|&b| b == b' ').count()
82 }; 76 };
83 77
78 let mut no_space = false;
79 if let Some(string) = ast::String::cast(token.clone()) {
80 if let Some(range) = string.open_quote_text_range() {
81 cov_mark::hit!(join_string_literal_open_quote);
82 no_space |= range.end() == offset;
83 }
84 if let Some(range) = string.close_quote_text_range() {
85 cov_mark::hit!(join_string_literal_close_quote);
86 no_space |= range.start()
87 == offset
88 + TextSize::of('\n')
89 + TextSize::try_from(n_spaces_after_line_break).unwrap();
90 }
91 }
92
84 let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into()); 93 let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into());
85 let replace_with = if string_open_quote { "" } else { " " }; 94 let replace_with = if no_space { "" } else { " " };
86 edit.replace(range, replace_with.to_string()); 95 edit.replace(range, replace_with.to_string());
87 return; 96 return;
88 } 97 }
@@ -797,22 +806,54 @@ fn foo() {
797 806
798 #[test] 807 #[test]
799 fn join_string_literal() { 808 fn join_string_literal() {
800 cov_mark::check!(join_string_literal); 809 {
801 check_join_lines( 810 cov_mark::check!(join_string_literal_open_quote);
802 r#" 811 check_join_lines(
812 r#"
803fn main() { 813fn main() {
804 $0" 814 $0"
805hello 815hello
806"; 816";
807} 817}
808"#, 818"#,
809 r#" 819 r#"
810fn main() { 820fn main() {
811 $0"hello 821 $0"hello
812"; 822";
813} 823}
814"#, 824"#,
815 ); 825 );
826 }
827
828 {
829 cov_mark::check!(join_string_literal_close_quote);
830 check_join_lines(
831 r#"
832fn main() {
833 $0"hello
834";
835}
836"#,
837 r#"
838fn main() {
839 $0"hello";
840}
841"#,
842 );
843 check_join_lines(
844 r#"
845fn main() {
846 $0r"hello
847 ";
848}
849"#,
850 r#"
851fn main() {
852 $0r"hello";
853}
854"#,
855 );
856 }
816 857
817 check_join_lines( 858 check_join_lines(
818 r#" 859 r#"
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 99e45633e..34360501a 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -49,6 +49,7 @@ mod syntax_tree;
49mod typing; 49mod typing;
50mod markdown_remove; 50mod markdown_remove;
51mod doc_links; 51mod doc_links;
52mod view_crate_graph;
52 53
53use std::sync::Arc; 54use std::sync::Arc;
54 55
@@ -87,7 +88,9 @@ pub use crate::{
87 }, 88 },
88}; 89};
89pub use hir::{Documentation, Semantics}; 90pub use hir::{Documentation, Semantics};
90pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; 91pub use ide_assists::{
92 Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
93};
91pub use ide_completion::{ 94pub use ide_completion::{
92 CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, 95 CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit,
93 InsertTextFormat, 96 InsertTextFormat,
@@ -285,6 +288,10 @@ impl Analysis {
285 self.with_db(|db| view_hir::view_hir(&db, position)) 288 self.with_db(|db| view_hir::view_hir(&db, position))
286 } 289 }
287 290
291 pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> {
292 self.with_db(|db| view_crate_graph::view_crate_graph(&db))
293 }
294
288 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { 295 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> {
289 self.with_db(|db| expand_macro::expand_macro(db, position)) 296 self.with_db(|db| expand_macro::expand_macro(db, position))
290 } 297 }
@@ -518,12 +525,13 @@ impl Analysis {
518 pub fn assists( 525 pub fn assists(
519 &self, 526 &self,
520 config: &AssistConfig, 527 config: &AssistConfig,
521 resolve: bool, 528 resolve: AssistResolveStrategy,
522 frange: FileRange, 529 frange: FileRange,
523 ) -> Cancelable<Vec<Assist>> { 530 ) -> Cancelable<Vec<Assist>> {
524 self.with_db(|db| { 531 self.with_db(|db| {
532 let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
525 let mut acc = Assist::get(db, config, resolve, frange); 533 let mut acc = Assist::get(db, config, resolve, frange);
526 ssr::add_ssr_assist(db, &mut acc, resolve, frange); 534 acc.extend(ssr_assists.into_iter());
527 acc 535 acc
528 }) 536 })
529 } 537 }
@@ -532,10 +540,10 @@ impl Analysis {
532 pub fn diagnostics( 540 pub fn diagnostics(
533 &self, 541 &self,
534 config: &DiagnosticsConfig, 542 config: &DiagnosticsConfig,
535 resolve: bool, 543 resolve: AssistResolveStrategy,
536 file_id: FileId, 544 file_id: FileId,
537 ) -> Cancelable<Vec<Diagnostic>> { 545 ) -> Cancelable<Vec<Diagnostic>> {
538 self.with_db(|db| diagnostics::diagnostics(db, config, resolve, file_id)) 546 self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id))
539 } 547 }
540 548
541 /// Convenience function to return assists + quick fixes for diagnostics 549 /// Convenience function to return assists + quick fixes for diagnostics
@@ -543,7 +551,7 @@ impl Analysis {
543 &self, 551 &self,
544 assist_config: &AssistConfig, 552 assist_config: &AssistConfig,
545 diagnostics_config: &DiagnosticsConfig, 553 diagnostics_config: &DiagnosticsConfig,
546 resolve: bool, 554 resolve: AssistResolveStrategy,
547 frange: FileRange, 555 frange: FileRange,
548 ) -> Cancelable<Vec<Assist>> { 556 ) -> Cancelable<Vec<Assist>> {
549 let include_fixes = match &assist_config.allowed { 557 let include_fixes = match &assist_config.allowed {
@@ -552,17 +560,21 @@ impl Analysis {
552 }; 560 };
553 561
554 self.with_db(|db| { 562 self.with_db(|db| {
563 let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
564 let diagnostic_assists = if include_fixes {
565 diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id)
566 .into_iter()
567 .filter_map(|it| it.fix)
568 .filter(|it| it.target.intersect(frange.range).is_some())
569 .collect()
570 } else {
571 Vec::new()
572 };
573
555 let mut res = Assist::get(db, assist_config, resolve, frange); 574 let mut res = Assist::get(db, assist_config, resolve, frange);
556 ssr::add_ssr_assist(db, &mut res, resolve, frange); 575 res.extend(ssr_assists.into_iter());
557 576 res.extend(diagnostic_assists.into_iter());
558 if include_fixes { 577
559 res.extend(
560 diagnostics::diagnostics(db, diagnostics_config, resolve, frange.file_id)
561 .into_iter()
562 .filter_map(|it| it.fix)
563 .filter(|it| it.target.intersect(frange.range).is_some()),
564 );
565 }
566 res 578 res
567 }) 579 })
568 } 580 }
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 11ca7ec6b..ae492a264 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -65,7 +65,7 @@ pub(crate) fn find_all_refs(
65 (find_def(&sema, &syntax, position)?, false) 65 (find_def(&sema, &syntax, position)?, false)
66 }; 66 };
67 67
68 let mut usages = def.usages(sema).set_scope(search_scope).all(); 68 let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all();
69 if is_literal_search { 69 if is_literal_search {
70 // filter for constructor-literals 70 // filter for constructor-literals
71 let refs = usages.references.values_mut(); 71 let refs = usages.references.values_mut();
@@ -1163,22 +1163,76 @@ fn foo<const FOO$0: usize>() -> usize {
1163 } 1163 }
1164 1164
1165 #[test] 1165 #[test]
1166 fn test_find_self_ty_in_trait_def() { 1166 fn test_trait() {
1167 check( 1167 check(
1168 r#" 1168 r#"
1169trait Foo { 1169trait Foo$0 where Self: {}
1170 fn f() -> Self$0; 1170
1171impl Foo for () {}
1172"#,
1173 expect![[r#"
1174 Foo Trait FileId(0) 0..24 6..9
1175
1176 FileId(0) 31..34
1177 "#]],
1178 );
1179 }
1180
1181 #[test]
1182 fn test_trait_self() {
1183 check(
1184 r#"
1185trait Foo where Self$0 {
1186 fn f() -> Self;
1171} 1187}
1188
1189impl Foo for () {}
1172"#, 1190"#,
1173 expect![[r#" 1191 expect![[r#"
1174 Self TypeParam FileId(0) 6..9 6..9 1192 Self TypeParam FileId(0) 6..9 6..9
1175 1193
1176 FileId(0) 26..30 1194 FileId(0) 16..20
1195 FileId(0) 37..41
1177 "#]], 1196 "#]],
1178 ); 1197 );
1179 } 1198 }
1180 1199
1181 #[test] 1200 #[test]
1201 fn test_self_ty() {
1202 check(
1203 r#"
1204 struct $0Foo;
1205
1206 impl Foo where Self: {
1207 fn f() -> Self;
1208 }
1209 "#,
1210 expect![[r#"
1211 Foo Struct FileId(0) 0..11 7..10
1212
1213 FileId(0) 18..21
1214 FileId(0) 28..32
1215 FileId(0) 50..54
1216 "#]],
1217 );
1218 check(
1219 r#"
1220struct Foo;
1221
1222impl Foo where Self: {
1223 fn f() -> Self$0;
1224}
1225"#,
1226 expect![[r#"
1227 impl Impl FileId(0) 13..57 18..21
1228
1229 FileId(0) 18..21
1230 FileId(0) 28..32
1231 FileId(0) 50..54
1232 "#]],
1233 );
1234 }
1235 #[test]
1182 fn test_self_variant_with_payload() { 1236 fn test_self_variant_with_payload() {
1183 check( 1237 check(
1184 r#" 1238 r#"
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 175e7a31d..2bf953305 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1888,4 +1888,21 @@ impl Foo {
1888 "error: Cannot rename `Self`", 1888 "error: Cannot rename `Self`",
1889 ); 1889 );
1890 } 1890 }
1891
1892 #[test]
1893 fn test_rename_ignores_self_ty() {
1894 check(
1895 "Fo0",
1896 r#"
1897struct $0Foo;
1898
1899impl Foo where Self: {}
1900"#,
1901 r#"
1902struct Fo0;
1903
1904impl Fo0 where Self: {}
1905"#,
1906 );
1907 }
1891} 1908}
diff --git a/crates/ide/src/ssr.rs b/crates/ide/src/ssr.rs
index f3638d928..57ec80261 100644
--- a/crates/ide/src/ssr.rs
+++ b/crates/ide/src/ssr.rs
@@ -2,18 +2,23 @@
2//! assist in ide_assists because that would require the ide_assists crate 2//! assist in ide_assists because that would require the ide_assists crate
3//! depend on the ide_ssr crate. 3//! depend on the ide_ssr crate.
4 4
5use ide_assists::{Assist, AssistId, AssistKind, GroupLabel}; 5use ide_assists::{Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel};
6use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase}; 6use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase};
7 7
8pub(crate) fn add_ssr_assist( 8pub(crate) fn ssr_assists(
9 db: &RootDatabase, 9 db: &RootDatabase,
10 base: &mut Vec<Assist>, 10 resolve: &AssistResolveStrategy,
11 resolve: bool,
12 frange: FileRange, 11 frange: FileRange,
13) -> Option<()> { 12) -> Vec<Assist> {
14 let (match_finder, comment_range) = ide_ssr::ssr_from_comment(db, frange)?; 13 let mut ssr_assists = Vec::with_capacity(2);
15 14
16 let (source_change_for_file, source_change_for_workspace) = if resolve { 15 let (match_finder, comment_range) = match ide_ssr::ssr_from_comment(db, frange) {
16 Some(ssr_data) => ssr_data,
17 None => return ssr_assists,
18 };
19 let id = AssistId("ssr", AssistKind::RefactorRewrite);
20
21 let (source_change_for_file, source_change_for_workspace) = if resolve.should_resolve(&id) {
17 let edits = match_finder.edits(); 22 let edits = match_finder.edits();
18 23
19 let source_change_for_file = { 24 let source_change_for_file = {
@@ -35,16 +40,17 @@ pub(crate) fn add_ssr_assist(
35 40
36 for (label, source_change) in assists.into_iter() { 41 for (label, source_change) in assists.into_iter() {
37 let assist = Assist { 42 let assist = Assist {
38 id: AssistId("ssr", AssistKind::RefactorRewrite), 43 id,
39 label: Label::new(label), 44 label: Label::new(label),
40 group: Some(GroupLabel("Apply SSR".into())), 45 group: Some(GroupLabel("Apply SSR".into())),
41 target: comment_range, 46 target: comment_range,
42 source_change, 47 source_change,
43 }; 48 };
44 49
45 base.push(assist); 50 ssr_assists.push(assist);
46 } 51 }
47 Some(()) 52
53 ssr_assists
48} 54}
49 55
50#[cfg(test)] 56#[cfg(test)]
@@ -52,7 +58,7 @@ mod tests {
52 use std::sync::Arc; 58 use std::sync::Arc;
53 59
54 use expect_test::expect; 60 use expect_test::expect;
55 use ide_assists::Assist; 61 use ide_assists::{Assist, AssistResolveStrategy};
56 use ide_db::{ 62 use ide_db::{
57 base_db::{fixture::WithFixture, salsa::Durability, FileRange}, 63 base_db::{fixture::WithFixture, salsa::Durability, FileRange},
58 symbol_index::SymbolsDatabase, 64 symbol_index::SymbolsDatabase,
@@ -60,24 +66,14 @@ mod tests {
60 }; 66 };
61 use rustc_hash::FxHashSet; 67 use rustc_hash::FxHashSet;
62 68
63 use super::add_ssr_assist; 69 use super::ssr_assists;
64 70
65 fn get_assists(ra_fixture: &str, resolve: bool) -> Vec<Assist> { 71 fn get_assists(ra_fixture: &str, resolve: AssistResolveStrategy) -> Vec<Assist> {
66 let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture); 72 let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
67 let mut local_roots = FxHashSet::default(); 73 let mut local_roots = FxHashSet::default();
68 local_roots.insert(ide_db::base_db::fixture::WORKSPACE); 74 local_roots.insert(ide_db::base_db::fixture::WORKSPACE);
69 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); 75 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
70 76 ssr_assists(&db, &resolve, FileRange { file_id, range: range_or_offset.into() })
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 } 77 }
82 78
83 #[test] 79 #[test]
@@ -88,16 +84,13 @@ mod tests {
88 // This is foo $0 84 // This is foo $0
89 fn foo() {} 85 fn foo() {}
90 "#; 86 "#;
91 let resolve = true; 87 let assists = get_assists(ra_fixture, AssistResolveStrategy::All);
92
93 let assists = get_assists(ra_fixture, resolve);
94 88
95 assert_eq!(0, assists.len()); 89 assert_eq!(0, assists.len());
96 } 90 }
97 91
98 #[test] 92 #[test]
99 fn resolve_edits_true() { 93 fn resolve_edits_true() {
100 let resolve = true;
101 let assists = get_assists( 94 let assists = get_assists(
102 r#" 95 r#"
103 //- /lib.rs 96 //- /lib.rs
@@ -109,7 +102,7 @@ mod tests {
109 //- /bar.rs 102 //- /bar.rs
110 fn bar() { 2 } 103 fn bar() { 2 }
111 "#, 104 "#,
112 resolve, 105 AssistResolveStrategy::All,
113 ); 106 );
114 107
115 assert_eq!(2, assists.len()); 108 assert_eq!(2, assists.len());
@@ -200,7 +193,6 @@ mod tests {
200 193
201 #[test] 194 #[test]
202 fn resolve_edits_false() { 195 fn resolve_edits_false() {
203 let resolve = false;
204 let assists = get_assists( 196 let assists = get_assists(
205 r#" 197 r#"
206 //- /lib.rs 198 //- /lib.rs
@@ -212,7 +204,7 @@ mod tests {
212 //- /bar.rs 204 //- /bar.rs
213 fn bar() { 2 } 205 fn bar() { 2 }
214 "#, 206 "#,
215 resolve, 207 AssistResolveStrategy::None,
216 ); 208 );
217 209
218 assert_eq!(2, assists.len()); 210 assert_eq!(2, assists.len());
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 638f42c2f..8d83ba206 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -142,6 +142,7 @@ It is beyond me why you'd use these when you got ///
142```rust 142```rust
143</span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> 143</span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation">
144``` 144```
145</span><span class="function documentation injected intra_doc_link">[`block_comments2`]</span><span class="comment documentation"> tests these with indentation
145 */</span> 146 */</span>
146<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 147<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
147 148
@@ -150,5 +151,6 @@ It is beyond me why you'd use these when you got ///
150 ```rust 151 ```rust
151</span><span class="comment documentation"> </span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> 152</span><span class="comment documentation"> </span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation">
152 ``` 153 ```
154 </span><span class="function documentation injected intra_doc_link">[`block_comments`]</span><span class="comment documentation"> tests these without indentation
153*/</span> 155*/</span>
154<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file 156<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 17cc6334b..b6e952b08 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -618,6 +618,7 @@ It is beyond me why you'd use these when you got ///
618```rust 618```rust
619let _ = example(&[1, 2, 3]); 619let _ = example(&[1, 2, 3]);
620``` 620```
621[`block_comments2`] tests these with indentation
621 */ 622 */
622pub fn block_comments() {} 623pub fn block_comments() {}
623 624
@@ -626,6 +627,7 @@ pub fn block_comments() {}
626 ```rust 627 ```rust
627 let _ = example(&[1, 2, 3]); 628 let _ = example(&[1, 2, 3]);
628 ``` 629 ```
630 [`block_comments`] tests these without indentation
629*/ 631*/
630pub fn block_comments2() {} 632pub fn block_comments2() {}
631"# 633"#
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 82c732390..4ad49eca0 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -88,7 +88,7 @@ fn on_char_typed_inner(
88} 88}
89 89
90/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a 90/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
91/// block. 91/// block, or a part of a `use` item.
92fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> { 92fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> {
93 if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { 93 if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) {
94 return None; 94 return None;
@@ -99,30 +99,59 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<
99 // Remove the `{` to get a better parse tree, and reparse 99 // Remove the `{` to get a better parse tree, and reparse
100 let file = file.reparse(&Indel::delete(brace_token.text_range())); 100 let file = file.reparse(&Indel::delete(brace_token.text_range()));
101 101
102 let mut expr: ast::Expr = find_node_at_offset(file.tree().syntax(), offset)?; 102 if let Some(edit) = brace_expr(&file.tree(), offset) {
103 if expr.syntax().text_range().start() != offset { 103 return Some(edit);
104 return None;
105 } 104 }
106 105
107 // Enclose the outermost expression starting at `offset` 106 if let Some(edit) = brace_use_path(&file.tree(), offset) {
108 while let Some(parent) = expr.syntax().parent() { 107 return Some(edit);
109 if parent.text_range().start() != expr.syntax().text_range().start() { 108 }
110 break;
111 }
112 109
113 match ast::Expr::cast(parent) { 110 return None;
114 Some(parent) => expr = parent, 111
115 None => break, 112 fn brace_use_path(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
113 let segment: ast::PathSegment = find_node_at_offset(file.syntax(), offset)?;
114 if segment.syntax().text_range().start() != offset {
115 return None;
116 } 116 }
117 }
118 117
119 // If it's a statement in a block, we don't know how many statements should be included 118 let tree: ast::UseTree = find_node_at_offset(file.syntax(), offset)?;
120 if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { 119
121 return None; 120 Some(TextEdit::insert(
121 tree.syntax().text_range().end() + TextSize::of("{"),
122 "}".to_string(),
123 ))
122 } 124 }
123 125
124 // Insert `}` right after the expression. 126 fn brace_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
125 Some(TextEdit::insert(expr.syntax().text_range().end() + TextSize::of("{"), "}".to_string())) 127 let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?;
128 if expr.syntax().text_range().start() != offset {
129 return None;
130 }
131
132 // Enclose the outermost expression starting at `offset`
133 while let Some(parent) = expr.syntax().parent() {
134 if parent.text_range().start() != expr.syntax().text_range().start() {
135 break;
136 }
137
138 match ast::Expr::cast(parent) {
139 Some(parent) => expr = parent,
140 None => break,
141 }
142 }
143
144 // If it's a statement in a block, we don't know how many statements should be included
145 if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) {
146 return None;
147 }
148
149 // Insert `}` right after the expression.
150 Some(TextEdit::insert(
151 expr.syntax().text_range().end() + TextSize::of("{"),
152 "}".to_string(),
153 ))
154 }
126} 155}
127 156
128/// Returns an edit which should be applied after `=` was typed. Primarily, 157/// Returns an edit which should be applied after `=` was typed. Primarily,
@@ -440,7 +469,7 @@ fn foo() -> { 92 }
440 } 469 }
441 470
442 #[test] 471 #[test]
443 fn adds_closing_brace() { 472 fn adds_closing_brace_for_expr() {
444 type_char( 473 type_char(
445 '{', 474 '{',
446 r#" 475 r#"
@@ -519,4 +548,87 @@ fn f() {
519 "#, 548 "#,
520 ); 549 );
521 } 550 }
551
552 #[test]
553 fn adds_closing_brace_for_use_tree() {
554 type_char(
555 '{',
556 r#"
557use some::$0Path;
558 "#,
559 r#"
560use some::{Path};
561 "#,
562 );
563 type_char(
564 '{',
565 r#"
566use some::{Path, $0Other};
567 "#,
568 r#"
569use some::{Path, {Other}};
570 "#,
571 );
572 type_char(
573 '{',
574 r#"
575use some::{$0Path, Other};
576 "#,
577 r#"
578use some::{{Path}, Other};
579 "#,
580 );
581 type_char(
582 '{',
583 r#"
584use some::path::$0to::Item;
585 "#,
586 r#"
587use some::path::{to::Item};
588 "#,
589 );
590 type_char(
591 '{',
592 r#"
593use some::$0path::to::Item;
594 "#,
595 r#"
596use some::{path::to::Item};
597 "#,
598 );
599 type_char(
600 '{',
601 r#"
602use $0some::path::to::Item;
603 "#,
604 r#"
605use {some::path::to::Item};
606 "#,
607 );
608 type_char(
609 '{',
610 r#"
611use some::path::$0to::{Item};
612 "#,
613 r#"
614use some::path::{to::{Item}};
615 "#,
616 );
617 type_char(
618 '{',
619 r#"
620use $0Thing as _;
621 "#,
622 r#"
623use {Thing as _};
624 "#,
625 );
626
627 type_char_noop(
628 '{',
629 r#"
630use some::pa$0th::to::Item;
631 "#,
632 );
633 }
522} 634}
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 7d2db201a..81c4d95b1 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -54,6 +54,14 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
54 cov_mark::hit!(indent_block_contents); 54 cov_mark::hit!(indent_block_contents);
55 return Some(edit); 55 return Some(edit);
56 } 56 }
57
58 // Typing enter after the `{` of a use tree list.
59 if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{'))
60 .and_then(|list| on_enter_in_use_tree_list(list, position))
61 {
62 cov_mark::hit!(indent_block_contents);
63 return Some(edit);
64 }
57 } 65 }
58 66
59 None 67 None
@@ -111,6 +119,21 @@ fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<Te
111 Some(edit) 119 Some(edit)
112} 120}
113 121
122fn on_enter_in_use_tree_list(list: ast::UseTreeList, position: FilePosition) -> Option<TextEdit> {
123 if list.syntax().text().contains_char('\n') {
124 return None;
125 }
126
127 let indent = IndentLevel::from_node(list.syntax());
128 let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1));
129 edit.union(TextEdit::insert(
130 list.r_curly_token()?.text_range().start(),
131 format!("\n{}", indent),
132 ))
133 .ok()?;
134 Some(edit)
135}
136
114fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> { 137fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> {
115 let mut node = block.tail_expr().map(|e| e.syntax().clone()); 138 let mut node = block.tail_expr().map(|e| e.syntax().clone());
116 139
@@ -484,4 +507,96 @@ fn f() {$0
484 "#, 507 "#,
485 ); 508 );
486 } 509 }
510
511 #[test]
512 fn indents_use_tree_list() {
513 do_check(
514 r#"
515use crate::{$0};
516 "#,
517 r#"
518use crate::{
519 $0
520};
521 "#,
522 );
523 do_check(
524 r#"
525use crate::{$0Object, path::to::OtherThing};
526 "#,
527 r#"
528use crate::{
529 $0Object, path::to::OtherThing
530};
531 "#,
532 );
533 do_check(
534 r#"
535use {crate::{$0Object, path::to::OtherThing}};
536 "#,
537 r#"
538use {crate::{
539 $0Object, path::to::OtherThing
540}};
541 "#,
542 );
543 do_check(
544 r#"
545use {
546 crate::{$0Object, path::to::OtherThing}
547};
548 "#,
549 r#"
550use {
551 crate::{
552 $0Object, path::to::OtherThing
553 }
554};
555 "#,
556 );
557 }
558
559 #[test]
560 fn does_not_indent_use_tree_list_when_not_at_curly_brace() {
561 do_check_noop(
562 r#"
563use path::{Thing$0};
564 "#,
565 );
566 }
567
568 #[test]
569 fn does_not_indent_use_tree_list_without_curly_braces() {
570 do_check_noop(
571 r#"
572use path::Thing$0;
573 "#,
574 );
575 do_check_noop(
576 r#"
577use path::$0Thing;
578 "#,
579 );
580 do_check_noop(
581 r#"
582use path::Thing$0};
583 "#,
584 );
585 do_check_noop(
586 r#"
587use path::{$0Thing;
588 "#,
589 );
590 }
591
592 #[test]
593 fn does_not_indent_multiline_use_tree_list() {
594 do_check_noop(
595 r#"
596use path::{$0
597 Thing
598};
599 "#,
600 );
601 }
487} 602}
diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs
new file mode 100644
index 000000000..5e4ba881e
--- /dev/null
+++ b/crates/ide/src/view_crate_graph.rs
@@ -0,0 +1,111 @@
1use std::{
2 error::Error,
3 io::{Read, Write},
4 process::{Command, Stdio},
5 sync::Arc,
6};
7
8use dot::{Id, LabelText};
9use ide_db::{
10 base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
11 RootDatabase,
12};
13use rustc_hash::FxHashSet;
14
15// Feature: View Crate Graph
16//
17// Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool, which
18// is part of graphviz, to be installed.
19//
20// Only workspace crates are included, no crates.io dependencies or sysroot crates.
21//
22// |===
23// | Editor | Action Name
24//
25// | VS Code | **Rust Analyzer: View Crate Graph**
26// |===
27pub(crate) fn view_crate_graph(db: &RootDatabase) -> Result<String, String> {
28 let crate_graph = db.crate_graph();
29 let crates_to_render = crate_graph
30 .iter()
31 .filter(|krate| {
32 // Only render workspace crates
33 let root_id = db.file_source_root(crate_graph[*krate].root_file_id);
34 !db.source_root(root_id).is_library
35 })
36 .collect();
37 let graph = DotCrateGraph { graph: crate_graph, crates_to_render };
38
39 let mut dot = Vec::new();
40 dot::render(&graph, &mut dot).unwrap();
41
42 render_svg(&dot).map_err(|e| e.to_string())
43}
44
45fn render_svg(dot: &[u8]) -> Result<String, Box<dyn Error>> {
46 // We shell out to `dot` to render to SVG, as there does not seem to be a pure-Rust renderer.
47 let child = Command::new("dot")
48 .arg("-Tsvg")
49 .stdin(Stdio::piped())
50 .stdout(Stdio::piped())
51 .spawn()
52 .map_err(|err| format!("failed to spawn `dot`: {}", err))?;
53 child.stdin.unwrap().write_all(&dot)?;
54
55 let mut svg = String::new();
56 child.stdout.unwrap().read_to_string(&mut svg)?;
57 Ok(svg)
58}
59
60struct DotCrateGraph {
61 graph: Arc<CrateGraph>,
62 crates_to_render: FxHashSet<CrateId>,
63}
64
65type Edge<'a> = (CrateId, &'a Dependency);
66
67impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph {
68 fn nodes(&'a self) -> dot::Nodes<'a, CrateId> {
69 self.crates_to_render.iter().copied().collect()
70 }
71
72 fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
73 self.crates_to_render
74 .iter()
75 .flat_map(|krate| {
76 self.graph[*krate]
77 .dependencies
78 .iter()
79 .filter(|dep| self.crates_to_render.contains(&dep.crate_id))
80 .map(move |dep| (*krate, dep))
81 })
82 .collect()
83 }
84
85 fn source(&'a self, edge: &Edge<'a>) -> CrateId {
86 edge.0
87 }
88
89 fn target(&'a self, edge: &Edge<'a>) -> CrateId {
90 edge.1.crate_id
91 }
92}
93
94impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
95 fn graph_id(&'a self) -> Id<'a> {
96 Id::new("rust_analyzer_crate_graph").unwrap()
97 }
98
99 fn node_id(&'a self, n: &CrateId) -> Id<'a> {
100 Id::new(format!("_{}", n.0)).unwrap()
101 }
102
103 fn node_shape(&'a self, _node: &CrateId) -> Option<LabelText<'a>> {
104 Some(LabelText::LabelStr("box".into()))
105 }
106
107 fn node_label(&'a self, n: &CrateId) -> LabelText<'a> {
108 let name = self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| &*name);
109 LabelText::LabelStr(name.into())
110 }
111}
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs
index 8714e4978..682f0ff5e 100644
--- a/crates/ide_assists/src/assist_context.rs
+++ b/crates/ide_assists/src/assist_context.rs
@@ -13,13 +13,15 @@ use ide_db::{
13 RootDatabase, 13 RootDatabase,
14}; 14};
15use syntax::{ 15use syntax::{
16 algo::{self, find_node_at_offset, find_node_at_range, SyntaxRewriter}, 16 algo::{self, find_node_at_offset, find_node_at_range},
17 AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr, 17 AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr,
18 SyntaxToken, TextRange, TextSize, TokenAtOffset, 18 SyntaxToken, TextRange, TextSize, TokenAtOffset,
19}; 19};
20use text_edit::{TextEdit, TextEditBuilder}; 20use text_edit::{TextEdit, TextEditBuilder};
21 21
22use crate::{assist_config::AssistConfig, Assist, AssistId, AssistKind, GroupLabel}; 22use crate::{
23 assist_config::AssistConfig, Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel,
24};
23 25
24/// `AssistContext` allows to apply an assist or check if it could be applied. 26/// `AssistContext` allows to apply an assist or check if it could be applied.
25/// 27///
@@ -105,14 +107,14 @@ impl<'a> AssistContext<'a> {
105} 107}
106 108
107pub(crate) struct Assists { 109pub(crate) struct Assists {
108 resolve: bool,
109 file: FileId, 110 file: FileId,
111 resolve: AssistResolveStrategy,
110 buf: Vec<Assist>, 112 buf: Vec<Assist>,
111 allowed: Option<Vec<AssistKind>>, 113 allowed: Option<Vec<AssistKind>>,
112} 114}
113 115
114impl Assists { 116impl Assists {
115 pub(crate) fn new(ctx: &AssistContext, resolve: bool) -> Assists { 117 pub(crate) fn new(ctx: &AssistContext, resolve: AssistResolveStrategy) -> Assists {
116 Assists { 118 Assists {
117 resolve, 119 resolve,
118 file: ctx.frange.file_id, 120 file: ctx.frange.file_id,
@@ -158,7 +160,7 @@ impl Assists {
158 } 160 }
159 161
160 fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { 162 fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
161 let source_change = if self.resolve { 163 let source_change = if self.resolve.should_resolve(&assist.id) {
162 let mut builder = AssistBuilder::new(self.file); 164 let mut builder = AssistBuilder::new(self.file);
163 f(&mut builder); 165 f(&mut builder);
164 Some(builder.finish()) 166 Some(builder.finish())
@@ -185,7 +187,29 @@ pub(crate) struct AssistBuilder {
185 source_change: SourceChange, 187 source_change: SourceChange,
186 188
187 /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. 189 /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
188 mutated_tree: Option<(SyntaxNode, SyntaxNode)>, 190 mutated_tree: Option<TreeMutator>,
191}
192
193pub(crate) struct TreeMutator {
194 immutable: SyntaxNode,
195 mutable_clone: SyntaxNode,
196}
197
198impl TreeMutator {
199 pub(crate) fn new(immutable: &SyntaxNode) -> TreeMutator {
200 let immutable = immutable.ancestors().last().unwrap();
201 let mutable_clone = immutable.clone_for_update();
202 TreeMutator { immutable, mutable_clone }
203 }
204
205 pub(crate) fn make_mut<N: AstNode>(&self, node: &N) -> N {
206 N::cast(self.make_syntax_mut(node.syntax())).unwrap()
207 }
208
209 pub(crate) fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode {
210 let ptr = SyntaxNodePtr::new(node);
211 ptr.to_node(&self.mutable_clone)
212 }
189} 213}
190 214
191impl AssistBuilder { 215impl AssistBuilder {
@@ -204,8 +228,8 @@ impl AssistBuilder {
204 } 228 }
205 229
206 fn commit(&mut self) { 230 fn commit(&mut self) {
207 if let Some((old, new)) = self.mutated_tree.take() { 231 if let Some(tm) = self.mutated_tree.take() {
208 algo::diff(&old, &new).into_text_edit(&mut self.edit) 232 algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit)
209 } 233 }
210 234
211 let edit = mem::take(&mut self.edit).finish(); 235 let edit = mem::take(&mut self.edit).finish();
@@ -228,16 +252,7 @@ impl AssistBuilder {
228 /// phase, and then get their mutable couterparts using `make_mut` in the 252 /// phase, and then get their mutable couterparts using `make_mut` in the
229 /// mutable state. 253 /// mutable state.
230 pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { 254 pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode {
231 let root = &self 255 self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node)
232 .mutated_tree
233 .get_or_insert_with(|| {
234 let immutable = node.ancestors().last().unwrap();
235 let mutable = immutable.clone_for_update();
236 (immutable, mutable)
237 })
238 .1;
239 let ptr = SyntaxNodePtr::new(&&node);
240 ptr.to_node(root)
241 } 256 }
242 257
243 /// Remove specified `range` of text. 258 /// Remove specified `range` of text.
@@ -275,12 +290,6 @@ impl AssistBuilder {
275 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { 290 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
276 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) 291 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
277 } 292 }
278 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
279 if let Some(node) = rewriter.rewrite_root() {
280 let new = rewriter.rewrite(&node);
281 algo::diff(&node, &new).into_text_edit(&mut self.edit);
282 }
283 }
284 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { 293 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) {
285 let file_system_edit = 294 let file_system_edit =
286 FileSystemEdit::CreateFile { dst: dst, initial_contents: content.into() }; 295 FileSystemEdit::CreateFile { dst: dst, initial_contents: content.into() };
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index b5b5ada5e..70949ca35 100644
--- a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -107,7 +107,7 @@ fn edit_struct_references(
107 names: &[ast::Name], 107 names: &[ast::Name],
108) { 108) {
109 let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); 109 let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt)));
110 let usages = strukt_def.usages(&ctx.sema).include_self_kw_refs(true).all(); 110 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
111 111
112 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { 112 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> {
113 match_ast! { 113 match_ast! {
diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs
index c66f8c05d..5eb6a57f0 100644
--- a/crates/ide_assists/src/handlers/early_return.rs
+++ b/crates/ide_assists/src/handlers/early_return.rs
@@ -130,9 +130,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
130 once(make::ident_pat(make::name("it")).into()), 130 once(make::ident_pat(make::name("it")).into()),
131 ); 131 );
132 let expr = { 132 let expr = {
133 let name_ref = make::name_ref("it"); 133 let path = make::ext::ident_path("it");
134 let segment = make::path_segment(name_ref);
135 let path = make::path_unqualified(segment);
136 make::expr_path(path) 134 make::expr_path(path)
137 }; 135 };
138 make::match_arm(once(pat.into()), expr) 136 make::match_arm(once(pat.into()), expr)
diff --git a/crates/ide_assists/src/handlers/expand_glob_import.rs b/crates/ide_assists/src/handlers/expand_glob_import.rs
index 98389e4f7..da8d245e5 100644
--- a/crates/ide_assists/src/handlers/expand_glob_import.rs
+++ b/crates/ide_assists/src/handlers/expand_glob_import.rs
@@ -4,10 +4,10 @@ use ide_db::{
4 defs::{Definition, NameRefClass}, 4 defs::{Definition, NameRefClass},
5 search::SearchScope, 5 search::SearchScope,
6}; 6};
7use stdx::never;
7use syntax::{ 8use syntax::{
8 algo::SyntaxRewriter,
9 ast::{self, make}, 9 ast::{self, make},
10 AstNode, Direction, SyntaxNode, SyntaxToken, T, 10 ted, AstNode, Direction, SyntaxNode, SyntaxToken, T,
11}; 11};
12 12
13use crate::{ 13use crate::{
@@ -42,6 +42,7 @@ use crate::{
42// ``` 42// ```
43pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 43pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
44 let star = ctx.find_token_syntax_at_offset(T![*])?; 44 let star = ctx.find_token_syntax_at_offset(T![*])?;
45 let use_tree = star.parent().and_then(ast::UseTree::cast)?;
45 let (parent, mod_path) = find_parent_and_path(&star)?; 46 let (parent, mod_path) = find_parent_and_path(&star)?;
46 let target_module = match ctx.sema.resolve_path(&mod_path)? { 47 let target_module = match ctx.sema.resolve_path(&mod_path)? {
47 PathResolution::Def(ModuleDef::Module(it)) => it, 48 PathResolution::Def(ModuleDef::Module(it)) => it,
@@ -53,7 +54,6 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti
53 54
54 let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?; 55 let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?;
55 let imported_defs = find_imported_defs(ctx, star)?; 56 let imported_defs = find_imported_defs(ctx, star)?;
56 let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
57 57
58 let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone()); 58 let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone());
59 acc.add( 59 acc.add(
@@ -61,9 +61,31 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti
61 "Expand glob import", 61 "Expand glob import",
62 target.text_range(), 62 target.text_range(),
63 |builder| { 63 |builder| {
64 let mut rewriter = SyntaxRewriter::default(); 64 let use_tree = builder.make_ast_mut(use_tree);
65 replace_ast(&mut rewriter, parent, mod_path, names_to_import); 65
66 builder.rewrite(rewriter); 66 let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
67 let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
68 let path = make::ext::ident_path(&n.to_string());
69 make::use_tree(path, None, None, false)
70 }))
71 .clone_for_update();
72
73 match use_tree.star_token() {
74 Some(star) => {
75 let needs_braces = use_tree.path().is_some() && names_to_import.len() > 1;
76 if needs_braces {
77 ted::replace(star, expanded.syntax())
78 } else {
79 let without_braces = expanded
80 .syntax()
81 .children_with_tokens()
82 .filter(|child| !matches!(child.kind(), T!['{'] | T!['}']))
83 .collect();
84 ted::replace_with_many(star, without_braces)
85 }
86 }
87 None => never!(),
88 }
67 }, 89 },
68 ) 90 )
69} 91}
@@ -232,53 +254,6 @@ fn find_names_to_import(
232 used_refs.0.iter().map(|r| r.visible_name.clone()).collect() 254 used_refs.0.iter().map(|r| r.visible_name.clone()).collect()
233} 255}
234 256
235fn replace_ast(
236 rewriter: &mut SyntaxRewriter,
237 parent: Either<ast::UseTree, ast::UseTreeList>,
238 path: ast::Path,
239 names_to_import: Vec<Name>,
240) {
241 let existing_use_trees = match parent.clone() {
242 Either::Left(_) => vec![],
243 Either::Right(u) => u
244 .use_trees()
245 .filter(|n|
246 // filter out star
247 n.star_token().is_none())
248 .collect(),
249 };
250
251 let new_use_trees: Vec<ast::UseTree> = names_to_import
252 .iter()
253 .map(|n| {
254 let path = make::path_unqualified(make::path_segment(make::name_ref(&n.to_string())));
255 make::use_tree(path, None, None, false)
256 })
257 .collect();
258
259 let use_trees = [&existing_use_trees[..], &new_use_trees[..]].concat();
260
261 match use_trees.as_slice() {
262 [name] => {
263 if let Some(end_path) = name.path() {
264 rewriter.replace_ast(
265 &parent.left_or_else(|tl| tl.parent_use_tree()),
266 &make::use_tree(make::path_concat(path, end_path), None, None, false),
267 );
268 }
269 }
270 names => match &parent {
271 Either::Left(parent) => rewriter.replace_ast(
272 parent,
273 &make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false),
274 ),
275 Either::Right(parent) => {
276 rewriter.replace_ast(parent, &make::use_tree_list(names.to_owned()))
277 }
278 },
279 };
280}
281
282#[cfg(test)] 257#[cfg(test)]
283mod tests { 258mod tests {
284 use crate::tests::{check_assist, check_assist_not_applicable}; 259 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -350,7 +325,7 @@ mod foo {
350 pub fn f() {} 325 pub fn f() {}
351} 326}
352 327
353use foo::{f, Baz, Bar}; 328use foo::{Baz, Bar, f};
354 329
355fn qux(bar: Bar, baz: Baz) { 330fn qux(bar: Bar, baz: Baz) {
356 f(); 331 f();
@@ -389,7 +364,7 @@ mod foo {
389} 364}
390 365
391use foo::Bar; 366use foo::Bar;
392use foo::{f, Baz}; 367use foo::{Baz, f};
393 368
394fn qux(bar: Bar, baz: Baz) { 369fn qux(bar: Bar, baz: Baz) {
395 f(); 370 f();
@@ -439,7 +414,7 @@ mod foo {
439 } 414 }
440} 415}
441 416
442use foo::{bar::{f, Baz, Bar}, baz::*}; 417use foo::{bar::{Baz, Bar, f}, baz::*};
443 418
444fn qux(bar: Bar, baz: Baz) { 419fn qux(bar: Bar, baz: Baz) {
445 f(); 420 f();
@@ -891,7 +866,7 @@ mod foo {
891 pub struct Bar; 866 pub struct Bar;
892} 867}
893 868
894use foo::Bar; 869use foo::{Bar};
895 870
896struct Baz { 871struct Baz {
897 bar: Bar 872 bar: Bar
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index b30652a9d..6311afc1f 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -10,18 +10,18 @@ use ide_db::{
10use itertools::Itertools; 10use itertools::Itertools;
11use stdx::format_to; 11use stdx::format_to;
12use syntax::{ 12use syntax::{
13 algo::SyntaxRewriter,
14 ast::{ 13 ast::{
15 self, 14 self,
16 edit::{AstNodeEdit, IndentLevel}, 15 edit::{AstNodeEdit, IndentLevel},
17 AstNode, 16 AstNode,
18 }, 17 },
18 ted,
19 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, 19 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR},
20 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, 20 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
21}; 21};
22 22
23use crate::{ 23use crate::{
24 assist_context::{AssistContext, Assists}, 24 assist_context::{AssistContext, Assists, TreeMutator},
25 AssistId, 25 AssistId,
26}; 26};
27 27
@@ -956,10 +956,10 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
956 let args = fun.params.iter().map(|param| param.to_arg(ctx)); 956 let args = fun.params.iter().map(|param| param.to_arg(ctx));
957 let args = make::arg_list(args); 957 let args = make::arg_list(args);
958 let call_expr = if fun.self_param.is_some() { 958 let call_expr = if fun.self_param.is_some() {
959 let self_arg = make::expr_path(make_path_from_text("self")); 959 let self_arg = make::expr_path(make::ext::ident_path("self"));
960 make::expr_method_call(self_arg, &fun.name, args) 960 make::expr_method_call(self_arg, &fun.name, args)
961 } else { 961 } else {
962 let func = make::expr_path(make_path_from_text(&fun.name)); 962 let func = make::expr_path(make::ext::ident_path(&fun.name));
963 make::expr_call(func, args) 963 make::expr_call(func, args)
964 }; 964 };
965 965
@@ -1054,11 +1054,11 @@ impl FlowHandler {
1054 make::expr_if(condition, block, None) 1054 make::expr_if(condition, block, None)
1055 } 1055 }
1056 FlowHandler::IfOption { action } => { 1056 FlowHandler::IfOption { action } => {
1057 let path = make_path_from_text("Some"); 1057 let path = make::ext::ident_path("Some");
1058 let value_pat = make::ident_pat(make::name("value")); 1058 let value_pat = make::ident_pat(make::name("value"));
1059 let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1059 let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1060 let cond = make::condition(call_expr, Some(pattern.into())); 1060 let cond = make::condition(call_expr, Some(pattern.into()));
1061 let value = make::expr_path(make_path_from_text("value")); 1061 let value = make::expr_path(make::ext::ident_path("value"));
1062 let action_expr = action.make_result_handler(Some(value)); 1062 let action_expr = action.make_result_handler(Some(value));
1063 let action_stmt = make::expr_stmt(action_expr); 1063 let action_stmt = make::expr_stmt(action_expr);
1064 let then = make::block_expr(iter::once(action_stmt.into()), None); 1064 let then = make::block_expr(iter::once(action_stmt.into()), None);
@@ -1068,14 +1068,14 @@ impl FlowHandler {
1068 let some_name = "value"; 1068 let some_name = "value";
1069 1069
1070 let some_arm = { 1070 let some_arm = {
1071 let path = make_path_from_text("Some"); 1071 let path = make::ext::ident_path("Some");
1072 let value_pat = make::ident_pat(make::name(some_name)); 1072 let value_pat = make::ident_pat(make::name(some_name));
1073 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1073 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1074 let value = make::expr_path(make_path_from_text(some_name)); 1074 let value = make::expr_path(make::ext::ident_path(some_name));
1075 make::match_arm(iter::once(pat.into()), value) 1075 make::match_arm(iter::once(pat.into()), value)
1076 }; 1076 };
1077 let none_arm = { 1077 let none_arm = {
1078 let path = make_path_from_text("None"); 1078 let path = make::ext::ident_path("None");
1079 let pat = make::path_pat(path); 1079 let pat = make::path_pat(path);
1080 make::match_arm(iter::once(pat), none.make_result_handler(None)) 1080 make::match_arm(iter::once(pat), none.make_result_handler(None))
1081 }; 1081 };
@@ -1087,17 +1087,17 @@ impl FlowHandler {
1087 let err_name = "value"; 1087 let err_name = "value";
1088 1088
1089 let ok_arm = { 1089 let ok_arm = {
1090 let path = make_path_from_text("Ok"); 1090 let path = make::ext::ident_path("Ok");
1091 let value_pat = make::ident_pat(make::name(ok_name)); 1091 let value_pat = make::ident_pat(make::name(ok_name));
1092 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1092 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1093 let value = make::expr_path(make_path_from_text(ok_name)); 1093 let value = make::expr_path(make::ext::ident_path(ok_name));
1094 make::match_arm(iter::once(pat.into()), value) 1094 make::match_arm(iter::once(pat.into()), value)
1095 }; 1095 };
1096 let err_arm = { 1096 let err_arm = {
1097 let path = make_path_from_text("Err"); 1097 let path = make::ext::ident_path("Err");
1098 let value_pat = make::ident_pat(make::name(err_name)); 1098 let value_pat = make::ident_pat(make::name(err_name));
1099 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1099 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1100 let value = make::expr_path(make_path_from_text(err_name)); 1100 let value = make::expr_path(make::ext::ident_path(err_name));
1101 make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value))) 1101 make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value)))
1102 }; 1102 };
1103 let arms = make::match_arm_list(vec![ok_arm, err_arm]); 1103 let arms = make::match_arm_list(vec![ok_arm, err_arm]);
@@ -1107,13 +1107,9 @@ impl FlowHandler {
1107 } 1107 }
1108} 1108}
1109 1109
1110fn make_path_from_text(text: &str) -> ast::Path {
1111 make::path_unqualified(make::path_segment(make::name_ref(text)))
1112}
1113
1114fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { 1110fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr {
1115 let name = var.name(ctx.db()).unwrap().to_string(); 1111 let name = var.name(ctx.db()).unwrap().to_string();
1116 make::expr_path(make_path_from_text(&name)) 1112 make::expr_path(make::ext::ident_path(&name))
1117} 1113}
1118 1114
1119fn format_function( 1115fn format_function(
@@ -1179,7 +1175,7 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti
1179 fun_ty.make_ty(ctx, module) 1175 fun_ty.make_ty(ctx, module)
1180 } 1176 }
1181 FlowHandler::Try { kind: TryKind::Option } => { 1177 FlowHandler::Try { kind: TryKind::Option } => {
1182 make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module))) 1178 make::ext::ty_option(fun_ty.make_ty(ctx, module))
1183 } 1179 }
1184 FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { 1180 FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => {
1185 let handler_ty = parent_ret_ty 1181 let handler_ty = parent_ret_ty
@@ -1187,29 +1183,21 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti
1187 .nth(1) 1183 .nth(1)
1188 .map(|ty| make_ty(&ty, ctx, module)) 1184 .map(|ty| make_ty(&ty, ctx, module))
1189 .unwrap_or_else(make::ty_unit); 1185 .unwrap_or_else(make::ty_unit);
1190 make::ty_generic( 1186 make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
1191 make::name_ref("Result"),
1192 vec![fun_ty.make_ty(ctx, module), handler_ty],
1193 )
1194 } 1187 }
1195 FlowHandler::If { .. } => make::ty("bool"), 1188 FlowHandler::If { .. } => make::ext::ty_bool(),
1196 FlowHandler::IfOption { action } => { 1189 FlowHandler::IfOption { action } => {
1197 let handler_ty = action 1190 let handler_ty = action
1198 .expr_ty(ctx) 1191 .expr_ty(ctx)
1199 .map(|ty| make_ty(&ty, ctx, module)) 1192 .map(|ty| make_ty(&ty, ctx, module))
1200 .unwrap_or_else(make::ty_unit); 1193 .unwrap_or_else(make::ty_unit);
1201 make::ty_generic(make::name_ref("Option"), iter::once(handler_ty)) 1194 make::ext::ty_option(handler_ty)
1202 }
1203 FlowHandler::MatchOption { .. } => {
1204 make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module)))
1205 } 1195 }
1196 FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)),
1206 FlowHandler::MatchResult { err } => { 1197 FlowHandler::MatchResult { err } => {
1207 let handler_ty = 1198 let handler_ty =
1208 err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit); 1199 err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit);
1209 make::ty_generic( 1200 make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
1210 make::name_ref("Result"),
1211 vec![fun_ty.make_ty(ctx, module), handler_ty],
1212 )
1213 } 1201 }
1214 }; 1202 };
1215 Some(make::ret_type(ret_ty)) 1203 Some(make::ret_type(ret_ty))
@@ -1296,7 +1284,7 @@ fn make_body(
1296 TryKind::Option => "Some", 1284 TryKind::Option => "Some",
1297 TryKind::Result { .. } => "Ok", 1285 TryKind::Result { .. } => "Ok",
1298 }; 1286 };
1299 let func = make::expr_path(make_path_from_text(constructor)); 1287 let func = make::expr_path(make::ext::ident_path(constructor));
1300 let args = make::arg_list(iter::once(tail_expr)); 1288 let args = make::arg_list(iter::once(tail_expr));
1301 make::expr_call(func, args) 1289 make::expr_call(func, args)
1302 }) 1290 })
@@ -1306,16 +1294,16 @@ fn make_body(
1306 with_tail_expr(block, lit_false.into()) 1294 with_tail_expr(block, lit_false.into())
1307 } 1295 }
1308 FlowHandler::IfOption { .. } => { 1296 FlowHandler::IfOption { .. } => {
1309 let none = make::expr_path(make_path_from_text("None")); 1297 let none = make::expr_path(make::ext::ident_path("None"));
1310 with_tail_expr(block, none) 1298 with_tail_expr(block, none)
1311 } 1299 }
1312 FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { 1300 FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| {
1313 let some = make::expr_path(make_path_from_text("Some")); 1301 let some = make::expr_path(make::ext::ident_path("Some"));
1314 let args = make::arg_list(iter::once(tail_expr)); 1302 let args = make::arg_list(iter::once(tail_expr));
1315 make::expr_call(some, args) 1303 make::expr_call(some, args)
1316 }), 1304 }),
1317 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { 1305 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| {
1318 let ok = make::expr_path(make_path_from_text("Ok")); 1306 let ok = make::expr_path(make::ext::ident_path("Ok"));
1319 let args = make::arg_list(iter::once(tail_expr)); 1307 let args = make::arg_list(iter::once(tail_expr));
1320 make::expr_call(ok, args) 1308 make::expr_call(ok, args)
1321 }), 1309 }),
@@ -1361,12 +1349,16 @@ fn rewrite_body_segment(
1361 syntax: &SyntaxNode, 1349 syntax: &SyntaxNode,
1362) -> SyntaxNode { 1350) -> SyntaxNode {
1363 let syntax = fix_param_usages(ctx, params, syntax); 1351 let syntax = fix_param_usages(ctx, params, syntax);
1364 update_external_control_flow(handler, &syntax) 1352 update_external_control_flow(handler, &syntax);
1353 syntax
1365} 1354}
1366 1355
1367/// change all usages to account for added `&`/`&mut` for some params 1356/// change all usages to account for added `&`/`&mut` for some params
1368fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { 1357fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode {
1369 let mut rewriter = SyntaxRewriter::default(); 1358 let mut usages_for_param: Vec<(&Param, Vec<ast::Expr>)> = Vec::new();
1359
1360 let tm = TreeMutator::new(syntax);
1361
1370 for param in params { 1362 for param in params {
1371 if !param.kind().is_ref() { 1363 if !param.kind().is_ref() {
1372 continue; 1364 continue;
@@ -1376,101 +1368,100 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode)
1376 let usages = usages 1368 let usages = usages
1377 .iter() 1369 .iter()
1378 .filter(|reference| syntax.text_range().contains_range(reference.range)) 1370 .filter(|reference| syntax.text_range().contains_range(reference.range))
1379 .filter_map(|reference| path_element_of_reference(syntax, reference)); 1371 .filter_map(|reference| path_element_of_reference(syntax, reference))
1380 for path in usages { 1372 .map(|expr| tm.make_mut(&expr));
1381 match path.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { 1373
1374 usages_for_param.push((param, usages.collect()));
1375 }
1376
1377 let res = tm.make_syntax_mut(syntax);
1378
1379 for (param, usages) in usages_for_param {
1380 for usage in usages {
1381 match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) {
1382 Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { 1382 Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => {
1383 // do nothing 1383 // do nothing
1384 } 1384 }
1385 Some(ast::Expr::RefExpr(node)) 1385 Some(ast::Expr::RefExpr(node))
1386 if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => 1386 if param.kind() == ParamKind::MutRef && node.mut_token().is_some() =>
1387 { 1387 {
1388 rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); 1388 ted::replace(node.syntax(), node.expr().unwrap().syntax());
1389 } 1389 }
1390 Some(ast::Expr::RefExpr(node)) 1390 Some(ast::Expr::RefExpr(node))
1391 if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => 1391 if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() =>
1392 { 1392 {
1393 rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); 1393 ted::replace(node.syntax(), node.expr().unwrap().syntax());
1394 } 1394 }
1395 Some(_) | None => { 1395 Some(_) | None => {
1396 rewriter.replace_ast(&path, &make::expr_prefix(T![*], path.clone())); 1396 let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update();
1397 ted::replace(usage.syntax(), p.syntax())
1397 } 1398 }
1398 }; 1399 }
1399 } 1400 }
1400 } 1401 }
1401 1402
1402 rewriter.rewrite(syntax) 1403 res
1403} 1404}
1404 1405
1405fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { 1406fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) {
1406 let mut rewriter = SyntaxRewriter::default();
1407
1408 let mut nested_loop = None; 1407 let mut nested_loop = None;
1409 let mut nested_scope = None; 1408 let mut nested_scope = None;
1410 for event in syntax.preorder() { 1409 for event in syntax.preorder() {
1411 let node = match event { 1410 match event {
1412 WalkEvent::Enter(e) => { 1411 WalkEvent::Enter(e) => match e.kind() {
1413 match e.kind() { 1412 SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => {
1414 SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { 1413 if nested_loop.is_none() {
1415 if nested_loop.is_none() { 1414 nested_loop = Some(e.clone());
1416 nested_loop = Some(e.clone());
1417 }
1418 } 1415 }
1419 SyntaxKind::FN 1416 }
1420 | SyntaxKind::CONST 1417 SyntaxKind::FN
1421 | SyntaxKind::STATIC 1418 | SyntaxKind::CONST
1422 | SyntaxKind::IMPL 1419 | SyntaxKind::STATIC
1423 | SyntaxKind::MODULE => { 1420 | SyntaxKind::IMPL
1424 if nested_scope.is_none() { 1421 | SyntaxKind::MODULE => {
1425 nested_scope = Some(e.clone()); 1422 if nested_scope.is_none() {
1426 } 1423 nested_scope = Some(e.clone());
1427 } 1424 }
1428 _ => {}
1429 } 1425 }
1430 e 1426 _ => {}
1431 } 1427 },
1432 WalkEvent::Leave(e) => { 1428 WalkEvent::Leave(e) => {
1429 if nested_scope.is_none() {
1430 if let Some(expr) = ast::Expr::cast(e.clone()) {
1431 match expr {
1432 ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => {
1433 let expr = return_expr.expr();
1434 if let Some(replacement) = make_rewritten_flow(handler, expr) {
1435 ted::replace(return_expr.syntax(), replacement.syntax())
1436 }
1437 }
1438 ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => {
1439 let expr = break_expr.expr();
1440 if let Some(replacement) = make_rewritten_flow(handler, expr) {
1441 ted::replace(break_expr.syntax(), replacement.syntax())
1442 }
1443 }
1444 ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => {
1445 if let Some(replacement) = make_rewritten_flow(handler, None) {
1446 ted::replace(continue_expr.syntax(), replacement.syntax())
1447 }
1448 }
1449 _ => {
1450 // do nothing
1451 }
1452 }
1453 }
1454 }
1455
1433 if nested_loop.as_ref() == Some(&e) { 1456 if nested_loop.as_ref() == Some(&e) {
1434 nested_loop = None; 1457 nested_loop = None;
1435 } 1458 }
1436 if nested_scope.as_ref() == Some(&e) { 1459 if nested_scope.as_ref() == Some(&e) {
1437 nested_scope = None; 1460 nested_scope = None;
1438 } 1461 }
1439 continue;
1440 } 1462 }
1441 }; 1463 };
1442 if nested_scope.is_some() {
1443 continue;
1444 }
1445 let expr = match ast::Expr::cast(node) {
1446 Some(e) => e,
1447 None => continue,
1448 };
1449 match expr {
1450 ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => {
1451 let expr = return_expr.expr();
1452 if let Some(replacement) = make_rewritten_flow(handler, expr) {
1453 rewriter.replace_ast(&return_expr.into(), &replacement);
1454 }
1455 }
1456 ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => {
1457 let expr = break_expr.expr();
1458 if let Some(replacement) = make_rewritten_flow(handler, expr) {
1459 rewriter.replace_ast(&break_expr.into(), &replacement);
1460 }
1461 }
1462 ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => {
1463 if let Some(replacement) = make_rewritten_flow(handler, None) {
1464 rewriter.replace_ast(&continue_expr.into(), &replacement);
1465 }
1466 }
1467 _ => {
1468 // do nothing
1469 }
1470 }
1471 } 1464 }
1472
1473 rewriter.rewrite(syntax)
1474} 1465}
1475 1466
1476fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { 1467fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> {
@@ -1480,16 +1471,16 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op
1480 FlowHandler::IfOption { .. } => { 1471 FlowHandler::IfOption { .. } => {
1481 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); 1472 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
1482 let args = make::arg_list(iter::once(expr)); 1473 let args = make::arg_list(iter::once(expr));
1483 make::expr_call(make::expr_path(make_path_from_text("Some")), args) 1474 make::expr_call(make::expr_path(make::ext::ident_path("Some")), args)
1484 } 1475 }
1485 FlowHandler::MatchOption { .. } => make::expr_path(make_path_from_text("None")), 1476 FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")),
1486 FlowHandler::MatchResult { .. } => { 1477 FlowHandler::MatchResult { .. } => {
1487 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); 1478 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
1488 let args = make::arg_list(iter::once(expr)); 1479 let args = make::arg_list(iter::once(expr));
1489 make::expr_call(make::expr_path(make_path_from_text("Err")), args) 1480 make::expr_call(make::expr_path(make::ext::ident_path("Err")), args)
1490 } 1481 }
1491 }; 1482 };
1492 Some(make::expr_return(Some(value))) 1483 Some(make::expr_return(Some(value)).clone_for_update())
1493} 1484}
1494 1485
1495#[cfg(test)] 1486#[cfg(test)]
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index 136b9a55b..ae084c86c 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -54,7 +54,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
54 54
55 let var_name = match &field_shorthand { 55 let var_name = match &field_shorthand {
56 Some(it) => it.to_string(), 56 Some(it) => it.to_string(),
57 None => suggest_name::variable(&to_extract, &ctx.sema), 57 None => suggest_name::for_variable(&to_extract, &ctx.sema),
58 }; 58 };
59 let expr_range = match &field_shorthand { 59 let expr_range = match &field_shorthand {
60 Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), 60 Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()),
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index 6f95b1a07..bc9fc524b 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -175,7 +175,7 @@ impl FunctionBuilder {
175 } 175 }
176 176
177 fn render(self) -> FunctionTemplate { 177 fn render(self) -> FunctionTemplate {
178 let placeholder_expr = make::expr_todo(); 178 let placeholder_expr = make::ext::expr_todo();
179 let fn_body = make::block_expr(vec![], Some(placeholder_expr)); 179 let fn_body = make::block_expr(vec![], Some(placeholder_expr));
180 let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; 180 let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
181 let mut fn_def = make::fn_( 181 let mut fn_def = make::fn_(
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
index 9f4f71d6c..68bc15120 100644
--- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
@@ -89,14 +89,12 @@ fn generate_fn_def_assist(
89 let loc_needing_lifetime = 89 let loc_needing_lifetime =
90 loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); 90 loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position());
91 91
92 add_lifetime_param(fn_def.get_or_create_generic_param_list(), new_lifetime_param); 92 fn_def.get_or_create_generic_param_list().add_generic_param(
93 ted::replace( 93 make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(),
94 lifetime.syntax(),
95 make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(),
96 ); 94 );
97 loc_needing_lifetime.map(|position| { 95 ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax());
98 ted::insert(position, make_ast_lifetime(new_lifetime_param).clone_for_update().syntax()) 96 loc_needing_lifetime
99 }); 97 .map(|position| ted::insert(position, new_lifetime_param.clone_for_update().syntax()));
100 }) 98 })
101} 99}
102 100
@@ -112,11 +110,10 @@ fn generate_impl_def_assist(
112 let impl_def = builder.make_ast_mut(impl_def); 110 let impl_def = builder.make_ast_mut(impl_def);
113 let lifetime = builder.make_ast_mut(lifetime); 111 let lifetime = builder.make_ast_mut(lifetime);
114 112
115 add_lifetime_param(impl_def.get_or_create_generic_param_list(), new_lifetime_param); 113 impl_def.get_or_create_generic_param_list().add_generic_param(
116 ted::replace( 114 make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(),
117 lifetime.syntax(),
118 make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(),
119 ); 115 );
116 ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax());
120 }) 117 })
121} 118}
122 119
@@ -124,31 +121,16 @@ fn generate_impl_def_assist(
124/// which is not in the list 121/// which is not in the list
125fn generate_unique_lifetime_param_name( 122fn generate_unique_lifetime_param_name(
126 existing_type_param_list: Option<ast::GenericParamList>, 123 existing_type_param_list: Option<ast::GenericParamList>,
127) -> Option<char> { 124) -> Option<ast::Lifetime> {
128 match existing_type_param_list { 125 match existing_type_param_list {
129 Some(type_params) => { 126 Some(type_params) => {
130 let used_lifetime_params: FxHashSet<_> = type_params 127 let used_lifetime_params: FxHashSet<_> =
131 .lifetime_params() 128 type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect();
132 .map(|p| p.syntax().text().to_string()[1..].to_owned()) 129 ('a'..='z').map(|it| format!("'{}", it)).find(|it| !used_lifetime_params.contains(it))
133 .collect();
134 (b'a'..=b'z').map(char::from).find(|c| !used_lifetime_params.contains(&c.to_string()))
135 } 130 }
136 None => Some('a'), 131 None => Some("'a".to_string()),
137 } 132 }
138} 133 .map(|it| make::lifetime(&it))
139
140fn add_lifetime_param(type_params: ast::GenericParamList, new_lifetime_param: char) {
141 let generic_param =
142 make::generic_param(format!("'{}", new_lifetime_param), None).clone_for_update();
143 type_params.add_generic_param(generic_param);
144}
145
146fn make_ast_lifetime(new_lifetime_param: char) -> ast::Lifetime {
147 make::generic_param(format!("'{}", new_lifetime_param), None)
148 .syntax()
149 .descendants()
150 .find_map(ast::Lifetime::cast)
151 .unwrap()
152} 134}
153 135
154enum NeedsLifetime { 136enum NeedsLifetime {
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs
index add7b8e37..3cd090737 100644
--- a/crates/ide_assists/src/handlers/merge_imports.rs
+++ b/crates/ide_assists/src/handlers/merge_imports.rs
@@ -27,14 +27,14 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
27 if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) { 27 if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) {
28 let (merged, to_remove) = 28 let (merged, to_remove) =
29 next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| { 29 next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| {
30 try_merge_imports(&use_item, &use_item2, MergeBehavior::Full).zip(Some(use_item2)) 30 try_merge_imports(&use_item, &use_item2, MergeBehavior::Crate).zip(Some(use_item2))
31 })?; 31 })?;
32 32
33 imports = Some((use_item, merged, to_remove)); 33 imports = Some((use_item, merged, to_remove));
34 } else { 34 } else {
35 let (merged, to_remove) = 35 let (merged, to_remove) =
36 next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| { 36 next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| {
37 try_merge_trees(&tree, &use_tree, MergeBehavior::Full).zip(Some(use_tree)) 37 try_merge_trees(&tree, &use_tree, MergeBehavior::Crate).zip(Some(use_tree))
38 })?; 38 })?;
39 39
40 uses = Some((tree.clone(), merged, to_remove)) 40 uses = Some((tree.clone(), merged, to_remove))
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs
index 011a28d44..fa3f76609 100644
--- a/crates/ide_assists/src/handlers/move_bounds.rs
+++ b/crates/ide_assists/src/handlers/move_bounds.rs
@@ -63,11 +63,7 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext
63} 63}
64 64
65fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { 65fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
66 let path = { 66 let path = make::ext::ident_path(&param.name()?.syntax().to_string());
67 let name_ref = make::name_ref(&param.name()?.syntax().to_string());
68 let segment = make::path_segment(name_ref);
69 make::path_unqualified(segment)
70 };
71 let predicate = make::where_pred(path, param.type_bound_list()?.bounds()); 67 let predicate = make::where_pred(path, param.type_bound_list()?.bounds());
72 Some(predicate.clone_for_update()) 68 Some(predicate.clone_for_update())
73} 69}
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs
index 04bae4e58..3128faa68 100644
--- a/crates/ide_assists/src/handlers/pull_assignment_up.rs
+++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs
@@ -1,6 +1,6 @@
1use syntax::{ 1use syntax::{
2 ast::{self, edit::AstNodeEdit, make}, 2 ast::{self, make},
3 AstNode, 3 ted, AstNode,
4}; 4};
5 5
6use crate::{ 6use crate::{
@@ -37,103 +37,120 @@ use crate::{
37// ``` 37// ```
38pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 38pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?; 39 let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
40 let name_expr = if assign_expr.op_kind()? == ast::BinOp::Assignment { 40
41 assign_expr.lhs()? 41 let op_kind = assign_expr.op_kind()?;
42 } else { 42 if op_kind != ast::BinOp::Assignment {
43 cov_mark::hit!(test_cant_pull_non_assignments);
43 return None; 44 return None;
45 }
46
47 let mut collector = AssignmentsCollector {
48 sema: &ctx.sema,
49 common_lhs: assign_expr.lhs()?,
50 assignments: Vec::new(),
44 }; 51 };
45 52
46 let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { 53 let tgt: ast::Expr = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
47 ( 54 collector.collect_if(&if_expr)?;
48 ast::Expr::cast(if_expr.syntax().to_owned())?, 55 if_expr.into()
49 exprify_if(&if_expr, &ctx.sema, &name_expr)?.indent(if_expr.indent_level()),
50 )
51 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { 56 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
52 ( 57 collector.collect_match(&match_expr)?;
53 ast::Expr::cast(match_expr.syntax().to_owned())?, 58 match_expr.into()
54 exprify_match(&match_expr, &ctx.sema, &name_expr)?,
55 )
56 } else { 59 } else {
57 return None; 60 return None;
58 }; 61 };
59 62
60 let expr_stmt = make::expr_stmt(new_stmt); 63 if let Some(parent) = tgt.syntax().parent() {
64 if matches!(parent.kind(), syntax::SyntaxKind::BIN_EXPR | syntax::SyntaxKind::LET_STMT) {
65 return None;
66 }
67 }
61 68
62 acc.add( 69 acc.add(
63 AssistId("pull_assignment_up", AssistKind::RefactorExtract), 70 AssistId("pull_assignment_up", AssistKind::RefactorExtract),
64 "Pull assignment up", 71 "Pull assignment up",
65 old_stmt.syntax().text_range(), 72 tgt.syntax().text_range(),
66 move |edit| { 73 move |edit| {
67 edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name_expr, expr_stmt)); 74 let assignments: Vec<_> = collector
75 .assignments
76 .into_iter()
77 .map(|(stmt, rhs)| (edit.make_ast_mut(stmt), rhs.clone_for_update()))
78 .collect();
79
80 let tgt = edit.make_ast_mut(tgt);
81
82 for (stmt, rhs) in assignments {
83 let mut stmt = stmt.syntax().clone();
84 if let Some(parent) = stmt.parent() {
85 if ast::ExprStmt::cast(parent.clone()).is_some() {
86 stmt = parent.clone();
87 }
88 }
89 ted::replace(stmt, rhs.syntax());
90 }
91 let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone());
92 let assign_stmt = make::expr_stmt(assign_expr);
93
94 ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update());
68 }, 95 },
69 ) 96 )
70} 97}
71 98
72fn exprify_match( 99struct AssignmentsCollector<'a> {
73 match_expr: &ast::MatchExpr, 100 sema: &'a hir::Semantics<'a, ide_db::RootDatabase>,
74 sema: &hir::Semantics<ide_db::RootDatabase>, 101 common_lhs: ast::Expr,
75 name: &ast::Expr, 102 assignments: Vec<(ast::BinExpr, ast::Expr)>,
76) -> Option<ast::Expr> { 103}
77 let new_arm_list = match_expr 104
78 .match_arm_list()? 105impl<'a> AssignmentsCollector<'a> {
79 .arms() 106 fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> {
80 .map(|arm| { 107 for arm in match_expr.match_arm_list()?.arms() {
81 if let ast::Expr::BlockExpr(block) = arm.expr()? { 108 match arm.expr()? {
82 let new_block = exprify_block(&block, sema, name)?.indent(block.indent_level()); 109 ast::Expr::BlockExpr(block) => self.collect_block(&block)?,
83 Some(arm.replace_descendant(block, new_block)) 110 ast::Expr::BinExpr(expr) => self.collect_expr(&expr)?,
111 _ => return None,
112 }
113 }
114
115 Some(())
116 }
117 fn collect_if(&mut self, if_expr: &ast::IfExpr) -> Option<()> {
118 let then_branch = if_expr.then_branch()?;
119 self.collect_block(&then_branch)?;
120
121 match if_expr.else_branch()? {
122 ast::ElseBranch::Block(block) => self.collect_block(&block),
123 ast::ElseBranch::IfExpr(expr) => {
124 cov_mark::hit!(test_pull_assignment_up_chained_if);
125 self.collect_if(&expr)
126 }
127 }
128 }
129 fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> {
130 let last_expr = block.tail_expr().or_else(|| {
131 if let ast::Stmt::ExprStmt(stmt) = block.statements().last()? {
132 stmt.expr()
84 } else { 133 } else {
85 None 134 None
86 } 135 }
87 }) 136 })?;
88 .collect::<Option<Vec<_>>>()?;
89 let new_arm_list = match_expr
90 .match_arm_list()?
91 .replace_descendants(match_expr.match_arm_list()?.arms().zip(new_arm_list));
92 Some(make::expr_match(match_expr.expr()?, new_arm_list))
93}
94 137
95fn exprify_if( 138 if let ast::Expr::BinExpr(expr) = last_expr {
96 statement: &ast::IfExpr, 139 return self.collect_expr(&expr);
97 sema: &hir::Semantics<ide_db::RootDatabase>,
98 name: &ast::Expr,
99) -> Option<ast::Expr> {
100 let then_branch = exprify_block(&statement.then_branch()?, sema, name)?;
101 let else_branch = match statement.else_branch()? {
102 ast::ElseBranch::Block(ref block) => {
103 ast::ElseBranch::Block(exprify_block(block, sema, name)?)
104 }
105 ast::ElseBranch::IfExpr(expr) => {
106 cov_mark::hit!(test_pull_assignment_up_chained_if);
107 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
108 exprify_if(&expr, sema, name)?.syntax().to_owned(),
109 )?)
110 } 140 }
111 };
112 Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch)))
113}
114 141
115fn exprify_block( 142 None
116 block: &ast::BlockExpr,
117 sema: &hir::Semantics<ide_db::RootDatabase>,
118 name: &ast::Expr,
119) -> Option<ast::BlockExpr> {
120 if block.tail_expr().is_some() {
121 return None;
122 } 143 }
123 144
124 let mut stmts: Vec<_> = block.statements().collect(); 145 fn collect_expr(&mut self, expr: &ast::BinExpr) -> Option<()> {
125 let stmt = stmts.pop()?; 146 if expr.op_kind()? == ast::BinOp::Assignment
126 147 && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs)
127 if let ast::Stmt::ExprStmt(stmt) = stmt { 148 {
128 if let ast::Expr::BinExpr(expr) = stmt.expr()? { 149 self.assignments.push((expr.clone(), expr.rhs()?));
129 if expr.op_kind()? == ast::BinOp::Assignment && is_equivalent(sema, &expr.lhs()?, name) 150 return Some(());
130 {
131 // The last statement in the block is an assignment to the name we want
132 return Some(make::block_expr(stmts, Some(expr.rhs()?)));
133 }
134 } 151 }
152 None
135 } 153 }
136 None
137} 154}
138 155
139fn is_equivalent( 156fn is_equivalent(
@@ -243,6 +260,37 @@ fn foo() {
243 } 260 }
244 261
245 #[test] 262 #[test]
263 fn test_pull_assignment_up_assignment_expressions() {
264 check_assist(
265 pull_assignment_up,
266 r#"
267fn foo() {
268 let mut a = 1;
269
270 match 1 {
271 1 => { $0a = 2; },
272 2 => a = 3,
273 3 => {
274 a = 4
275 }
276 }
277}"#,
278 r#"
279fn foo() {
280 let mut a = 1;
281
282 a = match 1 {
283 1 => { 2 },
284 2 => 3,
285 3 => {
286 4
287 }
288 };
289}"#,
290 );
291 }
292
293 #[test]
246 fn test_pull_assignment_up_not_last_not_applicable() { 294 fn test_pull_assignment_up_not_last_not_applicable() {
247 check_assist_not_applicable( 295 check_assist_not_applicable(
248 pull_assignment_up, 296 pull_assignment_up,
@@ -439,4 +487,24 @@ fn foo() {
439"#, 487"#,
440 ) 488 )
441 } 489 }
490
491 #[test]
492 fn test_cant_pull_non_assignments() {
493 cov_mark::check!(test_cant_pull_non_assignments);
494 check_assist_not_applicable(
495 pull_assignment_up,
496 r#"
497fn foo() {
498 let mut a = 1;
499 let b = &mut a;
500
501 if true {
502 $0*b + 2;
503 } else {
504 *b + 3;
505 }
506}
507"#,
508 )
509 }
442} 510}
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs
index 72d889248..54a9a468e 100644
--- a/crates/ide_assists/src/handlers/reorder_impl.rs
+++ b/crates/ide_assists/src/handlers/reorder_impl.rs
@@ -79,9 +79,12 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
79 "Sort methods", 79 "Sort methods",
80 target, 80 target,
81 |builder| { 81 |builder| {
82 methods.into_iter().zip(sorted).for_each(|(old, new)| { 82 let methods =
83 ted::replace(builder.make_ast_mut(old).syntax(), new.clone_for_update().syntax()) 83 methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>();
84 }); 84 methods
85 .into_iter()
86 .zip(sorted)
87 .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax()));
85 }, 88 },
86 ) 89 )
87} 90}
@@ -160,7 +163,7 @@ $0impl Bar for Foo {}
160 } 163 }
161 164
162 #[test] 165 #[test]
163 fn reorder_impl_trait_methods() { 166 fn reorder_impl_trait_functions() {
164 check_assist( 167 check_assist(
165 reorder_impl, 168 reorder_impl,
166 r#" 169 r#"
@@ -197,4 +200,33 @@ impl Bar for Foo {
197 "#, 200 "#,
198 ) 201 )
199 } 202 }
203
204 #[test]
205 fn reorder_impl_trait_methods_uneven_ident_lengths() {
206 check_assist(
207 reorder_impl,
208 r#"
209trait Bar {
210 fn foo(&mut self) {}
211 fn fooo(&mut self) {}
212}
213
214struct Foo;
215impl Bar for Foo {
216 fn fooo(&mut self) {}
217 fn foo(&mut self) {$0}
218}"#,
219 r#"
220trait Bar {
221 fn foo(&mut self) {}
222 fn fooo(&mut self) {}
223}
224
225struct Foo;
226impl Bar for Foo {
227 fn foo(&mut self) {}
228 fn fooo(&mut self) {}
229}"#,
230 )
231 }
200} 232}
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 694d897d1..10d9cec31 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
@@ -84,7 +84,7 @@ pub(crate) fn replace_derive_with_manual_impl(
84 add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?; 84 add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?;
85 } 85 }
86 if no_traits_found { 86 if no_traits_found {
87 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_name))); 87 let trait_path = make::ext::ident_path(trait_name);
88 add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?; 88 add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?;
89 } 89 }
90 Some(()) 90 Some(())
@@ -159,10 +159,8 @@ fn impl_def_from_trait(
159 if trait_items.is_empty() { 159 if trait_items.is_empty() {
160 return None; 160 return None;
161 } 161 }
162 let impl_def = make::impl_trait( 162 let impl_def =
163 trait_path.clone(), 163 make::impl_trait(trait_path.clone(), make::ext::ident_path(&annotated_name.text()));
164 make::path_unqualified(make::path_segment(make::name_ref(&annotated_name.text()))),
165 );
166 let (impl_def, first_assoc_item) = 164 let (impl_def, first_assoc_item) =
167 add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); 165 add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
168 Some((impl_def, first_assoc_item)) 166 Some((impl_def, first_assoc_item))
diff --git a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
index ff25b61ea..15420aedf 100644
--- a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
+++ b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
@@ -1,6 +1,9 @@
1use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner}; 1use syntax::{
2 ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode},
3 ted,
4};
2 5
3use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
4 7
5// Assist: replace_impl_trait_with_generic 8// Assist: replace_impl_trait_with_generic
6// 9//
@@ -17,30 +20,29 @@ pub(crate) fn replace_impl_trait_with_generic(
17 acc: &mut Assists, 20 acc: &mut Assists,
18 ctx: &AssistContext, 21 ctx: &AssistContext,
19) -> Option<()> { 22) -> Option<()> {
20 let type_impl_trait = ctx.find_node_at_offset::<ast::ImplTraitType>()?; 23 let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
21 let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; 24 let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?;
22 let type_fn = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; 25 let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?;
23 26
24 let impl_trait_ty = type_impl_trait.type_bound_list()?; 27 let type_bound_list = impl_trait_type.type_bound_list()?;
25 28
26 let target = type_fn.syntax().text_range(); 29 let target = fn_.syntax().text_range();
27 acc.add( 30 acc.add(
28 AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite), 31 AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite),
29 "Replace impl trait with generic", 32 "Replace impl trait with generic",
30 target, 33 target,
31 |edit| { 34 |edit| {
32 let generic_letter = impl_trait_ty.to_string().chars().next().unwrap().to_string(); 35 let impl_trait_type = edit.make_ast_mut(impl_trait_type);
36 let fn_ = edit.make_ast_mut(fn_);
33 37
34 let generic_param_list = type_fn 38 let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type);
35 .generic_param_list()
36 .unwrap_or_else(|| make::generic_param_list(None))
37 .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty)));
38 39
39 let new_type_fn = type_fn 40 let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
40 .replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter)) 41 .clone_for_update();
41 .with_generic_param_list(generic_param_list); 42 let new_ty = make::ty(&type_param_name).clone_for_update();
42 43
43 edit.replace_ast(type_fn.clone(), new_type_fn); 44 ted::replace(impl_trait_type.syntax(), new_ty.syntax());
45 fn_.get_or_create_generic_param_list().add_generic_param(type_param.into())
44 }, 46 },
45 ) 47 )
46} 48}
@@ -55,12 +57,8 @@ mod tests {
55 fn replace_impl_trait_with_generic_params() { 57 fn replace_impl_trait_with_generic_params() {
56 check_assist( 58 check_assist(
57 replace_impl_trait_with_generic, 59 replace_impl_trait_with_generic,
58 r#" 60 r#"fn foo<G>(bar: $0impl Bar) {}"#,
59 fn foo<G>(bar: $0impl Bar) {} 61 r#"fn foo<G, B: Bar>(bar: B) {}"#,
60 "#,
61 r#"
62 fn foo<G, B: Bar>(bar: B) {}
63 "#,
64 ); 62 );
65 } 63 }
66 64
@@ -68,12 +66,8 @@ mod tests {
68 fn replace_impl_trait_without_generic_params() { 66 fn replace_impl_trait_without_generic_params() {
69 check_assist( 67 check_assist(
70 replace_impl_trait_with_generic, 68 replace_impl_trait_with_generic,
71 r#" 69 r#"fn foo(bar: $0impl Bar) {}"#,
72 fn foo(bar: $0impl Bar) {} 70 r#"fn foo<B: Bar>(bar: B) {}"#,
73 "#,
74 r#"
75 fn foo<B: Bar>(bar: B) {}
76 "#,
77 ); 71 );
78 } 72 }
79 73
@@ -81,12 +75,8 @@ mod tests {
81 fn replace_two_impl_trait_with_generic_params() { 75 fn replace_two_impl_trait_with_generic_params() {
82 check_assist( 76 check_assist(
83 replace_impl_trait_with_generic, 77 replace_impl_trait_with_generic,
84 r#" 78 r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#,
85 fn foo<G>(foo: impl Foo, bar: $0impl Bar) {} 79 r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#,
86 "#,
87 r#"
88 fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}
89 "#,
90 ); 80 );
91 } 81 }
92 82
@@ -94,12 +84,8 @@ mod tests {
94 fn replace_impl_trait_with_empty_generic_params() { 84 fn replace_impl_trait_with_empty_generic_params() {
95 check_assist( 85 check_assist(
96 replace_impl_trait_with_generic, 86 replace_impl_trait_with_generic,
97 r#" 87 r#"fn foo<>(bar: $0impl Bar) {}"#,
98 fn foo<>(bar: $0impl Bar) {} 88 r#"fn foo<B: Bar>(bar: B) {}"#,
99 "#,
100 r#"
101 fn foo<B: Bar>(bar: B) {}
102 "#,
103 ); 89 );
104 } 90 }
105 91
@@ -108,13 +94,13 @@ mod tests {
108 check_assist( 94 check_assist(
109 replace_impl_trait_with_generic, 95 replace_impl_trait_with_generic,
110 r#" 96 r#"
111 fn foo< 97fn foo<
112 >(bar: $0impl Bar) {} 98>(bar: $0impl Bar) {}
113 "#, 99"#,
114 r#" 100 r#"
115 fn foo<B: Bar 101fn foo<B: Bar
116 >(bar: B) {} 102>(bar: B) {}
117 "#, 103"#,
118 ); 104 );
119 } 105 }
120 106
@@ -123,12 +109,8 @@ mod tests {
123 fn replace_impl_trait_with_exist_generic_letter() { 109 fn replace_impl_trait_with_exist_generic_letter() {
124 check_assist( 110 check_assist(
125 replace_impl_trait_with_generic, 111 replace_impl_trait_with_generic,
126 r#" 112 r#"fn foo<B>(bar: $0impl Bar) {}"#,
127 fn foo<B>(bar: $0impl Bar) {} 113 r#"fn foo<B, C: Bar>(bar: C) {}"#,
128 "#,
129 r#"
130 fn foo<B, C: Bar>(bar: C) {}
131 "#,
132 ); 114 );
133 } 115 }
134 116
@@ -137,19 +119,19 @@ mod tests {
137 check_assist( 119 check_assist(
138 replace_impl_trait_with_generic, 120 replace_impl_trait_with_generic,
139 r#" 121 r#"
140 fn foo< 122fn foo<
141 G: Foo, 123 G: Foo,
142 F, 124 F,
143 H, 125 H,
144 >(bar: $0impl Bar) {} 126>(bar: $0impl Bar) {}
145 "#, 127"#,
146 r#" 128 r#"
147 fn foo< 129fn foo<
148 G: Foo, 130 G: Foo,
149 F, 131 F,
150 H, B: Bar 132 H, B: Bar,
151 >(bar: B) {} 133>(bar: B) {}
152 "#, 134"#,
153 ); 135 );
154 } 136 }
155 137
@@ -157,12 +139,8 @@ mod tests {
157 fn replace_impl_trait_multiple() { 139 fn replace_impl_trait_multiple() {
158 check_assist( 140 check_assist(
159 replace_impl_trait_with_generic, 141 replace_impl_trait_with_generic,
160 r#" 142 r#"fn foo(bar: $0impl Foo + Bar) {}"#,
161 fn foo(bar: $0impl Foo + Bar) {} 143 r#"fn foo<F: Foo + Bar>(bar: F) {}"#,
162 "#,
163 r#"
164 fn foo<F: Foo + Bar>(bar: F) {}
165