aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--Cargo.toml2
-rw-r--r--crates/hir/src/attrs.rs2
-rw-r--r--crates/hir/src/lib.rs6
-rw-r--r--crates/hir/src/source_analyzer.rs2
-rw-r--r--crates/hir_def/src/attr.rs21
-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.rs2
-rw-r--r--crates/hir_def/src/item_tree/lower.rs34
-rw-r--r--crates/hir_def/src/lib.rs4
-rw-r--r--crates/hir_def/src/nameres.rs1
-rw-r--r--crates/hir_def/src/nameres/tests/incremental.rs52
-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/db.rs2
-rw-r--r--crates/hir_expand/src/hygiene.rs57
-rw-r--r--crates/hir_ty/src/display.rs2
-rwxr-xr-x[-rw-r--r--]crates/ide/src/folding_ranges.rs45
-rw-r--r--crates/ide/src/inlay_hints.rs4
-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.rs13
-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.rs2
-rw-r--r--crates/ide_db/src/line_index.rs7
-rw-r--r--crates/project_model/src/cfg_flag.rs3
-rw-r--r--crates/rust-analyzer/src/to_proto.rs1
-rw-r--r--crates/stdx/src/lib.rs78
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/algo.rs2
-rw-r--r--crates/syntax/src/ast/make.rs2
-rw-r--r--crates/syntax/src/ast/node_ext.rs25
-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
-rw-r--r--docs/dev/style.md2
-rw-r--r--xtask/src/install.rs2
43 files changed, 409 insertions, 260 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0ca0b7b63..c378014f0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1319,9 +1319,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
1319 1319
1320[[package]] 1320[[package]]
1321name = "rowan" 1321name = "rowan"
1322version = "0.13.0-pre.3" 1322version = "0.13.0-pre.5"
1323source = "registry+https://github.com/rust-lang/crates.io-index" 1323source = "registry+https://github.com/rust-lang/crates.io-index"
1324checksum = "77d315d6f2e33f294412faa47f41b56bdb3fce72c999d384b5e78c8d21551b13" 1324checksum = "32a5fc82ed0b7e7fba157331f0d8f64abd73bced6e7ac2a4dfa0c4cf0ab584e8"
1325dependencies = [ 1325dependencies = [
1326 "countme", 1326 "countme",
1327 "hashbrown", 1327 "hashbrown",
@@ -1411,9 +1411,9 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
1411 1411
1412[[package]] 1412[[package]]
1413name = "salsa" 1413name = "salsa"
1414version = "0.16.0" 1414version = "0.16.1"
1415source = "registry+https://github.com/rust-lang/crates.io-index" 1415source = "registry+https://github.com/rust-lang/crates.io-index"
1416checksum = "d8fadca2ab5de17acf66d744f4888049ca8f1bb9b8a1ab8afd9d032cc959c5dc" 1416checksum = "4b84d9f96071f3f3be0dc818eae3327625d8ebc95b58da37d6850724f31d3403"
1417dependencies = [ 1417dependencies = [
1418 "crossbeam-utils", 1418 "crossbeam-utils",
1419 "indexmap", 1419 "indexmap",
diff --git a/Cargo.toml b/Cargo.toml
index 46c64d35c..d34251fc0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,3 +27,5 @@ debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger.
27# chalk-recursive = { path = "../chalk/chalk-recursive" } 27# chalk-recursive = { path = "../chalk/chalk-recursive" }
28 28
29# ungrammar = { path = "../ungrammar" } 29# ungrammar = { path = "../ungrammar" }
30
31# salsa = { path = "../salsa" }
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..f876339de 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1666,7 +1666,7 @@ impl Impl {
1666 .value 1666 .value
1667 .attrs() 1667 .attrs()
1668 .filter_map(|it| { 1668 .filter_map(|it| {
1669 let path = ModPath::from_src(it.path()?, &hygenic)?; 1669 let path = ModPath::from_src(db.upcast(), it.path()?, &hygenic)?;
1670 if path.as_ident()?.to_string() == "derive" { 1670 if path.as_ident()?.to_string() == "derive" {
1671 Some(it) 1671 Some(it)
1672 } else { 1672 } else {
@@ -1744,6 +1744,10 @@ impl Type {
1744 } 1744 }
1745 } 1745 }
1746 1746
1747 pub fn strip_references(&self) -> Type {
1748 self.derived(self.ty.strip_references().clone())
1749 }
1750
1747 pub fn is_unknown(&self) -> bool { 1751 pub fn is_unknown(&self) -> bool {
1748 self.ty.is_unknown() 1752 self.ty.is_unknown()
1749 } 1753 }
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 0171d8a92..a2479016e 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -95,13 +95,17 @@ 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 .enumerate()
101 .flat_map(|(i, attr)| { 105 .flat_map(|(i, attr)| {
102 let index = AttrId(i as u32); 106 let index = AttrId(i as u32);
103 match attr { 107 match attr {
104 Either::Left(attr) => Attr::from_src(attr, hygiene, index), 108 Either::Left(attr) => Attr::from_src(db, attr, hygiene, index),
105 Either::Right(comment) => comment.doc_comment().map(|doc| Attr { 109 Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
106 id: index, 110 id: index,
107 input: Some(AttrInput::Literal(SmolStr::new(doc))), 111 input: Some(AttrInput::Literal(SmolStr::new(doc))),
@@ -116,7 +120,7 @@ impl RawAttrs {
116 120
117 fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self { 121 fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::AttrsOwner>) -> Self {
118 let hygiene = Hygiene::new(db.upcast(), owner.file_id); 122 let hygiene = Hygiene::new(db.upcast(), owner.file_id);
119 Self::new(owner.value, &hygiene) 123 Self::new(db, owner.value, &hygiene)
120 } 124 }
121 125
122 pub(crate) fn merge(&self, other: Self) -> Self { 126 pub(crate) fn merge(&self, other: Self) -> Self {
@@ -170,7 +174,7 @@ impl RawAttrs {
170 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; 174 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
171 // FIXME hygiene 175 // FIXME hygiene
172 let hygiene = Hygiene::new_unhygienic(); 176 let hygiene = Hygiene::new_unhygienic();
173 Attr::from_src(attr, &hygiene, index) 177 Attr::from_src(db, attr, &hygiene, index)
174 }); 178 });
175 179
176 let cfg_options = &crate_graph[krate].cfg_options; 180 let cfg_options = &crate_graph[krate].cfg_options;
@@ -627,8 +631,13 @@ pub enum AttrInput {
627} 631}
628 632
629impl Attr { 633impl Attr {
630 fn from_src(ast: ast::Attr, hygiene: &Hygiene, id: AttrId) -> Option<Attr> { 634 fn from_src(
631 let path = Interned::new(ModPath::from_src(ast.path()?, hygiene)?); 635 db: &dyn DefDatabase,
636 ast: ast::Attr,
637 hygiene: &Hygiene,
638 id: AttrId,
639 ) -> Option<Attr> {
640 let path = Interned::new(ModPath::from_src(db, ast.path()?, hygiene)?);
632 let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { 641 let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
633 let value = match lit.kind() { 642 let value = match lit.kind() {
634 ast::LiteralKind::String(string) => string.value()?.into(), 643 ast::LiteralKind::String(string) => string.value()?.into(),
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 c11da30d2..75dc19c11 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()
@@ -791,7 +794,8 @@ impl ExprCollector<'_> {
791 } 794 }
792 } 795 }
793 ast::Pat::TupleStructPat(p) => { 796 ast::Pat::TupleStructPat(p) => {
794 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); 797 let path =
798 p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
795 let (args, ellipsis) = self.collect_tuple_pat(p.fields()); 799 let (args, ellipsis) = self.collect_tuple_pat(p.fields());
796 Pat::TupleStruct { path, args, ellipsis } 800 Pat::TupleStruct { path, args, ellipsis }
797 } 801 }
@@ -801,7 +805,8 @@ impl ExprCollector<'_> {
801 Pat::Ref { pat, mutability } 805 Pat::Ref { pat, mutability }
802 } 806 }
803 ast::Pat::PathPat(p) => { 807 ast::Pat::PathPat(p) => {
804 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); 808 let path =
809 p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
805 path.map(Pat::Path).unwrap_or(Pat::Missing) 810 path.map(Pat::Path).unwrap_or(Pat::Missing)
806 } 811 }
807 ast::Pat::OrPat(p) => { 812 ast::Pat::OrPat(p) => {
@@ -815,7 +820,8 @@ impl ExprCollector<'_> {
815 } 820 }
816 ast::Pat::WildcardPat(_) => Pat::Wild, 821 ast::Pat::WildcardPat(_) => Pat::Wild,
817 ast::Pat::RecordPat(p) => { 822 ast::Pat::RecordPat(p) => {
818 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); 823 let path =
824 p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
819 let args: Vec<_> = p 825 let args: Vec<_> = p
820 .record_pat_field_list() 826 .record_pat_field_list()
821 .expect("every struct should have a field list") 827 .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..8d13c7e04 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -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) => {
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 45b099cf3..5743b3386 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,7 +622,7 @@ 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 res = MacroCall { path, ast_id };
624 Some(id(self.data().macro_calls.alloc(res))) 628 Some(id(self.data().macro_calls.alloc(res)))
@@ -647,7 +651,7 @@ impl Ctx {
647 list.extern_items() 651 list.extern_items()
648 .filter_map(|item| { 652 .filter_map(|item| {
649 self.collect_inner_items(item.syntax()); 653 self.collect_inner_items(item.syntax());
650 let attrs = RawAttrs::new(&item, &self.hygiene); 654 let attrs = RawAttrs::new(self.db, &item, &self.hygiene);
651 let id: ModItem = match item { 655 let id: ModItem = match item {
652 ast::ExternItem::Fn(ast) => { 656 ast::ExternItem::Fn(ast) => {
653 let func_id = self.lower_function(&ast)?; 657 let func_id = self.lower_function(&ast)?;
@@ -755,7 +759,7 @@ impl Ctx {
755 fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId { 759 fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId {
756 let vis = match self.forced_visibility { 760 let vis = match self.forced_visibility {
757 Some(vis) => return vis, 761 Some(vis) => return vis,
758 None => RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene), 762 None => RawVisibility::from_ast_with_hygiene(self.db, item.visibility(), &self.hygiene),
759 }; 763 };
760 764
761 self.data().vis.alloc(vis) 765 self.data().vis.alloc(vis)
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 25694f037..da46f16f7 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -654,7 +654,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
654 ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { 654 ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
655 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); 655 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); 656 let h = Hygiene::new(db.upcast(), self.file_id);
657 let path = self.value.path().and_then(|path| path::ModPath::from_src(path, &h)); 657 let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
658 658
659 let path = match error_sink 659 let path = match error_sink
660 .option(path, || mbe::ExpandError::Other("malformed macro invocation".into())) 660 .option(path, || mbe::ExpandError::Other("malformed macro invocation".into()))
@@ -712,7 +712,7 @@ fn macro_call_as_call_id(
712 krate, 712 krate,
713 macro_call, 713 macro_call,
714 def, 714 def,
715 &|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?), 715 &|path: ast::Path| resolver(path::ModPath::from_src(db, path, &hygiene)?),
716 error_sink, 716 error_sink,
717 ) 717 )
718 .map(MacroCallId::from) 718 .map(MacroCallId::from)
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index ba027c44a..1bc72ec1f 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| {
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs
index 509e1bbbc..227ecd162 100644
--- a/crates/hir_def/src/nameres/tests/incremental.rs
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -105,3 +105,55 @@ 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 }
141
142 let new_text = r#"
143m!(X);
144fn quux() { 92 }
145m!(Y);
146m!(Z);
147"#;
148 db.set_file_text(pos.file_id, Arc::new(new_text.to_string()));
149
150 {
151 let events = db.log_executed(|| {
152 let crate_def_map = db.crate_def_map(krate);
153 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
154 assert_eq!(module_data.scope.resolutions().count(), 4);
155 });
156 let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
157 assert_eq!(n_recalculated_item_trees, 1);
158 }
159}
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/db.rs b/crates/hir_expand/src/db.rs
index 3e9abd8a1..d61f4b31a 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -283,7 +283,7 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
283 }; 283 };
284 let loc = db.lookup_intern_macro(id); 284 let loc = db.lookup_intern_macro(id);
285 let arg = loc.kind.arg(db)?; 285 let arg = loc.kind.arg(db)?;
286 Some(arg.green()) 286 Some(arg.green().into())
287} 287}
288 288
289fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>> { 289fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>> {
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index ed61ebca3..aca69e35a 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -32,10 +32,14 @@ impl Hygiene {
32 } 32 }
33 33
34 // FIXME: this should just return name 34 // FIXME: this should just return name
35 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> {
36 if let Some(frames) = &self.frames { 40 if let Some(frames) = &self.frames {
37 if name_ref.text() == "$crate" { 41 if name_ref.text() == "$crate" {
38 if let Some(krate) = frames.root_crate(name_ref.syntax()) { 42 if let Some(krate) = frames.root_crate(db, name_ref.syntax()) {
39 return Either::Right(krate); 43 return Either::Right(krate);
40 } 44 }
41 } 45 }
@@ -44,15 +48,19 @@ impl Hygiene {
44 Either::Left(name_ref.as_name()) 48 Either::Left(name_ref.as_name())
45 } 49 }
46 50
47 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> {
48 let mut token = path.syntax().first_token()?.text_range(); 52 let mut token = path.syntax().first_token()?.text_range();
49 let frames = self.frames.as_ref()?; 53 let frames = self.frames.as_ref()?;
50 let mut current = frames.0.clone(); 54 let mut current = frames.0.clone();
51 55
52 loop { 56 loop {
53 let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(token)?; 57 let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(db, token)?;
54 if origin == Origin::Def { 58 if origin == Origin::Def {
55 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 };
56 } 64 }
57 current = current.call_site.as_ref()?.clone(); 65 current = current.call_site.as_ref()?.clone();
58 token = mapped.value; 66 token = mapped.value;
@@ -82,13 +90,13 @@ impl HygieneFrames {
82 HygieneFrames(Arc::new(HygieneFrame::new(db, file_id))) 90 HygieneFrames(Arc::new(HygieneFrame::new(db, file_id)))
83 } 91 }
84 92
85 fn root_crate(&self, node: &SyntaxNode) -> Option<CrateId> { 93 fn root_crate(&self, db: &dyn AstDatabase, node: &SyntaxNode) -> Option<CrateId> {
86 let mut token = node.first_token()?.text_range(); 94 let mut token = node.first_token()?.text_range();
87 let mut result = self.0.krate; 95 let mut result = self.0.krate;
88 let mut current = self.0.clone(); 96 let mut current = self.0.clone();
89 97
90 while let Some((mapped, origin)) = 98 while let Some((mapped, origin)) =
91 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))
92 { 100 {
93 result = current.krate; 101 result = current.krate;
94 102
@@ -112,7 +120,7 @@ impl HygieneFrames {
112 120
113#[derive(Debug, Clone, PartialEq, Eq)] 121#[derive(Debug, Clone, PartialEq, Eq)]
114struct HygieneInfo { 122struct HygieneInfo {
115 arg_start: InFile<TextSize>, 123 file: MacroFile,
116 /// The `macro_rules!` arguments. 124 /// The `macro_rules!` arguments.
117 def_start: Option<InFile<TextSize>>, 125 def_start: Option<InFile<TextSize>>,
118 126
@@ -122,12 +130,24 @@ struct HygieneInfo {
122} 130}
123 131
124impl HygieneInfo { 132impl HygieneInfo {
125 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)> {
126 let token_id = self.exp_map.token_by_range(token)?; 138 let token_id = self.exp_map.token_by_range(token)?;
127 139
128 let (token_id, origin) = self.macro_def.map_id_up(token_id); 140 let (token_id, origin) = self.macro_def.map_id_up(token_id);
129 let (token_map, tt) = match origin { 141 let (token_map, tt) = match origin {
130 mbe::Origin::Call => (&self.macro_arg.1, self.arg_start), 142 mbe::Origin::Call => {
143 let call_id = match self.file.macro_call_id {
144 MacroCallId::LazyMacro(lazy) => lazy,
145 MacroCallId::EagerMacro(_) => unreachable!(),
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 }
131 mbe::Origin::Def => match (&*self.macro_def, self.def_start) { 151 mbe::Origin::Def => match (&*self.macro_def, self.def_start) {
132 (TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt)) 152 (TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt))
133 | (TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt)) => { 153 | (TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt)) => {
@@ -147,8 +167,6 @@ fn make_hygiene_info(
147 macro_file: MacroFile, 167 macro_file: MacroFile,
148 loc: &MacroCallLoc, 168 loc: &MacroCallLoc,
149) -> Option<HygieneInfo> { 169) -> Option<HygieneInfo> {
150 let arg_tt = loc.kind.arg(db)?;
151
152 let def_offset = loc.def.ast_id().left().and_then(|id| { 170 let def_offset = loc.def.ast_id().left().and_then(|id| {
153 let def_tt = match id.to_node(db) { 171 let def_tt = match id.to_node(db) {
154 ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(), 172 ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(),
@@ -161,13 +179,7 @@ fn make_hygiene_info(
161 let (_, exp_map) = db.parse_macro_expansion(macro_file).value?; 179 let (_, exp_map) = db.parse_macro_expansion(macro_file).value?;
162 let macro_arg = db.macro_arg(macro_file.macro_call_id)?; 180 let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
163 181
164 Some(HygieneInfo { 182 Some(HygieneInfo { file: macro_file, def_start: def_offset, macro_arg, macro_def, exp_map })
165 arg_start: InFile::new(loc.kind.file_id(), arg_tt.text_range().start()),
166 def_start: def_offset,
167 macro_arg,
168 macro_def,
169 exp_map,
170 })
171} 183}
172 184
173impl HygieneFrame { 185impl HygieneFrame {
@@ -178,7 +190,8 @@ impl HygieneFrame {
178 MacroCallId::EagerMacro(_id) => (None, None, false), 190 MacroCallId::EagerMacro(_id) => (None, None, false),
179 MacroCallId::LazyMacro(id) => { 191 MacroCallId::LazyMacro(id) => {
180 let loc = db.lookup_intern_macro(id); 192 let loc = db.lookup_intern_macro(id);
181 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));
182 match loc.def.kind { 195 match loc.def.kind {
183 MacroDefKind::Declarative(_) => { 196 MacroDefKind::Declarative(_) => {
184 (info, Some(loc.def.krate), loc.def.local_inner) 197 (info, Some(loc.def.krate), loc.def.local_inner)
@@ -192,7 +205,7 @@ impl HygieneFrame {
192 }, 205 },
193 }; 206 };
194 207
195 let info = match info { 208 let (calling_file, info) = match info {
196 None => { 209 None => {
197 return HygieneFrame { 210 return HygieneFrame {
198 expansion: None, 211 expansion: None,
@@ -206,7 +219,7 @@ impl HygieneFrame {
206 }; 219 };
207 220
208 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));
209 let call_site = Some(db.hygiene_frame(info.arg_start.file_id)); 222 let call_site = Some(db.hygiene_frame(calling_file));
210 223
211 HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site } 224 HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site }
212 } 225 }
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/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/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_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index e2994eed4..78154bf3e 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -203,41 +203,37 @@ impl Completions {
203fn complete_enum_variants( 203fn complete_enum_variants(
204 acc: &mut Completions, 204 acc: &mut Completions,
205 ctx: &CompletionContext, 205 ctx: &CompletionContext,
206 ty: &hir::Type, 206 enum_data: hir::Enum,
207 cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath), 207 cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath),
208) { 208) {
209 if let Some(hir::Adt::Enum(enum_data)) = 209 let variants = enum_data.variants(ctx.db);
210 iter::successors(Some(ty.clone()), |ty| ty.remove_ref()).last().and_then(|ty| ty.as_adt()) 210
211 { 211 let module = if let Some(module) = ctx.scope.module() {
212 let variants = enum_data.variants(ctx.db); 212 // Compute path from the completion site if available.
213 213 module
214 let module = if let Some(module) = ctx.scope.module() { 214 } else {
215 // Compute path from the completion site if available. 215 // Otherwise fall back to the enum's definition site.
216 module 216 enum_data.module(ctx.db)
217 } else { 217 };
218 // Otherwise fall back to the enum's definition site. 218
219 enum_data.module(ctx.db) 219 if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) {
220 }; 220 if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_data)) {
221 221 for &variant in &variants {
222 if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { 222 let self_path = hir::ModPath::from_segments(
223 if impl_.self_ty(ctx.db) == *ty { 223 hir::PathKind::Plain,
224 for &variant in &variants { 224 iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))),
225 let self_path = hir::ModPath::from_segments( 225 );
226 hir::PathKind::Plain, 226 cb(acc, ctx, variant, self_path);
227 iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))),
228 );
229 cb(acc, ctx, variant, self_path);
230 }
231 } 227 }
232 } 228 }
229 }
233 230
234 for variant in variants { 231 for variant in variants {
235 if let Some(path) = module.find_use_path(ctx.db, hir::ModuleDef::from(variant)) { 232 if let Some(path) = module.find_use_path(ctx.db, hir::ModuleDef::from(variant)) {
236 // Variants with trivial paths are already added by the existing completion logic, 233 // Variants with trivial paths are already added by the existing completion logic,
237 // so we should avoid adding these twice 234 // so we should avoid adding these twice
238 if path.segments().len() > 1 { 235 if path.segments().len() > 1 {
239 cb(acc, ctx, variant, path); 236 cb(acc, ctx, variant, path);
240 }
241 } 237 }
242 } 238 }
243 } 239 }
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 808d7ff7e..8dc9ab73c 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -12,8 +12,10 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
12 } 12 }
13 13
14 if !ctx.is_irrefutable_pat_binding { 14 if !ctx.is_irrefutable_pat_binding {
15 if let Some(ty) = ctx.expected_type.as_ref() { 15 if let Some(hir::Adt::Enum(e)) =
16 super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { 16 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
17 {
18 super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| {
17 acc.add_qualified_variant_pat(ctx, variant, path.clone()); 19 acc.add_qualified_variant_pat(ctx, variant, path.clone());
18 acc.add_qualified_enum_variant(ctx, variant, path); 20 acc.add_qualified_enum_variant(ctx, variant, path);
19 }); 21 });
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index ac69b720a..962aaf0df 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -35,14 +35,11 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
35 None => return, 35 None => return,
36 }; 36 };
37 37
38 let ref_removed_ty =
39 std::iter::successors(Some(receiver_ty.clone()), |ty| ty.remove_ref()).last().unwrap();
40
41 let cap = match ctx.config.snippet_cap { 38 let cap = match ctx.config.snippet_cap {
42 Some(it) => it, 39 Some(it) => it,
43 None => return, 40 None => return,
44 }; 41 };
45 let try_enum = TryEnum::from_ty(&ctx.sema, &ref_removed_ty); 42 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references());
46 if let Some(try_enum) = &try_enum { 43 if let Some(try_enum) = &try_enum {
47 match try_enum { 44 match try_enum {
48 TryEnum::Result => { 45 TryEnum::Result => {
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index d2ebba65f..eedb44873 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -59,6 +59,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
59 hir::ModuleDef::TypeAlias(a) => { 59 hir::ModuleDef::TypeAlias(a) => {
60 let ty = a.ty(ctx.db); 60 let ty = a.ty(ctx.db);
61 if let Some(Adt::Enum(e)) = ty.as_adt() { 61 if let Some(Adt::Enum(e)) = ty.as_adt() {
62 cov_mark::hit!(completes_variant_through_alias);
62 add_enum_variants(ctx, acc, e); 63 add_enum_variants(ctx, acc, e);
63 } 64 }
64 ty 65 ty
@@ -68,6 +69,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
68 Some(it) => it, 69 Some(it) => it,
69 None => return, 70 None => return,
70 }; 71 };
72 cov_mark::hit!(completes_primitive_assoc_const);
71 builtin.ty(ctx.db, module) 73 builtin.ty(ctx.db, module)
72 } 74 }
73 _ => unreachable!(), 75 _ => unreachable!(),
@@ -96,9 +98,8 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
96 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 98 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
97 return None; 99 return None;
98 } 100 }
99 match item { 101 if let hir::AssocItem::TypeAlias(ty) = item {
100 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} 102 acc.add_type_alias(ctx, ty)
101 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
102 } 103 }
103 None::<()> 104 None::<()>
104 }); 105 });
@@ -745,7 +746,7 @@ fn f() {}
745 } 746 }
746 747
747 #[test] 748 #[test]
748 fn completes_self_enum() { 749 fn completes_variant_through_self() {
749 check( 750 check(
750 r#" 751 r#"
751enum Foo { 752enum Foo {
@@ -769,6 +770,7 @@ impl Foo {
769 770
770 #[test] 771 #[test]
771 fn completes_primitive_assoc_const() { 772 fn completes_primitive_assoc_const() {
773 cov_mark::check!(completes_primitive_assoc_const);
772 check( 774 check(
773 r#" 775 r#"
774//- /lib.rs crate:lib deps:core 776//- /lib.rs crate:lib deps:core
@@ -792,7 +794,8 @@ impl u8 {
792 } 794 }
793 795
794 #[test] 796 #[test]
795 fn completes_through_alias() { 797 fn completes_variant_through_alias() {
798 cov_mark::check!(completes_variant_through_alias);
796 check( 799 check(
797 r#" 800 r#"
798enum Foo { 801enum Foo {
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index a26fe7c6c..968c0254d 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -36,7 +36,7 @@ use ide_db::{traits::get_missing_assoc_items, SymbolKind};
36use syntax::{ 36use syntax::{
37 ast::{self, edit, Impl}, 37 ast::{self, edit, Impl},
38 display::function_declaration, 38 display::function_declaration,
39 AstNode, SyntaxKind, SyntaxNode, TextRange, T, 39 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
40}; 40};
41use text_edit::TextEdit; 41use text_edit::TextEdit;
42 42
@@ -154,8 +154,7 @@ fn add_function_impl(
154 } else { 154 } else {
155 CompletionItemKind::SymbolKind(SymbolKind::Function) 155 CompletionItemKind::SymbolKind(SymbolKind::Function)
156 }; 156 };
157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); 157 let range = replacement_range(ctx, fn_def_node);
158
159 if let Some(src) = func.source(ctx.db) { 158 if let Some(src) = func.source(ctx.db) {
160 let function_decl = function_declaration(&src.value); 159 let function_decl = function_declaration(&src.value);
161 match ctx.config.snippet_cap { 160 match ctx.config.snippet_cap {
@@ -183,8 +182,7 @@ fn add_type_alias_impl(
183 182
184 let snippet = format!("type {} = ", alias_name); 183 let snippet = format!("type {} = ", alias_name);
185 184
186 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); 185 let range = replacement_range(ctx, type_def_node);
187
188 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); 186 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
189 item.text_edit(TextEdit::replace(range, snippet)) 187 item.text_edit(TextEdit::replace(range, snippet))
190 .lookup_by(alias_name) 188 .lookup_by(alias_name)
@@ -205,9 +203,7 @@ fn add_const_impl(
205 if let Some(source) = const_.source(ctx.db) { 203 if let Some(source) = const_.source(ctx.db) {
206 let snippet = make_const_compl_syntax(&source.value); 204 let snippet = make_const_compl_syntax(&source.value);
207 205
208 let range = 206 let range = replacement_range(ctx, const_def_node);
209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
210
211 let mut item = 207 let mut item =
212 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); 208 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
213 item.text_edit(TextEdit::replace(range, snippet)) 209 item.text_edit(TextEdit::replace(range, snippet))
@@ -242,6 +238,17 @@ fn make_const_compl_syntax(const_: &ast::Const) -> String {
242 format!("{} = ", syntax.trim_end()) 238 format!("{} = ", syntax.trim_end())
243} 239}
244 240
241fn replacement_range(ctx: &CompletionContext, item: &SyntaxNode) -> TextRange {
242 let first_child = item
243 .children_with_tokens()
244 .find(|child| {
245 !matches!(child.kind(), SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR)
246 })
247 .unwrap_or_else(|| SyntaxElement::Node(item.clone()));
248
249 TextRange::new(first_child.text_range().start(), ctx.source_range().end())
250}
251
245#[cfg(test)] 252#[cfg(test)]
246mod tests { 253mod tests {
247 use expect_test::{expect, Expect}; 254 use expect_test::{expect, Expect};
@@ -734,4 +741,50 @@ impl Test for T {{
734 test("CONST", "const $0", "const CONST: u16 = ", next_sibling); 741 test("CONST", "const $0", "const CONST: u16 = ", next_sibling);
735 } 742 }
736 } 743 }
744
745 #[test]
746 fn snippet_does_not_overwrite_comment_or_attr() {
747 let test = |completion: &str, hint: &str, completed: &str| {
748 check_edit(
749 completion,
750 &format!(
751 r#"
752trait Foo {{
753 type Type;
754 fn function();
755 const CONST: i32 = 0;
756}}
757struct T;
758
759impl Foo for T {{
760 // Comment
761 #[bar]
762 {}
763}}
764"#,
765 hint
766 ),
767 &format!(
768 r#"
769trait Foo {{
770 type Type;
771 fn function();
772 const CONST: i32 = 0;
773}}
774struct T;
775
776impl Foo for T {{
777 // Comment
778 #[bar]
779 {}
780}}
781"#,
782 completed
783 ),
784 )
785 };
786 test("function", "fn f$0", "fn function() {\n $0\n}");
787 test("Type", "type T$0", "type Type = ");
788 test("CONST", "const C$0", "const CONST: i32 = ");
789 }
737} 790}
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 1b8b063e7..7875500c1 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -17,8 +17,10 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
17 return; 17 return;
18 } 18 }
19 19
20 if let Some(ty) = &ctx.expected_type { 20 if let Some(hir::Adt::Enum(e)) =
21 super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { 21 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
22 {
23 super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| {
22 acc.add_qualified_enum_variant(ctx, variant, path) 24 acc.add_qualified_enum_variant(ctx, variant, path)
23 }); 25 });
24 } 26 }
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index f3fcb712c..62ef40818 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -381,7 +381,7 @@ impl<'a> CompletionContext<'a> {
381 let def = self.sema.to_def(&it); 381 let def = self.sema.to_def(&it);
382 (def.map(|def| def.ret_type(self.db)), None) 382 (def.map(|def| def.ret_type(self.db)), None)
383 }, 383 },
384 ast::Stmt(it) => (None, None), 384 ast::Stmt(_it) => (None, None),
385 _ => { 385 _ => {
386 match node.parent() { 386 match node.parent() {
387 Some(n) => { 387 Some(n) => {
diff --git a/crates/ide_db/src/line_index.rs b/crates/ide_db/src/line_index.rs
index 8e9d8cca2..816edfe6a 100644
--- a/crates/ide_db/src/line_index.rs
+++ b/crates/ide_db/src/line_index.rs
@@ -3,7 +3,6 @@
3use std::iter; 3use std::iter;
4 4
5use rustc_hash::FxHashMap; 5use rustc_hash::FxHashMap;
6use stdx::partition_point;
7use syntax::{TextRange, TextSize}; 6use syntax::{TextRange, TextSize};
8 7
9#[derive(Clone, Debug, PartialEq, Eq)] 8#[derive(Clone, Debug, PartialEq, Eq)]
@@ -97,7 +96,7 @@ impl LineIndex {
97 } 96 }
98 97
99 pub fn line_col(&self, offset: TextSize) -> LineCol { 98 pub fn line_col(&self, offset: TextSize) -> LineCol {
100 let line = partition_point(&self.newlines, |&it| it <= offset) - 1; 99 let line = self.newlines.partition_point(|&it| it <= offset) - 1;
101 let line_start_offset = self.newlines[line]; 100 let line_start_offset = self.newlines[line];
102 let col = offset - line_start_offset; 101 let col = offset - line_start_offset;
103 LineCol { line: line as u32, col: col.into() } 102 LineCol { line: line as u32, col: col.into() }
@@ -118,8 +117,8 @@ impl LineIndex {
118 } 117 }
119 118
120 pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ { 119 pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
121 let lo = partition_point(&self.newlines, |&it| it < range.start()); 120 let lo = self.newlines.partition_point(|&it| it < range.start());
122 let hi = partition_point(&self.newlines, |&it| it <= range.end()); 121 let hi = self.newlines.partition_point(|&it| it <= range.end());
123 let all = iter::once(range.start()) 122 let all = iter::once(range.start())
124 .chain(self.newlines[lo..hi].iter().copied()) 123 .chain(self.newlines[lo..hi].iter().copied())
125 .chain(iter::once(range.end())); 124 .chain(iter::once(range.end()));
diff --git a/crates/project_model/src/cfg_flag.rs b/crates/project_model/src/cfg_flag.rs
index e92962cf6..bfdfd458f 100644
--- a/crates/project_model/src/cfg_flag.rs
+++ b/crates/project_model/src/cfg_flag.rs
@@ -4,7 +4,6 @@
4use std::str::FromStr; 4use std::str::FromStr;
5 5
6use cfg::CfgOptions; 6use cfg::CfgOptions;
7use stdx::split_once;
8 7
9#[derive(Clone, Eq, PartialEq, Debug)] 8#[derive(Clone, Eq, PartialEq, Debug)]
10pub enum CfgFlag { 9pub enum CfgFlag {
@@ -15,7 +14,7 @@ pub enum CfgFlag {
15impl FromStr for CfgFlag { 14impl FromStr for CfgFlag {
16 type Err = String; 15 type Err = String;
17 fn from_str(s: &str) -> Result<Self, Self::Err> { 16 fn from_str(s: &str) -> Result<Self, Self::Err> {
18 let res = match split_once(s, '=') { 17 let res = match s.split_once('=') {
19 Some((key, value)) => { 18 Some((key, value)) => {
20 if !(value.starts_with('"') && value.ends_with('"')) { 19 if !(value.starts_with('"') && value.ends_with('"')) {
21 return Err(format!("Invalid cfg ({:?}), value should be in quotes", s)); 20 return Err(format!("Invalid cfg ({:?}), value should be in quotes", s));
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 1d27aa7b3..ecf6fd12f 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -524,6 +524,7 @@ pub(crate) fn folding_range(
524 | FoldKind::ArgList 524 | FoldKind::ArgList
525 | FoldKind::Consts 525 | FoldKind::Consts
526 | FoldKind::Statics 526 | FoldKind::Statics
527 | FoldKind::WhereClause
527 | FoldKind::Array => None, 528 | FoldKind::Array => None,
528 }; 529 };
529 530
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 1b6211044..340fcacfa 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -65,20 +65,6 @@ pub fn replace(buf: &mut String, from: char, to: &str) {
65 *buf = buf.replace(from, to) 65 *buf = buf.replace(from, to)
66} 66}
67 67
68// https://github.com/rust-lang/rust/issues/74773
69pub fn split_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
70 let mut split = haystack.splitn(2, delim);
71 let prefix = split.next()?;
72 let suffix = split.next()?;
73 Some((prefix, suffix))
74}
75pub fn rsplit_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
76 let mut split = haystack.rsplitn(2, delim);
77 let suffix = split.next()?;
78 let prefix = split.next()?;
79 Some((prefix, suffix))
80}
81
82pub fn trim_indent(mut text: &str) -> String { 68pub fn trim_indent(mut text: &str) -> String {
83 if text.starts_with('\n') { 69 if text.starts_with('\n') {
84 text = &text[1..]; 70 text = &text[1..];
@@ -89,7 +75,7 @@ pub fn trim_indent(mut text: &str) -> String {
89 .map(|it| it.len() - it.trim_start().len()) 75 .map(|it| it.len() - it.trim_start().len())
90 .min() 76 .min()
91 .unwrap_or(0); 77 .unwrap_or(0);
92 lines_with_ends(text) 78 text.split_inclusive('\n')
93 .map( 79 .map(
94 |line| { 80 |line| {
95 if line.len() <= indent { 81 if line.len() <= indent {
@@ -102,70 +88,12 @@ pub fn trim_indent(mut text: &str) -> String {
102 .collect() 88 .collect()
103} 89}
104 90
105pub fn lines_with_ends(text: &str) -> LinesWithEnds {
106 LinesWithEnds { text }
107}
108
109pub struct LinesWithEnds<'a> {
110 text: &'a str,
111}
112
113impl<'a> Iterator for LinesWithEnds<'a> {
114 type Item = &'a str;
115 fn next(&mut self) -> Option<&'a str> {
116 if self.text.is_empty() {
117 return None;
118 }
119 let idx = self.text.find('\n').map_or(self.text.len(), |it| it + 1);
120 let (res, next) = self.text.split_at(idx);
121 self.text = next;
122 Some(res)
123 }
124}
125
126/// Returns `idx` such that:
127///
128/// ```text
129/// ∀ x in slice[..idx]: pred(x)
130/// && ∀ x in slice[idx..]: !pred(x)
131/// ```
132///
133/// https://github.com/rust-lang/rust/issues/73831
134pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
135where
136 P: FnMut(&T) -> bool,
137{
138 let mut left = 0;
139 let mut right = slice.len();
140
141 while left != right {
142 let mid = left + (right - left) / 2;
143 // SAFETY:
144 // When left < right, left <= mid < right.
145 // Therefore left always increases and right always decreases,
146 // and either of them is selected.
147 // In both cases left <= right is satisfied.
148 // Therefore if left < right in a step,
149 // left <= right is satisfied in the next step.
150 // Therefore as long as left != right, 0 <= left < right <= len is satisfied
151 // and if this case 0 <= mid < len is satisfied too.
152 let value = unsafe { slice.get_unchecked(mid) };
153 if pred(value) {
154 left = mid + 1;
155 } else {
156 right = mid;
157 }
158 }
159
160 left
161}
162
163pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize> 91pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
164where 92where
165 F: FnMut(&T) -> Ordering, 93 F: FnMut(&T) -> Ordering,
166{ 94{
167 let start = partition_point(slice, |it| key(it) == Ordering::Less); 95 let start = slice.partition_point(|it| key(it) == Ordering::Less);
168 let len = partition_point(&slice[start..], |it| key(it) == Ordering::Equal); 96 let len = slice[start..].partition_point(|it| key(it) == Ordering::Equal);
169 start..start + len 97 start..start + len
170} 98}
171 99
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 556f80882..c0bc59918 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13[dependencies] 13[dependencies]
14cov-mark = { version = "1.1", features = ["thread-local"] } 14cov-mark = { version = "1.1", features = ["thread-local"] }
15itertools = "0.10.0" 15itertools = "0.10.0"
16rowan = "=0.13.0-pre.3" 16rowan = "=0.13.0-pre.5"
17rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" } 17rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" }
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19arrayvec = "0.7" 19arrayvec = "0.7"
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index c9229c4e0..ba263be0d 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -555,7 +555,7 @@ impl SyntaxRewriter<'_> {
555 555
556fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { 556fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
557 match element { 557 match element {
558 NodeOrToken::Node(it) => NodeOrToken::Node(it.green()), 558 NodeOrToken::Node(it) => NodeOrToken::Node(it.green().into_owned()),
559 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().to_owned()), 559 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().to_owned()),
560 } 560 }
561} 561}
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 42da09606..4bcea28cc 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -572,7 +572,7 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
572} 572}
573 573
574fn unroot(n: SyntaxNode) -> SyntaxNode { 574fn unroot(n: SyntaxNode) -> SyntaxNode {
575 SyntaxNode::new_root(n.green()) 575 SyntaxNode::new_root(n.green().into())
576} 576}
577 577
578pub mod tokens { 578pub mod tokens {
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 492fbc4a0..bef49238f 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -1,30 +1,31 @@
1//! Various extension methods to ast Nodes, which are hard to code-generate. 1//! Various extension methods to ast Nodes, which are hard to code-generate.
2//! Extensions for various expressions live in a sibling `expr_extensions` module. 2//! Extensions for various expressions live in a sibling `expr_extensions` module.
3 3
4use std::{fmt, iter::successors}; 4use std::{borrow::Cow, fmt, iter::successors};
5 5
6use itertools::Itertools; 6use itertools::Itertools;
7use parser::SyntaxKind; 7use parser::SyntaxKind;
8use rowan::{GreenNodeData, GreenTokenData};
8 9
9use crate::{ 10use crate::{
10 ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode}, 11 ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode},
11 SmolStr, SyntaxElement, SyntaxToken, TokenText, T, 12 NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T,
12}; 13};
13 14
14impl ast::Lifetime { 15impl ast::Lifetime {
15 pub fn text(&self) -> TokenText { 16 pub fn text(&self) -> TokenText<'_> {
16 text_of_first_token(self.syntax()) 17 text_of_first_token(self.syntax())
17 } 18 }
18} 19}
19 20
20impl ast::Name { 21impl ast::Name {
21 pub fn text(&self) -> TokenText { 22 pub fn text(&self) -> TokenText<'_> {
22 text_of_first_token(self.syntax()) 23 text_of_first_token(self.syntax())
23 } 24 }
24} 25}
25 26
26impl ast::NameRef { 27impl ast::NameRef {
27 pub fn text(&self) -> TokenText { 28 pub fn text(&self) -> TokenText<'_> {
28 text_of_first_token(self.syntax()) 29 text_of_first_token(self.syntax())
29 } 30 }
30 31
@@ -33,11 +34,15 @@ impl ast::NameRef {
33 } 34 }
34} 35}
35 36
36fn text_of_first_token(node: &SyntaxNode) -> TokenText { 37fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
37 let first_token = 38 fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
38 node.green().children().next().and_then(|it| it.into_token()).unwrap().to_owned(); 39 green_ref.children().next().and_then(NodeOrToken::into_token).unwrap()
40 }
39 41
40 TokenText(first_token) 42 match node.green() {
43 Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()),
44 Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()),
45 }
41} 46}
42 47
43#[derive(Debug, PartialEq, Eq, Clone)] 48#[derive(Debug, PartialEq, Eq, Clone)]
@@ -412,7 +417,7 @@ impl fmt::Display for NameOrNameRef {
412} 417}
413 418
414impl NameOrNameRef { 419impl NameOrNameRef {
415 pub fn text(&self) -> TokenText { 420 pub fn text(&self) -> TokenText<'_> {
416 match self { 421 match self {
417 NameOrNameRef::Name(name) => name.text(), 422 NameOrNameRef::Name(name) => name.text(),
418 NameOrNameRef::NameRef(name_ref) => name_ref.text(), 423 NameOrNameRef::NameRef(name_ref) => name_ref.text(),
diff --git a/crates/syntax/src/token_text.rs b/crates/syntax/src/token_text.rs
index d2ed0a12a..f3e8b321a 100644
--- a/crates/syntax/src/token_text.rs
+++ b/crates/syntax/src/token_text.rs
@@ -2,75 +2,93 @@
2 2
3use std::{cmp::Ordering, fmt, ops}; 3use std::{cmp::Ordering, fmt, ops};
4 4
5pub struct TokenText(pub(crate) rowan::GreenToken); 5use rowan::GreenToken;
6
7pub struct TokenText<'a>(pub(crate) Repr<'a>);
8
9pub(crate) enum Repr<'a> {
10 Borrowed(&'a str),
11 Owned(GreenToken),
12}
13
14impl<'a> TokenText<'a> {
15 pub(crate) fn borrowed(text: &'a str) -> Self {
16 TokenText(Repr::Borrowed(text))
17 }
18
19 pub(crate) fn owned(green: GreenToken) -> Self {
20 TokenText(Repr::Owned(green))
21 }
6 22
7impl TokenText {
8 pub fn as_str(&self) -> &str { 23 pub fn as_str(&self) -> &str {
9 self.0.text() 24 match self.0 {
25 Repr::Borrowed(it) => it,
26 Repr::Owned(ref green) => green.text(),
27 }
10 } 28 }
11} 29}
12 30
13impl ops::Deref for TokenText { 31impl ops::Deref for TokenText<'_> {
14 type Target = str; 32 type Target = str;
15 33
16 fn deref(&self) -> &str { 34 fn deref(&self) -> &str {
17 self.as_str() 35 self.as_str()
18 } 36 }
19} 37}
20impl AsRef<str> for TokenText { 38impl AsRef<str> for TokenText<'_> {
21 fn as_ref(&self) -> &str { 39 fn as_ref(&self) -> &str {
22 self.as_str() 40 self.as_str()
23 } 41 }
24} 42}
25 43
26impl From<TokenText> for String { 44impl From<TokenText<'_>> for String {
27 fn from(token_text: TokenText) -> Self { 45 fn from(token_text: TokenText) -> Self {
28 token_text.as_str().into() 46 token_text.as_str().into()
29 } 47 }
30} 48}
31 49
32impl PartialEq<&'_ str> for TokenText { 50impl PartialEq<&'_ str> for TokenText<'_> {
33 fn eq(&self, other: &&str) -> bool { 51 fn eq(&self, other: &&str) -> bool {
34 self.as_str() == *other 52 self.as_str() == *other
35 } 53 }
36} 54}
37impl PartialEq<TokenText> for &'_ str { 55impl PartialEq<TokenText<'_>> for &'_ str {
38 fn eq(&self, other: &TokenText) -> bool { 56 fn eq(&self, other: &TokenText) -> bool {
39 other == self 57 other == self
40 } 58 }
41} 59}
42impl PartialEq<String> for TokenText { 60impl PartialEq<String> for TokenText<'_> {
43 fn eq(&self, other: &String) -> bool { 61 fn eq(&self, other: &String) -> bool {
44 self.as_str() == other.as_str() 62 self.as_str() == other.as_str()
45 } 63 }
46} 64}
47impl PartialEq<TokenText> for String { 65impl PartialEq<TokenText<'_>> for String {
48 fn eq(&self, other: &TokenText) -> bool { 66 fn eq(&self, other: &TokenText) -> bool {
49 other == self 67 other == self
50 } 68 }
51} 69}
52impl PartialEq for TokenText { 70impl PartialEq for TokenText<'_> {
53 fn eq(&self, other: &TokenText) -> bool { 71 fn eq(&self, other: &TokenText) -> bool {
54 self.as_str() == other.as_str() 72 self.as_str() == other.as_str()
55 } 73 }
56} 74}
57impl Eq for TokenText {} 75impl Eq for TokenText<'_> {}
58impl Ord for TokenText { 76impl Ord for TokenText<'_> {
59 fn cmp(&self, other: &Self) -> Ordering { 77 fn cmp(&self, other: &Self) -> Ordering {
60 self.as_str().cmp(other.as_str()) 78 self.as_str().cmp(other.as_str())
61 } 79 }
62} 80}
63impl PartialOrd for TokenText { 81impl PartialOrd for TokenText<'_> {
64 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 82 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
65 Some(self.cmp(other)) 83 Some(self.cmp(other))
66 } 84 }
67} 85}
68impl fmt::Display for TokenText { 86impl fmt::Display for TokenText<'_> {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 fmt::Display::fmt(self.as_str(), f) 88 fmt::Display::fmt(self.as_str(), f)
71 } 89 }
72} 90}
73impl fmt::Debug for TokenText { 91impl fmt::Debug for TokenText<'_> {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 fmt::Debug::fmt(self.as_str(), f) 93 fmt::Debug::fmt(self.as_str(), f)
76 } 94 }
diff --git a/crates/test_utils/src/fixture.rs b/crates/test_utils/src/fixture.rs
index 099baeca2..d0bddf7d8 100644
--- a/crates/test_utils/src/fixture.rs
+++ b/crates/test_utils/src/fixture.rs
@@ -62,7 +62,7 @@
62//! ``` 62//! ```
63 63
64use rustc_hash::FxHashMap; 64use rustc_hash::FxHashMap;
65use stdx::{lines_with_ends, split_once, trim_indent}; 65use stdx::trim_indent;
66 66
67#[derive(Debug, Eq, PartialEq)] 67#[derive(Debug, Eq, PartialEq)]
68pub struct Fixture { 68pub struct Fixture {
@@ -93,7 +93,7 @@ impl Fixture {
93 93
94 let default = if ra_fixture.contains("//-") { None } else { Some("//- /main.rs") }; 94 let default = if ra_fixture.contains("//-") { None } else { Some("//- /main.rs") };
95 95
96 for (ix, line) in default.into_iter().chain(lines_with_ends(&fixture)).enumerate() { 96 for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() {
97 if line.contains("//-") { 97 if line.contains("//-") {
98 assert!( 98 assert!(
99 line.starts_with("//-"), 99 line.starts_with("//-"),
@@ -133,14 +133,14 @@ impl Fixture {
133 let mut env = FxHashMap::default(); 133 let mut env = FxHashMap::default();
134 let mut introduce_new_source_root = false; 134 let mut introduce_new_source_root = false;
135 for component in components[1..].iter() { 135 for component in components[1..].iter() {
136 let (key, value) = split_once(component, ':').unwrap(); 136 let (key, value) = component.split_once(':').unwrap();
137 match key { 137 match key {
138 "crate" => krate = Some(value.to_string()), 138 "crate" => krate = Some(value.to_string()),
139 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(), 139 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
140 "edition" => edition = Some(value.to_string()), 140 "edition" => edition = Some(value.to_string()),
141 "cfg" => { 141 "cfg" => {
142 for entry in value.split(',') { 142 for entry in value.split(',') {
143 match split_once(entry, '=') { 143 match entry.split_once('=') {
144 Some((k, v)) => cfg_key_values.push((k.to_string(), v.to_string())), 144 Some((k, v)) => cfg_key_values.push((k.to_string(), v.to_string())),
145 None => cfg_atoms.push(entry.to_string()), 145 None => cfg_atoms.push(entry.to_string()),
146 } 146 }
@@ -148,7 +148,7 @@ impl Fixture {
148 } 148 }
149 "env" => { 149 "env" => {
150 for key in value.split(',') { 150 for key in value.split(',') {
151 if let Some((k, v)) = split_once(key, '=') { 151 if let Some((k, v)) = key.split_once('=') {
152 env.insert(k.into(), v.into()); 152 env.insert(k.into(), v.into());
153 } 153 }
154 } 154 }
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 72466c957..fce4fd6bf 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -17,7 +17,7 @@ use std::{
17}; 17};
18 18
19use profile::StopWatch; 19use profile::StopWatch;
20use stdx::{is_ci, lines_with_ends}; 20use stdx::is_ci;
21use text_size::{TextRange, TextSize}; 21use text_size::{TextRange, TextSize};
22 22
23pub use dissimilar::diff as __diff; 23pub use dissimilar::diff as __diff;
@@ -181,7 +181,7 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
181 let mut prev_line_start: Option<TextSize> = None; 181 let mut prev_line_start: Option<TextSize> = None;
182 let mut line_start: TextSize = 0.into(); 182 let mut line_start: TextSize = 0.into();
183 let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new(); 183 let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new();
184 for line in lines_with_ends(text) { 184 for line in text.split_inclusive('\n') {
185 let mut this_line_annotations = Vec::new(); 185 let mut this_line_annotations = Vec::new();
186 if let Some(idx) = line.find("//") { 186 if let Some(idx) = line.find("//") {
187 let annotation_offset = TextSize::of(&line[..idx + "//".len()]); 187 let annotation_offset = TextSize::of(&line[..idx + "//".len()]);
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 00de7a711..d24a5952e 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -947,4 +947,4 @@ match p.current() {
947For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. 947For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines.
948If the line is too long, you want to split the sentence in two :-) 948If the line is too long, you want to split the sentence in two :-)
949 949
950**Rationale:** much easier to edit the text and read the diff. 950**Rationale:** much easier to edit the text and read the diff, see [this link](https://asciidoctor.org/docs/asciidoc-recommended-practices/#one-sentence-per-line).
diff --git a/xtask/src/install.rs b/xtask/src/install.rs
index 91d6a2853..7e2dccdfe 100644
--- a/xtask/src/install.rs
+++ b/xtask/src/install.rs
@@ -8,7 +8,7 @@ use xshell::{cmd, pushd};
8use crate::flags; 8use crate::flags;
9 9
10// Latest stable, feel free to send a PR if this lags behind. 10// Latest stable, feel free to send a PR if this lags behind.
11const REQUIRED_RUST_VERSION: u32 = 51; 11const REQUIRED_RUST_VERSION: u32 = 52;
12 12
13impl flags::Install { 13impl flags::Install {
14 pub(crate) fn run(self) -> Result<()> { 14 pub(crate) fn run(self) -> Result<()> {