aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yaml3
-rw-r--r--crates/base_db/src/input.rs2
-rw-r--r--crates/cfg/src/dnf.rs8
-rw-r--r--crates/hir/src/lib.rs7
-rw-r--r--crates/hir_def/src/attr.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs13
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs21
-rw-r--r--crates/hir_def/src/resolver.rs2
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/name.rs5
-rw-r--r--crates/hir_ty/src/display.rs21
-rw-r--r--crates/hir_ty/src/infer/pat.rs2
-rw-r--r--crates/hir_ty/src/lib.rs17
-rw-r--r--crates/hir_ty/src/lower.rs28
-rw-r--r--crates/hir_ty/src/tests.rs69
-rw-r--r--crates/hir_ty/src/tests/traits.rs50
-rw-r--r--crates/hir_ty/src/traits/chalk.rs2
-rw-r--r--crates/ide/src/extend_selection.rs7
-rw-r--r--crates/ide_assists/src/handlers/convert_comment_block.rs5
-rw-r--r--crates/ide_assists/src/handlers/expand_glob_import.rs19
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs1
-rw-r--r--crates/ide_assists/src/handlers/reorder_impl.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs34
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs93
-rw-r--r--crates/ide_completion/src/completions/lifetime.rs31
-rw-r--r--crates/ide_completion/src/context.rs6
-rw-r--r--crates/ide_completion/src/lib.rs27
-rw-r--r--crates/ide_completion/src/patterns.rs2
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs212
-rw-r--r--crates/ide_db/src/items_locator.rs178
-rw-r--r--crates/ide_db/src/symbol_index.rs16
-rw-r--r--crates/ide_ssr/src/parsing.rs2
-rw-r--r--crates/mbe/src/expander/matcher.rs9
-rw-r--r--crates/mbe/src/lib.rs2
-rw-r--r--crates/mbe/src/parser.rs2
-rw-r--r--crates/mbe/src/syntax_bridge.rs2
-rw-r--r--crates/mbe/src/tests/expand.rs3
-rw-r--r--crates/project_model/src/build_data.rs91
-rw-r--r--crates/rust-analyzer/src/caps.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs7
-rw-r--r--crates/rust-analyzer/src/reload.rs2
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs2
-rw-r--r--crates/syntax/src/algo.rs4
-rw-r--r--crates/syntax/src/ast/edit.rs8
-rw-r--r--crates/syntax/src/ast/expr_ext.rs44
-rw-r--r--crates/syntax/src/ast/make.rs2
-rw-r--r--crates/syntax/src/ast/node_ext.rs19
-rw-r--r--crates/syntax/src/ast/token_ext.rs5
-rw-r--r--crates/syntax/src/fuzz.rs2
-rw-r--r--crates/syntax/src/validation.rs4
-rw-r--r--crates/tt/src/lib.rs3
-rw-r--r--xtask/src/codegen/gen_assists_docs.rs4
-rw-r--r--xtask/src/codegen/gen_parser_tests.rs10
-rw-r--r--xtask/src/codegen/gen_syntax.rs2
-rw-r--r--xtask/src/main.rs2
-rw-r--r--xtask/src/metrics.rs2
-rw-r--r--xtask/src/tidy.rs2
57 files changed, 694 insertions, 430 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 7549e998b..ae9dccce9 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -108,9 +108,6 @@ jobs:
108 with: 108 with:
109 node-version: 12.x 109 node-version: 12.x
110 110
111 - name: Print current revision
112 run: git describe --tags
113
114 - name: Dist 111 - name: Dist
115 if: github.ref == 'refs/heads/release' 112 if: github.ref == 'refs/heads/release'
116 run: cargo xtask dist --client 0.2.$GITHUB_RUN_NUMBER 113 run: cargo xtask dist --client 0.2.$GITHUB_RUN_NUMBER
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index d0def2181..e9e8dfc2e 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -410,7 +410,7 @@ impl CrateId {
410 410
411impl CrateData { 411impl CrateData {
412 fn add_dep(&mut self, name: CrateName, crate_id: CrateId) { 412 fn add_dep(&mut self, name: CrateName, crate_id: CrateId) {
413 self.dependencies.push(Dependency { name, crate_id }) 413 self.dependencies.push(Dependency { crate_id, name })
414 } 414 }
415} 415}
416 416
diff --git a/crates/cfg/src/dnf.rs b/crates/cfg/src/dnf.rs
index 30f4bcdf7..75ded9aa1 100644
--- a/crates/cfg/src/dnf.rs
+++ b/crates/cfg/src/dnf.rs
@@ -255,9 +255,9 @@ impl Builder {
255fn make_dnf(expr: CfgExpr) -> CfgExpr { 255fn make_dnf(expr: CfgExpr) -> CfgExpr {
256 match expr { 256 match expr {
257 CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => expr, 257 CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => expr,
258 CfgExpr::Any(e) => CfgExpr::Any(e.into_iter().map(|expr| make_dnf(expr)).collect()), 258 CfgExpr::Any(e) => CfgExpr::Any(e.into_iter().map(make_dnf).collect()),
259 CfgExpr::All(e) => { 259 CfgExpr::All(e) => {
260 let e = e.into_iter().map(|expr| make_nnf(expr)).collect::<Vec<_>>(); 260 let e = e.into_iter().map(make_nnf).collect::<Vec<_>>();
261 261
262 CfgExpr::Any(distribute_conj(&e)) 262 CfgExpr::Any(distribute_conj(&e))
263 } 263 }
@@ -300,8 +300,8 @@ fn distribute_conj(conj: &[CfgExpr]) -> Vec<CfgExpr> {
300fn make_nnf(expr: CfgExpr) -> CfgExpr { 300fn make_nnf(expr: CfgExpr) -> CfgExpr {
301 match expr { 301 match expr {
302 CfgExpr::Invalid | CfgExpr::Atom(_) => expr, 302 CfgExpr::Invalid | CfgExpr::Atom(_) => expr,
303 CfgExpr::Any(expr) => CfgExpr::Any(expr.into_iter().map(|expr| make_nnf(expr)).collect()), 303 CfgExpr::Any(expr) => CfgExpr::Any(expr.into_iter().map(make_nnf).collect()),
304 CfgExpr::All(expr) => CfgExpr::All(expr.into_iter().map(|expr| make_nnf(expr)).collect()), 304 CfgExpr::All(expr) => CfgExpr::All(expr.into_iter().map(make_nnf).collect()),
305 CfgExpr::Not(operand) => match *operand { 305 CfgExpr::Not(operand) => match *operand {
306 CfgExpr::Invalid | CfgExpr::Atom(_) => CfgExpr::Not(operand.clone()), // Original negated expr 306 CfgExpr::Invalid | CfgExpr::Atom(_) => CfgExpr::Not(operand.clone()), // Original negated expr
307 CfgExpr::Not(expr) => { 307 CfgExpr::Not(expr) => {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index e34be7e42..e3ac37e4c 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -213,7 +213,7 @@ impl Crate {
213 Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text), 213 Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text),
214 _ => None 214 _ => None
215 } 215 }
216 }).flat_map(|t| t).next(); 216 }).flatten().next();
217 217
218 doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") 218 doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
219 } 219 }
@@ -2068,7 +2068,10 @@ impl Type {
2068 match pred { 2068 match pred {
2069 WhereClause::Implemented(trait_ref) => { 2069 WhereClause::Implemented(trait_ref) => {
2070 cb(type_.clone()); 2070 cb(type_.clone());
2071 walk_substs(db, type_, &trait_ref.substitution, cb); 2071 // skip the self type. it's likely the type we just got the bounds from
2072 for ty in trait_ref.substitution.iter().skip(1) {
2073 walk_type(db, &type_.derived(ty.clone()), cb);
2074 }
2072 } 2075 }
2073 _ => (), 2076 _ => (),
2074 } 2077 }
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 2c10f46d8..52a2bce9b 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -638,7 +638,7 @@ fn collect_attrs(
638 owner: &dyn ast::AttrsOwner, 638 owner: &dyn ast::AttrsOwner,
639) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { 639) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> {
640 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) 640 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
641 .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs)))); 641 .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs)));
642 642
643 let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); 643 let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none());
644 let attrs = outer_attrs 644 let attrs = outer_attrs
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 46a3c60cd..28b73c3a1 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1467,12 +1467,13 @@ impl ModCollector<'_, '_> {
1467 }, 1467 },
1468 ) { 1468 ) {
1469 Ok(Ok(macro_call_id)) => { 1469 Ok(Ok(macro_call_id)) => {
1470 self.def_collector.unexpanded_macros.push(MacroDirective { 1470 // Legacy macros need to be expanded immediately, so that any macros they produce
1471 module_id: self.module_id, 1471 // are in scope.
1472 ast_id, 1472 self.def_collector.collect_macro_expansion(
1473 legacy: Some(macro_call_id), 1473 self.module_id,
1474 depth: self.macro_depth + 1, 1474 macro_call_id,
1475 }); 1475 self.macro_depth + 1,
1476 );
1476 1477
1477 return; 1478 return;
1478 } 1479 }
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index d59d3c0db..6d3cb8d7a 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -713,6 +713,27 @@ b! { static = #[] ();}
713} 713}
714 714
715#[test] 715#[test]
716fn macros_defining_macros() {
717 check(
718 r#"
719macro_rules! item {
720 ($item:item) => { $item }
721}
722
723item! {
724 macro_rules! indirect_macro { () => { struct S {} } }
725}
726
727indirect_macro!();
728 "#,
729 expect![[r#"
730 crate
731 S: t
732 "#]],
733 );
734}
735
736#[test]
716fn resolves_proc_macros() { 737fn resolves_proc_macros() {
717 check( 738 check(
718 r" 739 r"
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 4a2d1c087..04ea9c5d7 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -472,7 +472,7 @@ impl Scope {
472 } 472 }
473 Scope::ExprScope(scope) => { 473 Scope::ExprScope(scope) => {
474 if let Some((label, name)) = scope.expr_scopes.label(scope.scope_id) { 474 if let Some((label, name)) = scope.expr_scopes.label(scope.scope_id) {
475 f(name.clone(), ScopeDef::Label(label)) 475 f(name, ScopeDef::Label(label))
476 } 476 }
477 scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| { 477 scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| {
478 f(e.name().clone(), ScopeDef::Local(e.pat())); 478 f(e.name().clone(), ScopeDef::Local(e.pat()));
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 2748e25cf..fc73e435b 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -173,7 +173,7 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
173 }; 173 };
174 let loc = db.lookup_intern_macro(id); 174 let loc = db.lookup_intern_macro(id);
175 let arg = loc.kind.arg(db)?; 175 let arg = loc.kind.arg(db)?;
176 Some(arg.green().to_owned()) 176 Some(arg.green())
177} 177}
178 178
179fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { 179fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 43de9edd6..0aeea48d5 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -48,9 +48,8 @@ impl Name {
48 48
49 /// Resolve a name from the text of token. 49 /// Resolve a name from the text of token.
50 fn resolve(raw_text: &str) -> Name { 50 fn resolve(raw_text: &str) -> Name {
51 let raw_start = "r#"; 51 if let Some(text) = raw_text.strip_prefix("r#") {
52 if raw_text.starts_with(raw_start) { 52 Name::new_text(SmolStr::new(text))
53 Name::new_text(SmolStr::new(&raw_text[raw_start.len()..]))
54 } else { 53 } else {
55 Name::new_text(raw_text.into()) 54 Name::new_text(raw_text.into())
56 } 55 }
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 3845009ae..9d3b79be3 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -571,13 +571,22 @@ impl HirDisplay for Ty {
571 write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))? 571 write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))?
572 } 572 }
573 TypeParamProvenance::ArgumentImplTrait => { 573 TypeParamProvenance::ArgumentImplTrait => {
574 let bounds = f.db.generic_predicates_for_param(id);
575 let substs = Substitution::type_params_for_generics(f.db, &generics); 574 let substs = Substitution::type_params_for_generics(f.db, &generics);
576 write_bounds_like_dyn_trait_with_prefix( 575 let bounds = f
577 "impl", 576 .db
578 &bounds.iter().map(|b| b.clone().subst(&substs)).collect::<Vec<_>>(), 577 .generic_predicates(id.parent)
579 f, 578 .into_iter()
580 )?; 579 .map(|pred| pred.clone().subst(&substs))
580 .filter(|wc| match &wc {
581 WhereClause::Implemented(tr) => tr.self_type_parameter() == self,
582 WhereClause::AliasEq(AliasEq {
583 alias: AliasTy::Projection(proj),
584 ty: _,
585 }) => proj.self_type_parameter() == self,
586 _ => false,
587 })
588 .collect::<Vec<_>>();
589 write_bounds_like_dyn_trait_with_prefix("impl", &bounds, f)?;
581 } 590 }
582 } 591 }
583 } 592 }
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index befa0d69b..ec491648f 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -38,7 +38,7 @@ impl<'a> InferenceContext<'a> {
38 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); 38 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
39 let (pre, post) = match ellipsis { 39 let (pre, post) = match ellipsis {
40 Some(idx) => subpats.split_at(idx), 40 Some(idx) => subpats.split_at(idx),
41 None => (&subpats[..], &[][..]), 41 None => (subpats, &[][..]),
42 }; 42 };
43 let post_idx_offset = field_tys.iter().count() - post.len(); 43 let post_idx_offset = field_tys.iter().count() - post.len();
44 44
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index c46529879..ad908f957 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -106,6 +106,10 @@ impl ProjectionTy {
106 } 106 }
107 } 107 }
108 108
109 pub fn self_type_parameter(&self) -> &Ty {
110 &self.substitution[0]
111 }
112
109 fn trait_(&self, db: &dyn HirDatabase) -> TraitId { 113 fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
110 match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container { 114 match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container {
111 AssocContainerId::TraitId(it) => it, 115 AssocContainerId::TraitId(it) => it,
@@ -936,10 +940,19 @@ impl Ty {
936 let param_data = &generic_params.types[id.local_id]; 940 let param_data = &generic_params.types[id.local_id];
937 match param_data.provenance { 941 match param_data.provenance {
938 hir_def::generics::TypeParamProvenance::ArgumentImplTrait => { 942 hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
943 let substs = Substitution::type_params(db, id.parent);
939 let predicates = db 944 let predicates = db
940 .generic_predicates_for_param(id) 945 .generic_predicates(id.parent)
941 .into_iter() 946 .into_iter()
942 .map(|pred| pred.value.clone()) 947 .map(|pred| pred.clone().subst(&substs))
948 .filter(|wc| match &wc {
949 WhereClause::Implemented(tr) => tr.self_type_parameter() == self,
950 WhereClause::AliasEq(AliasEq {
951 alias: AliasTy::Projection(proj),
952 ty: _,
953 }) => proj.self_type_parameter() == self,
954 _ => false,
955 })
943 .collect_vec(); 956 .collect_vec();
944 957
945 Some(predicates) 958 Some(predicates)
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index cbbb535e5..fd451a823 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -189,7 +189,10 @@ impl<'a> TyLoweringContext<'a> {
189 let self_ty = 189 let self_ty =
190 TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(&Interner); 190 TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(&Interner);
191 let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { 191 let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
192 bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone())).collect() 192 bounds
193 .iter()
194 .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
195 .collect()
193 }); 196 });
194 TyKind::Dyn(predicates).intern(&Interner) 197 TyKind::Dyn(predicates).intern(&Interner)
195 } 198 }
@@ -666,6 +669,7 @@ impl<'a> TyLoweringContext<'a> {
666 pub(crate) fn lower_where_predicate( 669 pub(crate) fn lower_where_predicate(
667 &'a self, 670 &'a self,
668 where_predicate: &'a WherePredicate, 671 where_predicate: &'a WherePredicate,
672 ignore_bindings: bool,
669 ) -> impl Iterator<Item = WhereClause> + 'a { 673 ) -> impl Iterator<Item = WhereClause> + 'a {
670 match where_predicate { 674 match where_predicate {
671 WherePredicate::ForLifetime { target, bound, .. } 675 WherePredicate::ForLifetime { target, bound, .. }
@@ -688,7 +692,9 @@ impl<'a> TyLoweringContext<'a> {
688 .intern(&Interner) 692 .intern(&Interner)
689 } 693 }
690 }; 694 };
691 self.lower_type_bound(bound, self_ty).collect::<Vec<_>>().into_iter() 695 self.lower_type_bound(bound, self_ty, ignore_bindings)
696 .collect::<Vec<_>>()
697 .into_iter()
692 } 698 }
693 WherePredicate::Lifetime { .. } => vec![].into_iter(), 699 WherePredicate::Lifetime { .. } => vec![].into_iter(),
694 } 700 }
@@ -698,6 +704,7 @@ impl<'a> TyLoweringContext<'a> {
698 &'a self, 704 &'a self,
699 bound: &'a TypeBound, 705 bound: &'a TypeBound,
700 self_ty: Ty, 706 self_ty: Ty,
707 ignore_bindings: bool,
701 ) -> impl Iterator<Item = WhereClause> + 'a { 708 ) -> impl Iterator<Item = WhereClause> + 'a {
702 let mut bindings = None; 709 let mut bindings = None;
703 let trait_ref = match bound { 710 let trait_ref = match bound {
@@ -711,6 +718,7 @@ impl<'a> TyLoweringContext<'a> {
711 trait_ref.into_iter().chain( 718 trait_ref.into_iter().chain(
712 bindings 719 bindings
713 .into_iter() 720 .into_iter()
721 .filter(move |_| !ignore_bindings)
714 .flat_map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)), 722 .flat_map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)),
715 ) 723 )
716 } 724 }
@@ -755,6 +763,7 @@ impl<'a> TyLoweringContext<'a> {
755 preds.extend(self.lower_type_bound( 763 preds.extend(self.lower_type_bound(
756 bound, 764 bound,
757 TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(&Interner), 765 TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(&Interner),
766 false,
758 )); 767 ));
759 } 768 }
760 preds 769 preds
@@ -766,7 +775,7 @@ impl<'a> TyLoweringContext<'a> {
766 let self_ty = 775 let self_ty =
767 TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(&Interner); 776 TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(&Interner);
768 let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { 777 let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
769 bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone())).collect() 778 bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)).collect()
770 }); 779 });
771 ReturnTypeImplTrait { bounds: Binders::new(1, predicates) } 780 ReturnTypeImplTrait { bounds: Binders::new(1, predicates) }
772 } 781 }
@@ -896,7 +905,9 @@ pub(crate) fn generic_predicates_for_param_query(
896 }, 905 },
897 WherePredicate::Lifetime { .. } => false, 906 WherePredicate::Lifetime { .. } => false,
898 }) 907 })
899 .flat_map(|pred| ctx.lower_where_predicate(pred).map(|p| Binders::new(generics.len(), p))) 908 .flat_map(|pred| {
909 ctx.lower_where_predicate(pred, true).map(|p| Binders::new(generics.len(), p))
910 })
900 .collect() 911 .collect()
901} 912}
902 913
@@ -918,7 +929,7 @@ pub(crate) fn trait_environment_query(
918 let mut traits_in_scope = Vec::new(); 929 let mut traits_in_scope = Vec::new();
919 let mut clauses = Vec::new(); 930 let mut clauses = Vec::new();
920 for pred in resolver.where_predicates_in_scope() { 931 for pred in resolver.where_predicates_in_scope() {
921 for pred in ctx.lower_where_predicate(pred) { 932 for pred in ctx.lower_where_predicate(pred, false) {
922 if let WhereClause::Implemented(tr) = &pred { 933 if let WhereClause::Implemented(tr) = &pred {
923 traits_in_scope.push((tr.self_type_parameter().clone(), tr.hir_trait_id())); 934 traits_in_scope.push((tr.self_type_parameter().clone(), tr.hir_trait_id()));
924 } 935 }
@@ -946,8 +957,7 @@ pub(crate) fn trait_environment_query(
946 let substs = Substitution::type_params(db, trait_id); 957 let substs = Substitution::type_params(db, trait_id);
947 let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs }; 958 let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs };
948 let pred = WhereClause::Implemented(trait_ref); 959 let pred = WhereClause::Implemented(trait_ref);
949 let program_clause: chalk_ir::ProgramClause<Interner> = 960 let program_clause: chalk_ir::ProgramClause<Interner> = pred.to_chalk(db).cast(&Interner);
950 pred.clone().to_chalk(db).cast(&Interner);
951 clauses.push(program_clause.into_from_env_clause(&Interner)); 961 clauses.push(program_clause.into_from_env_clause(&Interner));
952 } 962 }
953 963
@@ -967,7 +977,9 @@ pub(crate) fn generic_predicates_query(
967 let generics = generics(db.upcast(), def); 977 let generics = generics(db.upcast(), def);
968 resolver 978 resolver
969 .where_predicates_in_scope() 979 .where_predicates_in_scope()
970 .flat_map(|pred| ctx.lower_where_predicate(pred).map(|p| Binders::new(generics.len(), p))) 980 .flat_map(|pred| {
981 ctx.lower_where_predicate(pred, false).map(|p| Binders::new(generics.len(), p))
982 })
971 .collect() 983 .collect()
972} 984}
973 985
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 0a4141e69..ad283c1e0 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -369,3 +369,72 @@ fn check_infer_with_mismatches(ra_fixture: &str, expect: Expect) {
369 actual.push('\n'); 369 actual.push('\n');
370 expect.assert_eq(&actual); 370 expect.assert_eq(&actual);
371} 371}
372
373#[test]
374fn salsa_bug() {
375 let (mut db, pos) = TestDB::with_position(
376 "
377 //- /lib.rs
378 trait Index {
379 type Output;
380 }
381
382 type Key<S: UnificationStoreBase> = <S as UnificationStoreBase>::Key;
383
384 pub trait UnificationStoreBase: Index<Output = Key<Self>> {
385 type Key;
386
387 fn len(&self) -> usize;
388 }
389
390 pub trait UnificationStoreMut: UnificationStoreBase {
391 fn push(&mut self, value: Self::Key);
392 }
393
394 fn main() {
395 let x = 1;
396 x.push(1);$0
397 }
398 ",
399 );
400
401 let module = db.module_for_file(pos.file_id);
402 let crate_def_map = module.def_map(&db);
403 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
404 db.infer(def);
405 });
406
407 let new_text = "
408 //- /lib.rs
409 trait Index {
410 type Output;
411 }
412
413 type Key<S: UnificationStoreBase> = <S as UnificationStoreBase>::Key;
414
415 pub trait UnificationStoreBase: Index<Output = Key<Self>> {
416 type Key;
417
418 fn len(&self) -> usize;
419 }
420
421 pub trait UnificationStoreMut: UnificationStoreBase {
422 fn push(&mut self, value: Self::Key);
423 }
424
425 fn main() {
426
427 let x = 1;
428 x.push(1);
429 }
430 "
431 .to_string();
432
433 db.set_file_text(pos.file_id, Arc::new(new_text));
434
435 let module = db.module_for_file(pos.file_id);
436 let crate_def_map = module.def_map(&db);
437 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
438 db.infer(def);
439 });
440}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 8f2bdffc0..37cd04c6f 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -2272,6 +2272,56 @@ fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
2272} 2272}
2273 2273
2274#[test] 2274#[test]
2275fn unselected_projection_in_trait_env_cycle_3() {
2276 // this is a cycle for rustc; we currently accept it
2277 check_types(
2278 r#"
2279//- /main.rs
2280trait Trait {
2281 type Item;
2282 type OtherItem;
2283}
2284
2285fn test<T>() where T: Trait<OtherItem = T::Item> {
2286 let x: T::Item = no_matter;
2287} //^ Trait::Item<T>
2288"#,
2289 );
2290}
2291
2292#[test]
2293fn unselected_projection_in_trait_env_no_cycle() {
2294 // this is not a cycle
2295 check_types(
2296 r#"
2297//- /main.rs
2298trait Index {
2299 type Output;
2300}
2301
2302type Key<S: UnificationStoreBase> = <S as UnificationStoreBase>::Key;
2303
2304pub trait UnificationStoreBase: Index<Output = Key<Self>> {
2305 type Key;
2306
2307 fn len(&self) -> usize;
2308}
2309
2310pub trait UnificationStoreMut: UnificationStoreBase {
2311 fn push(&mut self, value: Self::Key);
2312}
2313
2314fn test<T>(t: T) where T: UnificationStoreMut {
2315 let x;
2316 t.push(x);
2317 let y: Key<T>;
2318 (x, y);
2319} //^ (UnificationStoreBase::Key<T>, UnificationStoreBase::Key<T>)
2320"#,
2321 );
2322}
2323
2324#[test]
2275fn inline_assoc_type_bounds_1() { 2325fn inline_assoc_type_bounds_1() {
2276 check_types( 2326 check_types(
2277 r#" 2327 r#"
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 734679414..944145603 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -395,7 +395,7 @@ pub(crate) fn associated_ty_data_query(
395 let bounds = type_alias_data 395 let bounds = type_alias_data
396 .bounds 396 .bounds
397 .iter() 397 .iter()
398 .flat_map(|bound| ctx.lower_type_bound(bound, self_ty.clone())) 398 .flat_map(|bound| ctx.lower_type_bound(bound, self_ty.clone(), false))
399 .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty)) 399 .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty))
400 .map(|bound| make_binders(bound.shifted_in(&Interner), 0)) 400 .map(|bound| make_binders(bound.shifted_in(&Interner), 0))
401 .collect(); 401 .collect();
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index e187243cb..5201ce587 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -263,11 +263,10 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
263 ) -> Option<SyntaxToken> { 263 ) -> Option<SyntaxToken> {
264 node.siblings_with_tokens(dir) 264 node.siblings_with_tokens(dir)
265 .skip(1) 265 .skip(1)
266 .skip_while(|node| match node { 266 .find(|node| match node {
267 NodeOrToken::Node(_) => false, 267 NodeOrToken::Node(_) => true,
268 NodeOrToken::Token(it) => is_single_line_ws(it), 268 NodeOrToken::Token(it) => !is_single_line_ws(it),
269 }) 269 })
270 .next()
271 .and_then(|it| it.into_token()) 270 .and_then(|it| it.into_token())
272 .filter(|node| node.kind() == delimiter_kind) 271 .filter(|node| node.kind() == delimiter_kind)
273 } 272 }
diff --git a/crates/ide_assists/src/handlers/convert_comment_block.rs b/crates/ide_assists/src/handlers/convert_comment_block.rs
index cdc45fc42..9dc3ee28f 100644
--- a/crates/ide_assists/src/handlers/convert_comment_block.rs
+++ b/crates/ide_assists/src/handlers/convert_comment_block.rs
@@ -1,5 +1,4 @@
1use itertools::Itertools; 1use itertools::Itertools;
2use std::convert::identity;
3use syntax::{ 2use syntax::{
4 ast::{ 3 ast::{
5 self, 4 self,
@@ -140,7 +139,7 @@ fn relevant_line_comments(comment: &ast::Comment) -> Vec<Comment> {
140 .filter(|s| !skippable(s)) 139 .filter(|s| !skippable(s))
141 .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix)) 140 .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix))
142 .take_while(|opt_com| opt_com.is_some()) 141 .take_while(|opt_com| opt_com.is_some())
143 .filter_map(identity) 142 .flatten()
144 .skip(1); // skip the first element so we don't duplicate it in next_comments 143 .skip(1); // skip the first element so we don't duplicate it in next_comments
145 144
146 let next_comments = comment 145 let next_comments = comment
@@ -149,7 +148,7 @@ fn relevant_line_comments(comment: &ast::Comment) -> Vec<Comment> {
149 .filter(|s| !skippable(s)) 148 .filter(|s| !skippable(s))
150 .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix)) 149 .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix))
151 .take_while(|opt_com| opt_com.is_some()) 150 .take_while(|opt_com| opt_com.is_some())
152 .filter_map(identity); 151 .flatten();
153 152
154 let mut comments: Vec<_> = prev_comments.collect(); 153 let mut comments: Vec<_> = prev_comments.collect();
155 comments.reverse(); 154 comments.reverse();
diff --git a/crates/ide_assists/src/handlers/expand_glob_import.rs b/crates/ide_assists/src/handlers/expand_glob_import.rs
index 83aa11d52..98389e4f7 100644
--- a/crates/ide_assists/src/handlers/expand_glob_import.rs
+++ b/crates/ide_assists/src/handlers/expand_glob_import.rs
@@ -136,18 +136,13 @@ impl Refs {
136 .into_iter() 136 .into_iter()
137 .filter(|r| { 137 .filter(|r| {
138 if let Def::ModuleDef(ModuleDef::Trait(tr)) = r.def { 138 if let Def::ModuleDef(ModuleDef::Trait(tr)) = r.def {
139 if tr 139 if tr.items(ctx.db()).into_iter().any(|ai| {
140 .items(ctx.db()) 140 if let AssocItem::Function(f) = ai {
141 .into_iter() 141 Def::ModuleDef(ModuleDef::Function(f)).is_referenced_in(ctx)
142 .find(|ai| { 142 } else {
143 if let AssocItem::Function(f) = *ai { 143 false
144 Def::ModuleDef(ModuleDef::Function(f)).is_referenced_in(ctx) 144 }
145 } else { 145 }) {
146 false
147 }
148 })
149 .is_some()
150 {
151 return true; 146 return true;
152 } 147 }
153 } 148 }
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs
index e7444f7db..f91770a76 100644
--- a/crates/ide_assists/src/handlers/qualify_path.rs
+++ b/crates/ide_assists/src/handlers/qualify_path.rs
@@ -536,6 +536,7 @@ fn main() {
536 } 536 }
537 537
538 #[test] 538 #[test]
539 #[ignore = "FIXME: non-trait assoc items completion is unsupported yet, see FIXME in the import_assets.rs for more details"]
539 fn associated_struct_const_unqualified() { 540 fn associated_struct_const_unqualified() {
540 check_assist( 541 check_assist(
541 qualify_path, 542 qualify_path,
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs
index edf4b0bfe..f976e73ad 100644
--- a/crates/ide_assists/src/handlers/reorder_impl.rs
+++ b/crates/ide_assists/src/handlers/reorder_impl.rs
@@ -95,7 +95,7 @@ fn compute_method_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashM
95 _ => None, 95 _ => None,
96 }) 96 })
97 .enumerate() 97 .enumerate()
98 .map(|(idx, func)| ((func.name(ctx.db()).to_string(), idx))) 98 .map(|(idx, func)| (func.name(ctx.db()).to_string(), idx))
99 .collect(), 99 .collect(),
100 ) 100 )
101} 101}
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 88fe2fe90..4f0ef52ca 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,5 +1,5 @@
1use hir::ModuleDef; 1use hir::ModuleDef;
2use ide_db::helpers::mod_path_to_ast; 2use ide_db::helpers::{import_assets::NameToImport, mod_path_to_ast};
3use ide_db::items_locator; 3use ide_db::items_locator;
4use itertools::Itertools; 4use itertools::Itertools;
5use syntax::{ 5use syntax::{
@@ -65,20 +65,24 @@ pub(crate) fn replace_derive_with_manual_impl(
65 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; 65 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
66 let current_crate = current_module.krate(); 66 let current_crate = current_module.krate();
67 67
68 let found_traits = 68 let found_traits = items_locator::items_with_name(
69 items_locator::with_exact_name(&ctx.sema, current_crate, trait_token.text().to_string()) 69 &ctx.sema,
70 .into_iter() 70 current_crate,
71 .filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) { 71 NameToImport::Exact(trait_token.text().to_string()),
72 ModuleDef::Trait(trait_) => Some(trait_), 72 items_locator::AssocItemSearch::Exclude,
73 _ => None, 73 Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT),
74 }) 74 )
75 .flat_map(|trait_| { 75 .filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) {
76 current_module 76 ModuleDef::Trait(trait_) => Some(trait_),
77 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) 77 _ => None,
78 .as_ref() 78 })
79 .map(mod_path_to_ast) 79 .flat_map(|trait_| {
80 .zip(Some(trait_)) 80 current_module
81 }); 81 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
82 .as_ref()
83 .map(mod_path_to_ast)
84 .zip(Some(trait_))
85 });
82 86
83 let mut no_traits_found = true; 87 let mut no_traits_found = true;
84 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 88 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 08df2df3f..1ad017198 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -1,8 +1,10 @@
1//! Feature: completion with imports-on-the-fly 1//! Feature: completion with imports-on-the-fly
2//! 2//!
3//! When completing names in the current scope, proposes additional imports from other modules or crates, 3//! When completing names in the current scope, proposes additional imports from other modules or crates,
4//! if they can be qualified in the scope and their name contains all symbols from the completion input 4//! if they can be qualified in the scope and their name contains all symbols from the completion input.
5//! (case-insensitive, in any order or places). 5//!
6//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent.
7//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the contaning is checked case-insensitively.
6//! 8//!
7//! ``` 9//! ```
8//! fn main() { 10//! fn main() {
@@ -942,9 +944,94 @@ mod foo {
942} 944}
943 945
944fn main() { 946fn main() {
945 bar::Ass$0 947 bar::ASS$0
946}"#, 948}"#,
947 expect![[]], 949 expect![[]],
948 ) 950 )
949 } 951 }
952
953 #[test]
954 fn unqualified_assoc_items_are_omitted() {
955 check(
956 r#"
957mod something {
958 pub trait BaseTrait {
959 fn test_function() -> i32;
960 }
961
962 pub struct Item1;
963 pub struct Item2;
964
965 impl BaseTrait for Item1 {
966 fn test_function() -> i32 {
967 1
968 }
969 }
970
971 impl BaseTrait for Item2 {
972 fn test_function() -> i32 {
973 2
974 }
975 }
976}
977
978fn main() {
979 test_f$0
980}"#,
981 expect![[]],
982 )
983 }
984
985 #[test]
986 fn case_matters() {
987 check(
988 r#"
989mod foo {
990 pub const TEST_CONST: usize = 3;
991 pub fn test_function() -> i32 {
992 4
993 }
994}
995
996fn main() {
997 TE$0
998}"#,
999 expect![[r#"
1000 ct foo::TEST_CONST
1001 "#]],
1002 );
1003
1004 check(
1005 r#"
1006mod foo {
1007 pub const TEST_CONST: usize = 3;
1008 pub fn test_function() -> i32 {
1009 4
1010 }
1011}
1012
1013fn main() {
1014 te$0
1015}"#,
1016 expect![[r#"
1017 ct foo::TEST_CONST
1018 fn test_function() (foo::test_function) fn() -> i32
1019 "#]],
1020 );
1021
1022 check(
1023 r#"
1024mod foo {
1025 pub const TEST_CONST: usize = 3;
1026 pub fn test_function() -> i32 {
1027 4
1028 }
1029}
1030
1031fn main() {
1032 Te$0
1033}"#,
1034 expect![[]],
1035 );
1036 }
950} 1037}
diff --git a/crates/ide_completion/src/completions/lifetime.rs b/crates/ide_completion/src/completions/lifetime.rs
index 628c1fb9b..5eeddf7a4 100644
--- a/crates/ide_completion/src/completions/lifetime.rs
+++ b/crates/ide_completion/src/completions/lifetime.rs
@@ -70,6 +70,16 @@ fn func<'lifetime>(foo: &'li$0) {}
70fn func<'lifetime>(foo: &'lifetime) {} 70fn func<'lifetime>(foo: &'lifetime) {}
71"#, 71"#,
72 ); 72 );
73 cov_mark::check!(completes_if_lifetime_without_idents);
74 check_edit(
75 "'lifetime",
76 r#"
77fn func<'lifetime>(foo: &'$0) {}
78"#,
79 r#"
80fn func<'lifetime>(foo: &'lifetime) {}
81"#,
82 );
73 } 83 }
74 84
75 #[test] 85 #[test]
@@ -192,6 +202,27 @@ fn foo<'footime, 'lifetime: 'a$0>() {}
192 } 202 }
193 203
194 #[test] 204 #[test]
205 fn check_label_edit() {
206 check_edit(
207 "'label",
208 r#"
209fn foo() {
210 'label: loop {
211 break '$0
212 }
213}
214"#,
215 r#"
216fn foo() {
217 'label: loop {
218 break 'label
219 }
220}
221"#,
222 );
223 }
224
225 #[test]
195 fn complete_label_in_loop() { 226 fn complete_label_in_loop() {
196 check( 227 check(
197 r#" 228 r#"
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 67e2d6f6c..32f81aec1 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -255,6 +255,10 @@ impl<'a> CompletionContext<'a> {
255 if kind == IDENT || kind == LIFETIME_IDENT || kind == UNDERSCORE || kind.is_keyword() { 255 if kind == IDENT || kind == LIFETIME_IDENT || kind == UNDERSCORE || kind.is_keyword() {
256 cov_mark::hit!(completes_if_prefix_is_keyword); 256 cov_mark::hit!(completes_if_prefix_is_keyword);
257 self.original_token.text_range() 257 self.original_token.text_range()
258 } else if kind == CHAR {
259 // assume we are completing a lifetime but the user has only typed the '
260 cov_mark::hit!(completes_if_lifetime_without_idents);
261 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
258 } else { 262 } else {
259 TextRange::empty(self.position.offset) 263 TextRange::empty(self.position.offset)
260 } 264 }
@@ -471,7 +475,7 @@ impl<'a> CompletionContext<'a> {
471 self.lifetime_syntax = 475 self.lifetime_syntax =
472 find_node_at_offset(original_file, lifetime.syntax().text_range().start()); 476 find_node_at_offset(original_file, lifetime.syntax().text_range().start());
473 if let Some(parent) = lifetime.syntax().parent() { 477 if let Some(parent) = lifetime.syntax().parent() {
474 if parent.kind() == syntax::SyntaxKind::ERROR { 478 if parent.kind() == ERROR {
475 return; 479 return;
476 } 480 }
477 481
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 995970fca..5ac1cb48d 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -14,7 +14,10 @@ mod completions;
14use completions::flyimport::position_for_import; 14use completions::flyimport::position_for_import;
15use ide_db::{ 15use ide_db::{
16 base_db::FilePosition, 16 base_db::FilePosition,
17 helpers::{import_assets::LocatedImport, insert_use::ImportScope}, 17 helpers::{
18 import_assets::{LocatedImport, NameToImport},
19 insert_use::ImportScope,
20 },
18 items_locator, RootDatabase, 21 items_locator, RootDatabase,
19}; 22};
20use text_edit::TextEdit; 23use text_edit::TextEdit;
@@ -151,15 +154,19 @@ pub fn resolve_completion_edits(
151 let current_module = ctx.sema.scope(position_for_import).module()?; 154 let current_module = ctx.sema.scope(position_for_import).module()?;
152 let current_crate = current_module.krate(); 155 let current_crate = current_module.krate();
153 156
154 let (import_path, item_to_import) = 157 let (import_path, item_to_import) = items_locator::items_with_name(
155 items_locator::with_exact_name(&ctx.sema, current_crate, imported_name) 158 &ctx.sema,
156 .into_iter() 159 current_crate,
157 .filter_map(|candidate| { 160 NameToImport::Exact(imported_name),
158 current_module 161 items_locator::AssocItemSearch::Include,
159 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind) 162 Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT),
160 .zip(Some(candidate)) 163 )
161 }) 164 .filter_map(|candidate| {
162 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?; 165 current_module
166 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
167 .zip(Some(candidate))
168 })
169 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
163 let import = 170 let import =
164 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path)); 171 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path));
165 172
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index cf5ef07b7..d82564381 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -71,7 +71,7 @@ fn test_has_block_expr_parent() {
71} 71}
72 72
73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { 73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
74 element.ancestors().find(|it| it.kind() == IDENT_PAT).is_some() 74 element.ancestors().any(|it| it.kind() == IDENT_PAT)
75} 75}
76#[test] 76#[test]
77fn test_has_bind_pat_parent() { 77fn test_has_bind_pat_parent() {
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 7c8844e95..1881c746f 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -61,7 +61,7 @@ pub struct FirstSegmentUnresolved {
61} 61}
62 62
63/// A name that will be used during item lookups. 63/// A name that will be used during item lookups.
64#[derive(Debug)] 64#[derive(Debug, Clone)]
65pub enum NameToImport { 65pub enum NameToImport {
66 /// Requires items with names that exactly match the given string, case-sensitive. 66 /// Requires items with names that exactly match the given string, case-sensitive.
67 Exact(String), 67 Exact(String),
@@ -201,129 +201,101 @@ impl ImportAssets {
201 sema: &Semantics<RootDatabase>, 201 sema: &Semantics<RootDatabase>,
202 prefixed: Option<PrefixKind>, 202 prefixed: Option<PrefixKind>,
203 ) -> Vec<LocatedImport> { 203 ) -> Vec<LocatedImport> {
204 let items_with_candidate_name = match self.name_to_import() { 204 let _p = profile::span("import_assets::search_for");
205 NameToImport::Exact(exact_name) => items_locator::with_exact_name(
206 sema,
207 self.module_with_candidate.krate(),
208 exact_name.clone(),
209 ),
210 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
211 // instead, we need to look up all trait impls for a certain struct and search through them only
212 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
213 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
214 // for the details
215 NameToImport::Fuzzy(fuzzy_name) => {
216 let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() {
217 (AssocItemSearch::AssocItemsOnly, None)
218 } else {
219 (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT))
220 };
221
222 items_locator::with_similar_name(
223 sema,
224 self.module_with_candidate.krate(),
225 fuzzy_name.clone(),
226 assoc_item_search,
227 limit,
228 )
229 }
230 };
231 205
232 let scope_definitions = self.scope_definitions(sema); 206 let scope_definitions = self.scope_definitions(sema);
233 self.applicable_defs(sema.db, prefixed, items_with_candidate_name)
234 .into_iter()
235 .filter(|import| import.import_path.len() > 1)
236 .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
237 .sorted_by_key(|import| import.import_path.clone())
238 .collect()
239 }
240
241 fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> {
242 let mut scope_definitions = FxHashSet::default();
243 sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| {
244 scope_definitions.insert(scope_def);
245 });
246 scope_definitions
247 }
248
249 fn name_to_import(&self) -> &NameToImport {
250 match &self.import_candidate {
251 ImportCandidate::Path(candidate) => &candidate.name,
252 ImportCandidate::TraitAssocItem(candidate)
253 | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name,
254 }
255 }
256
257 fn applicable_defs(
258 &self,
259 db: &RootDatabase,
260 prefixed: Option<PrefixKind>,
261 items_with_candidate_name: FxHashSet<ItemInNs>,
262 ) -> FxHashSet<LocatedImport> {
263 let _p = profile::span("import_assets::applicable_defs");
264 let current_crate = self.module_with_candidate.krate(); 207 let current_crate = self.module_with_candidate.krate();
265
266 let mod_path = |item| { 208 let mod_path = |item| {
267 get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed) 209 get_mod_path(
210 sema.db,
211 item_for_path_search(sema.db, item)?,
212 &self.module_with_candidate,
213 prefixed,
214 )
268 }; 215 };
269 216
270 match &self.import_candidate { 217 match &self.import_candidate {
271 ImportCandidate::Path(path_candidate) => { 218 ImportCandidate::Path(path_candidate) => {
272 path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name) 219 path_applicable_imports(sema, current_crate, path_candidate, mod_path)
220 }
221 ImportCandidate::TraitAssocItem(trait_candidate) => {
222 trait_applicable_items(sema, current_crate, trait_candidate, true, mod_path)
223 }
224 ImportCandidate::TraitMethod(trait_candidate) => {
225 trait_applicable_items(sema, current_crate, trait_candidate, false, mod_path)
273 } 226 }
274 ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items(
275 db,
276 current_crate,
277 trait_candidate,
278 true,
279 mod_path,
280 items_with_candidate_name,
281 ),
282 ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
283 db,
284 current_crate,
285 trait_candidate,
286 false,
287 mod_path,
288 items_with_candidate_name,
289 ),
290 } 227 }
228 .into_iter()
229 .filter(|import| import.import_path.len() > 1)
230 .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
231 .sorted_by_key(|import| import.import_path.clone())
232 .collect()
233 }
234
235 fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> {
236 let _p = profile::span("import_assets::scope_definitions");
237 let mut scope_definitions = FxHashSet::default();
238 sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| {
239 scope_definitions.insert(scope_def);
240 });
241 scope_definitions
291 } 242 }
292} 243}
293 244
294fn path_applicable_imports( 245fn path_applicable_imports(
295 db: &RootDatabase, 246 sema: &Semantics<RootDatabase>,
247 current_crate: Crate,
296 path_candidate: &PathImportCandidate, 248 path_candidate: &PathImportCandidate,
297 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy, 249 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
298 items_with_candidate_name: FxHashSet<ItemInNs>,
299) -> FxHashSet<LocatedImport> { 250) -> FxHashSet<LocatedImport> {
300 let _p = profile::span("import_assets::path_applicable_imports"); 251 let _p = profile::span("import_assets::path_applicable_imports");
301 252
302 let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { 253 match &path_candidate.qualifier {
303 None => { 254 None => {
304 return items_with_candidate_name 255 items_locator::items_with_name(
305 .into_iter() 256 sema,
306 .filter_map(|item| { 257 current_crate,
307 let mut mod_path = mod_path(item)?; 258 path_candidate.name.clone(),
308 if let Some(assoc_item) = item_as_assoc(db, item) { 259 // FIXME: we could look up assoc items by the input and propose those in completion,
309 mod_path.push_segment(assoc_item.name(db)?); 260 // but that requries more preparation first:
310 } 261 // * store non-trait assoc items in import_map to fully enable this lookup
311 Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path))) 262 // * ensure that does not degrade the performance (bencmark it)
312 }) 263 // * write more logic to check for corresponding trait presence requirement (we're unable to flyimport multiple item right now)
313 .collect(); 264 // * improve the associated completion item matching and/or scoring to ensure no noisy completions appear
265 //
266 // see also an ignored test under FIXME comment in the qualify_path.rs module
267 AssocItemSearch::Exclude,
268 Some(DEFAULT_QUERY_SEARCH_LIMIT),
269 )
270 .filter_map(|item| {
271 let mod_path = mod_path(item)?;
272 Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path)))
273 })
274 .collect()
314 } 275 }
315 Some(first_segment_unresolved) => ( 276 Some(first_segment_unresolved) => {
316 first_segment_unresolved.fist_segment.to_string(), 277 let unresolved_qualifier =
317 path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier), 278 path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier);
318 ), 279 let unresolved_first_segment = first_segment_unresolved.fist_segment.text();
319 }; 280 items_locator::items_with_name(
320 281 sema,
321 items_with_candidate_name 282 current_crate,
322 .into_iter() 283 path_candidate.name.clone(),
323 .filter_map(|item| { 284 AssocItemSearch::Include,
324 import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item) 285 Some(DEFAULT_QUERY_SEARCH_LIMIT),
325 }) 286 )
326 .collect() 287 .filter_map(|item| {
288 import_for_item(
289 sema.db,
290 mod_path,
291 unresolved_first_segment,
292 &unresolved_qualifier,
293 item,
294 )
295 })
296 .collect()
297 }
298 }
327} 299}
328 300
329fn import_for_item( 301fn import_for_item(
@@ -438,25 +410,31 @@ fn module_with_segment_name(
438} 410}
439 411
440fn trait_applicable_items( 412fn trait_applicable_items(
441 db: &RootDatabase, 413 sema: &Semantics<RootDatabase>,
442 current_crate: Crate, 414 current_crate: Crate,
443 trait_candidate: &TraitImportCandidate, 415 trait_candidate: &TraitImportCandidate,
444 trait_assoc_item: bool, 416 trait_assoc_item: bool,
445 mod_path: impl Fn(ItemInNs) -> Option<ModPath>, 417 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
446 items_with_candidate_name: FxHashSet<ItemInNs>,
447) -> FxHashSet<LocatedImport> { 418) -> FxHashSet<LocatedImport> {
448 let _p = profile::span("import_assets::trait_applicable_items"); 419 let _p = profile::span("import_assets::trait_applicable_items");
449 let mut required_assoc_items = FxHashSet::default();
450 420
451 let trait_candidates = items_with_candidate_name 421 let db = sema.db;
452 .into_iter() 422
453 .filter_map(|input| item_as_assoc(db, input)) 423 let mut required_assoc_items = FxHashSet::default();
454 .filter_map(|assoc| { 424 let trait_candidates = items_locator::items_with_name(
455 let assoc_item_trait = assoc.containing_trait(db)?; 425 sema,
456 required_assoc_items.insert(assoc); 426 current_crate,
457 Some(assoc_item_trait.into()) 427 trait_candidate.assoc_item_name.clone(),
458 }) 428 AssocItemSearch::AssocItemsOnly,
459 .collect(); 429 Some(DEFAULT_QUERY_SEARCH_LIMIT),
430 )
431 .filter_map(|input| item_as_assoc(db, input))
432 .filter_map(|assoc| {
433 let assoc_item_trait = assoc.containing_trait(db)?;
434 required_assoc_items.insert(assoc);
435 Some(assoc_item_trait.into())
436 })
437 .collect();
460 438
461 let mut located_imports = FxHashSet::default(); 439 let mut located_imports = FxHashSet::default();
462 440
@@ -565,10 +543,6 @@ impl ImportCandidate {
565 ) -> Option<Self> { 543 ) -> Option<Self> {
566 path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) 544 path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name))
567 } 545 }
568
569 fn is_trait_candidate(&self) -> bool {
570 matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_))
571 }
572} 546}
573 547
574fn path_import_candidate( 548fn path_import_candidate(
diff --git a/crates/ide_db/src/items_locator.rs b/crates/ide_db/src/items_locator.rs
index 8a7f02935..ef796b6f7 100644
--- a/crates/ide_db/src/items_locator.rs
+++ b/crates/ide_db/src/items_locator.rs
@@ -1,6 +1,7 @@
1//! This module contains an import search functionality that is provided to the assists module. 1//! This module has the functionality to search the project and its dependencies for a certain item,
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! by its name and a few criteria.
3 3//! The main reason for this module to exist is the fact that project's items and dependencies' items
4//! are located in different caches, with different APIs.
4use either::Either; 5use either::Either;
5use hir::{ 6use hir::{
6 import_map::{self, ImportKind}, 7 import_map::{self, ImportKind},
@@ -10,122 +11,121 @@ use syntax::{ast, AstNode, SyntaxKind::NAME};
10 11
11use crate::{ 12use crate::{
12 defs::{Definition, NameClass}, 13 defs::{Definition, NameClass},
14 helpers::import_assets::NameToImport,
13 symbol_index::{self, FileSymbol}, 15 symbol_index::{self, FileSymbol},
14 RootDatabase, 16 RootDatabase,
15}; 17};
16use rustc_hash::FxHashSet;
17
18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
19 18
20pub fn with_exact_name( 19/// A value to use, when uncertain which limit to pick.
21 sema: &Semantics<'_, RootDatabase>, 20pub const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
22 krate: Crate,
23 exact_name: String,
24) -> FxHashSet<ItemInNs> {
25 let _p = profile::span("find_exact_imports");
26 find_items(
27 sema,
28 krate,
29 {
30 let mut local_query = symbol_index::Query::new(exact_name.clone());
31 local_query.exact();
32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
33 local_query
34 },
35 import_map::Query::new(exact_name)
36 .limit(DEFAULT_QUERY_SEARCH_LIMIT)
37 .name_only()
38 .search_mode(import_map::SearchMode::Equals)
39 .case_sensitive(),
40 )
41}
42 21
43#[derive(Debug)] 22/// Three possible ways to search for the name in associated and/or other items.
23#[derive(Debug, Clone, Copy)]
44pub enum AssocItemSearch { 24pub enum AssocItemSearch {
25 /// Search for the name in both associated and other items.
45 Include, 26 Include,
27 /// Search for the name in other items only.
46 Exclude, 28 Exclude,
29 /// Search for the name in the associated items only.
47 AssocItemsOnly, 30 AssocItemsOnly,
48} 31}
49 32
50pub fn with_similar_name( 33/// Searches for importable items with the given name in the crate and its dependencies.
51 sema: &Semantics<'_, RootDatabase>, 34pub fn items_with_name<'a>(
35 sema: &'a Semantics<'_, RootDatabase>,
52 krate: Crate, 36 krate: Crate,
53 fuzzy_search_string: String, 37 name: NameToImport,
54 assoc_item_search: AssocItemSearch, 38 assoc_item_search: AssocItemSearch,
55 limit: Option<usize>, 39 limit: Option<usize>,
56) -> FxHashSet<ItemInNs> { 40) -> impl Iterator<Item = ItemInNs> + 'a {
57 let _p = profile::span("find_similar_imports"); 41 let _p = profile::span("items_with_name").detail(|| {
42 format!(
43 "Name: {}, crate: {:?}, assoc items: {:?}, limit: {:?}",
44 name.text(),
45 assoc_item_search,
46 krate.display_name(sema.db).map(|name| name.to_string()),
47 limit,
48 )
49 });
50
51 let (mut local_query, mut external_query) = match name {
52 NameToImport::Exact(exact_name) => {
53 let mut local_query = symbol_index::Query::new(exact_name.clone());
54 local_query.exact();
58 55
59 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 56 let external_query = import_map::Query::new(exact_name)
60 .search_mode(import_map::SearchMode::Fuzzy) 57 .name_only()
61 .name_only(); 58 .search_mode(import_map::SearchMode::Equals)
59 .case_sensitive();
62 60
63 match assoc_item_search { 61 (local_query, external_query)
64 AssocItemSearch::Include => {}
65 AssocItemSearch::Exclude => {
66 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
67 } 62 }
68 AssocItemSearch::AssocItemsOnly => { 63 NameToImport::Fuzzy(fuzzy_search_string) => {
69 external_query = external_query.assoc_items_only(); 64 let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
65
66 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
67 .search_mode(import_map::SearchMode::Fuzzy)
68 .name_only();
69 match assoc_item_search {
70 AssocItemSearch::Include => {}
71 AssocItemSearch::Exclude => {
72 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
73 }
74 AssocItemSearch::AssocItemsOnly => {
75 external_query = external_query.assoc_items_only();
76 }
77 }
78
79 if fuzzy_search_string.to_lowercase() != fuzzy_search_string {
80 local_query.case_sensitive();
81 external_query = external_query.case_sensitive();
82 }
83
84 (local_query, external_query)
70 } 85 }
71 } 86 };
72
73 let mut local_query = symbol_index::Query::new(fuzzy_search_string);
74 87
75 if let Some(limit) = limit { 88 if let Some(limit) = limit {
76 external_query = external_query.limit(limit); 89 external_query = external_query.limit(limit);
77 local_query.limit(limit); 90 local_query.limit(limit);
78 } 91 }
79 92
80 find_items(sema, krate, local_query, external_query) 93 find_items(sema, krate, assoc_item_search, local_query, external_query)
81 .into_iter()
82 .filter(move |&item| match assoc_item_search {
83 AssocItemSearch::Include => true,
84 AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
85 AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
86 })
87 .collect()
88} 94}
89 95
90fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { 96fn find_items<'a>(
91 item.as_module_def_id() 97 sema: &'a Semantics<'_, RootDatabase>,
92 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
93 .is_some()
94}
95
96fn find_items(
97 sema: &Semantics<'_, RootDatabase>,
98 krate: Crate, 98 krate: Crate,
99 assoc_item_search: AssocItemSearch,
99 local_query: symbol_index::Query, 100 local_query: symbol_index::Query,
100 external_query: import_map::Query, 101 external_query: import_map::Query,
101) -> FxHashSet<ItemInNs> { 102) -> impl Iterator<Item = ItemInNs> + 'a {
102 let _p = profile::span("find_similar_imports"); 103 let _p = profile::span("find_items");
103 let db = sema.db; 104 let db = sema.db;
104 105
105 // Query dependencies first. 106 let external_importables =
106 let mut candidates = krate 107 krate.query_external_importables(db, external_query).map(|external_importable| {
107 .query_external_importables(db, external_query) 108 match external_importable {
108 .map(|external_importable| match external_importable { 109 Either::Left(module_def) => ItemInNs::from(module_def),
109 Either::Left(module_def) => ItemInNs::from(module_def), 110 Either::Right(macro_def) => ItemInNs::from(macro_def),
110 Either::Right(macro_def) => ItemInNs::from(macro_def), 111 }
111 }) 112 });
112 .collect::<FxHashSet<_>>();
113 113
114 // Query the local crate using the symbol index. 114 // Query the local crate using the symbol index.
115 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); 115 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query)
116 116 .into_iter()
117 candidates.extend( 117 .filter_map(move |local_candidate| get_name_definition(sema, &local_candidate))
118 local_results 118 .filter_map(|name_definition_to_import| match name_definition_to_import {
119 .into_iter() 119 Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)),
120 .filter_map(|local_candidate| get_name_definition(sema, &local_candidate)) 120 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
121 .filter_map(|name_definition_to_import| match name_definition_to_import { 121 _ => None,
122 Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)), 122 });
123 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)), 123
124 _ => None, 124 external_importables.chain(local_results).filter(move |&item| match assoc_item_search {
125 }), 125 AssocItemSearch::Include => true,
126 ); 126 AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
127 127 AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
128 candidates 128 })
129} 129}
130 130
131fn get_name_definition( 131fn get_name_definition(
@@ -144,3 +144,9 @@ fn get_name_definition(
144 let name = ast::Name::cast(candidate_name_node)?; 144 let name = ast::Name::cast(candidate_name_node)?;
145 NameClass::classify(sema, &name)?.defined(sema.db) 145 NameClass::classify(sema, &name)?.defined(sema.db)
146} 146}
147
148fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
149 item.as_module_def_id()
150 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
151 .is_some()
152}
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 9ed9568ce..35e382b5c 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -52,6 +52,7 @@ pub struct Query {
52 only_types: bool, 52 only_types: bool,
53 libs: bool, 53 libs: bool,
54 exact: bool, 54 exact: bool,
55 case_sensitive: bool,
55 limit: usize, 56 limit: usize,
56} 57}
57 58
@@ -64,6 +65,7 @@ impl Query {
64 only_types: false, 65 only_types: false,
65 libs: false, 66 libs: false,
66 exact: false, 67 exact: false,
68 case_sensitive: false,
67 limit: usize::max_value(), 69 limit: usize::max_value(),
68 } 70 }
69 } 71 }
@@ -80,6 +82,10 @@ impl Query {
80 self.exact = true; 82 self.exact = true;
81 } 83 }
82 84
85 pub fn case_sensitive(&mut self) {
86 self.case_sensitive = true;
87 }
88
83 pub fn limit(&mut self, limit: usize) { 89 pub fn limit(&mut self, limit: usize) {
84 self.limit = limit 90 self.limit = limit
85 } 91 }
@@ -326,8 +332,14 @@ impl Query {
326 if self.only_types && !symbol.kind.is_type() { 332 if self.only_types && !symbol.kind.is_type() {
327 continue; 333 continue;
328 } 334 }
329 if self.exact && symbol.name != self.query { 335 if self.exact {
330 continue; 336 if symbol.name != self.query {
337 continue;
338 }
339 } else if self.case_sensitive {
340 if self.query.chars().any(|c| !symbol.name.contains(c)) {
341 continue;
342 }
331 } 343 }
332 344
333 res.push(symbol.clone()); 345 res.push(symbol.clone());
diff --git a/crates/ide_ssr/src/parsing.rs b/crates/ide_ssr/src/parsing.rs
index 5ff25cb6d..5e757e701 100644
--- a/crates/ide_ssr/src/parsing.rs
+++ b/crates/ide_ssr/src/parsing.rs
@@ -67,7 +67,7 @@ impl ParsedRule {
67 ) -> Result<Vec<ParsedRule>, SsrError> { 67 ) -> Result<Vec<ParsedRule>, SsrError> {
68 let raw_pattern = pattern.as_rust_code(); 68 let raw_pattern = pattern.as_rust_code();
69 let raw_template = template.map(|t| t.as_rust_code()); 69 let raw_template = template.map(|t| t.as_rust_code());
70 let raw_template = raw_template.as_ref().map(|s| s.as_str()); 70 let raw_template = raw_template.as_deref();
71 let mut builder = RuleBuilder { 71 let mut builder = RuleBuilder {
72 placeholders_by_stand_in: pattern.placeholders_by_stand_in(), 72 placeholders_by_stand_in: pattern.placeholders_by_stand_in(),
73 rules: Vec::new(), 73 rules: Vec::new(),
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index 1682b21b0..75d2f2eed 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -304,7 +304,7 @@ impl BindingsBuilder {
304 link_nodes: &'a Vec<LinkNode<Rc<BindingKind>>>, 304 link_nodes: &'a Vec<LinkNode<Rc<BindingKind>>>,
305 nodes: &mut Vec<&'a Rc<BindingKind>>, 305 nodes: &mut Vec<&'a Rc<BindingKind>>,
306 ) { 306 ) {
307 link_nodes.into_iter().for_each(|it| match it { 307 link_nodes.iter().for_each(|it| match it {
308 LinkNode::Node(it) => nodes.push(it), 308 LinkNode::Node(it) => nodes.push(it),
309 LinkNode::Parent { idx, len } => self.collect_nodes_ref(*idx, *len, nodes), 309 LinkNode::Parent { idx, len } => self.collect_nodes_ref(*idx, *len, nodes),
310 }); 310 });
@@ -713,10 +713,9 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragmen
713 .map(|ident| Some(tt::Leaf::from(ident.clone()).into())) 713 .map(|ident| Some(tt::Leaf::from(ident.clone()).into()))
714 .map_err(|()| err!("expected ident")), 714 .map_err(|()| err!("expected ident")),
715 "tt" => input.expect_tt().map(Some).map_err(|()| err!()), 715 "tt" => input.expect_tt().map(Some).map_err(|()| err!()),
716 "lifetime" => input 716 "lifetime" => {
717 .expect_lifetime() 717 input.expect_lifetime().map(Some).map_err(|()| err!("expected lifetime"))
718 .map(|tt| Some(tt)) 718 }
719 .map_err(|()| err!("expected lifetime")),
720 "literal" => { 719 "literal" => {
721 let neg = input.eat_char('-'); 720 let neg = input.eat_char('-');
722 input 721 input
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 33b85e23d..e74f8cf3f 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -356,6 +356,6 @@ impl<T> ExpandResult<T> {
356 356
357impl<T: Default> From<Result<T, ExpandError>> for ExpandResult<T> { 357impl<T: Default> From<Result<T, ExpandError>> for ExpandResult<T> {
358 fn from(result: Result<T, ExpandError>) -> Self { 358 fn from(result: Result<T, ExpandError>) -> Self {
359 result.map_or_else(|e| Self::only_err(e), |it| Self::ok(it)) 359 result.map_or_else(Self::only_err, Self::ok)
360 } 360 }
361} 361}
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index c88387653..61b2a4955 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -57,7 +57,7 @@ impl<'a> Iterator for OpDelimitedIter<'a> {
57 57
58 fn size_hint(&self) -> (usize, Option<usize>) { 58 fn size_hint(&self) -> (usize, Option<usize>) {
59 let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 }; 59 let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
60 let remain = len.checked_sub(self.idx).unwrap_or(0); 60 let remain = len.saturating_sub(self.idx);
61 (remain, Some(remain)) 61 (remain, Some(remain))
62 } 62 }
63} 63}
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 8bba3d3d5..9d433b3b0 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -362,7 +362,7 @@ trait TokenConvertor {
362 if let Some((kind, closed)) = delim { 362 if let Some((kind, closed)) = delim {
363 let mut subtree = tt::Subtree::default(); 363 let mut subtree = tt::Subtree::default();
364 let (id, idx) = self.id_alloc().open_delim(range); 364 let (id, idx) = self.id_alloc().open_delim(range);
365 subtree.delimiter = Some(tt::Delimiter { kind, id }); 365 subtree.delimiter = Some(tt::Delimiter { id, kind });
366 366
367 while self.peek().map(|it| it.kind() != closed).unwrap_or(false) { 367 while self.peek().map(|it| it.kind() != closed).unwrap_or(false) {
368 self.collect_leaf(&mut subtree.token_trees); 368 self.collect_leaf(&mut subtree.token_trees);
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs
index 2cce62781..8951f3813 100644
--- a/crates/mbe/src/tests/expand.rs
+++ b/crates/mbe/src/tests/expand.rs
@@ -1225,8 +1225,7 @@ macro_rules! m {
1225 ) 1225 )
1226 .expand_statements(r#"m!(C("0"))"#) 1226 .expand_statements(r#"m!(C("0"))"#)
1227 .descendants() 1227 .descendants()
1228 .find(|token| token.kind() == ERROR) 1228 .any(|token| token.kind() == ERROR));
1229 .is_some());
1230} 1229}
1231 1230
1232#[test] 1231#[test]
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index 728a258ea..f7050be4e 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -137,60 +137,53 @@ fn collect_from_workspace(
137 let stdout = BufReader::new(child_stdout); 137 let stdout = BufReader::new(child_stdout);
138 138
139 let mut res = BuildDataMap::default(); 139 let mut res = BuildDataMap::default();
140 for message in cargo_metadata::Message::parse_stream(stdout) { 140 for message in cargo_metadata::Message::parse_stream(stdout).flatten() {
141 if let Ok(message) = message { 141 match message {
142 match message { 142 Message::BuildScriptExecuted(BuildScript {
143 Message::BuildScriptExecuted(BuildScript { 143 package_id, out_dir, cfgs, env, ..
144 package_id, 144 }) => {
145 out_dir, 145 let cfgs = {
146 cfgs, 146 let mut acc = Vec::new();
147 env, 147 for cfg in cfgs {
148 .. 148 match cfg.parse::<CfgFlag>() {
149 }) => { 149 Ok(it) => acc.push(it),
150 let cfgs = { 150 Err(err) => {
151 let mut acc = Vec::new(); 151 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
152 for cfg in cfgs { 152 }
153 match cfg.parse::<CfgFlag>() { 153 };
154 Ok(it) => acc.push(it),
155 Err(err) => {
156 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
157 }
158 };
159 }
160 acc
161 };
162 let res = res.entry(package_id.repr.clone()).or_default();
163 // cargo_metadata crate returns default (empty) path for
164 // older cargos, which is not absolute, so work around that.
165 if !out_dir.as_str().is_empty() {
166 let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string()));
167 res.out_dir = Some(out_dir);
168 res.cfgs = cfgs;
169 } 154 }
170 155 acc
171 res.envs = env; 156 };
157 let res = res.entry(package_id.repr.clone()).or_default();
158 // cargo_metadata crate returns default (empty) path for
159 // older cargos, which is not absolute, so work around that.
160 if !out_dir.as_str().is_empty() {
161 let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string()));
162 res.out_dir = Some(out_dir);
163 res.cfgs = cfgs;
172 } 164 }
173 Message::CompilerArtifact(message) => { 165
174 progress(format!("metadata {}", message.target.name)); 166 res.envs = env;
175 167 }
176 if message.target.kind.contains(&"proc-macro".to_string()) { 168 Message::CompilerArtifact(message) => {
177 let package_id = message.package_id; 169 progress(format!("metadata {}", message.target.name));
178 // Skip rmeta file 170
179 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) 171 if message.target.kind.contains(&"proc-macro".to_string()) {
180 { 172 let package_id = message.package_id;
181 let filename = AbsPathBuf::assert(PathBuf::from(&filename)); 173 // Skip rmeta file
182 let res = res.entry(package_id.repr.clone()).or_default(); 174 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) {
183 res.proc_macro_dylib_path = Some(filename); 175 let filename = AbsPathBuf::assert(PathBuf::from(&filename));
184 } 176 let res = res.entry(package_id.repr.clone()).or_default();
177 res.proc_macro_dylib_path = Some(filename);
185 } 178 }
186 } 179 }
187 Message::CompilerMessage(message) => {
188 progress(message.target.name.clone());
189 }
190 Message::BuildFinished(_) => {}
191 Message::TextLine(_) => {}
192 _ => {}
193 } 180 }
181 Message::CompilerMessage(message) => {
182 progress(message.target.name.clone());
183 }
184 Message::BuildFinished(_) => {}
185 Message::TextLine(_) => {}
186 _ => {}
194 } 187 }
195 } 188 }
196 189
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index aa48c455c..7a5bcb8c7 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -33,7 +33,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
33 hover_provider: Some(HoverProviderCapability::Simple(true)), 33 hover_provider: Some(HoverProviderCapability::Simple(true)),
34 completion_provider: Some(CompletionOptions { 34 completion_provider: Some(CompletionOptions {
35 resolve_provider: completions_resolve_provider(client_caps), 35 resolve_provider: completions_resolve_provider(client_caps),
36 trigger_characters: Some(vec![":".to_string(), ".".to_string()]), 36 trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]),
37 all_commit_characters: None, 37 all_commit_characters: None,
38 completion_item: None, 38 completion_item: None,
39 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, 39 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 984790d35..c63a0eaea 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -242,11 +242,8 @@ impl GlobalState {
242 } 242 }
243 BuildDataProgress::End(collector) => { 243 BuildDataProgress::End(collector) => {
244 self.fetch_build_data_completed(); 244 self.fetch_build_data_completed();
245 let workspaces = (*self.workspaces) 245 let workspaces =
246 .clone() 246 (*self.workspaces).clone().into_iter().map(Ok).collect();
247 .into_iter()
248 .map(|it| Ok(it))
249 .collect();
250 self.switch_workspaces(workspaces, Some(collector)); 247 self.switch_workspaces(workspaces, Some(collector));
251 (Some(Progress::End), None) 248 (Some(Progress::End), None)
252 } 249 }
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index aa8504c3d..76fdbcddd 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -237,7 +237,7 @@ impl GlobalState {
237 None => None, 237 None => None,
238 }; 238 };
239 239
240 if &*self.workspaces == &workspaces && self.workspace_build_data == workspace_build_data { 240 if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data {
241 return; 241 return;
242 } 242 }
243 243
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index cd0c91481..95bf26f01 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -54,7 +54,7 @@ impl<'a> Project<'a> {
54 } 54 }
55 55
56 pub(crate) fn server(self) -> Server { 56 pub(crate) fn server(self) -> Server {
57 let tmp_dir = self.tmp_dir.unwrap_or_else(|| TestDir::new()); 57 let tmp_dir = self.tmp_dir.unwrap_or_else(TestDir::new);
58 static INIT: Once = Once::new(); 58 static INIT: Once = Once::new();
59 INIT.call_once(|| { 59 INIT.call_once(|| {
60 env_logger::builder().is_test(true).parse_env("RA_LOG").try_init().unwrap(); 60 env_logger::builder().is_test(true).parse_env("RA_LOG").try_init().unwrap();
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 82ebf9037..a153a9e1c 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -567,7 +567,7 @@ impl<'a> SyntaxRewriter<'a> {
567 567
568fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { 568fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
569 match element { 569 match element {
570 NodeOrToken::Node(it) => NodeOrToken::Node(it.green().to_owned()), 570 NodeOrToken::Node(it) => NodeOrToken::Node(it.green()),
571 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().to_owned()), 571 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().to_owned()),
572 } 572 }
573} 573}
@@ -625,7 +625,7 @@ fn position_of_child(parent: &SyntaxNode, child: SyntaxElement) -> usize {
625 625
626fn to_green_element(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { 626fn to_green_element(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
627 match element { 627 match element {
628 NodeOrToken::Node(it) => it.green().to_owned().into(), 628 NodeOrToken::Node(it) => it.green().into(),
629 NodeOrToken::Token(it) => it.green().to_owned().into(), 629 NodeOrToken::Token(it) => it.green().to_owned().into(),
630 } 630 }
631} 631}
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 64fac13a7..347862b8a 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -333,8 +333,7 @@ impl ast::Use {
333 .and_then(ast::Whitespace::cast); 333 .and_then(ast::Whitespace::cast);
334 if let Some(next_ws) = next_ws { 334 if let Some(next_ws) = next_ws {
335 let ws_text = next_ws.syntax().text(); 335 let ws_text = next_ws.syntax().text();
336 if ws_text.starts_with('\n') { 336 if let Some(rest) = ws_text.strip_prefix('\n') {
337 let rest = &ws_text[1..];
338 if rest.is_empty() { 337 if rest.is_empty() {
339 res.delete(next_ws.syntax()) 338 res.delete(next_ws.syntax())
340 } else { 339 } else {
@@ -462,8 +461,7 @@ impl ast::MatchArmList {
462 let end = if let Some(comma) = start 461 let end = if let Some(comma) = start
463 .siblings_with_tokens(Direction::Next) 462 .siblings_with_tokens(Direction::Next)
464 .skip(1) 463 .skip(1)
465 .skip_while(|it| it.kind().is_trivia()) 464 .find(|it| !it.kind().is_trivia())
466 .next()
467 .filter(|it| it.kind() == T![,]) 465 .filter(|it| it.kind() == T![,])
468 { 466 {
469 comma 467 comma
@@ -597,7 +595,7 @@ impl IndentLevel {
597 pub fn from_node(node: &SyntaxNode) -> IndentLevel { 595 pub fn from_node(node: &SyntaxNode) -> IndentLevel {
598 match node.first_token() { 596 match node.first_token() {
599 Some(it) => Self::from_token(&it), 597 Some(it) => Self::from_token(&it),
600 None => return IndentLevel(0), 598 None => IndentLevel(0),
601 } 599 }
602 } 600 }
603 601
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index 636ce166d..6317d84ba 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -11,16 +11,16 @@ impl ast::AttrsOwner for ast::Expr {}
11 11
12impl ast::Expr { 12impl ast::Expr {
13 pub fn is_block_like(&self) -> bool { 13 pub fn is_block_like(&self) -> bool {
14 match self { 14 matches!(
15 self,
15 ast::Expr::IfExpr(_) 16 ast::Expr::IfExpr(_)
16 | ast::Expr::LoopExpr(_) 17 | ast::Expr::LoopExpr(_)
17 | ast::Expr::ForExpr(_) 18 | ast::Expr::ForExpr(_)
18 | ast::Expr::WhileExpr(_) 19 | ast::Expr::WhileExpr(_)
19 | ast::Expr::BlockExpr(_) 20 | ast::Expr::BlockExpr(_)
20 | ast::Expr::MatchExpr(_) 21 | ast::Expr::MatchExpr(_)
21 | ast::Expr::EffectExpr(_) => true, 22 | ast::Expr::EffectExpr(_)
22 _ => false, 23 )
23 }
24 } 24 }
25 25
26 pub fn name_ref(&self) -> Option<ast::NameRef> { 26 pub fn name_ref(&self) -> Option<ast::NameRef> {
@@ -151,20 +151,20 @@ pub enum BinOp {
151 151
152impl BinOp { 152impl BinOp {
153 pub fn is_assignment(self) -> bool { 153 pub fn is_assignment(self) -> bool {
154 match self { 154 matches!(
155 self,
155 BinOp::Assignment 156 BinOp::Assignment
156 | BinOp::AddAssign 157 | BinOp::AddAssign
157 | BinOp::DivAssign 158 | BinOp::DivAssign
158 | BinOp::MulAssign 159 | BinOp::MulAssign
159 | BinOp::RemAssign 160 | BinOp::RemAssign
160 | BinOp::ShrAssign 161 | BinOp::ShrAssign
161 | BinOp::ShlAssign 162 | BinOp::ShlAssign
162 | BinOp::SubAssign 163 | BinOp::SubAssign
163 | BinOp::BitOrAssign 164 | BinOp::BitOrAssign
164 | BinOp::BitAndAssign 165 | BinOp::BitAndAssign
165 | BinOp::BitXorAssign => true, 166 | BinOp::BitXorAssign
166 _ => false, 167 )
167 }
168 } 168 }
169} 169}
170 170
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 810c8d4c8..7049affd9 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -532,7 +532,7 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
532} 532}
533 533
534fn unroot(n: SyntaxNode) -> SyntaxNode { 534fn unroot(n: SyntaxNode) -> SyntaxNode {
535 SyntaxNode::new_root(n.green().to_owned()) 535 SyntaxNode::new_root(n.green())
536} 536}
537 537
538pub mod tokens { 538pub mod tokens {
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 42a7b9c2a..bdf907a21 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -58,10 +58,7 @@ impl From<ast::MacroDef> for Macro {
58 58
59impl AstNode for Macro { 59impl AstNode for Macro {
60 fn can_cast(kind: SyntaxKind) -> bool { 60 fn can_cast(kind: SyntaxKind) -> bool {
61 match kind { 61 matches!(kind, SyntaxKind::MACRO_RULES | SyntaxKind::MACRO_DEF)
62 SyntaxKind::MACRO_RULES | SyntaxKind::MACRO_DEF => true,
63 _ => false,
64 }
65 } 62 }
66 fn cast(syntax: SyntaxNode) -> Option<Self> { 63 fn cast(syntax: SyntaxNode) -> Option<Self> {
67 let res = match syntax.kind() { 64 let res = match syntax.kind() {
@@ -462,10 +459,8 @@ impl ast::FieldExpr {
462 pub fn field_access(&self) -> Option<FieldKind> { 459 pub fn field_access(&self) -> Option<FieldKind> {
463 if let Some(nr) = self.name_ref() { 460 if let Some(nr) = self.name_ref() {
464 Some(FieldKind::Name(nr)) 461 Some(FieldKind::Name(nr))
465 } else if let Some(tok) = self.index_token() {
466 Some(FieldKind::Index(tok))
467 } else { 462 } else {
468 None 463 self.index_token().map(FieldKind::Index)
469 } 464 }
470 } 465 }
471} 466}
@@ -482,16 +477,10 @@ impl ast::SlicePat {
482 let prefix = args 477 let prefix = args
483 .peeking_take_while(|p| match p { 478 .peeking_take_while(|p| match p {
484 ast::Pat::RestPat(_) => false, 479 ast::Pat::RestPat(_) => false,
485 ast::Pat::IdentPat(bp) => match bp.pat() { 480 ast::Pat::IdentPat(bp) => !matches!(bp.pat(), Some(ast::Pat::RestPat(_))),
486 Some(ast::Pat::RestPat(_)) => false,
487 _ => true,
488 },
489 ast::Pat::RefPat(rp) => match rp.pat() { 481 ast::Pat::RefPat(rp) => match rp.pat() {
490 Some(ast::Pat::RestPat(_)) => false, 482 Some(ast::Pat::RestPat(_)) => false,
491 Some(ast::Pat::IdentPat(bp)) => match bp.pat() { 483 Some(ast::Pat::IdentPat(bp)) => !matches!(bp.pat(), Some(ast::Pat::RestPat(_))),
492 Some(ast::Pat::RestPat(_)) => false,
493 _ => true,
494 },
495 _ => true, 484 _ => true,
496 }, 485 },
497 _ => true, 486 _ => true,
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 6c242d126..090282d28 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -494,9 +494,8 @@ pub trait HasFormatSpecifier: AstToken {
494 } 494 }
495 _ => { 495 _ => {
496 while let Some((_, Ok(next_char))) = chars.peek() { 496 while let Some((_, Ok(next_char))) = chars.peek() {
497 match next_char { 497 if next_char == &'{' {
498 '{' => break, 498 break;
499 _ => {}
500 } 499 }
501 chars.next(); 500 chars.next();
502 } 501 }
diff --git a/crates/syntax/src/fuzz.rs b/crates/syntax/src/fuzz.rs
index fbb97aa27..aa84239d2 100644
--- a/crates/syntax/src/fuzz.rs
+++ b/crates/syntax/src/fuzz.rs
@@ -43,7 +43,7 @@ impl CheckReparse {
43 TextRange::at(delete_start.try_into().unwrap(), delete_len.try_into().unwrap()); 43 TextRange::at(delete_start.try_into().unwrap(), delete_len.try_into().unwrap());
44 let edited_text = 44 let edited_text =
45 format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]); 45 format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]);
46 let edit = Indel { delete, insert }; 46 let edit = Indel { insert, delete };
47 Some(CheckReparse { text, edit, edited_text }) 47 Some(CheckReparse { text, edit, edited_text })
48 } 48 }
49 49
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 3e216fb70..bbe802174 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -297,7 +297,7 @@ fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxErro
297 } 297 }
298 }; 298 };
299 } 299 }
300 return None; 300 None
301 } 301 }
302 302
303 fn all_supers(path: &ast::Path) -> bool { 303 fn all_supers(path: &ast::Path) -> bool {
@@ -314,7 +314,7 @@ fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxErro
314 return all_supers(subpath); 314 return all_supers(subpath);
315 } 315 }
316 316
317 return true; 317 true
318 } 318 }
319} 319}
320 320
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index 9d9a01e30..bed44d600 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -239,9 +239,8 @@ impl Subtree {
239 239
240 let mut res = String::new(); 240 let mut res = String::new();
241 res.push_str(delim.0); 241 res.push_str(delim.0);
242 let mut iter = self.token_trees.iter();
243 let mut last = None; 242 let mut last = None;
244 while let Some(child) = iter.next() { 243 for child in &self.token_trees {
245 let s = match child { 244 let s = match child {
246 TokenTree::Leaf(it) => { 245 TokenTree::Leaf(it) => {
247 let s = match it { 246 let s = match it {
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 158680993..c91716409 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -154,8 +154,8 @@ fn hide_hash_comments(text: &str) -> String {
154fn reveal_hash_comments(text: &str) -> String { 154fn reveal_hash_comments(text: &str) -> String {
155 text.split('\n') // want final newline 155 text.split('\n') // want final newline
156 .map(|it| { 156 .map(|it| {
157 if it.starts_with("# ") { 157 if let Some(stripped) = it.strip_prefix("# ") {
158 &it[2..] 158 stripped
159 } else if it == "#" { 159 } else if it == "#" {
160 "" 160 ""
161 } else { 161 } else {
diff --git a/xtask/src/codegen/gen_parser_tests.rs b/xtask/src/codegen/gen_parser_tests.rs
index 096590653..2fecb9b5b 100644
--- a/xtask/src/codegen/gen_parser_tests.rs
+++ b/xtask/src/codegen/gen_parser_tests.rs
@@ -60,12 +60,10 @@ fn collect_tests(s: &str) -> Vec<Test> {
60 let mut res = Vec::new(); 60 let mut res = Vec::new();
61 for comment_block in extract_comment_blocks(s) { 61 for comment_block in extract_comment_blocks(s) {
62 let first_line = &comment_block[0]; 62 let first_line = &comment_block[0];
63 let (name, ok) = if first_line.starts_with("test ") { 63 let (name, ok) = if let Some(name) = first_line.strip_prefix("test ") {
64 let name = first_line["test ".len()..].to_string(); 64 (name.to_string(), true)
65 (name, true) 65 } else if let Some(name) = first_line.strip_prefix("test_err ") {
66 } else if first_line.starts_with("test_err ") { 66 (name.to_string(), false)
67 let name = first_line["test_err ".len()..].to_string();
68 (name, false)
69 } else { 67 } else {
70 continue; 68 continue;
71 }; 69 };
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs
index 80f26e8f5..ba4b24848 100644
--- a/xtask/src/codegen/gen_syntax.rs
+++ b/xtask/src/codegen/gen_syntax.rs
@@ -707,7 +707,7 @@ fn extract_struct_trait(node: &mut AstNodeSrc, trait_name: &str, methods: &[&str
707 let mut to_remove = Vec::new(); 707 let mut to_remove = Vec::new();
708 for (i, field) in node.fields.iter().enumerate() { 708 for (i, field) in node.fields.iter().enumerate() {
709 let method_name = field.method_name().to_string(); 709 let method_name = field.method_name().to_string();
710 if methods.iter().any(|&it| it == &method_name) { 710 if methods.iter().any(|&it| it == method_name) {
711 to_remove.push(i); 711 to_remove.push(i);
712 } 712 }
713 } 713 }
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 915aae71a..960927fc0 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -37,7 +37,7 @@ fn main() -> Result<()> {
37 match flags.subcommand { 37 match flags.subcommand {
38 flags::XtaskCmd::Help(_) => { 38 flags::XtaskCmd::Help(_) => {
39 println!("{}", flags::Xtask::HELP); 39 println!("{}", flags::Xtask::HELP);
40 return Ok(()); 40 Ok(())
41 } 41 }
42 flags::XtaskCmd::Install(cmd) => cmd.run(), 42 flags::XtaskCmd::Install(cmd) => cmd.run(),
43 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), 43 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(),
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs
index 97395738b..b0b76b8aa 100644
--- a/xtask/src/metrics.rs
+++ b/xtask/src/metrics.rs
@@ -167,7 +167,7 @@ impl Host {
167 167
168 return Ok(Host { os, cpu, mem }); 168 return Ok(Host { os, cpu, mem });
169 169
170 fn read_field<'a>(path: &str, field: &str) -> Result<String> { 170 fn read_field(path: &str, field: &str) -> Result<String> {
171 let text = read_file(path)?; 171 let text = read_file(path)?;
172 172
173 let line = text 173 let line = text
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index 1352d1218..50d9efccd 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -193,7 +193,7 @@ https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/#redo-after-
193 } 193 }
194} 194}
195 195
196fn deny_clippy(path: &PathBuf, text: &String) { 196fn deny_clippy(path: &Path, text: &str) {
197 let ignore = &[ 197 let ignore = &[
198 // The documentation in string literals may contain anything for its own purposes 198 // The documentation in string literals may contain anything for its own purposes
199 "ide_completion/src/generated_lint_completions.rs", 199 "ide_completion/src/generated_lint_completions.rs",