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 | })?; | ||
145 | 147 | ||
146 | let ty = match_ast! { | 148 | let ty = match_ast! { |
147 | match node { | 149 | match node { |
148 | ast::Expr(it) => sema.type_of_expr(&it)?, | 150 | ast::Expr(it) => sema.type_of_expr(&it)?, |
149 | ast::Pat(it) => sema.type_of_pat(&it)?, | 151 | ast::Pat(it) => sema.type_of_pat(&it)?, |
152 | ast::SelfParam(self_param) => sema.type_of_self(&self_param)?, | ||
150 | // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. | 153 | // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. |
151 | // (e.g expanding a builtin macro). So we give up here. | 154 | // (e.g expanding a builtin macro). So we give up here. |
152 | ast::MacroCall(_it) => return None, | 155 | ast::MacroCall(_it) => return None, |
@@ -292,7 +295,6 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> | |||
292 | ModuleDef::EnumVariant(e) => Some(e.parent_enum(db).name(db)), | 295 | ModuleDef::EnumVariant(e) => Some(e.parent_enum(db).name(db)), |
293 | _ => None, | 296 | _ => None, |
294 | }, | 297 | }, |
295 | Definition::SelfType(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)), | ||
296 | _ => None, | 298 | _ => None, |
297 | } | 299 | } |
298 | .map(|name| name.to_string()) | 300 | .map(|name| name.to_string()) |
@@ -354,7 +356,14 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
354 | ModuleDef::BuiltinType(it) => return Some(it.to_string().into()), | 356 | ModuleDef::BuiltinType(it) => return Some(it.to_string().into()), |
355 | }, | 357 | }, |
356 | Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))), | 358 | Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))), |
357 | Definition::TypeParam(_) | Definition::SelfType(_) => { | 359 | Definition::SelfType(impl_def) => { |
360 | impl_def.target_ty(db).as_adt().and_then(|adt| match adt { | ||
361 | Adt::Struct(it) => from_def_source(db, it, mod_path), | ||
362 | Adt::Union(it) => from_def_source(db, it, mod_path), | ||
363 | Adt::Enum(it) => from_def_source(db, it, mod_path), | ||
364 | }) | ||
365 | } | ||
366 | Definition::TypeParam(_) => { | ||
358 | // FIXME: Hover for generic param | 367 | // FIXME: Hover for generic param |
359 | None | 368 | None |
360 | } | 369 | } |
@@ -1022,52 +1031,75 @@ impl Thing { | |||
1022 | } | 1031 | } |
1023 | "#, | 1032 | "#, |
1024 | expect![[r#" | 1033 | expect![[r#" |
1025 | *Self { x: 0 }* | 1034 | *Self* |
1035 | |||
1026 | ```rust | 1036 | ```rust |
1027 | Thing | 1037 | test |
1038 | ``` | ||
1039 | |||
1040 | ```rust | ||
1041 | struct Thing | ||
1028 | ``` | 1042 | ``` |
1029 | "#]], | 1043 | "#]], |
1030 | ) | 1044 | ); |
1031 | } /* FIXME: revive these tests | 1045 | check( |
1032 | let (analysis, position) = fixture::position( | 1046 | r#" |
1033 | " | 1047 | struct Thing { x: u32 } |
1034 | struct Thing { x: u32 } | 1048 | impl Thing { |
1035 | impl Thing { | 1049 | fn new() -> Self<|> { Self { x: 0 } } |
1036 | fn new() -> Self<|> { | 1050 | } |
1037 | Self { x: 0 } | 1051 | "#, |
1038 | } | 1052 | expect![[r#" |
1039 | } | 1053 | *Self* |
1040 | ", | 1054 | |
1041 | ); | 1055 | ```rust |
1042 | 1056 | test | |
1043 | let hover = analysis.hover(position).unwrap().unwrap(); | 1057 | ``` |
1044 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("Thing")); | 1058 | |
1045 | 1059 | ```rust | |
1046 | let (analysis, position) = fixture::position( | 1060 | struct Thing |
1047 | " | 1061 | ``` |
1048 | enum Thing { A } | 1062 | "#]], |
1049 | impl Thing { | 1063 | ); |
1050 | pub fn new() -> Self<|> { | 1064 | check( |
1051 | Thing::A | 1065 | r#" |
1052 | } | 1066 | enum Thing { A } |
1053 | } | 1067 | impl Thing { |
1054 | ", | 1068 | pub fn new() -> Self<|> { Thing::A } |
1055 | ); | 1069 | } |
1056 | let hover = analysis.hover(position).unwrap().unwrap(); | 1070 | "#, |
1057 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing")); | 1071 | expect![[r#" |
1058 | 1072 | *Self* | |
1059 | let (analysis, position) = fixture::position( | 1073 | |
1060 | " | 1074 | ```rust |
1061 | enum Thing { A } | 1075 | test |
1062 | impl Thing { | 1076 | ``` |
1063 | pub fn thing(a: Self<|>) { | 1077 | |
1064 | } | 1078 | ```rust |
1065 | } | 1079 | enum Thing |
1066 | ", | 1080 | ``` |
1067 | ); | 1081 | "#]], |
1068 | let hover = analysis.hover(position).unwrap().unwrap(); | 1082 | ); |
1069 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing")); | 1083 | check( |
1070 | */ | 1084 | r#" |
1085 | enum Thing { A } | ||
1086 | impl Thing { | ||
1087 | pub fn thing(a: Self<|>) {} | ||
1088 | } | ||
1089 | "#, | ||
1090 | expect![[r#" | ||
1091 | *Self* | ||
1092 | |||
1093 | ```rust | ||
1094 | test | ||
1095 | ``` | ||
1096 | |||
1097 | ```rust | ||
1098 | enum Thing | ||
1099 | ``` | ||
1100 | "#]], | ||
1101 | ); | ||
1102 | } | ||
1071 | 1103 | ||
1072 | #[test] | 1104 | #[test] |
1073 | fn test_hover_shadowing_pat() { | 1105 | fn test_hover_shadowing_pat() { |
@@ -3282,4 +3314,41 @@ fn main() { | |||
3282 | "#]], | 3314 | "#]], |
3283 | ); | 3315 | ); |
3284 | } | 3316 | } |
3317 | |||
3318 | #[test] | ||
3319 | fn hover_self_param_shows_type() { | ||
3320 | check( | ||
3321 | r#" | ||
3322 | struct Foo {} | ||
3323 | impl Foo { | ||
3324 | fn bar(&sel<|>f) {} | ||
3325 | } | ||
3326 | "#, | ||
3327 | expect![[r#" | ||
3328 | *&self* | ||
3329 | ```rust | ||
3330 | &Foo | ||
3331 | ``` | ||
3332 | "#]], | ||
3333 | ); | ||
3334 | } | ||
3335 | |||
3336 | #[test] | ||
3337 | fn hover_self_param_shows_type_for_arbitrary_self_type() { | ||
3338 | check( | ||
3339 | r#" | ||
3340 | struct Arc<T>(T); | ||
3341 | struct Foo {} | ||
3342 | impl Foo { | ||
3343 | fn bar(sel<|>f: Arc<Foo>) {} | ||
3344 | } | ||
3345 | "#, | ||
3346 | expect![[r#" | ||
3347 | *self: Arc<Foo>* | ||
3348 | ```rust | ||
3349 | Arc<Foo> | ||
3350 | ``` | ||
3351 | "#]], | ||
3352 | ); | ||
3353 | } | ||
3285 | } | 3354 | } |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 6cfb22e13..65df7979c 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use assists::utils::FamousDefs; | ||
2 | use either::Either; | 1 | use either::Either; |
3 | use hir::{known, Callable, HirDisplay, Semantics}; | 2 | use hir::{known, Callable, HirDisplay, Semantics}; |
3 | use ide_db::helpers::FamousDefs; | ||
4 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
5 | use stdx::to_lower_snake_case; | 5 | use stdx::to_lower_snake_case; |
6 | use syntax::{ | 6 | use syntax::{ |
@@ -427,8 +427,8 @@ fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir: | |||
427 | 427 | ||
428 | #[cfg(test)] | 428 | #[cfg(test)] |
429 | mod tests { | 429 | mod tests { |
430 | use assists::utils::FamousDefs; | ||
431 | use expect_test::{expect, Expect}; | 430 | use expect_test::{expect, Expect}; |
431 | use ide_db::helpers::FamousDefs; | ||
432 | use test_utils::extract_annotations; | 432 | use test_utils::extract_annotations; |
433 | 433 | ||
434 | use crate::{fixture, inlay_hints::InlayHintsConfig}; | 434 | use crate::{fixture, inlay_hints::InlayHintsConfig}; |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 6288f7ea7..5244bdd61 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -87,9 +87,7 @@ pub use ide_db::{ | |||
87 | search::{Reference, ReferenceAccess, ReferenceKind}, | 87 | search::{Reference, ReferenceAccess, ReferenceKind}, |
88 | }; | 88 | }; |
89 | 89 | ||
90 | pub use assists::{ | 90 | pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist}; |
91 | utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist, | ||
92 | }; | ||
93 | pub use hir::{Documentation, Semantics}; | 91 | pub use hir::{Documentation, Semantics}; |
94 | pub use ide_db::base_db::{ | 92 | pub use ide_db::base_db::{ |
95 | Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, | 93 | Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 5693dd400..7395b81bd 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -21,7 +21,7 @@ use ide_db::{ | |||
21 | use syntax::{ | 21 | use syntax::{ |
22 | algo::find_node_at_offset, | 22 | algo::find_node_at_offset, |
23 | ast::{self, NameOwner}, | 23 | ast::{self, NameOwner}, |
24 | AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, | 24 | match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; | 27 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; |
@@ -89,6 +89,10 @@ pub(crate) fn find_all_refs( | |||
89 | let _p = profile::span("find_all_refs"); | 89 | let _p = profile::span("find_all_refs"); |
90 | let syntax = sema.parse(position.file_id).syntax().clone(); | 90 | let syntax = sema.parse(position.file_id).syntax().clone(); |
91 | 91 | ||
92 | if let Some(res) = try_find_self_references(&syntax, position) { | ||
93 | return Some(res); | ||
94 | } | ||
95 | |||
92 | let (opt_name, search_kind) = if let Some(name) = | 96 | let (opt_name, search_kind) = if let Some(name) = |
93 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) | 97 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) |
94 | { | 98 | { |
@@ -194,6 +198,77 @@ fn get_struct_def_name_for_struct_literal_search( | |||
194 | None | 198 | None |
195 | } | 199 | } |
196 | 200 | ||
201 | fn try_find_self_references( | ||
202 | syntax: &SyntaxNode, | ||
203 | position: FilePosition, | ||
204 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | ||
205 | let self_token = | ||
206 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)?; | ||
207 | let parent = self_token.parent(); | ||
208 | match_ast! { | ||
209 | match parent { | ||
210 | ast::SelfParam(it) => (), | ||
211 | ast::PathSegment(segment) => { | ||
212 | segment.self_token()?; | ||
213 | let path = segment.parent_path(); | ||
214 | if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) { | ||
215 | return None; | ||
216 | } | ||
217 | }, | ||
218 | _ => return None, | ||
219 | } | ||
220 | }; | ||
221 | let function = parent.ancestors().find_map(ast::Fn::cast)?; | ||
222 | let self_param = function.param_list()?.self_param()?; | ||
223 | let param_self_token = self_param.self_token()?; | ||
224 | |||
225 | let declaration = Declaration { | ||
226 | nav: NavigationTarget { | ||
227 | file_id: position.file_id, | ||
228 | full_range: self_param.syntax().text_range(), | ||
229 | focus_range: Some(param_self_token.text_range()), | ||
230 | name: param_self_token.text().clone(), | ||
231 | kind: param_self_token.kind(), | ||
232 | container_name: None, | ||
233 | description: None, | ||
234 | docs: None, | ||
235 | }, | ||
236 | kind: ReferenceKind::SelfKw, | ||
237 | access: Some(if self_param.mut_token().is_some() { | ||
238 | ReferenceAccess::Write | ||
239 | } else { | ||
240 | ReferenceAccess::Read | ||
241 | }), | ||
242 | }; | ||
243 | let references = function | ||
244 | .body() | ||
245 | .map(|body| { | ||
246 | body.syntax() | ||
247 | .descendants() | ||
248 | .filter_map(ast::PathExpr::cast) | ||
249 | .filter_map(|expr| { | ||
250 | let path = expr.path()?; | ||
251 | if path.qualifier().is_none() { | ||
252 | path.segment()?.self_token() | ||
253 | } else { | ||
254 | None | ||
255 | } | ||
256 | }) | ||
257 | .map(|token| Reference { | ||
258 | file_range: FileRange { file_id: position.file_id, range: token.text_range() }, | ||
259 | kind: ReferenceKind::SelfKw, | ||
260 | access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration | ||
261 | }) | ||
262 | .collect() | ||
263 | }) | ||
264 | .unwrap_or_default(); | ||
265 | |||
266 | Some(RangeInfo::new( | ||
267 | param_self_token.text_range(), | ||
268 | ReferenceSearchResult { declaration, references }, | ||
269 | )) | ||
270 | } | ||
271 | |||
197 | #[cfg(test)] | 272 | #[cfg(test)] |
198 | mod tests { | 273 | mod tests { |
199 | use expect_test::{expect, Expect}; | 274 | use expect_test::{expect, Expect}; |
@@ -762,6 +837,32 @@ fn f() -> m::En { | |||
762 | ); | 837 | ); |
763 | } | 838 | } |
764 | 839 | ||
840 | #[test] | ||
841 | fn test_find_self_refs() { | ||
842 | check( | ||
843 | r#" | ||
844 | struct Foo { bar: i32 } | ||
845 | |||
846 | impl Foo { | ||
847 | fn foo(self) { | ||
848 | let x = self<|>.bar; | ||
849 | if true { | ||
850 | let _ = match () { | ||
851 | () => self, | ||
852 | }; | ||
853 | } | ||
854 | } | ||
855 | } | ||
856 | "#, | ||
857 | expect![[r#" | ||
858 | self SELF_KW FileId(0) 47..51 47..51 SelfKw Read | ||
859 | |||
860 | FileId(0) 71..75 SelfKw Read | ||
861 | FileId(0) 152..156 SelfKw Read | ||
862 | "#]], | ||
863 | ); | ||
864 | } | ||
865 | |||
765 | fn check(ra_fixture: &str, expect: Expect) { | 866 | fn check(ra_fixture: &str, expect: Expect) { |
766 | check_with_scope(ra_fixture, None, expect) | 867 | check_with_scope(ra_fixture, None, expect) |
767 | } | 868 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 91c64bd4a..64fe8bd65 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -221,24 +221,47 @@ fn rename_to_self( | |||
221 | let source_file = sema.parse(position.file_id); | 221 | let source_file = sema.parse(position.file_id); |
222 | let syn = source_file.syntax(); | 222 | let syn = source_file.syntax(); |
223 | 223 | ||
224 | let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset) | 224 | let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset) |
225 | .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast))) | ||
225 | .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; | 226 | .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; |
226 | let params = | 227 | let param_range = fn_ast |
227 | fn_def.param_list().ok_or_else(|| RenameError("Method has no parameters".to_string()))?; | 228 | .param_list() |
228 | if params.self_param().is_some() { | 229 | .and_then(|p| p.params().next()) |
230 | .ok_or_else(|| RenameError("Method has no parameters".to_string()))? | ||
231 | .syntax() | ||
232 | .text_range(); | ||
233 | if !param_range.contains(position.offset) { | ||
234 | return Err(RenameError("Only the first parameter can be self".to_string())); | ||
235 | } | ||
236 | |||
237 | let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset) | ||
238 | .and_then(|def| sema.to_def(&def)) | ||
239 | .ok_or_else(|| RenameError("No impl block found for function".to_string()))?; | ||
240 | if fn_def.self_param(sema.db).is_some() { | ||
229 | return Err(RenameError("Method already has a self parameter".to_string())); | 241 | return Err(RenameError("Method already has a self parameter".to_string())); |
230 | } | 242 | } |
243 | |||
244 | let params = fn_def.assoc_fn_params(sema.db); | ||
231 | let first_param = | 245 | let first_param = |
232 | params.params().next().ok_or_else(|| RenameError("Method has no parameters".into()))?; | 246 | params.first().ok_or_else(|| RenameError("Method has no parameters".into()))?; |
233 | let mutable = match first_param.ty() { | 247 | let first_param_ty = first_param.ty(); |
234 | Some(ast::Type::RefType(rt)) => rt.mut_token().is_some(), | 248 | let impl_ty = impl_block.target_ty(sema.db); |
235 | _ => return Err(RenameError("Not renaming other types".to_string())), | 249 | let (ty, self_param) = if impl_ty.remove_ref().is_some() { |
250 | // if the impl is a ref to the type we can just match the `&T` with self directly | ||
251 | (first_param_ty.clone(), "self") | ||
252 | } else { | ||
253 | first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| { | ||
254 | (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" }) | ||
255 | }) | ||
236 | }; | 256 | }; |
237 | 257 | ||
258 | if ty != impl_ty { | ||
259 | return Err(RenameError("Parameter type differs from impl block type".to_string())); | ||
260 | } | ||
261 | |||
238 | let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) | 262 | let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) |
239 | .ok_or_else(|| RenameError("No reference found at position".to_string()))?; | 263 | .ok_or_else(|| RenameError("No reference found at position".to_string()))?; |
240 | 264 | ||
241 | let param_range = first_param.syntax().text_range(); | ||
242 | let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs | 265 | let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs |
243 | .into_iter() | 266 | .into_iter() |
244 | .partition(|reference| param_range.intersect(reference.file_range.range).is_some()); | 267 | .partition(|reference| param_range.intersect(reference.file_range.range).is_some()); |
@@ -254,10 +277,7 @@ fn rename_to_self( | |||
254 | 277 | ||
255 | edits.push(SourceFileEdit { | 278 | edits.push(SourceFileEdit { |
256 | file_id: position.file_id, | 279 | file_id: position.file_id, |
257 | edit: TextEdit::replace( | 280 | edit: TextEdit::replace(param_range, String::from(self_param)), |
258 | param_range, | ||
259 | String::from(if mutable { "&mut self" } else { "&self" }), | ||
260 | ), | ||
261 | }); | 281 | }); |
262 | 282 | ||
263 | Ok(RangeInfo::new(range, SourceChange::from(edits))) | 283 | Ok(RangeInfo::new(range, SourceChange::from(edits))) |
@@ -280,7 +300,11 @@ fn text_edit_from_self_param( | |||
280 | 300 | ||
281 | let mut replacement_text = String::from(new_name); | 301 | let mut replacement_text = String::from(new_name); |
282 | replacement_text.push_str(": "); | 302 | replacement_text.push_str(": "); |
283 | replacement_text.push_str(self_param.mut_token().map_or("&", |_| "&mut ")); | 303 | match (self_param.amp_token(), self_param.mut_token()) { |
304 | (None, None) => (), | ||
305 | (Some(_), None) => replacement_text.push('&'), | ||
306 | (_, Some(_)) => replacement_text.push_str("&mut "), | ||
307 | }; | ||
284 | replacement_text.push_str(type_name.as_str()); | 308 | replacement_text.push_str(type_name.as_str()); |
285 | 309 | ||
286 | Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) | 310 | Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) |
@@ -1082,6 +1106,95 @@ impl Foo { | |||
1082 | } | 1106 | } |
1083 | "#, | 1107 | "#, |
1084 | ); | 1108 | ); |
1109 | check( | ||
1110 | "self", | ||
1111 | r#" | ||
1112 | struct Foo { i: i32 } | ||
1113 | |||
1114 | impl Foo { | ||
1115 | fn f(foo<|>: Foo) -> i32 { | ||
1116 | foo.i | ||
1117 | } | ||
1118 | } | ||
1119 | "#, | ||
1120 | r#" | ||
1121 | struct Foo { i: i32 } | ||
1122 | |||
1123 | impl Foo { | ||
1124 | fn f(self) -> i32 { | ||
1125 | self.i | ||
1126 | } | ||
1127 | } | ||
1128 | "#, | ||
1129 | ); | ||
1130 | } | ||
1131 | |||
1132 | #[test] | ||
1133 | fn test_parameter_to_self_error_no_impl() { | ||
1134 | check( | ||
1135 | "self", | ||
1136 | r#" | ||
1137 | struct Foo { i: i32 } | ||
1138 | |||
1139 | fn f(foo<|>: &mut Foo) -> i32 { | ||
1140 | foo.i | ||
1141 | } | ||
1142 | "#, | ||
1143 | "error: No impl block found for function", | ||
1144 | ); | ||
1145 | check( | ||
1146 | "self", | ||
1147 | r#" | ||
1148 | struct Foo { i: i32 } | ||
1149 | struct Bar; | ||
1150 | |||
1151 | impl Bar { | ||
1152 | fn f(foo<|>: &mut Foo) -> i32 { | ||
1153 | foo.i | ||
1154 | } | ||
1155 | } | ||
1156 | "#, | ||
1157 | "error: Parameter type differs from impl block type", | ||
1158 | ); | ||
1159 | } | ||
1160 | |||
1161 | #[test] | ||
1162 | fn test_parameter_to_self_error_not_first() { | ||
1163 | check( | ||
1164 | "self", | ||
1165 | r#" | ||
1166 | struct Foo { i: i32 } | ||
1167 | impl Foo { | ||
1168 | fn f(x: (), foo<|>: &mut Foo) -> i32 { | ||
1169 | foo.i | ||
1170 | } | ||
1171 | } | ||
1172 | "#, | ||
1173 | "error: Only the first parameter can be self", | ||
1174 | ); | ||
1175 | } | ||
1176 | |||
1177 | #[test] | ||
1178 | fn test_parameter_to_self_impl_ref() { | ||
1179 | check( | ||
1180 | "self", | ||
1181 | r#" | ||
1182 | struct Foo { i: i32 } | ||
1183 | impl &Foo { | ||
1184 | fn f(foo<|>: &Foo) -> i32 { | ||
1185 | foo.i | ||
1186 | } | ||
1187 | } | ||
1188 | "#, | ||
1189 | r#" | ||
1190 | struct Foo { i: i32 } | ||
1191 | impl &Foo { | ||
1192 | fn f(self) -> i32 { | ||
1193 | self.i | ||
1194 | } | ||
1195 | } | ||
1196 | "#, | ||
1197 | ); | ||
1085 | } | 1198 | } |
1086 | 1199 | ||
1087 | #[test] | 1200 | #[test] |
@@ -1110,6 +1223,31 @@ impl Foo { | |||
1110 | } | 1223 | } |
1111 | 1224 | ||
1112 | #[test] | 1225 | #[test] |
1226 | fn test_owned_self_to_parameter() { | ||
1227 | check( | ||
1228 | "foo", | ||
1229 | r#" | ||
1230 | struct Foo { i: i32 } | ||
1231 | |||
1232 | impl Foo { | ||
1233 | fn f(<|>self) -> i32 { | ||
1234 | self.i | ||
1235 | } | ||
1236 | } | ||
1237 | "#, | ||
1238 | r#" | ||
1239 | struct Foo { i: i32 } | ||
1240 | |||
1241 | impl Foo { | ||
1242 | fn f(foo: Foo) -> i32 { | ||
1243 | foo.i | ||
1244 | } | ||
1245 | } | ||
1246 | "#, | ||
1247 | ); | ||
1248 | } | ||
1249 | |||
1250 | #[test] | ||
1113 | fn test_self_in_path_to_parameter() { | 1251 | fn test_self_in_path_to_parameter() { |
1114 | check( | 1252 | check( |
1115 | "foo", | 1253 | "foo", |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 1ed77b40b..5150a970c 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -76,6 +76,7 @@ pub(crate) fn highlight( | |||
76 | let mut current_macro_call: Option<ast::MacroCall> = None; | 76 | let mut current_macro_call: Option<ast::MacroCall> = None; |
77 | let mut format_string_highlighter = FormatStringHighlighter::default(); | 77 | let mut format_string_highlighter = FormatStringHighlighter::default(); |
78 | let mut macro_rules_highlighter = MacroRulesHighlighter::default(); | 78 | let mut macro_rules_highlighter = MacroRulesHighlighter::default(); |
79 | let mut inside_attribute = false; | ||
79 | 80 | ||
80 | // Walk all nodes, keeping track of whether we are inside a macro or not. | 81 | // Walk all nodes, keeping track of whether we are inside a macro or not. |
81 | // If in macro, expand it first and highlight the expanded code. | 82 | // If in macro, expand it first and highlight the expanded code. |
@@ -132,9 +133,12 @@ pub(crate) fn highlight( | |||
132 | _ => (), | 133 | _ => (), |
133 | } | 134 | } |
134 | 135 | ||
135 | // Check for Rust code in documentation | ||
136 | match &event { | 136 | match &event { |
137 | // Check for Rust code in documentation | ||
137 | WalkEvent::Leave(NodeOrToken::Node(node)) => { | 138 | WalkEvent::Leave(NodeOrToken::Node(node)) => { |
139 | if ast::Attr::can_cast(node.kind()) { | ||
140 | inside_attribute = false | ||
141 | } | ||
138 | if let Some((doctest, range_mapping, new_comments)) = | 142 | if let Some((doctest, range_mapping, new_comments)) = |
139 | injection::extract_doc_comments(node) | 143 | injection::extract_doc_comments(node) |
140 | { | 144 | { |
@@ -146,6 +150,9 @@ pub(crate) fn highlight( | |||
146 | ); | 150 | ); |
147 | } | 151 | } |
148 | } | 152 | } |
153 | WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { | ||
154 | inside_attribute = true | ||
155 | } | ||
149 | _ => (), | 156 | _ => (), |
150 | } | 157 | } |
151 | 158 | ||
@@ -188,12 +195,16 @@ pub(crate) fn highlight( | |||
188 | } | 195 | } |
189 | } | 196 | } |
190 | 197 | ||
191 | if let Some((highlight, binding_hash)) = highlight_element( | 198 | if let Some((mut highlight, binding_hash)) = highlight_element( |
192 | &sema, | 199 | &sema, |
193 | &mut bindings_shadow_count, | 200 | &mut bindings_shadow_count, |
194 | syntactic_name_ref_highlighting, | 201 | syntactic_name_ref_highlighting, |
195 | element_to_highlight.clone(), | 202 | element_to_highlight.clone(), |
196 | ) { | 203 | ) { |
204 | if inside_attribute { | ||
205 | highlight = highlight | HighlightModifier::Attribute; | ||
206 | } | ||
207 | |||
197 | if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() { | 208 | if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() { |
198 | stack.add(HighlightedRange { range, highlight, binding_hash }); | 209 | stack.add(HighlightedRange { range, highlight, binding_hash }); |
199 | } | 210 | } |
@@ -474,7 +485,9 @@ fn highlight_element( | |||
474 | 485 | ||
475 | // Highlight references like the definitions they resolve to | 486 | // Highlight references like the definitions they resolve to |
476 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { | 487 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { |
477 | Highlight::from(HighlightTag::Function) | HighlightModifier::Attribute | 488 | // even though we track whether we are in an attribute or not we still need this special case |
489 | // as otherwise we would emit unresolved references for name refs inside attributes | ||
490 | Highlight::from(HighlightTag::Function) | ||
478 | } | 491 | } |
479 | NAME_REF => { | 492 | NAME_REF => { |
480 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); | 493 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 6be88f856..d79fa6dca 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -50,7 +50,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
50 | <span class="comment documentation">/// # Examples</span> | 50 | <span class="comment documentation">/// # Examples</span> |
51 | <span class="comment documentation">///</span> | 51 | <span class="comment documentation">///</span> |
52 | <span class="comment documentation">/// ```</span> | 52 | <span class="comment documentation">/// ```</span> |
53 | <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#</span><span class="attribute injected">!</span><span class="attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="attribute injected">]</span> | 53 | <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="punctuation attribute injected">)</span><span class="attribute attribute injected">]</span> |
54 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> | 54 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> |
55 | </span> <span class="comment documentation">/// ```</span> | 55 | </span> <span class="comment documentation">/// ```</span> |
56 | <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="struct">Foo</span> <span class="punctuation">{</span> | 56 | <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="struct">Foo</span> <span class="punctuation">{</span> |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 4b6d6adc9..1d05b7713 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html | |||
@@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
54 | 54 | ||
55 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 55 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> |
56 | 56 | ||
57 | <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">repr</span><span class="punctuation">(</span><span class="attribute">packed</span><span class="punctuation">)</span><span class="attribute">]</span> | 57 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">repr</span><span class="punctuation attribute">(</span><span class="attribute attribute">packed</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span> |
58 | <span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="punctuation">{</span> | 58 | <span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="punctuation">{</span> |
59 | <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u16</span><span class="punctuation">,</span> | 59 | <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u16</span><span class="punctuation">,</span> |
60 | <span class="punctuation">}</span> | 60 | <span class="punctuation">}</span> |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 6a10a9dcd..15fbf2ce3 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -40,18 +40,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
40 | 40 | ||
41 | <span class="comment">// Needed for function consuming vs normal</span> | 41 | <span class="comment">// Needed for function consuming vs normal</span> |
42 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span> | 42 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span> |
43 | <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"copy"</span><span class="attribute">]</span> | 43 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span> |
44 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 44 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> |
45 | <span class="punctuation">}</span> | 45 | <span class="punctuation">}</span> |
46 | 46 | ||
47 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="punctuation">{</span> | 47 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="punctuation">{</span> |
48 | <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"fn_once"</span><span class="attribute">]</span> | 48 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span> |
49 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="punctuation"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">}</span> | 49 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="punctuation"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">}</span> |
50 | 50 | ||
51 | <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"fn_mut"</span><span class="attribute">]</span> | 51 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span> |
52 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="punctuation"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">}</span> | 52 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="punctuation"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">}</span> |
53 | 53 | ||
54 | <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"fn"</span><span class="attribute">]</span> | 54 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span> |
55 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="punctuation"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">}</span> | 55 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="punctuation"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">}</span> |
56 | <span class="punctuation">}</span> | 56 | <span class="punctuation">}</span> |
57 | 57 | ||
@@ -85,7 +85,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
85 | <span class="punctuation">}</span> | 85 | <span class="punctuation">}</span> |
86 | <span class="punctuation">}</span> | 86 | <span class="punctuation">}</span> |
87 | 87 | ||
88 | <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Copy</span><span class="punctuation">)</span><span class="attribute">]</span> | 88 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">derive</span><span class="punctuation attribute">(</span><span class="attribute attribute">Copy</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span> |
89 | <span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> | 89 | <span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> |
90 | <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> | 90 | <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> |
91 | <span class="punctuation">}</span> | 91 | <span class="punctuation">}</span> |
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml index 72a9212f1..0ad6e1000 100644 --- a/crates/ide_db/Cargo.toml +++ b/crates/ide_db/Cargo.toml | |||
@@ -18,7 +18,8 @@ rayon = "1.5.0" | |||
18 | fst = { version = "0.4", default-features = false } | 18 | fst = { version = "0.4", default-features = false } |
19 | rustc-hash = "1.1.0" | 19 | rustc-hash = "1.1.0" |
20 | once_cell = "1.3.1" | 20 | once_cell = "1.3.1" |
21 | either = "1.5.3" | 21 | either = "1.6.1" |
22 | itertools = "0.9.0" | ||
22 | 23 | ||
23 | stdx = { path = "../stdx", version = "0.0.0" } | 24 | stdx = { path = "../stdx", version = "0.0.0" } |
24 | syntax = { path = "../syntax", version = "0.0.0" } | 25 | syntax = { path = "../syntax", version = "0.0.0" } |
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs new file mode 100644 index 000000000..d988588ff --- /dev/null +++ b/crates/ide_db/src/helpers.rs | |||
@@ -0,0 +1,203 @@ | |||
1 | //! A module with ide helpers for high-level ide features. | ||
2 | use crate::RootDatabase; | ||
3 | use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; | ||
4 | use syntax::ast::{self, make}; | ||
5 | |||
6 | pub mod insert_use; | ||
7 | |||
8 | /// Converts the mod path struct into its ast representation. | ||
9 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { | ||
10 | let _p = profile::span("mod_path_to_ast"); | ||
11 | |||
12 | let mut segments = Vec::new(); | ||
13 | let mut is_abs = false; | ||
14 | match path.kind { | ||
15 | hir::PathKind::Plain => {} | ||
16 | hir::PathKind::Super(0) => segments.push(make::path_segment_self()), | ||
17 | hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())), | ||
18 | hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => { | ||
19 | segments.push(make::path_segment_crate()) | ||
20 | } | ||
21 | hir::PathKind::Abs => is_abs = true, | ||
22 | } | ||
23 | |||
24 | segments.extend( | ||
25 | path.segments | ||
26 | .iter() | ||
27 | .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))), | ||
28 | ); | ||
29 | make::path_from_segments(segments, is_abs) | ||
30 | } | ||
31 | |||
32 | /// Helps with finding well-know things inside the standard library. This is | ||
33 | /// somewhat similar to the known paths infra inside hir, but it different; We | ||
34 | /// want to make sure that IDE specific paths don't become interesting inside | ||
35 | /// the compiler itself as well. | ||
36 | pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Crate>); | ||
37 | |||
38 | #[allow(non_snake_case)] | ||
39 | impl FamousDefs<'_, '_> { | ||
40 | pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core | ||
41 | pub mod convert { | ||
42 | pub trait From<T> { | ||
43 | fn from(t: T) -> Self; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | pub mod default { | ||
48 | pub trait Default { | ||
49 | fn default() -> Self; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | pub mod iter { | ||
54 | pub use self::traits::{collect::IntoIterator, iterator::Iterator}; | ||
55 | mod traits { | ||
56 | pub(crate) mod iterator { | ||
57 | use crate::option::Option; | ||
58 | pub trait Iterator { | ||
59 | type Item; | ||
60 | fn next(&mut self) -> Option<Self::Item>; | ||
61 | fn by_ref(&mut self) -> &mut Self { | ||
62 | self | ||
63 | } | ||
64 | fn take(self, n: usize) -> crate::iter::Take<Self> { | ||
65 | crate::iter::Take { inner: self } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | impl<I: Iterator> Iterator for &mut I { | ||
70 | type Item = I::Item; | ||
71 | fn next(&mut self) -> Option<I::Item> { | ||
72 | (**self).next() | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | pub(crate) mod collect { | ||
77 | pub trait IntoIterator { | ||
78 | type Item; | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | pub use self::sources::*; | ||
84 | pub(crate) mod sources { | ||
85 | use super::Iterator; | ||
86 | use crate::option::Option::{self, *}; | ||
87 | pub struct Repeat<A> { | ||
88 | element: A, | ||
89 | } | ||
90 | |||
91 | pub fn repeat<T>(elt: T) -> Repeat<T> { | ||
92 | Repeat { element: elt } | ||
93 | } | ||
94 | |||
95 | impl<A> Iterator for Repeat<A> { | ||
96 | type Item = A; | ||
97 | |||
98 | fn next(&mut self) -> Option<A> { | ||
99 | None | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | pub use self::adapters::*; | ||
105 | pub(crate) mod adapters { | ||
106 | use super::Iterator; | ||
107 | use crate::option::Option::{self, *}; | ||
108 | pub struct Take<I> { pub(crate) inner: I } | ||
109 | impl<I> Iterator for Take<I> where I: Iterator { | ||
110 | type Item = <I as Iterator>::Item; | ||
111 | fn next(&mut self) -> Option<<I as Iterator>::Item> { | ||
112 | None | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | |||
118 | pub mod option { | ||
119 | pub enum Option<T> { None, Some(T)} | ||
120 | } | ||
121 | |||
122 | pub mod prelude { | ||
123 | pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default}; | ||
124 | } | ||
125 | #[prelude_import] | ||
126 | pub use prelude::*; | ||
127 | "#; | ||
128 | |||
129 | pub fn core(&self) -> Option<Crate> { | ||
130 | self.find_crate("core") | ||
131 | } | ||
132 | |||
133 | pub fn core_convert_From(&self) -> Option<Trait> { | ||
134 | self.find_trait("core:convert:From") | ||
135 | } | ||
136 | |||
137 | pub fn core_option_Option(&self) -> Option<Enum> { | ||
138 | self.find_enum("core:option:Option") | ||
139 | } | ||
140 | |||
141 | pub fn core_default_Default(&self) -> Option<Trait> { | ||
142 | self.find_trait("core:default:Default") | ||
143 | } | ||
144 | |||
145 | pub fn core_iter_Iterator(&self) -> Option<Trait> { | ||
146 | self.find_trait("core:iter:traits:iterator:Iterator") | ||
147 | } | ||
148 | |||
149 | pub fn core_iter(&self) -> Option<Module> { | ||
150 | self.find_module("core:iter") | ||
151 | } | ||
152 | |||
153 | fn find_trait(&self, path: &str) -> Option<Trait> { | ||
154 | match self.find_def(path)? { | ||
155 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), | ||
156 | _ => None, | ||
157 | } | ||
158 | } | ||
159 | |||
160 | fn find_enum(&self, path: &str) -> Option<Enum> { | ||
161 | match self.find_def(path)? { | ||
162 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it), | ||
163 | _ => None, | ||
164 | } | ||
165 | } | ||
166 | |||
167 | fn find_module(&self, path: &str) -> Option<Module> { | ||
168 | match self.find_def(path)? { | ||
169 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it), | ||
170 | _ => None, | ||
171 | } | ||
172 | } | ||
173 | |||
174 | fn find_crate(&self, name: &str) -> Option<Crate> { | ||
175 | let krate = self.1?; | ||
176 | let db = self.0.db; | ||
177 | let res = | ||
178 | krate.dependencies(db).into_iter().find(|dep| dep.name.to_string() == name)?.krate; | ||
179 | Some(res) | ||
180 | } | ||
181 | |||
182 | fn find_def(&self, path: &str) -> Option<ScopeDef> { | ||
183 | let db = self.0.db; | ||
184 | let mut path = path.split(':'); | ||
185 | let trait_ = path.next_back()?; | ||
186 | let std_crate = path.next()?; | ||
187 | let std_crate = self.find_crate(std_crate)?; | ||
188 | let mut module = std_crate.root_module(db); | ||
189 | for segment in path { | ||
190 | module = module.children(db).find_map(|child| { | ||
191 | let name = child.name(db)?; | ||
192 | if name.to_string() == segment { | ||
193 | Some(child) | ||
194 | } else { | ||
195 | None | ||
196 | } | ||
197 | })?; | ||
198 | } | ||
199 | let def = | ||
200 | module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1; | ||
201 | Some(def) | ||
202 | } | ||
203 | } | ||
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index 423782a0e..08d246c16 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -1,8 +1,8 @@ | |||
1 | //! Handle syntactic aspects of inserting a new `use`. | 1 | //! Handle syntactic aspects of inserting a new `use`. |
2 | use std::{cmp::Ordering, iter::successors}; | 2 | use std::{cmp::Ordering, iter::successors}; |
3 | 3 | ||
4 | use crate::RootDatabase; | ||
4 | use hir::Semantics; | 5 | use hir::Semantics; |
5 | use ide_db::RootDatabase; | ||
6 | use itertools::{EitherOrBoth, Itertools}; | 6 | use itertools::{EitherOrBoth, Itertools}; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | algo::SyntaxRewriter, | 8 | algo::SyntaxRewriter, |
@@ -22,7 +22,7 @@ pub enum ImportScope { | |||
22 | } | 22 | } |
23 | 23 | ||
24 | impl ImportScope { | 24 | impl ImportScope { |
25 | pub(crate) fn from(syntax: SyntaxNode) -> Option<Self> { | 25 | pub fn from(syntax: SyntaxNode) -> Option<Self> { |
26 | if let Some(module) = ast::Module::cast(syntax.clone()) { | 26 | if let Some(module) = ast::Module::cast(syntax.clone()) { |
27 | module.item_list().map(ImportScope::Module) | 27 | module.item_list().map(ImportScope::Module) |
28 | } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) { | 28 | } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) { |
@@ -95,6 +95,7 @@ pub fn insert_use<'a>( | |||
95 | path: ast::Path, | 95 | path: ast::Path, |
96 | merge: Option<MergeBehaviour>, | 96 | merge: Option<MergeBehaviour>, |
97 | ) -> SyntaxRewriter<'a> { | 97 | ) -> SyntaxRewriter<'a> { |
98 | let _p = profile::span("insert_use"); | ||
98 | let mut rewriter = SyntaxRewriter::default(); | 99 | let mut rewriter = SyntaxRewriter::default(); |
99 | let use_item = make::use_(make::use_tree(path.clone(), None, None, false)); | 100 | let use_item = make::use_(make::use_tree(path.clone(), None, None, false)); |
100 | // merge into existing imports if possible | 101 | // merge into existing imports if possible |
@@ -179,7 +180,7 @@ fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) - | |||
179 | } | 180 | } |
180 | } | 181 | } |
181 | 182 | ||
182 | pub(crate) fn try_merge_imports( | 183 | pub fn try_merge_imports( |
183 | lhs: &ast::Use, | 184 | lhs: &ast::Use, |
184 | rhs: &ast::Use, | 185 | rhs: &ast::Use, |
185 | merge_behaviour: MergeBehaviour, | 186 | merge_behaviour: MergeBehaviour, |
@@ -194,7 +195,7 @@ pub(crate) fn try_merge_imports( | |||
194 | Some(lhs.with_use_tree(merged)) | 195 | Some(lhs.with_use_tree(merged)) |
195 | } | 196 | } |
196 | 197 | ||
197 | pub(crate) fn try_merge_trees( | 198 | pub fn try_merge_trees( |
198 | lhs: &ast::UseTree, | 199 | lhs: &ast::UseTree, |
199 | rhs: &ast::UseTree, | 200 | rhs: &ast::UseTree, |
200 | merge: MergeBehaviour, | 201 | merge: MergeBehaviour, |
@@ -383,7 +384,7 @@ fn path_cmp_for_sort(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering { | |||
383 | 384 | ||
384 | /// Path comparison func for binary searching for merging. | 385 | /// Path comparison func for binary searching for merging. |
385 | fn path_cmp_bin_search(lhs: Option<ast::Path>, rhs: Option<ast::Path>) -> Ordering { | 386 | fn path_cmp_bin_search(lhs: Option<ast::Path>, rhs: Option<ast::Path>) -> Ordering { |
386 | match (lhs.and_then(|path| path.segment()), rhs.and_then(|path| path.segment())) { | 387 | match (lhs.as_ref().and_then(first_segment), rhs.as_ref().and_then(first_segment)) { |
387 | (None, None) => Ordering::Equal, | 388 | (None, None) => Ordering::Equal, |
388 | (None, Some(_)) => Ordering::Less, | 389 | (None, Some(_)) => Ordering::Less, |
389 | (Some(_), None) => Ordering::Greater, | 390 | (Some(_), None) => Ordering::Greater, |
@@ -1081,6 +1082,15 @@ use std::io;", | |||
1081 | } | 1082 | } |
1082 | 1083 | ||
1083 | #[test] | 1084 | #[test] |
1085 | fn merge_nested_considers_first_segments() { | ||
1086 | check_full( | ||
1087 | "hir_ty::display::write_bounds_like_dyn_trait", | ||
1088 | r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};", | ||
1089 | r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};", | ||
1090 | ); | ||
1091 | } | ||
1092 | |||
1093 | #[test] | ||
1084 | fn skip_merge_last_too_long() { | 1094 | fn skip_merge_last_too_long() { |
1085 | check_last( | 1095 | check_last( |
1086 | "foo::bar", | 1096 | "foo::bar", |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 05139a651..fceaa089a 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -13,6 +13,7 @@ pub mod source_change; | |||
13 | pub mod ty_filter; | 13 | pub mod ty_filter; |
14 | pub mod traits; | 14 | pub mod traits; |
15 | pub mod call_info; | 15 | pub mod call_info; |
16 | pub mod helpers; | ||
16 | 17 | ||
17 | use std::{fmt, sync::Arc}; | 18 | use std::{fmt, sync::Arc}; |
18 | 19 | ||
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index a3e765d05..607185ca9 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -31,6 +31,7 @@ pub enum ReferenceKind { | |||
31 | FieldShorthandForLocal, | 31 | FieldShorthandForLocal, |
32 | StructLiteral, | 32 | StructLiteral, |
33 | RecordFieldExprOrPat, | 33 | RecordFieldExprOrPat, |
34 | SelfKw, | ||
34 | Other, | 35 | Other, |
35 | } | 36 | } |
36 | 37 | ||
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 2d0763c47..3ad609a00 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs | |||
@@ -35,6 +35,7 @@ pub enum ExpandError { | |||
35 | ConversionError, | 35 | ConversionError, |
36 | InvalidRepeat, | 36 | InvalidRepeat, |
37 | ProcMacroError(tt::ExpansionError), | 37 | ProcMacroError(tt::ExpansionError), |
38 | UnresolvedProcMacro, | ||
38 | Other(String), | 39 | Other(String), |
39 | } | 40 | } |
40 | 41 | ||
@@ -53,6 +54,7 @@ impl fmt::Display for ExpandError { | |||
53 | ExpandError::ConversionError => f.write_str("could not convert tokens"), | 54 | ExpandError::ConversionError => f.write_str("could not convert tokens"), |
54 | ExpandError::InvalidRepeat => f.write_str("invalid repeat expression"), | 55 | ExpandError::InvalidRepeat => f.write_str("invalid repeat expression"), |
55 | ExpandError::ProcMacroError(e) => e.fmt(f), | 56 | ExpandError::ProcMacroError(e) => e.fmt(f), |
57 | ExpandError::UnresolvedProcMacro => f.write_str("unresolved proc macro"), | ||
56 | ExpandError::Other(e) => f.write_str(e), | 58 | ExpandError::Other(e) => f.write_str(e), |
57 | } | 59 | } |
58 | } | 60 | } |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 436f5041b..0a055b039 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -21,7 +21,7 @@ env_logger = { version = "0.8.1", default-features = false } | |||
21 | itertools = "0.9.0" | 21 | itertools = "0.9.0" |
22 | jod-thread = "0.1.0" | 22 | jod-thread = "0.1.0" |
23 | log = "0.4.8" | 23 | log = "0.4.8" |
24 | lsp-types = { version = "0.84.0", features = ["proposed"] } | 24 | lsp-types = { version = "0.85.0", features = ["proposed"] } |
25 | parking_lot = "0.11.0" | 25 | parking_lot = "0.11.0" |
26 | pico-args = "0.3.1" | 26 | pico-args = "0.3.1" |
27 | oorandom = "11.1.2" | 27 | oorandom = "11.1.2" |
@@ -39,6 +39,7 @@ tracing-tree = { version = "0.1.4" } | |||
39 | stdx = { path = "../stdx", version = "0.0.0" } | 39 | stdx = { path = "../stdx", version = "0.0.0" } |
40 | flycheck = { path = "../flycheck", version = "0.0.0" } | 40 | flycheck = { path = "../flycheck", version = "0.0.0" } |
41 | ide = { path = "../ide", version = "0.0.0" } | 41 | ide = { path = "../ide", version = "0.0.0" } |
42 | ide_db = { path = "../ide_db", version = "0.0.0" } | ||
42 | profile = { path = "../profile", version = "0.0.0" } | 43 | profile = { path = "../profile", version = "0.0.0" } |
43 | project_model = { path = "../project_model", version = "0.0.0" } | 44 | project_model = { path = "../project_model", version = "0.0.0" } |
44 | syntax = { path = "../syntax", version = "0.0.0" } | 45 | syntax = { path = "../syntax", version = "0.0.0" } |
@@ -49,7 +50,6 @@ cfg = { path = "../cfg", version = "0.0.0" } | |||
49 | toolchain = { path = "../toolchain", version = "0.0.0" } | 50 | toolchain = { path = "../toolchain", version = "0.0.0" } |
50 | 51 | ||
51 | # This should only be used in CLI | 52 | # This should only be used in CLI |
52 | ide_db = { path = "../ide_db", version = "0.0.0" } | ||
53 | ssr = { path = "../ssr", version = "0.0.0" } | 53 | ssr = { path = "../ssr", version = "0.0.0" } |
54 | hir = { path = "../hir", version = "0.0.0" } | 54 | hir = { path = "../hir", version = "0.0.0" } |
55 | hir_def = { path = "../hir_def", version = "0.0.0" } | 55 | hir_def = { path = "../hir_def", version = "0.0.0" } |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a334cdb11..59269a74b 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -11,10 +11,8 @@ use std::{convert::TryFrom, ffi::OsString, path::PathBuf}; | |||
11 | 11 | ||
12 | use flycheck::FlycheckConfig; | 12 | use flycheck::FlycheckConfig; |
13 | use hir::PrefixKind; | 13 | use hir::PrefixKind; |
14 | use ide::{ | 14 | use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig}; |
15 | AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, | 15 | use ide_db::helpers::insert_use::MergeBehaviour; |
16 | MergeBehaviour, | ||
17 | }; | ||
18 | use lsp_types::{ClientCapabilities, MarkupKind}; | 16 | use lsp_types::{ClientCapabilities, MarkupKind}; |
19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; | 17 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; |
20 | use rustc_hash::FxHashSet; | 18 | use rustc_hash::FxHashSet; |
@@ -482,7 +480,7 @@ macro_rules! config_data { | |||
482 | 480 | ||
483 | config_data! { | 481 | config_data! { |
484 | struct ConfigData { | 482 | struct ConfigData { |
485 | assist_importMergeBehaviour: MergeBehaviourDef = MergeBehaviourDef::None, | 483 | assist_importMergeBehaviour: MergeBehaviourDef = MergeBehaviourDef::Full, |
486 | assist_importPrefix: ImportPrefixDef = ImportPrefixDef::Plain, | 484 | assist_importPrefix: ImportPrefixDef = ImportPrefixDef::Plain, |
487 | 485 | ||
488 | callInfo_full: bool = true, | 486 | callInfo_full: bool = true, |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 866d1d176..f349b0810 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -289,55 +289,69 @@ impl GlobalState { | |||
289 | } | 289 | } |
290 | } | 290 | } |
291 | } | 291 | } |
292 | Event::Flycheck(task) => match task { | 292 | Event::Flycheck(mut task) => { |
293 | flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { | 293 | let _p = profile::span("GlobalState::handle_event/flycheck"); |
294 | let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( | 294 | loop { |
295 | &self.config.diagnostics_map, | 295 | match task { |
296 | &diagnostic, | 296 | flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { |
297 | &workspace_root, | 297 | let diagnostics = |
298 | ); | 298 | crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( |
299 | for diag in diagnostics { | 299 | &self.config.diagnostics_map, |
300 | match url_to_file_id(&self.vfs.read().0, &diag.url) { | 300 | &diagnostic, |
301 | Ok(file_id) => self.diagnostics.add_check_diagnostic( | 301 | &workspace_root, |
302 | file_id, | 302 | ); |
303 | diag.diagnostic, | 303 | for diag in diagnostics { |
304 | diag.fixes, | 304 | match url_to_file_id(&self.vfs.read().0, &diag.url) { |
305 | ), | 305 | Ok(file_id) => self.diagnostics.add_check_diagnostic( |
306 | Err(err) => { | 306 | file_id, |
307 | log::error!("File with cargo diagnostic not found in VFS: {}", err); | 307 | diag.diagnostic, |
308 | } | 308 | diag.fixes, |
309 | }; | 309 | ), |
310 | } | 310 | Err(err) => { |
311 | } | 311 | log::error!( |
312 | 312 | "File with cargo diagnostic not found in VFS: {}", | |
313 | flycheck::Message::Progress { id, progress } => { | 313 | err |
314 | let (state, message) = match progress { | 314 | ); |
315 | flycheck::Progress::DidStart => { | 315 | } |
316 | self.diagnostics.clear_check(); | 316 | }; |
317 | (Progress::Begin, None) | ||
318 | } | ||
319 | flycheck::Progress::DidCheckCrate(target) => { | ||
320 | (Progress::Report, Some(target)) | ||
321 | } | ||
322 | flycheck::Progress::DidCancel => (Progress::End, None), | ||
323 | flycheck::Progress::DidFinish(result) => { | ||
324 | if let Err(err) = result { | ||
325 | log::error!("cargo check failed: {}", err) | ||
326 | } | 317 | } |
327 | (Progress::End, None) | ||
328 | } | 318 | } |
329 | }; | ||
330 | 319 | ||
331 | // When we're running multiple flychecks, we have to include a disambiguator in | 320 | flycheck::Message::Progress { id, progress } => { |
332 | // the title, or the editor complains. Note that this is a user-facing string. | 321 | let (state, message) = match progress { |
333 | let title = if self.flycheck.len() == 1 { | 322 | flycheck::Progress::DidStart => { |
334 | "cargo check".to_string() | 323 | self.diagnostics.clear_check(); |
335 | } else { | 324 | (Progress::Begin, None) |
336 | format!("cargo check (#{})", id + 1) | 325 | } |
337 | }; | 326 | flycheck::Progress::DidCheckCrate(target) => { |
338 | self.report_progress(&title, state, message, None); | 327 | (Progress::Report, Some(target)) |
328 | } | ||
329 | flycheck::Progress::DidCancel => (Progress::End, None), | ||
330 | flycheck::Progress::DidFinish(result) => { | ||
331 | if let Err(err) = result { | ||
332 | log::error!("cargo check failed: {}", err) | ||
333 | } | ||
334 | (Progress::End, None) | ||
335 | } | ||
336 | }; | ||
337 | |||
338 | // When we're running multiple flychecks, we have to include a disambiguator in | ||
339 | // the title, or the editor complains. Note that this is a user-facing string. | ||
340 | let title = if self.flycheck.len() == 1 { | ||
341 | "cargo check".to_string() | ||
342 | } else { | ||
343 | format!("cargo check (#{})", id + 1) | ||
344 | }; | ||
345 | self.report_progress(&title, state, message, None); | ||
346 | } | ||
347 | } | ||
348 | // Coalesce many flycheck updates into a single loop turn | ||
349 | task = match self.flycheck_receiver.try_recv() { | ||
350 | Ok(task) => task, | ||
351 | Err(_) => break, | ||
352 | } | ||
339 | } | 353 | } |
340 | }, | 354 | } |
341 | } | 355 | } |
342 | 356 | ||
343 | let state_changed = self.process_changes(); | 357 | let state_changed = self.process_changes(); |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 001bf5949..b2d35f535 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -205,7 +205,7 @@ impl GlobalState { | |||
205 | } | 205 | } |
206 | let res = vfs.file_id(&vfs_path); | 206 | let res = vfs.file_id(&vfs_path); |
207 | if res.is_none() { | 207 | if res.is_none() { |
208 | log::error!("failed to load {}", path.display()) | 208 | log::warn!("failed to load {}", path.display()) |
209 | } | 209 | } |
210 | res | 210 | res |
211 | }; | 211 | }; |
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index e7991fd28..1daad1c98 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -27,7 +27,7 @@ macro_rules! define_semantic_token_types { | |||
27 | SemanticTokenType::ENUM_MEMBER, | 27 | SemanticTokenType::ENUM_MEMBER, |
28 | SemanticTokenType::TYPE_PARAMETER, | 28 | SemanticTokenType::TYPE_PARAMETER, |
29 | SemanticTokenType::FUNCTION, | 29 | SemanticTokenType::FUNCTION, |
30 | SemanticTokenType::MEMBER, | 30 | SemanticTokenType::METHOD, |
31 | SemanticTokenType::PROPERTY, | 31 | SemanticTokenType::PROPERTY, |
32 | SemanticTokenType::MACRO, | 32 | SemanticTokenType::MACRO, |
33 | SemanticTokenType::VARIABLE, | 33 | SemanticTokenType::VARIABLE, |
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 1fe907753..c6a6f11e1 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
15 | rowan = "0.10.0" | 15 | rowan = "0.10.0" |
16 | rustc_lexer = { version = "688.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "691.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |
@@ -28,6 +28,7 @@ stdx = { path = "../stdx", version = "0.0.0" } | |||
28 | text_edit = { path = "../text_edit", version = "0.0.0" } | 28 | text_edit = { path = "../text_edit", version = "0.0.0" } |
29 | parser = { path = "../parser", version = "0.0.0" } | 29 | parser = { path = "../parser", version = "0.0.0" } |
30 | test_utils = { path = "../test_utils", version = "0.0.0" } | 30 | test_utils = { path = "../test_utils", version = "0.0.0" } |
31 | profile = { path = "../profile", version = "0.0.0" } | ||
31 | 32 | ||
32 | [dev-dependencies] | 33 | [dev-dependencies] |
33 | walkdir = "2.3.1" | 34 | walkdir = "2.3.1" |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index 320c430c9..ee89d9867 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -127,6 +127,8 @@ pub struct TreeDiff { | |||
127 | 127 | ||
128 | impl TreeDiff { | 128 | impl TreeDiff { |
129 | pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { | 129 | pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { |
130 | let _p = profile::span("into_text_edit"); | ||
131 | |||
130 | for (anchor, to) in self.insertions.iter() { | 132 | for (anchor, to) in self.insertions.iter() { |
131 | let offset = match anchor { | 133 | let offset = match anchor { |
132 | TreeDiffInsertPos::After(it) => it.text_range().end(), | 134 | TreeDiffInsertPos::After(it) => it.text_range().end(), |
@@ -154,6 +156,8 @@ impl TreeDiff { | |||
154 | /// | 156 | /// |
155 | /// This function tries to find a fine-grained diff. | 157 | /// This function tries to find a fine-grained diff. |
156 | pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | 158 | pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { |
159 | let _p = profile::span("diff"); | ||
160 | |||
157 | let mut diff = TreeDiff { | 161 | let mut diff = TreeDiff { |
158 | replacements: FxHashMap::default(), | 162 | replacements: FxHashMap::default(), |
159 | insertions: FxIndexMap::default(), | 163 | insertions: FxIndexMap::default(), |
@@ -467,6 +471,8 @@ impl<'a> SyntaxRewriter<'a> { | |||
467 | } | 471 | } |
468 | 472 | ||
469 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { | 473 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { |
474 | let _p = profile::span("rewrite"); | ||
475 | |||
470 | if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() { | 476 | if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() { |
471 | return node.clone(); | 477 | return node.clone(); |
472 | } | 478 | } |
@@ -483,6 +489,7 @@ impl<'a> SyntaxRewriter<'a> { | |||
483 | /// | 489 | /// |
484 | /// Returns `None` when there are no replacements. | 490 | /// Returns `None` when there are no replacements. |
485 | pub fn rewrite_root(&self) -> Option<SyntaxNode> { | 491 | pub fn rewrite_root(&self) -> Option<SyntaxNode> { |
492 | let _p = profile::span("rewrite_root"); | ||
486 | fn element_to_node_or_parent(element: &SyntaxElement) -> SyntaxNode { | 493 | fn element_to_node_or_parent(element: &SyntaxElement) -> SyntaxNode { |
487 | match element { | 494 | match element { |
488 | SyntaxElement::Node(it) => it.clone(), | 495 | SyntaxElement::Node(it) => it.clone(), |
@@ -517,6 +524,8 @@ impl<'a> SyntaxRewriter<'a> { | |||
517 | } | 524 | } |
518 | 525 | ||
519 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { | 526 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { |
527 | let _p = profile::span("rewrite_children"); | ||
528 | |||
520 | // FIXME: this could be made much faster. | 529 | // FIXME: this could be made much faster. |
521 | let mut new_children = Vec::new(); | 530 | let mut new_children = Vec::new(); |
522 | if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) { | 531 | if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) { |
@@ -533,6 +542,8 @@ impl<'a> SyntaxRewriter<'a> { | |||
533 | acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, | 542 | acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, |
534 | element: &SyntaxElement, | 543 | element: &SyntaxElement, |
535 | ) { | 544 | ) { |
545 | let _p = profile::span("rewrite_self"); | ||
546 | |||
536 | if let Some(replacement) = self.replacement(&element) { | 547 | if let Some(replacement) = self.replacement(&element) { |
537 | match replacement { | 548 | match replacement { |
538 | Replacement::Single(element) => acc.push(element_to_green(element)), | 549 | Replacement::Single(element) => acc.push(element_to_green(element)), |
@@ -588,6 +599,8 @@ fn with_children( | |||
588 | parent: &SyntaxNode, | 599 | parent: &SyntaxNode, |
589 | new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, | 600 | new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, |
590 | ) -> SyntaxNode { | 601 | ) -> SyntaxNode { |
602 | let _p = profile::span("with_children"); | ||
603 | |||
591 | let len = new_children.iter().map(|it| it.text_len()).sum::<TextSize>(); | 604 | let len = new_children.iter().map(|it| it.text_len()).sum::<TextSize>(); |
592 | let new_node = rowan::GreenNode::new(rowan::SyntaxKind(parent.kind() as u16), new_children); | 605 | let new_node = rowan::GreenNode::new(rowan::SyntaxKind(parent.kind() as u16), new_children); |
593 | let new_root_node = parent.replace_with(new_node); | 606 | let new_root_node = parent.replace_with(new_node); |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 0c178039e..fa40e64e8 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -130,19 +130,28 @@ impl ast::String { | |||
130 | let text = self.text().as_str(); | 130 | let text = self.text().as_str(); |
131 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | 131 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
132 | 132 | ||
133 | let mut buf = String::with_capacity(text.len()); | 133 | let mut buf = String::new(); |
134 | let mut text_iter = text.chars(); | ||
134 | let mut has_error = false; | 135 | let mut has_error = false; |
135 | unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char { | 136 | unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match ( |
136 | Ok(c) => buf.push(c), | 137 | unescaped_char, |
137 | Err(_) => has_error = true, | 138 | buf.capacity() == 0, |
139 | ) { | ||
140 | (Ok(c), false) => buf.push(c), | ||
141 | (Ok(c), true) if Some(c) == text_iter.next() => (), | ||
142 | (Ok(c), true) => { | ||
143 | buf.reserve_exact(text.len()); | ||
144 | buf.push_str(&text[..char_range.start]); | ||
145 | buf.push(c); | ||
146 | } | ||
147 | (Err(_), _) => has_error = true, | ||
138 | }); | 148 | }); |
139 | 149 | ||
140 | if has_error { | 150 | match (has_error, buf.capacity() == 0) { |
141 | return None; | 151 | (true, _) => None, |
152 | (false, true) => Some(Cow::Borrowed(text)), | ||
153 | (false, false) => Some(Cow::Owned(buf)), | ||
142 | } | 154 | } |
143 | // FIXME: don't actually allocate for borrowed case | ||
144 | let res = if buf == text { Cow::Borrowed(text) } else { Cow::Owned(buf) }; | ||
145 | Some(res) | ||
146 | } | 155 | } |
147 | 156 | ||
148 | pub fn quote_offsets(&self) -> Option<QuoteOffsets> { | 157 | pub fn quote_offsets(&self) -> Option<QuoteOffsets> { |
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 4aa2d6526..9093fbd97 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -158,53 +158,4 @@ impl fst::Automaton for PrefixOf<'_> { | |||
158 | } | 158 | } |
159 | 159 | ||
160 | #[cfg(test)] | 160 | #[cfg(test)] |
161 | mod tests { | 161 | mod tests; |
162 | use super::*; | ||
163 | |||
164 | #[test] | ||
165 | fn path_prefix() { | ||
166 | let mut file_set = FileSetConfig::builder(); | ||
167 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]); | ||
168 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo/bar/baz".into())]); | ||
169 | let file_set = file_set.build(); | ||
170 | |||
171 | let mut vfs = Vfs::default(); | ||
172 | vfs.set_file_contents( | ||
173 | VfsPath::new_virtual_path("/foo/src/lib.rs".into()), | ||
174 | Some(Vec::new()), | ||
175 | ); | ||
176 | vfs.set_file_contents( | ||
177 | VfsPath::new_virtual_path("/foo/src/bar/baz/lib.rs".into()), | ||
178 | Some(Vec::new()), | ||
179 | ); | ||
180 | vfs.set_file_contents( | ||
181 | VfsPath::new_virtual_path("/foo/bar/baz/lib.rs".into()), | ||
182 | Some(Vec::new()), | ||
183 | ); | ||
184 | vfs.set_file_contents(VfsPath::new_virtual_path("/quux/lib.rs".into()), Some(Vec::new())); | ||
185 | |||
186 | let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>(); | ||
187 | assert_eq!(partition, vec![2, 1, 1]); | ||
188 | } | ||
189 | |||
190 | #[test] | ||
191 | fn name_prefix() { | ||
192 | let mut file_set = FileSetConfig::builder(); | ||
193 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]); | ||
194 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo-things".into())]); | ||
195 | let file_set = file_set.build(); | ||
196 | |||
197 | let mut vfs = Vfs::default(); | ||
198 | vfs.set_file_contents( | ||
199 | VfsPath::new_virtual_path("/foo/src/lib.rs".into()), | ||
200 | Some(Vec::new()), | ||
201 | ); | ||
202 | vfs.set_file_contents( | ||
203 | VfsPath::new_virtual_path("/foo-things/src/lib.rs".into()), | ||
204 | Some(Vec::new()), | ||
205 | ); | ||
206 | |||
207 | let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>(); | ||
208 | assert_eq!(partition, vec![1, 1, 0]); | ||
209 | } | ||
210 | } | ||
diff --git a/crates/vfs/src/file_set/tests.rs b/crates/vfs/src/file_set/tests.rs new file mode 100644 index 000000000..2146df185 --- /dev/null +++ b/crates/vfs/src/file_set/tests.rs | |||
@@ -0,0 +1,42 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn path_prefix() { | ||
5 | let mut file_set = FileSetConfig::builder(); | ||
6 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]); | ||
7 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo/bar/baz".into())]); | ||
8 | let file_set = file_set.build(); | ||
9 | |||
10 | let mut vfs = Vfs::default(); | ||
11 | vfs.set_file_contents(VfsPath::new_virtual_path("/foo/src/lib.rs".into()), Some(Vec::new())); | ||
12 | vfs.set_file_contents( | ||
13 | VfsPath::new_virtual_path("/foo/src/bar/baz/lib.rs".into()), | ||
14 | Some(Vec::new()), | ||
15 | ); | ||
16 | vfs.set_file_contents( | ||
17 | VfsPath::new_virtual_path("/foo/bar/baz/lib.rs".into()), | ||
18 | Some(Vec::new()), | ||
19 | ); | ||
20 | vfs.set_file_contents(VfsPath::new_virtual_path("/quux/lib.rs".into()), Some(Vec::new())); | ||
21 | |||
22 | let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>(); | ||
23 | assert_eq!(partition, vec![2, 1, 1]); | ||
24 | } | ||
25 | |||
26 | #[test] | ||
27 | fn name_prefix() { | ||
28 | let mut file_set = FileSetConfig::builder(); | ||
29 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]); | ||
30 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo-things".into())]); | ||
31 | let file_set = file_set.build(); | ||
32 | |||
33 | let mut vfs = Vfs::default(); | ||
34 | vfs.set_file_contents(VfsPath::new_virtual_path("/foo/src/lib.rs".into()), Some(Vec::new())); | ||
35 | vfs.set_file_contents( | ||
36 | VfsPath::new_virtual_path("/foo-things/src/lib.rs".into()), | ||
37 | Some(Vec::new()), | ||
38 | ); | ||
39 | |||
40 | let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>(); | ||
41 | assert_eq!(partition, vec![1, 1, 0]); | ||
42 | } | ||
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs index 815697597..bd14911c9 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs | |||
@@ -311,35 +311,4 @@ impl VirtualPath { | |||
311 | } | 311 | } |
312 | 312 | ||
313 | #[cfg(test)] | 313 | #[cfg(test)] |
314 | mod tests { | 314 | mod tests; |
315 | use super::*; | ||
316 | |||
317 | #[test] | ||
318 | fn virtual_path_extensions() { | ||
319 | assert_eq!(VirtualPath("/".to_string()).name_and_extension(), None); | ||
320 | assert_eq!( | ||
321 | VirtualPath("/directory".to_string()).name_and_extension(), | ||
322 | Some(("directory", None)) | ||
323 | ); | ||
324 | assert_eq!( | ||
325 | VirtualPath("/directory/".to_string()).name_and_extension(), | ||
326 | Some(("directory", None)) | ||
327 | ); | ||
328 | assert_eq!( | ||
329 | VirtualPath("/directory/file".to_string()).name_and_extension(), | ||
330 | Some(("file", None)) | ||
331 | ); | ||
332 | assert_eq!( | ||
333 | VirtualPath("/directory/.file".to_string()).name_and_extension(), | ||
334 | Some((".file", None)) | ||
335 | ); | ||
336 | assert_eq!( | ||
337 | VirtualPath("/directory/.file.rs".to_string()).name_and_extension(), | ||
338 | Some((".file", Some("rs"))) | ||
339 | ); | ||
340 | assert_eq!( | ||
341 | VirtualPath("/directory/file.rs".to_string()).name_and_extension(), | ||
342 | Some(("file", Some("rs"))) | ||
343 | ); | ||
344 | } | ||
345 | } | ||
diff --git a/crates/vfs/src/vfs_path/tests.rs b/crates/vfs/src/vfs_path/tests.rs new file mode 100644 index 000000000..510e021e8 --- /dev/null +++ b/crates/vfs/src/vfs_path/tests.rs | |||
@@ -0,0 +1,30 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn virtual_path_extensions() { | ||
5 | assert_eq!(VirtualPath("/".to_string()).name_and_extension(), None); | ||
6 | assert_eq!( | ||
7 | VirtualPath("/directory".to_string()).name_and_extension(), | ||
8 | Some(("directory", None)) | ||
9 | ); | ||
10 | assert_eq!( | ||
11 | VirtualPath("/directory/".to_string()).name_and_extension(), | ||
12 | Some(("directory", None)) | ||
13 | ); | ||
14 | assert_eq!( | ||
15 | VirtualPath("/directory/file".to_string()).name_and_extension(), | ||
16 | Some(("file", None)) | ||
17 | ); | ||
18 | assert_eq!( | ||
19 | VirtualPath("/directory/.file".to_string()).name_and_extension(), | ||
20 | Some((".file", None)) | ||
21 | ); | ||
22 | assert_eq!( | ||
23 | VirtualPath("/directory/.file.rs".to_string()).name_and_extension(), | ||
24 | Some((".file", Some("rs"))) | ||
25 | ); | ||
26 | assert_eq!( | ||
27 | VirtualPath("/directory/file.rs".to_string()).name_and_extension(), | ||
28 | Some(("file", Some("rs"))) | ||
29 | ); | ||
30 | } | ||
diff --git a/docs/user/generated_diagnostic.adoc b/docs/user/generated_diagnostic.adoc index 34c4f98a3..ec8581a03 100644 --- a/docs/user/generated_diagnostic.adoc +++ b/docs/user/generated_diagnostic.adoc | |||
@@ -17,6 +17,12 @@ This diagnostic is shown for code with inactive `#[cfg]` attributes. | |||
17 | This diagnostic is triggered if item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. | 17 | This diagnostic is triggered if item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. |
18 | 18 | ||
19 | 19 | ||
20 | === macro-error | ||
21 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L167[diagnostics.rs] | ||
22 | |||
23 | This diagnostic is shown for macro expansion errors. | ||
24 | |||
25 | |||
20 | === mismatched-arg-count | 26 | === mismatched-arg-count |
21 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_ty/src/diagnostics.rs#L267[diagnostics.rs] | 27 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_ty/src/diagnostics.rs#L267[diagnostics.rs] |
22 | 28 | ||
@@ -103,3 +109,11 @@ This diagnostic is triggered if rust-analyzer is unable to discover imported mod | |||
103 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L18[diagnostics.rs] | 109 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L18[diagnostics.rs] |
104 | 110 | ||
105 | This diagnostic is triggered if rust-analyzer is unable to discover referred module. | 111 | This diagnostic is triggered if rust-analyzer is unable to discover referred module. |
112 | |||
113 | |||
114 | === unresolved-proc-macro | ||
115 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L131[diagnostics.rs] | ||
116 | |||
117 | This diagnostic is shown when a procedural macro can not be found. This usually means that | ||
118 | procedural macro support is simply disabled (and hence is only a weak hint instead of an error), | ||
119 | but can also indicate project setup problems. | ||