aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir_def/src/find_path.rs24
-rw-r--r--crates/hir_def/src/nameres/collector.rs81
-rw-r--r--crates/hir_def/src/nameres/tests.rs131
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs32
-rw-r--r--crates/hir_expand/src/name.rs5
-rw-r--r--crates/hir_ty/src/infer/pat.rs4
-rw-r--r--crates/hir_ty/src/tests/macros.rs50
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs14
-rw-r--r--crates/hir_ty/src/tests/patterns.rs47
-rw-r--r--crates/hir_ty/src/tests/regression.rs9
-rw-r--r--crates/hir_ty/src/tests/simple.rs20
-rw-r--r--crates/hir_ty/src/tests/traits.rs48
-rw-r--r--crates/ide/src/expand_macro.rs6
-rw-r--r--crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs169
-rw-r--r--crates/ide_assists/src/handlers/extract_type_alias.rs78
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs33
-rw-r--r--crates/ide_completion/src/completions/dot.rs7
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs6
-rw-r--r--crates/ide_completion/src/completions/keyword.rs2
-rw-r--r--crates/ide_completion/src/completions/macro_in_item_position.rs2
-rw-r--r--crates/ide_completion/src/completions/postfix.rs17
-rw-r--r--crates/ide_completion/src/completions/snippet.rs2
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs37
-rw-r--r--crates/ide_completion/src/context.rs65
-rw-r--r--crates/ide_completion/src/patterns.rs49
-rw-r--r--crates/ide_completion/src/render/function.rs2
-rw-r--r--crates/ide_db/src/helpers/famous_defs_fixture.rs22
-rw-r--r--crates/rust-analyzer/src/config.rs1
-rw-r--r--crates/syntax/src/ast/make.rs5
29 files changed, 686 insertions, 282 deletions
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index ee52794aa..219ed4c07 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -682,9 +682,11 @@ pub struct S;
682//- /main.rs crate:main deps:std 682//- /main.rs crate:main deps:std
683$0 683$0
684//- /std.rs crate:std 684//- /std.rs crate:std
685pub mod prelude { pub struct S; } 685pub mod prelude {
686#[prelude_import] 686 pub mod rust_2018 {
687pub use prelude::*; 687 pub struct S;
688 }
689}
688 "#, 690 "#,
689 "S", 691 "S",
690 "S", 692 "S",
@@ -700,11 +702,11 @@ pub use prelude::*;
700$0 702$0
701//- /std.rs crate:std 703//- /std.rs crate:std
702pub mod prelude { 704pub mod prelude {
703 pub enum Option<T> { Some(T), None } 705 pub mod rust_2018 {
704 pub use Option::*; 706 pub enum Option<T> { Some(T), None }
707 pub use Option::*;
708 }
705} 709}
706#[prelude_import]
707pub use prelude::*;
708 "#; 710 "#;
709 check_found_path(code, "None", "None", "None", "None"); 711 check_found_path(code, "None", "None", "None", "None");
710 check_found_path(code, "Some", "Some", "Some", "Some"); 712 check_found_path(code, "Some", "Some", "Some", "Some");
@@ -1080,11 +1082,11 @@ fn f() {
1080} 1082}
1081//- /std.rs crate:std 1083//- /std.rs crate:std
1082pub mod prelude { 1084pub mod prelude {
1083 pub enum Option { None } 1085 pub mod rust_2018 {
1084 pub use Option::*; 1086 pub enum Option { None }
1087 pub use Option::*;
1088 }
1085} 1089}
1086#[prelude_import]
1087pub use prelude::*;
1088 "#, 1090 "#,
1089 "None", 1091 "None",
1090 "None", 1092 "None",
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 7f9fdb379..6b41921ae 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -5,13 +5,13 @@
5 5
6use std::iter; 6use std::iter;
7 7
8use base_db::{CrateId, FileId, ProcMacroId}; 8use base_db::{CrateId, Edition, FileId, ProcMacroId};
9use cfg::{CfgExpr, CfgOptions}; 9use cfg::{CfgExpr, CfgOptions};
10use hir_expand::{ 10use hir_expand::{
11 ast_id_map::FileAstId, 11 ast_id_map::FileAstId,
12 builtin_derive::find_builtin_derive, 12 builtin_derive::find_builtin_derive,
13 builtin_macro::find_builtin_macro, 13 builtin_macro::find_builtin_macro,
14 name::{AsName, Name}, 14 name::{name, AsName, Name},
15 proc_macro::ProcMacroExpander, 15 proc_macro::ProcMacroExpander,
16 FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 16 FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
17}; 17};
@@ -67,14 +67,6 @@ pub(super) fn collect_defs(
67 def_map 67 def_map
68 .extern_prelude 68 .extern_prelude
69 .insert(dep.as_name(), dep_def_map.module_id(dep_def_map.root).into()); 69 .insert(dep.as_name(), dep_def_map.module_id(dep_def_map.root).into());
70
71 // look for the prelude
72 // If the dependency defines a prelude, we overwrite an already defined
73 // prelude. This is necessary to import the "std" prelude if a crate
74 // depends on both "core" and "std".
75 if dep_def_map.prelude.is_some() {
76 def_map.prelude = dep_def_map.prelude;
77 }
78 } 70 }
79 } 71 }
80 72
@@ -283,6 +275,8 @@ impl DefCollector<'_> {
283 275
284 let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); 276 let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
285 if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) { 277 if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) {
278 self.inject_prelude(&attrs);
279
286 // Process other crate-level attributes. 280 // Process other crate-level attributes.
287 for attr in &*attrs { 281 for attr in &*attrs {
288 let attr_name = match attr.path.as_ident() { 282 let attr_name = match attr.path.as_ident() {
@@ -460,6 +454,71 @@ impl DefCollector<'_> {
460 } 454 }
461 } 455 }
462 456
457 fn inject_prelude(&mut self, crate_attrs: &Attrs) {
458 // See compiler/rustc_builtin_macros/src/standard_library_imports.rs
459
460 if crate_attrs.by_key("no_core").exists() {
461 // libcore does not get a prelude.
462 return;
463 }
464
465 let krate = if crate_attrs.by_key("no_std").exists() {
466 name![core]
467 } else {
468 let std = name![std];
469 if self.def_map.extern_prelude().any(|(name, _)| *name == std) {
470 std
471 } else {
472 // If `std` does not exist for some reason, fall back to core. This mostly helps
473 // keep r-a's own tests minimal.
474 name![core]
475 }
476 };
477
478 let edition = match self.def_map.edition {
479 Edition::Edition2015 => name![rust_2015],
480 Edition::Edition2018 => name![rust_2018],
481 Edition::Edition2021 => name![rust_2021],
482 };
483
484 let path_kind = if self.def_map.edition == Edition::Edition2015 {
485 PathKind::Plain
486 } else {
487 PathKind::Abs
488 };
489 let path = ModPath::from_segments(
490 path_kind.clone(),
491 [krate.clone(), name![prelude], edition].iter().cloned(),
492 );
493 // Fall back to the older `std::prelude::v1` for compatibility with Rust <1.52.0
494 // FIXME remove this fallback
495 let fallback_path =
496 ModPath::from_segments(path_kind, [krate, name![prelude], name![v1]].iter().cloned());
497
498 for path in &[path, fallback_path] {
499 let (per_ns, _) = self.def_map.resolve_path(
500 self.db,
501 self.def_map.root,
502 &path,
503 BuiltinShadowMode::Other,
504 );
505
506 match &per_ns.types {
507 Some((ModuleDefId::ModuleId(m), _)) => {
508 self.def_map.prelude = Some(*m);
509 return;
510 }
511 _ => {
512 log::debug!(
513 "could not resolve prelude path `{}` to module (resolved to {:?})",
514 path,
515 per_ns.types
516 );
517 }
518 }
519 }
520 }
521
463 /// Adds a definition of procedural macro `name` to the root module. 522 /// Adds a definition of procedural macro `name` to the root module.
464 /// 523 ///
465 /// # Notes on procedural macro resolution 524 /// # Notes on procedural macro resolution
@@ -718,6 +777,8 @@ impl DefCollector<'_> {
718 match def.take_types() { 777 match def.take_types() {
719 Some(ModuleDefId::ModuleId(m)) => { 778 Some(ModuleDefId::ModuleId(m)) => {
720 if import.is_prelude { 779 if import.is_prelude {
780 // Note: This dodgily overrides the injected prelude. The rustc
781 // implementation seems to work the same though.
721 cov_mark::hit!(std_prelude); 782 cov_mark::hit!(std_prelude);
722 self.def_map.prelude = Some(m); 783 self.def_map.prelude = Some(m);
723 } else if m.krate != self.def_map.krate { 784 } else if m.krate != self.def_map.krate {
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 9f652731d..58c01354a 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -246,15 +246,16 @@ fn std_prelude() {
246 check( 246 check(
247 r#" 247 r#"
248//- /main.rs crate:main deps:test_crate 248//- /main.rs crate:main deps:test_crate
249#[prelude_import]
250use ::test_crate::prelude::*;
251
249use Foo::*; 252use Foo::*;
250 253
251//- /lib.rs crate:test_crate 254//- /lib.rs crate:test_crate
252mod prelude; 255pub mod prelude;
253#[prelude_import]
254use prelude::*;
255 256
256//- /prelude.rs 257//- /prelude.rs
257pub enum Foo { Bar, Baz }; 258pub enum Foo { Bar, Baz }
258"#, 259"#,
259 expect![[r#" 260 expect![[r#"
260 crate 261 crate
@@ -467,6 +468,74 @@ pub struct Bar;
467} 468}
468 469
469#[test] 470#[test]
471fn no_std_prelude() {
472 check(
473 r#"
474 //- /main.rs crate:main deps:core,std
475 #![cfg_attr(not(never), no_std)]
476 use Rust;
477
478 //- /core.rs crate:core
479 pub mod prelude {
480 pud mod rust_2018 {
481 pub struct Rust;
482 }
483 }
484 //- /std.rs crate:std deps:core
485 pub mod prelude {
486 pud mod rust_2018 {
487 }
488 }
489 "#,
490 expect![[r#"
491 crate
492 Rust: t v
493 "#]],
494 );
495}
496
497#[test]
498fn edition_specific_preludes() {
499 // We can't test the 2015 prelude here since you can't reexport its contents with 2015's
500 // absolute paths.
501
502 check(
503 r#"
504 //- /main.rs edition:2018 crate:main deps:std
505 use Rust2018;
506
507 //- /std.rs crate:std
508 pub mod prelude {
509 pud mod rust_2018 {
510 pub struct Rust2018;
511 }
512 }
513 "#,
514 expect![[r#"
515 crate
516 Rust2018: t v
517 "#]],
518 );
519 check(
520 r#"
521 //- /main.rs edition:2021 crate:main deps:std
522 use Rust2021;
523
524 //- /std.rs crate:std
525 pub mod prelude {
526 pud mod rust_2021 {
527 pub struct Rust2021;
528 }
529 }
530 "#,
531 expect![[r#"
532 crate
533 Rust2021: t v
534 "#]],
535 );
536}
537
538#[test]
470fn std_prelude_takes_precedence_above_core_prelude() { 539fn std_prelude_takes_precedence_above_core_prelude() {
471 check( 540 check(
472 r#" 541 r#"
@@ -474,18 +543,18 @@ fn std_prelude_takes_precedence_above_core_prelude() {
474use {Foo, Bar}; 543use {Foo, Bar};
475 544
476//- /std.rs crate:std deps:core 545//- /std.rs crate:std deps:core
477#[prelude_import] 546pub mod prelude {
478pub use self::prelude::*; 547 pub mod rust_2018 {
479mod prelude { 548 pub struct Foo;
480 pub struct Foo; 549 pub use core::prelude::rust_2018::Bar;
481 pub use core::prelude::Bar; 550 }
482} 551}
483 552
484//- /core.rs crate:core 553//- /core.rs crate:core
485#[prelude_import] 554pub mod prelude {
486pub use self::prelude::*; 555 pub mod rust_2018 {
487mod prelude { 556 pub struct Bar;
488 pub struct Bar; 557 }
489} 558}
490"#, 559"#,
491 expect![[r#" 560 expect![[r#"
@@ -504,15 +573,15 @@ fn cfg_not_test() {
504use {Foo, Bar, Baz}; 573use {Foo, Bar, Baz};
505 574
506//- /lib.rs crate:std 575//- /lib.rs crate:std
507#[prelude_import] 576pub mod prelude {
508pub use self::prelude::*; 577 pub mod rust_2018 {
509mod prelude { 578 #[cfg(test)]
510 #[cfg(test)] 579 pub struct Foo;
511 pub struct Foo; 580 #[cfg(not(test))]
512 #[cfg(not(test))] 581 pub struct Bar;
513 pub struct Bar; 582 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))]
514 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] 583 pub struct Baz;
515 pub struct Baz; 584 }
516} 585}
517"#, 586"#,
518 expect![[r#" 587 expect![[r#"
@@ -532,15 +601,15 @@ fn cfg_test() {
532use {Foo, Bar, Baz}; 601use {Foo, Bar, Baz};
533 602
534//- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42 603//- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42
535#[prelude_import] 604pub mod prelude {
536pub use self::prelude::*; 605 pub mod rust_2018 {
537mod prelude { 606 #[cfg(test)]
538 #[cfg(test)] 607 pub struct Foo;
539 pub struct Foo; 608 #[cfg(not(test))]
540 #[cfg(not(test))] 609 pub struct Bar;
541 pub struct Bar; 610 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))]
542 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] 611 pub struct Baz;
543 pub struct Baz; 612 }
544} 613}
545"#, 614"#,
546 expect![[r#" 615 expect![[r#"
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 3065efd65..371618438 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -264,7 +264,7 @@ fn prelude_is_macro_use() {
264 cov_mark::check!(prelude_is_macro_use); 264 cov_mark::check!(prelude_is_macro_use);
265 check( 265 check(
266 r#" 266 r#"
267//- /main.rs crate:main deps:foo 267//- /main.rs crate:main deps:std
268structs!(Foo); 268structs!(Foo);
269structs_priv!(Bar); 269structs_priv!(Bar);
270structs_outside!(Out); 270structs_outside!(Out);
@@ -276,21 +276,20 @@ mod bar;
276structs!(Baz); 276structs!(Baz);
277crate::structs!(MacroNotResolved3); 277crate::structs!(MacroNotResolved3);
278 278
279//- /lib.rs crate:foo 279//- /lib.rs crate:std
280#[prelude_import] 280pub mod prelude {
281use self::prelude::*; 281 pub mod rust_2018 {
282
283mod prelude {
284 #[macro_export]
285 macro_rules! structs {
286 ($i:ident) => { struct $i; }
287 }
288
289 mod priv_mod {
290 #[macro_export] 282 #[macro_export]
291 macro_rules! structs_priv { 283 macro_rules! structs {
292 ($i:ident) => { struct $i; } 284 ($i:ident) => { struct $i; }
293 } 285 }
286
287 mod priv_mod {
288 #[macro_export]
289 macro_rules! structs_priv {
290 ($i:ident) => { struct $i; }
291 }
292 }
294 } 293 }
295} 294}
296 295
@@ -617,12 +616,11 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() {
617foo!(); 616foo!();
618 617
619//- /std.rs crate:std deps:core 618//- /std.rs crate:std deps:core
620#[prelude_import]
621use self::prelude::*;
622
623pub use core::foo; 619pub use core::foo;
624 620
625mod prelude {} 621pub mod prelude {
622 pub mod rust_2018 {}
623}
626 624
627#[macro_use] 625#[macro_use]
628mod std_macros; 626mod std_macros;
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index b07fbf8b3..00b8adc1e 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -176,6 +176,11 @@ pub mod known {
176 result, 176 result,
177 boxed, 177 boxed,
178 option, 178 option,
179 prelude,
180 rust_2015,
181 rust_2018,
182 rust_2021,
183 v1,
179 // Components of known path (type name) 184 // Components of known path (type name)
180 Iterator, 185 Iterator,
181 IntoIterator, 186 IntoIterator,
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index a21f44d6a..25dff7e49 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -297,6 +297,10 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
297 Expr::Literal(Literal::String(..)) => false, 297 Expr::Literal(Literal::String(..)) => false,
298 _ => true, 298 _ => true,
299 }, 299 },
300 Pat::Bind { mode: BindingAnnotation::Mutable, subpat: Some(subpat), .. }
301 | Pat::Bind { mode: BindingAnnotation::Unannotated, subpat: Some(subpat), .. } => {
302 is_non_ref_pat(body, *subpat)
303 }
300 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false, 304 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
301 } 305 }
302} 306}
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index 7647bb08b..d14103aab 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -982,14 +982,18 @@ fn test() {
982} //^ S 982} //^ S
983 983
984//- /lib.rs crate:core 984//- /lib.rs crate:core
985#[prelude_import] 985pub mod prelude {
986use clone::*; 986 pub mod rust_2018 {
987mod clone { 987 #[rustc_builtin_macro]
988 trait Clone { 988 pub macro Clone {}
989 pub use crate::clone::Clone;
990 }
991}
992
993pub mod clone {
994 pub trait Clone {
989 fn clone(&self) -> Self; 995 fn clone(&self) -> Self;
990 } 996 }
991 #[rustc_builtin_macro]
992 macro Clone {}
993} 997}
994"#, 998"#,
995 ); 999 );
@@ -1001,14 +1005,22 @@ fn infer_derive_clone_in_core() {
1001 r#" 1005 r#"
1002//- /lib.rs crate:core 1006//- /lib.rs crate:core
1003#[prelude_import] 1007#[prelude_import]
1004use clone::*; 1008use prelude::rust_2018::*;
1005mod clone { 1009
1006 trait Clone { 1010pub mod prelude {
1011 pub mod rust_2018 {
1012 #[rustc_builtin_macro]
1013 pub macro Clone {}
1014 pub use crate::clone::Clone;
1015 }
1016}
1017
1018pub mod clone {
1019 pub trait Clone {
1007 fn clone(&self) -> Self; 1020 fn clone(&self) -> Self;
1008 } 1021 }
1009 #[rustc_builtin_macro]
1010 macro Clone {}
1011} 1022}
1023
1012#[derive(Clone)] 1024#[derive(Clone)]
1013pub struct S; 1025pub struct S;
1014 1026
@@ -1037,14 +1049,18 @@ fn test() {
1037} 1049}
1038 1050
1039//- /lib.rs crate:core 1051//- /lib.rs crate:core
1040#[prelude_import] 1052pub mod prelude {
1041use clone::*; 1053 pub mod rust_2018 {
1042mod clone { 1054 #[rustc_builtin_macro]
1043 trait Clone { 1055 pub macro Clone {}
1056 pub use crate::clone::Clone;
1057 }
1058}
1059
1060pub mod clone {
1061 pub trait Clone {
1044 fn clone(&self) -> Self; 1062 fn clone(&self) -> Self;
1045 } 1063 }
1046 #[rustc_builtin_macro]
1047 macro Clone {}
1048} 1064}
1049"#, 1065"#,
1050 ); 1066 );
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index a4c132bc5..058eb9129 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -796,7 +796,7 @@ fn test() {
796fn method_resolution_trait_from_prelude() { 796fn method_resolution_trait_from_prelude() {
797 check_types( 797 check_types(
798 r#" 798 r#"
799//- /main.rs crate:main deps:other_crate 799//- /main.rs crate:main deps:core
800struct S; 800struct S;
801impl Clone for S {} 801impl Clone for S {}
802 802
@@ -805,12 +805,12 @@ fn test() {
805 //^ S 805 //^ S
806} 806}
807 807
808//- /lib.rs crate:other_crate 808//- /lib.rs crate:core
809#[prelude_import] use foo::*; 809pub mod prelude {
810 810 pub mod rust_2018 {
811mod foo { 811 pub trait Clone {
812 trait Clone { 812 fn clone(&self) -> Self;
813 fn clone(&self) -> Self; 813 }
814 } 814 }
815} 815}
816"#, 816"#,
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index cd08b5c7a..7d00cee9b 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -20,6 +20,8 @@ fn infer_pattern() {
20 let h = val; 20 let h = val;
21 } 21 }
22 22
23 if let x @ true = &true {}
24
23 let lambda = |a: u64, b, c: i32| { a + b; c }; 25 let lambda = |a: u64, b, c: i32| { a + b; c };
24 26
25 let ref ref_to_x = x; 27 let ref ref_to_x = x;
@@ -30,7 +32,7 @@ fn infer_pattern() {
30 "#, 32 "#,
31 expect![[r#" 33 expect![[r#"
32 8..9 'x': &i32 34 8..9 'x': &i32
33 17..368 '{ ...o_x; }': () 35 17..400 '{ ...o_x; }': ()
34 27..28 'y': &i32 36 27..28 'y': &i32
35 31..32 'x': &i32 37 31..32 'x': &i32
36 42..44 '&z': &i32 38 42..44 '&z': &i32
@@ -59,24 +61,31 @@ fn infer_pattern() {
59 176..204 '{ ... }': () 61 176..204 '{ ... }': ()
60 190..191 'h': {unknown} 62 190..191 'h': {unknown}
61 194..197 'val': {unknown} 63 194..197 'val': {unknown}
62 214..220 'lambda': |u64, u64, i32| -> i32 64 210..236 'if let...rue {}': ()
63 223..255 '|a: u6...b; c }': |u64, u64, i32| -> i32 65 217..225 'x @ true': &bool
64 224..225 'a': u64 66 221..225 'true': bool
65 232..233 'b': u64 67 221..225 'true': bool
66 235..236 'c': i32 68 228..233 '&true': &bool
67 243..255 '{ a + b; c }': i32 69 229..233 'true': bool
68 245..246 'a': u64 70 234..236 '{}': ()
69 245..250 'a + b': u64 71 246..252 'lambda': |u64, u64, i32| -> i32
70 249..250 'b': u64 72 255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32
71 252..253 'c': i32 73 256..257 'a': u64
72 266..278 'ref ref_to_x': &&i32 74 264..265 'b': u64
73 281..282 'x': &i32 75 267..268 'c': i32
74 292..301 'mut mut_x': &i32 76 275..287 '{ a + b; c }': i32
75 304..305 'x': &i32 77 277..278 'a': u64
76 315..335 'ref mu...f_to_x': &mut &i32 78 277..282 'a + b': u64
77 338..339 'x': &i32 79 281..282 'b': u64
78 349..350 'k': &mut &i32 80 284..285 'c': i32
79 353..365 'mut_ref_to_x': &mut &i32 81 298..310 'ref ref_to_x': &&i32
82 313..314 'x': &i32
83 324..333 'mut mut_x': &i32
84 336..337 'x': &i32
85 347..367 'ref mu...f_to_x': &mut &i32
86 370..371 'x': &i32
87 381..382 'k': &mut &i32
88 385..397 'mut_ref_to_x': &mut &i32
80 "#]], 89 "#]],
81 ); 90 );
82} 91}
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index ad9edf11c..1019e783b 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -426,11 +426,12 @@ fn test() {
426 426
427//- /std.rs crate:std 427//- /std.rs crate:std
428#[prelude_import] 428#[prelude_import]
429use prelude::*; 429use self::prelude::rust_2018::*;
430
431pub mod prelude { 430pub mod prelude {
432 pub use crate::iter::Iterator; 431 pub mod rust_2018 {
433 pub use crate::option::Option; 432 pub use crate::iter::Iterator;
433 pub use crate::option::Option;
434 }
434} 435}
435 436
436pub mod iter { 437pub mod iter {
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index ac312981d..3418ed21e 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2712,3 +2712,23 @@ fn main() {
2712 "#]], 2712 "#]],
2713 ); 2713 );
2714} 2714}
2715
2716#[test]
2717fn prelude_2015() {
2718 check_types(
2719 r#"
2720//- /main.rs edition:2015 crate:main deps:core
2721fn f() {
2722 Rust;
2723 //^ Rust
2724}
2725
2726//- /core.rs crate:core
2727pub mod prelude {
2728 pub mod rust_2015 {
2729 pub struct Rust;
2730 }
2731}
2732 "#,
2733 );
2734}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 49add4ab9..588f0d1d4 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -20,11 +20,12 @@ fn test() {
20} //^ u64 20} //^ u64
21 21
22//- /core.rs crate:core 22//- /core.rs crate:core
23#[prelude_import] use future::*; 23pub mod prelude {
24mod future { 24 pub mod rust_2018 {
25 #[lang = "future_trait"] 25 #[lang = "future_trait"]
26 trait Future { 26 pub trait Future {
27 type Output; 27 type Output;
28 }
28 } 29 }
29} 30}
30"#, 31"#,
@@ -136,17 +137,15 @@ fn test() {
136} //^ i32 137} //^ i32
137 138
138//- /core.rs crate:core 139//- /core.rs crate:core
139#[prelude_import] use ops::*; 140pub mod ops {
140mod ops { 141 pub trait Try {
141 trait Try {
142 type Ok; 142 type Ok;
143 type Error; 143 type Error;
144 } 144 }
145} 145}
146 146
147#[prelude_import] use result::*; 147pub mod result {
148mod result { 148 pub enum Result<O, E> {
149 enum Result<O, E> {
150 Ok(O), 149 Ok(O),
151 Err(E) 150 Err(E)
152 } 151 }
@@ -156,6 +155,12 @@ mod result {
156 type Error = E; 155 type Error = E;
157 } 156 }
158} 157}
158
159pub mod prelude {
160 pub mod rust_2018 {
161 pub use crate::{result::*, ops::*};
162 }
163}
159"#, 164"#,
160 ); 165 );
161} 166}
@@ -190,8 +195,7 @@ mov convert {
190 impl<T> From<T> for T {} 195 impl<T> From<T> for T {}
191} 196}
192 197
193#[prelude_import] use result::*; 198pub mod result {
194mod result {
195 use crate::convert::From; 199 use crate::convert::From;
196 use crate::ops::{Try, FromResidual}; 200 use crate::ops::{Try, FromResidual};
197 201
@@ -208,6 +212,12 @@ mod result {
208 212
209 impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {} 213 impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {}
210} 214}
215
216pub mod prelude {
217 pub mod rust_2018 {
218 pub use crate::result::*;
219 }
220}
211"#, 221"#,
212 ); 222 );
213} 223}
@@ -217,6 +227,7 @@ fn infer_for_loop() {
217 check_types( 227 check_types(
218 r#" 228 r#"
219//- /main.rs crate:main deps:core,alloc 229//- /main.rs crate:main deps:core,alloc
230#![no_std]
220use alloc::collections::Vec; 231use alloc::collections::Vec;
221 232
222fn test() { 233fn test() {
@@ -228,14 +239,19 @@ fn test() {
228} 239}
229 240
230//- /core.rs crate:core 241//- /core.rs crate:core
231#[prelude_import] use iter::*; 242pub mod iter {
232mod iter { 243 pub trait IntoIterator {
233 trait IntoIterator {
234 type Item; 244 type Item;
235 } 245 }
236} 246}
247pub mod prelude {
248 pub mod rust_2018 {
249 pub use crate::iter::*;
250 }
251}
237 252
238//- /alloc.rs crate:alloc deps:core 253//- /alloc.rs crate:alloc deps:core
254#![no_std]
239mod collections { 255mod collections {
240 struct Vec<T> {} 256 struct Vec<T> {}
241 impl<T> Vec<T> { 257 impl<T> Vec<T> {
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index eebae5ebe..e0d01fa96 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -28,8 +28,8 @@ pub struct ExpandedMacro {
28pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { 28pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
29 let sema = Semantics::new(db); 29 let sema = Semantics::new(db);
30 let file = sema.parse(position.file_id); 30 let file = sema.parse(position.file_id);
31 let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; 31 let mac = find_node_at_offset::<ast::MacroCall>(file.syntax(), position.offset)?;
32 let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; 32 let name = mac.path()?.segment()?.name_ref()?;
33 33
34 let expanded = expand_macro_recur(&sema, &mac)?; 34 let expanded = expand_macro_recur(&sema, &mac)?;
35 35
@@ -37,7 +37,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
37 // macro expansion may lose all white space information 37 // macro expansion may lose all white space information
38 // But we hope someday we can use ra_fmt for that 38 // But we hope someday we can use ra_fmt for that
39 let expansion = insert_whitespaces(expanded); 39 let expansion = insert_whitespaces(expanded);
40 Some(ExpandedMacro { name: name_ref.text().to_string(), expansion }) 40 Some(ExpandedMacro { name: name.to_string(), expansion })
41} 41}
42 42
43fn expand_macro_recur( 43fn expand_macro_recur(
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
index 007aba23d..d3ff7b65c 100644
--- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -11,14 +11,19 @@ use ide_db::{
11 search::FileReference, 11 search::FileReference,
12 RootDatabase, 12 RootDatabase,
13}; 13};
14use itertools::Itertools;
14use rustc_hash::FxHashSet; 15use rustc_hash::FxHashSet;
15use syntax::{ 16use syntax::{
16 algo::find_node_at_offset, 17 ast::{
17 ast::{self, make, AstNode, NameOwner, VisibilityOwner}, 18 self, make, AstNode, AttrsOwner, GenericParamsOwner, NameOwner, TypeBoundsOwner,
18 ted, SyntaxNode, T, 19 VisibilityOwner,
20 },
21 match_ast,
22 ted::{self, Position},
23 SyntaxNode, T,
19}; 24};
20 25
21use crate::{AssistContext, AssistId, AssistKind, Assists}; 26use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
22 27
23// Assist: extract_struct_from_enum_variant 28// Assist: extract_struct_from_enum_variant
24// 29//
@@ -70,11 +75,10 @@ pub(crate) fn extract_struct_from_enum_variant(
70 continue; 75 continue;
71 } 76 }
72 builder.edit_file(file_id); 77 builder.edit_file(file_id);
73 let source_file = builder.make_mut(ctx.sema.parse(file_id));
74 let processed = process_references( 78 let processed = process_references(
75 ctx, 79 ctx,
80 builder,
76 &mut visited_modules_set, 81 &mut visited_modules_set,
77 source_file.syntax(),
78 &enum_module_def, 82 &enum_module_def,
79 &variant_hir_name, 83 &variant_hir_name,
80 references, 84 references,
@@ -84,13 +88,12 @@ pub(crate) fn extract_struct_from_enum_variant(
84 }); 88 });
85 } 89 }
86 builder.edit_file(ctx.frange.file_id); 90 builder.edit_file(ctx.frange.file_id);
87 let source_file = builder.make_mut(ctx.sema.parse(ctx.frange.file_id));
88 let variant = builder.make_mut(variant.clone()); 91 let variant = builder.make_mut(variant.clone());
89 if let Some(references) = def_file_references { 92 if let Some(references) = def_file_references {
90 let processed = process_references( 93 let processed = process_references(
91 ctx, 94 ctx,
95 builder,
92 &mut visited_modules_set, 96 &mut visited_modules_set,
93 source_file.syntax(),
94 &enum_module_def, 97 &enum_module_def,
95 &variant_hir_name, 98 &variant_hir_name,
96 references, 99 references,
@@ -100,12 +103,12 @@ pub(crate) fn extract_struct_from_enum_variant(
100 }); 103 });
101 } 104 }
102 105
103 let def = create_struct_def(variant_name.clone(), &field_list, enum_ast.visibility()); 106 let def = create_struct_def(variant_name.clone(), &field_list, &enum_ast);
104 let start_offset = &variant.parent_enum().syntax().clone(); 107 let start_offset = &variant.parent_enum().syntax().clone();
105 ted::insert_raw(ted::Position::before(start_offset), def.syntax()); 108 ted::insert_raw(ted::Position::before(start_offset), def.syntax());
106 ted::insert_raw(ted::Position::before(start_offset), &make::tokens::blank_line()); 109 ted::insert_raw(ted::Position::before(start_offset), &make::tokens::blank_line());
107 110
108 update_variant(&variant); 111 update_variant(&variant, enum_ast.generic_param_list());
109 }, 112 },
110 ) 113 )
111} 114}
@@ -149,7 +152,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
149fn create_struct_def( 152fn create_struct_def(
150 variant_name: ast::Name, 153 variant_name: ast::Name,
151 field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, 154 field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
152 visibility: Option<ast::Visibility>, 155 enum_: &ast::Enum,
153) -> ast::Struct { 156) -> ast::Struct {
154 let pub_vis = make::visibility_pub(); 157 let pub_vis = make::visibility_pub();
155 158
@@ -184,12 +187,38 @@ fn create_struct_def(
184 } 187 }
185 }; 188 };
186 189
187 make::struct_(visibility, variant_name, None, field_list).clone_for_update() 190 // FIXME: This uses all the generic params of the enum, but the variant might not use all of them.
191 let strukt =
192 make::struct_(enum_.visibility(), variant_name, enum_.generic_param_list(), field_list)
193 .clone_for_update();
194
195 // copy attributes
196 ted::insert_all(
197 Position::first_child_of(strukt.syntax()),
198 enum_.attrs().map(|it| it.syntax().clone_for_update().into()).collect(),
199 );
200 strukt
188} 201}
189 202
190fn update_variant(variant: &ast::Variant) -> Option<()> { 203fn update_variant(variant: &ast::Variant, generic: Option<ast::GenericParamList>) -> Option<()> {
191 let name = variant.name()?; 204 let name = variant.name()?;
192 let tuple_field = make::tuple_field(None, make::ty(&name.text())); 205 let ty = match generic {
206 // FIXME: This uses all the generic params of the enum, but the variant might not use all of them.
207 Some(gpl) => {
208 let gpl = gpl.clone_for_update();
209 gpl.generic_params().for_each(|gp| {
210 match gp {
211 ast::GenericParam::LifetimeParam(it) => it.type_bound_list(),
212 ast::GenericParam::TypeParam(it) => it.type_bound_list(),
213 ast::GenericParam::ConstParam(_) => return,
214 }
215 .map(|it| it.remove());
216 });
217 make::ty(&format!("{}<{}>", name.text(), gpl.generic_params().join(", ")))
218 }
219 None => make::ty(&name.text()),
220 };
221 let tuple_field = make::tuple_field(None, ty);
193 let replacement = make::variant( 222 let replacement = make::variant(
194 name, 223 name,
195 Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), 224 Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))),
@@ -208,18 +237,17 @@ fn apply_references(
208 if let Some((scope, path)) = import { 237 if let Some((scope, path)) = import {
209 insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg); 238 insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg);
210 } 239 }
211 ted::insert_raw( 240 // deep clone to prevent cycle
212 ted::Position::before(segment.syntax()), 241 let path = make::path_from_segments(iter::once(segment.clone_subtree()), false);
213 make::path_from_text(&format!("{}", segment)).clone_for_update().syntax(), 242 ted::insert_raw(ted::Position::before(segment.syntax()), path.clone_for_update().syntax());
214 );
215 ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['('])); 243 ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['(']));
216 ted::insert_raw(ted::Position::after(&node), make::token(T![')'])); 244 ted::insert_raw(ted::Position::after(&node), make::token(T![')']));
217} 245}
218 246
219fn process_references( 247fn process_references(
220 ctx: &AssistContext, 248 ctx: &AssistContext,
249 builder: &mut AssistBuilder,
221 visited_modules: &mut FxHashSet<Module>, 250 visited_modules: &mut FxHashSet<Module>,
222 source_file: &SyntaxNode,
223 enum_module_def: &ModuleDef, 251 enum_module_def: &ModuleDef,
224 variant_hir_name: &Name, 252 variant_hir_name: &Name,
225 refs: Vec<FileReference>, 253 refs: Vec<FileReference>,
@@ -228,8 +256,9 @@ fn process_references(
228 // and corresponding nodes up front 256 // and corresponding nodes up front
229 refs.into_iter() 257 refs.into_iter()
230 .flat_map(|reference| { 258 .flat_map(|reference| {
231 let (segment, scope_node, module) = 259 let (segment, scope_node, module) = reference_to_node(&ctx.sema, reference)?;
232 reference_to_node(&ctx.sema, source_file, reference)?; 260 let segment = builder.make_mut(segment);
261 let scope_node = builder.make_syntax_mut(scope_node);
233 if !visited_modules.contains(&module) { 262 if !visited_modules.contains(&module) {
234 let mod_path = module.find_use_path_prefixed( 263 let mod_path = module.find_use_path_prefixed(
235 ctx.sema.db, 264 ctx.sema.db,
@@ -251,23 +280,22 @@ fn process_references(
251 280
252fn reference_to_node( 281fn reference_to_node(
253 sema: &hir::Semantics<RootDatabase>, 282 sema: &hir::Semantics<RootDatabase>,
254 source_file: &SyntaxNode,
255 reference: FileReference, 283 reference: FileReference,
256) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> { 284) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> {
257 let offset = reference.range.start(); 285 let segment =
258 if let Some(path_expr) = find_node_at_offset::<ast::PathExpr>(source_file, offset) { 286 reference.name.as_name_ref()?.syntax().parent().and_then(ast::PathSegment::cast)?;
259 // tuple variant 287 let parent = segment.parent_path().syntax().parent()?;
260 Some((path_expr.path()?.segment()?, path_expr.syntax().parent()?)) 288 let expr_or_pat = match_ast! {
261 } else if let Some(record_expr) = find_node_at_offset::<ast::RecordExpr>(source_file, offset) { 289 match parent {
262 // record variant 290 ast::PathExpr(_it) => parent.parent()?,
263 Some((record_expr.path()?.segment()?, record_expr.syntax().clone())) 291 ast::RecordExpr(_it) => parent,
264 } else { 292 ast::TupleStructPat(_it) => parent,
265 None 293 ast::RecordPat(_it) => parent,
266 } 294 _ => return None,
267 .and_then(|(segment, expr)| { 295 }
268 let module = sema.scope(&expr).module()?; 296 };
269 Some((segment, expr, module)) 297 let module = sema.scope(&expr_or_pat).module()?;
270 }) 298 Some((segment, expr_or_pat, module))
271} 299}
272 300
273#[cfg(test)] 301#[cfg(test)]
@@ -278,6 +306,12 @@ mod tests {
278 306
279 use super::*; 307 use super::*;
280 308
309 fn check_not_applicable(ra_fixture: &str) {
310 let fixture =
311 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
312 check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
313 }
314
281 #[test] 315 #[test]
282 fn test_extract_struct_several_fields_tuple() { 316 fn test_extract_struct_several_fields_tuple() {
283 check_assist( 317 check_assist(
@@ -312,6 +346,32 @@ enum A { One(One) }"#,
312 } 346 }
313 347
314 #[test] 348 #[test]
349 fn test_extract_struct_carries_over_generics() {
350 check_assist(
351 extract_struct_from_enum_variant,
352 r"enum En<T> { Var { a: T$0 } }",
353 r#"struct Var<T>{ pub a: T }
354
355enum En<T> { Var(Var<T>) }"#,
356 );
357 }
358
359 #[test]
360 fn test_extract_struct_carries_over_attributes() {
361 check_assist(
362 extract_struct_from_enum_variant,
363 r#"#[derive(Debug)]
364#[derive(Clone)]
365enum Enum { Variant{ field: u32$0 } }"#,
366 r#"#[derive(Debug)]#[derive(Clone)] struct Variant{ pub field: u32 }
367
368#[derive(Debug)]
369#[derive(Clone)]
370enum Enum { Variant(Variant) }"#,
371 );
372 }
373
374 #[test]
315 fn test_extract_struct_keep_comments_and_attrs_one_field_named() { 375 fn test_extract_struct_keep_comments_and_attrs_one_field_named() {
316 check_assist( 376 check_assist(
317 extract_struct_from_enum_variant, 377 extract_struct_from_enum_variant,
@@ -496,7 +556,7 @@ enum E {
496} 556}
497 557
498fn f() { 558fn f() {
499 let e = E::V { i: 9, j: 2 }; 559 let E::V { i, j } = E::V { i: 9, j: 2 };
500} 560}
501"#, 561"#,
502 r#" 562 r#"
@@ -507,7 +567,34 @@ enum E {
507} 567}
508 568
509fn f() { 569fn f() {
510 let e = E::V(V { i: 9, j: 2 }); 570 let E::V(V { i, j }) = E::V(V { i: 9, j: 2 });
571}
572"#,
573 )
574 }
575
576 #[test]
577 fn extract_record_fix_references2() {
578 check_assist(
579 extract_struct_from_enum_variant,
580 r#"
581enum E {
582 $0V(i32, i32)
583}
584
585fn f() {
586 let E::V(i, j) = E::V(9, 2);
587}
588"#,
589 r#"
590struct V(pub i32, pub i32);
591
592enum E {
593 V(V)
594}
595
596fn f() {
597 let E::V(V(i, j)) = E::V(V(9, 2));
511} 598}
512"#, 599"#,
513 ) 600 )
@@ -610,12 +697,6 @@ fn foo() {
610 ); 697 );
611 } 698 }
612 699
613 fn check_not_applicable(ra_fixture: &str) {
614 let fixture =
615 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
616 check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
617 }
618
619 #[test] 700 #[test]
620 fn test_extract_enum_not_applicable_for_element_with_no_fields() { 701 fn test_extract_enum_not_applicable_for_element_with_no_fields() {
621 check_not_applicable("enum A { $0One }"); 702 check_not_applicable("enum A { $0One }");
diff --git a/crates/ide_assists/src/handlers/extract_type_alias.rs b/crates/ide_assists/src/handlers/extract_type_alias.rs
index 442a209b9..4bccf5984 100644
--- a/crates/ide_assists/src/handlers/extract_type_alias.rs
+++ b/crates/ide_assists/src/handlers/extract_type_alias.rs
@@ -1,4 +1,7 @@
1use syntax::ast::{self, AstNode}; 1use syntax::{
2 ast::{self, edit::IndentLevel, AstNode},
3 match_ast,
4};
2 5
3use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
4 7
@@ -25,7 +28,15 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti
25 } 28 }
26 29
27 let node = ctx.find_node_at_range::<ast::Type>()?; 30 let node = ctx.find_node_at_range::<ast::Type>()?;
28 let insert = ctx.find_node_at_offset::<ast::Item>()?.syntax().text_range().start(); 31 let item = ctx.find_node_at_offset::<ast::Item>()?;
32 let insert = match_ast! {
33 match (item.syntax().parent()?) {
34 ast::AssocItemList(it) => it.syntax().parent()?.clone(),
35 _ => item.syntax().clone(),
36 }
37 };
38 let indent = IndentLevel::from_node(&insert);
39 let insert = insert.text_range().start();
29 let target = node.syntax().text_range(); 40 let target = node.syntax().text_range();
30 41
31 acc.add( 42 acc.add(
@@ -37,10 +48,14 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti
37 builder.replace(target, "Type"); 48 builder.replace(target, "Type");
38 match ctx.config.snippet_cap { 49 match ctx.config.snippet_cap {
39 Some(cap) => { 50 Some(cap) => {
40 builder.insert_snippet(cap, insert, format!("type $0Type = {};\n\n", node)); 51 builder.insert_snippet(
52 cap,
53 insert,
54 format!("type $0Type = {};\n\n{}", node, indent),
55 );
41 } 56 }
42 None => { 57 None => {
43 builder.insert(insert, format!("type Type = {};\n\n", node)); 58 builder.insert(insert, format!("type Type = {};\n\n{}", node, indent));
44 } 59 }
45 } 60 }
46 }, 61 },
@@ -146,4 +161,59 @@ struct S {
146 "#, 161 "#,
147 ); 162 );
148 } 163 }
164
165 #[test]
166 fn extract_from_impl_or_trait() {
167 // When invoked in an impl/trait, extracted type alias should be placed next to the
168 // impl/trait, not inside.
169 check_assist(
170 extract_type_alias,
171 r#"
172impl S {
173 fn f() -> $0(u8, u8)$0 {}
174}
175 "#,
176 r#"
177type $0Type = (u8, u8);
178
179impl S {
180 fn f() -> Type {}
181}
182 "#,
183 );
184 check_assist(
185 extract_type_alias,
186 r#"
187trait Tr {
188 fn f() -> $0(u8, u8)$0 {}
189}
190 "#,
191 r#"
192type $0Type = (u8, u8);
193
194trait Tr {
195 fn f() -> Type {}
196}
197 "#,
198 );
199 }
200
201 #[test]
202 fn indentation() {
203 check_assist(
204 extract_type_alias,
205 r#"
206mod m {
207 fn f() -> $0u8$0 {}
208}
209 "#,
210 r#"
211mod m {
212 type $0Type = u8;
213
214 fn f() -> Type {}
215}
216 "#,
217 );
218 }
149} 219}
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index ae084c86c..46b54a5f5 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -36,6 +36,11 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
36 return None; 36 return None;
37 } 37 }
38 let to_extract = node.ancestors().find_map(valid_target_expr)?; 38 let to_extract = node.ancestors().find_map(valid_target_expr)?;
39 if let Some(ty) = ctx.sema.type_of_expr(&to_extract) {
40 if ty.is_unit() {
41 return None;
42 }
43 }
39 let anchor = Anchor::from(&to_extract)?; 44 let anchor = Anchor::from(&to_extract)?;
40 let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone(); 45 let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone();
41 let target = to_extract.syntax().text_range(); 46 let target = to_extract.syntax().text_range();
@@ -275,15 +280,23 @@ fn foo() {
275 check_assist( 280 check_assist(
276 extract_variable, 281 extract_variable,
277 r#" 282 r#"
278fn foo() { 283fn foo() -> i32 {
279 $0bar(1 + 1)$0 284 $0bar(1 + 1)$0
280} 285}
286
287fn bar(i: i32) -> i32 {
288 i
289}
281"#, 290"#,
282 r#" 291 r#"
283fn foo() { 292fn foo() -> i32 {
284 let $0bar = bar(1 + 1); 293 let $0bar = bar(1 + 1);
285 bar 294 bar
286} 295}
296
297fn bar(i: i32) -> i32 {
298 i
299}
287"#, 300"#,
288 ) 301 )
289 } 302 }
@@ -796,6 +809,22 @@ fn foo() {
796 check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }"); 809 check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }");
797 } 810 }
798 811
812 #[test]
813 fn test_extract_var_unit_expr_not_applicable() {
814 check_assist_not_applicable(
815 extract_variable,
816 r#"
817fn foo() {
818 let mut i = 3;
819 $0if i >= 0 {
820 i += 1;
821 } else {
822 i -= 1;
823 }$0
824}"#,
825 );
826 }
827
799 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic 828 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
800 #[test] 829 #[test]
801 fn extract_var_target() { 830 fn extract_var_target() {
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index 302c9ccbd..e0a7021fd 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -8,7 +8,7 @@ use crate::{context::CompletionContext, Completions};
8 8
9/// Complete dot accesses, i.e. fields or methods. 9/// Complete dot accesses, i.e. fields or methods.
10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
11 let dot_receiver = match &ctx.dot_receiver { 11 let dot_receiver = match ctx.dot_receiver() {
12 Some(expr) => expr, 12 Some(expr) => expr,
13 _ => return complete_undotted_self(acc, ctx), 13 _ => return complete_undotted_self(acc, ctx),
14 }; 14 };
@@ -30,7 +30,10 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
30} 30}
31 31
32fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) { 32fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
33 if !ctx.is_trivial_path || !ctx.config.enable_self_on_the_fly { 33 if !ctx.config.enable_self_on_the_fly {
34 return;
35 }
36 if !ctx.is_trivial_path || ctx.is_path_disallowed() {
34 return; 37 return;
35 } 38 }
36 ctx.scope.process_all_names(&mut |name, def| { 39 ctx.scope.process_all_names(&mut |name, def| {
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index df27e7a84..d72bf13d3 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -162,19 +162,19 @@ pub(crate) fn position_for_import<'a>(
162 Some(match import_candidate { 162 Some(match import_candidate {
163 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(), 163 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
164 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(), 164 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(),
165 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.syntax(), 165 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
166 None => ctx 166 None => ctx
167 .name_ref_syntax 167 .name_ref_syntax
168 .as_ref() 168 .as_ref()
169 .map(|name_ref| name_ref.syntax()) 169 .map(|name_ref| name_ref.syntax())
170 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax())) 170 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax()))
171 .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?, 171 .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
172 }) 172 })
173} 173}
174 174
175fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> { 175fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
176 let current_module = ctx.scope.module()?; 176 let current_module = ctx.scope.module()?;
177 if let Some(dot_receiver) = &ctx.dot_receiver { 177 if let Some(dot_receiver) = ctx.dot_receiver() {
178 ImportAssets::for_fuzzy_method_call( 178 ImportAssets::for_fuzzy_method_call(
179 current_module, 179 current_module,
180 ctx.sema.type_of_expr(dot_receiver)?, 180 ctx.sema.type_of_expr(dot_receiver)?,
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 0d035c611..1a7a484a4 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -31,7 +31,7 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
31 } 31 }
32 32
33 // Suggest .await syntax for types that implement Future trait 33 // Suggest .await syntax for types that implement Future trait
34 if let Some(receiver) = &ctx.dot_receiver { 34 if let Some(receiver) = ctx.dot_receiver() {
35 if let Some(ty) = ctx.sema.type_of_expr(receiver) { 35 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
36 if ty.impls_future(ctx.db) { 36 if ty.impls_future(ctx.db) {
37 let mut item = kw_completion("await"); 37 let mut item = kw_completion("await");
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs
index 202e71215..781b96ff1 100644
--- a/crates/ide_completion/src/completions/macro_in_item_position.rs
+++ b/crates/ide_completion/src/completions/macro_in_item_position.rs
@@ -5,7 +5,7 @@ use crate::{CompletionContext, Completions};
5// Ideally this should be removed and moved into `(un)qualified_path` respectively 5// Ideally this should be removed and moved into `(un)qualified_path` respectively
6pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { 6pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
7 // Show only macros in top level. 7 // Show only macros in top level.
8 if !ctx.is_new_item { 8 if !ctx.expects_item() {
9 return; 9 return;
10 } 10 }
11 11
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 962aaf0df..86bbb58e2 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -14,6 +14,7 @@ use crate::{
14 completions::postfix::format_like::add_format_like_completions, 14 completions::postfix::format_like::add_format_like_completions,
15 context::CompletionContext, 15 context::CompletionContext,
16 item::{Builder, CompletionKind}, 16 item::{Builder, CompletionKind},
17 patterns::ImmediateLocation,
17 CompletionItem, CompletionItemKind, Completions, 18 CompletionItem, CompletionItemKind, Completions,
18}; 19};
19 20
@@ -22,13 +23,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
22 return; 23 return;
23 } 24 }
24 25
25 let dot_receiver = match &ctx.dot_receiver { 26 let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
26 Some(it) => it, 27 Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false),
27 None => return, 28 Some(ImmediateLocation::FieldAccess {
29 receiver: Some(it),
30 receiver_is_ambiguous_float_literal,
31 }) => (it, *receiver_is_ambiguous_float_literal),
32 _ => return,
28 }; 33 };
29 34
30 let receiver_text = 35 let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
31 get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
32 36
33 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 37 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
34 Some(it) => it, 38 Some(it) => it,
@@ -123,8 +127,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
123 // The rest of the postfix completions create an expression that moves an argument, 127 // The rest of the postfix completions create an expression that moves an argument,
124 // so it's better to consider references now to avoid breaking the compilation 128 // so it's better to consider references now to avoid breaking the compilation
125 let dot_receiver = include_references(dot_receiver); 129 let dot_receiver = include_references(dot_receiver);
126 let receiver_text = 130 let receiver_text = get_receiver_text(&dot_receiver, receiver_is_ambiguous_float_literal);
127 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
128 131
129 match try_enum { 132 match try_enum {
130 Some(try_enum) => match try_enum { 133 Some(try_enum) => match try_enum {
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index defc25b00..6e6a6eb92 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -29,7 +29,7 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
29} 29}
30 30
31pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { 31pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
32 if !ctx.is_new_item { 32 if !ctx.expects_item() {
33 return; 33 return;
34 } 34 }
35 let cap = match ctx.config.snippet_cap { 35 let cap = match ctx.config.snippet_cap {
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 20188a7dd..bd955aa85 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -385,10 +385,11 @@ fn foo() {
385fn foo() { let x: $0 } 385fn foo() { let x: $0 }
386 386
387//- /std/lib.rs crate:std 387//- /std/lib.rs crate:std
388#[prelude_import] 388pub mod prelude {
389use prelude::*; 389 pub mod rust_2018 {
390 390 pub struct Option;
391mod prelude { struct Option; } 391 }
392}
392"#, 393"#,
393 expect![[r#" 394 expect![[r#"
394 fn foo() fn() 395 fn foo() fn()
@@ -406,12 +407,10 @@ mod prelude { struct Option; }
406fn f() {$0} 407fn f() {$0}
407 408
408//- /std/lib.rs crate:std 409//- /std/lib.rs crate:std
409#[prelude_import] 410pub mod prelude {
410pub use prelude::*; 411 pub mod rust_2018 {
411 412 pub use crate::concat;
412#[macro_use] 413 }
413mod prelude {
414 pub use crate::concat;
415} 414}
416 415
417mod macros { 416mod macros {
@@ -436,16 +435,18 @@ mod macros {
436fn foo() { let x: $0 } 435fn foo() { let x: $0 }
437 436
438//- /core/lib.rs crate:core 437//- /core/lib.rs crate:core
439#[prelude_import] 438pub mod prelude {
440use prelude::*; 439 pub mod rust_2018 {
441 440 pub struct Option;
442mod prelude { struct Option; } 441 }
442}
443 443
444//- /std/lib.rs crate:std deps:core 444//- /std/lib.rs crate:std deps:core
445#[prelude_import] 445pub mod prelude {
446use prelude::*; 446 pub mod rust_2018 {
447 447 pub struct String;
448mod prelude { struct String; } 448 }
449}
449"#, 450"#,
450 expect![[r#" 451 expect![[r#"
451 fn foo() fn() 452 fn foo() fn()
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 7c46c815d..6f685c02f 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -78,11 +78,6 @@ pub(crate) struct CompletionContext<'a> {
78 pub(super) can_be_stmt: bool, 78 pub(super) can_be_stmt: bool,
79 /// `true` if we expect an expression at the cursor position. 79 /// `true` if we expect an expression at the cursor position.
80 pub(super) is_expr: bool, 80 pub(super) is_expr: bool,
81 /// Something is typed at the "top" level, in module or impl/trait.
82 pub(super) is_new_item: bool,
83 /// The receiver if this is a field or method access, i.e. writing something.$0
84 pub(super) dot_receiver: Option<ast::Expr>,
85 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
86 /// If this is a call (method or function) in particular, i.e. the () are already there. 81 /// If this is a call (method or function) in particular, i.e. the () are already there.
87 pub(super) is_call: bool, 82 pub(super) is_call: bool,
88 /// Like `is_call`, but for tuple patterns. 83 /// Like `is_call`, but for tuple patterns.
@@ -158,9 +153,6 @@ impl<'a> CompletionContext<'a> {
158 path_qual: None, 153 path_qual: None,
159 can_be_stmt: false, 154 can_be_stmt: false,
160 is_expr: false, 155 is_expr: false,
161 is_new_item: false,
162 dot_receiver: None,
163 dot_receiver_is_ambiguous_float_literal: false,
164 is_call: false, 156 is_call: false,
165 is_pattern_call: false, 157 is_pattern_call: false,
166 is_macro_call: false, 158 is_macro_call: false,
@@ -255,6 +247,22 @@ impl<'a> CompletionContext<'a> {
255 ) 247 )
256 } 248 }
257 249
250 pub(crate) fn has_dot_receiver(&self) -> bool {
251 matches!(
252 &self.completion_location,
253 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver })
254 if receiver.is_some()
255 )
256 }
257
258 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
259 match &self.completion_location {
260 Some(ImmediateLocation::MethodCall { receiver })
261 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(),
262 _ => None,
263 }
264 }
265
258 pub(crate) fn expects_use_tree(&self) -> bool { 266 pub(crate) fn expects_use_tree(&self) -> bool {
259 matches!(self.completion_location, Some(ImmediateLocation::Use)) 267 matches!(self.completion_location, Some(ImmediateLocation::Use))
260 } 268 }
@@ -267,6 +275,7 @@ impl<'a> CompletionContext<'a> {
267 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 275 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
268 } 276 }
269 277
278 // fn expects_value(&self) -> bool {
270 pub(crate) fn expects_expression(&self) -> bool { 279 pub(crate) fn expects_expression(&self) -> bool {
271 self.is_expr 280 self.is_expr
272 } 281 }
@@ -540,16 +549,7 @@ impl<'a> CompletionContext<'a> {
540 self.name_ref_syntax = 549 self.name_ref_syntax =
541 find_node_at_offset(original_file, name_ref.syntax().text_range().start()); 550 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
542 551
543 let name_range = name_ref.syntax().text_range(); 552 if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) {
544 let top_node = name_ref
545 .syntax()
546 .ancestors()
547 .take_while(|it| it.text_range() == name_range)
548 .last()
549 .unwrap();
550
551 if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) {
552 self.is_new_item = true;
553 return; 553 return;
554 } 554 }
555 555
@@ -623,33 +623,8 @@ impl<'a> CompletionContext<'a> {
623 .unwrap_or(false); 623 .unwrap_or(false);
624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
625 } 625 }
626 626 self.is_call |=
627 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 627 matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
628 // The receiver comes before the point of insertion of the fake
629 // ident, so it should have the same range in the non-modified file
630 self.dot_receiver = field_expr
631 .expr()
632 .map(|e| e.syntax().text_range())
633 .and_then(|r| find_node_with_range(original_file, r));
634 self.dot_receiver_is_ambiguous_float_literal =
635 if let Some(ast::Expr::Literal(l)) = &self.dot_receiver {
636 match l.kind() {
637 ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
638 _ => false,
639 }
640 } else {
641 false
642 };
643 }
644
645 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
646 // As above
647 self.dot_receiver = method_call_expr
648 .receiver()
649 .map(|e| e.syntax().text_range())
650 .and_then(|r| find_node_with_range(original_file, r));
651 self.is_call = true;
652 }
653 } 628 }
654} 629}
655 630
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 26516046b..080898aef 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -7,13 +7,13 @@ use syntax::{
7 ast::{self, LoopBodyOwner}, 7 ast::{self, LoopBodyOwner},
8 match_ast, AstNode, Direction, SyntaxElement, 8 match_ast, AstNode, Direction, SyntaxElement,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextSize, T, 10 SyntaxNode, SyntaxToken, TextRange, TextSize, T,
11}; 11};
12 12
13#[cfg(test)] 13#[cfg(test)]
14use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; 14use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
15 15
16/// Direct parent container of the cursor position 16/// Immediate previous node to what we are completing.
17#[derive(Copy, Clone, Debug, PartialEq, Eq)] 17#[derive(Copy, Clone, Debug, PartialEq, Eq)]
18pub(crate) enum ImmediatePrevSibling { 18pub(crate) enum ImmediatePrevSibling {
19 IfExpr, 19 IfExpr,
@@ -21,7 +21,7 @@ pub(crate) enum ImmediatePrevSibling {
21 ImplDefType, 21 ImplDefType,
22} 22}
23 23
24/// Direct parent container of the cursor position 24/// Direct parent "thing" of what we are currently completing.
25#[derive(Clone, Debug, PartialEq, Eq)] 25#[derive(Clone, Debug, PartialEq, Eq)]
26pub(crate) enum ImmediateLocation { 26pub(crate) enum ImmediateLocation {
27 Use, 27 Use,
@@ -37,6 +37,15 @@ pub(crate) enum ImmediateLocation {
37 // Fake file ast node 37 // Fake file ast node
38 ModDeclaration(ast::Module), 38 ModDeclaration(ast::Module),
39 // Original file ast node 39 // Original file ast node
40 MethodCall {
41 receiver: Option<ast::Expr>,
42 },
43 // Original file ast node
44 FieldAccess {
45 receiver: Option<ast::Expr>,
46 receiver_is_ambiguous_float_literal: bool,
47 },
48 // Original file ast node
40 /// The record expr of the field name we are completing 49 /// The record expr of the field name we are completing
41 RecordExpr(ast::RecordExpr), 50 RecordExpr(ast::RecordExpr),
42 // Original file ast node 51 // Original file ast node
@@ -164,12 +173,38 @@ pub(crate) fn determine_location(
164 Some(TRAIT) => ImmediateLocation::Trait, 173 Some(TRAIT) => ImmediateLocation::Trait,
165 _ => return None, 174 _ => return None,
166 }, 175 },
167 ast::Module(it) => if it.item_list().is_none() { 176 ast::Module(it) => {
177 if it.item_list().is_none() {
168 ImmediateLocation::ModDeclaration(it) 178 ImmediateLocation::ModDeclaration(it)
169 } else { 179 } else {
170 return None 180 return None;
181 }
171 }, 182 },
172 ast::Attr(it) => ImmediateLocation::Attribute(it), 183 ast::Attr(it) => ImmediateLocation::Attribute(it),
184 ast::FieldExpr(it) => {
185 let receiver = it
186 .expr()
187 .map(|e| e.syntax().text_range())
188 .and_then(|r| find_node_with_range(original_file, r));
189 let receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = &receiver {
190 match l.kind() {
191 ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
192 _ => false,
193 }
194 } else {
195 false
196 };
197 ImmediateLocation::FieldAccess {
198 receiver,
199 receiver_is_ambiguous_float_literal,
200 }
201 },
202 ast::MethodCallExpr(it) => ImmediateLocation::MethodCall {
203 receiver: it
204 .receiver()
205 .map(|e| e.syntax().text_range())
206 .and_then(|r| find_node_with_range(original_file, r)),
207 },
173 _ => return None, 208 _ => return None,
174 } 209 }
175 }; 210 };
@@ -194,6 +229,10 @@ fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode {
194 name_ref.syntax().clone() 229 name_ref.syntax().clone()
195} 230}
196 231
232fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
233 syntax.covering_element(range).ancestors().find_map(N::cast)
234}
235
197pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { 236pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
198 // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`, 237 // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
199 // where we only check the first parent with different text range. 238 // where we only check the first parent with different text range.
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 3ec77ca0f..1abeed96d 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -154,7 +154,7 @@ impl<'a> FunctionRender<'a> {
154 }; 154 };
155 155
156 let mut params_pats = Vec::new(); 156 let mut params_pats = Vec::new();
157 let params_ty = if self.ctx.completion.dot_receiver.is_some() || self.receiver.is_some() { 157 let params_ty = if self.ctx.completion.has_dot_receiver() || self.receiver.is_some() {
158 self.func.method_params(self.ctx.db()).unwrap_or_default() 158 self.func.method_params(self.ctx.db()).unwrap_or_default()
159 } else { 159 } else {
160 if let Some(s) = ast_params.self_param() { 160 if let Some(s) = ast_params.self_param() {
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs
index 29ae12dcf..312851966 100644
--- a/crates/ide_db/src/helpers/famous_defs_fixture.rs
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -128,17 +128,19 @@ pub mod option {
128} 128}
129 129
130pub mod prelude { 130pub mod prelude {
131 pub use crate::{ 131 pub mod rust_2018 {
132 cmp::Ord, 132 pub use crate::{
133 convert::{From, Into}, 133 cmp::Ord,
134 default::Default, 134 convert::{From, Into},
135 iter::{IntoIterator, Iterator}, 135 default::Default,
136 ops::{Fn, FnMut, FnOnce}, 136 iter::{IntoIterator, Iterator},
137 option::Option::{self, *}, 137 ops::{Fn, FnMut, FnOnce},
138 }; 138 option::Option::{self, *},
139 };
140 }
139} 141}
140#[prelude_import] 142#[prelude_import]
141pub use prelude::*; 143pub use prelude::rust_2018::*;
142//- /libstd.rs crate:std deps:core 144//- /libstd.rs crate:std deps:core
143//! Signatures of traits, types and functions from the std lib for use in tests. 145//! Signatures of traits, types and functions from the std lib for use in tests.
144 146
@@ -148,4 +150,4 @@ mod return_keyword {}
148/// Docs for prim_str 150/// Docs for prim_str
149mod prim_str {} 151mod prim_str {}
150 152
151pub use core::ops; \ No newline at end of file 153pub use core::ops;
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index ae78fd4f6..c33cdb740 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -92,6 +92,7 @@ config_data! {
92 checkOnSave_overrideCommand: Option<Vec<String>> = "null", 92 checkOnSave_overrideCommand: Option<Vec<String>> = "null",
93 93
94 /// Whether to add argument snippets when completing functions. 94 /// Whether to add argument snippets when completing functions.
95 /// Only applies when `#rust-analyzer.completion.addCallParenthesis#` is set.
95 completion_addCallArgumentSnippets: bool = "true", 96 completion_addCallArgumentSnippets: bool = "true",
96 /// Whether to add parenthesis when completing functions. 97 /// Whether to add parenthesis when completing functions.
97 completion_addCallParenthesis: bool = "true", 98 completion_addCallParenthesis: bool = "true",
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 0cf170626..4c3c9661d 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -580,12 +580,11 @@ pub fn fn_(
580pub fn struct_( 580pub fn struct_(
581 visibility: Option<ast::Visibility>, 581 visibility: Option<ast::Visibility>,
582 strukt_name: ast::Name, 582 strukt_name: ast::Name,
583 type_params: Option<ast::GenericParamList>, 583 generic_param_list: Option<ast::GenericParamList>,
584 field_list: ast::FieldList, 584 field_list: ast::FieldList,
585) -> ast::Struct { 585) -> ast::Struct {
586 let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" }; 586 let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" };
587 let type_params = 587 let type_params = generic_param_list.map_or_else(String::new, |it| it.to_string());
588 if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() };
589 let visibility = match visibility { 588 let visibility = match visibility {
590 None => String::new(), 589 None => String::new(),
591 Some(it) => format!("{} ", it), 590 Some(it) => format!("{} ", it),