diff options
-rw-r--r-- | .github/workflows/ci.yaml | 35 | ||||
-rw-r--r-- | Cargo.lock | 12 | ||||
-rw-r--r-- | crates/ide_assists/src/assist_context.rs | 39 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/extract_function.rs | 33 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/reorder_impl.rs | 40 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/qualified_path.rs | 22 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 208 | ||||
-rw-r--r-- | crates/proc_macro_api/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/proc_macro_api/src/version.rs | 2 | ||||
-rw-r--r-- | docs/user/manual.adoc | 35 | ||||
-rw-r--r-- | xtask/src/dist.rs | 1 | ||||
-rw-r--r-- | xtask/src/flags.rs | 5 | ||||
-rw-r--r-- | xtask/src/main.rs | 2 | ||||
-rw-r--r-- | xtask/src/pre_cache.rs | 79 |
14 files changed, 241 insertions, 274 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0f68b234c..63518e67f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml | |||
@@ -42,14 +42,6 @@ jobs: | |||
42 | if: matrix.os == 'windows-latest' | 42 | if: matrix.os == 'windows-latest' |
43 | run: Rename-Item C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc.old | 43 | run: Rename-Item C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc.old |
44 | 44 | ||
45 | # Work around https://github.com/actions/cache/issues/403 by using GNU tar | ||
46 | # instead of BSD tar. | ||
47 | - name: Install GNU tar | ||
48 | if: matrix.os == 'macos-latest' | ||
49 | run: | | ||
50 | brew install gnu-tar | ||
51 | echo PATH="/usr/local/opt/gnu-tar/libexec/gnubin:$PATH" >> $GITHUB_ENV | ||
52 | |||
53 | - name: Install Rust toolchain | 45 | - name: Install Rust toolchain |
54 | uses: actions-rs/toolchain@v1 | 46 | uses: actions-rs/toolchain@v1 |
55 | with: | 47 | with: |
@@ -58,19 +50,8 @@ jobs: | |||
58 | override: true | 50 | override: true |
59 | components: rustfmt, rust-src | 51 | components: rustfmt, rust-src |
60 | 52 | ||
61 | - name: Cache cargo directories | 53 | - name: Cache Dependencies |
62 | uses: actions/cache@v2 | 54 | uses: Swatinem/rust-cache@ce325b60658c1b38465c06cc965b79baf32c1e72 |
63 | with: | ||
64 | path: | | ||
65 | ~/.cargo/registry | ||
66 | ~/.cargo/git | ||
67 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | ||
68 | |||
69 | - name: Cache cargo target dir | ||
70 | uses: actions/cache@v2 | ||
71 | with: | ||
72 | path: target | ||
73 | key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} | ||
74 | 55 | ||
75 | - name: Compile | 56 | - name: Compile |
76 | run: cargo test --no-run --locked | 57 | run: cargo test --no-run --locked |
@@ -78,9 +59,6 @@ jobs: | |||
78 | - name: Test | 59 | - name: Test |
79 | run: cargo test -- --nocapture | 60 | run: cargo test -- --nocapture |
80 | 61 | ||
81 | - name: Prepare cache | ||
82 | run: cargo xtask pre-cache | ||
83 | |||
84 | # Weird targets to catch non-portable code | 62 | # Weird targets to catch non-portable code |
85 | rust-cross: | 63 | rust-cross: |
86 | name: Rust Cross | 64 | name: Rust Cross |
@@ -103,13 +81,8 @@ jobs: | |||
103 | - name: Install Rust targets | 81 | - name: Install Rust targets |
104 | run: rustup target add ${{ env.targets }} | 82 | run: rustup target add ${{ env.targets }} |
105 | 83 | ||
106 | - name: Cache cargo directories | 84 | - name: Cache Dependencies |
107 | uses: actions/cache@v2 | 85 | uses: Swatinem/rust-cache@ce325b60658c1b38465c06cc965b79baf32c1e72 |
108 | with: | ||
109 | path: | | ||
110 | ~/.cargo/registry | ||
111 | ~/.cargo/git | ||
112 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | ||
113 | 86 | ||
114 | - name: Check | 87 | - name: Check |
115 | run: | | 88 | run: | |
diff --git a/Cargo.lock b/Cargo.lock index 1cfd704f6..0ca0b7b63 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -903,16 +903,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
903 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" | 903 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" |
904 | 904 | ||
905 | [[package]] | 905 | [[package]] |
906 | name = "memmap" | ||
907 | version = "0.7.0" | ||
908 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
909 | checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" | ||
910 | dependencies = [ | ||
911 | "libc", | ||
912 | "winapi", | ||
913 | ] | ||
914 | |||
915 | [[package]] | ||
916 | name = "memmap2" | 906 | name = "memmap2" |
917 | version = "0.2.2" | 907 | version = "0.2.2" |
918 | source = "registry+https://github.com/rust-lang/crates.io-index" | 908 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1174,7 +1164,7 @@ dependencies = [ | |||
1174 | "crossbeam-channel", | 1164 | "crossbeam-channel", |
1175 | "jod-thread", | 1165 | "jod-thread", |
1176 | "log", | 1166 | "log", |
1177 | "memmap", | 1167 | "memmap2", |
1178 | "object", | 1168 | "object", |
1179 | "profile", | 1169 | "profile", |
1180 | "serde", | 1170 | "serde", |
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 19e9f179e..112939948 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs | |||
@@ -187,7 +187,29 @@ pub(crate) struct AssistBuilder { | |||
187 | source_change: SourceChange, | 187 | source_change: SourceChange, |
188 | 188 | ||
189 | /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. | 189 | /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. |
190 | mutated_tree: Option<(SyntaxNode, SyntaxNode)>, | 190 | mutated_tree: Option<TreeMutator>, |
191 | } | ||
192 | |||
193 | pub(crate) struct TreeMutator { | ||
194 | immutable: SyntaxNode, | ||
195 | mutable_clone: SyntaxNode, | ||
196 | } | ||
197 | |||
198 | impl TreeMutator { | ||
199 | pub(crate) fn new(immutable: &SyntaxNode) -> TreeMutator { | ||
200 | let immutable = immutable.ancestors().last().unwrap(); | ||
201 | let mutable_clone = immutable.clone_for_update(); | ||
202 | TreeMutator { immutable, mutable_clone } | ||
203 | } | ||
204 | |||
205 | pub(crate) fn make_mut<N: AstNode>(&self, node: &N) -> N { | ||
206 | N::cast(self.make_syntax_mut(node.syntax())).unwrap() | ||
207 | } | ||
208 | |||
209 | pub(crate) fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { | ||
210 | let ptr = SyntaxNodePtr::new(node); | ||
211 | ptr.to_node(&self.mutable_clone) | ||
212 | } | ||
191 | } | 213 | } |
192 | 214 | ||
193 | impl AssistBuilder { | 215 | impl AssistBuilder { |
@@ -206,8 +228,8 @@ impl AssistBuilder { | |||
206 | } | 228 | } |
207 | 229 | ||
208 | fn commit(&mut self) { | 230 | fn commit(&mut self) { |
209 | if let Some((old, new)) = self.mutated_tree.take() { | 231 | if let Some(tm) = self.mutated_tree.take() { |
210 | algo::diff(&old, &new).into_text_edit(&mut self.edit) | 232 | algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) |
211 | } | 233 | } |
212 | 234 | ||
213 | let edit = mem::take(&mut self.edit).finish(); | 235 | let edit = mem::take(&mut self.edit).finish(); |
@@ -230,16 +252,7 @@ impl AssistBuilder { | |||
230 | /// phase, and then get their mutable couterparts using `make_mut` in the | 252 | /// phase, and then get their mutable couterparts using `make_mut` in the |
231 | /// mutable state. | 253 | /// mutable state. |
232 | pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { | 254 | pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { |
233 | let root = &self | 255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) |
234 | .mutated_tree | ||
235 | .get_or_insert_with(|| { | ||
236 | let immutable = node.ancestors().last().unwrap(); | ||
237 | let mutable = immutable.clone_for_update(); | ||
238 | (immutable, mutable) | ||
239 | }) | ||
240 | .1; | ||
241 | let ptr = SyntaxNodePtr::new(&&node); | ||
242 | ptr.to_node(root) | ||
243 | } | 256 | } |
244 | 257 | ||
245 | /// Remove specified `range` of text. | 258 | /// Remove specified `range` of text. |
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index b30652a9d..93b28370c 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -16,12 +16,13 @@ use syntax::{ | |||
16 | edit::{AstNodeEdit, IndentLevel}, | 16 | edit::{AstNodeEdit, IndentLevel}, |
17 | AstNode, | 17 | AstNode, |
18 | }, | 18 | }, |
19 | ted, | ||
19 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, | 20 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, |
20 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, | 21 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, |
21 | }; | 22 | }; |
22 | 23 | ||
23 | use crate::{ | 24 | use crate::{ |
24 | assist_context::{AssistContext, Assists}, | 25 | assist_context::{AssistContext, Assists, TreeMutator}, |
25 | AssistId, | 26 | AssistId, |
26 | }; | 27 | }; |
27 | 28 | ||
@@ -1366,7 +1367,10 @@ fn rewrite_body_segment( | |||
1366 | 1367 | ||
1367 | /// change all usages to account for added `&`/`&mut` for some params | 1368 | /// change all usages to account for added `&`/`&mut` for some params |
1368 | fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { | 1369 | fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { |
1369 | let mut rewriter = SyntaxRewriter::default(); | 1370 | let mut usages_for_param: Vec<(&Param, Vec<ast::Expr>)> = Vec::new(); |
1371 | |||
1372 | let tm = TreeMutator::new(syntax); | ||
1373 | |||
1370 | for param in params { | 1374 | for param in params { |
1371 | if !param.kind().is_ref() { | 1375 | if !param.kind().is_ref() { |
1372 | continue; | 1376 | continue; |
@@ -1376,30 +1380,39 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) | |||
1376 | let usages = usages | 1380 | let usages = usages |
1377 | .iter() | 1381 | .iter() |
1378 | .filter(|reference| syntax.text_range().contains_range(reference.range)) | 1382 | .filter(|reference| syntax.text_range().contains_range(reference.range)) |
1379 | .filter_map(|reference| path_element_of_reference(syntax, reference)); | 1383 | .filter_map(|reference| path_element_of_reference(syntax, reference)) |
1380 | for path in usages { | 1384 | .map(|expr| tm.make_mut(&expr)); |
1381 | match path.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { | 1385 | |
1386 | usages_for_param.push((param, usages.collect())); | ||
1387 | } | ||
1388 | |||
1389 | let res = tm.make_syntax_mut(syntax); | ||
1390 | |||
1391 | for (param, usages) in usages_for_param { | ||
1392 | for usage in usages { | ||
1393 | match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { | ||
1382 | Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { | 1394 | Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { |
1383 | // do nothing | 1395 | // do nothing |
1384 | } | 1396 | } |
1385 | Some(ast::Expr::RefExpr(node)) | 1397 | Some(ast::Expr::RefExpr(node)) |
1386 | if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => | 1398 | if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => |
1387 | { | 1399 | { |
1388 | rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); | 1400 | ted::replace(node.syntax(), node.expr().unwrap().syntax()); |
1389 | } | 1401 | } |
1390 | Some(ast::Expr::RefExpr(node)) | 1402 | Some(ast::Expr::RefExpr(node)) |
1391 | if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => | 1403 | if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => |
1392 | { | 1404 | { |
1393 | rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); | 1405 | ted::replace(node.syntax(), node.expr().unwrap().syntax()); |
1394 | } | 1406 | } |
1395 | Some(_) | None => { | 1407 | Some(_) | None => { |
1396 | rewriter.replace_ast(&path, &make::expr_prefix(T![*], path.clone())); | 1408 | let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); |
1409 | ted::replace(usage.syntax(), p.syntax()) | ||
1397 | } | 1410 | } |
1398 | }; | 1411 | } |
1399 | } | 1412 | } |
1400 | } | 1413 | } |
1401 | 1414 | ||
1402 | rewriter.rewrite(syntax) | 1415 | res |
1403 | } | 1416 | } |
1404 | 1417 | ||
1405 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { | 1418 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { |
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs index 72d889248..54a9a468e 100644 --- a/crates/ide_assists/src/handlers/reorder_impl.rs +++ b/crates/ide_assists/src/handlers/reorder_impl.rs | |||
@@ -79,9 +79,12 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
79 | "Sort methods", | 79 | "Sort methods", |
80 | target, | 80 | target, |
81 | |builder| { | 81 | |builder| { |
82 | methods.into_iter().zip(sorted).for_each(|(old, new)| { | 82 | let methods = |
83 | ted::replace(builder.make_ast_mut(old).syntax(), new.clone_for_update().syntax()) | 83 | methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>(); |
84 | }); | 84 | methods |
85 | .into_iter() | ||
86 | .zip(sorted) | ||
87 | .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax())); | ||
85 | }, | 88 | }, |
86 | ) | 89 | ) |
87 | } | 90 | } |
@@ -160,7 +163,7 @@ $0impl Bar for Foo {} | |||
160 | } | 163 | } |
161 | 164 | ||
162 | #[test] | 165 | #[test] |
163 | fn reorder_impl_trait_methods() { | 166 | fn reorder_impl_trait_functions() { |
164 | check_assist( | 167 | check_assist( |
165 | reorder_impl, | 168 | reorder_impl, |
166 | r#" | 169 | r#" |
@@ -197,4 +200,33 @@ impl Bar for Foo { | |||
197 | "#, | 200 | "#, |
198 | ) | 201 | ) |
199 | } | 202 | } |
203 | |||
204 | #[test] | ||
205 | fn reorder_impl_trait_methods_uneven_ident_lengths() { | ||
206 | check_assist( | ||
207 | reorder_impl, | ||
208 | r#" | ||
209 | trait Bar { | ||
210 | fn foo(&mut self) {} | ||
211 | fn fooo(&mut self) {} | ||
212 | } | ||
213 | |||
214 | struct Foo; | ||
215 | impl Bar for Foo { | ||
216 | fn fooo(&mut self) {} | ||
217 | fn foo(&mut self) {$0} | ||
218 | }"#, | ||
219 | r#" | ||
220 | trait Bar { | ||
221 | fn foo(&mut self) {} | ||
222 | fn fooo(&mut self) {} | ||
223 | } | ||
224 | |||
225 | struct Foo; | ||
226 | impl Bar for Foo { | ||
227 | fn foo(&mut self) {} | ||
228 | fn fooo(&mut self) {} | ||
229 | }"#, | ||
230 | ) | ||
231 | } | ||
200 | } | 232 | } |
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index 969249df6..d8f23d1eb 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -737,28 +737,6 @@ fn f() {} | |||
737 | } | 737 | } |
738 | 738 | ||
739 | #[test] | 739 | #[test] |
740 | fn completes_function() { | ||
741 | check( | ||
742 | r#" | ||
743 | fn foo( | ||
744 | a: i32, | ||
745 | b: i32 | ||
746 | ) { | ||
747 | |||
748 | } | ||
749 | |||
750 | fn main() { | ||
751 | fo$0 | ||
752 | } | ||
753 | "#, | ||
754 | expect![[r#" | ||
755 | fn main() fn() | ||
756 | fn foo(…) fn(i32, i32) | ||
757 | "#]], | ||
758 | ); | ||
759 | } | ||
760 | |||
761 | #[test] | ||
762 | fn completes_self_enum() { | 740 | fn completes_self_enum() { |
763 | check( | 741 | check( |
764 | r#" | 742 | r#" |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index b005bd773..f3fcb712c 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -301,103 +301,108 @@ impl<'a> CompletionContext<'a> { | |||
301 | .find_map(ast::Impl::cast); | 301 | .find_map(ast::Impl::cast); |
302 | } | 302 | } |
303 | 303 | ||
304 | fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) { | ||
305 | let mut node = match self.token.parent() { | ||
306 | Some(it) => it, | ||
307 | None => return (None, None), | ||
308 | }; | ||
309 | loop { | ||
310 | break match_ast! { | ||
311 | match node { | ||
312 | ast::LetStmt(it) => { | ||
313 | cov_mark::hit!(expected_type_let_with_leading_char); | ||
314 | cov_mark::hit!(expected_type_let_without_leading_char); | ||
315 | let ty = it.pat() | ||
316 | .and_then(|pat| self.sema.type_of_pat(&pat)); | ||
317 | let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { | ||
318 | ident.name().map(NameOrNameRef::Name) | ||
319 | } else { | ||
320 | None | ||
321 | }; | ||
322 | |||
323 | (ty, name) | ||
324 | }, | ||
325 | ast::ArgList(_it) => { | ||
326 | cov_mark::hit!(expected_type_fn_param_with_leading_char); | ||
327 | cov_mark::hit!(expected_type_fn_param_without_leading_char); | ||
328 | ActiveParameter::at_token( | ||
329 | &self.sema, | ||
330 | self.token.clone(), | ||
331 | ).map(|ap| { | ||
332 | let name = ap.ident().map(NameOrNameRef::Name); | ||
333 | (Some(ap.ty), name) | ||
334 | }) | ||
335 | .unwrap_or((None, None)) | ||
336 | }, | ||
337 | ast::RecordExprFieldList(_it) => { | ||
338 | cov_mark::hit!(expected_type_struct_field_without_leading_char); | ||
339 | self.token.prev_sibling_or_token() | ||
340 | .and_then(|se| se.into_node()) | ||
341 | .and_then(|node| ast::RecordExprField::cast(node)) | ||
342 | .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf))) | ||
343 | .map(|(f, rf)|( | ||
344 | Some(f.0.ty(self.db)), | ||
345 | rf.field_name().map(NameOrNameRef::NameRef), | ||
346 | )) | ||
347 | .unwrap_or((None, None)) | ||
348 | }, | ||
349 | ast::RecordExprField(it) => { | ||
350 | cov_mark::hit!(expected_type_struct_field_with_leading_char); | ||
351 | self.sema | ||
352 | .resolve_record_field(&it) | ||
353 | .map(|f|( | ||
354 | Some(f.0.ty(self.db)), | ||
355 | it.field_name().map(NameOrNameRef::NameRef), | ||
356 | )) | ||
357 | .unwrap_or((None, None)) | ||
358 | }, | ||
359 | ast::MatchExpr(it) => { | ||
360 | cov_mark::hit!(expected_type_match_arm_without_leading_char); | ||
361 | let ty = it.expr() | ||
362 | .and_then(|e| self.sema.type_of_expr(&e)); | ||
363 | (ty, None) | ||
364 | }, | ||
365 | ast::IfExpr(it) => { | ||
366 | cov_mark::hit!(expected_type_if_let_without_leading_char); | ||
367 | let ty = it.condition() | ||
368 | .and_then(|cond| cond.expr()) | ||
369 | .and_then(|e| self.sema.type_of_expr(&e)); | ||
370 | (ty, None) | ||
371 | }, | ||
372 | ast::IdentPat(it) => { | ||
373 | cov_mark::hit!(expected_type_if_let_with_leading_char); | ||
374 | cov_mark::hit!(expected_type_match_arm_with_leading_char); | ||
375 | let ty = self.sema.type_of_pat(&ast::Pat::from(it)); | ||
376 | (ty, None) | ||
377 | }, | ||
378 | ast::Fn(it) => { | ||
379 | cov_mark::hit!(expected_type_fn_ret_with_leading_char); | ||
380 | cov_mark::hit!(expected_type_fn_ret_without_leading_char); | ||
381 | let def = self.sema.to_def(&it); | ||
382 | (def.map(|def| def.ret_type(self.db)), None) | ||
383 | }, | ||
384 | ast::Stmt(it) => (None, None), | ||
385 | _ => { | ||
386 | match node.parent() { | ||
387 | Some(n) => { | ||
388 | node = n; | ||
389 | continue; | ||
390 | }, | ||
391 | None => (None, None), | ||
392 | } | ||
393 | }, | ||
394 | } | ||
395 | }; | ||
396 | } | ||
397 | } | ||
398 | |||
304 | fn fill( | 399 | fn fill( |
305 | &mut self, | 400 | &mut self, |
306 | original_file: &SyntaxNode, | 401 | original_file: &SyntaxNode, |
307 | file_with_fake_ident: SyntaxNode, | 402 | file_with_fake_ident: SyntaxNode, |
308 | offset: TextSize, | 403 | offset: TextSize, |
309 | ) { | 404 | ) { |
310 | let (expected_type, expected_name) = { | 405 | let (expected_type, expected_name) = self.expected_type_and_name(); |
311 | let mut node = match self.token.parent() { | ||
312 | Some(it) => it, | ||
313 | None => return, | ||
314 | }; | ||
315 | loop { | ||
316 | break match_ast! { | ||
317 | match node { | ||
318 | ast::LetStmt(it) => { | ||
319 | cov_mark::hit!(expected_type_let_with_leading_char); | ||
320 | cov_mark::hit!(expected_type_let_without_leading_char); | ||
321 | let ty = it.pat() | ||
322 | .and_then(|pat| self.sema.type_of_pat(&pat)); | ||
323 | let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { | ||
324 | ident.name().map(NameOrNameRef::Name) | ||
325 | } else { | ||
326 | None | ||
327 | }; | ||
328 | |||
329 | (ty, name) | ||
330 | }, | ||
331 | ast::ArgList(_it) => { | ||
332 | cov_mark::hit!(expected_type_fn_param_with_leading_char); | ||
333 | cov_mark::hit!(expected_type_fn_param_without_leading_char); | ||
334 | ActiveParameter::at_token( | ||
335 | &self.sema, | ||
336 | self.token.clone(), | ||
337 | ).map(|ap| { | ||
338 | let name = ap.ident().map(NameOrNameRef::Name); | ||
339 | (Some(ap.ty), name) | ||
340 | }) | ||
341 | .unwrap_or((None, None)) | ||
342 | }, | ||
343 | ast::RecordExprFieldList(_it) => { | ||
344 | cov_mark::hit!(expected_type_struct_field_without_leading_char); | ||
345 | self.token.prev_sibling_or_token() | ||
346 | .and_then(|se| se.into_node()) | ||
347 | .and_then(|node| ast::RecordExprField::cast(node)) | ||
348 | .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf))) | ||
349 | .map(|(f, rf)|( | ||
350 | Some(f.0.ty(self.db)), | ||
351 | rf.field_name().map(NameOrNameRef::NameRef), | ||
352 | )) | ||
353 | .unwrap_or((None, None)) | ||
354 | }, | ||
355 | ast::RecordExprField(it) => { | ||
356 | cov_mark::hit!(expected_type_struct_field_with_leading_char); | ||
357 | self.sema | ||
358 | .resolve_record_field(&it) | ||
359 | .map(|f|( | ||
360 | Some(f.0.ty(self.db)), | ||
361 | it.field_name().map(NameOrNameRef::NameRef), | ||
362 | )) | ||
363 | .unwrap_or((None, None)) | ||
364 | }, | ||
365 | ast::MatchExpr(it) => { | ||
366 | cov_mark::hit!(expected_type_match_arm_without_leading_char); | ||
367 | let ty = it.expr() | ||
368 | .and_then(|e| self.sema.type_of_expr(&e)); | ||
369 | |||
370 | (ty, None) | ||
371 | }, | ||
372 | ast::IdentPat(it) => { | ||
373 | cov_mark::hit!(expected_type_if_let_with_leading_char); | ||
374 | cov_mark::hit!(expected_type_match_arm_with_leading_char); | ||
375 | let ty = self.sema.type_of_pat(&ast::Pat::from(it)); | ||
376 | |||
377 | (ty, None) | ||
378 | }, | ||
379 | ast::Fn(_it) => { | ||
380 | cov_mark::hit!(expected_type_fn_ret_with_leading_char); | ||
381 | cov_mark::hit!(expected_type_fn_ret_without_leading_char); | ||
382 | let ty = self.token.ancestors() | ||
383 | .find_map(|ancestor| ast::Expr::cast(ancestor)) | ||
384 | .and_then(|expr| self.sema.type_of_expr(&expr)); | ||
385 | |||
386 | (ty, None) | ||
387 | }, | ||
388 | _ => { | ||
389 | match node.parent() { | ||
390 | Some(n) => { | ||
391 | node = n; | ||
392 | continue; | ||
393 | }, | ||
394 | None => (None, None), | ||
395 | } | ||
396 | }, | ||
397 | } | ||
398 | }; | ||
399 | } | ||
400 | }; | ||
401 | self.expected_type = expected_type; | 406 | self.expected_type = expected_type; |
402 | self.expected_name = expected_name; | 407 | self.expected_name = expected_name; |
403 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); | 408 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); |
@@ -802,6 +807,7 @@ fn foo() { | |||
802 | 807 | ||
803 | #[test] | 808 | #[test] |
804 | fn expected_type_if_let_without_leading_char() { | 809 | fn expected_type_if_let_without_leading_char() { |
810 | cov_mark::check!(expected_type_if_let_without_leading_char); | ||
805 | check_expected_type_and_name( | 811 | check_expected_type_and_name( |
806 | r#" | 812 | r#" |
807 | enum Foo { Bar, Baz, Quux } | 813 | enum Foo { Bar, Baz, Quux } |
@@ -811,8 +817,8 @@ fn foo() { | |||
811 | if let $0 = f { } | 817 | if let $0 = f { } |
812 | } | 818 | } |
813 | "#, | 819 | "#, |
814 | expect![[r#"ty: (), name: ?"#]], | 820 | expect![[r#"ty: Foo, name: ?"#]], |
815 | ) // FIXME should be `ty: u32, name: ?` | 821 | ) |
816 | } | 822 | } |
817 | 823 | ||
818 | #[test] | 824 | #[test] |
@@ -840,8 +846,8 @@ fn foo() -> u32 { | |||
840 | $0 | 846 | $0 |
841 | } | 847 | } |
842 | "#, | 848 | "#, |
843 | expect![[r#"ty: (), name: ?"#]], | 849 | expect![[r#"ty: u32, name: ?"#]], |
844 | ) // FIXME this should be `ty: u32, name: ?` | 850 | ) |
845 | } | 851 | } |
846 | 852 | ||
847 | #[test] | 853 | #[test] |
@@ -856,4 +862,16 @@ fn foo() -> u32 { | |||
856 | expect![[r#"ty: u32, name: ?"#]], | 862 | expect![[r#"ty: u32, name: ?"#]], |
857 | ) | 863 | ) |
858 | } | 864 | } |
865 | |||
866 | #[test] | ||
867 | fn expected_type_fn_ret_fn_ref_fully_typed() { | ||
868 | check_expected_type_and_name( | ||
869 | r#" | ||
870 | fn foo() -> u32 { | ||
871 | foo$0 | ||
872 | } | ||
873 | "#, | ||
874 | expect![[r#"ty: u32, name: ?"#]], | ||
875 | ) | ||
876 | } | ||
859 | } | 877 | } |
diff --git a/crates/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml index 1ba1e4abd..2ce5eeedd 100644 --- a/crates/proc_macro_api/Cargo.toml +++ b/crates/proc_macro_api/Cargo.toml | |||
@@ -15,7 +15,7 @@ serde_json = { version = "1.0", features = ["unbounded_depth"] } | |||
15 | log = "0.4.8" | 15 | log = "0.4.8" |
16 | crossbeam-channel = "0.5.0" | 16 | crossbeam-channel = "0.5.0" |
17 | jod-thread = "0.1.1" | 17 | jod-thread = "0.1.1" |
18 | memmap = "0.7.0" | 18 | memmap2 = "0.2.0" |
19 | object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] } | 19 | object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] } |
20 | snap = "1.0" | 20 | snap = "1.0" |
21 | 21 | ||
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs index dcf8fae8f..6dbac50b4 100644 --- a/crates/proc_macro_api/src/version.rs +++ b/crates/proc_macro_api/src/version.rs | |||
@@ -6,7 +6,7 @@ use std::{ | |||
6 | path::Path, | 6 | path::Path, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use memmap::Mmap; | 9 | use memmap2::Mmap; |
10 | use object::read::{File as BinaryFile, Object, ObjectSection}; | 10 | use object::read::{File as BinaryFile, Object, ObjectSection}; |
11 | use snap::read::FrameDecoder as SnapDecoder; | 11 | use snap::read::FrameDecoder as SnapDecoder; |
12 | 12 | ||
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 54195adb7..58722aaa3 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -611,6 +611,41 @@ For example, mutable bindings are underlined by default and you can override thi | |||
611 | } | 611 | } |
612 | ---- | 612 | ---- |
613 | 613 | ||
614 | Most themes doesn't support styling unsafe operations differently yet. You can fix this by adding overrides for the rules `operator.unsafe`, `function.unsafe`, and `method.unsafe`: | ||
615 | |||
616 | [source,jsonc] | ||
617 | ---- | ||
618 | { | ||
619 | "editor.semanticTokenColorCustomizations": { | ||
620 | "rules": { | ||
621 | "operator.unsafe": "#ff6600", | ||
622 | "function.unsafe": "#ff6600" | ||
623 | "method.unsafe": "#ff6600" | ||
624 | } | ||
625 | }, | ||
626 | } | ||
627 | ---- | ||
628 | |||
629 | In addition to the top-level rules you can specify overrides for specific themes. For example, if you wanted to use a darker text color on a specific light theme, you might write: | ||
630 | |||
631 | [source,jsonc] | ||
632 | ---- | ||
633 | { | ||
634 | "editor.semanticTokenColorCustomizations": { | ||
635 | "rules": { | ||
636 | "operator.unsafe": "#ff6600" | ||
637 | }, | ||
638 | "[Ayu Light]": { | ||
639 | "rules": { | ||
640 | "operator.unsafe": "#572300" | ||
641 | } | ||
642 | } | ||
643 | }, | ||
644 | } | ||
645 | ---- | ||
646 | |||
647 | Make sure you include the brackets around the theme name. For example, use `"[Ayu Light]"` to customize the theme Ayu Light. | ||
648 | |||
614 | ==== Special `when` clause context for keybindings. | 649 | ==== Special `when` clause context for keybindings. |
615 | You may use `inRustProject` context to configure keybindings for rust projects only. | 650 | You may use `inRustProject` context to configure keybindings for rust projects only. |
616 | For example: | 651 | For example: |
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs index 12a7fea1e..b6aa2f52a 100644 --- a/xtask/src/dist.rs +++ b/xtask/src/dist.rs | |||
@@ -66,6 +66,7 @@ fn dist_client(version: &str, release_tag: &str) -> Result<()> { | |||
66 | 66 | ||
67 | fn dist_server(release_channel: &str) -> Result<()> { | 67 | fn dist_server(release_channel: &str) -> Result<()> { |
68 | let _e = pushenv("RUST_ANALYZER_CHANNEL", release_channel); | 68 | let _e = pushenv("RUST_ANALYZER_CHANNEL", release_channel); |
69 | let _e = pushenv("CARGO_PROFILE_RELEASE_LTO", "true"); | ||
69 | let target = get_target(); | 70 | let target = get_target(); |
70 | if target.contains("-linux-gnu") || target.contains("-linux-musl") { | 71 | if target.contains("-linux-gnu") || target.contains("-linux-musl") { |
71 | env::set_var("CC", "clang"); | 72 | env::set_var("CC", "clang"); |
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs index f80a5dd16..69b3cb9c1 100644 --- a/xtask/src/flags.rs +++ b/xtask/src/flags.rs | |||
@@ -28,7 +28,6 @@ xflags::xflags! { | |||
28 | } | 28 | } |
29 | 29 | ||
30 | cmd fuzz-tests {} | 30 | cmd fuzz-tests {} |
31 | cmd pre-cache {} | ||
32 | 31 | ||
33 | cmd release { | 32 | cmd release { |
34 | optional --dry-run | 33 | optional --dry-run |
@@ -62,7 +61,6 @@ pub enum XtaskCmd { | |||
62 | Help(Help), | 61 | Help(Help), |
63 | Install(Install), | 62 | Install(Install), |
64 | FuzzTests(FuzzTests), | 63 | FuzzTests(FuzzTests), |
65 | PreCache(PreCache), | ||
66 | Release(Release), | 64 | Release(Release), |
67 | Promote(Promote), | 65 | Promote(Promote), |
68 | Dist(Dist), | 66 | Dist(Dist), |
@@ -88,9 +86,6 @@ pub struct Install { | |||
88 | pub struct FuzzTests; | 86 | pub struct FuzzTests; |
89 | 87 | ||
90 | #[derive(Debug)] | 88 | #[derive(Debug)] |
91 | pub struct PreCache; | ||
92 | |||
93 | #[derive(Debug)] | ||
94 | pub struct Release { | 89 | pub struct Release { |
95 | pub dry_run: bool, | 90 | pub dry_run: bool, |
96 | } | 91 | } |
diff --git a/xtask/src/main.rs b/xtask/src/main.rs index ce3353410..d0bef7b7a 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -18,7 +18,6 @@ mod install; | |||
18 | mod release; | 18 | mod release; |
19 | mod dist; | 19 | mod dist; |
20 | mod metrics; | 20 | mod metrics; |
21 | mod pre_cache; | ||
22 | 21 | ||
23 | use anyhow::{bail, Result}; | 22 | use anyhow::{bail, Result}; |
24 | use std::{ | 23 | use std::{ |
@@ -39,7 +38,6 @@ fn main() -> Result<()> { | |||
39 | } | 38 | } |
40 | flags::XtaskCmd::Install(cmd) => cmd.run(), | 39 | flags::XtaskCmd::Install(cmd) => cmd.run(), |
41 | flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), | 40 | flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), |
42 | flags::XtaskCmd::PreCache(cmd) => cmd.run(), | ||
43 | flags::XtaskCmd::Release(cmd) => cmd.run(), | 41 | flags::XtaskCmd::Release(cmd) => cmd.run(), |
44 | flags::XtaskCmd::Promote(cmd) => cmd.run(), | 42 | flags::XtaskCmd::Promote(cmd) => cmd.run(), |
45 | flags::XtaskCmd::Dist(cmd) => cmd.run(), | 43 | flags::XtaskCmd::Dist(cmd) => cmd.run(), |
diff --git a/xtask/src/pre_cache.rs b/xtask/src/pre_cache.rs deleted file mode 100644 index b456224fd..000000000 --- a/xtask/src/pre_cache.rs +++ /dev/null | |||
@@ -1,79 +0,0 @@ | |||
1 | use std::{ | ||
2 | fs::FileType, | ||
3 | path::{Path, PathBuf}, | ||
4 | }; | ||
5 | |||
6 | use anyhow::Result; | ||
7 | use xshell::rm_rf; | ||
8 | |||
9 | use crate::flags; | ||
10 | |||
11 | impl flags::PreCache { | ||
12 | /// Cleans the `./target` dir after the build such that only | ||
13 | /// dependencies are cached on CI. | ||
14 | pub(crate) fn run(self) -> Result<()> { | ||
15 | let slow_tests_cookie = Path::new("./target/.slow_tests_cookie"); | ||
16 | if !slow_tests_cookie.exists() { | ||
17 | panic!("slow tests were skipped on CI!") | ||
18 | } | ||
19 | rm_rf(slow_tests_cookie)?; | ||
20 | |||
21 | for path in read_dir("./target/debug", FileType::is_file)? { | ||
22 | // Can't delete yourself on windows :-( | ||
23 | if !path.ends_with("xtask.exe") { | ||
24 | rm_rf(&path)? | ||
25 | } | ||
26 | } | ||
27 | |||
28 | rm_rf("./target/.rustc_info.json")?; | ||
29 | |||
30 | let to_delete = read_dir("./crates", FileType::is_dir)? | ||
31 | .into_iter() | ||
32 | .map(|path| path.file_name().unwrap().to_string_lossy().replace('-', "_")) | ||
33 | .collect::<Vec<_>>(); | ||
34 | |||
35 | for &dir in ["./target/debug/deps", "target/debug/.fingerprint"].iter() { | ||
36 | for path in read_dir(dir, |_file_type| true)? { | ||
37 | if path.ends_with("xtask.exe") { | ||
38 | continue; | ||
39 | } | ||
40 | let file_name = path.file_name().unwrap().to_string_lossy(); | ||
41 | let (stem, _) = match rsplit_once(&file_name, '-') { | ||
42 | Some(it) => it, | ||
43 | None => { | ||
44 | rm_rf(path)?; | ||
45 | continue; | ||
46 | } | ||
47 | }; | ||
48 | let stem = stem.replace('-', "_"); | ||
49 | if to_delete.contains(&stem) { | ||
50 | rm_rf(path)?; | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
55 | Ok(()) | ||
56 | } | ||
57 | } | ||
58 | fn read_dir(path: impl AsRef<Path>, cond: impl Fn(&FileType) -> bool) -> Result<Vec<PathBuf>> { | ||
59 | read_dir_impl(path.as_ref(), &cond) | ||
60 | } | ||
61 | |||
62 | fn read_dir_impl(path: &Path, cond: &dyn Fn(&FileType) -> bool) -> Result<Vec<PathBuf>> { | ||
63 | let mut res = Vec::new(); | ||
64 | for entry in path.read_dir()? { | ||
65 | let entry = entry?; | ||
66 | let file_type = entry.file_type()?; | ||
67 | if cond(&file_type) { | ||
68 | res.push(entry.path()) | ||
69 | } | ||
70 | } | ||
71 | Ok(res) | ||
72 | } | ||
73 | |||
74 | fn rsplit_once(haystack: &str, delim: char) -> Option<(&str, &str)> { | ||
75 | let mut split = haystack.rsplitn(2, delim); | ||
76 | let suffix = split.next()?; | ||
77 | let prefix = split.next()?; | ||
78 | Some((prefix, suffix)) | ||
79 | } | ||