mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-29 20:46:07 +03:00
Merge branch 'master' into feature/themes
This commit is contained in:
Generated
+119
-72
@@ -108,8 +108,8 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -123,12 +123,22 @@ name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "chalk-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chalk-engine"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/rust-lang/chalk.git#13303bb0067c6ed0572322080ae367ee38f9e7c9"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
||||
dependencies = [
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stacker 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -136,17 +146,18 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "chalk-ir"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-lang/chalk.git#13303bb0067c6ed0572322080ae367ee38f9e7c9"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
||||
dependencies = [
|
||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chalk-macros"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/rust-lang/chalk.git#13303bb0067c6ed0572322080ae367ee38f9e7c9"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -154,24 +165,25 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "chalk-rust-ir"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-lang/chalk.git#13303bb0067c6ed0572322080ae367ee38f9e7c9"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
||||
dependencies = [
|
||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chalk-solve"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-lang/chalk.git#13303bb0067c6ed0572322080ae367ee38f9e7c9"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809"
|
||||
dependencies = [
|
||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -288,7 +300,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -413,7 +425,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -443,7 +455,7 @@ name = "heck"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-segmentation 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -501,7 +513,7 @@ dependencies = [
|
||||
"console 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -517,7 +529,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -624,7 +636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -634,7 +646,7 @@ version = "0.61.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -790,7 +802,7 @@ dependencies = [
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -811,7 +823,7 @@ dependencies = [
|
||||
"proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -845,7 +857,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -903,7 +915,7 @@ name = "ra_assists"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_db 0.1.0",
|
||||
"ra_fmt 0.1.0",
|
||||
@@ -970,7 +982,7 @@ dependencies = [
|
||||
name = "ra_fmt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_syntax 0.1.0",
|
||||
]
|
||||
|
||||
@@ -979,9 +991,9 @@ name = "ra_hir"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)",
|
||||
"ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -991,6 +1003,8 @@ dependencies = [
|
||||
"ra_arena 0.1.0",
|
||||
"ra_cfg 0.1.0",
|
||||
"ra_db 0.1.0",
|
||||
"ra_hir_def 0.1.0",
|
||||
"ra_hir_expand 0.1.0",
|
||||
"ra_mbe 0.1.0",
|
||||
"ra_prof 0.1.0",
|
||||
"ra_syntax 0.1.0",
|
||||
@@ -1000,6 +1014,38 @@ dependencies = [
|
||||
"test_utils 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra_hir_def"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_arena 0.1.0",
|
||||
"ra_cfg 0.1.0",
|
||||
"ra_db 0.1.0",
|
||||
"ra_hir_expand 0.1.0",
|
||||
"ra_mbe 0.1.0",
|
||||
"ra_prof 0.1.0",
|
||||
"ra_syntax 0.1.0",
|
||||
"ra_tt 0.1.0",
|
||||
"relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"test_utils 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra_hir_expand"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_arena 0.1.0",
|
||||
"ra_db 0.1.0",
|
||||
"ra_mbe 0.1.0",
|
||||
"ra_prof 0.1.0",
|
||||
"ra_syntax 0.1.0",
|
||||
"ra_tt 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra_ide_api"
|
||||
version = "0.1.0"
|
||||
@@ -1007,7 +1053,7 @@ dependencies = [
|
||||
"format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fst 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1049,7 +1095,7 @@ dependencies = [
|
||||
"ra_vfs_glob 0.1.0",
|
||||
"relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"test_utils 0.1.0",
|
||||
@@ -1065,7 +1111,7 @@ dependencies = [
|
||||
"ra_syntax 0.1.0",
|
||||
"ra_tt 0.1.0",
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"test_utils 0.1.0",
|
||||
]
|
||||
|
||||
@@ -1081,7 +1127,7 @@ name = "ra_prof"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jemalloc-ctl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1097,7 +1143,7 @@ dependencies = [
|
||||
"ra_cfg 0.1.0",
|
||||
"ra_db 0.1.0",
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1106,7 +1152,7 @@ name = "ra_syntax"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_parser 0.1.0",
|
||||
"ra_text_edit 0.1.0",
|
||||
@@ -1180,7 +1226,7 @@ name = "rand"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1224,7 +1270,7 @@ name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1370,7 +1416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1430,7 +1476,7 @@ dependencies = [
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"salsa-macros 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1441,7 +1487,7 @@ dependencies = [
|
||||
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1463,7 +1509,7 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1473,20 +1519,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.101"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.101"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1496,7 +1542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1506,7 +1552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1516,7 +1562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1527,7 +1573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.6.10"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -1535,7 +1581,7 @@ name = "smol_str"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1557,7 +1603,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.5"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1647,12 +1693,12 @@ name = "unicode-normalization"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -1673,7 +1719,7 @@ dependencies = [
|
||||
"idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1682,7 +1728,7 @@ version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1759,7 +1805,7 @@ dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1794,11 +1840,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum cargo_metadata 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8d2d1617e838936c0d2323a65cc151e03ae19a7678dd24f72bccf27119b90a5d"
|
||||
"checksum cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)" = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git)" = "<none>"
|
||||
"checksum chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git)" = "<none>"
|
||||
"checksum chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git)" = "<none>"
|
||||
"checksum chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git)" = "<none>"
|
||||
"checksum chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git)" = "<none>"
|
||||
"checksum chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
||||
"checksum chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
||||
"checksum chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
||||
"checksum chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
||||
"checksum chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
||||
"checksum chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "<none>"
|
||||
"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68"
|
||||
"checksum clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
@@ -1828,7 +1875,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571"
|
||||
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
|
||||
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
"checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2"
|
||||
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||
@@ -1839,7 +1886,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
|
||||
"checksum insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d499dc062e841590a67230d853bce62d0abeb91304927871670b7c55c461349"
|
||||
"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
||||
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
|
||||
"checksum itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "87fa75c9dea7b07be3138c49abbb83fd4bea199b5cdc76f9804458edc5da0d6e"
|
||||
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||
"checksum jemalloc-ctl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c502a5ff9dd2924f1ed32ba96e3b65735d837b4bfd978d3161b1702e66aca4b7"
|
||||
"checksum jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45"
|
||||
@@ -1922,17 +1969,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd"
|
||||
"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e"
|
||||
"checksum serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4b39bd9b0b087684013a792c59e3e07a46a01d2322518d8a1104641a0b1be0"
|
||||
"checksum serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "ca13fc1a832f793322228923fbb3aba9f3f44444898f835d31ad1b74fa0a2bf8"
|
||||
"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2"
|
||||
"checksum serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573"
|
||||
"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
|
||||
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7"
|
||||
"checksum smallvec 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cefaa50e76a6f10b86f36e640eb1739eafbd4084865067778463913e43a77ff3"
|
||||
"checksum smol_str 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "34836c9a295c62c2ce3514471117c5cb269891e8421b2aafdd910050576c4d8b"
|
||||
"checksum stacker 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d96fc4f13a0ac088e9a3cd9af1cc8c5cc1ab5deb2145cef661267dfc9c542f8a"
|
||||
"checksum superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
|
||||
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
||||
"checksum syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c"
|
||||
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625"
|
||||
"checksum text_unit 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e08bbcb7a3adbda0eb23431206b653bdad3d8dea311e72d36bf2215e27a42579"
|
||||
@@ -1942,7 +1989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150"
|
||||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426"
|
||||
"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9"
|
||||
"checksum unicode-segmentation 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dc5415c074426c7c65db13bd647c23d78c0fb2e10dca0b8fb0f40058a59bccdf"
|
||||
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
//! FIXME: write short doc here
|
||||
//! This module defines `AssistCtx` -- the API surface that is exposed to assists.
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
use ra_db::FileRange;
|
||||
use ra_fmt::{leading_indent, reindent};
|
||||
use ra_syntax::{
|
||||
algo::{self, find_covering_element, find_node_at_offset},
|
||||
AstNode, SourceFile, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextUnit,
|
||||
AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextUnit,
|
||||
TokenAtOffset,
|
||||
};
|
||||
use ra_text_edit::TextEditBuilder;
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum Assist {
|
||||
Unresolved(Vec<AssistLabel>),
|
||||
Resolved(Vec<(AssistLabel, AssistAction)>),
|
||||
Unresolved { label: AssistLabel },
|
||||
Resolved { label: AssistLabel, action: AssistAction },
|
||||
}
|
||||
|
||||
/// `AssistCtx` allows to apply an assist or check if it could be applied.
|
||||
@@ -54,7 +54,6 @@ pub(crate) struct AssistCtx<'a, DB> {
|
||||
pub(crate) frange: FileRange,
|
||||
source_file: SourceFile,
|
||||
should_compute_edit: bool,
|
||||
assist: Assist,
|
||||
}
|
||||
|
||||
impl<'a, DB> Clone for AssistCtx<'a, DB> {
|
||||
@@ -64,7 +63,6 @@ fn clone(&self) -> Self {
|
||||
frange: self.frange,
|
||||
source_file: self.source_file.clone(),
|
||||
should_compute_edit: self.should_compute_edit,
|
||||
assist: self.assist.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,43 +73,41 @@ pub(crate) fn with_ctx<F, T>(db: &DB, frange: FileRange, should_compute_edit: bo
|
||||
F: FnOnce(AssistCtx<DB>) -> T,
|
||||
{
|
||||
let parse = db.parse(frange.file_id);
|
||||
let assist =
|
||||
if should_compute_edit { Assist::Resolved(vec![]) } else { Assist::Unresolved(vec![]) };
|
||||
|
||||
let ctx = AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit, assist };
|
||||
let ctx = AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit };
|
||||
f(ctx)
|
||||
}
|
||||
|
||||
pub(crate) fn add_action(
|
||||
&mut self,
|
||||
pub(crate) fn add_assist(
|
||||
self,
|
||||
id: AssistId,
|
||||
label: impl Into<String>,
|
||||
f: impl FnOnce(&mut AssistBuilder),
|
||||
) -> &mut Self {
|
||||
) -> Option<Assist> {
|
||||
let label = AssistLabel { label: label.into(), id };
|
||||
match &mut self.assist {
|
||||
Assist::Unresolved(labels) => labels.push(label),
|
||||
Assist::Resolved(labels_actions) => {
|
||||
let action = {
|
||||
let mut edit = AssistBuilder::default();
|
||||
f(&mut edit);
|
||||
edit.build()
|
||||
};
|
||||
labels_actions.push((label, action));
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
let assist = if self.should_compute_edit {
|
||||
let action = {
|
||||
let mut edit = AssistBuilder::default();
|
||||
f(&mut edit);
|
||||
edit.build()
|
||||
};
|
||||
Assist::Resolved { label, action }
|
||||
} else {
|
||||
Assist::Unresolved { label }
|
||||
};
|
||||
|
||||
pub(crate) fn build(self) -> Option<Assist> {
|
||||
Some(self.assist)
|
||||
Some(assist)
|
||||
}
|
||||
|
||||
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
|
||||
self.source_file.syntax().token_at_offset(self.frange.range.start())
|
||||
}
|
||||
|
||||
pub(crate) fn node_at_offset<N: AstNode>(&self) -> Option<N> {
|
||||
pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
|
||||
self.token_at_offset().find(|it| it.kind() == kind)
|
||||
}
|
||||
|
||||
pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
|
||||
find_node_at_offset(self.source_file.syntax(), self.frange.range.start())
|
||||
}
|
||||
pub(crate) fn covering_element(&self) -> SyntaxElement {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, AttrsOwner},
|
||||
@@ -9,10 +7,28 @@
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
|
||||
// Assist: add_derive
|
||||
//
|
||||
// Adds a new `#[derive()]` clause to a struct or enum.
|
||||
//
|
||||
// ```
|
||||
// struct Point {
|
||||
// x: u32,
|
||||
// y: u32,<|>
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// #[derive()]
|
||||
// struct Point {
|
||||
// x: u32,
|
||||
// y: u32,
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
|
||||
let node_start = derive_insertion_offset(&nominal)?;
|
||||
ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| {
|
||||
ctx.add_assist(AssistId("add_derive"), "add `#[derive]`", |edit| {
|
||||
let derive_attr = nominal
|
||||
.attrs()
|
||||
.filter_map(|x| x.as_simple_call())
|
||||
@@ -28,9 +44,7 @@ pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
|
||||
};
|
||||
edit.target(nominal.syntax().text_range());
|
||||
edit.set_cursor(offset)
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
// Insert `derive` after doc comments.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::{db::HirDatabase, HirDisplay, Ty};
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, LetStmt, NameOwner},
|
||||
@@ -8,9 +6,23 @@
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
/// Add explicit type assist.
|
||||
pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let stmt = ctx.node_at_offset::<LetStmt>()?;
|
||||
// Assist: add_explicit_type
|
||||
//
|
||||
// Specify type for a let binding.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// let x<|> = 92;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// let x: i32 = 92;
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let stmt = ctx.find_node_at_offset::<LetStmt>()?;
|
||||
let expr = stmt.initializer()?;
|
||||
let pat = stmt.pat()?;
|
||||
// Must be a binding
|
||||
@@ -35,11 +47,10 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<
|
||||
return None;
|
||||
}
|
||||
|
||||
ctx.add_action(AssistId("add_explicit_type"), "add explicit type", |edit| {
|
||||
ctx.add_assist(AssistId("add_explicit_type"), "add explicit type", |edit| {
|
||||
edit.target(pat_range);
|
||||
edit.insert(name_range.end(), format!(": {}", ty.display(db)));
|
||||
});
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if any type parameter is unknown
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use format_buf::format;
|
||||
use hir::db::HirDatabase;
|
||||
use join_to_string::join;
|
||||
@@ -10,10 +8,29 @@
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
|
||||
// Assist: add_impl
|
||||
//
|
||||
// Adds a new inherent impl for a type.
|
||||
//
|
||||
// ```
|
||||
// struct Ctx<T: Clone> {
|
||||
// data: T,<|>
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// struct Ctx<T: Clone> {
|
||||
// data: T,
|
||||
// }
|
||||
//
|
||||
// impl<T: Clone> Ctx<T> {
|
||||
//
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
|
||||
let name = nominal.name()?;
|
||||
ctx.add_action(AssistId("add_impl"), "add impl", |edit| {
|
||||
ctx.add_assist(AssistId("add_impl"), "add impl", |edit| {
|
||||
edit.target(nominal.syntax().text_range());
|
||||
let type_params = nominal.type_param_list();
|
||||
let start_offset = nominal.syntax().text_range().end();
|
||||
@@ -37,9 +54,7 @@ pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
edit.set_cursor(start_offset + TextUnit::of_str(&buf));
|
||||
buf.push_str("\n}");
|
||||
edit.insert(start_offset, buf);
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+136
-113
@@ -1,18 +1,81 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::{self, db::HirDatabase};
|
||||
use ra_text_edit::TextEditBuilder;
|
||||
|
||||
use crate::{
|
||||
assist_ctx::{Assist, AssistCtx},
|
||||
AssistId,
|
||||
};
|
||||
use ra_syntax::{
|
||||
ast::{self, NameOwner},
|
||||
AstNode, Direction, SmolStr,
|
||||
SyntaxKind::{PATH, PATH_SEGMENT},
|
||||
SyntaxNode, TextRange, T,
|
||||
};
|
||||
use ra_text_edit::TextEditBuilder;
|
||||
|
||||
use crate::{
|
||||
assist_ctx::{Assist, AssistCtx},
|
||||
AssistId,
|
||||
};
|
||||
|
||||
/// This function produces sequence of text edits into edit
|
||||
/// to import the target path in the most appropriate scope given
|
||||
/// the cursor position
|
||||
pub fn auto_import_text_edit(
|
||||
// Ideally the position of the cursor, used to
|
||||
position: &SyntaxNode,
|
||||
// The statement to use as anchor (last resort)
|
||||
anchor: &SyntaxNode,
|
||||
// The path to import as a sequence of strings
|
||||
target: &[SmolStr],
|
||||
edit: &mut TextEditBuilder,
|
||||
) {
|
||||
let container = position.ancestors().find_map(|n| {
|
||||
if let Some(module) = ast::Module::cast(n.clone()) {
|
||||
return module.item_list().map(|it| it.syntax().clone());
|
||||
}
|
||||
ast::SourceFile::cast(n).map(|it| it.syntax().clone())
|
||||
});
|
||||
|
||||
if let Some(container) = container {
|
||||
let action = best_action_for_target(container, anchor.clone(), target);
|
||||
make_assist(&action, target, edit);
|
||||
}
|
||||
}
|
||||
|
||||
// Assist: add_import
|
||||
//
|
||||
// Adds a use statement for a given fully-qualified path.
|
||||
//
|
||||
// ```
|
||||
// fn process(map: std::collections::<|>HashMap<String, String>) {}
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// use std::collections::HashMap;
|
||||
//
|
||||
// fn process(map: HashMap<String, String>) {}
|
||||
// ```
|
||||
pub(crate) fn add_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let path: ast::Path = ctx.find_node_at_offset()?;
|
||||
// We don't want to mess with use statements
|
||||
if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let hir_path = hir::Path::from_ast(path.clone())?;
|
||||
let segments = collect_hir_path_segments(&hir_path)?;
|
||||
if segments.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let module = path.syntax().ancestors().find_map(ast::Module::cast);
|
||||
let position = match module.and_then(|it| it.item_list()) {
|
||||
Some(item_list) => item_list.syntax().clone(),
|
||||
None => {
|
||||
let current_file = path.syntax().ancestors().find_map(ast::SourceFile::cast)?;
|
||||
current_file.syntax().clone()
|
||||
}
|
||||
};
|
||||
|
||||
ctx.add_assist(AssistId("add_import"), format!("import {}", fmt_segments(&segments)), |edit| {
|
||||
apply_auto_import(&position, &path, &segments, edit.text_edit_builder());
|
||||
})
|
||||
}
|
||||
|
||||
fn collect_path_segments_raw(
|
||||
segments: &mut Vec<ast::PathSegment>,
|
||||
@@ -61,9 +124,9 @@ fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the numeber of common segments.
|
||||
/// Returns the number of common segments.
|
||||
fn compare_path_segments(left: &[SmolStr], right: &[ast::PathSegment]) -> usize {
|
||||
left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count()
|
||||
left.iter().zip(right).take_while(|(l, r)| compare_path_segment(l, r)).count()
|
||||
}
|
||||
|
||||
fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool {
|
||||
@@ -84,7 +147,7 @@ fn compare_path_segment_with_name(a: &SmolStr, b: &ast::Name) -> bool {
|
||||
a == b.text()
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
enum ImportAction {
|
||||
Nothing,
|
||||
// Add a brand new use statement.
|
||||
@@ -154,10 +217,18 @@ fn is_better(&self, other: &ImportAction) -> bool {
|
||||
(
|
||||
ImportAction::AddNestedImport { common_segments: n, .. },
|
||||
ImportAction::AddInTreeList { common_segments: m, .. },
|
||||
) => n > m,
|
||||
(
|
||||
)
|
||||
| (
|
||||
ImportAction::AddInTreeList { common_segments: n, .. },
|
||||
ImportAction::AddNestedImport { common_segments: m, .. },
|
||||
)
|
||||
| (
|
||||
ImportAction::AddInTreeList { common_segments: n, .. },
|
||||
ImportAction::AddInTreeList { common_segments: m, .. },
|
||||
)
|
||||
| (
|
||||
ImportAction::AddNestedImport { common_segments: n, .. },
|
||||
ImportAction::AddNestedImport { common_segments: m, .. },
|
||||
) => n > m,
|
||||
(ImportAction::AddInTreeList { .. }, _) => true,
|
||||
(ImportAction::AddNestedImport { .. }, ImportAction::Nothing) => false,
|
||||
@@ -226,7 +297,7 @@ fn walk_use_tree_for_best_action(
|
||||
common if common == left.len() && left.len() == right.len() => {
|
||||
// e.g: target is std::fmt and we can have
|
||||
// 1- use std::fmt;
|
||||
// 2- use std::fmt:{ ... }
|
||||
// 2- use std::fmt::{ ... }
|
||||
if let Some(list) = tree_list {
|
||||
// In case 2 we need to add self to the nested list
|
||||
// unless it's already there
|
||||
@@ -474,7 +545,7 @@ fn make_assist_add_nested_import(
|
||||
if add_colon_colon {
|
||||
buf.push_str("::");
|
||||
}
|
||||
buf.push_str("{ ");
|
||||
buf.push_str("{");
|
||||
if add_self {
|
||||
buf.push_str("self, ");
|
||||
}
|
||||
@@ -505,7 +576,7 @@ fn apply_auto_import(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
|
||||
fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
|
||||
let mut ps = Vec::<SmolStr>::with_capacity(10);
|
||||
match path.kind {
|
||||
hir::PathKind::Abs => ps.push("".into()),
|
||||
@@ -521,87 +592,16 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
|
||||
Some(ps)
|
||||
}
|
||||
|
||||
// This function produces sequence of text edits into edit
|
||||
// to import the target path in the most appropriate scope given
|
||||
// the cursor position
|
||||
pub fn auto_import_text_edit(
|
||||
// Ideally the position of the cursor, used to
|
||||
position: &SyntaxNode,
|
||||
// The statement to use as anchor (last resort)
|
||||
anchor: &SyntaxNode,
|
||||
// The path to import as a sequence of strings
|
||||
target: &[SmolStr],
|
||||
edit: &mut TextEditBuilder,
|
||||
) {
|
||||
let container = position.ancestors().find_map(|n| {
|
||||
if let Some(module) = ast::Module::cast(n.clone()) {
|
||||
return module.item_list().map(|it| it.syntax().clone());
|
||||
}
|
||||
ast::SourceFile::cast(n).map(|it| it.syntax().clone())
|
||||
});
|
||||
|
||||
if let Some(container) = container {
|
||||
let action = best_action_for_target(container, anchor.clone(), target);
|
||||
make_assist(&action, target, edit);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let path: ast::Path = ctx.node_at_offset()?;
|
||||
// We don't want to mess with use statements
|
||||
if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let hir_path = hir::Path::from_ast(path.clone())?;
|
||||
let segments = collect_hir_path_segments(&hir_path)?;
|
||||
if segments.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(module) = path.syntax().ancestors().find_map(ast::Module::cast) {
|
||||
if let (Some(item_list), Some(name)) = (module.item_list(), module.name()) {
|
||||
ctx.add_action(
|
||||
AssistId("auto_import"),
|
||||
format!("import {} in mod {}", fmt_segments(&segments), name.text()),
|
||||
|edit| {
|
||||
apply_auto_import(
|
||||
item_list.syntax(),
|
||||
&path,
|
||||
&segments,
|
||||
edit.text_edit_builder(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let current_file = path.syntax().ancestors().find_map(ast::SourceFile::cast)?;
|
||||
ctx.add_action(
|
||||
AssistId("auto_import"),
|
||||
format!("import {} in the current file", fmt_segments(&segments)),
|
||||
|edit| {
|
||||
apply_auto_import(
|
||||
current_file.syntax(),
|
||||
&path,
|
||||
&segments,
|
||||
edit.text_edit_builder(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
ctx.build()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::helpers::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_auto_import_add_use_no_anchor() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
std::fmt::Debug<|>
|
||||
",
|
||||
@@ -615,7 +615,7 @@ fn test_auto_import_add_use_no_anchor() {
|
||||
#[test]
|
||||
fn test_auto_import_add_use_no_anchor_with_item_below() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
std::fmt::Debug<|>
|
||||
|
||||
@@ -636,7 +636,7 @@ fn main() {
|
||||
#[test]
|
||||
fn test_auto_import_add_use_no_anchor_with_item_above() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
fn main() {
|
||||
}
|
||||
@@ -657,7 +657,7 @@ fn main() {
|
||||
#[test]
|
||||
fn test_auto_import_add_use_no_anchor_2seg() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
std::fmt<|>::Debug
|
||||
",
|
||||
@@ -672,7 +672,7 @@ fn test_auto_import_add_use_no_anchor_2seg() {
|
||||
#[test]
|
||||
fn test_auto_import_add_use() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use stdx;
|
||||
|
||||
@@ -692,7 +692,7 @@ impl Debug<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_file_use_other_anchor() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
impl std::fmt::Debug<|> for Foo {
|
||||
}
|
||||
@@ -709,7 +709,7 @@ impl Debug<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_add_use_other_anchor_indent() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
impl std::fmt::Debug<|> for Foo {
|
||||
}
|
||||
@@ -726,7 +726,7 @@ impl Debug<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_split_different() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use std::fmt;
|
||||
|
||||
@@ -734,7 +734,7 @@ impl std::io<|> for Foo {
|
||||
}
|
||||
",
|
||||
"
|
||||
use std::{ io, fmt};
|
||||
use std::{io, fmt};
|
||||
|
||||
impl io<|> for Foo {
|
||||
}
|
||||
@@ -745,7 +745,7 @@ impl io<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_split_self_for_use() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use std::fmt;
|
||||
|
||||
@@ -753,7 +753,7 @@ impl std::fmt::Debug<|> for Foo {
|
||||
}
|
||||
",
|
||||
"
|
||||
use std::fmt::{ self, Debug, };
|
||||
use std::fmt::{self, Debug, };
|
||||
|
||||
impl Debug<|> for Foo {
|
||||
}
|
||||
@@ -764,7 +764,7 @@ impl Debug<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_split_self_for_target() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use std::fmt::Debug;
|
||||
|
||||
@@ -772,7 +772,7 @@ impl std::fmt<|> for Foo {
|
||||
}
|
||||
",
|
||||
"
|
||||
use std::fmt::{ self, Debug};
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
impl fmt<|> for Foo {
|
||||
}
|
||||
@@ -783,7 +783,7 @@ impl fmt<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_add_to_nested_self_nested() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use std::fmt::{Debug, nested::{Display}};
|
||||
|
||||
@@ -802,7 +802,7 @@ impl nested<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_add_to_nested_self_already_included() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use std::fmt::{Debug, nested::{self, Display}};
|
||||
|
||||
@@ -821,7 +821,7 @@ impl nested<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_add_to_nested_nested() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use std::fmt::{Debug, nested::{Display}};
|
||||
|
||||
@@ -840,7 +840,7 @@ impl Debug<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_split_common_target_longer() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use std::fmt::Debug;
|
||||
|
||||
@@ -848,7 +848,7 @@ impl std::fmt::nested::Display<|> for Foo {
|
||||
}
|
||||
",
|
||||
"
|
||||
use std::fmt::{ nested::Display, Debug};
|
||||
use std::fmt::{nested::Display, Debug};
|
||||
|
||||
impl Display<|> for Foo {
|
||||
}
|
||||
@@ -859,7 +859,7 @@ impl Display<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_split_common_use_longer() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use std::fmt::nested::Debug;
|
||||
|
||||
@@ -867,7 +867,7 @@ impl std::fmt::Display<|> for Foo {
|
||||
}
|
||||
",
|
||||
"
|
||||
use std::fmt::{ Display, nested::Debug};
|
||||
use std::fmt::{Display, nested::Debug};
|
||||
|
||||
impl Display<|> for Foo {
|
||||
}
|
||||
@@ -875,10 +875,33 @@ impl Display<|> for Foo {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_import_use_nested_import() {
|
||||
check_assist(
|
||||
add_import,
|
||||
"
|
||||
use crate::{
|
||||
ty::{Substs, Ty},
|
||||
AssocItem,
|
||||
};
|
||||
|
||||
fn foo() { crate::ty::lower<|>::trait_env() }
|
||||
",
|
||||
"
|
||||
use crate::{
|
||||
ty::{Substs, Ty, lower},
|
||||
AssocItem,
|
||||
};
|
||||
|
||||
fn foo() { lower<|>::trait_env() }
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_import_alias() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use std::fmt as foo;
|
||||
|
||||
@@ -897,7 +920,7 @@ impl Debug<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_not_applicable_one_segment() {
|
||||
check_assist_not_applicable(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
impl foo<|> for Foo {
|
||||
}
|
||||
@@ -908,7 +931,7 @@ impl foo<|> for Foo {
|
||||
#[test]
|
||||
fn test_auto_import_not_applicable_in_use() {
|
||||
check_assist_not_applicable(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
use std::fmt<|>;
|
||||
",
|
||||
@@ -918,7 +941,7 @@ fn test_auto_import_not_applicable_in_use() {
|
||||
#[test]
|
||||
fn test_auto_import_add_use_no_anchor_in_mod_mod() {
|
||||
check_assist(
|
||||
auto_import,
|
||||
add_import,
|
||||
"
|
||||
mod foo {
|
||||
mod bar {
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::{db::HirDatabase, HasSource};
|
||||
use ra_syntax::{
|
||||
ast::{self, edit, make, AstNode, NameOwner},
|
||||
@@ -14,6 +12,34 @@ enum AddMissingImplMembersMode {
|
||||
NoDefaultMethods,
|
||||
}
|
||||
|
||||
// Assist: add_impl_missing_members
|
||||
//
|
||||
// Adds scaffold for required impl members.
|
||||
//
|
||||
// ```
|
||||
// trait T {
|
||||
// Type X;
|
||||
// fn foo(&self);
|
||||
// fn bar(&self) {}
|
||||
// }
|
||||
//
|
||||
// impl T for () {<|>
|
||||
//
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// trait T {
|
||||
// Type X;
|
||||
// fn foo(&self);
|
||||
// fn bar(&self) {}
|
||||
// }
|
||||
//
|
||||
// impl T for () {
|
||||
// fn foo(&self) { unimplemented!() }
|
||||
//
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
add_missing_impl_members_inner(
|
||||
ctx,
|
||||
@@ -23,6 +49,38 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti
|
||||
)
|
||||
}
|
||||
|
||||
// Assist: add_impl_default_members
|
||||
//
|
||||
// Adds scaffold for overriding default impl members.
|
||||
//
|
||||
// ```
|
||||
// trait T {
|
||||
// Type X;
|
||||
// fn foo(&self);
|
||||
// fn bar(&self) {}
|
||||
// }
|
||||
//
|
||||
// impl T for () {
|
||||
// Type X = ();
|
||||
// fn foo(&self) {}<|>
|
||||
//
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// trait T {
|
||||
// Type X;
|
||||
// fn foo(&self);
|
||||
// fn bar(&self) {}
|
||||
// }
|
||||
//
|
||||
// impl T for () {
|
||||
// Type X = ();
|
||||
// fn foo(&self) {}
|
||||
// fn bar(&self) {}
|
||||
//
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_missing_default_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
add_missing_impl_members_inner(
|
||||
ctx,
|
||||
@@ -33,12 +91,12 @@ pub(crate) fn add_missing_default_members(ctx: AssistCtx<impl HirDatabase>) -> O
|
||||
}
|
||||
|
||||
fn add_missing_impl_members_inner(
|
||||
mut ctx: AssistCtx<impl HirDatabase>,
|
||||
ctx: AssistCtx<impl HirDatabase>,
|
||||
mode: AddMissingImplMembersMode,
|
||||
assist_id: &'static str,
|
||||
label: &'static str,
|
||||
) -> Option<Assist> {
|
||||
let impl_node = ctx.node_at_offset::<ast::ImplBlock>()?;
|
||||
let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?;
|
||||
let impl_item_list = impl_node.item_list()?;
|
||||
|
||||
let trait_def = {
|
||||
@@ -75,7 +133,7 @@ fn add_missing_impl_members_inner(
|
||||
return None;
|
||||
}
|
||||
|
||||
ctx.add_action(AssistId(assist_id), label, |edit| {
|
||||
ctx.add_assist(AssistId(assist_id), label, |edit| {
|
||||
let n_existing_items = impl_item_list.impl_items().count();
|
||||
let items = missing_items
|
||||
.into_iter()
|
||||
@@ -92,9 +150,7 @@ fn add_missing_impl_members_inner(
|
||||
|
||||
edit.replace_ast(impl_item_list, new_impl_item_list);
|
||||
edit.set_cursor(cursor_position);
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
|
||||
|
||||
@@ -1,20 +1,30 @@
|
||||
//! This contains the functions associated with the demorgan assist.
|
||||
//! This assist transforms boolean expressions of the form `!a || !b` into
|
||||
//! `!(a && b)`.
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::ast::{self, AstNode};
|
||||
use ra_syntax::SyntaxNode;
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
/// Assist for applying demorgan's law
|
||||
///
|
||||
/// This transforms expressions of the form `!l || !r` into `!(l && r)`.
|
||||
/// This also works with `&&`. This assist can only be applied with the cursor
|
||||
/// on either `||` or `&&`, with both operands being a negation of some kind.
|
||||
/// This means something of the form `!x` or `x != y`.
|
||||
pub(crate) fn apply_demorgan(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let expr = ctx.node_at_offset::<ast::BinExpr>()?;
|
||||
// Assist: apply_demorgan
|
||||
//
|
||||
// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
|
||||
// This transforms expressions of the form `!l || !r` into `!(l && r)`.
|
||||
// This also works with `&&`. This assist can only be applied with the cursor
|
||||
// on either `||` or `&&`, with both operands being a negation of some kind.
|
||||
// This means something of the form `!x` or `x != y`.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// if x != 4 ||<|> !y {}
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// if !(x == 4 && y) {}
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn apply_demorgan(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
|
||||
let op = expr.op_kind()?;
|
||||
let op_range = expr.op_token()?.text_range();
|
||||
let opposite_op = opposite_logic_op(op)?;
|
||||
@@ -29,13 +39,12 @@ pub(crate) fn apply_demorgan(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
|
||||
let not_lhs = undo_negation(lhs)?;
|
||||
let not_rhs = undo_negation(rhs)?;
|
||||
|
||||
ctx.add_action(AssistId("apply_demorgan"), "apply demorgan's law", |edit| {
|
||||
ctx.add_assist(AssistId("apply_demorgan"), "apply demorgan's law", |edit| {
|
||||
edit.target(op_range);
|
||||
edit.replace(op_range, opposite_op);
|
||||
edit.replace(lhs_range, format!("!({}", not_lhs));
|
||||
edit.replace(rhs_range, format!("{})", not_rhs));
|
||||
});
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
// Return the opposite text for a given logical operator, if it makes sense
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, NameOwner, VisibilityOwner},
|
||||
@@ -13,14 +11,25 @@
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
// Assist: change_visibility
|
||||
//
|
||||
// Adds or changes existing visibility specifier.
|
||||
//
|
||||
// ```
|
||||
// <|>fn frobnicate() {}
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// pub(crate) fn frobnicate() {}
|
||||
// ```
|
||||
pub(crate) fn change_visibility(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
if let Some(vis) = ctx.node_at_offset::<ast::Visibility>() {
|
||||
if let Some(vis) = ctx.find_node_at_offset::<ast::Visibility>() {
|
||||
return change_vis(ctx, vis);
|
||||
}
|
||||
add_vis(ctx)
|
||||
}
|
||||
|
||||
fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() {
|
||||
T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true,
|
||||
_ => false,
|
||||
@@ -48,13 +57,11 @@ fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
(vis_offset(field.syntax()), ident.text_range())
|
||||
};
|
||||
|
||||
ctx.add_action(AssistId("change_visibility"), "make pub(crate)", |edit| {
|
||||
ctx.add_assist(AssistId("change_visibility"), "make pub(crate)", |edit| {
|
||||
edit.target(target);
|
||||
edit.insert(offset, "pub(crate) ");
|
||||
edit.set_cursor(offset);
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
fn vis_offset(node: &SyntaxNode) -> TextUnit {
|
||||
@@ -68,24 +75,20 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
|
||||
.unwrap_or_else(|| node.text_range().start())
|
||||
}
|
||||
|
||||
fn change_vis(mut ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> {
|
||||
fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> {
|
||||
if vis.syntax().text() == "pub" {
|
||||
ctx.add_action(AssistId("change_visibility"), "change to pub(crate)", |edit| {
|
||||
return ctx.add_assist(AssistId("change_visibility"), "change to pub(crate)", |edit| {
|
||||
edit.target(vis.syntax().text_range());
|
||||
edit.replace(vis.syntax().text_range(), "pub(crate)");
|
||||
edit.set_cursor(vis.syntax().text_range().start())
|
||||
});
|
||||
|
||||
return ctx.build();
|
||||
}
|
||||
if vis.syntax().text() == "pub(crate)" {
|
||||
ctx.add_action(AssistId("change_visibility"), "change to pub", |edit| {
|
||||
return ctx.add_assist(AssistId("change_visibility"), "change to pub", |edit| {
|
||||
edit.target(vis.syntax().text_range());
|
||||
edit.replace(vis.syntax().text_range(), "pub");
|
||||
edit.set_cursor(vis.syntax().text_range().start());
|
||||
});
|
||||
|
||||
return ctx.build();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1,26 +1,3 @@
|
||||
//! Assist: `convert_to_guarded_return`
|
||||
//!
|
||||
//! Replace a large conditional with a guarded return.
|
||||
//!
|
||||
//! ```text
|
||||
//! fn <|>main() {
|
||||
//! if cond {
|
||||
//! foo();
|
||||
//! bar();
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//! ->
|
||||
//! ```text
|
||||
//! fn main() {
|
||||
//! if !cond {
|
||||
//! return;
|
||||
//! }
|
||||
//! foo();
|
||||
//! bar();
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
@@ -36,8 +13,30 @@
|
||||
AssistId,
|
||||
};
|
||||
|
||||
pub(crate) fn convert_to_guarded_return(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let if_expr: ast::IfExpr = ctx.node_at_offset()?;
|
||||
// Assist: convert_to_guarded_return
|
||||
//
|
||||
// Replace a large conditional with a guarded return.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// <|>if cond {
|
||||
// foo();
|
||||
// bar();
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// if !cond {
|
||||
// return;
|
||||
// }
|
||||
// foo();
|
||||
// bar();
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
|
||||
let expr = if_expr.condition()?.expr()?;
|
||||
let then_block = if_expr.then_branch()?.block()?;
|
||||
if if_expr.else_branch().is_some() {
|
||||
@@ -51,7 +50,7 @@ pub(crate) fn convert_to_guarded_return(mut ctx: AssistCtx<impl HirDatabase>) ->
|
||||
}
|
||||
|
||||
// check for early return and continue
|
||||
let first_in_then_block = then_block.syntax().first_child()?.clone();
|
||||
let first_in_then_block = then_block.syntax().first_child()?;
|
||||
if ast::ReturnExpr::can_cast(first_in_then_block.kind())
|
||||
|| ast::ContinueExpr::can_cast(first_in_then_block.kind())
|
||||
|| first_in_then_block
|
||||
@@ -76,7 +75,7 @@ pub(crate) fn convert_to_guarded_return(mut ctx: AssistCtx<impl HirDatabase>) ->
|
||||
then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
|
||||
let cursor_position = ctx.frange.range.start();
|
||||
|
||||
ctx.add_action(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
|
||||
ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
|
||||
let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
|
||||
let new_if_expr =
|
||||
if_indent_level.increase_indent(make::if_expression(&expr, early_expression));
|
||||
@@ -106,8 +105,7 @@ pub(crate) fn convert_to_guarded_return(mut ctx: AssistCtx<impl HirDatabase>) ->
|
||||
edit.target(if_expr.syntax().text_range());
|
||||
edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap());
|
||||
edit.set_cursor(cursor_position);
|
||||
});
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -7,8 +7,32 @@
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?;
|
||||
// Assist: fill_match_arms
|
||||
//
|
||||
// Adds missing clauses to a `match` expression.
|
||||
//
|
||||
// ```
|
||||
// enum Action { Move { distance: u32 }, Stop }
|
||||
//
|
||||
// fn handle(action: Action) {
|
||||
// match action {
|
||||
// <|>
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// enum Action { Move { distance: u32 }, Stop }
|
||||
//
|
||||
// fn handle(action: Action) {
|
||||
// match action {
|
||||
// Action::Move { distance } => (),
|
||||
// Action::Stop => (),
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
|
||||
let match_arm_list = match_expr.match_arm_list()?;
|
||||
|
||||
// We already have some match arms, so we don't provide any assists.
|
||||
@@ -29,7 +53,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
|
||||
};
|
||||
let variant_list = enum_def.variant_list()?;
|
||||
|
||||
ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| {
|
||||
ctx.add_assist(AssistId("fill_match_arms"), "fill match arms", |edit| {
|
||||
let indent_level = IndentLevel::from_node(match_arm_list.syntax());
|
||||
|
||||
let new_arm_list = {
|
||||
@@ -43,9 +67,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
|
||||
edit.target(match_expr.syntax().text_range());
|
||||
edit.set_cursor(expr.syntax().text_range().start());
|
||||
edit.replace_ast(match_arm_list, new_arm_list);
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
fn is_trivial(arm: &ast::MatchArm) -> bool {
|
||||
@@ -130,7 +152,7 @@ fn main() {
|
||||
A::Bs => (),
|
||||
A::Cs(_) => (),
|
||||
A::Ds(_, _) => (),
|
||||
A::Es{ x, y } => (),
|
||||
A::Es { x, y } => (),
|
||||
}
|
||||
}
|
||||
"#,
|
||||
@@ -183,7 +205,7 @@ enum A {
|
||||
|
||||
fn foo(a: &mut A) {
|
||||
match <|>a {
|
||||
A::Es{ x, y } => (),
|
||||
A::Es { x, y } => (),
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::ast::{AstNode, BinExpr, BinOp};
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
/// Flip binary expression assist.
|
||||
pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let expr = ctx.node_at_offset::<BinExpr>()?;
|
||||
// Assist: flip_binexpr
|
||||
//
|
||||
// Flips operands of a binary expression.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// let _ = 90 +<|> 2;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// let _ = 2 + 90;
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn flip_binexpr(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let expr = ctx.find_node_at_offset::<BinExpr>()?;
|
||||
let lhs = expr.lhs()?.syntax().clone();
|
||||
let rhs = expr.rhs()?.syntax().clone();
|
||||
let op_range = expr.op_token()?.text_range();
|
||||
@@ -22,16 +34,14 @@ pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assis
|
||||
return None;
|
||||
}
|
||||
|
||||
ctx.add_action(AssistId("flip_binexpr"), "flip binary expression", |edit| {
|
||||
ctx.add_assist(AssistId("flip_binexpr"), "flip binary expression", |edit| {
|
||||
edit.target(op_range);
|
||||
if let FlipAction::FlipAndReplaceOp(new_op) = action {
|
||||
edit.replace(op_range, new_op);
|
||||
}
|
||||
edit.replace(lhs.text_range(), rhs.text());
|
||||
edit.replace(rhs.text_range(), lhs.text());
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
enum FlipAction {
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{algo::non_trivia_sibling, Direction, T};
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let comma = ctx.token_at_offset().find(|leaf| leaf.kind() == T![,])?;
|
||||
// Assist: flip_comma
|
||||
//
|
||||
// Flips two comma-separated items.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// ((1, 2),<|> (3, 4));
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// ((3, 4), (1, 2));
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let comma = ctx.find_token_at_offset(T![,])?;
|
||||
let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
|
||||
let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
|
||||
|
||||
@@ -16,13 +29,11 @@ pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
|
||||
return None;
|
||||
}
|
||||
|
||||
ctx.add_action(AssistId("flip_comma"), "flip comma", |edit| {
|
||||
ctx.add_assist(AssistId("flip_comma"), "flip comma", |edit| {
|
||||
edit.target(comma.text_range());
|
||||
edit.replace(prev.text_range(), next.to_string());
|
||||
edit.replace(next.text_range(), prev.to_string());
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{
|
||||
algo::non_trivia_sibling,
|
||||
ast::{self, AstNode},
|
||||
Direction, T,
|
||||
};
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
// Assist: flip_trait_bound
|
||||
//
|
||||
// Flips two trait bounds.
|
||||
//
|
||||
// ```
|
||||
// fn foo<T: Clone +<|> Copy>() { }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn foo<T: Copy + Clone>() { }
|
||||
// ```
|
||||
pub(crate) fn flip_trait_bound(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
// We want to replicate the behavior of `flip_binexpr` by only suggesting
|
||||
// the assist when the cursor is on a `+`
|
||||
let plus = ctx.find_token_at_offset(T![+])?;
|
||||
|
||||
// Make sure we're in a `TypeBoundList`
|
||||
if ast::TypeBoundList::cast(plus.parent()).is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (before, after) = (
|
||||
non_trivia_sibling(plus.clone().into(), Direction::Prev)?,
|
||||
non_trivia_sibling(plus.clone().into(), Direction::Next)?,
|
||||
);
|
||||
|
||||
ctx.add_assist(AssistId("flip_trait_bound"), "flip trait bound", |edit| {
|
||||
edit.target(plus.text_range());
|
||||
edit.replace(before.text_range(), after.to_string());
|
||||
edit.replace(after.text_range(), before.to_string());
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
|
||||
|
||||
#[test]
|
||||
fn flip_trait_bound_assist_available() {
|
||||
check_assist_target(flip_trait_bound, "struct S<T> where T: A <|>+ B + C { }", "+")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flip_trait_bound_not_applicable_for_single_trait_bound() {
|
||||
check_assist_not_applicable(flip_trait_bound, "struct S<T> where T: <|>A { }")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flip_trait_bound_works_for_struct() {
|
||||
check_assist(
|
||||
flip_trait_bound,
|
||||
"struct S<T> where T: A <|>+ B { }",
|
||||
"struct S<T> where T: B <|>+ A { }",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flip_trait_bound_works_for_trait_impl() {
|
||||
check_assist(
|
||||
flip_trait_bound,
|
||||
"impl X for S<T> where T: A +<|> B { }",
|
||||
"impl X for S<T> where T: B +<|> A { }",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flip_trait_bound_works_for_fn() {
|
||||
check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B <|>+ A>(t: T) { }")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flip_trait_bound_works_for_fn_where_clause() {
|
||||
check_assist(
|
||||
flip_trait_bound,
|
||||
"fn f<T>(t: T) where T: A +<|> B { }",
|
||||
"fn f<T>(t: T) where T: B +<|> A { }",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flip_trait_bound_works_for_lifetime() {
|
||||
check_assist(
|
||||
flip_trait_bound,
|
||||
"fn f<T>(t: T) where T: A <|>+ 'static { }",
|
||||
"fn f<T>(t: T) where T: 'static <|>+ A { }",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flip_trait_bound_works_for_complex_bounds() {
|
||||
check_assist(
|
||||
flip_trait_bound,
|
||||
"struct S<T> where T: A<T> <|>+ b_mod::B<T> + C<T> { }",
|
||||
"struct S<T> where T: b_mod::B<T> <|>+ A<T> + C<T> { }",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flip_trait_bound_works_for_long_bounds() {
|
||||
check_assist(
|
||||
flip_trait_bound,
|
||||
"struct S<T> where T: A + B + C + D + E + F +<|> G + H + I + J { }",
|
||||
"struct S<T> where T: A + B + C + D + E + G +<|> F + H + I + J { }",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, AstToken},
|
||||
@@ -9,8 +7,24 @@
|
||||
use crate::assist_ctx::AssistBuilder;
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let let_stmt = ctx.node_at_offset::<ast::LetStmt>()?;
|
||||
// Assist: inline_local_variable
|
||||
//
|
||||
// Inlines local variable.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// let x<|> = 1 + 2;
|
||||
// x * 4;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// (1 + 2) * 4;
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn inline_local_varialbe(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?;
|
||||
let bind_pat = match let_stmt.pat()? {
|
||||
ast::Pat::BindPat(pat) => pat,
|
||||
_ => return None,
|
||||
@@ -37,10 +51,8 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
|
||||
let mut wrap_in_parens = vec![true; refs.len()];
|
||||
|
||||
for (i, desc) in refs.iter().enumerate() {
|
||||
let usage_node = ctx
|
||||
.covering_node_for_range(desc.range)
|
||||
.ancestors()
|
||||
.find_map(|node| ast::PathExpr::cast(node))?;
|
||||
let usage_node =
|
||||
ctx.covering_node_for_range(desc.range).ancestors().find_map(ast::PathExpr::cast)?;
|
||||
let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast);
|
||||
let usage_parent = match usage_parent_option {
|
||||
Some(u) => u,
|
||||
@@ -79,7 +91,7 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
|
||||
let init_str = initializer_expr.syntax().text().to_string();
|
||||
let init_in_paren = format!("({})", &init_str);
|
||||
|
||||
ctx.add_action(
|
||||
ctx.add_assist(
|
||||
AssistId("inline_local_variable"),
|
||||
"inline local variable",
|
||||
move |edit: &mut AssistBuilder| {
|
||||
@@ -93,9 +105,7 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
|
||||
}
|
||||
edit.set_cursor(delete_range.start())
|
||||
},
|
||||
);
|
||||
|
||||
ctx.build()
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use format_buf::format;
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{
|
||||
@@ -14,7 +12,23 @@
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
// Assist: introduce_variable
|
||||
//
|
||||
// Extracts subexpression into a variable.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// <|>(1 + 2)<|> * 4;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// let var_name = (1 + 2);
|
||||
// var_name * 4;
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
if ctx.frange.range.is_empty() {
|
||||
return None;
|
||||
}
|
||||
@@ -29,7 +43,7 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
|
||||
if indent.kind() != WHITESPACE {
|
||||
return None;
|
||||
}
|
||||
ctx.add_action(AssistId("introduce_variable"), "introduce variable", move |edit| {
|
||||
ctx.add_assist(AssistId("introduce_variable"), "introduce variable", move |edit| {
|
||||
let mut buf = String::new();
|
||||
|
||||
let cursor_offset = if wrap_in_block {
|
||||
@@ -74,9 +88,7 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
|
||||
}
|
||||
}
|
||||
edit.set_cursor(anchor_stmt.text_range().start() + cursor_offset);
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
/// Check whether the node is a valid expression which can be extracted to a variable.
|
||||
|
||||
@@ -1,11 +1,33 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId, TextRange, TextUnit};
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::ast::{AstNode, MatchArm};
|
||||
|
||||
pub(crate) fn merge_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let current_arm = ctx.node_at_offset::<MatchArm>()?;
|
||||
// Assist: merge_match_arms
|
||||
//
|
||||
// Merges identical match arms.
|
||||
//
|
||||
// ```
|
||||
// enum Action { Move { distance: u32 }, Stop }
|
||||
//
|
||||
// fn handle(action: Action) {
|
||||
// match action {
|
||||
// <|>Action::Move(..) => foo(),
|
||||
// Action::Stop => foo(),
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// enum Action { Move { distance: u32 }, Stop }
|
||||
//
|
||||
// fn handle(action: Action) {
|
||||
// match action {
|
||||
// Action::Move(..) | Action::Stop => foo(),
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn merge_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let current_arm = ctx.find_node_at_offset::<MatchArm>()?;
|
||||
|
||||
// We check if the following match arm matches this one. We could, but don't,
|
||||
// compare to the previous match arm as well.
|
||||
@@ -30,7 +52,7 @@ pub(crate) fn merge_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<A
|
||||
|
||||
let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start();
|
||||
|
||||
ctx.add_action(AssistId("merge_match_arms"), "merge match arms", |edit| {
|
||||
ctx.add_assist(AssistId("merge_match_arms"), "merge match arms", |edit| {
|
||||
fn contains_placeholder(a: &MatchArm) -> bool {
|
||||
a.pats().any(|x| match x {
|
||||
ra_syntax::ast::Pat::PlaceholderPat(..) => true,
|
||||
@@ -58,9 +80,7 @@ fn contains_placeholder(a: &MatchArm) -> bool {
|
||||
edit.target(current_arm.syntax().text_range());
|
||||
edit.replace(TextRange::from_to(start, end), arm);
|
||||
edit.set_cursor(start + offset);
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, edit, make, AstNode, NameOwner, TypeBoundsOwner},
|
||||
@@ -9,8 +7,23 @@
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let type_param_list = ctx.node_at_offset::<ast::TypeParamList>()?;
|
||||
// Assist: move_bounds_to_where_clause
|
||||
//
|
||||
// Moves inline type bounds to a where clause.
|
||||
//
|
||||
// ```
|
||||
// fn apply<T, U, <|>F: FnOnce(T) -> U>(f: F, x: T) -> U {
|
||||
// f(x)
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
|
||||
// f(x)
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let type_param_list = ctx.find_node_at_offset::<ast::TypeParamList>()?;
|
||||
|
||||
let mut type_params = type_param_list.type_params();
|
||||
if type_params.all(|p| p.type_bound_list().is_none()) {
|
||||
@@ -33,38 +46,30 @@ pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>)
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
ctx.add_action(
|
||||
AssistId("move_bounds_to_where_clause"),
|
||||
"move_bounds_to_where_clause",
|
||||
|edit| {
|
||||
let new_params = type_param_list
|
||||
.type_params()
|
||||
.filter(|it| it.type_bound_list().is_some())
|
||||
.map(|type_param| {
|
||||
let without_bounds = type_param.remove_bounds();
|
||||
(type_param, without_bounds)
|
||||
});
|
||||
ctx.add_assist(AssistId("move_bounds_to_where_clause"), "move_bounds_to_where_clause", |edit| {
|
||||
let new_params = type_param_list
|
||||
.type_params()
|
||||
.filter(|it| it.type_bound_list().is_some())
|
||||
.map(|type_param| {
|
||||
let without_bounds = type_param.remove_bounds();
|
||||
(type_param, without_bounds)
|
||||
});
|
||||
|
||||
let new_type_param_list = edit::replace_descendants(&type_param_list, new_params);
|
||||
edit.replace_ast(type_param_list.clone(), new_type_param_list);
|
||||
let new_type_param_list = edit::replace_descendants(&type_param_list, new_params);
|
||||
edit.replace_ast(type_param_list.clone(), new_type_param_list);
|
||||
|
||||
let where_clause = {
|
||||
let predicates = type_param_list.type_params().filter_map(build_predicate);
|
||||
make::where_clause(predicates)
|
||||
};
|
||||
let where_clause = {
|
||||
let predicates = type_param_list.type_params().filter_map(build_predicate);
|
||||
make::where_clause(predicates)
|
||||
};
|
||||
|
||||
let to_insert = match anchor.prev_sibling_or_token() {
|
||||
Some(ref elem) if elem.kind() == WHITESPACE => {
|
||||
format!("{} ", where_clause.syntax())
|
||||
}
|
||||
_ => format!(" {}", where_clause.syntax()),
|
||||
};
|
||||
edit.insert(anchor.text_range().start(), to_insert);
|
||||
edit.target(type_param_list.syntax().text_range());
|
||||
},
|
||||
);
|
||||
|
||||
ctx.build()
|
||||
let to_insert = match anchor.prev_sibling_or_token() {
|
||||
Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()),
|
||||
_ => format!(" {}", where_clause.syntax()),
|
||||
};
|
||||
edit.insert(anchor.text_range().start(), to_insert);
|
||||
edit.target(type_param_list.syntax().text_range());
|
||||
})
|
||||
}
|
||||
|
||||
fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{
|
||||
ast,
|
||||
@@ -9,8 +7,33 @@
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let match_arm = ctx.node_at_offset::<MatchArm>()?;
|
||||
// Assist: move_guard_to_arm_body
|
||||
//
|
||||
// Moves match guard into match arm body.
|
||||
//
|
||||
// ```
|
||||
// enum Action { Move { distance: u32 }, Stop }
|
||||
//
|
||||
// fn handle(action: Action) {
|
||||
// match action {
|
||||
// Action::Move { distance } <|>if distance > 10 => foo(),
|
||||
// _ => (),
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// enum Action { Move { distance: u32 }, Stop }
|
||||
//
|
||||
// fn handle(action: Action) {
|
||||
// match action {
|
||||
// Action::Move { distance } => if distance > 10 { foo() },
|
||||
// _ => (),
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let match_arm = ctx.find_node_at_offset::<MatchArm>()?;
|
||||
let guard = match_arm.guard()?;
|
||||
let space_before_guard = guard.syntax().prev_sibling_or_token();
|
||||
|
||||
@@ -18,7 +41,7 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
|
||||
let arm_expr = match_arm.expr()?;
|
||||
let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
|
||||
|
||||
ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| {
|
||||
ctx.add_assist(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| {
|
||||
edit.target(guard.syntax().text_range());
|
||||
let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) {
|
||||
Some(tok) => {
|
||||
@@ -38,12 +61,36 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
|
||||
edit.set_cursor(
|
||||
arm_expr.syntax().text_range().start() + TextUnit::from(3) - offseting_amount,
|
||||
);
|
||||
});
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let match_arm: MatchArm = ctx.node_at_offset::<MatchArm>()?;
|
||||
// Assist: move_arm_cond_to_match_guard
|
||||
//
|
||||
// Moves if expression from match arm body into a guard.
|
||||
//
|
||||
// ```
|
||||
// enum Action { Move { distance: u32 }, Stop }
|
||||
//
|
||||
// fn handle(action: Action) {
|
||||
// match action {
|
||||
// Action::Move { distance } => <|>if distance > 10 { foo() },
|
||||
// _ => (),
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// enum Action { Move { distance: u32 }, Stop }
|
||||
//
|
||||
// fn handle(action: Action) {
|
||||
// match action {
|
||||
// Action::Move { distance } if distance > 10 => foo(),
|
||||
// _ => (),
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
|
||||
let last_match_pat = match_arm.pats().last()?;
|
||||
|
||||
let arm_body = match_arm.expr()?;
|
||||
@@ -62,7 +109,7 @@ pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>)
|
||||
|
||||
let buf = format!(" if {}", cond.syntax().text());
|
||||
|
||||
ctx.add_action(
|
||||
ctx.add_assist(
|
||||
AssistId("move_arm_cond_to_match_guard"),
|
||||
"move condition to match guard",
|
||||
|edit| {
|
||||
@@ -79,8 +126,7 @@ pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>)
|
||||
edit.insert(last_match_pat.syntax().text_range().end(), buf);
|
||||
edit.set_cursor(last_match_pat.syntax().text_range().end() + TextUnit::from(1));
|
||||
},
|
||||
);
|
||||
ctx.build()
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit};
|
||||
use ra_syntax::{
|
||||
SyntaxKind::{RAW_STRING, STRING},
|
||||
TextRange, TextUnit,
|
||||
};
|
||||
use rustc_lexer;
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let literal = ctx.node_at_offset::<Literal>()?;
|
||||
if literal.token().kind() != ra_syntax::SyntaxKind::STRING {
|
||||
return None;
|
||||
}
|
||||
let token = literal.token();
|
||||
// Assist: make_raw_string
|
||||
//
|
||||
// Adds `r#` to a plain string literal.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// "Hello,<|> World!";
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// r#"Hello, World!"#;
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn make_raw_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let token = ctx.find_token_at_offset(STRING)?;
|
||||
let text = token.text().as_str();
|
||||
let usual_string_range = find_usual_string_range(text)?;
|
||||
let start_of_inside = usual_string_range.start().to_usize() + 1;
|
||||
@@ -29,19 +41,105 @@ pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
|
||||
if error.is_err() {
|
||||
return None;
|
||||
}
|
||||
ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| {
|
||||
edit.target(literal.syntax().text_range());
|
||||
ctx.add_assist(AssistId("make_raw_string"), "make raw string", |edit| {
|
||||
edit.target(token.text_range());
|
||||
let max_hash_streak = count_hashes(&unescaped);
|
||||
let mut hashes = String::with_capacity(max_hash_streak + 1);
|
||||
for _ in 0..hashes.capacity() {
|
||||
hashes.push('#');
|
||||
}
|
||||
edit.replace(
|
||||
literal.syntax().text_range(),
|
||||
format!("r{}\"{}\"{}", hashes, unescaped, hashes),
|
||||
);
|
||||
});
|
||||
ctx.build()
|
||||
edit.replace(token.text_range(), format!("r{}\"{}\"{}", hashes, unescaped, hashes));
|
||||
})
|
||||
}
|
||||
|
||||
// Assist: make_usual_string
|
||||
//
|
||||
// Turns a raw string into a plain string.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// r#"Hello,<|> "World!""#;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// "Hello, \"World!\"";
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn make_usual_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
||||
let text = token.text().as_str();
|
||||
let usual_string_range = find_usual_string_range(text)?;
|
||||
ctx.add_assist(AssistId("make_usual_string"), "make usual string", |edit| {
|
||||
edit.target(token.text_range());
|
||||
// parse inside string to escape `"`
|
||||
let start_of_inside = usual_string_range.start().to_usize() + 1;
|
||||
let end_of_inside = usual_string_range.end().to_usize();
|
||||
let inside_str = &text[start_of_inside..end_of_inside];
|
||||
let escaped = inside_str.escape_default().to_string();
|
||||
edit.replace(token.text_range(), format!("\"{}\"", escaped));
|
||||
})
|
||||
}
|
||||
|
||||
// Assist: add_hash
|
||||
//
|
||||
// Adds a hash to a raw string literal.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// r#"Hello,<|> World!"#;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// r##"Hello, World!"##;
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_hash(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
||||
ctx.add_assist(AssistId("add_hash"), "add hash to raw string", |edit| {
|
||||
edit.target(token.text_range());
|
||||
edit.insert(token.text_range().start() + TextUnit::of_char('r'), "#");
|
||||
edit.insert(token.text_range().end(), "#");
|
||||
})
|
||||
}
|
||||
|
||||
// Assist: remove_hash
|
||||
//
|
||||
// Removes a hash from a raw string literal.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// r#"Hello,<|> World!"#;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// r"Hello, World!";
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn remove_hash(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
||||
let text = token.text().as_str();
|
||||
if text.starts_with("r\"") {
|
||||
// no hash to remove
|
||||
return None;
|
||||
}
|
||||
ctx.add_assist(AssistId("remove_hash"), "remove hash from raw string", |edit| {
|
||||
edit.target(token.text_range());
|
||||
let result = &text[2..text.len() - 1];
|
||||
let result = if result.starts_with('\"') {
|
||||
// no more hash, escape
|
||||
let internal_str = &result[1..result.len() - 1];
|
||||
format!("\"{}\"", internal_str.escape_default().to_string())
|
||||
} else {
|
||||
result.to_owned()
|
||||
};
|
||||
edit.replace(token.text_range(), format!("r{}", result));
|
||||
})
|
||||
}
|
||||
|
||||
fn count_hashes(s: &str) -> usize {
|
||||
@@ -57,69 +155,17 @@ fn count_hashes(s: &str) -> usize {
|
||||
}
|
||||
|
||||
fn find_usual_string_range(s: &str) -> Option<TextRange> {
|
||||
Some(TextRange::from_to(
|
||||
TextUnit::from(s.find('"')? as u32),
|
||||
TextUnit::from(s.rfind('"')? as u32),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let literal = ctx.node_at_offset::<Literal>()?;
|
||||
if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {
|
||||
return None;
|
||||
let left_quote = s.find('"')?;
|
||||
let right_quote = s.rfind('"')?;
|
||||
if left_quote == right_quote {
|
||||
// `s` only contains one quote
|
||||
None
|
||||
} else {
|
||||
Some(TextRange::from_to(
|
||||
TextUnit::from(left_quote as u32),
|
||||
TextUnit::from(right_quote as u32),
|
||||
))
|
||||
}
|
||||
let token = literal.token();
|
||||
let text = token.text().as_str();
|
||||
let usual_string_range = find_usual_string_range(text)?;
|
||||
ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| {
|
||||
edit.target(literal.syntax().text_range());
|
||||
// parse inside string to escape `"`
|
||||
let start_of_inside = usual_string_range.start().to_usize() + 1;
|
||||
let end_of_inside = usual_string_range.end().to_usize();
|
||||
let inside_str = &text[start_of_inside..end_of_inside];
|
||||
let escaped = inside_str.escape_default().to_string();
|
||||
edit.replace(literal.syntax().text_range(), format!("\"{}\"", escaped));
|
||||
});
|
||||
ctx.build()
|
||||
}
|
||||
|
||||
pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let literal = ctx.node_at_offset::<Literal>()?;
|
||||
if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {
|
||||
return None;
|
||||
}
|
||||
ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| {
|
||||
edit.target(literal.syntax().text_range());
|
||||
edit.insert(literal.syntax().text_range().start() + TextUnit::of_char('r'), "#");
|
||||
edit.insert(literal.syntax().text_range().end(), "#");
|
||||
});
|
||||
ctx.build()
|
||||
}
|
||||
|
||||
pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let literal = ctx.node_at_offset::<Literal>()?;
|
||||
if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {
|
||||
return None;
|
||||
}
|
||||
let token = literal.token();
|
||||
let text = token.text().as_str();
|
||||
if text.starts_with("r\"") {
|
||||
// no hash to remove
|
||||
return None;
|
||||
}
|
||||
ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| {
|
||||
edit.target(literal.syntax().text_range());
|
||||
let result = &text[2..text.len() - 1];
|
||||
let result = if result.starts_with("\"") {
|
||||
// no more hash, escape
|
||||
let internal_str = &result[1..result.len() - 1];
|
||||
format!("\"{}\"", internal_str.escape_default().to_string())
|
||||
} else {
|
||||
result.to_owned()
|
||||
};
|
||||
edit.replace(literal.syntax().text_range(), format!("r{}", result));
|
||||
});
|
||||
ctx.build()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -158,6 +204,23 @@ fn f() {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_raw_string_works_inside_macros() {
|
||||
check_assist(
|
||||
make_raw_string,
|
||||
r#"
|
||||
fn f() {
|
||||
format!(<|>"x = {}", 92)
|
||||
}
|
||||
"#,
|
||||
r##"
|
||||
fn f() {
|
||||
format!(<|>r#"x = {}"#, 92)
|
||||
}
|
||||
"##,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_raw_string_hashes_inside_works() {
|
||||
check_assist(
|
||||
@@ -211,6 +274,30 @@ fn f() {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_raw_string_not_works_on_partial_string() {
|
||||
check_assist_not_applicable(
|
||||
make_raw_string,
|
||||
r#"
|
||||
fn f() {
|
||||
let s = "foo<|>
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_usual_string_not_works_on_partial_string() {
|
||||
check_assist_not_applicable(
|
||||
make_usual_string,
|
||||
r#"
|
||||
fn main() {
|
||||
let s = r#"bar<|>
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_hash_target() {
|
||||
check_assist_target(
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode},
|
||||
TextUnit, T,
|
||||
};
|
||||
|
||||
pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let macro_call = ctx.node_at_offset::<ast::MacroCall>()?;
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
// Assist: remove_dbg
|
||||
//
|
||||
// Removes `dbg!()` macro call.
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// <|>dbg!(92);
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// 92;
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
|
||||
|
||||
if !is_valid_macrocall(¯o_call, "dbg")? {
|
||||
return None;
|
||||
@@ -44,13 +58,11 @@ pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
|
||||
text.slice(without_parens).to_string()
|
||||
};
|
||||
|
||||
ctx.add_action(AssistId("remove_dbg"), "remove dbg!()", |edit| {
|
||||
ctx.add_assist(AssistId("remove_dbg"), "remove dbg!()", |edit| {
|
||||
edit.target(macro_call.syntax().text_range());
|
||||
edit.replace(macro_range, macro_content);
|
||||
edit.set_cursor(cursor_pos);
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
/// Verifies that the given macro_call actually matches the given name
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use format_buf::format;
|
||||
use hir::db::HirDatabase;
|
||||
use ra_fmt::extract_trivial_expression;
|
||||
@@ -7,8 +5,34 @@
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let if_expr: ast::IfExpr = ctx.node_at_offset()?;
|
||||
// Assist: replace_if_let_with_match
|
||||
//
|
||||
// Replaces `if let` with an else branch with a `match` expression.
|
||||
//
|
||||
// ```
|
||||
// enum Action { Move { distance: u32 }, Stop }
|
||||
//
|
||||
// fn handle(action: Action) {
|
||||
// <|>if let Action::Move { distance } = action {
|
||||
// foo(distance)
|
||||
// } else {
|
||||
// bar()
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// enum Action { Move { distance: u32 }, Stop }
|
||||
//
|
||||
// fn handle(action: Action) {
|
||||
// match action {
|
||||
// Action::Move { distance } => foo(distance),
|
||||
// _ => bar(),
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn replace_if_let_with_match(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
|
||||
let cond = if_expr.condition()?;
|
||||
let pat = cond.pat()?;
|
||||
let expr = cond.expr()?;
|
||||
@@ -18,14 +42,12 @@ pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) ->
|
||||
ast::ElseBranch::IfExpr(_) => return None,
|
||||
};
|
||||
|
||||
ctx.add_action(AssistId("replace_if_let_with_match"), "replace with match", |edit| {
|
||||
ctx.add_assist(AssistId("replace_if_let_with_match"), "replace with match", |edit| {
|
||||
let match_expr = build_match_expr(expr, pat, then_block, else_block);
|
||||
edit.target(if_expr.syntax().text_range());
|
||||
edit.replace_node_and_indent(if_expr.syntax(), match_expr);
|
||||
edit.set_cursor(if_expr.syntax().text_range().start())
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
fn build_match_expr(
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use std::iter::successors;
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
@@ -7,8 +5,19 @@
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
pub(crate) fn split_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let colon_colon = ctx.token_at_offset().find(|leaf| leaf.kind() == T![::])?;
|
||||
// Assist: split_import
|
||||
//
|
||||
// Wraps the tail of import into braces.
|
||||
//
|
||||
// ```
|
||||
// use std::<|>collections::HashMap;
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// use std::{collections::HashMap};
|
||||
// ```
|
||||
pub(crate) fn split_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let colon_colon = ctx.find_token_at_offset(T![::])?;
|
||||
let path = ast::Path::cast(colon_colon.parent())?;
|
||||
let top_path = successors(Some(path), |it| it.parent_path()).last()?;
|
||||
|
||||
@@ -23,14 +32,12 @@ pub(crate) fn split_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assis
|
||||
None => top_path.syntax().text_range().end(),
|
||||
};
|
||||
|
||||
ctx.add_action(AssistId("split_import"), "split import", |edit| {
|
||||
ctx.add_assist(AssistId("split_import"), "split import", |edit| {
|
||||
edit.target(colon_colon.text_range());
|
||||
edit.insert(l_curly, "{");
|
||||
edit.insert(r_curly, "}");
|
||||
edit.set_cursor(l_curly + TextUnit::of_str("{"));
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//! Each assist definition has a special comment, which specifies docs and
|
||||
//! example.
|
||||
//!
|
||||
//! We collect all the example and write the as tests in this module.
|
||||
|
||||
mod generated;
|
||||
|
||||
use hir::mock::MockDatabase;
|
||||
use ra_db::FileRange;
|
||||
use test_utils::{assert_eq_text, extract_range_or_offset};
|
||||
|
||||
fn check(assist_id: &str, before: &str, after: &str) {
|
||||
let (selection, before) = extract_range_or_offset(before);
|
||||
let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
|
||||
let frange = FileRange { file_id, range: selection.into() };
|
||||
|
||||
let (_assist_id, action) = crate::assists(&db, frange)
|
||||
.into_iter()
|
||||
.find(|(id, _)| id.id.0 == assist_id)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"\n\nAssist is not applicable: {}\nAvailable assists: {}",
|
||||
assist_id,
|
||||
crate::assists(&db, frange)
|
||||
.into_iter()
|
||||
.map(|(id, _)| id.id.0)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
});
|
||||
|
||||
let actual = action.edit.apply(&before);
|
||||
assert_eq_text!(after, &actual);
|
||||
}
|
||||
@@ -0,0 +1,526 @@
|
||||
//! Generated file, do not edit by hand, see `crate/ra_tools/src/codegen`
|
||||
|
||||
use super::check;
|
||||
|
||||
#[test]
|
||||
fn doctest_add_derive() {
|
||||
check(
|
||||
"add_derive",
|
||||
r#####"
|
||||
struct Point {
|
||||
x: u32,
|
||||
y: u32,<|>
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
#[derive()]
|
||||
struct Point {
|
||||
x: u32,
|
||||
y: u32,
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_explicit_type() {
|
||||
check(
|
||||
"add_explicit_type",
|
||||
r#####"
|
||||
fn main() {
|
||||
let x<|> = 92;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
let x: i32 = 92;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_hash() {
|
||||
check(
|
||||
"add_hash",
|
||||
r#####"
|
||||
fn main() {
|
||||
r#"Hello,<|> World!"#;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
r##"Hello, World!"##;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_impl() {
|
||||
check(
|
||||
"add_impl",
|
||||
r#####"
|
||||
struct Ctx<T: Clone> {
|
||||
data: T,<|>
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
struct Ctx<T: Clone> {
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Ctx<T> {
|
||||
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_impl_default_members() {
|
||||
check(
|
||||
"add_impl_default_members",
|
||||
r#####"
|
||||
trait T {
|
||||
Type X;
|
||||
fn foo(&self);
|
||||
fn bar(&self) {}
|
||||
}
|
||||
|
||||
impl T for () {
|
||||
Type X = ();
|
||||
fn foo(&self) {}<|>
|
||||
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
trait T {
|
||||
Type X;
|
||||
fn foo(&self);
|
||||
fn bar(&self) {}
|
||||
}
|
||||
|
||||
impl T for () {
|
||||
Type X = ();
|
||||
fn foo(&self) {}
|
||||
fn bar(&self) {}
|
||||
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_impl_missing_members() {
|
||||
check(
|
||||
"add_impl_missing_members",
|
||||
r#####"
|
||||
trait T {
|
||||
Type X;
|
||||
fn foo(&self);
|
||||
fn bar(&self) {}
|
||||
}
|
||||
|
||||
impl T for () {<|>
|
||||
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
trait T {
|
||||
Type X;
|
||||
fn foo(&self);
|
||||
fn bar(&self) {}
|
||||
}
|
||||
|
||||
impl T for () {
|
||||
fn foo(&self) { unimplemented!() }
|
||||
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_import() {
|
||||
check(
|
||||
"add_import",
|
||||
r#####"
|
||||
fn process(map: std::collections::<|>HashMap<String, String>) {}
|
||||
"#####,
|
||||
r#####"
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn process(map: HashMap<String, String>) {}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_apply_demorgan() {
|
||||
check(
|
||||
"apply_demorgan",
|
||||
r#####"
|
||||
fn main() {
|
||||
if x != 4 ||<|> !y {}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
if !(x == 4 && y) {}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_change_visibility() {
|
||||
check(
|
||||
"change_visibility",
|
||||
r#####"
|
||||
<|>fn frobnicate() {}
|
||||
"#####,
|
||||
r#####"
|
||||
pub(crate) fn frobnicate() {}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_convert_to_guarded_return() {
|
||||
check(
|
||||
"convert_to_guarded_return",
|
||||
r#####"
|
||||
fn main() {
|
||||
<|>if cond {
|
||||
foo();
|
||||
bar();
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
if !cond {
|
||||
return;
|
||||
}
|
||||
foo();
|
||||
bar();
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_fill_match_arms() {
|
||||
check(
|
||||
"fill_match_arms",
|
||||
r#####"
|
||||
enum Action { Move { distance: u32 }, Stop }
|
||||
|
||||
fn handle(action: Action) {
|
||||
match action {
|
||||
<|>
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
enum Action { Move { distance: u32 }, Stop }
|
||||
|
||||
fn handle(action: Action) {
|
||||
match action {
|
||||
Action::Move { distance } => (),
|
||||
Action::Stop => (),
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_flip_binexpr() {
|
||||
check(
|
||||
"flip_binexpr",
|
||||
r#####"
|
||||
fn main() {
|
||||
let _ = 90 +<|> 2;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
let _ = 2 + 90;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_flip_comma() {
|
||||
check(
|
||||
"flip_comma",
|
||||
r#####"
|
||||
fn main() {
|
||||
((1, 2),<|> (3, 4));
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
((3, 4), (1, 2));
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_flip_trait_bound() {
|
||||
check(
|
||||
"flip_trait_bound",
|
||||
r#####"
|
||||
fn foo<T: Clone +<|> Copy>() { }
|
||||
"#####,
|
||||
r#####"
|
||||
fn foo<T: Copy + Clone>() { }
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_inline_local_variable() {
|
||||
check(
|
||||
"inline_local_variable",
|
||||
r#####"
|
||||
fn main() {
|
||||
let x<|> = 1 + 2;
|
||||
x * 4;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
(1 + 2) * 4;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_introduce_variable() {
|
||||
check(
|
||||
"introduce_variable",
|
||||
r#####"
|
||||
fn main() {
|
||||
<|>(1 + 2)<|> * 4;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
let var_name = (1 + 2);
|
||||
var_name * 4;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_make_raw_string() {
|
||||
check(
|
||||
"make_raw_string",
|
||||
r#####"
|
||||
fn main() {
|
||||
"Hello,<|> World!";
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
r#"Hello, World!"#;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_make_usual_string() {
|
||||
check(
|
||||
"make_usual_string",
|
||||
r#####"
|
||||
fn main() {
|
||||
r#"Hello,<|> "World!""#;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
"Hello, \"World!\"";
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_merge_match_arms() {
|
||||
check(
|
||||
"merge_match_arms",
|
||||
r#####"
|
||||
enum Action { Move { distance: u32 }, Stop }
|
||||
|
||||
fn handle(action: Action) {
|
||||
match action {
|
||||
<|>Action::Move(..) => foo(),
|
||||
Action::Stop => foo(),
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
enum Action { Move { distance: u32 }, Stop }
|
||||
|
||||
fn handle(action: Action) {
|
||||
match action {
|
||||
Action::Move(..) | Action::Stop => foo(),
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_move_arm_cond_to_match_guard() {
|
||||
check(
|
||||
"move_arm_cond_to_match_guard",
|
||||
r#####"
|
||||
enum Action { Move { distance: u32 }, Stop }
|
||||
|
||||
fn handle(action: Action) {
|
||||
match action {
|
||||
Action::Move { distance } => <|>if distance > 10 { foo() },
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
enum Action { Move { distance: u32 }, Stop }
|
||||
|
||||
fn handle(action: Action) {
|
||||
match action {
|
||||
Action::Move { distance } if distance > 10 => foo(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_move_bounds_to_where_clause() {
|
||||
check(
|
||||
"move_bounds_to_where_clause",
|
||||
r#####"
|
||||
fn apply<T, U, <|>F: FnOnce(T) -> U>(f: F, x: T) -> U {
|
||||
f(x)
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
|
||||
f(x)
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_move_guard_to_arm_body() {
|
||||
check(
|
||||
"move_guard_to_arm_body",
|
||||
r#####"
|
||||
enum Action { Move { distance: u32 }, Stop }
|
||||
|
||||
fn handle(action: Action) {
|
||||
match action {
|
||||
Action::Move { distance } <|>if distance > 10 => foo(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
enum Action { Move { distance: u32 }, Stop }
|
||||
|
||||
fn handle(action: Action) {
|
||||
match action {
|
||||
Action::Move { distance } => if distance > 10 { foo() },
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_remove_dbg() {
|
||||
check(
|
||||
"remove_dbg",
|
||||
r#####"
|
||||
fn main() {
|
||||
<|>dbg!(92);
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
92;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_remove_hash() {
|
||||
check(
|
||||
"remove_hash",
|
||||
r#####"
|
||||
fn main() {
|
||||
r#"Hello,<|> World!"#;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
r"Hello, World!";
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_replace_if_let_with_match() {
|
||||
check(
|
||||
"replace_if_let_with_match",
|
||||
r#####"
|
||||
enum Action { Move { distance: u32 }, Stop }
|
||||
|
||||
fn handle(action: Action) {
|
||||
<|>if let Action::Move { distance } = action {
|
||||
foo(distance)
|
||||
} else {
|
||||
bar()
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
enum Action { Move { distance: u32 }, Stop }
|
||||
|
||||
fn handle(action: Action) {
|
||||
match action {
|
||||
Action::Move { distance } => foo(distance),
|
||||
_ => bar(),
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_split_import() {
|
||||
check(
|
||||
"split_import",
|
||||
r#####"
|
||||
use std::<|>collections::HashMap;
|
||||
"#####,
|
||||
r#####"
|
||||
use std::{collections::HashMap};
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
@@ -7,15 +7,16 @@
|
||||
|
||||
mod assist_ctx;
|
||||
mod marks;
|
||||
#[cfg(test)]
|
||||
mod doc_tests;
|
||||
|
||||
use hir::db::HirDatabase;
|
||||
use itertools::Itertools;
|
||||
use ra_db::FileRange;
|
||||
use ra_syntax::{TextRange, TextUnit};
|
||||
use ra_text_edit::TextEdit;
|
||||
|
||||
pub(crate) use crate::assist_ctx::{Assist, AssistCtx};
|
||||
pub use crate::assists::auto_import::auto_import_text_edit;
|
||||
pub use crate::assists::add_import::auto_import_text_edit;
|
||||
|
||||
/// Unique identifier of the assist, should not be shown to the user
|
||||
/// directly.
|
||||
@@ -36,7 +37,7 @@ pub struct AssistAction {
|
||||
pub target: Option<TextRange>,
|
||||
}
|
||||
|
||||
/// Return all the assists eapplicable at the given position.
|
||||
/// Return all the assists applicable at the given position.
|
||||
///
|
||||
/// Assists are returned in the "unresolved" state, that is only labels are
|
||||
/// returned, without actual edits.
|
||||
@@ -49,10 +50,10 @@ pub fn applicable_assists<H>(db: &H, range: FileRange) -> Vec<AssistLabel>
|
||||
.iter()
|
||||
.filter_map(|f| f(ctx.clone()))
|
||||
.map(|a| match a {
|
||||
Assist::Unresolved(labels) => labels,
|
||||
Assist::Resolved(..) => unreachable!(),
|
||||
Assist::Unresolved { label } => label,
|
||||
Assist::Resolved { .. } => unreachable!(),
|
||||
})
|
||||
.concat()
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -71,10 +72,10 @@ pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)>
|
||||
.iter()
|
||||
.filter_map(|f| f(ctx.clone()))
|
||||
.map(|a| match a {
|
||||
Assist::Resolved(labels_actions) => labels_actions,
|
||||
Assist::Unresolved(..) => unreachable!(),
|
||||
Assist::Resolved { label, action } => (label, action),
|
||||
Assist::Unresolved { .. } => unreachable!(),
|
||||
})
|
||||
.concat();
|
||||
.collect::<Vec<_>>();
|
||||
a.sort_by(|a, b| match (a.1.target, b.1.target) {
|
||||
(Some(a), Some(b)) => a.len().cmp(&b.len()),
|
||||
(Some(_), None) => Ordering::Less,
|
||||
@@ -95,6 +96,7 @@ mod assists {
|
||||
mod apply_demorgan;
|
||||
mod flip_comma;
|
||||
mod flip_binexpr;
|
||||
mod flip_trait_bound;
|
||||
mod change_visibility;
|
||||
mod fill_match_arms;
|
||||
mod merge_match_arms;
|
||||
@@ -104,7 +106,7 @@ mod assists {
|
||||
mod replace_if_let_with_match;
|
||||
mod split_import;
|
||||
mod remove_dbg;
|
||||
pub(crate) mod auto_import;
|
||||
pub(crate) mod add_import;
|
||||
mod add_missing_impl_members;
|
||||
mod move_guard;
|
||||
mod move_bounds;
|
||||
@@ -121,11 +123,12 @@ pub(crate) fn all<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<As
|
||||
merge_match_arms::merge_match_arms,
|
||||
flip_comma::flip_comma,
|
||||
flip_binexpr::flip_binexpr,
|
||||
flip_trait_bound::flip_trait_bound,
|
||||
introduce_variable::introduce_variable,
|
||||
replace_if_let_with_match::replace_if_let_with_match,
|
||||
split_import::split_import,
|
||||
remove_dbg::remove_dbg,
|
||||
auto_import::auto_import,
|
||||
add_import::add_import,
|
||||
add_missing_impl_members::add_missing_impl_members,
|
||||
add_missing_impl_members::add_missing_default_members,
|
||||
inline_local_variable::inline_local_varialbe,
|
||||
@@ -154,39 +157,6 @@ pub(crate) fn check_assist(
|
||||
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
|
||||
before: &str,
|
||||
after: &str,
|
||||
) {
|
||||
check_assist_nth_action(assist, before, after, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn check_assist_range(
|
||||
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
|
||||
before: &str,
|
||||
after: &str,
|
||||
) {
|
||||
check_assist_range_nth_action(assist, before, after, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn check_assist_target(
|
||||
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
|
||||
before: &str,
|
||||
target: &str,
|
||||
) {
|
||||
check_assist_target_nth_action(assist, before, target, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn check_assist_range_target(
|
||||
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
|
||||
before: &str,
|
||||
target: &str,
|
||||
) {
|
||||
check_assist_range_target_nth_action(assist, before, target, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn check_assist_nth_action(
|
||||
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
|
||||
before: &str,
|
||||
after: &str,
|
||||
index: usize,
|
||||
) {
|
||||
let (before_cursor_pos, before) = extract_offset(before);
|
||||
let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
|
||||
@@ -194,12 +164,11 @@ pub(crate) fn check_assist_nth_action(
|
||||
FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
|
||||
let assist =
|
||||
AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
|
||||
let labels_actions = match assist {
|
||||
Assist::Unresolved(_) => unreachable!(),
|
||||
Assist::Resolved(labels_actions) => labels_actions,
|
||||
let action = match assist {
|
||||
Assist::Unresolved { .. } => unreachable!(),
|
||||
Assist::Resolved { action, .. } => action,
|
||||
};
|
||||
|
||||
let (_, action) = labels_actions.get(index).expect("expect assist action at index");
|
||||
let actual = action.edit.apply(&before);
|
||||
let actual_cursor_pos = match action.cursor_position {
|
||||
None => action
|
||||
@@ -212,23 +181,21 @@ pub(crate) fn check_assist_nth_action(
|
||||
assert_eq_text!(after, &actual);
|
||||
}
|
||||
|
||||
pub(crate) fn check_assist_range_nth_action(
|
||||
pub(crate) fn check_assist_range(
|
||||
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
|
||||
before: &str,
|
||||
after: &str,
|
||||
index: usize,
|
||||
) {
|
||||
let (range, before) = extract_range(before);
|
||||
let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
|
||||
let frange = FileRange { file_id, range };
|
||||
let assist =
|
||||
AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
|
||||
let labels_actions = match assist {
|
||||
Assist::Unresolved(_) => unreachable!(),
|
||||
Assist::Resolved(labels_actions) => labels_actions,
|
||||
let action = match assist {
|
||||
Assist::Unresolved { .. } => unreachable!(),
|
||||
Assist::Resolved { action, .. } => action,
|
||||
};
|
||||
|
||||
let (_, action) = labels_actions.get(index).expect("expect assist action at index");
|
||||
let mut actual = action.edit.apply(&before);
|
||||
if let Some(pos) = action.cursor_position {
|
||||
actual = add_cursor(&actual, pos);
|
||||
@@ -236,11 +203,10 @@ pub(crate) fn check_assist_range_nth_action(
|
||||
assert_eq_text!(after, &actual);
|
||||
}
|
||||
|
||||
pub(crate) fn check_assist_target_nth_action(
|
||||
pub(crate) fn check_assist_target(
|
||||
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
|
||||
before: &str,
|
||||
target: &str,
|
||||
index: usize,
|
||||
) {
|
||||
let (before_cursor_pos, before) = extract_offset(before);
|
||||
let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
|
||||
@@ -248,33 +214,30 @@ pub(crate) fn check_assist_target_nth_action(
|
||||
FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
|
||||
let assist =
|
||||
AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
|
||||
let labels_actions = match assist {
|
||||
Assist::Unresolved(_) => unreachable!(),
|
||||
Assist::Resolved(labels_actions) => labels_actions,
|
||||
let action = match assist {
|
||||
Assist::Unresolved { .. } => unreachable!(),
|
||||
Assist::Resolved { action, .. } => action,
|
||||
};
|
||||
|
||||
let (_, action) = labels_actions.get(index).expect("expect assist action at index");
|
||||
let range = action.target.expect("expected target on action");
|
||||
assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
|
||||
}
|
||||
|
||||
pub(crate) fn check_assist_range_target_nth_action(
|
||||
pub(crate) fn check_assist_range_target(
|
||||
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
|
||||
before: &str,
|
||||
target: &str,
|
||||
index: usize,
|
||||
) {
|
||||
let (range, before) = extract_range(before);
|
||||
let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
|
||||
let frange = FileRange { file_id, range };
|
||||
let assist =
|
||||
AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
|
||||
let labels_actions = match assist {
|
||||
Assist::Unresolved(_) => unreachable!(),
|
||||
Assist::Resolved(labels_actions) => labels_actions,
|
||||
let action = match assist {
|
||||
Assist::Unresolved { .. } => unreachable!(),
|
||||
Assist::Resolved { action, .. } => action,
|
||||
};
|
||||
|
||||
let (_, action) = labels_actions.get(index).expect("expect assist action at index");
|
||||
let range = action.target.expect("expected target on action");
|
||||
assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
|
||||
}
|
||||
|
||||
@@ -134,10 +134,7 @@ fn resolve_relative_path(
|
||||
) -> Option<FileId> {
|
||||
let path = {
|
||||
let mut path = self.0.file_relative_path(anchor);
|
||||
// Workaround for relative path API: turn `lib.rs` into ``.
|
||||
if !path.pop() {
|
||||
path = RelativePathBuf::default();
|
||||
}
|
||||
assert!(path.pop());
|
||||
path.push(relative_path);
|
||||
path.normalize()
|
||||
};
|
||||
|
||||
@@ -19,12 +19,14 @@ ra_cfg = { path = "../ra_cfg" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
mbe = { path = "../ra_mbe", package = "ra_mbe" }
|
||||
tt = { path = "../ra_tt", package = "ra_tt" }
|
||||
hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" }
|
||||
hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
|
||||
test_utils = { path = "../test_utils" }
|
||||
ra_prof = { path = "../ra_prof" }
|
||||
|
||||
chalk-solve = { git = "https://github.com/rust-lang/chalk.git" }
|
||||
chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git" }
|
||||
chalk-ir = { git = "https://github.com/rust-lang/chalk.git" }
|
||||
chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "8314f2fcec8582a58c24b638f1a259d4145a0809" }
|
||||
chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "8314f2fcec8582a58c24b638f1a259d4145a0809" }
|
||||
chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "8314f2fcec8582a58c24b638f1a259d4145a0809" }
|
||||
lalrpop-intern = "0.15.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{type_ref::TypeRef, LocalEnumVariantId};
|
||||
use hir_expand::name::AsName;
|
||||
use ra_arena::{impl_arena_id, Arena, RawId};
|
||||
use ra_syntax::ast::{self, NameOwner, StructKind, TypeAscriptionOwner};
|
||||
|
||||
use crate::{
|
||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
type_ref::TypeRef,
|
||||
AsName, Enum, EnumVariant, FieldSource, HasSource, Module, Name, Source, Struct, StructField,
|
||||
Enum, EnumVariant, FieldSource, HasSource, Module, Name, Source, Struct, StructField,
|
||||
};
|
||||
|
||||
impl Struct {
|
||||
@@ -67,7 +68,7 @@ pub(crate) fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EnumData {
|
||||
pub(crate) name: Option<Name>,
|
||||
pub(crate) variants: Arena<EnumVariantId, EnumVariantData>,
|
||||
pub(crate) variants: Arena<LocalEnumVariantId, EnumVariantData>,
|
||||
}
|
||||
|
||||
impl EnumData {
|
||||
@@ -84,10 +85,6 @@ pub(crate) fn enum_data_query(db: &(impl DefDatabase + AstDatabase), e: Enum) ->
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct EnumVariantId(RawId);
|
||||
impl_arena_id!(EnumVariantId);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct EnumVariantData {
|
||||
pub(crate) name: Option<Name>,
|
||||
|
||||
+62
-122
@@ -5,11 +5,17 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use ra_db::{CrateId, Edition, FileId};
|
||||
use hir_def::{
|
||||
builtin_type::BuiltinType,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
CrateModuleId, LocalEnumVariantId, ModuleId,
|
||||
};
|
||||
use hir_expand::name::{self, AsName};
|
||||
use ra_db::{CrateId, Edition};
|
||||
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
|
||||
|
||||
use crate::{
|
||||
adt::{EnumVariantId, StructFieldId, VariantDef},
|
||||
adt::{StructFieldId, VariantDef},
|
||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
diagnostics::DiagnosticSink,
|
||||
expr::{validation::ExprValidator, Body, BodySourceMap},
|
||||
@@ -19,20 +25,11 @@
|
||||
TypeAliasId,
|
||||
},
|
||||
impl_block::ImplBlock,
|
||||
name::{
|
||||
BOOL, CHAR, F32, F64, I128, I16, I32, I64, I8, ISIZE, SELF_TYPE, STR, U128, U16, U32, U64,
|
||||
U8, USIZE,
|
||||
},
|
||||
nameres::{CrateModuleId, ImportId, ModuleScope, Namespace},
|
||||
nameres::{ImportId, ModuleScope, Namespace},
|
||||
resolve::{Resolver, Scope, TypeNs},
|
||||
traits::TraitData,
|
||||
ty::{
|
||||
primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness},
|
||||
InferenceResult, TraitRef,
|
||||
},
|
||||
type_ref::Mutability,
|
||||
type_ref::TypeRef,
|
||||
AsName, AstId, Either, HasSource, Name, Ty,
|
||||
ty::{InferenceResult, TraitRef},
|
||||
Either, HasSource, Name, Ty,
|
||||
};
|
||||
|
||||
/// hir::Crate describes a single crate. It's the main interface with which
|
||||
@@ -67,8 +64,7 @@ pub fn dependencies(self, db: &impl DefDatabase) -> Vec<CrateDependency> {
|
||||
|
||||
pub fn root_module(self, db: &impl DefDatabase) -> Option<Module> {
|
||||
let module_id = db.crate_def_map(self).root();
|
||||
let module = Module { krate: self, module_id };
|
||||
Some(module)
|
||||
Some(Module::new(self, module_id))
|
||||
}
|
||||
|
||||
pub fn edition(self, db: &impl DefDatabase) -> Edition {
|
||||
@@ -83,43 +79,7 @@ pub fn all(db: &impl DefDatabase) -> Vec<Crate> {
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Module {
|
||||
pub(crate) krate: Crate,
|
||||
pub(crate) module_id: CrateModuleId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum BuiltinType {
|
||||
Char,
|
||||
Bool,
|
||||
Str,
|
||||
Int(IntTy),
|
||||
Float(FloatTy),
|
||||
}
|
||||
|
||||
impl BuiltinType {
|
||||
#[rustfmt::skip]
|
||||
pub(crate) const ALL: &'static [(Name, BuiltinType)] = &[
|
||||
(CHAR, BuiltinType::Char),
|
||||
(BOOL, BuiltinType::Bool),
|
||||
(STR, BuiltinType::Str),
|
||||
|
||||
(ISIZE, BuiltinType::Int(IntTy { signedness: Signedness::Signed, bitness: IntBitness::Xsize })),
|
||||
(I8, BuiltinType::Int(IntTy { signedness: Signedness::Signed, bitness: IntBitness::X8 })),
|
||||
(I16, BuiltinType::Int(IntTy { signedness: Signedness::Signed, bitness: IntBitness::X16 })),
|
||||
(I32, BuiltinType::Int(IntTy { signedness: Signedness::Signed, bitness: IntBitness::X32 })),
|
||||
(I64, BuiltinType::Int(IntTy { signedness: Signedness::Signed, bitness: IntBitness::X64 })),
|
||||
(I128, BuiltinType::Int(IntTy { signedness: Signedness::Signed, bitness: IntBitness::X128 })),
|
||||
|
||||
(USIZE, BuiltinType::Int(IntTy { signedness: Signedness::Unsigned, bitness: IntBitness::Xsize })),
|
||||
(U8, BuiltinType::Int(IntTy { signedness: Signedness::Unsigned, bitness: IntBitness::X8 })),
|
||||
(U16, BuiltinType::Int(IntTy { signedness: Signedness::Unsigned, bitness: IntBitness::X16 })),
|
||||
(U32, BuiltinType::Int(IntTy { signedness: Signedness::Unsigned, bitness: IntBitness::X32 })),
|
||||
(U64, BuiltinType::Int(IntTy { signedness: Signedness::Unsigned, bitness: IntBitness::X64 })),
|
||||
(U128, BuiltinType::Int(IntTy { signedness: Signedness::Unsigned, bitness: IntBitness::X128 })),
|
||||
|
||||
(F32, BuiltinType::Float(FloatTy { bitness: FloatBitness::X32 })),
|
||||
(F64, BuiltinType::Float(FloatTy { bitness: FloatBitness::X64 })),
|
||||
];
|
||||
pub(crate) id: ModuleId,
|
||||
}
|
||||
|
||||
/// The defs which can be visible in the module.
|
||||
@@ -148,39 +108,19 @@ pub enum ModuleDef {
|
||||
BuiltinType
|
||||
);
|
||||
|
||||
pub enum ModuleSource {
|
||||
SourceFile(ast::SourceFile),
|
||||
Module(ast::Module),
|
||||
}
|
||||
|
||||
impl ModuleSource {
|
||||
pub(crate) fn new(
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
file_id: Option<FileId>,
|
||||
decl_id: Option<AstId<ast::Module>>,
|
||||
) -> ModuleSource {
|
||||
match (file_id, decl_id) {
|
||||
(Some(file_id), _) => {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
(None, Some(item_id)) => {
|
||||
let module = item_id.to_node(db);
|
||||
assert!(module.item_list().is_some(), "expected inline module");
|
||||
ModuleSource::Module(module)
|
||||
}
|
||||
(None, None) => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use hir_def::ModuleSource;
|
||||
|
||||
impl Module {
|
||||
pub(crate) fn new(krate: Crate, crate_module_id: CrateModuleId) -> Module {
|
||||
Module { id: ModuleId { krate: krate.crate_id, module_id: crate_module_id } }
|
||||
}
|
||||
|
||||
/// Name of this module.
|
||||
pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
|
||||
let def_map = db.crate_def_map(self.krate);
|
||||
let parent = def_map[self.module_id].parent?;
|
||||
let def_map = db.crate_def_map(self.krate());
|
||||
let parent = def_map[self.id.module_id].parent?;
|
||||
def_map[parent].children.iter().find_map(|(name, module_id)| {
|
||||
if *module_id == self.module_id {
|
||||
if *module_id == self.id.module_id {
|
||||
Some(name.clone())
|
||||
} else {
|
||||
None
|
||||
@@ -200,29 +140,29 @@ pub fn import_source(
|
||||
}
|
||||
|
||||
/// Returns the crate this module is part of.
|
||||
pub fn krate(self, _db: &impl DefDatabase) -> Option<Crate> {
|
||||
Some(self.krate)
|
||||
pub fn krate(self) -> Crate {
|
||||
Crate { crate_id: self.id.krate }
|
||||
}
|
||||
|
||||
/// Topmost parent of this module. Every module has a `crate_root`, but some
|
||||
/// might be missing `krate`. This can happen if a module's file is not included
|
||||
/// in the module tree of any target in `Cargo.toml`.
|
||||
pub fn crate_root(self, db: &impl DefDatabase) -> Module {
|
||||
let def_map = db.crate_def_map(self.krate);
|
||||
let def_map = db.crate_def_map(self.krate());
|
||||
self.with_module_id(def_map.root())
|
||||
}
|
||||
|
||||
/// Finds a child module with the specified name.
|
||||
pub fn child(self, db: &impl HirDatabase, name: &Name) -> Option<Module> {
|
||||
let def_map = db.crate_def_map(self.krate);
|
||||
let child_id = def_map[self.module_id].children.get(name)?;
|
||||
let def_map = db.crate_def_map(self.krate());
|
||||
let child_id = def_map[self.id.module_id].children.get(name)?;
|
||||
Some(self.with_module_id(*child_id))
|
||||
}
|
||||
|
||||
/// Iterates over all child modules.
|
||||
pub fn children(self, db: &impl DefDatabase) -> impl Iterator<Item = Module> {
|
||||
let def_map = db.crate_def_map(self.krate);
|
||||
let children = def_map[self.module_id]
|
||||
let def_map = db.crate_def_map(self.krate());
|
||||
let children = def_map[self.id.module_id]
|
||||
.children
|
||||
.iter()
|
||||
.map(|(_, module_id)| self.with_module_id(*module_id))
|
||||
@@ -232,8 +172,8 @@ pub fn children(self, db: &impl DefDatabase) -> impl Iterator<Item = Module> {
|
||||
|
||||
/// Finds a parent module.
|
||||
pub fn parent(self, db: &impl DefDatabase) -> Option<Module> {
|
||||
let def_map = db.crate_def_map(self.krate);
|
||||
let parent_id = def_map[self.module_id].parent?;
|
||||
let def_map = db.crate_def_map(self.krate());
|
||||
let parent_id = def_map[self.id.module_id].parent?;
|
||||
Some(self.with_module_id(parent_id))
|
||||
}
|
||||
|
||||
@@ -249,11 +189,11 @@ pub fn path_to_root(self, db: &impl HirDatabase) -> Vec<Module> {
|
||||
|
||||
/// Returns a `ModuleScope`: a set of items, visible in this module.
|
||||
pub fn scope(self, db: &impl HirDatabase) -> ModuleScope {
|
||||
db.crate_def_map(self.krate)[self.module_id].scope.clone()
|
||||
db.crate_def_map(self.krate())[self.id.module_id].scope.clone()
|
||||
}
|
||||
|
||||
pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) {
|
||||
db.crate_def_map(self.krate).add_diagnostics(db, self.module_id, sink);
|
||||
db.crate_def_map(self.krate()).add_diagnostics(db, self.id.module_id, sink);
|
||||
for decl in self.declarations(db) {
|
||||
match decl {
|
||||
crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
|
||||
@@ -277,13 +217,13 @@ pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) {
|
||||
}
|
||||
|
||||
pub(crate) fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
||||
let def_map = db.crate_def_map(self.krate);
|
||||
Resolver::default().push_module_scope(def_map, self.module_id)
|
||||
let def_map = db.crate_def_map(self.krate());
|
||||
Resolver::default().push_module_scope(def_map, self.id.module_id)
|
||||
}
|
||||
|
||||
pub fn declarations(self, db: &impl DefDatabase) -> Vec<ModuleDef> {
|
||||
let def_map = db.crate_def_map(self.krate);
|
||||
def_map[self.module_id]
|
||||
let def_map = db.crate_def_map(self.krate());
|
||||
def_map[self.id.module_id]
|
||||
.scope
|
||||
.entries()
|
||||
.filter_map(|(_name, res)| if res.import.is_none() { Some(res.def) } else { None })
|
||||
@@ -303,7 +243,7 @@ pub fn impl_blocks(self, db: &impl DefDatabase) -> Vec<ImplBlock> {
|
||||
}
|
||||
|
||||
fn with_module_id(self, module_id: CrateModuleId) -> Module {
|
||||
Module { module_id, krate: self.krate }
|
||||
Module::new(self.krate(), module_id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,11 +280,11 @@ pub struct Struct {
|
||||
|
||||
impl Struct {
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
self.id.module(db)
|
||||
Module { id: self.id.module(db) }
|
||||
}
|
||||
|
||||
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
|
||||
self.module(db).krate(db)
|
||||
Some(self.module(db).krate())
|
||||
}
|
||||
|
||||
pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
|
||||
@@ -402,7 +342,7 @@ pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
|
||||
}
|
||||
|
||||
pub fn module(self, db: &impl HirDatabase) -> Module {
|
||||
self.id.module(db)
|
||||
Module { id: self.id.module(db) }
|
||||
}
|
||||
|
||||
pub fn ty(self, db: &impl HirDatabase) -> Ty {
|
||||
@@ -428,11 +368,11 @@ pub struct Enum {
|
||||
|
||||
impl Enum {
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
self.id.module(db)
|
||||
Module { id: self.id.module(db) }
|
||||
}
|
||||
|
||||
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
|
||||
self.module(db).krate(db)
|
||||
Some(self.module(db).krate())
|
||||
}
|
||||
|
||||
pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
|
||||
@@ -470,7 +410,7 @@ pub(crate) fn resolver(self, db: &impl HirDatabase) -> Resolver {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct EnumVariant {
|
||||
pub(crate) parent: Enum,
|
||||
pub(crate) id: EnumVariantId,
|
||||
pub(crate) id: LocalEnumVariantId,
|
||||
}
|
||||
|
||||
impl EnumVariant {
|
||||
@@ -523,12 +463,14 @@ pub fn ty(self, db: &impl HirDatabase) -> Ty {
|
||||
}
|
||||
|
||||
pub fn krate(self, db: &impl HirDatabase) -> Option<Crate> {
|
||||
match self {
|
||||
Adt::Struct(s) => s.module(db),
|
||||
Adt::Union(s) => s.module(db),
|
||||
Adt::Enum(e) => e.module(db),
|
||||
}
|
||||
.krate(db)
|
||||
Some(
|
||||
match self {
|
||||
Adt::Struct(s) => s.module(db),
|
||||
Adt::Union(s) => s.module(db),
|
||||
Adt::Enum(e) => e.module(db),
|
||||
}
|
||||
.krate(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn resolver(self, db: &impl HirDatabase) -> Resolver {
|
||||
@@ -643,7 +585,7 @@ pub(crate) fn fn_data_query(
|
||||
let self_type = if let Some(type_ref) = self_param.ascribed_type() {
|
||||
TypeRef::from_ast(type_ref)
|
||||
} else {
|
||||
let self_type = TypeRef::Path(SELF_TYPE.into());
|
||||
let self_type = TypeRef::Path(name::SELF_TYPE.into());
|
||||
match self_param.kind() {
|
||||
ast::SelfParamKind::Owned => self_type,
|
||||
ast::SelfParamKind::Ref => {
|
||||
@@ -692,11 +634,11 @@ pub fn has_self_param(&self) -> bool {
|
||||
|
||||
impl Function {
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
self.id.module(db)
|
||||
Module { id: self.id.module(db) }
|
||||
}
|
||||
|
||||
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
|
||||
self.module(db).krate(db)
|
||||
Some(self.module(db).krate())
|
||||
}
|
||||
|
||||
pub fn name(self, db: &impl HirDatabase) -> Name {
|
||||
@@ -770,11 +712,11 @@ pub struct Const {
|
||||
|
||||
impl Const {
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
self.id.module(db)
|
||||
Module { id: self.id.module(db) }
|
||||
}
|
||||
|
||||
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
|
||||
self.module(db).krate(db)
|
||||
Some(self.module(db).krate())
|
||||
}
|
||||
|
||||
pub fn data(self, db: &impl HirDatabase) -> Arc<ConstData> {
|
||||
@@ -867,11 +809,11 @@ pub struct Static {
|
||||
|
||||
impl Static {
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
self.id.module(db)
|
||||
Module { id: self.id.module(db) }
|
||||
}
|
||||
|
||||
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
|
||||
self.module(db).krate(db)
|
||||
Some(self.module(db).krate())
|
||||
}
|
||||
|
||||
pub fn data(self, db: &impl HirDatabase) -> Arc<ConstData> {
|
||||
@@ -896,7 +838,7 @@ pub struct Trait {
|
||||
|
||||
impl Trait {
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
self.id.module(db)
|
||||
Module { id: self.id.module(db) }
|
||||
}
|
||||
|
||||
pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
|
||||
@@ -917,9 +859,7 @@ fn direct_super_traits(self, db: &impl HirDatabase) -> Vec<Trait> {
|
||||
.where_predicates
|
||||
.iter()
|
||||
.filter_map(|pred| match &pred.type_ref {
|
||||
TypeRef::Path(p) if p.as_ident() == Some(&crate::name::SELF_TYPE) => {
|
||||
pred.bound.as_path()
|
||||
}
|
||||
TypeRef::Path(p) if p.as_ident() == Some(&name::SELF_TYPE) => pred.bound.as_path(),
|
||||
_ => None,
|
||||
})
|
||||
.filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path) {
|
||||
@@ -998,11 +938,11 @@ pub struct TypeAlias {
|
||||
|
||||
impl TypeAlias {
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
self.id.module(db)
|
||||
Module { id: self.id.module(db) }
|
||||
}
|
||||
|
||||
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
|
||||
self.module(db).krate(db)
|
||||
Some(self.module(db).krate())
|
||||
}
|
||||
|
||||
/// The containing impl block, if this is a method.
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode},
|
||||
SyntaxNode,
|
||||
};
|
||||
use ra_syntax::ast::{self, AstNode};
|
||||
|
||||
use crate::{
|
||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
@@ -12,34 +9,21 @@
|
||||
ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Source<T> {
|
||||
pub file_id: HirFileId,
|
||||
pub ast: T,
|
||||
}
|
||||
pub use hir_def::Source;
|
||||
|
||||
pub trait HasSource {
|
||||
type Ast;
|
||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<Self::Ast>;
|
||||
}
|
||||
|
||||
impl<T> Source<T> {
|
||||
pub(crate) fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||
Source { file_id: self.file_id, ast: f(self.ast) }
|
||||
}
|
||||
pub(crate) fn file_syntax(&self, db: &impl AstDatabase) -> SyntaxNode {
|
||||
db.parse_or_expand(self.file_id).expect("source created from invalid file")
|
||||
}
|
||||
}
|
||||
|
||||
/// NB: Module is !HasSource, because it has two source nodes at the same time:
|
||||
/// definition and declaration.
|
||||
impl Module {
|
||||
/// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
|
||||
pub fn definition_source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ModuleSource> {
|
||||
let def_map = db.crate_def_map(self.krate);
|
||||
let decl_id = def_map[self.module_id].declaration;
|
||||
let file_id = def_map[self.module_id].definition;
|
||||
let def_map = db.crate_def_map(self.krate());
|
||||
let decl_id = def_map[self.id.module_id].declaration;
|
||||
let file_id = def_map[self.id.module_id].definition;
|
||||
let ast = ModuleSource::new(db, file_id, decl_id);
|
||||
let file_id = file_id.map(HirFileId::from).unwrap_or_else(|| decl_id.unwrap().file_id());
|
||||
Source { file_id, ast }
|
||||
@@ -51,8 +35,8 @@ pub fn declaration_source(
|
||||
self,
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
) -> Option<Source<ast::Module>> {
|
||||
let def_map = db.crate_def_map(self.krate);
|
||||
let decl = def_map[self.module_id].declaration?;
|
||||
let def_map = db.crate_def_map(self.krate());
|
||||
let decl = def_map[self.id.module_id].declaration?;
|
||||
let ast = decl.to_node(db);
|
||||
Some(Source { file_id: decl.file_id(), ast })
|
||||
}
|
||||
|
||||
+20
-74
@@ -2,8 +2,8 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use ra_db::{salsa, SourceDatabase};
|
||||
use ra_syntax::{ast, Parse, SmolStr, SyntaxNode};
|
||||
use ra_db::salsa;
|
||||
use ra_syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
adt::{EnumData, StructData},
|
||||
@@ -12,81 +12,30 @@
|
||||
ids,
|
||||
impl_block::{ImplBlock, ImplSourceMap, ModuleImplBlocks},
|
||||
lang_item::{LangItemTarget, LangItems},
|
||||
nameres::{CrateDefMap, ImportSourceMap, Namespace, RawItems},
|
||||
nameres::{CrateDefMap, Namespace},
|
||||
traits::TraitData,
|
||||
ty::{
|
||||
method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate,
|
||||
InferenceResult, Substs, Ty, TypableDef, TypeCtor,
|
||||
},
|
||||
type_alias::TypeAliasData,
|
||||
AstIdMap, Const, ConstData, Crate, DefWithBody, Enum, ErasedFileAstId, ExprScopes, FnData,
|
||||
Function, HirFileId, MacroCallLoc, MacroDefId, Module, Static, Struct, StructField, Trait,
|
||||
TypeAlias,
|
||||
Const, ConstData, Crate, DefWithBody, Enum, ExprScopes, FnData, Function, Module, Static,
|
||||
Struct, StructField, Trait, TypeAlias,
|
||||
};
|
||||
|
||||
/// We store all interned things in the single QueryGroup.
|
||||
///
|
||||
/// This is done mainly to allow both "volatile" `AstDatabase` and "stable"
|
||||
/// `DefDatabase` to access macros, without adding hard dependencies between the
|
||||
/// two.
|
||||
#[salsa::query_group(InternDatabaseStorage)]
|
||||
pub trait InternDatabase: SourceDatabase {
|
||||
#[salsa::interned]
|
||||
fn intern_macro(&self, macro_call: MacroCallLoc) -> ids::MacroCallId;
|
||||
#[salsa::interned]
|
||||
fn intern_function(&self, loc: ids::ItemLoc<ast::FnDef>) -> ids::FunctionId;
|
||||
#[salsa::interned]
|
||||
fn intern_struct(&self, loc: ids::ItemLoc<ast::StructDef>) -> ids::StructId;
|
||||
#[salsa::interned]
|
||||
fn intern_enum(&self, loc: ids::ItemLoc<ast::EnumDef>) -> ids::EnumId;
|
||||
#[salsa::interned]
|
||||
fn intern_const(&self, loc: ids::ItemLoc<ast::ConstDef>) -> ids::ConstId;
|
||||
#[salsa::interned]
|
||||
fn intern_static(&self, loc: ids::ItemLoc<ast::StaticDef>) -> ids::StaticId;
|
||||
#[salsa::interned]
|
||||
fn intern_trait(&self, loc: ids::ItemLoc<ast::TraitDef>) -> ids::TraitId;
|
||||
#[salsa::interned]
|
||||
fn intern_type_alias(&self, loc: ids::ItemLoc<ast::TypeAliasDef>) -> ids::TypeAliasId;
|
||||
|
||||
// Interned IDs for Chalk integration
|
||||
#[salsa::interned]
|
||||
fn intern_type_ctor(&self, type_ctor: TypeCtor) -> ids::TypeCtorId;
|
||||
#[salsa::interned]
|
||||
fn intern_impl(&self, impl_: Impl) -> ids::GlobalImplId;
|
||||
}
|
||||
|
||||
/// This database has access to source code, so queries here are not really
|
||||
/// incremental.
|
||||
#[salsa::query_group(AstDatabaseStorage)]
|
||||
pub trait AstDatabase: InternDatabase {
|
||||
#[salsa::invoke(crate::source_id::AstIdMap::ast_id_map_query)]
|
||||
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
|
||||
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(crate::source_id::AstIdMap::file_item_query)]
|
||||
fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> SyntaxNode;
|
||||
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(crate::ids::HirFileId::parse_or_expand_query)]
|
||||
fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
|
||||
|
||||
#[salsa::invoke(crate::ids::HirFileId::parse_macro_query)]
|
||||
fn parse_macro(&self, macro_file: ids::MacroFile) -> Option<Parse<SyntaxNode>>;
|
||||
|
||||
#[salsa::invoke(crate::ids::macro_def_query)]
|
||||
fn macro_def(&self, macro_id: MacroDefId) -> Option<Arc<mbe::MacroRules>>;
|
||||
|
||||
#[salsa::invoke(crate::ids::macro_arg_query)]
|
||||
fn macro_arg(&self, macro_call: ids::MacroCallId) -> Option<Arc<tt::Subtree>>;
|
||||
|
||||
#[salsa::invoke(crate::ids::macro_expand_query)]
|
||||
fn macro_expand(&self, macro_call: ids::MacroCallId) -> Result<Arc<tt::Subtree>, String>;
|
||||
}
|
||||
pub use hir_def::db::{
|
||||
DefDatabase2, DefDatabase2Storage, InternDatabase, InternDatabaseStorage, RawItemsQuery,
|
||||
RawItemsWithSourceMapQuery,
|
||||
};
|
||||
pub use hir_expand::db::{
|
||||
AstDatabase, AstDatabaseStorage, AstIdMapQuery, MacroArgQuery, MacroDefQuery, MacroExpandQuery,
|
||||
ParseMacroQuery,
|
||||
};
|
||||
|
||||
// This database uses `AstDatabase` internally,
|
||||
#[salsa::query_group(DefDatabaseStorage)]
|
||||
#[salsa::requires(AstDatabase)]
|
||||
pub trait DefDatabase: InternDatabase + HirDebugDatabase {
|
||||
pub trait DefDatabase: HirDebugDatabase + DefDatabase2 {
|
||||
#[salsa::invoke(crate::adt::StructData::struct_data_query)]
|
||||
fn struct_data(&self, s: Struct) -> Arc<StructData>;
|
||||
|
||||
@@ -99,15 +48,6 @@ pub trait DefDatabase: InternDatabase + HirDebugDatabase {
|
||||
#[salsa::invoke(crate::traits::TraitItemsIndex::trait_items_index)]
|
||||
fn trait_items_index(&self, module: Module) -> crate::traits::TraitItemsIndex;
|
||||
|
||||
#[salsa::invoke(RawItems::raw_items_with_source_map_query)]
|
||||
fn raw_items_with_source_map(
|
||||
&self,
|
||||
file_id: HirFileId,
|
||||
) -> (Arc<RawItems>, Arc<ImportSourceMap>);
|
||||
|
||||
#[salsa::invoke(RawItems::raw_items_query)]
|
||||
fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
|
||||
|
||||
#[salsa::invoke(CrateDefMap::crate_def_map_query)]
|
||||
fn crate_def_map(&self, krate: Crate) -> Arc<CrateDefMap>;
|
||||
|
||||
@@ -202,6 +142,12 @@ fn body_with_source_map(
|
||||
#[salsa::invoke(crate::ty::traits::trait_solver_query)]
|
||||
fn trait_solver(&self, krate: Crate) -> crate::ty::traits::TraitSolver;
|
||||
|
||||
// Interned IDs for Chalk integration
|
||||
#[salsa::interned]
|
||||
fn intern_type_ctor(&self, type_ctor: TypeCtor) -> ids::TypeCtorId;
|
||||
#[salsa::interned]
|
||||
fn intern_impl(&self, impl_: Impl) -> ids::GlobalImplId;
|
||||
|
||||
#[salsa::invoke(crate::ty::traits::chalk::associated_ty_data_query)]
|
||||
fn associated_ty_data(&self, id: chalk_ir::TypeId) -> Arc<chalk_rust_ir::AssociatedTyDatum>;
|
||||
|
||||
|
||||
@@ -36,12 +36,6 @@ pub fn debug(self, db: &impl HirDebugDatabase) -> impl fmt::Debug + '_ {
|
||||
}
|
||||
}
|
||||
|
||||
impl HirFileId {
|
||||
pub fn debug(self, db: &impl HirDebugDatabase) -> impl fmt::Debug + '_ {
|
||||
debug_fn(move |fmt| db.debug_hir_file_id(self, fmt))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HirDebugHelper: HirDatabase {
|
||||
fn crate_name(&self, _krate: CrateId) -> Option<String> {
|
||||
None
|
||||
|
||||
@@ -6,15 +6,17 @@
|
||||
|
||||
use std::{ops::Index, sync::Arc};
|
||||
|
||||
use hir_def::{
|
||||
path::GenericArgs,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
};
|
||||
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
||||
use ra_syntax::{ast, AstPtr};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
path::GenericArgs,
|
||||
ty::primitive::{UncertainFloatTy, UncertainIntTy},
|
||||
type_ref::{Mutability, TypeRef},
|
||||
DefWithBody, Either, HasSource, Name, Path, Resolver, Source,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir_def::{path::GenericArgs, type_ref::TypeRef};
|
||||
use hir_expand::{
|
||||
hygiene::Hygiene,
|
||||
name::{self, AsName, Name},
|
||||
};
|
||||
use ra_arena::Arena;
|
||||
use ra_syntax::{
|
||||
ast::{
|
||||
@@ -12,11 +17,8 @@
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
name::{AsName, Name, SELF_PARAM},
|
||||
path::GenericArgs,
|
||||
ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy},
|
||||
type_ref::TypeRef,
|
||||
DefWithBody, Either, HirFileId, MacroCallLoc, MacroFileKind, Mutability, Path, Resolver,
|
||||
AstId, DefWithBody, Either, HirFileId, MacroCallLoc, MacroFileKind, Mutability, Path, Resolver,
|
||||
Source,
|
||||
};
|
||||
|
||||
@@ -78,7 +80,7 @@ fn collect(
|
||||
let ptr = AstPtr::new(&self_param);
|
||||
let param_pat = self.alloc_pat(
|
||||
Pat::Bind {
|
||||
name: SELF_PARAM,
|
||||
name: name::SELF_PARAM,
|
||||
mode: BindingAnnotation::Unannotated,
|
||||
subpat: None,
|
||||
},
|
||||
@@ -458,15 +460,14 @@ fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
|
||||
ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
ast::Expr::MacroCall(e) => {
|
||||
let ast_id = self
|
||||
.db
|
||||
.ast_id_map(self.current_file_id)
|
||||
.ast_id(&e)
|
||||
.with_file_id(self.current_file_id);
|
||||
let ast_id = AstId::new(
|
||||
self.current_file_id,
|
||||
self.db.ast_id_map(self.current_file_id).ast_id(&e),
|
||||
);
|
||||
|
||||
if let Some(path) = e.path().and_then(|path| self.parse_path(path)) {
|
||||
if let Some(def) = self.resolver.resolve_path_as_macro(self.db, &path) {
|
||||
let call_id = MacroCallLoc { def: def.id, ast_id }.id(self.db);
|
||||
let call_id = self.db.intern_macro(MacroCallLoc { def: def.id, ast_id });
|
||||
let file_id = call_id.as_file(MacroFileKind::Expr);
|
||||
if let Some(node) = self.db.parse_or_expand(file_id) {
|
||||
if let Some(expr) = ast::Expr::cast(node) {
|
||||
@@ -596,7 +597,8 @@ fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
|
||||
}
|
||||
|
||||
fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
|
||||
Path::from_src(Source { ast: path, file_id: self.current_file_id }, self.db)
|
||||
let hygiene = Hygiene::new(self.db, self.current_file_id);
|
||||
Path::from_src(path, &hygiene)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::path::known;
|
||||
use ra_syntax::ast;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
@@ -9,7 +10,6 @@
|
||||
db::HirDatabase,
|
||||
diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr},
|
||||
expr::AstPtr,
|
||||
path::known,
|
||||
ty::{ApplicationTy, InferenceResult, Ty, TypeCtor},
|
||||
Adt, Function, Name, Path,
|
||||
};
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use ra_db::{FileId, FilePosition};
|
||||
use ra_syntax::{
|
||||
algo::find_node_at_offset,
|
||||
ast::{self, AstNode, NameOwner},
|
||||
SyntaxNode,
|
||||
};
|
||||
use hir_expand::name::AsName;
|
||||
use ra_syntax::ast::{self, AstNode, NameOwner};
|
||||
|
||||
use crate::{
|
||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
ids::{AstItemDef, LocationCtx},
|
||||
name::AsName,
|
||||
Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module,
|
||||
AstId, Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module,
|
||||
ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef,
|
||||
};
|
||||
|
||||
@@ -129,41 +124,6 @@ fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) ->
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: simplify it
|
||||
impl ModuleSource {
|
||||
pub fn from_position(
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
position: FilePosition,
|
||||
) -> ModuleSource {
|
||||
let parse = db.parse(position.file_id);
|
||||
match &find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset) {
|
||||
Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()),
|
||||
_ => {
|
||||
let source_file = parse.tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_child_node(
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
file_id: FileId,
|
||||
child: &SyntaxNode,
|
||||
) -> ModuleSource {
|
||||
if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
|
||||
ModuleSource::Module(m)
|
||||
} else {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file_id(db: &(impl DefDatabase + AstDatabase), file_id: FileId) -> ModuleSource {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn from_declaration(db: &impl HirDatabase, src: Source<ast::Module>) -> Option<Self> {
|
||||
let src_parent = Source {
|
||||
@@ -183,7 +143,7 @@ pub fn from_definition(
|
||||
ModuleSource::Module(ref module) => {
|
||||
assert!(!module.has_semi());
|
||||
let ast_id_map = db.ast_id_map(src.file_id);
|
||||
let item_id = ast_id_map.ast_id(module).with_file_id(src.file_id);
|
||||
let item_id = AstId::new(src.file_id, ast_id_map.ast_id(module));
|
||||
Some(item_id)
|
||||
}
|
||||
ModuleSource::SourceFile(_) => None,
|
||||
@@ -195,7 +155,7 @@ pub fn from_definition(
|
||||
.find_map(|krate| {
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let module_id = def_map.find_module_by_source(src.file_id, decl_id)?;
|
||||
Some(Module { krate, module_id })
|
||||
Some(Module::new(krate, module_id))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -208,6 +168,6 @@ fn from_source<N, DEF>(db: &(impl DefDatabase + AstDatabase), src: Source<N>) ->
|
||||
let module_src =
|
||||
crate::ModuleSource::from_child_node(db, src.file_id.original_file(db), &src.ast.syntax());
|
||||
let module = Module::from_definition(db, Source { file_id: src.file_id, ast: module_src })?;
|
||||
let ctx = LocationCtx::new(db, module, src.file_id);
|
||||
let ctx = LocationCtx::new(db, module.id, src.file_id);
|
||||
Some(DEF::from_ast(ctx, &src.ast))
|
||||
}
|
||||
|
||||
@@ -5,15 +5,17 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{
|
||||
path::Path,
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
};
|
||||
use hir_expand::name::{self, AsName};
|
||||
use ra_syntax::ast::{self, DefaultTypeParamOwner, NameOwner, TypeBoundsOwner, TypeParamsOwner};
|
||||
|
||||
use crate::{
|
||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
name::SELF_TYPE,
|
||||
path::Path,
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
Adt, AsName, Const, Container, Enum, EnumVariant, Function, HasSource, ImplBlock, Name, Struct,
|
||||
Trait, TypeAlias, Union,
|
||||
Adt, Const, Container, Enum, EnumVariant, Function, HasSource, ImplBlock, Name, Struct, Trait,
|
||||
TypeAlias, Union,
|
||||
};
|
||||
|
||||
/// Data about a generic parameter (to a function, struct, impl, ...).
|
||||
@@ -94,11 +96,15 @@ pub(crate) fn generic_params_query(
|
||||
GenericDef::Adt(Adt::Enum(it)) => generics.fill(&it.source(db).ast, start),
|
||||
GenericDef::Trait(it) => {
|
||||
// traits get the Self type as an implicit first type parameter
|
||||
generics.params.push(GenericParam { idx: start, name: SELF_TYPE, default: None });
|
||||
generics.params.push(GenericParam {
|
||||
idx: start,
|
||||
name: name::SELF_TYPE,
|
||||
default: None,
|
||||
});
|
||||
generics.fill(&it.source(db).ast, start + 1);
|
||||
// add super traits as bounds on Self
|
||||
// i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
|
||||
let self_param = TypeRef::Path(SELF_TYPE.into());
|
||||
let self_param = TypeRef::Path(name::SELF_TYPE.into());
|
||||
generics.fill_bounds(&it.source(db).ast, self_param);
|
||||
}
|
||||
GenericDef::TypeAlias(it) => generics.fill(&it.source(db).ast, start),
|
||||
|
||||
+12
-349
@@ -1,168 +1,17 @@
|
||||
//! FIXME: write short doc here
|
||||
//! hir makes heavy use of ids: integer (u32) handlers to various things. You
|
||||
//! can think of id as a pointer (but without a lifetime) or a file descriptor
|
||||
//! (but for hir objects).
|
||||
//!
|
||||
//! This module defines a bunch of ids we are using. The most important ones are
|
||||
//! probably `HirFileId` and `DefId`.
|
||||
|
||||
use std::{
|
||||
hash::{Hash, Hasher},
|
||||
sync::Arc,
|
||||
use ra_db::salsa;
|
||||
|
||||
pub use hir_def::{
|
||||
AstItemDef, ConstId, EnumId, FunctionId, ItemLoc, LocationCtx, StaticId, StructId, TraitId,
|
||||
TypeAliasId,
|
||||
};
|
||||
|
||||
use mbe::MacroRules;
|
||||
use ra_db::{salsa, FileId};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{ast, AstNode, Parse, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
db::{AstDatabase, DefDatabase, InternDatabase},
|
||||
AstId, Crate, FileAstId, Module, Source,
|
||||
};
|
||||
|
||||
/// hir makes heavy use of ids: integer (u32) handlers to various things. You
|
||||
/// can think of id as a pointer (but without a lifetime) or a file descriptor
|
||||
/// (but for hir objects).
|
||||
///
|
||||
/// This module defines a bunch of ids we are using. The most important ones are
|
||||
/// probably `HirFileId` and `DefId`.
|
||||
|
||||
/// Input to the analyzer is a set of files, where each file is identified by
|
||||
/// `FileId` and contains source code. However, another source of source code in
|
||||
/// Rust are macros: each macro can be thought of as producing a "temporary
|
||||
/// file". To assign an id to such a file, we use the id of the macro call that
|
||||
/// produced the file. So, a `HirFileId` is either a `FileId` (source code
|
||||
/// written by user), or a `MacroCallId` (source code produced by macro).
|
||||
///
|
||||
/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
|
||||
/// containing the call plus the offset of the macro call in the file. Note that
|
||||
/// this is a recursive definition! However, the size_of of `HirFileId` is
|
||||
/// finite (because everything bottoms out at the real `FileId`) and small
|
||||
/// (`MacroCallId` uses the location interner).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct HirFileId(HirFileIdRepr);
|
||||
|
||||
impl HirFileId {
|
||||
/// For macro-expansion files, returns the file original source file the
|
||||
/// expansion originated from.
|
||||
pub fn original_file(self, db: &impl InternDatabase) -> FileId {
|
||||
match self.0 {
|
||||
HirFileIdRepr::File(file_id) => file_id,
|
||||
HirFileIdRepr::Macro(macro_file) => {
|
||||
let loc = macro_file.macro_call_id.loc(db);
|
||||
loc.ast_id.file_id().original_file(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the crate which the macro lives in, if it is a macro file.
|
||||
pub(crate) fn macro_crate(self, db: &impl AstDatabase) -> Option<Crate> {
|
||||
match self.0 {
|
||||
HirFileIdRepr::File(_) => None,
|
||||
HirFileIdRepr::Macro(macro_file) => {
|
||||
let loc = macro_file.macro_call_id.loc(db);
|
||||
Some(loc.def.krate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_or_expand_query(
|
||||
db: &impl AstDatabase,
|
||||
file_id: HirFileId,
|
||||
) -> Option<SyntaxNode> {
|
||||
match file_id.0 {
|
||||
HirFileIdRepr::File(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
|
||||
HirFileIdRepr::Macro(macro_file) => {
|
||||
db.parse_macro(macro_file).map(|it| it.syntax_node())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_macro_query(
|
||||
db: &impl AstDatabase,
|
||||
macro_file: MacroFile,
|
||||
) -> Option<Parse<SyntaxNode>> {
|
||||
let _p = profile("parse_macro_query");
|
||||
let macro_call_id = macro_file.macro_call_id;
|
||||
let tt = db
|
||||
.macro_expand(macro_call_id)
|
||||
.map_err(|err| {
|
||||
// Note:
|
||||
// The final goal we would like to make all parse_macro success,
|
||||
// such that the following log will not call anyway.
|
||||
log::warn!("fail on macro_parse: (reason: {})", err,);
|
||||
})
|
||||
.ok()?;
|
||||
match macro_file.macro_file_kind {
|
||||
MacroFileKind::Items => mbe::token_tree_to_items(&tt).ok().map(Parse::to_syntax),
|
||||
MacroFileKind::Expr => mbe::token_tree_to_expr(&tt).ok().map(Parse::to_syntax),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum HirFileIdRepr {
|
||||
File(FileId),
|
||||
Macro(MacroFile),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct MacroFile {
|
||||
macro_call_id: MacroCallId,
|
||||
macro_file_kind: MacroFileKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) enum MacroFileKind {
|
||||
Items,
|
||||
Expr,
|
||||
}
|
||||
|
||||
impl From<FileId> for HirFileId {
|
||||
fn from(file_id: FileId) -> HirFileId {
|
||||
HirFileId(HirFileIdRepr::File(file_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct MacroDefId {
|
||||
pub(crate) ast_id: AstId<ast::MacroCall>,
|
||||
pub(crate) krate: Crate,
|
||||
}
|
||||
|
||||
pub(crate) fn macro_def_query(db: &impl AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
|
||||
let macro_call = id.ast_id.to_node(db);
|
||||
let arg = macro_call.token_tree()?;
|
||||
let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| {
|
||||
log::warn!("fail on macro_def to token tree: {:#?}", arg);
|
||||
None
|
||||
})?;
|
||||
let rules = MacroRules::parse(&tt).ok().or_else(|| {
|
||||
log::warn!("fail on macro_def parse: {:#?}", tt);
|
||||
None
|
||||
})?;
|
||||
Some(Arc::new(rules))
|
||||
}
|
||||
|
||||
pub(crate) fn macro_arg_query(db: &impl AstDatabase, id: MacroCallId) -> Option<Arc<tt::Subtree>> {
|
||||
let loc = id.loc(db);
|
||||
let macro_call = loc.ast_id.to_node(db);
|
||||
let arg = macro_call.token_tree()?;
|
||||
let (tt, _) = mbe::ast_to_token_tree(&arg)?;
|
||||
Some(Arc::new(tt))
|
||||
}
|
||||
|
||||
pub(crate) fn macro_expand_query(
|
||||
db: &impl AstDatabase,
|
||||
id: MacroCallId,
|
||||
) -> Result<Arc<tt::Subtree>, String> {
|
||||
let loc = id.loc(db);
|
||||
let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
|
||||
|
||||
let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
|
||||
let tt = macro_rules.expand(¯o_arg).map_err(|err| format!("{:?}", err))?;
|
||||
// Set a hard limit for the expanded tt
|
||||
let count = tt.count();
|
||||
if count > 65536 {
|
||||
return Err(format!("Total tokens count exceed limit : count = {}", count));
|
||||
}
|
||||
Ok(Arc::new(tt))
|
||||
}
|
||||
pub use hir_expand::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, MacroFileKind};
|
||||
|
||||
macro_rules! impl_intern_key {
|
||||
($name:ident) => {
|
||||
@@ -177,192 +26,6 @@ fn as_intern_id(&self) -> salsa::InternId {
|
||||
};
|
||||
}
|
||||
|
||||
/// `MacroCallId` identifies a particular macro invocation, like
|
||||
/// `println!("Hello, {}", world)`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct MacroCallId(salsa::InternId);
|
||||
impl_intern_key!(MacroCallId);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MacroCallLoc {
|
||||
pub(crate) def: MacroDefId,
|
||||
pub(crate) ast_id: AstId<ast::MacroCall>,
|
||||
}
|
||||
|
||||
impl MacroCallId {
|
||||
pub(crate) fn loc(self, db: &impl InternDatabase) -> MacroCallLoc {
|
||||
db.lookup_intern_macro(self)
|
||||
}
|
||||
|
||||
pub(crate) fn as_file(self, kind: MacroFileKind) -> HirFileId {
|
||||
let macro_file = MacroFile { macro_call_id: self, macro_file_kind: kind };
|
||||
HirFileId(HirFileIdRepr::Macro(macro_file))
|
||||
}
|
||||
}
|
||||
|
||||
impl MacroCallLoc {
|
||||
pub(crate) fn id(self, db: &impl InternDatabase) -> MacroCallId {
|
||||
db.intern_macro(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ItemLoc<N: AstNode> {
|
||||
pub(crate) module: Module,
|
||||
ast_id: AstId<N>,
|
||||
}
|
||||
|
||||
impl<N: AstNode> PartialEq for ItemLoc<N> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.module == other.module && self.ast_id == other.ast_id
|
||||
}
|
||||
}
|
||||
impl<N: AstNode> Eq for ItemLoc<N> {}
|
||||
impl<N: AstNode> Hash for ItemLoc<N> {
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.module.hash(hasher);
|
||||
self.ast_id.hash(hasher);
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> Clone for ItemLoc<N> {
|
||||
fn clone(&self) -> ItemLoc<N> {
|
||||
ItemLoc { module: self.module, ast_id: self.ast_id }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct LocationCtx<DB> {
|
||||
db: DB,
|
||||
module: Module,
|
||||
file_id: HirFileId,
|
||||
}
|
||||
|
||||
impl<'a, DB: DefDatabase> LocationCtx<&'a DB> {
|
||||
pub(crate) fn new(db: &'a DB, module: Module, file_id: HirFileId) -> LocationCtx<&'a DB> {
|
||||
LocationCtx { db, module, file_id }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, DB: DefDatabase + AstDatabase> LocationCtx<&'a DB> {
|
||||
pub(crate) fn to_def<N, DEF>(self, ast: &N) -> DEF
|
||||
where
|
||||
N: AstNode,
|
||||
DEF: AstItemDef<N>,
|
||||
{
|
||||
DEF::from_ast(self, ast)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait AstItemDef<N: AstNode>: salsa::InternKey + Clone {
|
||||
fn intern(db: &impl DefDatabase, loc: ItemLoc<N>) -> Self;
|
||||
fn lookup_intern(self, db: &impl DefDatabase) -> ItemLoc<N>;
|
||||
|
||||
fn from_ast(ctx: LocationCtx<&(impl AstDatabase + DefDatabase)>, ast: &N) -> Self {
|
||||
let items = ctx.db.ast_id_map(ctx.file_id);
|
||||
let item_id = items.ast_id(ast);
|
||||
Self::from_ast_id(ctx, item_id)
|
||||
}
|
||||
fn from_ast_id(ctx: LocationCtx<&impl DefDatabase>, ast_id: FileAstId<N>) -> Self {
|
||||
let loc = ItemLoc { module: ctx.module, ast_id: ast_id.with_file_id(ctx.file_id) };
|
||||
Self::intern(ctx.db, loc)
|
||||
}
|
||||
fn source(self, db: &(impl AstDatabase + DefDatabase)) -> Source<N> {
|
||||
let loc = self.lookup_intern(db);
|
||||
let ast = loc.ast_id.to_node(db);
|
||||
Source { file_id: loc.ast_id.file_id(), ast }
|
||||
}
|
||||
fn module(self, db: &impl DefDatabase) -> Module {
|
||||
let loc = self.lookup_intern(db);
|
||||
loc.module
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct FunctionId(salsa::InternId);
|
||||
impl_intern_key!(FunctionId);
|
||||
|
||||
impl AstItemDef<ast::FnDef> for FunctionId {
|
||||
fn intern(db: &impl DefDatabase, loc: ItemLoc<ast::FnDef>) -> Self {
|
||||
db.intern_function(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl DefDatabase) -> ItemLoc<ast::FnDef> {
|
||||
db.lookup_intern_function(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct StructId(salsa::InternId);
|
||||
impl_intern_key!(StructId);
|
||||
impl AstItemDef<ast::StructDef> for StructId {
|
||||
fn intern(db: &impl DefDatabase, loc: ItemLoc<ast::StructDef>) -> Self {
|
||||
db.intern_struct(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl DefDatabase) -> ItemLoc<ast::StructDef> {
|
||||
db.lookup_intern_struct(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct EnumId(salsa::InternId);
|
||||
impl_intern_key!(EnumId);
|
||||
impl AstItemDef<ast::EnumDef> for EnumId {
|
||||
fn intern(db: &impl DefDatabase, loc: ItemLoc<ast::EnumDef>) -> Self {
|
||||
db.intern_enum(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl DefDatabase) -> ItemLoc<ast::EnumDef> {
|
||||
db.lookup_intern_enum(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ConstId(salsa::InternId);
|
||||
impl_intern_key!(ConstId);
|
||||
impl AstItemDef<ast::ConstDef> for ConstId {
|
||||
fn intern(db: &impl DefDatabase, loc: ItemLoc<ast::ConstDef>) -> Self {
|
||||
db.intern_const(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl DefDatabase) -> ItemLoc<ast::ConstDef> {
|
||||
db.lookup_intern_const(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct StaticId(salsa::InternId);
|
||||
impl_intern_key!(StaticId);
|
||||
impl AstItemDef<ast::StaticDef> for StaticId {
|
||||
fn intern(db: &impl DefDatabase, loc: ItemLoc<ast::StaticDef>) -> Self {
|
||||
db.intern_static(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl DefDatabase) -> ItemLoc<ast::StaticDef> {
|
||||
db.lookup_intern_static(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TraitId(salsa::InternId);
|
||||
impl_intern_key!(TraitId);
|
||||
impl AstItemDef<ast::TraitDef> for TraitId {
|
||||
fn intern(db: &impl DefDatabase, loc: ItemLoc<ast::TraitDef>) -> Self {
|
||||
db.intern_trait(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl DefDatabase) -> ItemLoc<ast::TraitDef> {
|
||||
db.lookup_intern_trait(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TypeAliasId(salsa::InternId);
|
||||
impl_intern_key!(TypeAliasId);
|
||||
impl AstItemDef<ast::TypeAliasDef> for TypeAliasId {
|
||||
fn intern(db: &impl DefDatabase, loc: ItemLoc<ast::TypeAliasDef>) -> Self {
|
||||
db.intern_type_alias(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl DefDatabase) -> ItemLoc<ast::TypeAliasDef> {
|
||||
db.lookup_intern_type_alias(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// This exists just for Chalk, because Chalk just has a single `StructId` where
|
||||
/// we have different kinds of ADTs, primitive types and special type
|
||||
/// constructors like tuples and function pointers.
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{attr::Attr, type_ref::TypeRef};
|
||||
use hir_expand::hygiene::Hygiene;
|
||||
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_syntax::{
|
||||
@@ -11,7 +13,6 @@
|
||||
};
|
||||
|
||||
use crate::{
|
||||
attr::Attr,
|
||||
code_model::{Module, ModuleSource},
|
||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
generics::HasGenericParams,
|
||||
@@ -19,8 +20,7 @@
|
||||
ids::MacroCallLoc,
|
||||
resolve::Resolver,
|
||||
ty::Ty,
|
||||
type_ref::TypeRef,
|
||||
AssocItem, Const, Function, HasSource, HirFileId, MacroFileKind, Path, Source, TraitRef,
|
||||
AssocItem, AstId, Const, Function, HasSource, HirFileId, MacroFileKind, Path, Source, TraitRef,
|
||||
TypeAlias,
|
||||
};
|
||||
|
||||
@@ -129,7 +129,7 @@ pub(crate) fn from_ast(
|
||||
) -> Self {
|
||||
let target_trait = node.target_trait().map(TypeRef::from_ast);
|
||||
let target_type = TypeRef::from_ast_opt(node.target_type());
|
||||
let ctx = LocationCtx::new(db, module, file_id);
|
||||
let ctx = LocationCtx::new(db, module.id, file_id);
|
||||
let negative = node.is_negative();
|
||||
let items = if let Some(item_list) = node.item_list() {
|
||||
item_list
|
||||
@@ -182,7 +182,7 @@ pub(crate) fn impls_in_module_with_source_map_query(
|
||||
) -> (Arc<ModuleImplBlocks>, Arc<ImplSourceMap>) {
|
||||
let mut source_map = ImplSourceMap::default();
|
||||
let crate_graph = db.crate_graph();
|
||||
let cfg_options = crate_graph.cfg_options(module.krate.crate_id());
|
||||
let cfg_options = crate_graph.cfg_options(module.id.krate);
|
||||
|
||||
let result = ModuleImplBlocks::collect(db, cfg_options, module, &mut source_map);
|
||||
(Arc::new(result), Arc::new(source_map))
|
||||
@@ -228,10 +228,11 @@ fn collect_from_item_owner(
|
||||
owner: &dyn ast::ModuleItemOwner,
|
||||
file_id: HirFileId,
|
||||
) {
|
||||
let hygiene = Hygiene::new(db, file_id);
|
||||
for item in owner.items_with_macros() {
|
||||
match item {
|
||||
ast::ItemOrMacro::Item(ast::ModuleItem::ImplBlock(impl_block_ast)) => {
|
||||
let attrs = Attr::from_attrs_owner(file_id, &impl_block_ast, db);
|
||||
let attrs = Attr::from_attrs_owner(&impl_block_ast, &hygiene);
|
||||
if attrs.map_or(false, |attrs| {
|
||||
attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false))
|
||||
}) {
|
||||
@@ -248,7 +249,7 @@ fn collect_from_item_owner(
|
||||
}
|
||||
ast::ItemOrMacro::Item(_) => (),
|
||||
ast::ItemOrMacro::Macro(macro_call) => {
|
||||
let attrs = Attr::from_attrs_owner(file_id, ¯o_call, db);
|
||||
let attrs = Attr::from_attrs_owner(¯o_call, &hygiene);
|
||||
if attrs.map_or(false, |attrs| {
|
||||
attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false))
|
||||
}) {
|
||||
@@ -256,14 +257,13 @@ fn collect_from_item_owner(
|
||||
}
|
||||
|
||||
//FIXME: we should really cut down on the boilerplate required to process a macro
|
||||
let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id);
|
||||
if let Some(path) = macro_call
|
||||
.path()
|
||||
.and_then(|path| Path::from_src(Source { ast: path, file_id }, db))
|
||||
let ast_id = AstId::new(file_id, db.ast_id_map(file_id).ast_id(¯o_call));
|
||||
if let Some(path) =
|
||||
macro_call.path().and_then(|path| Path::from_src(path, &hygiene))
|
||||
{
|
||||
if let Some(def) = self.module.resolver(db).resolve_path_as_macro(db, &path)
|
||||
{
|
||||
let call_id = MacroCallLoc { def: def.id, ast_id }.id(db);
|
||||
let call_id = db.intern_macro(MacroCallLoc { def: def.id, ast_id });
|
||||
let file_id = call_id.as_file(MacroFileKind::Items);
|
||||
if let Some(item_list) =
|
||||
db.parse_or_expand(file_id).and_then(ast::MacroItems::cast)
|
||||
|
||||
@@ -22,14 +22,14 @@ pub enum LangItemTarget {
|
||||
|
||||
impl LangItemTarget {
|
||||
pub(crate) fn krate(&self, db: &impl HirDatabase) -> Option<Crate> {
|
||||
match self {
|
||||
LangItemTarget::Enum(e) => e.module(db).krate(db),
|
||||
LangItemTarget::Function(f) => f.module(db).krate(db),
|
||||
LangItemTarget::ImplBlock(i) => i.module().krate(db),
|
||||
LangItemTarget::Static(s) => s.module(db).krate(db),
|
||||
LangItemTarget::Struct(s) => s.module(db).krate(db),
|
||||
LangItemTarget::Trait(t) => t.module(db).krate(db),
|
||||
}
|
||||
Some(match self {
|
||||
LangItemTarget::Enum(e) => e.module(db).krate(),
|
||||
LangItemTarget::Function(f) => f.module(db).krate(),
|
||||
LangItemTarget::ImplBlock(i) => i.module().krate(),
|
||||
LangItemTarget::Static(s) => s.module(db).krate(),
|
||||
LangItemTarget::Struct(s) => s.module(db).krate(),
|
||||
LangItemTarget::Trait(t) => t.module(db).krate(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-24
@@ -26,25 +26,19 @@ fn from(it: $sv) -> $e {
|
||||
}
|
||||
}
|
||||
|
||||
mod either;
|
||||
pub mod debug;
|
||||
|
||||
pub mod db;
|
||||
#[macro_use]
|
||||
pub mod mock;
|
||||
mod path;
|
||||
pub mod source_binder;
|
||||
|
||||
mod source_id;
|
||||
mod ids;
|
||||
mod name;
|
||||
mod nameres;
|
||||
mod adt;
|
||||
mod traits;
|
||||
mod type_alias;
|
||||
mod type_ref;
|
||||
mod ty;
|
||||
mod attr;
|
||||
mod impl_block;
|
||||
mod expr;
|
||||
mod lang_item;
|
||||
@@ -60,37 +54,35 @@ fn from(it: $sv) -> $e {
|
||||
#[cfg(test)]
|
||||
mod marks;
|
||||
|
||||
use crate::{
|
||||
ids::MacroFileKind,
|
||||
name::AsName,
|
||||
resolve::Resolver,
|
||||
source_id::{AstId, FileAstId},
|
||||
};
|
||||
use hir_expand::AstId;
|
||||
|
||||
pub use self::{
|
||||
use crate::{ids::MacroFileKind, resolve::Resolver};
|
||||
|
||||
pub use crate::{
|
||||
adt::VariantDef,
|
||||
either::Either,
|
||||
code_model::{
|
||||
docs::{DocDef, Docs, Documentation},
|
||||
src::{HasBodySource, HasSource, Source},
|
||||
Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
|
||||
EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef,
|
||||
ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
|
||||
},
|
||||
expr::ExprScopes,
|
||||
from_source::FromSource,
|
||||
generics::{GenericDef, GenericParam, GenericParams, HasGenericParams},
|
||||
ids::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile},
|
||||
impl_block::ImplBlock,
|
||||
name::Name,
|
||||
nameres::{ImportId, Namespace, PerNs},
|
||||
path::{Path, PathKind},
|
||||
resolve::ScopeDef,
|
||||
source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
|
||||
source_id::{AstIdMap, ErasedFileAstId},
|
||||
ty::{
|
||||
display::HirDisplay, ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
},
|
||||
type_ref::Mutability,
|
||||
};
|
||||
|
||||
pub use self::code_model::{
|
||||
docs::{DocDef, Docs, Documentation},
|
||||
src::{HasBodySource, HasSource, Source},
|
||||
Adt, AssocItem, BuiltinType, Const, ConstData, Container, Crate, CrateDependency, DefWithBody,
|
||||
Enum, EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef,
|
||||
ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
|
||||
pub use hir_def::{
|
||||
builtin_type::BuiltinType,
|
||||
path::{Path, PathKind},
|
||||
type_ref::Mutability,
|
||||
};
|
||||
pub use hir_expand::{either::Either, name::Name};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
test_utils::marks!(
|
||||
bogus_paths
|
||||
// FIXME: restore this mark once hir is split
|
||||
name_res_works_for_broken_modules
|
||||
can_import_enum_variant
|
||||
type_var_cycles_resolve_completely
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
db::InternDatabaseStorage,
|
||||
db::AstDatabaseStorage,
|
||||
db::DefDatabaseStorage,
|
||||
db::DefDatabase2Storage,
|
||||
db::HirDatabaseStorage
|
||||
)]
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -48,16 +48,15 @@
|
||||
//! on the result
|
||||
|
||||
mod per_ns;
|
||||
mod raw;
|
||||
mod collector;
|
||||
mod mod_resolution;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{builtin_type::BuiltinType, CrateModuleId};
|
||||
use once_cell::sync::Lazy;
|
||||
use ra_arena::{impl_arena_id, Arena, RawId};
|
||||
use ra_arena::Arena;
|
||||
use ra_db::{Edition, FileId};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::ast;
|
||||
@@ -69,16 +68,12 @@
|
||||
diagnostics::DiagnosticSink,
|
||||
ids::MacroDefId,
|
||||
nameres::diagnostics::DefDiagnostic,
|
||||
Adt, AstId, BuiltinType, Crate, HirFileId, MacroDef, Module, ModuleDef, Name, Path, PathKind,
|
||||
Trait,
|
||||
Adt, AstId, Crate, HirFileId, MacroDef, Module, ModuleDef, Name, Path, PathKind, Trait,
|
||||
};
|
||||
|
||||
pub(crate) use self::raw::{ImportSourceMap, RawItems};
|
||||
pub use self::per_ns::{Namespace, PerNs};
|
||||
|
||||
pub use self::{
|
||||
per_ns::{Namespace, PerNs},
|
||||
raw::ImportId,
|
||||
};
|
||||
pub use hir_def::nameres::raw::ImportId;
|
||||
|
||||
/// Contains all top-level defs from a macro-expanded crate
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
@@ -115,13 +110,8 @@ fn index(&self, id: CrateModuleId) -> &ModuleData {
|
||||
}
|
||||
}
|
||||
|
||||
/// An ID of a module, **local** to a specific crate
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) struct CrateModuleId(RawId);
|
||||
impl_arena_id!(CrateModuleId);
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct ModuleData {
|
||||
pub struct ModuleData {
|
||||
pub(crate) parent: Option<CrateModuleId>,
|
||||
pub(crate) children: FxHashMap<Name, CrateModuleId>,
|
||||
pub(crate) scope: ModuleScope,
|
||||
@@ -332,10 +322,11 @@ fn resolve_path_fp_with_macro(
|
||||
) -> ResolvePathResult {
|
||||
let mut segments = path.segments.iter().enumerate();
|
||||
let mut curr_per_ns: PerNs = match path.kind {
|
||||
PathKind::DollarCrate(krate) => {
|
||||
PathKind::DollarCrate(crate_id) => {
|
||||
let krate = Crate { crate_id };
|
||||
if krate == self.krate {
|
||||
tested_by!(macro_dollar_crate_self);
|
||||
PerNs::types(Module { krate: self.krate, module_id: self.root }.into())
|
||||
PerNs::types(Module::new(self.krate, self.root).into())
|
||||
} else {
|
||||
match krate.root_module(db) {
|
||||
Some(module) => {
|
||||
@@ -346,12 +337,8 @@ fn resolve_path_fp_with_macro(
|
||||
}
|
||||
}
|
||||
}
|
||||
PathKind::Crate => {
|
||||
PerNs::types(Module { krate: self.krate, module_id: self.root }.into())
|
||||
}
|
||||
PathKind::Self_ => {
|
||||
PerNs::types(Module { krate: self.krate, module_id: original_module }.into())
|
||||
}
|
||||
PathKind::Crate => PerNs::types(Module::new(self.krate, self.root).into()),
|
||||
PathKind::Self_ => PerNs::types(Module::new(self.krate, original_module).into()),
|
||||
// plain import or absolute path in 2015: crate-relative with
|
||||
// fallback to extern prelude (with the simplification in
|
||||
// rust-lang/rust#57745)
|
||||
@@ -377,7 +364,7 @@ fn resolve_path_fp_with_macro(
|
||||
}
|
||||
PathKind::Super => {
|
||||
if let Some(p) = self.modules[original_module].parent {
|
||||
PerNs::types(Module { krate: self.krate, module_id: p }.into())
|
||||
PerNs::types(Module::new(self.krate, p).into())
|
||||
} else {
|
||||
log::debug!("super path in root module");
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||
@@ -419,12 +406,12 @@ fn resolve_path_fp_with_macro(
|
||||
|
||||
curr_per_ns = match curr {
|
||||
ModuleDef::Module(module) => {
|
||||
if module.krate != self.krate {
|
||||
if module.krate() != self.krate {
|
||||
let path =
|
||||
Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
|
||||
log::debug!("resolving {:?} in other crate", path);
|
||||
let defp_map = db.crate_def_map(module.krate);
|
||||
let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
|
||||
let defp_map = db.crate_def_map(module.krate());
|
||||
let (def, s) = defp_map.resolve_path(db, module.id.module_id, &path);
|
||||
return ResolvePathResult::with(
|
||||
def,
|
||||
ReachedFixedPoint::Yes,
|
||||
@@ -433,7 +420,7 @@ fn resolve_path_fp_with_macro(
|
||||
}
|
||||
|
||||
// Since it is a qualified path here, it should not contains legacy macros
|
||||
match self[module.module_id].scope.get(&segment.name) {
|
||||
match self[module.id.module_id].scope.get(&segment.name) {
|
||||
Some(res) => res.def,
|
||||
_ => {
|
||||
log::debug!("path segment {:?} not found", segment.name);
|
||||
@@ -511,14 +498,14 @@ fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
|
||||
fn resolve_in_prelude(&self, db: &impl DefDatabase, name: &Name) -> PerNs {
|
||||
if let Some(prelude) = self.prelude {
|
||||
let keep;
|
||||
let def_map = if prelude.krate == self.krate {
|
||||
let def_map = if prelude.krate() == self.krate {
|
||||
self
|
||||
} else {
|
||||
// Extend lifetime
|
||||
keep = db.crate_def_map(prelude.krate);
|
||||
keep = db.crate_def_map(prelude.krate());
|
||||
&keep
|
||||
};
|
||||
def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
|
||||
def_map[prelude.id.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
|
||||
} else {
|
||||
PerNs::none()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir_def::{
|
||||
attr::Attr,
|
||||
nameres::{mod_resolution::ModDir, raw},
|
||||
};
|
||||
use hir_expand::name;
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_db::FileId;
|
||||
use ra_syntax::{ast, SmolStr};
|
||||
@@ -7,13 +12,11 @@
|
||||
use test_utils::tested_by;
|
||||
|
||||
use crate::{
|
||||
attr::Attr,
|
||||
db::DefDatabase,
|
||||
ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind},
|
||||
name::MACRO_RULES,
|
||||
nameres::{
|
||||
diagnostics::DefDiagnostic, mod_resolution::ModDir, raw, Crate, CrateDefMap, CrateModuleId,
|
||||
ModuleData, ModuleDef, PerNs, ReachedFixedPoint, Resolution, ResolveMode,
|
||||
diagnostics::DefDiagnostic, Crate, CrateDefMap, CrateModuleId, ModuleData, ModuleDef,
|
||||
PerNs, ReachedFixedPoint, Resolution, ResolveMode,
|
||||
},
|
||||
Adt, AstId, Const, Enum, Function, HirFileId, MacroDef, Module, Name, Path, PathKind, Static,
|
||||
Struct, Trait, TypeAlias, Union,
|
||||
@@ -212,7 +215,7 @@ fn import_macros_from_extern_crate(
|
||||
|
||||
if let Some(ModuleDef::Module(m)) = res.take_types() {
|
||||
tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||
self.import_all_macros_exported(current_module_id, m.krate);
|
||||
self.import_all_macros_exported(current_module_id, m.krate());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,11 +292,11 @@ fn record_resolved_import(
|
||||
if import.is_prelude {
|
||||
tested_by!(std_prelude);
|
||||
self.def_map.prelude = Some(m);
|
||||
} else if m.krate != self.def_map.krate {
|
||||
} else if m.krate() != self.def_map.krate {
|
||||
tested_by!(glob_across_crates);
|
||||
// glob import from other crate => we can just import everything once
|
||||
let item_map = self.db.crate_def_map(m.krate);
|
||||
let scope = &item_map[m.module_id].scope;
|
||||
let item_map = self.db.crate_def_map(m.krate());
|
||||
let scope = &item_map[m.id.module_id].scope;
|
||||
|
||||
// Module scoped macros is included
|
||||
let items = scope
|
||||
@@ -307,7 +310,7 @@ fn record_resolved_import(
|
||||
// glob import from same crate => we do an initial
|
||||
// import, and then need to propagate any further
|
||||
// additions
|
||||
let scope = &self.def_map[m.module_id].scope;
|
||||
let scope = &self.def_map[m.id.module_id].scope;
|
||||
|
||||
// Module scoped macros is included
|
||||
let items = scope
|
||||
@@ -319,7 +322,7 @@ fn record_resolved_import(
|
||||
self.update(module_id, Some(import_id), &items);
|
||||
// record the glob import in case we add further items
|
||||
self.glob_imports
|
||||
.entry(m.module_id)
|
||||
.entry(m.id.module_id)
|
||||
.or_default()
|
||||
.push((module_id, import_id));
|
||||
}
|
||||
@@ -448,7 +451,7 @@ fn resolve_macros(&mut self) -> ReachedFixedPoint {
|
||||
);
|
||||
|
||||
if let Some(def) = resolved_res.resolved_def.get_macros() {
|
||||
let call_id = MacroCallLoc { def: def.id, ast_id: *ast_id }.id(self.db);
|
||||
let call_id = self.db.intern_macro(MacroCallLoc { def: def.id, ast_id: *ast_id });
|
||||
resolved.push((*module_id, call_id, def.id));
|
||||
res = ReachedFixedPoint::No;
|
||||
return false;
|
||||
@@ -523,9 +526,10 @@ fn collect(&mut self, items: &[raw::RawItem]) {
|
||||
|
||||
// Prelude module is always considered to be `#[macro_use]`.
|
||||
if let Some(prelude_module) = self.def_collector.def_map.prelude {
|
||||
if prelude_module.krate != self.def_collector.def_map.krate {
|
||||
if prelude_module.krate() != self.def_collector.def_map.krate {
|
||||
tested_by!(prelude_is_macro_use);
|
||||
self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
|
||||
self.def_collector
|
||||
.import_all_macros_exported(self.module_id, prelude_module.krate());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,7 +571,7 @@ fn collect_module(&mut self, module: &raw::ModuleData, attrs: &[Attr]) {
|
||||
// inline module, just recurse
|
||||
raw::ModuleData::Definition { name, items, ast_id } => {
|
||||
let module_id =
|
||||
self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None);
|
||||
self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None);
|
||||
|
||||
ModCollector {
|
||||
def_collector: &mut *self.def_collector,
|
||||
@@ -583,7 +587,7 @@ fn collect_module(&mut self, module: &raw::ModuleData, attrs: &[Attr]) {
|
||||
}
|
||||
// out of line module, resolve, parse and recurse
|
||||
raw::ModuleData::Declaration { name, ast_id } => {
|
||||
let ast_id = ast_id.with_file_id(self.file_id);
|
||||
let ast_id = AstId::new(self.file_id, *ast_id);
|
||||
match self.mod_dir.resolve_declaration(
|
||||
self.def_collector.db,
|
||||
self.file_id,
|
||||
@@ -631,9 +635,7 @@ fn push_child_module(
|
||||
modules[res].scope.legacy_macros = modules[self.module_id].scope.legacy_macros.clone();
|
||||
modules[self.module_id].children.insert(name.clone(), res);
|
||||
let resolution = Resolution {
|
||||
def: PerNs::types(
|
||||
Module { krate: self.def_collector.def_map.krate, module_id: res }.into(),
|
||||
),
|
||||
def: PerNs::types(Module::new(self.def_collector.def_map.krate, res).into()),
|
||||
import: None,
|
||||
};
|
||||
self.def_collector.update(self.module_id, None, &[(name, resolution)]);
|
||||
@@ -641,8 +643,8 @@ fn push_child_module(
|
||||
}
|
||||
|
||||
fn define_def(&mut self, def: &raw::DefData) {
|
||||
let module = Module { krate: self.def_collector.def_map.krate, module_id: self.module_id };
|
||||
let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id);
|
||||
let module = Module::new(self.def_collector.def_map.krate, self.module_id);
|
||||
let ctx = LocationCtx::new(self.def_collector.db, module.id, self.file_id);
|
||||
|
||||
macro_rules! def {
|
||||
($kind:ident, $ast_id:ident) => {
|
||||
@@ -671,28 +673,26 @@ macro_rules! def {
|
||||
}
|
||||
|
||||
fn collect_macro(&mut self, mac: &raw::MacroData) {
|
||||
let ast_id = AstId::new(self.file_id, mac.ast_id);
|
||||
|
||||
// Case 1: macro rules, define a macro in crate-global mutable scope
|
||||
if is_macro_rules(&mac.path) {
|
||||
if let Some(name) = &mac.name {
|
||||
let macro_id = MacroDefId {
|
||||
ast_id: mac.ast_id.with_file_id(self.file_id),
|
||||
krate: self.def_collector.def_map.krate,
|
||||
};
|
||||
let macro_id =
|
||||
MacroDefId { ast_id, krate: self.def_collector.def_map.krate.crate_id };
|
||||
let macro_ = MacroDef { id: macro_id };
|
||||
self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ast_id = mac.ast_id.with_file_id(self.file_id);
|
||||
|
||||
// Case 2: try to resolve in legacy scope and expand macro_rules, triggering
|
||||
// recursive item collection.
|
||||
if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
|
||||
self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
|
||||
}) {
|
||||
let def = macro_def.id;
|
||||
let macro_call_id = MacroCallLoc { def, ast_id }.id(self.def_collector.db);
|
||||
let macro_call_id = self.def_collector.db.intern_macro(MacroCallLoc { def, ast_id });
|
||||
|
||||
self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def);
|
||||
return;
|
||||
@@ -728,7 +728,7 @@ fn is_macro_use<'a>(&self, attrs: &'a [Attr]) -> bool {
|
||||
}
|
||||
|
||||
fn is_macro_rules(path: &Path) -> bool {
|
||||
path.as_ident() == Some(&MACRO_RULES)
|
||||
path.as_ident() == Some(&name::MACRO_RULES)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#[test]
|
||||
fn name_res_works_for_broken_modules() {
|
||||
covers!(name_res_works_for_broken_modules);
|
||||
// covers!(name_res_works_for_broken_modules);
|
||||
let map = def_map(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
//! Name resolution.
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{
|
||||
builtin_type::BuiltinType,
|
||||
path::{Path, PathKind},
|
||||
CrateModuleId,
|
||||
};
|
||||
use hir_expand::name::{self, Name};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
@@ -12,11 +18,8 @@
|
||||
},
|
||||
generics::GenericParams,
|
||||
impl_block::ImplBlock,
|
||||
name::{Name, SELF_PARAM, SELF_TYPE},
|
||||
nameres::{CrateDefMap, CrateModuleId, PerNs},
|
||||
path::{Path, PathKind},
|
||||
Adt, BuiltinType, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, Static, Struct,
|
||||
Trait, TypeAlias,
|
||||
nameres::{CrateDefMap, PerNs},
|
||||
Adt, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, Static, Struct, Trait, TypeAlias,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -149,13 +152,13 @@ pub(crate) fn resolve_path_in_type_ns(
|
||||
}
|
||||
}
|
||||
Scope::ImplBlockScope(impl_) => {
|
||||
if first_name == &SELF_TYPE {
|
||||
if first_name == &name::SELF_TYPE {
|
||||
let idx = if path.segments.len() == 1 { None } else { Some(1) };
|
||||
return Some((TypeNs::SelfType(*impl_), idx));
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(adt) => {
|
||||
if first_name == &SELF_TYPE {
|
||||
if first_name == &name::SELF_TYPE {
|
||||
let idx = if path.segments.len() == 1 { None } else { Some(1) };
|
||||
return Some((TypeNs::AdtSelfType(*adt), idx));
|
||||
}
|
||||
@@ -204,7 +207,7 @@ pub(crate) fn resolve_path_in_value_ns<'p>(
|
||||
return None;
|
||||
}
|
||||
let n_segments = path.segments.len();
|
||||
let tmp = SELF_PARAM;
|
||||
let tmp = name::SELF_PARAM;
|
||||
let first_name = if path.is_self() { &tmp } else { &path.segments.first()?.name };
|
||||
let skip_to_mod = path.kind != PathKind::Plain && !path.is_self();
|
||||
for scope in self.scopes.iter().rev() {
|
||||
@@ -240,13 +243,13 @@ pub(crate) fn resolve_path_in_value_ns<'p>(
|
||||
Scope::GenericParams(_) => continue,
|
||||
|
||||
Scope::ImplBlockScope(impl_) if n_segments > 1 => {
|
||||
if first_name == &SELF_TYPE {
|
||||
if first_name == &name::SELF_TYPE {
|
||||
let ty = TypeNs::SelfType(*impl_);
|
||||
return Some(ResolveValueResult::Partial(ty, 1));
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(adt) if n_segments > 1 => {
|
||||
if first_name == &SELF_TYPE {
|
||||
if first_name == &name::SELF_TYPE {
|
||||
let ty = TypeNs::AdtSelfType(*adt);
|
||||
return Some(ResolveValueResult::Partial(ty, 1));
|
||||
}
|
||||
@@ -330,8 +333,8 @@ pub(crate) fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet<Trait>
|
||||
for scope in &self.scopes {
|
||||
if let Scope::ModuleScope(m) = scope {
|
||||
if let Some(prelude) = m.crate_def_map.prelude() {
|
||||
let prelude_def_map = db.crate_def_map(prelude.krate);
|
||||
traits.extend(prelude_def_map[prelude.module_id].scope.traits());
|
||||
let prelude_def_map = db.crate_def_map(prelude.krate());
|
||||
traits.extend(prelude_def_map[prelude.id.module_id].scope.traits());
|
||||
}
|
||||
traits.extend(m.crate_def_map[m.module_id].scope.traits());
|
||||
}
|
||||
@@ -444,10 +447,12 @@ fn process_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)
|
||||
f(name.clone(), ScopeDef::ModuleDef(*def));
|
||||
});
|
||||
if let Some(prelude) = m.crate_def_map.prelude() {
|
||||
let prelude_def_map = db.crate_def_map(prelude.krate);
|
||||
prelude_def_map[prelude.module_id].scope.entries().for_each(|(name, res)| {
|
||||
f(name.clone(), res.def.into());
|
||||
});
|
||||
let prelude_def_map = db.crate_def_map(prelude.krate());
|
||||
prelude_def_map[prelude.id.module_id].scope.entries().for_each(
|
||||
|(name, res)| {
|
||||
f(name.clone(), res.def.into());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Scope::GenericParams(gp) => {
|
||||
@@ -456,10 +461,10 @@ fn process_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)
|
||||
}
|
||||
}
|
||||
Scope::ImplBlockScope(i) => {
|
||||
f(SELF_TYPE, ScopeDef::ImplSelfType(*i));
|
||||
f(name::SELF_TYPE, ScopeDef::ImplSelfType(*i));
|
||||
}
|
||||
Scope::AdtScope(i) => {
|
||||
f(SELF_TYPE, ScopeDef::AdtSelfType(*i));
|
||||
f(name::SELF_TYPE, ScopeDef::AdtSelfType(*i));
|
||||
}
|
||||
Scope::ExprScope(e) => {
|
||||
e.expr_scopes.entries(e.scope_id).iter().for_each(|e| {
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
//! purely for "IDE needs".
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::path::known;
|
||||
use hir_expand::name::AsName;
|
||||
use ra_db::FileId;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode},
|
||||
AstPtr,
|
||||
match_ast, AstPtr,
|
||||
SyntaxKind::*,
|
||||
SyntaxNode, SyntaxNodePtr, TextRange, TextUnit,
|
||||
};
|
||||
@@ -24,11 +26,10 @@
|
||||
BodySourceMap,
|
||||
},
|
||||
ids::LocationCtx,
|
||||
path::known,
|
||||
resolve::{ScopeDef, TypeNs, ValueNs},
|
||||
ty::method_resolution::implements_trait,
|
||||
AsName, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef,
|
||||
Module, Name, Path, Resolver, Static, Struct, Ty,
|
||||
Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef, Module,
|
||||
Name, Path, Resolver, Static, Struct, Ty,
|
||||
};
|
||||
|
||||
fn try_get_resolver_for_node(
|
||||
@@ -36,24 +37,34 @@ fn try_get_resolver_for_node(
|
||||
file_id: FileId,
|
||||
node: &SyntaxNode,
|
||||
) -> Option<Resolver> {
|
||||
if let Some(module) = ast::Module::cast(node.clone()) {
|
||||
let src = crate::Source { file_id: file_id.into(), ast: module };
|
||||
Some(crate::Module::from_declaration(db, src)?.resolver(db))
|
||||
} else if let Some(file) = ast::SourceFile::cast(node.clone()) {
|
||||
let src =
|
||||
crate::Source { file_id: file_id.into(), ast: crate::ModuleSource::SourceFile(file) };
|
||||
Some(crate::Module::from_definition(db, src)?.resolver(db))
|
||||
} else if let Some(s) = ast::StructDef::cast(node.clone()) {
|
||||
let src = crate::Source { file_id: file_id.into(), ast: s };
|
||||
Some(Struct::from_source(db, src)?.resolver(db))
|
||||
} else if let Some(e) = ast::EnumDef::cast(node.clone()) {
|
||||
let src = crate::Source { file_id: file_id.into(), ast: e };
|
||||
Some(Enum::from_source(db, src)?.resolver(db))
|
||||
} else if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF {
|
||||
Some(def_with_body_from_child_node(db, file_id, node)?.resolver(db))
|
||||
} else {
|
||||
// FIXME add missing cases
|
||||
None
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::Module(it) => {
|
||||
let src = crate::Source { file_id: file_id.into(), ast: it };
|
||||
Some(crate::Module::from_declaration(db, src)?.resolver(db))
|
||||
},
|
||||
ast::SourceFile(it) => {
|
||||
let src =
|
||||
crate::Source { file_id: file_id.into(), ast: crate::ModuleSource::SourceFile(it) };
|
||||
Some(crate::Module::from_definition(db, src)?.resolver(db))
|
||||
},
|
||||
ast::StructDef(it) => {
|
||||
let src = crate::Source { file_id: file_id.into(), ast: it };
|
||||
Some(Struct::from_source(db, src)?.resolver(db))
|
||||
},
|
||||
ast::EnumDef(it) => {
|
||||
let src = crate::Source { file_id: file_id.into(), ast: it };
|
||||
Some(Enum::from_source(db, src)?.resolver(db))
|
||||
},
|
||||
_ => {
|
||||
if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF {
|
||||
Some(def_with_body_from_child_node(db, file_id, node)?.resolver(db))
|
||||
} else {
|
||||
// FIXME add missing cases
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,19 +75,17 @@ fn def_with_body_from_child_node(
|
||||
) -> Option<DefWithBody> {
|
||||
let src = crate::ModuleSource::from_child_node(db, file_id, node);
|
||||
let module = Module::from_definition(db, crate::Source { file_id: file_id.into(), ast: src })?;
|
||||
let ctx = LocationCtx::new(db, module, file_id.into());
|
||||
let ctx = LocationCtx::new(db, module.id, file_id.into());
|
||||
|
||||
node.ancestors().find_map(|node| {
|
||||
if let Some(def) = ast::FnDef::cast(node.clone()) {
|
||||
return Some(Function { id: ctx.to_def(&def) }.into());
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::FnDef(def) => { Some(Function {id: ctx.to_def(&def) }.into()) },
|
||||
ast::ConstDef(def) => { Some(Const { id: ctx.to_def(&def) }.into()) },
|
||||
ast::StaticDef(def) => { Some(Static { id: ctx.to_def(&def) }.into()) },
|
||||
_ => { None },
|
||||
}
|
||||
}
|
||||
if let Some(def) = ast::ConstDef::cast(node.clone()) {
|
||||
return Some(Const { id: ctx.to_def(&def) }.into());
|
||||
}
|
||||
if let Some(def) = ast::StaticDef::cast(node) {
|
||||
return Some(Static { id: ctx.to_def(&def) }.into());
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
//! HIR for trait definitions.
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::name::AsName;
|
||||
|
||||
use ra_syntax::ast::{self, NameOwner};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{
|
||||
db::{AstDatabase, DefDatabase},
|
||||
ids::LocationCtx,
|
||||
name::AsName,
|
||||
AssocItem, Const, Function, HasSource, Module, Name, Trait, TypeAlias,
|
||||
};
|
||||
|
||||
@@ -27,7 +28,7 @@ pub(crate) fn trait_data_query(
|
||||
let src = tr.source(db);
|
||||
let name = src.ast.name().map(|n| n.as_name());
|
||||
let module = tr.module(db);
|
||||
let ctx = LocationCtx::new(db, module, src.file_id);
|
||||
let ctx = LocationCtx::new(db, module.id, src.file_id);
|
||||
let auto = src.ast.is_auto();
|
||||
let items = if let Some(item_list) = src.ast.item_list() {
|
||||
item_list
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
use std::{fmt, iter, mem};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, expr::ExprId, type_ref::Mutability, util::make_mut_slice, Adt, Crate,
|
||||
DefWithBody, GenericParams, HasGenericParams, Name, Trait, TypeAlias,
|
||||
db::HirDatabase, expr::ExprId, util::make_mut_slice, Adt, Crate, DefWithBody, GenericParams,
|
||||
HasGenericParams, Mutability, Name, Trait, TypeAlias,
|
||||
};
|
||||
use display::{HirDisplay, HirFormatter};
|
||||
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
use std::iter::successors;
|
||||
|
||||
use hir_expand::name;
|
||||
use log::{info, warn};
|
||||
|
||||
use super::{traits::Solution, Canonical, Substs, Ty, TypeWalk};
|
||||
use crate::{db::HirDatabase, name, HasGenericParams, Resolver};
|
||||
use crate::{db::HirDatabase, HasGenericParams, Resolver};
|
||||
|
||||
const AUTODEREF_RECURSION_LIMIT: usize = 10;
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use hir_def::{
|
||||
path::known,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
};
|
||||
use hir_expand::name;
|
||||
use ra_arena::map::ArenaMap;
|
||||
use ra_prof::profile;
|
||||
use test_utils::tested_by;
|
||||
@@ -37,11 +42,8 @@
|
||||
db::HirDatabase,
|
||||
diagnostics::DiagnosticSink,
|
||||
expr::{BindingAnnotation, Body, ExprId, PatId},
|
||||
name,
|
||||
path::known,
|
||||
resolve::{Resolver, TypeNs},
|
||||
ty::infer::diagnostics::InferenceDiagnostic,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
Adt, AssocItem, ConstData, DefWithBody, FnData, Function, HasBody, Path, StructField,
|
||||
};
|
||||
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
lang_item::LangItemTarget,
|
||||
resolve::Resolver,
|
||||
ty::{autoderef, Substs, Ty, TypeCtor, TypeWalk},
|
||||
type_ref::Mutability,
|
||||
Adt,
|
||||
Adt, Mutability,
|
||||
};
|
||||
|
||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
use std::iter::{repeat, repeat_with};
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::path::{GenericArg, GenericArgs};
|
||||
use hir_expand::name;
|
||||
|
||||
use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch};
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
expr::{self, Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
|
||||
generics::{GenericParams, HasGenericParams},
|
||||
name,
|
||||
nameres::Namespace,
|
||||
path::{GenericArg, GenericArgs},
|
||||
ty::{
|
||||
autoderef, method_resolution, op, primitive, CallableDef, InferTy, Mutability, Obligation,
|
||||
ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! Path expression resolution.
|
||||
|
||||
use hir_def::path::PathSegment;
|
||||
|
||||
use super::{ExprOrPatId, InferenceContext, TraitRef};
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
@@ -131,7 +133,7 @@ fn resolve_assoc_item(
|
||||
fn resolve_trait_assoc_item(
|
||||
&mut self,
|
||||
trait_ref: TraitRef,
|
||||
segment: &crate::path::PathSegment,
|
||||
segment: &PathSegment,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<(ValueNs, Option<Substs>)> {
|
||||
let trait_ = trait_ref.trait_;
|
||||
@@ -170,7 +172,7 @@ fn resolve_trait_assoc_item(
|
||||
fn resolve_ty_assoc_item(
|
||||
&mut self,
|
||||
ty: Ty,
|
||||
segment: &crate::path::PathSegment,
|
||||
segment: &PathSegment,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<(ValueNs, Option<Substs>)> {
|
||||
if let Ty::Unknown = ty {
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
use std::iter;
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{
|
||||
builtin_type::BuiltinType,
|
||||
path::{GenericArg, PathSegment},
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
};
|
||||
|
||||
use super::{
|
||||
FnSig, GenericPredicate, ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
|
||||
TypeWalk,
|
||||
@@ -18,13 +24,14 @@
|
||||
generics::HasGenericParams,
|
||||
generics::{GenericDef, WherePredicate},
|
||||
nameres::Namespace,
|
||||
path::{GenericArg, PathSegment},
|
||||
resolve::{Resolver, TypeNs},
|
||||
ty::Adt,
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
ty::{
|
||||
primitive::{FloatTy, IntTy},
|
||||
Adt,
|
||||
},
|
||||
util::make_mut_slice,
|
||||
BuiltinType, Const, Enum, EnumVariant, Function, ModuleDef, Path, Static, Struct, StructField,
|
||||
Trait, TypeAlias, Union,
|
||||
Const, Enum, EnumVariant, Function, ModuleDef, Path, Static, Struct, StructField, Trait,
|
||||
TypeAlias, Union,
|
||||
};
|
||||
|
||||
impl Ty {
|
||||
@@ -640,8 +647,10 @@ fn type_for_builtin(def: BuiltinType) -> Ty {
|
||||
BuiltinType::Char => TypeCtor::Char,
|
||||
BuiltinType::Bool => TypeCtor::Bool,
|
||||
BuiltinType::Str => TypeCtor::Str,
|
||||
BuiltinType::Int(ty) => TypeCtor::Int(ty.into()),
|
||||
BuiltinType::Float(ty) => TypeCtor::Float(ty.into()),
|
||||
BuiltinType::Int { signedness, bitness } => {
|
||||
TypeCtor::Int(IntTy { signedness, bitness }.into())
|
||||
}
|
||||
BuiltinType::Float { bitness } => TypeCtor::Float(FloatTy { bitness }.into()),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,17 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use hir_def::CrateModuleId;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use super::{autoderef, lower, Canonical, InEnvironment, TraitEnvironment, TraitRef};
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
impl_block::{ImplBlock, ImplId},
|
||||
nameres::CrateModuleId,
|
||||
resolve::Resolver,
|
||||
ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy},
|
||||
ty::{Ty, TypeCtor},
|
||||
type_ref::Mutability,
|
||||
AssocItem, Crate, Function, Module, Name, Trait,
|
||||
AssocItem, Crate, Function, Module, Mutability, Name, Trait,
|
||||
};
|
||||
|
||||
/// This is used as a key for indexing impls.
|
||||
@@ -50,7 +49,7 @@ pub fn lookup_impl_blocks<'a>(&'a self, ty: &Ty) -> impl Iterator<Item = ImplBlo
|
||||
let fingerprint = TyFingerprint::for_impl(ty);
|
||||
fingerprint.and_then(|f| self.impls.get(&f)).into_iter().flat_map(|i| i.iter()).map(
|
||||
move |(module_id, impl_id)| {
|
||||
let module = Module { krate: self.krate, module_id: *module_id };
|
||||
let module = Module::new(self.krate, *module_id);
|
||||
ImplBlock::from_id(module, *impl_id)
|
||||
},
|
||||
)
|
||||
@@ -62,7 +61,7 @@ pub fn lookup_impl_blocks_for_trait<'a>(
|
||||
) -> impl Iterator<Item = ImplBlock> + 'a {
|
||||
self.impls_by_trait.get(&tr).into_iter().flat_map(|i| i.iter()).map(
|
||||
move |(module_id, impl_id)| {
|
||||
let module = Module { krate: self.krate, module_id: *module_id };
|
||||
let module = Module::new(self.krate, *module_id);
|
||||
ImplBlock::from_id(module, *impl_id)
|
||||
},
|
||||
)
|
||||
@@ -71,7 +70,7 @@ pub fn lookup_impl_blocks_for_trait<'a>(
|
||||
pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplBlock> + 'a {
|
||||
self.impls.values().chain(self.impls_by_trait.values()).flat_map(|i| i.iter()).map(
|
||||
move |(module_id, impl_id)| {
|
||||
let module = Module { krate: self.krate, module_id: *module_id };
|
||||
let module = Module::new(self.krate, *module_id);
|
||||
ImplBlock::from_id(module, *impl_id)
|
||||
},
|
||||
)
|
||||
@@ -90,14 +89,14 @@ fn collect_recursive(&mut self, db: &impl HirDatabase, module: Module) {
|
||||
self.impls_by_trait
|
||||
.entry(tr.trait_)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((module.module_id, impl_id));
|
||||
.push((module.id.module_id, impl_id));
|
||||
}
|
||||
} else {
|
||||
if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) {
|
||||
self.impls
|
||||
.entry(target_ty_fp)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((module.module_id, impl_id));
|
||||
.push((module.id.module_id, impl_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,27 +2,7 @@
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Signedness {
|
||||
Signed,
|
||||
Unsigned,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum IntBitness {
|
||||
Xsize,
|
||||
X8,
|
||||
X16,
|
||||
X32,
|
||||
X64,
|
||||
X128,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum FloatBitness {
|
||||
X32,
|
||||
X64,
|
||||
}
|
||||
pub use hir_def::builtin_type::{FloatBitness, IntBitness, Signedness};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum UncertainIntTy {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Trait solving using Chalk.
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use chalk_ir::cast::Cast;
|
||||
use chalk_ir::{cast::Cast, family::ChalkIr};
|
||||
use log::debug;
|
||||
use ra_db::salsa;
|
||||
use ra_prof::profile;
|
||||
@@ -33,7 +33,7 @@ impl TraitSolver {
|
||||
fn solve(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal>>,
|
||||
goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<ChalkIr>>>,
|
||||
) -> Option<chalk_solve::Solution> {
|
||||
let context = ChalkContext { db, krate: self.krate };
|
||||
debug!("solve goal: {:?}", goal);
|
||||
@@ -196,7 +196,7 @@ pub(crate) fn trait_solve_query(
|
||||
}
|
||||
|
||||
fn solution_from_chalk(db: &impl HirDatabase, solution: chalk_solve::Solution) -> Solution {
|
||||
let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution>| {
|
||||
let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<ChalkIr>>| {
|
||||
let value = subst
|
||||
.value
|
||||
.parameters
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
use log::debug;
|
||||
|
||||
use chalk_ir::{
|
||||
cast::Cast, Identifier, ImplId, Parameter, PlaceholderIndex, TypeId, TypeKindId, TypeName,
|
||||
UniverseIndex,
|
||||
cast::Cast, family::ChalkIr, Identifier, ImplId, Parameter, PlaceholderIndex, TypeId,
|
||||
TypeKindId, TypeName, UniverseIndex,
|
||||
};
|
||||
use chalk_rust_ir::{AssociatedTyDatum, ImplDatum, StructDatum, TraitDatum};
|
||||
|
||||
use hir_expand::name;
|
||||
|
||||
use ra_db::salsa::{InternId, InternKey};
|
||||
|
||||
use super::{Canonical, ChalkContext, Impl, Obligation};
|
||||
@@ -38,8 +40,8 @@ pub(super) fn from_chalk<T, ChalkT>(db: &impl HirDatabase, chalk: ChalkT) -> T
|
||||
}
|
||||
|
||||
impl ToChalk for Ty {
|
||||
type Chalk = chalk_ir::Ty;
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty {
|
||||
type Chalk = chalk_ir::Ty<ChalkIr>;
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty<ChalkIr> {
|
||||
match self {
|
||||
Ty::Apply(apply_ty) => {
|
||||
let name = match apply_ty.ctor {
|
||||
@@ -62,21 +64,19 @@ fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty {
|
||||
chalk_ir::ProjectionTy { associated_ty_id, parameters }.cast()
|
||||
}
|
||||
Ty::Param { idx, .. } => {
|
||||
PlaceholderIndex { ui: UniverseIndex::ROOT, idx: idx as usize }.to_ty()
|
||||
PlaceholderIndex { ui: UniverseIndex::ROOT, idx: idx as usize }.to_ty::<ChalkIr>()
|
||||
}
|
||||
Ty::Bound(idx) => chalk_ir::Ty::BoundVar(idx as usize),
|
||||
Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"),
|
||||
// FIXME this is clearly incorrect, but probably not too incorrect
|
||||
// and I'm not sure what to actually do with Ty::Unknown
|
||||
// maybe an alternative would be `for<T> T`? (meaningless in rust, but expressible in chalk's Ty)
|
||||
//
|
||||
// FIXME also dyn and impl Trait are currently handled like Unknown because Chalk doesn't have them yet
|
||||
// FIXME use Chalk's Dyn/Opaque once the bugs with that are fixed
|
||||
Ty::Unknown | Ty::Dyn(_) | Ty::Opaque(_) => {
|
||||
PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::max_value() }.to_ty()
|
||||
let parameters = Vec::new();
|
||||
let name = TypeName::Error;
|
||||
chalk_ir::ApplicationTy { name, parameters }.cast()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty) -> Self {
|
||||
fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty<ChalkIr>) -> Self {
|
||||
match chalk {
|
||||
chalk_ir::Ty::Apply(apply_ty) => {
|
||||
// FIXME this is kind of hacky due to the fact that
|
||||
@@ -92,6 +92,7 @@ fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty) -> Self {
|
||||
let parameters = from_chalk(db, apply_ty.parameters);
|
||||
Ty::Apply(ApplicationTy { ctor, parameters })
|
||||
}
|
||||
TypeName::Error => Ty::Unknown,
|
||||
// FIXME handle TypeKindId::Trait/Type here
|
||||
TypeName::TypeKindId(_) => unimplemented!(),
|
||||
TypeName::Placeholder(idx) => {
|
||||
@@ -108,18 +109,30 @@ fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty) -> Self {
|
||||
chalk_ir::Ty::ForAll(_) => unimplemented!(),
|
||||
chalk_ir::Ty::BoundVar(idx) => Ty::Bound(idx as u32),
|
||||
chalk_ir::Ty::InferenceVar(_iv) => Ty::Unknown,
|
||||
chalk_ir::Ty::Dyn(where_clauses) => {
|
||||
assert_eq!(where_clauses.binders.len(), 1);
|
||||
let predicates =
|
||||
where_clauses.value.into_iter().map(|c| from_chalk(db, c)).collect();
|
||||
Ty::Dyn(predicates)
|
||||
}
|
||||
chalk_ir::Ty::Opaque(where_clauses) => {
|
||||
assert_eq!(where_clauses.binders.len(), 1);
|
||||
let predicates =
|
||||
where_clauses.value.into_iter().map(|c| from_chalk(db, c)).collect();
|
||||
Ty::Opaque(predicates)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToChalk for Substs {
|
||||
type Chalk = Vec<chalk_ir::Parameter>;
|
||||
type Chalk = Vec<chalk_ir::Parameter<ChalkIr>>;
|
||||
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> Vec<Parameter> {
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> Vec<Parameter<ChalkIr>> {
|
||||
self.iter().map(|ty| ty.clone().to_chalk(db).cast()).collect()
|
||||
}
|
||||
|
||||
fn from_chalk(db: &impl HirDatabase, parameters: Vec<chalk_ir::Parameter>) -> Substs {
|
||||
fn from_chalk(db: &impl HirDatabase, parameters: Vec<chalk_ir::Parameter<ChalkIr>>) -> Substs {
|
||||
let tys = parameters
|
||||
.into_iter()
|
||||
.map(|p| match p {
|
||||
@@ -132,15 +145,15 @@ fn from_chalk(db: &impl HirDatabase, parameters: Vec<chalk_ir::Parameter>) -> Su
|
||||
}
|
||||
|
||||
impl ToChalk for TraitRef {
|
||||
type Chalk = chalk_ir::TraitRef;
|
||||
type Chalk = chalk_ir::TraitRef<ChalkIr>;
|
||||
|
||||
fn to_chalk(self: TraitRef, db: &impl HirDatabase) -> chalk_ir::TraitRef {
|
||||
fn to_chalk(self: TraitRef, db: &impl HirDatabase) -> chalk_ir::TraitRef<ChalkIr> {
|
||||
let trait_id = self.trait_.to_chalk(db);
|
||||
let parameters = self.substs.to_chalk(db);
|
||||
chalk_ir::TraitRef { trait_id, parameters }
|
||||
}
|
||||
|
||||
fn from_chalk(db: &impl HirDatabase, trait_ref: chalk_ir::TraitRef) -> Self {
|
||||
fn from_chalk(db: &impl HirDatabase, trait_ref: chalk_ir::TraitRef<ChalkIr>) -> Self {
|
||||
let trait_ = from_chalk(db, trait_ref.trait_id);
|
||||
let substs = from_chalk(db, trait_ref.parameters);
|
||||
TraitRef { trait_, substs }
|
||||
@@ -151,11 +164,11 @@ impl ToChalk for Trait {
|
||||
type Chalk = chalk_ir::TraitId;
|
||||
|
||||
fn to_chalk(self, _db: &impl HirDatabase) -> chalk_ir::TraitId {
|
||||
self.id.into()
|
||||
chalk_ir::TraitId(id_to_chalk(self.id))
|
||||
}
|
||||
|
||||
fn from_chalk(_db: &impl HirDatabase, trait_id: chalk_ir::TraitId) -> Trait {
|
||||
Trait { id: trait_id.into() }
|
||||
Trait { id: id_from_chalk(trait_id.0) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,18 +200,18 @@ impl ToChalk for TypeAlias {
|
||||
type Chalk = chalk_ir::TypeId;
|
||||
|
||||
fn to_chalk(self, _db: &impl HirDatabase) -> chalk_ir::TypeId {
|
||||
self.id.into()
|
||||
chalk_ir::TypeId(id_to_chalk(self.id))
|
||||
}
|
||||
|
||||
fn from_chalk(_db: &impl HirDatabase, impl_id: chalk_ir::TypeId) -> TypeAlias {
|
||||
TypeAlias { id: impl_id.into() }
|
||||
fn from_chalk(_db: &impl HirDatabase, type_alias_id: chalk_ir::TypeId) -> TypeAlias {
|
||||
TypeAlias { id: id_from_chalk(type_alias_id.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToChalk for GenericPredicate {
|
||||
type Chalk = chalk_ir::QuantifiedWhereClause;
|
||||
type Chalk = chalk_ir::QuantifiedWhereClause<ChalkIr>;
|
||||
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::QuantifiedWhereClause {
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::QuantifiedWhereClause<ChalkIr> {
|
||||
match self {
|
||||
GenericPredicate::Implemented(trait_ref) => {
|
||||
make_binders(chalk_ir::WhereClause::Implemented(trait_ref.to_chalk(db)), 0)
|
||||
@@ -221,25 +234,40 @@ fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::QuantifiedWhereClause {
|
||||
}
|
||||
|
||||
fn from_chalk(
|
||||
_db: &impl HirDatabase,
|
||||
_where_clause: chalk_ir::QuantifiedWhereClause,
|
||||
db: &impl HirDatabase,
|
||||
where_clause: chalk_ir::QuantifiedWhereClause<ChalkIr>,
|
||||
) -> GenericPredicate {
|
||||
// This should never need to be called
|
||||
unimplemented!()
|
||||
match where_clause.value {
|
||||
chalk_ir::WhereClause::Implemented(tr) => {
|
||||
if tr.trait_id == UNKNOWN_TRAIT {
|
||||
// FIXME we need an Error enum on the Chalk side to avoid this
|
||||
return GenericPredicate::Error;
|
||||
}
|
||||
GenericPredicate::Implemented(from_chalk(db, tr))
|
||||
}
|
||||
chalk_ir::WhereClause::ProjectionEq(projection_eq) => {
|
||||
let projection_ty = from_chalk(db, projection_eq.projection);
|
||||
let ty = from_chalk(db, projection_eq.ty);
|
||||
GenericPredicate::Projection(super::ProjectionPredicate { projection_ty, ty })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToChalk for ProjectionTy {
|
||||
type Chalk = chalk_ir::ProjectionTy;
|
||||
type Chalk = chalk_ir::ProjectionTy<ChalkIr>;
|
||||
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::ProjectionTy {
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::ProjectionTy<ChalkIr> {
|
||||
chalk_ir::ProjectionTy {
|
||||
associated_ty_id: self.associated_ty.to_chalk(db),
|
||||
parameters: self.parameters.to_chalk(db),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_chalk(db: &impl HirDatabase, projection_ty: chalk_ir::ProjectionTy) -> ProjectionTy {
|
||||
fn from_chalk(
|
||||
db: &impl HirDatabase,
|
||||
projection_ty: chalk_ir::ProjectionTy<ChalkIr>,
|
||||
) -> ProjectionTy {
|
||||
ProjectionTy {
|
||||
associated_ty: from_chalk(db, projection_ty.associated_ty_id),
|
||||
parameters: from_chalk(db, projection_ty.parameters),
|
||||
@@ -248,31 +276,31 @@ fn from_chalk(db: &impl HirDatabase, projection_ty: chalk_ir::ProjectionTy) -> P
|
||||
}
|
||||
|
||||
impl ToChalk for super::ProjectionPredicate {
|
||||
type Chalk = chalk_ir::Normalize;
|
||||
type Chalk = chalk_ir::Normalize<ChalkIr>;
|
||||
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Normalize {
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Normalize<ChalkIr> {
|
||||
chalk_ir::Normalize {
|
||||
projection: self.projection_ty.to_chalk(db),
|
||||
ty: self.ty.to_chalk(db),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_chalk(_db: &impl HirDatabase, _normalize: chalk_ir::Normalize) -> Self {
|
||||
fn from_chalk(_db: &impl HirDatabase, _normalize: chalk_ir::Normalize<ChalkIr>) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToChalk for Obligation {
|
||||
type Chalk = chalk_ir::DomainGoal;
|
||||
type Chalk = chalk_ir::DomainGoal<ChalkIr>;
|
||||
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::DomainGoal {
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::DomainGoal<ChalkIr> {
|
||||
match self {
|
||||
Obligation::Trait(tr) => tr.to_chalk(db).cast(),
|
||||
Obligation::Projection(pr) => pr.to_chalk(db).cast(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_chalk(_db: &impl HirDatabase, _goal: chalk_ir::DomainGoal) -> Self {
|
||||
fn from_chalk(_db: &impl HirDatabase, _goal: chalk_ir::DomainGoal<ChalkIr>) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
@@ -296,16 +324,16 @@ fn from_chalk(db: &impl HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -
|
||||
}
|
||||
|
||||
impl ToChalk for Arc<super::TraitEnvironment> {
|
||||
type Chalk = Arc<chalk_ir::Environment>;
|
||||
type Chalk = chalk_ir::Environment<ChalkIr>;
|
||||
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> Arc<chalk_ir::Environment> {
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Environment<ChalkIr> {
|
||||
let mut clauses = Vec::new();
|
||||
for pred in &self.predicates {
|
||||
if pred.is_error() {
|
||||
// for env, we just ignore errors
|
||||
continue;
|
||||
}
|
||||
let program_clause: chalk_ir::ProgramClause = pred.clone().to_chalk(db).cast();
|
||||
let program_clause: chalk_ir::ProgramClause<ChalkIr> = pred.clone().to_chalk(db).cast();
|
||||
clauses.push(program_clause.into_from_env_clause());
|
||||
}
|
||||
chalk_ir::Environment::new().add_clauses(clauses)
|
||||
@@ -313,13 +341,16 @@ fn to_chalk(self, db: &impl HirDatabase) -> Arc<chalk_ir::Environment> {
|
||||
|
||||
fn from_chalk(
|
||||
_db: &impl HirDatabase,
|
||||
_env: Arc<chalk_ir::Environment>,
|
||||
_env: chalk_ir::Environment<ChalkIr>,
|
||||
) -> Arc<super::TraitEnvironment> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToChalk> ToChalk for super::InEnvironment<T> {
|
||||
impl<T: ToChalk> ToChalk for super::InEnvironment<T>
|
||||
where
|
||||
T::Chalk: chalk_ir::family::HasTypeFamily<TypeFamily = ChalkIr>,
|
||||
{
|
||||
type Chalk = chalk_ir::InEnvironment<T::Chalk>;
|
||||
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> {
|
||||
@@ -351,7 +382,7 @@ fn convert_where_clauses(
|
||||
db: &impl HirDatabase,
|
||||
def: GenericDef,
|
||||
substs: &Substs,
|
||||
) -> Vec<chalk_ir::QuantifiedWhereClause> {
|
||||
) -> Vec<chalk_ir::QuantifiedWhereClause<ChalkIr>> {
|
||||
let generic_predicates = db.generic_predicates(def);
|
||||
let mut result = Vec::with_capacity(generic_predicates.len());
|
||||
for pred in generic_predicates.iter() {
|
||||
@@ -384,7 +415,7 @@ fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> {
|
||||
fn impls_for_trait(
|
||||
&self,
|
||||
trait_id: chalk_ir::TraitId,
|
||||
parameters: &[Parameter],
|
||||
parameters: &[Parameter<ChalkIr>],
|
||||
) -> Vec<ImplId> {
|
||||
debug!("impls_for_trait {:?}", trait_id);
|
||||
if trait_id == UNKNOWN_TRAIT {
|
||||
@@ -430,13 +461,13 @@ fn type_name(&self, _id: TypeKindId) -> Identifier {
|
||||
}
|
||||
fn split_projection<'p>(
|
||||
&self,
|
||||
projection: &'p chalk_ir::ProjectionTy,
|
||||
) -> (Arc<AssociatedTyDatum>, &'p [Parameter], &'p [Parameter]) {
|
||||
projection: &'p chalk_ir::ProjectionTy<ChalkIr>,
|
||||
) -> (Arc<AssociatedTyDatum>, &'p [Parameter<ChalkIr>], &'p [Parameter<ChalkIr>]) {
|
||||
debug!("split_projection {:?}", projection);
|
||||
// we don't support GATs, so I think this should always be correct currently
|
||||
(self.db.associated_ty_data(projection.associated_ty_id), &projection.parameters, &[])
|
||||
}
|
||||
fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause> {
|
||||
fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<ChalkIr>> {
|
||||
vec![]
|
||||
}
|
||||
fn local_impls_to_coherence_check(
|
||||
@@ -508,7 +539,7 @@ pub(crate) fn trait_datum_query(
|
||||
let trait_ref = trait_.trait_ref(db).subst(&bound_vars).to_chalk(db);
|
||||
let flags = chalk_rust_ir::TraitFlags {
|
||||
auto: trait_.is_auto(db),
|
||||
upstream: trait_.module(db).krate(db) != Some(krate),
|
||||
upstream: trait_.module(db).krate() != krate,
|
||||
non_enumerable: true,
|
||||
// FIXME set these flags correctly
|
||||
marker: false,
|
||||
@@ -596,7 +627,7 @@ fn impl_block_datum(
|
||||
.target_trait_ref(db)
|
||||
.expect("FIXME handle unresolved impl block trait ref")
|
||||
.subst(&bound_vars);
|
||||
let impl_type = if impl_block.module().krate(db) == Some(krate) {
|
||||
let impl_type = if impl_block.module().krate() == krate {
|
||||
chalk_rust_ir::ImplType::Local
|
||||
} else {
|
||||
chalk_rust_ir::ImplType::External
|
||||
@@ -705,7 +736,7 @@ fn closure_fn_trait_impl_datum(
|
||||
substs: Substs::build_for_def(db, trait_).push(self_ty).push(arg_ty).build(),
|
||||
};
|
||||
|
||||
let output_ty_id = fn_once_trait.associated_type_by_name(db, &crate::name::OUTPUT_TYPE)?;
|
||||
let output_ty_id = fn_once_trait.associated_type_by_name(db, &name::OUTPUT_TYPE)?;
|
||||
|
||||
let output_ty_value = chalk_rust_ir::AssociatedTyValue {
|
||||
associated_ty_id: output_ty_id.to_chalk(db),
|
||||
@@ -746,30 +777,6 @@ fn id_to_chalk<T: InternKey>(salsa_id: T) -> chalk_ir::RawId {
|
||||
chalk_ir::RawId { index: salsa_id.as_intern_id().as_u32() }
|
||||
}
|
||||
|
||||
impl From<chalk_ir::TraitId> for crate::ids::TraitId {
|
||||
fn from(trait_id: chalk_ir::TraitId) -> Self {
|
||||
id_from_chalk(trait_id.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::ids::TraitId> for chalk_ir::TraitId {
|
||||
fn from(trait_id: crate::ids::TraitId) -> Self {
|
||||
chalk_ir::TraitId(id_to_chalk(trait_id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<chalk_ir::TypeId> for crate::ids::TypeAliasId {
|
||||
fn from(type_id: chalk_ir::TypeId) -> Self {
|
||||
id_from_chalk(type_id.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::ids::TypeAliasId> for chalk_ir::TypeId {
|
||||
fn from(type_id: crate::ids::TypeAliasId) -> Self {
|
||||
chalk_ir::TypeId(id_to_chalk(type_id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<chalk_ir::StructId> for crate::ids::TypeCtorId {
|
||||
fn from(struct_id: chalk_ir::StructId) -> Self {
|
||||
id_from_chalk(struct_id.0)
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::type_ref::TypeRef;
|
||||
use hir_expand::name::{AsName, Name};
|
||||
|
||||
use ra_syntax::ast::NameOwner;
|
||||
|
||||
use crate::{
|
||||
db::{AstDatabase, DefDatabase},
|
||||
name::{AsName, Name},
|
||||
type_ref::TypeRef,
|
||||
HasSource, TypeAlias,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "ra_hir_def"
|
||||
version = "0.1.0"
|
||||
authors = ["rust-analyzer developers"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.5"
|
||||
once_cell = "1.0.1"
|
||||
relative-path = "1.0.0"
|
||||
rustc-hash = "1.0"
|
||||
|
||||
ra_arena = { path = "../ra_arena" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
ra_syntax = { path = "../ra_syntax" }
|
||||
ra_prof = { path = "../ra_prof" }
|
||||
hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" }
|
||||
test_utils = { path = "../test_utils" }
|
||||
mbe = { path = "../ra_mbe", package = "ra_mbe" }
|
||||
ra_cfg = { path = "../ra_cfg" }
|
||||
tt = { path = "../ra_tt", package = "ra_tt" }
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::hygiene::Hygiene;
|
||||
use mbe::ast_to_token_tree;
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_syntax::{
|
||||
@@ -10,10 +11,10 @@
|
||||
};
|
||||
use tt::Subtree;
|
||||
|
||||
use crate::{db::AstDatabase, path::Path, HirFileId, Source};
|
||||
use crate::path::Path;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct Attr {
|
||||
pub struct Attr {
|
||||
pub(crate) path: Path,
|
||||
pub(crate) input: Option<AttrInput>,
|
||||
}
|
||||
@@ -25,11 +26,8 @@ pub enum AttrInput {
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
pub(crate) fn from_src(
|
||||
Source { file_id, ast }: Source<ast::Attr>,
|
||||
db: &impl AstDatabase,
|
||||
) -> Option<Attr> {
|
||||
let path = Path::from_src(Source { file_id, ast: ast.path()? }, db)?;
|
||||
pub(crate) fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
|
||||
let path = Path::from_src(ast.path()?, hygiene)?;
|
||||
let input = match ast.input() {
|
||||
None => None,
|
||||
Some(ast::AttrInput::Literal(lit)) => {
|
||||
@@ -45,26 +43,22 @@ pub(crate) fn from_src(
|
||||
Some(Attr { path, input })
|
||||
}
|
||||
|
||||
pub(crate) fn from_attrs_owner(
|
||||
file_id: HirFileId,
|
||||
owner: &dyn AttrsOwner,
|
||||
db: &impl AstDatabase,
|
||||
) -> Option<Arc<[Attr]>> {
|
||||
pub fn from_attrs_owner(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Option<Arc<[Attr]>> {
|
||||
let mut attrs = owner.attrs().peekable();
|
||||
if attrs.peek().is_none() {
|
||||
// Avoid heap allocation
|
||||
return None;
|
||||
}
|
||||
Some(attrs.flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect())
|
||||
Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect())
|
||||
}
|
||||
|
||||
pub(crate) fn is_simple_atom(&self, name: &str) -> bool {
|
||||
pub fn is_simple_atom(&self, name: &str) -> bool {
|
||||
// FIXME: Avoid cloning
|
||||
self.path.as_ident().map_or(false, |s| s.to_string() == name)
|
||||
}
|
||||
|
||||
// FIXME: handle cfg_attr :-)
|
||||
pub(crate) fn as_cfg(&self) -> Option<&Subtree> {
|
||||
pub fn as_cfg(&self) -> Option<&Subtree> {
|
||||
if !self.is_simple_atom("cfg") {
|
||||
return None;
|
||||
}
|
||||
@@ -74,7 +68,7 @@ pub(crate) fn as_cfg(&self) -> Option<&Subtree> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_path(&self) -> Option<&SmolStr> {
|
||||
pub fn as_path(&self) -> Option<&SmolStr> {
|
||||
if !self.is_simple_atom("path") {
|
||||
return None;
|
||||
}
|
||||
@@ -84,7 +78,7 @@ pub(crate) fn as_path(&self) -> Option<&SmolStr> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> {
|
||||
pub fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> {
|
||||
cfg_options.is_cfg_enabled(self.as_cfg()?)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//! This module defines built-in types.
|
||||
//!
|
||||
//! A peculiarity of built-in types is that they are always available and are
|
||||
//! not associated with any particular crate.
|
||||
|
||||
use hir_expand::name::{self, Name};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Signedness {
|
||||
Signed,
|
||||
Unsigned,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum IntBitness {
|
||||
Xsize,
|
||||
X8,
|
||||
X16,
|
||||
X32,
|
||||
X64,
|
||||
X128,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum FloatBitness {
|
||||
X32,
|
||||
X64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum BuiltinType {
|
||||
Char,
|
||||
Bool,
|
||||
Str,
|
||||
Int { signedness: Signedness, bitness: IntBitness },
|
||||
Float { bitness: FloatBitness },
|
||||
}
|
||||
|
||||
impl BuiltinType {
|
||||
#[rustfmt::skip]
|
||||
pub const ALL: &'static [(Name, BuiltinType)] = &[
|
||||
(name::CHAR, BuiltinType::Char),
|
||||
(name::BOOL, BuiltinType::Bool),
|
||||
(name::STR, BuiltinType::Str ),
|
||||
|
||||
(name::ISIZE, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::Xsize }),
|
||||
(name::I8, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::X8 }),
|
||||
(name::I16, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::X16 }),
|
||||
(name::I32, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::X32 }),
|
||||
(name::I64, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::X64 }),
|
||||
(name::I128, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::X128 }),
|
||||
|
||||
(name::USIZE, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::Xsize }),
|
||||
(name::U8, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::X8 }),
|
||||
(name::U16, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::X16 }),
|
||||
(name::U32, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::X32 }),
|
||||
(name::U64, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::X64 }),
|
||||
(name::U128, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::X128 }),
|
||||
|
||||
(name::F32, BuiltinType::Float { bitness: FloatBitness::X32 }),
|
||||
(name::F64, BuiltinType::Float { bitness: FloatBitness::X64 }),
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//! Defines database & queries for name resolution.
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::{db::AstDatabase, HirFileId};
|
||||
use ra_db::{salsa, SourceDatabase};
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::nameres::raw::{ImportSourceMap, RawItems};
|
||||
|
||||
#[salsa::query_group(InternDatabaseStorage)]
|
||||
pub trait InternDatabase: SourceDatabase {
|
||||
#[salsa::interned]
|
||||
fn intern_function(&self, loc: crate::ItemLoc<ast::FnDef>) -> crate::FunctionId;
|
||||
#[salsa::interned]
|
||||
fn intern_struct(&self, loc: crate::ItemLoc<ast::StructDef>) -> crate::StructId;
|
||||
#[salsa::interned]
|
||||
fn intern_union(&self, loc: crate::ItemLoc<ast::StructDef>) -> crate::UnionId;
|
||||
#[salsa::interned]
|
||||
fn intern_enum(&self, loc: crate::ItemLoc<ast::EnumDef>) -> crate::EnumId;
|
||||
#[salsa::interned]
|
||||
fn intern_const(&self, loc: crate::ItemLoc<ast::ConstDef>) -> crate::ConstId;
|
||||
#[salsa::interned]
|
||||
fn intern_static(&self, loc: crate::ItemLoc<ast::StaticDef>) -> crate::StaticId;
|
||||
#[salsa::interned]
|
||||
fn intern_trait(&self, loc: crate::ItemLoc<ast::TraitDef>) -> crate::TraitId;
|
||||
#[salsa::interned]
|
||||
fn intern_type_alias(&self, loc: crate::ItemLoc<ast::TypeAliasDef>) -> crate::TypeAliasId;
|
||||
}
|
||||
|
||||
#[salsa::query_group(DefDatabase2Storage)]
|
||||
pub trait DefDatabase2: InternDatabase + AstDatabase {
|
||||
#[salsa::invoke(RawItems::raw_items_with_source_map_query)]
|
||||
fn raw_items_with_source_map(
|
||||
&self,
|
||||
file_id: HirFileId,
|
||||
) -> (Arc<RawItems>, Arc<ImportSourceMap>);
|
||||
|
||||
#[salsa::invoke(RawItems::raw_items_query)]
|
||||
fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
//! `hir_def` crate contains everything between macro expansion and type
|
||||
//! inference.
|
||||
//!
|
||||
//! It defines various items (structs, enums, traits) which comprises Rust code,
|
||||
//! as well as an algorithm for resolving paths to such entities.
|
||||
//!
|
||||
//! Note that `hir_def` is a work in progress, so not all of the above is
|
||||
//! actually true.
|
||||
|
||||
pub mod db;
|
||||
pub mod attr;
|
||||
pub mod path;
|
||||
pub mod type_ref;
|
||||
pub mod builtin_type;
|
||||
|
||||
// FIXME: this should be private
|
||||
pub mod nameres;
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId};
|
||||
use ra_arena::{impl_arena_id, RawId};
|
||||
use ra_db::{salsa, CrateId, FileId};
|
||||
use ra_syntax::{ast, AstNode, SyntaxNode};
|
||||
|
||||
use crate::{builtin_type::BuiltinType, db::InternDatabase};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Source<T> {
|
||||
pub file_id: HirFileId,
|
||||
pub ast: T,
|
||||
}
|
||||
|
||||
pub enum ModuleSource {
|
||||
SourceFile(ast::SourceFile),
|
||||
Module(ast::Module),
|
||||
}
|
||||
|
||||
impl ModuleSource {
|
||||
pub fn new(
|
||||
db: &impl db::DefDatabase2,
|
||||
file_id: Option<FileId>,
|
||||
decl_id: Option<AstId<ast::Module>>,
|
||||
) -> ModuleSource {
|
||||
match (file_id, decl_id) {
|
||||
(Some(file_id), _) => {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
(None, Some(item_id)) => {
|
||||
let module = item_id.to_node(db);
|
||||
assert!(module.item_list().is_some(), "expected inline module");
|
||||
ModuleSource::Module(module)
|
||||
}
|
||||
(None, None) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this methods do not belong here
|
||||
pub fn from_position(
|
||||
db: &impl db::DefDatabase2,
|
||||
position: ra_db::FilePosition,
|
||||
) -> ModuleSource {
|
||||
let parse = db.parse(position.file_id);
|
||||
match &ra_syntax::algo::find_node_at_offset::<ast::Module>(
|
||||
parse.tree().syntax(),
|
||||
position.offset,
|
||||
) {
|
||||
Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()),
|
||||
_ => {
|
||||
let source_file = parse.tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_child_node(
|
||||
db: &impl db::DefDatabase2,
|
||||
file_id: FileId,
|
||||
child: &SyntaxNode,
|
||||
) -> ModuleSource {
|
||||
if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
|
||||
ModuleSource::Module(m)
|
||||
} else {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file_id(db: &impl db::DefDatabase2, file_id: FileId) -> ModuleSource {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Source<T> {
|
||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||
Source { file_id: self.file_id, ast: f(self.ast) }
|
||||
}
|
||||
pub fn file_syntax(&self, db: &impl AstDatabase) -> SyntaxNode {
|
||||
db.parse_or_expand(self.file_id).expect("source created from invalid file")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ModuleId {
|
||||
pub krate: CrateId,
|
||||
pub module_id: CrateModuleId,
|
||||
}
|
||||
|
||||
/// An ID of a module, **local** to a specific crate
|
||||
// FIXME: rename to `LocalModuleId`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct CrateModuleId(RawId);
|
||||
impl_arena_id!(CrateModuleId);
|
||||
|
||||
macro_rules! impl_intern_key {
|
||||
($name:ident) => {
|
||||
impl salsa::InternKey for $name {
|
||||
fn from_intern_id(v: salsa::InternId) -> Self {
|
||||
$name(v)
|
||||
}
|
||||
fn as_intern_id(&self) -> salsa::InternId {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ItemLoc<N: AstNode> {
|
||||
pub(crate) module: ModuleId,
|
||||
ast_id: AstId<N>,
|
||||
}
|
||||
|
||||
impl<N: AstNode> PartialEq for ItemLoc<N> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.module == other.module && self.ast_id == other.ast_id
|
||||
}
|
||||
}
|
||||
impl<N: AstNode> Eq for ItemLoc<N> {}
|
||||
impl<N: AstNode> Hash for ItemLoc<N> {
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.module.hash(hasher);
|
||||
self.ast_id.hash(hasher);
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> Clone for ItemLoc<N> {
|
||||
fn clone(&self) -> ItemLoc<N> {
|
||||
ItemLoc { module: self.module, ast_id: self.ast_id }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LocationCtx<DB> {
|
||||
db: DB,
|
||||
module: ModuleId,
|
||||
file_id: HirFileId,
|
||||
}
|
||||
|
||||
impl<'a, DB> LocationCtx<&'a DB> {
|
||||
pub fn new(db: &'a DB, module: ModuleId, file_id: HirFileId) -> LocationCtx<&'a DB> {
|
||||
LocationCtx { db, module, file_id }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, DB: AstDatabase + InternDatabase> LocationCtx<&'a DB> {
|
||||
pub fn to_def<N, DEF>(self, ast: &N) -> DEF
|
||||
where
|
||||
N: AstNode,
|
||||
DEF: AstItemDef<N>,
|
||||
{
|
||||
DEF::from_ast(self, ast)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AstItemDef<N: AstNode>: salsa::InternKey + Clone {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<N>) -> Self;
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<N>;
|
||||
|
||||
fn from_ast(ctx: LocationCtx<&(impl AstDatabase + InternDatabase)>, ast: &N) -> Self {
|
||||
let items = ctx.db.ast_id_map(ctx.file_id);
|
||||
let item_id = items.ast_id(ast);
|
||||
Self::from_ast_id(ctx, item_id)
|
||||
}
|
||||
fn from_ast_id(ctx: LocationCtx<&impl InternDatabase>, ast_id: FileAstId<N>) -> Self {
|
||||
let loc = ItemLoc { module: ctx.module, ast_id: AstId::new(ctx.file_id, ast_id) };
|
||||
Self::intern(ctx.db, loc)
|
||||
}
|
||||
fn source(self, db: &(impl AstDatabase + InternDatabase)) -> Source<N> {
|
||||
let loc = self.lookup_intern(db);
|
||||
let ast = loc.ast_id.to_node(db);
|
||||
Source { file_id: loc.ast_id.file_id(), ast }
|
||||
}
|
||||
fn module(self, db: &impl InternDatabase) -> ModuleId {
|
||||
let loc = self.lookup_intern(db);
|
||||
loc.module
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct FunctionId(salsa::InternId);
|
||||
impl_intern_key!(FunctionId);
|
||||
|
||||
impl AstItemDef<ast::FnDef> for FunctionId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::FnDef>) -> Self {
|
||||
db.intern_function(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::FnDef> {
|
||||
db.lookup_intern_function(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct StructId(salsa::InternId);
|
||||
impl_intern_key!(StructId);
|
||||
impl AstItemDef<ast::StructDef> for StructId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::StructDef>) -> Self {
|
||||
db.intern_struct(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::StructDef> {
|
||||
db.lookup_intern_struct(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct UnionId(salsa::InternId);
|
||||
impl_intern_key!(UnionId);
|
||||
impl AstItemDef<ast::StructDef> for UnionId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::StructDef>) -> Self {
|
||||
db.intern_union(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::StructDef> {
|
||||
db.lookup_intern_union(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct EnumId(salsa::InternId);
|
||||
impl_intern_key!(EnumId);
|
||||
impl AstItemDef<ast::EnumDef> for EnumId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::EnumDef>) -> Self {
|
||||
db.intern_enum(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::EnumDef> {
|
||||
db.lookup_intern_enum(self)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: rename to `VariantId`, only enums can ave variants
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct EnumVariantId {
|
||||
parent: EnumId,
|
||||
local_id: LocalEnumVariantId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct LocalEnumVariantId(RawId);
|
||||
impl_arena_id!(LocalEnumVariantId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ConstId(salsa::InternId);
|
||||
impl_intern_key!(ConstId);
|
||||
impl AstItemDef<ast::ConstDef> for ConstId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::ConstDef>) -> Self {
|
||||
db.intern_const(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::ConstDef> {
|
||||
db.lookup_intern_const(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct StaticId(salsa::InternId);
|
||||
impl_intern_key!(StaticId);
|
||||
impl AstItemDef<ast::StaticDef> for StaticId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::StaticDef>) -> Self {
|
||||
db.intern_static(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::StaticDef> {
|
||||
db.lookup_intern_static(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TraitId(salsa::InternId);
|
||||
impl_intern_key!(TraitId);
|
||||
impl AstItemDef<ast::TraitDef> for TraitId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::TraitDef>) -> Self {
|
||||
db.intern_trait(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::TraitDef> {
|
||||
db.lookup_intern_trait(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TypeAliasId(salsa::InternId);
|
||||
impl_intern_key!(TypeAliasId);
|
||||
impl AstItemDef<ast::TypeAliasDef> for TypeAliasId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::TypeAliasDef>) -> Self {
|
||||
db.intern_type_alias(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::TypeAliasDef> {
|
||||
db.lookup_intern_type_alias(self)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_froms {
|
||||
($e:ident: $($v:ident $(($($sv:ident),*))?),*) => {
|
||||
$(
|
||||
impl From<$v> for $e {
|
||||
fn from(it: $v) -> $e {
|
||||
$e::$v(it)
|
||||
}
|
||||
}
|
||||
$($(
|
||||
impl From<$sv> for $e {
|
||||
fn from(it: $sv) -> $e {
|
||||
$e::$v($v::$sv(it))
|
||||
}
|
||||
}
|
||||
)*)?
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
/// A Data Type
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum AdtId {
|
||||
StructId(StructId),
|
||||
UnionId(UnionId),
|
||||
EnumId(EnumId),
|
||||
}
|
||||
impl_froms!(AdtId: StructId, UnionId, EnumId);
|
||||
|
||||
/// The defs which can be visible in the module.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ModuleDefId {
|
||||
ModuleId(ModuleId),
|
||||
FunctionId(FunctionId),
|
||||
AdtId(AdtId),
|
||||
// Can't be directly declared, but can be imported.
|
||||
EnumVariantId(EnumVariantId),
|
||||
ConstId(ConstId),
|
||||
StaticId(StaticId),
|
||||
TraitId(TraitId),
|
||||
TypeAliasId(TypeAliasId),
|
||||
BuiltinType(BuiltinType),
|
||||
}
|
||||
impl_froms!(
|
||||
ModuleDefId: ModuleId,
|
||||
FunctionId,
|
||||
AdtId(StructId, EnumId, UnionId),
|
||||
EnumVariantId,
|
||||
ConstId,
|
||||
StaticId,
|
||||
TraitId,
|
||||
TypeAliasId,
|
||||
BuiltinType
|
||||
);
|
||||
@@ -0,0 +1,5 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
// FIXME: review privacy of submodules
|
||||
pub mod raw;
|
||||
pub mod mod_resolution;
|
||||
+10
-20
@@ -1,12 +1,13 @@
|
||||
//! This module resolves `mod foo;` declaration to file.
|
||||
use hir_expand::name::Name;
|
||||
use ra_db::FileId;
|
||||
use ra_syntax::SmolStr;
|
||||
use relative_path::RelativePathBuf;
|
||||
|
||||
use crate::{db::DefDatabase, HirFileId, Name};
|
||||
use crate::{db::DefDatabase2, HirFileId};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct ModDir {
|
||||
pub struct ModDir {
|
||||
/// `.` for `mod.rs`, `lib.rs`
|
||||
/// `./foo` for `foo.rs`
|
||||
/// `./foo/bar` for `mod bar { mod x; }` nested in `foo.rs`
|
||||
@@ -16,24 +17,17 @@ pub(super) struct ModDir {
|
||||
}
|
||||
|
||||
impl ModDir {
|
||||
pub(super) fn root() -> ModDir {
|
||||
pub fn root() -> ModDir {
|
||||
ModDir { path: RelativePathBuf::default(), root_non_dir_owner: false }
|
||||
}
|
||||
|
||||
pub(super) fn descend_into_definition(
|
||||
&self,
|
||||
name: &Name,
|
||||
attr_path: Option<&SmolStr>,
|
||||
) -> ModDir {
|
||||
pub fn descend_into_definition(&self, name: &Name, attr_path: Option<&SmolStr>) -> ModDir {
|
||||
let mut path = self.path.clone();
|
||||
match attr_to_path(attr_path) {
|
||||
None => path.push(&name.to_string()),
|
||||
Some(attr_path) => {
|
||||
if self.root_non_dir_owner {
|
||||
// Workaround for relative path API: turn `lib.rs` into ``.
|
||||
if !path.pop() {
|
||||
path = RelativePathBuf::default();
|
||||
}
|
||||
assert!(path.pop());
|
||||
}
|
||||
path.push(attr_path);
|
||||
}
|
||||
@@ -41,24 +35,20 @@ pub(super) fn descend_into_definition(
|
||||
ModDir { path, root_non_dir_owner: false }
|
||||
}
|
||||
|
||||
pub(super) fn resolve_declaration(
|
||||
pub fn resolve_declaration(
|
||||
&self,
|
||||
db: &impl DefDatabase,
|
||||
db: &impl DefDatabase2,
|
||||
file_id: HirFileId,
|
||||
name: &Name,
|
||||
attr_path: Option<&SmolStr>,
|
||||
) -> Result<(FileId, ModDir), RelativePathBuf> {
|
||||
let empty_path = RelativePathBuf::default();
|
||||
let file_id = file_id.original_file(db);
|
||||
|
||||
let mut candidate_files = Vec::new();
|
||||
match attr_to_path(attr_path) {
|
||||
Some(attr_path) => {
|
||||
let base = if self.root_non_dir_owner {
|
||||
self.path.parent().unwrap_or(&empty_path)
|
||||
} else {
|
||||
&self.path
|
||||
};
|
||||
let base =
|
||||
if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path };
|
||||
candidate_files.push(base.join(attr_path))
|
||||
}
|
||||
None => {
|
||||
@@ -2,18 +2,20 @@
|
||||
|
||||
use std::{ops::Index, sync::Arc};
|
||||
|
||||
use hir_expand::{
|
||||
ast_id_map::AstIdMap,
|
||||
db::AstDatabase,
|
||||
either::Either,
|
||||
hygiene::Hygiene,
|
||||
name::{AsName, Name},
|
||||
};
|
||||
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
||||
use ra_syntax::{
|
||||
ast::{self, AttrsOwner, NameOwner},
|
||||
AstNode, AstPtr, SourceFile,
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
|
||||
use crate::{
|
||||
attr::Attr,
|
||||
db::{AstDatabase, DefDatabase},
|
||||
AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source,
|
||||
};
|
||||
use crate::{attr::Attr, db::DefDatabase2, path::Path, FileAstId, HirFileId, ModuleSource, Source};
|
||||
|
||||
/// `RawItems` is a set of top-level items in a file (except for impls).
|
||||
///
|
||||
@@ -37,10 +39,8 @@ pub struct ImportSourceMap {
|
||||
type ImportSourcePtr = Either<AstPtr<ast::UseTree>, AstPtr<ast::ExternCrateItem>>;
|
||||
type ImportSource = Either<ast::UseTree, ast::ExternCrateItem>;
|
||||
|
||||
impl ImportSourcePtr {
|
||||
fn to_node(self, file: &SourceFile) -> ImportSource {
|
||||
self.map(|ptr| ptr.to_node(file.syntax()), |ptr| ptr.to_node(file.syntax()))
|
||||
}
|
||||
fn to_node(ptr: ImportSourcePtr, file: &SourceFile) -> ImportSource {
|
||||
ptr.map(|ptr| ptr.to_node(file.syntax()), |ptr| ptr.to_node(file.syntax()))
|
||||
}
|
||||
|
||||
impl ImportSourceMap {
|
||||
@@ -48,26 +48,26 @@ fn insert(&mut self, import: ImportId, ptr: ImportSourcePtr) {
|
||||
self.map.insert(import, ptr)
|
||||
}
|
||||
|
||||
pub(crate) fn get(&self, source: &ModuleSource, import: ImportId) -> ImportSource {
|
||||
pub fn get(&self, source: &ModuleSource, import: ImportId) -> ImportSource {
|
||||
let file = match source {
|
||||
ModuleSource::SourceFile(file) => file.clone(),
|
||||
ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
|
||||
};
|
||||
|
||||
self.map[import].to_node(&file)
|
||||
to_node(self.map[import], &file)
|
||||
}
|
||||
}
|
||||
|
||||
impl RawItems {
|
||||
pub(crate) fn raw_items_query(
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
db: &(impl DefDatabase2 + AstDatabase),
|
||||
file_id: HirFileId,
|
||||
) -> Arc<RawItems> {
|
||||
db.raw_items_with_source_map(file_id).0
|
||||
}
|
||||
|
||||
pub(crate) fn raw_items_with_source_map_query(
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
db: &(impl DefDatabase2 + AstDatabase),
|
||||
file_id: HirFileId,
|
||||
) -> (Arc<RawItems>, Arc<ImportSourceMap>) {
|
||||
let mut collector = RawItemsCollector {
|
||||
@@ -75,7 +75,7 @@ pub(crate) fn raw_items_with_source_map_query(
|
||||
source_ast_id_map: db.ast_id_map(file_id),
|
||||
source_map: ImportSourceMap::default(),
|
||||
file_id,
|
||||
db,
|
||||
hygiene: Hygiene::new(db, file_id),
|
||||
};
|
||||
if let Some(node) = db.parse_or_expand(file_id) {
|
||||
if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
|
||||
@@ -87,7 +87,7 @@ pub(crate) fn raw_items_with_source_map_query(
|
||||
(Arc::new(collector.raw_items), Arc::new(collector.source_map))
|
||||
}
|
||||
|
||||
pub(super) fn items(&self) -> &[RawItem] {
|
||||
pub fn items(&self) -> &[RawItem] {
|
||||
&self.items
|
||||
}
|
||||
}
|
||||
@@ -124,19 +124,19 @@ fn index(&self, idx: Macro) -> &MacroData {
|
||||
type Attrs = Option<Arc<[Attr]>>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub(super) struct RawItem {
|
||||
pub struct RawItem {
|
||||
attrs: Attrs,
|
||||
pub(super) kind: RawItemKind,
|
||||
pub kind: RawItemKind,
|
||||
}
|
||||
|
||||
impl RawItem {
|
||||
pub(super) fn attrs(&self) -> &[Attr] {
|
||||
pub fn attrs(&self) -> &[Attr] {
|
||||
self.attrs.as_ref().map_or(&[], |it| &*it)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(super) enum RawItemKind {
|
||||
pub enum RawItemKind {
|
||||
Module(Module),
|
||||
Import(ImportId),
|
||||
Def(Def),
|
||||
@@ -144,11 +144,11 @@ pub(super) enum RawItemKind {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(super) struct Module(RawId);
|
||||
pub struct Module(RawId);
|
||||
impl_arena_id!(Module);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) enum ModuleData {
|
||||
pub enum ModuleData {
|
||||
Declaration { name: Name, ast_id: FileAstId<ast::Module> },
|
||||
Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
|
||||
}
|
||||
@@ -159,26 +159,26 @@ pub(super) enum ModuleData {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ImportData {
|
||||
pub(super) path: Path,
|
||||
pub(super) alias: Option<Name>,
|
||||
pub(super) is_glob: bool,
|
||||
pub(super) is_prelude: bool,
|
||||
pub(super) is_extern_crate: bool,
|
||||
pub(super) is_macro_use: bool,
|
||||
pub path: Path,
|
||||
pub alias: Option<Name>,
|
||||
pub is_glob: bool,
|
||||
pub is_prelude: bool,
|
||||
pub is_extern_crate: bool,
|
||||
pub is_macro_use: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(super) struct Def(RawId);
|
||||
pub struct Def(RawId);
|
||||
impl_arena_id!(Def);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct DefData {
|
||||
pub(super) name: Name,
|
||||
pub(super) kind: DefKind,
|
||||
pub struct DefData {
|
||||
pub name: Name,
|
||||
pub kind: DefKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(super) enum DefKind {
|
||||
pub enum DefKind {
|
||||
Function(FileAstId<ast::FnDef>),
|
||||
Struct(FileAstId<ast::StructDef>),
|
||||
Union(FileAstId<ast::StructDef>),
|
||||
@@ -190,26 +190,26 @@ pub(super) enum DefKind {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(super) struct Macro(RawId);
|
||||
pub struct Macro(RawId);
|
||||
impl_arena_id!(Macro);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct MacroData {
|
||||
pub(super) ast_id: FileAstId<ast::MacroCall>,
|
||||
pub(super) path: Path,
|
||||
pub(super) name: Option<Name>,
|
||||
pub(super) export: bool,
|
||||
pub struct MacroData {
|
||||
pub ast_id: FileAstId<ast::MacroCall>,
|
||||
pub path: Path,
|
||||
pub name: Option<Name>,
|
||||
pub export: bool,
|
||||
}
|
||||
|
||||
struct RawItemsCollector<DB> {
|
||||
struct RawItemsCollector {
|
||||
raw_items: RawItems,
|
||||
source_ast_id_map: Arc<AstIdMap>,
|
||||
source_map: ImportSourceMap,
|
||||
file_id: HirFileId,
|
||||
db: DB,
|
||||
hygiene: Hygiene,
|
||||
}
|
||||
|
||||
impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
||||
impl RawItemsCollector {
|
||||
fn process_module(&mut self, current_module: Option<Module>, body: impl ast::ModuleItemOwner) {
|
||||
for item_or_macro in body.items_with_macros() {
|
||||
match item_or_macro {
|
||||
@@ -297,7 +297,8 @@ fn add_module(&mut self, current_module: Option<Module>, module: ast::Module) {
|
||||
self.push_item(current_module, attrs, RawItemKind::Module(item));
|
||||
return;
|
||||
}
|
||||
tested_by!(name_res_works_for_broken_modules);
|
||||
// FIXME: restore this mark once we complete hir splitting
|
||||
// tested_by!(name_res_works_for_broken_modules);
|
||||
}
|
||||
|
||||
fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
|
||||
@@ -305,9 +306,10 @@ fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseIte
|
||||
let is_prelude = use_item.has_atom_attr("prelude_import");
|
||||
let attrs = self.parse_attrs(&use_item);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
Path::expand_use_item(
|
||||
Source { ast: use_item, file_id: self.file_id },
|
||||
self.db,
|
||||
&self.hygiene,
|
||||
|path, use_tree, is_glob, alias| {
|
||||
let import_data = ImportData {
|
||||
path,
|
||||
@@ -317,14 +319,12 @@ fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseIte
|
||||
is_extern_crate: false,
|
||||
is_macro_use: false,
|
||||
};
|
||||
self.push_import(
|
||||
current_module,
|
||||
attrs.clone(),
|
||||
import_data,
|
||||
Either::A(AstPtr::new(use_tree)),
|
||||
);
|
||||
buf.push((import_data, Either::A(AstPtr::new(use_tree))));
|
||||
},
|
||||
)
|
||||
);
|
||||
for (import_data, ptr) in buf {
|
||||
self.push_import(current_module, attrs.clone(), import_data, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_extern_crate_item(
|
||||
@@ -357,10 +357,7 @@ fn add_extern_crate_item(
|
||||
|
||||
fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
|
||||
let attrs = self.parse_attrs(&m);
|
||||
let path = match m
|
||||
.path()
|
||||
.and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db))
|
||||
{
|
||||
let path = match m.path().and_then(|path| Path::from_src(path, &self.hygiene)) {
|
||||
Some(it) => it,
|
||||
_ => return,
|
||||
};
|
||||
@@ -398,6 +395,6 @@ fn push_item(&mut self, current_module: Option<Module>, attrs: Attrs, kind: RawI
|
||||
}
|
||||
|
||||
fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
|
||||
Attr::from_attrs_owner(self.file_id, item, self.db)
|
||||
Attr::from_attrs_owner(item, &self.hygiene)
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,18 @@
|
||||
|
||||
use std::{iter, sync::Arc};
|
||||
|
||||
use hir_expand::{
|
||||
either::Either,
|
||||
hygiene::Hygiene,
|
||||
name::{self, AsName, Name},
|
||||
};
|
||||
use ra_db::CrateId;
|
||||
use ra_syntax::{
|
||||
ast::{self, NameOwner, TypeAscriptionOwner},
|
||||
AstNode,
|
||||
};
|
||||
|
||||
use crate::{db::AstDatabase, name, type_ref::TypeRef, AsName, Crate, Name, Source};
|
||||
use crate::{type_ref::TypeRef, Source};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Path {
|
||||
@@ -55,18 +61,18 @@ pub enum PathKind {
|
||||
// Type based path like `<T>::foo`
|
||||
Type(Box<TypeRef>),
|
||||
// `$crate` from macro expansion
|
||||
DollarCrate(Crate),
|
||||
DollarCrate(CrateId),
|
||||
}
|
||||
|
||||
impl Path {
|
||||
/// Calls `cb` with all paths, represented by this use item.
|
||||
pub fn expand_use_item(
|
||||
item_src: Source<ast::UseItem>,
|
||||
db: &impl AstDatabase,
|
||||
hygiene: &Hygiene,
|
||||
mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||
) {
|
||||
if let Some(tree) = item_src.ast.use_tree() {
|
||||
expand_use_tree(None, tree, &|| item_src.file_id.macro_crate(db), &mut cb);
|
||||
expand_use_tree(None, tree, hygiene, &mut cb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,17 +89,12 @@ pub fn from_simple_segments(kind: PathKind, segments: impl IntoIterator<Item = N
|
||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||
/// DEPRECATED: It does not handle `$crate` from macro call.
|
||||
pub fn from_ast(path: ast::Path) -> Option<Path> {
|
||||
Path::parse(path, &|| None)
|
||||
Path::from_src(path, &Hygiene::new_unhygienic())
|
||||
}
|
||||
|
||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||
/// It correctly handles `$crate` based path from macro call.
|
||||
pub fn from_src(source: Source<ast::Path>, db: &impl AstDatabase) -> Option<Path> {
|
||||
let file_id = source.file_id;
|
||||
Path::parse(source.ast, &|| file_id.macro_crate(db))
|
||||
}
|
||||
|
||||
fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option<Crate>) -> Option<Path> {
|
||||
pub fn from_src(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
|
||||
let mut kind = PathKind::Plain;
|
||||
let mut segments = Vec::new();
|
||||
loop {
|
||||
@@ -104,26 +105,28 @@ fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option<Crate>) -> Optio
|
||||
}
|
||||
|
||||
match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name) => {
|
||||
if name.text() == "$crate" {
|
||||
if let Some(macro_crate) = macro_crate() {
|
||||
kind = PathKind::DollarCrate(macro_crate);
|
||||
ast::PathSegmentKind::Name(name_ref) => {
|
||||
// FIXME: this should just return name
|
||||
match hygiene.name_ref_to_name(name_ref) {
|
||||
Either::A(name) => {
|
||||
let args = segment
|
||||
.type_arg_list()
|
||||
.and_then(GenericArgs::from_ast)
|
||||
.or_else(|| {
|
||||
GenericArgs::from_fn_like_path_ast(
|
||||
segment.param_list(),
|
||||
segment.ret_type(),
|
||||
)
|
||||
})
|
||||
.map(Arc::new);
|
||||
let segment = PathSegment { name, args_and_bindings: args };
|
||||
segments.push(segment);
|
||||
}
|
||||
Either::B(crate_id) => {
|
||||
kind = PathKind::DollarCrate(crate_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let args = segment
|
||||
.type_arg_list()
|
||||
.and_then(GenericArgs::from_ast)
|
||||
.or_else(|| {
|
||||
GenericArgs::from_fn_like_path_ast(
|
||||
segment.param_list(),
|
||||
segment.ret_type(),
|
||||
)
|
||||
})
|
||||
.map(Arc::new);
|
||||
let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
|
||||
segments.push(segment);
|
||||
}
|
||||
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
|
||||
assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
@@ -137,7 +140,7 @@ fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option<Crate>) -> Optio
|
||||
}
|
||||
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||
Some(trait_ref) => {
|
||||
let path = Path::parse(trait_ref.path()?, macro_crate)?;
|
||||
let path = Path::from_src(trait_ref.path()?, hygiene)?;
|
||||
kind = path.kind;
|
||||
let mut prefix_segments = path.segments;
|
||||
prefix_segments.reverse();
|
||||
@@ -224,7 +227,7 @@ pub fn is_type_relative(&self) -> bool {
|
||||
}
|
||||
|
||||
impl GenericArgs {
|
||||
pub(crate) fn from_ast(node: ast::TypeArgList) -> Option<GenericArgs> {
|
||||
pub fn from_ast(node: ast::TypeArgList) -> Option<GenericArgs> {
|
||||
let mut args = Vec::new();
|
||||
for type_arg in node.type_args() {
|
||||
let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
|
||||
@@ -288,8 +291,8 @@ fn from(name: Name) -> Path {
|
||||
fn expand_use_tree(
|
||||
prefix: Option<Path>,
|
||||
tree: ast::UseTree,
|
||||
macro_crate: &impl Fn() -> Option<Crate>,
|
||||
cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||
hygiene: &Hygiene,
|
||||
cb: &mut dyn FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||
) {
|
||||
if let Some(use_tree_list) = tree.use_tree_list() {
|
||||
let prefix = match tree.path() {
|
||||
@@ -297,13 +300,13 @@ fn expand_use_tree(
|
||||
None => prefix,
|
||||
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
|
||||
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
|
||||
Some(path) => match convert_path(prefix, path, macro_crate) {
|
||||
Some(path) => match convert_path(prefix, path, hygiene) {
|
||||
Some(it) => Some(it),
|
||||
None => return, // FIXME: report errors somewhere
|
||||
},
|
||||
};
|
||||
for child_tree in use_tree_list.use_trees() {
|
||||
expand_use_tree(prefix.clone(), child_tree, macro_crate, cb);
|
||||
expand_use_tree(prefix.clone(), child_tree, hygiene, cb);
|
||||
}
|
||||
} else {
|
||||
let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
|
||||
@@ -320,7 +323,7 @@ fn expand_use_tree(
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(path) = convert_path(prefix, ast_path, macro_crate) {
|
||||
if let Some(path) = convert_path(prefix, ast_path, hygiene) {
|
||||
let is_glob = tree.has_star();
|
||||
cb(path, &tree, is_glob, alias)
|
||||
}
|
||||
@@ -330,37 +333,36 @@ fn expand_use_tree(
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_path(
|
||||
prefix: Option<Path>,
|
||||
path: ast::Path,
|
||||
macro_crate: &impl Fn() -> Option<Crate>,
|
||||
) -> Option<Path> {
|
||||
fn convert_path(prefix: Option<Path>, path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
|
||||
let prefix = if let Some(qual) = path.qualifier() {
|
||||
Some(convert_path(prefix, qual, macro_crate)?)
|
||||
Some(convert_path(prefix, qual, hygiene)?)
|
||||
} else {
|
||||
prefix
|
||||
};
|
||||
|
||||
let segment = path.segment()?;
|
||||
let res = match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name) => {
|
||||
if name.text() == "$crate" {
|
||||
if let Some(krate) = macro_crate() {
|
||||
ast::PathSegmentKind::Name(name_ref) => {
|
||||
match hygiene.name_ref_to_name(name_ref) {
|
||||
Either::A(name) => {
|
||||
// no type args in use
|
||||
let mut res = prefix.unwrap_or_else(|| Path {
|
||||
kind: PathKind::Plain,
|
||||
segments: Vec::with_capacity(1),
|
||||
});
|
||||
res.segments.push(PathSegment {
|
||||
name,
|
||||
args_and_bindings: None, // no type args in use
|
||||
});
|
||||
res
|
||||
}
|
||||
Either::B(crate_id) => {
|
||||
return Some(Path::from_simple_segments(
|
||||
PathKind::DollarCrate(krate),
|
||||
PathKind::DollarCrate(crate_id),
|
||||
iter::empty(),
|
||||
));
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// no type args in use
|
||||
let mut res = prefix
|
||||
.unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) });
|
||||
res.segments.push(PathSegment {
|
||||
name: name.as_name(),
|
||||
args_and_bindings: None, // no type args in use
|
||||
});
|
||||
res
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => {
|
||||
if prefix.is_some() {
|
||||
@@ -389,8 +391,9 @@ fn convert_path(
|
||||
}
|
||||
|
||||
pub mod known {
|
||||
use hir_expand::name;
|
||||
|
||||
use super::{Path, PathKind};
|
||||
use crate::name;
|
||||
|
||||
pub fn std_iter_into_iterator() -> Path {
|
||||
Path::from_simple_segments(
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use ra_syntax::ast::{self, TypeAscriptionOwner, TypeBoundsOwner};
|
||||
|
||||
use crate::Path;
|
||||
use crate::path::Path;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Mutability {
|
||||
@@ -64,7 +64,7 @@ pub enum TypeBound {
|
||||
|
||||
impl TypeRef {
|
||||
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
|
||||
pub(crate) fn from_ast(node: ast::TypeRef) -> Self {
|
||||
pub fn from_ast(node: ast::TypeRef) -> Self {
|
||||
match node {
|
||||
ast::TypeRef::ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
|
||||
ast::TypeRef::TupleType(inner) => {
|
||||
@@ -113,7 +113,7 @@ pub(crate) fn from_ast(node: ast::TypeRef) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast_opt(node: Option<ast::TypeRef>) -> Self {
|
||||
pub fn from_ast_opt(node: Option<ast::TypeRef>) -> Self {
|
||||
if let Some(node) = node {
|
||||
TypeRef::from_ast(node)
|
||||
} else {
|
||||
@@ -135,7 +135,7 @@ pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option<ast::TypeBoundList>)
|
||||
}
|
||||
|
||||
impl TypeBound {
|
||||
pub(crate) fn from_ast(node: ast::TypeBound) -> Self {
|
||||
pub fn from_ast(node: ast::TypeBound) -> Self {
|
||||
match node.kind() {
|
||||
ast::TypeBoundKind::PathType(path_type) => {
|
||||
let path = match path_type.path() {
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "ra_hir_expand"
|
||||
version = "0.1.0"
|
||||
authors = ["rust-analyzer developers"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.5"
|
||||
|
||||
ra_arena = { path = "../ra_arena" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
ra_syntax = { path = "../ra_syntax" }
|
||||
ra_prof = { path = "../ra_prof" }
|
||||
tt = { path = "../ra_tt", package = "ra_tt" }
|
||||
mbe = { path = "../ra_mbe", package = "ra_mbe" }
|
||||
@@ -1,58 +1,21 @@
|
||||
//! FIXME: write short doc here
|
||||
//! `AstIdMap` allows to create stable IDs for "large" syntax nodes like items
|
||||
//! and macro calls.
|
||||
//!
|
||||
//! Specifically, it enumerates all items in a file and uses position of a an
|
||||
//! item as an ID. That way, id's don't change unless the set of items itself
|
||||
//! changes.
|
||||
|
||||
use std::{
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use ra_arena::{impl_arena_id, Arena, RawId};
|
||||
use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr};
|
||||
|
||||
use crate::{db::AstDatabase, HirFileId};
|
||||
|
||||
/// `AstId` points to an AST node in any file.
|
||||
///
|
||||
/// It is stable across reparses, and can be used as salsa key/value.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AstId<N: AstNode> {
|
||||
file_id: HirFileId,
|
||||
file_ast_id: FileAstId<N>,
|
||||
}
|
||||
|
||||
impl<N: AstNode> Clone for AstId<N> {
|
||||
fn clone(&self) -> AstId<N> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<N: AstNode> Copy for AstId<N> {}
|
||||
|
||||
impl<N: AstNode> PartialEq for AstId<N> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id)
|
||||
}
|
||||
}
|
||||
impl<N: AstNode> Eq for AstId<N> {}
|
||||
impl<N: AstNode> Hash for AstId<N> {
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
(self.file_id, self.file_ast_id).hash(hasher);
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> AstId<N> {
|
||||
pub(crate) fn file_id(&self) -> HirFileId {
|
||||
self.file_id
|
||||
}
|
||||
|
||||
pub(crate) fn to_node(&self, db: &impl AstDatabase) -> N {
|
||||
let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.raw);
|
||||
N::cast(syntax_node).unwrap()
|
||||
}
|
||||
}
|
||||
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
|
||||
|
||||
/// `AstId` points to an AST node in a specific file.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FileAstId<N: AstNode> {
|
||||
pub struct FileAstId<N: AstNode> {
|
||||
raw: ErasedFileAstId,
|
||||
_ty: PhantomData<fn() -> N>,
|
||||
}
|
||||
@@ -76,14 +39,8 @@ fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> FileAstId<N> {
|
||||
pub(crate) fn with_file_id(self, file_id: HirFileId) -> AstId<N> {
|
||||
AstId { file_id, file_ast_id: self }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ErasedFileAstId(RawId);
|
||||
struct ErasedFileAstId(RawId);
|
||||
impl_arena_id!(ErasedFileAstId);
|
||||
|
||||
/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
|
||||
@@ -93,39 +50,7 @@ pub struct AstIdMap {
|
||||
}
|
||||
|
||||
impl AstIdMap {
|
||||
pub(crate) fn ast_id_map_query(db: &impl AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
|
||||
let map = if let Some(node) = db.parse_or_expand(file_id) {
|
||||
AstIdMap::from_source(&node)
|
||||
} else {
|
||||
AstIdMap::default()
|
||||
};
|
||||
Arc::new(map)
|
||||
}
|
||||
|
||||
pub(crate) fn file_item_query(
|
||||
db: &impl AstDatabase,
|
||||
file_id: HirFileId,
|
||||
ast_id: ErasedFileAstId,
|
||||
) -> SyntaxNode {
|
||||
let node = db.parse_or_expand(file_id).unwrap();
|
||||
db.ast_id_map(file_id).arena[ast_id].to_node(&node)
|
||||
}
|
||||
|
||||
pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
|
||||
let ptr = SyntaxNodePtr::new(item.syntax());
|
||||
let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) {
|
||||
Some((it, _)) => it,
|
||||
None => panic!(
|
||||
"Can't find {:?} in AstIdMap:\n{:?}",
|
||||
item.syntax(),
|
||||
self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
|
||||
),
|
||||
};
|
||||
|
||||
FileAstId { raw, _ty: PhantomData }
|
||||
}
|
||||
|
||||
fn from_source(node: &SyntaxNode) -> AstIdMap {
|
||||
pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap {
|
||||
assert!(node.parent().is_none());
|
||||
let mut res = AstIdMap { arena: Arena::default() };
|
||||
// By walking the tree in bread-first order we make sure that parents
|
||||
@@ -142,6 +67,26 @@ fn from_source(node: &SyntaxNode) -> AstIdMap {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
|
||||
let raw = self.erased_ast_id(item.syntax());
|
||||
FileAstId { raw, _ty: PhantomData }
|
||||
}
|
||||
fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
|
||||
let ptr = SyntaxNodePtr::new(item);
|
||||
match self.arena.iter().find(|(_id, i)| **i == ptr) {
|
||||
Some((it, _)) => it,
|
||||
None => panic!(
|
||||
"Can't find {:?} in AstIdMap:\n{:?}",
|
||||
item,
|
||||
self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
|
||||
self.arena[id.raw].cast::<N>().unwrap()
|
||||
}
|
||||
|
||||
fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
|
||||
self.arena.alloc(SyntaxNodePtr::new(item))
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
//! Defines database & queries for macro expansion.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use mbe::MacroRules;
|
||||
use ra_db::{salsa, SourceDatabase};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{AstNode, Parse, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
ast_id_map::AstIdMap, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId,
|
||||
MacroFile, MacroFileKind,
|
||||
};
|
||||
|
||||
// FIXME: rename to ExpandDatabase
|
||||
#[salsa::query_group(AstDatabaseStorage)]
|
||||
pub trait AstDatabase: SourceDatabase {
|
||||
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
|
||||
|
||||
#[salsa::transparent]
|
||||
fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
|
||||
|
||||
#[salsa::interned]
|
||||
fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId;
|
||||
fn macro_arg(&self, id: MacroCallId) -> Option<Arc<tt::Subtree>>;
|
||||
fn macro_def(&self, id: MacroDefId) -> Option<Arc<mbe::MacroRules>>;
|
||||
fn parse_macro(&self, macro_file: MacroFile) -> Option<Parse<SyntaxNode>>;
|
||||
fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>;
|
||||
}
|
||||
|
||||
pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
|
||||
let map =
|
||||
db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it));
|
||||
Arc::new(map)
|
||||
}
|
||||
|
||||
pub(crate) fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
|
||||
let macro_call = id.ast_id.to_node(db);
|
||||
let arg = macro_call.token_tree()?;
|
||||
let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| {
|
||||
log::warn!("fail on macro_def to token tree: {:#?}", arg);
|
||||
None
|
||||
})?;
|
||||
let rules = MacroRules::parse(&tt).ok().or_else(|| {
|
||||
log::warn!("fail on macro_def parse: {:#?}", tt);
|
||||
None
|
||||
})?;
|
||||
Some(Arc::new(rules))
|
||||
}
|
||||
|
||||
pub(crate) fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<tt::Subtree>> {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
let macro_call = loc.ast_id.to_node(db);
|
||||
let arg = macro_call.token_tree()?;
|
||||
let (tt, _) = mbe::ast_to_token_tree(&arg)?;
|
||||
Some(Arc::new(tt))
|
||||
}
|
||||
|
||||
pub(crate) fn macro_expand(
|
||||
db: &dyn AstDatabase,
|
||||
id: MacroCallId,
|
||||
) -> Result<Arc<tt::Subtree>, String> {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
|
||||
|
||||
let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
|
||||
let tt = macro_rules.expand(¯o_arg).map_err(|err| format!("{:?}", err))?;
|
||||
// Set a hard limit for the expanded tt
|
||||
let count = tt.count();
|
||||
if count > 65536 {
|
||||
return Err(format!("Total tokens count exceed limit : count = {}", count));
|
||||
}
|
||||
Ok(Arc::new(tt))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
|
||||
match file_id.0 {
|
||||
HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
db.parse_macro(macro_file).map(|it| it.syntax_node())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_macro(
|
||||
db: &dyn AstDatabase,
|
||||
macro_file: MacroFile,
|
||||
) -> Option<Parse<SyntaxNode>> {
|
||||
let _p = profile("parse_macro_query");
|
||||
let macro_call_id = macro_file.macro_call_id;
|
||||
let tt = db
|
||||
.macro_expand(macro_call_id)
|
||||
.map_err(|err| {
|
||||
// Note:
|
||||
// The final goal we would like to make all parse_macro success,
|
||||
// such that the following log will not call anyway.
|
||||
log::warn!("fail on macro_parse: (reason: {})", err,);
|
||||
})
|
||||
.ok()?;
|
||||
match macro_file.macro_file_kind {
|
||||
MacroFileKind::Items => mbe::token_tree_to_items(&tt).ok().map(Parse::to_syntax),
|
||||
MacroFileKind::Expr => mbe::token_tree_to_expr(&tt).ok().map(Parse::to_syntax),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//! This modules handles hygiene information.
|
||||
//!
|
||||
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
|
||||
//! this moment, this is horribly incomplete and handles only `$crate`.
|
||||
use ra_db::CrateId;
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::{
|
||||
db::AstDatabase,
|
||||
either::Either,
|
||||
name::{AsName, Name},
|
||||
HirFileId, HirFileIdRepr,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Hygiene {
|
||||
// This is what `$crate` expands to
|
||||
def_crate: Option<CrateId>,
|
||||
}
|
||||
|
||||
impl Hygiene {
|
||||
pub fn new(db: &impl AstDatabase, file_id: HirFileId) -> Hygiene {
|
||||
let def_crate = match file_id.0 {
|
||||
HirFileIdRepr::FileId(_) => None,
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let loc = db.lookup_intern_macro(macro_file.macro_call_id);
|
||||
Some(loc.def.krate)
|
||||
}
|
||||
};
|
||||
Hygiene { def_crate }
|
||||
}
|
||||
|
||||
pub fn new_unhygienic() -> Hygiene {
|
||||
Hygiene { def_crate: None }
|
||||
}
|
||||
|
||||
// FIXME: this should just return name
|
||||
pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
|
||||
if let Some(def_crate) = self.def_crate {
|
||||
if name_ref.text() == "$crate" {
|
||||
return Either::B(def_crate);
|
||||
}
|
||||
}
|
||||
Either::A(name_ref.as_name())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
//! `ra_hir_expand` deals with macro expansion.
|
||||
//!
|
||||
//! Specifically, it implements a concept of `MacroFile` -- a file whose syntax
|
||||
//! tree originates not from the text of some `FileId`, but from some macro
|
||||
//! expansion.
|
||||
|
||||
pub mod db;
|
||||
pub mod ast_id_map;
|
||||
pub mod either;
|
||||
pub mod name;
|
||||
pub mod hygiene;
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use ra_db::{salsa, CrateId, FileId};
|
||||
use ra_syntax::ast::{self, AstNode};
|
||||
|
||||
use crate::ast_id_map::FileAstId;
|
||||
|
||||
/// Input to the analyzer is a set of files, where each file is identified by
|
||||
/// `FileId` and contains source code. However, another source of source code in
|
||||
/// Rust are macros: each macro can be thought of as producing a "temporary
|
||||
/// file". To assign an id to such a file, we use the id of the macro call that
|
||||
/// produced the file. So, a `HirFileId` is either a `FileId` (source code
|
||||
/// written by user), or a `MacroCallId` (source code produced by macro).
|
||||
///
|
||||
/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
|
||||
/// containing the call plus the offset of the macro call in the file. Note that
|
||||
/// this is a recursive definition! However, the size_of of `HirFileId` is
|
||||
/// finite (because everything bottoms out at the real `FileId`) and small
|
||||
/// (`MacroCallId` uses the location interner).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct HirFileId(HirFileIdRepr);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum HirFileIdRepr {
|
||||
FileId(FileId),
|
||||
MacroFile(MacroFile),
|
||||
}
|
||||
|
||||
impl From<FileId> for HirFileId {
|
||||
fn from(id: FileId) -> Self {
|
||||
HirFileId(HirFileIdRepr::FileId(id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MacroFile> for HirFileId {
|
||||
fn from(id: MacroFile) -> Self {
|
||||
HirFileId(HirFileIdRepr::MacroFile(id))
|
||||
}
|
||||
}
|
||||
|
||||
impl HirFileId {
|
||||
/// For macro-expansion files, returns the file original source file the
|
||||
/// expansion originated from.
|
||||
pub fn original_file(self, db: &dyn db::AstDatabase) -> FileId {
|
||||
match self.0 {
|
||||
HirFileIdRepr::FileId(file_id) => file_id,
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let loc = db.lookup_intern_macro(macro_file.macro_call_id);
|
||||
loc.ast_id.file_id().original_file(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct MacroFile {
|
||||
macro_call_id: MacroCallId,
|
||||
macro_file_kind: MacroFileKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum MacroFileKind {
|
||||
Items,
|
||||
Expr,
|
||||
}
|
||||
|
||||
/// `MacroCallId` identifies a particular macro invocation, like
|
||||
/// `println!("Hello, {}", world)`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct MacroCallId(salsa::InternId);
|
||||
impl salsa::InternKey for MacroCallId {
|
||||
fn from_intern_id(v: salsa::InternId) -> Self {
|
||||
MacroCallId(v)
|
||||
}
|
||||
fn as_intern_id(&self) -> salsa::InternId {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct MacroDefId {
|
||||
pub krate: CrateId,
|
||||
pub ast_id: AstId<ast::MacroCall>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MacroCallLoc {
|
||||
pub def: MacroDefId,
|
||||
pub ast_id: AstId<ast::MacroCall>,
|
||||
}
|
||||
|
||||
impl MacroCallId {
|
||||
pub fn as_file(self, kind: MacroFileKind) -> HirFileId {
|
||||
let macro_file = MacroFile { macro_call_id: self, macro_file_kind: kind };
|
||||
macro_file.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// `AstId` points to an AST node in any file.
|
||||
///
|
||||
/// It is stable across reparses, and can be used as salsa key/value.
|
||||
// FIXME: isn't this just a `Source<FileAstId<N>>` ?
|
||||
#[derive(Debug)]
|
||||
pub struct AstId<N: AstNode> {
|
||||
file_id: HirFileId,
|
||||
file_ast_id: FileAstId<N>,
|
||||
}
|
||||
|
||||
impl<N: AstNode> Clone for AstId<N> {
|
||||
fn clone(&self) -> AstId<N> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<N: AstNode> Copy for AstId<N> {}
|
||||
|
||||
impl<N: AstNode> PartialEq for AstId<N> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id)
|
||||
}
|
||||
}
|
||||
impl<N: AstNode> Eq for AstId<N> {}
|
||||
impl<N: AstNode> Hash for AstId<N> {
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
(self.file_id, self.file_ast_id).hash(hasher);
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> AstId<N> {
|
||||
pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
|
||||
AstId { file_id, file_ast_id }
|
||||
}
|
||||
|
||||
pub fn file_id(&self) -> HirFileId {
|
||||
self.file_id
|
||||
}
|
||||
|
||||
pub fn to_node(&self, db: &dyn db::AstDatabase) -> N {
|
||||
let root = db.parse_or_expand(self.file_id).unwrap();
|
||||
db.ast_id_map(self.file_id).get(self.file_ast_id).to_node(&root)
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ const fn new_text(text: SmolStr) -> Name {
|
||||
Name(Repr::Text(text))
|
||||
}
|
||||
|
||||
pub(crate) fn new_tuple_field(idx: usize) -> Name {
|
||||
pub fn new_tuple_field(idx: usize) -> Name {
|
||||
Name(Repr::TupleField(idx))
|
||||
}
|
||||
|
||||
@@ -52,11 +52,11 @@ fn resolve(raw_text: &SmolStr) -> Name {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn missing() -> Name {
|
||||
pub fn missing() -> Name {
|
||||
Name::new_text("[missing name]".into())
|
||||
}
|
||||
|
||||
pub(crate) fn as_tuple_index(&self) -> Option<usize> {
|
||||
pub fn as_tuple_index(&self) -> Option<usize> {
|
||||
match self.0 {
|
||||
Repr::TupleField(idx) => Some(idx),
|
||||
_ => None,
|
||||
@@ -64,7 +64,7 @@ pub(crate) fn as_tuple_index(&self) -> Option<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait AsName {
|
||||
pub trait AsName {
|
||||
fn as_name(&self) -> Name;
|
||||
}
|
||||
|
||||
@@ -99,44 +99,44 @@ fn as_name(&self) -> Name {
|
||||
}
|
||||
|
||||
// Primitives
|
||||
pub(crate) const ISIZE: Name = Name::new_inline_ascii(5, b"isize");
|
||||
pub(crate) const I8: Name = Name::new_inline_ascii(2, b"i8");
|
||||
pub(crate) const I16: Name = Name::new_inline_ascii(3, b"i16");
|
||||
pub(crate) const I32: Name = Name::new_inline_ascii(3, b"i32");
|
||||
pub(crate) const I64: Name = Name::new_inline_ascii(3, b"i64");
|
||||
pub(crate) const I128: Name = Name::new_inline_ascii(4, b"i128");
|
||||
pub(crate) const USIZE: Name = Name::new_inline_ascii(5, b"usize");
|
||||
pub(crate) const U8: Name = Name::new_inline_ascii(2, b"u8");
|
||||
pub(crate) const U16: Name = Name::new_inline_ascii(3, b"u16");
|
||||
pub(crate) const U32: Name = Name::new_inline_ascii(3, b"u32");
|
||||
pub(crate) const U64: Name = Name::new_inline_ascii(3, b"u64");
|
||||
pub(crate) const U128: Name = Name::new_inline_ascii(4, b"u128");
|
||||
pub(crate) const F32: Name = Name::new_inline_ascii(3, b"f32");
|
||||
pub(crate) const F64: Name = Name::new_inline_ascii(3, b"f64");
|
||||
pub(crate) const BOOL: Name = Name::new_inline_ascii(4, b"bool");
|
||||
pub(crate) const CHAR: Name = Name::new_inline_ascii(4, b"char");
|
||||
pub(crate) const STR: Name = Name::new_inline_ascii(3, b"str");
|
||||
pub const ISIZE: Name = Name::new_inline_ascii(5, b"isize");
|
||||
pub const I8: Name = Name::new_inline_ascii(2, b"i8");
|
||||
pub const I16: Name = Name::new_inline_ascii(3, b"i16");
|
||||
pub const I32: Name = Name::new_inline_ascii(3, b"i32");
|
||||
pub const I64: Name = Name::new_inline_ascii(3, b"i64");
|
||||
pub const I128: Name = Name::new_inline_ascii(4, b"i128");
|
||||
pub const USIZE: Name = Name::new_inline_ascii(5, b"usize");
|
||||
pub const U8: Name = Name::new_inline_ascii(2, b"u8");
|
||||
pub const U16: Name = Name::new_inline_ascii(3, b"u16");
|
||||
pub const U32: Name = Name::new_inline_ascii(3, b"u32");
|
||||
pub const U64: Name = Name::new_inline_ascii(3, b"u64");
|
||||
pub const U128: Name = Name::new_inline_ascii(4, b"u128");
|
||||
pub const F32: Name = Name::new_inline_ascii(3, b"f32");
|
||||
pub const F64: Name = Name::new_inline_ascii(3, b"f64");
|
||||
pub const BOOL: Name = Name::new_inline_ascii(4, b"bool");
|
||||
pub const CHAR: Name = Name::new_inline_ascii(4, b"char");
|
||||
pub const STR: Name = Name::new_inline_ascii(3, b"str");
|
||||
|
||||
// Special names
|
||||
pub(crate) const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self");
|
||||
pub(crate) const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self");
|
||||
pub(crate) const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules");
|
||||
pub const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self");
|
||||
pub const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self");
|
||||
pub const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules");
|
||||
|
||||
// Components of known path (value or mod name)
|
||||
pub(crate) const STD: Name = Name::new_inline_ascii(3, b"std");
|
||||
pub(crate) const ITER: Name = Name::new_inline_ascii(4, b"iter");
|
||||
pub(crate) const OPS: Name = Name::new_inline_ascii(3, b"ops");
|
||||
pub(crate) const FUTURE: Name = Name::new_inline_ascii(6, b"future");
|
||||
pub(crate) const RESULT: Name = Name::new_inline_ascii(6, b"result");
|
||||
pub(crate) const BOXED: Name = Name::new_inline_ascii(5, b"boxed");
|
||||
pub const STD: Name = Name::new_inline_ascii(3, b"std");
|
||||
pub const ITER: Name = Name::new_inline_ascii(4, b"iter");
|
||||
pub const OPS: Name = Name::new_inline_ascii(3, b"ops");
|
||||
pub const FUTURE: Name = Name::new_inline_ascii(6, b"future");
|
||||
pub const RESULT: Name = Name::new_inline_ascii(6, b"result");
|
||||
pub const BOXED: Name = Name::new_inline_ascii(5, b"boxed");
|
||||
|
||||
// Components of known path (type name)
|
||||
pub(crate) const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator");
|
||||
pub(crate) const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item");
|
||||
pub(crate) const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try");
|
||||
pub(crate) const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok");
|
||||
pub(crate) const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future");
|
||||
pub(crate) const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
|
||||
pub(crate) const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
|
||||
pub(crate) const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
|
||||
pub(crate) const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
|
||||
pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator");
|
||||
pub const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item");
|
||||
pub const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try");
|
||||
pub const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok");
|
||||
pub const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future");
|
||||
pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
|
||||
pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
|
||||
pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
|
||||
pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
|
||||
@@ -27,10 +27,13 @@ ra_db = { path = "../ra_db" }
|
||||
ra_cfg = { path = "../ra_cfg" }
|
||||
ra_fmt = { path = "../ra_fmt" }
|
||||
ra_prof = { path = "../ra_prof" }
|
||||
hir = { path = "../ra_hir", package = "ra_hir" }
|
||||
test_utils = { path = "../test_utils" }
|
||||
ra_assists = { path = "../ra_assists" }
|
||||
|
||||
# ra_ide_api should depend only on the top-level `hir` package. if you need
|
||||
# something from some `hir_xxx` subpackage, reexport the API via `hir`.
|
||||
hir = { path = "../ra_hir", package = "ra_hir" }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "0.12.0"
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
use ra_db::SourceDatabase;
|
||||
use ra_syntax::{
|
||||
algo::find_node_at_offset,
|
||||
algo::ancestors_at_offset,
|
||||
ast::{self, ArgListOwner},
|
||||
AstNode, SyntaxNode, TextUnit,
|
||||
match_ast, AstNode, SyntaxNode, TextUnit,
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
|
||||
@@ -20,24 +20,30 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||
let name_ref = calling_node.name_ref()?;
|
||||
|
||||
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
|
||||
let function = match &calling_node {
|
||||
let (mut call_info, has_self) = match &calling_node {
|
||||
FnCallNode::CallExpr(expr) => {
|
||||
//FIXME: apply subst
|
||||
let (callable_def, _subst) = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
|
||||
match callable_def {
|
||||
hir::CallableDef::Function(it) => it,
|
||||
//FIXME: handle other callables
|
||||
_ => return None,
|
||||
hir::CallableDef::Function(it) => {
|
||||
(CallInfo::with_fn(db, it), it.data(db).has_self_param())
|
||||
}
|
||||
hir::CallableDef::Struct(it) => (CallInfo::with_struct(db, it)?, false),
|
||||
hir::CallableDef::EnumVariant(it) => (CallInfo::with_enum_variant(db, it)?, false),
|
||||
}
|
||||
}
|
||||
FnCallNode::MethodCallExpr(expr) => analyzer.resolve_method_call(&expr)?,
|
||||
FnCallNode::MethodCallExpr(expr) => {
|
||||
let function = analyzer.resolve_method_call(&expr)?;
|
||||
(CallInfo::with_fn(db, function), function.data(db).has_self_param())
|
||||
}
|
||||
FnCallNode::MacroCallExpr(expr) => {
|
||||
let macro_def = analyzer.resolve_macro_call(db, &expr)?;
|
||||
(CallInfo::with_macro(db, macro_def)?, false)
|
||||
}
|
||||
};
|
||||
|
||||
let mut call_info = CallInfo::new(db, function);
|
||||
|
||||
// If we have a calling expression let's find which argument we are on
|
||||
let num_params = call_info.parameters().len();
|
||||
let has_self = function.data(db).has_self_param();
|
||||
|
||||
if num_params == 1 {
|
||||
if !has_self {
|
||||
@@ -75,20 +81,25 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||
Some(call_info)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FnCallNode {
|
||||
CallExpr(ast::CallExpr),
|
||||
MethodCallExpr(ast::MethodCallExpr),
|
||||
MacroCallExpr(ast::MacroCall),
|
||||
}
|
||||
|
||||
impl FnCallNode {
|
||||
fn with_node(syntax: &SyntaxNode, offset: TextUnit) -> Option<FnCallNode> {
|
||||
if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
|
||||
return Some(FnCallNode::CallExpr(expr));
|
||||
}
|
||||
if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) {
|
||||
return Some(FnCallNode::MethodCallExpr(expr));
|
||||
}
|
||||
None
|
||||
ancestors_at_offset(syntax, offset).find_map(|node| {
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) },
|
||||
ast::MethodCallExpr(it) => { Some(FnCallNode::MethodCallExpr(it)) },
|
||||
ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) },
|
||||
_ => { None },
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn name_ref(&self) -> Option<ast::NameRef> {
|
||||
@@ -101,6 +112,8 @@ fn name_ref(&self) -> Option<ast::NameRef> {
|
||||
FnCallNode::MethodCallExpr(call_expr) => {
|
||||
call_expr.syntax().children().filter_map(ast::NameRef::cast).nth(0)
|
||||
}
|
||||
|
||||
FnCallNode::MacroCallExpr(call_expr) => call_expr.path()?.segment()?.name_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,17 +121,36 @@ fn arg_list(&self) -> Option<ast::ArgList> {
|
||||
match self {
|
||||
FnCallNode::CallExpr(expr) => expr.arg_list(),
|
||||
FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
|
||||
FnCallNode::MacroCallExpr(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallInfo {
|
||||
fn new(db: &RootDatabase, function: hir::Function) -> Self {
|
||||
fn with_fn(db: &RootDatabase, function: hir::Function) -> Self {
|
||||
let signature = FunctionSignature::from_hir(db, function);
|
||||
|
||||
CallInfo { signature, active_parameter: None }
|
||||
}
|
||||
|
||||
fn with_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
|
||||
let signature = FunctionSignature::from_struct(db, st)?;
|
||||
|
||||
Some(CallInfo { signature, active_parameter: None })
|
||||
}
|
||||
|
||||
fn with_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
|
||||
let signature = FunctionSignature::from_enum_variant(db, variant)?;
|
||||
|
||||
Some(CallInfo { signature, active_parameter: None })
|
||||
}
|
||||
|
||||
fn with_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
|
||||
let signature = FunctionSignature::from_macro(db, macro_def)?;
|
||||
|
||||
Some(CallInfo { signature, active_parameter: None })
|
||||
}
|
||||
|
||||
fn parameters(&self) -> &[String] {
|
||||
&self.signature.parameters
|
||||
}
|
||||
@@ -415,6 +447,7 @@ pub fn foo(mut r: WriteHandler<()>) {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_eq!(info.label(), "fn finished(&mut self, ctx: &mut Self::Context)".to_string());
|
||||
assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]);
|
||||
assert_eq!(info.active_parameter, Some(1));
|
||||
assert_eq!(
|
||||
@@ -438,4 +471,118 @@ fn call_info_bad_offset() {
|
||||
let call_info = analysis.call_info(position).unwrap();
|
||||
assert!(call_info.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_method_in_lamba() {
|
||||
let info = call_info(
|
||||
r#"struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn bar(&self, _: u32) { }
|
||||
}
|
||||
|
||||
fn bar(_: u32) { }
|
||||
|
||||
fn main() {
|
||||
let foo = Foo;
|
||||
std::thread::spawn(move || foo.bar(<|>));
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_eq!(info.parameters(), ["&self", "_: u32"]);
|
||||
assert_eq!(info.active_parameter, Some(1));
|
||||
assert_eq!(info.label(), "fn bar(&self, _: u32)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_for_tuple_structs() {
|
||||
let info = call_info(
|
||||
r#"
|
||||
/// A cool tuple struct
|
||||
struct TS(u32, i32);
|
||||
fn main() {
|
||||
let s = TS(0, <|>);
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_eq!(info.label(), "struct TS(u32, i32) -> TS");
|
||||
assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string()));
|
||||
assert_eq!(info.active_parameter, Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn cant_call_named_structs() {
|
||||
let _ = call_info(
|
||||
r#"
|
||||
struct TS { x: u32, y: i32 }
|
||||
fn main() {
|
||||
let s = TS(<|>);
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_for_enum_variants() {
|
||||
let info = call_info(
|
||||
r#"
|
||||
enum E {
|
||||
/// A Variant
|
||||
A(i32),
|
||||
/// Another
|
||||
B,
|
||||
/// And C
|
||||
C { a: i32, b: i32 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = E::A(<|>);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_eq!(info.label(), "E::A(0: i32)");
|
||||
assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string()));
|
||||
assert_eq!(info.active_parameter, Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn cant_call_enum_records() {
|
||||
let _ = call_info(
|
||||
r#"
|
||||
enum E {
|
||||
/// A Variant
|
||||
A(i32),
|
||||
/// Another
|
||||
B,
|
||||
/// And C
|
||||
C { a: i32, b: i32 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = E::C(<|>);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_signature_for_macro() {
|
||||
let info = call_info(
|
||||
r#"
|
||||
/// empty macro
|
||||
macro_rules! foo {
|
||||
() => {}
|
||||
}
|
||||
|
||||
fn f() {
|
||||
foo!(<|>);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_eq!(info.label(), "foo!()");
|
||||
assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if !self.libraries_added.is_empty() {
|
||||
d.field("libraries_added", &self.libraries_added.len());
|
||||
}
|
||||
if !self.crate_graph.is_some() {
|
||||
if !self.crate_graph.is_none() {
|
||||
d.field("crate_graph", &self.crate_graph);
|
||||
}
|
||||
d.finish()
|
||||
|
||||
@@ -50,7 +50,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let krate = ctx.module.and_then(|m| m.krate(ctx.db));
|
||||
let krate = ctx.module.map(|m| m.krate());
|
||||
if let Some(krate) = krate {
|
||||
ty.iterate_impl_items(ctx.db, krate, |item| {
|
||||
match item {
|
||||
@@ -67,7 +67,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => return,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,16 +9,14 @@
|
||||
};
|
||||
use hir::{Ty, TypeCtor};
|
||||
use ra_syntax::{ast::AstNode, TextRange, TextUnit};
|
||||
use ra_text_edit::TextEditBuilder;
|
||||
use ra_text_edit::TextEdit;
|
||||
|
||||
fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
|
||||
let edit = {
|
||||
let receiver_range =
|
||||
ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range();
|
||||
let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
|
||||
let mut builder = TextEditBuilder::default();
|
||||
builder.replace(delete_range, snippet.to_string());
|
||||
builder.finish()
|
||||
TextEdit::replace(delete_range, snippet.to_string())
|
||||
};
|
||||
CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
|
||||
.detail(detail)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use hir::Documentation;
|
||||
use ra_syntax::TextRange;
|
||||
use ra_text_edit::{TextEdit, TextEditBuilder};
|
||||
use ra_text_edit::TextEdit;
|
||||
|
||||
/// `CompletionItem` describes a single completion variant in the editor pop-up.
|
||||
/// It is basically a POD with various properties. To construct a
|
||||
@@ -192,12 +192,10 @@ pub(crate) fn build(self) -> CompletionItem {
|
||||
let label = self.label;
|
||||
let text_edit = match self.text_edit {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
let mut builder = TextEditBuilder::default();
|
||||
builder
|
||||
.replace(self.source_range, self.insert_text.unwrap_or_else(|| label.clone()));
|
||||
builder.finish()
|
||||
}
|
||||
None => TextEdit::replace(
|
||||
self.source_range,
|
||||
self.insert_text.unwrap_or_else(|| label.clone()),
|
||||
),
|
||||
};
|
||||
|
||||
CompletionItem {
|
||||
|
||||
@@ -136,7 +136,7 @@ fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str {
|
||||
for (idx, s) in docs.match_indices(¯o_name) {
|
||||
let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
|
||||
// Ensure to match the full word
|
||||
if after.starts_with("!")
|
||||
if after.starts_with('!')
|
||||
&& before
|
||||
.chars()
|
||||
.rev()
|
||||
@@ -164,27 +164,32 @@ pub(crate) fn add_macro(
|
||||
name: Option<String>,
|
||||
macro_: hir::MacroDef,
|
||||
) {
|
||||
let ast_node = macro_.source(ctx.db).ast;
|
||||
if let Some(name) = name {
|
||||
let detail = macro_label(&ast_node);
|
||||
let name = match name {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let docs = macro_.docs(ctx.db);
|
||||
let ast_node = macro_.source(ctx.db).ast;
|
||||
let detail = macro_label(&ast_node);
|
||||
|
||||
let docs = macro_.docs(ctx.db);
|
||||
let macro_declaration = format!("{}!", name);
|
||||
|
||||
let mut builder =
|
||||
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), ¯o_declaration)
|
||||
.kind(CompletionItemKind::Macro)
|
||||
.set_documentation(docs.clone())
|
||||
.detail(detail);
|
||||
|
||||
builder = if ctx.use_item_syntax.is_some() {
|
||||
builder.insert_text(name)
|
||||
} else {
|
||||
let macro_braces_to_insert =
|
||||
self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
|
||||
let macro_declaration = name + "!";
|
||||
builder.insert_snippet(macro_declaration + macro_braces_to_insert)
|
||||
};
|
||||
|
||||
let builder = CompletionItem::new(
|
||||
CompletionKind::Reference,
|
||||
ctx.source_range(),
|
||||
¯o_declaration,
|
||||
)
|
||||
.kind(CompletionItemKind::Macro)
|
||||
.set_documentation(docs)
|
||||
.detail(detail)
|
||||
.insert_snippet(macro_declaration + macro_braces_to_insert);
|
||||
|
||||
self.add(builder);
|
||||
}
|
||||
self.add(builder);
|
||||
}
|
||||
|
||||
fn add_function_with_name(
|
||||
@@ -220,7 +225,7 @@ fn add_function_with_name(
|
||||
} else {
|
||||
(format!("{}($0)", data.name()), format!("{}(…)", name))
|
||||
};
|
||||
builder = builder.lookup_by(name.clone()).label(label).insert_snippet(snippet);
|
||||
builder = builder.lookup_by(name).label(label).insert_snippet(snippet);
|
||||
}
|
||||
|
||||
self.add(builder)
|
||||
@@ -281,10 +286,11 @@ fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> b
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::completion::{do_completion, CompletionItem, CompletionKind};
|
||||
use insta::assert_debug_snapshot;
|
||||
use test_utils::covers;
|
||||
|
||||
use crate::completion::{do_completion, CompletionItem, CompletionKind};
|
||||
|
||||
fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
|
||||
do_completion(code, CompletionKind::Reference)
|
||||
}
|
||||
@@ -576,4 +582,34 @@ fn foo(xs: Ve<|><i128>)
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dont_insert_macro_call_braces_in_use() {
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
r"
|
||||
//- /main.rs
|
||||
use foo::<|>;
|
||||
|
||||
//- /foo/lib.rs
|
||||
#[macro_export]
|
||||
macro_rules frobnicate {
|
||||
() => ()
|
||||
}
|
||||
"
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "frobnicate!",
|
||||
source_range: [9; 9),
|
||||
delete: [9; 9),
|
||||
insert: "frobnicate",
|
||||
kind: Macro,
|
||||
detail: "#[macro_export]\nmacro_rules! frobnicate",
|
||||
},
|
||||
]
|
||||
"###
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
hir::db::InternDatabaseStorage,
|
||||
hir::db::AstDatabaseStorage,
|
||||
hir::db::DefDatabaseStorage,
|
||||
hir::db::DefDatabase2Storage,
|
||||
hir::db::HirDatabaseStorage
|
||||
)]
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -85,10 +85,9 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
||||
})
|
||||
.on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
|
||||
let node = d.ast(db);
|
||||
let mut builder = TextEditBuilder::default();
|
||||
let replacement = format!("Ok({})", node.syntax());
|
||||
builder.replace(node.syntax().text_range(), replacement);
|
||||
let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, builder.finish());
|
||||
let edit = TextEdit::replace(node.syntax().text_range(), replacement);
|
||||
let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit);
|
||||
res.borrow_mut().push(Diagnostic {
|
||||
range: d.highlight_range(),
|
||||
message: d.message(),
|
||||
@@ -152,9 +151,7 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
|
||||
let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
|
||||
let end = use_tree_list_node.text_range().end();
|
||||
let range = TextRange::from_to(start, end);
|
||||
let mut edit_builder = TextEditBuilder::default();
|
||||
edit_builder.delete(range);
|
||||
return Some(edit_builder.finish());
|
||||
return Some(TextEdit::delete(range));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
use hir::{Docs, Documentation, HasSource};
|
||||
use hir::{Docs, Documentation, HasSource, HirDisplay};
|
||||
use join_to_string::join;
|
||||
use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
|
||||
use std::convert::From;
|
||||
@@ -12,9 +12,18 @@
|
||||
display::{generic_parameters, where_predicates},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CallableKind {
|
||||
Function,
|
||||
StructConstructor,
|
||||
VariantConstructor,
|
||||
Macro,
|
||||
}
|
||||
|
||||
/// Contains information about a function signature
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionSignature {
|
||||
pub kind: CallableKind,
|
||||
/// Optional visibility
|
||||
pub visibility: Option<String>,
|
||||
/// Name of the function
|
||||
@@ -42,6 +51,99 @@ pub(crate) fn from_hir(db: &db::RootDatabase, function: hir::Function) -> Self {
|
||||
let ast_node = function.source(db).ast;
|
||||
FunctionSignature::from(&ast_node).with_doc_opt(doc)
|
||||
}
|
||||
|
||||
pub(crate) fn from_struct(db: &db::RootDatabase, st: hir::Struct) -> Option<Self> {
|
||||
let node: ast::StructDef = st.source(db).ast;
|
||||
match node.kind() {
|
||||
ast::StructKind::Named(_) => return None,
|
||||
_ => (),
|
||||
};
|
||||
|
||||
let params = st
|
||||
.fields(db)
|
||||
.into_iter()
|
||||
.map(|field: hir::StructField| {
|
||||
let ty = field.ty(db);
|
||||
format!("{}", ty.display(db))
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some(
|
||||
FunctionSignature {
|
||||
kind: CallableKind::StructConstructor,
|
||||
visibility: node.visibility().map(|n| n.syntax().text().to_string()),
|
||||
name: node.name().map(|n| n.text().to_string()),
|
||||
ret_type: node.name().map(|n| n.text().to_string()),
|
||||
parameters: params,
|
||||
generic_parameters: generic_parameters(&node),
|
||||
where_predicates: where_predicates(&node),
|
||||
doc: None,
|
||||
}
|
||||
.with_doc_opt(st.docs(db)),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn from_enum_variant(
|
||||
db: &db::RootDatabase,
|
||||
variant: hir::EnumVariant,
|
||||
) -> Option<Self> {
|
||||
let node: ast::EnumVariant = variant.source(db).ast;
|
||||
match node.kind() {
|
||||
ast::StructKind::Named(_) | ast::StructKind::Unit => return None,
|
||||
_ => (),
|
||||
};
|
||||
|
||||
let parent_name = match variant.parent_enum(db).name(db) {
|
||||
Some(name) => name.to_string(),
|
||||
None => "missing".into(),
|
||||
};
|
||||
|
||||
let name = format!("{}::{}", parent_name, variant.name(db).unwrap());
|
||||
|
||||
let params = variant
|
||||
.fields(db)
|
||||
.into_iter()
|
||||
.map(|field: hir::StructField| {
|
||||
let name = field.name(db);
|
||||
let ty = field.ty(db);
|
||||
format!("{}: {}", name, ty.display(db))
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some(
|
||||
FunctionSignature {
|
||||
kind: CallableKind::VariantConstructor,
|
||||
visibility: None,
|
||||
name: Some(name),
|
||||
ret_type: None,
|
||||
parameters: params,
|
||||
generic_parameters: vec![],
|
||||
where_predicates: vec![],
|
||||
doc: None,
|
||||
}
|
||||
.with_doc_opt(variant.docs(db)),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn from_macro(db: &db::RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
|
||||
let node: ast::MacroCall = macro_def.source(db).ast;
|
||||
|
||||
let params = vec![];
|
||||
|
||||
Some(
|
||||
FunctionSignature {
|
||||
kind: CallableKind::Macro,
|
||||
visibility: None,
|
||||
name: node.name().map(|n| n.text().to_string()),
|
||||
ret_type: None,
|
||||
parameters: params,
|
||||
generic_parameters: vec![],
|
||||
where_predicates: vec![],
|
||||
doc: None,
|
||||
}
|
||||
.with_doc_opt(macro_def.docs(db)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'_ ast::FnDef> for FunctionSignature {
|
||||
@@ -59,6 +161,7 @@ fn param_list(node: &ast::FnDef) -> Vec<String> {
|
||||
}
|
||||
|
||||
FunctionSignature {
|
||||
kind: CallableKind::Function,
|
||||
visibility: node.visibility().map(|n| n.syntax().text().to_string()),
|
||||
name: node.name().map(|n| n.text().to_string()),
|
||||
ret_type: node
|
||||
@@ -81,7 +184,12 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
}
|
||||
|
||||
if let Some(name) = &self.name {
|
||||
write!(f, "fn {}", name)?;
|
||||
match self.kind {
|
||||
CallableKind::Function => write!(f, "fn {}", name)?,
|
||||
CallableKind::StructConstructor => write!(f, "struct {}", name)?,
|
||||
CallableKind::VariantConstructor => write!(f, "{}", name)?,
|
||||
CallableKind::Macro => write!(f, "{}!", name)?,
|
||||
}
|
||||
}
|
||||
|
||||
if !self.generic_parameters.is_empty() {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
algo::find_covering_element,
|
||||
ast::{self, AstNode, AstToken},
|
||||
Direction, NodeOrToken,
|
||||
SyntaxKind::*,
|
||||
SyntaxKind::{self, *},
|
||||
SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T,
|
||||
};
|
||||
|
||||
@@ -29,10 +29,12 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
|
||||
USE_TREE_LIST,
|
||||
TYPE_PARAM_LIST,
|
||||
TYPE_ARG_LIST,
|
||||
TYPE_BOUND_LIST,
|
||||
PARAM_LIST,
|
||||
ARG_LIST,
|
||||
ARRAY_EXPR,
|
||||
TUPLE_EXPR,
|
||||
WHERE_CLAUSE,
|
||||
];
|
||||
|
||||
if range.is_empty() {
|
||||
@@ -146,13 +148,17 @@ fn priority(n: &SyntaxToken) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extend list item selection to include nearby comma and whitespace.
|
||||
/// Extend list item selection to include nearby delimiter and whitespace.
|
||||
fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
|
||||
fn is_single_line_ws(node: &SyntaxToken) -> bool {
|
||||
node.kind() == WHITESPACE && !node.text().contains('\n')
|
||||
}
|
||||
|
||||
fn nearby_comma(node: &SyntaxNode, dir: Direction) -> Option<SyntaxToken> {
|
||||
fn nearby_delimiter(
|
||||
delimiter_kind: SyntaxKind,
|
||||
node: &SyntaxNode,
|
||||
dir: Direction,
|
||||
) -> Option<SyntaxToken> {
|
||||
node.siblings_with_tokens(dir)
|
||||
.skip(1)
|
||||
.skip_while(|node| match node {
|
||||
@@ -161,19 +167,26 @@ fn nearby_comma(node: &SyntaxNode, dir: Direction) -> Option<SyntaxToken> {
|
||||
})
|
||||
.next()
|
||||
.and_then(|it| it.into_token())
|
||||
.filter(|node| node.kind() == T![,])
|
||||
.filter(|node| node.kind() == delimiter_kind)
|
||||
}
|
||||
|
||||
if let Some(comma_node) = nearby_comma(node, Direction::Prev) {
|
||||
return Some(TextRange::from_to(comma_node.text_range().start(), node.text_range().end()));
|
||||
let delimiter = match node.kind() {
|
||||
TYPE_BOUND => T![+],
|
||||
_ => T![,],
|
||||
};
|
||||
if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Prev) {
|
||||
return Some(TextRange::from_to(
|
||||
delimiter_node.text_range().start(),
|
||||
node.text_range().end(),
|
||||
));
|
||||
}
|
||||
if let Some(comma_node) = nearby_comma(node, Direction::Next) {
|
||||
// Include any following whitespace when comma if after list item.
|
||||
let final_node = comma_node
|
||||
if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Next) {
|
||||
// Include any following whitespace when delimiter is after list item.
|
||||
let final_node = delimiter_node
|
||||
.next_sibling_or_token()
|
||||
.and_then(|it| it.into_token())
|
||||
.filter(|node| is_single_line_ws(node))
|
||||
.unwrap_or(comma_node);
|
||||
.unwrap_or(delimiter_node);
|
||||
|
||||
return Some(TextRange::from_to(node.text_range().start(), final_node.text_range().end()));
|
||||
}
|
||||
@@ -387,4 +400,53 @@ fn bar(){}
|
||||
&["foo", "\" fn foo() {\""],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extend_trait_bounds_list_in_where_clause() {
|
||||
do_check(
|
||||
r#"
|
||||
fn foo<R>()
|
||||
where
|
||||
R: req::Request + 'static,
|
||||
R::Params: DeserializeOwned<|> + panic::UnwindSafe + 'static,
|
||||
R::Result: Serialize + 'static,
|
||||
"#,
|
||||
&[
|
||||
"DeserializeOwned",
|
||||
"DeserializeOwned + ",
|
||||
"DeserializeOwned + panic::UnwindSafe + 'static",
|
||||
"R::Params: DeserializeOwned + panic::UnwindSafe + 'static",
|
||||
"R::Params: DeserializeOwned + panic::UnwindSafe + 'static,",
|
||||
],
|
||||
);
|
||||
do_check(r#"fn foo<T>() where T: <|>Copy"#, &["Copy"]);
|
||||
do_check(r#"fn foo<T>() where T: <|>Copy + Display"#, &["Copy", "Copy + "]);
|
||||
do_check(r#"fn foo<T>() where T: <|>Copy +Display"#, &["Copy", "Copy +"]);
|
||||
do_check(r#"fn foo<T>() where T: <|>Copy+Display"#, &["Copy", "Copy+"]);
|
||||
do_check(r#"fn foo<T>() where T: Copy + <|>Display"#, &["Display", "+ Display"]);
|
||||
do_check(r#"fn foo<T>() where T: Copy + <|>Display + Sync"#, &["Display", "+ Display"]);
|
||||
do_check(r#"fn foo<T>() where T: Copy +<|>Display"#, &["Display", "+Display"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extend_trait_bounds_list_inline() {
|
||||
do_check(r#"fn foo<T: <|>Copy>() {}"#, &["Copy"]);
|
||||
do_check(r#"fn foo<T: <|>Copy + Display>() {}"#, &["Copy", "Copy + "]);
|
||||
do_check(r#"fn foo<T: <|>Copy +Display>() {}"#, &["Copy", "Copy +"]);
|
||||
do_check(r#"fn foo<T: <|>Copy+Display>() {}"#, &["Copy", "Copy+"]);
|
||||
do_check(r#"fn foo<T: Copy + <|>Display>() {}"#, &["Display", "+ Display"]);
|
||||
do_check(r#"fn foo<T: Copy + <|>Display + Sync>() {}"#, &["Display", "+ Display"]);
|
||||
do_check(r#"fn foo<T: Copy +<|>Display>() {}"#, &["Display", "+Display"]);
|
||||
do_check(
|
||||
r#"fn foo<T: Copy<|> + Display, U: Copy>() {}"#,
|
||||
&[
|
||||
"Copy",
|
||||
"Copy + ",
|
||||
"Copy + Display",
|
||||
"T: Copy + Display",
|
||||
"T: Copy + Display, ",
|
||||
"<T: Copy + Display, U: Copy>",
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ fn impls_for_def(
|
||||
}
|
||||
};
|
||||
|
||||
let krate = module.krate(db)?;
|
||||
let krate = module.krate();
|
||||
let impls = db.impls_in_crate(krate);
|
||||
|
||||
Some(
|
||||
@@ -72,7 +72,7 @@ fn impls_for_trait(
|
||||
let src = hir::Source { file_id: position.file_id.into(), ast: node.clone() };
|
||||
let tr = hir::Trait::from_source(db, src)?;
|
||||
|
||||
let krate = module.krate(db)?;
|
||||
let krate = module.krate();
|
||||
let impls = db.impls_in_crate(krate);
|
||||
|
||||
Some(
|
||||
|
||||
+16
-113
@@ -14,6 +14,7 @@
|
||||
pub mod mock_analysis;
|
||||
mod symbol_index;
|
||||
mod change;
|
||||
mod source_change;
|
||||
mod feature_flags;
|
||||
|
||||
mod status;
|
||||
@@ -54,8 +55,6 @@
|
||||
CheckCanceled, FileLoader, SourceDatabase,
|
||||
};
|
||||
use ra_syntax::{SourceFile, TextRange, TextUnit};
|
||||
use ra_text_edit::TextEdit;
|
||||
use relative_path::RelativePathBuf;
|
||||
|
||||
use crate::{db::LineIndexDatabase, symbol_index::FileSymbol};
|
||||
|
||||
@@ -73,6 +72,7 @@
|
||||
line_index_utils::translate_offset_with_edit,
|
||||
references::{ReferenceSearchResult, SearchScope},
|
||||
runnables::{Runnable, RunnableKind},
|
||||
source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
|
||||
syntax_highlighting::HighlightedRange,
|
||||
};
|
||||
|
||||
@@ -83,99 +83,6 @@
|
||||
|
||||
pub type Cancelable<T> = Result<T, Canceled>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SourceChange {
|
||||
pub label: String,
|
||||
pub source_file_edits: Vec<SourceFileEdit>,
|
||||
pub file_system_edits: Vec<FileSystemEdit>,
|
||||
pub cursor_position: Option<FilePosition>,
|
||||
}
|
||||
|
||||
impl SourceChange {
|
||||
/// Creates a new SourceChange with the given label
|
||||
/// from the edits.
|
||||
pub(crate) fn from_edits<L: Into<String>>(
|
||||
label: L,
|
||||
source_file_edits: Vec<SourceFileEdit>,
|
||||
file_system_edits: Vec<FileSystemEdit>,
|
||||
) -> Self {
|
||||
SourceChange {
|
||||
label: label.into(),
|
||||
source_file_edits,
|
||||
file_system_edits,
|
||||
cursor_position: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new SourceChange with the given label,
|
||||
/// containing only the given `SourceFileEdits`.
|
||||
pub(crate) fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self {
|
||||
SourceChange {
|
||||
label: label.into(),
|
||||
source_file_edits: edits,
|
||||
file_system_edits: vec![],
|
||||
cursor_position: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new SourceChange with the given label,
|
||||
/// containing only the given `FileSystemEdits`.
|
||||
pub(crate) fn file_system_edits<L: Into<String>>(label: L, edits: Vec<FileSystemEdit>) -> Self {
|
||||
SourceChange {
|
||||
label: label.into(),
|
||||
source_file_edits: vec![],
|
||||
file_system_edits: edits,
|
||||
cursor_position: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new SourceChange with the given label,
|
||||
/// containing only a single `SourceFileEdit`.
|
||||
pub(crate) fn source_file_edit<L: Into<String>>(label: L, edit: SourceFileEdit) -> Self {
|
||||
SourceChange::source_file_edits(label, vec![edit])
|
||||
}
|
||||
|
||||
/// Creates a new SourceChange with the given label
|
||||
/// from the given `FileId` and `TextEdit`
|
||||
pub(crate) fn source_file_edit_from<L: Into<String>>(
|
||||
label: L,
|
||||
file_id: FileId,
|
||||
edit: TextEdit,
|
||||
) -> Self {
|
||||
SourceChange::source_file_edit(label, SourceFileEdit { file_id, edit })
|
||||
}
|
||||
|
||||
/// Creates a new SourceChange with the given label
|
||||
/// from the given `FileId` and `TextEdit`
|
||||
pub(crate) fn file_system_edit<L: Into<String>>(label: L, edit: FileSystemEdit) -> Self {
|
||||
SourceChange::file_system_edits(label, vec![edit])
|
||||
}
|
||||
|
||||
/// Sets the cursor position to the given `FilePosition`
|
||||
pub(crate) fn with_cursor(mut self, cursor_position: FilePosition) -> Self {
|
||||
self.cursor_position = Some(cursor_position);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the cursor position to the given `FilePosition`
|
||||
pub(crate) fn with_cursor_opt(mut self, cursor_position: Option<FilePosition>) -> Self {
|
||||
self.cursor_position = cursor_position;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SourceFileEdit {
|
||||
pub file_id: FileId,
|
||||
pub edit: TextEdit,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FileSystemEdit {
|
||||
CreateFile { source_root: SourceRootId, path: RelativePathBuf },
|
||||
MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Diagnostic {
|
||||
pub message: String,
|
||||
@@ -407,24 +314,20 @@ pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<SourceChange
|
||||
self.with_db(|db| typing::on_enter(&db, position))
|
||||
}
|
||||
|
||||
/// Returns an edit which should be applied after `=` was typed. Primarily,
|
||||
/// this works when adding `let =`.
|
||||
// FIXME: use a snippet completion instead of this hack here.
|
||||
pub fn on_eq_typed(&self, position: FilePosition) -> Cancelable<Option<SourceChange>> {
|
||||
self.with_db(|db| {
|
||||
let parse = db.parse(position.file_id);
|
||||
let file = parse.tree();
|
||||
let edit = typing::on_eq_typed(&file, position.offset)?;
|
||||
Some(SourceChange::source_file_edit(
|
||||
"add semicolon",
|
||||
SourceFileEdit { edit, file_id: position.file_id },
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
|
||||
pub fn on_dot_typed(&self, position: FilePosition) -> Cancelable<Option<SourceChange>> {
|
||||
self.with_db(|db| typing::on_dot_typed(&db, position))
|
||||
/// Returns an edit which should be applied after a character was typed.
|
||||
///
|
||||
/// This is useful for some on-the-fly fixups, like adding `;` to `let =`
|
||||
/// automatically.
|
||||
pub fn on_char_typed(
|
||||
&self,
|
||||
position: FilePosition,
|
||||
char_typed: char,
|
||||
) -> Cancelable<Option<SourceChange>> {
|
||||
// Fast path to not even parse the file.
|
||||
if !typing::TRIGGER_CHARS.contains(char_typed) {
|
||||
return Ok(None);
|
||||
}
|
||||
self.with_db(|db| typing::on_char_typed(&db, position, char_typed))
|
||||
}
|
||||
|
||||
/// Returns a tree representation of symbols in the file. Useful to draw a
|
||||
|
||||
@@ -27,10 +27,7 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
|
||||
Some(it) => it,
|
||||
None => return Vec::new(),
|
||||
};
|
||||
let krate = match module.krate(db) {
|
||||
Some(it) => it,
|
||||
None => return Vec::new(),
|
||||
};
|
||||
let krate = module.krate();
|
||||
vec![krate.crate_id()]
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use hir::ModuleSource;
|
||||
use ra_db::{SourceDatabase, SourceDatabaseExt};
|
||||
use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode};
|
||||
use ra_text_edit::TextEdit;
|
||||
use relative_path::{RelativePath, RelativePathBuf};
|
||||
|
||||
use crate::{
|
||||
@@ -43,14 +44,7 @@ fn source_edit_from_file_id_range(
|
||||
range: TextRange,
|
||||
new_name: &str,
|
||||
) -> SourceFileEdit {
|
||||
SourceFileEdit {
|
||||
file_id,
|
||||
edit: {
|
||||
let mut builder = ra_text_edit::TextEditBuilder::default();
|
||||
builder.replace(range, new_name.into());
|
||||
builder.finish()
|
||||
},
|
||||
}
|
||||
SourceFileEdit { file_id, edit: TextEdit::replace(range, new_name.into()) }
|
||||
}
|
||||
|
||||
fn rename_mod(
|
||||
@@ -94,11 +88,7 @@ fn rename_mod(
|
||||
|
||||
let edit = SourceFileEdit {
|
||||
file_id: position.file_id,
|
||||
edit: {
|
||||
let mut builder = ra_text_edit::TextEditBuilder::default();
|
||||
builder.replace(ast_name.syntax().text_range(), new_name.into());
|
||||
builder.finish()
|
||||
},
|
||||
edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()),
|
||||
};
|
||||
source_file_edits.push(edit);
|
||||
|
||||
@@ -126,12 +116,14 @@ fn rename_reference(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
use ra_text_edit::TextEditBuilder;
|
||||
use test_utils::assert_eq_text;
|
||||
|
||||
use crate::{
|
||||
mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId,
|
||||
ReferenceSearchResult,
|
||||
};
|
||||
use insta::assert_debug_snapshot;
|
||||
use test_utils::assert_eq_text;
|
||||
|
||||
#[test]
|
||||
fn test_find_all_refs_for_local() {
|
||||
@@ -452,7 +444,7 @@ fn test_rename_mod_in_dir() {
|
||||
fn test_rename(text: &str, new_name: &str, expected: &str) {
|
||||
let (analysis, position) = single_file_with_position(text);
|
||||
let source_change = analysis.rename(position, new_name).unwrap();
|
||||
let mut text_edit_builder = ra_text_edit::TextEditBuilder::default();
|
||||
let mut text_edit_builder = TextEditBuilder::default();
|
||||
let mut file_id: Option<FileId> = None;
|
||||
if let Some(change) = source_change {
|
||||
for edit in change.info.source_file_edits {
|
||||
|
||||
@@ -111,8 +111,7 @@ pub(crate) fn search_scope(&self, db: &RootDatabase) -> SearchScope {
|
||||
if vis.as_str() != "" {
|
||||
let source_root_id = db.file_source_root(file_id);
|
||||
let source_root = db.source_root(source_root_id);
|
||||
let mut res =
|
||||
source_root.walk().map(|id| (id.into(), None)).collect::<FxHashMap<_, _>>();
|
||||
let mut res = source_root.walk().map(|id| (id, None)).collect::<FxHashMap<_, _>>();
|
||||
|
||||
// FIXME: add "pub(in path)"
|
||||
|
||||
@@ -120,7 +119,7 @@ pub(crate) fn search_scope(&self, db: &RootDatabase) -> SearchScope {
|
||||
return SearchScope::new(res);
|
||||
}
|
||||
if vis.as_str() == "pub" {
|
||||
let krate = self.container.krate(db).unwrap();
|
||||
let krate = self.container.krate();
|
||||
let crate_graph = db.crate_graph();
|
||||
for crate_id in crate_graph.iter() {
|
||||
let mut crate_deps = crate_graph.dependencies(crate_id);
|
||||
@@ -128,7 +127,7 @@ pub(crate) fn search_scope(&self, db: &RootDatabase) -> SearchScope {
|
||||
let root_file = crate_graph.crate_root(crate_id);
|
||||
let source_root_id = db.file_source_root(root_file);
|
||||
let source_root = db.source_root(source_root_id);
|
||||
res.extend(source_root.walk().map(|id| (id.into(), None)));
|
||||
res.extend(source_root.walk().map(|id| (id, None)));
|
||||
}
|
||||
}
|
||||
return SearchScope::new(res);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use ra_db::SourceDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
|
||||
SyntaxNode, TextRange,
|
||||
match_ast, SyntaxNode, TextRange,
|
||||
};
|
||||
|
||||
use crate::{db::RootDatabase, FileId};
|
||||
@@ -29,12 +29,12 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
||||
}
|
||||
|
||||
fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNode) -> Option<Runnable> {
|
||||
if let Some(fn_def) = ast::FnDef::cast(item.clone()) {
|
||||
runnable_fn(fn_def)
|
||||
} else if let Some(m) = ast::Module::cast(item) {
|
||||
runnable_mod(db, file_id, m)
|
||||
} else {
|
||||
None
|
||||
match_ast! {
|
||||
match item {
|
||||
ast::FnDef(it) => { runnable_fn(it) },
|
||||
ast::Module(it) => { runnable_mod(db, file_id, it) },
|
||||
_ => { None },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
//! This modules defines type to represent changes to the source code, that flow
|
||||
//! from the server to the client.
|
||||
//!
|
||||
//! It can be viewed as a dual for `AnalysisChange`.
|
||||
|
||||
use ra_text_edit::TextEdit;
|
||||
use relative_path::RelativePathBuf;
|
||||
|
||||
use crate::{FileId, FilePosition, SourceRootId, TextUnit};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SourceChange {
|
||||
pub label: String,
|
||||
pub source_file_edits: Vec<SourceFileEdit>,
|
||||
pub file_system_edits: Vec<FileSystemEdit>,
|
||||
pub cursor_position: Option<FilePosition>,
|
||||
}
|
||||
|
||||
impl SourceChange {
|
||||
/// Creates a new SourceChange with the given label
|
||||
/// from the edits.
|
||||
pub(crate) fn from_edits<L: Into<String>>(
|
||||
label: L,
|
||||
source_file_edits: Vec<SourceFileEdit>,
|
||||
file_system_edits: Vec<FileSystemEdit>,
|
||||
) -> Self {
|
||||
SourceChange {
|
||||
label: label.into(),
|
||||
source_file_edits,
|
||||
file_system_edits,
|
||||
cursor_position: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new SourceChange with the given label,
|
||||
/// containing only the given `SourceFileEdits`.
|
||||
pub(crate) fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self {
|
||||
SourceChange {
|
||||
label: label.into(),
|
||||
source_file_edits: edits,
|
||||
file_system_edits: vec![],
|
||||
cursor_position: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new SourceChange with the given label,
|
||||
/// containing only the given `FileSystemEdits`.
|
||||
pub(crate) fn file_system_edits<L: Into<String>>(label: L, edits: Vec<FileSystemEdit>) -> Self {
|
||||
SourceChange {
|
||||
label: label.into(),
|
||||
source_file_edits: vec![],
|
||||
file_system_edits: edits,
|
||||
cursor_position: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new SourceChange with the given label,
|
||||
/// containing only a single `SourceFileEdit`.
|
||||
pub(crate) fn source_file_edit<L: Into<String>>(label: L, edit: SourceFileEdit) -> Self {
|
||||
SourceChange::source_file_edits(label, vec![edit])
|
||||
}
|
||||
|
||||
/// Creates a new SourceChange with the given label
|
||||
/// from the given `FileId` and `TextEdit`
|
||||
pub(crate) fn source_file_edit_from<L: Into<String>>(
|
||||
label: L,
|
||||
file_id: FileId,
|
||||
edit: TextEdit,
|
||||
) -> Self {
|
||||
SourceChange::source_file_edit(label, SourceFileEdit { file_id, edit })
|
||||
}
|
||||
|
||||
/// Creates a new SourceChange with the given label
|
||||
/// from the given `FileId` and `TextEdit`
|
||||
pub(crate) fn file_system_edit<L: Into<String>>(label: L, edit: FileSystemEdit) -> Self {
|
||||
SourceChange::file_system_edits(label, vec![edit])
|
||||
}
|
||||
|
||||
/// Sets the cursor position to the given `FilePosition`
|
||||
pub(crate) fn with_cursor(mut self, cursor_position: FilePosition) -> Self {
|
||||
self.cursor_position = Some(cursor_position);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the cursor position to the given `FilePosition`
|
||||
pub(crate) fn with_cursor_opt(mut self, cursor_position: Option<FilePosition>) -> Self {
|
||||
self.cursor_position = cursor_position;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SourceFileEdit {
|
||||
pub file_id: FileId,
|
||||
pub edit: TextEdit,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FileSystemEdit {
|
||||
CreateFile { source_root: SourceRootId, path: RelativePathBuf },
|
||||
MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf },
|
||||
}
|
||||
|
||||
pub(crate) struct SingleFileChange {
|
||||
pub label: String,
|
||||
pub edit: TextEdit,
|
||||
pub cursor_position: Option<TextUnit>,
|
||||
}
|
||||
|
||||
impl SingleFileChange {
|
||||
pub(crate) fn into_source_change(self, file_id: FileId) -> SourceChange {
|
||||
SourceChange {
|
||||
label: self.label,
|
||||
source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }],
|
||||
file_system_edits: Vec::new(),
|
||||
cursor_position: self.cursor_position.map(|offset| FilePosition { file_id, offset }),
|
||||
}
|
||||
}
|
||||
}
|
||||
+302
-269
@@ -1,4 +1,17 @@
|
||||
//! FIXME: write short doc here
|
||||
//! This module handles auto-magic editing actions applied together with users
|
||||
//! edits. For example, if the user typed
|
||||
//!
|
||||
//! ```text
|
||||
//! foo
|
||||
//! .bar()
|
||||
//! .baz()
|
||||
//! | // <- cursor is here
|
||||
//! ```
|
||||
//!
|
||||
//! and types `.` next, we want to indent the dot.
|
||||
//!
|
||||
//! Language server executes such typing assists synchronously. That is, they
|
||||
//! block user's typing and should be pretty fast for this reason!
|
||||
|
||||
use ra_db::{FilePosition, SourceDatabase};
|
||||
use ra_fmt::leading_indent;
|
||||
@@ -9,9 +22,9 @@
|
||||
SyntaxKind::*,
|
||||
SyntaxToken, TextRange, TextUnit, TokenAtOffset,
|
||||
};
|
||||
use ra_text_edit::{TextEdit, TextEditBuilder};
|
||||
use ra_text_edit::TextEdit;
|
||||
|
||||
use crate::{db::RootDatabase, SourceChange, SourceFileEdit};
|
||||
use crate::{db::RootDatabase, source_change::SingleFileChange, SourceChange, SourceFileEdit};
|
||||
|
||||
pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
|
||||
let parse = db.parse(position.file_id);
|
||||
@@ -36,13 +49,12 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour
|
||||
let indent = node_indent(&file, comment.syntax())?;
|
||||
let inserted = format!("\n{}{} ", indent, prefix);
|
||||
let cursor_position = position.offset + TextUnit::of_str(&inserted);
|
||||
let mut edit = TextEditBuilder::default();
|
||||
edit.insert(position.offset, inserted);
|
||||
let edit = TextEdit::insert(position.offset, inserted);
|
||||
|
||||
Some(
|
||||
SourceChange::source_file_edit(
|
||||
"on enter",
|
||||
SourceFileEdit { edit: edit.finish(), file_id: position.file_id },
|
||||
SourceFileEdit { edit, file_id: position.file_id },
|
||||
)
|
||||
.with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }),
|
||||
)
|
||||
@@ -68,39 +80,67 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
|
||||
Some(text[pos..].into())
|
||||
}
|
||||
|
||||
pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<TextEdit> {
|
||||
assert_eq!(file.syntax().text().char_at(eq_offset), Some('='));
|
||||
let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), eq_offset)?;
|
||||
pub(crate) const TRIGGER_CHARS: &str = ".=>";
|
||||
|
||||
pub(crate) fn on_char_typed(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
||||
char_typed: char,
|
||||
) -> Option<SourceChange> {
|
||||
assert!(TRIGGER_CHARS.contains(char_typed));
|
||||
let file = &db.parse(position.file_id).tree();
|
||||
assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
|
||||
let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?;
|
||||
Some(single_file_change.into_source_change(position.file_id))
|
||||
}
|
||||
|
||||
fn on_char_typed_inner(
|
||||
file: &SourceFile,
|
||||
offset: TextUnit,
|
||||
char_typed: char,
|
||||
) -> Option<SingleFileChange> {
|
||||
assert!(TRIGGER_CHARS.contains(char_typed));
|
||||
match char_typed {
|
||||
'.' => on_dot_typed(file, offset),
|
||||
'=' => on_eq_typed(file, offset),
|
||||
'>' => on_arrow_typed(file, offset),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an edit which should be applied after `=` was typed. Primarily,
|
||||
/// this works when adding `let =`.
|
||||
// FIXME: use a snippet completion instead of this hack here.
|
||||
fn on_eq_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
|
||||
assert_eq!(file.syntax().text().char_at(offset), Some('='));
|
||||
let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
|
||||
if let_stmt.has_semi() {
|
||||
return None;
|
||||
}
|
||||
if let Some(expr) = let_stmt.initializer() {
|
||||
let expr_range = expr.syntax().text_range();
|
||||
if expr_range.contains(eq_offset) && eq_offset != expr_range.start() {
|
||||
if expr_range.contains(offset) && offset != expr_range.start() {
|
||||
return None;
|
||||
}
|
||||
if file.syntax().text().slice(eq_offset..expr_range.start()).contains_char('\n') {
|
||||
if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
let offset = let_stmt.syntax().text_range().end();
|
||||
let mut edit = TextEditBuilder::default();
|
||||
edit.insert(offset, ";".to_string());
|
||||
Some(edit.finish())
|
||||
Some(SingleFileChange {
|
||||
label: "add semicolon".to_string(),
|
||||
edit: TextEdit::insert(offset, ";".to_string()),
|
||||
cursor_position: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn on_dot_typed(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
|
||||
let parse = db.parse(position.file_id);
|
||||
assert_eq!(parse.tree().syntax().text().char_at(position.offset), Some('.'));
|
||||
|
||||
let whitespace = parse
|
||||
.tree()
|
||||
.syntax()
|
||||
.token_at_offset(position.offset)
|
||||
.left_biased()
|
||||
.and_then(ast::Whitespace::cast)?;
|
||||
/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
|
||||
fn on_dot_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
|
||||
assert_eq!(file.syntax().text().char_at(offset), Some('.'));
|
||||
let whitespace =
|
||||
file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;
|
||||
|
||||
let current_indent = {
|
||||
let text = whitespace.text();
|
||||
@@ -117,20 +157,36 @@ pub(crate) fn on_dot_typed(db: &RootDatabase, position: FilePosition) -> Option<
|
||||
if current_indent_len == target_indent_len {
|
||||
return None;
|
||||
}
|
||||
let mut edit = TextEditBuilder::default();
|
||||
edit.replace(
|
||||
TextRange::from_to(position.offset - current_indent_len, position.offset),
|
||||
target_indent,
|
||||
);
|
||||
|
||||
let res = SourceChange::source_file_edit_from("reindent dot", position.file_id, edit.finish())
|
||||
.with_cursor(FilePosition {
|
||||
offset: position.offset + target_indent_len - current_indent_len
|
||||
+ TextUnit::of_char('.'),
|
||||
file_id: position.file_id,
|
||||
});
|
||||
Some(SingleFileChange {
|
||||
label: "reindent dot".to_string(),
|
||||
edit: TextEdit::replace(
|
||||
TextRange::from_to(offset - current_indent_len, offset),
|
||||
target_indent,
|
||||
),
|
||||
cursor_position: Some(
|
||||
offset + target_indent_len - current_indent_len + TextUnit::of_char('.'),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
Some(res)
|
||||
/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
|
||||
fn on_arrow_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
|
||||
let file_text = file.syntax().text();
|
||||
assert_eq!(file_text.char_at(offset), Some('>'));
|
||||
let after_arrow = offset + TextUnit::of_char('>');
|
||||
if file_text.char_at(after_arrow) != Some('{') {
|
||||
return None;
|
||||
}
|
||||
if find_node_at_offset::<ast::RetType>(file.syntax(), offset).is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(SingleFileChange {
|
||||
label: "add space after return type".to_string(),
|
||||
edit: TextEdit::insert(after_arrow, " ".to_string()),
|
||||
cursor_position: Some(after_arrow),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -141,239 +197,6 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_on_eq_typed() {
|
||||
fn type_eq(before: &str, after: &str) {
|
||||
let (offset, before) = extract_offset(before);
|
||||
let mut edit = TextEditBuilder::default();
|
||||
edit.insert(offset, "=".to_string());
|
||||
let before = edit.finish().apply(&before);
|
||||
let parse = SourceFile::parse(&before);
|
||||
if let Some(result) = on_eq_typed(&parse.tree(), offset) {
|
||||
let actual = result.apply(&before);
|
||||
assert_eq_text!(after, &actual);
|
||||
} else {
|
||||
assert_eq_text!(&before, after)
|
||||
};
|
||||
}
|
||||
|
||||
// do_check(r"
|
||||
// fn foo() {
|
||||
// let foo =<|>
|
||||
// }
|
||||
// ", r"
|
||||
// fn foo() {
|
||||
// let foo =;
|
||||
// }
|
||||
// ");
|
||||
type_eq(
|
||||
r"
|
||||
fn foo() {
|
||||
let foo <|> 1 + 1
|
||||
}
|
||||
",
|
||||
r"
|
||||
fn foo() {
|
||||
let foo = 1 + 1;
|
||||
}
|
||||
",
|
||||
);
|
||||
// do_check(r"
|
||||
// fn foo() {
|
||||
// let foo =<|>
|
||||
// let bar = 1;
|
||||
// }
|
||||
// ", r"
|
||||
// fn foo() {
|
||||
// let foo =;
|
||||
// let bar = 1;
|
||||
// }
|
||||
// ");
|
||||
}
|
||||
|
||||
fn type_dot(before: &str, after: &str) {
|
||||
let (offset, before) = extract_offset(before);
|
||||
let mut edit = TextEditBuilder::default();
|
||||
edit.insert(offset, ".".to_string());
|
||||
let before = edit.finish().apply(&before);
|
||||
let (analysis, file_id) = single_file(&before);
|
||||
if let Some(result) = analysis.on_dot_typed(FilePosition { offset, file_id }).unwrap() {
|
||||
assert_eq!(result.source_file_edits.len(), 1);
|
||||
let actual = result.source_file_edits[0].edit.apply(&before);
|
||||
assert_eq_text!(after, &actual);
|
||||
} else {
|
||||
assert_eq_text!(&before, after)
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indents_new_chain_call() {
|
||||
type_dot(
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.
|
||||
}
|
||||
",
|
||||
);
|
||||
type_dot(
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.
|
||||
}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indents_new_chain_call_with_semi() {
|
||||
type_dot(
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
<|>;
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.;
|
||||
}
|
||||
",
|
||||
);
|
||||
type_dot(
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
<|>;
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.;
|
||||
}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indents_continued_chain_call() {
|
||||
type_dot(
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.first()
|
||||
<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.first()
|
||||
.
|
||||
}
|
||||
",
|
||||
);
|
||||
type_dot(
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.first()
|
||||
<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.first()
|
||||
.
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indents_middle_of_chain_call() {
|
||||
type_dot(
|
||||
r"
|
||||
fn source_impl() {
|
||||
let var = enum_defvariant_list().unwrap()
|
||||
<|>
|
||||
.nth(92)
|
||||
.unwrap();
|
||||
}
|
||||
",
|
||||
r"
|
||||
fn source_impl() {
|
||||
let var = enum_defvariant_list().unwrap()
|
||||
.
|
||||
.nth(92)
|
||||
.unwrap();
|
||||
}
|
||||
",
|
||||
);
|
||||
type_dot(
|
||||
r"
|
||||
fn source_impl() {
|
||||
let var = enum_defvariant_list().unwrap()
|
||||
<|>
|
||||
.nth(92)
|
||||
.unwrap();
|
||||
}
|
||||
",
|
||||
r"
|
||||
fn source_impl() {
|
||||
let var = enum_defvariant_list().unwrap()
|
||||
.
|
||||
.nth(92)
|
||||
.unwrap();
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dont_indent_freestanding_dot() {
|
||||
type_dot(
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
.
|
||||
}
|
||||
",
|
||||
);
|
||||
type_dot(
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
.
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_on_enter() {
|
||||
fn apply_on_enter(before: &str) -> Option<String> {
|
||||
@@ -426,4 +249,214 @@ fn foo() {}
|
||||
);
|
||||
do_check_noop(r"<|>//! docz");
|
||||
}
|
||||
|
||||
fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> {
|
||||
let (offset, before) = extract_offset(before);
|
||||
let edit = TextEdit::insert(offset, char_typed.to_string());
|
||||
let before = edit.apply(&before);
|
||||
let parse = SourceFile::parse(&before);
|
||||
on_char_typed_inner(&parse.tree(), offset, char_typed)
|
||||
.map(|it| (it.edit.apply(&before), it))
|
||||
}
|
||||
|
||||
fn type_char(char_typed: char, before: &str, after: &str) {
|
||||
let (actual, file_change) = do_type_char(char_typed, before)
|
||||
.unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
|
||||
|
||||
if after.contains("<|>") {
|
||||
let (offset, after) = extract_offset(after);
|
||||
assert_eq_text!(&after, &actual);
|
||||
assert_eq!(file_change.cursor_position, Some(offset))
|
||||
} else {
|
||||
assert_eq_text!(after, &actual);
|
||||
}
|
||||
}
|
||||
|
||||
fn type_char_noop(char_typed: char, before: &str) {
|
||||
let file_change = do_type_char(char_typed, before);
|
||||
assert!(file_change.is_none())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_on_eq_typed() {
|
||||
// do_check(r"
|
||||
// fn foo() {
|
||||
// let foo =<|>
|
||||
// }
|
||||
// ", r"
|
||||
// fn foo() {
|
||||
// let foo =;
|
||||
// }
|
||||
// ");
|
||||
type_char(
|
||||
'=',
|
||||
r"
|
||||
fn foo() {
|
||||
let foo <|> 1 + 1
|
||||
}
|
||||
",
|
||||
r"
|
||||
fn foo() {
|
||||
let foo = 1 + 1;
|
||||
}
|
||||
",
|
||||
);
|
||||
// do_check(r"
|
||||
// fn foo() {
|
||||
// let foo =<|>
|
||||
// let bar = 1;
|
||||
// }
|
||||
// ", r"
|
||||
// fn foo() {
|
||||
// let foo =;
|
||||
// let bar = 1;
|
||||
// }
|
||||
// ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indents_new_chain_call() {
|
||||
type_char(
|
||||
'.',
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.
|
||||
}
|
||||
",
|
||||
);
|
||||
type_char_noop(
|
||||
'.',
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
<|>
|
||||
}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indents_new_chain_call_with_semi() {
|
||||
type_char(
|
||||
'.',
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
<|>;
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.;
|
||||
}
|
||||
",
|
||||
);
|
||||
type_char_noop(
|
||||
'.',
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
<|>;
|
||||
}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indents_continued_chain_call() {
|
||||
type_char(
|
||||
'.',
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.first()
|
||||
<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.first()
|
||||
.
|
||||
}
|
||||
",
|
||||
);
|
||||
type_char_noop(
|
||||
'.',
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.first()
|
||||
<|>
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indents_middle_of_chain_call() {
|
||||
type_char(
|
||||
'.',
|
||||
r"
|
||||
fn source_impl() {
|
||||
let var = enum_defvariant_list().unwrap()
|
||||
<|>
|
||||
.nth(92)
|
||||
.unwrap();
|
||||
}
|
||||
",
|
||||
r"
|
||||
fn source_impl() {
|
||||
let var = enum_defvariant_list().unwrap()
|
||||
.
|
||||
.nth(92)
|
||||
.unwrap();
|
||||
}
|
||||
",
|
||||
);
|
||||
type_char_noop(
|
||||
'.',
|
||||
r"
|
||||
fn source_impl() {
|
||||
let var = enum_defvariant_list().unwrap()
|
||||
<|>
|
||||
.nth(92)
|
||||
.unwrap();
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dont_indent_freestanding_dot() {
|
||||
type_char_noop(
|
||||
'.',
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
<|>
|
||||
}
|
||||
",
|
||||
);
|
||||
type_char_noop(
|
||||
'.',
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
<|>
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adds_space_after_return_type() {
|
||||
type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ pub fn server_capabilities() -> ServerCapabilities {
|
||||
document_range_formatting_provider: None,
|
||||
document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
|
||||
first_trigger_character: "=".to_string(),
|
||||
more_trigger_character: Some(vec![".".to_string()]),
|
||||
more_trigger_character: Some(vec![".".to_string(), ">".to_string()]),
|
||||
}),
|
||||
selection_range_provider: Some(GenericCapability::default()),
|
||||
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
//! FIXME: write short doc here
|
||||
//! Config used by the language server.
|
||||
//!
|
||||
//! We currently get this config from `initialize` LSP request, which is not the
|
||||
//! best way to do it, but was the simplest thing we could implement.
|
||||
//!
|
||||
//! Of particular interest is the `feature_flags` hash map: while other fields
|
||||
//! configure the server itself, feature flags are passed into analysis, and
|
||||
//! tweak things like automatic insertion of `()` in completions.
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
@@ -72,10 +79,7 @@ fn deserialize_init_options_defaults() {
|
||||
assert_eq!(default, serde_json::from_str(r#"{}"#).unwrap());
|
||||
assert_eq!(
|
||||
default,
|
||||
serde_json::from_str(
|
||||
r#"{"publishDecorations":null, "showWorkspaceLoaded":null, "lruCapacity":null}"#
|
||||
)
|
||||
.unwrap()
|
||||
serde_json::from_str(r#"{"publishDecorations":null, "lruCapacity":null}"#).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user