diff options
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/hir_ty/src/chalk_ext.rs | 36 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 64 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 17 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/extract_function.rs | 102 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/pull_assignment_up.rs | 198 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 23 | ||||
-rw-r--r-- | crates/ide_db/src/search.rs | 177 | ||||
-rw-r--r-- | crates/parser/src/grammar/patterns.rs | 2 | ||||
-rw-r--r-- | crates/syntax/src/algo.rs | 122 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 12 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 3 |
14 files changed, 427 insertions, 342 deletions
diff --git a/Cargo.toml b/Cargo.toml index 1f97a1dc5..cf3013c08 100644 --- a/Cargo.toml +++ b/Cargo.toml | |||
@@ -2,6 +2,10 @@ | |||
2 | members = ["xtask/", "lib/*", "crates/*"] | 2 | members = ["xtask/", "lib/*", "crates/*"] |
3 | 3 | ||
4 | [profile.dev] | 4 | [profile.dev] |
5 | # We do want incremental builds, but they are broken at the moment :( | ||
6 | # https://github.com/rust-lang/rust/issues/85003#issuecomment-833796289 | ||
7 | incremental = false | ||
8 | |||
5 | # Disabling debug info speeds up builds a bunch, | 9 | # Disabling debug info speeds up builds a bunch, |
6 | # and we don't rely on it for debugging that much. | 10 | # and we don't rely on it for debugging that much. |
7 | debug = 0 | 11 | debug = 0 |
@@ -21,6 +25,9 @@ miniz_oxide.opt-level = 3 | |||
21 | incremental = false | 25 | incremental = false |
22 | debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger. | 26 | debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger. |
23 | 27 | ||
28 | [profile.test] | ||
29 | incremental = false | ||
30 | |||
24 | [patch.'crates-io'] | 31 | [patch.'crates-io'] |
25 | # rowan = { path = "../rowan" } | 32 | # rowan = { path = "../rowan" } |
26 | 33 | ||
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index ac23e385e..c9ef4b420 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -2071,6 +2071,10 @@ impl Type { | |||
2071 | Some(adt.into()) | 2071 | Some(adt.into()) |
2072 | } | 2072 | } |
2073 | 2073 | ||
2074 | pub fn as_builtin(&self) -> Option<BuiltinType> { | ||
2075 | self.ty.as_builtin().map(|inner| BuiltinType { inner }) | ||
2076 | } | ||
2077 | |||
2074 | pub fn as_dyn_trait(&self) -> Option<Trait> { | 2078 | pub fn as_dyn_trait(&self) -> Option<Trait> { |
2075 | self.ty.dyn_trait().map(Into::into) | 2079 | self.ty.dyn_trait().map(Into::into) |
2076 | } | 2080 | } |
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs index 8c4542956..5232a7d80 100644 --- a/crates/hir_ty/src/chalk_ext.rs +++ b/crates/hir_ty/src/chalk_ext.rs | |||
@@ -1,8 +1,10 @@ | |||
1 | //! Various extensions traits for Chalk types. | 1 | //! Various extensions traits for Chalk types. |
2 | 2 | ||
3 | use chalk_ir::Mutability; | 3 | use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, UintTy}; |
4 | use hir_def::{ | 4 | use hir_def::{ |
5 | type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId, | 5 | builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint}, |
6 | type_ref::Rawness, | ||
7 | AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId, | ||
6 | }; | 8 | }; |
7 | 9 | ||
8 | use crate::{ | 10 | use crate::{ |
@@ -18,6 +20,7 @@ pub trait TyExt { | |||
18 | fn is_unknown(&self) -> bool; | 20 | fn is_unknown(&self) -> bool; |
19 | 21 | ||
20 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; | 22 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; |
23 | fn as_builtin(&self) -> Option<BuiltinType>; | ||
21 | fn as_tuple(&self) -> Option<&Substitution>; | 24 | fn as_tuple(&self) -> Option<&Substitution>; |
22 | fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>; | 25 | fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>; |
23 | fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; | 26 | fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; |
@@ -59,6 +62,35 @@ impl TyExt for Ty { | |||
59 | } | 62 | } |
60 | } | 63 | } |
61 | 64 | ||
65 | fn as_builtin(&self) -> Option<BuiltinType> { | ||
66 | match self.kind(&Interner) { | ||
67 | TyKind::Str => Some(BuiltinType::Str), | ||
68 | TyKind::Scalar(Scalar::Bool) => Some(BuiltinType::Bool), | ||
69 | TyKind::Scalar(Scalar::Char) => Some(BuiltinType::Char), | ||
70 | TyKind::Scalar(Scalar::Float(fty)) => Some(BuiltinType::Float(match fty { | ||
71 | FloatTy::F64 => BuiltinFloat::F64, | ||
72 | FloatTy::F32 => BuiltinFloat::F32, | ||
73 | })), | ||
74 | TyKind::Scalar(Scalar::Int(ity)) => Some(BuiltinType::Int(match ity { | ||
75 | IntTy::Isize => BuiltinInt::Isize, | ||
76 | IntTy::I8 => BuiltinInt::I8, | ||
77 | IntTy::I16 => BuiltinInt::I16, | ||
78 | IntTy::I32 => BuiltinInt::I32, | ||
79 | IntTy::I64 => BuiltinInt::I64, | ||
80 | IntTy::I128 => BuiltinInt::I128, | ||
81 | })), | ||
82 | TyKind::Scalar(Scalar::Uint(ity)) => Some(BuiltinType::Uint(match ity { | ||
83 | UintTy::Usize => BuiltinUint::Usize, | ||
84 | UintTy::U8 => BuiltinUint::U8, | ||
85 | UintTy::U16 => BuiltinUint::U16, | ||
86 | UintTy::U32 => BuiltinUint::U32, | ||
87 | UintTy::U64 => BuiltinUint::U64, | ||
88 | UintTy::U128 => BuiltinUint::U128, | ||
89 | })), | ||
90 | _ => None, | ||
91 | } | ||
92 | } | ||
93 | |||
62 | fn as_tuple(&self) -> Option<&Substitution> { | 94 | fn as_tuple(&self) -> Option<&Substitution> { |
63 | match self.kind(&Interner) { | 95 | match self.kind(&Interner) { |
64 | TyKind::Tuple(_, substs) => Some(substs), | 96 | TyKind::Tuple(_, substs) => Some(substs), |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 11ca7ec6b..ae492a264 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -65,7 +65,7 @@ pub(crate) fn find_all_refs( | |||
65 | (find_def(&sema, &syntax, position)?, false) | 65 | (find_def(&sema, &syntax, position)?, false) |
66 | }; | 66 | }; |
67 | 67 | ||
68 | let mut usages = def.usages(sema).set_scope(search_scope).all(); | 68 | let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all(); |
69 | if is_literal_search { | 69 | if is_literal_search { |
70 | // filter for constructor-literals | 70 | // filter for constructor-literals |
71 | let refs = usages.references.values_mut(); | 71 | let refs = usages.references.values_mut(); |
@@ -1163,22 +1163,76 @@ fn foo<const FOO$0: usize>() -> usize { | |||
1163 | } | 1163 | } |
1164 | 1164 | ||
1165 | #[test] | 1165 | #[test] |
1166 | fn test_find_self_ty_in_trait_def() { | 1166 | fn test_trait() { |
1167 | check( | 1167 | check( |
1168 | r#" | 1168 | r#" |
1169 | trait Foo { | 1169 | trait Foo$0 where Self: {} |
1170 | fn f() -> Self$0; | 1170 | |
1171 | impl Foo for () {} | ||
1172 | "#, | ||
1173 | expect![[r#" | ||
1174 | Foo Trait FileId(0) 0..24 6..9 | ||
1175 | |||
1176 | FileId(0) 31..34 | ||
1177 | "#]], | ||
1178 | ); | ||
1179 | } | ||
1180 | |||
1181 | #[test] | ||
1182 | fn test_trait_self() { | ||
1183 | check( | ||
1184 | r#" | ||
1185 | trait Foo where Self$0 { | ||
1186 | fn f() -> Self; | ||
1171 | } | 1187 | } |
1188 | |||
1189 | impl Foo for () {} | ||
1172 | "#, | 1190 | "#, |
1173 | expect![[r#" | 1191 | expect![[r#" |
1174 | Self TypeParam FileId(0) 6..9 6..9 | 1192 | Self TypeParam FileId(0) 6..9 6..9 |
1175 | 1193 | ||
1176 | FileId(0) 26..30 | 1194 | FileId(0) 16..20 |
1195 | FileId(0) 37..41 | ||
1177 | "#]], | 1196 | "#]], |
1178 | ); | 1197 | ); |
1179 | } | 1198 | } |
1180 | 1199 | ||
1181 | #[test] | 1200 | #[test] |
1201 | fn test_self_ty() { | ||
1202 | check( | ||
1203 | r#" | ||
1204 | struct $0Foo; | ||
1205 | |||
1206 | impl Foo where Self: { | ||
1207 | fn f() -> Self; | ||
1208 | } | ||
1209 | "#, | ||
1210 | expect![[r#" | ||
1211 | Foo Struct FileId(0) 0..11 7..10 | ||
1212 | |||
1213 | FileId(0) 18..21 | ||
1214 | FileId(0) 28..32 | ||
1215 | FileId(0) 50..54 | ||
1216 | "#]], | ||
1217 | ); | ||
1218 | check( | ||
1219 | r#" | ||
1220 | struct Foo; | ||
1221 | |||
1222 | impl Foo where Self: { | ||
1223 | fn f() -> Self$0; | ||
1224 | } | ||
1225 | "#, | ||
1226 | expect![[r#" | ||
1227 | impl Impl FileId(0) 13..57 18..21 | ||
1228 | |||
1229 | FileId(0) 18..21 | ||
1230 | FileId(0) 28..32 | ||
1231 | FileId(0) 50..54 | ||
1232 | "#]], | ||
1233 | ); | ||
1234 | } | ||
1235 | #[test] | ||
1182 | fn test_self_variant_with_payload() { | 1236 | fn test_self_variant_with_payload() { |
1183 | check( | 1237 | check( |
1184 | r#" | 1238 | r#" |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 175e7a31d..2bf953305 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -1888,4 +1888,21 @@ impl Foo { | |||
1888 | "error: Cannot rename `Self`", | 1888 | "error: Cannot rename `Self`", |
1889 | ); | 1889 | ); |
1890 | } | 1890 | } |
1891 | |||
1892 | #[test] | ||
1893 | fn test_rename_ignores_self_ty() { | ||
1894 | check( | ||
1895 | "Fo0", | ||
1896 | r#" | ||
1897 | struct $0Foo; | ||
1898 | |||
1899 | impl Foo where Self: {} | ||
1900 | "#, | ||
1901 | r#" | ||
1902 | struct Fo0; | ||
1903 | |||
1904 | impl Fo0 where Self: {} | ||
1905 | "#, | ||
1906 | ); | ||
1907 | } | ||
1891 | } | 1908 | } |
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs index b5b5ada5e..70949ca35 100644 --- a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs | |||
@@ -107,7 +107,7 @@ fn edit_struct_references( | |||
107 | names: &[ast::Name], | 107 | names: &[ast::Name], |
108 | ) { | 108 | ) { |
109 | let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); | 109 | let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); |
110 | let usages = strukt_def.usages(&ctx.sema).include_self_kw_refs(true).all(); | 110 | let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); |
111 | 111 | ||
112 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { | 112 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { |
113 | match_ast! { | 113 | match_ast! { |
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index 93b28370c..4116985ae 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -10,7 +10,6 @@ use ide_db::{ | |||
10 | use itertools::Itertools; | 10 | use itertools::Itertools; |
11 | use stdx::format_to; | 11 | use stdx::format_to; |
12 | use syntax::{ | 12 | use syntax::{ |
13 | algo::SyntaxRewriter, | ||
14 | ast::{ | 13 | ast::{ |
15 | self, | 14 | self, |
16 | edit::{AstNodeEdit, IndentLevel}, | 15 | edit::{AstNodeEdit, IndentLevel}, |
@@ -1362,7 +1361,8 @@ fn rewrite_body_segment( | |||
1362 | syntax: &SyntaxNode, | 1361 | syntax: &SyntaxNode, |
1363 | ) -> SyntaxNode { | 1362 | ) -> SyntaxNode { |
1364 | let syntax = fix_param_usages(ctx, params, syntax); | 1363 | let syntax = fix_param_usages(ctx, params, syntax); |
1365 | update_external_control_flow(handler, &syntax) | 1364 | update_external_control_flow(handler, &syntax); |
1365 | syntax | ||
1366 | } | 1366 | } |
1367 | 1367 | ||
1368 | /// change all usages to account for added `&`/`&mut` for some params | 1368 | /// change all usages to account for added `&`/`&mut` for some params |
@@ -1415,75 +1415,65 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) | |||
1415 | res | 1415 | res |
1416 | } | 1416 | } |
1417 | 1417 | ||
1418 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { | 1418 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) { |
1419 | let mut rewriter = SyntaxRewriter::default(); | ||
1420 | |||
1421 | let mut nested_loop = None; | 1419 | let mut nested_loop = None; |
1422 | let mut nested_scope = None; | 1420 | let mut nested_scope = None; |
1423 | for event in syntax.preorder() { | 1421 | for event in syntax.preorder() { |
1424 | let node = match event { | 1422 | match event { |
1425 | WalkEvent::Enter(e) => { | 1423 | WalkEvent::Enter(e) => match e.kind() { |
1426 | match e.kind() { | 1424 | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { |
1427 | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { | 1425 | if nested_loop.is_none() { |
1428 | if nested_loop.is_none() { | 1426 | nested_loop = Some(e.clone()); |
1429 | nested_loop = Some(e.clone()); | ||
1430 | } | ||
1431 | } | 1427 | } |
1432 | SyntaxKind::FN | 1428 | } |
1433 | | SyntaxKind::CONST | 1429 | SyntaxKind::FN |
1434 | | SyntaxKind::STATIC | 1430 | | SyntaxKind::CONST |
1435 | | SyntaxKind::IMPL | 1431 | | SyntaxKind::STATIC |
1436 | | SyntaxKind::MODULE => { | 1432 | | SyntaxKind::IMPL |
1437 | if nested_scope.is_none() { | 1433 | | SyntaxKind::MODULE => { |
1438 | nested_scope = Some(e.clone()); | 1434 | if nested_scope.is_none() { |
1439 | } | 1435 | nested_scope = Some(e.clone()); |
1440 | } | 1436 | } |
1441 | _ => {} | ||
1442 | } | 1437 | } |
1443 | e | 1438 | _ => {} |
1444 | } | 1439 | }, |
1445 | WalkEvent::Leave(e) => { | 1440 | WalkEvent::Leave(e) => { |
1441 | if nested_scope.is_none() { | ||
1442 | if let Some(expr) = ast::Expr::cast(e.clone()) { | ||
1443 | match expr { | ||
1444 | ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => { | ||
1445 | let expr = return_expr.expr(); | ||
1446 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1447 | ted::replace(return_expr.syntax(), replacement.syntax()) | ||
1448 | } | ||
1449 | } | ||
1450 | ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => { | ||
1451 | let expr = break_expr.expr(); | ||
1452 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1453 | ted::replace(break_expr.syntax(), replacement.syntax()) | ||
1454 | } | ||
1455 | } | ||
1456 | ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => { | ||
1457 | if let Some(replacement) = make_rewritten_flow(handler, None) { | ||
1458 | ted::replace(continue_expr.syntax(), replacement.syntax()) | ||
1459 | } | ||
1460 | } | ||
1461 | _ => { | ||
1462 | // do nothing | ||
1463 | } | ||
1464 | } | ||
1465 | } | ||
1466 | } | ||
1467 | |||
1446 | if nested_loop.as_ref() == Some(&e) { | 1468 | if nested_loop.as_ref() == Some(&e) { |
1447 | nested_loop = None; | 1469 | nested_loop = None; |
1448 | } | 1470 | } |
1449 | if nested_scope.as_ref() == Some(&e) { | 1471 | if nested_scope.as_ref() == Some(&e) { |
1450 | nested_scope = None; | 1472 | nested_scope = None; |
1451 | } | 1473 | } |
1452 | continue; | ||
1453 | } | 1474 | } |
1454 | }; | 1475 | }; |
1455 | if nested_scope.is_some() { | ||
1456 | continue; | ||
1457 | } | ||
1458 | let expr = match ast::Expr::cast(node) { | ||
1459 | Some(e) => e, | ||
1460 | None => continue, | ||
1461 | }; | ||
1462 | match expr { | ||
1463 | ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => { | ||
1464 | let expr = return_expr.expr(); | ||
1465 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1466 | rewriter.replace_ast(&return_expr.into(), &replacement); | ||
1467 | } | ||
1468 | } | ||
1469 | ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => { | ||
1470 | let expr = break_expr.expr(); | ||
1471 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1472 | rewriter.replace_ast(&break_expr.into(), &replacement); | ||
1473 | } | ||
1474 | } | ||
1475 | ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => { | ||
1476 | if let Some(replacement) = make_rewritten_flow(handler, None) { | ||
1477 | rewriter.replace_ast(&continue_expr.into(), &replacement); | ||
1478 | } | ||
1479 | } | ||
1480 | _ => { | ||
1481 | // do nothing | ||
1482 | } | ||
1483 | } | ||
1484 | } | 1476 | } |
1485 | |||
1486 | rewriter.rewrite(syntax) | ||
1487 | } | 1477 | } |
1488 | 1478 | ||
1489 | fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { | 1479 | fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { |
@@ -1502,7 +1492,7 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op | |||
1502 | make::expr_call(make::expr_path(make_path_from_text("Err")), args) | 1492 | make::expr_call(make::expr_path(make_path_from_text("Err")), args) |
1503 | } | 1493 | } |
1504 | }; | 1494 | }; |
1505 | Some(make::expr_return(Some(value))) | 1495 | Some(make::expr_return(Some(value)).clone_for_update()) |
1506 | } | 1496 | } |
1507 | 1497 | ||
1508 | #[cfg(test)] | 1498 | #[cfg(test)] |
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs index 04bae4e58..28d14b9c3 100644 --- a/crates/ide_assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{self, edit::AstNodeEdit, make}, | 2 | ast::{self, make}, |
3 | AstNode, | 3 | ted, AstNode, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
@@ -37,103 +37,101 @@ use crate::{ | |||
37 | // ``` | 37 | // ``` |
38 | pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 38 | pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
39 | let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?; | 39 | let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?; |
40 | let name_expr = if assign_expr.op_kind()? == ast::BinOp::Assignment { | 40 | |
41 | assign_expr.lhs()? | 41 | let op_kind = assign_expr.op_kind()?; |
42 | } else { | 42 | if op_kind != ast::BinOp::Assignment { |
43 | cov_mark::hit!(test_cant_pull_non_assignments); | ||
43 | return None; | 44 | return None; |
45 | } | ||
46 | |||
47 | let mut collector = AssignmentsCollector { | ||
48 | sema: &ctx.sema, | ||
49 | common_lhs: assign_expr.lhs()?, | ||
50 | assignments: Vec::new(), | ||
44 | }; | 51 | }; |
45 | 52 | ||
46 | let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { | 53 | let tgt: ast::Expr = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { |
47 | ( | 54 | collector.collect_if(&if_expr)?; |
48 | ast::Expr::cast(if_expr.syntax().to_owned())?, | 55 | if_expr.into() |
49 | exprify_if(&if_expr, &ctx.sema, &name_expr)?.indent(if_expr.indent_level()), | ||
50 | ) | ||
51 | } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { | 56 | } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { |
52 | ( | 57 | collector.collect_match(&match_expr)?; |
53 | ast::Expr::cast(match_expr.syntax().to_owned())?, | 58 | match_expr.into() |
54 | exprify_match(&match_expr, &ctx.sema, &name_expr)?, | ||
55 | ) | ||
56 | } else { | 59 | } else { |
57 | return None; | 60 | return None; |
58 | }; | 61 | }; |
59 | 62 | ||
60 | let expr_stmt = make::expr_stmt(new_stmt); | ||
61 | |||
62 | acc.add( | 63 | acc.add( |
63 | AssistId("pull_assignment_up", AssistKind::RefactorExtract), | 64 | AssistId("pull_assignment_up", AssistKind::RefactorExtract), |
64 | "Pull assignment up", | 65 | "Pull assignment up", |
65 | old_stmt.syntax().text_range(), | 66 | tgt.syntax().text_range(), |
66 | move |edit| { | 67 | move |edit| { |
67 | edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name_expr, expr_stmt)); | 68 | let assignments: Vec<_> = collector |
69 | .assignments | ||
70 | .into_iter() | ||
71 | .map(|(stmt, rhs)| (edit.make_ast_mut(stmt), rhs.clone_for_update())) | ||
72 | .collect(); | ||
73 | |||
74 | let tgt = edit.make_ast_mut(tgt); | ||
75 | |||
76 | for (stmt, rhs) in assignments { | ||
77 | ted::replace(stmt.syntax(), rhs.syntax()); | ||
78 | } | ||
79 | let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone()); | ||
80 | let assign_stmt = make::expr_stmt(assign_expr); | ||
81 | |||
82 | ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update()); | ||
68 | }, | 83 | }, |
69 | ) | 84 | ) |
70 | } | 85 | } |
71 | 86 | ||
72 | fn exprify_match( | 87 | struct AssignmentsCollector<'a> { |
73 | match_expr: &ast::MatchExpr, | 88 | sema: &'a hir::Semantics<'a, ide_db::RootDatabase>, |
74 | sema: &hir::Semantics<ide_db::RootDatabase>, | 89 | common_lhs: ast::Expr, |
75 | name: &ast::Expr, | 90 | assignments: Vec<(ast::ExprStmt, ast::Expr)>, |
76 | ) -> Option<ast::Expr> { | ||
77 | let new_arm_list = match_expr | ||
78 | .match_arm_list()? | ||
79 | .arms() | ||
80 | .map(|arm| { | ||
81 | if let ast::Expr::BlockExpr(block) = arm.expr()? { | ||
82 | let new_block = exprify_block(&block, sema, name)?.indent(block.indent_level()); | ||
83 | Some(arm.replace_descendant(block, new_block)) | ||
84 | } else { | ||
85 | None | ||
86 | } | ||
87 | }) | ||
88 | .collect::<Option<Vec<_>>>()?; | ||
89 | let new_arm_list = match_expr | ||
90 | .match_arm_list()? | ||
91 | .replace_descendants(match_expr.match_arm_list()?.arms().zip(new_arm_list)); | ||
92 | Some(make::expr_match(match_expr.expr()?, new_arm_list)) | ||
93 | } | 91 | } |
94 | 92 | ||
95 | fn exprify_if( | 93 | impl<'a> AssignmentsCollector<'a> { |
96 | statement: &ast::IfExpr, | 94 | fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> { |
97 | sema: &hir::Semantics<ide_db::RootDatabase>, | 95 | for arm in match_expr.match_arm_list()?.arms() { |
98 | name: &ast::Expr, | 96 | match arm.expr()? { |
99 | ) -> Option<ast::Expr> { | 97 | ast::Expr::BlockExpr(block) => self.collect_block(&block)?, |
100 | let then_branch = exprify_block(&statement.then_branch()?, sema, name)?; | 98 | _ => return None, |
101 | let else_branch = match statement.else_branch()? { | 99 | } |
102 | ast::ElseBranch::Block(ref block) => { | ||
103 | ast::ElseBranch::Block(exprify_block(block, sema, name)?) | ||
104 | } | ||
105 | ast::ElseBranch::IfExpr(expr) => { | ||
106 | cov_mark::hit!(test_pull_assignment_up_chained_if); | ||
107 | ast::ElseBranch::IfExpr(ast::IfExpr::cast( | ||
108 | exprify_if(&expr, sema, name)?.syntax().to_owned(), | ||
109 | )?) | ||
110 | } | 100 | } |
111 | }; | ||
112 | Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch))) | ||
113 | } | ||
114 | 101 | ||
115 | fn exprify_block( | 102 | Some(()) |
116 | block: &ast::BlockExpr, | ||
117 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
118 | name: &ast::Expr, | ||
119 | ) -> Option<ast::BlockExpr> { | ||
120 | if block.tail_expr().is_some() { | ||
121 | return None; | ||
122 | } | 103 | } |
104 | fn collect_if(&mut self, if_expr: &ast::IfExpr) -> Option<()> { | ||
105 | let then_branch = if_expr.then_branch()?; | ||
106 | self.collect_block(&then_branch)?; | ||
107 | |||
108 | match if_expr.else_branch()? { | ||
109 | ast::ElseBranch::Block(block) => self.collect_block(&block), | ||
110 | ast::ElseBranch::IfExpr(expr) => { | ||
111 | cov_mark::hit!(test_pull_assignment_up_chained_if); | ||
112 | self.collect_if(&expr) | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> { | ||
117 | if block.tail_expr().is_some() { | ||
118 | return None; | ||
119 | } | ||
123 | 120 | ||
124 | let mut stmts: Vec<_> = block.statements().collect(); | 121 | let last_stmt = block.statements().last()?; |
125 | let stmt = stmts.pop()?; | 122 | if let ast::Stmt::ExprStmt(stmt) = last_stmt { |
126 | 123 | if let ast::Expr::BinExpr(expr) = stmt.expr()? { | |
127 | if let ast::Stmt::ExprStmt(stmt) = stmt { | 124 | if expr.op_kind()? == ast::BinOp::Assignment |
128 | if let ast::Expr::BinExpr(expr) = stmt.expr()? { | 125 | && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs) |
129 | if expr.op_kind()? == ast::BinOp::Assignment && is_equivalent(sema, &expr.lhs()?, name) | 126 | { |
130 | { | 127 | self.assignments.push((stmt, expr.rhs()?)); |
131 | // The last statement in the block is an assignment to the name we want | 128 | return Some(()); |
132 | return Some(make::block_expr(stmts, Some(expr.rhs()?))); | 129 | } |
133 | } | 130 | } |
134 | } | 131 | } |
132 | |||
133 | None | ||
135 | } | 134 | } |
136 | None | ||
137 | } | 135 | } |
138 | 136 | ||
139 | fn is_equivalent( | 137 | fn is_equivalent( |
@@ -243,6 +241,38 @@ fn foo() { | |||
243 | } | 241 | } |
244 | 242 | ||
245 | #[test] | 243 | #[test] |
244 | #[ignore] | ||
245 | fn test_pull_assignment_up_assignment_expressions() { | ||
246 | check_assist( | ||
247 | pull_assignment_up, | ||
248 | r#" | ||
249 | fn foo() { | ||
250 | let mut a = 1; | ||
251 | |||
252 | match 1 { | ||
253 | 1 => { $0a = 2; }, | ||
254 | 2 => a = 3, | ||
255 | 3 => { | ||
256 | a = 4 | ||
257 | } | ||
258 | } | ||
259 | }"#, | ||
260 | r#" | ||
261 | fn foo() { | ||
262 | let mut a = 1; | ||
263 | |||
264 | a = match 1 { | ||
265 | 1 => { 2 }, | ||
266 | 2 => 3, | ||
267 | 3 => { | ||
268 | 4 | ||
269 | } | ||
270 | }; | ||
271 | }"#, | ||
272 | ); | ||
273 | } | ||
274 | |||
275 | #[test] | ||
246 | fn test_pull_assignment_up_not_last_not_applicable() { | 276 | fn test_pull_assignment_up_not_last_not_applicable() { |
247 | check_assist_not_applicable( | 277 | check_assist_not_applicable( |
248 | pull_assignment_up, | 278 | pull_assignment_up, |
@@ -439,4 +469,24 @@ fn foo() { | |||
439 | "#, | 469 | "#, |
440 | ) | 470 | ) |
441 | } | 471 | } |
472 | |||
473 | #[test] | ||
474 | fn test_cant_pull_non_assignments() { | ||
475 | cov_mark::check!(test_cant_pull_non_assignments); | ||
476 | check_assist_not_applicable( | ||
477 | pull_assignment_up, | ||
478 | r#" | ||
479 | fn foo() { | ||
480 | let mut a = 1; | ||
481 | let b = &mut a; | ||
482 | |||
483 | if true { | ||
484 | $0*b + 2; | ||
485 | } else { | ||
486 | *b + 3; | ||
487 | } | ||
488 | } | ||
489 | "#, | ||
490 | ) | ||
491 | } | ||
442 | } | 492 | } |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 62ef40818..787eb2fd3 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -313,7 +313,8 @@ impl<'a> CompletionContext<'a> { | |||
313 | cov_mark::hit!(expected_type_let_with_leading_char); | 313 | cov_mark::hit!(expected_type_let_with_leading_char); |
314 | cov_mark::hit!(expected_type_let_without_leading_char); | 314 | cov_mark::hit!(expected_type_let_without_leading_char); |
315 | let ty = it.pat() | 315 | let ty = it.pat() |
316 | .and_then(|pat| self.sema.type_of_pat(&pat)); | 316 | .and_then(|pat| self.sema.type_of_pat(&pat)) |
317 | .or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it))); | ||
317 | let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { | 318 | let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { |
318 | ident.name().map(NameOrNameRef::Name) | 319 | ident.name().map(NameOrNameRef::Name) |
319 | } else { | 320 | } else { |
@@ -720,6 +721,26 @@ fn foo() { | |||
720 | } | 721 | } |
721 | 722 | ||
722 | #[test] | 723 | #[test] |
724 | fn expected_type_let_pat() { | ||
725 | check_expected_type_and_name( | ||
726 | r#" | ||
727 | fn foo() { | ||
728 | let x$0 = 0u32; | ||
729 | } | ||
730 | "#, | ||
731 | expect![[r#"ty: u32, name: ?"#]], | ||
732 | ); | ||
733 | check_expected_type_and_name( | ||
734 | r#" | ||
735 | fn foo() { | ||
736 | let $0 = 0u32; | ||
737 | } | ||
738 | "#, | ||
739 | expect![[r#"ty: u32, name: ?"#]], | ||
740 | ); | ||
741 | } | ||
742 | |||
743 | #[test] | ||
723 | fn expected_type_fn_param_without_leading_char() { | 744 | fn expected_type_fn_param_without_leading_char() { |
724 | cov_mark::check!(expected_type_fn_param_without_leading_char); | 745 | cov_mark::check!(expected_type_fn_param_without_leading_char); |
725 | check_expected_type_and_name( | 746 | check_expected_type_and_name( |
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 8f899ea56..67840602b 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -233,6 +233,13 @@ impl Definition { | |||
233 | }; | 233 | }; |
234 | } | 234 | } |
235 | 235 | ||
236 | if let Definition::SelfType(impl_) = self { | ||
237 | return match impl_.source(db).map(|src| src.value.syntax().text_range()) { | ||
238 | Some(range) => SearchScope::file_range(FileRange { file_id, range }), | ||
239 | None => SearchScope::single_file(file_id), | ||
240 | }; | ||
241 | } | ||
242 | |||
236 | if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self { | 243 | if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self { |
237 | let range = match param.parent(db) { | 244 | let range = match param.parent(db) { |
238 | hir::GenericDef::Function(it) => { | 245 | hir::GenericDef::Function(it) => { |
@@ -297,7 +304,7 @@ impl Definition { | |||
297 | } | 304 | } |
298 | 305 | ||
299 | pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { | 306 | pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { |
300 | FindUsages { def: self, sema, scope: None, include_self_kw_refs: false } | 307 | FindUsages { def: self, sema, scope: None, include_self_kw_refs: None } |
301 | } | 308 | } |
302 | } | 309 | } |
303 | 310 | ||
@@ -305,12 +312,13 @@ pub struct FindUsages<'a> { | |||
305 | def: &'a Definition, | 312 | def: &'a Definition, |
306 | sema: &'a Semantics<'a, RootDatabase>, | 313 | sema: &'a Semantics<'a, RootDatabase>, |
307 | scope: Option<SearchScope>, | 314 | scope: Option<SearchScope>, |
308 | include_self_kw_refs: bool, | 315 | include_self_kw_refs: Option<hir::Type>, |
309 | } | 316 | } |
310 | 317 | ||
311 | impl<'a> FindUsages<'a> { | 318 | impl<'a> FindUsages<'a> { |
312 | pub fn include_self_kw_refs(mut self, include: bool) -> FindUsages<'a> { | 319 | /// Enable searching for `Self` when the definition is a type. |
313 | self.include_self_kw_refs = include; | 320 | pub fn include_self_refs(mut self) -> FindUsages<'a> { |
321 | self.include_self_kw_refs = def_to_ty(self.sema, self.def); | ||
314 | self | 322 | self |
315 | } | 323 | } |
316 | 324 | ||
@@ -354,13 +362,18 @@ impl<'a> FindUsages<'a> { | |||
354 | } | 362 | } |
355 | }; | 363 | }; |
356 | 364 | ||
357 | let name = match self.def.name(sema.db) { | 365 | let name = self.def.name(sema.db).or_else(|| { |
358 | Some(it) => it.to_string(), | 366 | self.include_self_kw_refs.as_ref().and_then(|ty| { |
367 | ty.as_adt() | ||
368 | .map(|adt| adt.name(self.sema.db)) | ||
369 | .or_else(|| ty.as_builtin().map(|builtin| builtin.name())) | ||
370 | }) | ||
371 | }); | ||
372 | let name = match name { | ||
373 | Some(name) => name.to_string(), | ||
359 | None => return, | 374 | None => return, |
360 | }; | 375 | }; |
361 | 376 | let name = name.as_str(); | |
362 | let pat = name.as_str(); | ||
363 | let search_for_self = self.include_self_kw_refs; | ||
364 | 377 | ||
365 | for (file_id, search_range) in search_scope { | 378 | for (file_id, search_range) in search_scope { |
366 | let text = sema.db.file_text(file_id); | 379 | let text = sema.db.file_text(file_id); |
@@ -369,51 +382,63 @@ impl<'a> FindUsages<'a> { | |||
369 | 382 | ||
370 | let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); | 383 | let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); |
371 | 384 | ||
372 | let mut handle_match = |idx: usize| -> bool { | 385 | for (idx, _) in text.match_indices(name) { |
373 | let offset: TextSize = idx.try_into().unwrap(); | 386 | let offset: TextSize = idx.try_into().unwrap(); |
374 | if !search_range.contains_inclusive(offset) { | 387 | if !search_range.contains_inclusive(offset) { |
375 | return false; | 388 | continue; |
376 | } | 389 | } |
377 | 390 | ||
378 | if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) { | 391 | if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) { |
379 | match name { | 392 | if match name { |
380 | ast::NameLike::NameRef(name_ref) => { | 393 | ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink), |
381 | if self.found_name_ref(&name_ref, sink) { | 394 | ast::NameLike::Name(name) => self.found_name(&name, sink), |
382 | return true; | 395 | ast::NameLike::Lifetime(lifetime) => self.found_lifetime(&lifetime, sink), |
383 | } | 396 | } { |
384 | } | 397 | return; |
385 | ast::NameLike::Name(name) => { | ||
386 | if self.found_name(&name, sink) { | ||
387 | return true; | ||
388 | } | ||
389 | } | ||
390 | ast::NameLike::Lifetime(lifetime) => { | ||
391 | if self.found_lifetime(&lifetime, sink) { | ||
392 | return true; | ||
393 | } | ||
394 | } | ||
395 | } | 398 | } |
396 | } | 399 | } |
397 | |||
398 | return false; | ||
399 | }; | ||
400 | |||
401 | for (idx, _) in text.match_indices(pat) { | ||
402 | if handle_match(idx) { | ||
403 | return; | ||
404 | } | ||
405 | } | 400 | } |
406 | 401 | if let Some(self_ty) = &self.include_self_kw_refs { | |
407 | if search_for_self { | ||
408 | for (idx, _) in text.match_indices("Self") { | 402 | for (idx, _) in text.match_indices("Self") { |
409 | if handle_match(idx) { | 403 | let offset: TextSize = idx.try_into().unwrap(); |
410 | return; | 404 | if !search_range.contains_inclusive(offset) { |
405 | continue; | ||
406 | } | ||
407 | |||
408 | if let Some(ast::NameLike::NameRef(name_ref)) = | ||
409 | sema.find_node_at_offset_with_descend(&tree, offset) | ||
410 | { | ||
411 | if self.found_self_ty_name_ref(&self_ty, &name_ref, sink) { | ||
412 | return; | ||
413 | } | ||
411 | } | 414 | } |
412 | } | 415 | } |
413 | } | 416 | } |
414 | } | 417 | } |
415 | } | 418 | } |
416 | 419 | ||
420 | fn found_self_ty_name_ref( | ||
421 | &self, | ||
422 | self_ty: &hir::Type, | ||
423 | name_ref: &ast::NameRef, | ||
424 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | ||
425 | ) -> bool { | ||
426 | match NameRefClass::classify(self.sema, &name_ref) { | ||
427 | Some(NameRefClass::Definition(Definition::SelfType(impl_))) | ||
428 | if impl_.self_ty(self.sema.db) == *self_ty => | ||
429 | { | ||
430 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | ||
431 | let reference = FileReference { | ||
432 | range, | ||
433 | name: ast::NameLike::NameRef(name_ref.clone()), | ||
434 | access: None, | ||
435 | }; | ||
436 | sink(file_id, reference) | ||
437 | } | ||
438 | _ => false, | ||
439 | } | ||
440 | } | ||
441 | |||
417 | fn found_lifetime( | 442 | fn found_lifetime( |
418 | &self, | 443 | &self, |
419 | lifetime: &ast::Lifetime, | 444 | lifetime: &ast::Lifetime, |
@@ -429,7 +454,7 @@ impl<'a> FindUsages<'a> { | |||
429 | }; | 454 | }; |
430 | sink(file_id, reference) | 455 | sink(file_id, reference) |
431 | } | 456 | } |
432 | _ => false, // not a usage | 457 | _ => false, |
433 | } | 458 | } |
434 | } | 459 | } |
435 | 460 | ||
@@ -448,42 +473,35 @@ impl<'a> FindUsages<'a> { | |||
448 | }; | 473 | }; |
449 | sink(file_id, reference) | 474 | sink(file_id, reference) |
450 | } | 475 | } |
451 | Some(NameRefClass::Definition(Definition::SelfType(impl_))) => { | 476 | Some(NameRefClass::Definition(def)) if self.include_self_kw_refs.is_some() => { |
452 | let ty = impl_.self_ty(self.sema.db); | 477 | if self.include_self_kw_refs == def_to_ty(self.sema, &def) { |
453 | 478 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | |
454 | if let Some(adt) = ty.as_adt() { | 479 | let reference = FileReference { |
455 | if &Definition::ModuleDef(ModuleDef::Adt(adt)) == self.def { | 480 | range, |
456 | let FileRange { file_id, range } = | 481 | name: ast::NameLike::NameRef(name_ref.clone()), |
457 | self.sema.original_range(name_ref.syntax()); | 482 | access: reference_access(&def, &name_ref), |
458 | let reference = FileReference { | 483 | }; |
459 | range, | 484 | sink(file_id, reference) |
460 | name: ast::NameLike::NameRef(name_ref.clone()), | 485 | } else { |
461 | access: None, | 486 | false |
462 | }; | ||
463 | return sink(file_id, reference); | ||
464 | } | ||
465 | } | 487 | } |
466 | |||
467 | false | ||
468 | } | 488 | } |
469 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { | 489 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { |
470 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | 490 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
471 | let reference = match self.def { | 491 | let access = match self.def { |
472 | Definition::Field(_) if &field == self.def => FileReference { | 492 | Definition::Field(_) if &field == self.def => { |
473 | range, | 493 | reference_access(&field, &name_ref) |
474 | name: ast::NameLike::NameRef(name_ref.clone()), | 494 | } |
475 | access: reference_access(&field, &name_ref), | 495 | Definition::Local(l) if &local == l => { |
476 | }, | 496 | reference_access(&Definition::Local(local), &name_ref) |
477 | Definition::Local(l) if &local == l => FileReference { | 497 | } |
478 | range, | 498 | _ => return false, |
479 | name: ast::NameLike::NameRef(name_ref.clone()), | ||
480 | access: reference_access(&Definition::Local(local), &name_ref), | ||
481 | }, | ||
482 | _ => return false, // not a usage | ||
483 | }; | 499 | }; |
500 | let reference = | ||
501 | FileReference { range, name: ast::NameLike::NameRef(name_ref.clone()), access }; | ||
484 | sink(file_id, reference) | 502 | sink(file_id, reference) |
485 | } | 503 | } |
486 | _ => false, // not a usage | 504 | _ => false, |
487 | } | 505 | } |
488 | } | 506 | } |
489 | 507 | ||
@@ -513,11 +531,30 @@ impl<'a> FindUsages<'a> { | |||
513 | FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; | 531 | FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; |
514 | sink(file_id, reference) | 532 | sink(file_id, reference) |
515 | } | 533 | } |
516 | _ => false, // not a usage | 534 | _ => false, |
517 | } | 535 | } |
518 | } | 536 | } |
519 | } | 537 | } |
520 | 538 | ||
539 | fn def_to_ty(sema: &Semantics<RootDatabase>, def: &Definition) -> Option<hir::Type> { | ||
540 | match def { | ||
541 | Definition::ModuleDef(def) => match def { | ||
542 | ModuleDef::Adt(adt) => Some(adt.ty(sema.db)), | ||
543 | ModuleDef::TypeAlias(it) => Some(it.ty(sema.db)), | ||
544 | ModuleDef::BuiltinType(it) => { | ||
545 | let graph = sema.db.crate_graph(); | ||
546 | let krate = graph.iter().next()?; | ||
547 | let root_file = graph[krate].root_file_id; | ||
548 | let module = sema.to_module_def(root_file)?; | ||
549 | Some(it.ty(sema.db, module)) | ||
550 | } | ||
551 | _ => None, | ||
552 | }, | ||
553 | Definition::SelfType(it) => Some(it.self_ty(sema.db)), | ||
554 | _ => None, | ||
555 | } | ||
556 | } | ||
557 | |||
521 | fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { | 558 | fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { |
522 | // Only Locals and Fields have accesses for now. | 559 | // Only Locals and Fields have accesses for now. |
523 | if !matches!(def, Definition::Local(_) | Definition::Field(_)) { | 560 | if !matches!(def, Definition::Local(_) | Definition::Field(_)) { |
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index 3ab347834..f1d1f9eaa 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs | |||
@@ -83,7 +83,7 @@ fn pattern_single_r(p: &mut Parser, recovery_set: TokenSet) { | |||
83 | } | 83 | } |
84 | 84 | ||
85 | const PAT_RECOVERY_SET: TokenSet = | 85 | const PAT_RECOVERY_SET: TokenSet = |
86 | TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,]]); | 86 | TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,], T![=]]); |
87 | 87 | ||
88 | fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { | 88 | fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { |
89 | let m = match p.nth(0) { | 89 | let m = match p.nth(0) { |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index ba263be0d..3f9b84ab9 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -1,10 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::{ | 3 | use std::{fmt, hash::BuildHasherDefault, ops::RangeInclusive}; |
4 | fmt, | ||
5 | hash::BuildHasherDefault, | ||
6 | ops::{self, RangeInclusive}, | ||
7 | }; | ||
8 | 4 | ||
9 | use indexmap::IndexMap; | 5 | use indexmap::IndexMap; |
10 | use itertools::Itertools; | 6 | use itertools::Itertools; |
@@ -358,107 +354,11 @@ impl fmt::Debug for SyntaxRewriter<'_> { | |||
358 | } | 354 | } |
359 | 355 | ||
360 | impl SyntaxRewriter<'_> { | 356 | impl SyntaxRewriter<'_> { |
361 | pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) { | ||
362 | let what = what.clone().into(); | ||
363 | let replacement = Replacement::Delete; | ||
364 | self.replacements.insert(what, replacement); | ||
365 | } | ||
366 | pub fn insert_before<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>( | ||
367 | &mut self, | ||
368 | before: &T, | ||
369 | what: &U, | ||
370 | ) { | ||
371 | let before = before.clone().into(); | ||
372 | let pos = match before.prev_sibling_or_token() { | ||
373 | Some(sibling) => InsertPos::After(sibling), | ||
374 | None => match before.parent() { | ||
375 | Some(parent) => InsertPos::FirstChildOf(parent), | ||
376 | None => return, | ||
377 | }, | ||
378 | }; | ||
379 | self.insertions.entry(pos).or_insert_with(Vec::new).push(what.clone().into()); | ||
380 | } | ||
381 | pub fn insert_after<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>( | ||
382 | &mut self, | ||
383 | after: &T, | ||
384 | what: &U, | ||
385 | ) { | ||
386 | self.insertions | ||
387 | .entry(InsertPos::After(after.clone().into())) | ||
388 | .or_insert_with(Vec::new) | ||
389 | .push(what.clone().into()); | ||
390 | } | ||
391 | pub fn insert_as_first_child<T: Clone + Into<SyntaxNode>, U: Clone + Into<SyntaxElement>>( | ||
392 | &mut self, | ||
393 | parent: &T, | ||
394 | what: &U, | ||
395 | ) { | ||
396 | self.insertions | ||
397 | .entry(InsertPos::FirstChildOf(parent.clone().into())) | ||
398 | .or_insert_with(Vec::new) | ||
399 | .push(what.clone().into()); | ||
400 | } | ||
401 | pub fn insert_many_before< | ||
402 | T: Clone + Into<SyntaxElement>, | ||
403 | U: IntoIterator<Item = SyntaxElement>, | ||
404 | >( | ||
405 | &mut self, | ||
406 | before: &T, | ||
407 | what: U, | ||
408 | ) { | ||
409 | let before = before.clone().into(); | ||
410 | let pos = match before.prev_sibling_or_token() { | ||
411 | Some(sibling) => InsertPos::After(sibling), | ||
412 | None => match before.parent() { | ||
413 | Some(parent) => InsertPos::FirstChildOf(parent), | ||
414 | None => return, | ||
415 | }, | ||
416 | }; | ||
417 | self.insertions.entry(pos).or_insert_with(Vec::new).extend(what); | ||
418 | } | ||
419 | pub fn insert_many_after< | ||
420 | T: Clone + Into<SyntaxElement>, | ||
421 | U: IntoIterator<Item = SyntaxElement>, | ||
422 | >( | ||
423 | &mut self, | ||
424 | after: &T, | ||
425 | what: U, | ||
426 | ) { | ||
427 | self.insertions | ||
428 | .entry(InsertPos::After(after.clone().into())) | ||
429 | .or_insert_with(Vec::new) | ||
430 | .extend(what); | ||
431 | } | ||
432 | pub fn insert_many_as_first_children< | ||
433 | T: Clone + Into<SyntaxNode>, | ||
434 | U: IntoIterator<Item = SyntaxElement>, | ||
435 | >( | ||
436 | &mut self, | ||
437 | parent: &T, | ||
438 | what: U, | ||
439 | ) { | ||
440 | self.insertions | ||
441 | .entry(InsertPos::FirstChildOf(parent.clone().into())) | ||
442 | .or_insert_with(Vec::new) | ||
443 | .extend(what) | ||
444 | } | ||
445 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { | 357 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { |
446 | let what = what.clone().into(); | 358 | let what = what.clone().into(); |
447 | let replacement = Replacement::Single(with.clone().into()); | 359 | let replacement = Replacement::Single(with.clone().into()); |
448 | self.replacements.insert(what, replacement); | 360 | self.replacements.insert(what, replacement); |
449 | } | 361 | } |
450 | pub fn replace_with_many<T: Clone + Into<SyntaxElement>>( | ||
451 | &mut self, | ||
452 | what: &T, | ||
453 | with: Vec<SyntaxElement>, | ||
454 | ) { | ||
455 | let what = what.clone().into(); | ||
456 | let replacement = Replacement::Many(with); | ||
457 | self.replacements.insert(what, replacement); | ||
458 | } | ||
459 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { | ||
460 | self.replace(what.syntax(), with.syntax()) | ||
461 | } | ||
462 | 362 | ||
463 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { | 363 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { |
464 | let _p = profile::span("rewrite"); | 364 | let _p = profile::span("rewrite"); |
@@ -534,10 +434,6 @@ impl SyntaxRewriter<'_> { | |||
534 | if let Some(replacement) = self.replacement(&element) { | 434 | if let Some(replacement) = self.replacement(&element) { |
535 | match replacement { | 435 | match replacement { |
536 | Replacement::Single(element) => acc.push(element_to_green(element)), | 436 | Replacement::Single(element) => acc.push(element_to_green(element)), |
537 | Replacement::Many(replacements) => { | ||
538 | acc.extend(replacements.into_iter().map(element_to_green)) | ||
539 | } | ||
540 | Replacement::Delete => (), | ||
541 | }; | 437 | }; |
542 | } else { | 438 | } else { |
543 | match element { | 439 | match element { |
@@ -560,25 +456,9 @@ fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, row | |||
560 | } | 456 | } |
561 | } | 457 | } |
562 | 458 | ||
563 | impl ops::AddAssign for SyntaxRewriter<'_> { | ||
564 | fn add_assign(&mut self, rhs: SyntaxRewriter) { | ||
565 | self.replacements.extend(rhs.replacements); | ||
566 | for (pos, insertions) in rhs.insertions.into_iter() { | ||
567 | match self.insertions.entry(pos) { | ||
568 | indexmap::map::Entry::Occupied(mut occupied) => { | ||
569 | occupied.get_mut().extend(insertions) | ||
570 | } | ||
571 | indexmap::map::Entry::Vacant(vacant) => drop(vacant.insert(insertions)), | ||
572 | } | ||
573 | } | ||
574 | } | ||
575 | } | ||
576 | |||
577 | #[derive(Clone, Debug)] | 459 | #[derive(Clone, Debug)] |
578 | enum Replacement { | 460 | enum Replacement { |
579 | Delete, | ||
580 | Single(SyntaxElement), | 461 | Single(SyntaxElement), |
581 | Many(Vec<SyntaxElement>), | ||
582 | } | 462 | } |
583 | 463 | ||
584 | fn with_children( | 464 | fn with_children( |
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 8c60927e4..cbc75f922 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -665,18 +665,8 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { | |||
665 | 665 | ||
666 | #[must_use] | 666 | #[must_use] |
667 | fn replace_descendant<D: AstNode>(&self, old: D, new: D) -> Self { | 667 | fn replace_descendant<D: AstNode>(&self, old: D, new: D) -> Self { |
668 | self.replace_descendants(iter::once((old, new))) | ||
669 | } | ||
670 | |||
671 | #[must_use] | ||
672 | fn replace_descendants<D: AstNode>( | ||
673 | &self, | ||
674 | replacement_map: impl IntoIterator<Item = (D, D)>, | ||
675 | ) -> Self { | ||
676 | let mut rewriter = SyntaxRewriter::default(); | 668 | let mut rewriter = SyntaxRewriter::default(); |
677 | for (from, to) in replacement_map { | 669 | rewriter.replace(old.syntax(), new.syntax()); |
678 | rewriter.replace(from.syntax(), to.syntax()) | ||
679 | } | ||
680 | rewriter.rewrite_ast(self) | 670 | rewriter.rewrite_ast(self) |
681 | } | 671 | } |
682 | fn indent_level(&self) -> IndentLevel { | 672 | fn indent_level(&self) -> IndentLevel { |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index f8b508a90..5a6687397 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -275,6 +275,9 @@ pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::Expr { | |||
275 | let expr = elements.into_iter().format(", "); | 275 | let expr = elements.into_iter().format(", "); |
276 | expr_from_text(&format!("({})", expr)) | 276 | expr_from_text(&format!("({})", expr)) |
277 | } | 277 | } |
278 | pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { | ||
279 | expr_from_text(&format!("{} = {}", lhs, rhs)) | ||
280 | } | ||
278 | fn expr_from_text(text: &str) -> ast::Expr { | 281 | fn expr_from_text(text: &str) -> ast::Expr { |
279 | ast_from_text(&format!("const C: () = {};", text)) | 282 | ast_from_text(&format!("const C: () = {};", text)) |
280 | } | 283 | } |