diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/remove_dbg.rs | 155 | ||||
-rw-r--r-- | crates/hir/src/semantics.rs | 8 | ||||
-rw-r--r-- | crates/hir/src/source_analyzer.rs | 4 | ||||
-rw-r--r-- | crates/hir_ty/src/infer.rs | 6 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/pat.rs | 2 | ||||
-rw-r--r-- | crates/ide_db/src/defs.rs | 10 | ||||
-rw-r--r-- | crates/parser/src/grammar/patterns.rs | 4 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rast (renamed from crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast) | 0 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rs (renamed from crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rs) | 0 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast (renamed from crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rast) | 0 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs (renamed from crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rs) | 0 |
11 files changed, 135 insertions, 54 deletions
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs index 4e252edf0..0b581dc22 100644 --- a/crates/assists/src/handlers/remove_dbg.rs +++ b/crates/assists/src/handlers/remove_dbg.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
3 | TextRange, TextSize, T, | 3 | SyntaxElement, TextRange, TextSize, T, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
@@ -22,62 +22,118 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; | |||
22 | // ``` | 22 | // ``` |
23 | pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 23 | pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
24 | let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?; | 24 | let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?; |
25 | let new_contents = adjusted_macro_contents(¯o_call)?; | ||
25 | 26 | ||
26 | if !is_valid_macrocall(¯o_call, "dbg")? { | 27 | let macro_text_range = macro_call.syntax().text_range(); |
27 | return None; | ||
28 | } | ||
29 | |||
30 | let is_leaf = macro_call.syntax().next_sibling().is_none(); | ||
31 | |||
32 | let macro_end = if macro_call.semicolon_token().is_some() { | 28 | let macro_end = if macro_call.semicolon_token().is_some() { |
33 | macro_call.syntax().text_range().end() - TextSize::of(';') | 29 | macro_text_range.end() - TextSize::of(';') |
34 | } else { | 30 | } else { |
35 | macro_call.syntax().text_range().end() | 31 | macro_text_range.end() |
36 | }; | 32 | }; |
37 | 33 | ||
38 | // macro_range determines what will be deleted and replaced with macro_content | 34 | acc.add( |
39 | let macro_range = TextRange::new(macro_call.syntax().text_range().start(), macro_end); | 35 | AssistId("remove_dbg", AssistKind::Refactor), |
40 | let paste_instead_of_dbg = { | 36 | "Remove dbg!()", |
41 | let text = macro_call.token_tree()?.syntax().text(); | 37 | macro_text_range, |
42 | 38 | |builder| { | |
43 | // leafiness determines if we should include the parenthesis or not | 39 | builder.replace(TextRange::new(macro_text_range.start(), macro_end), new_contents); |
44 | let slice_index: TextRange = if is_leaf { | 40 | }, |
45 | // leaf means - we can extract the contents of the dbg! in text | 41 | ) |
46 | TextRange::new(TextSize::of('('), text.len() - TextSize::of(')')) | 42 | } |
47 | } else { | ||
48 | // not leaf - means we should keep the parens | ||
49 | TextRange::up_to(text.len()) | ||
50 | }; | ||
51 | text.slice(slice_index).to_string() | ||
52 | }; | ||
53 | 43 | ||
54 | let target = macro_call.syntax().text_range(); | 44 | fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { |
55 | acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", target, |builder| { | 45 | let contents = get_valid_macrocall_contents(¯o_call, "dbg")?; |
56 | builder.replace(macro_range, paste_instead_of_dbg); | 46 | let is_leaf = macro_call.syntax().next_sibling().is_none(); |
57 | }) | 47 | let macro_text_with_brackets = macro_call.token_tree()?.syntax().text(); |
48 | let slice_index = if is_leaf || !needs_parentheses_around_macro_contents(contents) { | ||
49 | TextRange::new(TextSize::of('('), macro_text_with_brackets.len() - TextSize::of(')')) | ||
50 | } else { | ||
51 | // leave parenthesis around macro contents to preserve the semantics | ||
52 | TextRange::up_to(macro_text_with_brackets.len()) | ||
53 | }; | ||
54 | Some(macro_text_with_brackets.slice(slice_index).to_string()) | ||
58 | } | 55 | } |
59 | 56 | ||
60 | /// Verifies that the given macro_call actually matches the given name | 57 | /// Verifies that the given macro_call actually matches the given name |
61 | /// and contains proper ending tokens | 58 | /// and contains proper ending tokens, then returns the contents between the ending tokens |
62 | fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<bool> { | 59 | fn get_valid_macrocall_contents( |
60 | macro_call: &ast::MacroCall, | ||
61 | macro_name: &str, | ||
62 | ) -> Option<Vec<SyntaxElement>> { | ||
63 | let path = macro_call.path()?; | 63 | let path = macro_call.path()?; |
64 | let name_ref = path.segment()?.name_ref()?; | 64 | let name_ref = path.segment()?.name_ref()?; |
65 | 65 | ||
66 | // Make sure it is actually a dbg-macro call, dbg followed by ! | 66 | // Make sure it is actually a dbg-macro call, dbg followed by ! |
67 | let excl = path.syntax().next_sibling_or_token()?; | 67 | let excl = path.syntax().next_sibling_or_token()?; |
68 | |||
69 | if name_ref.text() != macro_name || excl.kind() != T![!] { | 68 | if name_ref.text() != macro_name || excl.kind() != T![!] { |
70 | return None; | 69 | return None; |
71 | } | 70 | } |
72 | 71 | ||
73 | let node = macro_call.token_tree()?.syntax().clone(); | 72 | let mut children_with_tokens = macro_call.token_tree()?.syntax().children_with_tokens(); |
74 | let first_child = node.first_child_or_token()?; | 73 | let first_child = children_with_tokens.next()?; |
75 | let last_child = node.last_child_or_token()?; | 74 | let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>(); |
75 | let last_child = contents_between_brackets.pop()?; | ||
76 | |||
77 | if contents_between_brackets.is_empty() { | ||
78 | None | ||
79 | } else { | ||
80 | match (first_child.kind(), last_child.kind()) { | ||
81 | (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => { | ||
82 | Some(contents_between_brackets) | ||
83 | } | ||
84 | _ => None, | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -> bool { | ||
90 | if macro_contents.len() < 2 { | ||
91 | return false; | ||
92 | } | ||
93 | |||
94 | let mut macro_contents_kind_not_in_brackets = Vec::with_capacity(macro_contents.len()); | ||
76 | 95 | ||
77 | match (first_child.kind(), last_child.kind()) { | 96 | let mut first_bracket_in_macro = None; |
78 | (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => Some(true), | 97 | let mut unpaired_brackets_in_contents = Vec::new(); |
79 | _ => Some(false), | 98 | for element in macro_contents { |
99 | match element.kind() { | ||
100 | T!['('] | T!['['] | T!['{'] => { | ||
101 | if let None = first_bracket_in_macro { | ||
102 | first_bracket_in_macro = Some(element.clone()) | ||
103 | } | ||
104 | unpaired_brackets_in_contents.push(element); | ||
105 | } | ||
106 | T![')'] => { | ||
107 | if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['(']) | ||
108 | { | ||
109 | return true; | ||
110 | } | ||
111 | } | ||
112 | T![']'] => { | ||
113 | if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['[']) | ||
114 | { | ||
115 | return true; | ||
116 | } | ||
117 | } | ||
118 | T!['}'] => { | ||
119 | if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['{']) | ||
120 | { | ||
121 | return true; | ||
122 | } | ||
123 | } | ||
124 | other_kind => { | ||
125 | if unpaired_brackets_in_contents.is_empty() { | ||
126 | macro_contents_kind_not_in_brackets.push(other_kind); | ||
127 | } | ||
128 | } | ||
129 | } | ||
80 | } | 130 | } |
131 | |||
132 | !unpaired_brackets_in_contents.is_empty() | ||
133 | || matches!(first_bracket_in_macro, Some(bracket) if bracket.kind() != T!['(']) | ||
134 | || macro_contents_kind_not_in_brackets | ||
135 | .into_iter() | ||
136 | .any(|macro_contents_kind| macro_contents_kind.is_punct()) | ||
81 | } | 137 | } |
82 | 138 | ||
83 | #[cfg(test)] | 139 | #[cfg(test)] |
@@ -157,12 +213,37 @@ fn foo(n: usize) { | |||
157 | } | 213 | } |
158 | 214 | ||
159 | #[test] | 215 | #[test] |
216 | fn remove_dbg_from_non_leaf_simple_expression() { | ||
217 | check_assist( | ||
218 | remove_dbg, | ||
219 | " | ||
220 | fn main() { | ||
221 | let mut a = 1; | ||
222 | while dbg!<|>(a) < 10000 { | ||
223 | a += 1; | ||
224 | } | ||
225 | } | ||
226 | ", | ||
227 | " | ||
228 | fn main() { | ||
229 | let mut a = 1; | ||
230 | while a < 10000 { | ||
231 | a += 1; | ||
232 | } | ||
233 | } | ||
234 | ", | ||
235 | ); | ||
236 | } | ||
237 | |||
238 | #[test] | ||
160 | fn test_remove_dbg_keep_expression() { | 239 | fn test_remove_dbg_keep_expression() { |
161 | check_assist( | 240 | check_assist( |
162 | remove_dbg, | 241 | remove_dbg, |
163 | r#"let res = <|>dbg!(a + b).foo();"#, | 242 | r#"let res = <|>dbg!(a + b).foo();"#, |
164 | r#"let res = (a + b).foo();"#, | 243 | r#"let res = (a + b).foo();"#, |
165 | ); | 244 | ); |
245 | |||
246 | check_assist(remove_dbg, r#"let res = <|>dbg!(2 + 2) * 5"#, r#"let res = (2 + 2) * 5"#); | ||
166 | } | 247 | } |
167 | 248 | ||
168 | #[test] | 249 | #[test] |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 1594d4f0f..0516a05b4 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -207,8 +207,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
207 | self.imp.resolve_record_field(field) | 207 | self.imp.resolve_record_field(field) |
208 | } | 208 | } |
209 | 209 | ||
210 | pub fn resolve_record_field_pat(&self, field: &ast::RecordPatField) -> Option<Field> { | 210 | pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<Field> { |
211 | self.imp.resolve_record_field_pat(field) | 211 | self.imp.resolve_record_pat_field(field) |
212 | } | 212 | } |
213 | 213 | ||
214 | pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { | 214 | pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { |
@@ -433,8 +433,8 @@ impl<'db> SemanticsImpl<'db> { | |||
433 | self.analyze(field.syntax()).resolve_record_field(self.db, field) | 433 | self.analyze(field.syntax()).resolve_record_field(self.db, field) |
434 | } | 434 | } |
435 | 435 | ||
436 | fn resolve_record_field_pat(&self, field: &ast::RecordPatField) -> Option<Field> { | 436 | fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<Field> { |
437 | self.analyze(field.syntax()).resolve_record_field_pat(self.db, field) | 437 | self.analyze(field.syntax()).resolve_record_pat_field(self.db, field) |
438 | } | 438 | } |
439 | 439 | ||
440 | fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { | 440 | fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { |
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 1d13c4f1d..1aef0f33f 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs | |||
@@ -179,13 +179,13 @@ impl SourceAnalyzer { | |||
179 | Some((struct_field.into(), local)) | 179 | Some((struct_field.into(), local)) |
180 | } | 180 | } |
181 | 181 | ||
182 | pub(crate) fn resolve_record_field_pat( | 182 | pub(crate) fn resolve_record_pat_field( |
183 | &self, | 183 | &self, |
184 | _db: &dyn HirDatabase, | 184 | _db: &dyn HirDatabase, |
185 | field: &ast::RecordPatField, | 185 | field: &ast::RecordPatField, |
186 | ) -> Option<Field> { | 186 | ) -> Option<Field> { |
187 | let pat_id = self.pat_id(&field.pat()?)?; | 187 | let pat_id = self.pat_id(&field.pat()?)?; |
188 | let struct_field = self.infer.as_ref()?.record_field_pat_resolution(pat_id)?; | 188 | let struct_field = self.infer.as_ref()?.record_pat_field_resolution(pat_id)?; |
189 | Some(struct_field.into()) | 189 | Some(struct_field.into()) |
190 | } | 190 | } |
191 | 191 | ||
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 03b00b101..2b53b8297 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -125,7 +125,7 @@ pub struct InferenceResult { | |||
125 | field_resolutions: FxHashMap<ExprId, FieldId>, | 125 | field_resolutions: FxHashMap<ExprId, FieldId>, |
126 | /// For each field in record literal, records the field it resolves to. | 126 | /// For each field in record literal, records the field it resolves to. |
127 | record_field_resolutions: FxHashMap<ExprId, FieldId>, | 127 | record_field_resolutions: FxHashMap<ExprId, FieldId>, |
128 | record_field_pat_resolutions: FxHashMap<PatId, FieldId>, | 128 | record_pat_field_resolutions: FxHashMap<PatId, FieldId>, |
129 | /// For each struct literal, records the variant it resolves to. | 129 | /// For each struct literal, records the variant it resolves to. |
130 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, | 130 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, |
131 | /// For each associated item record what it resolves to | 131 | /// For each associated item record what it resolves to |
@@ -146,8 +146,8 @@ impl InferenceResult { | |||
146 | pub fn record_field_resolution(&self, expr: ExprId) -> Option<FieldId> { | 146 | pub fn record_field_resolution(&self, expr: ExprId) -> Option<FieldId> { |
147 | self.record_field_resolutions.get(&expr).copied() | 147 | self.record_field_resolutions.get(&expr).copied() |
148 | } | 148 | } |
149 | pub fn record_field_pat_resolution(&self, pat: PatId) -> Option<FieldId> { | 149 | pub fn record_pat_field_resolution(&self, pat: PatId) -> Option<FieldId> { |
150 | self.record_field_pat_resolutions.get(&pat).copied() | 150 | self.record_pat_field_resolutions.get(&pat).copied() |
151 | } | 151 | } |
152 | pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> { | 152 | pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> { |
153 | self.variant_resolutions.get(&id.into()).copied() | 153 | self.variant_resolutions.get(&id.into()).copied() |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index 4dd4f9802..dde38bc39 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -70,7 +70,7 @@ impl<'a> InferenceContext<'a> { | |||
70 | let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name)); | 70 | let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name)); |
71 | if let Some(local_id) = matching_field { | 71 | if let Some(local_id) = matching_field { |
72 | let field_def = FieldId { parent: def.unwrap(), local_id }; | 72 | let field_def = FieldId { parent: def.unwrap(), local_id }; |
73 | self.result.record_field_pat_resolutions.insert(subpat.pat, field_def); | 73 | self.result.record_pat_field_resolutions.insert(subpat.pat, field_def); |
74 | } | 74 | } |
75 | 75 | ||
76 | let expected_ty = | 76 | let expected_ty = |
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index 0d0affc27..f8c7aa491 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs | |||
@@ -157,9 +157,9 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option | |||
157 | ast::IdentPat(it) => { | 157 | ast::IdentPat(it) => { |
158 | let local = sema.to_def(&it)?; | 158 | let local = sema.to_def(&it)?; |
159 | 159 | ||
160 | if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordPatField::cast) { | 160 | if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) { |
161 | if record_field_pat.name_ref().is_none() { | 161 | if record_pat_field.name_ref().is_none() { |
162 | if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { | 162 | if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) { |
163 | let field = Definition::Field(field); | 163 | let field = Definition::Field(field); |
164 | return Some(NameClass::FieldShorthand { local, field }); | 164 | return Some(NameClass::FieldShorthand { local, field }); |
165 | } | 165 | } |
@@ -275,8 +275,8 @@ pub fn classify_name_ref( | |||
275 | } | 275 | } |
276 | } | 276 | } |
277 | 277 | ||
278 | if let Some(record_field_pat) = ast::RecordPatField::cast(parent.clone()) { | 278 | if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) { |
279 | if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { | 279 | if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) { |
280 | let field = Definition::Field(field); | 280 | let field = Definition::Field(field); |
281 | return Some(NameRefClass::Definition(field)); | 281 | return Some(NameRefClass::Definition(field)); |
282 | } | 282 | } |
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index 796f206e1..7e7f73dee 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs | |||
@@ -188,7 +188,7 @@ fn tuple_pat_fields(p: &mut Parser) { | |||
188 | p.expect(T![')']); | 188 | p.expect(T![')']); |
189 | } | 189 | } |
190 | 190 | ||
191 | // test record_field_pat_list | 191 | // test record_pat_field_list |
192 | // fn foo() { | 192 | // fn foo() { |
193 | // let S {} = (); | 193 | // let S {} = (); |
194 | // let S { f, ref mut g } = (); | 194 | // let S { f, ref mut g } = (); |
@@ -208,7 +208,7 @@ fn record_pat_field_list(p: &mut Parser) { | |||
208 | c => { | 208 | c => { |
209 | let m = p.start(); | 209 | let m = p.start(); |
210 | match c { | 210 | match c { |
211 | // test record_field_pat | 211 | // test record_pat_field |
212 | // fn foo() { | 212 | // fn foo() { |
213 | // let S { 0: 1 } = (); | 213 | // let S { 0: 1 } = (); |
214 | // let S { x: 1 } = (); | 214 | // let S { x: 1 } = (); |
diff --git a/crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rast index 866e60ed8..866e60ed8 100644 --- a/crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast +++ b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rast | |||
diff --git a/crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rs b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rs index da3412fa8..da3412fa8 100644 --- a/crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rs +++ b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rs | |||
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rast b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast index 925409bdf..925409bdf 100644 --- a/crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rast +++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast | |||
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rs b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs index 26b1d5f89..26b1d5f89 100644 --- a/crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rs +++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs | |||