diff options
Diffstat (limited to 'crates')
32 files changed, 617 insertions, 176 deletions
diff --git a/crates/assists/src/handlers/move_guard.rs b/crates/assists/src/handlers/move_guard.rs index 452115fe6..e1855b63d 100644 --- a/crates/assists/src/handlers/move_guard.rs +++ b/crates/assists/src/handlers/move_guard.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{edit::AstNodeEdit, make, AstNode, IfExpr, MatchArm}, | 2 | ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, Expr, IfExpr, MatchArm}, |
3 | SyntaxKind::WHITESPACE, | 3 | SyntaxKind::WHITESPACE, |
4 | }; | 4 | }; |
5 | 5 | ||
@@ -92,9 +92,20 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> | |||
92 | pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 92 | pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
93 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; | 93 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; |
94 | let match_pat = match_arm.pat()?; | 94 | let match_pat = match_arm.pat()?; |
95 | |||
96 | let arm_body = match_arm.expr()?; | 95 | let arm_body = match_arm.expr()?; |
97 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?; | 96 | |
97 | let mut replace_node = None; | ||
98 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| { | ||
99 | let block_expr = BlockExpr::cast(arm_body.syntax().clone())?; | ||
100 | if let Expr::IfExpr(e) = block_expr.expr()? { | ||
101 | replace_node = Some(block_expr.syntax().clone()); | ||
102 | Some(e) | ||
103 | } else { | ||
104 | None | ||
105 | } | ||
106 | })?; | ||
107 | let replace_node = replace_node.unwrap_or_else(|| if_expr.syntax().clone()); | ||
108 | |||
98 | let cond = if_expr.condition()?; | 109 | let cond = if_expr.condition()?; |
99 | let then_block = if_expr.then_branch()?; | 110 | let then_block = if_expr.then_branch()?; |
100 | 111 | ||
@@ -109,19 +120,23 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex | |||
109 | 120 | ||
110 | let buf = format!(" if {}", cond.syntax().text()); | 121 | let buf = format!(" if {}", cond.syntax().text()); |
111 | 122 | ||
112 | let target = if_expr.syntax().text_range(); | ||
113 | acc.add( | 123 | acc.add( |
114 | AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite), | 124 | AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite), |
115 | "Move condition to match guard", | 125 | "Move condition to match guard", |
116 | target, | 126 | replace_node.text_range(), |
117 | |edit| { | 127 | |edit| { |
118 | let then_only_expr = then_block.statements().next().is_none(); | 128 | let then_only_expr = then_block.statements().next().is_none(); |
119 | 129 | ||
120 | match &then_block.expr() { | 130 | match &then_block.expr() { |
121 | Some(then_expr) if then_only_expr => { | 131 | Some(then_expr) if then_only_expr => { |
122 | edit.replace(if_expr.syntax().text_range(), then_expr.syntax().text()) | 132 | edit.replace(replace_node.text_range(), then_expr.syntax().text()) |
123 | } | 133 | } |
124 | _ => edit.replace(if_expr.syntax().text_range(), then_block.syntax().text()), | 134 | _ if replace_node != *if_expr.syntax() => { |
135 | // Dedent because if_expr is in a BlockExpr | ||
136 | let replace_with = then_block.dedent(1.into()).syntax().text(); | ||
137 | edit.replace(replace_node.text_range(), replace_with) | ||
138 | } | ||
139 | _ => edit.replace(replace_node.text_range(), then_block.syntax().text()), | ||
125 | } | 140 | } |
126 | 141 | ||
127 | edit.insert(match_pat.syntax().text_range().end(), buf); | 142 | edit.insert(match_pat.syntax().text_range().end(), buf); |
@@ -225,6 +240,33 @@ fn main() { | |||
225 | } | 240 | } |
226 | 241 | ||
227 | #[test] | 242 | #[test] |
243 | fn move_arm_cond_in_block_to_match_guard_works() { | ||
244 | check_assist( | ||
245 | move_arm_cond_to_match_guard, | ||
246 | r#" | ||
247 | fn main() { | ||
248 | match 92 { | ||
249 | x => { | ||
250 | <|>if x > 10 { | ||
251 | false | ||
252 | } | ||
253 | }, | ||
254 | _ => true | ||
255 | } | ||
256 | } | ||
257 | "#, | ||
258 | r#" | ||
259 | fn main() { | ||
260 | match 92 { | ||
261 | x if x > 10 => false, | ||
262 | _ => true | ||
263 | } | ||
264 | } | ||
265 | "#, | ||
266 | ); | ||
267 | } | ||
268 | |||
269 | #[test] | ||
228 | fn move_arm_cond_to_match_guard_if_let_not_works() { | 270 | fn move_arm_cond_to_match_guard_if_let_not_works() { |
229 | check_assist_not_applicable( | 271 | check_assist_not_applicable( |
230 | move_arm_cond_to_match_guard, | 272 | move_arm_cond_to_match_guard, |
@@ -290,4 +332,35 @@ fn main() { | |||
290 | "#, | 332 | "#, |
291 | ); | 333 | ); |
292 | } | 334 | } |
335 | |||
336 | #[test] | ||
337 | fn move_arm_cond_in_block_to_match_guard_if_multiline_body_works() { | ||
338 | check_assist( | ||
339 | move_arm_cond_to_match_guard, | ||
340 | r#" | ||
341 | fn main() { | ||
342 | match 92 { | ||
343 | x => { | ||
344 | if x > 10 { | ||
345 | 92;<|> | ||
346 | false | ||
347 | } | ||
348 | } | ||
349 | _ => true | ||
350 | } | ||
351 | } | ||
352 | "#, | ||
353 | r#" | ||
354 | fn main() { | ||
355 | match 92 { | ||
356 | x if x > 10 => { | ||
357 | 92; | ||
358 | false | ||
359 | } | ||
360 | _ => true | ||
361 | } | ||
362 | } | ||
363 | "#, | ||
364 | ) | ||
365 | } | ||
293 | } | 366 | } |
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs index e10616779..9731344b8 100644 --- a/crates/assists/src/handlers/remove_dbg.rs +++ b/crates/assists/src/handlers/remove_dbg.rs | |||
@@ -93,8 +93,9 @@ fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) - | |||
93 | if macro_contents.len() < 2 { | 93 | if macro_contents.len() < 2 { |
94 | return false; | 94 | return false; |
95 | } | 95 | } |
96 | let mut macro_contents = macro_contents.into_iter().peekable(); | ||
96 | let mut unpaired_brackets_in_contents = Vec::new(); | 97 | let mut unpaired_brackets_in_contents = Vec::new(); |
97 | for element in macro_contents { | 98 | while let Some(element) = macro_contents.next() { |
98 | match element.kind() { | 99 | match element.kind() { |
99 | T!['('] | T!['['] | T!['{'] => unpaired_brackets_in_contents.push(element), | 100 | T!['('] | T!['['] | T!['{'] => unpaired_brackets_in_contents.push(element), |
100 | T![')'] => { | 101 | T![')'] => { |
@@ -118,8 +119,14 @@ fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) - | |||
118 | symbol_kind => { | 119 | symbol_kind => { |
119 | let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); | 120 | let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); |
120 | if symbol_not_in_bracket | 121 | if symbol_not_in_bracket |
121 | && symbol_kind != SyntaxKind::COLON | 122 | && symbol_kind != SyntaxKind::COLON // paths |
122 | && symbol_kind.is_punct() | 123 | && (symbol_kind != SyntaxKind::DOT // field/method access |
124 | || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations | ||
125 | .peek() | ||
126 | .map(|element| element.kind() == SyntaxKind::DOT) | ||
127 | .unwrap_or(false)) | ||
128 | && symbol_kind != SyntaxKind::QUESTION // try operator | ||
129 | && (symbol_kind.is_punct() || symbol_kind == SyntaxKind::AS_KW) | ||
123 | { | 130 | { |
124 | return true; | 131 | return true; |
125 | } | 132 | } |
@@ -243,6 +250,25 @@ fn main() { | |||
243 | } | 250 | } |
244 | 251 | ||
245 | #[test] | 252 | #[test] |
253 | fn test_remove_dbg_method_chaining() { | ||
254 | check_assist( | ||
255 | remove_dbg, | ||
256 | r#"let res = <|>dbg!(foo().bar()).baz();"#, | ||
257 | r#"let res = foo().bar().baz();"#, | ||
258 | ); | ||
259 | check_assist( | ||
260 | remove_dbg, | ||
261 | r#"let res = <|>dbg!(foo.bar()).baz();"#, | ||
262 | r#"let res = foo.bar().baz();"#, | ||
263 | ); | ||
264 | } | ||
265 | |||
266 | #[test] | ||
267 | fn test_remove_dbg_field_chaining() { | ||
268 | check_assist(remove_dbg, r#"let res = <|>dbg!(foo.bar).baz;"#, r#"let res = foo.bar.baz;"#); | ||
269 | } | ||
270 | |||
271 | #[test] | ||
246 | fn test_remove_dbg_from_inside_fn() { | 272 | fn test_remove_dbg_from_inside_fn() { |
247 | check_assist_target( | 273 | check_assist_target( |
248 | remove_dbg, | 274 | remove_dbg, |
@@ -280,4 +306,59 @@ fn main() { | |||
280 | }"#, | 306 | }"#, |
281 | ); | 307 | ); |
282 | } | 308 | } |
309 | |||
310 | #[test] | ||
311 | fn test_remove_dbg_try_expr() { | ||
312 | check_assist( | ||
313 | remove_dbg, | ||
314 | r#"let res = <|>dbg!(result?).foo();"#, | ||
315 | r#"let res = result?.foo();"#, | ||
316 | ); | ||
317 | } | ||
318 | |||
319 | #[test] | ||
320 | fn test_remove_dbg_await_expr() { | ||
321 | check_assist( | ||
322 | remove_dbg, | ||
323 | r#"let res = <|>dbg!(fut.await).foo();"#, | ||
324 | r#"let res = fut.await.foo();"#, | ||
325 | ); | ||
326 | } | ||
327 | |||
328 | #[test] | ||
329 | fn test_remove_dbg_as_cast() { | ||
330 | check_assist( | ||
331 | remove_dbg, | ||
332 | r#"let res = <|>dbg!(3 as usize).foo();"#, | ||
333 | r#"let res = (3 as usize).foo();"#, | ||
334 | ); | ||
335 | } | ||
336 | |||
337 | #[test] | ||
338 | fn test_remove_dbg_index_expr() { | ||
339 | check_assist( | ||
340 | remove_dbg, | ||
341 | r#"let res = <|>dbg!(array[3]).foo();"#, | ||
342 | r#"let res = array[3].foo();"#, | ||
343 | ); | ||
344 | check_assist( | ||
345 | remove_dbg, | ||
346 | r#"let res = <|>dbg!(tuple.3).foo();"#, | ||
347 | r#"let res = tuple.3.foo();"#, | ||
348 | ); | ||
349 | } | ||
350 | |||
351 | #[test] | ||
352 | fn test_remove_dbg_range_expr() { | ||
353 | check_assist( | ||
354 | remove_dbg, | ||
355 | r#"let res = <|>dbg!(foo..bar).foo();"#, | ||
356 | r#"let res = (foo..bar).foo();"#, | ||
357 | ); | ||
358 | check_assist( | ||
359 | remove_dbg, | ||
360 | r#"let res = <|>dbg!(foo..=bar).foo();"#, | ||
361 | r#"let res = (foo..=bar).foo();"#, | ||
362 | ); | ||
363 | } | ||
283 | } | 364 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index f30172d90..1ff45d244 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -218,15 +218,18 @@ impl DefCollector<'_> { | |||
218 | let item_tree = self.db.item_tree(file_id.into()); | 218 | let item_tree = self.db.item_tree(file_id.into()); |
219 | let module_id = self.def_map.root; | 219 | let module_id = self.def_map.root; |
220 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; | 220 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; |
221 | ModCollector { | 221 | let mut root_collector = ModCollector { |
222 | def_collector: &mut *self, | 222 | def_collector: &mut *self, |
223 | macro_depth: 0, | 223 | macro_depth: 0, |
224 | module_id, | 224 | module_id, |
225 | file_id: file_id.into(), | 225 | file_id: file_id.into(), |
226 | item_tree: &item_tree, | 226 | item_tree: &item_tree, |
227 | mod_dir: ModDir::root(), | 227 | mod_dir: ModDir::root(), |
228 | }; | ||
229 | if item_tree.top_level_attrs().cfg().map_or(true, |cfg| root_collector.is_cfg_enabled(&cfg)) | ||
230 | { | ||
231 | root_collector.collect(item_tree.top_level_items()); | ||
228 | } | 232 | } |
229 | .collect(item_tree.top_level_items()); | ||
230 | 233 | ||
231 | // main name resolution fixed-point loop. | 234 | // main name resolution fixed-point loop. |
232 | let mut i = 0; | 235 | let mut i = 0; |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 11d84f808..9c19bf572 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -691,3 +691,20 @@ mod tr { | |||
691 | "#]], | 691 | "#]], |
692 | ); | 692 | ); |
693 | } | 693 | } |
694 | |||
695 | #[test] | ||
696 | fn cfg_the_entire_crate() { | ||
697 | check( | ||
698 | r#" | ||
699 | //- /main.rs | ||
700 | #![cfg(never)] | ||
701 | |||
702 | pub struct S; | ||
703 | pub enum E {} | ||
704 | pub fn f() {} | ||
705 | "#, | ||
706 | expect![[r#" | ||
707 | crate | ||
708 | "#]], | ||
709 | ); | ||
710 | } | ||
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index be7c812cb..367a1b98d 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -17,7 +17,7 @@ ena = "0.14.0" | |||
17 | log = "0.4.8" | 17 | log = "0.4.8" |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | scoped-tls = "1" | 19 | scoped-tls = "1" |
20 | chalk-solve = "0.34" | 20 | chalk-solve = { version = "0.34", default-features = false } |
21 | chalk-ir = "0.34" | 21 | chalk-ir = "0.34" |
22 | chalk-recursive = "0.34" | 22 | chalk-recursive = "0.34" |
23 | 23 | ||
diff --git a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs index 324d60765..b0144a289 100644 --- a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs +++ b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs | |||
@@ -1,156 +1,145 @@ | |||
1 | //! Functions for string case manipulation, such as detecting the identifier case, | 1 | //! Functions for string case manipulation, such as detecting the identifier case, |
2 | //! and converting it into appropriate form. | 2 | //! and converting it into appropriate form. |
3 | 3 | ||
4 | #[derive(Debug)] | 4 | // Code that was taken from rustc was taken at commit 89fdb30, |
5 | enum DetectedCase { | 5 | // from file /compiler/rustc_lint/src/nonstandard_style.rs |
6 | LowerCamelCase, | ||
7 | UpperCamelCase, | ||
8 | LowerSnakeCase, | ||
9 | UpperSnakeCase, | ||
10 | Unknown, | ||
11 | } | ||
12 | |||
13 | fn detect_case(ident: &str) -> DetectedCase { | ||
14 | let trimmed_ident = ident.trim_matches('_'); | ||
15 | let first_lowercase = trimmed_ident.starts_with(|chr: char| chr.is_ascii_lowercase()); | ||
16 | let mut has_lowercase = first_lowercase; | ||
17 | let mut has_uppercase = false; | ||
18 | let mut has_underscore = false; | ||
19 | |||
20 | for chr in trimmed_ident.chars() { | ||
21 | if chr == '_' { | ||
22 | has_underscore = true; | ||
23 | } else if chr.is_ascii_uppercase() { | ||
24 | has_uppercase = true; | ||
25 | } else if chr.is_ascii_lowercase() { | ||
26 | has_lowercase = true; | ||
27 | } | ||
28 | } | ||
29 | |||
30 | if has_uppercase { | ||
31 | if !has_lowercase { | ||
32 | if has_underscore { | ||
33 | DetectedCase::UpperSnakeCase | ||
34 | } else { | ||
35 | // It has uppercase only and no underscores. Ex: "AABB" | ||
36 | // This is a camel cased acronym. | ||
37 | DetectedCase::UpperCamelCase | ||
38 | } | ||
39 | } else if !has_underscore { | ||
40 | if first_lowercase { | ||
41 | DetectedCase::LowerCamelCase | ||
42 | } else { | ||
43 | DetectedCase::UpperCamelCase | ||
44 | } | ||
45 | } else { | ||
46 | // It has uppercase, it has lowercase, it has underscore. | ||
47 | // No assumptions here | ||
48 | DetectedCase::Unknown | ||
49 | } | ||
50 | } else { | ||
51 | DetectedCase::LowerSnakeCase | ||
52 | } | ||
53 | } | ||
54 | 6 | ||
55 | /// Converts an identifier to an UpperCamelCase form. | 7 | /// Converts an identifier to an UpperCamelCase form. |
56 | /// Returns `None` if the string is already is UpperCamelCase. | 8 | /// Returns `None` if the string is already is UpperCamelCase. |
57 | pub fn to_camel_case(ident: &str) -> Option<String> { | 9 | pub fn to_camel_case(ident: &str) -> Option<String> { |
58 | let detected_case = detect_case(ident); | 10 | if is_camel_case(ident) { |
59 | 11 | return None; | |
60 | match detected_case { | ||
61 | DetectedCase::UpperCamelCase => return None, | ||
62 | DetectedCase::LowerCamelCase => { | ||
63 | let mut first_capitalized = false; | ||
64 | let output = ident | ||
65 | .chars() | ||
66 | .map(|chr| { | ||
67 | if !first_capitalized && chr.is_ascii_lowercase() { | ||
68 | first_capitalized = true; | ||
69 | chr.to_ascii_uppercase() | ||
70 | } else { | ||
71 | chr | ||
72 | } | ||
73 | }) | ||
74 | .collect(); | ||
75 | return Some(output); | ||
76 | } | ||
77 | _ => {} | ||
78 | } | 12 | } |
79 | 13 | ||
80 | let mut output = String::with_capacity(ident.len()); | 14 | // Taken from rustc. |
81 | 15 | let ret = ident | |
82 | let mut capital_added = false; | 16 | .trim_matches('_') |
83 | for chr in ident.chars() { | 17 | .split('_') |
84 | if chr.is_alphabetic() { | 18 | .filter(|component| !component.is_empty()) |
85 | if !capital_added { | 19 | .map(|component| { |
86 | output.push(chr.to_ascii_uppercase()); | 20 | let mut camel_cased_component = String::new(); |
87 | capital_added = true; | 21 | |
88 | } else { | 22 | let mut new_word = true; |
89 | output.push(chr.to_ascii_lowercase()); | 23 | let mut prev_is_lower_case = true; |
24 | |||
25 | for c in component.chars() { | ||
26 | // Preserve the case if an uppercase letter follows a lowercase letter, so that | ||
27 | // `camelCase` is converted to `CamelCase`. | ||
28 | if prev_is_lower_case && c.is_uppercase() { | ||
29 | new_word = true; | ||
30 | } | ||
31 | |||
32 | if new_word { | ||
33 | camel_cased_component.push_str(&c.to_uppercase().to_string()); | ||
34 | } else { | ||
35 | camel_cased_component.push_str(&c.to_lowercase().to_string()); | ||
36 | } | ||
37 | |||
38 | prev_is_lower_case = c.is_lowercase(); | ||
39 | new_word = false; | ||
90 | } | 40 | } |
91 | } else if chr == '_' { | ||
92 | // Skip this character and make the next one capital. | ||
93 | capital_added = false; | ||
94 | } else { | ||
95 | // Put the characted as-is. | ||
96 | output.push(chr); | ||
97 | } | ||
98 | } | ||
99 | 41 | ||
100 | if output == ident { | 42 | camel_cased_component |
101 | // While we didn't detect the correct case at the beginning, there | 43 | }) |
102 | // may be special cases: e.g. `A` is both valid CamelCase and UPPER_SNAKE_CASE. | 44 | .fold((String::new(), None), |(acc, prev): (String, Option<String>), next| { |
103 | None | 45 | // separate two components with an underscore if their boundary cannot |
104 | } else { | 46 | // be distinguished using a uppercase/lowercase case distinction |
105 | Some(output) | 47 | let join = if let Some(prev) = prev { |
106 | } | 48 | let l = prev.chars().last().unwrap(); |
49 | let f = next.chars().next().unwrap(); | ||
50 | !char_has_case(l) && !char_has_case(f) | ||
51 | } else { | ||
52 | false | ||
53 | }; | ||
54 | (acc + if join { "_" } else { "" } + &next, Some(next)) | ||
55 | }) | ||
56 | .0; | ||
57 | Some(ret) | ||
107 | } | 58 | } |
108 | 59 | ||
109 | /// Converts an identifier to a lower_snake_case form. | 60 | /// Converts an identifier to a lower_snake_case form. |
110 | /// Returns `None` if the string is already in lower_snake_case. | 61 | /// Returns `None` if the string is already in lower_snake_case. |
111 | pub fn to_lower_snake_case(ident: &str) -> Option<String> { | 62 | pub fn to_lower_snake_case(ident: &str) -> Option<String> { |
112 | // First, assume that it's UPPER_SNAKE_CASE. | 63 | if is_lower_snake_case(ident) { |
113 | match detect_case(ident) { | 64 | return None; |
114 | DetectedCase::LowerSnakeCase => return None, | 65 | } else if is_upper_snake_case(ident) { |
115 | DetectedCase::UpperSnakeCase => { | 66 | return Some(ident.to_lowercase()); |
116 | return Some(ident.chars().map(|chr| chr.to_ascii_lowercase()).collect()) | ||
117 | } | ||
118 | _ => {} | ||
119 | } | 67 | } |
120 | 68 | ||
121 | // Otherwise, assume that it's CamelCase. | 69 | Some(stdx::to_lower_snake_case(ident)) |
122 | let lower_snake_case = stdx::to_lower_snake_case(ident); | ||
123 | |||
124 | if lower_snake_case == ident { | ||
125 | // While we didn't detect the correct case at the beginning, there | ||
126 | // may be special cases: e.g. `a` is both valid camelCase and snake_case. | ||
127 | None | ||
128 | } else { | ||
129 | Some(lower_snake_case) | ||
130 | } | ||
131 | } | 70 | } |
132 | 71 | ||
133 | /// Converts an identifier to an UPPER_SNAKE_CASE form. | 72 | /// Converts an identifier to an UPPER_SNAKE_CASE form. |
134 | /// Returns `None` if the string is already is UPPER_SNAKE_CASE. | 73 | /// Returns `None` if the string is already is UPPER_SNAKE_CASE. |
135 | pub fn to_upper_snake_case(ident: &str) -> Option<String> { | 74 | pub fn to_upper_snake_case(ident: &str) -> Option<String> { |
136 | match detect_case(ident) { | 75 | if is_upper_snake_case(ident) { |
137 | DetectedCase::UpperSnakeCase => return None, | 76 | return None; |
138 | DetectedCase::LowerSnakeCase => { | 77 | } else if is_lower_snake_case(ident) { |
139 | return Some(ident.chars().map(|chr| chr.to_ascii_uppercase()).collect()) | 78 | return Some(ident.to_uppercase()); |
140 | } | 79 | } |
141 | _ => {} | 80 | |
81 | Some(stdx::to_upper_snake_case(ident)) | ||
82 | } | ||
83 | |||
84 | // Taken from rustc. | ||
85 | // Modified by replacing the use of unstable feature `array_windows`. | ||
86 | fn is_camel_case(name: &str) -> bool { | ||
87 | let name = name.trim_matches('_'); | ||
88 | if name.is_empty() { | ||
89 | return true; | ||
142 | } | 90 | } |
143 | 91 | ||
144 | // Normalize the string from whatever form it's in currently, and then just make it uppercase. | 92 | let mut fst = None; |
145 | let upper_snake_case = stdx::to_upper_snake_case(ident); | 93 | // start with a non-lowercase letter rather than non-uppercase |
94 | // ones (some scripts don't have a concept of upper/lowercase) | ||
95 | !name.chars().next().unwrap().is_lowercase() | ||
96 | && !name.contains("__") | ||
97 | && !name.chars().any(|snd| { | ||
98 | let ret = match (fst, snd) { | ||
99 | (None, _) => false, | ||
100 | (Some(fst), snd) => { | ||
101 | char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_' | ||
102 | } | ||
103 | }; | ||
104 | fst = Some(snd); | ||
105 | |||
106 | ret | ||
107 | }) | ||
108 | } | ||
109 | |||
110 | fn is_lower_snake_case(ident: &str) -> bool { | ||
111 | is_snake_case(ident, char::is_uppercase) | ||
112 | } | ||
146 | 113 | ||
147 | if upper_snake_case == ident { | 114 | fn is_upper_snake_case(ident: &str) -> bool { |
148 | // While we didn't detect the correct case at the beginning, there | 115 | is_snake_case(ident, char::is_lowercase) |
149 | // may be special cases: e.g. `A` is both valid CamelCase and UPPER_SNAKE_CASE. | 116 | } |
150 | None | 117 | |
151 | } else { | 118 | // Taken from rustc. |
152 | Some(upper_snake_case) | 119 | // Modified to allow checking for both upper and lower snake case. |
120 | fn is_snake_case<F: Fn(char) -> bool>(ident: &str, wrong_case: F) -> bool { | ||
121 | if ident.is_empty() { | ||
122 | return true; | ||
153 | } | 123 | } |
124 | let ident = ident.trim_matches('_'); | ||
125 | |||
126 | let mut allow_underscore = true; | ||
127 | ident.chars().all(|c| { | ||
128 | allow_underscore = match c { | ||
129 | '_' if !allow_underscore => return false, | ||
130 | '_' => false, | ||
131 | // It would be more obvious to check for the correct case, | ||
132 | // but some characters do not have a case. | ||
133 | c if !wrong_case(c) => true, | ||
134 | _ => return false, | ||
135 | }; | ||
136 | true | ||
137 | }) | ||
138 | } | ||
139 | |||
140 | // Taken from rustc. | ||
141 | fn char_has_case(c: char) -> bool { | ||
142 | c.is_lowercase() || c.is_uppercase() | ||
154 | } | 143 | } |
155 | 144 | ||
156 | #[cfg(test)] | 145 | #[cfg(test)] |
@@ -173,6 +162,7 @@ mod tests { | |||
173 | check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]); | 162 | check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]); |
174 | check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]); | 163 | check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]); |
175 | check(to_lower_snake_case, "a", expect![[""]]); | 164 | check(to_lower_snake_case, "a", expect![[""]]); |
165 | check(to_lower_snake_case, "abc", expect![[""]]); | ||
176 | } | 166 | } |
177 | 167 | ||
178 | #[test] | 168 | #[test] |
@@ -187,6 +177,11 @@ mod tests { | |||
187 | check(to_camel_case, "name", expect![["Name"]]); | 177 | check(to_camel_case, "name", expect![["Name"]]); |
188 | check(to_camel_case, "A", expect![[""]]); | 178 | check(to_camel_case, "A", expect![[""]]); |
189 | check(to_camel_case, "AABB", expect![[""]]); | 179 | check(to_camel_case, "AABB", expect![[""]]); |
180 | // Taken from rustc: /compiler/rustc_lint/src/nonstandard_style/tests.rs | ||
181 | check(to_camel_case, "X86_64", expect![[""]]); | ||
182 | check(to_camel_case, "x86__64", expect![["X86_64"]]); | ||
183 | check(to_camel_case, "Abc_123", expect![["Abc123"]]); | ||
184 | check(to_camel_case, "A1_b2_c3", expect![["A1B2C3"]]); | ||
190 | } | 185 | } |
191 | 186 | ||
192 | #[test] | 187 | #[test] |
@@ -197,5 +192,7 @@ mod tests { | |||
197 | check(to_upper_snake_case, "CamelCase", expect![["CAMEL_CASE"]]); | 192 | check(to_upper_snake_case, "CamelCase", expect![["CAMEL_CASE"]]); |
198 | check(to_upper_snake_case, "lowerCamelCase", expect![["LOWER_CAMEL_CASE"]]); | 193 | check(to_upper_snake_case, "lowerCamelCase", expect![["LOWER_CAMEL_CASE"]]); |
199 | check(to_upper_snake_case, "A", expect![[""]]); | 194 | check(to_upper_snake_case, "A", expect![[""]]); |
195 | check(to_upper_snake_case, "ABC", expect![[""]]); | ||
196 | check(to_upper_snake_case, "X86_64", expect![[""]]); | ||
200 | } | 197 | } |
201 | } | 198 | } |
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs index be3301313..dd7affcec 100644 --- a/crates/hir_ty/src/traits/chalk/mapping.rs +++ b/crates/hir_ty/src/traits/chalk/mapping.rs | |||
@@ -4,8 +4,8 @@ | |||
4 | //! conversions. | 4 | //! conversions. |
5 | 5 | ||
6 | use chalk_ir::{ | 6 | use chalk_ir::{ |
7 | cast::Cast, fold::shift::Shift, interner::HasInterner, PlaceholderIndex, Scalar, TypeName, | 7 | cast::Cast, fold::shift::Shift, interner::HasInterner, LifetimeData, PlaceholderIndex, Scalar, |
8 | UniverseIndex, | 8 | TypeName, UniverseIndex, |
9 | }; | 9 | }; |
10 | use chalk_solve::rust_ir; | 10 | use chalk_solve::rust_ir; |
11 | 11 | ||
@@ -76,7 +76,7 @@ impl ToChalk for Ty { | |||
76 | ); | 76 | ); |
77 | let bounded_ty = chalk_ir::DynTy { | 77 | let bounded_ty = chalk_ir::DynTy { |
78 | bounds: make_binders(where_clauses, 1), | 78 | bounds: make_binders(where_clauses, 1), |
79 | lifetime: FAKE_PLACEHOLDER.to_lifetime(&Interner), | 79 | lifetime: LifetimeData::Static.intern(&Interner), |
80 | }; | 80 | }; |
81 | chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) | 81 | chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) |
82 | } | 82 | } |
@@ -161,9 +161,6 @@ impl ToChalk for Ty { | |||
161 | } | 161 | } |
162 | } | 162 | } |
163 | 163 | ||
164 | const FAKE_PLACEHOLDER: PlaceholderIndex = | ||
165 | PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::MAX }; | ||
166 | |||
167 | /// We currently don't model lifetimes, but Chalk does. So, we have to insert a | 164 | /// We currently don't model lifetimes, but Chalk does. So, we have to insert a |
168 | /// fake lifetime here, because Chalks built-in logic may expect it to be there. | 165 | /// fake lifetime here, because Chalks built-in logic may expect it to be there. |
169 | fn ref_to_chalk( | 166 | fn ref_to_chalk( |
@@ -172,7 +169,7 @@ fn ref_to_chalk( | |||
172 | subst: Substs, | 169 | subst: Substs, |
173 | ) -> chalk_ir::Ty<Interner> { | 170 | ) -> chalk_ir::Ty<Interner> { |
174 | let arg = subst[0].clone().to_chalk(db); | 171 | let arg = subst[0].clone().to_chalk(db); |
175 | let lifetime = FAKE_PLACEHOLDER.to_lifetime(&Interner); | 172 | let lifetime = LifetimeData::Static.intern(&Interner); |
176 | chalk_ir::ApplicationTy { | 173 | chalk_ir::ApplicationTy { |
177 | name: TypeName::Ref(mutability.to_chalk(db)), | 174 | name: TypeName::Ref(mutability.to_chalk(db)), |
178 | substitution: chalk_ir::Substitution::from_iter( | 175 | substitution: chalk_ir::Substitution::from_iter( |
@@ -205,7 +202,11 @@ fn array_to_chalk(db: &dyn HirDatabase, subst: Substs) -> chalk_ir::Ty<Interner> | |||
205 | substitution: chalk_ir::Substitution::empty(&Interner), | 202 | substitution: chalk_ir::Substitution::empty(&Interner), |
206 | } | 203 | } |
207 | .intern(&Interner); | 204 | .intern(&Interner); |
208 | let const_ = FAKE_PLACEHOLDER.to_const(&Interner, usize_ty); | 205 | let const_ = chalk_ir::ConstData { |
206 | ty: usize_ty, | ||
207 | value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }), | ||
208 | } | ||
209 | .intern(&Interner); | ||
209 | chalk_ir::ApplicationTy { | 210 | chalk_ir::ApplicationTy { |
210 | name: TypeName::Array, | 211 | name: TypeName::Array, |
211 | substitution: chalk_ir::Substitution::from_iter( | 212 | substitution: chalk_ir::Substitution::from_iter( |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 750848467..9f864179e 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -579,7 +579,14 @@ fn highlight_element( | |||
579 | } | 579 | } |
580 | } | 580 | } |
581 | T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | 581 | T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { |
582 | HighlightTag::NumericLiteral.into() | 582 | let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; |
583 | |||
584 | let expr = prefix_expr.expr()?; | ||
585 | match expr { | ||
586 | ast::Expr::Literal(_) => HighlightTag::NumericLiteral, | ||
587 | _ => HighlightTag::Operator, | ||
588 | } | ||
589 | .into() | ||
583 | } | 590 | } |
584 | _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | 591 | _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { |
585 | HighlightTag::Operator.into() | 592 | HighlightTag::Operator.into() |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 0cb84866d..c6b4f5a00 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -176,6 +176,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
176 | 176 | ||
177 | <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="punctuation">;</span> | 177 | <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="punctuation">;</span> |
178 | <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function">baz</span><span class="punctuation">;</span> | 178 | <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function">baz</span><span class="punctuation">;</span> |
179 | |||
180 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="punctuation">;</span> | ||
181 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span> | ||
179 | <span class="punctuation">}</span> | 182 | <span class="punctuation">}</span> |
180 | 183 | ||
181 | <span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation"><</span><span class="type_param declaration">T</span><span class="punctuation">></span> <span class="punctuation">{</span> | 184 | <span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation"><</span><span class="type_param declaration">T</span><span class="punctuation">></span> <span class="punctuation">{</span> |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index da20c300e..dd43f9dd9 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -150,6 +150,9 @@ fn main() { | |||
150 | 150 | ||
151 | let a = |x| x; | 151 | let a = |x| x; |
152 | let bar = Foo::baz; | 152 | let bar = Foo::baz; |
153 | |||
154 | let baz = -42; | ||
155 | let baz = -baz; | ||
153 | } | 156 | } |
154 | 157 | ||
155 | enum Option<T> { | 158 | enum Option<T> { |
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 562e92252..4ab206a83 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs | |||
@@ -184,7 +184,11 @@ fn opt_visibility(p: &mut Parser) -> bool { | |||
184 | // pub(self) struct S; | 184 | // pub(self) struct S; |
185 | // pub(self) struct S; | 185 | // pub(self) struct S; |
186 | // pub(self) struct S; | 186 | // pub(self) struct S; |
187 | T![crate] | T![self] | T![super] => { | 187 | |
188 | // test pub_parens_typepath | ||
189 | // struct B(pub (super::A)); | ||
190 | // struct B(pub (crate::A,)); | ||
191 | T![crate] | T![self] | T![super] if p.nth(2) != T![:] => { | ||
188 | p.bump_any(); | 192 | p.bump_any(); |
189 | p.bump_any(); | 193 | p.bump_any(); |
190 | p.expect(T![')']); | 194 | p.expect(T![')']); |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 2f0fa9726..d25c4bf83 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -21,7 +21,7 @@ env_logger = { version = "0.8.1", default-features = false } | |||
21 | itertools = "0.9.0" | 21 | itertools = "0.9.0" |
22 | jod-thread = "0.1.0" | 22 | jod-thread = "0.1.0" |
23 | log = "0.4.8" | 23 | log = "0.4.8" |
24 | lsp-types = { version = "0.82.0", features = ["proposed"] } | 24 | lsp-types = { version = "0.83.0", features = ["proposed"] } |
25 | parking_lot = "0.11.0" | 25 | parking_lot = "0.11.0" |
26 | pico-args = "0.3.1" | 26 | pico-args = "0.3.1" |
27 | oorandom = "11.1.2" | 27 | oorandom = "11.1.2" |
@@ -32,6 +32,9 @@ threadpool = "1.7.1" | |||
32 | rayon = "1.5" | 32 | rayon = "1.5" |
33 | mimalloc = { version = "0.1.19", default-features = false, optional = true } | 33 | mimalloc = { version = "0.1.19", default-features = false, optional = true } |
34 | lsp-server = "0.4.0" | 34 | lsp-server = "0.4.0" |
35 | tracing = "0.1" | ||
36 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } | ||
37 | tracing-tree = { version = "0.1.4" } | ||
35 | 38 | ||
36 | stdx = { path = "../stdx", version = "0.0.0" } | 39 | stdx = { path = "../stdx", version = "0.0.0" } |
37 | flycheck = { path = "../flycheck", version = "0.0.0" } | 40 | flycheck = { path = "../flycheck", version = "0.0.0" } |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 97b246a32..4175e569e 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -68,10 +68,32 @@ fn setup_logging(log_file: Option<PathBuf>) -> Result<()> { | |||
68 | let filter = env::var("RA_LOG").ok(); | 68 | let filter = env::var("RA_LOG").ok(); |
69 | logger::Logger::new(log_file, filter.as_deref()).install(); | 69 | logger::Logger::new(log_file, filter.as_deref()).install(); |
70 | 70 | ||
71 | tracing_setup::setup_tracing()?; | ||
72 | |||
71 | profile::init(); | 73 | profile::init(); |
72 | Ok(()) | 74 | Ok(()) |
73 | } | 75 | } |
74 | 76 | ||
77 | mod tracing_setup { | ||
78 | use tracing::subscriber; | ||
79 | use tracing_subscriber::layer::SubscriberExt; | ||
80 | use tracing_subscriber::EnvFilter; | ||
81 | use tracing_subscriber::Registry; | ||
82 | use tracing_tree::HierarchicalLayer; | ||
83 | |||
84 | pub fn setup_tracing() -> super::Result<()> { | ||
85 | let filter = EnvFilter::from_env("CHALK_DEBUG"); | ||
86 | let layer = HierarchicalLayer::default() | ||
87 | .with_indent_lines(true) | ||
88 | .with_ansi(false) | ||
89 | .with_indent_amount(2) | ||
90 | .with_writer(std::io::stderr); | ||
91 | let subscriber = Registry::default().with(filter).with(layer); | ||
92 | subscriber::set_global_default(subscriber)?; | ||
93 | Ok(()) | ||
94 | } | ||
95 | } | ||
96 | |||
75 | fn run_server() -> Result<()> { | 97 | fn run_server() -> Result<()> { |
76 | log::info!("server will start"); | 98 | log::info!("server will start"); |
77 | 99 | ||
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index c589afeaf..ff1ae9575 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -48,7 +48,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
48 | references_provider: Some(OneOf::Left(true)), | 48 | references_provider: Some(OneOf::Left(true)), |
49 | document_highlight_provider: Some(OneOf::Left(true)), | 49 | document_highlight_provider: Some(OneOf::Left(true)), |
50 | document_symbol_provider: Some(OneOf::Left(true)), | 50 | document_symbol_provider: Some(OneOf::Left(true)), |
51 | workspace_symbol_provider: Some(true), | 51 | workspace_symbol_provider: Some(OneOf::Left(true)), |
52 | code_action_provider: Some(code_action_provider), | 52 | code_action_provider: Some(code_action_provider), |
53 | code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), | 53 | code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), |
54 | document_formatting_provider: Some(OneOf::Left(true)), | 54 | document_formatting_provider: Some(OneOf::Left(true)), |
@@ -113,6 +113,7 @@ fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProvi | |||
113 | CodeActionKind::REFACTOR_INLINE, | 113 | CodeActionKind::REFACTOR_INLINE, |
114 | CodeActionKind::REFACTOR_REWRITE, | 114 | CodeActionKind::REFACTOR_REWRITE, |
115 | ]), | 115 | ]), |
116 | resolve_provider: None, | ||
116 | work_done_progress_options: Default::default(), | 117 | work_done_progress_options: Default::default(), |
117 | }) | 118 | }) |
118 | }) | 119 | }) |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 1b9b24698..2ed6a0d82 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -47,6 +47,7 @@ pub struct Config { | |||
47 | pub call_info_full: bool, | 47 | pub call_info_full: bool, |
48 | pub lens: LensConfig, | 48 | pub lens: LensConfig, |
49 | pub hover: HoverConfig, | 49 | pub hover: HoverConfig, |
50 | pub semantic_tokens_refresh: bool, | ||
50 | 51 | ||
51 | pub with_sysroot: bool, | 52 | pub with_sysroot: bool, |
52 | pub linked_projects: Vec<LinkedProject>, | 53 | pub linked_projects: Vec<LinkedProject>, |
@@ -193,6 +194,7 @@ impl Config { | |||
193 | call_info_full: true, | 194 | call_info_full: true, |
194 | lens: LensConfig::default(), | 195 | lens: LensConfig::default(), |
195 | hover: HoverConfig::default(), | 196 | hover: HoverConfig::default(), |
197 | semantic_tokens_refresh: false, | ||
196 | linked_projects: Vec::new(), | 198 | linked_projects: Vec::new(), |
197 | root_path, | 199 | root_path, |
198 | } | 200 | } |
@@ -402,6 +404,14 @@ impl Config { | |||
402 | self.client_caps.hover_actions = get_bool("hoverActions"); | 404 | self.client_caps.hover_actions = get_bool("hoverActions"); |
403 | self.client_caps.status_notification = get_bool("statusNotification"); | 405 | self.client_caps.status_notification = get_bool("statusNotification"); |
404 | } | 406 | } |
407 | |||
408 | if let Some(workspace_caps) = caps.workspace.as_ref() { | ||
409 | if let Some(refresh_support) = | ||
410 | workspace_caps.semantic_tokens.as_ref().and_then(|it| it.refresh_support) | ||
411 | { | ||
412 | self.semantic_tokens_refresh = refresh_support; | ||
413 | } | ||
414 | } | ||
405 | } | 415 | } |
406 | } | 416 | } |
407 | 417 | ||
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt index d06517126..58d47d32a 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt | |||
@@ -20,6 +20,7 @@ | |||
20 | "trivially_copy_pass_by_ref", | 20 | "trivially_copy_pass_by_ref", |
21 | ), | 21 | ), |
22 | ), | 22 | ), |
23 | code_description: None, | ||
23 | source: Some( | 24 | source: Some( |
24 | "clippy", | 25 | "clippy", |
25 | ), | 26 | ), |
@@ -61,6 +62,7 @@ | |||
61 | ], | 62 | ], |
62 | ), | 63 | ), |
63 | tags: None, | 64 | tags: None, |
65 | data: None, | ||
64 | }, | 66 | }, |
65 | fixes: [], | 67 | fixes: [], |
66 | }, | 68 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt index f5de2f07f..6aa26bf63 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt | |||
@@ -20,12 +20,14 @@ | |||
20 | "E0277", | 20 | "E0277", |
21 | ), | 21 | ), |
22 | ), | 22 | ), |
23 | code_description: None, | ||
23 | source: Some( | 24 | source: Some( |
24 | "rustc", | 25 | "rustc", |
25 | ), | 26 | ), |
26 | message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", | 27 | message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", |
27 | related_information: None, | 28 | related_information: None, |
28 | tags: None, | 29 | tags: None, |
30 | data: None, | ||
29 | }, | 31 | }, |
30 | fixes: [], | 32 | fixes: [], |
31 | }, | 33 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt index 00e8da8a7..7aaffaba2 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt | |||
@@ -16,6 +16,7 @@ | |||
16 | Error, | 16 | Error, |
17 | ), | 17 | ), |
18 | code: None, | 18 | code: None, |
19 | code_description: None, | ||
19 | source: Some( | 20 | source: Some( |
20 | "rustc", | 21 | "rustc", |
21 | ), | 22 | ), |
@@ -41,6 +42,7 @@ | |||
41 | ], | 42 | ], |
42 | ), | 43 | ), |
43 | tags: None, | 44 | tags: None, |
45 | data: None, | ||
44 | }, | 46 | }, |
45 | fixes: [], | 47 | fixes: [], |
46 | }, | 48 | }, |
@@ -61,6 +63,7 @@ | |||
61 | Error, | 63 | Error, |
62 | ), | 64 | ), |
63 | code: None, | 65 | code: None, |
66 | code_description: None, | ||
64 | source: Some( | 67 | source: Some( |
65 | "rustc", | 68 | "rustc", |
66 | ), | 69 | ), |
@@ -86,6 +89,7 @@ | |||
86 | ], | 89 | ], |
87 | ), | 90 | ), |
88 | tags: None, | 91 | tags: None, |
92 | data: None, | ||
89 | }, | 93 | }, |
90 | fixes: [], | 94 | fixes: [], |
91 | }, | 95 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt index fc54440be..584213420 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt | |||
@@ -20,12 +20,14 @@ | |||
20 | "E0053", | 20 | "E0053", |
21 | ), | 21 | ), |
22 | ), | 22 | ), |
23 | code_description: None, | ||
23 | source: Some( | 24 | source: Some( |
24 | "rustc", | 25 | "rustc", |
25 | ), | 26 | ), |
26 | message: "method `next` has an incompatible type for trait\nexpected type `fn(&mut ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&\'list ty::Ref<M>>`", | 27 | message: "method `next` has an incompatible type for trait\nexpected type `fn(&mut ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&\'list ty::Ref<M>>`", |
27 | related_information: None, | 28 | related_information: None, |
28 | tags: None, | 29 | tags: None, |
30 | data: None, | ||
29 | }, | 31 | }, |
30 | fixes: [], | 32 | fixes: [], |
31 | }, | 33 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt index c269af218..2610e4e20 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt | |||
@@ -20,12 +20,14 @@ | |||
20 | "E0308", | 20 | "E0308", |
21 | ), | 21 | ), |
22 | ), | 22 | ), |
23 | code_description: None, | ||
23 | source: Some( | 24 | source: Some( |
24 | "rustc", | 25 | "rustc", |
25 | ), | 26 | ), |
26 | message: "mismatched types\nexpected usize, found u32", | 27 | message: "mismatched types\nexpected usize, found u32", |
27 | related_information: None, | 28 | related_information: None, |
28 | tags: None, | 29 | tags: None, |
30 | data: None, | ||
29 | }, | 31 | }, |
30 | fixes: [], | 32 | fixes: [], |
31 | }, | 33 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt index 74d91bc77..8dc53391e 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt | |||
@@ -20,6 +20,7 @@ | |||
20 | "unused_variables", | 20 | "unused_variables", |
21 | ), | 21 | ), |
22 | ), | 22 | ), |
23 | code_description: None, | ||
23 | source: Some( | 24 | source: Some( |
24 | "rustc", | 25 | "rustc", |
25 | ), | 26 | ), |
@@ -30,6 +31,7 @@ | |||
30 | Unnecessary, | 31 | Unnecessary, |
31 | ], | 32 | ], |
32 | ), | 33 | ), |
34 | data: None, | ||
33 | }, | 35 | }, |
34 | fixes: [ | 36 | fixes: [ |
35 | CodeAction { | 37 | CodeAction { |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt index 8a420c949..c8703194c 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt | |||
@@ -20,6 +20,7 @@ | |||
20 | "unused_variables", | 20 | "unused_variables", |
21 | ), | 21 | ), |
22 | ), | 22 | ), |
23 | code_description: None, | ||
23 | source: Some( | 24 | source: Some( |
24 | "rustc", | 25 | "rustc", |
25 | ), | 26 | ), |
@@ -30,6 +31,7 @@ | |||
30 | Unnecessary, | 31 | Unnecessary, |
31 | ], | 32 | ], |
32 | ), | 33 | ), |
34 | data: None, | ||
33 | }, | 35 | }, |
34 | fixes: [ | 36 | fixes: [ |
35 | CodeAction { | 37 | CodeAction { |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt index 79910660b..dc93227ad 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt | |||
@@ -20,6 +20,7 @@ | |||
20 | "unused_variables", | 20 | "unused_variables", |
21 | ), | 21 | ), |
22 | ), | 22 | ), |
23 | code_description: None, | ||
23 | source: Some( | 24 | source: Some( |
24 | "rustc", | 25 | "rustc", |
25 | ), | 26 | ), |
@@ -30,6 +31,7 @@ | |||
30 | Unnecessary, | 31 | Unnecessary, |
31 | ], | 32 | ], |
32 | ), | 33 | ), |
34 | data: None, | ||
33 | }, | 35 | }, |
34 | fixes: [ | 36 | fixes: [ |
35 | CodeAction { | 37 | CodeAction { |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt index efe37261d..ba1b98b33 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt | |||
@@ -20,6 +20,7 @@ | |||
20 | "E0061", | 20 | "E0061", |
21 | ), | 21 | ), |
22 | ), | 22 | ), |
23 | code_description: None, | ||
23 | source: Some( | 24 | source: Some( |
24 | "rustc", | 25 | "rustc", |
25 | ), | 26 | ), |
@@ -45,6 +46,7 @@ | |||
45 | ], | 46 | ], |
46 | ), | 47 | ), |
47 | tags: None, | 48 | tags: None, |
49 | data: None, | ||
48 | }, | 50 | }, |
49 | fixes: [], | 51 | fixes: [], |
50 | }, | 52 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt index 4f811ab64..81f752672 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt | |||
@@ -20,6 +20,7 @@ | |||
20 | "let_and_return", | 20 | "let_and_return", |
21 | ), | 21 | ), |
22 | ), | 22 | ), |
23 | code_description: None, | ||
23 | source: Some( | 24 | source: Some( |
24 | "clippy", | 25 | "clippy", |
25 | ), | 26 | ), |
@@ -45,6 +46,7 @@ | |||
45 | ], | 46 | ], |
46 | ), | 47 | ), |
47 | tags: None, | 48 | tags: None, |
49 | data: None, | ||
48 | }, | 50 | }, |
49 | fixes: [ | 51 | fixes: [ |
50 | CodeAction { | 52 | CodeAction { |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 33606edda..b949577c1 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -248,10 +248,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
248 | range: in_macro_location.range, | 248 | range: in_macro_location.range, |
249 | severity, | 249 | severity, |
250 | code: code.clone().map(lsp_types::NumberOrString::String), | 250 | code: code.clone().map(lsp_types::NumberOrString::String), |
251 | code_description: None, | ||
251 | source: Some(source.clone()), | 252 | source: Some(source.clone()), |
252 | message: message.clone(), | 253 | message: message.clone(), |
253 | related_information: Some(information_for_additional_diagnostic), | 254 | related_information: Some(information_for_additional_diagnostic), |
254 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, | 255 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, |
256 | data: None, | ||
255 | }; | 257 | }; |
256 | 258 | ||
257 | Some(MappedRustDiagnostic { | 259 | Some(MappedRustDiagnostic { |
@@ -267,6 +269,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
267 | range: location.range, | 269 | range: location.range, |
268 | severity, | 270 | severity, |
269 | code: code.clone().map(lsp_types::NumberOrString::String), | 271 | code: code.clone().map(lsp_types::NumberOrString::String), |
272 | code_description: None, | ||
270 | source: Some(source.clone()), | 273 | source: Some(source.clone()), |
271 | message, | 274 | message, |
272 | related_information: if related_information.is_empty() { | 275 | related_information: if related_information.is_empty() { |
@@ -275,6 +278,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
275 | Some(related_information.clone()) | 278 | Some(related_information.clone()) |
276 | }, | 279 | }, |
277 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, | 280 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, |
281 | data: None, | ||
278 | }; | 282 | }; |
279 | 283 | ||
280 | let main_diagnostic = | 284 | let main_diagnostic = |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 2680e5f08..049c583a4 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -1121,10 +1121,12 @@ pub(crate) fn publish_diagnostics( | |||
1121 | range: to_proto::range(&line_index, d.range), | 1121 | range: to_proto::range(&line_index, d.range), |
1122 | severity: Some(to_proto::diagnostic_severity(d.severity)), | 1122 | severity: Some(to_proto::diagnostic_severity(d.severity)), |
1123 | code: None, | 1123 | code: None, |
1124 | code_description: None, | ||
1124 | source: Some("rust-analyzer".to_string()), | 1125 | source: Some("rust-analyzer".to_string()), |
1125 | message: d.message, | 1126 | message: d.message, |
1126 | related_information: None, | 1127 | related_information: None, |
1127 | tags: if d.unused { Some(vec![DiagnosticTag::Unnecessary]) } else { None }, | 1128 | tags: if d.unused { Some(vec![DiagnosticTag::Unnecessary]) } else { None }, |
1129 | data: None, | ||
1128 | }) | 1130 | }) |
1129 | .collect(); | 1131 | .collect(); |
1130 | Ok(diagnostics) | 1132 | Ok(diagnostics) |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ed5292733..ff855fe1a 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -330,6 +330,12 @@ impl GlobalState { | |||
330 | .collect::<Vec<_>>(); | 330 | .collect::<Vec<_>>(); |
331 | 331 | ||
332 | self.update_file_notifications_on_threadpool(subscriptions); | 332 | self.update_file_notifications_on_threadpool(subscriptions); |
333 | |||
334 | // Refresh semantic tokens if the client supports it. | ||
335 | if self.config.semantic_tokens_refresh { | ||
336 | self.semantic_tokens_cache.lock().clear(); | ||
337 | self.send_request::<lsp_types::request::SemanticTokensRefesh>((), |_, _| ()); | ||
338 | } | ||
333 | } | 339 | } |
334 | 340 | ||
335 | if let Some(diagnostic_changes) = self.diagnostics.take_changes() { | 341 | if let Some(diagnostic_changes) = self.diagnostics.take_changes() { |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 24a658fc6..92b7c7b68 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -709,7 +709,16 @@ pub(crate) fn call_hierarchy_item( | |||
709 | let detail = target.description.clone(); | 709 | let detail = target.description.clone(); |
710 | let kind = symbol_kind(target.kind); | 710 | let kind = symbol_kind(target.kind); |
711 | let (uri, range, selection_range) = location_info(snap, target)?; | 711 | let (uri, range, selection_range) = location_info(snap, target)?; |
712 | Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) | 712 | Ok(lsp_types::CallHierarchyItem { |
713 | name, | ||
714 | kind, | ||
715 | tags: None, | ||
716 | detail, | ||
717 | uri, | ||
718 | range, | ||
719 | selection_range, | ||
720 | data: None, | ||
721 | }) | ||
713 | } | 722 | } |
714 | 723 | ||
715 | pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind { | 724 | pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind { |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index 4f9a7a6e8..065035fe6 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -289,11 +289,19 @@ fn _replace_children( | |||
289 | with_children(parent, new_children) | 289 | with_children(parent, new_children) |
290 | } | 290 | } |
291 | 291 | ||
292 | #[derive(Debug, PartialEq, Eq, Hash)] | ||
293 | enum InsertPos { | ||
294 | FirstChildOf(SyntaxNode), | ||
295 | // Before(SyntaxElement), | ||
296 | After(SyntaxElement), | ||
297 | } | ||
298 | |||
292 | #[derive(Default)] | 299 | #[derive(Default)] |
293 | pub struct SyntaxRewriter<'a> { | 300 | pub struct SyntaxRewriter<'a> { |
294 | f: Option<Box<dyn Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a>>, | 301 | f: Option<Box<dyn Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a>>, |
295 | //FIXME: add debug_assertions that all elements are in fact from the same file. | 302 | //FIXME: add debug_assertions that all elements are in fact from the same file. |
296 | replacements: FxHashMap<SyntaxElement, Replacement>, | 303 | replacements: FxHashMap<SyntaxElement, Replacement>, |
304 | insertions: IndexMap<InsertPos, Vec<SyntaxElement>>, | ||
297 | } | 305 | } |
298 | 306 | ||
299 | impl fmt::Debug for SyntaxRewriter<'_> { | 307 | impl fmt::Debug for SyntaxRewriter<'_> { |
@@ -304,13 +312,96 @@ impl fmt::Debug for SyntaxRewriter<'_> { | |||
304 | 312 | ||
305 | impl<'a> SyntaxRewriter<'a> { | 313 | impl<'a> SyntaxRewriter<'a> { |
306 | pub fn from_fn(f: impl Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a) -> SyntaxRewriter<'a> { | 314 | pub fn from_fn(f: impl Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a) -> SyntaxRewriter<'a> { |
307 | SyntaxRewriter { f: Some(Box::new(f)), replacements: FxHashMap::default() } | 315 | SyntaxRewriter { |
316 | f: Some(Box::new(f)), | ||
317 | replacements: FxHashMap::default(), | ||
318 | insertions: IndexMap::default(), | ||
319 | } | ||
308 | } | 320 | } |
309 | pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) { | 321 | pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) { |
310 | let what = what.clone().into(); | 322 | let what = what.clone().into(); |
311 | let replacement = Replacement::Delete; | 323 | let replacement = Replacement::Delete; |
312 | self.replacements.insert(what, replacement); | 324 | self.replacements.insert(what, replacement); |
313 | } | 325 | } |
326 | pub fn insert_before<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>( | ||
327 | &mut self, | ||
328 | before: &T, | ||
329 | what: &U, | ||
330 | ) { | ||
331 | let before = before.clone().into(); | ||
332 | let pos = match before.prev_sibling_or_token() { | ||
333 | Some(sibling) => InsertPos::After(sibling), | ||
334 | None => match before.parent() { | ||
335 | Some(parent) => InsertPos::FirstChildOf(parent), | ||
336 | None => return, | ||
337 | }, | ||
338 | }; | ||
339 | self.insertions.entry(pos).or_insert_with(Vec::new).push(what.clone().into()); | ||
340 | } | ||
341 | pub fn insert_after<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>( | ||
342 | &mut self, | ||
343 | after: &T, | ||
344 | what: &U, | ||
345 | ) { | ||
346 | self.insertions | ||
347 | .entry(InsertPos::After(after.clone().into())) | ||
348 | .or_insert_with(Vec::new) | ||
349 | .push(what.clone().into()); | ||
350 | } | ||
351 | pub fn insert_as_first_child<T: Clone + Into<SyntaxNode>, U: Clone + Into<SyntaxElement>>( | ||
352 | &mut self, | ||
353 | parent: &T, | ||
354 | what: &U, | ||
355 | ) { | ||
356 | self.insertions | ||
357 | .entry(InsertPos::FirstChildOf(parent.clone().into())) | ||
358 | .or_insert_with(Vec::new) | ||
359 | .push(what.clone().into()); | ||
360 | } | ||
361 | pub fn insert_many_before< | ||
362 | T: Clone + Into<SyntaxElement>, | ||
363 | U: IntoIterator<Item = SyntaxElement>, | ||
364 | >( | ||
365 | &mut self, | ||
366 | before: &T, | ||
367 | what: U, | ||
368 | ) { | ||
369 | let before = before.clone().into(); | ||
370 | let pos = match before.prev_sibling_or_token() { | ||
371 | Some(sibling) => InsertPos::After(sibling), | ||
372 | None => match before.parent() { | ||
373 | Some(parent) => InsertPos::FirstChildOf(parent), | ||
374 | None => return, | ||
375 | }, | ||
376 | }; | ||
377 | self.insertions.entry(pos).or_insert_with(Vec::new).extend(what); | ||
378 | } | ||
379 | pub fn insert_many_after< | ||
380 | T: Clone + Into<SyntaxElement>, | ||
381 | U: IntoIterator<Item = SyntaxElement>, | ||
382 | >( | ||
383 | &mut self, | ||
384 | after: &T, | ||
385 | what: U, | ||
386 | ) { | ||
387 | self.insertions | ||
388 | .entry(InsertPos::After(after.clone().into())) | ||
389 | .or_insert_with(Vec::new) | ||
390 | .extend(what); | ||
391 | } | ||
392 | pub fn insert_many_as_first_children< | ||
393 | T: Clone + Into<SyntaxNode>, | ||
394 | U: IntoIterator<Item = SyntaxElement>, | ||
395 | >( | ||
396 | &mut self, | ||
397 | parent: &T, | ||
398 | what: U, | ||
399 | ) { | ||
400 | self.insertions | ||
401 | .entry(InsertPos::FirstChildOf(parent.clone().into())) | ||
402 | .or_insert_with(Vec::new) | ||
403 | .extend(what) | ||
404 | } | ||
314 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { | 405 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { |
315 | let what = what.clone().into(); | 406 | let what = what.clone().into(); |
316 | let replacement = Replacement::Single(with.clone().into()); | 407 | let replacement = Replacement::Single(with.clone().into()); |
@@ -330,7 +421,7 @@ impl<'a> SyntaxRewriter<'a> { | |||
330 | } | 421 | } |
331 | 422 | ||
332 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { | 423 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { |
333 | if self.f.is_none() && self.replacements.is_empty() { | 424 | if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() { |
334 | return node.clone(); | 425 | return node.clone(); |
335 | } | 426 | } |
336 | self.rewrite_children(node) | 427 | self.rewrite_children(node) |
@@ -346,14 +437,22 @@ impl<'a> SyntaxRewriter<'a> { | |||
346 | /// | 437 | /// |
347 | /// Returns `None` when there are no replacements. | 438 | /// Returns `None` when there are no replacements. |
348 | pub fn rewrite_root(&self) -> Option<SyntaxNode> { | 439 | pub fn rewrite_root(&self) -> Option<SyntaxNode> { |
440 | fn element_to_node_or_parent(element: &SyntaxElement) -> SyntaxNode { | ||
441 | match element { | ||
442 | SyntaxElement::Node(it) => it.clone(), | ||
443 | SyntaxElement::Token(it) => it.parent(), | ||
444 | } | ||
445 | } | ||
446 | |||
349 | assert!(self.f.is_none()); | 447 | assert!(self.f.is_none()); |
350 | self.replacements | 448 | self.replacements |
351 | .keys() | 449 | .keys() |
352 | .map(|element| match element { | 450 | .map(element_to_node_or_parent) |
353 | SyntaxElement::Node(it) => it.clone(), | 451 | .chain(self.insertions.keys().map(|pos| match pos { |
354 | SyntaxElement::Token(it) => it.parent(), | 452 | InsertPos::FirstChildOf(it) => it.clone(), |
355 | }) | 453 | InsertPos::After(it) => element_to_node_or_parent(it), |
356 | // If we only have one replacement, we must return its parent node, since `rewrite` does | 454 | })) |
455 | // If we only have one replacement/insertion, we must return its parent node, since `rewrite` does | ||
357 | // not replace the node passed to it. | 456 | // not replace the node passed to it. |
358 | .map(|it| it.parent().unwrap_or(it)) | 457 | .map(|it| it.parent().unwrap_or(it)) |
359 | .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) | 458 | .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) |
@@ -367,9 +466,16 @@ impl<'a> SyntaxRewriter<'a> { | |||
367 | self.replacements.get(element).cloned() | 466 | self.replacements.get(element).cloned() |
368 | } | 467 | } |
369 | 468 | ||
469 | fn insertions(&self, pos: &InsertPos) -> Option<impl Iterator<Item = SyntaxElement> + '_> { | ||
470 | self.insertions.get(pos).map(|insertions| insertions.iter().cloned()) | ||
471 | } | ||
472 | |||
370 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { | 473 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { |
371 | // FIXME: this could be made much faster. | 474 | // FIXME: this could be made much faster. |
372 | let mut new_children = Vec::new(); | 475 | let mut new_children = Vec::new(); |
476 | if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) { | ||
477 | new_children.extend(elements.map(element_to_green)); | ||
478 | } | ||
373 | for child in node.children_with_tokens() { | 479 | for child in node.children_with_tokens() { |
374 | self.rewrite_self(&mut new_children, &child); | 480 | self.rewrite_self(&mut new_children, &child); |
375 | } | 481 | } |
@@ -383,34 +489,45 @@ impl<'a> SyntaxRewriter<'a> { | |||
383 | ) { | 489 | ) { |
384 | if let Some(replacement) = self.replacement(&element) { | 490 | if let Some(replacement) = self.replacement(&element) { |
385 | match replacement { | 491 | match replacement { |
386 | Replacement::Single(NodeOrToken::Node(it)) => { | 492 | Replacement::Single(element) => acc.push(element_to_green(element)), |
387 | acc.push(NodeOrToken::Node(it.green().clone())) | ||
388 | } | ||
389 | Replacement::Single(NodeOrToken::Token(it)) => { | ||
390 | acc.push(NodeOrToken::Token(it.green().clone())) | ||
391 | } | ||
392 | Replacement::Many(replacements) => { | 493 | Replacement::Many(replacements) => { |
393 | acc.extend(replacements.iter().map(|it| match it { | 494 | acc.extend(replacements.into_iter().map(element_to_green)) |
394 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), | ||
395 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | ||
396 | })) | ||
397 | } | 495 | } |
398 | Replacement::Delete => (), | 496 | Replacement::Delete => (), |
399 | }; | 497 | }; |
400 | return; | 498 | } else { |
499 | match element { | ||
500 | NodeOrToken::Token(it) => acc.push(NodeOrToken::Token(it.green().clone())), | ||
501 | NodeOrToken::Node(it) => { | ||
502 | acc.push(NodeOrToken::Node(self.rewrite_children(it).green().clone())); | ||
503 | } | ||
504 | } | ||
505 | } | ||
506 | if let Some(elements) = self.insertions(&InsertPos::After(element.clone())) { | ||
507 | acc.extend(elements.map(element_to_green)); | ||
401 | } | 508 | } |
402 | let res = match element { | 509 | } |
403 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | 510 | } |
404 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), | 511 | |
405 | }; | 512 | fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { |
406 | acc.push(res) | 513 | match element { |
514 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), | ||
515 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | ||
407 | } | 516 | } |
408 | } | 517 | } |
409 | 518 | ||
410 | impl ops::AddAssign for SyntaxRewriter<'_> { | 519 | impl ops::AddAssign for SyntaxRewriter<'_> { |
411 | fn add_assign(&mut self, rhs: SyntaxRewriter) { | 520 | fn add_assign(&mut self, rhs: SyntaxRewriter) { |
412 | assert!(rhs.f.is_none()); | 521 | assert!(rhs.f.is_none()); |
413 | self.replacements.extend(rhs.replacements) | 522 | self.replacements.extend(rhs.replacements); |
523 | for (pos, insertions) in rhs.insertions.into_iter() { | ||
524 | match self.insertions.entry(pos) { | ||
525 | indexmap::map::Entry::Occupied(mut occupied) => { | ||
526 | occupied.get_mut().extend(insertions) | ||
527 | } | ||
528 | indexmap::map::Entry::Vacant(vacant) => drop(vacant.insert(insertions)), | ||
529 | } | ||
530 | } | ||
414 | } | 531 | } |
415 | } | 532 | } |
416 | 533 | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast b/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast new file mode 100644 index 000000000..c204f0e2d --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rast | |||
@@ -0,0 +1,54 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "struct" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "B" | ||
7 | [email protected] | ||
8 | [email protected] "(" | ||
9 | [email protected] | ||
10 | [email protected] | ||
11 | [email protected] "pub" | ||
12 | [email protected] " " | ||
13 | [email protected] | ||
14 | [email protected] "(" | ||
15 | [email protected] | ||
16 | [email protected] | ||
17 | [email protected] | ||
18 | [email protected] | ||
19 | [email protected] "super" | ||
20 | [email protected] "::" | ||
21 | [email protected] | ||
22 | [email protected] | ||
23 | [email protected] "A" | ||
24 | [email protected] ")" | ||
25 | [email protected] ")" | ||
26 | [email protected] ";" | ||
27 | [email protected] "\n" | ||
28 | [email protected] | ||
29 | [email protected] "struct" | ||
30 | [email protected] " " | ||
31 | [email protected] | ||
32 | [email protected] "B" | ||
33 | [email protected] | ||
34 | [email protected] "(" | ||
35 | [email protected] | ||
36 | [email protected] | ||
37 | [email protected] "pub" | ||
38 | [email protected] " " | ||
39 | [email protected] | ||
40 | [email protected] "(" | ||
41 | [email protected] | ||
42 | [email protected] | ||
43 | [email protected] | ||
44 | [email protected] | ||
45 | [email protected] "crate" | ||
46 | [email protected] "::" | ||
47 | [email protected] | ||
48 | [email protected] | ||
49 | [email protected] "A" | ||
50 | [email protected] "," | ||
51 | [email protected] ")" | ||
52 | [email protected] ")" | ||
53 | [email protected] ";" | ||
54 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rs b/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rs new file mode 100644 index 000000000..d4c163822 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0153_pub_parens_typepath.rs | |||
@@ -0,0 +1,2 @@ | |||
1 | struct B(pub (super::A)); | ||
2 | struct B(pub (crate::A,)); | ||