aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs155
-rw-r--r--crates/hir/src/semantics.rs8
-rw-r--r--crates/hir/src/source_analyzer.rs4
-rw-r--r--crates/hir_ty/src/infer.rs6
-rw-r--r--crates/hir_ty/src/infer/pat.rs2
-rw-r--r--crates/ide_db/src/defs.rs10
-rw-r--r--crates/parser/src/grammar/patterns.rs4
-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 @@
1use syntax::{ 1use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 TextRange, TextSize, T, 3 SyntaxElement, TextRange, TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -22,62 +22,118 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
22// ``` 22// ```
23pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 23pub(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(&macro_call)?;
25 26
26 if !is_valid_macrocall(&macro_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(); 44fn 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(&macro_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
62fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<bool> { 59fn 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
89fn 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 "
220fn main() {
221 let mut a = 1;
222 while dbg!<|>(a) < 10000 {
223 a += 1;
224 }
225}
226",
227 "
228fn 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