diff options
78 files changed, 1606 insertions, 730 deletions
diff --git a/Cargo.lock b/Cargo.lock index 051d9e734..d8838cf2b 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -253,7 +253,6 @@ dependencies = [ | |||
253 | name = "completion" | 253 | name = "completion" |
254 | version = "0.0.0" | 254 | version = "0.0.0" |
255 | dependencies = [ | 255 | dependencies = [ |
256 | "assists", | ||
257 | "base_db", | 256 | "base_db", |
258 | "either", | 257 | "either", |
259 | "expect-test", | 258 | "expect-test", |
@@ -301,7 +300,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
301 | checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" | 300 | checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" |
302 | dependencies = [ | 301 | dependencies = [ |
303 | "cfg-if 1.0.0", | 302 | "cfg-if 1.0.0", |
304 | "crossbeam-utils 0.8.0", | 303 | "crossbeam-utils 0.8.1", |
305 | ] | 304 | ] |
306 | 305 | ||
307 | [[package]] | 306 | [[package]] |
@@ -312,18 +311,18 @@ checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" | |||
312 | dependencies = [ | 311 | dependencies = [ |
313 | "cfg-if 1.0.0", | 312 | "cfg-if 1.0.0", |
314 | "crossbeam-epoch", | 313 | "crossbeam-epoch", |
315 | "crossbeam-utils 0.8.0", | 314 | "crossbeam-utils 0.8.1", |
316 | ] | 315 | ] |
317 | 316 | ||
318 | [[package]] | 317 | [[package]] |
319 | name = "crossbeam-epoch" | 318 | name = "crossbeam-epoch" |
320 | version = "0.9.0" | 319 | version = "0.9.1" |
321 | source = "registry+https://github.com/rust-lang/crates.io-index" | 320 | source = "registry+https://github.com/rust-lang/crates.io-index" |
322 | checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f" | 321 | checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" |
323 | dependencies = [ | 322 | dependencies = [ |
324 | "cfg-if 1.0.0", | 323 | "cfg-if 1.0.0", |
325 | "const_fn", | 324 | "const_fn", |
326 | "crossbeam-utils 0.8.0", | 325 | "crossbeam-utils 0.8.1", |
327 | "lazy_static", | 326 | "lazy_static", |
328 | "memoffset", | 327 | "memoffset", |
329 | "scopeguard", | 328 | "scopeguard", |
@@ -342,13 +341,12 @@ dependencies = [ | |||
342 | 341 | ||
343 | [[package]] | 342 | [[package]] |
344 | name = "crossbeam-utils" | 343 | name = "crossbeam-utils" |
345 | version = "0.8.0" | 344 | version = "0.8.1" |
346 | source = "registry+https://github.com/rust-lang/crates.io-index" | 345 | source = "registry+https://github.com/rust-lang/crates.io-index" |
347 | checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5" | 346 | checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" |
348 | dependencies = [ | 347 | dependencies = [ |
349 | "autocfg", | 348 | "autocfg", |
350 | "cfg-if 1.0.0", | 349 | "cfg-if 1.0.0", |
351 | "const_fn", | ||
352 | "lazy_static", | 350 | "lazy_static", |
353 | ] | 351 | ] |
354 | 352 | ||
@@ -682,6 +680,7 @@ dependencies = [ | |||
682 | "expect-test", | 680 | "expect-test", |
683 | "fst", | 681 | "fst", |
684 | "hir", | 682 | "hir", |
683 | "itertools", | ||
685 | "log", | 684 | "log", |
686 | "once_cell", | 685 | "once_cell", |
687 | "profile", | 686 | "profile", |
@@ -865,9 +864,9 @@ dependencies = [ | |||
865 | 864 | ||
866 | [[package]] | 865 | [[package]] |
867 | name = "lsp-types" | 866 | name = "lsp-types" |
868 | version = "0.84.0" | 867 | version = "0.85.0" |
869 | source = "registry+https://github.com/rust-lang/crates.io-index" | 868 | source = "registry+https://github.com/rust-lang/crates.io-index" |
870 | checksum = "3b95be71fe205e44de754185bcf86447b65813ce1ceb298f8d3793ade5fff08d" | 869 | checksum = "857650f3e83fb62f89d15410414e0ed7d0735445020da398d37f65d20a5423b9" |
871 | dependencies = [ | 870 | dependencies = [ |
872 | "base64", | 871 | "base64", |
873 | "bitflags", | 872 | "bitflags", |
@@ -929,9 +928,9 @@ dependencies = [ | |||
929 | 928 | ||
930 | [[package]] | 929 | [[package]] |
931 | name = "memoffset" | 930 | name = "memoffset" |
932 | version = "0.5.6" | 931 | version = "0.6.1" |
933 | source = "registry+https://github.com/rust-lang/crates.io-index" | 932 | source = "registry+https://github.com/rust-lang/crates.io-index" |
934 | checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" | 933 | checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" |
935 | dependencies = [ | 934 | dependencies = [ |
936 | "autocfg", | 935 | "autocfg", |
937 | ] | 936 | ] |
@@ -988,9 +987,9 @@ dependencies = [ | |||
988 | 987 | ||
989 | [[package]] | 988 | [[package]] |
990 | name = "miow" | 989 | name = "miow" |
991 | version = "0.2.1" | 990 | version = "0.2.2" |
992 | source = "registry+https://github.com/rust-lang/crates.io-index" | 991 | source = "registry+https://github.com/rust-lang/crates.io-index" |
993 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" | 992 | checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" |
994 | dependencies = [ | 993 | dependencies = [ |
995 | "kernel32-sys", | 994 | "kernel32-sys", |
996 | "net2", | 995 | "net2", |
@@ -1000,9 +999,9 @@ dependencies = [ | |||
1000 | 999 | ||
1001 | [[package]] | 1000 | [[package]] |
1002 | name = "net2" | 1001 | name = "net2" |
1003 | version = "0.2.35" | 1002 | version = "0.2.36" |
1004 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1005 | checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" | 1004 | checksum = "d7cf75f38f16cb05ea017784dc6dbfd354f76c223dba37701734c4f5a9337d02" |
1006 | dependencies = [ | 1005 | dependencies = [ |
1007 | "cfg-if 0.1.10", | 1006 | "cfg-if 0.1.10", |
1008 | "libc", | 1007 | "libc", |
@@ -1298,7 +1297,7 @@ checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" | |||
1298 | dependencies = [ | 1297 | dependencies = [ |
1299 | "crossbeam-channel 0.5.0", | 1298 | "crossbeam-channel 0.5.0", |
1300 | "crossbeam-deque", | 1299 | "crossbeam-deque", |
1301 | "crossbeam-utils 0.8.0", | 1300 | "crossbeam-utils 0.8.1", |
1302 | "lazy_static", | 1301 | "lazy_static", |
1303 | "num_cpus", | 1302 | "num_cpus", |
1304 | ] | 1303 | ] |
@@ -1396,9 +1395,9 @@ dependencies = [ | |||
1396 | 1395 | ||
1397 | [[package]] | 1396 | [[package]] |
1398 | name = "rustc-ap-rustc_lexer" | 1397 | name = "rustc-ap-rustc_lexer" |
1399 | version = "688.0.0" | 1398 | version = "691.0.0" |
1400 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1399 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1401 | checksum = "ebbdcc99bd015349093fcbae4780fda21416fec5d8843acfb3d1733e130cd4db" | 1400 | checksum = "44bc89d9ca7a78fb82e103b389362c55f03800745f8ba14e068b805cfaf783ec" |
1402 | dependencies = [ | 1401 | dependencies = [ |
1403 | "unicode-xid", | 1402 | "unicode-xid", |
1404 | ] | 1403 | ] |
@@ -1436,7 +1435,7 @@ version = "0.16.0" | |||
1436 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1437 | checksum = "d8fadca2ab5de17acf66d744f4888049ca8f1bb9b8a1ab8afd9d032cc959c5dc" | 1436 | checksum = "d8fadca2ab5de17acf66d744f4888049ca8f1bb9b8a1ab8afd9d032cc959c5dc" |
1438 | dependencies = [ | 1437 | dependencies = [ |
1439 | "crossbeam-utils 0.8.0", | 1438 | "crossbeam-utils 0.8.1", |
1440 | "indexmap", | 1439 | "indexmap", |
1441 | "lock_api", | 1440 | "lock_api", |
1442 | "log", | 1441 | "log", |
@@ -1627,9 +1626,9 @@ version = "0.0.0" | |||
1627 | 1626 | ||
1628 | [[package]] | 1627 | [[package]] |
1629 | name = "syn" | 1628 | name = "syn" |
1630 | version = "1.0.51" | 1629 | version = "1.0.53" |
1631 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1630 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1632 | checksum = "3b4f34193997d92804d359ed09953e25d5138df6bcc055a71bf68ee89fdf9223" | 1631 | checksum = "8833e20724c24de12bbaba5ad230ea61c3eafb05b881c7c9d3cfe8638b187e68" |
1633 | dependencies = [ | 1632 | dependencies = [ |
1634 | "proc-macro2", | 1633 | "proc-macro2", |
1635 | "quote", | 1634 | "quote", |
@@ -1658,6 +1657,7 @@ dependencies = [ | |||
1658 | "itertools", | 1657 | "itertools", |
1659 | "once_cell", | 1658 | "once_cell", |
1660 | "parser", | 1659 | "parser", |
1660 | "profile", | ||
1661 | "rayon", | 1661 | "rayon", |
1662 | "rowan", | 1662 | "rowan", |
1663 | "rustc-ap-rustc_lexer", | 1663 | "rustc-ap-rustc_lexer", |
@@ -1898,9 +1898,9 @@ dependencies = [ | |||
1898 | 1898 | ||
1899 | [[package]] | 1899 | [[package]] |
1900 | name = "unicode-segmentation" | 1900 | name = "unicode-segmentation" |
1901 | version = "1.7.0" | 1901 | version = "1.7.1" |
1902 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1902 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1903 | checksum = "db8716a166f290ff49dabc18b44aa407cb7c6dbe1aa0971b44b8a24b0ca35aae" | 1903 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" |
1904 | 1904 | ||
1905 | [[package]] | 1905 | [[package]] |
1906 | name = "unicode-xid" | 1906 | name = "unicode-xid" |
diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml index 108f656e9..3fd8327d6 100644 --- a/crates/assists/Cargo.toml +++ b/crates/assists/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | [dependencies] | 12 | [dependencies] |
13 | rustc-hash = "1.1.0" | 13 | rustc-hash = "1.1.0" |
14 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
15 | either = "1.5.3" | 15 | either = "1.6.1" |
16 | 16 | ||
17 | stdx = { path = "../stdx", version = "0.0.0" } | 17 | stdx = { path = "../stdx", version = "0.0.0" } |
18 | syntax = { path = "../syntax", version = "0.0.0" } | 18 | syntax = { path = "../syntax", version = "0.0.0" } |
diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs index b24527ec4..786224cfa 100644 --- a/crates/assists/src/assist_config.rs +++ b/crates/assists/src/assist_config.rs | |||
@@ -5,8 +5,9 @@ | |||
5 | //! assists if we are allowed to. | 5 | //! assists if we are allowed to. |
6 | 6 | ||
7 | use hir::PrefixKind; | 7 | use hir::PrefixKind; |
8 | use ide_db::helpers::insert_use::MergeBehaviour; | ||
8 | 9 | ||
9 | use crate::{utils::MergeBehaviour, AssistKind}; | 10 | use crate::AssistKind; |
10 | 11 | ||
11 | #[derive(Clone, Debug, PartialEq, Eq)] | 12 | #[derive(Clone, Debug, PartialEq, Eq)] |
12 | pub struct AssistConfig { | 13 | pub struct AssistConfig { |
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs index ac72f3f02..66e4634b1 100644 --- a/crates/assists/src/ast_transform.rs +++ b/crates/assists/src/ast_transform.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. | 1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. |
2 | use hir::{HirDisplay, PathResolution, SemanticsScope}; | 2 | use hir::{HirDisplay, PathResolution, SemanticsScope}; |
3 | use ide_db::helpers::mod_path_to_ast; | ||
3 | use rustc_hash::FxHashMap; | 4 | use rustc_hash::FxHashMap; |
4 | use syntax::{ | 5 | use syntax::{ |
5 | algo::SyntaxRewriter, | 6 | algo::SyntaxRewriter, |
@@ -7,8 +8,6 @@ use syntax::{ | |||
7 | SyntaxNode, | 8 | SyntaxNode, |
8 | }; | 9 | }; |
9 | 10 | ||
10 | use crate::utils::mod_path_to_ast; | ||
11 | |||
12 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { | 11 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { |
13 | SyntaxRewriter::from_fn(|element| match element { | 12 | SyntaxRewriter::from_fn(|element| match element { |
14 | syntax::SyntaxElement::Node(n) => { | 13 | syntax::SyntaxElement::Node(n) => { |
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs index d665837a2..bd5bba646 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs | |||
@@ -1,8 +1,11 @@ | |||
1 | use ide_db::helpers::{ | ||
2 | insert_use::{insert_use, ImportScope}, | ||
3 | mod_path_to_ast, | ||
4 | }; | ||
1 | use syntax::ast; | 5 | use syntax::ast; |
2 | 6 | ||
3 | use crate::{ | 7 | use crate::{ |
4 | utils::import_assets::{ImportAssets, ImportCandidate}, | 8 | utils::import_assets::{ImportAssets, ImportCandidate}, |
5 | utils::{insert_use, mod_path_to_ast, ImportScope}, | ||
6 | AssistContext, AssistId, AssistKind, Assists, GroupLabel, | 9 | AssistContext, AssistId, AssistKind, Assists, GroupLabel, |
7 | }; | 10 | }; |
8 | 11 | ||
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index cac77c49b..d85767b4e 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -2,6 +2,10 @@ use std::iter; | |||
2 | 2 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; | 4 | use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; |
5 | use ide_db::helpers::{ | ||
6 | insert_use::{insert_use, ImportScope}, | ||
7 | mod_path_to_ast, | ||
8 | }; | ||
5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; | 9 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; |
6 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
7 | use syntax::{ | 11 | use syntax::{ |
@@ -10,10 +14,7 @@ use syntax::{ | |||
10 | SourceFile, SyntaxElement, SyntaxNode, T, | 14 | SourceFile, SyntaxElement, SyntaxNode, T, |
11 | }; | 15 | }; |
12 | 16 | ||
13 | use crate::{ | 17 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
14 | utils::{insert_use, mod_path_to_ast, ImportScope}, | ||
15 | AssistContext, AssistId, AssistKind, Assists, | ||
16 | }; | ||
17 | 18 | ||
18 | // Assist: extract_struct_from_enum_variant | 19 | // Assist: extract_struct_from_enum_variant |
19 | // | 20 | // |
@@ -236,10 +237,9 @@ fn update_reference( | |||
236 | 237 | ||
237 | #[cfg(test)] | 238 | #[cfg(test)] |
238 | mod tests { | 239 | mod tests { |
239 | use crate::{ | 240 | use ide_db::helpers::FamousDefs; |
240 | tests::{check_assist, check_assist_not_applicable}, | 241 | |
241 | utils::FamousDefs, | 242 | use crate::tests::{check_assist, check_assist_not_applicable}; |
242 | }; | ||
243 | 243 | ||
244 | use super::*; | 244 | use super::*; |
245 | 245 | ||
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs index eda45f5b3..ef12ef0cf 100644 --- a/crates/assists/src/handlers/fill_match_arms.rs +++ b/crates/assists/src/handlers/fill_match_arms.rs | |||
@@ -1,13 +1,14 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use hir::{Adt, HasSource, ModuleDef, Semantics}; | 3 | use hir::{Adt, HasSource, ModuleDef, Semantics}; |
4 | use ide_db::helpers::{mod_path_to_ast, FamousDefs}; | ||
4 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
5 | use itertools::Itertools; | 6 | use itertools::Itertools; |
6 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | 7 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; |
7 | use test_utils::mark; | 8 | use test_utils::mark; |
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | utils::{mod_path_to_ast, render_snippet, Cursor, FamousDefs}, | 11 | utils::{render_snippet, Cursor}, |
11 | AssistContext, AssistId, AssistKind, Assists, | 12 | AssistContext, AssistId, AssistKind, Assists, |
12 | }; | 13 | }; |
13 | 14 | ||
@@ -212,12 +213,10 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O | |||
212 | 213 | ||
213 | #[cfg(test)] | 214 | #[cfg(test)] |
214 | mod tests { | 215 | mod tests { |
216 | use ide_db::helpers::FamousDefs; | ||
215 | use test_utils::mark; | 217 | use test_utils::mark; |
216 | 218 | ||
217 | use crate::{ | 219 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
218 | tests::{check_assist, check_assist_not_applicable, check_assist_target}, | ||
219 | utils::FamousDefs, | ||
220 | }; | ||
221 | 220 | ||
222 | use super::fill_match_arms; | 221 | use super::fill_match_arms; |
223 | 222 | ||
diff --git a/crates/assists/src/handlers/generate_from_impl_for_enum.rs b/crates/assists/src/handlers/generate_from_impl_for_enum.rs index 674e5a175..3c374e5d9 100644 --- a/crates/assists/src/handlers/generate_from_impl_for_enum.rs +++ b/crates/assists/src/handlers/generate_from_impl_for_enum.rs | |||
@@ -1,8 +1,9 @@ | |||
1 | use ide_db::helpers::FamousDefs; | ||
1 | use ide_db::RootDatabase; | 2 | use ide_db::RootDatabase; |
2 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
3 | use test_utils::mark; | 4 | use test_utils::mark; |
4 | 5 | ||
5 | use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 7 | ||
7 | // Assist: generate_from_impl_for_enum | 8 | // Assist: generate_from_impl_for_enum |
8 | // | 9 | // |
diff --git a/crates/assists/src/handlers/infer_function_return_type.rs b/crates/assists/src/handlers/infer_function_return_type.rs index 520d07ae0..aa584eb03 100644 --- a/crates/assists/src/handlers/infer_function_return_type.rs +++ b/crates/assists/src/handlers/infer_function_return_type.rs | |||
@@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; | |||
17 | // fn foo() -> i32 { 42i32 } | 17 | // fn foo() -> i32 { 42i32 } |
18 | // ``` | 18 | // ``` |
19 | pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 19 | pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
20 | let (tail_expr, builder_edit_pos, wrap_expr) = extract_tail(ctx)?; | 20 | let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?; |
21 | let module = ctx.sema.scope(tail_expr.syntax()).module()?; | 21 | let module = ctx.sema.scope(tail_expr.syntax()).module()?; |
22 | let ty = ctx.sema.type_of_expr(&tail_expr)?; | 22 | let ty = ctx.sema.type_of_expr(&tail_expr)?; |
23 | if ty.is_unit() { | 23 | if ty.is_unit() { |
@@ -27,7 +27,10 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) | |||
27 | 27 | ||
28 | acc.add( | 28 | acc.add( |
29 | AssistId("infer_function_return_type", AssistKind::RefactorRewrite), | 29 | AssistId("infer_function_return_type", AssistKind::RefactorRewrite), |
30 | "Add this function's return type", | 30 | match fn_type { |
31 | FnType::Function => "Add this function's return type", | ||
32 | FnType::Closure { .. } => "Add this closure's return type", | ||
33 | }, | ||
31 | tail_expr.syntax().text_range(), | 34 | tail_expr.syntax().text_range(), |
32 | |builder| { | 35 | |builder| { |
33 | match builder_edit_pos { | 36 | match builder_edit_pos { |
@@ -38,7 +41,7 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) | |||
38 | builder.replace(text_range, &format!("-> {}", ty)) | 41 | builder.replace(text_range, &format!("-> {}", ty)) |
39 | } | 42 | } |
40 | } | 43 | } |
41 | if wrap_expr { | 44 | if let FnType::Closure { wrap_expr: true } = fn_type { |
42 | mark::hit!(wrap_closure_non_block_expr); | 45 | mark::hit!(wrap_closure_non_block_expr); |
43 | // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block | 46 | // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block |
44 | builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); | 47 | builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); |
@@ -72,8 +75,13 @@ fn ret_ty_to_action(ret_ty: Option<ast::RetType>, insert_pos: TextSize) -> Optio | |||
72 | } | 75 | } |
73 | } | 76 | } |
74 | 77 | ||
75 | fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool)> { | 78 | enum FnType { |
76 | let (tail_expr, return_type_range, action, wrap_expr) = | 79 | Function, |
80 | Closure { wrap_expr: bool }, | ||
81 | } | ||
82 | |||
83 | fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrReplace)> { | ||
84 | let (fn_type, tail_expr, return_type_range, action) = | ||
77 | if let Some(closure) = ctx.find_node_at_offset::<ast::ClosureExpr>() { | 85 | if let Some(closure) = ctx.find_node_at_offset::<ast::ClosureExpr>() { |
78 | let rpipe_pos = closure.param_list()?.syntax().last_token()?.text_range().end(); | 86 | let rpipe_pos = closure.param_list()?.syntax().last_token()?.text_range().end(); |
79 | let action = ret_ty_to_action(closure.ret_type(), rpipe_pos)?; | 87 | let action = ret_ty_to_action(closure.ret_type(), rpipe_pos)?; |
@@ -86,7 +94,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool | |||
86 | }; | 94 | }; |
87 | 95 | ||
88 | let ret_range = TextRange::new(rpipe_pos, body_start); | 96 | let ret_range = TextRange::new(rpipe_pos, body_start); |
89 | (tail_expr, ret_range, action, wrap_expr) | 97 | (FnType::Closure { wrap_expr }, tail_expr, ret_range, action) |
90 | } else { | 98 | } else { |
91 | let func = ctx.find_node_at_offset::<ast::Fn>()?; | 99 | let func = ctx.find_node_at_offset::<ast::Fn>()?; |
92 | let rparen_pos = func.param_list()?.r_paren_token()?.text_range().end(); | 100 | let rparen_pos = func.param_list()?.r_paren_token()?.text_range().end(); |
@@ -97,7 +105,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool | |||
97 | 105 | ||
98 | let ret_range_end = body.l_curly_token()?.text_range().start(); | 106 | let ret_range_end = body.l_curly_token()?.text_range().start(); |
99 | let ret_range = TextRange::new(rparen_pos, ret_range_end); | 107 | let ret_range = TextRange::new(rparen_pos, ret_range_end); |
100 | (tail_expr, ret_range, action, false) | 108 | (FnType::Function, tail_expr, ret_range, action) |
101 | }; | 109 | }; |
102 | let frange = ctx.frange.range; | 110 | let frange = ctx.frange.range; |
103 | if return_type_range.contains_range(frange) { | 111 | if return_type_range.contains_range(frange) { |
@@ -109,7 +117,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool | |||
109 | } else { | 117 | } else { |
110 | return None; | 118 | return None; |
111 | } | 119 | } |
112 | Some((tail_expr, action, wrap_expr)) | 120 | Some((fn_type, tail_expr, action)) |
113 | } | 121 | } |
114 | 122 | ||
115 | #[cfg(test)] | 123 | #[cfg(test)] |
diff --git a/crates/assists/src/handlers/merge_imports.rs b/crates/assists/src/handlers/merge_imports.rs index fd9c9e03c..b7e853994 100644 --- a/crates/assists/src/handlers/merge_imports.rs +++ b/crates/assists/src/handlers/merge_imports.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use ide_db::helpers::insert_use::{try_merge_imports, try_merge_trees, MergeBehaviour}; | ||
1 | use syntax::{ | 2 | use syntax::{ |
2 | algo::{neighbor, SyntaxRewriter}, | 3 | algo::{neighbor, SyntaxRewriter}, |
3 | ast, AstNode, | 4 | ast, AstNode, |
@@ -5,10 +6,7 @@ use syntax::{ | |||
5 | 6 | ||
6 | use crate::{ | 7 | use crate::{ |
7 | assist_context::{AssistContext, Assists}, | 8 | assist_context::{AssistContext, Assists}, |
8 | utils::{ | 9 | utils::next_prev, |
9 | insert_use::{try_merge_imports, try_merge_trees}, | ||
10 | next_prev, MergeBehaviour, | ||
11 | }, | ||
12 | AssistId, AssistKind, | 10 | AssistId, AssistKind, |
13 | }; | 11 | }; |
14 | 12 | ||
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs index d5bc4e574..6f9810fe8 100644 --- a/crates/assists/src/handlers/qualify_path.rs +++ b/crates/assists/src/handlers/qualify_path.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use hir::AsName; | 3 | use hir::AsName; |
4 | use ide_db::helpers::mod_path_to_ast; | ||
4 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
5 | use syntax::{ | 6 | use syntax::{ |
6 | ast, | 7 | ast, |
@@ -12,7 +13,6 @@ use test_utils::mark; | |||
12 | use crate::{ | 13 | use crate::{ |
13 | assist_context::{AssistContext, Assists}, | 14 | assist_context::{AssistContext, Assists}, |
14 | utils::import_assets::{ImportAssets, ImportCandidate}, | 15 | utils::import_assets::{ImportAssets, ImportCandidate}, |
15 | utils::mod_path_to_ast, | ||
16 | AssistId, AssistKind, GroupLabel, | 16 | AssistId, AssistKind, GroupLabel, |
17 | }; | 17 | }; |
18 | 18 | ||
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs index 453a6cebf..4d6a1956b 100644 --- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use ide_db::helpers::mod_path_to_ast; | ||
1 | use ide_db::imports_locator; | 2 | use ide_db::imports_locator; |
2 | use itertools::Itertools; | 3 | use itertools::Itertools; |
3 | use syntax::{ | 4 | use syntax::{ |
@@ -10,8 +11,7 @@ use syntax::{ | |||
10 | use crate::{ | 11 | use crate::{ |
11 | assist_context::{AssistBuilder, AssistContext, Assists}, | 12 | assist_context::{AssistBuilder, AssistContext, Assists}, |
12 | utils::{ | 13 | utils::{ |
13 | add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor, | 14 | add_trait_assoc_items_to_impl, filter_assoc_items, render_snippet, Cursor, DefaultMethods, |
14 | DefaultMethods, | ||
15 | }, | 15 | }, |
16 | AssistId, AssistKind, | 16 | AssistId, AssistKind, |
17 | }; | 17 | }; |
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs index a66db9ae3..8193e45a8 100644 --- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -1,10 +1,8 @@ | |||
1 | use ide_db::helpers::insert_use::{insert_use, ImportScope}; | ||
1 | use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; | 2 | use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; |
2 | use test_utils::mark; | 3 | use test_utils::mark; |
3 | 4 | ||
4 | use crate::{ | 5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
5 | utils::{insert_use, ImportScope}, | ||
6 | AssistContext, AssistId, AssistKind, Assists, | ||
7 | }; | ||
8 | 6 | ||
9 | // Assist: replace_qualified_name_with_use | 7 | // Assist: replace_qualified_name_with_use |
10 | // | 8 | // |
@@ -409,7 +407,7 @@ impl std::fmt::Display<|> for Foo { | |||
409 | } | 407 | } |
410 | ", | 408 | ", |
411 | r" | 409 | r" |
412 | use std::fmt::{nested::Debug, Display}; | 410 | use std::fmt::{Display, nested::Debug}; |
413 | 411 | ||
414 | impl Display for Foo { | 412 | impl Display for Foo { |
415 | } | 413 | } |
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/toggle_ignore.rs index 5096a0005..14b420421 100644 --- a/crates/assists/src/handlers/ignore_test.rs +++ b/crates/assists/src/handlers/toggle_ignore.rs | |||
@@ -5,7 +5,7 @@ use syntax::{ | |||
5 | 5 | ||
6 | use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; |
7 | 7 | ||
8 | // Assist: ignore_test | 8 | // Assist: toggle_ignore |
9 | // | 9 | // |
10 | // Adds `#[ignore]` attribute to the test. | 10 | // Adds `#[ignore]` attribute to the test. |
11 | // | 11 | // |
@@ -23,20 +23,20 @@ use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, | |||
23 | // assert_eq!(2 + 2, 5); | 23 | // assert_eq!(2 + 2, 5); |
24 | // } | 24 | // } |
25 | // ``` | 25 | // ``` |
26 | pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 26 | pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
27 | let attr: ast::Attr = ctx.find_node_at_offset()?; | 27 | let attr: ast::Attr = ctx.find_node_at_offset()?; |
28 | let func = attr.syntax().parent().and_then(ast::Fn::cast)?; | 28 | let func = attr.syntax().parent().and_then(ast::Fn::cast)?; |
29 | let attr = test_related_attribute(&func)?; | 29 | let attr = test_related_attribute(&func)?; |
30 | 30 | ||
31 | match has_ignore_attribute(&func) { | 31 | match has_ignore_attribute(&func) { |
32 | None => acc.add( | 32 | None => acc.add( |
33 | AssistId("ignore_test", AssistKind::None), | 33 | AssistId("toggle_ignore", AssistKind::None), |
34 | "Ignore this test", | 34 | "Ignore this test", |
35 | attr.syntax().text_range(), | 35 | attr.syntax().text_range(), |
36 | |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")), | 36 | |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")), |
37 | ), | 37 | ), |
38 | Some(ignore_attr) => acc.add( | 38 | Some(ignore_attr) => acc.add( |
39 | AssistId("unignore_test", AssistKind::None), | 39 | AssistId("toggle_ignore", AssistKind::None), |
40 | "Re-enable this test", | 40 | "Re-enable this test", |
41 | ignore_attr.syntax().text_range(), | 41 | ignore_attr.syntax().text_range(), |
42 | |builder| { | 42 | |builder| { |
@@ -55,24 +55,19 @@ pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
55 | } | 55 | } |
56 | 56 | ||
57 | fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { | 57 | fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { |
58 | fn_def.attrs().find_map(|attr| { | 58 | fn_def.attrs().find(|attr| attr.path().map(|it| it.syntax().text() == "ignore") == Some(true)) |
59 | if attr.path()?.syntax().text() == "ignore" { | ||
60 | Some(attr) | ||
61 | } else { | ||
62 | None | ||
63 | } | ||
64 | }) | ||
65 | } | 59 | } |
66 | 60 | ||
67 | #[cfg(test)] | 61 | #[cfg(test)] |
68 | mod tests { | 62 | mod tests { |
69 | use super::ignore_test; | ||
70 | use crate::tests::check_assist; | 63 | use crate::tests::check_assist; |
71 | 64 | ||
65 | use super::*; | ||
66 | |||
72 | #[test] | 67 | #[test] |
73 | fn test_base_case() { | 68 | fn test_base_case() { |
74 | check_assist( | 69 | check_assist( |
75 | ignore_test, | 70 | toggle_ignore, |
76 | r#" | 71 | r#" |
77 | #[test<|>] | 72 | #[test<|>] |
78 | fn test() {} | 73 | fn test() {} |
@@ -88,7 +83,7 @@ mod tests { | |||
88 | #[test] | 83 | #[test] |
89 | fn test_unignore() { | 84 | fn test_unignore() { |
90 | check_assist( | 85 | check_assist( |
91 | ignore_test, | 86 | toggle_ignore, |
92 | r#" | 87 | r#" |
93 | #[test<|>] | 88 | #[test<|>] |
94 | #[ignore] | 89 | #[ignore] |
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 17e9312db..dfe6c2729 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -141,7 +141,6 @@ mod handlers { | |||
141 | mod generate_function; | 141 | mod generate_function; |
142 | mod generate_impl; | 142 | mod generate_impl; |
143 | mod generate_new; | 143 | mod generate_new; |
144 | mod ignore_test; | ||
145 | mod infer_function_return_type; | 144 | mod infer_function_return_type; |
146 | mod inline_local_variable; | 145 | mod inline_local_variable; |
147 | mod introduce_named_lifetime; | 146 | mod introduce_named_lifetime; |
@@ -164,6 +163,7 @@ mod handlers { | |||
164 | mod replace_string_with_char; | 163 | mod replace_string_with_char; |
165 | mod replace_unwrap_with_match; | 164 | mod replace_unwrap_with_match; |
166 | mod split_import; | 165 | mod split_import; |
166 | mod toggle_ignore; | ||
167 | mod unwrap_block; | 167 | mod unwrap_block; |
168 | mod wrap_return_type_in_result; | 168 | mod wrap_return_type_in_result; |
169 | 169 | ||
@@ -190,7 +190,6 @@ mod handlers { | |||
190 | generate_function::generate_function, | 190 | generate_function::generate_function, |
191 | generate_impl::generate_impl, | 191 | generate_impl::generate_impl, |
192 | generate_new::generate_new, | 192 | generate_new::generate_new, |
193 | ignore_test::ignore_test, | ||
194 | infer_function_return_type::infer_function_return_type, | 193 | infer_function_return_type::infer_function_return_type, |
195 | inline_local_variable::inline_local_variable, | 194 | inline_local_variable::inline_local_variable, |
196 | introduce_named_lifetime::introduce_named_lifetime, | 195 | introduce_named_lifetime::introduce_named_lifetime, |
@@ -215,6 +214,7 @@ mod handlers { | |||
215 | replace_qualified_name_with_use::replace_qualified_name_with_use, | 214 | replace_qualified_name_with_use::replace_qualified_name_with_use, |
216 | replace_unwrap_with_match::replace_unwrap_with_match, | 215 | replace_unwrap_with_match::replace_unwrap_with_match, |
217 | split_import::split_import, | 216 | split_import::split_import, |
217 | toggle_ignore::toggle_ignore, | ||
218 | unwrap_block::unwrap_block, | 218 | unwrap_block::unwrap_block, |
219 | wrap_return_type_in_result::wrap_return_type_in_result, | 219 | wrap_return_type_in_result::wrap_return_type_in_result, |
220 | // These are manually sorted for better priorities | 220 | // These are manually sorted for better priorities |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index 5a9d1a01b..8d50c8791 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -474,26 +474,6 @@ impl<T: Clone> Ctx<T> { | |||
474 | } | 474 | } |
475 | 475 | ||
476 | #[test] | 476 | #[test] |
477 | fn doctest_ignore_test() { | ||
478 | check_doc_test( | ||
479 | "ignore_test", | ||
480 | r#####" | ||
481 | <|>#[test] | ||
482 | fn arithmetics { | ||
483 | assert_eq!(2 + 2, 5); | ||
484 | } | ||
485 | "#####, | ||
486 | r#####" | ||
487 | #[test] | ||
488 | #[ignore] | ||
489 | fn arithmetics { | ||
490 | assert_eq!(2 + 2, 5); | ||
491 | } | ||
492 | "#####, | ||
493 | ) | ||
494 | } | ||
495 | |||
496 | #[test] | ||
497 | fn doctest_infer_function_return_type() { | 477 | fn doctest_infer_function_return_type() { |
498 | check_doc_test( | 478 | check_doc_test( |
499 | "infer_function_return_type", | 479 | "infer_function_return_type", |
@@ -979,6 +959,26 @@ use std::{collections::HashMap}; | |||
979 | } | 959 | } |
980 | 960 | ||
981 | #[test] | 961 | #[test] |
962 | fn doctest_toggle_ignore() { | ||
963 | check_doc_test( | ||
964 | "toggle_ignore", | ||
965 | r#####" | ||
966 | <|>#[test] | ||
967 | fn arithmetics { | ||
968 | assert_eq!(2 + 2, 5); | ||
969 | } | ||
970 | "#####, | ||
971 | r#####" | ||
972 | #[test] | ||
973 | #[ignore] | ||
974 | fn arithmetics { | ||
975 | assert_eq!(2 + 2, 5); | ||
976 | } | ||
977 | "#####, | ||
978 | ) | ||
979 | } | ||
980 | |||
981 | #[test] | ||
982 | fn doctest_unwrap_block() { | 982 | fn doctest_unwrap_block() { |
983 | check_doc_test( | 983 | check_doc_test( |
984 | "unwrap_block", | 984 | "unwrap_block", |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 66c0cdd5f..01f5c291f 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -1,10 +1,9 @@ | |||
1 | //! Assorted functions shared by several assists. | 1 | //! Assorted functions shared by several assists. |
2 | pub(crate) mod insert_use; | ||
3 | pub(crate) mod import_assets; | 2 | pub(crate) mod import_assets; |
4 | 3 | ||
5 | use std::ops; | 4 | use std::ops; |
6 | 5 | ||
7 | use hir::{Crate, Enum, HasSource, Module, ScopeDef, Semantics, Trait}; | 6 | use hir::HasSource; |
8 | use ide_db::RootDatabase; | 7 | use ide_db::RootDatabase; |
9 | use itertools::Itertools; | 8 | use itertools::Itertools; |
10 | use syntax::{ | 9 | use syntax::{ |
@@ -22,29 +21,6 @@ use crate::{ | |||
22 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 21 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, |
23 | }; | 22 | }; |
24 | 23 | ||
25 | pub use insert_use::{insert_use, ImportScope, MergeBehaviour}; | ||
26 | |||
27 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { | ||
28 | let mut segments = Vec::new(); | ||
29 | let mut is_abs = false; | ||
30 | match path.kind { | ||
31 | hir::PathKind::Plain => {} | ||
32 | hir::PathKind::Super(0) => segments.push(make::path_segment_self()), | ||
33 | hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())), | ||
34 | hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => { | ||
35 | segments.push(make::path_segment_crate()) | ||
36 | } | ||
37 | hir::PathKind::Abs => is_abs = true, | ||
38 | } | ||
39 | |||
40 | segments.extend( | ||
41 | path.segments | ||
42 | .iter() | ||
43 | .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))), | ||
44 | ); | ||
45 | make::path_from_segments(segments, is_abs) | ||
46 | } | ||
47 | |||
48 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { | 24 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { |
49 | extract_trivial_expression(&block) | 25 | extract_trivial_expression(&block) |
50 | .filter(|expr| !expr.syntax().text().contains_char('\n')) | 26 | .filter(|expr| !expr.syntax().text().contains_char('\n')) |
@@ -259,179 +235,6 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
259 | } | 235 | } |
260 | } | 236 | } |
261 | 237 | ||
262 | /// Helps with finding well-know things inside the standard library. This is | ||
263 | /// somewhat similar to the known paths infra inside hir, but it different; We | ||
264 | /// want to make sure that IDE specific paths don't become interesting inside | ||
265 | /// the compiler itself as well. | ||
266 | pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Crate>); | ||
267 | |||
268 | #[allow(non_snake_case)] | ||
269 | impl FamousDefs<'_, '_> { | ||
270 | pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core | ||
271 | pub mod convert { | ||
272 | pub trait From<T> { | ||
273 | fn from(t: T) -> Self; | ||
274 | } | ||
275 | } | ||
276 | |||
277 | pub mod default { | ||
278 | pub trait Default { | ||
279 | fn default() -> Self; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | pub mod iter { | ||
284 | pub use self::traits::{collect::IntoIterator, iterator::Iterator}; | ||
285 | mod traits { | ||
286 | pub(crate) mod iterator { | ||
287 | use crate::option::Option; | ||
288 | pub trait Iterator { | ||
289 | type Item; | ||
290 | fn next(&mut self) -> Option<Self::Item>; | ||
291 | fn by_ref(&mut self) -> &mut Self { | ||
292 | self | ||
293 | } | ||
294 | fn take(self, n: usize) -> crate::iter::Take<Self> { | ||
295 | crate::iter::Take { inner: self } | ||
296 | } | ||
297 | } | ||
298 | |||
299 | impl<I: Iterator> Iterator for &mut I { | ||
300 | type Item = I::Item; | ||
301 | fn next(&mut self) -> Option<I::Item> { | ||
302 | (**self).next() | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | pub(crate) mod collect { | ||
307 | pub trait IntoIterator { | ||
308 | type Item; | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | |||
313 | pub use self::sources::*; | ||
314 | pub(crate) mod sources { | ||
315 | use super::Iterator; | ||
316 | use crate::option::Option::{self, *}; | ||
317 | pub struct Repeat<A> { | ||
318 | element: A, | ||
319 | } | ||
320 | |||
321 | pub fn repeat<T>(elt: T) -> Repeat<T> { | ||
322 | Repeat { element: elt } | ||
323 | } | ||
324 | |||
325 | impl<A> Iterator for Repeat<A> { | ||
326 | type Item = A; | ||
327 | |||
328 | fn next(&mut self) -> Option<A> { | ||
329 | None | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | |||
334 | pub use self::adapters::*; | ||
335 | pub(crate) mod adapters { | ||
336 | use super::Iterator; | ||
337 | use crate::option::Option::{self, *}; | ||
338 | pub struct Take<I> { pub(crate) inner: I } | ||
339 | impl<I> Iterator for Take<I> where I: Iterator { | ||
340 | type Item = <I as Iterator>::Item; | ||
341 | fn next(&mut self) -> Option<<I as Iterator>::Item> { | ||
342 | None | ||
343 | } | ||
344 | } | ||
345 | } | ||
346 | } | ||
347 | |||
348 | pub mod option { | ||
349 | pub enum Option<T> { None, Some(T)} | ||
350 | } | ||
351 | |||
352 | pub mod prelude { | ||
353 | pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default}; | ||
354 | } | ||
355 | #[prelude_import] | ||
356 | pub use prelude::*; | ||
357 | "#; | ||
358 | |||
359 | pub fn core(&self) -> Option<Crate> { | ||
360 | self.find_crate("core") | ||
361 | } | ||
362 | |||
363 | pub(crate) fn core_convert_From(&self) -> Option<Trait> { | ||
364 | self.find_trait("core:convert:From") | ||
365 | } | ||
366 | |||
367 | pub(crate) fn core_option_Option(&self) -> Option<Enum> { | ||
368 | self.find_enum("core:option:Option") | ||
369 | } | ||
370 | |||
371 | pub fn core_default_Default(&self) -> Option<Trait> { | ||
372 | self.find_trait("core:default:Default") | ||
373 | } | ||
374 | |||
375 | pub fn core_iter_Iterator(&self) -> Option<Trait> { | ||
376 | self.find_trait("core:iter:traits:iterator:Iterator") | ||
377 | } | ||
378 | |||
379 | pub fn core_iter(&self) -> Option<Module> { | ||
380 | self.find_module("core:iter") | ||
381 | } | ||
382 | |||
383 | fn find_trait(&self, path: &str) -> Option<Trait> { | ||
384 | match self.find_def(path)? { | ||
385 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), | ||
386 | _ => None, | ||
387 | } | ||
388 | } | ||
389 | |||
390 | fn find_enum(&self, path: &str) -> Option<Enum> { | ||
391 | match self.find_def(path)? { | ||
392 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it), | ||
393 | _ => None, | ||
394 | } | ||
395 | } | ||
396 | |||
397 | fn find_module(&self, path: &str) -> Option<Module> { | ||
398 | match self.find_def(path)? { | ||
399 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it), | ||
400 | _ => None, | ||
401 | } | ||
402 | } | ||
403 | |||
404 | fn find_crate(&self, name: &str) -> Option<Crate> { | ||
405 | let krate = self.1?; | ||
406 | let db = self.0.db; | ||
407 | let res = | ||
408 | krate.dependencies(db).into_iter().find(|dep| dep.name.to_string() == name)?.krate; | ||
409 | Some(res) | ||
410 | } | ||
411 | |||
412 | fn find_def(&self, path: &str) -> Option<ScopeDef> { | ||
413 | let db = self.0.db; | ||
414 | let mut path = path.split(':'); | ||
415 | let trait_ = path.next_back()?; | ||
416 | let std_crate = path.next()?; | ||
417 | let std_crate = self.find_crate(std_crate)?; | ||
418 | let mut module = std_crate.root_module(db); | ||
419 | for segment in path { | ||
420 | module = module.children(db).find_map(|child| { | ||
421 | let name = child.name(db)?; | ||
422 | if name.to_string() == segment { | ||
423 | Some(child) | ||
424 | } else { | ||
425 | None | ||
426 | } | ||
427 | })?; | ||
428 | } | ||
429 | let def = | ||
430 | module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1; | ||
431 | Some(def) | ||
432 | } | ||
433 | } | ||
434 | |||
435 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { | 238 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { |
436 | [Direction::Next, Direction::Prev].iter().copied() | 239 | [Direction::Next, Direction::Prev].iter().copied() |
437 | } | 240 | } |
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml index e7df9d955..35e169a28 100644 --- a/crates/completion/Cargo.toml +++ b/crates/completion/Cargo.toml | |||
@@ -15,7 +15,6 @@ log = "0.4.8" | |||
15 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
16 | either = "1.6.1" | 16 | either = "1.6.1" |
17 | 17 | ||
18 | assists = { path = "../assists", version = "0.0.0" } | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | 18 | stdx = { path = "../stdx", version = "0.0.0" } |
20 | syntax = { path = "../syntax", version = "0.0.0" } | 19 | syntax = { path = "../syntax", version = "0.0.0" } |
21 | text_edit = { path = "../text_edit", version = "0.0.0" } | 20 | text_edit = { path = "../text_edit", version = "0.0.0" } |
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs index 7fbda7a6b..c8ba63cd3 100644 --- a/crates/completion/src/completions/postfix.rs +++ b/crates/completion/src/completions/postfix.rs | |||
@@ -5,6 +5,7 @@ mod format_like; | |||
5 | use ide_db::ty_filter::TryEnum; | 5 | use ide_db::ty_filter::TryEnum; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | ast::{self, AstNode, AstToken}, | 7 | ast::{self, AstNode, AstToken}, |
8 | SyntaxKind::{BLOCK_EXPR, EXPR_STMT}, | ||
8 | TextRange, TextSize, | 9 | TextRange, TextSize, |
9 | }; | 10 | }; |
10 | use text_edit::TextEdit; | 11 | use text_edit::TextEdit; |
@@ -220,6 +221,29 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
220 | ) | 221 | ) |
221 | .add_to(acc); | 222 | .add_to(acc); |
222 | 223 | ||
224 | if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) { | ||
225 | if matches!(parent.kind(), BLOCK_EXPR | EXPR_STMT) { | ||
226 | postfix_snippet( | ||
227 | ctx, | ||
228 | cap, | ||
229 | &dot_receiver, | ||
230 | "let", | ||
231 | "let", | ||
232 | &format!("let $0 = {};", receiver_text), | ||
233 | ) | ||
234 | .add_to(acc); | ||
235 | postfix_snippet( | ||
236 | ctx, | ||
237 | cap, | ||
238 | &dot_receiver, | ||
239 | "letm", | ||
240 | "let mut", | ||
241 | &format!("let mut $0 = {};", receiver_text), | ||
242 | ) | ||
243 | .add_to(acc); | ||
244 | } | ||
245 | } | ||
246 | |||
223 | if let ast::Expr::Literal(literal) = dot_receiver.clone() { | 247 | if let ast::Expr::Literal(literal) = dot_receiver.clone() { |
224 | if let Some(literal_text) = ast::String::cast(literal.token()) { | 248 | if let Some(literal_text) = ast::String::cast(literal.token()) { |
225 | add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text); | 249 | add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text); |
@@ -296,6 +320,38 @@ fn main() { | |||
296 | sn dbg dbg!(expr) | 320 | sn dbg dbg!(expr) |
297 | sn dbgr dbg!(&expr) | 321 | sn dbgr dbg!(&expr) |
298 | sn if if expr {} | 322 | sn if if expr {} |
323 | sn let let | ||
324 | sn letm let mut | ||
325 | sn match match expr {} | ||
326 | sn not !expr | ||
327 | sn ok Ok(expr) | ||
328 | sn ref &expr | ||
329 | sn refm &mut expr | ||
330 | sn some Some(expr) | ||
331 | sn while while expr {} | ||
332 | "#]], | ||
333 | ); | ||
334 | } | ||
335 | |||
336 | #[test] | ||
337 | fn postfix_completion_works_for_function_calln() { | ||
338 | check( | ||
339 | r#" | ||
340 | fn foo(elt: bool) -> bool { | ||
341 | !elt | ||
342 | } | ||
343 | |||
344 | fn main() { | ||
345 | let bar = true; | ||
346 | foo(bar.<|>) | ||
347 | } | ||
348 | "#, | ||
349 | expect![[r#" | ||
350 | sn box Box::new(expr) | ||
351 | sn call function(expr) | ||
352 | sn dbg dbg!(expr) | ||
353 | sn dbgr dbg!(&expr) | ||
354 | sn if if expr {} | ||
299 | sn match match expr {} | 355 | sn match match expr {} |
300 | sn not !expr | 356 | sn not !expr |
301 | sn ok Ok(expr) | 357 | sn ok Ok(expr) |
@@ -321,6 +377,8 @@ fn main() { | |||
321 | sn call function(expr) | 377 | sn call function(expr) |
322 | sn dbg dbg!(expr) | 378 | sn dbg dbg!(expr) |
323 | sn dbgr dbg!(&expr) | 379 | sn dbgr dbg!(&expr) |
380 | sn let let | ||
381 | sn letm let mut | ||
324 | sn match match expr {} | 382 | sn match match expr {} |
325 | sn ok Ok(expr) | 383 | sn ok Ok(expr) |
326 | sn ref &expr | 384 | sn ref &expr |
@@ -331,6 +389,34 @@ fn main() { | |||
331 | } | 389 | } |
332 | 390 | ||
333 | #[test] | 391 | #[test] |
392 | fn let_middle_block() { | ||
393 | check( | ||
394 | r#" | ||
395 | fn main() { | ||
396 | baz.l<|> | ||
397 | res | ||
398 | } | ||
399 | "#, | ||
400 | expect![[r#" | ||
401 | sn box Box::new(expr) | ||
402 | sn call function(expr) | ||
403 | sn dbg dbg!(expr) | ||
404 | sn dbgr dbg!(&expr) | ||
405 | sn if if expr {} | ||
406 | sn let let | ||
407 | sn letm let mut | ||
408 | sn match match expr {} | ||
409 | sn not !expr | ||
410 | sn ok Ok(expr) | ||
411 | sn ref &expr | ||
412 | sn refm &mut expr | ||
413 | sn some Some(expr) | ||
414 | sn while while expr {} | ||
415 | "#]], | ||
416 | ); | ||
417 | } | ||
418 | |||
419 | #[test] | ||
334 | fn option_iflet() { | 420 | fn option_iflet() { |
335 | check_edit( | 421 | check_edit( |
336 | "ifl", | 422 | "ifl", |
diff --git a/crates/completion/src/completions/qualified_path.rs b/crates/completion/src/completions/qualified_path.rs index d9387054d..bc23bea3f 100644 --- a/crates/completion/src/completions/qualified_path.rs +++ b/crates/completion/src/completions/qualified_path.rs | |||
@@ -353,10 +353,10 @@ impl S { | |||
353 | fn foo() { let _ = S::<|> } | 353 | fn foo() { let _ = S::<|> } |
354 | "#, | 354 | "#, |
355 | expect![[r#" | 355 | expect![[r#" |
356 | ct C const C: i32 = 42; | 356 | ct C const C: i32 = 42; |
357 | ta T type T = i32; | 357 | ta T type T = i32; |
358 | fn a() fn a() | 358 | fn a() fn a() |
359 | me b() fn b(&self) | 359 | me b(…) fn b(&self) |
360 | "#]], | 360 | "#]], |
361 | ); | 361 | ); |
362 | } | 362 | } |
@@ -503,14 +503,14 @@ trait Sub: Super { | |||
503 | fn foo<T: Sub>() { T::<|> } | 503 | fn foo<T: Sub>() { T::<|> } |
504 | "#, | 504 | "#, |
505 | expect![[r#" | 505 | expect![[r#" |
506 | ct C2 const C2: (); | 506 | ct C2 const C2: (); |
507 | ct CONST const CONST: u8; | 507 | ct CONST const CONST: u8; |
508 | ta SubTy type SubTy; | 508 | ta SubTy type SubTy; |
509 | ta Ty type Ty; | 509 | ta Ty type Ty; |
510 | fn func() fn func() | 510 | fn func() fn func() |
511 | me method() fn method(&self) | 511 | me method(…) fn method(&self) |
512 | fn subfunc() fn subfunc() | 512 | fn subfunc() fn subfunc() |
513 | me submethod() fn submethod(&self) | 513 | me submethod(…) fn submethod(&self) |
514 | "#]], | 514 | "#]], |
515 | ); | 515 | ); |
516 | } | 516 | } |
@@ -543,14 +543,14 @@ impl<T> Sub for Wrap<T> { | |||
543 | } | 543 | } |
544 | "#, | 544 | "#, |
545 | expect![[r#" | 545 | expect![[r#" |
546 | ct C2 const C2: () = (); | 546 | ct C2 const C2: () = (); |
547 | ct CONST const CONST: u8 = 0; | 547 | ct CONST const CONST: u8 = 0; |
548 | ta SubTy type SubTy; | 548 | ta SubTy type SubTy; |
549 | ta Ty type Ty; | 549 | ta Ty type Ty; |
550 | fn func() fn func() | 550 | fn func() fn func() |
551 | me method() fn method(&self) | 551 | me method(…) fn method(&self) |
552 | fn subfunc() fn subfunc() | 552 | fn subfunc() fn subfunc() |
553 | me submethod() fn submethod(&self) | 553 | me submethod(…) fn submethod(&self) |
554 | "#]], | 554 | "#]], |
555 | ); | 555 | ); |
556 | } | 556 | } |
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs index 2049b9d09..eaa44c97d 100644 --- a/crates/completion/src/completions/record.rs +++ b/crates/completion/src/completions/record.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! Complete fields in record literals and patterns. | 1 | //! Complete fields in record literals and patterns. |
2 | use assists::utils::FamousDefs; | 2 | use ide_db::helpers::FamousDefs; |
3 | use syntax::ast::Expr; | 3 | use syntax::ast::Expr; |
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
@@ -45,8 +45,8 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> | |||
45 | 45 | ||
46 | #[cfg(test)] | 46 | #[cfg(test)] |
47 | mod tests { | 47 | mod tests { |
48 | use assists::utils::FamousDefs; | ||
49 | use expect_test::{expect, Expect}; | 48 | use expect_test::{expect, Expect}; |
49 | use ide_db::helpers::FamousDefs; | ||
50 | 50 | ||
51 | use crate::{test_utils::completion_list, CompletionKind}; | 51 | use crate::{test_utils::completion_list, CompletionKind}; |
52 | 52 | ||
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs index a14be9c73..e2fe44aff 100644 --- a/crates/completion/src/completions/trait_impl.rs +++ b/crates/completion/src/completions/trait_impl.rs | |||
@@ -139,7 +139,7 @@ fn add_function_impl( | |||
139 | ) { | 139 | ) { |
140 | let fn_name = func.name(ctx.db).to_string(); | 140 | let fn_name = func.name(ctx.db).to_string(); |
141 | 141 | ||
142 | let label = if func.params(ctx.db).is_empty() { | 142 | let label = if func.assoc_fn_params(ctx.db).is_empty() { |
143 | format!("fn {}()", fn_name) | 143 | format!("fn {}()", fn_name) |
144 | } else { | 144 | } else { |
145 | format!("fn {}(..)", fn_name) | 145 | format!("fn {}(..)", fn_name) |
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 3bd776905..81691cd7f 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -1,8 +1,8 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use assists::utils::ImportScope; | ||
4 | use either::Either; | 3 | use either::Either; |
5 | use hir::{Adt, ModuleDef, ScopeDef, Type}; | 4 | use hir::{Adt, ModuleDef, ScopeDef, Type}; |
5 | use ide_db::helpers::insert_use::ImportScope; | ||
6 | use ide_db::imports_locator; | 6 | use ide_db::imports_locator; |
7 | use syntax::AstNode; | 7 | use syntax::AstNode; |
8 | use test_utils::mark; | 8 | use test_utils::mark; |
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index f50735372..654a76f7b 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs | |||
@@ -4,7 +4,7 @@ | |||
4 | //! module, and we use to statically check that we only produce snippet | 4 | //! module, and we use to statically check that we only produce snippet |
5 | //! completions if we are allowed to. | 5 | //! completions if we are allowed to. |
6 | 6 | ||
7 | use assists::utils::MergeBehaviour; | 7 | use ide_db::helpers::insert_use::MergeBehaviour; |
8 | 8 | ||
9 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
10 | pub struct CompletionConfig { | 10 | pub struct CompletionConfig { |
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index b13c3f376..e85549fef 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs | |||
@@ -2,8 +2,11 @@ | |||
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour}; | ||
6 | use hir::{Documentation, ModPath, Mutability}; | 5 | use hir::{Documentation, ModPath, Mutability}; |
6 | use ide_db::helpers::{ | ||
7 | insert_use::{self, ImportScope, MergeBehaviour}, | ||
8 | mod_path_to_ast, | ||
9 | }; | ||
7 | use syntax::{algo, TextRange}; | 10 | use syntax::{algo, TextRange}; |
8 | use text_edit::TextEdit; | 11 | use text_edit::TextEdit; |
9 | 12 | ||
@@ -201,7 +204,7 @@ impl CompletionItem { | |||
201 | trigger_call_info: None, | 204 | trigger_call_info: None, |
202 | score: None, | 205 | score: None, |
203 | ref_match: None, | 206 | ref_match: None, |
204 | import_data: None, | 207 | import_to_add: None, |
205 | } | 208 | } |
206 | } | 209 | } |
207 | 210 | ||
@@ -255,13 +258,21 @@ impl CompletionItem { | |||
255 | } | 258 | } |
256 | } | 259 | } |
257 | 260 | ||
261 | /// An extra import to add after the completion is applied. | ||
262 | #[derive(Clone)] | ||
263 | pub(crate) struct ImportToAdd { | ||
264 | pub(crate) import_path: ModPath, | ||
265 | pub(crate) import_scope: ImportScope, | ||
266 | pub(crate) merge_behaviour: Option<MergeBehaviour>, | ||
267 | } | ||
268 | |||
258 | /// A helper to make `CompletionItem`s. | 269 | /// A helper to make `CompletionItem`s. |
259 | #[must_use] | 270 | #[must_use] |
260 | #[derive(Clone)] | 271 | #[derive(Clone)] |
261 | pub(crate) struct Builder { | 272 | pub(crate) struct Builder { |
262 | source_range: TextRange, | 273 | source_range: TextRange, |
263 | completion_kind: CompletionKind, | 274 | completion_kind: CompletionKind, |
264 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | 275 | import_to_add: Option<ImportToAdd>, |
265 | label: String, | 276 | label: String, |
266 | insert_text: Option<String>, | 277 | insert_text: Option<String>, |
267 | insert_text_format: InsertTextFormat, | 278 | insert_text_format: InsertTextFormat, |
@@ -278,14 +289,16 @@ pub(crate) struct Builder { | |||
278 | 289 | ||
279 | impl Builder { | 290 | impl Builder { |
280 | pub(crate) fn build(self) -> CompletionItem { | 291 | pub(crate) fn build(self) -> CompletionItem { |
292 | let _p = profile::span("item::Builder::build"); | ||
293 | |||
281 | let mut label = self.label; | 294 | let mut label = self.label; |
282 | let mut lookup = self.lookup; | 295 | let mut lookup = self.lookup; |
283 | let mut insert_text = self.insert_text; | 296 | let mut insert_text = self.insert_text; |
284 | let mut text_edits = TextEdit::builder(); | 297 | let mut text_edits = TextEdit::builder(); |
285 | 298 | ||
286 | if let Some((import_path, import_scope, merge_behaviour)) = self.import_data { | 299 | if let Some(import_data) = self.import_to_add { |
287 | let import = mod_path_to_ast(&import_path); | 300 | let import = mod_path_to_ast(&import_data.import_path); |
288 | let mut import_path_without_last_segment = import_path; | 301 | let mut import_path_without_last_segment = import_data.import_path; |
289 | let _ = import_path_without_last_segment.segments.pop(); | 302 | let _ = import_path_without_last_segment.segments.pop(); |
290 | 303 | ||
291 | if !import_path_without_last_segment.segments.is_empty() { | 304 | if !import_path_without_last_segment.segments.is_empty() { |
@@ -298,7 +311,11 @@ impl Builder { | |||
298 | label = format!("{}::{}", import_path_without_last_segment, label); | 311 | label = format!("{}::{}", import_path_without_last_segment, label); |
299 | } | 312 | } |
300 | 313 | ||
301 | let rewriter = insert_use(&import_scope, import, merge_behaviour); | 314 | let rewriter = insert_use::insert_use( |
315 | &import_data.import_scope, | ||
316 | import, | ||
317 | import_data.merge_behaviour, | ||
318 | ); | ||
302 | if let Some(old_ast) = rewriter.rewrite_root() { | 319 | if let Some(old_ast) = rewriter.rewrite_root() { |
303 | algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); | 320 | algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); |
304 | } | 321 | } |
@@ -390,11 +407,8 @@ impl Builder { | |||
390 | self.trigger_call_info = Some(true); | 407 | self.trigger_call_info = Some(true); |
391 | self | 408 | self |
392 | } | 409 | } |
393 | pub(crate) fn import_data( | 410 | pub(crate) fn add_import(mut self, import_to_add: Option<ImportToAdd>) -> Builder { |
394 | mut self, | 411 | self.import_to_add = import_to_add; |
395 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
396 | ) -> Builder { | ||
397 | self.import_data = import_data; | ||
398 | self | 412 | self |
399 | } | 413 | } |
400 | pub(crate) fn set_ref_match( | 414 | pub(crate) fn set_ref_match( |
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index aecc1378b..1ec2e9be7 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs | |||
@@ -44,6 +44,8 @@ pub use crate::{ | |||
44 | // - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` | 44 | // - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` |
45 | // - `expr.ref` -> `&expr` | 45 | // - `expr.ref` -> `&expr` |
46 | // - `expr.refm` -> `&mut expr` | 46 | // - `expr.refm` -> `&mut expr` |
47 | // - `expr.let` -> `let <|> = expr;` | ||
48 | // - `expr.letm` -> `let mut <|> = expr;` | ||
47 | // - `expr.not` -> `!expr` | 49 | // - `expr.not` -> `!expr` |
48 | // - `expr.dbg` -> `dbg!(expr)` | 50 | // - `expr.dbg` -> `dbg!(expr)` |
49 | // - `expr.dbgr` -> `dbg!(&expr)` | 51 | // - `expr.dbgr` -> `dbg!(&expr)` |
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index bce02f577..504757a6a 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs | |||
@@ -9,15 +9,15 @@ pub(crate) mod type_alias; | |||
9 | 9 | ||
10 | mod builder_ext; | 10 | mod builder_ext; |
11 | 11 | ||
12 | use assists::utils::{ImportScope, MergeBehaviour}; | ||
13 | use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; | 12 | use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; |
13 | use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour}; | ||
14 | use ide_db::RootDatabase; | 14 | use ide_db::RootDatabase; |
15 | use syntax::TextRange; | 15 | use syntax::TextRange; |
16 | use test_utils::mark; | 16 | use test_utils::mark; |
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
19 | config::SnippetCap, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 19 | config::SnippetCap, item::ImportToAdd, CompletionContext, CompletionItem, CompletionItemKind, |
20 | CompletionScore, | 20 | CompletionKind, CompletionScore, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | use crate::render::{enum_variant::render_enum_variant, function::render_fn, macro_::render_macro}; | 23 | use crate::render::{enum_variant::render_enum_variant, function::render_fn, macro_::render_macro}; |
@@ -48,15 +48,15 @@ pub(crate) fn render_resolution<'a>( | |||
48 | 48 | ||
49 | pub(crate) fn render_resolution_with_import<'a>( | 49 | pub(crate) fn render_resolution_with_import<'a>( |
50 | ctx: RenderContext<'a>, | 50 | ctx: RenderContext<'a>, |
51 | import: ModPath, | 51 | import_path: ModPath, |
52 | import_scope: ImportScope, | 52 | import_scope: ImportScope, |
53 | merge_behaviour: Option<MergeBehaviour>, | 53 | merge_behaviour: Option<MergeBehaviour>, |
54 | resolution: &ScopeDef, | 54 | resolution: &ScopeDef, |
55 | ) -> Option<CompletionItem> { | 55 | ) -> Option<CompletionItem> { |
56 | let local_name = import.segments.last()?.to_string(); | 56 | let local_name = import_path.segments.last()?.to_string(); |
57 | Render::new(ctx).render_resolution( | 57 | Render::new(ctx).render_resolution( |
58 | local_name, | 58 | local_name, |
59 | Some((import, import_scope, merge_behaviour)), | 59 | Some(ImportToAdd { import_path, import_scope, merge_behaviour }), |
60 | resolution, | 60 | resolution, |
61 | ) | 61 | ) |
62 | } | 62 | } |
@@ -147,7 +147,7 @@ impl<'a> Render<'a> { | |||
147 | fn render_resolution( | 147 | fn render_resolution( |
148 | self, | 148 | self, |
149 | local_name: String, | 149 | local_name: String, |
150 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | 150 | import_to_add: Option<ImportToAdd>, |
151 | resolution: &ScopeDef, | 151 | resolution: &ScopeDef, |
152 | ) -> Option<CompletionItem> { | 152 | ) -> Option<CompletionItem> { |
153 | let _p = profile::span("render_resolution"); | 153 | let _p = profile::span("render_resolution"); |
@@ -160,15 +160,16 @@ impl<'a> Render<'a> { | |||
160 | 160 | ||
161 | let kind = match resolution { | 161 | let kind = match resolution { |
162 | ScopeDef::ModuleDef(Function(func)) => { | 162 | ScopeDef::ModuleDef(Function(func)) => { |
163 | let item = render_fn(self.ctx, import_data, Some(local_name), *func); | 163 | let item = render_fn(self.ctx, import_to_add, Some(local_name), *func); |
164 | return Some(item); | 164 | return Some(item); |
165 | } | 165 | } |
166 | ScopeDef::ModuleDef(EnumVariant(var)) => { | 166 | ScopeDef::ModuleDef(EnumVariant(var)) => { |
167 | let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None); | 167 | let item = |
168 | render_enum_variant(self.ctx, import_to_add, Some(local_name), *var, None); | ||
168 | return Some(item); | 169 | return Some(item); |
169 | } | 170 | } |
170 | ScopeDef::MacroDef(mac) => { | 171 | ScopeDef::MacroDef(mac) => { |
171 | let item = render_macro(self.ctx, import_data, local_name, *mac); | 172 | let item = render_macro(self.ctx, import_to_add, local_name, *mac); |
172 | return item; | 173 | return item; |
173 | } | 174 | } |
174 | 175 | ||
@@ -193,7 +194,7 @@ impl<'a> Render<'a> { | |||
193 | local_name, | 194 | local_name, |
194 | ) | 195 | ) |
195 | .kind(CompletionItemKind::UnresolvedReference) | 196 | .kind(CompletionItemKind::UnresolvedReference) |
196 | .import_data(import_data) | 197 | .add_import(import_to_add) |
197 | .build(); | 198 | .build(); |
198 | return Some(item); | 199 | return Some(item); |
199 | } | 200 | } |
@@ -248,7 +249,7 @@ impl<'a> Render<'a> { | |||
248 | 249 | ||
249 | let item = item | 250 | let item = item |
250 | .kind(kind) | 251 | .kind(kind) |
251 | .import_data(import_data) | 252 | .add_import(import_to_add) |
252 | .set_documentation(docs) | 253 | .set_documentation(docs) |
253 | .set_ref_match(ref_match) | 254 | .set_ref_match(ref_match) |
254 | .build(); | 255 | .build(); |
diff --git a/crates/completion/src/render/builder_ext.rs b/crates/completion/src/render/builder_ext.rs index 37b0d0459..ce8718bd5 100644 --- a/crates/completion/src/render/builder_ext.rs +++ b/crates/completion/src/render/builder_ext.rs | |||
@@ -5,6 +5,7 @@ use test_utils::mark; | |||
5 | 5 | ||
6 | use crate::{item::Builder, CompletionContext}; | 6 | use crate::{item::Builder, CompletionContext}; |
7 | 7 | ||
8 | #[derive(Debug)] | ||
8 | pub(super) enum Params { | 9 | pub(super) enum Params { |
9 | Named(Vec<String>), | 10 | Named(Vec<String>), |
10 | Anonymous(usize), | 11 | Anonymous(usize), |
@@ -24,7 +25,7 @@ impl Params { | |||
24 | } | 25 | } |
25 | 26 | ||
26 | impl Builder { | 27 | impl Builder { |
27 | pub(super) fn should_add_parems(&self, ctx: &CompletionContext) -> bool { | 28 | fn should_add_parens(&self, ctx: &CompletionContext) -> bool { |
28 | if !ctx.config.add_call_parenthesis { | 29 | if !ctx.config.add_call_parenthesis { |
29 | return false; | 30 | return false; |
30 | } | 31 | } |
@@ -58,7 +59,7 @@ impl Builder { | |||
58 | name: String, | 59 | name: String, |
59 | params: Params, | 60 | params: Params, |
60 | ) -> Builder { | 61 | ) -> Builder { |
61 | if !self.should_add_parems(ctx) { | 62 | if !self.should_add_parens(ctx) { |
62 | return self; | 63 | return self; |
63 | } | 64 | } |
64 | 65 | ||
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index 6070e9b1d..f4bd02f25 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs | |||
@@ -1,23 +1,23 @@ | |||
1 | //! Renderer for `enum` variants. | 1 | //! Renderer for `enum` variants. |
2 | 2 | ||
3 | use assists::utils::{ImportScope, MergeBehaviour}; | ||
4 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; | 3 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; |
5 | use itertools::Itertools; | 4 | use itertools::Itertools; |
6 | use test_utils::mark; | 5 | use test_utils::mark; |
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | 8 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, |
10 | render::{builder_ext::Params, RenderContext}, | 9 | render::{builder_ext::Params, RenderContext}, |
11 | }; | 10 | }; |
12 | 11 | ||
13 | pub(crate) fn render_enum_variant<'a>( | 12 | pub(crate) fn render_enum_variant<'a>( |
14 | ctx: RenderContext<'a>, | 13 | ctx: RenderContext<'a>, |
15 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | 14 | import_to_add: Option<ImportToAdd>, |
16 | local_name: Option<String>, | 15 | local_name: Option<String>, |
17 | variant: hir::EnumVariant, | 16 | variant: hir::EnumVariant, |
18 | path: Option<ModPath>, | 17 | path: Option<ModPath>, |
19 | ) -> CompletionItem { | 18 | ) -> CompletionItem { |
20 | EnumVariantRender::new(ctx, local_name, variant, path).render(import_data) | 19 | let _p = profile::span("render_enum_variant"); |
20 | EnumVariantRender::new(ctx, local_name, variant, path).render(import_to_add) | ||
21 | } | 21 | } |
22 | 22 | ||
23 | #[derive(Debug)] | 23 | #[derive(Debug)] |
@@ -62,10 +62,7 @@ impl<'a> EnumVariantRender<'a> { | |||
62 | } | 62 | } |
63 | } | 63 | } |
64 | 64 | ||
65 | fn render( | 65 | fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem { |
66 | self, | ||
67 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
68 | ) -> CompletionItem { | ||
69 | let mut builder = CompletionItem::new( | 66 | let mut builder = CompletionItem::new( |
70 | CompletionKind::Reference, | 67 | CompletionKind::Reference, |
71 | self.ctx.source_range(), | 68 | self.ctx.source_range(), |
@@ -74,7 +71,7 @@ impl<'a> EnumVariantRender<'a> { | |||
74 | .kind(CompletionItemKind::EnumVariant) | 71 | .kind(CompletionItemKind::EnumVariant) |
75 | .set_documentation(self.variant.docs(self.ctx.db())) | 72 | .set_documentation(self.variant.docs(self.ctx.db())) |
76 | .set_deprecated(self.ctx.is_deprecated(self.variant)) | 73 | .set_deprecated(self.ctx.is_deprecated(self.variant)) |
77 | .import_data(import_data) | 74 | .add_import(import_to_add) |
78 | .detail(self.detail()); | 75 | .detail(self.detail()); |
79 | 76 | ||
80 | if self.variant_kind == StructKind::Tuple { | 77 | if self.variant_kind == StructKind::Tuple { |
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs index 9dd5cd18c..00e3eb203 100644 --- a/crates/completion/src/render/function.rs +++ b/crates/completion/src/render/function.rs | |||
@@ -1,28 +1,29 @@ | |||
1 | //! Renderer for function calls. | 1 | //! Renderer for function calls. |
2 | 2 | ||
3 | use assists::utils::{ImportScope, MergeBehaviour}; | 3 | use hir::{HasSource, Type}; |
4 | use hir::{HasSource, ModPath, Type}; | ||
5 | use syntax::{ast::Fn, display::function_declaration}; | 4 | use syntax::{ast::Fn, display::function_declaration}; |
5 | use test_utils::mark; | ||
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | 8 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, |
9 | render::{builder_ext::Params, RenderContext}, | 9 | render::{builder_ext::Params, RenderContext}, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | pub(crate) fn render_fn<'a>( | 12 | pub(crate) fn render_fn<'a>( |
13 | ctx: RenderContext<'a>, | 13 | ctx: RenderContext<'a>, |
14 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | 14 | import_to_add: Option<ImportToAdd>, |
15 | local_name: Option<String>, | 15 | local_name: Option<String>, |
16 | fn_: hir::Function, | 16 | fn_: hir::Function, |
17 | ) -> CompletionItem { | 17 | ) -> CompletionItem { |
18 | FunctionRender::new(ctx, local_name, fn_).render(import_data) | 18 | let _p = profile::span("render_fn"); |
19 | FunctionRender::new(ctx, local_name, fn_).render(import_to_add) | ||
19 | } | 20 | } |
20 | 21 | ||
21 | #[derive(Debug)] | 22 | #[derive(Debug)] |
22 | struct FunctionRender<'a> { | 23 | struct FunctionRender<'a> { |
23 | ctx: RenderContext<'a>, | 24 | ctx: RenderContext<'a>, |
24 | name: String, | 25 | name: String, |
25 | fn_: hir::Function, | 26 | func: hir::Function, |
26 | ast_node: Fn, | 27 | ast_node: Fn, |
27 | } | 28 | } |
28 | 29 | ||
@@ -35,21 +36,18 @@ impl<'a> FunctionRender<'a> { | |||
35 | let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string()); | 36 | let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string()); |
36 | let ast_node = fn_.source(ctx.db()).value; | 37 | let ast_node = fn_.source(ctx.db()).value; |
37 | 38 | ||
38 | FunctionRender { ctx, name, fn_, ast_node } | 39 | FunctionRender { ctx, name, func: fn_, ast_node } |
39 | } | 40 | } |
40 | 41 | ||
41 | fn render( | 42 | fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem { |
42 | self, | ||
43 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
44 | ) -> CompletionItem { | ||
45 | let params = self.params(); | 43 | let params = self.params(); |
46 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) | 44 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) |
47 | .kind(self.kind()) | 45 | .kind(self.kind()) |
48 | .set_documentation(self.ctx.docs(self.fn_)) | 46 | .set_documentation(self.ctx.docs(self.func)) |
49 | .set_deprecated(self.ctx.is_deprecated(self.fn_)) | 47 | .set_deprecated(self.ctx.is_deprecated(self.func)) |
50 | .detail(self.detail()) | 48 | .detail(self.detail()) |
51 | .add_call_parens(self.ctx.completion, self.name, params) | 49 | .add_call_parens(self.ctx.completion, self.name, params) |
52 | .import_data(import_data) | 50 | .add_import(import_to_add) |
53 | .build() | 51 | .build() |
54 | } | 52 | } |
55 | 53 | ||
@@ -70,27 +68,39 @@ impl<'a> FunctionRender<'a> { | |||
70 | } | 68 | } |
71 | 69 | ||
72 | fn params(&self) -> Params { | 70 | fn params(&self) -> Params { |
73 | let params_ty = self.fn_.params(self.ctx.db()); | 71 | let ast_params = match self.ast_node.param_list() { |
74 | let params = self | 72 | Some(it) => it, |
75 | .ast_node | 73 | None => return Params::Named(Vec::new()), |
76 | .param_list() | 74 | }; |
75 | |||
76 | let mut params_pats = Vec::new(); | ||
77 | let params_ty = if self.ctx.completion.dot_receiver.is_some() { | ||
78 | self.func.method_params(self.ctx.db()).unwrap_or_default() | ||
79 | } else { | ||
80 | if let Some(s) = ast_params.self_param() { | ||
81 | mark::hit!(parens_for_method_call_as_assoc_fn); | ||
82 | params_pats.push(Some(s.to_string())); | ||
83 | } | ||
84 | self.func.assoc_fn_params(self.ctx.db()) | ||
85 | }; | ||
86 | params_pats | ||
87 | .extend(ast_params.params().into_iter().map(|it| it.pat().map(|it| it.to_string()))); | ||
88 | |||
89 | let params = params_pats | ||
77 | .into_iter() | 90 | .into_iter() |
78 | .flat_map(|it| it.params()) | ||
79 | .zip(params_ty) | 91 | .zip(params_ty) |
80 | .flat_map(|(it, param_ty)| { | 92 | .flat_map(|(pat, param_ty)| { |
81 | if let Some(pat) = it.pat() { | 93 | let pat = pat?; |
82 | let name = pat.to_string(); | 94 | let name = pat.to_string(); |
83 | let arg = name.trim_start_matches("mut ").trim_start_matches('_'); | 95 | let arg = name.trim_start_matches("mut ").trim_start_matches('_'); |
84 | return Some(self.add_arg(arg, param_ty.ty())); | 96 | Some(self.add_arg(arg, param_ty.ty())) |
85 | } | ||
86 | None | ||
87 | }) | 97 | }) |
88 | .collect(); | 98 | .collect(); |
89 | Params::Named(params) | 99 | Params::Named(params) |
90 | } | 100 | } |
91 | 101 | ||
92 | fn kind(&self) -> CompletionItemKind { | 102 | fn kind(&self) -> CompletionItemKind { |
93 | if self.fn_.self_param(self.ctx.db()).is_some() { | 103 | if self.func.self_param(self.ctx.db()).is_some() { |
94 | CompletionItemKind::Method | 104 | CompletionItemKind::Method |
95 | } else { | 105 | } else { |
96 | CompletionItemKind::Function | 106 | CompletionItemKind::Function |
@@ -176,6 +186,28 @@ fn bar(s: &S) { | |||
176 | } | 186 | } |
177 | 187 | ||
178 | #[test] | 188 | #[test] |
189 | fn parens_for_method_call_as_assoc_fn() { | ||
190 | mark::check!(parens_for_method_call_as_assoc_fn); | ||
191 | check_edit( | ||
192 | "foo", | ||
193 | r#" | ||
194 | struct S; | ||
195 | impl S { | ||
196 | fn foo(&self) {} | ||
197 | } | ||
198 | fn main() { S::f<|> } | ||
199 | "#, | ||
200 | r#" | ||
201 | struct S; | ||
202 | impl S { | ||
203 | fn foo(&self) {} | ||
204 | } | ||
205 | fn main() { S::foo(${1:&self})$0 } | ||
206 | "#, | ||
207 | ); | ||
208 | } | ||
209 | |||
210 | #[test] | ||
179 | fn suppress_arg_snippets() { | 211 | fn suppress_arg_snippets() { |
180 | mark::check!(suppress_arg_snippets); | 212 | mark::check!(suppress_arg_snippets); |
181 | check_edit_with_config( | 213 | check_edit_with_config( |
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index fead59e41..b4ab32c6e 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs | |||
@@ -1,22 +1,22 @@ | |||
1 | //! Renderer for macro invocations. | 1 | //! Renderer for macro invocations. |
2 | 2 | ||
3 | use assists::utils::{ImportScope, MergeBehaviour}; | 3 | use hir::{Documentation, HasSource}; |
4 | use hir::{Documentation, HasSource, ModPath}; | ||
5 | use syntax::display::macro_label; | 4 | use syntax::display::macro_label; |
6 | use test_utils::mark; | 5 | use test_utils::mark; |
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | 8 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, |
10 | render::RenderContext, | 9 | render::RenderContext, |
11 | }; | 10 | }; |
12 | 11 | ||
13 | pub(crate) fn render_macro<'a>( | 12 | pub(crate) fn render_macro<'a>( |
14 | ctx: RenderContext<'a>, | 13 | ctx: RenderContext<'a>, |
15 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | 14 | import_to_add: Option<ImportToAdd>, |
16 | name: String, | 15 | name: String, |
17 | macro_: hir::MacroDef, | 16 | macro_: hir::MacroDef, |
18 | ) -> Option<CompletionItem> { | 17 | ) -> Option<CompletionItem> { |
19 | MacroRender::new(ctx, name, macro_).render(import_data) | 18 | let _p = profile::span("render_macro"); |
19 | MacroRender::new(ctx, name, macro_).render(import_to_add) | ||
20 | } | 20 | } |
21 | 21 | ||
22 | #[derive(Debug)] | 22 | #[derive(Debug)] |
@@ -38,10 +38,7 @@ impl<'a> MacroRender<'a> { | |||
38 | MacroRender { ctx, name, macro_, docs, bra, ket } | 38 | MacroRender { ctx, name, macro_, docs, bra, ket } |
39 | } | 39 | } |
40 | 40 | ||
41 | fn render( | 41 | fn render(&self, import_to_add: Option<ImportToAdd>) -> Option<CompletionItem> { |
42 | &self, | ||
43 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
44 | ) -> Option<CompletionItem> { | ||
45 | // FIXME: Currently proc-macro do not have ast-node, | 42 | // FIXME: Currently proc-macro do not have ast-node, |
46 | // such that it does not have source | 43 | // such that it does not have source |
47 | if self.macro_.is_proc_macro() { | 44 | if self.macro_.is_proc_macro() { |
@@ -53,7 +50,7 @@ impl<'a> MacroRender<'a> { | |||
53 | .kind(CompletionItemKind::Macro) | 50 | .kind(CompletionItemKind::Macro) |
54 | .set_documentation(self.docs.clone()) | 51 | .set_documentation(self.docs.clone()) |
55 | .set_deprecated(self.ctx.is_deprecated(self.macro_)) | 52 | .set_deprecated(self.ctx.is_deprecated(self.macro_)) |
56 | .import_data(import_data) | 53 | .add_import(import_to_add) |
57 | .detail(self.detail()); | 54 | .detail(self.detail()); |
58 | 55 | ||
59 | let needs_bang = self.needs_bang(); | 56 | let needs_bang = self.needs_bang(); |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 37ed092ad..4500050f1 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -744,14 +744,13 @@ impl Function { | |||
744 | Some(SelfParam { func: self.id }) | 744 | Some(SelfParam { func: self.id }) |
745 | } | 745 | } |
746 | 746 | ||
747 | pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> { | 747 | pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> { |
748 | let resolver = self.id.resolver(db.upcast()); | 748 | let resolver = self.id.resolver(db.upcast()); |
749 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | 749 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); |
750 | let environment = TraitEnvironment::lower(db, &resolver); | 750 | let environment = TraitEnvironment::lower(db, &resolver); |
751 | db.function_data(self.id) | 751 | db.function_data(self.id) |
752 | .params | 752 | .params |
753 | .iter() | 753 | .iter() |
754 | .skip(if self.self_param(db).is_some() { 1 } else { 0 }) | ||
755 | .map(|type_ref| { | 754 | .map(|type_ref| { |
756 | let ty = Type { | 755 | let ty = Type { |
757 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate, | 756 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate, |
@@ -764,6 +763,14 @@ impl Function { | |||
764 | }) | 763 | }) |
765 | .collect() | 764 | .collect() |
766 | } | 765 | } |
766 | pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> { | ||
767 | if self.self_param(db).is_none() { | ||
768 | return None; | ||
769 | } | ||
770 | let mut res = self.assoc_fn_params(db); | ||
771 | res.remove(0); | ||
772 | Some(res) | ||
773 | } | ||
767 | 774 | ||
768 | pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { | 775 | pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { |
769 | db.function_data(self.id).is_unsafe | 776 | db.function_data(self.id).is_unsafe |
@@ -799,6 +806,7 @@ impl From<Mutability> for Access { | |||
799 | } | 806 | } |
800 | } | 807 | } |
801 | 808 | ||
809 | #[derive(Debug)] | ||
802 | pub struct Param { | 810 | pub struct Param { |
803 | ty: Type, | 811 | ty: Type, |
804 | } | 812 | } |
@@ -1420,11 +1428,11 @@ impl Type { | |||
1420 | pub fn normalize_trait_assoc_type( | 1428 | pub fn normalize_trait_assoc_type( |
1421 | &self, | 1429 | &self, |
1422 | db: &dyn HirDatabase, | 1430 | db: &dyn HirDatabase, |
1423 | r#trait: Trait, | 1431 | trait_: Trait, |
1424 | args: &[Type], | 1432 | args: &[Type], |
1425 | alias: TypeAlias, | 1433 | alias: TypeAlias, |
1426 | ) -> Option<Type> { | 1434 | ) -> Option<Type> { |
1427 | let subst = Substs::build_for_def(db, r#trait.id) | 1435 | let subst = Substs::build_for_def(db, trait_.id) |
1428 | .push(self.ty.value.clone()) | 1436 | .push(self.ty.value.clone()) |
1429 | .fill(args.iter().map(|t| t.ty.value.clone())) | 1437 | .fill(args.iter().map(|t| t.ty.value.clone())) |
1430 | .build(); | 1438 | .build(); |
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index d9ad8db6f..eaf1a14ec 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule}; | 2 | pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule, UnresolvedProcMacro}; |
3 | pub use hir_expand::diagnostics::{ | 3 | pub use hir_expand::diagnostics::{ |
4 | Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, | 4 | Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, |
5 | }; | 5 | }; |
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index d10b1af01..33eb5e78c 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -14,8 +14,8 @@ use cfg::CfgOptions; | |||
14 | use drop_bomb::DropBomb; | 14 | use drop_bomb::DropBomb; |
15 | use either::Either; | 15 | use either::Either; |
16 | use hir_expand::{ | 16 | use hir_expand::{ |
17 | ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, HirFileId, InFile, | 17 | ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, ExpandResult, |
18 | MacroDefId, | 18 | HirFileId, InFile, MacroDefId, |
19 | }; | 19 | }; |
20 | use rustc_hash::FxHashMap; | 20 | use rustc_hash::FxHashMap; |
21 | use syntax::{ast, AstNode, AstPtr}; | 21 | use syntax::{ast, AstNode, AstPtr}; |
@@ -102,11 +102,11 @@ impl Expander { | |||
102 | db: &dyn DefDatabase, | 102 | db: &dyn DefDatabase, |
103 | local_scope: Option<&ItemScope>, | 103 | local_scope: Option<&ItemScope>, |
104 | macro_call: ast::MacroCall, | 104 | macro_call: ast::MacroCall, |
105 | ) -> Option<(Mark, T)> { | 105 | ) -> ExpandResult<Option<(Mark, T)>> { |
106 | self.recursion_limit += 1; | 106 | self.recursion_limit += 1; |
107 | if self.recursion_limit > EXPANSION_RECURSION_LIMIT { | 107 | if self.recursion_limit > EXPANSION_RECURSION_LIMIT { |
108 | mark::hit!(your_stack_belongs_to_me); | 108 | mark::hit!(your_stack_belongs_to_me); |
109 | return None; | 109 | return ExpandResult::str_err("reached recursion limit during macro expansion".into()); |
110 | } | 110 | } |
111 | 111 | ||
112 | let macro_call = InFile::new(self.current_file_id, ¯o_call); | 112 | let macro_call = InFile::new(self.current_file_id, ¯o_call); |
@@ -120,28 +120,55 @@ impl Expander { | |||
120 | self.resolve_path_as_macro(db, &path) | 120 | self.resolve_path_as_macro(db, &path) |
121 | }; | 121 | }; |
122 | 122 | ||
123 | if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, resolver) { | 123 | let call_id = match macro_call.as_call_id(db, self.crate_def_map.krate, resolver) { |
124 | let file_id = call_id.as_file(); | 124 | Some(it) => it, |
125 | if let Some(node) = db.parse_or_expand(file_id) { | 125 | None => { |
126 | if let Some(expr) = T::cast(node) { | 126 | // FIXME: this can mean other things too, but `as_call_id` doesn't provide enough |
127 | log::debug!("macro expansion {:#?}", expr.syntax()); | 127 | // info. |
128 | 128 | return ExpandResult::only_err(mbe::ExpandError::Other( | |
129 | let mark = Mark { | 129 | "failed to parse or resolve macro invocation".into(), |
130 | file_id: self.current_file_id, | 130 | )); |
131 | ast_id_map: mem::take(&mut self.ast_id_map), | 131 | } |
132 | bomb: DropBomb::new("expansion mark dropped"), | 132 | }; |
133 | }; | 133 | |
134 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); | 134 | let err = db.macro_expand_error(call_id); |
135 | self.current_file_id = file_id; | 135 | |
136 | self.ast_id_map = db.ast_id_map(file_id); | 136 | let file_id = call_id.as_file(); |
137 | return Some((mark, expr)); | 137 | |
138 | let raw_node = match db.parse_or_expand(file_id) { | ||
139 | Some(it) => it, | ||
140 | None => { | ||
141 | // Only `None` if the macro expansion produced no usable AST. | ||
142 | if err.is_none() { | ||
143 | log::warn!("no error despite `parse_or_expand` failing"); | ||
138 | } | 144 | } |
145 | |||
146 | return ExpandResult::only_err(err.unwrap_or_else(|| { | ||
147 | mbe::ExpandError::Other("failed to parse macro invocation".into()) | ||
148 | })); | ||
139 | } | 149 | } |
140 | } | 150 | }; |
151 | |||
152 | let node = match T::cast(raw_node) { | ||
153 | Some(it) => it, | ||
154 | None => { | ||
155 | // This can happen without being an error, so only forward previous errors. | ||
156 | return ExpandResult { value: None, err }; | ||
157 | } | ||
158 | }; | ||
159 | |||
160 | log::debug!("macro expansion {:#?}", node.syntax()); | ||
161 | |||
162 | let mark = Mark { | ||
163 | file_id: self.current_file_id, | ||
164 | ast_id_map: mem::take(&mut self.ast_id_map), | ||
165 | bomb: DropBomb::new("expansion mark dropped"), | ||
166 | }; | ||
167 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); | ||
168 | self.current_file_id = file_id; | ||
169 | self.ast_id_map = db.ast_id_map(file_id); | ||
141 | 170 | ||
142 | // FIXME: Instead of just dropping the error from expansion | 171 | ExpandResult { value: Some((mark, node)), err } |
143 | // report it | ||
144 | None | ||
145 | } | 172 | } |
146 | 173 | ||
147 | pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { | 174 | pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { |
diff --git a/crates/hir_def/src/body/diagnostics.rs b/crates/hir_def/src/body/diagnostics.rs index e57bdc133..1de7d30e2 100644 --- a/crates/hir_def/src/body/diagnostics.rs +++ b/crates/hir_def/src/body/diagnostics.rs | |||
@@ -2,11 +2,13 @@ | |||
2 | 2 | ||
3 | use hir_expand::diagnostics::DiagnosticSink; | 3 | use hir_expand::diagnostics::DiagnosticSink; |
4 | 4 | ||
5 | use crate::diagnostics::InactiveCode; | 5 | use crate::diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro}; |
6 | 6 | ||
7 | #[derive(Debug, Eq, PartialEq)] | 7 | #[derive(Debug, Eq, PartialEq)] |
8 | pub(crate) enum BodyDiagnostic { | 8 | pub(crate) enum BodyDiagnostic { |
9 | InactiveCode(InactiveCode), | 9 | InactiveCode(InactiveCode), |
10 | MacroError(MacroError), | ||
11 | UnresolvedProcMacro(UnresolvedProcMacro), | ||
10 | } | 12 | } |
11 | 13 | ||
12 | impl BodyDiagnostic { | 14 | impl BodyDiagnostic { |
@@ -15,6 +17,12 @@ impl BodyDiagnostic { | |||
15 | BodyDiagnostic::InactiveCode(diag) => { | 17 | BodyDiagnostic::InactiveCode(diag) => { |
16 | sink.push(diag.clone()); | 18 | sink.push(diag.clone()); |
17 | } | 19 | } |
20 | BodyDiagnostic::MacroError(diag) => { | ||
21 | sink.push(diag.clone()); | ||
22 | } | ||
23 | BodyDiagnostic::UnresolvedProcMacro(diag) => { | ||
24 | sink.push(diag.clone()); | ||
25 | } | ||
18 | } | 26 | } |
19 | } | 27 | } |
20 | } | 28 | } |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index cd7958746..2c41c0005 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -8,7 +8,7 @@ use either::Either; | |||
8 | use hir_expand::{ | 8 | use hir_expand::{ |
9 | hygiene::Hygiene, | 9 | hygiene::Hygiene, |
10 | name::{name, AsName, Name}, | 10 | name::{name, AsName, Name}, |
11 | HirFileId, MacroDefId, MacroDefKind, | 11 | ExpandError, HirFileId, MacroDefId, MacroDefKind, |
12 | }; | 12 | }; |
13 | use rustc_hash::FxHashMap; | 13 | use rustc_hash::FxHashMap; |
14 | use syntax::{ | 14 | use syntax::{ |
@@ -25,7 +25,7 @@ use crate::{ | |||
25 | body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, | 25 | body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, |
26 | builtin_type::{BuiltinFloat, BuiltinInt}, | 26 | builtin_type::{BuiltinFloat, BuiltinInt}, |
27 | db::DefDatabase, | 27 | db::DefDatabase, |
28 | diagnostics::InactiveCode, | 28 | diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro}, |
29 | expr::{ | 29 | expr::{ |
30 | dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, | 30 | dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, |
31 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, | 31 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, |
@@ -561,7 +561,32 @@ impl ExprCollector<'_> { | |||
561 | self.alloc_expr(Expr::Missing, syntax_ptr) | 561 | self.alloc_expr(Expr::Missing, syntax_ptr) |
562 | } else { | 562 | } else { |
563 | let macro_call = self.expander.to_source(AstPtr::new(&e)); | 563 | let macro_call = self.expander.to_source(AstPtr::new(&e)); |
564 | match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) { | 564 | let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e); |
565 | |||
566 | match res.err { | ||
567 | Some(ExpandError::UnresolvedProcMacro) => { | ||
568 | self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro( | ||
569 | UnresolvedProcMacro { | ||
570 | file: self.expander.current_file_id, | ||
571 | node: syntax_ptr.clone().into(), | ||
572 | precise_location: None, | ||
573 | macro_name: None, | ||
574 | }, | ||
575 | )); | ||
576 | } | ||
577 | Some(err) => { | ||
578 | self.source_map.diagnostics.push(BodyDiagnostic::MacroError( | ||
579 | MacroError { | ||
580 | file: self.expander.current_file_id, | ||
581 | node: syntax_ptr.clone().into(), | ||
582 | message: err.to_string(), | ||
583 | }, | ||
584 | )); | ||
585 | } | ||
586 | None => {} | ||
587 | } | ||
588 | |||
589 | match res.value { | ||
565 | Some((mark, expansion)) => { | 590 | Some((mark, expansion)) => { |
566 | self.source_map | 591 | self.source_map |
567 | .expansions | 592 | .expansions |
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index ff1ef0df6..146045938 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -257,7 +257,7 @@ fn collect_items( | |||
257 | let root = db.parse_or_expand(file_id).unwrap(); | 257 | let root = db.parse_or_expand(file_id).unwrap(); |
258 | let call = ast_id_map.get(call.ast_id).to_node(&root); | 258 | let call = ast_id_map.get(call.ast_id).to_node(&root); |
259 | 259 | ||
260 | if let Some((mark, mac)) = expander.enter_expand(db, None, call) { | 260 | if let Some((mark, mac)) = expander.enter_expand(db, None, call).value { |
261 | let src: InFile<ast::MacroItems> = expander.to_source(mac); | 261 | let src: InFile<ast::MacroItems> = expander.to_source(mac); |
262 | let item_tree = db.item_tree(src.file_id); | 262 | let item_tree = db.item_tree(src.file_id); |
263 | let iter = | 263 | let iter = |
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index b221b290c..c71266dc0 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -6,7 +6,7 @@ use stdx::format_to; | |||
6 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; | 6 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; |
7 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; | 7 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; |
8 | use hir_expand::{HirFileId, InFile}; | 8 | use hir_expand::{HirFileId, InFile}; |
9 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | 9 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; |
10 | 10 | ||
11 | use crate::{db::DefDatabase, DefWithBodyId}; | 11 | use crate::{db::DefDatabase, DefWithBodyId}; |
12 | 12 | ||
@@ -127,3 +127,68 @@ impl Diagnostic for InactiveCode { | |||
127 | self | 127 | self |
128 | } | 128 | } |
129 | } | 129 | } |
130 | |||
131 | // Diagnostic: unresolved-proc-macro | ||
132 | // | ||
133 | // This diagnostic is shown when a procedural macro can not be found. This usually means that | ||
134 | // procedural macro support is simply disabled (and hence is only a weak hint instead of an error), | ||
135 | // but can also indicate project setup problems. | ||
136 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
137 | pub struct UnresolvedProcMacro { | ||
138 | pub file: HirFileId, | ||
139 | pub node: SyntaxNodePtr, | ||
140 | /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange` | ||
141 | /// to use instead. | ||
142 | pub precise_location: Option<TextRange>, | ||
143 | pub macro_name: Option<String>, | ||
144 | } | ||
145 | |||
146 | impl Diagnostic for UnresolvedProcMacro { | ||
147 | fn code(&self) -> DiagnosticCode { | ||
148 | DiagnosticCode("unresolved-proc-macro") | ||
149 | } | ||
150 | |||
151 | fn message(&self) -> String { | ||
152 | match &self.macro_name { | ||
153 | Some(name) => format!("proc macro `{}` not expanded", name), | ||
154 | None => "proc macro not expanded".to_string(), | ||
155 | } | ||
156 | } | ||
157 | |||
158 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
159 | InFile::new(self.file, self.node.clone()) | ||
160 | } | ||
161 | |||
162 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
163 | self | ||
164 | } | ||
165 | } | ||
166 | |||
167 | // Diagnostic: macro-error | ||
168 | // | ||
169 | // This diagnostic is shown for macro expansion errors. | ||
170 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
171 | pub struct MacroError { | ||
172 | pub file: HirFileId, | ||
173 | pub node: SyntaxNodePtr, | ||
174 | pub message: String, | ||
175 | } | ||
176 | |||
177 | impl Diagnostic for MacroError { | ||
178 | fn code(&self) -> DiagnosticCode { | ||
179 | DiagnosticCode("macro-error") | ||
180 | } | ||
181 | fn message(&self) -> String { | ||
182 | self.message.clone() | ||
183 | } | ||
184 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
185 | InFile::new(self.file, self.node.clone()) | ||
186 | } | ||
187 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
188 | self | ||
189 | } | ||
190 | fn is_experimental(&self) -> bool { | ||
191 | // Newly added and not very well-tested, might contain false positives. | ||
192 | true | ||
193 | } | ||
194 | } | ||
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 202a7dcb6..ffd0381d4 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -286,8 +286,9 @@ mod diagnostics { | |||
286 | use cfg::{CfgExpr, CfgOptions}; | 286 | use cfg::{CfgExpr, CfgOptions}; |
287 | use hir_expand::diagnostics::DiagnosticSink; | 287 | use hir_expand::diagnostics::DiagnosticSink; |
288 | use hir_expand::hygiene::Hygiene; | 288 | use hir_expand::hygiene::Hygiene; |
289 | use hir_expand::InFile; | 289 | use hir_expand::{InFile, MacroCallKind}; |
290 | use syntax::{ast, AstPtr}; | 290 | use syntax::ast::AttrsOwner; |
291 | use syntax::{ast, AstNode, AstPtr, SyntaxKind, SyntaxNodePtr}; | ||
291 | 292 | ||
292 | use crate::path::ModPath; | 293 | use crate::path::ModPath; |
293 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; | 294 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; |
@@ -301,6 +302,10 @@ mod diagnostics { | |||
301 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, | 302 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, |
302 | 303 | ||
303 | UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, | 304 | UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, |
305 | |||
306 | UnresolvedProcMacro { ast: MacroCallKind }, | ||
307 | |||
308 | MacroError { ast: MacroCallKind, message: String }, | ||
304 | } | 309 | } |
305 | 310 | ||
306 | #[derive(Debug, PartialEq, Eq)] | 311 | #[derive(Debug, PartialEq, Eq)] |
@@ -348,6 +353,18 @@ mod diagnostics { | |||
348 | Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } | 353 | Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } |
349 | } | 354 | } |
350 | 355 | ||
356 | pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self { | ||
357 | Self { in_module: container, kind: DiagnosticKind::UnresolvedProcMacro { ast } } | ||
358 | } | ||
359 | |||
360 | pub(super) fn macro_error( | ||
361 | container: LocalModuleId, | ||
362 | ast: MacroCallKind, | ||
363 | message: String, | ||
364 | ) -> Self { | ||
365 | Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } } | ||
366 | } | ||
367 | |||
351 | pub(super) fn add_to( | 368 | pub(super) fn add_to( |
352 | &self, | 369 | &self, |
353 | db: &dyn DefDatabase, | 370 | db: &dyn DefDatabase, |
@@ -407,6 +424,72 @@ mod diagnostics { | |||
407 | opts: opts.clone(), | 424 | opts: opts.clone(), |
408 | }); | 425 | }); |
409 | } | 426 | } |
427 | |||
428 | DiagnosticKind::UnresolvedProcMacro { ast } => { | ||
429 | let mut precise_location = None; | ||
430 | let (file, ast, name) = match ast { | ||
431 | MacroCallKind::FnLike(ast) => { | ||
432 | let node = ast.to_node(db.upcast()); | ||
433 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) | ||
434 | } | ||
435 | MacroCallKind::Attr(ast, name) => { | ||
436 | let node = ast.to_node(db.upcast()); | ||
437 | |||
438 | // Compute the precise location of the macro name's token in the derive | ||
439 | // list. | ||
440 | // FIXME: This does not handle paths to the macro, but neither does the | ||
441 | // rest of r-a. | ||
442 | let derive_attrs = | ||
443 | node.attrs().filter_map(|attr| match attr.as_simple_call() { | ||
444 | Some((name, args)) if name == "derive" => Some(args), | ||
445 | _ => None, | ||
446 | }); | ||
447 | 'outer: for attr in derive_attrs { | ||
448 | let tokens = | ||
449 | attr.syntax().children_with_tokens().filter_map(|elem| { | ||
450 | match elem { | ||
451 | syntax::NodeOrToken::Node(_) => None, | ||
452 | syntax::NodeOrToken::Token(tok) => Some(tok), | ||
453 | } | ||
454 | }); | ||
455 | for token in tokens { | ||
456 | if token.kind() == SyntaxKind::IDENT | ||
457 | && token.to_string() == *name | ||
458 | { | ||
459 | precise_location = Some(token.text_range()); | ||
460 | break 'outer; | ||
461 | } | ||
462 | } | ||
463 | } | ||
464 | |||
465 | ( | ||
466 | ast.file_id, | ||
467 | SyntaxNodePtr::from(AstPtr::new(&node)), | ||
468 | Some(name.clone()), | ||
469 | ) | ||
470 | } | ||
471 | }; | ||
472 | sink.push(UnresolvedProcMacro { | ||
473 | file, | ||
474 | node: ast, | ||
475 | precise_location, | ||
476 | macro_name: name, | ||
477 | }); | ||
478 | } | ||
479 | |||
480 | DiagnosticKind::MacroError { ast, message } => { | ||
481 | let (file, ast) = match ast { | ||
482 | MacroCallKind::FnLike(ast) => { | ||
483 | let node = ast.to_node(db.upcast()); | ||
484 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | ||
485 | } | ||
486 | MacroCallKind::Attr(ast, _) => { | ||
487 | let node = ast.to_node(db.upcast()); | ||
488 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | ||
489 | } | ||
490 | }; | ||
491 | sink.push(MacroError { file, node: ast, message: message.clone() }); | ||
492 | } | ||
410 | } | 493 | } |
411 | } | 494 | } |
412 | } | 495 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 5ed9073e0..19cd713ba 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -7,7 +7,6 @@ use std::iter; | |||
7 | 7 | ||
8 | use base_db::{CrateId, FileId, ProcMacroId}; | 8 | use base_db::{CrateId, FileId, ProcMacroId}; |
9 | use cfg::{CfgExpr, CfgOptions}; | 9 | use cfg::{CfgExpr, CfgOptions}; |
10 | use hir_expand::InFile; | ||
11 | use hir_expand::{ | 10 | use hir_expand::{ |
12 | ast_id_map::FileAstId, | 11 | ast_id_map::FileAstId, |
13 | builtin_derive::find_builtin_derive, | 12 | builtin_derive::find_builtin_derive, |
@@ -16,6 +15,7 @@ use hir_expand::{ | |||
16 | proc_macro::ProcMacroExpander, | 15 | proc_macro::ProcMacroExpander, |
17 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, | 16 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, |
18 | }; | 17 | }; |
18 | use hir_expand::{InFile, MacroCallLoc}; | ||
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
20 | use syntax::ast; | 20 | use syntax::ast; |
21 | use test_utils::mark; | 21 | use test_utils::mark; |
@@ -812,7 +812,30 @@ impl DefCollector<'_> { | |||
812 | log::warn!("macro expansion is too deep"); | 812 | log::warn!("macro expansion is too deep"); |
813 | return; | 813 | return; |
814 | } | 814 | } |
815 | let file_id: HirFileId = macro_call_id.as_file(); | 815 | let file_id = macro_call_id.as_file(); |
816 | |||
817 | // First, fetch the raw expansion result for purposes of error reporting. This goes through | ||
818 | // `macro_expand_error` to avoid depending on the full expansion result (to improve | ||
819 | // incrementality). | ||
820 | let err = self.db.macro_expand_error(macro_call_id); | ||
821 | if let Some(err) = err { | ||
822 | if let MacroCallId::LazyMacro(id) = macro_call_id { | ||
823 | let loc: MacroCallLoc = self.db.lookup_intern_macro(id); | ||
824 | |||
825 | let diag = match err { | ||
826 | hir_expand::ExpandError::UnresolvedProcMacro => { | ||
827 | // Missing proc macros are non-fatal, so they are handled specially. | ||
828 | DefDiagnostic::unresolved_proc_macro(module_id, loc.kind) | ||
829 | } | ||
830 | _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()), | ||
831 | }; | ||
832 | |||
833 | self.def_map.diagnostics.push(diag); | ||
834 | } | ||
835 | // FIXME: Handle eager macros. | ||
836 | } | ||
837 | |||
838 | // Then, fetch and process the item tree. This will reuse the expansion result from above. | ||
816 | let item_tree = self.db.item_tree(file_id); | 839 | let item_tree = self.db.item_tree(file_id); |
817 | let mod_dir = self.mod_dirs[&module_id].clone(); | 840 | let mod_dir = self.mod_dirs[&module_id].clone(); |
818 | ModCollector { | 841 | ModCollector { |
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs index ba295fd9e..ef6f85e15 100644 --- a/crates/hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -798,3 +798,24 @@ mod foo; | |||
798 | "#, | 798 | "#, |
799 | ); | 799 | ); |
800 | } | 800 | } |
801 | |||
802 | #[test] | ||
803 | fn abs_path_ignores_local() { | ||
804 | check( | ||
805 | r#" | ||
806 | //- /main.rs crate:main deps:core | ||
807 | pub use ::core::hash::Hash; | ||
808 | pub mod core {} | ||
809 | |||
810 | //- /lib.rs crate:core | ||
811 | pub mod hash { pub trait Hash {} } | ||
812 | "#, | ||
813 | expect![[r#" | ||
814 | crate | ||
815 | Hash: t | ||
816 | core: t | ||
817 | |||
818 | crate::core | ||
819 | "#]], | ||
820 | ); | ||
821 | } | ||
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs index 53cecb05f..ba0d1f0e7 100644 --- a/crates/hir_def/src/path/lower/lower_use.rs +++ b/crates/hir_def/src/path/lower/lower_use.rs | |||
@@ -76,7 +76,7 @@ fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> | |||
76 | Either::Left(name) => { | 76 | Either::Left(name) => { |
77 | // no type args in use | 77 | // no type args in use |
78 | let mut res = prefix.unwrap_or_else(|| ModPath { | 78 | let mut res = prefix.unwrap_or_else(|| ModPath { |
79 | kind: PathKind::Plain, | 79 | kind: segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs), |
80 | segments: Vec::with_capacity(1), | 80 | segments: Vec::with_capacity(1), |
81 | }); | 81 | }); |
82 | res.segments.push(name); | 82 | res.segments.push(name); |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index aebbfc4df..7f4db106d 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -6,7 +6,7 @@ use crate::{ | |||
6 | 6 | ||
7 | use base_db::FileId; | 7 | use base_db::FileId; |
8 | use either::Either; | 8 | use either::Either; |
9 | use mbe::parse_to_token_tree; | 9 | use mbe::{parse_to_token_tree, ExpandResult}; |
10 | use parser::FragmentKind; | 10 | use parser::FragmentKind; |
11 | use syntax::ast::{self, AstToken}; | 11 | use syntax::ast::{self, AstToken}; |
12 | 12 | ||
@@ -28,7 +28,7 @@ macro_rules! register_builtin { | |||
28 | db: &dyn AstDatabase, | 28 | db: &dyn AstDatabase, |
29 | id: LazyMacroId, | 29 | id: LazyMacroId, |
30 | tt: &tt::Subtree, | 30 | tt: &tt::Subtree, |
31 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 31 | ) -> ExpandResult<tt::Subtree> { |
32 | let expander = match *self { | 32 | let expander = match *self { |
33 | $( BuiltinFnLikeExpander::$kind => $expand, )* | 33 | $( BuiltinFnLikeExpander::$kind => $expand, )* |
34 | }; | 34 | }; |
@@ -42,7 +42,7 @@ macro_rules! register_builtin { | |||
42 | db: &dyn AstDatabase, | 42 | db: &dyn AstDatabase, |
43 | arg_id: EagerMacroId, | 43 | arg_id: EagerMacroId, |
44 | tt: &tt::Subtree, | 44 | tt: &tt::Subtree, |
45 | ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { | 45 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { |
46 | let expander = match *self { | 46 | let expander = match *self { |
47 | $( EagerExpander::$e_kind => $e_expand, )* | 47 | $( EagerExpander::$e_kind => $e_expand, )* |
48 | }; | 48 | }; |
@@ -109,25 +109,28 @@ fn line_expand( | |||
109 | _db: &dyn AstDatabase, | 109 | _db: &dyn AstDatabase, |
110 | _id: LazyMacroId, | 110 | _id: LazyMacroId, |
111 | _tt: &tt::Subtree, | 111 | _tt: &tt::Subtree, |
112 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 112 | ) -> ExpandResult<tt::Subtree> { |
113 | // dummy implementation for type-checking purposes | 113 | // dummy implementation for type-checking purposes |
114 | let line_num = 0; | 114 | let line_num = 0; |
115 | let expanded = quote! { | 115 | let expanded = quote! { |
116 | #line_num | 116 | #line_num |
117 | }; | 117 | }; |
118 | 118 | ||
119 | Ok(expanded) | 119 | ExpandResult::ok(expanded) |
120 | } | 120 | } |
121 | 121 | ||
122 | fn stringify_expand( | 122 | fn stringify_expand( |
123 | db: &dyn AstDatabase, | 123 | db: &dyn AstDatabase, |
124 | id: LazyMacroId, | 124 | id: LazyMacroId, |
125 | _tt: &tt::Subtree, | 125 | _tt: &tt::Subtree, |
126 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 126 | ) -> ExpandResult<tt::Subtree> { |
127 | let loc = db.lookup_intern_macro(id); | 127 | let loc = db.lookup_intern_macro(id); |
128 | 128 | ||
129 | let macro_content = { | 129 | let macro_content = { |
130 | let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | 130 | let arg = match loc.kind.arg(db) { |
131 | Some(arg) => arg, | ||
132 | None => return ExpandResult::only_err(mbe::ExpandError::UnexpectedToken), | ||
133 | }; | ||
131 | let macro_args = arg; | 134 | let macro_args = arg; |
132 | let text = macro_args.text(); | 135 | let text = macro_args.text(); |
133 | let without_parens = TextSize::of('(')..text.len() - TextSize::of(')'); | 136 | let without_parens = TextSize::of('(')..text.len() - TextSize::of(')'); |
@@ -138,28 +141,28 @@ fn stringify_expand( | |||
138 | #macro_content | 141 | #macro_content |
139 | }; | 142 | }; |
140 | 143 | ||
141 | Ok(expanded) | 144 | ExpandResult::ok(expanded) |
142 | } | 145 | } |
143 | 146 | ||
144 | fn column_expand( | 147 | fn column_expand( |
145 | _db: &dyn AstDatabase, | 148 | _db: &dyn AstDatabase, |
146 | _id: LazyMacroId, | 149 | _id: LazyMacroId, |
147 | _tt: &tt::Subtree, | 150 | _tt: &tt::Subtree, |
148 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 151 | ) -> ExpandResult<tt::Subtree> { |
149 | // dummy implementation for type-checking purposes | 152 | // dummy implementation for type-checking purposes |
150 | let col_num = 0; | 153 | let col_num = 0; |
151 | let expanded = quote! { | 154 | let expanded = quote! { |
152 | #col_num | 155 | #col_num |
153 | }; | 156 | }; |
154 | 157 | ||
155 | Ok(expanded) | 158 | ExpandResult::ok(expanded) |
156 | } | 159 | } |
157 | 160 | ||
158 | fn assert_expand( | 161 | fn assert_expand( |
159 | _db: &dyn AstDatabase, | 162 | _db: &dyn AstDatabase, |
160 | _id: LazyMacroId, | 163 | _id: LazyMacroId, |
161 | tt: &tt::Subtree, | 164 | tt: &tt::Subtree, |
162 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 165 | ) -> ExpandResult<tt::Subtree> { |
163 | // A hacky implementation for goto def and hover | 166 | // A hacky implementation for goto def and hover |
164 | // We expand `assert!(cond, arg1, arg2)` to | 167 | // We expand `assert!(cond, arg1, arg2)` to |
165 | // ``` | 168 | // ``` |
@@ -191,14 +194,14 @@ fn assert_expand( | |||
191 | let expanded = quote! { | 194 | let expanded = quote! { |
192 | { { (##arg_tts); } } | 195 | { { (##arg_tts); } } |
193 | }; | 196 | }; |
194 | Ok(expanded) | 197 | ExpandResult::ok(expanded) |
195 | } | 198 | } |
196 | 199 | ||
197 | fn file_expand( | 200 | fn file_expand( |
198 | _db: &dyn AstDatabase, | 201 | _db: &dyn AstDatabase, |
199 | _id: LazyMacroId, | 202 | _id: LazyMacroId, |
200 | _tt: &tt::Subtree, | 203 | _tt: &tt::Subtree, |
201 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 204 | ) -> ExpandResult<tt::Subtree> { |
202 | // FIXME: RA purposefully lacks knowledge of absolute file names | 205 | // FIXME: RA purposefully lacks knowledge of absolute file names |
203 | // so just return "". | 206 | // so just return "". |
204 | let file_name = ""; | 207 | let file_name = ""; |
@@ -207,31 +210,33 @@ fn file_expand( | |||
207 | #file_name | 210 | #file_name |
208 | }; | 211 | }; |
209 | 212 | ||
210 | Ok(expanded) | 213 | ExpandResult::ok(expanded) |
211 | } | 214 | } |
212 | 215 | ||
213 | fn compile_error_expand( | 216 | fn compile_error_expand( |
214 | _db: &dyn AstDatabase, | 217 | _db: &dyn AstDatabase, |
215 | _id: LazyMacroId, | 218 | _id: LazyMacroId, |
216 | tt: &tt::Subtree, | 219 | tt: &tt::Subtree, |
217 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 220 | ) -> ExpandResult<tt::Subtree> { |
218 | if tt.count() == 1 { | 221 | if tt.count() == 1 { |
219 | if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] { | 222 | if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] { |
220 | let s = it.text.as_str(); | 223 | let s = it.text.as_str(); |
221 | if s.contains('"') { | 224 | if s.contains('"') { |
222 | return Ok(quote! { loop { #it }}); | 225 | return ExpandResult::ok(quote! { loop { #it }}); |
223 | } | 226 | } |
224 | }; | 227 | }; |
225 | } | 228 | } |
226 | 229 | ||
227 | Err(mbe::ExpandError::BindingError("Must be a string".into())) | 230 | ExpandResult::only_err(mbe::ExpandError::BindingError( |
231 | "`compile_error!` argument be a string".into(), | ||
232 | )) | ||
228 | } | 233 | } |
229 | 234 | ||
230 | fn format_args_expand( | 235 | fn format_args_expand( |
231 | _db: &dyn AstDatabase, | 236 | _db: &dyn AstDatabase, |
232 | _id: LazyMacroId, | 237 | _id: LazyMacroId, |
233 | tt: &tt::Subtree, | 238 | tt: &tt::Subtree, |
234 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 239 | ) -> ExpandResult<tt::Subtree> { |
235 | // We expand `format_args!("", a1, a2)` to | 240 | // We expand `format_args!("", a1, a2)` to |
236 | // ``` | 241 | // ``` |
237 | // std::fmt::Arguments::new_v1(&[], &[ | 242 | // std::fmt::Arguments::new_v1(&[], &[ |
@@ -257,7 +262,7 @@ fn format_args_expand( | |||
257 | args.push(current); | 262 | args.push(current); |
258 | } | 263 | } |
259 | if args.is_empty() { | 264 | if args.is_empty() { |
260 | return Err(mbe::ExpandError::NoMatchingRule); | 265 | return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule); |
261 | } | 266 | } |
262 | let _format_string = args.remove(0); | 267 | let _format_string = args.remove(0); |
263 | let arg_tts = args.into_iter().flat_map(|arg| { | 268 | let arg_tts = args.into_iter().flat_map(|arg| { |
@@ -266,7 +271,7 @@ fn format_args_expand( | |||
266 | let expanded = quote! { | 271 | let expanded = quote! { |
267 | std::fmt::Arguments::new_v1(&[], &[##arg_tts]) | 272 | std::fmt::Arguments::new_v1(&[], &[##arg_tts]) |
268 | }; | 273 | }; |
269 | Ok(expanded) | 274 | ExpandResult::ok(expanded) |
270 | } | 275 | } |
271 | 276 | ||
272 | fn unquote_str(lit: &tt::Literal) -> Option<String> { | 277 | fn unquote_str(lit: &tt::Literal) -> Option<String> { |
@@ -279,19 +284,24 @@ fn concat_expand( | |||
279 | _db: &dyn AstDatabase, | 284 | _db: &dyn AstDatabase, |
280 | _arg_id: EagerMacroId, | 285 | _arg_id: EagerMacroId, |
281 | tt: &tt::Subtree, | 286 | tt: &tt::Subtree, |
282 | ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { | 287 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { |
283 | let mut text = String::new(); | 288 | let mut text = String::new(); |
284 | for (i, t) in tt.token_trees.iter().enumerate() { | 289 | for (i, t) in tt.token_trees.iter().enumerate() { |
285 | match t { | 290 | match t { |
286 | tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { | 291 | tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { |
287 | text += &unquote_str(&it).ok_or_else(|| mbe::ExpandError::ConversionError)?; | 292 | text += &match unquote_str(&it) { |
293 | Some(s) => s, | ||
294 | None => { | ||
295 | return ExpandResult::only_err(mbe::ExpandError::ConversionError); | ||
296 | } | ||
297 | }; | ||
288 | } | 298 | } |
289 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), | 299 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), |
290 | _ => return Err(mbe::ExpandError::UnexpectedToken), | 300 | _ => return ExpandResult::only_err(mbe::ExpandError::UnexpectedToken), |
291 | } | 301 | } |
292 | } | 302 | } |
293 | 303 | ||
294 | Ok((quote!(#text), FragmentKind::Expr)) | 304 | ExpandResult::ok(Some((quote!(#text), FragmentKind::Expr))) |
295 | } | 305 | } |
296 | 306 | ||
297 | fn relative_file( | 307 | fn relative_file( |
@@ -324,26 +334,35 @@ fn include_expand( | |||
324 | db: &dyn AstDatabase, | 334 | db: &dyn AstDatabase, |
325 | arg_id: EagerMacroId, | 335 | arg_id: EagerMacroId, |
326 | tt: &tt::Subtree, | 336 | tt: &tt::Subtree, |
327 | ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { | 337 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { |
328 | let path = parse_string(tt)?; | 338 | let res = (|| { |
329 | let file_id = relative_file(db, arg_id.into(), &path, false) | 339 | let path = parse_string(tt)?; |
330 | .ok_or_else(|| mbe::ExpandError::ConversionError)?; | 340 | let file_id = relative_file(db, arg_id.into(), &path, false) |
331 | 341 | .ok_or_else(|| mbe::ExpandError::ConversionError)?; | |
332 | // FIXME: | 342 | |
333 | // Handle include as expression | 343 | Ok(parse_to_token_tree(&db.file_text(file_id)) |
334 | let res = parse_to_token_tree(&db.file_text(file_id)) | 344 | .ok_or_else(|| mbe::ExpandError::ConversionError)? |
335 | .ok_or_else(|| mbe::ExpandError::ConversionError)? | 345 | .0) |
336 | .0; | 346 | })(); |
337 | 347 | ||
338 | Ok((res, FragmentKind::Items)) | 348 | match res { |
349 | Ok(res) => { | ||
350 | // FIXME: | ||
351 | // Handle include as expression | ||
352 | ExpandResult::ok(Some((res, FragmentKind::Items))) | ||
353 | } | ||
354 | Err(e) => ExpandResult::only_err(e), | ||
355 | } | ||
339 | } | 356 | } |
340 | 357 | ||
341 | fn include_bytes_expand( | 358 | fn include_bytes_expand( |
342 | _db: &dyn AstDatabase, | 359 | _db: &dyn AstDatabase, |
343 | _arg_id: EagerMacroId, | 360 | _arg_id: EagerMacroId, |
344 | tt: &tt::Subtree, | 361 | tt: &tt::Subtree, |
345 | ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { | 362 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { |
346 | let _path = parse_string(tt)?; | 363 | if let Err(e) = parse_string(tt) { |
364 | return ExpandResult::only_err(e); | ||
365 | } | ||
347 | 366 | ||
348 | // FIXME: actually read the file here if the user asked for macro expansion | 367 | // FIXME: actually read the file here if the user asked for macro expansion |
349 | let res = tt::Subtree { | 368 | let res = tt::Subtree { |
@@ -353,15 +372,18 @@ fn include_bytes_expand( | |||
353 | id: tt::TokenId::unspecified(), | 372 | id: tt::TokenId::unspecified(), |
354 | }))], | 373 | }))], |
355 | }; | 374 | }; |
356 | Ok((res, FragmentKind::Expr)) | 375 | ExpandResult::ok(Some((res, FragmentKind::Expr))) |
357 | } | 376 | } |
358 | 377 | ||
359 | fn include_str_expand( | 378 | fn include_str_expand( |
360 | db: &dyn AstDatabase, | 379 | db: &dyn AstDatabase, |
361 | arg_id: EagerMacroId, | 380 | arg_id: EagerMacroId, |
362 | tt: &tt::Subtree, | 381 | tt: &tt::Subtree, |
363 | ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { | 382 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { |
364 | let path = parse_string(tt)?; | 383 | let path = match parse_string(tt) { |
384 | Ok(it) => it, | ||
385 | Err(e) => return ExpandResult::only_err(e), | ||
386 | }; | ||
365 | 387 | ||
366 | // FIXME: we're not able to read excluded files (which is most of them because | 388 | // FIXME: we're not able to read excluded files (which is most of them because |
367 | // it's unusual to `include_str!` a Rust file), but we can return an empty string. | 389 | // it's unusual to `include_str!` a Rust file), but we can return an empty string. |
@@ -370,14 +392,14 @@ fn include_str_expand( | |||
370 | let file_id = match relative_file(db, arg_id.into(), &path, true) { | 392 | let file_id = match relative_file(db, arg_id.into(), &path, true) { |
371 | Some(file_id) => file_id, | 393 | Some(file_id) => file_id, |
372 | None => { | 394 | None => { |
373 | return Ok((quote!(""), FragmentKind::Expr)); | 395 | return ExpandResult::ok(Some((quote!(""), FragmentKind::Expr))); |
374 | } | 396 | } |
375 | }; | 397 | }; |
376 | 398 | ||
377 | let text = db.file_text(file_id); | 399 | let text = db.file_text(file_id); |
378 | let text = &*text; | 400 | let text = &*text; |
379 | 401 | ||
380 | Ok((quote!(#text), FragmentKind::Expr)) | 402 | ExpandResult::ok(Some((quote!(#text), FragmentKind::Expr))) |
381 | } | 403 | } |
382 | 404 | ||
383 | fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> { | 405 | fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> { |
@@ -389,8 +411,11 @@ fn env_expand( | |||
389 | db: &dyn AstDatabase, | 411 | db: &dyn AstDatabase, |
390 | arg_id: EagerMacroId, | 412 | arg_id: EagerMacroId, |
391 | tt: &tt::Subtree, | 413 | tt: &tt::Subtree, |
392 | ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { | 414 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { |
393 | let key = parse_string(tt)?; | 415 | let key = match parse_string(tt) { |
416 | Ok(it) => it, | ||
417 | Err(e) => return ExpandResult::only_err(e), | ||
418 | }; | ||
394 | 419 | ||
395 | // FIXME: | 420 | // FIXME: |
396 | // If the environment variable is not defined int rustc, then a compilation error will be emitted. | 421 | // If the environment variable is not defined int rustc, then a compilation error will be emitted. |
@@ -402,21 +427,25 @@ fn env_expand( | |||
402 | let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string()); | 427 | let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string()); |
403 | let expanded = quote! { #s }; | 428 | let expanded = quote! { #s }; |
404 | 429 | ||
405 | Ok((expanded, FragmentKind::Expr)) | 430 | ExpandResult::ok(Some((expanded, FragmentKind::Expr))) |
406 | } | 431 | } |
407 | 432 | ||
408 | fn option_env_expand( | 433 | fn option_env_expand( |
409 | db: &dyn AstDatabase, | 434 | db: &dyn AstDatabase, |
410 | arg_id: EagerMacroId, | 435 | arg_id: EagerMacroId, |
411 | tt: &tt::Subtree, | 436 | tt: &tt::Subtree, |
412 | ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { | 437 | ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> { |
413 | let key = parse_string(tt)?; | 438 | let key = match parse_string(tt) { |
439 | Ok(it) => it, | ||
440 | Err(e) => return ExpandResult::only_err(e), | ||
441 | }; | ||
442 | |||
414 | let expanded = match get_env_inner(db, arg_id, &key) { | 443 | let expanded = match get_env_inner(db, arg_id, &key) { |
415 | None => quote! { std::option::Option::None::<&str> }, | 444 | None => quote! { std::option::Option::None::<&str> }, |
416 | Some(s) => quote! { std::option::Some(#s) }, | 445 | Some(s) => quote! { std::option::Some(#s) }, |
417 | }; | 446 | }; |
418 | 447 | ||
419 | Ok((expanded, FragmentKind::Expr)) | 448 | ExpandResult::ok(Some((expanded, FragmentKind::Expr))) |
420 | } | 449 | } |
421 | 450 | ||
422 | #[cfg(test)] | 451 | #[cfg(test)] |
@@ -485,7 +514,7 @@ mod tests { | |||
485 | } | 514 | } |
486 | }); | 515 | }); |
487 | 516 | ||
488 | let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).unwrap(); | 517 | let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).value.unwrap(); |
489 | let eager = EagerCallLoc { | 518 | let eager = EagerCallLoc { |
490 | def, | 519 | def, |
491 | fragment, | 520 | fragment, |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 46ebdbc74..4fd0ba290 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use base_db::{salsa, SourceDatabase}; | 5 | use base_db::{salsa, SourceDatabase}; |
6 | use mbe::{ExpandResult, MacroRules}; | 6 | use mbe::{ExpandError, ExpandResult, MacroRules}; |
7 | use parser::FragmentKind; | 7 | use parser::FragmentKind; |
8 | use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; | 8 | use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; |
9 | 9 | ||
@@ -30,8 +30,8 @@ impl TokenExpander { | |||
30 | ) -> mbe::ExpandResult<tt::Subtree> { | 30 | ) -> mbe::ExpandResult<tt::Subtree> { |
31 | match self { | 31 | match self { |
32 | TokenExpander::MacroRules(it) => it.expand(tt), | 32 | TokenExpander::MacroRules(it) => it.expand(tt), |
33 | TokenExpander::Builtin(it) => it.expand(db, id, tt), | ||
33 | // FIXME switch these to ExpandResult as well | 34 | // FIXME switch these to ExpandResult as well |
34 | TokenExpander::Builtin(it) => it.expand(db, id, tt).into(), | ||
35 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), | 35 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), |
36 | TokenExpander::ProcMacro(_) => { | 36 | TokenExpander::ProcMacro(_) => { |
37 | // We store the result in salsa db to prevent non-determinisc behavior in | 37 | // We store the result in salsa db to prevent non-determinisc behavior in |
@@ -81,6 +81,9 @@ pub trait AstDatabase: SourceDatabase { | |||
81 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; | 81 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; |
82 | fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; | 82 | fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; |
83 | 83 | ||
84 | /// Firewall query that returns the error from the `macro_expand` query. | ||
85 | fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>; | ||
86 | |||
84 | #[salsa::interned] | 87 | #[salsa::interned] |
85 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 88 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
86 | 89 | ||
@@ -171,6 +174,10 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar | |||
171 | macro_expand_with_arg(db, id, None) | 174 | macro_expand_with_arg(db, id, None) |
172 | } | 175 | } |
173 | 176 | ||
177 | fn macro_expand_error(db: &dyn AstDatabase, macro_call: MacroCallId) -> Option<ExpandError> { | ||
178 | db.macro_expand(macro_call).err | ||
179 | } | ||
180 | |||
174 | fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | 181 | fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { |
175 | let lazy_id = match id { | 182 | let lazy_id = match id { |
176 | MacroCallId::LazyMacro(id) => id, | 183 | MacroCallId::LazyMacro(id) => id, |
@@ -189,6 +196,7 @@ fn macro_expand_with_arg( | |||
189 | id: MacroCallId, | 196 | id: MacroCallId, |
190 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | 197 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, |
191 | ) -> ExpandResult<Option<Arc<tt::Subtree>>> { | 198 | ) -> ExpandResult<Option<Arc<tt::Subtree>>> { |
199 | let _p = profile::span("macro_expand"); | ||
192 | let lazy_id = match id { | 200 | let lazy_id = match id { |
193 | MacroCallId::LazyMacro(id) => id, | 201 | MacroCallId::LazyMacro(id) => id, |
194 | MacroCallId::EagerMacro(id) => { | 202 | MacroCallId::EagerMacro(id) => { |
@@ -276,14 +284,15 @@ fn parse_macro_with_arg( | |||
276 | macro_file: MacroFile, | 284 | macro_file: MacroFile, |
277 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | 285 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, |
278 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { | 286 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { |
279 | let _p = profile::span("parse_macro_query"); | ||
280 | |||
281 | let macro_call_id = macro_file.macro_call_id; | 287 | let macro_call_id = macro_file.macro_call_id; |
282 | let result = if let Some(arg) = arg { | 288 | let result = if let Some(arg) = arg { |
283 | macro_expand_with_arg(db, macro_call_id, Some(arg)) | 289 | macro_expand_with_arg(db, macro_call_id, Some(arg)) |
284 | } else { | 290 | } else { |
285 | db.macro_expand(macro_call_id) | 291 | db.macro_expand(macro_call_id) |
286 | }; | 292 | }; |
293 | |||
294 | let _p = profile::span("parse_macro_expansion"); | ||
295 | |||
287 | if let Some(err) = &result.err { | 296 | if let Some(err) = &result.err { |
288 | // Note: | 297 | // Note: |
289 | // The final goal we would like to make all parse_macro success, | 298 | // The final goal we would like to make all parse_macro success, |
diff --git a/crates/hir_expand/src/diagnostics.rs b/crates/hir_expand/src/diagnostics.rs index 1043c6aeb..bf0b85ce9 100644 --- a/crates/hir_expand/src/diagnostics.rs +++ b/crates/hir_expand/src/diagnostics.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | //! | 5 | //! |
6 | //! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating | 6 | //! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating |
7 | //! a `DiagnosticSink`, you supply a callback which can react to a `dyn | 7 | //! a `DiagnosticSink`, you supply a callback which can react to a `dyn |
8 | //! Diagnostic` or to any concrete diagnostic (downcasting is sued internally). | 8 | //! Diagnostic` or to any concrete diagnostic (downcasting is used internally). |
9 | //! | 9 | //! |
10 | //! Because diagnostics store file offsets, it's a bad idea to store them | 10 | //! Because diagnostics store file offsets, it's a bad idea to store them |
11 | //! directly in salsa. For this reason, every hir subsytem defines it's own | 11 | //! directly in salsa. For this reason, every hir subsytem defines it's own |
@@ -32,7 +32,12 @@ impl DiagnosticCode { | |||
32 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | 32 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { |
33 | fn code(&self) -> DiagnosticCode; | 33 | fn code(&self) -> DiagnosticCode; |
34 | fn message(&self) -> String; | 34 | fn message(&self) -> String; |
35 | /// Used in highlighting and related purposes | 35 | /// Source element that triggered the diagnostics. |
36 | /// | ||
37 | /// Note that this should reflect "semantics", rather than specific span we | ||
38 | /// want to highlight. When rendering the diagnostics into an error message, | ||
39 | /// the IDE will fetch the `SyntaxNode` and will narrow the span | ||
40 | /// appropriately. | ||
36 | fn display_source(&self) -> InFile<SyntaxNodePtr>; | 41 | fn display_source(&self) -> InFile<SyntaxNodePtr>; |
37 | fn as_any(&self) -> &(dyn Any + Send + 'static); | 42 | fn as_any(&self) -> &(dyn Any + Send + 'static); |
38 | fn is_experimental(&self) -> bool { | 43 | fn is_experimental(&self) -> bool { |
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index 2f37d7189..ab6b4477c 100644 --- a/crates/hir_expand/src/eager.rs +++ b/crates/hir_expand/src/eager.rs | |||
@@ -65,7 +65,7 @@ pub fn expand_eager_macro( | |||
65 | let subtree = to_subtree(&result)?; | 65 | let subtree = to_subtree(&result)?; |
66 | 66 | ||
67 | if let MacroDefKind::BuiltInEager(eager) = def.kind { | 67 | if let MacroDefKind::BuiltInEager(eager) = def.kind { |
68 | let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?; | 68 | let (subtree, fragment) = eager.expand(db, arg_id, &subtree).value?; |
69 | let eager = EagerCallLoc { | 69 | let eager = EagerCallLoc { |
70 | def, | 70 | def, |
71 | fragment, | 71 | fragment, |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index d5ba691b7..6dad2507b 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -255,7 +255,7 @@ pub enum MacroDefKind { | |||
255 | pub struct MacroCallLoc { | 255 | pub struct MacroCallLoc { |
256 | pub(crate) def: MacroDefId, | 256 | pub(crate) def: MacroDefId, |
257 | pub(crate) krate: CrateId, | 257 | pub(crate) krate: CrateId, |
258 | pub(crate) kind: MacroCallKind, | 258 | pub kind: MacroCallKind, |
259 | } | 259 | } |
260 | 260 | ||
261 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 261 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index 7505cb061..97edf0fb6 100644 --- a/crates/hir_expand/src/proc_macro.rs +++ b/crates/hir_expand/src/proc_macro.rs | |||
@@ -50,7 +50,7 @@ impl ProcMacroExpander { | |||
50 | 50 | ||
51 | proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) | 51 | proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) |
52 | } | 52 | } |
53 | None => Err(err!("Unresolved proc macro")), | 53 | None => Err(mbe::ExpandError::UnresolvedProcMacro), |
54 | } | 54 | } |
55 | } | 55 | } |
56 | } | 56 | } |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 3df73ed4f..9d3d88289 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -142,6 +142,15 @@ pub(crate) fn diagnostics( | |||
142 | .with_code(Some(d.code())), | 142 | .with_code(Some(d.code())), |
143 | ); | 143 | ); |
144 | }) | 144 | }) |
145 | .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { | ||
146 | // Use more accurate position if available. | ||
147 | let display_range = | ||
148 | d.precise_location.unwrap_or_else(|| sema.diagnostics_display_range(d).range); | ||
149 | |||
150 | // FIXME: it would be nice to tell the user whether proc macros are currently disabled | ||
151 | res.borrow_mut() | ||
152 | .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code()))); | ||
153 | }) | ||
145 | // Only collect experimental diagnostics when they're enabled. | 154 | // Only collect experimental diagnostics when they're enabled. |
146 | .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) | 155 | .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) |
147 | .filter(|diag| !config.disabled.contains(diag.code().as_str())); | 156 | .filter(|diag| !config.disabled.contains(diag.code().as_str())); |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 15792f947..b9810457f 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,14 +1,10 @@ | |||
1 | use hir::Semantics; | 1 | use hir::Semantics; |
2 | use ide_db::{ | 2 | use ide_db::{ |
3 | base_db::FileId, | ||
3 | defs::{NameClass, NameRefClass}, | 4 | defs::{NameClass, NameRefClass}, |
4 | symbol_index, RootDatabase, | 5 | symbol_index, RootDatabase, |
5 | }; | 6 | }; |
6 | use syntax::{ | 7 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; |
7 | ast::{self}, | ||
8 | match_ast, AstNode, | ||
9 | SyntaxKind::*, | ||
10 | SyntaxToken, TokenAtOffset, T, | ||
11 | }; | ||
12 | 8 | ||
13 | use crate::{ | 9 | use crate::{ |
14 | display::{ToNav, TryToNav}, | 10 | display::{ToNav, TryToNav}, |
@@ -44,6 +40,19 @@ pub(crate) fn goto_definition( | |||
44 | let nav = def.try_to_nav(sema.db)?; | 40 | let nav = def.try_to_nav(sema.db)?; |
45 | vec![nav] | 41 | vec![nav] |
46 | }, | 42 | }, |
43 | ast::SelfParam(self_param) => { | ||
44 | vec![self_to_nav_target(self_param, position.file_id)?] | ||
45 | }, | ||
46 | ast::PathSegment(segment) => { | ||
47 | segment.self_token()?; | ||
48 | let path = segment.parent_path(); | ||
49 | if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) { | ||
50 | return None; | ||
51 | } | ||
52 | let func = segment.syntax().ancestors().find_map(ast::Fn::cast)?; | ||
53 | let self_param = func.param_list()?.self_param()?; | ||
54 | vec![self_to_nav_target(self_param, position.file_id)?] | ||
55 | }, | ||
47 | _ => return None, | 56 | _ => return None, |
48 | } | 57 | } |
49 | }; | 58 | }; |
@@ -62,6 +71,20 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
62 | } | 71 | } |
63 | } | 72 | } |
64 | 73 | ||
74 | fn self_to_nav_target(self_param: ast::SelfParam, file_id: FileId) -> Option<NavigationTarget> { | ||
75 | let self_token = self_param.self_token()?; | ||
76 | Some(NavigationTarget { | ||
77 | file_id, | ||
78 | full_range: self_param.syntax().text_range(), | ||
79 | focus_range: Some(self_token.text_range()), | ||
80 | name: self_token.text().clone(), | ||
81 | kind: self_token.kind(), | ||
82 | container_name: None, | ||
83 | description: None, | ||
84 | docs: None, | ||
85 | }) | ||
86 | } | ||
87 | |||
65 | #[derive(Debug)] | 88 | #[derive(Debug)] |
66 | pub(crate) enum ReferenceResult { | 89 | pub(crate) enum ReferenceResult { |
67 | Exact(NavigationTarget), | 90 | Exact(NavigationTarget), |
@@ -984,4 +1007,33 @@ fn g() -> <() as Iterator<A = (), B<|> = u8>>::A {} | |||
984 | "#, | 1007 | "#, |
985 | ); | 1008 | ); |
986 | } | 1009 | } |
1010 | |||
1011 | #[test] | ||
1012 | fn goto_self_param_ty_specified() { | ||
1013 | check( | ||
1014 | r#" | ||
1015 | struct Foo {} | ||
1016 | |||
1017 | impl Foo { | ||
1018 | fn bar(self: &Foo) { | ||
1019 | //^^^^ | ||
1020 | let foo = sel<|>f; | ||
1021 | } | ||
1022 | }"#, | ||
1023 | ) | ||
1024 | } | ||
1025 | |||
1026 | #[test] | ||
1027 | fn goto_self_param_on_decl() { | ||
1028 | check( | ||
1029 | r#" | ||
1030 | struct Foo {} | ||
1031 | |||
1032 | impl Foo { | ||
1033 | fn bar(&self<|>) { | ||
1034 | //^^^^ | ||
1035 | } | ||
1036 | }"#, | ||
1037 | ) | ||
1038 | } | ||
987 | } | 1039 | } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 832192881..462f5c2b8 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -139,14 +139,17 @@ pub(crate) fn hover( | |||
139 | } | 139 | } |
140 | } | 140 | } |
141 | 141 | ||
142 | let node = token | 142 | let node = token.ancestors().find(|n| { |
143 | .ancestors() | 143 | ast::Expr::can_cast(n.kind()) |
144 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; | 144 | || ast::Pat::can_cast(n.kind()) |
145 | || ast::SelfParam::can_cast(n.kind()) | ||
146 | })?; | ||