mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Merge branch 'master' into feature/themes
This commit is contained in:
commit
b21d9337d9
233 changed files with 14900 additions and 10781 deletions
8
.github/workflows/ci.yaml
vendored
8
.github/workflows/ci.yaml
vendored
|
@ -14,6 +14,7 @@ jobs:
|
|||
env:
|
||||
RUSTFLAGS: -D warnings
|
||||
CARGO_INCREMENTAL: 0
|
||||
RUN_SLOW_TESTS: 1
|
||||
steps:
|
||||
|
||||
- name: Checkout repository
|
||||
|
@ -46,9 +47,10 @@ jobs:
|
|||
|
||||
- name: Prepare build directory for cache
|
||||
run: |
|
||||
find ./target/debug -maxdepth 1 -type f -delete && \
|
||||
rm -fr ./target/debug/{deps,.fingerprint}/{*ra_*,*heavy_test*,*gen_lsp*,*thread_worker*} && \
|
||||
rm -f ./target/.rustc_info.json
|
||||
find ./target/debug -maxdepth 1 -type f -delete \
|
||||
&& rm -fr ./target/debug/{deps,.fingerprint}/{*ra_*,*heavy_test*,*gen_lsp*,*thread_worker*} \
|
||||
&& rm -f ./target/.rustc_info.json \
|
||||
&& rm ./target/.slow_tests_cookie
|
||||
|
||||
type-script:
|
||||
name: TypeScript
|
||||
|
|
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
@ -16,7 +16,7 @@
|
|||
"env": {
|
||||
"__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/debug/ra_lsp_server"
|
||||
},
|
||||
"outFiles": ["${workspaceFolder}/editors/code/out/**/*.js"],
|
||||
"outFiles": ["${workspaceFolder}/editors/code/bundle/**/*.js"],
|
||||
"preLaunchTask": "Build All"
|
||||
},
|
||||
{
|
||||
|
|
361
Cargo.lock
generated
361
Cargo.lock
generated
|
@ -10,7 +10,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.24"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "anymap"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -23,7 +28,7 @@ name = "atty"
|
|||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -39,7 +44,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -48,8 +53,8 @@ name = "backtrace-sys"
|
|||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -101,18 +106,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.47"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -123,38 +128,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
[[package]]
|
||||
name = "chalk-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30#095cd38a4f16337913bba487f2055b9ca0179f30"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5#ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5"
|
||||
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.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chalk-engine"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30#095cd38a4f16337913bba487f2055b9ca0179f30"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5#ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5"
|
||||
dependencies = [
|
||||
"chalk-macros 0.1.1 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chalk-ir"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30#095cd38a4f16337913bba487f2055b9ca0179f30"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5#ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5"
|
||||
dependencies = [
|
||||
"chalk-derive 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-engine 0.9.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"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/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30#095cd38a4f16337913bba487f2055b9ca0179f30"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5#ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -162,40 +166,30 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "chalk-rust-ir"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30#095cd38a4f16337913bba487f2055b9ca0179f30"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5#ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5"
|
||||
dependencies = [
|
||||
"chalk-derive 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-engine 0.9.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chalk-solve"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30#095cd38a4f16337913bba487f2055b9ca0179f30"
|
||||
source = "git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5#ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5"
|
||||
dependencies = [
|
||||
"chalk-derive 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-engine 0.9.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-rust-ir 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.2 (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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clicolors-control"
|
||||
version = "1.0.1"
|
||||
|
@ -203,7 +197,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -223,7 +217,7 @@ dependencies = [
|
|||
"clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -323,13 +317,22 @@ name = "encode_unicode"
|
|||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.8"
|
||||
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)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -339,18 +342,6 @@ name = "fixedbitset"
|
|||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "flexi_logger"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.6"
|
||||
|
@ -380,7 +371,7 @@ name = "fsevent-sys"
|
|||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -416,15 +407,10 @@ 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)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.4"
|
||||
|
@ -447,10 +433,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.3"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -478,7 +472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -486,7 +480,7 @@ name = "inotify-sys"
|
|||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -497,8 +491,8 @@ dependencies = [
|
|||
"console 0.9.1 (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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (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)",
|
||||
]
|
||||
|
@ -508,7 +502,7 @@ name = "iovec"
|
|||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -530,7 +524,7 @@ version = "0.3.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -539,9 +533,9 @@ name = "jemalloc-sys"
|
|||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -550,7 +544,7 @@ version = "0.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -589,7 +583,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.65"
|
||||
version = "0.2.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -620,18 +614,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"crossbeam-channel 0.4.0 (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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lsp-types"
|
||||
version = "0.61.0"
|
||||
version = "0.66.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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (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)",
|
||||
]
|
||||
|
@ -664,7 +658,7 @@ dependencies = [
|
|||
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -674,7 +668,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mio-extras"
|
||||
version = "2.0.5"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -700,7 +694,7 @@ version = "0.2.33"
|
|||
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)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -715,22 +709,13 @@ dependencies = [
|
|||
"fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.10"
|
||||
|
@ -744,8 +729,8 @@ name = "num_cpus"
|
|||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hermit-abi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -774,9 +759,9 @@ 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)",
|
||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -797,7 +782,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.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -831,7 +816,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.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -859,14 +844,6 @@ dependencies = [
|
|||
"regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.2"
|
||||
|
@ -928,11 +905,13 @@ dependencies = [
|
|||
name = "ra_cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"flexi_logger 0.14.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pico-args 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_batch 0.1.0",
|
||||
"ra_db 0.1.0",
|
||||
"ra_hir 0.1.0",
|
||||
"ra_hir_def 0.1.0",
|
||||
"ra_hir_ty 0.1.0",
|
||||
"ra_ide 0.1.0",
|
||||
"ra_prof 0.1.0",
|
||||
"ra_syntax 0.1.0",
|
||||
|
@ -963,11 +942,13 @@ dependencies = [
|
|||
name = "ra_hir"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_db 0.1.0",
|
||||
"ra_hir_def 0.1.0",
|
||||
"ra_hir_expand 0.1.0",
|
||||
"ra_hir_ty 0.1.0",
|
||||
"ra_prof 0.1.0",
|
||||
"ra_syntax 0.1.0",
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -976,6 +957,9 @@ dependencies = [
|
|||
name = "ra_hir_def"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anymap 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"insta 0.12.0 (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)",
|
||||
|
@ -995,6 +979,7 @@ dependencies = [
|
|||
name = "ra_hir_expand"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_arena 0.1.0",
|
||||
"ra_db 0.1.0",
|
||||
|
@ -1010,9 +995,9 @@ name = "ra_hir_ty"
|
|||
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/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-rust-ir 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-solve 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)",
|
||||
"chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)",
|
||||
"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)",
|
||||
|
@ -1031,6 +1016,7 @@ dependencies = [
|
|||
name = "ra_ide"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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)",
|
||||
|
@ -1060,11 +1046,11 @@ name = "ra_lsp_server"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"flexi_logger 0.14.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lsp-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lsp-types 0.61.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lsp-types 0.66.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_ide 0.1.0",
|
||||
"ra_prof 0.1.0",
|
||||
|
@ -1075,8 +1061,8 @@ 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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (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",
|
||||
"threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1091,7 +1077,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 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"test_utils 0.1.0",
|
||||
]
|
||||
|
||||
|
@ -1117,14 +1103,14 @@ dependencies = [
|
|||
name = "ra_project_model"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cargo_metadata 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cargo_metadata 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_arena 0.1.0",
|
||||
"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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1136,9 +1122,10 @@ dependencies = [
|
|||
"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",
|
||||
"rowan 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rowan 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smol_str 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"test_utils 0.1.0",
|
||||
"walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1189,7 +1176,7 @@ version = "0.6.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1207,7 +1194,7 @@ version = "0.7.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"libc 0.2.66 (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)",
|
||||
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1282,7 +1269,7 @@ name = "rand_jitter"
|
|||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1294,7 +1281,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1396,12 +1383,12 @@ 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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rowan"
|
||||
version = "0.7.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1456,7 +1443,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.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1467,7 +1454,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.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1489,7 +1476,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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1499,30 +1486,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.103"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.103"
|
||||
version = "1.0.104"
|
||||
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.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.42"
|
||||
version = "1.0.44"
|
||||
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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1532,7 +1519,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.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1542,7 +1529,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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1553,7 +1540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -1561,19 +1548,7 @@ name = "smol_str"
|
|||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"psm 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1583,7 +1558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.8"
|
||||
version = "1.0.11"
|
||||
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)",
|
||||
|
@ -1597,7 +1572,7 @@ version = "3.1.0"
|
|||
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)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1609,7 +1584,7 @@ name = "termios"
|
|||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1617,7 +1592,7 @@ name = "test_utils"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"text_unit 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1647,16 +1622,6 @@ dependencies = [
|
|||
"num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
|
@ -1678,7 +1643,7 @@ name = "unicode-normalization"
|
|||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1699,7 +1664,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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1708,7 +1673,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.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1781,12 +1746,12 @@ dependencies = [
|
|||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pico-args 0.3.0 (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)",
|
||||
"ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1798,14 +1763,10 @@ dependencies = [
|
|||
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
|
||||
"checksum anyhow 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b412394828b7ca486b362f300b762d8e43dafd6f0d727b63f1cd2ade207c6cef"
|
||||
"checksum anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14"
|
||||
"checksum anymap 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344"
|
||||
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
|
||||
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
||||
|
@ -1818,16 +1779,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245"
|
||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
|
||||
"checksum cargo_metadata 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8d2d1617e838936c0d2323a65cc151e03ae19a7678dd24f72bccf27119b90a5d"
|
||||
"checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8"
|
||||
"checksum cargo_metadata 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46e3374c604fb39d1a2f35ed5e4a4e30e60d01fab49446e08f1b3e9a90aef202"
|
||||
"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum chalk-derive 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "<none>"
|
||||
"checksum chalk-engine 0.9.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "<none>"
|
||||
"checksum chalk-ir 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "<none>"
|
||||
"checksum chalk-macros 0.1.1 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "<none>"
|
||||
"checksum chalk-rust-ir 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "<none>"
|
||||
"checksum chalk-solve 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "<none>"
|
||||
"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01"
|
||||
"checksum chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "<none>"
|
||||
"checksum chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "<none>"
|
||||
"checksum chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "<none>"
|
||||
"checksum chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "<none>"
|
||||
"checksum chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "<none>"
|
||||
"checksum chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "<none>"
|
||||
"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"
|
||||
"checksum console 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f5d540c2d34ac9dd0deb5f3b5f54c36c79efa78f6b3ad19106a554d07a7b5d9f"
|
||||
|
@ -1843,9 +1803,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
"checksum ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8944dc8fa28ce4a38f778bd46bf7d923fe73eed5a439398507246c8e017e6f36"
|
||||
"checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
"checksum filetime 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1ff6d4dab0aa0c8e6346d46052e93b13a16cf847b54ed357087c35011048cc7d"
|
||||
"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
|
||||
"checksum flexi_logger 0.14.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a13ea6b8a4debecf47bf3966d56db0e21366bc3a3649ba159e1a9e6fdd36a4f4"
|
||||
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||
"checksum format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f7aea5a5909a74969507051a3b17adc84737e31a5f910559892aedce026f4d53"
|
||||
"checksum fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"
|
||||
|
@ -1856,10 +1816,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"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.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"
|
||||
"checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120"
|
||||
"checksum hermit-abi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f629dc602392d3ec14bfc8a09b5e644d7ffd725102b48b81e59f90f2633621d7"
|
||||
"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
|
||||
"checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2"
|
||||
"checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718"
|
||||
|
@ -1877,21 +1837,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cc4fd87be4a815fd373e02773983940f0d75fb26fde8c098e9e45f7af03154c0"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
|
||||
"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8"
|
||||
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
||||
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
|
||||
"checksum lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586"
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum lsp-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ba36405bd742139ab79c246ca5adb7fde2fe1a0f495e2c8e2f607b607dedb12"
|
||||
"checksum lsp-types 0.61.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa3268fbe8beb2795c2fb327bf44f4f3d24f5fe9ebc18d7e2980afd444d72bcf"
|
||||
"checksum lsp-types 0.66.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a2dddfe2791cbf4b5eff5a581e45becf47a24b128a62de80e7cc135bf50064"
|
||||
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
||||
"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
|
||||
"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f"
|
||||
"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40"
|
||||
"checksum mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
|
||||
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
||||
"checksum notify 4.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "199628fc33b21bc767baa057490b00b382ecbae030803a7b36292422d15b778b"
|
||||
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
|
||||
"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72"
|
||||
"checksum once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed"
|
||||
|
@ -1907,7 +1866,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
|
||||
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
|
||||
"checksum proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cf147e022eacf0c8a054ab864914a7602618adba841d800a9a9868a5237a529f"
|
||||
"checksum psm 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b14fc68b454f875abc8354c2555e1d56596f74833ddc0f77f87f4871ed6a30e0"
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
"checksum ra_vfs 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bc898f237e4b4498959ae0100c688793a23e77624d44ef710ba70094217f98e0"
|
||||
|
@ -1935,7 +1893,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bedde000f40f2921ce439ea165c9c53fd629bfa115140c72e22aceacb4a21954"
|
||||
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
||||
"checksum ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ece421e0c4129b90e4a35b6f625e472e96c552136f5093a2f4fa2bbb75a62d5"
|
||||
"checksum rowan 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca620bbf9c48c92b5cef19f96354a309ac36b7d8ef7c591e66117335c8b1988b"
|
||||
"checksum rowan 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3eb10a10a48f0f809a217bcf074b85a03dcf79831bae80e7f1a043d0897463e2"
|
||||
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
"checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8"
|
||||
"checksum rustc_lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5"
|
||||
|
@ -1947,24 +1905,22 @@ 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.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702"
|
||||
"checksum serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0"
|
||||
"checksum serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "1a3351dcbc1f067e2c92ab7c3c1f288ad1a4cffc470b5aaddb4c2e0a3ae80043"
|
||||
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||
"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7"
|
||||
"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 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86"
|
||||
"checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4"
|
||||
"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.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92"
|
||||
"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238"
|
||||
"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"
|
||||
"checksum thin-dst 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c52fd98a9e4913c466d83381a59245691875d2f3e04611fca57f964bd8aa96e1"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865"
|
||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
"checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf"
|
||||
|
@ -1983,4 +1939,3 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
|
||||
"checksum yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
|
||||
|
|
|
@ -11,3 +11,4 @@ incremental = true
|
|||
debug = 0 # set this to 1 or 2 to get more useful backtraces in debugger
|
||||
|
||||
[patch.'crates-io']
|
||||
# rowan = { path = "../rowan" }
|
|
@ -37,7 +37,8 @@ $ cargo xtask install
|
|||
$ cargo xtask install --server
|
||||
```
|
||||
|
||||
For non-standard setup of VS Code and other editors, see [./docs/user](./docs/user).
|
||||
For non-standard setup of VS Code and other editors, or if the language server
|
||||
cannot start, see [./docs/user](./docs/user).
|
||||
|
||||
## Documentation
|
||||
|
||||
|
@ -57,7 +58,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frls-2.2E0
|
|||
## Quick Links
|
||||
|
||||
* API docs: https://rust-analyzer.github.io/rust-analyzer/ra_ide/
|
||||
|
||||
* Website: https://rust-analyzer.github.io/
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! This module defines `AssistCtx` -- the API surface that is exposed to assists.
|
||||
use hir::{db::HirDatabase, SourceAnalyzer};
|
||||
use hir::{db::HirDatabase, InFile, SourceAnalyzer};
|
||||
use ra_db::FileRange;
|
||||
use ra_fmt::{leading_indent, reindent};
|
||||
use ra_syntax::{
|
||||
|
@ -117,7 +117,7 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
|
|||
node: &SyntaxNode,
|
||||
offset: Option<TextUnit>,
|
||||
) -> SourceAnalyzer {
|
||||
SourceAnalyzer::new(self.db, hir::Source::new(self.frange.file_id.into(), node), offset)
|
||||
SourceAnalyzer::new(self.db, InFile::new(self.frange.file_id.into(), node), offset)
|
||||
}
|
||||
|
||||
pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
|
||||
|
|
206
crates/ra_assists/src/assists/add_custom_impl.rs
Normal file
206
crates/ra_assists/src/assists/add_custom_impl.rs
Normal file
|
@ -0,0 +1,206 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
use hir::db::HirDatabase;
|
||||
use join_to_string::join;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode},
|
||||
Direction, SmolStr,
|
||||
SyntaxKind::{IDENT, WHITESPACE},
|
||||
TextRange, TextUnit,
|
||||
};
|
||||
|
||||
const DERIVE_TRAIT: &'static str = "derive";
|
||||
|
||||
// Assist: add_custom_impl
|
||||
//
|
||||
// Adds impl block for derived trait.
|
||||
//
|
||||
// ```
|
||||
// #[derive(Deb<|>ug, Display)]
|
||||
// struct S;
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// #[derive(Display)]
|
||||
// struct S;
|
||||
//
|
||||
// impl Debug for S {
|
||||
//
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_custom_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
let input = ctx.find_node_at_offset::<ast::AttrInput>()?;
|
||||
let attr = input.syntax().parent().and_then(ast::Attr::cast)?;
|
||||
|
||||
let attr_name = attr
|
||||
.syntax()
|
||||
.descendants_with_tokens()
|
||||
.filter(|t| t.kind() == IDENT)
|
||||
.find_map(|i| i.into_token())
|
||||
.filter(|t| *t.text() == DERIVE_TRAIT)?
|
||||
.text()
|
||||
.clone();
|
||||
|
||||
let trait_token =
|
||||
ctx.token_at_offset().filter(|t| t.kind() == IDENT && *t.text() != attr_name).next()?;
|
||||
|
||||
let annotated = attr.syntax().siblings(Direction::Next).find_map(|s| ast::Name::cast(s))?;
|
||||
let annotated_name = annotated.syntax().text().to_string();
|
||||
let start_offset = annotated.syntax().parent()?.text_range().end();
|
||||
|
||||
ctx.add_assist(AssistId("add_custom_impl"), "add custom impl", |edit| {
|
||||
edit.target(attr.syntax().text_range());
|
||||
|
||||
let new_attr_input = input
|
||||
.syntax()
|
||||
.descendants_with_tokens()
|
||||
.filter(|t| t.kind() == IDENT)
|
||||
.filter_map(|t| t.into_token().map(|t| t.text().clone()))
|
||||
.filter(|t| t != trait_token.text())
|
||||
.collect::<Vec<SmolStr>>();
|
||||
let has_more_derives = new_attr_input.len() > 0;
|
||||
let new_attr_input =
|
||||
join(new_attr_input.iter()).separator(", ").surround_with("(", ")").to_string();
|
||||
let new_attr_input_len = new_attr_input.len();
|
||||
|
||||
let mut buf = String::new();
|
||||
buf.push_str("\n\nimpl ");
|
||||
buf.push_str(trait_token.text().as_str());
|
||||
buf.push_str(" for ");
|
||||
buf.push_str(annotated_name.as_str());
|
||||
buf.push_str(" {\n");
|
||||
|
||||
let cursor_delta = if has_more_derives {
|
||||
edit.replace(input.syntax().text_range(), new_attr_input);
|
||||
input.syntax().text_range().len() - TextUnit::from_usize(new_attr_input_len)
|
||||
} else {
|
||||
let attr_range = attr.syntax().text_range();
|
||||
edit.delete(attr_range);
|
||||
|
||||
let line_break_range = attr
|
||||
.syntax()
|
||||
.next_sibling_or_token()
|
||||
.filter(|t| t.kind() == WHITESPACE)
|
||||
.map(|t| t.text_range())
|
||||
.unwrap_or(TextRange::from_to(TextUnit::from(0), TextUnit::from(0)));
|
||||
edit.delete(line_break_range);
|
||||
|
||||
attr_range.len() + line_break_range.len()
|
||||
};
|
||||
|
||||
edit.set_cursor(start_offset + TextUnit::of_str(&buf) - cursor_delta);
|
||||
buf.push_str("\n}");
|
||||
edit.insert(start_offset, buf);
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::helpers::{check_assist, check_assist_not_applicable};
|
||||
|
||||
#[test]
|
||||
fn add_custom_impl_for_unique_input() {
|
||||
check_assist(
|
||||
add_custom_impl,
|
||||
"
|
||||
#[derive(Debu<|>g)]
|
||||
struct Foo {
|
||||
bar: String,
|
||||
}
|
||||
",
|
||||
"
|
||||
struct Foo {
|
||||
bar: String,
|
||||
}
|
||||
|
||||
impl Debug for Foo {
|
||||
<|>
|
||||
}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_custom_impl_for_with_visibility_modifier() {
|
||||
check_assist(
|
||||
add_custom_impl,
|
||||
"
|
||||
#[derive(Debug<|>)]
|
||||
pub struct Foo {
|
||||
bar: String,
|
||||
}
|
||||
",
|
||||
"
|
||||
pub struct Foo {
|
||||
bar: String,
|
||||
}
|
||||
|
||||
impl Debug for Foo {
|
||||
<|>
|
||||
}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_custom_impl_when_multiple_inputs() {
|
||||
check_assist(
|
||||
add_custom_impl,
|
||||
"
|
||||
#[derive(Display, Debug<|>, Serialize)]
|
||||
struct Foo {}
|
||||
",
|
||||
"
|
||||
#[derive(Display, Serialize)]
|
||||
struct Foo {}
|
||||
|
||||
impl Debug for Foo {
|
||||
<|>
|
||||
}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ignore_derive_macro_without_input() {
|
||||
check_assist_not_applicable(
|
||||
add_custom_impl,
|
||||
"
|
||||
#[derive(<|>)]
|
||||
struct Foo {}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ignore_if_cursor_on_param() {
|
||||
check_assist_not_applicable(
|
||||
add_custom_impl,
|
||||
"
|
||||
#[derive<|>(Debug)]
|
||||
struct Foo {}
|
||||
",
|
||||
);
|
||||
|
||||
check_assist_not_applicable(
|
||||
add_custom_impl,
|
||||
"
|
||||
#[derive(Debug)<|>]
|
||||
struct Foo {}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ignore_if_not_derive() {
|
||||
check_assist_not_applicable(
|
||||
add_custom_impl,
|
||||
"
|
||||
#[allow(non_camel_<|>case_types)]
|
||||
struct Foo {}
|
||||
",
|
||||
)
|
||||
}
|
||||
}
|
|
@ -578,17 +578,21 @@ fn apply_auto_import(
|
|||
|
||||
fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
|
||||
let mut ps = Vec::<SmolStr>::with_capacity(10);
|
||||
match path.kind {
|
||||
match path.kind() {
|
||||
hir::PathKind::Abs => ps.push("".into()),
|
||||
hir::PathKind::Crate => ps.push("crate".into()),
|
||||
hir::PathKind::Plain => {}
|
||||
hir::PathKind::Self_ => ps.push("self".into()),
|
||||
hir::PathKind::Super => ps.push("super".into()),
|
||||
hir::PathKind::Type(_) | hir::PathKind::DollarCrate(_) => return None,
|
||||
hir::PathKind::Super(0) => ps.push("self".into()),
|
||||
hir::PathKind::Super(lvl) => {
|
||||
let mut chain = "super".to_string();
|
||||
for _ in 0..*lvl {
|
||||
chain += "::super";
|
||||
}
|
||||
for s in path.segments.iter() {
|
||||
ps.push(s.name.to_string().into());
|
||||
ps.push(chain.into());
|
||||
}
|
||||
hir::PathKind::DollarCrate(_) => return None,
|
||||
}
|
||||
ps.extend(path.segments().iter().map(|it| it.name.to_string().into()));
|
||||
Some(ps)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use format_buf::format;
|
||||
use hir::{db::HirDatabase, FromSource};
|
||||
use hir::{db::HirDatabase, FromSource, InFile};
|
||||
use join_to_string::join;
|
||||
use ra_syntax::{
|
||||
ast::{
|
||||
|
@ -56,42 +56,39 @@ pub(crate) fn add_new(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
|||
let vis = vis.as_ref().map(String::as_str).unwrap_or("");
|
||||
write!(&mut buf, " {}fn new(", vis).unwrap();
|
||||
|
||||
join(field_list.fields().map(|f| {
|
||||
format!(
|
||||
"{}: {}",
|
||||
f.name().unwrap().syntax().text(),
|
||||
f.ascribed_type().unwrap().syntax().text()
|
||||
)
|
||||
join(field_list.fields().filter_map(|f| {
|
||||
Some(format!("{}: {}", f.name()?.syntax().text(), f.ascribed_type()?.syntax().text()))
|
||||
}))
|
||||
.separator(", ")
|
||||
.to_buf(&mut buf);
|
||||
|
||||
buf.push_str(") -> Self { Self {");
|
||||
|
||||
join(field_list.fields().map(|f| f.name().unwrap().syntax().text()))
|
||||
join(field_list.fields().filter_map(|f| Some(f.name()?.syntax().text())))
|
||||
.separator(", ")
|
||||
.surround_with(" ", " ")
|
||||
.to_buf(&mut buf);
|
||||
|
||||
buf.push_str("} }");
|
||||
|
||||
let (start_offset, end_offset) = if let Some(impl_block) = impl_block {
|
||||
let (start_offset, end_offset) = impl_block
|
||||
.and_then(|impl_block| {
|
||||
buf.push('\n');
|
||||
let start = impl_block
|
||||
.syntax()
|
||||
.descendants_with_tokens()
|
||||
.find(|t| t.kind() == T!['{'])
|
||||
.unwrap()
|
||||
.find(|t| t.kind() == T!['{'])?
|
||||
.text_range()
|
||||
.end();
|
||||
|
||||
(start, TextUnit::from_usize(1))
|
||||
} else {
|
||||
Some((start, TextUnit::from_usize(1)))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
buf = generate_impl_text(&strukt, &buf);
|
||||
let start = strukt.syntax().text_range().end();
|
||||
|
||||
(start, TextUnit::from_usize(3))
|
||||
};
|
||||
});
|
||||
|
||||
edit.set_cursor(start_offset + TextUnit::of_str(&buf) - end_offset);
|
||||
edit.insert(start_offset, buf);
|
||||
|
@ -141,48 +138,45 @@ fn find_struct_impl(
|
|||
})?;
|
||||
|
||||
let struct_ty = {
|
||||
let src = hir::Source { file_id: ctx.frange.file_id.into(), value: strukt.clone() };
|
||||
hir::Struct::from_source(db, src).unwrap().ty(db)
|
||||
let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() };
|
||||
hir::Struct::from_source(db, src)?.ty(db)
|
||||
};
|
||||
|
||||
let mut found_new_fn = false;
|
||||
|
||||
let block = module.descendants().filter_map(ast::ImplBlock::cast).find(|impl_blk| {
|
||||
if found_new_fn {
|
||||
return false;
|
||||
}
|
||||
|
||||
let src = hir::Source { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() };
|
||||
let blk = hir::ImplBlock::from_source(db, src).unwrap();
|
||||
let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| {
|
||||
let src = InFile { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() };
|
||||
let blk = hir::ImplBlock::from_source(db, src)?;
|
||||
|
||||
let same_ty = blk.target_ty(db) == struct_ty;
|
||||
let not_trait_impl = blk.target_trait(db).is_none();
|
||||
|
||||
if !(same_ty && not_trait_impl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
found_new_fn = has_new_fn(impl_blk);
|
||||
true
|
||||
});
|
||||
|
||||
if found_new_fn {
|
||||
None
|
||||
} else {
|
||||
Some(block)
|
||||
Some(impl_blk)
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(ref impl_blk) = block {
|
||||
if has_new_fn(impl_blk) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(block)
|
||||
}
|
||||
|
||||
fn has_new_fn(imp: &ast::ImplBlock) -> bool {
|
||||
if let Some(il) = imp.item_list() {
|
||||
for item in il.impl_items() {
|
||||
if let ast::ImplItem::FnDef(f) = item {
|
||||
if f.name().unwrap().text().eq_ignore_ascii_case("new") {
|
||||
if let Some(name) = f.name() {
|
||||
if name.text().eq_ignore_ascii_case("new") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
|
|
@ -83,8 +83,8 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
|
|||
let parent_container = parent_block.syntax().parent()?.parent()?;
|
||||
|
||||
let early_expression: ast::Expr = match parent_container.kind() {
|
||||
WHILE_EXPR | LOOP_EXPR => make::expr_continue().into(),
|
||||
FN_DEF => make::expr_return().into(),
|
||||
WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
|
||||
FN_DEF => make::expr_return(),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
@ -116,13 +116,13 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
|
|||
)
|
||||
.into(),
|
||||
),
|
||||
make::expr_path(make::path_from_name_ref(make::name_ref("it"))).into(),
|
||||
make::expr_path(make::path_from_name_ref(make::name_ref("it"))),
|
||||
);
|
||||
|
||||
let sad_arm = make::match_arm(
|
||||
// FIXME: would be cool to use `None` or `Err(_)` if appropriate
|
||||
once(make::placeholder_pat().into()),
|
||||
early_expression.into(),
|
||||
early_expression,
|
||||
);
|
||||
|
||||
make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
|
||||
|
@ -130,7 +130,7 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
|
|||
|
||||
let let_stmt = make::let_stmt(
|
||||
make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
|
||||
Some(match_expr.into()),
|
||||
Some(match_expr),
|
||||
);
|
||||
let let_stmt = if_indent_level.increase_indent(let_stmt);
|
||||
replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
|
||||
|
|
|
@ -2,6 +2,25 @@
|
|||
|
||||
use super::check;
|
||||
|
||||
#[test]
|
||||
fn doctest_add_custom_impl() {
|
||||
check(
|
||||
"add_custom_impl",
|
||||
r#####"
|
||||
#[derive(Deb<|>ug, Display)]
|
||||
struct S;
|
||||
"#####,
|
||||
r#####"
|
||||
#[derive(Display)]
|
||||
struct S;
|
||||
|
||||
impl Debug for S {
|
||||
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_derive() {
|
||||
check(
|
||||
|
|
|
@ -95,6 +95,7 @@ mod assists {
|
|||
mod add_derive;
|
||||
mod add_explicit_type;
|
||||
mod add_impl;
|
||||
mod add_custom_impl;
|
||||
mod add_new;
|
||||
mod apply_demorgan;
|
||||
mod invert_if;
|
||||
|
@ -121,6 +122,7 @@ mod assists {
|
|||
add_derive::add_derive,
|
||||
add_explicit_type::add_explicit_type,
|
||||
add_impl::add_impl,
|
||||
add_custom_impl::add_custom_impl,
|
||||
add_new::add_new,
|
||||
apply_demorgan::apply_demorgan,
|
||||
invert_if::invert_if,
|
||||
|
|
|
@ -43,5 +43,3 @@ impl FileLoader for TestDB {
|
|||
FileLoaderDelegate(self).relevant_crates(file_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl hir::debug::HirDebugHelper for TestDB {}
|
||||
|
|
|
@ -22,7 +22,7 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
|
|||
|
||||
pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
|
||||
let root = std::env::current_dir()?.join(root);
|
||||
let ws = ProjectWorkspace::discover(root.as_ref())?;
|
||||
let ws = ProjectWorkspace::discover(root.as_ref(), &Default::default())?;
|
||||
let project_roots = ws.to_roots();
|
||||
let (sender, receiver) = unbounded();
|
||||
let sender = Box::new(move |t| sender.send(t).unwrap());
|
||||
|
|
|
@ -7,12 +7,14 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
pico-args = "0.3.0"
|
||||
flexi_logger = "0.14.0"
|
||||
env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] }
|
||||
|
||||
ra_syntax = { path = "../ra_syntax" }
|
||||
ra_ide = { path = "../ra_ide" }
|
||||
ra_batch = { path = "../ra_batch" }
|
||||
ra_hir = { path = "../ra_hir" }
|
||||
hir = { path = "../ra_hir", package = "ra_hir" }
|
||||
hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" }
|
||||
hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
|
||||
[dependencies.ra_prof]
|
||||
|
|
|
@ -2,8 +2,13 @@
|
|||
|
||||
use std::{collections::HashSet, fmt::Write, path::Path, time::Instant};
|
||||
|
||||
use hir::{
|
||||
db::{DefDatabase, HirDatabase},
|
||||
AssocItem, Crate, HasSource, HirDisplay, ModuleDef,
|
||||
};
|
||||
use hir_def::FunctionId;
|
||||
use hir_ty::{Ty, TypeWalk};
|
||||
use ra_db::SourceDatabaseExt;
|
||||
use ra_hir::{AssocItem, Crate, HasSource, HirDisplay, ModuleDef, Ty, TypeWalk};
|
||||
use ra_syntax::AstNode;
|
||||
|
||||
use crate::{progress_report::ProgressReport, Result, Verbosity};
|
||||
|
@ -101,8 +106,9 @@ pub fn run(
|
|||
continue;
|
||||
}
|
||||
}
|
||||
let body = f.body(db);
|
||||
let inference_result = f.infer(db);
|
||||
let f_id = FunctionId::from(f);
|
||||
let body = db.body(f_id.into());
|
||||
let inference_result = db.infer(f_id.into());
|
||||
for (expr_id, _) in body.exprs.iter() {
|
||||
let ty = &inference_result[expr_id];
|
||||
num_exprs += 1;
|
||||
|
@ -122,7 +128,8 @@ pub fn run(
|
|||
if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
|
||||
num_type_mismatches += 1;
|
||||
if verbosity.is_verbose() {
|
||||
let src = f.body_source_map(db).expr_syntax(expr_id);
|
||||
let (_, sm) = db.body_with_source_map(f_id.into());
|
||||
let src = sm.expr_syntax(expr_id);
|
||||
if let Some(src) = src {
|
||||
// FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly
|
||||
let original_file = src.file_id.original_file(db);
|
||||
|
|
|
@ -7,7 +7,6 @@ mod progress_report;
|
|||
|
||||
use std::{error::Error, fmt::Write, io::Read};
|
||||
|
||||
use flexi_logger::Logger;
|
||||
use pico_args::Arguments;
|
||||
use ra_ide::{file_structure, Analysis};
|
||||
use ra_prof::profile;
|
||||
|
@ -32,7 +31,7 @@ impl Verbosity {
|
|||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
Logger::with_env_or_str("error").start()?;
|
||||
env_logger::try_init()?;
|
||||
|
||||
let subcommand = match std::env::args_os().nth(1) {
|
||||
None => {
|
||||
|
|
|
@ -235,6 +235,15 @@ impl FromStr for Edition {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Edition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
Edition::Edition2015 => "2015",
|
||||
Edition::Edition2018 => "2018",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Dependency {
|
||||
pub fn crate_id(&self) -> CrateId {
|
||||
self.crate_id
|
||||
|
|
|
@ -10,9 +10,11 @@ doctest = false
|
|||
[dependencies]
|
||||
log = "0.4.5"
|
||||
rustc-hash = "1.0"
|
||||
either = "1.5"
|
||||
|
||||
ra_syntax = { path = "../ra_syntax" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
ra_prof = { path = "../ra_prof" }
|
||||
hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" }
|
||||
hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
|
||||
hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" }
|
||||
|
|
|
@ -1,36 +1,35 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
pub(crate) mod src;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
adt::VariantData,
|
||||
body::{Body, BodySourceMap},
|
||||
builtin_type::BuiltinType,
|
||||
docs::Documentation,
|
||||
expr::{BindingAnnotation, Pat, PatId},
|
||||
nameres::ModuleSource,
|
||||
per_ns::PerNs,
|
||||
resolver::HasResolver,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
AdtId, AstItemDef, ConstId, ContainerId, DefWithBodyId, EnumId, FunctionId, GenericDefId,
|
||||
HasModule, ImplId, LocalEnumVariantId, LocalImportId, LocalModuleId, LocalStructFieldId,
|
||||
Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||
AdtId, ConstId, DefWithBodyId, EnumId, FunctionId, HasModule, ImplId, LocalEnumVariantId,
|
||||
LocalModuleId, LocalStructFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
|
||||
TypeParamId, UnionId,
|
||||
};
|
||||
use hir_expand::{
|
||||
diagnostics::DiagnosticSink,
|
||||
name::{self, AsName},
|
||||
AstId, MacroDefId,
|
||||
name::{name, AsName},
|
||||
MacroDefId,
|
||||
};
|
||||
use hir_ty::expr::ExprValidator;
|
||||
use ra_db::{CrateId, Edition, FileId, FilePosition};
|
||||
use ra_syntax::{ast, AstNode, SyntaxNode};
|
||||
use hir_ty::{
|
||||
autoderef, display::HirFormatter, expr::ExprValidator, ApplicationTy, Canonical, InEnvironment,
|
||||
TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk,
|
||||
};
|
||||
use ra_db::{CrateId, Edition, FileId};
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::{
|
||||
db::{DefDatabase, HirDatabase},
|
||||
ty::display::HirFormatter,
|
||||
ty::{self, InEnvironment, InferenceResult, TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk},
|
||||
CallableDef, Either, HirDisplay, Name, Source,
|
||||
CallableDef, HirDisplay, InFile, Name,
|
||||
};
|
||||
|
||||
/// hir::Crate describes a single crate. It's the main interface with which
|
||||
|
@ -38,7 +37,7 @@ use crate::{
|
|||
/// root module.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Crate {
|
||||
pub(crate) crate_id: CrateId,
|
||||
pub(crate) id: CrateId,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -48,91 +47,43 @@ pub struct CrateDependency {
|
|||
}
|
||||
|
||||
impl Crate {
|
||||
pub fn crate_id(self) -> CrateId {
|
||||
self.crate_id
|
||||
}
|
||||
|
||||
pub fn dependencies(self, db: &impl DefDatabase) -> Vec<CrateDependency> {
|
||||
db.crate_graph()
|
||||
.dependencies(self.crate_id)
|
||||
.dependencies(self.id)
|
||||
.map(|dep| {
|
||||
let krate = Crate { crate_id: dep.crate_id() };
|
||||
let krate = Crate { id: dep.crate_id() };
|
||||
let name = dep.as_name();
|
||||
CrateDependency { krate, name }
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// FIXME: add `transitive_reverse_dependencies`.
|
||||
pub fn reverse_dependencies(self, db: &impl DefDatabase) -> Vec<Crate> {
|
||||
let crate_graph = db.crate_graph();
|
||||
crate_graph
|
||||
.iter()
|
||||
.filter(|&krate| crate_graph.dependencies(krate).any(|it| it.crate_id == self.id))
|
||||
.map(|id| Crate { id })
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn root_module(self, db: &impl DefDatabase) -> Option<Module> {
|
||||
let module_id = db.crate_def_map(self.crate_id).root;
|
||||
let module_id = db.crate_def_map(self.id).root;
|
||||
Some(Module::new(self, module_id))
|
||||
}
|
||||
|
||||
pub fn root_file(self, db: &impl DefDatabase) -> FileId {
|
||||
db.crate_graph().crate_root(self.id)
|
||||
}
|
||||
|
||||
pub fn edition(self, db: &impl DefDatabase) -> Edition {
|
||||
let crate_graph = db.crate_graph();
|
||||
crate_graph.edition(self.crate_id)
|
||||
crate_graph.edition(self.id)
|
||||
}
|
||||
|
||||
pub fn all(db: &impl DefDatabase) -> Vec<Crate> {
|
||||
db.crate_graph().iter().map(|crate_id| Crate { crate_id }).collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ModuleSource {
|
||||
SourceFile(ast::SourceFile),
|
||||
Module(ast::Module),
|
||||
}
|
||||
|
||||
impl ModuleSource {
|
||||
pub fn new(
|
||||
db: &impl DefDatabase,
|
||||
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 DefDatabase, position: 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 DefDatabase, child: Source<&SyntaxNode>) -> ModuleSource {
|
||||
if let Some(m) =
|
||||
child.value.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi())
|
||||
{
|
||||
ModuleSource::Module(m)
|
||||
} else {
|
||||
let file_id = child.file_id.original_file(db);
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file_id(db: &impl DefDatabase, file_id: FileId) -> ModuleSource {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
db.crate_graph().iter().map(|id| Crate { id }).collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,7 +122,7 @@ pub use hir_def::attr::Attrs;
|
|||
|
||||
impl Module {
|
||||
pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module {
|
||||
Module { id: ModuleId { krate: krate.crate_id, local_id: crate_module_id } }
|
||||
Module { id: ModuleId { krate: krate.id, local_id: crate_module_id } }
|
||||
}
|
||||
|
||||
/// Name of this module.
|
||||
|
@ -189,7 +140,7 @@ impl Module {
|
|||
|
||||
/// Returns the crate this module is part of.
|
||||
pub fn krate(self) -> Crate {
|
||||
Crate { crate_id: self.id.krate }
|
||||
Crate { id: self.id.krate }
|
||||
}
|
||||
|
||||
/// Topmost parent of this module. Every module has a `crate_root`, but some
|
||||
|
@ -200,13 +151,6 @@ impl Module {
|
|||
self.with_module_id(def_map.root)
|
||||
}
|
||||
|
||||
/// Finds a child module with the specified name.
|
||||
pub fn child(self, db: &impl DefDatabase, name: &Name) -> Option<Module> {
|
||||
let def_map = db.crate_def_map(self.id.krate);
|
||||
let child_id = def_map[self.id.local_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.id.krate);
|
||||
|
@ -236,13 +180,11 @@ impl Module {
|
|||
}
|
||||
|
||||
/// Returns a `ModuleScope`: a set of items, visible in this module.
|
||||
pub fn scope(self, db: &impl HirDatabase) -> Vec<(Name, ScopeDef, Option<Import>)> {
|
||||
pub fn scope(self, db: &impl HirDatabase) -> Vec<(Name, ScopeDef)> {
|
||||
db.crate_def_map(self.id.krate)[self.id.local_id]
|
||||
.scope
|
||||
.entries()
|
||||
.map(|(name, res)| {
|
||||
(name.clone(), res.def.into(), res.import.map(|id| Import { parent: self, id }))
|
||||
})
|
||||
.map(|(name, def)| (name.clone(), def.into()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
@ -277,19 +219,14 @@ impl Module {
|
|||
|
||||
pub fn impl_blocks(self, db: &impl DefDatabase) -> Vec<ImplBlock> {
|
||||
let def_map = db.crate_def_map(self.id.krate);
|
||||
def_map[self.id.local_id].impls.iter().copied().map(ImplBlock::from).collect()
|
||||
def_map[self.id.local_id].scope.impls().map(ImplBlock::from).collect()
|
||||
}
|
||||
|
||||
fn with_module_id(self, module_id: LocalModuleId) -> Module {
|
||||
pub(crate) fn with_module_id(self, module_id: LocalModuleId) -> Module {
|
||||
Module::new(self.krate(), module_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Import {
|
||||
pub(crate) parent: Module,
|
||||
pub(crate) id: LocalImportId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct StructField {
|
||||
pub(crate) parent: VariantDef,
|
||||
|
@ -307,8 +244,10 @@ impl StructField {
|
|||
self.parent.variant_data(db).fields()[self.id].name.clone()
|
||||
}
|
||||
|
||||
pub fn ty(&self, db: &impl HirDatabase) -> Ty {
|
||||
db.field_types(self.parent.into())[self.id].clone()
|
||||
pub fn ty(&self, db: &impl HirDatabase) -> Type {
|
||||
let var_id = self.parent.into();
|
||||
let ty = db.field_types(var_id)[self.id].clone();
|
||||
Type::new(db, self.parent.module(db).id.krate.into(), var_id, ty)
|
||||
}
|
||||
|
||||
pub fn parent_def(&self, _db: &impl HirDatabase) -> VariantDef {
|
||||
|
@ -323,7 +262,7 @@ pub struct Struct {
|
|||
|
||||
impl Struct {
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
Module { id: self.id.module(db) }
|
||||
Module { id: self.id.lookup(db).container.module(db) }
|
||||
}
|
||||
|
||||
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
|
||||
|
@ -343,21 +282,8 @@ impl Struct {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> {
|
||||
db.struct_data(self.id.into())
|
||||
.variant_data
|
||||
.fields()
|
||||
.iter()
|
||||
.find(|(_id, data)| data.name == *name)
|
||||
.map(|(id, _)| StructField { parent: self.into(), id })
|
||||
}
|
||||
|
||||
pub fn ty(self, db: &impl HirDatabase) -> Type {
|
||||
Type::from_def(db, self.id.module(db).krate, self.id)
|
||||
}
|
||||
|
||||
pub fn constructor_ty(self, db: &impl HirDatabase) -> Ty {
|
||||
db.value_ty(self.id.into())
|
||||
Type::from_def(db, self.id.lookup(db).container.module(db).krate, self.id)
|
||||
}
|
||||
|
||||
fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> {
|
||||
|
@ -376,11 +302,11 @@ impl Union {
|
|||
}
|
||||
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
Module { id: self.id.module(db) }
|
||||
Module { id: self.id.lookup(db).container.module(db) }
|
||||
}
|
||||
|
||||
pub fn ty(self, db: &impl HirDatabase) -> Type {
|
||||
Type::from_def(db, self.id.module(db).krate, self.id)
|
||||
Type::from_def(db, self.id.lookup(db).container.module(db).krate, self.id)
|
||||
}
|
||||
|
||||
pub fn fields(self, db: &impl HirDatabase) -> Vec<StructField> {
|
||||
|
@ -392,15 +318,6 @@ impl Union {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> {
|
||||
db.union_data(self.id)
|
||||
.variant_data
|
||||
.fields()
|
||||
.iter()
|
||||
.find(|(_id, data)| data.name == *name)
|
||||
.map(|(id, _)| StructField { parent: self.into(), id })
|
||||
}
|
||||
|
||||
fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> {
|
||||
db.union_data(self.id).variant_data.clone()
|
||||
}
|
||||
|
@ -413,7 +330,7 @@ pub struct Enum {
|
|||
|
||||
impl Enum {
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
Module { id: self.id.module(db) }
|
||||
Module { id: self.id.lookup(db).container.module(db) }
|
||||
}
|
||||
|
||||
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
|
||||
|
@ -432,13 +349,8 @@ impl Enum {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn variant(self, db: &impl DefDatabase, name: &Name) -> Option<EnumVariant> {
|
||||
let id = db.enum_data(self.id).variant(name)?;
|
||||
Some(EnumVariant { parent: self, id })
|
||||
}
|
||||
|
||||
pub fn ty(self, db: &impl HirDatabase) -> Type {
|
||||
Type::from_def(db, self.id.module(db).krate, self.id)
|
||||
Type::from_def(db, self.id.lookup(db).container.module(db).krate, self.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -468,14 +380,6 @@ impl EnumVariant {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> {
|
||||
self.variant_data(db)
|
||||
.fields()
|
||||
.iter()
|
||||
.find(|(_id, data)| data.name == *name)
|
||||
.map(|(id, _)| StructField { parent: self.into(), id })
|
||||
}
|
||||
|
||||
pub(crate) fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> {
|
||||
db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
|
||||
}
|
||||
|
@ -593,48 +497,8 @@ impl Function {
|
|||
db.function_data(self.id).params.clone()
|
||||
}
|
||||
|
||||
pub fn body_source_map(self, db: &impl HirDatabase) -> Arc<BodySourceMap> {
|
||||
db.body_with_source_map(self.id.into()).1
|
||||
}
|
||||
|
||||
pub fn body(self, db: &impl HirDatabase) -> Arc<Body> {
|
||||
db.body(self.id.into())
|
||||
}
|
||||
|
||||
pub fn ty(self, db: &impl HirDatabase) -> Ty {
|
||||
db.value_ty(self.id.into())
|
||||
}
|
||||
|
||||
pub fn infer(self, db: &impl HirDatabase) -> Arc<InferenceResult> {
|
||||
db.infer(self.id.into())
|
||||
}
|
||||
|
||||
/// The containing impl block, if this is a method.
|
||||
pub fn impl_block(self, db: &impl DefDatabase) -> Option<ImplBlock> {
|
||||
match self.container(db) {
|
||||
Some(Container::ImplBlock(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The containing trait, if this is a trait method definition.
|
||||
pub fn parent_trait(self, db: &impl DefDatabase) -> Option<Trait> {
|
||||
match self.container(db) {
|
||||
Some(Container::Trait(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn container(self, db: &impl DefDatabase) -> Option<Container> {
|
||||
match self.id.lookup(db).container {
|
||||
ContainerId::TraitId(it) => Some(Container::Trait(it.into())),
|
||||
ContainerId::ImplId(it) => Some(Container::ImplBlock(it.into())),
|
||||
ContainerId::ModuleId(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) {
|
||||
let infer = self.infer(db);
|
||||
let infer = db.infer(self.id.into());
|
||||
infer.add_diagnostics(db, self.id, sink);
|
||||
let mut validator = ExprValidator::new(self.id, infer, sink);
|
||||
validator.validate_body(db);
|
||||
|
@ -658,34 +522,6 @@ impl Const {
|
|||
pub fn name(self, db: &impl HirDatabase) -> Option<Name> {
|
||||
db.const_data(self.id).name.clone()
|
||||
}
|
||||
|
||||
pub fn infer(self, db: &impl HirDatabase) -> Arc<InferenceResult> {
|
||||
db.infer(self.id.into())
|
||||
}
|
||||
|
||||
/// The containing impl block, if this is a type alias.
|
||||
pub fn impl_block(self, db: &impl DefDatabase) -> Option<ImplBlock> {
|
||||
match self.container(db) {
|
||||
Some(Container::ImplBlock(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The containing trait, if this is a trait type alias definition.
|
||||
pub fn parent_trait(self, db: &impl DefDatabase) -> Option<Trait> {
|
||||
match self.container(db) {
|
||||
Some(Container::Trait(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn container(self, db: &impl DefDatabase) -> Option<Container> {
|
||||
match self.id.lookup(db).container {
|
||||
ContainerId::TraitId(it) => Some(Container::Trait(it.into())),
|
||||
ContainerId::ImplId(it) => Some(Container::ImplBlock(it.into())),
|
||||
ContainerId::ModuleId(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -701,10 +537,6 @@ impl Static {
|
|||
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
|
||||
Some(self.module(db).krate())
|
||||
}
|
||||
|
||||
pub fn infer(self, db: &impl HirDatabase) -> Arc<InferenceResult> {
|
||||
db.infer(self.id.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -714,7 +546,7 @@ pub struct Trait {
|
|||
|
||||
impl Trait {
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
Module { id: self.id.module(db) }
|
||||
Module { id: self.id.lookup(db).container.module(db) }
|
||||
}
|
||||
|
||||
pub fn name(self, db: &impl DefDatabase) -> Name {
|
||||
|
@ -749,30 +581,6 @@ impl TypeAlias {
|
|||
Some(self.module(db).krate())
|
||||
}
|
||||
|
||||
/// The containing impl block, if this is a type alias.
|
||||
pub fn impl_block(self, db: &impl DefDatabase) -> Option<ImplBlock> {
|
||||
match self.container(db) {
|
||||
Some(Container::ImplBlock(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The containing trait, if this is a trait type alias definition.
|
||||
pub fn parent_trait(self, db: &impl DefDatabase) -> Option<Trait> {
|
||||
match self.container(db) {
|
||||
Some(Container::Trait(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn container(self, db: &impl DefDatabase) -> Option<Container> {
|
||||
match self.id.lookup(db).container {
|
||||
ContainerId::TraitId(it) => Some(Container::Trait(it.into())),
|
||||
ContainerId::ImplId(it) => Some(Container::ImplBlock(it.into())),
|
||||
ContainerId::ModuleId(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_ref(self, db: &impl DefDatabase) -> Option<TypeRef> {
|
||||
db.type_alias_data(self.id).type_ref.clone()
|
||||
}
|
||||
|
@ -791,14 +599,6 @@ pub struct MacroDef {
|
|||
pub(crate) id: MacroDefId,
|
||||
}
|
||||
|
||||
impl MacroDef {}
|
||||
|
||||
pub enum Container {
|
||||
Trait(Trait),
|
||||
ImplBlock(ImplBlock),
|
||||
}
|
||||
impl_froms!(Container: Trait, ImplBlock);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum AssocItem {
|
||||
Function(Function),
|
||||
|
@ -819,15 +619,6 @@ impl AssocItem {
|
|||
AssocItem::TypeAlias(t) => t.module(db),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn container(self, db: &impl DefDatabase) -> Container {
|
||||
match self {
|
||||
AssocItem::Function(f) => f.container(db),
|
||||
AssocItem::Const(c) => c.container(db),
|
||||
AssocItem::TypeAlias(t) => t.container(db),
|
||||
}
|
||||
.expect("AssocItem without container")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
|
@ -869,7 +660,7 @@ impl Local {
|
|||
}
|
||||
|
||||
pub fn is_self(self, db: &impl HirDatabase) -> bool {
|
||||
self.name(db) == Some(name::SELF_PARAM)
|
||||
self.name(db) == Some(name![self])
|
||||
}
|
||||
|
||||
pub fn is_mut(self, db: &impl HirDatabase) -> bool {
|
||||
|
@ -901,18 +692,30 @@ impl Local {
|
|||
Type { krate, ty: InEnvironment { value: ty, environment } }
|
||||
}
|
||||
|
||||
pub fn source(self, db: &impl HirDatabase) -> Source<Either<ast::BindPat, ast::SelfParam>> {
|
||||
pub fn source(self, db: &impl HirDatabase) -> InFile<Either<ast::BindPat, ast::SelfParam>> {
|
||||
let (_body, source_map) = db.body_with_source_map(self.parent.into());
|
||||
let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
|
||||
let root = src.file_syntax(db);
|
||||
src.map(|ast| ast.map(|it| it.cast().unwrap().to_node(&root), |it| it.to_node(&root)))
|
||||
src.map(|ast| {
|
||||
ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct GenericParam {
|
||||
pub(crate) parent: GenericDefId,
|
||||
pub(crate) idx: u32,
|
||||
pub struct TypeParam {
|
||||
pub(crate) id: TypeParamId,
|
||||
}
|
||||
|
||||
impl TypeParam {
|
||||
pub fn name(self, db: &impl HirDatabase) -> Name {
|
||||
let params = db.generic_params(self.id.parent);
|
||||
params.types[self.id.local_id].name.clone()
|
||||
}
|
||||
|
||||
pub fn module(self, db: &impl HirDatabase) -> Module {
|
||||
self.id.parent.module(db).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -922,11 +725,11 @@ pub struct ImplBlock {
|
|||
|
||||
impl ImplBlock {
|
||||
pub fn all_in_crate(db: &impl HirDatabase, krate: Crate) -> Vec<ImplBlock> {
|
||||
let impls = db.impls_in_crate(krate.crate_id);
|
||||
let impls = db.impls_in_crate(krate.id);
|
||||
impls.all_impls().map(Self::from).collect()
|
||||
}
|
||||
pub fn for_trait(db: &impl HirDatabase, krate: Crate, trait_: Trait) -> Vec<ImplBlock> {
|
||||
let impls = db.impls_in_crate(krate.crate_id);
|
||||
let impls = db.impls_in_crate(krate.id);
|
||||
impls.lookup_impl_blocks_for_trait(trait_.id).map(Self::from).collect()
|
||||
}
|
||||
|
||||
|
@ -943,7 +746,10 @@ impl ImplBlock {
|
|||
let resolver = self.id.resolver(db);
|
||||
let environment = TraitEnvironment::lower(db, &resolver);
|
||||
let ty = Ty::from_hir(db, &resolver, &impl_data.target_type);
|
||||
Type { krate: self.id.module(db).krate, ty: InEnvironment { value: ty, environment } }
|
||||
Type {
|
||||
krate: self.id.lookup(db).container.module(db).krate,
|
||||
ty: InEnvironment { value: ty, environment },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn items(&self, db: &impl DefDatabase) -> Vec<AssocItem> {
|
||||
|
@ -955,11 +761,11 @@ impl ImplBlock {
|
|||
}
|
||||
|
||||
pub fn module(&self, db: &impl DefDatabase) -> Module {
|
||||
self.id.module(db).into()
|
||||
self.id.lookup(db).container.module(db).into()
|
||||
}
|
||||
|
||||
pub fn krate(&self, db: &impl DefDatabase) -> Crate {
|
||||
Crate { crate_id: self.module(db).id.krate }
|
||||
Crate { id: self.module(db).id.krate }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -970,15 +776,19 @@ pub struct Type {
|
|||
}
|
||||
|
||||
impl Type {
|
||||
fn new(db: &impl HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
|
||||
let resolver = lexical_env.resolver(db);
|
||||
let environment = TraitEnvironment::lower(db, &resolver);
|
||||
Type { krate, ty: InEnvironment { value: ty, environment } }
|
||||
}
|
||||
|
||||
fn from_def(
|
||||
db: &impl HirDatabase,
|
||||
krate: CrateId,
|
||||
def: impl HasResolver + Into<TyDefId>,
|
||||
) -> Type {
|
||||
let resolver = def.resolver(db);
|
||||
let environment = TraitEnvironment::lower(db, &resolver);
|
||||
let ty = db.ty(def.into());
|
||||
Type { krate, ty: InEnvironment { value: ty, environment } }
|
||||
Type::new(db, krate, def, ty)
|
||||
}
|
||||
|
||||
pub fn is_bool(&self) -> bool {
|
||||
|
@ -1028,7 +838,7 @@ impl Type {
|
|||
pub fn fields(&self, db: &impl HirDatabase) -> Vec<(StructField, Type)> {
|
||||
if let Ty::Apply(a_ty) = &self.ty.value {
|
||||
match a_ty.ctor {
|
||||
ty::TypeCtor::Adt(AdtId::StructId(s)) => {
|
||||
TypeCtor::Adt(AdtId::StructId(s)) => {
|
||||
let var_def = s.into();
|
||||
return db
|
||||
.field_types(var_def)
|
||||
|
@ -1050,7 +860,7 @@ impl Type {
|
|||
let mut res = Vec::new();
|
||||
if let Ty::Apply(a_ty) = &self.ty.value {
|
||||
match a_ty.ctor {
|
||||
ty::TypeCtor::Tuple { .. } => {
|
||||
TypeCtor::Tuple { .. } => {
|
||||
for ty in a_ty.parameters.iter() {
|
||||
let ty = ty.clone().subst(&a_ty.parameters);
|
||||
res.push(self.derived(ty));
|
||||
|
@ -1069,11 +879,16 @@ impl Type {
|
|||
) -> Vec<(StructField, Type)> {
|
||||
// FIXME: check that ty and def match
|
||||
match &self.ty.value {
|
||||
Ty::Apply(a_ty) => def
|
||||
.fields(db)
|
||||
Ty::Apply(a_ty) => {
|
||||
let field_types = db.field_types(def.into());
|
||||
def.fields(db)
|
||||
.into_iter()
|
||||
.map(|it| (it, self.derived(it.ty(db).subst(&a_ty.parameters))))
|
||||
.collect(),
|
||||
.map(|it| {
|
||||
let ty = field_types[it.id].clone().subst(&a_ty.parameters);
|
||||
(it, self.derived(ty))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -1081,10 +896,10 @@ impl Type {
|
|||
pub fn autoderef<'a>(&'a self, db: &'a impl HirDatabase) -> impl Iterator<Item = Type> + 'a {
|
||||
// There should be no inference vars in types passed here
|
||||
// FIXME check that?
|
||||
let canonical = crate::ty::Canonical { value: self.ty.value.clone(), num_vars: 0 };
|
||||
let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 };
|
||||
let environment = self.ty.environment.clone();
|
||||
let ty = InEnvironment { value: canonical, environment: environment.clone() };
|
||||
ty::autoderef(db, Some(self.krate), ty)
|
||||
autoderef(db, Some(self.krate), ty)
|
||||
.map(|canonical| canonical.value)
|
||||
.map(move |ty| self.derived(ty))
|
||||
}
|
||||
|
@ -1097,7 +912,7 @@ impl Type {
|
|||
krate: Crate,
|
||||
mut callback: impl FnMut(AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
for krate in self.ty.value.def_crates(db, krate.crate_id)? {
|
||||
for krate in self.ty.value.def_crates(db, krate.id)? {
|
||||
let impls = db.impls_in_crate(krate);
|
||||
|
||||
for impl_block in impls.lookup_impl_blocks(&self.ty.value) {
|
||||
|
@ -1111,11 +926,6 @@ impl Type {
|
|||
None
|
||||
}
|
||||
|
||||
// FIXME: remove
|
||||
pub fn into_ty(self) -> Ty {
|
||||
self.ty.value
|
||||
}
|
||||
|
||||
pub fn as_adt(&self) -> Option<Adt> {
|
||||
let (adt, _subst) = self.ty.value.as_adt()?;
|
||||
Some(adt.into())
|
||||
|
@ -1124,15 +934,14 @@ impl Type {
|
|||
// FIXME: provide required accessors such that it becomes implementable from outside.
|
||||
pub fn is_equal_for_find_impls(&self, other: &Type) -> bool {
|
||||
match (&self.ty.value, &other.ty.value) {
|
||||
(Ty::Apply(a_original_ty), Ty::Apply(ty::ApplicationTy { ctor, parameters })) => {
|
||||
match ctor {
|
||||
(Ty::Apply(a_original_ty), Ty::Apply(ApplicationTy { ctor, parameters })) => match ctor
|
||||
{
|
||||
TypeCtor::Ref(..) => match parameters.as_single() {
|
||||
Ty::Apply(a_ty) => a_original_ty.ctor == a_ty.ctor,
|
||||
_ => false,
|
||||
},
|
||||
_ => a_original_ty.ctor == *ctor,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -1155,7 +964,7 @@ impl HirDisplay for Type {
|
|||
pub enum ScopeDef {
|
||||
ModuleDef(ModuleDef),
|
||||
MacroDef(MacroDef),
|
||||
GenericParam(GenericParam),
|
||||
GenericParam(TypeParam),
|
||||
ImplSelfType(ImplBlock),
|
||||
AdtSelfType(Adt),
|
||||
Local(Local),
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use hir_def::{AstItemDef, HasChildSource, HasSource as _, Lookup, VariantId};
|
||||
use hir_expand::either::Either;
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, Const, Enum, EnumVariant, FieldSource, Function, ImplBlock, Import, MacroDef,
|
||||
Module, ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
|
||||
};
|
||||
|
||||
pub use hir_expand::Source;
|
||||
|
||||
pub trait HasSource {
|
||||
type Ast;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<Self::Ast>;
|
||||
}
|
||||
|
||||
/// 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) -> Source<ModuleSource> {
|
||||
let def_map = db.crate_def_map(self.id.krate);
|
||||
let src = def_map[self.id.local_id].definition_source(db);
|
||||
src.map(|it| match it {
|
||||
Either::A(it) => ModuleSource::SourceFile(it),
|
||||
Either::B(it) => ModuleSource::Module(it),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root.
|
||||
pub fn declaration_source(self, db: &impl DefDatabase) -> Option<Source<ast::Module>> {
|
||||
let def_map = db.crate_def_map(self.id.krate);
|
||||
def_map[self.id.local_id].declaration_source(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for StructField {
|
||||
type Ast = FieldSource;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<FieldSource> {
|
||||
let var = VariantId::from(self.parent);
|
||||
let src = var.child_source(db);
|
||||
src.map(|it| match it[self.id].clone() {
|
||||
Either::A(it) => FieldSource::Pos(it),
|
||||
Either::B(it) => FieldSource::Named(it),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasSource for Struct {
|
||||
type Ast = ast::StructDef;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::StructDef> {
|
||||
self.id.source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Union {
|
||||
type Ast = ast::UnionDef;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::UnionDef> {
|
||||
self.id.source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Enum {
|
||||
type Ast = ast::EnumDef;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::EnumDef> {
|
||||
self.id.source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for EnumVariant {
|
||||
type Ast = ast::EnumVariant;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::EnumVariant> {
|
||||
self.parent.id.child_source(db).map(|map| map[self.id].clone())
|
||||
}
|
||||
}
|
||||
impl HasSource for Function {
|
||||
type Ast = ast::FnDef;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::FnDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Const {
|
||||
type Ast = ast::ConstDef;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::ConstDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Static {
|
||||
type Ast = ast::StaticDef;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::StaticDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Trait {
|
||||
type Ast = ast::TraitDef;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::TraitDef> {
|
||||
self.id.source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for TypeAlias {
|
||||
type Ast = ast::TypeAliasDef;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::TypeAliasDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for MacroDef {
|
||||
type Ast = ast::MacroCall;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::MacroCall> {
|
||||
Source { file_id: self.id.ast_id.file_id(), value: self.id.ast_id.to_node(db) }
|
||||
}
|
||||
}
|
||||
impl HasSource for ImplBlock {
|
||||
type Ast = ast::ImplBlock;
|
||||
fn source(self, db: &impl DefDatabase) -> Source<ast::ImplBlock> {
|
||||
self.id.source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Import {
|
||||
type Ast = Either<ast::UseTree, ast::ExternCrateItem>;
|
||||
|
||||
/// Returns the syntax of the last path segment corresponding to this import
|
||||
fn source(self, db: &impl DefDatabase) -> Source<Self::Ast> {
|
||||
let src = self.parent.definition_source(db);
|
||||
let (_, source_map) = db.raw_items_with_source_map(src.file_id);
|
||||
let root = db.parse_or_expand(src.file_id).unwrap();
|
||||
let ptr = source_map.get(self.id);
|
||||
src.with_value(ptr.map(|it| it.to_node(&root), |it| it.to_node(&root)))
|
||||
}
|
||||
}
|
|
@ -4,8 +4,8 @@ pub use hir_def::db::{
|
|||
BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQuery, CrateLangItemsQuery,
|
||||
DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, ExprScopesQuery,
|
||||
FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternDatabase, InternDatabaseStorage,
|
||||
LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, RawItemsWithSourceMapQuery,
|
||||
StaticDataQuery, StructDataQuery, TraitDataQuery, TypeAliasDataQuery,
|
||||
LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery,
|
||||
TraitDataQuery, TypeAliasDataQuery,
|
||||
};
|
||||
pub use hir_expand::db::{
|
||||
AstDatabase, AstDatabaseStorage, AstIdMapQuery, MacroArgQuery, MacroDefQuery, MacroExpandQuery,
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
//! XXX: This does not work at the moment.
|
||||
//!
|
||||
//! printf debugging infrastructure for rust-analyzer.
|
||||
//!
|
||||
//! When you print a hir type, like a module, using `eprintln!("{:?}", module)`,
|
||||
//! you usually get back a numeric ID, which doesn't tell you much:
|
||||
//! `Module(92)`.
|
||||
//!
|
||||
//! This module adds convenience `debug` methods to various types, which resolve
|
||||
//! the id to a human-readable location info:
|
||||
//!
|
||||
//! ```not_rust
|
||||
//! eprintln!("{:?}", module.debug(db));
|
||||
//! =>
|
||||
//! Module { name: collections, path: "liballoc/collections/mod.rs" }
|
||||
//! ```
|
||||
//!
|
||||
//! Note that to get this info, we might need to execute queries! So
|
||||
//!
|
||||
//! * don't use the `debug` methods for logging
|
||||
//! * when debugging, be aware that interference is possible.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use hir_expand::HirFileId;
|
||||
use ra_db::{CrateId, FileId};
|
||||
|
||||
use crate::{db::HirDatabase, Crate, Module, Name};
|
||||
|
||||
impl Crate {
|
||||
pub fn debug(self, db: &impl HirDebugDatabase) -> impl fmt::Debug + '_ {
|
||||
debug_fn(move |fmt| db.debug_crate(self, fmt))
|
||||
}
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn debug(self, db: &impl HirDebugDatabase) -> impl fmt::Debug + '_ {
|
||||
debug_fn(move |fmt| db.debug_module(self, fmt))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HirDebugHelper: HirDatabase {
|
||||
fn crate_name(&self, _krate: CrateId) -> Option<String> {
|
||||
None
|
||||
}
|
||||
fn file_path(&self, _file_id: FileId) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HirDebugDatabase {
|
||||
fn debug_crate(&self, krate: Crate, fmt: &mut fmt::Formatter<'_>) -> fmt::Result;
|
||||
fn debug_module(&self, module: Module, fmt: &mut fmt::Formatter<'_>) -> fmt::Result;
|
||||
fn debug_hir_file_id(&self, file_id: HirFileId, fmt: &mut fmt::Formatter<'_>) -> fmt::Result;
|
||||
}
|
||||
|
||||
impl<DB: HirDebugHelper> HirDebugDatabase for DB {
|
||||
fn debug_crate(&self, krate: Crate, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut builder = fmt.debug_tuple("Crate");
|
||||
match self.crate_name(krate.crate_id) {
|
||||
Some(name) => builder.field(&name),
|
||||
None => builder.field(&krate.crate_id),
|
||||
}
|
||||
.finish()
|
||||
}
|
||||
|
||||
fn debug_module(&self, module: Module, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let file_id = module.definition_source(self).file_id.original_file(self);
|
||||
let path = self.file_path(file_id).unwrap_or_else(|| "N/A".to_string());
|
||||
fmt.debug_struct("Module")
|
||||
.field("name", &module.name(self).unwrap_or_else(Name::missing))
|
||||
.field("path", &path)
|
||||
.finish()
|
||||
}
|
||||
|
||||
fn debug_hir_file_id(&self, file_id: HirFileId, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let original = file_id.original_file(self);
|
||||
let path = self.file_path(original).unwrap_or_else(|| "N/A".to_string());
|
||||
let is_macro = file_id != original.into();
|
||||
fmt.debug_struct("HirFileId").field("path", &path).field("macro", &is_macro).finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_fn(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Debug {
|
||||
struct DebugFn<F>(F);
|
||||
|
||||
impl<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Debug for DebugFn<F> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(&self.0)(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
DebugFn(f)
|
||||
}
|
|
@ -9,16 +9,10 @@ use hir_def::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
Adt, AssocItem, AttrDef, Crate, DefWithBody, EnumVariant, GenericDef, ModuleDef, StructField,
|
||||
Adt, AssocItem, AttrDef, DefWithBody, EnumVariant, GenericDef, ModuleDef, StructField,
|
||||
VariantDef,
|
||||
};
|
||||
|
||||
impl From<ra_db::CrateId> for Crate {
|
||||
fn from(crate_id: ra_db::CrateId) -> Self {
|
||||
Crate { crate_id }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_id {
|
||||
($(($id:path, $ty:path)),*) => {$(
|
||||
impl From<$id> for $ty {
|
||||
|
@ -26,10 +20,16 @@ macro_rules! from_id {
|
|||
$ty { id }
|
||||
}
|
||||
}
|
||||
impl From<$ty> for $id {
|
||||
fn from(ty: $ty) -> $id {
|
||||
ty.id
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
from_id![
|
||||
(ra_db::CrateId, crate::Crate),
|
||||
(hir_def::ModuleId, crate::Module),
|
||||
(hir_def::StructId, crate::Struct),
|
||||
(hir_def::UnionId, crate::Union),
|
||||
|
|
|
@ -1,219 +1,140 @@
|
|||
//! FIXME: write short doc here
|
||||
//! Finds a corresponding hir data structure for a syntax node in a specific
|
||||
//! file.
|
||||
|
||||
use hir_def::{AstItemDef, LocationCtx, ModuleId};
|
||||
use hir_def::{
|
||||
child_by_source::ChildBySource, dyn_map::DynMap, keys, keys::Key, nameres::ModuleSource,
|
||||
ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId,
|
||||
StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
|
||||
};
|
||||
use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
|
||||
use ra_db::FileId;
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, NameOwner},
|
||||
match_ast, AstPtr, SyntaxNode,
|
||||
match_ast, SyntaxNode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
AssocItem, Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock,
|
||||
Local, MacroDef, Module, ModuleDef, ModuleSource, Source, Static, Struct, StructField, Trait,
|
||||
TypeAlias, Union, VariantDef,
|
||||
db::{DefDatabase, HirDatabase},
|
||||
Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local,
|
||||
MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, TypeParam, Union,
|
||||
};
|
||||
|
||||
pub trait FromSource: Sized {
|
||||
type Ast;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self>;
|
||||
fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self>;
|
||||
}
|
||||
|
||||
impl FromSource for Struct {
|
||||
type Ast = ast::StructDef;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
let id = from_source(db, src)?;
|
||||
Some(Struct { id })
|
||||
pub trait FromSourceByContainer: Sized {
|
||||
type Ast: AstNode + 'static;
|
||||
type Id: Copy + 'static;
|
||||
const KEY: Key<Self::Ast, Self::Id>;
|
||||
}
|
||||
}
|
||||
impl FromSource for Union {
|
||||
type Ast = ast::UnionDef;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
let id = from_source(db, src)?;
|
||||
Some(Union { id })
|
||||
}
|
||||
}
|
||||
impl FromSource for Enum {
|
||||
type Ast = ast::EnumDef;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
let id = from_source(db, src)?;
|
||||
Some(Enum { id })
|
||||
}
|
||||
}
|
||||
impl FromSource for Trait {
|
||||
type Ast = ast::TraitDef;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
let id = from_source(db, src)?;
|
||||
Some(Trait { id })
|
||||
}
|
||||
}
|
||||
impl FromSource for Function {
|
||||
type Ast = ast::FnDef;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
|
||||
Container::Trait(it) => it.items(db),
|
||||
Container::ImplBlock(it) => it.items(db),
|
||||
Container::Module(m) => {
|
||||
return m
|
||||
.declarations(db)
|
||||
.into_iter()
|
||||
.filter_map(|it| match it {
|
||||
ModuleDef::Function(it) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.find(|it| same_source(&it.source(db), &src))
|
||||
}
|
||||
};
|
||||
items
|
||||
.into_iter()
|
||||
.filter_map(|it| match it {
|
||||
AssocItem::Function(it) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.find(|it| same_source(&it.source(db), &src))
|
||||
|
||||
impl<T: FromSourceByContainer> FromSource for T
|
||||
where
|
||||
T: From<<T as FromSourceByContainer>::Id>,
|
||||
{
|
||||
type Ast = <T as FromSourceByContainer>::Ast;
|
||||
fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
|
||||
analyze_container(db, src.as_ref().map(|it| it.syntax()))[T::KEY]
|
||||
.get(&src)
|
||||
.copied()
|
||||
.map(Self::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSource for Const {
|
||||
type Ast = ast::ConstDef;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
|
||||
Container::Trait(it) => it.items(db),
|
||||
Container::ImplBlock(it) => it.items(db),
|
||||
Container::Module(m) => {
|
||||
return m
|
||||
.declarations(db)
|
||||
.into_iter()
|
||||
.filter_map(|it| match it {
|
||||
ModuleDef::Const(it) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.find(|it| same_source(&it.source(db), &src))
|
||||
}
|
||||
};
|
||||
items
|
||||
.into_iter()
|
||||
.filter_map(|it| match it {
|
||||
AssocItem::Const(it) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.find(|it| same_source(&it.source(db), &src))
|
||||
}
|
||||
}
|
||||
impl FromSource for Static {
|
||||
type Ast = ast::StaticDef;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
let module = match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
|
||||
Container::Module(it) => it,
|
||||
Container::Trait(_) | Container::ImplBlock(_) => return None,
|
||||
};
|
||||
module
|
||||
.declarations(db)
|
||||
.into_iter()
|
||||
.filter_map(|it| match it {
|
||||
ModuleDef::Static(it) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.find(|it| same_source(&it.source(db), &src))
|
||||
macro_rules! from_source_by_container_impls {
|
||||
($(($hir:ident, $id:ident, $ast:path, $key:path)),* ,) => {$(
|
||||
impl FromSourceByContainer for $hir {
|
||||
type Ast = $ast;
|
||||
type Id = $id;
|
||||
const KEY: Key<Self::Ast, Self::Id> = $key;
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
impl FromSource for TypeAlias {
|
||||
type Ast = ast::TypeAliasDef;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
let items = match Container::find(db, src.as_ref().map(|it| it.syntax()))? {
|
||||
Container::Trait(it) => it.items(db),
|
||||
Container::ImplBlock(it) => it.items(db),
|
||||
Container::Module(m) => {
|
||||
return m
|
||||
.declarations(db)
|
||||
.into_iter()
|
||||
.filter_map(|it| match it {
|
||||
ModuleDef::TypeAlias(it) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.find(|it| same_source(&it.source(db), &src))
|
||||
}
|
||||
};
|
||||
items
|
||||
.into_iter()
|
||||
.filter_map(|it| match it {
|
||||
AssocItem::TypeAlias(it) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.find(|it| same_source(&it.source(db), &src))
|
||||
}
|
||||
}
|
||||
from_source_by_container_impls![
|
||||
(Struct, StructId, ast::StructDef, keys::STRUCT),
|
||||
(Union, UnionId, ast::UnionDef, keys::UNION),
|
||||
(Enum, EnumId, ast::EnumDef, keys::ENUM),
|
||||
(Trait, TraitId, ast::TraitDef, keys::TRAIT),
|
||||
(Function, FunctionId, ast::FnDef, keys::FUNCTION),
|
||||
(Static, StaticId, ast::StaticDef, keys::STATIC),
|
||||
(Const, ConstId, ast::ConstDef, keys::CONST),
|
||||
(TypeAlias, TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS),
|
||||
(ImplBlock, ImplId, ast::ImplBlock, keys::IMPL),
|
||||
];
|
||||
|
||||
impl FromSource for MacroDef {
|
||||
type Ast = ast::MacroCall;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
|
||||
let kind = MacroDefKind::Declarative;
|
||||
|
||||
let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
|
||||
let module = Module::from_definition(db, Source::new(src.file_id, module_src))?;
|
||||
let krate = module.krate().crate_id();
|
||||
let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
|
||||
let krate = Some(module.krate().id);
|
||||
|
||||
let ast_id = AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value));
|
||||
let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));
|
||||
|
||||
let id: MacroDefId = MacroDefId { krate, ast_id, kind };
|
||||
Some(MacroDef { id })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSource for ImplBlock {
|
||||
type Ast = ast::ImplBlock;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
let id = from_source(db, src)?;
|
||||
Some(ImplBlock { id })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSource for EnumVariant {
|
||||
type Ast = ast::EnumVariant;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
|
||||
let parent_enum = src.value.parent_enum();
|
||||
let src_enum = Source { file_id: src.file_id, value: parent_enum };
|
||||
let variants = Enum::from_source(db, src_enum)?.variants(db);
|
||||
variants.into_iter().find(|v| same_source(&v.source(db), &src))
|
||||
let src_enum = InFile { file_id: src.file_id, value: parent_enum };
|
||||
let parent_enum = Enum::from_source(db, src_enum)?;
|
||||
parent_enum.id.child_by_source(db)[keys::ENUM_VARIANT]
|
||||
.get(&src)
|
||||
.copied()
|
||||
.map(EnumVariant::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSource for StructField {
|
||||
type Ast = FieldSource;
|
||||
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
|
||||
let variant_def: VariantDef = match src.value {
|
||||
FieldSource::Named(ref field) => {
|
||||
fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
|
||||
let src = src.as_ref();
|
||||
|
||||
// FIXME this is buggy
|
||||
let variant_id: VariantId = match src.value {
|
||||
FieldSource::Named(field) => {
|
||||
let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
|
||||
let src = Source { file_id: src.file_id, value };
|
||||
let src = InFile { file_id: src.file_id, value };
|
||||
let def = Struct::from_source(db, src)?;
|
||||
VariantDef::from(def)
|
||||
def.id.into()
|
||||
}
|
||||
FieldSource::Pos(ref field) => {
|
||||
FieldSource::Pos(field) => {
|
||||
let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
|
||||
let src = Source { file_id: src.file_id, value };
|
||||
let src = InFile { file_id: src.file_id, value };
|
||||
let def = EnumVariant::from_source(db, src)?;
|
||||
VariantDef::from(def)
|
||||
EnumVariantId::from(def).into()
|
||||
}
|
||||
};
|
||||
variant_def
|
||||
.variant_data(db)
|
||||
.fields()
|
||||
.iter()
|
||||
.map(|(id, _)| StructField { parent: variant_def, id })
|
||||
.find(|f| f.source(db) == src)
|
||||
|
||||
let dyn_map = variant_id.child_by_source(db);
|
||||
match src.value {
|
||||
FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())),
|
||||
FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())),
|
||||
}
|
||||
.copied()
|
||||
.map(StructField::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl Local {
|
||||
pub fn from_source(db: &impl HirDatabase, src: Source<ast::BindPat>) -> Option<Self> {
|
||||
pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> {
|
||||
let file_id = src.file_id;
|
||||
let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| {
|
||||
let res = match_ast! {
|
||||
match it {
|
||||
ast::ConstDef(value) => { Const::from_source(db, Source { value, file_id})?.into() },
|
||||
ast::StaticDef(value) => { Static::from_source(db, Source { value, file_id})?.into() },
|
||||
ast::FnDef(value) => { Function::from_source(db, Source { value, file_id})?.into() },
|
||||
ast::ConstDef(value) => { Const::from_source(db, InFile { value, file_id})?.into() },
|
||||
ast::StaticDef(value) => { Static::from_source(db, InFile { value, file_id})?.into() },
|
||||
ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.into() },
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
|
@ -226,83 +147,110 @@ impl Local {
|
|||
}
|
||||
}
|
||||
|
||||
impl TypeParam {
|
||||
pub fn from_source(db: &impl HirDatabase, src: InFile<ast::TypeParam>) -> Option<Self> {
|
||||
let file_id = src.file_id;
|
||||
let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
|
||||
let res = match_ast! {
|
||||
match it {
|
||||
ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.id.into() },
|
||||
ast::StructDef(value) => { Struct::from_source(db, InFile { value, file_id})?.id.into() },
|
||||
ast::EnumDef(value) => { Enum::from_source(db, InFile { value, file_id})?.id.into() },
|
||||
ast::TraitDef(value) => { Trait::from_source(db, InFile { value, file_id})?.id.into() },
|
||||
ast::TypeAliasDef(value) => { TypeAlias::from_source(db, InFile { value, file_id})?.id.into() },
|
||||
ast::ImplBlock(value) => { ImplBlock::from_source(db, InFile { value, file_id})?.id.into() },
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
})?;
|
||||
let &id = parent.child_by_source(db)[keys::TYPE_PARAM].get(&src)?;
|
||||
Some(TypeParam { id })
|
||||
}
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn from_declaration(db: &impl DefDatabase, src: Source<ast::Module>) -> Option<Self> {
|
||||
pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> {
|
||||
let _p = profile("Module::from_declaration");
|
||||
let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast);
|
||||
|
||||
let parent_module = match parent_declaration {
|
||||
Some(parent_declaration) => {
|
||||
let src_parent = Source { file_id: src.file_id, value: parent_declaration };
|
||||
let src_parent = InFile { file_id: src.file_id, value: parent_declaration };
|
||||
Module::from_declaration(db, src_parent)
|
||||
}
|
||||
_ => {
|
||||
let src_parent = Source {
|
||||
file_id: src.file_id,
|
||||
value: ModuleSource::new(db, Some(src.file_id.original_file(db)), None),
|
||||
};
|
||||
None => {
|
||||
let source_file = db.parse(src.file_id.original_file(db)).tree();
|
||||
let src_parent =
|
||||
InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) };
|
||||
Module::from_definition(db, src_parent)
|
||||
}
|
||||
}?;
|
||||
|
||||
let child_name = src.value.name()?;
|
||||
parent_module.child(db, &child_name.as_name())
|
||||
let child_name = src.value.name()?.as_name();
|
||||
let def_map = db.crate_def_map(parent_module.id.krate);
|
||||
let child_id = def_map[parent_module.id.local_id].children.get(&child_name)?;
|
||||
Some(parent_module.with_module_id(*child_id))
|
||||
}
|
||||
|
||||
pub fn from_definition(db: &impl DefDatabase, src: Source<ModuleSource>) -> Option<Self> {
|
||||
pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> {
|
||||
let _p = profile("Module::from_definition");
|
||||
match src.value {
|
||||
ModuleSource::Module(ref module) => {
|
||||
assert!(!module.has_semi());
|
||||
return Module::from_declaration(
|
||||
db,
|
||||
Source { file_id: src.file_id, value: module.clone() },
|
||||
InFile { file_id: src.file_id, value: module.clone() },
|
||||
);
|
||||
}
|
||||
ModuleSource::SourceFile(_) => (),
|
||||
};
|
||||
|
||||
let original_file = src.file_id.original_file(db);
|
||||
Module::from_file(db, original_file)
|
||||
}
|
||||
|
||||
let (krate, local_id) = db.relevant_crates(original_file).iter().find_map(|&crate_id| {
|
||||
fn from_file(db: &impl DefDatabase, file: FileId) -> Option<Self> {
|
||||
let _p = profile("Module::from_file");
|
||||
let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| {
|
||||
let crate_def_map = db.crate_def_map(crate_id);
|
||||
let local_id = crate_def_map.modules_for_file(original_file).next()?;
|
||||
let local_id = crate_def_map.modules_for_file(file).next()?;
|
||||
Some((crate_id, local_id))
|
||||
})?;
|
||||
Some(Module { id: ModuleId { krate, local_id } })
|
||||
}
|
||||
}
|
||||
|
||||
fn from_source<N, DEF>(db: &(impl DefDatabase + AstDatabase), src: Source<N>) -> Option<DEF>
|
||||
where
|
||||
N: AstNode,
|
||||
DEF: AstItemDef<N>,
|
||||
{
|
||||
let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
|
||||
let module = Module::from_definition(db, Source::new(src.file_id, module_src))?;
|
||||
let ctx = LocationCtx::new(db, module.id, src.file_id);
|
||||
let items = db.ast_id_map(src.file_id);
|
||||
let item_id = items.ast_id(&src.value);
|
||||
Some(DEF::from_ast_id(ctx, item_id))
|
||||
}
|
||||
fn analyze_container(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> DynMap {
|
||||
let _p = profile("analyze_container");
|
||||
return child_by_source(db, src).unwrap_or_default();
|
||||
|
||||
enum Container {
|
||||
Trait(Trait),
|
||||
ImplBlock(ImplBlock),
|
||||
Module(Module),
|
||||
}
|
||||
|
||||
impl Container {
|
||||
fn find(db: &impl DefDatabase, src: Source<&SyntaxNode>) -> Option<Container> {
|
||||
// FIXME: this doesn't try to handle nested declarations
|
||||
for container in src.value.ancestors() {
|
||||
fn child_by_source(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<DynMap> {
|
||||
for container in src.value.ancestors().skip(1) {
|
||||
let res = match_ast! {
|
||||
match container {
|
||||
ast::TraitDef(it) => {
|
||||
let c = Trait::from_source(db, src.with_value(it))?;
|
||||
Container::Trait(c)
|
||||
let def = Trait::from_source(db, src.with_value(it))?;
|
||||
def.id.child_by_source(db)
|
||||
},
|
||||
ast::ImplBlock(it) => {
|
||||
let c = ImplBlock::from_source(db, src.with_value(it))?;
|
||||
Container::ImplBlock(c)
|
||||
let def = ImplBlock::from_source(db, src.with_value(it))?;
|
||||
def.id.child_by_source(db)
|
||||
},
|
||||
ast::FnDef(it) => {
|
||||
let def = Function::from_source(db, src.with_value(it))?;
|
||||
DefWithBodyId::from(def.id)
|
||||
.child_by_source(db)
|
||||
},
|
||||
ast::StaticDef(it) => {
|
||||
let def = Static::from_source(db, src.with_value(it))?;
|
||||
DefWithBodyId::from(def.id)
|
||||
.child_by_source(db)
|
||||
},
|
||||
ast::ConstDef(it) => {
|
||||
let def = Const::from_source(db, src.with_value(it))?;
|
||||
DefWithBodyId::from(def.id)
|
||||
.child_by_source(db)
|
||||
},
|
||||
_ => { continue },
|
||||
}
|
||||
|
@ -312,16 +260,6 @@ impl Container {
|
|||
|
||||
let module_source = ModuleSource::from_child_node(db, src);
|
||||
let c = Module::from_definition(db, src.with_value(module_source))?;
|
||||
Some(Container::Module(c))
|
||||
Some(c.id.child_by_source(db))
|
||||
}
|
||||
}
|
||||
|
||||
/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
|
||||
/// equal if they point to exactly the same object.
|
||||
///
|
||||
/// In general, we do not guarantee that we have exactly one instance of a
|
||||
/// syntax tree for each file. We probably should add such guarantee, but, for
|
||||
/// the time being, we will use identity-less AstPtr comparison.
|
||||
fn same_source<N: AstNode>(s1: &Source<N>, s2: &Source<N>) -> bool {
|
||||
s1.as_ref().map(AstPtr::new) == s2.as_ref().map(AstPtr::new)
|
||||
}
|
||||
|
|
127
crates/ra_hir/src/has_source.rs
Normal file
127
crates/ra_hir/src/has_source.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
nameres::ModuleSource,
|
||||
src::{HasChildSource, HasSource as _},
|
||||
Lookup, VariantId,
|
||||
};
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, Const, Enum, EnumVariant, FieldSource, Function, ImplBlock, MacroDef, Module,
|
||||
Static, Struct, StructField, Trait, TypeAlias, TypeParam, Union,
|
||||
};
|
||||
|
||||
pub use hir_expand::InFile;
|
||||
|
||||
pub trait HasSource {
|
||||
type Ast;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<Self::Ast>;
|
||||
}
|
||||
|
||||
/// 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) -> InFile<ModuleSource> {
|
||||
let def_map = db.crate_def_map(self.id.krate);
|
||||
def_map[self.id.local_id].definition_source(db)
|
||||
}
|
||||
|
||||
/// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root.
|
||||
pub fn declaration_source(self, db: &impl DefDatabase) -> Option<InFile<ast::Module>> {
|
||||
let def_map = db.crate_def_map(self.id.krate);
|
||||
def_map[self.id.local_id].declaration_source(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for StructField {
|
||||
type Ast = FieldSource;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<FieldSource> {
|
||||
let var = VariantId::from(self.parent);
|
||||
let src = var.child_source(db);
|
||||
src.map(|it| match it[self.id].clone() {
|
||||
Either::Left(it) => FieldSource::Pos(it),
|
||||
Either::Right(it) => FieldSource::Named(it),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl HasSource for Struct {
|
||||
type Ast = ast::StructDef;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::StructDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Union {
|
||||
type Ast = ast::UnionDef;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::UnionDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Enum {
|
||||
type Ast = ast::EnumDef;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::EnumDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for EnumVariant {
|
||||
type Ast = ast::EnumVariant;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::EnumVariant> {
|
||||
self.parent.id.child_source(db).map(|map| map[self.id].clone())
|
||||
}
|
||||
}
|
||||
impl HasSource for Function {
|
||||
type Ast = ast::FnDef;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::FnDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Const {
|
||||
type Ast = ast::ConstDef;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::ConstDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Static {
|
||||
type Ast = ast::StaticDef;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::StaticDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for Trait {
|
||||
type Ast = ast::TraitDef;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::TraitDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for TypeAlias {
|
||||
type Ast = ast::TypeAliasDef;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::TypeAliasDef> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
impl HasSource for MacroDef {
|
||||
type Ast = ast::MacroCall;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::MacroCall> {
|
||||
InFile {
|
||||
file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id,
|
||||
value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl HasSource for ImplBlock {
|
||||
type Ast = ast::ImplBlock;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<ast::ImplBlock> {
|
||||
self.id.lookup(db).source(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for TypeParam {
|
||||
type Ast = Either<ast::TraitDef, ast::TypeParam>;
|
||||
fn source(self, db: &impl DefDatabase) -> InFile<Self::Ast> {
|
||||
let child_source = self.id.parent.child_source(db);
|
||||
child_source.map(|it| it[self.id.local_id].clone())
|
||||
}
|
||||
}
|
|
@ -26,42 +26,38 @@ macro_rules! impl_froms {
|
|||
}
|
||||
}
|
||||
|
||||
pub mod debug;
|
||||
|
||||
pub mod db;
|
||||
pub mod source_binder;
|
||||
|
||||
mod ty;
|
||||
pub mod diagnostics;
|
||||
|
||||
mod from_id;
|
||||
mod code_model;
|
||||
|
||||
pub mod from_source;
|
||||
mod has_source;
|
||||
mod from_source;
|
||||
|
||||
pub use crate::{
|
||||
code_model::{
|
||||
src::HasSource, Adt, AssocItem, AttrDef, Const, Container, Crate, CrateDependency,
|
||||
DefWithBody, Docs, Enum, EnumVariant, FieldSource, Function, GenericDef, GenericParam,
|
||||
HasAttrs, ImplBlock, Import, Local, MacroDef, Module, ModuleDef, ModuleSource, ScopeDef,
|
||||
Static, Struct, StructField, Trait, Type, TypeAlias, Union, VariantDef,
|
||||
Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum,
|
||||
EnumVariant, FieldSource, Function, GenericDef, HasAttrs, ImplBlock, Local, MacroDef,
|
||||
Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias,
|
||||
TypeParam, Union, VariantDef,
|
||||
},
|
||||
from_source::FromSource,
|
||||
has_source::HasSource,
|
||||
source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
|
||||
ty::{
|
||||
display::HirDisplay,
|
||||
primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain},
|
||||
ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
},
|
||||
};
|
||||
|
||||
pub use hir_def::{
|
||||
body::scope::ExprScopes,
|
||||
builtin_type::BuiltinType,
|
||||
docs::Documentation,
|
||||
path::{Path, PathKind},
|
||||
nameres::ModuleSource,
|
||||
path::{ModPath, Path, PathKind},
|
||||
type_ref::Mutability,
|
||||
};
|
||||
pub use hir_expand::{
|
||||
either::Either, name::Name, HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Source,
|
||||
name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin,
|
||||
};
|
||||
pub use hir_ty::{display::HirDisplay, CallableDef};
|
||||
|
|
|
@ -7,19 +7,26 @@
|
|||
//! purely for "IDE needs".
|
||||
use std::sync::Arc;
|
||||
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
body::{
|
||||
scope::{ExprScopes, ScopeId},
|
||||
BodySourceMap,
|
||||
},
|
||||
expr::{ExprId, PatId},
|
||||
path::known,
|
||||
nameres::ModuleSource,
|
||||
path::path,
|
||||
resolver::{self, resolver_for_scope, HasResolver, Resolver, TypeNs, ValueNs},
|
||||
AssocItemId, DefWithBodyId,
|
||||
};
|
||||
use hir_expand::{
|
||||
hygiene::Hygiene, name::AsName, AstId, HirFileId, MacroCallId, MacroFileKind, Source,
|
||||
hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind,
|
||||
};
|
||||
use hir_ty::{
|
||||
method_resolution::{self, implements_trait},
|
||||
Canonical, InEnvironment, InferenceResult, TraitEnvironment, Ty,
|
||||
};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode},
|
||||
match_ast, AstPtr,
|
||||
|
@ -28,16 +35,12 @@ use ra_syntax::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
ty::{
|
||||
method_resolution::{self, implements_trait},
|
||||
InEnvironment, TraitEnvironment, Ty,
|
||||
},
|
||||
Adt, AssocItem, Const, DefWithBody, Either, Enum, EnumVariant, FromSource, Function,
|
||||
GenericParam, Local, MacroDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias,
|
||||
db::HirDatabase, Adt, AssocItem, Const, DefWithBody, Enum, EnumVariant, FromSource, Function,
|
||||
ImplBlock, Local, MacroDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias,
|
||||
TypeParam,
|
||||
};
|
||||
|
||||
fn try_get_resolver_for_node(db: &impl HirDatabase, node: Source<&SyntaxNode>) -> Option<Resolver> {
|
||||
fn try_get_resolver_for_node(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> Option<Resolver> {
|
||||
match_ast! {
|
||||
match (node.value) {
|
||||
ast::Module(it) => {
|
||||
|
@ -45,7 +48,7 @@ fn try_get_resolver_for_node(db: &impl HirDatabase, node: Source<&SyntaxNode>) -
|
|||
Some(crate::Module::from_declaration(db, src)?.id.resolver(db))
|
||||
},
|
||||
ast::SourceFile(it) => {
|
||||
let src = node.with_value(crate::ModuleSource::SourceFile(it));
|
||||
let src = node.with_value(ModuleSource::SourceFile(it));
|
||||
Some(crate::Module::from_definition(db, src)?.id.resolver(db))
|
||||
},
|
||||
ast::StructDef(it) => {
|
||||
|
@ -56,6 +59,14 @@ fn try_get_resolver_for_node(db: &impl HirDatabase, node: Source<&SyntaxNode>) -
|
|||
let src = node.with_value(it);
|
||||
Some(Enum::from_source(db, src)?.id.resolver(db))
|
||||
},
|
||||
ast::ImplBlock(it) => {
|
||||
let src = node.with_value(it);
|
||||
Some(ImplBlock::from_source(db, src)?.id.resolver(db))
|
||||
},
|
||||
ast::TraitDef(it) => {
|
||||
let src = node.with_value(it);
|
||||
Some(Trait::from_source(db, src)?.id.resolver(db))
|
||||
},
|
||||
_ => match node.value.kind() {
|
||||
FN_DEF | CONST_DEF | STATIC_DEF => {
|
||||
let def = def_with_body_from_child_node(db, node)?;
|
||||
|
@ -71,14 +82,16 @@ fn try_get_resolver_for_node(db: &impl HirDatabase, node: Source<&SyntaxNode>) -
|
|||
|
||||
fn def_with_body_from_child_node(
|
||||
db: &impl HirDatabase,
|
||||
child: Source<&SyntaxNode>,
|
||||
child: InFile<&SyntaxNode>,
|
||||
) -> Option<DefWithBody> {
|
||||
child.value.ancestors().find_map(|node| {
|
||||
let _p = profile("def_with_body_from_child_node");
|
||||
child.cloned().ancestors_with_macros(db).find_map(|node| {
|
||||
let n = &node.value;
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::FnDef(def) => { return Function::from_source(db, child.with_value(def)).map(DefWithBody::from); },
|
||||
ast::ConstDef(def) => { return Const::from_source(db, child.with_value(def)).map(DefWithBody::from); },
|
||||
ast::StaticDef(def) => { return Static::from_source(db, child.with_value(def)).map(DefWithBody::from); },
|
||||
match n {
|
||||
ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); },
|
||||
ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); },
|
||||
ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); },
|
||||
_ => { None },
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +106,7 @@ pub struct SourceAnalyzer {
|
|||
resolver: Resolver,
|
||||
body_owner: Option<DefWithBody>,
|
||||
body_source_map: Option<Arc<BodySourceMap>>,
|
||||
infer: Option<Arc<crate::ty::InferenceResult>>,
|
||||
infer: Option<Arc<InferenceResult>>,
|
||||
scopes: Option<Arc<ExprScopes>>,
|
||||
}
|
||||
|
||||
|
@ -104,7 +117,7 @@ pub enum PathResolution {
|
|||
/// A local binding (only value namespace)
|
||||
Local(Local),
|
||||
/// A generic parameter
|
||||
GenericParam(GenericParam),
|
||||
TypeParam(TypeParam),
|
||||
SelfType(crate::ImplBlock),
|
||||
Macro(MacroDef),
|
||||
AssocItem(crate::AssocItem),
|
||||
|
@ -132,8 +145,8 @@ pub struct ReferenceDescriptor {
|
|||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Expansion {
|
||||
macro_file_kind: MacroFileKind,
|
||||
macro_call_id: MacroCallId,
|
||||
}
|
||||
|
||||
|
@ -141,23 +154,24 @@ impl Expansion {
|
|||
pub fn map_token_down(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
token: Source<&SyntaxToken>,
|
||||
) -> Option<Source<SyntaxToken>> {
|
||||
token: InFile<&SyntaxToken>,
|
||||
) -> Option<InFile<SyntaxToken>> {
|
||||
let exp_info = self.file_id().expansion_info(db)?;
|
||||
exp_info.map_token_down(token)
|
||||
}
|
||||
|
||||
pub fn file_id(&self) -> HirFileId {
|
||||
self.macro_call_id.as_file(self.macro_file_kind)
|
||||
self.macro_call_id.as_file()
|
||||
}
|
||||
}
|
||||
|
||||
impl SourceAnalyzer {
|
||||
pub fn new(
|
||||
db: &impl HirDatabase,
|
||||
node: Source<&SyntaxNode>,
|
||||
node: InFile<&SyntaxNode>,
|
||||
offset: Option<TextUnit>,
|
||||
) -> SourceAnalyzer {
|
||||
let _p = profile("SourceAnalyzer::new");
|
||||
let def_with_body = def_with_body_from_child_node(db, node);
|
||||
if let Some(def) = def_with_body {
|
||||
let (_body, source_map) = db.body_with_source_map(def.into());
|
||||
|
@ -192,12 +206,12 @@ impl SourceAnalyzer {
|
|||
}
|
||||
|
||||
fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> {
|
||||
let src = Source { file_id: self.file_id, value: expr };
|
||||
let src = InFile { file_id: self.file_id, value: expr };
|
||||
self.body_source_map.as_ref()?.node_expr(src)
|
||||
}
|
||||
|
||||
fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> {
|
||||
let src = Source { file_id: self.file_id, value: pat };
|
||||
let src = InFile { file_id: self.file_id, value: pat };
|
||||
self.body_source_map.as_ref()?.node_pat(src)
|
||||
}
|
||||
|
||||
|
@ -226,7 +240,13 @@ impl SourceAnalyzer {
|
|||
}
|
||||
|
||||
pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> {
|
||||
let expr_id = self.expr_id(&field.expr()?)?;
|
||||
let expr_id = match field.expr() {
|
||||
Some(it) => self.expr_id(&it)?,
|
||||
None => {
|
||||
let src = InFile { file_id: self.file_id, value: field };
|
||||
self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?
|
||||
}
|
||||
};
|
||||
self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into())
|
||||
}
|
||||
|
||||
|
@ -243,11 +263,11 @@ impl SourceAnalyzer {
|
|||
pub fn resolve_macro_call(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
macro_call: Source<&ast::MacroCall>,
|
||||
macro_call: InFile<&ast::MacroCall>,
|
||||
) -> Option<MacroDef> {
|
||||
let hygiene = Hygiene::new(db, macro_call.file_id);
|
||||
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?;
|
||||
self.resolver.resolve_path_as_macro(db, &path).map(|it| it.into())
|
||||
self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into())
|
||||
}
|
||||
|
||||
pub fn resolve_hir_path(
|
||||
|
@ -255,12 +275,10 @@ impl SourceAnalyzer {
|
|||
db: &impl HirDatabase,
|
||||
path: &crate::Path,
|
||||
) -> Option<PathResolution> {
|
||||
let types = self.resolver.resolve_path_in_type_ns_fully(db, &path).map(|ty| match ty {
|
||||
let types =
|
||||
self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty {
|
||||
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
|
||||
TypeNs::GenericParam(idx) => PathResolution::GenericParam(GenericParam {
|
||||
parent: self.resolver.generic_def().unwrap(),
|
||||
idx,
|
||||
}),
|
||||
TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }),
|
||||
TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
|
||||
PathResolution::Def(Adt::from(it).into())
|
||||
}
|
||||
|
@ -269,7 +287,8 @@ impl SourceAnalyzer {
|
|||
TypeNs::BuiltinType(it) => PathResolution::Def(it.into()),
|
||||
TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
|
||||
});
|
||||
let values = self.resolver.resolve_path_in_value_ns_fully(db, &path).and_then(|val| {
|
||||
let values =
|
||||
self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| {
|
||||
let res = match val {
|
||||
ValueNs::LocalBinding(pat_id) => {
|
||||
let var = Local { parent: self.body_owner?, pat_id };
|
||||
|
@ -286,12 +305,12 @@ impl SourceAnalyzer {
|
|||
|
||||
let items = self
|
||||
.resolver
|
||||
.resolve_module_path(db, &path)
|
||||
.resolve_module_path_in_items(db, path.mod_path())
|
||||
.take_types()
|
||||
.map(|it| PathResolution::Def(it.into()));
|
||||
types.or(values).or(items).or_else(|| {
|
||||
self.resolver
|
||||
.resolve_path_as_macro(db, &path)
|
||||
.resolve_path_as_macro(db, path.mod_path())
|
||||
.map(|def| PathResolution::Macro(def.into()))
|
||||
})
|
||||
}
|
||||
|
@ -318,7 +337,7 @@ impl SourceAnalyzer {
|
|||
let name = name_ref.as_name();
|
||||
let source_map = self.body_source_map.as_ref()?;
|
||||
let scopes = self.scopes.as_ref()?;
|
||||
let scope = scope_for(scopes, source_map, Source::new(self.file_id, name_ref.syntax()))?;
|
||||
let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?;
|
||||
let entry = scopes.resolve_name_in_scope(scope, &name)?;
|
||||
Some(ScopeEntryWithSyntax {
|
||||
name: entry.name().clone(),
|
||||
|
@ -332,10 +351,7 @@ impl SourceAnalyzer {
|
|||
resolver::ScopeDef::PerNs(it) => it.into(),
|
||||
resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
|
||||
resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
|
||||
resolver::ScopeDef::GenericParam(idx) => {
|
||||
let parent = self.resolver.generic_def().unwrap();
|
||||
ScopeDef::GenericParam(GenericParam { parent, idx })
|
||||
}
|
||||
resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }),
|
||||
resolver::ScopeDef::Local(pat_id) => {
|
||||
let parent = self.resolver.body_owner().unwrap().into();
|
||||
ScopeDef::Local(Local { parent, pat_id })
|
||||
|
@ -349,7 +365,7 @@ impl SourceAnalyzer {
|
|||
// should switch to general reference search infra there.
|
||||
pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
|
||||
let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
|
||||
let ptr = Either::A(AstPtr::new(&ast::Pat::from(pat.clone())));
|
||||
let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone())));
|
||||
fn_def
|
||||
.syntax()
|
||||
.descendants()
|
||||
|
@ -375,7 +391,7 @@ impl SourceAnalyzer {
|
|||
// There should be no inference vars in types passed here
|
||||
// FIXME check that?
|
||||
// FIXME replace Unknown by bound vars here
|
||||
let canonical = crate::ty::Canonical { value: ty.ty.value.clone(), num_vars: 0 };
|
||||
let canonical = Canonical { value: ty.ty.value.clone(), num_vars: 0 };
|
||||
method_resolution::iterate_method_candidates(
|
||||
&canonical,
|
||||
db,
|
||||
|
@ -399,7 +415,7 @@ impl SourceAnalyzer {
|
|||
// There should be no inference vars in types passed here
|
||||
// FIXME check that?
|
||||
// FIXME replace Unknown by bound vars here
|
||||
let canonical = crate::ty::Canonical { value: ty.ty.value.clone(), num_vars: 0 };
|
||||
let canonical = Canonical { value: ty.ty.value.clone(), num_vars: 0 };
|
||||
method_resolution::iterate_method_candidates(
|
||||
&canonical,
|
||||
db,
|
||||
|
@ -410,24 +426,10 @@ impl SourceAnalyzer {
|
|||
)
|
||||
}
|
||||
|
||||
// pub fn autoderef<'a>(
|
||||
// &'a self,
|
||||
// db: &'a impl HirDatabase,
|
||||
// ty: Ty,
|
||||
// ) -> impl Iterator<Item = Ty> + 'a {
|
||||
// // There should be no inference vars in types passed here
|
||||
// // FIXME check that?
|
||||
// let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
|
||||
// let krate = self.resolver.krate();
|
||||
// let environment = TraitEnvironment::lower(db, &self.resolver);
|
||||
// let ty = crate::ty::InEnvironment { value: canonical, environment };
|
||||
// crate::ty::autoderef(db, krate, ty).map(|canonical| canonical.value)
|
||||
// }
|
||||
|
||||
/// Checks that particular type `ty` implements `std::future::Future`.
|
||||
/// This function is used in `.await` syntax completion.
|
||||
pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool {
|
||||
let std_future_path = known::std_future_future();
|
||||
pub fn impls_future(&self, db: &impl HirDatabase, ty: Type) -> bool {
|
||||
let std_future_path = path![std::future::Future];
|
||||
|
||||
let std_future_trait = match self.resolver.resolve_known_trait(db, &std_future_path) {
|
||||
Some(it) => it.into(),
|
||||
|
@ -439,43 +441,40 @@ impl SourceAnalyzer {
|
|||
_ => return false,
|
||||
};
|
||||
|
||||
let canonical_ty = crate::ty::Canonical { value: ty, num_vars: 0 };
|
||||
let canonical_ty = Canonical { value: ty.ty.value, num_vars: 0 };
|
||||
implements_trait(&canonical_ty, db, &self.resolver, krate.into(), std_future_trait)
|
||||
}
|
||||
|
||||
pub fn expand(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
macro_call: Source<&ast::MacroCall>,
|
||||
macro_call: InFile<&ast::MacroCall>,
|
||||
) -> Option<Expansion> {
|
||||
let def = self.resolve_macro_call(db, macro_call)?.id;
|
||||
let ast_id = AstId::new(
|
||||
macro_call.file_id,
|
||||
db.ast_id_map(macro_call.file_id).ast_id(macro_call.value),
|
||||
);
|
||||
Some(Expansion {
|
||||
macro_call_id: def.as_call_id(db, ast_id),
|
||||
macro_file_kind: to_macro_file_kind(macro_call.value),
|
||||
})
|
||||
Some(Expansion { macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)) })
|
||||
}
|
||||
}
|
||||
|
||||
fn scope_for(
|
||||
scopes: &ExprScopes,
|
||||
source_map: &BodySourceMap,
|
||||
node: Source<&SyntaxNode>,
|
||||
node: InFile<&SyntaxNode>,
|
||||
) -> Option<ScopeId> {
|
||||
node.value
|
||||
.ancestors()
|
||||
.filter_map(ast::Expr::cast)
|
||||
.filter_map(|it| source_map.node_expr(Source::new(node.file_id, &it)))
|
||||
.filter_map(|it| source_map.node_expr(InFile::new(node.file_id, &it)))
|
||||
.find_map(|it| scopes.scope_for(it))
|
||||
}
|
||||
|
||||
fn scope_for_offset(
|
||||
scopes: &ExprScopes,
|
||||
source_map: &BodySourceMap,
|
||||
offset: Source<TextUnit>,
|
||||
offset: InFile<TextUnit>,
|
||||
) -> Option<ScopeId> {
|
||||
scopes
|
||||
.scope_by_expr()
|
||||
|
@ -540,35 +539,3 @@ fn adjust(
|
|||
})
|
||||
.map(|(_ptr, scope)| *scope)
|
||||
}
|
||||
|
||||
/// Given a `ast::MacroCall`, return what `MacroKindFile` it belongs to.
|
||||
/// FIXME: Not completed
|
||||
fn to_macro_file_kind(macro_call: &ast::MacroCall) -> MacroFileKind {
|
||||
let syn = macro_call.syntax();
|
||||
let parent = match syn.parent() {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
// FIXME:
|
||||
// If it is root, which means the parent HirFile
|
||||
// MacroKindFile must be non-items
|
||||
// return expr now.
|
||||
return MacroFileKind::Expr;
|
||||
}
|
||||
};
|
||||
|
||||
match parent.kind() {
|
||||
MACRO_ITEMS | SOURCE_FILE => MacroFileKind::Items,
|
||||
LET_STMT => {
|
||||
// FIXME: Handle Pattern
|
||||
MacroFileKind::Expr
|
||||
}
|
||||
EXPR_STMT => MacroFileKind::Statements,
|
||||
BLOCK => MacroFileKind::Statements,
|
||||
ARG_LIST => MacroFileKind::Expr,
|
||||
TRY_EXPR => MacroFileKind::Expr,
|
||||
_ => {
|
||||
// Unknown , Just guess it is `Items`
|
||||
MacroFileKind::Items
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
//! The type system. We currently use this to infer types for completion, hover
|
||||
//! information and various assists.
|
||||
|
||||
pub use hir_ty::*;
|
|
@ -11,6 +11,9 @@ doctest = false
|
|||
log = "0.4.5"
|
||||
once_cell = "1.0.1"
|
||||
rustc-hash = "1.0"
|
||||
either = "1.5"
|
||||
anymap = "0.12"
|
||||
drop_bomb = "0.1.4"
|
||||
|
||||
ra_arena = { path = "../ra_arena" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
|
|
|
@ -2,17 +2,18 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
either::Either,
|
||||
name::{AsName, Name},
|
||||
Source,
|
||||
InFile,
|
||||
};
|
||||
use ra_arena::{map::ArenaMap, Arena};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, trace::Trace, type_ref::TypeRef, AstItemDef, EnumId, HasChildSource,
|
||||
LocalEnumVariantId, LocalStructFieldId, StructId, UnionId, VariantId,
|
||||
db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, type_ref::TypeRef, EnumId,
|
||||
LocalEnumVariantId, LocalStructFieldId, Lookup, StructId, UnionId, VariantId,
|
||||
};
|
||||
|
||||
/// Note that we use `StructData` for unions as well!
|
||||
|
@ -50,14 +51,14 @@ pub struct StructFieldData {
|
|||
|
||||
impl StructData {
|
||||
pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructId) -> Arc<StructData> {
|
||||
let src = id.source(db);
|
||||
let src = id.lookup(db).source(db);
|
||||
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||
let variant_data = VariantData::new(src.value.kind());
|
||||
let variant_data = Arc::new(variant_data);
|
||||
Arc::new(StructData { name, variant_data })
|
||||
}
|
||||
pub(crate) fn union_data_query(db: &impl DefDatabase, id: UnionId) -> Arc<StructData> {
|
||||
let src = id.source(db);
|
||||
let src = id.lookup(db).source(db);
|
||||
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||
let variant_data = VariantData::new(
|
||||
src.value
|
||||
|
@ -72,7 +73,8 @@ impl StructData {
|
|||
|
||||
impl EnumData {
|
||||
pub(crate) fn enum_data_query(db: &impl DefDatabase, e: EnumId) -> Arc<EnumData> {
|
||||
let src = e.source(db);
|
||||
let _p = profile("enum_data_query");
|
||||
let src = e.lookup(db).source(db);
|
||||
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||
let mut trace = Trace::new_for_arena();
|
||||
lower_enum(&mut trace, &src.value);
|
||||
|
@ -88,8 +90,8 @@ impl EnumData {
|
|||
impl HasChildSource for EnumId {
|
||||
type ChildId = LocalEnumVariantId;
|
||||
type Value = ast::EnumVariant;
|
||||
fn child_source(&self, db: &impl DefDatabase) -> Source<ArenaMap<Self::ChildId, Self::Value>> {
|
||||
let src = self.source(db);
|
||||
fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
|
||||
let src = self.lookup(db).source(db);
|
||||
let mut trace = Trace::new_for_map();
|
||||
lower_enum(&mut trace, &src.value);
|
||||
src.with_value(trace.into_map())
|
||||
|
@ -145,7 +147,7 @@ impl HasChildSource for VariantId {
|
|||
type ChildId = LocalStructFieldId;
|
||||
type Value = Either<ast::TupleFieldDef, ast::RecordFieldDef>;
|
||||
|
||||
fn child_source(&self, db: &impl DefDatabase) -> Source<ArenaMap<Self::ChildId, Self::Value>> {
|
||||
fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
|
||||
let src = match self {
|
||||
VariantId::EnumVariantId(it) => {
|
||||
// I don't really like the fact that we call into parent source
|
||||
|
@ -153,8 +155,8 @@ impl HasChildSource for VariantId {
|
|||
let src = it.parent.child_source(db);
|
||||
src.map(|map| map[it.local_id].kind())
|
||||
}
|
||||
VariantId::StructId(it) => it.source(db).map(|it| it.kind()),
|
||||
VariantId::UnionId(it) => it.source(db).map(|it| {
|
||||
VariantId::StructId(it) => it.lookup(db).source(db).map(|it| it.kind()),
|
||||
VariantId::UnionId(it) => it.lookup(db).source(db).map(|it| {
|
||||
it.record_field_def_list()
|
||||
.map(ast::StructKind::Record)
|
||||
.unwrap_or(ast::StructKind::Unit)
|
||||
|
@ -184,7 +186,7 @@ fn lower_struct(
|
|||
ast::StructKind::Tuple(fl) => {
|
||||
for (i, fd) in fl.fields().enumerate() {
|
||||
trace.alloc(
|
||||
|| Either::A(fd.clone()),
|
||||
|| Either::Left(fd.clone()),
|
||||
|| StructFieldData {
|
||||
name: Name::new_tuple_field(i),
|
||||
type_ref: TypeRef::from_ast_opt(fd.type_ref()),
|
||||
|
@ -196,7 +198,7 @@ fn lower_struct(
|
|||
ast::StructKind::Record(fl) => {
|
||||
for fd in fl.fields() {
|
||||
trace.alloc(
|
||||
|| Either::B(fd.clone()),
|
||||
|| Either::Right(fd.clone()),
|
||||
|| StructFieldData {
|
||||
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
|
||||
type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
use std::{ops, sync::Arc};
|
||||
|
||||
use hir_expand::{either::Either, hygiene::Hygiene, AstId, Source};
|
||||
use either::Either;
|
||||
use hir_expand::{hygiene::Hygiene, AstId, InFile};
|
||||
use mbe::ast_to_token_tree;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, AttrsOwner},
|
||||
|
@ -11,7 +12,7 @@ use ra_syntax::{
|
|||
use tt::Subtree;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, path::Path, AdtId, AstItemDef, AttrDefId, HasChildSource, HasSource, Lookup,
|
||||
db::DefDatabase, path::ModPath, src::HasChildSource, src::HasSource, AdtId, AttrDefId, Lookup,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -44,8 +45,8 @@ impl Attrs {
|
|||
AttrDefId::StructFieldId(it) => {
|
||||
let src = it.parent.child_source(db);
|
||||
match &src.value[it.local_id] {
|
||||
Either::A(_tuple) => Attrs::default(),
|
||||
Either::B(record) => Attrs::from_attrs_owner(db, src.with_value(record)),
|
||||
Either::Left(_tuple) => Attrs::default(),
|
||||
Either::Right(record) => Attrs::from_attrs_owner(db, src.with_value(record)),
|
||||
}
|
||||
}
|
||||
AttrDefId::EnumVariantId(var_id) => {
|
||||
|
@ -54,13 +55,15 @@ impl Attrs {
|
|||
Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
|
||||
}
|
||||
AttrDefId::AdtId(it) => match it {
|
||||
AdtId::StructId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
|
||||
AdtId::EnumId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
|
||||
AdtId::UnionId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
|
||||
AdtId::StructId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AdtId::EnumId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AdtId::UnionId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
},
|
||||
AttrDefId::TraitId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
|
||||
AttrDefId::MacroDefId(it) => attrs_from_ast(it.ast_id, db),
|
||||
AttrDefId::ImplId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
|
||||
AttrDefId::TraitId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AttrDefId::MacroDefId(it) => {
|
||||
it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
|
||||
}
|
||||
AttrDefId::ImplId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AttrDefId::FunctionId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
|
@ -68,7 +71,7 @@ impl Attrs {
|
|||
}
|
||||
}
|
||||
|
||||
fn from_attrs_owner(db: &impl DefDatabase, owner: Source<&dyn AttrsOwner>) -> Attrs {
|
||||
fn from_attrs_owner(db: &impl DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs {
|
||||
let hygiene = Hygiene::new(db, owner.file_id);
|
||||
Attrs::new(owner.value, &hygiene)
|
||||
}
|
||||
|
@ -91,7 +94,7 @@ impl Attrs {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Attr {
|
||||
pub(crate) path: Path,
|
||||
pub(crate) path: ModPath,
|
||||
pub(crate) input: Option<AttrInput>,
|
||||
}
|
||||
|
||||
|
@ -103,7 +106,7 @@ pub enum AttrInput {
|
|||
|
||||
impl Attr {
|
||||
fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
|
||||
let path = Path::from_src(ast.path()?, hygiene)?;
|
||||
let path = ModPath::from_src(ast.path()?, hygiene)?;
|
||||
let input = match ast.input() {
|
||||
None => None,
|
||||
Some(ast::AttrInput::Literal(lit)) => {
|
||||
|
@ -157,7 +160,7 @@ where
|
|||
N: ast::AttrsOwner,
|
||||
D: DefDatabase,
|
||||
{
|
||||
let src = Source::new(src.file_id(), src.to_node(db));
|
||||
let src = InFile::new(src.file_id, src.to_node(db));
|
||||
Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
|
||||
}
|
||||
|
||||
|
|
|
@ -3,58 +3,75 @@
|
|||
mod lower;
|
||||
pub mod scope;
|
||||
|
||||
use std::{ops::Index, sync::Arc};
|
||||
use std::{mem, ops::Index, sync::Arc};
|
||||
|
||||
use drop_bomb::DropBomb;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
either::Either, hygiene::Hygiene, AstId, HirFileId, MacroDefId, MacroFileKind, Source,
|
||||
ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroCallKind, MacroDefId,
|
||||
};
|
||||
use ra_arena::{map::ArenaMap, Arena};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{ast, AstNode, AstPtr};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
expr::{Expr, ExprId, Pat, PatId},
|
||||
item_scope::BuiltinShadowMode,
|
||||
item_scope::ItemScope,
|
||||
nameres::CrateDefMap,
|
||||
path::Path,
|
||||
DefWithBodyId, HasModule, HasSource, Lookup, ModuleId,
|
||||
path::{ModPath, Path},
|
||||
src::HasSource,
|
||||
DefWithBodyId, HasModule, Lookup, ModuleId,
|
||||
};
|
||||
|
||||
struct Expander {
|
||||
pub(crate) struct Expander {
|
||||
crate_def_map: Arc<CrateDefMap>,
|
||||
current_file_id: HirFileId,
|
||||
hygiene: Hygiene,
|
||||
ast_id_map: Arc<AstIdMap>,
|
||||
module: ModuleId,
|
||||
}
|
||||
|
||||
impl Expander {
|
||||
fn new(db: &impl DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
|
||||
pub(crate) fn new(
|
||||
db: &impl DefDatabase,
|
||||
current_file_id: HirFileId,
|
||||
module: ModuleId,
|
||||
) -> Expander {
|
||||
let crate_def_map = db.crate_def_map(module.krate);
|
||||
let hygiene = Hygiene::new(db, current_file_id);
|
||||
Expander { crate_def_map, current_file_id, hygiene, module }
|
||||
let ast_id_map = db.ast_id_map(current_file_id);
|
||||
Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module }
|
||||
}
|
||||
|
||||
fn enter_expand(
|
||||
pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>(
|
||||
&mut self,
|
||||
db: &impl DefDatabase,
|
||||
db: &DB,
|
||||
macro_call: ast::MacroCall,
|
||||
) -> Option<(Mark, ast::Expr)> {
|
||||
) -> Option<(Mark, T)> {
|
||||
let ast_id = AstId::new(
|
||||
self.current_file_id,
|
||||
db.ast_id_map(self.current_file_id).ast_id(¯o_call),
|
||||
);
|
||||
|
||||
if let Some(path) = macro_call.path().and_then(|path| self.parse_path(path)) {
|
||||
if let Some(path) = macro_call.path().and_then(|path| self.parse_mod_path(path)) {
|
||||
if let Some(def) = self.resolve_path_as_macro(db, &path) {
|
||||
let call_id = def.as_call_id(db, ast_id);
|
||||
let file_id = call_id.as_file(MacroFileKind::Expr);
|
||||
let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id));
|
||||
let file_id = call_id.as_file();
|
||||
if let Some(node) = db.parse_or_expand(file_id) {
|
||||
if let Some(expr) = ast::Expr::cast(node) {
|
||||
if let Some(expr) = T::cast(node) {
|
||||
log::debug!("macro expansion {:#?}", expr.syntax());
|
||||
|
||||
let mark = Mark { file_id: self.current_file_id };
|
||||
let mark = Mark {
|
||||
file_id: self.current_file_id,
|
||||
ast_id_map: mem::take(&mut self.ast_id_map),
|
||||
bomb: DropBomb::new("expansion mark dropped"),
|
||||
};
|
||||
self.hygiene = Hygiene::new(db, file_id);
|
||||
self.current_file_id = file_id;
|
||||
self.ast_id_map = db.ast_id_map(file_id);
|
||||
|
||||
return Some((mark, expr));
|
||||
}
|
||||
|
@ -67,35 +84,42 @@ impl Expander {
|
|||
None
|
||||
}
|
||||
|
||||
fn exit(&mut self, db: &impl DefDatabase, mark: Mark) {
|
||||
pub(crate) fn exit(&mut self, db: &impl DefDatabase, mut mark: Mark) {
|
||||
self.hygiene = Hygiene::new(db, mark.file_id);
|
||||
self.current_file_id = mark.file_id;
|
||||
std::mem::forget(mark);
|
||||
self.ast_id_map = mem::take(&mut mark.ast_id_map);
|
||||
mark.bomb.defuse();
|
||||
}
|
||||
|
||||
fn to_source<T>(&self, value: T) -> Source<T> {
|
||||
Source { file_id: self.current_file_id, value }
|
||||
pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
|
||||
InFile { file_id: self.current_file_id, value }
|
||||
}
|
||||
|
||||
fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
|
||||
Path::from_src(path, &self.hygiene)
|
||||
}
|
||||
|
||||
fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &Path) -> Option<MacroDefId> {
|
||||
self.crate_def_map.resolve_path(db, self.module.local_id, path).0.take_macros()
|
||||
fn parse_mod_path(&mut self, path: ast::Path) -> Option<ModPath> {
|
||||
ModPath::from_src(path, &self.hygiene)
|
||||
}
|
||||
|
||||
fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &ModPath) -> Option<MacroDefId> {
|
||||
self.crate_def_map
|
||||
.resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other)
|
||||
.0
|
||||
.take_macros()
|
||||
}
|
||||
|
||||
fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> {
|
||||
let file_local_id = self.ast_id_map.ast_id(item);
|
||||
AstId::new(self.current_file_id, file_local_id)
|
||||
}
|
||||
}
|
||||
|
||||
struct Mark {
|
||||
pub(crate) struct Mark {
|
||||
file_id: HirFileId,
|
||||
}
|
||||
|
||||
impl Drop for Mark {
|
||||
fn drop(&mut self) {
|
||||
if !std::thread::panicking() {
|
||||
panic!("dropped mark")
|
||||
}
|
||||
}
|
||||
ast_id_map: Arc<AstIdMap>,
|
||||
bomb: DropBomb,
|
||||
}
|
||||
|
||||
/// The body of an item (function, const etc.).
|
||||
|
@ -112,13 +136,14 @@ pub struct Body {
|
|||
pub params: Vec<PatId>,
|
||||
/// The `ExprId` of the actual body expression.
|
||||
pub body_expr: ExprId,
|
||||
pub item_scope: ItemScope,
|
||||
}
|
||||
|
||||
pub type ExprPtr = Either<AstPtr<ast::Expr>, AstPtr<ast::RecordField>>;
|
||||
pub type ExprSource = Source<ExprPtr>;
|
||||
pub type ExprSource = InFile<ExprPtr>;
|
||||
|
||||
pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
|
||||
pub type PatSource = Source<PatPtr>;
|
||||
pub type PatSource = InFile<PatPtr>;
|
||||
|
||||
/// An item body together with the mapping from syntax nodes to HIR expression
|
||||
/// IDs. This is needed to go from e.g. a position in a file to the HIR
|
||||
|
@ -145,6 +170,7 @@ impl Body {
|
|||
db: &impl DefDatabase,
|
||||
def: DefWithBodyId,
|
||||
) -> (Arc<Body>, Arc<BodySourceMap>) {
|
||||
let _p = profile("body_with_source_map_query");
|
||||
let mut params = None;
|
||||
|
||||
let (file_id, module, body) = match def {
|
||||
|
@ -166,7 +192,7 @@ impl Body {
|
|||
}
|
||||
};
|
||||
let expander = Expander::new(db, file_id, module);
|
||||
let (body, source_map) = Body::new(db, expander, params, body);
|
||||
let (body, source_map) = Body::new(db, def, expander, params, body);
|
||||
(Arc::new(body), Arc::new(source_map))
|
||||
}
|
||||
|
||||
|
@ -176,11 +202,12 @@ impl Body {
|
|||
|
||||
fn new(
|
||||
db: &impl DefDatabase,
|
||||
def: DefWithBodyId,
|
||||
expander: Expander,
|
||||
params: Option<ast::ParamList>,
|
||||
body: Option<ast::Expr>,
|
||||
) -> (Body, BodySourceMap) {
|
||||
lower::lower(db, expander, params, body)
|
||||
lower::lower(db, def, expander, params, body)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,8 +232,13 @@ impl BodySourceMap {
|
|||
self.expr_map_back.get(expr).copied()
|
||||
}
|
||||
|
||||
pub fn node_expr(&self, node: Source<&ast::Expr>) -> Option<ExprId> {
|
||||
let src = node.map(|it| Either::A(AstPtr::new(it)));
|
||||
pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
|
||||
let src = node.map(|it| Either::Left(AstPtr::new(it)));
|
||||
self.expr_map.get(&src).cloned()
|
||||
}
|
||||
|
||||
pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> {
|
||||
let src = node.map(|it| Either::Right(AstPtr::new(it)));
|
||||
self.expr_map.get(&src).cloned()
|
||||
}
|
||||
|
||||
|
@ -214,8 +246,8 @@ impl BodySourceMap {
|
|||
self.pat_map_back.get(pat).copied()
|
||||
}
|
||||
|
||||
pub fn node_pat(&self, node: Source<&ast::Pat>) -> Option<PatId> {
|
||||
let src = node.map(|it| Either::A(AstPtr::new(it)));
|
||||
pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
|
||||
let src = node.map(|it| Either::Left(AstPtr::new(it)));
|
||||
self.pat_map.get(&src).cloned()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
|
||||
//! representation.
|
||||
|
||||
use hir_expand::{
|
||||
either::Either,
|
||||
name::{self, AsName, Name},
|
||||
};
|
||||
use either::Either;
|
||||
|
||||
use hir_expand::name::{name, AsName, Name};
|
||||
use ra_arena::Arena;
|
||||
use ra_syntax::{
|
||||
ast::{
|
||||
self, ArgListOwner, ArrayExprKind, LiteralKind, LoopBodyOwner, NameOwner,
|
||||
self, ArgListOwner, ArrayExprKind, LiteralKind, LoopBodyOwner, ModuleItemOwner, NameOwner,
|
||||
TypeAscriptionOwner,
|
||||
},
|
||||
AstNode, AstPtr,
|
||||
|
@ -26,23 +25,28 @@ use crate::{
|
|||
path::GenericArgs,
|
||||
path::Path,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, StaticLoc,
|
||||
StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
|
||||
};
|
||||
|
||||
pub(super) fn lower(
|
||||
db: &impl DefDatabase,
|
||||
def: DefWithBodyId,
|
||||
expander: Expander,
|
||||
params: Option<ast::ParamList>,
|
||||
body: Option<ast::Expr>,
|
||||
) -> (Body, BodySourceMap) {
|
||||
ExprCollector {
|
||||
expander,
|
||||
db,
|
||||
def,
|
||||
expander,
|
||||
source_map: BodySourceMap::default(),
|
||||
body: Body {
|
||||
exprs: Arena::default(),
|
||||
pats: Arena::default(),
|
||||
params: Vec::new(),
|
||||
body_expr: ExprId::dummy(),
|
||||
item_scope: Default::default(),
|
||||
},
|
||||
}
|
||||
.collect(params, body)
|
||||
|
@ -50,6 +54,7 @@ pub(super) fn lower(
|
|||
|
||||
struct ExprCollector<DB> {
|
||||
db: DB,
|
||||
def: DefWithBodyId,
|
||||
expander: Expander,
|
||||
|
||||
body: Body,
|
||||
|
@ -70,11 +75,11 @@ where
|
|||
let ptr = AstPtr::new(&self_param);
|
||||
let param_pat = self.alloc_pat(
|
||||
Pat::Bind {
|
||||
name: name::SELF_PARAM,
|
||||
name: name![self],
|
||||
mode: BindingAnnotation::Unannotated,
|
||||
subpat: None,
|
||||
},
|
||||
Either::B(ptr),
|
||||
Either::Right(ptr),
|
||||
);
|
||||
self.body.params.push(param_pat);
|
||||
}
|
||||
|
@ -94,7 +99,7 @@ where
|
|||
}
|
||||
|
||||
fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId {
|
||||
let ptr = Either::A(ptr);
|
||||
let ptr = Either::Left(ptr);
|
||||
let id = self.body.exprs.alloc(expr);
|
||||
let src = self.expander.to_source(ptr);
|
||||
self.source_map.expr_map.insert(src, id);
|
||||
|
@ -107,7 +112,7 @@ where
|
|||
self.body.exprs.alloc(expr)
|
||||
}
|
||||
fn alloc_expr_field_shorthand(&mut self, expr: Expr, ptr: AstPtr<ast::RecordField>) -> ExprId {
|
||||
let ptr = Either::B(ptr);
|
||||
let ptr = Either::Right(ptr);
|
||||
let id = self.body.exprs.alloc(expr);
|
||||
let src = self.expander.to_source(ptr);
|
||||
self.source_map.expr_map.insert(src, id);
|
||||
|
@ -277,7 +282,7 @@ where
|
|||
ast::Expr::ParenExpr(e) => {
|
||||
let inner = self.collect_expr_opt(e.expr());
|
||||
// make the paren expr point to the inner expression as well
|
||||
let src = self.expander.to_source(Either::A(syntax_ptr));
|
||||
let src = self.expander.to_source(Either::Left(syntax_ptr));
|
||||
self.source_map.expr_map.insert(src, inner);
|
||||
inner
|
||||
}
|
||||
|
@ -367,8 +372,9 @@ where
|
|||
arg_types.push(type_ref);
|
||||
}
|
||||
}
|
||||
let ret_type = e.ret_type().and_then(|r| r.type_ref()).map(TypeRef::from_ast);
|
||||
let body = self.collect_expr_opt(e.body());
|
||||
self.alloc_expr(Expr::Lambda { args, arg_types, body }, syntax_ptr)
|
||||
self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::BinExpr(e) => {
|
||||
let lhs = self.collect_expr_opt(e.lhs());
|
||||
|
@ -429,10 +435,17 @@ where
|
|||
let index = self.collect_expr_opt(e.index());
|
||||
self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
|
||||
}
|
||||
|
||||
// FIXME implement HIR for these:
|
||||
ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
ast::Expr::RangeExpr(e) => {
|
||||
let lhs = e.start().map(|lhs| self.collect_expr(lhs));
|
||||
let rhs = e.end().map(|rhs| self.collect_expr(rhs));
|
||||
match e.op_kind() {
|
||||
Some(range_type) => {
|
||||
self.alloc_expr(Expr::Range { lhs, rhs, range_type }, syntax_ptr)
|
||||
}
|
||||
None => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
}
|
||||
}
|
||||
// FIXME expand to statements in statement position
|
||||
ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) {
|
||||
Some((mark, expansion)) => {
|
||||
let id = self.collect_expr(expansion);
|
||||
|
@ -441,6 +454,9 @@ where
|
|||
}
|
||||
None => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
},
|
||||
|
||||
// FIXME implement HIR for these:
|
||||
ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -458,6 +474,7 @@ where
|
|||
Some(block) => block,
|
||||
None => return self.alloc_expr(Expr::Missing, syntax_node_ptr),
|
||||
};
|
||||
self.collect_block_items(&block);
|
||||
let statements = block
|
||||
.statements()
|
||||
.map(|s| match s {
|
||||
|
@ -474,6 +491,63 @@ where
|
|||
self.alloc_expr(Expr::Block { statements, tail }, syntax_node_ptr)
|
||||
}
|
||||
|
||||
fn collect_block_items(&mut self, block: &ast::Block) {
|
||||
let container = ContainerId::DefWithBodyId(self.def);
|
||||
for item in block.items() {
|
||||
let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
|
||||
ast::ModuleItem::FnDef(def) => {
|
||||
let ast_id = self.expander.ast_id(&def);
|
||||
(
|
||||
FunctionLoc { container: container.into(), ast_id }.intern(self.db).into(),
|
||||
def.name(),
|
||||
)
|
||||
}
|
||||
ast::ModuleItem::TypeAliasDef(def) => {
|
||||
let ast_id = self.expander.ast_id(&def);
|
||||
(
|
||||
TypeAliasLoc { container: container.into(), ast_id }.intern(self.db).into(),
|
||||
def.name(),
|
||||
)
|
||||
}
|
||||
ast::ModuleItem::ConstDef(def) => {
|
||||
let ast_id = self.expander.ast_id(&def);
|
||||
(
|
||||
ConstLoc { container: container.into(), ast_id }.intern(self.db).into(),
|
||||
def.name(),
|
||||
)
|
||||
}
|
||||
ast::ModuleItem::StaticDef(def) => {
|
||||
let ast_id = self.expander.ast_id(&def);
|
||||
(StaticLoc { container, ast_id }.intern(self.db).into(), def.name())
|
||||
}
|
||||
ast::ModuleItem::StructDef(def) => {
|
||||
let ast_id = self.expander.ast_id(&def);
|
||||
(StructLoc { container, ast_id }.intern(self.db).into(), def.name())
|
||||
}
|
||||
ast::ModuleItem::EnumDef(def) => {
|
||||
let ast_id = self.expander.ast_id(&def);
|
||||
(EnumLoc { container, ast_id }.intern(self.db).into(), def.name())
|
||||
}
|
||||
ast::ModuleItem::UnionDef(def) => {
|
||||
let ast_id = self.expander.ast_id(&def);
|
||||
(UnionLoc { container, ast_id }.intern(self.db).into(), def.name())
|
||||
}
|
||||
ast::ModuleItem::TraitDef(def) => {
|
||||
let ast_id = self.expander.ast_id(&def);
|
||||
(TraitLoc { container, ast_id }.intern(self.db).into(), def.name())
|
||||
}
|
||||
ast::ModuleItem::ImplBlock(_)
|
||||
| ast::ModuleItem::UseItem(_)
|
||||
| ast::ModuleItem::ExternCrateItem(_)
|
||||
| ast::ModuleItem::Module(_) => continue,
|
||||
};
|
||||
self.body.item_scope.define_def(def);
|
||||
if let Some(name) = name {
|
||||
self.body.item_scope.push_res(name.as_name(), def.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId {
|
||||
if let Some(block) = expr {
|
||||
self.collect_block(block)
|
||||
|
@ -541,7 +615,7 @@ where
|
|||
ast::Pat::SlicePat(_) | ast::Pat::RangePat(_) => Pat::Missing,
|
||||
};
|
||||
let ptr = AstPtr::new(&pat);
|
||||
self.alloc_pat(pattern, Either::A(ptr))
|
||||
self.alloc_pat(pattern, Either::Left(ptr))
|
||||
}
|
||||
|
||||
fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
|
||||
|
|
|
@ -171,7 +171,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hir_expand::{name::AsName, Source};
|
||||
use hir_expand::{name::AsName, InFile};
|
||||
use ra_db::{fixture::WithFixture, FileId, SourceDatabase};
|
||||
use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
|
||||
use test_utils::{assert_eq_text, covers, extract_offset};
|
||||
|
@ -183,8 +183,8 @@ mod tests {
|
|||
let crate_def_map = db.crate_def_map(krate);
|
||||
|
||||
let module = crate_def_map.modules_for_file(file_id).next().unwrap();
|
||||
let (_, res) = crate_def_map[module].scope.entries().next().unwrap();
|
||||
match res.def.take_values().unwrap() {
|
||||
let (_, def) = crate_def_map[module].scope.entries().next().unwrap();
|
||||
match def.take_values().unwrap() {
|
||||
ModuleDefId::FunctionId(it) => it,
|
||||
_ => panic!(),
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ mod tests {
|
|||
let (_body, source_map) = db.body_with_source_map(function.into());
|
||||
|
||||
let expr_id = source_map
|
||||
.node_expr(Source { file_id: file_id.into(), value: &marker.into() })
|
||||
.node_expr(InFile { file_id: file_id.into(), value: &marker.into() })
|
||||
.unwrap();
|
||||
let scope = scopes.scope_for(expr_id);
|
||||
|
||||
|
@ -318,7 +318,7 @@ mod tests {
|
|||
let expr_scope = {
|
||||
let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
|
||||
let expr_id =
|
||||
source_map.node_expr(Source { file_id: file_id.into(), value: &expr_ast }).unwrap();
|
||||
source_map.node_expr(InFile { file_id: file_id.into(), value: &expr_ast }).unwrap();
|
||||
scopes.scope_for(expr_id).unwrap()
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use hir_expand::name::{self, Name};
|
||||
use hir_expand::name::{name, Name};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Signedness {
|
||||
|
@ -52,26 +52,26 @@ pub enum BuiltinType {
|
|||
impl BuiltinType {
|
||||
#[rustfmt::skip]
|
||||
pub const ALL: &'static [(Name, BuiltinType)] = &[
|
||||
(name::CHAR, BuiltinType::Char),
|
||||
(name::BOOL, BuiltinType::Bool),
|
||||
(name::STR, BuiltinType::Str ),
|
||||
(name![char], BuiltinType::Char),
|
||||
(name![bool], BuiltinType::Bool),
|
||||
(name![str], BuiltinType::Str),
|
||||
|
||||
(name::ISIZE, BuiltinType::Int(BuiltinInt::ISIZE)),
|
||||
(name::I8, BuiltinType::Int(BuiltinInt::I8)),
|
||||
(name::I16, BuiltinType::Int(BuiltinInt::I16)),
|
||||
(name::I32, BuiltinType::Int(BuiltinInt::I32)),
|
||||
(name::I64, BuiltinType::Int(BuiltinInt::I64)),
|
||||
(name::I128, BuiltinType::Int(BuiltinInt::I128)),
|
||||
(name![isize], BuiltinType::Int(BuiltinInt::ISIZE)),
|
||||
(name![i8], BuiltinType::Int(BuiltinInt::I8)),
|
||||
(name![i16], BuiltinType::Int(BuiltinInt::I16)),
|
||||
(name![i32], BuiltinType::Int(BuiltinInt::I32)),
|
||||
(name![i64], BuiltinType::Int(BuiltinInt::I64)),
|
||||
(name![i128], BuiltinType::Int(BuiltinInt::I128)),
|
||||
|
||||
(name::USIZE, BuiltinType::Int(BuiltinInt::USIZE)),
|
||||
(name::U8, BuiltinType::Int(BuiltinInt::U8)),
|
||||
(name::U16, BuiltinType::Int(BuiltinInt::U16)),
|
||||
(name::U32, BuiltinType::Int(BuiltinInt::U32)),
|
||||
(name::U64, BuiltinType::Int(BuiltinInt::U64)),
|
||||
(name::U128, BuiltinType::Int(BuiltinInt::U128)),
|
||||
(name![usize], BuiltinType::Int(BuiltinInt::USIZE)),
|
||||
(name![u8], BuiltinType::Int(BuiltinInt::U8)),
|
||||
(name![u16], BuiltinType::Int(BuiltinInt::U16)),
|
||||
(name![u32], BuiltinType::Int(BuiltinInt::U32)),
|
||||
(name![u64], BuiltinType::Int(BuiltinInt::U64)),
|
||||
(name![u128], BuiltinType::Int(BuiltinInt::U128)),
|
||||
|
||||
(name::F32, BuiltinType::Float(BuiltinFloat::F32)),
|
||||
(name::F64, BuiltinType::Float(BuiltinFloat::F64)),
|
||||
(name![f32], BuiltinType::Float(BuiltinFloat::F32)),
|
||||
(name![f64], BuiltinType::Float(BuiltinFloat::F64)),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
177
crates/ra_hir_def/src/child_by_source.rs
Normal file
177
crates/ra_hir_def/src/child_by_source.rs
Normal file
|
@ -0,0 +1,177 @@
|
|||
//! When *constructing* `hir`, we start at some parent syntax node and recursively
|
||||
//! lower the children.
|
||||
//!
|
||||
//! This modules allows one to go in the opposite direction: start with a syntax
|
||||
//! node for a *child*, and get its hir.
|
||||
|
||||
use either::Either;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
dyn_map::DynMap,
|
||||
item_scope::ItemScope,
|
||||
keys,
|
||||
src::{HasChildSource, HasSource},
|
||||
AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, ImplId, Lookup, ModuleDefId,
|
||||
ModuleId, StructFieldId, TraitId, VariantId,
|
||||
};
|
||||
|
||||
pub trait ChildBySource {
|
||||
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap;
|
||||
}
|
||||
|
||||
impl ChildBySource for TraitId {
|
||||
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
|
||||
let mut res = DynMap::default();
|
||||
|
||||
let data = db.trait_data(*self);
|
||||
for (_name, item) in data.items.iter() {
|
||||
match *item {
|
||||
AssocItemId::FunctionId(func) => {
|
||||
let src = func.lookup(db).source(db);
|
||||
res[keys::FUNCTION].insert(src, func)
|
||||
}
|
||||
AssocItemId::ConstId(konst) => {
|
||||
let src = konst.lookup(db).source(db);
|
||||
res[keys::CONST].insert(src, konst)
|
||||
}
|
||||
AssocItemId::TypeAliasId(ty) => {
|
||||
let src = ty.lookup(db).source(db);
|
||||
res[keys::TYPE_ALIAS].insert(src, ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildBySource for ImplId {
|
||||
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
|
||||
let mut res = DynMap::default();
|
||||
|
||||
let data = db.impl_data(*self);
|
||||
for &item in data.items.iter() {
|
||||
match item {
|
||||
AssocItemId::FunctionId(func) => {
|
||||
let src = func.lookup(db).source(db);
|
||||
res[keys::FUNCTION].insert(src, func)
|
||||
}
|
||||
AssocItemId::ConstId(konst) => {
|
||||
let src = konst.lookup(db).source(db);
|
||||
res[keys::CONST].insert(src, konst)
|
||||
}
|
||||
AssocItemId::TypeAliasId(ty) => {
|
||||
let src = ty.lookup(db).source(db);
|
||||
res[keys::TYPE_ALIAS].insert(src, ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildBySource for ModuleId {
|
||||
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
|
||||
let crate_def_map = db.crate_def_map(self.krate);
|
||||
let module_data = &crate_def_map[self.local_id];
|
||||
module_data.scope.child_by_source(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildBySource for ItemScope {
|
||||
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
|
||||
let mut res = DynMap::default();
|
||||
self.declarations().for_each(|item| add_module_def(db, &mut res, item));
|
||||
self.impls().for_each(|imp| add_impl(db, &mut res, imp));
|
||||
return res;
|
||||
|
||||
fn add_module_def(db: &impl DefDatabase, map: &mut DynMap, item: ModuleDefId) {
|
||||
match item {
|
||||
ModuleDefId::FunctionId(func) => {
|
||||
let src = func.lookup(db).source(db);
|
||||
map[keys::FUNCTION].insert(src, func)
|
||||
}
|
||||
ModuleDefId::ConstId(konst) => {
|
||||
let src = konst.lookup(db).source(db);
|
||||
map[keys::CONST].insert(src, konst)
|
||||
}
|
||||
ModuleDefId::StaticId(statik) => {
|
||||
let src = statik.lookup(db).source(db);
|
||||
map[keys::STATIC].insert(src, statik)
|
||||
}
|
||||
ModuleDefId::TypeAliasId(ty) => {
|
||||
let src = ty.lookup(db).source(db);
|
||||
map[keys::TYPE_ALIAS].insert(src, ty)
|
||||
}
|
||||
ModuleDefId::TraitId(trait_) => {
|
||||
let src = trait_.lookup(db).source(db);
|
||||
map[keys::TRAIT].insert(src, trait_)
|
||||
}
|
||||
ModuleDefId::AdtId(adt) => match adt {
|
||||
AdtId::StructId(strukt) => {
|
||||
let src = strukt.lookup(db).source(db);
|
||||
map[keys::STRUCT].insert(src, strukt)
|
||||
}
|
||||
AdtId::UnionId(union_) => {
|
||||
let src = union_.lookup(db).source(db);
|
||||
map[keys::UNION].insert(src, union_)
|
||||
}
|
||||
AdtId::EnumId(enum_) => {
|
||||
let src = enum_.lookup(db).source(db);
|
||||
map[keys::ENUM].insert(src, enum_)
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
fn add_impl(db: &impl DefDatabase, map: &mut DynMap, imp: ImplId) {
|
||||
let src = imp.lookup(db).source(db);
|
||||
map[keys::IMPL].insert(src, imp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildBySource for VariantId {
|
||||
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
|
||||
let mut res = DynMap::default();
|
||||
|
||||
let arena_map = self.child_source(db);
|
||||
let arena_map = arena_map.as_ref();
|
||||
for (local_id, source) in arena_map.value.iter() {
|
||||
let id = StructFieldId { parent: *self, local_id };
|
||||
match source {
|
||||
Either::Left(source) => {
|
||||
res[keys::TUPLE_FIELD].insert(arena_map.with_value(source.clone()), id)
|
||||
}
|
||||
Either::Right(source) => {
|
||||
res[keys::RECORD_FIELD].insert(arena_map.with_value(source.clone()), id)
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildBySource for EnumId {
|
||||
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
|
||||
let mut res = DynMap::default();
|
||||
|
||||
let arena_map = self.child_source(db);
|
||||
let arena_map = arena_map.as_ref();
|
||||
for (local_id, source) in arena_map.value.iter() {
|
||||
let id = EnumVariantId { parent: *self, local_id };
|
||||
res[keys::ENUM_VARIANT].insert(arena_map.with_value(source.clone()), id)
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildBySource for DefWithBodyId {
|
||||
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
|
||||
let body = db.body(*self);
|
||||
body.item_scope.child_by_source(db)
|
||||
}
|
||||
}
|
|
@ -3,16 +3,17 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::{
|
||||
name::{self, AsName, Name},
|
||||
AstId,
|
||||
name::{name, AsName, Name},
|
||||
AstId, InFile,
|
||||
};
|
||||
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
|
||||
use ra_syntax::ast::{self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner};
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
src::HasSource,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
AssocItemId, AstItemDef, ConstId, ConstLoc, ContainerId, FunctionId, FunctionLoc, HasSource,
|
||||
ImplId, Intern, Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule,
|
||||
ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -36,7 +37,7 @@ impl FunctionData {
|
|||
let self_type = if let Some(type_ref) = self_param.ascribed_type() {
|
||||
TypeRef::from_ast(type_ref)
|
||||
} else {
|
||||
let self_type = TypeRef::Path(name::SELF_TYPE.into());
|
||||
let self_type = TypeRef::Path(name![Self].into());
|
||||
match self_param.kind() {
|
||||
ast::SelfParamKind::Owned => self_type,
|
||||
ast::SelfParamKind::Ref => {
|
||||
|
@ -93,12 +94,12 @@ pub struct TraitData {
|
|||
|
||||
impl TraitData {
|
||||
pub(crate) fn trait_data_query(db: &impl DefDatabase, tr: TraitId) -> Arc<TraitData> {
|
||||
let src = tr.source(db);
|
||||
let src = tr.lookup(db).source(db);
|
||||
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||
let auto = src.value.is_auto();
|
||||
let ast_id_map = db.ast_id_map(src.file_id);
|
||||
|
||||
let container = ContainerId::TraitId(tr);
|
||||
let container = AssocContainerId::TraitId(tr);
|
||||
let items = if let Some(item_list) = src.value.item_list() {
|
||||
item_list
|
||||
.impl_items()
|
||||
|
@ -166,46 +167,24 @@ pub struct ImplData {
|
|||
|
||||
impl ImplData {
|
||||
pub(crate) fn impl_data_query(db: &impl DefDatabase, id: ImplId) -> Arc<ImplData> {
|
||||
let src = id.source(db);
|
||||
let items = db.ast_id_map(src.file_id);
|
||||
let impl_loc = id.lookup(db);
|
||||
let src = impl_loc.source(db);
|
||||
|
||||
let target_trait = src.value.target_trait().map(TypeRef::from_ast);
|
||||
let target_type = TypeRef::from_ast_opt(src.value.target_type());
|
||||
let is_negative = src.value.is_negative();
|
||||
let module_id = impl_loc.container.module(db);
|
||||
|
||||
let items = if let Some(item_list) = src.value.item_list() {
|
||||
item_list
|
||||
.impl_items()
|
||||
.map(|item_node| match item_node {
|
||||
ast::ImplItem::FnDef(it) => {
|
||||
let def = FunctionLoc {
|
||||
container: ContainerId::ImplId(id),
|
||||
ast_id: AstId::new(src.file_id, items.ast_id(&it)),
|
||||
let mut items = Vec::new();
|
||||
if let Some(item_list) = src.value.item_list() {
|
||||
items.extend(collect_impl_items(db, item_list.impl_items(), src.file_id, id));
|
||||
items.extend(collect_impl_items_in_macros(
|
||||
db,
|
||||
module_id,
|
||||
&src.with_value(item_list),
|
||||
id,
|
||||
));
|
||||
}
|
||||
.intern(db);
|
||||
def.into()
|
||||
}
|
||||
ast::ImplItem::ConstDef(it) => {
|
||||
let def = ConstLoc {
|
||||
container: ContainerId::ImplId(id),
|
||||
ast_id: AstId::new(src.file_id, items.ast_id(&it)),
|
||||
}
|
||||
.intern(db);
|
||||
def.into()
|
||||
}
|
||||
ast::ImplItem::TypeAliasDef(it) => {
|
||||
let def = TypeAliasLoc {
|
||||
container: ContainerId::ImplId(id),
|
||||
ast_id: AstId::new(src.file_id, items.ast_id(&it)),
|
||||
}
|
||||
.intern(db);
|
||||
def.into()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let res = ImplData { target_trait, target_type, items, is_negative };
|
||||
Arc::new(res)
|
||||
|
@ -236,3 +215,92 @@ impl ConstData {
|
|||
ConstData { name, type_ref }
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_impl_items_in_macros(
|
||||
db: &impl DefDatabase,
|
||||
module_id: ModuleId,
|
||||
impl_block: &InFile<ast::ItemList>,
|
||||
id: ImplId,
|
||||
) -> Vec<AssocItemId> {
|
||||
let mut expander = Expander::new(db, impl_block.file_id, module_id);
|
||||
let mut res = Vec::new();
|
||||
|
||||
// We set a limit to protect against infinite recursion
|
||||
let limit = 100;
|
||||
|
||||
for m in impl_block.value.syntax().children().filter_map(ast::MacroCall::cast) {
|
||||
res.extend(collect_impl_items_in_macro(db, &mut expander, m, id, limit))
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn collect_impl_items_in_macro(
|
||||
db: &impl DefDatabase,
|
||||
expander: &mut Expander,
|
||||
m: ast::MacroCall,
|
||||
id: ImplId,
|
||||
limit: usize,
|
||||
) -> Vec<AssocItemId> {
|
||||
if limit == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
if let Some((mark, items)) = expander.enter_expand(db, m) {
|
||||
let items: InFile<ast::MacroItems> = expander.to_source(items);
|
||||
let mut res = collect_impl_items(
|
||||
db,
|
||||
items.value.items().filter_map(|it| ImplItem::cast(it.syntax().clone())),
|
||||
items.file_id,
|
||||
id,
|
||||
);
|
||||
// Recursive collect macros
|
||||
// Note that ast::ModuleItem do not include ast::MacroCall
|
||||
// We cannot use ModuleItemOwner::items here
|
||||
for it in items.value.syntax().children().filter_map(ast::MacroCall::cast) {
|
||||
res.extend(collect_impl_items_in_macro(db, expander, it, id, limit - 1))
|
||||
}
|
||||
expander.exit(db, mark);
|
||||
res
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_impl_items(
|
||||
db: &impl DefDatabase,
|
||||
impl_items: impl Iterator<Item = ImplItem>,
|
||||
file_id: crate::HirFileId,
|
||||
id: ImplId,
|
||||
) -> Vec<AssocItemId> {
|
||||
let items = db.ast_id_map(file_id);
|
||||
|
||||
impl_items
|
||||
.map(|item_node| match item_node {
|
||||
ast::ImplItem::FnDef(it) => {
|
||||
let def = FunctionLoc {
|
||||
container: AssocContainerId::ImplId(id),
|
||||
ast_id: AstId::new(file_id, items.ast_id(&it)),
|
||||
}
|
||||
.intern(db);
|
||||
def.into()
|
||||
}
|
||||
ast::ImplItem::ConstDef(it) => {
|
||||
let def = ConstLoc {
|
||||
container: AssocContainerId::ImplId(id),
|
||||
ast_id: AstId::new(file_id, items.ast_id(&it)),
|
||||
}
|
||||
.intern(db);
|
||||
def.into()
|
||||
}
|
||||
ast::ImplItem::TypeAliasDef(it) => {
|
||||
let def = TypeAliasLoc {
|
||||
container: AssocContainerId::ImplId(id),
|
||||
ast_id: AstId::new(file_id, items.ast_id(&it)),
|
||||
}
|
||||
.intern(db);
|
||||
def.into()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||
|
||||
use hir_expand::{db::AstDatabase, HirFileId};
|
||||
use ra_db::{salsa, CrateId, SourceDatabase};
|
||||
use ra_syntax::{ast, SmolStr};
|
||||
use ra_syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
adt::{EnumData, StructData},
|
||||
|
@ -13,13 +13,10 @@ use crate::{
|
|||
docs::Documentation,
|
||||
generics::GenericParams,
|
||||
lang_item::{LangItemTarget, LangItems},
|
||||
nameres::{
|
||||
raw::{ImportSourceMap, RawItems},
|
||||
CrateDefMap,
|
||||
},
|
||||
AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, FunctionId, FunctionLoc, GenericDefId,
|
||||
ImplId, ItemLoc, ModuleId, StaticId, StaticLoc, StructId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
UnionId,
|
||||
nameres::{raw::RawItems, CrateDefMap},
|
||||
AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
|
||||
GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
|
||||
TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
|
||||
};
|
||||
|
||||
#[salsa::query_group(InternDatabaseStorage)]
|
||||
|
@ -27,31 +24,25 @@ pub trait InternDatabase: SourceDatabase {
|
|||
#[salsa::interned]
|
||||
fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
|
||||
#[salsa::interned]
|
||||
fn intern_struct(&self, loc: ItemLoc<ast::StructDef>) -> StructId;
|
||||
fn intern_struct(&self, loc: StructLoc) -> StructId;
|
||||
#[salsa::interned]
|
||||
fn intern_union(&self, loc: ItemLoc<ast::UnionDef>) -> UnionId;
|
||||
fn intern_union(&self, loc: UnionLoc) -> UnionId;
|
||||
#[salsa::interned]
|
||||
fn intern_enum(&self, loc: ItemLoc<ast::EnumDef>) -> EnumId;
|
||||
fn intern_enum(&self, loc: EnumLoc) -> EnumId;
|
||||
#[salsa::interned]
|
||||
fn intern_const(&self, loc: ConstLoc) -> ConstId;
|
||||
#[salsa::interned]
|
||||
fn intern_static(&self, loc: StaticLoc) -> StaticId;
|
||||
#[salsa::interned]
|
||||
fn intern_trait(&self, loc: ItemLoc<ast::TraitDef>) -> TraitId;
|
||||
fn intern_trait(&self, loc: TraitLoc) -> TraitId;
|
||||
#[salsa::interned]
|
||||
fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId;
|
||||
#[salsa::interned]
|
||||
fn intern_impl(&self, loc: ItemLoc<ast::ImplBlock>) -> ImplId;
|
||||
fn intern_impl(&self, loc: ImplLoc) -> ImplId;
|
||||
}
|
||||
|
||||
#[salsa::query_group(DefDatabaseStorage)]
|
||||
pub trait DefDatabase: 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>;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use hir_expand::diagnostics::Diagnostic;
|
|||
use ra_db::RelativePathBuf;
|
||||
use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
|
||||
|
||||
use hir_expand::{HirFileId, Source};
|
||||
use hir_expand::{HirFileId, InFile};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedModule {
|
||||
|
@ -19,8 +19,8 @@ impl Diagnostic for UnresolvedModule {
|
|||
fn message(&self) -> String {
|
||||
"unresolved module".to_string()
|
||||
}
|
||||
fn source(&self) -> Source<SyntaxNodePtr> {
|
||||
Source { file_id: self.file, value: self.decl.into() }
|
||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile { file_id: self.file, value: self.decl.into() }
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
|
|
|
@ -5,10 +5,14 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::either::Either;
|
||||
use either::Either;
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::{db::DefDatabase, AdtId, AstItemDef, AttrDefId, HasChildSource, HasSource, Lookup};
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
src::{HasChildSource, HasSource},
|
||||
AdtId, AttrDefId, Lookup,
|
||||
};
|
||||
|
||||
/// Holds documentation
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -42,21 +46,21 @@ impl Documentation {
|
|||
AttrDefId::StructFieldId(it) => {
|
||||
let src = it.parent.child_source(db);
|
||||
match &src.value[it.local_id] {
|
||||
Either::A(_tuple) => None,
|
||||
Either::B(record) => docs_from_ast(record),
|
||||
Either::Left(_tuple) => None,
|
||||
Either::Right(record) => docs_from_ast(record),
|
||||
}
|
||||
}
|
||||
AttrDefId::AdtId(it) => match it {
|
||||
AdtId::StructId(it) => docs_from_ast(&it.source(db).value),
|
||||
AdtId::EnumId(it) => docs_from_ast(&it.source(db).value),
|
||||
AdtId::UnionId(it) => docs_from_ast(&it.source(db).value),
|
||||
AdtId::StructId(it) => docs_from_ast(&it.lookup(db).source(db).value),
|
||||
AdtId::EnumId(it) => docs_from_ast(&it.lookup(db).source(db).value),
|
||||
AdtId::UnionId(it) => docs_from_ast(&it.lookup(db).source(db).value),
|
||||
},
|
||||
AttrDefId::EnumVariantId(it) => {
|
||||
let src = it.parent.child_source(db);
|
||||
docs_from_ast(&src.value[it.local_id])
|
||||
}
|
||||
AttrDefId::TraitId(it) => docs_from_ast(&it.source(db).value),
|
||||
AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id.to_node(db)),
|
||||
AttrDefId::TraitId(it) => docs_from_ast(&it.lookup(db).source(db).value),
|
||||
AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id?.to_node(db)),
|
||||
AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value),
|
||||
AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value),
|
||||
AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value),
|
||||
|
|
108
crates/ra_hir_def/src/dyn_map.rs
Normal file
108
crates/ra_hir_def/src/dyn_map.rs
Normal file
|
@ -0,0 +1,108 @@
|
|||
//! This module defines a `DynMap` -- a container for heterogeneous maps.
|
||||
//!
|
||||
//! This means that `DynMap` stores a bunch of hash maps inside, and those maps
|
||||
//! can be of different types.
|
||||
//!
|
||||
//! It is used like this:
|
||||
//!
|
||||
//! ```
|
||||
//! // keys define submaps of a `DynMap`
|
||||
//! const STRING_TO_U32: Key<String, u32> = Key::new();
|
||||
//! const U32_TO_VEC: Key<u32, Vec<bool>> = Key::new();
|
||||
//!
|
||||
//! // Note: concrete type, no type params!
|
||||
//! let mut map = DynMap::new();
|
||||
//!
|
||||
//! // To access a specific map, index the `DynMap` by `Key`:
|
||||
//! map[STRING_TO_U32].insert("hello".to_string(), 92);
|
||||
//! let value = map[U32_TO_VEC].get(92);
|
||||
//! assert!(value.is_none());
|
||||
//! ```
|
||||
//!
|
||||
//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
|
||||
//! a coincidence.
|
||||
use std::{
|
||||
hash::Hash,
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
use anymap::Map;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
pub struct Key<K, V, P = (K, V)> {
|
||||
_phantom: PhantomData<(K, V, P)>,
|
||||
}
|
||||
|
||||
impl<K, V, P> Key<K, V, P> {
|
||||
pub(crate) const fn new() -> Key<K, V, P> {
|
||||
Key { _phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, P> Copy for Key<K, V, P> {}
|
||||
|
||||
impl<K, V, P> Clone for Key<K, V, P> {
|
||||
fn clone(&self) -> Key<K, V, P> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Policy {
|
||||
type K;
|
||||
type V;
|
||||
|
||||
fn insert(map: &mut DynMap, key: Self::K, value: Self::V);
|
||||
fn get<'a>(map: &'a DynMap, key: &Self::K) -> Option<&'a Self::V>;
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq + 'static, V: 'static> Policy for (K, V) {
|
||||
type K = K;
|
||||
type V = V;
|
||||
fn insert(map: &mut DynMap, key: K, value: V) {
|
||||
map.map.entry::<FxHashMap<K, V>>().or_insert_with(Default::default).insert(key, value);
|
||||
}
|
||||
fn get<'a>(map: &'a DynMap, key: &K) -> Option<&'a V> {
|
||||
map.map.get::<FxHashMap<K, V>>()?.get(key)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DynMap {
|
||||
pub(crate) map: Map,
|
||||
}
|
||||
|
||||
impl Default for DynMap {
|
||||
fn default() -> Self {
|
||||
DynMap { map: Map::new() }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct KeyMap<KEY> {
|
||||
map: DynMap,
|
||||
_phantom: PhantomData<KEY>,
|
||||
}
|
||||
|
||||
impl<P: Policy> KeyMap<Key<P::K, P::V, P>> {
|
||||
pub fn insert(&mut self, key: P::K, value: P::V) {
|
||||
P::insert(&mut self.map, key, value)
|
||||
}
|
||||
pub fn get(&self, key: &P::K) -> Option<&P::V> {
|
||||
P::get(&self.map, key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Policy> Index<Key<P::K, P::V, P>> for DynMap {
|
||||
type Output = KeyMap<Key<P::K, P::V, P>>;
|
||||
fn index(&self, _key: Key<P::K, P::V, P>) -> &Self::Output {
|
||||
// Safe due to `#[repr(transparent)]`.
|
||||
unsafe { std::mem::transmute::<&DynMap, &KeyMap<Key<P::K, P::V, P>>>(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Policy> IndexMut<Key<P::K, P::V, P>> for DynMap {
|
||||
fn index_mut(&mut self, _key: Key<P::K, P::V, P>) -> &mut Self::Output {
|
||||
// Safe due to `#[repr(transparent)]`.
|
||||
unsafe { std::mem::transmute::<&mut DynMap, &mut KeyMap<Key<P::K, P::V, P>>>(self) }
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
use hir_expand::name::Name;
|
||||
use ra_arena::{impl_arena_id, RawId};
|
||||
use ra_syntax::ast::RangeOp;
|
||||
|
||||
use crate::{
|
||||
builtin_type::{BuiltinFloat, BuiltinInt},
|
||||
|
@ -130,6 +131,11 @@ pub enum Expr {
|
|||
rhs: ExprId,
|
||||
op: Option<BinaryOp>,
|
||||
},
|
||||
Range {
|
||||
lhs: Option<ExprId>,
|
||||
rhs: Option<ExprId>,
|
||||
range_type: RangeOp,
|
||||
},
|
||||
Index {
|
||||
base: ExprId,
|
||||
index: ExprId,
|
||||
|
@ -137,6 +143,7 @@ pub enum Expr {
|
|||
Lambda {
|
||||
args: Vec<PatId>,
|
||||
arg_types: Vec<Option<TypeRef>>,
|
||||
ret_type: Option<TypeRef>,
|
||||
body: ExprId,
|
||||
},
|
||||
Tuple {
|
||||
|
@ -288,6 +295,14 @@ impl Expr {
|
|||
f(*lhs);
|
||||
f(*rhs);
|
||||
}
|
||||
Expr::Range { lhs, rhs, .. } => {
|
||||
if let Some(lhs) = rhs {
|
||||
f(*lhs);
|
||||
}
|
||||
if let Some(rhs) = lhs {
|
||||
f(*rhs);
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index } => {
|
||||
f(*base);
|
||||
f(*index);
|
||||
|
|
|
@ -4,20 +4,29 @@
|
|||
//! in rustc.
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::name::{self, AsName, Name};
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
name::{name, AsName, Name},
|
||||
InFile,
|
||||
};
|
||||
use ra_arena::{map::ArenaMap, Arena};
|
||||
use ra_db::FileId;
|
||||
use ra_syntax::ast::{self, NameOwner, TypeBoundsOwner, TypeParamsOwner};
|
||||
|
||||
use crate::{
|
||||
child_by_source::ChildBySource,
|
||||
db::DefDatabase,
|
||||
dyn_map::DynMap,
|
||||
keys,
|
||||
src::HasChildSource,
|
||||
src::HasSource,
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
AdtId, AstItemDef, ContainerId, GenericDefId, HasSource, Lookup,
|
||||
AdtId, GenericDefId, LocalTypeParamId, Lookup, TypeParamId,
|
||||
};
|
||||
|
||||
/// Data about a generic parameter (to a function, struct, impl, ...).
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct GenericParam {
|
||||
// FIXME: give generic params proper IDs
|
||||
pub idx: u32,
|
||||
pub struct TypeParamData {
|
||||
pub name: Name,
|
||||
pub default: Option<TypeRef>,
|
||||
}
|
||||
|
@ -25,8 +34,8 @@ pub struct GenericParam {
|
|||
/// Data about the generic parameters of a function, struct, impl, etc.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct GenericParams {
|
||||
pub parent_params: Option<Arc<GenericParams>>,
|
||||
pub params: Vec<GenericParam>,
|
||||
pub types: Arena<LocalTypeParamId, TypeParamData>,
|
||||
// lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>,
|
||||
pub where_predicates: Vec<WherePredicate>,
|
||||
}
|
||||
|
||||
|
@ -40,63 +49,87 @@ pub struct WherePredicate {
|
|||
pub bound: TypeBound,
|
||||
}
|
||||
|
||||
type SourceMap = ArenaMap<LocalTypeParamId, Either<ast::TraitDef, ast::TypeParam>>;
|
||||
|
||||
impl GenericParams {
|
||||
pub(crate) fn generic_params_query(
|
||||
db: &impl DefDatabase,
|
||||
def: GenericDefId,
|
||||
) -> Arc<GenericParams> {
|
||||
let parent_generics = parent_generic_def(db, def).map(|it| db.generic_params(it));
|
||||
Arc::new(GenericParams::new(db, def.into(), parent_generics))
|
||||
let (params, _source_map) = GenericParams::new(db, def.into());
|
||||
Arc::new(params)
|
||||
}
|
||||
|
||||
fn new(
|
||||
db: &impl DefDatabase,
|
||||
def: GenericDefId,
|
||||
parent_params: Option<Arc<GenericParams>>,
|
||||
) -> GenericParams {
|
||||
let mut generics =
|
||||
GenericParams { params: Vec::new(), parent_params, where_predicates: Vec::new() };
|
||||
let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32;
|
||||
fn new(db: &impl DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
|
||||
let mut generics = GenericParams { types: Arena::default(), where_predicates: Vec::new() };
|
||||
let mut sm = ArenaMap::default();
|
||||
// FIXME: add `: Sized` bound for everything except for `Self` in traits
|
||||
match def {
|
||||
GenericDefId::FunctionId(it) => generics.fill(&it.lookup(db).source(db).value, start),
|
||||
GenericDefId::AdtId(AdtId::StructId(it)) => generics.fill(&it.source(db).value, start),
|
||||
GenericDefId::AdtId(AdtId::UnionId(it)) => generics.fill(&it.source(db).value, start),
|
||||
GenericDefId::AdtId(AdtId::EnumId(it)) => generics.fill(&it.source(db).value, start),
|
||||
let file_id = match def {
|
||||
GenericDefId::FunctionId(it) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::StructId(it)) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::UnionId(it)) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::EnumId(it)) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
GenericDefId::TraitId(it) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
|
||||
// traits get the Self type as an implicit first type parameter
|
||||
generics.params.push(GenericParam {
|
||||
idx: start,
|
||||
name: name::SELF_TYPE,
|
||||
default: None,
|
||||
});
|
||||
generics.fill(&it.source(db).value, start + 1);
|
||||
let self_param_id =
|
||||
generics.types.alloc(TypeParamData { name: name![Self], default: None });
|
||||
sm.insert(self_param_id, Either::Left(src.value.clone()));
|
||||
// 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(name::SELF_TYPE.into());
|
||||
generics.fill_bounds(&it.source(db).value, self_param);
|
||||
let self_param = TypeRef::Path(name![Self].into());
|
||||
generics.fill_bounds(&src.value, self_param);
|
||||
|
||||
generics.fill(&mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
GenericDefId::TypeAliasId(it) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
GenericDefId::TypeAliasId(it) => generics.fill(&it.lookup(db).source(db).value, start),
|
||||
// Note that we don't add `Self` here: in `impl`s, `Self` is not a
|
||||
// type-parameter, but rather is a type-alias for impl's target
|
||||
// type, so this is handled by the resolver.
|
||||
GenericDefId::ImplId(it) => generics.fill(&it.source(db).value, start),
|
||||
GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {}
|
||||
GenericDefId::ImplId(it) => {
|
||||
let src = it.lookup(db).source(db);
|
||||
generics.fill(&mut sm, &src.value);
|
||||
src.file_id
|
||||
}
|
||||
// We won't be using this ID anyway
|
||||
GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => FileId(!0).into(),
|
||||
};
|
||||
|
||||
(generics, InFile::new(file_id, sm))
|
||||
}
|
||||
|
||||
generics
|
||||
}
|
||||
|
||||
fn fill(&mut self, node: &impl TypeParamsOwner, start: u32) {
|
||||
fn fill(&mut self, sm: &mut SourceMap, node: &dyn TypeParamsOwner) {
|
||||
if let Some(params) = node.type_param_list() {
|
||||
self.fill_params(params, start)
|
||||
self.fill_params(sm, params)
|
||||
}
|
||||
if let Some(where_clause) = node.where_clause() {
|
||||
self.fill_where_predicates(where_clause);
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_bounds(&mut self, node: &impl ast::TypeBoundsOwner, type_ref: TypeRef) {
|
||||
fn fill_bounds(&mut self, node: &dyn ast::TypeBoundsOwner, type_ref: TypeRef) {
|
||||
for bound in
|
||||
node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds())
|
||||
{
|
||||
|
@ -104,13 +137,14 @@ impl GenericParams {
|
|||
}
|
||||
}
|
||||
|
||||
fn fill_params(&mut self, params: ast::TypeParamList, start: u32) {
|
||||
for (idx, type_param) in params.type_params().enumerate() {
|
||||
fn fill_params(&mut self, sm: &mut SourceMap, params: ast::TypeParamList) {
|
||||
for type_param in params.type_params() {
|
||||
let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
|
||||
// FIXME: Use `Path::from_src`
|
||||
let default = type_param.default_type().map(TypeRef::from_ast);
|
||||
let param = GenericParam { idx: idx as u32 + start, name: name.clone(), default };
|
||||
self.params.push(param);
|
||||
let param = TypeParamData { name: name.clone(), default };
|
||||
let param_id = self.types.alloc(param);
|
||||
sm.insert(param_id, Either::Right(type_param.clone()));
|
||||
|
||||
let type_ref = TypeRef::Path(name.into());
|
||||
self.fill_bounds(&type_param, type_ref);
|
||||
|
@ -139,45 +173,31 @@ impl GenericParams {
|
|||
self.where_predicates.push(WherePredicate { type_ref, bound });
|
||||
}
|
||||
|
||||
pub fn find_by_name(&self, name: &Name) -> Option<&GenericParam> {
|
||||
self.params.iter().find(|p| &p.name == name)
|
||||
}
|
||||
|
||||
pub fn count_parent_params(&self) -> usize {
|
||||
self.parent_params.as_ref().map(|p| p.count_params_including_parent()).unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn count_params_including_parent(&self) -> usize {
|
||||
let parent_count = self.count_parent_params();
|
||||
parent_count + self.params.len()
|
||||
}
|
||||
|
||||
fn for_each_param<'a>(&'a self, f: &mut impl FnMut(&'a GenericParam)) {
|
||||
if let Some(parent) = &self.parent_params {
|
||||
parent.for_each_param(f);
|
||||
}
|
||||
self.params.iter().for_each(f);
|
||||
}
|
||||
|
||||
pub fn params_including_parent(&self) -> Vec<&GenericParam> {
|
||||
let mut vec = Vec::with_capacity(self.count_params_including_parent());
|
||||
self.for_each_param(&mut |p| vec.push(p));
|
||||
vec
|
||||
pub fn find_by_name(&self, name: &Name) -> Option<LocalTypeParamId> {
|
||||
self.types.iter().find_map(|(id, p)| if &p.name == name { Some(id) } else { None })
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_generic_def(db: &impl DefDatabase, def: GenericDefId) -> Option<GenericDefId> {
|
||||
let container = match def {
|
||||
GenericDefId::FunctionId(it) => it.lookup(db).container,
|
||||
GenericDefId::TypeAliasId(it) => it.lookup(db).container,
|
||||
GenericDefId::ConstId(it) => it.lookup(db).container,
|
||||
GenericDefId::EnumVariantId(it) => return Some(it.parent.into()),
|
||||
GenericDefId::AdtId(_) | GenericDefId::TraitId(_) | GenericDefId::ImplId(_) => return None,
|
||||
};
|
||||
impl HasChildSource for GenericDefId {
|
||||
type ChildId = LocalTypeParamId;
|
||||
type Value = Either<ast::TraitDef, ast::TypeParam>;
|
||||
fn child_source(&self, db: &impl DefDatabase) -> InFile<SourceMap> {
|
||||
let (_, sm) = GenericParams::new(db, *self);
|
||||
sm
|
||||
}
|
||||
}
|
||||
|
||||
match container {
|
||||
ContainerId::ImplId(it) => Some(it.into()),
|
||||
ContainerId::TraitId(it) => Some(it.into()),
|
||||
ContainerId::ModuleId(_) => None,
|
||||
impl ChildBySource for GenericDefId {
|
||||
fn child_by_source(&self, db: &impl DefDatabase) -> DynMap {
|
||||
let mut res = DynMap::default();
|
||||
let arena_map = self.child_source(db);
|
||||
let arena_map = arena_map.as_ref();
|
||||
for (local_id, src) in arena_map.value.iter() {
|
||||
let id = TypeParamId { parent: *self, local_id };
|
||||
if let Either::Right(type_param) = src {
|
||||
res[keys::TYPE_PARAM].insert(arena_map.with_value(type_param.clone()), id)
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
|
172
crates/ra_hir_def/src/item_scope.rs
Normal file
172
crates/ra_hir_def/src/item_scope.rs
Normal file
|
@ -0,0 +1,172 @@
|
|||
//! Describes items defined or visible (ie, imported) in a certain scope.
|
||||
//! This is shared between modules and blocks.
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{per_ns::PerNs, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, TraitId};
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct ItemScope {
|
||||
visible: FxHashMap<Name, PerNs>,
|
||||
defs: Vec<ModuleDefId>,
|
||||
impls: Vec<ImplId>,
|
||||
/// Macros visible in current module in legacy textual scope
|
||||
///
|
||||
/// For macros invoked by an unqualified identifier like `bar!()`, `legacy_macros` will be searched in first.
|
||||
/// If it yields no result, then it turns to module scoped `macros`.
|
||||
/// It macros with name qualified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped,
|
||||
/// and only normal scoped `macros` will be searched in.
|
||||
///
|
||||
/// Note that this automatically inherit macros defined textually before the definition of module itself.
|
||||
///
|
||||
/// Module scoped macros will be inserted into `items` instead of here.
|
||||
// FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
|
||||
// be all resolved to the last one defined if shadowing happens.
|
||||
legacy_macros: FxHashMap<Name, MacroDefId>,
|
||||
}
|
||||
|
||||
static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
|
||||
BuiltinType::ALL
|
||||
.iter()
|
||||
.map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into())))
|
||||
.collect()
|
||||
});
|
||||
|
||||
/// Shadow mode for builtin type which can be shadowed by module.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum BuiltinShadowMode {
|
||||
// Prefer Module
|
||||
Module,
|
||||
// Prefer Other Types
|
||||
Other,
|
||||
}
|
||||
|
||||
/// Legacy macros can only be accessed through special methods like `get_legacy_macros`.
|
||||
/// Other methods will only resolve values, types and module scoped macros only.
|
||||
impl ItemScope {
|
||||
pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a {
|
||||
//FIXME: shadowing
|
||||
self.visible.iter().chain(BUILTIN_SCOPE.iter()).map(|(n, def)| (n, *def))
|
||||
}
|
||||
|
||||
pub fn entries_without_primitives<'a>(
|
||||
&'a self,
|
||||
) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a {
|
||||
self.visible.iter().map(|(n, def)| (n, *def))
|
||||
}
|
||||
|
||||
pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
|
||||
self.defs.iter().copied()
|
||||
}
|
||||
|
||||
pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ {
|
||||
self.impls.iter().copied()
|
||||
}
|
||||
|
||||
/// Iterate over all module scoped macros
|
||||
pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
|
||||
self.visible.iter().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_)))
|
||||
}
|
||||
|
||||
/// Iterate over all legacy textual scoped macros visible at the end of the module
|
||||
pub(crate) fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
|
||||
self.legacy_macros.iter().map(|(name, def)| (name, *def))
|
||||
}
|
||||
|
||||
/// Get a name from current module scope, legacy macros are not included
|
||||
pub(crate) fn get(&self, name: &Name, shadow: BuiltinShadowMode) -> PerNs {
|
||||
match shadow {
|
||||
BuiltinShadowMode::Module => self
|
||||
.visible
|
||||
.get(name)
|
||||
.or_else(|| BUILTIN_SCOPE.get(name))
|
||||
.copied()
|
||||
.unwrap_or_else(PerNs::none),
|
||||
BuiltinShadowMode::Other => {
|
||||
let item = self.visible.get(name).copied();
|
||||
if let Some(def) = item {
|
||||
if let Some(ModuleDefId::ModuleId(_)) = def.take_types() {
|
||||
return BUILTIN_SCOPE
|
||||
.get(name)
|
||||
.copied()
|
||||
.or(item)
|
||||
.unwrap_or_else(PerNs::none);
|
||||
}
|
||||
}
|
||||
|
||||
item.or_else(|| BUILTIN_SCOPE.get(name).copied()).unwrap_or_else(PerNs::none)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a {
|
||||
self.visible.values().filter_map(|def| match def.take_types() {
|
||||
Some(ModuleDefId::TraitId(t)) => Some(t),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn define_def(&mut self, def: ModuleDefId) {
|
||||
self.defs.push(def)
|
||||
}
|
||||
|
||||
pub(crate) fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> {
|
||||
self.legacy_macros.get(name).copied()
|
||||
}
|
||||
|
||||
pub(crate) fn define_impl(&mut self, imp: ImplId) {
|
||||
self.impls.push(imp)
|
||||
}
|
||||
|
||||
pub(crate) fn define_legacy_macro(&mut self, name: Name, mac: MacroDefId) {
|
||||
self.legacy_macros.insert(name, mac);
|
||||
}
|
||||
|
||||
pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool {
|
||||
let mut changed = false;
|
||||
let existing = self.visible.entry(name.clone()).or_default();
|
||||
|
||||
if existing.types.is_none() && def.types.is_some() {
|
||||
existing.types = def.types;
|
||||
changed = true;
|
||||
}
|
||||
if existing.values.is_none() && def.values.is_some() {
|
||||
existing.values = def.values;
|
||||
changed = true;
|
||||
}
|
||||
if existing.macros.is_none() && def.macros.is_some() {
|
||||
existing.macros = def.macros;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
changed
|
||||
}
|
||||
|
||||
pub(crate) fn collect_resolutions(&self) -> Vec<(Name, PerNs)> {
|
||||
self.visible.iter().map(|(name, res)| (name.clone(), res.clone())).collect()
|
||||
}
|
||||
|
||||
pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
|
||||
self.legacy_macros.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ModuleDefId> for PerNs {
|
||||
fn from(def: ModuleDefId) -> PerNs {
|
||||
match def {
|
||||
ModuleDefId::ModuleId(_) => PerNs::types(def),
|
||||
ModuleDefId::FunctionId(_) => PerNs::values(def),
|
||||
ModuleDefId::AdtId(adt) => match adt {
|
||||
AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def),
|
||||
AdtId::EnumId(_) => PerNs::types(def),
|
||||
},
|
||||
ModuleDefId::EnumVariantId(_) => PerNs::both(def, def),
|
||||
ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def),
|
||||
ModuleDefId::TraitId(_) => PerNs::types(def),
|
||||
ModuleDefId::TypeAliasId(_) => PerNs::types(def),
|
||||
ModuleDefId::BuiltinType(_) => PerNs::types(def),
|
||||
}
|
||||
}
|
||||
}
|
56
crates/ra_hir_def/src/keys.rs
Normal file
56
crates/ra_hir_def/src/keys.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
//! keys to be used with `DynMap`
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use hir_expand::InFile;
|
||||
use ra_syntax::{ast, AstNode, AstPtr};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{
|
||||
dyn_map::{DynMap, Policy},
|
||||
ConstId, EnumId, EnumVariantId, FunctionId, ImplId, StaticId, StructFieldId, StructId, TraitId,
|
||||
TypeAliasId, TypeParamId, UnionId,
|
||||
};
|
||||
|
||||
pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>;
|
||||
|
||||
pub const FUNCTION: Key<ast::FnDef, FunctionId> = Key::new();
|
||||
pub const CONST: Key<ast::ConstDef, ConstId> = Key::new();
|
||||
pub const STATIC: Key<ast::StaticDef, StaticId> = Key::new();
|
||||
pub const TYPE_ALIAS: Key<ast::TypeAliasDef, TypeAliasId> = Key::new();
|
||||
pub const IMPL: Key<ast::ImplBlock, ImplId> = Key::new();
|
||||
pub const TRAIT: Key<ast::TraitDef, TraitId> = Key::new();
|
||||
pub const STRUCT: Key<ast::StructDef, StructId> = Key::new();
|
||||
pub const UNION: Key<ast::UnionDef, UnionId> = Key::new();
|
||||
pub const ENUM: Key<ast::EnumDef, EnumId> = Key::new();
|
||||
|
||||
pub const ENUM_VARIANT: Key<ast::EnumVariant, EnumVariantId> = Key::new();
|
||||
pub const TUPLE_FIELD: Key<ast::TupleFieldDef, StructFieldId> = Key::new();
|
||||
pub const RECORD_FIELD: Key<ast::RecordFieldDef, StructFieldId> = Key::new();
|
||||
pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new();
|
||||
|
||||
/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
|
||||
/// equal if they point to exactly the same object.
|
||||
///
|
||||
/// In general, we do not guarantee that we have exactly one instance of a
|
||||
/// syntax tree for each file. We probably should add such guarantee, but, for
|
||||
/// the time being, we will use identity-less AstPtr comparison.
|
||||
pub struct AstPtrPolicy<AST, ID> {
|
||||
_phantom: PhantomData<(AST, ID)>,
|
||||
}
|
||||
|
||||
impl<AST: AstNode + 'static, ID: 'static> Policy for AstPtrPolicy<AST, ID> {
|
||||
type K = InFile<AST>;
|
||||
type V = ID;
|
||||
fn insert(map: &mut DynMap, key: InFile<AST>, value: ID) {
|
||||
let key = key.as_ref().map(AstPtr::new);
|
||||
map.map
|
||||
.entry::<FxHashMap<InFile<AstPtr<AST>>, ID>>()
|
||||
.or_insert_with(Default::default)
|
||||
.insert(key, value);
|
||||
}
|
||||
fn get<'a>(map: &'a DynMap, key: &InFile<AST>) -> Option<&'a ID> {
|
||||
let key = key.as_ref().map(AstPtr::new);
|
||||
map.map.get::<FxHashMap<InFile<AstPtr<AST>>, ID>>()?.get(&key)
|
||||
}
|
||||
}
|
|
@ -81,7 +81,7 @@ impl LangItems {
|
|||
// Look for impl targets
|
||||
let def_map = db.crate_def_map(module.krate);
|
||||
let module_data = &def_map[module.local_id];
|
||||
for &impl_block in module_data.impls.iter() {
|
||||
for impl_block in module_data.scope.impls() {
|
||||
self.collect_lang_item(db, impl_block, LangItemTarget::ImplBlockId)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,10 @@ pub mod type_ref;
|
|||
pub mod builtin_type;
|
||||
pub mod diagnostics;
|
||||
pub mod per_ns;
|
||||
pub mod item_scope;
|
||||
|
||||
pub mod dyn_map;
|
||||
pub mod keys;
|
||||
|
||||
pub mod adt;
|
||||
pub mod data;
|
||||
|
@ -29,23 +33,23 @@ pub mod resolver;
|
|||
mod trace;
|
||||
pub mod nameres;
|
||||
|
||||
pub mod src;
|
||||
pub mod child_by_source;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_db;
|
||||
#[cfg(test)]
|
||||
mod marks;
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::hash::Hash;
|
||||
|
||||
use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId, MacroDefId, Source};
|
||||
use ra_arena::{impl_arena_id, map::ArenaMap, RawId};
|
||||
use hir_expand::{ast_id_map::FileAstId, AstId, HirFileId, InFile, MacroDefId};
|
||||
use ra_arena::{impl_arena_id, RawId};
|
||||
use ra_db::{impl_intern_key, salsa, CrateId};
|
||||
use ra_syntax::{ast, AstNode};
|
||||
|
||||
use crate::{builtin_type::BuiltinType, db::InternDatabase};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct LocalImportId(RawId);
|
||||
impl_arena_id!(LocalImportId);
|
||||
use crate::body::Expander;
|
||||
use crate::builtin_type::BuiltinType;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ModuleId {
|
||||
|
@ -59,122 +63,57 @@ pub struct ModuleId {
|
|||
pub struct LocalModuleId(RawId);
|
||||
impl_arena_id!(LocalModuleId);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ItemLoc<N: AstNode> {
|
||||
pub(crate) module: ModuleId,
|
||||
ast_id: AstId<N>,
|
||||
pub container: ContainerId,
|
||||
pub 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
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AssocItemLoc<N: AstNode> {
|
||||
pub container: AssocContainerId,
|
||||
pub ast_id: AstId<N>,
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
macro_rules! impl_intern {
|
||||
($id:ident, $loc:ident, $intern:ident, $lookup:ident) => {
|
||||
impl_intern_key!($id);
|
||||
|
||||
impl Intern for $loc {
|
||||
type ID = $id;
|
||||
fn intern(self, db: &impl db::DefDatabase) -> $id {
|
||||
db.$intern(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> Clone for ItemLoc<N> {
|
||||
fn clone(&self) -> ItemLoc<N> {
|
||||
ItemLoc { module: self.module, ast_id: self.ast_id }
|
||||
impl Lookup for $id {
|
||||
type Data = $loc;
|
||||
fn lookup(&self, db: &impl db::DefDatabase) -> $loc {
|
||||
db.$lookup(*self)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 }
|
||||
}
|
||||
}
|
||||
|
||||
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_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 value = loc.ast_id.to_node(db);
|
||||
Source { file_id: loc.ast_id.file_id(), value }
|
||||
}
|
||||
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);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FunctionLoc {
|
||||
pub container: ContainerId,
|
||||
pub ast_id: AstId<ast::FnDef>,
|
||||
}
|
||||
|
||||
impl Intern for FunctionLoc {
|
||||
type ID = FunctionId;
|
||||
fn intern(self, db: &impl db::DefDatabase) -> FunctionId {
|
||||
db.intern_function(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Lookup for FunctionId {
|
||||
type Data = FunctionLoc;
|
||||
fn lookup(&self, db: &impl db::DefDatabase) -> FunctionLoc {
|
||||
db.lookup_intern_function(*self)
|
||||
}
|
||||
}
|
||||
type FunctionLoc = AssocItemLoc<ast::FnDef>;
|
||||
impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
type StructLoc = ItemLoc<ast::StructDef>;
|
||||
impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct UnionId(salsa::InternId);
|
||||
impl_intern_key!(UnionId);
|
||||
impl AstItemDef<ast::UnionDef> for UnionId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::UnionDef>) -> Self {
|
||||
db.intern_union(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::UnionDef> {
|
||||
db.lookup_intern_union(self)
|
||||
}
|
||||
}
|
||||
pub type UnionLoc = ItemLoc<ast::UnionDef>;
|
||||
impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
pub type EnumLoc = ItemLoc<ast::EnumDef>;
|
||||
impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
|
||||
|
||||
// FIXME: rename to `VariantId`, only enums can ave variants
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -199,99 +138,39 @@ impl_arena_id!(LocalStructFieldId);
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ConstId(salsa::InternId);
|
||||
impl_intern_key!(ConstId);
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ConstLoc {
|
||||
pub container: ContainerId,
|
||||
pub ast_id: AstId<ast::ConstDef>,
|
||||
}
|
||||
|
||||
impl Intern for ConstLoc {
|
||||
type ID = ConstId;
|
||||
fn intern(self, db: &impl db::DefDatabase) -> ConstId {
|
||||
db.intern_const(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Lookup for ConstId {
|
||||
type Data = ConstLoc;
|
||||
fn lookup(&self, db: &impl db::DefDatabase) -> ConstLoc {
|
||||
db.lookup_intern_const(*self)
|
||||
}
|
||||
}
|
||||
type ConstLoc = AssocItemLoc<ast::ConstDef>;
|
||||
impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct StaticId(salsa::InternId);
|
||||
impl_intern_key!(StaticId);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct StaticLoc {
|
||||
pub container: ModuleId,
|
||||
pub ast_id: AstId<ast::StaticDef>,
|
||||
}
|
||||
|
||||
impl Intern for StaticLoc {
|
||||
type ID = StaticId;
|
||||
fn intern(self, db: &impl db::DefDatabase) -> StaticId {
|
||||
db.intern_static(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Lookup for StaticId {
|
||||
type Data = StaticLoc;
|
||||
fn lookup(&self, db: &impl db::DefDatabase) -> StaticLoc {
|
||||
db.lookup_intern_static(*self)
|
||||
}
|
||||
}
|
||||
pub type StaticLoc = ItemLoc<ast::StaticDef>;
|
||||
impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
pub type TraitLoc = ItemLoc<ast::TraitDef>;
|
||||
impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TypeAliasId(salsa::InternId);
|
||||
impl_intern_key!(TypeAliasId);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TypeAliasLoc {
|
||||
pub container: ContainerId,
|
||||
pub ast_id: AstId<ast::TypeAliasDef>,
|
||||
}
|
||||
|
||||
impl Intern for TypeAliasLoc {
|
||||
type ID = TypeAliasId;
|
||||
fn intern(self, db: &impl db::DefDatabase) -> TypeAliasId {
|
||||
db.intern_type_alias(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Lookup for TypeAliasId {
|
||||
type Data = TypeAliasLoc;
|
||||
fn lookup(&self, db: &impl db::DefDatabase) -> TypeAliasLoc {
|
||||
db.lookup_intern_type_alias(*self)
|
||||
}
|
||||
}
|
||||
type TypeAliasLoc = AssocItemLoc<ast::TypeAliasDef>;
|
||||
impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ImplId(salsa::InternId);
|
||||
impl_intern_key!(ImplId);
|
||||
impl AstItemDef<ast::ImplBlock> for ImplId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::ImplBlock>) -> Self {
|
||||
db.intern_impl(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::ImplBlock> {
|
||||
db.lookup_intern_impl(self)
|
||||
}
|
||||
type ImplLoc = ItemLoc<ast::ImplBlock>;
|
||||
impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TypeParamId {
|
||||
pub parent: GenericDefId,
|
||||
pub local_id: LocalTypeParamId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct LocalTypeParamId(RawId);
|
||||
impl_arena_id!(LocalTypeParamId);
|
||||
|
||||
macro_rules! impl_froms {
|
||||
($e:ident: $($v:ident $(($($sv:ident),*))?),*) => {
|
||||
$(
|
||||
|
@ -314,9 +193,16 @@ macro_rules! impl_froms {
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ContainerId {
|
||||
ModuleId(ModuleId),
|
||||
DefWithBodyId(DefWithBodyId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum AssocContainerId {
|
||||
ContainerId(ContainerId),
|
||||
ImplId(ImplId),
|
||||
TraitId(TraitId),
|
||||
}
|
||||
impl_froms!(AssocContainerId: ContainerId);
|
||||
|
||||
/// A Data Type
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
|
@ -459,43 +345,39 @@ pub trait HasModule {
|
|||
fn module(&self, db: &impl db::DefDatabase) -> ModuleId;
|
||||
}
|
||||
|
||||
impl HasModule for FunctionLoc {
|
||||
impl HasModule for ContainerId {
|
||||
fn module(&self, db: &impl db::DefDatabase) -> ModuleId {
|
||||
match self.container {
|
||||
match *self {
|
||||
ContainerId::ModuleId(it) => it,
|
||||
ContainerId::ImplId(it) => it.module(db),
|
||||
ContainerId::TraitId(it) => it.module(db),
|
||||
ContainerId::DefWithBodyId(it) => it.module(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for TypeAliasLoc {
|
||||
impl HasModule for AssocContainerId {
|
||||
fn module(&self, db: &impl db::DefDatabase) -> ModuleId {
|
||||
match self.container {
|
||||
ContainerId::ModuleId(it) => it,
|
||||
ContainerId::ImplId(it) => it.module(db),
|
||||
ContainerId::TraitId(it) => it.module(db),
|
||||
match *self {
|
||||
AssocContainerId::ContainerId(it) => it.module(db),
|
||||
AssocContainerId::ImplId(it) => it.lookup(db).container.module(db),
|
||||
AssocContainerId::TraitId(it) => it.lookup(db).container.module(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for ConstLoc {
|
||||
impl<N: AstNode> HasModule for AssocItemLoc<N> {
|
||||
fn module(&self, db: &impl db::DefDatabase) -> ModuleId {
|
||||
match self.container {
|
||||
ContainerId::ModuleId(it) => it,
|
||||
ContainerId::ImplId(it) => it.module(db),
|
||||
ContainerId::TraitId(it) => it.module(db),
|
||||
}
|
||||
self.container.module(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for AdtId {
|
||||
fn module(&self, db: &impl db::DefDatabase) -> ModuleId {
|
||||
match self {
|
||||
AdtId::StructId(it) => it.module(db),
|
||||
AdtId::UnionId(it) => it.module(db),
|
||||
AdtId::EnumId(it) => it.module(db),
|
||||
AdtId::StructId(it) => it.lookup(db).container,
|
||||
AdtId::UnionId(it) => it.lookup(db).container,
|
||||
AdtId::EnumId(it) => it.lookup(db).container,
|
||||
}
|
||||
.module(db)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -509,58 +391,22 @@ impl HasModule for DefWithBodyId {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasModule for GenericDefId {
|
||||
fn module(&self, db: &impl db::DefDatabase) -> ModuleId {
|
||||
match self {
|
||||
GenericDefId::FunctionId(it) => it.lookup(db).module(db),
|
||||
GenericDefId::AdtId(it) => it.module(db),
|
||||
GenericDefId::TraitId(it) => it.lookup(db).container.module(db),
|
||||
GenericDefId::TypeAliasId(it) => it.lookup(db).module(db),
|
||||
GenericDefId::ImplId(it) => it.lookup(db).container.module(db),
|
||||
GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db),
|
||||
GenericDefId::ConstId(it) => it.lookup(db).module(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for StaticLoc {
|
||||
fn module(&self, _db: &impl db::DefDatabase) -> ModuleId {
|
||||
self.container
|
||||
fn module(&self, db: &impl db::DefDatabase) -> ModuleId {
|
||||
self.container.module(db)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasSource {
|
||||
type Value;
|
||||
fn source(&self, db: &impl db::DefDatabase) -> Source<Self::Value>;
|
||||
}
|
||||
|
||||
impl HasSource for FunctionLoc {
|
||||
type Value = ast::FnDef;
|
||||
|
||||
fn source(&self, db: &impl db::DefDatabase) -> Source<ast::FnDef> {
|
||||
let node = self.ast_id.to_node(db);
|
||||
Source::new(self.ast_id.file_id(), node)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for TypeAliasLoc {
|
||||
type Value = ast::TypeAliasDef;
|
||||
|
||||
fn source(&self, db: &impl db::DefDatabase) -> Source<ast::TypeAliasDef> {
|
||||
let node = self.ast_id.to_node(db);
|
||||
Source::new(self.ast_id.file_id(), node)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for ConstLoc {
|
||||
type Value = ast::ConstDef;
|
||||
|
||||
fn source(&self, db: &impl db::DefDatabase) -> Source<ast::ConstDef> {
|
||||
let node = self.ast_id.to_node(db);
|
||||
Source::new(self.ast_id.file_id(), node)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for StaticLoc {
|
||||
type Value = ast::StaticDef;
|
||||
|
||||
fn source(&self, db: &impl db::DefDatabase) -> Source<ast::StaticDef> {
|
||||
let node = self.ast_id.to_node(db);
|
||||
Source::new(self.ast_id.file_id(), node)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasChildSource {
|
||||
type ChildId;
|
||||
type Value;
|
||||
fn child_source(
|
||||
&self,
|
||||
db: &impl db::DefDatabase,
|
||||
) -> Source<ArenaMap<Self::ChildId, Self::Value>>;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ test_utils::marks!(
|
|||
name_res_works_for_broken_modules
|
||||
can_import_enum_variant
|
||||
glob_enum
|
||||
glob_enum_group
|
||||
glob_across_crates
|
||||
std_prelude
|
||||
macro_rules_from_other_crates_are_visible_with_macro_use
|
||||
|
|
|
@ -57,24 +57,23 @@ mod tests;
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::{
|
||||
ast_id_map::FileAstId, diagnostics::DiagnosticSink, either::Either, name::Name, MacroDefId,
|
||||
Source,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile};
|
||||
use ra_arena::Arena;
|
||||
use ra_db::{CrateId, Edition, FileId};
|
||||
use ra_db::{CrateId, Edition, FileId, FilePosition};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::ast;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode},
|
||||
SyntaxNode,
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{
|
||||
builtin_type::BuiltinType,
|
||||
db::DefDatabase,
|
||||
item_scope::{BuiltinShadowMode, ItemScope},
|
||||
nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode},
|
||||
path::Path,
|
||||
path::ModPath,
|
||||
per_ns::PerNs,
|
||||
AstId, FunctionId, ImplId, LocalImportId, LocalModuleId, ModuleDefId, ModuleId, TraitId,
|
||||
AstId, LocalModuleId, ModuleDefId, ModuleId,
|
||||
};
|
||||
|
||||
/// Contains all top-level defs from a macro-expanded crate
|
||||
|
@ -100,106 +99,76 @@ impl std::ops::Index<LocalModuleId> for CrateDefMap {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub enum ModuleOrigin {
|
||||
CrateRoot {
|
||||
definition: FileId,
|
||||
},
|
||||
/// Note that non-inline modules, by definition, live inside non-macro file.
|
||||
File {
|
||||
declaration: AstId<ast::Module>,
|
||||
definition: FileId,
|
||||
},
|
||||
Inline {
|
||||
definition: AstId<ast::Module>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for ModuleOrigin {
|
||||
fn default() -> Self {
|
||||
ModuleOrigin::CrateRoot { definition: FileId(0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleOrigin {
|
||||
pub(crate) fn not_sure_file(file: Option<FileId>, declaration: AstId<ast::Module>) -> Self {
|
||||
match file {
|
||||
None => ModuleOrigin::Inline { definition: declaration },
|
||||
Some(definition) => ModuleOrigin::File { declaration, definition },
|
||||
}
|
||||
}
|
||||
|
||||
fn declaration(&self) -> Option<AstId<ast::Module>> {
|
||||
match self {
|
||||
ModuleOrigin::File { declaration: module, .. }
|
||||
| ModuleOrigin::Inline { definition: module, .. } => Some(*module),
|
||||
ModuleOrigin::CrateRoot { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_id(&self) -> Option<FileId> {
|
||||
match self {
|
||||
ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
|
||||
Some(*definition)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a node which defines this module.
|
||||
/// That is, a file or a `mod foo {}` with items.
|
||||
fn definition_source(&self, db: &impl DefDatabase) -> InFile<ModuleSource> {
|
||||
match self {
|
||||
ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
|
||||
let file_id = *definition;
|
||||
let sf = db.parse(file_id).tree();
|
||||
return InFile::new(file_id.into(), ModuleSource::SourceFile(sf));
|
||||
}
|
||||
ModuleOrigin::Inline { definition } => {
|
||||
InFile::new(definition.file_id, ModuleSource::Module(definition.to_node(db)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
pub struct ModuleData {
|
||||
pub parent: Option<LocalModuleId>,
|
||||
pub children: FxHashMap<Name, LocalModuleId>,
|
||||
pub scope: ModuleScope,
|
||||
pub scope: ItemScope,
|
||||
|
||||
// FIXME: these can't be both null, we need a three-state enum here.
|
||||
/// None for root
|
||||
pub declaration: Option<AstId<ast::Module>>,
|
||||
/// None for inline modules.
|
||||
///
|
||||
/// Note that non-inline modules, by definition, live inside non-macro file.
|
||||
pub definition: Option<FileId>,
|
||||
|
||||
pub impls: Vec<ImplId>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct Declarations {
|
||||
fns: FxHashMap<FileAstId<ast::FnDef>, FunctionId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct ModuleScope {
|
||||
items: FxHashMap<Name, Resolution>,
|
||||
/// Macros visable in current module in legacy textual scope
|
||||
///
|
||||
/// For macros invoked by an unquatified identifier like `bar!()`, `legacy_macros` will be searched in first.
|
||||
/// If it yields no result, then it turns to module scoped `macros`.
|
||||
/// It macros with name quatified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped,
|
||||
/// and only normal scoped `macros` will be searched in.
|
||||
///
|
||||
/// Note that this automatically inherit macros defined textually before the definition of module itself.
|
||||
///
|
||||
/// Module scoped macros will be inserted into `items` instead of here.
|
||||
// FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
|
||||
// be all resolved to the last one defined if shadowing happens.
|
||||
legacy_macros: FxHashMap<Name, MacroDefId>,
|
||||
}
|
||||
|
||||
static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| {
|
||||
BuiltinType::ALL
|
||||
.iter()
|
||||
.map(|(name, ty)| {
|
||||
(name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None })
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
|
||||
/// Legacy macros can only be accessed through special methods like `get_legacy_macros`.
|
||||
/// Other methods will only resolve values, types and module scoped macros only.
|
||||
impl ModuleScope {
|
||||
pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
|
||||
//FIXME: shadowing
|
||||
self.items.iter().chain(BUILTIN_SCOPE.iter())
|
||||
}
|
||||
|
||||
pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
|
||||
self.entries()
|
||||
.filter_map(|(_name, res)| if res.import.is_none() { Some(res.def) } else { None })
|
||||
.flat_map(|per_ns| {
|
||||
per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter())
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterate over all module scoped macros
|
||||
pub fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
|
||||
self.items
|
||||
.iter()
|
||||
.filter_map(|(name, res)| res.def.take_macros().map(|macro_| (name, macro_)))
|
||||
}
|
||||
|
||||
/// Iterate over all legacy textual scoped macros visable at the end of the module
|
||||
pub fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
|
||||
self.legacy_macros.iter().map(|(name, def)| (name, *def))
|
||||
}
|
||||
|
||||
/// Get a name from current module scope, legacy macros are not included
|
||||
pub fn get(&self, name: &Name) -> Option<&Resolution> {
|
||||
self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name))
|
||||
}
|
||||
|
||||
pub fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a {
|
||||
self.items.values().filter_map(|r| match r.def.take_types() {
|
||||
Some(ModuleDefId::TraitId(t)) => Some(t),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> {
|
||||
self.legacy_macros.get(name).copied()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct Resolution {
|
||||
/// None for unresolved
|
||||
pub def: PerNs,
|
||||
/// ident by which this is imported into local scope.
|
||||
pub import: Option<LocalImportId>,
|
||||
/// Where does this module come from?
|
||||
pub origin: ModuleOrigin,
|
||||
}
|
||||
|
||||
impl CrateDefMap {
|
||||
|
@ -241,7 +210,7 @@ impl CrateDefMap {
|
|||
pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ {
|
||||
self.modules
|
||||
.iter()
|
||||
.filter(move |(_id, data)| data.definition == Some(file_id))
|
||||
.filter(move |(_id, data)| data.origin.file_id() == Some(file_id))
|
||||
.map(|(id, _data)| id)
|
||||
}
|
||||
|
||||
|
@ -249,33 +218,62 @@ impl CrateDefMap {
|
|||
&self,
|
||||
db: &impl DefDatabase,
|
||||
original_module: LocalModuleId,
|
||||
path: &Path,
|
||||
path: &ModPath,
|
||||
shadow: BuiltinShadowMode,
|
||||
) -> (PerNs, Option<usize>) {
|
||||
let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
|
||||
let res =
|
||||
self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow);
|
||||
(res.resolved_def, res.segment_index)
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleData {
|
||||
/// 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,
|
||||
) -> Source<Either<ast::SourceFile, ast::Module>> {
|
||||
if let Some(file_id) = self.definition {
|
||||
let sf = db.parse(file_id).tree();
|
||||
return Source::new(file_id.into(), Either::A(sf));
|
||||
}
|
||||
let decl = self.declaration.unwrap();
|
||||
Source::new(decl.file_id(), Either::B(decl.to_node(db)))
|
||||
pub fn definition_source(&self, db: &impl DefDatabase) -> InFile<ModuleSource> {
|
||||
self.origin.definition_source(db)
|
||||
}
|
||||
|
||||
/// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root.
|
||||
pub fn declaration_source(&self, db: &impl DefDatabase) -> Option<Source<ast::Module>> {
|
||||
let decl = self.declaration?;
|
||||
/// `None` for the crate root or block.
|
||||
pub fn declaration_source(&self, db: &impl DefDatabase) -> Option<InFile<ast::Module>> {
|
||||
let decl = self.origin.declaration()?;
|
||||
let value = decl.to_node(db);
|
||||
Some(Source { file_id: decl.file_id(), value })
|
||||
Some(InFile { file_id: decl.file_id, value })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ModuleSource {
|
||||
SourceFile(ast::SourceFile),
|
||||
Module(ast::Module),
|
||||
}
|
||||
|
||||
impl ModuleSource {
|
||||
// FIXME: this methods do not belong here
|
||||
pub fn from_position(db: &impl DefDatabase, position: 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 DefDatabase, child: InFile<&SyntaxNode>) -> ModuleSource {
|
||||
if let Some(m) =
|
||||
child.value.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi())
|
||||
{
|
||||
ModuleSource::Module(m)
|
||||
} else {
|
||||
let file_id = child.file_id.original_file(db);
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,7 +307,7 @@ mod diagnostics {
|
|||
}
|
||||
let decl = declaration.to_node(db);
|
||||
sink.push(UnresolvedModule {
|
||||
file: declaration.file_id(),
|
||||
file: declaration.file_id,
|
||||
decl: AstPtr::new(&decl),
|
||||
candidate: candidate.clone(),
|
||||
})
|
||||
|
|
|
@ -4,14 +4,15 @@
|
|||
//! resolves imports and expands macros.
|
||||
|
||||
use hir_expand::{
|
||||
builtin_derive::find_builtin_derive,
|
||||
builtin_macro::find_builtin_macro,
|
||||
name::{self, AsName, Name},
|
||||
HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind,
|
||||
name::{name, AsName, Name},
|
||||
HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
|
||||
};
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_db::{CrateId, FileId};
|
||||
use ra_syntax::ast;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustc_hash::FxHashMap;
|
||||
use test_utils::tested_by;
|
||||
|
||||
use crate::{
|
||||
|
@ -19,13 +20,12 @@ use crate::{
|
|||
db::DefDatabase,
|
||||
nameres::{
|
||||
diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
|
||||
raw, CrateDefMap, ModuleData, Resolution, ResolveMode,
|
||||
raw, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
|
||||
},
|
||||
path::{Path, PathKind},
|
||||
path::{ModPath, PathKind},
|
||||
per_ns::PerNs,
|
||||
AdtId, AstId, AstItemDef, ConstLoc, ContainerId, EnumId, EnumVariantId, FunctionLoc, ImplId,
|
||||
Intern, LocalImportId, LocalModuleId, LocationCtx, ModuleDefId, ModuleId, StaticLoc, StructId,
|
||||
TraitId, TypeAliasLoc, UnionId,
|
||||
AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern,
|
||||
LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
|
||||
};
|
||||
|
||||
pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap {
|
||||
|
@ -57,68 +57,63 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C
|
|||
def_map,
|
||||
glob_imports: FxHashMap::default(),
|
||||
unresolved_imports: Vec::new(),
|
||||
resolved_imports: Vec::new(),
|
||||
|
||||
unexpanded_macros: Vec::new(),
|
||||
unexpanded_attribute_macros: Vec::new(),
|
||||
mod_dirs: FxHashMap::default(),
|
||||
macro_stack_monitor: MacroStackMonitor::default(),
|
||||
poison_macros: FxHashSet::default(),
|
||||
cfg_options,
|
||||
};
|
||||
collector.collect();
|
||||
collector.finish()
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MacroStackMonitor {
|
||||
counts: FxHashMap<MacroDefId, u32>,
|
||||
|
||||
/// Mainly use for test
|
||||
validator: Option<Box<dyn Fn(u32) -> bool>>,
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
enum PartialResolvedImport {
|
||||
/// None of any namespaces is resolved
|
||||
Unresolved,
|
||||
/// One of namespaces is resolved
|
||||
Indeterminate(PerNs),
|
||||
/// All namespaces are resolved, OR it is came from other crate
|
||||
Resolved(PerNs),
|
||||
}
|
||||
|
||||
impl MacroStackMonitor {
|
||||
fn increase(&mut self, macro_def_id: MacroDefId) {
|
||||
*self.counts.entry(macro_def_id).or_default() += 1;
|
||||
impl PartialResolvedImport {
|
||||
fn namespaces(&self) -> PerNs {
|
||||
match self {
|
||||
PartialResolvedImport::Unresolved => PerNs::none(),
|
||||
PartialResolvedImport::Indeterminate(ns) => *ns,
|
||||
PartialResolvedImport::Resolved(ns) => *ns,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decrease(&mut self, macro_def_id: MacroDefId) {
|
||||
*self.counts.entry(macro_def_id).or_default() -= 1;
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct ImportDirective {
|
||||
module_id: LocalModuleId,
|
||||
import_id: raw::Import,
|
||||
import: raw::ImportData,
|
||||
status: PartialResolvedImport,
|
||||
}
|
||||
|
||||
fn is_poison(&self, macro_def_id: MacroDefId) -> bool {
|
||||
let cur = *self.counts.get(¯o_def_id).unwrap_or(&0);
|
||||
|
||||
if let Some(validator) = &self.validator {
|
||||
validator(cur)
|
||||
} else {
|
||||
cur > 100
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct MacroDirective {
|
||||
module_id: LocalModuleId,
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
path: ModPath,
|
||||
legacy: Option<MacroCallId>,
|
||||
}
|
||||
|
||||
/// Walks the tree of module recursively
|
||||
struct DefCollector<'a, DB> {
|
||||
db: &'a DB,
|
||||
def_map: CrateDefMap,
|
||||
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, LocalImportId)>>,
|
||||
unresolved_imports: Vec<(LocalModuleId, LocalImportId, raw::ImportData)>,
|
||||
unexpanded_macros: Vec<(LocalModuleId, AstId<ast::MacroCall>, Path)>,
|
||||
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, raw::Import)>>,
|
||||
unresolved_imports: Vec<ImportDirective>,
|
||||
resolved_imports: Vec<ImportDirective>,
|
||||
unexpanded_macros: Vec<MacroDirective>,
|
||||
unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, ModPath)>,
|
||||
mod_dirs: FxHashMap<LocalModuleId, ModDir>,
|
||||
|
||||
/// Some macro use `$tt:tt which mean we have to handle the macro perfectly
|
||||
/// To prevent stack overflow, we add a deep counter here for prevent that.
|
||||
macro_stack_monitor: MacroStackMonitor,
|
||||
/// Some macros are not well-behavior, which leads to infinite loop
|
||||
/// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } }
|
||||
/// We mark it down and skip it in collector
|
||||
///
|
||||
/// FIXME:
|
||||
/// Right now it only handle a poison macro in a single crate,
|
||||
/// such that if other crate try to call that macro,
|
||||
/// the whole process will do again until it became poisoned in that crate.
|
||||
/// We should handle this macro set globally
|
||||
/// However, do we want to put it as a global variable?
|
||||
poison_macros: FxHashSet<MacroDefId>,
|
||||
|
||||
cfg_options: &'a CfgOptions,
|
||||
}
|
||||
|
||||
|
@ -131,7 +126,7 @@ where
|
|||
let file_id = crate_graph.crate_root(self.def_map.krate);
|
||||
let raw_items = self.db.raw_items(file_id.into());
|
||||
let module_id = self.def_map.root;
|
||||
self.def_map.modules[module_id].definition = Some(file_id);
|
||||
self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
|
||||
ModCollector {
|
||||
def_collector: &mut *self,
|
||||
module_id,
|
||||
|
@ -145,9 +140,11 @@ where
|
|||
let mut i = 0;
|
||||
loop {
|
||||
self.db.check_canceled();
|
||||
match (self.resolve_imports(), self.resolve_macros()) {
|
||||
(ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break,
|
||||
_ => i += 1,
|
||||
self.resolve_imports();
|
||||
|
||||
match self.resolve_macros() {
|
||||
ReachedFixedPoint::Yes => break,
|
||||
ReachedFixedPoint::No => i += 1,
|
||||
}
|
||||
if i == 1000 {
|
||||
log::error!("name resolution is stuck");
|
||||
|
@ -155,10 +152,26 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// Resolve all indeterminate resolved imports again
|
||||
// As some of the macros will expand newly import shadowing partial resolved imports
|
||||
// FIXME: We maybe could skip this, if we handle the Indetermine imports in `resolve_imports`
|
||||
// correctly
|
||||
let partial_resolved = self.resolved_imports.iter().filter_map(|directive| {
|
||||
if let PartialResolvedImport::Indeterminate(_) = directive.status {
|
||||
let mut directive = directive.clone();
|
||||
directive.status = PartialResolvedImport::Unresolved;
|
||||
Some(directive)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
self.unresolved_imports.extend(partial_resolved);
|
||||
self.resolve_imports();
|
||||
|
||||
let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
|
||||
// show unresolved imports in completion, etc
|
||||
for (module_id, import, import_data) in unresolved_imports {
|
||||
self.record_resolved_import(module_id, PerNs::none(), import, &import_data)
|
||||
for directive in unresolved_imports {
|
||||
self.record_resolved_import(&directive)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,24 +214,20 @@ where
|
|||
// In Rust, `#[macro_export]` macros are unconditionally visible at the
|
||||
// crate root, even if the parent modules is **not** visible.
|
||||
if export {
|
||||
self.update(
|
||||
self.def_map.root,
|
||||
None,
|
||||
&[(name, Resolution { def: PerNs::macros(macro_), import: None })],
|
||||
);
|
||||
self.update(self.def_map.root, &[(name, PerNs::macros(macro_))]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Define a legacy textual scoped macro in module
|
||||
///
|
||||
/// We use a map `legacy_macros` to store all legacy textual scoped macros visable per module.
|
||||
/// We use a map `legacy_macros` to store all legacy textual scoped macros visible per module.
|
||||
/// It will clone all macros from parent legacy scope, whose definition is prior to
|
||||
/// the definition of current module.
|
||||
/// And also, `macro_use` on a module will import all legacy macros visable inside to
|
||||
/// And also, `macro_use` on a module will import all legacy macros visible inside to
|
||||
/// current legacy scope, with possible shadowing.
|
||||
fn define_legacy_macro(&mut self, module_id: LocalModuleId, name: Name, macro_: MacroDefId) {
|
||||
fn define_legacy_macro(&mut self, module_id: LocalModuleId, name: Name, mac: MacroDefId) {
|
||||
// Always shadowing
|
||||
self.def_map.modules[module_id].scope.legacy_macros.insert(name, macro_);
|
||||
self.def_map.modules[module_id].scope.define_legacy_macro(name, mac);
|
||||
}
|
||||
|
||||
/// Import macros from `#[macro_use] extern crate`.
|
||||
|
@ -259,31 +268,43 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_imports(&mut self) -> ReachedFixedPoint {
|
||||
let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
|
||||
let mut resolved = Vec::new();
|
||||
imports.retain(|(module_id, import, import_data)| {
|
||||
let (def, fp) = self.resolve_import(*module_id, import_data);
|
||||
if fp == ReachedFixedPoint::Yes {
|
||||
resolved.push((*module_id, def, *import, import_data.clone()))
|
||||
/// Import resolution
|
||||
///
|
||||
/// This is a fix point algorithm. We resolve imports until no forward
|
||||
/// progress in resolving imports is made
|
||||
fn resolve_imports(&mut self) {
|
||||
let mut n_previous_unresolved = self.unresolved_imports.len() + 1;
|
||||
|
||||
while self.unresolved_imports.len() < n_previous_unresolved {
|
||||
n_previous_unresolved = self.unresolved_imports.len();
|
||||
let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
|
||||
for mut directive in imports {
|
||||
directive.status = self.resolve_import(directive.module_id, &directive.import);
|
||||
|
||||
match directive.status {
|
||||
PartialResolvedImport::Indeterminate(_) => {
|
||||
self.record_resolved_import(&directive);
|
||||
// FIXME: For avoid performance regression,
|
||||
// we consider an imported resolved if it is indeterminate (i.e not all namespace resolved)
|
||||
self.resolved_imports.push(directive)
|
||||
}
|
||||
PartialResolvedImport::Resolved(_) => {
|
||||
self.record_resolved_import(&directive);
|
||||
self.resolved_imports.push(directive)
|
||||
}
|
||||
PartialResolvedImport::Unresolved => {
|
||||
self.unresolved_imports.push(directive);
|
||||
}
|
||||
}
|
||||
}
|
||||
fp == ReachedFixedPoint::No
|
||||
});
|
||||
self.unresolved_imports = imports;
|
||||
// Resolves imports, filling-in module scopes
|
||||
let result =
|
||||
if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No };
|
||||
for (module_id, def, import, import_data) in resolved {
|
||||
self.record_resolved_import(module_id, def, import, &import_data)
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn resolve_import(
|
||||
&self,
|
||||
module_id: LocalModuleId,
|
||||
import: &raw::ImportData,
|
||||
) -> (PerNs, ReachedFixedPoint) {
|
||||
) -> PartialResolvedImport {
|
||||
log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
|
||||
if import.is_extern_crate {
|
||||
let res = self.def_map.resolve_name_in_extern_prelude(
|
||||
|
@ -292,26 +313,45 @@ where
|
|||
.as_ident()
|
||||
.expect("extern crate should have been desugared to one-element path"),
|
||||
);
|
||||
(res, ReachedFixedPoint::Yes)
|
||||
PartialResolvedImport::Resolved(res)
|
||||
} else {
|
||||
let res = self.def_map.resolve_path_fp_with_macro(
|
||||
self.db,
|
||||
ResolveMode::Import,
|
||||
module_id,
|
||||
&import.path,
|
||||
BuiltinShadowMode::Module,
|
||||
);
|
||||
|
||||
(res.resolved_def, res.reached_fixedpoint)
|
||||
let def = res.resolved_def;
|
||||
if res.reached_fixedpoint == ReachedFixedPoint::No {
|
||||
return PartialResolvedImport::Unresolved;
|
||||
}
|
||||
|
||||
if let Some(krate) = res.krate {
|
||||
if krate != self.def_map.krate {
|
||||
return PartialResolvedImport::Resolved(def);
|
||||
}
|
||||
}
|
||||
|
||||
fn record_resolved_import(
|
||||
&mut self,
|
||||
module_id: LocalModuleId,
|
||||
def: PerNs,
|
||||
import_id: LocalImportId,
|
||||
import: &raw::ImportData,
|
||||
) {
|
||||
// Check whether all namespace is resolved
|
||||
if def.take_types().is_some()
|
||||
&& def.take_values().is_some()
|
||||
&& def.take_macros().is_some()
|
||||
{
|
||||
PartialResolvedImport::Resolved(def)
|
||||
} else {
|
||||
PartialResolvedImport::Indeterminate(def)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn record_resolved_import(&mut self, directive: &ImportDirective) {
|
||||
let module_id = directive.module_id;
|
||||
let import_id = directive.import_id;
|
||||
let import = &directive.import;
|
||||
let def = directive.status.namespaces();
|
||||
|
||||
if import.is_glob {
|
||||
log::debug!("glob import: {:?}", import);
|
||||
match def.take_types() {
|
||||
|
@ -326,13 +366,9 @@ where
|
|||
let scope = &item_map[m.local_id].scope;
|
||||
|
||||
// Module scoped macros is included
|
||||
let items = scope
|
||||
.items
|
||||
.iter()
|
||||
.map(|(name, res)| (name.clone(), res.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
let items = scope.collect_resolutions();
|
||||
|
||||
self.update(module_id, Some(import_id), &items);
|
||||
self.update(module_id, &items);
|
||||
} else {
|
||||
// glob import from same crate => we do an initial
|
||||
// import, and then need to propagate any further
|
||||
|
@ -340,18 +376,14 @@ where
|
|||
let scope = &self.def_map[m.local_id].scope;
|
||||
|
||||
// Module scoped macros is included
|
||||
let items = scope
|
||||
.items
|
||||
.iter()
|
||||
.map(|(name, res)| (name.clone(), res.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
let items = scope.collect_resolutions();
|
||||
|
||||
self.update(module_id, Some(import_id), &items);
|
||||
self.update(module_id, &items);
|
||||
// record the glob import in case we add further items
|
||||
self.glob_imports
|
||||
.entry(m.local_id)
|
||||
.or_default()
|
||||
.push((module_id, import_id));
|
||||
let glob = self.glob_imports.entry(m.local_id).or_default();
|
||||
if !glob.iter().any(|it| *it == (module_id, import_id)) {
|
||||
glob.push((module_id, import_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
|
||||
|
@ -361,17 +393,14 @@ where
|
|||
let resolutions = enum_data
|
||||
.variants
|
||||
.iter()
|
||||
.filter_map(|(local_id, variant_data)| {
|
||||
.map(|(local_id, variant_data)| {
|
||||
let name = variant_data.name.clone();
|
||||
let variant = EnumVariantId { parent: e, local_id };
|
||||
let res = Resolution {
|
||||
def: PerNs::both(variant.into(), variant.into()),
|
||||
import: Some(import_id),
|
||||
};
|
||||
Some((name, res))
|
||||
let res = PerNs::both(variant.into(), variant.into());
|
||||
(name, res)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
self.update(module_id, Some(import_id), &resolutions);
|
||||
self.update(module_id, &resolutions);
|
||||
}
|
||||
Some(d) => {
|
||||
log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
|
||||
|
@ -383,7 +412,7 @@ where
|
|||
} else {
|
||||
match import.path.segments.last() {
|
||||
Some(last_segment) => {
|
||||
let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
|
||||
let name = import.alias.clone().unwrap_or_else(|| last_segment.clone());
|
||||
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
|
||||
|
||||
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
|
||||
|
@ -393,62 +422,31 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let resolution = Resolution { def, import: Some(import_id) };
|
||||
self.update(module_id, Some(import_id), &[(name, resolution)]);
|
||||
self.update(module_id, &[(name, def)]);
|
||||
}
|
||||
None => tested_by!(bogus_paths),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
module_id: LocalModuleId,
|
||||
import: Option<LocalImportId>,
|
||||
resolutions: &[(Name, Resolution)],
|
||||
) {
|
||||
self.update_recursive(module_id, import, resolutions, 0)
|
||||
fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)]) {
|
||||
self.update_recursive(module_id, resolutions, 0)
|
||||
}
|
||||
|
||||
fn update_recursive(
|
||||
&mut self,
|
||||
module_id: LocalModuleId,
|
||||
import: Option<LocalImportId>,
|
||||
resolutions: &[(Name, Resolution)],
|
||||
resolutions: &[(Name, PerNs)],
|
||||
depth: usize,
|
||||
) {
|
||||
if depth > 100 {
|
||||
// prevent stack overflows (but this shouldn't be possible)
|
||||
panic!("infinite recursion in glob imports!");
|
||||
}
|
||||
let module_items = &mut self.def_map.modules[module_id].scope;
|
||||
let scope = &mut self.def_map.modules[module_id].scope;
|
||||
let mut changed = false;
|
||||
for (name, res) in resolutions {
|
||||
let existing = module_items.items.entry(name.clone()).or_default();
|
||||
|
||||
if existing.def.types.is_none() && res.def.types.is_some() {
|
||||
existing.def.types = res.def.types;
|
||||
existing.import = import.or(res.import);
|
||||
changed = true;
|
||||
}
|
||||
if existing.def.values.is_none() && res.def.values.is_some() {
|
||||
existing.def.values = res.def.values;
|
||||
existing.import = import.or(res.import);
|
||||
changed = true;
|
||||
}
|
||||
if existing.def.macros.is_none() && res.def.macros.is_some() {
|
||||
existing.def.macros = res.def.macros;
|
||||
existing.import = import.or(res.import);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if existing.def.is_none()
|
||||
&& res.def.is_none()
|
||||
&& existing.import.is_none()
|
||||
&& res.import.is_some()
|
||||
{
|
||||
existing.import = res.import;
|
||||
}
|
||||
changed |= scope.push_res(name.clone(), *res);
|
||||
}
|
||||
|
||||
if !changed {
|
||||
|
@ -461,27 +459,48 @@ where
|
|||
.flat_map(|v| v.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
for (glob_importing_module, glob_import) in glob_imports {
|
||||
for (glob_importing_module, _glob_import) in glob_imports {
|
||||
// We pass the glob import so that the tracked import in those modules is that glob import
|
||||
self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1);
|
||||
self.update_recursive(glob_importing_module, resolutions, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_macros(&mut self) -> ReachedFixedPoint {
|
||||
let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
|
||||
let mut attribute_macros =
|
||||
std::mem::replace(&mut self.unexpanded_attribute_macros, Vec::new());
|
||||
let mut resolved = Vec::new();
|
||||
let mut res = ReachedFixedPoint::Yes;
|
||||
macros.retain(|(module_id, ast_id, path)| {
|
||||
macros.retain(|directive| {
|
||||
if let Some(call_id) = directive.legacy {
|
||||
res = ReachedFixedPoint::No;
|
||||
resolved.push((directive.module_id, call_id));
|
||||
return false;
|
||||
}
|
||||
|
||||
let resolved_res = self.def_map.resolve_path_fp_with_macro(
|
||||
self.db,
|
||||
ResolveMode::Other,
|
||||
*module_id,
|
||||
path,
|
||||
directive.module_id,
|
||||
&directive.path,
|
||||
BuiltinShadowMode::Module,
|
||||
);
|
||||
|
||||
if let Some(def) = resolved_res.resolved_def.take_macros() {
|
||||
let call_id = def.as_call_id(self.db, *ast_id);
|
||||
resolved.push((*module_id, call_id, def));
|
||||
let call_id = def.as_call_id(self.db, MacroCallKind::FnLike(directive.ast_id));
|
||||
resolved.push((directive.module_id, call_id));
|
||||
res = ReachedFixedPoint::No;
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
attribute_macros.retain(|(module_id, ast_id, path)| {
|
||||
let resolved_res = self.resolve_attribute_macro(path);
|
||||
|
||||
if let Some(def) = resolved_res {
|
||||
let call_id = def.as_call_id(self.db, MacroCallKind::Attr(*ast_id));
|
||||
resolved.push((*module_id, call_id));
|
||||
res = ReachedFixedPoint::No;
|
||||
return false;
|
||||
}
|
||||
|
@ -490,28 +509,31 @@ where
|
|||
});
|
||||
|
||||
self.unexpanded_macros = macros;
|
||||
self.unexpanded_attribute_macros = attribute_macros;
|
||||
|
||||
for (module_id, macro_call_id, macro_def_id) in resolved {
|
||||
self.collect_macro_expansion(module_id, macro_call_id, macro_def_id);
|
||||
for (module_id, macro_call_id) in resolved {
|
||||
self.collect_macro_expansion(module_id, macro_call_id);
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn collect_macro_expansion(
|
||||
&mut self,
|
||||
module_id: LocalModuleId,
|
||||
macro_call_id: MacroCallId,
|
||||
macro_def_id: MacroDefId,
|
||||
) {
|
||||
if self.poison_macros.contains(¯o_def_id) {
|
||||
return;
|
||||
fn resolve_attribute_macro(&self, path: &ModPath) -> Option<MacroDefId> {
|
||||
// FIXME this is currently super hacky, just enough to support the
|
||||
// built-in derives
|
||||
if let Some(name) = path.as_ident() {
|
||||
// FIXME this should actually be handled with the normal name
|
||||
// resolution; the std lib defines built-in stubs for the derives,
|
||||
// but these are new-style `macro`s, which we don't support yet
|
||||
if let Some(def_id) = find_builtin_derive(name) {
|
||||
return Some(def_id);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
self.macro_stack_monitor.increase(macro_def_id);
|
||||
|
||||
if !self.macro_stack_monitor.is_poison(macro_def_id) {
|
||||
let file_id: HirFileId = macro_call_id.as_file(MacroFileKind::Items);
|
||||
fn collect_macro_expansion(&mut self, module_id: LocalModuleId, macro_call_id: MacroCallId) {
|
||||
let file_id: HirFileId = macro_call_id.as_file();
|
||||
let raw_items = self.db.raw_items(file_id);
|
||||
let mod_dir = self.mod_dirs[&module_id].clone();
|
||||
ModCollector {
|
||||
|
@ -522,12 +544,6 @@ where
|
|||
mod_dir,
|
||||
}
|
||||
.collect(raw_items.items());
|
||||
} else {
|
||||
log::error!("Too deep macro expansion: {:?}", macro_call_id);
|
||||
self.poison_macros.insert(macro_def_id);
|
||||
}
|
||||
|
||||
self.macro_stack_monitor.decrease(macro_def_id);
|
||||
}
|
||||
|
||||
fn finish(self) -> CrateDefMap {
|
||||
|
@ -581,20 +597,31 @@ where
|
|||
raw::RawItemKind::Module(m) => {
|
||||
self.collect_module(&self.raw_items[m], &item.attrs)
|
||||
}
|
||||
raw::RawItemKind::Import(import_id) => self
|
||||
.def_collector
|
||||
.unresolved_imports
|
||||
.push((self.module_id, import_id, self.raw_items[import_id].clone())),
|
||||
raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]),
|
||||
raw::RawItemKind::Import(import_id) => {
|
||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||
module_id: self.module_id,
|
||||
import_id,
|
||||
import: self.raw_items[import_id].clone(),
|
||||
status: PartialResolvedImport::Unresolved,
|
||||
})
|
||||
}
|
||||
raw::RawItemKind::Def(def) => {
|
||||
self.define_def(&self.raw_items[def], &item.attrs)
|
||||
}
|
||||
raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
|
||||
raw::RawItemKind::Impl(imp) => {
|
||||
let module = ModuleId {
|
||||
krate: self.def_collector.def_map.krate,
|
||||
local_id: self.module_id,
|
||||
};
|
||||
let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id);
|
||||
let imp_id = ImplId::from_ast_id(ctx, self.raw_items[imp].ast_id);
|
||||
self.def_collector.def_map.modules[self.module_id].impls.push(imp_id)
|
||||
let container = ContainerId::ModuleId(module);
|
||||
let ast_id = self.raw_items[imp].ast_id;
|
||||
let impl_id =
|
||||
ImplLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db);
|
||||
self.def_collector.def_map.modules[self.module_id]
|
||||
.scope
|
||||
.define_impl(impl_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -667,72 +694,91 @@ where
|
|||
let modules = &mut self.def_collector.def_map.modules;
|
||||
let res = modules.alloc(ModuleData::default());
|
||||
modules[res].parent = Some(self.module_id);
|
||||
modules[res].declaration = Some(declaration);
|
||||
modules[res].definition = definition;
|
||||
modules[res].scope.legacy_macros = modules[self.module_id].scope.legacy_macros.clone();
|
||||
modules[res].origin = ModuleOrigin::not_sure_file(definition, declaration);
|
||||
for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
|
||||
modules[res].scope.define_legacy_macro(name, mac)
|
||||
}
|
||||
modules[self.module_id].children.insert(name.clone(), res);
|
||||
let resolution = Resolution {
|
||||
def: PerNs::types(
|
||||
ModuleId { krate: self.def_collector.def_map.krate, local_id: res }.into(),
|
||||
),
|
||||
import: None,
|
||||
};
|
||||
self.def_collector.update(self.module_id, None, &[(name, resolution)]);
|
||||
let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res };
|
||||
let def: ModuleDefId = module.into();
|
||||
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
|
||||
self.def_collector.update(self.module_id, &[(name, def.into())]);
|
||||
res
|
||||
}
|
||||
|
||||
fn define_def(&mut self, def: &raw::DefData) {
|
||||
fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) {
|
||||
let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
|
||||
let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id);
|
||||
// FIXME: check attrs to see if this is an attribute macro invocation;
|
||||
// in which case we don't add the invocation, just a single attribute
|
||||
// macro invocation
|
||||
|
||||
self.collect_derives(attrs, def);
|
||||
|
||||
let name = def.name.clone();
|
||||
let def: PerNs = match def.kind {
|
||||
raw::DefKind::Function(ast_id) => {
|
||||
let def = FunctionLoc {
|
||||
container: ContainerId::ModuleId(module),
|
||||
let container = ContainerId::ModuleId(module);
|
||||
let def: ModuleDefId = match def.kind {
|
||||
raw::DefKind::Function(ast_id) => FunctionLoc {
|
||||
container: container.into(),
|
||||
ast_id: AstId::new(self.file_id, ast_id),
|
||||
}
|
||||
.intern(self.def_collector.db);
|
||||
|
||||
PerNs::values(def.into())
|
||||
}
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
raw::DefKind::Struct(ast_id) => {
|
||||
let id = StructId::from_ast_id(ctx, ast_id).into();
|
||||
PerNs::both(id, id)
|
||||
StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::Union(ast_id) => {
|
||||
let id = UnionId::from_ast_id(ctx, ast_id).into();
|
||||
PerNs::both(id, id)
|
||||
UnionLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::Enum(ast_id) => {
|
||||
EnumLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::Enum(ast_id) => PerNs::types(EnumId::from_ast_id(ctx, ast_id).into()),
|
||||
raw::DefKind::Const(ast_id) => {
|
||||
let def = ConstLoc {
|
||||
container: ContainerId::ModuleId(module),
|
||||
ast_id: AstId::new(self.file_id, ast_id),
|
||||
}
|
||||
.intern(self.def_collector.db);
|
||||
|
||||
PerNs::values(def.into())
|
||||
ConstLoc { container: container.into(), ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::Static(ast_id) => {
|
||||
let def = StaticLoc { container: module, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db);
|
||||
|
||||
PerNs::values(def.into())
|
||||
StaticLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::Trait(ast_id) => PerNs::types(TraitId::from_ast_id(ctx, ast_id).into()),
|
||||
raw::DefKind::TypeAlias(ast_id) => {
|
||||
let def = TypeAliasLoc {
|
||||
container: ContainerId::ModuleId(module),
|
||||
raw::DefKind::Trait(ast_id) => {
|
||||
TraitLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::TypeAlias(ast_id) => TypeAliasLoc {
|
||||
container: container.into(),
|
||||
ast_id: AstId::new(self.file_id, ast_id),
|
||||
}
|
||||
.intern(self.def_collector.db);
|
||||
|
||||
PerNs::types(def.into())
|
||||
}
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
};
|
||||
let resolution = Resolution { def, import: None };
|
||||
self.def_collector.update(self.module_id, None, &[(name, resolution)])
|
||||
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
|
||||
self.def_collector.update(self.module_id, &[(name, def.into())])
|
||||
}
|
||||
|
||||
fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
|
||||
for derive_subtree in attrs.by_key("derive").tt_values() {
|
||||
// for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
|
||||
for tt in &derive_subtree.token_trees {
|
||||
let ident = match &tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident,
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok
|
||||
_ => continue, // anything else would be an error (which we currently ignore)
|
||||
};
|
||||
let path = ModPath::from_tt_ident(ident);
|
||||
|
||||
let ast_id = AstId::new(self.file_id, def.kind.ast_id());
|
||||
self.def_collector.unexpanded_attribute_macros.push((self.module_id, ast_id, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_macro(&mut self, mac: &raw::MacroData) {
|
||||
|
@ -758,8 +804,8 @@ where
|
|||
if is_macro_rules(&mac.path) {
|
||||
if let Some(name) = &mac.name {
|
||||
let macro_id = MacroDefId {
|
||||
ast_id,
|
||||
krate: self.def_collector.def_map.krate,
|
||||
ast_id: Some(ast_id),
|
||||
krate: Some(self.def_collector.def_map.krate),
|
||||
kind: MacroDefKind::Declarative,
|
||||
};
|
||||
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
|
||||
|
@ -767,14 +813,20 @@ where
|
|||
return;
|
||||
}
|
||||
|
||||
// Case 2: try to resolve in legacy scope and expand macro_rules, triggering
|
||||
// recursive item collection.
|
||||
// Case 2: try to resolve in legacy scope and expand macro_rules
|
||||
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 macro_call_id = macro_def.as_call_id(self.def_collector.db, ast_id);
|
||||
let macro_call_id =
|
||||
macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id));
|
||||
|
||||
self.def_collector.unexpanded_macros.push(MacroDirective {
|
||||
module_id: self.module_id,
|
||||
path: mac.path.clone(),
|
||||
ast_id,
|
||||
legacy: Some(macro_call_id),
|
||||
});
|
||||
|
||||
self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -782,13 +834,19 @@ where
|
|||
// We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only.
|
||||
let mut path = mac.path.clone();
|
||||
if path.is_ident() {
|
||||
path.kind = PathKind::Self_;
|
||||
path.kind = PathKind::Super(0);
|
||||
}
|
||||
self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path));
|
||||
|
||||
self.def_collector.unexpanded_macros.push(MacroDirective {
|
||||
module_id: self.module_id,
|
||||
path,
|
||||
ast_id,
|
||||
legacy: None,
|
||||
});
|
||||
}
|
||||
|
||||
fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) {
|
||||
let macros = self.def_collector.def_map[module_id].scope.legacy_macros.clone();
|
||||
let macros = self.def_collector.def_map[module_id].scope.collect_legacy_macros();
|
||||
for (name, macro_) in macros {
|
||||
self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_);
|
||||
}
|
||||
|
@ -803,45 +861,35 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn is_macro_rules(path: &Path) -> bool {
|
||||
path.as_ident() == Some(&name::MACRO_RULES)
|
||||
fn is_macro_rules(path: &ModPath) -> bool {
|
||||
path.as_ident() == Some(&name![macro_rules])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{db::DefDatabase, test_db::TestDB};
|
||||
use ra_arena::Arena;
|
||||
use ra_db::{fixture::WithFixture, SourceDatabase};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{db::DefDatabase, test_db::TestDB};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn do_collect_defs(
|
||||
db: &impl DefDatabase,
|
||||
def_map: CrateDefMap,
|
||||
monitor: MacroStackMonitor,
|
||||
) -> (CrateDefMap, FxHashSet<MacroDefId>) {
|
||||
fn do_collect_defs(db: &impl DefDatabase, def_map: CrateDefMap) -> CrateDefMap {
|
||||
let mut collector = DefCollector {
|
||||
db,
|
||||
def_map,
|
||||
glob_imports: FxHashMap::default(),
|
||||
unresolved_imports: Vec::new(),
|
||||
resolved_imports: Vec::new(),
|
||||
unexpanded_macros: Vec::new(),
|
||||
unexpanded_attribute_macros: Vec::new(),
|
||||
mod_dirs: FxHashMap::default(),
|
||||
macro_stack_monitor: monitor,
|
||||
poison_macros: FxHashSet::default(),
|
||||
cfg_options: &CfgOptions::default(),
|
||||
};
|
||||
collector.collect();
|
||||
(collector.def_map, collector.poison_macros)
|
||||
collector.def_map
|
||||
}
|
||||
|
||||
fn do_limited_resolve(
|
||||
code: &str,
|
||||
limit: u32,
|
||||
poison_limit: u32,
|
||||
) -> (CrateDefMap, FxHashSet<MacroDefId>) {
|
||||
fn do_resolve(code: &str) -> CrateDefMap {
|
||||
let (db, _file_id) = TestDB::with_single_file(&code);
|
||||
let krate = db.test_crate();
|
||||
|
||||
|
@ -859,59 +907,18 @@ mod tests {
|
|||
diagnostics: Vec::new(),
|
||||
}
|
||||
};
|
||||
|
||||
let mut monitor = MacroStackMonitor::default();
|
||||
monitor.validator = Some(Box::new(move |count| {
|
||||
assert!(count < limit);
|
||||
count >= poison_limit
|
||||
}));
|
||||
|
||||
do_collect_defs(&db, def_map, monitor)
|
||||
do_collect_defs(&db, def_map)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_expand_limit_width() {
|
||||
do_limited_resolve(
|
||||
fn test_macro_expand_will_stop() {
|
||||
do_resolve(
|
||||
r#"
|
||||
macro_rules! foo {
|
||||
($($ty:ty)*) => { foo!($($ty)*, $($ty)*); }
|
||||
}
|
||||
foo!(KABOOM);
|
||||
"#,
|
||||
16,
|
||||
1000,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_expand_poisoned() {
|
||||
let (_, poison_macros) = do_limited_resolve(
|
||||
r#"
|
||||
macro_rules! foo {
|
||||
($ty:ty) => { foo!($ty); }
|
||||
}
|
||||
foo!(KABOOM);
|
||||
"#,
|
||||
100,
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(poison_macros.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_expand_normal() {
|
||||
let (_, poison_macros) = do_limited_resolve(
|
||||
r#"
|
||||
macro_rules! foo {
|
||||
($ident:ident) => { struct $ident {} }
|
||||
}
|
||||
foo!(Bar);
|
||||
"#,
|
||||
16,
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(poison_macros.len(), 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,16 +10,18 @@
|
|||
//!
|
||||
//! `ReachedFixedPoint` signals about this.
|
||||
|
||||
use std::iter::successors;
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use ra_db::Edition;
|
||||
use test_utils::tested_by;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
nameres::CrateDefMap,
|
||||
path::{Path, PathKind},
|
||||
nameres::{BuiltinShadowMode, CrateDefMap},
|
||||
path::{ModPath, PathKind},
|
||||
per_ns::PerNs,
|
||||
AdtId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
|
||||
AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -39,19 +41,21 @@ pub(super) struct ResolvePathResult {
|
|||
pub(super) resolved_def: PerNs,
|
||||
pub(super) segment_index: Option<usize>,
|
||||
pub(super) reached_fixedpoint: ReachedFixedPoint,
|
||||
pub(super) krate: Option<CrateId>,
|
||||
}
|
||||
|
||||
impl ResolvePathResult {
|
||||
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
|
||||
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
|
||||
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
|
||||
}
|
||||
|
||||
fn with(
|
||||
resolved_def: PerNs,
|
||||
reached_fixedpoint: ReachedFixedPoint,
|
||||
segment_index: Option<usize>,
|
||||
krate: Option<CrateId>,
|
||||
) -> ResolvePathResult {
|
||||
ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
|
||||
ResolvePathResult { resolved_def, reached_fixedpoint, segment_index, krate }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,8 +71,18 @@ impl CrateDefMap {
|
|||
db: &impl DefDatabase,
|
||||
mode: ResolveMode,
|
||||
original_module: LocalModuleId,
|
||||
path: &Path,
|
||||
path: &ModPath,
|
||||
shadow: BuiltinShadowMode,
|
||||
) -> ResolvePathResult {
|
||||
// if it is not the last segment, we prefer the module to the builtin
|
||||
let prefer_module = |index| {
|
||||
if index == path.segments.len() - 1 {
|
||||
shadow
|
||||
} else {
|
||||
BuiltinShadowMode::Module
|
||||
}
|
||||
};
|
||||
|
||||
let mut segments = path.segments.iter().enumerate();
|
||||
let mut curr_per_ns: PerNs = match path.kind {
|
||||
PathKind::DollarCrate(krate) => {
|
||||
|
@ -85,9 +99,6 @@ impl CrateDefMap {
|
|||
PathKind::Crate => {
|
||||
PerNs::types(ModuleId { krate: self.krate, local_id: self.root }.into())
|
||||
}
|
||||
PathKind::Self_ => {
|
||||
PerNs::types(ModuleId { krate: self.krate, local_id: 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)
|
||||
|
@ -96,24 +107,26 @@ impl CrateDefMap {
|
|||
if self.edition == Edition::Edition2015
|
||||
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
|
||||
{
|
||||
let segment = match segments.next() {
|
||||
Some((_, segment)) => segment,
|
||||
let (idx, segment) = match segments.next() {
|
||||
Some((idx, segment)) => (idx, segment),
|
||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||
};
|
||||
log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
|
||||
self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
|
||||
self.resolve_name_in_crate_root_or_extern_prelude(&segment, prefer_module(idx))
|
||||
}
|
||||
PathKind::Plain => {
|
||||
let segment = match segments.next() {
|
||||
Some((_, segment)) => segment,
|
||||
let (idx, segment) = match segments.next() {
|
||||
Some((idx, segment)) => (idx, segment),
|
||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||
};
|
||||
log::debug!("resolving {:?} in module", segment);
|
||||
self.resolve_name_in_module(db, original_module, &segment.name)
|
||||
self.resolve_name_in_module(db, original_module, &segment, prefer_module(idx))
|
||||
}
|
||||
PathKind::Super => {
|
||||
if let Some(p) = self.modules[original_module].parent {
|
||||
PerNs::types(ModuleId { krate: self.krate, local_id: p }.into())
|
||||
PathKind::Super(lvl) => {
|
||||
let m = successors(Some(original_module), |m| self.modules[*m].parent)
|
||||
.nth(lvl as usize);
|
||||
if let Some(local_id) = m {
|
||||
PerNs::types(ModuleId { krate: self.krate, local_id }.into())
|
||||
} else {
|
||||
log::debug!("super path in root module");
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||
|
@ -125,18 +138,13 @@ impl CrateDefMap {
|
|||
Some((_, segment)) => segment,
|
||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||
};
|
||||
if let Some(def) = self.extern_prelude.get(&segment.name) {
|
||||
if let Some(def) = self.extern_prelude.get(&segment) {
|
||||
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
||||
PerNs::types(*def)
|
||||
} else {
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
|
||||
}
|
||||
}
|
||||
PathKind::Type(_) => {
|
||||
// This is handled in `infer::infer_path_expr`
|
||||
// The result returned here does not matter
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||
}
|
||||
};
|
||||
|
||||
for (i, segment) in segments {
|
||||
|
@ -156,32 +164,29 @@ impl CrateDefMap {
|
|||
curr_per_ns = match curr {
|
||||
ModuleDefId::ModuleId(module) => {
|
||||
if module.krate != self.krate {
|
||||
let path =
|
||||
Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
|
||||
let path = ModPath {
|
||||
segments: path.segments[i..].to_vec(),
|
||||
kind: PathKind::Super(0),
|
||||
};
|
||||
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.local_id, &path);
|
||||
let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
|
||||
return ResolvePathResult::with(
|
||||
def,
|
||||
ReachedFixedPoint::Yes,
|
||||
s.map(|s| s + i),
|
||||
Some(module.krate),
|
||||
);
|
||||
}
|
||||
|
||||
// Since it is a qualified path here, it should not contains legacy macros
|
||||
match self[module.local_id].scope.get(&segment.name) {
|
||||
Some(res) => res.def,
|
||||
_ => {
|
||||
log::debug!("path segment {:?} not found", segment.name);
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
||||
}
|
||||
}
|
||||
self[module.local_id].scope.get(&segment, prefer_module(i))
|
||||
}
|
||||
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
|
||||
// enum variant
|
||||
tested_by!(can_import_enum_variant);
|
||||
let enum_data = db.enum_data(e);
|
||||
match enum_data.variant(&segment.name) {
|
||||
match enum_data.variant(&segment) {
|
||||
Some(local_id) => {
|
||||
let variant = EnumVariantId { parent: e, local_id };
|
||||
PerNs::both(variant.into(), variant.into())
|
||||
|
@ -191,6 +196,7 @@ impl CrateDefMap {
|
|||
PerNs::types(e.into()),
|
||||
ReachedFixedPoint::Yes,
|
||||
Some(i),
|
||||
Some(self.krate),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +206,7 @@ impl CrateDefMap {
|
|||
// (`Struct::method`), or some other kind of associated item
|
||||
log::debug!(
|
||||
"path segment {:?} resolved to non-module {:?}, but is not last",
|
||||
segment.name,
|
||||
segment,
|
||||
curr,
|
||||
);
|
||||
|
||||
|
@ -208,11 +214,13 @@ impl CrateDefMap {
|
|||
PerNs::types(s),
|
||||
ReachedFixedPoint::Yes,
|
||||
Some(i),
|
||||
Some(self.krate),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
|
||||
|
||||
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
|
||||
}
|
||||
|
||||
fn resolve_name_in_module(
|
||||
|
@ -220,6 +228,7 @@ impl CrateDefMap {
|
|||
db: &impl DefDatabase,
|
||||
module: LocalModuleId,
|
||||
name: &Name,
|
||||
shadow: BuiltinShadowMode,
|
||||
) -> PerNs {
|
||||
// Resolve in:
|
||||
// - legacy scope of macro
|
||||
|
@ -228,23 +237,31 @@ impl CrateDefMap {
|
|||
// - std prelude
|
||||
let from_legacy_macro =
|
||||
self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
|
||||
let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
|
||||
let from_scope = self[module].scope.get(name, shadow);
|
||||
let from_extern_prelude =
|
||||
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
|
||||
let from_prelude = self.resolve_in_prelude(db, name);
|
||||
let from_prelude = self.resolve_in_prelude(db, name, shadow);
|
||||
|
||||
from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
|
||||
}
|
||||
|
||||
fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
|
||||
let from_crate_root =
|
||||
self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
|
||||
fn resolve_name_in_crate_root_or_extern_prelude(
|
||||
&self,
|
||||
name: &Name,
|
||||
shadow: BuiltinShadowMode,
|
||||
) -> PerNs {
|
||||
let from_crate_root = self[self.root].scope.get(name, shadow);
|
||||
let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
|
||||
|
||||
from_crate_root.or(from_extern_prelude)
|
||||
}
|
||||
|
||||
fn resolve_in_prelude(&self, db: &impl DefDatabase, name: &Name) -> PerNs {
|
||||
fn resolve_in_prelude(
|
||||
&self,
|
||||
db: &impl DefDatabase,
|
||||
name: &Name,
|
||||
shadow: BuiltinShadowMode,
|
||||
) -> PerNs {
|
||||
if let Some(prelude) = self.prelude {
|
||||
let keep;
|
||||
let def_map = if prelude.krate == self.krate {
|
||||
|
@ -254,7 +271,7 @@ impl CrateDefMap {
|
|||
keep = db.crate_def_map(prelude.krate);
|
||||
&keep
|
||||
};
|
||||
def_map[prelude.local_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
|
||||
def_map[prelude.local_id].scope.get(name, shadow)
|
||||
} else {
|
||||
PerNs::none()
|
||||
}
|
||||
|
|
|
@ -10,21 +10,18 @@ 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_arena::{impl_arena_id, Arena, RawId};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{
|
||||
ast::{self, AttrsOwner, NameOwner},
|
||||
AstNode, AstPtr,
|
||||
AstNode,
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs, db::DefDatabase, path::Path, trace::Trace, FileAstId, HirFileId, LocalImportId,
|
||||
Source,
|
||||
};
|
||||
use crate::{attr::Attrs, db::DefDatabase, path::ModPath, FileAstId, HirFileId, InFile};
|
||||
|
||||
/// `RawItems` is a set of top-level items in a file (except for impls).
|
||||
///
|
||||
|
@ -33,7 +30,7 @@ use crate::{
|
|||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct RawItems {
|
||||
modules: Arena<Module, ModuleData>,
|
||||
imports: Arena<LocalImportId, ImportData>,
|
||||
imports: Arena<Import, ImportData>,
|
||||
defs: Arena<Def, DefData>,
|
||||
macros: Arena<Macro, MacroData>,
|
||||
impls: Arena<Impl, ImplData>,
|
||||
|
@ -41,35 +38,15 @@ pub struct RawItems {
|
|||
items: Vec<RawItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct ImportSourceMap {
|
||||
map: ArenaMap<LocalImportId, ImportSourcePtr>,
|
||||
}
|
||||
|
||||
type ImportSourcePtr = Either<AstPtr<ast::UseTree>, AstPtr<ast::ExternCrateItem>>;
|
||||
|
||||
impl ImportSourceMap {
|
||||
pub fn get(&self, import: LocalImportId) -> ImportSourcePtr {
|
||||
self.map[import].clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl RawItems {
|
||||
pub(crate) fn raw_items_query(
|
||||
db: &(impl DefDatabase + 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),
|
||||
file_id: HirFileId,
|
||||
) -> (Arc<RawItems>, Arc<ImportSourceMap>) {
|
||||
let _p = profile("raw_items_query");
|
||||
let mut collector = RawItemsCollector {
|
||||
raw_items: RawItems::default(),
|
||||
source_ast_id_map: db.ast_id_map(file_id),
|
||||
imports: Trace::new(),
|
||||
file_id,
|
||||
hygiene: Hygiene::new(db, file_id),
|
||||
};
|
||||
|
@ -80,11 +57,8 @@ impl RawItems {
|
|||
collector.process_module(None, item_list);
|
||||
}
|
||||
}
|
||||
let mut raw_items = collector.raw_items;
|
||||
let (arena, map) = collector.imports.into_arena_and_map();
|
||||
raw_items.imports = arena;
|
||||
let source_map = ImportSourceMap { map };
|
||||
(Arc::new(raw_items), Arc::new(source_map))
|
||||
let raw_items = collector.raw_items;
|
||||
Arc::new(raw_items)
|
||||
}
|
||||
|
||||
pub(super) fn items(&self) -> &[RawItem] {
|
||||
|
@ -99,9 +73,9 @@ impl Index<Module> for RawItems {
|
|||
}
|
||||
}
|
||||
|
||||
impl Index<LocalImportId> for RawItems {
|
||||
impl Index<Import> for RawItems {
|
||||
type Output = ImportData;
|
||||
fn index(&self, idx: LocalImportId) -> &ImportData {
|
||||
fn index(&self, idx: Import) -> &ImportData {
|
||||
&self.imports[idx]
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +110,7 @@ pub(super) struct RawItem {
|
|||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(super) enum RawItemKind {
|
||||
Module(Module),
|
||||
Import(LocalImportId),
|
||||
Import(Import),
|
||||
Def(Def),
|
||||
Macro(Macro),
|
||||
Impl(Impl),
|
||||
|
@ -152,9 +126,13 @@ pub(super) enum ModuleData {
|
|||
Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct Import(RawId);
|
||||
impl_arena_id!(Import);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ImportData {
|
||||
pub(super) path: Path,
|
||||
pub(super) path: ModPath,
|
||||
pub(super) alias: Option<Name>,
|
||||
pub(super) is_glob: bool,
|
||||
pub(super) is_prelude: bool,
|
||||
|
@ -184,6 +162,21 @@ pub(super) enum DefKind {
|
|||
TypeAlias(FileAstId<ast::TypeAliasDef>),
|
||||
}
|
||||
|
||||
impl DefKind {
|
||||
pub fn ast_id(&self) -> FileAstId<ast::ModuleItem> {
|
||||
match self {
|
||||
DefKind::Function(it) => it.upcast(),
|
||||
DefKind::Struct(it) => it.upcast(),
|
||||
DefKind::Union(it) => it.upcast(),
|
||||
DefKind::Enum(it) => it.upcast(),
|
||||
DefKind::Const(it) => it.upcast(),
|
||||
DefKind::Static(it) => it.upcast(),
|
||||
DefKind::Trait(it) => it.upcast(),
|
||||
DefKind::TypeAlias(it) => it.upcast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(super) struct Macro(RawId);
|
||||
impl_arena_id!(Macro);
|
||||
|
@ -191,7 +184,7 @@ 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) path: ModPath,
|
||||
pub(super) name: Option<Name>,
|
||||
pub(super) export: bool,
|
||||
pub(super) builtin: bool,
|
||||
|
@ -208,7 +201,6 @@ pub(super) struct ImplData {
|
|||
|
||||
struct RawItemsCollector {
|
||||
raw_items: RawItems,
|
||||
imports: Trace<LocalImportId, ImportData, ImportSourcePtr>,
|
||||
source_ast_id_map: Arc<AstIdMap>,
|
||||
file_id: HirFileId,
|
||||
hygiene: Hygiene,
|
||||
|
@ -312,10 +304,10 @@ impl RawItemsCollector {
|
|||
let attrs = self.parse_attrs(&use_item);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
Path::expand_use_item(
|
||||
Source { value: use_item, file_id: self.file_id },
|
||||
ModPath::expand_use_item(
|
||||
InFile { value: use_item, file_id: self.file_id },
|
||||
&self.hygiene,
|
||||
|path, use_tree, is_glob, alias| {
|
||||
|path, _use_tree, is_glob, alias| {
|
||||
let import_data = ImportData {
|
||||
path,
|
||||
alias,
|
||||
|
@ -324,11 +316,11 @@ impl RawItemsCollector {
|
|||
is_extern_crate: false,
|
||||
is_macro_use: false,
|
||||
};
|
||||
buf.push((import_data, Either::A(AstPtr::new(use_tree))));
|
||||
buf.push(import_data);
|
||||
},
|
||||
);
|
||||
for (import_data, ptr) in buf {
|
||||
self.push_import(current_module, attrs.clone(), import_data, ptr);
|
||||
for import_data in buf {
|
||||
self.push_import(current_module, attrs.clone(), import_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,7 +330,7 @@ impl RawItemsCollector {
|
|||
extern_crate: ast::ExternCrateItem,
|
||||
) {
|
||||
if let Some(name_ref) = extern_crate.name_ref() {
|
||||
let path = Path::from_name_ref(&name_ref);
|
||||
let path = ModPath::from_name_ref(&name_ref);
|
||||
let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
|
||||
let attrs = self.parse_attrs(&extern_crate);
|
||||
// FIXME: cfg_attr
|
||||
|
@ -351,18 +343,13 @@ impl RawItemsCollector {
|
|||
is_extern_crate: true,
|
||||
is_macro_use,
|
||||
};
|
||||
self.push_import(
|
||||
current_module,
|
||||
attrs,
|
||||
import_data,
|
||||
Either::B(AstPtr::new(&extern_crate)),
|
||||
);
|
||||
self.push_import(current_module, attrs, import_data);
|
||||
}
|
||||
}
|
||||
|
||||
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(path, &self.hygiene)) {
|
||||
let path = match m.path().and_then(|path| ModPath::from_src(path, &self.hygiene)) {
|
||||
Some(it) => it,
|
||||
_ => return,
|
||||
};
|
||||
|
@ -387,14 +374,8 @@ impl RawItemsCollector {
|
|||
self.push_item(current_module, attrs, RawItemKind::Impl(imp))
|
||||
}
|
||||
|
||||
fn push_import(
|
||||
&mut self,
|
||||
current_module: Option<Module>,
|
||||
attrs: Attrs,
|
||||
data: ImportData,
|
||||
source: ImportSourcePtr,
|
||||
) {
|
||||
let import = self.imports.alloc(|| source, || data);
|
||||
fn push_import(&mut self, current_module: Option<Module>, attrs: Attrs, data: ImportData) {
|
||||
let import = self.raw_items.imports.alloc(data);
|
||||
self.push_item(current_module, attrs, RawItemKind::Import(import))
|
||||
}
|
||||
|
||||
|
|
|
@ -32,27 +32,22 @@ fn render_crate_def_map(map: &CrateDefMap) -> String {
|
|||
*buf += path;
|
||||
*buf += "\n";
|
||||
|
||||
let mut entries = map.modules[module]
|
||||
.scope
|
||||
.items
|
||||
.iter()
|
||||
.map(|(name, res)| (name, res.def))
|
||||
.collect::<Vec<_>>();
|
||||
entries.sort_by_key(|(name, _)| *name);
|
||||
let mut entries = map.modules[module].scope.collect_resolutions();
|
||||
entries.sort_by_key(|(name, _)| name.clone());
|
||||
|
||||
for (name, res) in entries {
|
||||
for (name, def) in entries {
|
||||
*buf += &format!("{}:", name);
|
||||
|
||||
if res.types.is_some() {
|
||||
if def.types.is_some() {
|
||||
*buf += " t";
|
||||
}
|
||||
if res.values.is_some() {
|
||||
if def.values.is_some() {
|
||||
*buf += " v";
|
||||
}
|
||||
if res.macros.is_some() {
|
||||
if def.macros.is_some() {
|
||||
*buf += " m";
|
||||
}
|
||||
if res.is_none() {
|
||||
if def.is_none() {
|
||||
*buf += " _";
|
||||
}
|
||||
|
||||
|
@ -558,3 +553,35 @@ fn cfg_test() {
|
|||
⋮Foo: t v
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_multiple_namespace() {
|
||||
let map = def_map(
|
||||
r#"
|
||||
//- /main.rs
|
||||
mod a {
|
||||
pub type T = ();
|
||||
pub use crate::b::*;
|
||||
}
|
||||
|
||||
use crate::a::T;
|
||||
|
||||
mod b {
|
||||
pub const T: () = ();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(map, @r###"
|
||||
⋮crate
|
||||
⋮T: t v
|
||||
⋮a: t
|
||||
⋮b: t
|
||||
⋮
|
||||
⋮crate::b
|
||||
⋮T: v
|
||||
⋮
|
||||
⋮crate::a
|
||||
⋮T: t v
|
||||
"###);
|
||||
}
|
||||
|
|
|
@ -112,3 +112,24 @@ fn glob_enum() {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn glob_enum_group() {
|
||||
covers!(glob_enum_group);
|
||||
let map = def_map(
|
||||
"
|
||||
//- /lib.rs
|
||||
enum Foo {
|
||||
Bar, Baz
|
||||
}
|
||||
use self::Foo::{*};
|
||||
",
|
||||
);
|
||||
assert_snapshot!(map, @r###"
|
||||
⋮crate
|
||||
⋮Bar: t v
|
||||
⋮Baz: t v
|
||||
⋮Foo: t
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
|||
let events = db.log_executed(|| {
|
||||
let crate_def_map = db.crate_def_map(krate);
|
||||
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||
assert_eq!(module_data.scope.items.len(), 1);
|
||||
assert_eq!(module_data.scope.collect_resolutions().len(), 1);
|
||||
});
|
||||
assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
|||
let events = db.log_executed(|| {
|
||||
let crate_def_map = db.crate_def_map(krate);
|
||||
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
|
||||
assert_eq!(module_data.scope.items.len(), 1);
|
||||
assert_eq!(module_data.scope.collect_resolutions().len(), 1);
|
||||
});
|
||||
assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
|
||||
}
|
||||
|
|
|
@ -600,3 +600,27 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() {
|
|||
⋮bar: t v
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_derive() {
|
||||
let map = compute_crate_def_map(
|
||||
"
|
||||
//- /main.rs
|
||||
#[derive(Clone)]
|
||||
struct Foo;
|
||||
",
|
||||
);
|
||||
assert_eq!(map.modules[map.root].scope.impls().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_multiple_derive() {
|
||||
let map = compute_crate_def_map(
|
||||
"
|
||||
//- /main.rs
|
||||
#[derive(Copy, Clone)]
|
||||
struct Foo;
|
||||
",
|
||||
);
|
||||
assert_eq!(map.modules[map.root].scope.impls().len(), 2);
|
||||
}
|
||||
|
|
|
@ -668,7 +668,7 @@ fn unresolved_module_diagnostics() {
|
|||
module: LocalModuleId(
|
||||
0,
|
||||
),
|
||||
declaration: AstId {
|
||||
declaration: InFile {
|
||||
file_id: HirFileId(
|
||||
FileId(
|
||||
FileId(
|
||||
|
@ -676,7 +676,7 @@ fn unresolved_module_diagnostics() {
|
|||
),
|
||||
),
|
||||
),
|
||||
file_ast_id: FileAstId {
|
||||
value: FileAstId {
|
||||
raw: ErasedFileAstId(
|
||||
1,
|
||||
),
|
||||
|
|
|
@ -1,35 +1,97 @@
|
|||
//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`.
|
||||
mod lower;
|
||||
|
||||
use std::{iter, sync::Arc};
|
||||
|
||||
use hir_expand::{
|
||||
either::Either,
|
||||
hygiene::Hygiene,
|
||||
name::{self, AsName, Name},
|
||||
name::{AsName, Name},
|
||||
};
|
||||
use ra_db::CrateId;
|
||||
use ra_syntax::{
|
||||
ast::{self, NameOwner, TypeAscriptionOwner},
|
||||
AstNode,
|
||||
};
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::{type_ref::TypeRef, Source};
|
||||
use crate::{type_ref::TypeRef, InFile};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ModPath {
|
||||
pub kind: PathKind,
|
||||
pub segments: Vec<Name>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum PathKind {
|
||||
Plain,
|
||||
/// `self::` is `Super(0)`
|
||||
Super(u8),
|
||||
Crate,
|
||||
/// Absolute path (::foo)
|
||||
Abs,
|
||||
/// `$crate` from macro expansion
|
||||
DollarCrate(CrateId),
|
||||
}
|
||||
|
||||
impl ModPath {
|
||||
pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
|
||||
lower::lower_path(path, hygiene).map(|it| it.mod_path)
|
||||
}
|
||||
|
||||
pub fn from_simple_segments(
|
||||
kind: PathKind,
|
||||
segments: impl IntoIterator<Item = Name>,
|
||||
) -> ModPath {
|
||||
let segments = segments.into_iter().collect::<Vec<_>>();
|
||||
ModPath { kind, segments }
|
||||
}
|
||||
|
||||
pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> ModPath {
|
||||
name_ref.as_name().into()
|
||||
}
|
||||
|
||||
/// Converts an `tt::Ident` into a single-identifier `Path`.
|
||||
pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath {
|
||||
ident.as_name().into()
|
||||
}
|
||||
|
||||
/// Calls `cb` with all paths, represented by this use item.
|
||||
pub(crate) fn expand_use_item(
|
||||
item_src: InFile<ast::UseItem>,
|
||||
hygiene: &Hygiene,
|
||||
mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<Name>),
|
||||
) {
|
||||
if let Some(tree) = item_src.value.use_tree() {
|
||||
lower::lower_use_tree(None, tree, hygiene, &mut cb);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ident(&self) -> bool {
|
||||
self.kind == PathKind::Plain && self.segments.len() == 1
|
||||
}
|
||||
|
||||
pub fn is_self(&self) -> bool {
|
||||
self.kind == PathKind::Super(0) && self.segments.is_empty()
|
||||
}
|
||||
|
||||
/// If this path is a single identifier, like `foo`, return its name.
|
||||
pub fn as_ident(&self) -> Option<&Name> {
|
||||
if self.kind != PathKind::Plain || self.segments.len() > 1 {
|
||||
return None;
|
||||
}
|
||||
self.segments.first()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Path {
|
||||
pub kind: PathKind,
|
||||
pub segments: Vec<PathSegment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PathSegment {
|
||||
pub name: Name,
|
||||
pub args_and_bindings: Option<Arc<GenericArgs>>,
|
||||
/// Type based path like `<T>::foo`.
|
||||
/// Note that paths like `<Type as Trait>::foo` are desugard to `Trait::<Self=Type>::foo`.
|
||||
type_anchor: Option<Box<TypeRef>>,
|
||||
mod_path: ModPath,
|
||||
/// Invariant: the same len as self.path.segments
|
||||
generic_args: Vec<Option<Arc<GenericArgs>>>,
|
||||
}
|
||||
|
||||
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
|
||||
/// can (in the future) also include bindings of associated types, like in
|
||||
/// `Iterator<Item = Foo>`.
|
||||
/// also includes bindings of associated types, like in `Iterator<Item = Foo>`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GenericArgs {
|
||||
pub args: Vec<GenericArg>,
|
||||
|
@ -50,234 +112,111 @@ pub enum GenericArg {
|
|||
// or lifetime...
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum PathKind {
|
||||
Plain,
|
||||
Self_,
|
||||
Super,
|
||||
Crate,
|
||||
// Absolute path
|
||||
Abs,
|
||||
// Type based path like `<T>::foo`
|
||||
Type(Box<TypeRef>),
|
||||
// `$crate` from macro expansion
|
||||
DollarCrate(CrateId),
|
||||
}
|
||||
|
||||
impl Path {
|
||||
/// Calls `cb` with all paths, represented by this use item.
|
||||
pub(crate) fn expand_use_item(
|
||||
item_src: Source<ast::UseItem>,
|
||||
hygiene: &Hygiene,
|
||||
mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||
) {
|
||||
if let Some(tree) = item_src.value.use_tree() {
|
||||
expand_use_tree(None, tree, hygiene, &mut cb);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_simple_segments(
|
||||
kind: PathKind,
|
||||
segments: impl IntoIterator<Item = Name>,
|
||||
) -> Path {
|
||||
Path {
|
||||
kind,
|
||||
segments: segments
|
||||
.into_iter()
|
||||
.map(|name| PathSegment { name, args_and_bindings: None })
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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::from_src(path, &Hygiene::new_unhygienic())
|
||||
lower::lower_path(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(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
|
||||
let mut kind = PathKind::Plain;
|
||||
let mut segments = Vec::new();
|
||||
loop {
|
||||
let segment = path.segment()?;
|
||||
|
||||
if segment.has_colon_colon() {
|
||||
kind = PathKind::Abs;
|
||||
}
|
||||
|
||||
match segment.kind()? {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
|
||||
assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
|
||||
let self_type = TypeRef::from_ast(type_ref?);
|
||||
|
||||
match trait_ref {
|
||||
// <T>::foo
|
||||
None => {
|
||||
kind = PathKind::Type(Box::new(self_type));
|
||||
}
|
||||
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||
Some(trait_ref) => {
|
||||
let path = Path::from_src(trait_ref.path()?, hygiene)?;
|
||||
kind = path.kind;
|
||||
let mut prefix_segments = path.segments;
|
||||
prefix_segments.reverse();
|
||||
segments.extend(prefix_segments);
|
||||
// Insert the type reference (T in the above example) as Self parameter for the trait
|
||||
let mut last_segment = segments.last_mut()?;
|
||||
if last_segment.args_and_bindings.is_none() {
|
||||
last_segment.args_and_bindings =
|
||||
Some(Arc::new(GenericArgs::empty()));
|
||||
};
|
||||
let args = last_segment.args_and_bindings.as_mut().unwrap();
|
||||
let mut args_inner = Arc::make_mut(args);
|
||||
args_inner.has_self_type = true;
|
||||
args_inner.args.insert(0, GenericArg::Type(self_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => {
|
||||
kind = PathKind::Crate;
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::SelfKw => {
|
||||
kind = PathKind::Self_;
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::SuperKw => {
|
||||
kind = PathKind::Super;
|
||||
break;
|
||||
}
|
||||
}
|
||||
path = match qualifier(&path) {
|
||||
Some(it) => it,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
segments.reverse();
|
||||
return Some(Path { kind, segments });
|
||||
|
||||
fn qualifier(path: &ast::Path) -> Option<ast::Path> {
|
||||
if let Some(q) = path.qualifier() {
|
||||
return Some(q);
|
||||
}
|
||||
// FIXME: this bottom up traversal is not too precise.
|
||||
// Should we handle do a top-down analysis, recording results?
|
||||
let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
|
||||
let use_tree = use_tree_list.parent_use_tree();
|
||||
use_tree.path()
|
||||
}
|
||||
pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
|
||||
lower::lower_path(path, hygiene)
|
||||
}
|
||||
|
||||
/// Converts an `ast::NameRef` into a single-identifier `Path`.
|
||||
pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> Path {
|
||||
name_ref.as_name().into()
|
||||
Path { type_anchor: None, mod_path: name_ref.as_name().into(), generic_args: vec![None] }
|
||||
}
|
||||
|
||||
/// `true` is this path is a single identifier, like `foo`
|
||||
pub fn is_ident(&self) -> bool {
|
||||
self.kind == PathKind::Plain && self.segments.len() == 1
|
||||
pub fn kind(&self) -> &PathKind {
|
||||
&self.mod_path.kind
|
||||
}
|
||||
|
||||
/// `true` if this path is just a standalone `self`
|
||||
pub fn is_self(&self) -> bool {
|
||||
self.kind == PathKind::Self_ && self.segments.is_empty()
|
||||
pub fn type_anchor(&self) -> Option<&TypeRef> {
|
||||
self.type_anchor.as_deref()
|
||||
}
|
||||
|
||||
/// If this path is a single identifier, like `foo`, return its name.
|
||||
pub fn as_ident(&self) -> Option<&Name> {
|
||||
if self.kind != PathKind::Plain || self.segments.len() > 1 {
|
||||
pub fn segments(&self) -> PathSegments<'_> {
|
||||
PathSegments {
|
||||
segments: self.mod_path.segments.as_slice(),
|
||||
generic_args: self.generic_args.as_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mod_path(&self) -> &ModPath {
|
||||
&self.mod_path
|
||||
}
|
||||
|
||||
pub fn qualifier(&self) -> Option<Path> {
|
||||
if self.mod_path.is_ident() {
|
||||
return None;
|
||||
}
|
||||
self.segments.first().map(|s| &s.name)
|
||||
let res = Path {
|
||||
type_anchor: self.type_anchor.clone(),
|
||||
mod_path: ModPath {
|
||||
kind: self.mod_path.kind.clone(),
|
||||
segments: self.mod_path.segments[..self.mod_path.segments.len() - 1].to_vec(),
|
||||
},
|
||||
generic_args: self.generic_args[..self.generic_args.len() - 1].to_vec(),
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_macro_expr(&self) -> Option<Name> {
|
||||
self.as_ident().and_then(|name| Some(name.clone()))
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PathSegment<'a> {
|
||||
pub name: &'a Name,
|
||||
pub args_and_bindings: Option<&'a GenericArgs>,
|
||||
}
|
||||
|
||||
pub fn is_type_relative(&self) -> bool {
|
||||
match self.kind {
|
||||
PathKind::Type(_) => true,
|
||||
_ => false,
|
||||
pub struct PathSegments<'a> {
|
||||
segments: &'a [Name],
|
||||
generic_args: &'a [Option<Arc<GenericArgs>>],
|
||||
}
|
||||
|
||||
impl<'a> PathSegments<'a> {
|
||||
pub const EMPTY: PathSegments<'static> = PathSegments { segments: &[], generic_args: &[] };
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.segments.len()
|
||||
}
|
||||
pub fn first(&self) -> Option<PathSegment<'a>> {
|
||||
self.get(0)
|
||||
}
|
||||
pub fn last(&self) -> Option<PathSegment<'a>> {
|
||||
self.get(self.len().checked_sub(1)?)
|
||||
}
|
||||
pub fn get(&self, idx: usize) -> Option<PathSegment<'a>> {
|
||||
assert_eq!(self.segments.len(), self.generic_args.len());
|
||||
let res = PathSegment {
|
||||
name: self.segments.get(idx)?,
|
||||
args_and_bindings: self.generic_args.get(idx).unwrap().as_ref().map(|it| &**it),
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
pub fn skip(&self, len: usize) -> PathSegments<'a> {
|
||||
assert_eq!(self.segments.len(), self.generic_args.len());
|
||||
PathSegments { segments: &self.segments[len..], generic_args: &self.generic_args[len..] }
|
||||
}
|
||||
pub fn take(&self, len: usize) -> PathSegments<'a> {
|
||||
assert_eq!(self.segments.len(), self.generic_args.len());
|
||||
PathSegments { segments: &self.segments[..len], generic_args: &self.generic_args[..len] }
|
||||
}
|
||||
pub fn iter(&self) -> impl Iterator<Item = PathSegment<'a>> {
|
||||
self.segments.iter().zip(self.generic_args.iter()).map(|(name, args)| PathSegment {
|
||||
name,
|
||||
args_and_bindings: args.as_ref().map(|it| &**it),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericArgs {
|
||||
pub(crate) 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());
|
||||
args.push(GenericArg::Type(type_ref));
|
||||
}
|
||||
// lifetimes ignored for now
|
||||
let mut bindings = Vec::new();
|
||||
for assoc_type_arg in node.assoc_type_args() {
|
||||
if let Some(name_ref) = assoc_type_arg.name_ref() {
|
||||
let name = name_ref.as_name();
|
||||
let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref());
|
||||
bindings.push((name, type_ref));
|
||||
}
|
||||
}
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(GenericArgs { args, has_self_type: false, bindings })
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
|
||||
/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
|
||||
pub(crate) fn from_fn_like_path_ast(
|
||||
params: Option<ast::ParamList>,
|
||||
ret_type: Option<ast::RetType>,
|
||||
) -> Option<GenericArgs> {
|
||||
let mut args = Vec::new();
|
||||
let mut bindings = Vec::new();
|
||||
if let Some(params) = params {
|
||||
let mut param_types = Vec::new();
|
||||
for param in params.params() {
|
||||
let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
|
||||
param_types.push(type_ref);
|
||||
}
|
||||
let arg = GenericArg::Type(TypeRef::Tuple(param_types));
|
||||
args.push(arg);
|
||||
}
|
||||
if let Some(ret_type) = ret_type {
|
||||
let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
|
||||
bindings.push((name::OUTPUT_TYPE, type_ref))
|
||||
}
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(GenericArgs { args, has_self_type: false, bindings })
|
||||
}
|
||||
lower::lower_generic_args(node)
|
||||
}
|
||||
|
||||
pub(crate) fn empty() -> GenericArgs {
|
||||
|
@ -287,137 +226,51 @@ impl GenericArgs {
|
|||
|
||||
impl From<Name> for Path {
|
||||
fn from(name: Name) -> Path {
|
||||
Path::from_simple_segments(PathKind::Plain, iter::once(name))
|
||||
Path {
|
||||
type_anchor: None,
|
||||
mod_path: ModPath::from_simple_segments(PathKind::Plain, iter::once(name)),
|
||||
generic_args: vec![None],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_use_tree(
|
||||
prefix: Option<Path>,
|
||||
tree: ast::UseTree,
|
||||
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() {
|
||||
// E.g. use something::{{{inner}}};
|
||||
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, hygiene) {
|
||||
Some(it) => Some(it),
|
||||
None => return, // FIXME: report errors somewhere
|
||||
},
|
||||
impl From<Name> for ModPath {
|
||||
fn from(name: Name) -> ModPath {
|
||||
ModPath::from_simple_segments(PathKind::Plain, iter::once(name))
|
||||
}
|
||||
}
|
||||
|
||||
pub use hir_expand::name as __name;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! __known_path {
|
||||
(std::iter::IntoIterator) => {};
|
||||
(std::result::Result) => {};
|
||||
(std::ops::Range) => {};
|
||||
(std::ops::RangeFrom) => {};
|
||||
(std::ops::RangeFull) => {};
|
||||
(std::ops::RangeTo) => {};
|
||||
(std::ops::RangeToInclusive) => {};
|
||||
(std::ops::RangeInclusive) => {};
|
||||
(std::boxed::Box) => {};
|
||||
(std::future::Future) => {};
|
||||
(std::ops::Try) => {};
|
||||
(std::ops::Neg) => {};
|
||||
(std::ops::Not) => {};
|
||||
(std::ops::Index) => {};
|
||||
($path:path) => {
|
||||
compile_error!("Please register your known path in the path module")
|
||||
};
|
||||
for child_tree in use_tree_list.use_trees() {
|
||||
expand_use_tree(prefix.clone(), child_tree, hygiene, cb);
|
||||
}
|
||||
} else {
|
||||
let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
|
||||
if let Some(ast_path) = tree.path() {
|
||||
// Handle self in a path.
|
||||
// E.g. `use something::{self, <...>}`
|
||||
if ast_path.qualifier().is_none() {
|
||||
if let Some(segment) = ast_path.segment() {
|
||||
if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
|
||||
if let Some(prefix) = prefix {
|
||||
cb(prefix, &tree, false, alias);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(path) = convert_path(prefix, ast_path, hygiene) {
|
||||
let is_glob = tree.has_star();
|
||||
cb(path, &tree, is_glob, alias)
|
||||
}
|
||||
// FIXME: report errors somewhere
|
||||
// We get here if we do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, hygiene)?)
|
||||
} else {
|
||||
prefix
|
||||
};
|
||||
|
||||
let segment = path.segment()?;
|
||||
let res = match segment.kind()? {
|
||||
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),
|
||||
#[macro_export]
|
||||
macro_rules! __path {
|
||||
($start:ident $(:: $seg:ident)*) => ({
|
||||
$crate::__known_path!($start $(:: $seg)*);
|
||||
$crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec![
|
||||
$crate::path::__name![$start], $($crate::path::__name![$seg],)*
|
||||
])
|
||||
});
|
||||
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(crate_id),
|
||||
iter::empty(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
Path::from_simple_segments(PathKind::Crate, iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::SelfKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
Path::from_simple_segments(PathKind::Self_, iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::SuperKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
Path::from_simple_segments(PathKind::Super, iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::Type { .. } => {
|
||||
// not allowed in imports
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub mod known {
|
||||
use hir_expand::name;
|
||||
|
||||
use super::{Path, PathKind};
|
||||
|
||||
pub fn std_iter_into_iterator() -> Path {
|
||||
Path::from_simple_segments(
|
||||
PathKind::Abs,
|
||||
vec![name::STD, name::ITER, name::INTO_ITERATOR_TYPE],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn std_ops_try() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_result_result() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_future_future() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::FUTURE, name::FUTURE_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_boxed_box() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::BOXED, name::BOX_TYPE])
|
||||
}
|
||||
}
|
||||
pub use crate::__path as path;
|
||||
|
|
178
crates/ra_hir_def/src/path/lower.rs
Normal file
178
crates/ra_hir_def/src/path/lower.rs
Normal file
|
@ -0,0 +1,178 @@
|
|||
//! Transforms syntax into `Path` objects, ideally with accounting for hygiene
|
||||
|
||||
mod lower_use;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName},
|
||||
};
|
||||
use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner};
|
||||
|
||||
use crate::{
|
||||
path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
|
||||
type_ref::TypeRef,
|
||||
};
|
||||
|
||||
pub(super) use lower_use::lower_use_tree;
|
||||
|
||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||
/// It correctly handles `$crate` based path from macro call.
|
||||
pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
|
||||
let mut kind = PathKind::Plain;
|
||||
let mut type_anchor = None;
|
||||
let mut segments = Vec::new();
|
||||
let mut generic_args = Vec::new();
|
||||
loop {
|
||||
let segment = path.segment()?;
|
||||
|
||||
if segment.has_colon_colon() {
|
||||
kind = PathKind::Abs;
|
||||
}
|
||||
|
||||
match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name_ref) => {
|
||||
// FIXME: this should just return name
|
||||
match hygiene.name_ref_to_name(name_ref) {
|
||||
Either::Left(name) => {
|
||||
let args = segment
|
||||
.type_arg_list()
|
||||
.and_then(lower_generic_args)
|
||||
.or_else(|| {
|
||||
lower_generic_args_from_fn_path(
|
||||
segment.param_list(),
|
||||
segment.ret_type(),
|
||||
)
|
||||
})
|
||||
.map(Arc::new);
|
||||
segments.push(name);
|
||||
generic_args.push(args)
|
||||
}
|
||||
Either::Right(crate_id) => {
|
||||
kind = PathKind::DollarCrate(crate_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
|
||||
assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
|
||||
let self_type = TypeRef::from_ast(type_ref?);
|
||||
|
||||
match trait_ref {
|
||||
// <T>::foo
|
||||
None => {
|
||||
type_anchor = Some(Box::new(self_type));
|
||||
kind = PathKind::Plain;
|
||||
}
|
||||
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||
Some(trait_ref) => {
|
||||
let path = Path::from_src(trait_ref.path()?, hygiene)?;
|
||||
kind = path.mod_path.kind;
|
||||
|
||||
let mut prefix_segments = path.mod_path.segments;
|
||||
prefix_segments.reverse();
|
||||
segments.extend(prefix_segments);
|
||||
|
||||
let mut prefix_args = path.generic_args;
|
||||
prefix_args.reverse();
|
||||
generic_args.extend(prefix_args);
|
||||
|
||||
// Insert the type reference (T in the above example) as Self parameter for the trait
|
||||
let last_segment = generic_args.last_mut()?;
|
||||
if last_segment.is_none() {
|
||||
*last_segment = Some(Arc::new(GenericArgs::empty()));
|
||||
};
|
||||
let args = last_segment.as_mut().unwrap();
|
||||
let mut args_inner = Arc::make_mut(args);
|
||||
args_inner.has_self_type = true;
|
||||
args_inner.args.insert(0, GenericArg::Type(self_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => {
|
||||
kind = PathKind::Crate;
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::SelfKw => {
|
||||
kind = PathKind::Super(0);
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::SuperKw => {
|
||||
kind = PathKind::Super(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
path = match qualifier(&path) {
|
||||
Some(it) => it,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
segments.reverse();
|
||||
generic_args.reverse();
|
||||
let mod_path = ModPath { kind, segments };
|
||||
return Some(Path { type_anchor, mod_path, generic_args });
|
||||
|
||||
fn qualifier(path: &ast::Path) -> Option<ast::Path> {
|
||||
if let Some(q) = path.qualifier() {
|
||||
return Some(q);
|
||||
}
|
||||
// FIXME: this bottom up traversal is not too precise.
|
||||
// Should we handle do a top-down analysis, recording results?
|
||||
let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
|
||||
let use_tree = use_tree_list.parent_use_tree();
|
||||
use_tree.path()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_generic_args(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());
|
||||
args.push(GenericArg::Type(type_ref));
|
||||
}
|
||||
// lifetimes ignored for now
|
||||
let mut bindings = Vec::new();
|
||||
for assoc_type_arg in node.assoc_type_args() {
|
||||
if let Some(name_ref) = assoc_type_arg.name_ref() {
|
||||
let name = name_ref.as_name();
|
||||
let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref());
|
||||
bindings.push((name, type_ref));
|
||||
}
|
||||
}
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(GenericArgs { args, has_self_type: false, bindings })
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
|
||||
/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
|
||||
fn lower_generic_args_from_fn_path(
|
||||
params: Option<ast::ParamList>,
|
||||
ret_type: Option<ast::RetType>,
|
||||
) -> Option<GenericArgs> {
|
||||
let mut args = Vec::new();
|
||||
let mut bindings = Vec::new();
|
||||
if let Some(params) = params {
|
||||
let mut param_types = Vec::new();
|
||||
for param in params.params() {
|
||||
let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
|
||||
param_types.push(type_ref);
|
||||
}
|
||||
let arg = GenericArg::Type(TypeRef::Tuple(param_types));
|
||||
args.push(arg);
|
||||
}
|
||||
if let Some(ret_type) = ret_type {
|
||||
let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
|
||||
bindings.push((name![Output], type_ref))
|
||||
}
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(GenericArgs { args, has_self_type: false, bindings })
|
||||
}
|
||||
}
|
118
crates/ra_hir_def/src/path/lower/lower_use.rs
Normal file
118
crates/ra_hir_def/src/path/lower/lower_use.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
//! Lowers a single complex use like `use foo::{bar, baz};` into a list of paths like
|
||||
//! `foo::bar`, `foo::baz`;
|
||||
|
||||
use std::iter;
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
hygiene::Hygiene,
|
||||
name::{AsName, Name},
|
||||
};
|
||||
use ra_syntax::ast::{self, NameOwner};
|
||||
use test_utils::tested_by;
|
||||
|
||||
use crate::path::{ModPath, PathKind};
|
||||
|
||||
pub(crate) fn lower_use_tree(
|
||||
prefix: Option<ModPath>,
|
||||
tree: ast::UseTree,
|
||||
hygiene: &Hygiene,
|
||||
cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option<Name>),
|
||||
) {
|
||||
if let Some(use_tree_list) = tree.use_tree_list() {
|
||||
let prefix = match tree.path() {
|
||||
// E.g. use something::{{{inner}}};
|
||||
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, hygiene) {
|
||||
Some(it) => Some(it),
|
||||
None => return, // FIXME: report errors somewhere
|
||||
},
|
||||
};
|
||||
for child_tree in use_tree_list.use_trees() {
|
||||
lower_use_tree(prefix.clone(), child_tree, hygiene, cb);
|
||||
}
|
||||
} else {
|
||||
let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
|
||||
let is_glob = tree.has_star();
|
||||
if let Some(ast_path) = tree.path() {
|
||||
// Handle self in a path.
|
||||
// E.g. `use something::{self, <...>}`
|
||||
if ast_path.qualifier().is_none() {
|
||||
if let Some(segment) = ast_path.segment() {
|
||||
if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
|
||||
if let Some(prefix) = prefix {
|
||||
cb(prefix, &tree, false, alias);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(path) = convert_path(prefix, ast_path, hygiene) {
|
||||
cb(path, &tree, is_glob, alias)
|
||||
}
|
||||
// FIXME: report errors somewhere
|
||||
// We get here if we do
|
||||
} else if is_glob {
|
||||
tested_by!(glob_enum_group);
|
||||
if let Some(prefix) = prefix {
|
||||
cb(prefix, &tree, is_glob, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
|
||||
let prefix = if let Some(qual) = path.qualifier() {
|
||||
Some(convert_path(prefix, qual, hygiene)?)
|
||||
} else {
|
||||
prefix
|
||||
};
|
||||
|
||||
let segment = path.segment()?;
|
||||
let res = match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name_ref) => {
|
||||
match hygiene.name_ref_to_name(name_ref) {
|
||||
Either::Left(name) => {
|
||||
// no type args in use
|
||||
let mut res = prefix.unwrap_or_else(|| ModPath {
|
||||
kind: PathKind::Plain,
|
||||
segments: Vec::with_capacity(1),
|
||||
});
|
||||
res.segments.push(name);
|
||||
res
|
||||
}
|
||||
Either::Right(crate_id) => {
|
||||
return Some(ModPath::from_simple_segments(
|
||||
PathKind::DollarCrate(crate_id),
|
||||
iter::empty(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
ModPath::from_simple_segments(PathKind::Crate, iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::SelfKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
ModPath::from_simple_segments(PathKind::Super(0), iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::SuperKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
ModPath::from_simple_segments(PathKind::Super(1), iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::Type { .. } => {
|
||||
// not allowed in imports
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
}
|
|
@ -11,8 +11,6 @@ use crate::ModuleDefId;
|
|||
pub struct PerNs {
|
||||
pub types: Option<ModuleDefId>,
|
||||
pub values: Option<ModuleDefId>,
|
||||
/// Since macros has different type, many methods simply ignore it.
|
||||
/// We can only use special method like `get_macros` to access it.
|
||||
pub macros: Option<MacroDefId>,
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::{
|
||||
name::{self, Name},
|
||||
name::{name, Name},
|
||||
MacroDefId,
|
||||
};
|
||||
use ra_db::CrateId;
|
||||
|
@ -10,20 +10,23 @@ use rustc_hash::FxHashSet;
|
|||
|
||||
use crate::{
|
||||
body::scope::{ExprScopes, ScopeId},
|
||||
body::Body,
|
||||
builtin_type::BuiltinType,
|
||||
db::DefDatabase,
|
||||
expr::{ExprId, PatId},
|
||||
generics::GenericParams,
|
||||
item_scope::BuiltinShadowMode,
|
||||
nameres::CrateDefMap,
|
||||
path::{Path, PathKind},
|
||||
path::{ModPath, PathKind},
|
||||
per_ns::PerNs,
|
||||
AdtId, AstItemDef, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
|
||||
GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId, StaticId,
|
||||
StructId, TraitId, TypeAliasId,
|
||||
AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId,
|
||||
FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId,
|
||||
StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Resolver {
|
||||
// FIXME: all usages generally call `.rev`, so maybe reverse once in consturciton?
|
||||
scopes: Vec<Scope>,
|
||||
}
|
||||
|
||||
|
@ -53,12 +56,14 @@ enum Scope {
|
|||
AdtScope(AdtId),
|
||||
/// Local bindings
|
||||
ExprScope(ExprScope),
|
||||
/// Temporary hack to support local items.
|
||||
LocalItemsScope(Arc<Body>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum TypeNs {
|
||||
SelfType(ImplId),
|
||||
GenericParam(u32),
|
||||
GenericParam(TypeParamId),
|
||||
AdtId(AdtId),
|
||||
AdtSelfType(AdtId),
|
||||
// Yup, enum variants are added to the types ns, but any usage of variant as
|
||||
|
@ -90,8 +95,8 @@ pub enum ValueNs {
|
|||
|
||||
impl Resolver {
|
||||
/// Resolve known trait from std, like `std::futures::Future`
|
||||
pub fn resolve_known_trait(&self, db: &impl DefDatabase, path: &Path) -> Option<TraitId> {
|
||||
let res = self.resolve_module_path(db, path).take_types()?;
|
||||
pub fn resolve_known_trait(&self, db: &impl DefDatabase, path: &ModPath) -> Option<TraitId> {
|
||||
let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?;
|
||||
match res {
|
||||
ModuleDefId::TraitId(it) => Some(it),
|
||||
_ => None,
|
||||
|
@ -99,8 +104,8 @@ impl Resolver {
|
|||
}
|
||||
|
||||
/// Resolve known struct from std, like `std::boxed::Box`
|
||||
pub fn resolve_known_struct(&self, db: &impl DefDatabase, path: &Path) -> Option<StructId> {
|
||||
let res = self.resolve_module_path(db, path).take_types()?;
|
||||
pub fn resolve_known_struct(&self, db: &impl DefDatabase, path: &ModPath) -> Option<StructId> {
|
||||
let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?;
|
||||
match res {
|
||||
ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it),
|
||||
_ => None,
|
||||
|
@ -108,63 +113,95 @@ impl Resolver {
|
|||
}
|
||||
|
||||
/// Resolve known enum from std, like `std::result::Result`
|
||||
pub fn resolve_known_enum(&self, db: &impl DefDatabase, path: &Path) -> Option<EnumId> {
|
||||
let res = self.resolve_module_path(db, path).take_types()?;
|
||||
pub fn resolve_known_enum(&self, db: &impl DefDatabase, path: &ModPath) -> Option<EnumId> {
|
||||
let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?;
|
||||
match res {
|
||||
ModuleDefId::AdtId(AdtId::EnumId(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// pub only for source-binder
|
||||
pub fn resolve_module_path(&self, db: &impl DefDatabase, path: &Path) -> PerNs {
|
||||
fn resolve_module_path(
|
||||
&self,
|
||||
db: &impl DefDatabase,
|
||||
path: &ModPath,
|
||||
shadow: BuiltinShadowMode,
|
||||
) -> PerNs {
|
||||
let (item_map, module) = match self.module() {
|
||||
Some(it) => it,
|
||||
None => return PerNs::none(),
|
||||
};
|
||||
let (module_res, segment_index) = item_map.resolve_path(db, module, path);
|
||||
let (module_res, segment_index) = item_map.resolve_path(db, module, &path, shadow);
|
||||
if segment_index.is_some() {
|
||||
return PerNs::none();
|
||||
}
|
||||
module_res
|
||||
}
|
||||
|
||||
pub fn resolve_module_path_in_items(&self, db: &impl DefDatabase, path: &ModPath) -> PerNs {
|
||||
self.resolve_module_path(db, path, BuiltinShadowMode::Module)
|
||||
}
|
||||
|
||||
pub fn resolve_path_in_type_ns(
|
||||
&self,
|
||||
db: &impl DefDatabase,
|
||||
path: &Path,
|
||||
path: &ModPath,
|
||||
) -> Option<(TypeNs, Option<usize>)> {
|
||||
if path.is_type_relative() {
|
||||
return None;
|
||||
}
|
||||
let first_name = &path.segments.first()?.name;
|
||||
let first_name = path.segments.first()?;
|
||||
let skip_to_mod = path.kind != PathKind::Plain;
|
||||
for scope in self.scopes.iter().rev() {
|
||||
match scope {
|
||||
Scope::ExprScope(_) => continue,
|
||||
Scope::GenericParams { .. } | Scope::ImplBlockScope(_) if skip_to_mod => continue,
|
||||
Scope::GenericParams { .. }
|
||||
| Scope::ImplBlockScope(_)
|
||||
| Scope::LocalItemsScope(_)
|
||||
if skip_to_mod =>
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
Scope::GenericParams { params, .. } => {
|
||||
if let Some(param) = params.find_by_name(first_name) {
|
||||
Scope::GenericParams { params, def } => {
|
||||
if let Some(local_id) = params.find_by_name(first_name) {
|
||||
let idx = if path.segments.len() == 1 { None } else { Some(1) };
|
||||
return Some((TypeNs::GenericParam(param.idx), idx));
|
||||
return Some((
|
||||
TypeNs::GenericParam(TypeParamId { local_id, parent: *def }),
|
||||
idx,
|
||||
));
|
||||
}
|
||||
}
|
||||
Scope::ImplBlockScope(impl_) => {
|
||||
if first_name == &name::SELF_TYPE {
|
||||
if first_name == &name![Self] {
|
||||
let idx = if path.segments.len() == 1 { None } else { Some(1) };
|
||||
return Some((TypeNs::SelfType(*impl_), idx));
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(adt) => {
|
||||
if first_name == &name::SELF_TYPE {
|
||||
if first_name == &name![Self] {
|
||||
let idx = if path.segments.len() == 1 { None } else { Some(1) };
|
||||
return Some((TypeNs::AdtSelfType(*adt), idx));
|
||||
}
|
||||
}
|
||||
Scope::ModuleScope(m) => {
|
||||
let (module_def, idx) = m.crate_def_map.resolve_path(db, m.module_id, path);
|
||||
let res = match module_def.take_types()? {
|
||||
let (module_def, idx) = m.crate_def_map.resolve_path(
|
||||
db,
|
||||
m.module_id,
|
||||
&path,
|
||||
BuiltinShadowMode::Other,
|
||||
);
|
||||
let res = to_type_ns(module_def)?;
|
||||
return Some((res, idx));
|
||||
}
|
||||
Scope::LocalItemsScope(body) => {
|
||||
let def = body.item_scope.get(first_name, BuiltinShadowMode::Other);
|
||||
if let Some(res) = to_type_ns(def) {
|
||||
return Some((res, None));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
|
||||
let res = match per_ns.take_types()? {
|
||||
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
|
||||
ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it),
|
||||
|
||||
|
@ -178,17 +215,14 @@ impl Resolver {
|
|||
| ModuleDefId::StaticId(_)
|
||||
| ModuleDefId::ModuleId(_) => return None,
|
||||
};
|
||||
return Some((res, idx));
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn resolve_path_in_type_ns_fully(
|
||||
&self,
|
||||
db: &impl DefDatabase,
|
||||
path: &Path,
|
||||
path: &ModPath,
|
||||
) -> Option<TypeNs> {
|
||||
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
|
||||
if unresolved.is_some() {
|
||||
|
@ -197,17 +231,14 @@ impl Resolver {
|
|||
Some(res)
|
||||
}
|
||||
|
||||
pub fn resolve_path_in_value_ns<'p>(
|
||||
pub fn resolve_path_in_value_ns(
|
||||
&self,
|
||||
db: &impl DefDatabase,
|
||||
path: &'p Path,
|
||||
path: &ModPath,
|
||||
) -> Option<ResolveValueResult> {
|
||||
if path.is_type_relative() {
|
||||
return None;
|
||||
}
|
||||
let n_segments = path.segments.len();
|
||||
let tmp = name::SELF_PARAM;
|
||||
let first_name = if path.is_self() { &tmp } else { &path.segments.first()?.name };
|
||||
let tmp = name![self];
|
||||
let first_name = if path.is_self() { &tmp } else { &path.segments.first()? };
|
||||
let skip_to_mod = path.kind != PathKind::Plain && !path.is_self();
|
||||
for scope in self.scopes.iter().rev() {
|
||||
match scope {
|
||||
|
@ -215,6 +246,7 @@ impl Resolver {
|
|||
| Scope::ExprScope(_)
|
||||
| Scope::GenericParams { .. }
|
||||
| Scope::ImplBlockScope(_)
|
||||
| Scope::LocalItemsScope(_)
|
||||
if skip_to_mod =>
|
||||
{
|
||||
continue
|
||||
|
@ -233,22 +265,22 @@ impl Resolver {
|
|||
}
|
||||
Scope::ExprScope(_) => continue,
|
||||
|
||||
Scope::GenericParams { params, .. } if n_segments > 1 => {
|
||||
if let Some(param) = params.find_by_name(first_name) {
|
||||
let ty = TypeNs::GenericParam(param.idx);
|
||||
Scope::GenericParams { params, def } if n_segments > 1 => {
|
||||
if let Some(local_id) = params.find_by_name(first_name) {
|
||||
let ty = TypeNs::GenericParam(TypeParamId { local_id, parent: *def });
|
||||
return Some(ResolveValueResult::Partial(ty, 1));
|
||||
}
|
||||
}
|
||||
Scope::GenericParams { .. } => continue,
|
||||
|
||||
Scope::ImplBlockScope(impl_) if n_segments > 1 => {
|
||||
if first_name == &name::SELF_TYPE {
|
||||
if first_name == &name![Self] {
|
||||
let ty = TypeNs::SelfType(*impl_);
|
||||
return Some(ResolveValueResult::Partial(ty, 1));
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(adt) if n_segments > 1 => {
|
||||
if first_name == &name::SELF_TYPE {
|
||||
if first_name == &name![Self] {
|
||||
let ty = TypeNs::AdtSelfType(*adt);
|
||||
return Some(ResolveValueResult::Partial(ty, 1));
|
||||
}
|
||||
|
@ -256,23 +288,15 @@ impl Resolver {
|
|||
Scope::ImplBlockScope(_) | Scope::AdtScope(_) => continue,
|
||||
|
||||
Scope::ModuleScope(m) => {
|
||||
let (module_def, idx) = m.crate_def_map.resolve_path(db, m.module_id, path);
|
||||
let (module_def, idx) = m.crate_def_map.resolve_path(
|
||||
db,
|
||||
m.module_id,
|
||||
&path,
|
||||
BuiltinShadowMode::Other,
|
||||
);
|
||||
return match idx {
|
||||
None => {
|
||||
let value = match module_def.take_values()? {
|
||||
ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
|
||||
ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it),
|
||||
ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it),
|
||||
ModuleDefId::ConstId(it) => ValueNs::ConstId(it),
|
||||
ModuleDefId::StaticId(it) => ValueNs::StaticId(it),
|
||||
|
||||
ModuleDefId::AdtId(AdtId::EnumId(_))
|
||||
| ModuleDefId::AdtId(AdtId::UnionId(_))
|
||||
| ModuleDefId::TraitId(_)
|
||||
| ModuleDefId::TypeAliasId(_)
|
||||
| ModuleDefId::BuiltinType(_)
|
||||
| ModuleDefId::ModuleId(_) => return None,
|
||||
};
|
||||
let value = to_value_ns(module_def)?;
|
||||
Some(ResolveValueResult::ValueNs(value))
|
||||
}
|
||||
Some(idx) => {
|
||||
|
@ -292,15 +316,39 @@ impl Resolver {
|
|||
}
|
||||
};
|
||||
}
|
||||
Scope::LocalItemsScope(body) => {
|
||||
let def = body.item_scope.get(first_name, BuiltinShadowMode::Other);
|
||||
if let Some(res) = to_value_ns(def) {
|
||||
return Some(ResolveValueResult::ValueNs(res));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
return None;
|
||||
|
||||
fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
|
||||
let res = match per_ns.take_values()? {
|
||||
ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
|
||||
ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it),
|
||||
ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it),
|
||||
ModuleDefId::ConstId(it) => ValueNs::ConstId(it),
|
||||
ModuleDefId::StaticId(it) => ValueNs::StaticId(it),
|
||||
|
||||
ModuleDefId::AdtId(AdtId::EnumId(_))
|
||||
| ModuleDefId::AdtId(AdtId::UnionId(_))
|
||||
| ModuleDefId::TraitId(_)
|
||||
| ModuleDefId::TypeAliasId(_)
|
||||
| ModuleDefId::BuiltinType(_)
|
||||
| ModuleDefId::ModuleId(_) => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_path_in_value_ns_fully(
|
||||
&self,
|
||||
db: &impl DefDatabase,
|
||||
path: &Path,
|
||||
path: &ModPath,
|
||||
) -> Option<ValueNs> {
|
||||
match self.resolve_path_in_value_ns(db, path)? {
|
||||
ResolveValueResult::ValueNs(it) => Some(it),
|
||||
|
@ -308,9 +356,13 @@ impl Resolver {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &Path) -> Option<MacroDefId> {
|
||||
pub fn resolve_path_as_macro(
|
||||
&self,
|
||||
db: &impl DefDatabase,
|
||||
path: &ModPath,
|
||||
) -> Option<MacroDefId> {
|
||||
let (item_map, module) = self.module()?;
|
||||
item_map.resolve_path(db, module, path).0.take_macros()
|
||||
item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros()
|
||||
}
|
||||
|
||||
pub fn process_all_names(&self, db: &impl DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
|
||||
|
@ -350,6 +402,7 @@ impl Resolver {
|
|||
) -> impl Iterator<Item = &'a crate::generics::WherePredicate> + 'a {
|
||||
self.scopes
|
||||
.iter()
|
||||
.rev()
|
||||
.filter_map(|scope| match scope {
|
||||
Scope::GenericParams { params, .. } => Some(params),
|
||||
_ => None,
|
||||
|
@ -358,14 +411,14 @@ impl Resolver {
|
|||
}
|
||||
|
||||
pub fn generic_def(&self) -> Option<GenericDefId> {
|
||||
self.scopes.iter().find_map(|scope| match scope {
|
||||
self.scopes.iter().rev().find_map(|scope| match scope {
|
||||
Scope::GenericParams { def, .. } => Some(*def),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn body_owner(&self) -> Option<DefWithBodyId> {
|
||||
self.scopes.iter().find_map(|scope| match scope {
|
||||
self.scopes.iter().rev().find_map(|scope| match scope {
|
||||
Scope::ExprScope(it) => Some(it.owner),
|
||||
_ => None,
|
||||
})
|
||||
|
@ -376,7 +429,7 @@ pub enum ScopeDef {
|
|||
PerNs(PerNs),
|
||||
ImplSelfType(ImplId),
|
||||
AdtSelfType(AdtId),
|
||||
GenericParam(u32),
|
||||
GenericParam(TypeParamId),
|
||||
Local(PatId),
|
||||
}
|
||||
|
||||
|
@ -391,8 +444,8 @@ impl Scope {
|
|||
// def: m.module.into(),
|
||||
// }),
|
||||
// );
|
||||
m.crate_def_map[m.module_id].scope.entries().for_each(|(name, res)| {
|
||||
f(name.clone(), ScopeDef::PerNs(res.def));
|
||||
m.crate_def_map[m.module_id].scope.entries().for_each(|(name, def)| {
|
||||
f(name.clone(), ScopeDef::PerNs(def));
|
||||
});
|
||||
m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
|
||||
f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_)));
|
||||
|
@ -402,21 +455,29 @@ impl Scope {
|
|||
});
|
||||
if let Some(prelude) = m.crate_def_map.prelude {
|
||||
let prelude_def_map = db.crate_def_map(prelude.krate);
|
||||
prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, res)| {
|
||||
f(name.clone(), ScopeDef::PerNs(res.def));
|
||||
prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| {
|
||||
f(name.clone(), ScopeDef::PerNs(def));
|
||||
});
|
||||
}
|
||||
}
|
||||
Scope::GenericParams { params, .. } => {
|
||||
for param in params.params.iter() {
|
||||
f(param.name.clone(), ScopeDef::GenericParam(param.idx))
|
||||
Scope::LocalItemsScope(body) => {
|
||||
body.item_scope.entries_without_primitives().for_each(|(name, def)| {
|
||||
f(name.clone(), ScopeDef::PerNs(def));
|
||||
})
|
||||
}
|
||||
Scope::GenericParams { params, def } => {
|
||||
for (local_id, param) in params.types.iter() {
|
||||
f(
|
||||
param.name.clone(),
|
||||
ScopeDef::GenericParam(TypeParamId { local_id, parent: *def }),
|
||||
)
|
||||
}
|
||||
}
|
||||
Scope::ImplBlockScope(i) => {
|
||||
f(name::SELF_TYPE, ScopeDef::ImplSelfType((*i).into()));
|
||||
f(name![Self], ScopeDef::ImplSelfType((*i).into()));
|
||||
}
|
||||
Scope::AdtScope(i) => {
|
||||
f(name::SELF_TYPE, ScopeDef::AdtSelfType((*i).into()));
|
||||
f(name![Self], ScopeDef::AdtSelfType((*i).into()));
|
||||
}
|
||||
Scope::ExprScope(scope) => {
|
||||
scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| {
|
||||
|
@ -439,6 +500,7 @@ pub fn resolver_for_scope(
|
|||
scope_id: Option<ScopeId>,
|
||||
) -> Resolver {
|
||||
let mut r = owner.resolver(db);
|
||||
r = r.push_local_items_scope(db.body(owner));
|
||||
let scopes = db.expr_scopes(owner);
|
||||
let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>();
|
||||
for scope in scope_chain.into_iter().rev() {
|
||||
|
@ -455,7 +517,7 @@ impl Resolver {
|
|||
|
||||
fn push_generic_params_scope(self, db: &impl DefDatabase, def: GenericDefId) -> Resolver {
|
||||
let params = db.generic_params(def);
|
||||
if params.params.is_empty() {
|
||||
if params.types.is_empty() {
|
||||
self
|
||||
} else {
|
||||
self.push_scope(Scope::GenericParams { def, params })
|
||||
|
@ -474,6 +536,10 @@ impl Resolver {
|
|||
self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id }))
|
||||
}
|
||||
|
||||
fn push_local_items_scope(self, body: Arc<Body>) -> Resolver {
|
||||
self.push_scope(Scope::LocalItemsScope(body))
|
||||
}
|
||||
|
||||
fn push_expr_scope(
|
||||
self,
|
||||
owner: DefWithBodyId,
|
||||
|
@ -498,7 +564,7 @@ impl HasResolver for ModuleId {
|
|||
|
||||
impl HasResolver for TraitId {
|
||||
fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
||||
self.module(db).resolver(db).push_generic_params_scope(db, self.into())
|
||||
self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -518,16 +584,6 @@ impl HasResolver for FunctionId {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasResolver for DefWithBodyId {
|
||||
fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
||||
match self {
|
||||
DefWithBodyId::ConstId(c) => c.resolver(db),
|
||||
DefWithBodyId::FunctionId(f) => f.resolver(db),
|
||||
DefWithBodyId::StaticId(s) => s.resolver(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasResolver for ConstId {
|
||||
fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
||||
self.lookup(db).container.resolver(db)
|
||||
|
@ -546,12 +602,41 @@ impl HasResolver for TypeAliasId {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasResolver for ImplId {
|
||||
fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
||||
self.lookup(db)
|
||||
.container
|
||||
.resolver(db)
|
||||
.push_generic_params_scope(db, self.into())
|
||||
.push_impl_block_scope(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasResolver for DefWithBodyId {
|
||||
fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
||||
match self {
|
||||
DefWithBodyId::ConstId(c) => c.resolver(db),
|
||||
DefWithBodyId::FunctionId(f) => f.resolver(db),
|
||||
DefWithBodyId::StaticId(s) => s.resolver(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasResolver for ContainerId {
|
||||
fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
||||
match self {
|
||||
ContainerId::TraitId(it) => it.resolver(db),
|
||||
ContainerId::ImplId(it) => it.resolver(db),
|
||||
ContainerId::ModuleId(it) => it.resolver(db),
|
||||
ContainerId::DefWithBodyId(it) => it.resolver(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasResolver for AssocContainerId {
|
||||
fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
||||
match self {
|
||||
AssocContainerId::ContainerId(it) => it.resolver(db),
|
||||
AssocContainerId::TraitId(it) => it.resolver(db),
|
||||
AssocContainerId::ImplId(it) => it.resolver(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -570,11 +655,12 @@ impl HasResolver for GenericDefId {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasResolver for ImplId {
|
||||
impl HasResolver for VariantId {
|
||||
fn resolver(self, db: &impl DefDatabase) -> Resolver {
|
||||
self.module(db)
|
||||
.resolver(db)
|
||||
.push_generic_params_scope(db, self.into())
|
||||
.push_impl_block_scope(self)
|
||||
match self {
|
||||
VariantId::EnumVariantId(it) => it.parent.resolver(db),
|
||||
VariantId::StructId(it) => it.resolver(db),
|
||||
VariantId::UnionId(it) => it.resolver(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
36
crates/ra_hir_def/src/src.rs
Normal file
36
crates/ra_hir_def/src/src.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
//! Utilities for mapping between hir IDs and the surface syntax.
|
||||
|
||||
use hir_expand::InFile;
|
||||
use ra_arena::map::ArenaMap;
|
||||
use ra_syntax::AstNode;
|
||||
|
||||
use crate::{db::DefDatabase, AssocItemLoc, ItemLoc};
|
||||
|
||||
pub trait HasSource {
|
||||
type Value;
|
||||
fn source(&self, db: &impl DefDatabase) -> InFile<Self::Value>;
|
||||
}
|
||||
|
||||
impl<N: AstNode> HasSource for AssocItemLoc<N> {
|
||||
type Value = N;
|
||||
|
||||
fn source(&self, db: &impl DefDatabase) -> InFile<N> {
|
||||
let node = self.ast_id.to_node(db);
|
||||
InFile::new(self.ast_id.file_id, node)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> HasSource for ItemLoc<N> {
|
||||
type Value = N;
|
||||
|
||||
fn source(&self, db: &impl DefDatabase) -> InFile<N> {
|
||||
let node = self.ast_id.to_node(db);
|
||||
InFile::new(self.ast_id.file_id, node)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasChildSource {
|
||||
type ChildId;
|
||||
type Value;
|
||||
fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>>;
|
||||
}
|
|
@ -18,10 +18,6 @@ pub(crate) struct Trace<ID: ArenaId, T, V> {
|
|||
}
|
||||
|
||||
impl<ID: ra_arena::ArenaId + Copy, T, V> Trace<ID, T, V> {
|
||||
pub(crate) fn new() -> Trace<ID, T, V> {
|
||||
Trace { arena: Some(Arena::default()), map: Some(ArenaMap::default()), len: 0 }
|
||||
}
|
||||
|
||||
pub(crate) fn new_for_arena() -> Trace<ID, T, V> {
|
||||
Trace { arena: Some(Arena::default()), map: None, len: 0 }
|
||||
}
|
||||
|
@ -52,8 +48,4 @@ impl<ID: ra_arena::ArenaId + Copy, T, V> Trace<ID, T, V> {
|
|||
pub(crate) fn into_map(mut self) -> ArenaMap<ID, V> {
|
||||
self.map.take().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn into_arena_and_map(mut self) -> (Arena<ID, T>, ArenaMap<ID, V>) {
|
||||
(self.arena.take().unwrap(), self.map.take().unwrap())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ doctest = false
|
|||
|
||||
[dependencies]
|
||||
log = "0.4.5"
|
||||
either = "1.5"
|
||||
|
||||
ra_arena = { path = "../ra_arena" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
|
|
|
@ -39,6 +39,16 @@ impl<N: AstNode> Hash for FileAstId<N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> FileAstId<N> {
|
||||
// Can't make this a From implementation because of coherence
|
||||
pub fn upcast<M: AstNode>(self) -> FileAstId<M>
|
||||
where
|
||||
M: From<N>,
|
||||
{
|
||||
FileAstId { raw: self.raw, _ty: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct ErasedFileAstId(RawId);
|
||||
impl_arena_id!(ErasedFileAstId);
|
||||
|
@ -53,7 +63,7 @@ impl 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
|
||||
// By walking the tree in breadth-first order we make sure that parents
|
||||
// get lower ids then children. That is, adding a new child does not
|
||||
// change parent's id. This means that, say, adding a new function to a
|
||||
// trait does not change ids of top-level items, which helps caching.
|
||||
|
|
321
crates/ra_hir_expand/src/builtin_derive.rs
Normal file
321
crates/ra_hir_expand/src/builtin_derive.rs
Normal file
|
@ -0,0 +1,321 @@
|
|||
//! Builtin derives.
|
||||
|
||||
use log::debug;
|
||||
|
||||
use ra_parser::FragmentKind;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, ModuleItemOwner, NameOwner, TypeParamsOwner},
|
||||
match_ast,
|
||||
};
|
||||
|
||||
use crate::db::AstDatabase;
|
||||
use crate::{name, quote, MacroCallId, MacroDefId, MacroDefKind};
|
||||
|
||||
macro_rules! register_builtin {
|
||||
( $($trait:ident => $expand:ident),* ) => {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum BuiltinDeriveExpander {
|
||||
$($trait),*
|
||||
}
|
||||
|
||||
impl BuiltinDeriveExpander {
|
||||
pub fn expand(
|
||||
&self,
|
||||
db: &dyn AstDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
let expander = match *self {
|
||||
$( BuiltinDeriveExpander::$trait => $expand, )*
|
||||
};
|
||||
expander(db, id, tt)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_builtin_derive(ident: &name::Name) -> Option<MacroDefId> {
|
||||
let kind = match ident {
|
||||
$( id if id == &name::name![$trait] => BuiltinDeriveExpander::$trait, )*
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(kind) })
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
register_builtin! {
|
||||
Copy => copy_expand,
|
||||
Clone => clone_expand,
|
||||
Default => default_expand,
|
||||
Debug => debug_expand,
|
||||
Hash => hash_expand,
|
||||
Ord => ord_expand,
|
||||
PartialOrd => partial_ord_expand,
|
||||
Eq => eq_expand,
|
||||
PartialEq => partial_eq_expand
|
||||
}
|
||||
|
||||
struct BasicAdtInfo {
|
||||
name: tt::Ident,
|
||||
type_params: usize,
|
||||
}
|
||||
|
||||
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
|
||||
let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, FragmentKind::Items)?; // FragmentKind::Items doesn't parse attrs?
|
||||
let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
|
||||
debug!("derive node didn't parse");
|
||||
mbe::ExpandError::UnexpectedToken
|
||||
})?;
|
||||
let item = macro_items.items().next().ok_or_else(|| {
|
||||
debug!("no module item parsed");
|
||||
mbe::ExpandError::NoMatchingRule
|
||||
})?;
|
||||
let node = item.syntax();
|
||||
let (name, params) = match_ast! {
|
||||
match node {
|
||||
ast::StructDef(it) => { (it.name(), it.type_param_list()) },
|
||||
ast::EnumDef(it) => { (it.name(), it.type_param_list()) },
|
||||
ast::UnionDef(it) => { (it.name(), it.type_param_list()) },
|
||||
_ => {
|
||||
debug!("unexpected node is {:?}", node);
|
||||
return Err(mbe::ExpandError::ConversionError)
|
||||
},
|
||||
}
|
||||
};
|
||||
let name = name.ok_or_else(|| {
|
||||
debug!("parsed item has no name");
|
||||
mbe::ExpandError::NoMatchingRule
|
||||
})?;
|
||||
let name_token_id = token_map.token_by_range(name.syntax().text_range()).ok_or_else(|| {
|
||||
debug!("name token not found");
|
||||
mbe::ExpandError::ConversionError
|
||||
})?;
|
||||
let name_token = tt::Ident { id: name_token_id, text: name.text().clone() };
|
||||
let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count());
|
||||
Ok(BasicAdtInfo { name: name_token, type_params })
|
||||
}
|
||||
|
||||
fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
|
||||
let mut result = Vec::<tt::TokenTree>::new();
|
||||
result.push(
|
||||
tt::Leaf::Punct(tt::Punct {
|
||||
char: '<',
|
||||
spacing: tt::Spacing::Alone,
|
||||
id: tt::TokenId::unspecified(),
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
for i in 0..n {
|
||||
if i > 0 {
|
||||
result.push(
|
||||
tt::Leaf::Punct(tt::Punct {
|
||||
char: ',',
|
||||
spacing: tt::Spacing::Alone,
|
||||
id: tt::TokenId::unspecified(),
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
result.push(
|
||||
tt::Leaf::Ident(tt::Ident {
|
||||
id: tt::TokenId::unspecified(),
|
||||
text: format!("T{}", i).into(),
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
result.extend(bound.iter().cloned());
|
||||
}
|
||||
result.push(
|
||||
tt::Leaf::Punct(tt::Punct {
|
||||
char: '>',
|
||||
spacing: tt::Spacing::Alone,
|
||||
id: tt::TokenId::unspecified(),
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
result
|
||||
}
|
||||
|
||||
fn expand_simple_derive(
|
||||
tt: &tt::Subtree,
|
||||
trait_path: tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
let info = parse_adt(tt)?;
|
||||
let name = info.name;
|
||||
let trait_path_clone = trait_path.token_trees.clone();
|
||||
let bound = (quote! { : ##trait_path_clone }).token_trees;
|
||||
let type_params = make_type_args(info.type_params, bound);
|
||||
let type_args = make_type_args(info.type_params, Vec::new());
|
||||
let trait_path = trait_path.token_trees;
|
||||
let expanded = quote! {
|
||||
impl ##type_params ##trait_path for #name ##type_args {}
|
||||
};
|
||||
Ok(expanded)
|
||||
}
|
||||
|
||||
fn copy_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
expand_simple_derive(tt, quote! { std::marker::Copy })
|
||||
}
|
||||
|
||||
fn clone_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
expand_simple_derive(tt, quote! { std::clone::Clone })
|
||||
}
|
||||
|
||||
fn default_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
expand_simple_derive(tt, quote! { std::default::Default })
|
||||
}
|
||||
|
||||
fn debug_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
expand_simple_derive(tt, quote! { std::fmt::Debug })
|
||||
}
|
||||
|
||||
fn hash_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
expand_simple_derive(tt, quote! { std::hash::Hash })
|
||||
}
|
||||
|
||||
fn eq_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
expand_simple_derive(tt, quote! { std::cmp::Eq })
|
||||
}
|
||||
|
||||
fn partial_eq_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
expand_simple_derive(tt, quote! { std::cmp::PartialEq })
|
||||
}
|
||||
|
||||
fn ord_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
expand_simple_derive(tt, quote! { std::cmp::Ord })
|
||||
}
|
||||
|
||||
fn partial_ord_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
expand_simple_derive(tt, quote! { std::cmp::PartialOrd })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{test_db::TestDB, AstId, MacroCallKind, MacroCallLoc};
|
||||
use ra_db::{fixture::WithFixture, SourceDatabase};
|
||||
|
||||
fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String {
|
||||
let (db, file_id) = TestDB::with_single_file(&s);
|
||||
let parsed = db.parse(file_id);
|
||||
let items: Vec<_> =
|
||||
parsed.syntax_node().descendants().filter_map(|it| ast::ModuleItem::cast(it)).collect();
|
||||
|
||||
let ast_id_map = db.ast_id_map(file_id.into());
|
||||
|
||||
// the first one should be a macro_rules
|
||||
let def =
|
||||
MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(expander) };
|
||||
|
||||
let loc = MacroCallLoc {
|
||||
def,
|
||||
kind: MacroCallKind::Attr(AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]))),
|
||||
};
|
||||
|
||||
let id = db.intern_macro(loc);
|
||||
let parsed = db.parse_or_expand(id.as_file()).unwrap();
|
||||
|
||||
// FIXME text() for syntax nodes parsed from token tree looks weird
|
||||
// because there's no whitespace, see below
|
||||
parsed.text().to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy_expand_simple() {
|
||||
let expanded = expand_builtin_derive(
|
||||
r#"
|
||||
#[derive(Copy)]
|
||||
struct Foo;
|
||||
"#,
|
||||
BuiltinDeriveExpander::Copy,
|
||||
);
|
||||
|
||||
assert_eq!(expanded, "impl <>std::marker::CopyforFoo <>{}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy_expand_with_type_params() {
|
||||
let expanded = expand_builtin_derive(
|
||||
r#"
|
||||
#[derive(Copy)]
|
||||
struct Foo<A, B>;
|
||||
"#,
|
||||
BuiltinDeriveExpander::Copy,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
expanded,
|
||||
"impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy_expand_with_lifetimes() {
|
||||
let expanded = expand_builtin_derive(
|
||||
r#"
|
||||
#[derive(Copy)]
|
||||
struct Foo<A, B, 'a, 'b>;
|
||||
"#,
|
||||
BuiltinDeriveExpander::Copy,
|
||||
);
|
||||
|
||||
// We currently just ignore lifetimes
|
||||
|
||||
assert_eq!(
|
||||
expanded,
|
||||
"impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clone_expand() {
|
||||
let expanded = expand_builtin_derive(
|
||||
r#"
|
||||
#[derive(Clone)]
|
||||
struct Foo<A, B>;
|
||||
"#,
|
||||
BuiltinDeriveExpander::Clone,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
expanded,
|
||||
"impl<T0:std::clone::Clone,T1:std::clone::Clone>std::clone::CloneforFoo<T0,T1>{}"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,8 +2,7 @@
|
|||
use crate::db::AstDatabase;
|
||||
use crate::{
|
||||
ast::{self, AstNode},
|
||||
name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind,
|
||||
TextUnit,
|
||||
name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, TextUnit,
|
||||
};
|
||||
|
||||
use crate::quote;
|
||||
|
@ -27,6 +26,13 @@ macro_rules! register_builtin {
|
|||
};
|
||||
expander(db, id, tt)
|
||||
}
|
||||
|
||||
fn by_name(ident: &name::Name) -> Option<BuiltinFnLikeExpander> {
|
||||
match ident {
|
||||
$( id if id == &name::name![$name] => Some(BuiltinFnLikeExpander::$kind), )*
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_builtin_macro(
|
||||
|
@ -34,22 +40,25 @@ macro_rules! register_builtin {
|
|||
krate: CrateId,
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
) -> Option<MacroDefId> {
|
||||
let kind = match ident {
|
||||
$( id if id == &name::$name => BuiltinFnLikeExpander::$kind, )*
|
||||
_ => return None,
|
||||
};
|
||||
let kind = BuiltinFnLikeExpander::by_name(ident)?;
|
||||
|
||||
Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(kind) })
|
||||
Some(MacroDefId { krate: Some(krate), ast_id: Some(ast_id), kind: MacroDefKind::BuiltIn(kind) })
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
register_builtin! {
|
||||
(COLUMN_MACRO, Column) => column_expand,
|
||||
(COMPILE_ERROR_MACRO, CompileError) => compile_error_expand,
|
||||
(FILE_MACRO, File) => file_expand,
|
||||
(LINE_MACRO, Line) => line_expand,
|
||||
(STRINGIFY_MACRO, Stringify) => stringify_expand
|
||||
(column, Column) => column_expand,
|
||||
(compile_error, CompileError) => compile_error_expand,
|
||||
(file, File) => file_expand,
|
||||
(line, Line) => line_expand,
|
||||
(stringify, Stringify) => stringify_expand,
|
||||
(format_args, FormatArgs) => format_args_expand,
|
||||
(env, Env) => env_expand,
|
||||
(option_env, OptionEnv) => option_env_expand,
|
||||
// format_args_nl only differs in that it adds a newline in the end,
|
||||
// so we use the same stub expansion for now
|
||||
(format_args_nl, FormatArgsNl) => format_args_expand
|
||||
}
|
||||
|
||||
fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
|
||||
|
@ -82,12 +91,11 @@ fn line_expand(
|
|||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
let macro_call = loc.ast_id.to_node(db);
|
||||
|
||||
let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
|
||||
let arg_start = arg.syntax().text_range().start();
|
||||
let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
|
||||
let arg_start = arg.text_range().start();
|
||||
|
||||
let file = id.as_file(MacroFileKind::Expr);
|
||||
let file = id.as_file();
|
||||
let line_num = to_line_number(db, file, arg_start);
|
||||
|
||||
let expanded = quote! {
|
||||
|
@ -103,11 +111,10 @@ fn stringify_expand(
|
|||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
let macro_call = loc.ast_id.to_node(db);
|
||||
|
||||
let macro_content = {
|
||||
let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
|
||||
let macro_args = arg.syntax().clone();
|
||||
let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
|
||||
let macro_args = arg;
|
||||
let text = macro_args.text();
|
||||
let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')');
|
||||
text.slice(without_parens).to_string()
|
||||
|
@ -120,6 +127,28 @@ fn stringify_expand(
|
|||
Ok(expanded)
|
||||
}
|
||||
|
||||
fn env_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
// dummy implementation for type-checking purposes
|
||||
let expanded = quote! { "" };
|
||||
|
||||
Ok(expanded)
|
||||
}
|
||||
|
||||
fn option_env_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
// dummy implementation for type-checking purposes
|
||||
let expanded = quote! { std::option::Option::None::<&str> };
|
||||
|
||||
Ok(expanded)
|
||||
}
|
||||
|
||||
fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
|
||||
// FIXME: Use expansion info
|
||||
let file_id = file.original_file(db);
|
||||
|
@ -137,7 +166,7 @@ fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize
|
|||
if c == '\n' {
|
||||
break;
|
||||
}
|
||||
col_num = col_num + 1;
|
||||
col_num += 1;
|
||||
}
|
||||
col_num
|
||||
}
|
||||
|
@ -148,12 +177,15 @@ fn column_expand(
|
|||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
let macro_call = loc.ast_id.to_node(db);
|
||||
let macro_call = match loc.kind {
|
||||
crate::MacroCallKind::FnLike(ast_id) => ast_id.to_node(db),
|
||||
_ => panic!("column macro called as attr"),
|
||||
};
|
||||
|
||||
let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
|
||||
let col_start = macro_call.syntax().text_range().start();
|
||||
|
||||
let file = id.as_file(MacroFileKind::Expr);
|
||||
let file = id.as_file();
|
||||
let col_num = to_col_number(db, file, col_start);
|
||||
|
||||
let expanded = quote! {
|
||||
|
@ -164,15 +196,10 @@ fn column_expand(
|
|||
}
|
||||
|
||||
fn file_expand(
|
||||
db: &dyn AstDatabase,
|
||||
id: MacroCallId,
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
let macro_call = loc.ast_id.to_node(db);
|
||||
|
||||
let _ = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
|
||||
|
||||
// FIXME: RA purposefully lacks knowledge of absolute file names
|
||||
// so just return "".
|
||||
let file_name = "";
|
||||
|
@ -204,13 +231,56 @@ fn compile_error_expand(
|
|||
Err(mbe::ExpandError::BindingError("Must be a string".into()))
|
||||
}
|
||||
|
||||
fn format_args_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
// We expand `format_args!("", a1, a2)` to
|
||||
// ```
|
||||
// std::fmt::Arguments::new_v1(&[], &[
|
||||
// std::fmt::ArgumentV1::new(&arg1,std::fmt::Display::fmt),
|
||||
// std::fmt::ArgumentV1::new(&arg2,std::fmt::Display::fmt),
|
||||
// ])
|
||||
// ```,
|
||||
// which is still not really correct, but close enough for now
|
||||
let mut args = Vec::new();
|
||||
let mut current = Vec::new();
|
||||
for tt in tt.token_trees.iter().cloned() {
|
||||
match tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
|
||||
args.push(current);
|
||||
current = Vec::new();
|
||||
}
|
||||
_ => {
|
||||
current.push(tt);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !current.is_empty() {
|
||||
args.push(current);
|
||||
}
|
||||
if args.is_empty() {
|
||||
return Err(mbe::ExpandError::NoMatchingRule);
|
||||
}
|
||||
let _format_string = args.remove(0);
|
||||
let arg_tts = args.into_iter().flat_map(|arg| {
|
||||
quote! { std::fmt::ArgumentV1::new(&(##arg), std::fmt::Display::fmt), }
|
||||
}.token_trees).collect::<Vec<_>>();
|
||||
let expanded = quote! {
|
||||
std::fmt::Arguments::new_v1(&[], &[##arg_tts])
|
||||
};
|
||||
Ok(expanded)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{test_db::TestDB, MacroCallLoc};
|
||||
use crate::{name::AsName, test_db::TestDB, MacroCallKind, MacroCallLoc};
|
||||
use ra_db::{fixture::WithFixture, SourceDatabase};
|
||||
use ra_syntax::ast::NameOwner;
|
||||
|
||||
fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String {
|
||||
fn expand_builtin_macro(s: &str) -> String {
|
||||
let (db, file_id) = TestDB::with_single_file(&s);
|
||||
let parsed = db.parse(file_id);
|
||||
let macro_calls: Vec<_> =
|
||||
|
@ -218,20 +288,26 @@ mod tests {
|
|||
|
||||
let ast_id_map = db.ast_id_map(file_id.into());
|
||||
|
||||
let expander =
|
||||
BuiltinFnLikeExpander::by_name(¯o_calls[0].name().unwrap().as_name()).unwrap();
|
||||
|
||||
// the first one should be a macro_rules
|
||||
let def = MacroDefId {
|
||||
krate: CrateId(0),
|
||||
ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0])),
|
||||
krate: Some(CrateId(0)),
|
||||
ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))),
|
||||
kind: MacroDefKind::BuiltIn(expander),
|
||||
};
|
||||
|
||||
let loc = MacroCallLoc {
|
||||
def,
|
||||
ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[1])),
|
||||
kind: MacroCallKind::FnLike(AstId::new(
|
||||
file_id.into(),
|
||||
ast_id_map.ast_id(¯o_calls[1]),
|
||||
)),
|
||||
};
|
||||
|
||||
let id = db.intern_macro(loc);
|
||||
let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Expr)).unwrap();
|
||||
let parsed = db.parse_or_expand(id.as_file()).unwrap();
|
||||
|
||||
parsed.text().to_string()
|
||||
}
|
||||
|
@ -244,10 +320,9 @@ mod tests {
|
|||
macro_rules! column {() => {}}
|
||||
column!()
|
||||
"#,
|
||||
BuiltinFnLikeExpander::Column,
|
||||
);
|
||||
|
||||
assert_eq!(expanded, "9");
|
||||
assert_eq!(expanded, "13");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -258,7 +333,6 @@ mod tests {
|
|||
macro_rules! line {() => {}}
|
||||
line!()
|
||||
"#,
|
||||
BuiltinFnLikeExpander::Line,
|
||||
);
|
||||
|
||||
assert_eq!(expanded, "4");
|
||||
|
@ -272,12 +346,37 @@ mod tests {
|
|||
macro_rules! stringify {() => {}}
|
||||
stringify!(a b c)
|
||||
"#,
|
||||
BuiltinFnLikeExpander::Stringify,
|
||||
);
|
||||
|
||||
assert_eq!(expanded, "\"a b c\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_expand() {
|
||||
let expanded = expand_builtin_macro(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! env {() => {}}
|
||||
env!("TEST_ENV_VAR")
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_eq!(expanded, "\"\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_option_env_expand() {
|
||||
let expanded = expand_builtin_macro(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! option_env {() => {}}
|
||||
option_env!("TEST_ENV_VAR")
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_eq!(expanded, "std::option::Option::None:: <&str>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_expand() {
|
||||
let expanded = expand_builtin_macro(
|
||||
|
@ -286,7 +385,6 @@ mod tests {
|
|||
macro_rules! file {() => {}}
|
||||
file!()
|
||||
"#,
|
||||
BuiltinFnLikeExpander::File,
|
||||
);
|
||||
|
||||
assert_eq!(expanded, "\"\"");
|
||||
|
@ -303,9 +401,27 @@ mod tests {
|
|||
}
|
||||
compile_error!("error!");
|
||||
"#,
|
||||
BuiltinFnLikeExpander::CompileError,
|
||||
);
|
||||
|
||||
assert_eq!(expanded, r#"loop{"error!"}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_args_expand() {
|
||||
let expanded = expand_builtin_macro(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! format_args {
|
||||
($fmt:expr) => ({ /* compiler built-in */ });
|
||||
($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
|
||||
}
|
||||
format_args!("{} {:?}", arg1(a, b, c), arg2);
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
expanded,
|
||||
r#"std::fmt::Arguments::new_v1(&[] ,&[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,17 +6,18 @@ use mbe::MacroRules;
|
|||
use ra_db::{salsa, SourceDatabase};
|
||||
use ra_parser::FragmentKind;
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{AstNode, Parse, SyntaxNode};
|
||||
use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
ast_id_map::AstIdMap, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId,
|
||||
MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, MacroFileKind,
|
||||
ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr,
|
||||
MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum TokenExpander {
|
||||
MacroRules(mbe::MacroRules),
|
||||
Builtin(BuiltinFnLikeExpander),
|
||||
BuiltinDerive(BuiltinDeriveExpander),
|
||||
}
|
||||
|
||||
impl TokenExpander {
|
||||
|
@ -29,6 +30,7 @@ impl TokenExpander {
|
|||
match self {
|
||||
TokenExpander::MacroRules(it) => it.expand(tt),
|
||||
TokenExpander::Builtin(it) => it.expand(db, id, tt),
|
||||
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,13 +38,15 @@ impl TokenExpander {
|
|||
match self {
|
||||
TokenExpander::MacroRules(it) => it.map_id_down(id),
|
||||
TokenExpander::Builtin(..) => id,
|
||||
TokenExpander::BuiltinDerive(..) => id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
|
||||
match self {
|
||||
TokenExpander::MacroRules(it) => it.map_id_up(id),
|
||||
TokenExpander::Builtin(..) => (id, mbe::Origin::Def),
|
||||
TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
|
||||
TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +80,7 @@ pub(crate) fn macro_def(
|
|||
) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
|
||||
match id.kind {
|
||||
MacroDefKind::Declarative => {
|
||||
let macro_call = id.ast_id.to_node(db);
|
||||
let macro_call = id.ast_id?.to_node(db);
|
||||
let arg = macro_call.token_tree()?;
|
||||
let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
|
||||
log::warn!("fail on macro_def to token tree: {:#?}", arg);
|
||||
|
@ -89,7 +93,10 @@ pub(crate) fn macro_def(
|
|||
Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
|
||||
}
|
||||
MacroDefKind::BuiltIn(expander) => {
|
||||
Some(Arc::new((TokenExpander::Builtin(expander.clone()), mbe::TokenMap::default())))
|
||||
Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default())))
|
||||
}
|
||||
MacroDefKind::BuiltInDerive(expander) => {
|
||||
Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,9 +106,8 @@ pub(crate) fn macro_arg(
|
|||
id: MacroCallId,
|
||||
) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
let macro_call = loc.ast_id.to_node(db);
|
||||
let arg = macro_call.token_tree()?;
|
||||
let (tt, tmap) = mbe::ast_to_token_tree(&arg)?;
|
||||
let arg = loc.kind.arg(db)?;
|
||||
let (tt, tmap) = mbe::syntax_node_to_token_tree(&arg)?;
|
||||
Some(Arc::new((tt, tmap)))
|
||||
}
|
||||
|
||||
|
@ -148,11 +154,43 @@ pub(crate) fn parse_macro(
|
|||
})
|
||||
.ok()?;
|
||||
|
||||
let fragment_kind = match macro_file.macro_file_kind {
|
||||
MacroFileKind::Items => FragmentKind::Items,
|
||||
MacroFileKind::Expr => FragmentKind::Expr,
|
||||
MacroFileKind::Statements => FragmentKind::Statements,
|
||||
};
|
||||
let fragment_kind = to_fragment_kind(db, macro_call_id);
|
||||
|
||||
let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?;
|
||||
Some((parse, Arc::new(rev_token_map)))
|
||||
}
|
||||
|
||||
/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.
|
||||
/// FIXME: Not completed
|
||||
fn to_fragment_kind(db: &dyn AstDatabase, macro_call_id: MacroCallId) -> FragmentKind {
|
||||
let syn = db.lookup_intern_macro(macro_call_id).kind.node(db).value;
|
||||
|
||||
let parent = match syn.parent() {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
// FIXME:
|
||||
// If it is root, which means the parent HirFile
|
||||
// MacroKindFile must be non-items
|
||||
// return expr now.
|
||||
return FragmentKind::Expr;
|
||||
}
|
||||
};
|
||||
|
||||
match parent.kind() {
|
||||
MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
|
||||
LET_STMT => {
|
||||
// FIXME: Handle Pattern
|
||||
FragmentKind::Expr
|
||||
}
|
||||
// FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that
|
||||
EXPR_STMT | BLOCK => FragmentKind::Expr,
|
||||
ARG_LIST => FragmentKind::Expr,
|
||||
TRY_EXPR => FragmentKind::Expr,
|
||||
TUPLE_EXPR => FragmentKind::Expr,
|
||||
ITEM_LIST => FragmentKind::Items,
|
||||
_ => {
|
||||
// Unknown , Just guess it is `Items`
|
||||
FragmentKind::Items
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,11 +18,11 @@ use std::{any::Any, fmt};
|
|||
|
||||
use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange};
|
||||
|
||||
use crate::{db::AstDatabase, Source};
|
||||
use crate::{db::AstDatabase, InFile};
|
||||
|
||||
pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
|
||||
fn message(&self) -> String;
|
||||
fn source(&self) -> Source<SyntaxNodePtr>;
|
||||
fn source(&self) -> InFile<SyntaxNodePtr>;
|
||||
fn highlight_range(&self) -> TextRange {
|
||||
self.source().value.range()
|
||||
}
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Either<A, B> {
|
||||
A(A),
|
||||
B(B),
|
||||
}
|
||||
|
||||
impl<A, B> Either<A, B> {
|
||||
pub fn either<R, F1, F2>(self, f1: F1, f2: F2) -> R
|
||||
where
|
||||
F1: FnOnce(A) -> R,
|
||||
F2: FnOnce(B) -> R,
|
||||
{
|
||||
match self {
|
||||
Either::A(a) => f1(a),
|
||||
Either::B(b) => f2(b),
|
||||
}
|
||||
}
|
||||
pub fn map<U, V, F1, F2>(self, f1: F1, f2: F2) -> Either<U, V>
|
||||
where
|
||||
F1: FnOnce(A) -> U,
|
||||
F2: FnOnce(B) -> V,
|
||||
{
|
||||
match self {
|
||||
Either::A(a) => Either::A(f1(a)),
|
||||
Either::B(b) => Either::B(f2(b)),
|
||||
}
|
||||
}
|
||||
pub fn map_a<U, F>(self, f: F) -> Either<U, B>
|
||||
where
|
||||
F: FnOnce(A) -> U,
|
||||
{
|
||||
self.map(f, |it| it)
|
||||
}
|
||||
pub fn a(self) -> Option<A> {
|
||||
match self {
|
||||
Either::A(it) => Some(it),
|
||||
Either::B(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn b(self) -> Option<B> {
|
||||
match self {
|
||||
Either::A(_) => None,
|
||||
Either::B(it) => Some(it),
|
||||
}
|
||||
}
|
||||
pub fn as_ref(&self) -> Either<&A, &B> {
|
||||
match self {
|
||||
Either::A(it) => Either::A(it),
|
||||
Either::B(it) => Either::B(it),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,12 +2,12 @@
|
|||
//!
|
||||
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
|
||||
//! this moment, this is horribly incomplete and handles only `$crate`.
|
||||
use either::Either;
|
||||
use ra_db::CrateId;
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::{
|
||||
db::AstDatabase,
|
||||
either::Either,
|
||||
name::{AsName, Name},
|
||||
HirFileId, HirFileIdRepr, MacroDefKind,
|
||||
};
|
||||
|
@ -25,8 +25,9 @@ impl Hygiene {
|
|||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let loc = db.lookup_intern_macro(macro_file.macro_call_id);
|
||||
match loc.def.kind {
|
||||
MacroDefKind::Declarative => Some(loc.def.krate),
|
||||
MacroDefKind::Declarative => loc.def.krate,
|
||||
MacroDefKind::BuiltIn(_) => None,
|
||||
MacroDefKind::BuiltInDerive(_) => None,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -41,9 +42,9 @@ impl Hygiene {
|
|||
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);
|
||||
return Either::Right(def_crate);
|
||||
}
|
||||
}
|
||||
Either::A(name_ref.as_name())
|
||||
Either::Left(name_ref.as_name())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
pub mod db;
|
||||
pub mod ast_id_map;
|
||||
pub mod either;
|
||||
pub mod name;
|
||||
pub mod hygiene;
|
||||
pub mod diagnostics;
|
||||
pub mod builtin_derive;
|
||||
pub mod builtin_macro;
|
||||
pub mod quote;
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::hash::Hash;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ra_db::{salsa, CrateId, FileId};
|
||||
|
@ -24,6 +24,7 @@ use ra_syntax::{
|
|||
};
|
||||
|
||||
use crate::ast_id_map::FileAstId;
|
||||
use crate::builtin_derive::BuiltinDeriveExpander;
|
||||
use crate::builtin_macro::BuiltinFnLikeExpander;
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -70,7 +71,18 @@ impl HirFileId {
|
|||
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)
|
||||
loc.kind.file_id().original_file(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If this is a macro call, returns the syntax node of the call.
|
||||
pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
|
||||
match self.0 {
|
||||
HirFileIdRepr::FileId(_) => None,
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let loc = db.lookup_intern_macro(macro_file.macro_call_id);
|
||||
Some(loc.kind.node(db))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,17 +94,17 @@ impl HirFileId {
|
|||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
|
||||
|
||||
let arg_tt = loc.ast_id.to_node(db).token_tree()?;
|
||||
let def_tt = loc.def.ast_id.to_node(db).token_tree()?;
|
||||
let arg_tt = loc.kind.arg(db)?;
|
||||
let def_tt = loc.def.ast_id?.to_node(db).token_tree()?;
|
||||
|
||||
let macro_def = db.macro_def(loc.def)?;
|
||||
let (parse, exp_map) = db.parse_macro(macro_file)?;
|
||||
let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
|
||||
|
||||
Some(ExpansionInfo {
|
||||
expanded: Source::new(self, parse.syntax_node()),
|
||||
arg: Source::new(loc.ast_id.file_id, arg_tt),
|
||||
def: Source::new(loc.ast_id.file_id, def_tt),
|
||||
expanded: InFile::new(self, parse.syntax_node()),
|
||||
arg: InFile::new(loc.kind.file_id(), arg_tt),
|
||||
def: InFile::new(loc.def.ast_id?.file_id, def_tt),
|
||||
macro_arg,
|
||||
macro_def,
|
||||
exp_map,
|
||||
|
@ -105,14 +117,6 @@ impl HirFileId {
|
|||
#[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,
|
||||
Statements,
|
||||
}
|
||||
|
||||
/// `MacroCallId` identifies a particular macro invocation, like
|
||||
|
@ -130,18 +134,20 @@ impl salsa::InternKey for MacroCallId {
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct MacroDefId {
|
||||
pub krate: CrateId,
|
||||
pub ast_id: AstId<ast::MacroCall>,
|
||||
// FIXME: krate and ast_id are currently optional because we don't have a
|
||||
// definition location for built-in derives. There is one, though: the
|
||||
// standard library defines them. The problem is that it uses the new
|
||||
// `macro` syntax for this, which we don't support yet. As soon as we do
|
||||
// (which will probably require touching this code), we can instead use
|
||||
// that (and also remove the hacks for resolving built-in derives).
|
||||
pub krate: Option<CrateId>,
|
||||
pub ast_id: Option<AstId<ast::MacroCall>>,
|
||||
pub kind: MacroDefKind,
|
||||
}
|
||||
|
||||
impl MacroDefId {
|
||||
pub fn as_call_id(
|
||||
self,
|
||||
db: &dyn db::AstDatabase,
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
) -> MacroCallId {
|
||||
db.intern_macro(MacroCallLoc { def: self, ast_id })
|
||||
pub fn as_call_id(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> MacroCallId {
|
||||
db.intern_macro(MacroCallLoc { def: self, kind })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,64 +155,103 @@ impl MacroDefId {
|
|||
pub enum MacroDefKind {
|
||||
Declarative,
|
||||
BuiltIn(BuiltinFnLikeExpander),
|
||||
// FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
|
||||
BuiltInDerive(BuiltinDeriveExpander),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MacroCallLoc {
|
||||
pub(crate) def: MacroDefId,
|
||||
pub(crate) ast_id: AstId<ast::MacroCall>,
|
||||
pub(crate) kind: MacroCallKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum MacroCallKind {
|
||||
FnLike(AstId<ast::MacroCall>),
|
||||
Attr(AstId<ast::ModuleItem>),
|
||||
}
|
||||
|
||||
impl MacroCallKind {
|
||||
pub fn file_id(&self) -> HirFileId {
|
||||
match self {
|
||||
MacroCallKind::FnLike(ast_id) => ast_id.file_id,
|
||||
MacroCallKind::Attr(ast_id) => ast_id.file_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
|
||||
match self {
|
||||
MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()),
|
||||
MacroCallKind::Attr(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
|
||||
match self {
|
||||
MacroCallKind::FnLike(ast_id) => {
|
||||
Some(ast_id.to_node(db).token_tree()?.syntax().clone())
|
||||
}
|
||||
MacroCallKind::Attr(ast_id) => Some(ast_id.to_node(db).syntax().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
pub fn as_file(self) -> HirFileId {
|
||||
MacroFile { macro_call_id: self }.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExpansionInfo {
|
||||
expanded: Source<SyntaxNode>,
|
||||
arg: Source<ast::TokenTree>,
|
||||
def: Source<ast::TokenTree>,
|
||||
expanded: InFile<SyntaxNode>,
|
||||
arg: InFile<SyntaxNode>,
|
||||
def: InFile<ast::TokenTree>,
|
||||
|
||||
macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
|
||||
macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
|
||||
exp_map: Arc<mbe::TokenMap>,
|
||||
}
|
||||
|
||||
pub use mbe::Origin;
|
||||
|
||||
impl ExpansionInfo {
|
||||
pub fn map_token_down(&self, token: Source<&SyntaxToken>) -> Option<Source<SyntaxToken>> {
|
||||
pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
|
||||
Some(self.arg.with_value(self.arg.value.parent()?))
|
||||
}
|
||||
|
||||
pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> {
|
||||
assert_eq!(token.file_id, self.arg.file_id);
|
||||
let range =
|
||||
token.value.text_range().checked_sub(self.arg.value.syntax().text_range().start())?;
|
||||
let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
|
||||
let token_id = self.macro_arg.1.token_by_range(range)?;
|
||||
let token_id = self.macro_def.0.map_id_down(token_id);
|
||||
|
||||
let range = self.exp_map.range_by_token(token_id)?;
|
||||
let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
|
||||
|
||||
let token = algo::find_covering_element(&self.expanded.value, range).into_token()?;
|
||||
|
||||
Some(self.expanded.with_value(token))
|
||||
}
|
||||
|
||||
pub fn map_token_up(&self, token: Source<&SyntaxToken>) -> Option<Source<SyntaxToken>> {
|
||||
pub fn map_token_up(
|
||||
&self,
|
||||
token: InFile<&SyntaxToken>,
|
||||
) -> Option<(InFile<SyntaxToken>, Origin)> {
|
||||
let token_id = self.exp_map.token_by_range(token.value.text_range())?;
|
||||
|
||||
let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
|
||||
let (token_map, tt) = match origin {
|
||||
mbe::Origin::Call => (&self.macro_arg.1, &self.arg),
|
||||
mbe::Origin::Def => (&self.macro_def.1, &self.def),
|
||||
mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
|
||||
mbe::Origin::Def => {
|
||||
(&self.macro_def.1, self.def.as_ref().map(|tt| tt.syntax().clone()))
|
||||
}
|
||||
};
|
||||
|
||||
let range = token_map.range_by_token(token_id)?;
|
||||
let token = algo::find_covering_element(
|
||||
tt.value.syntax(),
|
||||
range + tt.value.syntax().text_range().start(),
|
||||
)
|
||||
let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
|
||||
let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start())
|
||||
.into_token()?;
|
||||
Some(tt.with_value(token))
|
||||
Some((tt.with_value(token), origin))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,76 +259,66 @@ impl ExpansionInfo {
|
|||
///
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
pub type AstId<N> = InFile<FileAstId<N>>;
|
||||
|
||||
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)
|
||||
db.ast_id_map(self.file_id).get(self.value).to_node(&root)
|
||||
}
|
||||
}
|
||||
|
||||
/// `Source<T>` stores a value of `T` inside a particular file/syntax tree.
|
||||
/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
|
||||
///
|
||||
/// Typical usages are:
|
||||
///
|
||||
/// * `Source<SyntaxNode>` -- syntax node in a file
|
||||
/// * `Source<ast::FnDef>` -- ast node in a file
|
||||
/// * `Source<TextUnit>` -- offset in a file
|
||||
/// * `InFile<SyntaxNode>` -- syntax node in a file
|
||||
/// * `InFile<ast::FnDef>` -- ast node in a file
|
||||
/// * `InFile<TextUnit>` -- offset in a file
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub struct Source<T> {
|
||||
pub struct InFile<T> {
|
||||
pub file_id: HirFileId,
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
impl<T> Source<T> {
|
||||
pub fn new(file_id: HirFileId, value: T) -> Source<T> {
|
||||
Source { file_id, value }
|
||||
impl<T> InFile<T> {
|
||||
pub fn new(file_id: HirFileId, value: T) -> InFile<T> {
|
||||
InFile { file_id, value }
|
||||
}
|
||||
|
||||
// Similarly, naming here is stupid...
|
||||
pub fn with_value<U>(&self, value: U) -> Source<U> {
|
||||
Source::new(self.file_id, value)
|
||||
pub fn with_value<U>(&self, value: U) -> InFile<U> {
|
||||
InFile::new(self.file_id, value)
|
||||
}
|
||||
|
||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||
Source::new(self.file_id, f(self.value))
|
||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> InFile<U> {
|
||||
InFile::new(self.file_id, f(self.value))
|
||||
}
|
||||
pub fn as_ref(&self) -> Source<&T> {
|
||||
pub fn as_ref(&self) -> InFile<&T> {
|
||||
self.with_value(&self.value)
|
||||
}
|
||||
pub fn file_syntax(&self, db: &impl db::AstDatabase) -> SyntaxNode {
|
||||
db.parse_or_expand(self.file_id).expect("source created from invalid file")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> InFile<&T> {
|
||||
pub fn cloned(&self) -> InFile<T> {
|
||||
self.with_value(self.value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl InFile<SyntaxNode> {
|
||||
pub fn ancestors_with_macros<'a>(
|
||||
self,
|
||||
db: &'a impl crate::db::AstDatabase,
|
||||
) -> impl Iterator<Item = InFile<SyntaxNode>> + 'a {
|
||||
std::iter::successors(Some(self), move |node| match node.value.parent() {
|
||||
Some(parent) => Some(node.with_value(parent)),
|
||||
None => {
|
||||
let parent_node = node.file_id.call_node(db)?;
|
||||
Some(parent_node)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ impl Name {
|
|||
}
|
||||
|
||||
/// Shortcut to create inline plain text name
|
||||
const fn new_inline_ascii(len: usize, text: &[u8]) -> Name {
|
||||
Name::new_text(SmolStr::new_inline_from_ascii(len, text))
|
||||
const fn new_inline_ascii(text: &[u8]) -> Name {
|
||||
Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text))
|
||||
}
|
||||
|
||||
/// Resolve a name from the text of token.
|
||||
|
@ -83,6 +83,12 @@ impl AsName for ast::Name {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsName for tt::Ident {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::resolve(&self.text)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsName for ast::FieldKind {
|
||||
fn as_name(&self) -> Name {
|
||||
match self {
|
||||
|
@ -98,52 +104,102 @@ impl AsName for ra_db::Dependency {
|
|||
}
|
||||
}
|
||||
|
||||
pub mod known {
|
||||
macro_rules! known_names {
|
||||
($($ident:ident),* $(,)?) => {
|
||||
$(
|
||||
#[allow(bad_style)]
|
||||
pub const $ident: super::Name =
|
||||
super::Name::new_inline_ascii(stringify!($ident).as_bytes());
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
known_names!(
|
||||
// Primitives
|
||||
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");
|
||||
|
||||
isize,
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
i128,
|
||||
usize,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
u128,
|
||||
f32,
|
||||
f64,
|
||||
bool,
|
||||
char,
|
||||
str,
|
||||
// Special names
|
||||
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");
|
||||
|
||||
macro_rules,
|
||||
// Components of known path (value or mod name)
|
||||
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");
|
||||
|
||||
std,
|
||||
iter,
|
||||
ops,
|
||||
future,
|
||||
result,
|
||||
boxed,
|
||||
// Components of known path (type name)
|
||||
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");
|
||||
IntoIterator,
|
||||
Item,
|
||||
Try,
|
||||
Ok,
|
||||
Future,
|
||||
Result,
|
||||
Output,
|
||||
Target,
|
||||
Box,
|
||||
RangeFrom,
|
||||
RangeFull,
|
||||
RangeInclusive,
|
||||
RangeToInclusive,
|
||||
RangeTo,
|
||||
Range,
|
||||
Neg,
|
||||
Not,
|
||||
Index,
|
||||
// Builtin macros
|
||||
file,
|
||||
column,
|
||||
compile_error,
|
||||
line,
|
||||
stringify,
|
||||
format_args,
|
||||
format_args_nl,
|
||||
env,
|
||||
option_env,
|
||||
// Builtin derives
|
||||
Copy,
|
||||
Clone,
|
||||
Default,
|
||||
Debug,
|
||||
Hash,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Eq,
|
||||
PartialEq,
|
||||
);
|
||||
|
||||
// Builtin Macros
|
||||
pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file");
|
||||
pub const COLUMN_MACRO: Name = Name::new_inline_ascii(6, b"column");
|
||||
pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error");
|
||||
pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line");
|
||||
pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify");
|
||||
// self/Self cannot be used as an identifier
|
||||
pub const SELF_PARAM: super::Name = super::Name::new_inline_ascii(b"self");
|
||||
pub const SELF_TYPE: super::Name = super::Name::new_inline_ascii(b"Self");
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! name {
|
||||
(self) => {
|
||||
$crate::name::known::SELF_PARAM
|
||||
};
|
||||
(Self) => {
|
||||
$crate::name::known::SELF_TYPE
|
||||
};
|
||||
($ident:ident) => {
|
||||
$crate::name::known::$ident
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub use crate::name;
|
||||
|
|
|
@ -16,7 +16,10 @@ macro_rules! __quote {
|
|||
{
|
||||
let children = $crate::__quote!($($tt)*);
|
||||
let subtree = tt::Subtree {
|
||||
delimiter: tt::Delimiter::$delim,
|
||||
delimiter: Some(tt::Delimiter {
|
||||
kind: tt::DelimiterKind::$delim,
|
||||
id: tt::TokenId::unspecified(),
|
||||
}),
|
||||
token_trees: $crate::quote::IntoTt::to_tokens(children),
|
||||
};
|
||||
subtree
|
||||
|
@ -29,6 +32,7 @@ macro_rules! __quote {
|
|||
tt::Leaf::Punct(tt::Punct {
|
||||
char: $first,
|
||||
spacing: tt::Spacing::Alone,
|
||||
id: tt::TokenId::unspecified(),
|
||||
}).into()
|
||||
]
|
||||
}
|
||||
|
@ -40,10 +44,12 @@ macro_rules! __quote {
|
|||
tt::Leaf::Punct(tt::Punct {
|
||||
char: $first,
|
||||
spacing: tt::Spacing::Joint,
|
||||
id: tt::TokenId::unspecified(),
|
||||
}).into(),
|
||||
tt::Leaf::Punct(tt::Punct {
|
||||
char: $sec,
|
||||
spacing: tt::Spacing::Alone,
|
||||
id: tt::TokenId::unspecified(),
|
||||
}).into()
|
||||
]
|
||||
}
|
||||
|
@ -60,6 +66,15 @@ macro_rules! __quote {
|
|||
}
|
||||
};
|
||||
|
||||
( ## $first:ident $($tail:tt)* ) => {
|
||||
{
|
||||
let mut tokens = $first.into_iter().map($crate::quote::ToTokenTree::to_token).collect::<Vec<tt::TokenTree>>();
|
||||
let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
|
||||
tokens.append(&mut tail_tokens);
|
||||
tokens
|
||||
}
|
||||
};
|
||||
|
||||
// Brace
|
||||
( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) };
|
||||
// Bracket
|
||||
|
@ -85,7 +100,10 @@ macro_rules! __quote {
|
|||
( & ) => {$crate::__quote!(@PUNCT '&')};
|
||||
( , ) => {$crate::__quote!(@PUNCT ',')};
|
||||
( : ) => {$crate::__quote!(@PUNCT ':')};
|
||||
( :: ) => {$crate::__quote!(@PUNCT ':', ':')};
|
||||
( . ) => {$crate::__quote!(@PUNCT '.')};
|
||||
( < ) => {$crate::__quote!(@PUNCT '<')};
|
||||
( > ) => {$crate::__quote!(@PUNCT '>')};
|
||||
|
||||
( $first:tt $($tail:tt)+ ) => {
|
||||
{
|
||||
|
@ -114,7 +132,7 @@ pub(crate) trait IntoTt {
|
|||
|
||||
impl IntoTt for Vec<tt::TokenTree> {
|
||||
fn to_subtree(self) -> tt::Subtree {
|
||||
tt::Subtree { delimiter: tt::Delimiter::None, token_trees: self }
|
||||
tt::Subtree { delimiter: None, token_trees: self }
|
||||
}
|
||||
|
||||
fn to_tokens(self) -> Vec<tt::TokenTree> {
|
||||
|
@ -169,15 +187,15 @@ macro_rules! impl_to_to_tokentrees {
|
|||
}
|
||||
|
||||
impl_to_to_tokentrees! {
|
||||
u32 => self { tt::Literal{text: self.to_string().into()} };
|
||||
usize => self { tt::Literal{text: self.to_string().into()}};
|
||||
i32 => self { tt::Literal{text: self.to_string().into()}};
|
||||
u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
|
||||
usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}};
|
||||
i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}};
|
||||
tt::Leaf => self { self };
|
||||
tt::Literal => self { self };
|
||||
tt::Ident => self { self };
|
||||
tt::Punct => self { self };
|
||||
&str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into()}};
|
||||
String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into()}}
|
||||
&str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}};
|
||||
String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -244,7 +262,13 @@ mod tests {
|
|||
let fields =
|
||||
fields.iter().map(|it| quote!(#it: self.#it.clone(), ).token_trees.clone()).flatten();
|
||||
|
||||
let list = tt::Subtree { delimiter: tt::Delimiter::Brace, token_trees: fields.collect() };
|
||||
let list = tt::Subtree {
|
||||
delimiter: Some(tt::Delimiter {
|
||||
kind: tt::DelimiterKind::Brace,
|
||||
id: tt::TokenId::unspecified(),
|
||||
}),
|
||||
token_trees: fields.collect(),
|
||||
};
|
||||
|
||||
let quoted = quote! {
|
||||
impl Clone for #struct_name {
|
||||
|
|
|
@ -21,10 +21,9 @@ ra_prof = { path = "../ra_prof" }
|
|||
ra_syntax = { path = "../ra_syntax" }
|
||||
test_utils = { path = "../test_utils" }
|
||||
|
||||
# https://github.com/rust-lang/chalk/pull/294
|
||||
chalk-solve = { git = "https://github.com/jackh726/chalk.git", rev = "095cd38a4f16337913bba487f2055b9ca0179f30" }
|
||||
chalk-rust-ir = { git = "https://github.com/jackh726/chalk.git", rev = "095cd38a4f16337913bba487f2055b9ca0179f30" }
|
||||
chalk-ir = { git = "https://github.com/jackh726/chalk.git", rev = "095cd38a4f16337913bba487f2055b9ca0179f30" }
|
||||
chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5" }
|
||||
chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5" }
|
||||
chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5" }
|
||||
|
||||
lalrpop-intern = "0.15.1"
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
use std::iter::successors;
|
||||
|
||||
use hir_def::lang_item::LangItemTarget;
|
||||
use hir_expand::name;
|
||||
use hir_expand::name::name;
|
||||
use log::{info, warn};
|
||||
use ra_db::CrateId;
|
||||
|
||||
use crate::db::HirDatabase;
|
||||
|
||||
use super::{
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
traits::{InEnvironment, Solution},
|
||||
utils::generics,
|
||||
Canonical, Substs, Ty, TypeWalk,
|
||||
};
|
||||
|
||||
|
@ -48,14 +48,14 @@ fn deref_by_trait(
|
|||
krate: CrateId,
|
||||
ty: InEnvironment<&Canonical<Ty>>,
|
||||
) -> Option<Canonical<Ty>> {
|
||||
let deref_trait = match db.lang_item(krate.into(), "deref".into())? {
|
||||
let deref_trait = match db.lang_item(krate, "deref".into())? {
|
||||
LangItemTarget::TraitId(it) => it,
|
||||
_ => return None,
|
||||
};
|
||||
let target = db.trait_data(deref_trait).associated_type_by_name(&name::TARGET_TYPE)?;
|
||||
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
|
||||
|
||||
let generic_params = db.generic_params(target.into());
|
||||
if generic_params.count_params_including_parent() != 1 {
|
||||
let generic_params = generics(db, target.into());
|
||||
if generic_params.len() != 1 {
|
||||
// the Target type + Deref trait should only have one generic parameter,
|
||||
// namely Deref's Self type
|
||||
return None;
|
||||
|
@ -78,7 +78,7 @@ fn deref_by_trait(
|
|||
|
||||
let canonical = super::Canonical { num_vars: 1 + ty.value.num_vars, value: in_env };
|
||||
|
||||
let solution = db.trait_solve(krate.into(), canonical)?;
|
||||
let solution = db.trait_solve(krate, canonical)?;
|
||||
|
||||
match &solution {
|
||||
Solution::Unique(vars) => {
|
||||
|
|
|
@ -10,8 +10,8 @@ use ra_db::{salsa, CrateId};
|
|||
|
||||
use crate::{
|
||||
method_resolution::CrateImplBlocks,
|
||||
traits::{AssocTyValue, Impl},
|
||||
CallableDef, FnSig, GenericPredicate, ImplTy, InferenceResult, Substs, Ty, TyDefId, TypeCtor,
|
||||
traits::{chalk, AssocTyValue, Impl},
|
||||
CallableDef, FnSig, GenericPredicate, InferenceResult, Substs, TraitRef, Ty, TyDefId, TypeCtor,
|
||||
ValueTyDefId,
|
||||
};
|
||||
|
||||
|
@ -22,13 +22,18 @@ pub trait HirDatabase: DefDatabase {
|
|||
fn infer(&self, def: DefWithBodyId) -> Arc<InferenceResult>;
|
||||
|
||||
#[salsa::invoke(crate::lower::ty_query)]
|
||||
#[salsa::cycle(crate::lower::ty_recover)]
|
||||
fn ty(&self, def: TyDefId) -> Ty;
|
||||
|
||||
#[salsa::invoke(crate::lower::value_ty_query)]
|
||||
fn value_ty(&self, def: ValueTyDefId) -> Ty;
|
||||
|
||||
#[salsa::invoke(crate::lower::impl_ty_query)]
|
||||
fn impl_ty(&self, def: ImplId) -> ImplTy;
|
||||
#[salsa::invoke(crate::lower::impl_self_ty_query)]
|
||||
#[salsa::cycle(crate::lower::impl_self_ty_recover)]
|
||||
fn impl_self_ty(&self, def: ImplId) -> Ty;
|
||||
|
||||
#[salsa::invoke(crate::lower::impl_trait_query)]
|
||||
fn impl_trait(&self, def: ImplId) -> Option<TraitRef>;
|
||||
|
||||
#[salsa::invoke(crate::lower::field_types_query)]
|
||||
fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalStructFieldId, Ty>>;
|
||||
|
@ -37,6 +42,7 @@ pub trait HirDatabase: DefDatabase {
|
|||
fn callable_item_signature(&self, def: CallableDef) -> FnSig;
|
||||
|
||||
#[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
|
||||
#[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]
|
||||
fn generic_predicates_for_param(
|
||||
&self,
|
||||
def: GenericDefId,
|
||||
|
@ -71,39 +77,24 @@ pub trait HirDatabase: DefDatabase {
|
|||
#[salsa::interned]
|
||||
fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId;
|
||||
|
||||
#[salsa::invoke(crate::traits::chalk::associated_ty_data_query)]
|
||||
fn associated_ty_data(
|
||||
&self,
|
||||
id: chalk_ir::TypeId,
|
||||
) -> Arc<chalk_rust_ir::AssociatedTyDatum<chalk_ir::family::ChalkIr>>;
|
||||
#[salsa::invoke(chalk::associated_ty_data_query)]
|
||||
fn associated_ty_data(&self, id: chalk::AssocTypeId) -> Arc<chalk::AssociatedTyDatum>;
|
||||
|
||||
#[salsa::invoke(crate::traits::chalk::trait_datum_query)]
|
||||
fn trait_datum(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
trait_id: chalk_ir::TraitId,
|
||||
) -> Arc<chalk_rust_ir::TraitDatum<chalk_ir::family::ChalkIr>>;
|
||||
#[salsa::invoke(chalk::trait_datum_query)]
|
||||
fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>;
|
||||
|
||||
#[salsa::invoke(crate::traits::chalk::struct_datum_query)]
|
||||
fn struct_datum(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
struct_id: chalk_ir::StructId,
|
||||
) -> Arc<chalk_rust_ir::StructDatum<chalk_ir::family::ChalkIr>>;
|
||||
#[salsa::invoke(chalk::struct_datum_query)]
|
||||
fn struct_datum(&self, krate: CrateId, struct_id: chalk::StructId) -> Arc<chalk::StructDatum>;
|
||||
|
||||
#[salsa::invoke(crate::traits::chalk::impl_datum_query)]
|
||||
fn impl_datum(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
impl_id: chalk_ir::ImplId,
|
||||
) -> Arc<chalk_rust_ir::ImplDatum<chalk_ir::family::ChalkIr>>;
|
||||
fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>;
|
||||
|
||||
#[salsa::invoke(crate::traits::chalk::associated_ty_value_query)]
|
||||
fn associated_ty_value(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
id: chalk_rust_ir::AssociatedTyValueId,
|
||||
) -> Arc<chalk_rust_ir::AssociatedTyValue<chalk_ir::family::ChalkIr>>;
|
||||
id: chalk::AssociatedTyValueId,
|
||||
) -> Arc<chalk::AssociatedTyValue>;
|
||||
|
||||
#[salsa::invoke(crate::traits::trait_solve_query)]
|
||||
fn trait_solve(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::any::Any;
|
||||
|
||||
use hir_expand::{db::AstDatabase, name::Name, HirFileId, Source};
|
||||
use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
|
||||
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
|
||||
|
||||
pub use hir_def::diagnostics::UnresolvedModule;
|
||||
|
@ -19,8 +19,8 @@ impl Diagnostic for NoSuchField {
|
|||
"no such field".to_string()
|
||||
}
|
||||
|
||||
fn source(&self) -> Source<SyntaxNodePtr> {
|
||||
Source { file_id: self.file, value: self.field.into() }
|
||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile { file_id: self.file, value: self.field.into() }
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
|
@ -44,8 +44,8 @@ impl Diagnostic for MissingFields {
|
|||
}
|
||||
message
|
||||
}
|
||||
fn source(&self) -> Source<SyntaxNodePtr> {
|
||||
Source { file_id: self.file, value: self.field_list.into() }
|
||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile { file_id: self.file, value: self.field_list.into() }
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
|
@ -72,8 +72,8 @@ impl Diagnostic for MissingOkInTailExpr {
|
|||
fn message(&self) -> String {
|
||||
"wrap return expression in Ok".to_string()
|
||||
}
|
||||
fn source(&self) -> Source<SyntaxNodePtr> {
|
||||
Source { file_id: self.file, value: self.expr.into() }
|
||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile { file_id: self.file, value: self.expr.into() }
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
|
|
|
@ -10,6 +10,7 @@ pub struct HirFormatter<'a, 'b, DB> {
|
|||
buf: String,
|
||||
curr_size: usize,
|
||||
max_size: Option<usize>,
|
||||
should_display_default_types: bool,
|
||||
}
|
||||
|
||||
pub trait HirDisplay {
|
||||
|
@ -19,7 +20,7 @@ pub trait HirDisplay {
|
|||
where
|
||||
Self: Sized,
|
||||
{
|
||||
HirDisplayWrapper(db, self, None)
|
||||
HirDisplayWrapper(db, self, None, true)
|
||||
}
|
||||
|
||||
fn display_truncated<'a, DB>(
|
||||
|
@ -30,7 +31,7 @@ pub trait HirDisplay {
|
|||
where
|
||||
Self: Sized,
|
||||
{
|
||||
HirDisplayWrapper(db, self, max_size)
|
||||
HirDisplayWrapper(db, self, max_size, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,9 +73,13 @@ where
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_display_default_types(&self) -> bool {
|
||||
self.should_display_default_types
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HirDisplayWrapper<'a, DB, T>(&'a DB, &'a T, Option<usize>);
|
||||
pub struct HirDisplayWrapper<'a, DB, T>(&'a DB, &'a T, Option<usize>, bool);
|
||||
|
||||
impl<'a, DB, T> fmt::Display for HirDisplayWrapper<'a, DB, T>
|
||||
where
|
||||
|
@ -88,6 +93,7 @@ where
|
|||
buf: String::with_capacity(20),
|
||||
curr_size: 0,
|
||||
max_size: self.2,
|
||||
should_display_default_types: self.3,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{
|
||||
path::{known, Path},
|
||||
path::{path, Path},
|
||||
resolver::HasResolver,
|
||||
AdtId, FunctionId,
|
||||
};
|
||||
|
@ -97,7 +97,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
|||
let (_, source_map) = db.body_with_source_map(self.func.into());
|
||||
|
||||
if let Some(source_ptr) = source_map.expr_syntax(id) {
|
||||
if let Some(expr) = source_ptr.value.a() {
|
||||
if let Some(expr) = source_ptr.value.left() {
|
||||
let root = source_ptr.file_syntax(db);
|
||||
if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
|
||||
if let Some(field_list) = record_lit.record_field_list() {
|
||||
|
@ -124,7 +124,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
|||
None => return,
|
||||
};
|
||||
|
||||
let std_result_path = known::std_result_result();
|
||||
let std_result_path = path![std::result::Result];
|
||||
|
||||
let resolver = self.func.resolver(db);
|
||||
let std_result_enum = match resolver.resolve_known_enum(db, &std_result_path) {
|
||||
|
@ -142,7 +142,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
|||
let (_, source_map) = db.body_with_source_map(self.func.into());
|
||||
|
||||
if let Some(source_ptr) = source_map.expr_syntax(id) {
|
||||
if let Some(expr) = source_ptr.value.a() {
|
||||
if let Some(expr) = source_ptr.value.left() {
|
||||
self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,19 +18,18 @@ use std::mem;
|
|||
use std::ops::Index;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use hir_def::{
|
||||
body::Body,
|
||||
data::{ConstData, FunctionData},
|
||||
expr::{BindingAnnotation, ExprId, PatId},
|
||||
path::{known, Path},
|
||||
path::{path, Path},
|
||||
resolver::{HasResolver, Resolver, TypeNs},
|
||||
type_ref::{Mutability, TypeRef},
|
||||
AdtId, AssocItemId, DefWithBodyId, FunctionId, StructFieldId, TypeAliasId, VariantId,
|
||||
};
|
||||
use hir_expand::{diagnostics::DiagnosticSink, name};
|
||||
use hir_expand::{diagnostics::DiagnosticSink, name::name};
|
||||
use ra_arena::map::ArenaMap;
|
||||
use ra_prof::profile;
|
||||
use test_utils::tested_by;
|
||||
|
@ -43,6 +42,8 @@ use super::{
|
|||
};
|
||||
use crate::{db::HirDatabase, infer::diagnostics::InferenceDiagnostic};
|
||||
|
||||
pub(crate) use unify::unify;
|
||||
|
||||
macro_rules! ty_app {
|
||||
($ctor:pat, $param:pat) => {
|
||||
crate::Ty::Apply(crate::ApplicationTy { ctor: $ctor, parameters: $param })
|
||||
|
@ -191,11 +192,16 @@ struct InferenceContext<'a, D: HirDatabase> {
|
|||
owner: DefWithBodyId,
|
||||
body: Arc<Body>,
|
||||
resolver: Resolver,
|
||||
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
||||
table: unify::InferenceTable,
|
||||
trait_env: Arc<TraitEnvironment>,
|
||||
obligations: Vec<Obligation>,
|
||||
result: InferenceResult,
|
||||
/// The return type of the function being inferred.
|
||||
/// The return type of the function being inferred, or the closure if we're
|
||||
/// currently within one.
|
||||
///
|
||||
/// We might consider using a nested inference context for checking
|
||||
/// closures, but currently this is the only field that will change there,
|
||||
/// so it doesn't make sense.
|
||||
return_ty: Ty,
|
||||
|
||||
/// Impls of `CoerceUnsized` used in coercion.
|
||||
|
@ -209,7 +215,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
fn new(db: &'a D, owner: DefWithBodyId, resolver: Resolver) -> Self {
|
||||
InferenceContext {
|
||||
result: InferenceResult::default(),
|
||||
var_unification_table: InPlaceUnificationTable::new(),
|
||||
table: unify::InferenceTable::new(),
|
||||
obligations: Vec::default(),
|
||||
return_ty: Ty::Unknown, // set in collect_fn_signature
|
||||
trait_env: TraitEnvironment::lower(db, &resolver),
|
||||
|
@ -224,13 +230,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
fn resolve_all(mut self) -> InferenceResult {
|
||||
// FIXME resolve obligations as well (use Guidance if necessary)
|
||||
let mut result = mem::replace(&mut self.result, InferenceResult::default());
|
||||
let mut tv_stack = Vec::new();
|
||||
for ty in result.type_of_expr.values_mut() {
|
||||
let resolved = self.resolve_ty_completely(&mut tv_stack, mem::replace(ty, Ty::Unknown));
|
||||
let resolved = self.table.resolve_ty_completely(mem::replace(ty, Ty::Unknown));
|
||||
*ty = resolved;
|
||||
}
|
||||
for ty in result.type_of_pat.values_mut() {
|
||||
let resolved = self.resolve_ty_completely(&mut tv_stack, mem::replace(ty, Ty::Unknown));
|
||||
let resolved = self.table.resolve_ty_completely(mem::replace(ty, Ty::Unknown));
|
||||
*ty = resolved;
|
||||
}
|
||||
result
|
||||
|
@ -275,96 +280,38 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
self.normalize_associated_types_in(ty)
|
||||
}
|
||||
|
||||
fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs, depth: usize) -> bool {
|
||||
substs1.0.iter().zip(substs2.0.iter()).all(|(t1, t2)| self.unify_inner(t1, t2, depth))
|
||||
/// Replaces `impl Trait` in `ty` by type variables and obligations for
|
||||
/// those variables. This is done for function arguments when calling a
|
||||
/// function, and for return types when inside the function body, i.e. in
|
||||
/// the cases where the `impl Trait` is 'transparent'. In other cases, `impl
|
||||
/// Trait` is represented by `Ty::Opaque`.
|
||||
fn insert_vars_for_impl_trait(&mut self, ty: Ty) -> Ty {
|
||||
ty.fold(&mut |ty| match ty {
|
||||
Ty::Opaque(preds) => {
|
||||
tested_by!(insert_vars_for_impl_trait);
|
||||
let var = self.table.new_type_var();
|
||||
let var_subst = Substs::builder(1).push(var.clone()).build();
|
||||
self.obligations.extend(
|
||||
preds
|
||||
.iter()
|
||||
.map(|pred| pred.clone().subst_bound_vars(&var_subst))
|
||||
.filter_map(Obligation::from_predicate),
|
||||
);
|
||||
var
|
||||
}
|
||||
|
||||
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
||||
self.unify_inner(ty1, ty2, 0)
|
||||
}
|
||||
|
||||
fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool {
|
||||
if depth > 1000 {
|
||||
// prevent stackoverflows
|
||||
panic!("infinite recursion in unification");
|
||||
}
|
||||
if ty1 == ty2 {
|
||||
return true;
|
||||
}
|
||||
// try to resolve type vars first
|
||||
let ty1 = self.resolve_ty_shallow(ty1);
|
||||
let ty2 = self.resolve_ty_shallow(ty2);
|
||||
match (&*ty1, &*ty2) {
|
||||
(Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => {
|
||||
self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1)
|
||||
}
|
||||
_ => self.unify_inner_trivial(&ty1, &ty2),
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
||||
match (ty1, ty2) {
|
||||
(Ty::Unknown, _) | (_, Ty::Unknown) => true,
|
||||
|
||||
(Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2)))
|
||||
| (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2)))
|
||||
| (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2)))
|
||||
| (
|
||||
Ty::Infer(InferTy::MaybeNeverTypeVar(tv1)),
|
||||
Ty::Infer(InferTy::MaybeNeverTypeVar(tv2)),
|
||||
) => {
|
||||
// both type vars are unknown since we tried to resolve them
|
||||
self.var_unification_table.union(*tv1, *tv2);
|
||||
true
|
||||
}
|
||||
|
||||
// The order of MaybeNeverTypeVar matters here.
|
||||
// Unifying MaybeNeverTypeVar and TypeVar will let the latter become MaybeNeverTypeVar.
|
||||
// Unifying MaybeNeverTypeVar and other concrete type will let the former become it.
|
||||
(Ty::Infer(InferTy::TypeVar(tv)), other)
|
||||
| (other, Ty::Infer(InferTy::TypeVar(tv)))
|
||||
| (Ty::Infer(InferTy::MaybeNeverTypeVar(tv)), other)
|
||||
| (other, Ty::Infer(InferTy::MaybeNeverTypeVar(tv)))
|
||||
| (Ty::Infer(InferTy::IntVar(tv)), other @ ty_app!(TypeCtor::Int(_)))
|
||||
| (other @ ty_app!(TypeCtor::Int(_)), Ty::Infer(InferTy::IntVar(tv)))
|
||||
| (Ty::Infer(InferTy::FloatVar(tv)), other @ ty_app!(TypeCtor::Float(_)))
|
||||
| (other @ ty_app!(TypeCtor::Float(_)), Ty::Infer(InferTy::FloatVar(tv))) => {
|
||||
// the type var is unknown since we tried to resolve it
|
||||
self.var_unification_table.union_value(*tv, TypeVarValue::Known(other.clone()));
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_type_var(&mut self) -> Ty {
|
||||
Ty::Infer(InferTy::TypeVar(self.var_unification_table.new_key(TypeVarValue::Unknown)))
|
||||
}
|
||||
|
||||
fn new_integer_var(&mut self) -> Ty {
|
||||
Ty::Infer(InferTy::IntVar(self.var_unification_table.new_key(TypeVarValue::Unknown)))
|
||||
}
|
||||
|
||||
fn new_float_var(&mut self) -> Ty {
|
||||
Ty::Infer(InferTy::FloatVar(self.var_unification_table.new_key(TypeVarValue::Unknown)))
|
||||
}
|
||||
|
||||
fn new_maybe_never_type_var(&mut self) -> Ty {
|
||||
Ty::Infer(InferTy::MaybeNeverTypeVar(
|
||||
self.var_unification_table.new_key(TypeVarValue::Unknown),
|
||||
))
|
||||
_ => ty,
|
||||
})
|
||||
}
|
||||
|
||||
/// Replaces Ty::Unknown by a new type var, so we can maybe still infer it.
|
||||
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
|
||||
match ty {
|
||||
Ty::Unknown => self.new_type_var(),
|
||||
Ty::Unknown => self.table.new_type_var(),
|
||||
Ty::Apply(ApplicationTy { ctor: TypeCtor::Int(Uncertain::Unknown), .. }) => {
|
||||
self.new_integer_var()
|
||||
self.table.new_integer_var()
|
||||
}
|
||||
Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(Uncertain::Unknown), .. }) => {
|
||||
self.new_float_var()
|
||||
self.table.new_float_var()
|
||||
}
|
||||
_ => ty,
|
||||
}
|
||||
|
@ -402,64 +349,52 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
}
|
||||
|
||||
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
||||
self.table.unify(ty1, ty2)
|
||||
}
|
||||
|
||||
/// Resolves the type as far as currently possible, replacing type variables
|
||||
/// by their known types. All types returned by the infer_* functions should
|
||||
/// be resolved as far as possible, i.e. contain no type variables with
|
||||
/// known type.
|
||||
fn resolve_ty_as_possible(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
|
||||
fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty {
|
||||
self.resolve_obligations_as_possible();
|
||||
|
||||
ty.fold(&mut |ty| match ty {
|
||||
Ty::Infer(tv) => {
|
||||
let inner = tv.to_inner();
|
||||
if tv_stack.contains(&inner) {
|
||||
tested_by!(type_var_cycles_resolve_as_possible);
|
||||
// recursive type
|
||||
return tv.fallback_value();
|
||||
}
|
||||
if let Some(known_ty) =
|
||||
self.var_unification_table.inlined_probe_value(inner).known()
|
||||
{
|
||||
// known_ty may contain other variables that are known by now
|
||||
tv_stack.push(inner);
|
||||
let result = self.resolve_ty_as_possible(tv_stack, known_ty.clone());
|
||||
tv_stack.pop();
|
||||
result
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
}
|
||||
_ => ty,
|
||||
})
|
||||
self.table.resolve_ty_as_possible(ty)
|
||||
}
|
||||
|
||||
/// If `ty` is a type variable with known type, returns that type;
|
||||
/// otherwise, return ty.
|
||||
fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
|
||||
let mut ty = Cow::Borrowed(ty);
|
||||
// The type variable could resolve to a int/float variable. Hence try
|
||||
// resolving up to three times; each type of variable shouldn't occur
|
||||
// more than once
|
||||
for i in 0..3 {
|
||||
if i > 0 {
|
||||
tested_by!(type_var_resolves_to_int_var);
|
||||
self.table.resolve_ty_shallow(ty)
|
||||
}
|
||||
match &*ty {
|
||||
Ty::Infer(tv) => {
|
||||
let inner = tv.to_inner();
|
||||
match self.var_unification_table.inlined_probe_value(inner).known() {
|
||||
Some(known_ty) => {
|
||||
// The known_ty can't be a type var itself
|
||||
ty = Cow::Owned(known_ty.clone());
|
||||
|
||||
fn resolve_associated_type(&mut self, inner_ty: Ty, assoc_ty: Option<TypeAliasId>) -> Ty {
|
||||
self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[])
|
||||
}
|
||||
_ => return ty,
|
||||
|
||||
fn resolve_associated_type_with_params(
|
||||
&mut self,
|
||||
inner_ty: Ty,
|
||||
assoc_ty: Option<TypeAliasId>,
|
||||
params: &[Ty],
|
||||
) -> Ty {
|
||||
match assoc_ty {
|
||||
Some(res_assoc_ty) => {
|
||||
let ty = self.table.new_type_var();
|
||||
let builder = Substs::build_for_def(self.db, res_assoc_ty)
|
||||
.push(inner_ty)
|
||||
.fill(params.iter().cloned());
|
||||
let projection = ProjectionPredicate {
|
||||
ty: ty.clone(),
|
||||
projection_ty: ProjectionTy {
|
||||
associated_ty: res_assoc_ty,
|
||||
parameters: builder.build(),
|
||||
},
|
||||
};
|
||||
self.obligations.push(Obligation::Projection(projection));
|
||||
self.resolve_ty_as_possible(ty)
|
||||
}
|
||||
None => Ty::Unknown,
|
||||
}
|
||||
_ => return ty,
|
||||
}
|
||||
}
|
||||
log::error!("Inference variable still not resolved: {:?}", ty);
|
||||
ty
|
||||
}
|
||||
|
||||
/// Recurses through the given type, normalizing associated types mentioned
|
||||
|
@ -469,7 +404,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
/// call). `make_ty` handles this already, but e.g. for field types we need
|
||||
/// to do it as well.
|
||||
fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
|
||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
|
||||
let ty = self.resolve_ty_as_possible(ty);
|
||||
ty.fold(&mut |ty| match ty {
|
||||
Ty::Projection(proj_ty) => self.normalize_projection_ty(proj_ty),
|
||||
_ => ty,
|
||||
|
@ -477,40 +412,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
|
||||
fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
|
||||
let var = self.new_type_var();
|
||||
let var = self.table.new_type_var();
|
||||
let predicate = ProjectionPredicate { projection_ty: proj_ty, ty: var.clone() };
|
||||
let obligation = Obligation::Projection(predicate);
|
||||
self.obligations.push(obligation);
|
||||
var
|
||||
}
|
||||
|
||||
/// Resolves the type completely; type variables without known type are
|
||||
/// replaced by Ty::Unknown.
|
||||
fn resolve_ty_completely(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
|
||||
ty.fold(&mut |ty| match ty {
|
||||
Ty::Infer(tv) => {
|
||||
let inner = tv.to_inner();
|
||||
if tv_stack.contains(&inner) {
|
||||
tested_by!(type_var_cycles_resolve_completely);
|
||||
// recursive type
|
||||
return tv.fallback_value();
|
||||
}
|
||||
if let Some(known_ty) =
|
||||
self.var_unification_table.inlined_probe_value(inner).known()
|
||||
{
|
||||
// known_ty may contain other variables that are known by now
|
||||
tv_stack.push(inner);
|
||||
let result = self.resolve_ty_completely(tv_stack, known_ty.clone());
|
||||
tv_stack.pop();
|
||||
result
|
||||
} else {
|
||||
tv.fallback_value()
|
||||
}
|
||||
}
|
||||
_ => ty,
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantId>) {
|
||||
let path = match path {
|
||||
Some(path) => path,
|
||||
|
@ -519,7 +427,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
let resolver = &self.resolver;
|
||||
// FIXME: this should resolve assoc items as well, see this example:
|
||||
// https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
|
||||
match resolver.resolve_path_in_type_ns_fully(self.db, &path) {
|
||||
match resolver.resolve_path_in_type_ns_fully(self.db, path.mod_path()) {
|
||||
Some(TypeNs::AdtId(AdtId::StructId(strukt))) => {
|
||||
let substs = Ty::substs_from_path(self.db, resolver, path, strukt.into());
|
||||
let ty = self.db.ty(strukt.into());
|
||||
|
@ -547,93 +455,90 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
|
||||
self.infer_pat(*pat, &ty, BindingMode::default());
|
||||
}
|
||||
self.return_ty = self.make_ty(&data.ret_type);
|
||||
let return_ty = self.make_ty(&data.ret_type);
|
||||
self.return_ty = self.insert_vars_for_impl_trait(return_ty);
|
||||
}
|
||||
|
||||
fn infer_body(&mut self) {
|
||||
self.infer_expr(self.body.body_expr, &Expectation::has_type(self.return_ty.clone()));
|
||||
self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone()));
|
||||
}
|
||||
|
||||
fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
|
||||
let path = known::std_iter_into_iterator();
|
||||
let path = path![std::iter::IntoIterator];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name::ITEM_TYPE)
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Item])
|
||||
}
|
||||
|
||||
fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
|
||||
let path = known::std_ops_try();
|
||||
let path = path![std::ops::Try];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name::OK_TYPE)
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Ok])
|
||||
}
|
||||
|
||||
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
|
||||
let path = path![std::ops::Neg];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
}
|
||||
|
||||
fn resolve_ops_not_output(&self) -> Option<TypeAliasId> {
|
||||
let path = path![std::ops::Not];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
}
|
||||
|
||||
fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
|
||||
let path = known::std_future_future();
|
||||
let path = path![std::future::Future];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name::OUTPUT_TYPE)
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
}
|
||||
|
||||
fn resolve_boxed_box(&self) -> Option<AdtId> {
|
||||
let path = known::std_boxed_box();
|
||||
let path = path![std::boxed::Box];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn resolve_range_full(&self) -> Option<AdtId> {
|
||||
let path = path![std::ops::RangeFull];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
/// The ID of a type variable.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct TypeVarId(pub(super) u32);
|
||||
|
||||
impl UnifyKey for TypeVarId {
|
||||
type Value = TypeVarValue;
|
||||
|
||||
fn index(&self) -> u32 {
|
||||
self.0
|
||||
fn resolve_range(&self) -> Option<AdtId> {
|
||||
let path = path![std::ops::Range];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn from_index(i: u32) -> Self {
|
||||
TypeVarId(i)
|
||||
fn resolve_range_inclusive(&self) -> Option<AdtId> {
|
||||
let path = path![std::ops::RangeInclusive];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn tag() -> &'static str {
|
||||
"TypeVarId"
|
||||
}
|
||||
fn resolve_range_from(&self) -> Option<AdtId> {
|
||||
let path = path![std::ops::RangeFrom];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
/// The value of a type variable: either we already know the type, or we don't
|
||||
/// know it yet.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum TypeVarValue {
|
||||
Known(Ty),
|
||||
Unknown,
|
||||
fn resolve_range_to(&self) -> Option<AdtId> {
|
||||
let path = path![std::ops::RangeTo];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
impl TypeVarValue {
|
||||
fn known(&self) -> Option<&Ty> {
|
||||
match self {
|
||||
TypeVarValue::Known(ty) => Some(ty),
|
||||
TypeVarValue::Unknown => None,
|
||||
}
|
||||
}
|
||||
fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
|
||||
let path = path![std::ops::RangeToInclusive];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
impl UnifyValue for TypeVarValue {
|
||||
type Error = NoError;
|
||||
|
||||
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, NoError> {
|
||||
match (value1, value2) {
|
||||
// We should never equate two type variables, both of which have
|
||||
// known types. Instead, we recursively equate those types.
|
||||
(TypeVarValue::Known(t1), TypeVarValue::Known(t2)) => panic!(
|
||||
"equating two type variables, both of which have known types: {:?} and {:?}",
|
||||
t1, t2
|
||||
),
|
||||
|
||||
// If one side is known, prefer that one.
|
||||
(TypeVarValue::Known(..), TypeVarValue::Unknown) => Ok(value1.clone()),
|
||||
(TypeVarValue::Unknown, TypeVarValue::Known(..)) => Ok(value2.clone()),
|
||||
|
||||
(TypeVarValue::Unknown, TypeVarValue::Unknown) => Ok(TypeVarValue::Unknown),
|
||||
}
|
||||
fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
|
||||
let path = path![std::ops::Index];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -643,14 +548,14 @@ impl UnifyValue for TypeVarValue {
|
|||
/// several integer types).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum InferTy {
|
||||
TypeVar(TypeVarId),
|
||||
IntVar(TypeVarId),
|
||||
FloatVar(TypeVarId),
|
||||
MaybeNeverTypeVar(TypeVarId),
|
||||
TypeVar(unify::TypeVarId),
|
||||
IntVar(unify::TypeVarId),
|
||||
FloatVar(unify::TypeVarId),
|
||||
MaybeNeverTypeVar(unify::TypeVarId),
|
||||
}
|
||||
|
||||
impl InferTy {
|
||||
fn to_inner(self) -> TypeVarId {
|
||||
fn to_inner(self) -> unify::TypeVarId {
|
||||
match self {
|
||||
InferTy::TypeVar(ty)
|
||||
| InferTy::IntVar(ty)
|
||||
|
@ -693,7 +598,7 @@ impl Expectation {
|
|||
}
|
||||
|
||||
mod diagnostics {
|
||||
use hir_def::{expr::ExprId, FunctionId, HasSource, Lookup};
|
||||
use hir_def::{expr::ExprId, src::HasSource, FunctionId, Lookup};
|
||||
use hir_expand::diagnostics::DiagnosticSink;
|
||||
|
||||
use crate::{db::HirDatabase, diagnostics::NoSuchField};
|
||||
|
|
|
@ -8,9 +8,9 @@ use hir_def::{lang_item::LangItemTarget, resolver::Resolver, type_ref::Mutabilit
|
|||
use rustc_hash::FxHashMap;
|
||||
use test_utils::tested_by;
|
||||
|
||||
use crate::{autoderef, db::HirDatabase, ImplTy, Substs, Ty, TypeCtor, TypeWalk};
|
||||
use crate::{autoderef, db::HirDatabase, Substs, Ty, TypeCtor, TypeWalk};
|
||||
|
||||
use super::{InEnvironment, InferTy, InferenceContext, TypeVarValue};
|
||||
use super::{unify::TypeVarValue, InEnvironment, InferTy, InferenceContext};
|
||||
|
||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
/// Unify two types, but may coerce the first one to the second one
|
||||
|
@ -54,10 +54,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
impls
|
||||
.iter()
|
||||
.filter_map(|&impl_id| {
|
||||
let trait_ref = match db.impl_ty(impl_id) {
|
||||
ImplTy::TraitRef(it) => it,
|
||||
ImplTy::Inherent(_) => return None,
|
||||
};
|
||||
let trait_ref = db.impl_trait(impl_id)?;
|
||||
|
||||
// `CoerseUnsized` has one generic parameter for the target type.
|
||||
let cur_from_ty = trait_ref.substs.0.get(0)?;
|
||||
|
@ -88,8 +85,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
match (&from_ty, to_ty) {
|
||||
// Never type will make type variable to fallback to Never Type instead of Unknown.
|
||||
(ty_app!(TypeCtor::Never), Ty::Infer(InferTy::TypeVar(tv))) => {
|
||||
let var = self.new_maybe_never_type_var();
|
||||
self.var_unification_table.union_value(*tv, TypeVarValue::Known(var));
|
||||
let var = self.table.new_maybe_never_type_var();
|
||||
self.table.var_unification_table.union_value(*tv, TypeVarValue::Known(var));
|
||||
return true;
|
||||
}
|
||||
(ty_app!(TypeCtor::Never), _) => return true,
|
||||
|
@ -97,7 +94,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
// Trivial cases, this should go after `never` check to
|
||||
// avoid infer result type to be never
|
||||
_ => {
|
||||
if self.unify_inner_trivial(&from_ty, &to_ty) {
|
||||
if self.table.unify_inner_trivial(&from_ty, &to_ty) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -137,6 +134,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
}
|
||||
|
||||
(ty_app!(TypeCtor::Closure { .. }, params), ty_app!(TypeCtor::FnPtr { .. })) => {
|
||||
from_ty = params[0].clone();
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -333,9 +334,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
// Stop when constructor matches.
|
||||
(ty_app!(from_ctor, st1), ty_app!(to_ctor, st2)) if from_ctor == to_ctor => {
|
||||
// It will not recurse to `coerce`.
|
||||
return self.unify_substs(st1, st2, 0);
|
||||
return self.table.unify_substs(st1, st2, 0);
|
||||
}
|
||||
_ => {
|
||||
if self.table.unify_inner_trivial(&derefed_ty, &to_ty) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,17 +6,21 @@ use std::sync::Arc;
|
|||
use hir_def::{
|
||||
builtin_type::Signedness,
|
||||
expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
|
||||
generics::GenericParams,
|
||||
path::{GenericArg, GenericArgs},
|
||||
resolver::resolver_for_expr,
|
||||
AdtId, ContainerId, Lookup, StructFieldId,
|
||||
AdtId, AssocContainerId, Lookup, StructFieldId,
|
||||
};
|
||||
use hir_expand::name::{self, Name};
|
||||
use hir_expand::name::{name, Name};
|
||||
use ra_syntax::ast::RangeOp;
|
||||
|
||||
use crate::{
|
||||
autoderef, db::HirDatabase, method_resolution, op, traits::InEnvironment, utils::variant_data,
|
||||
CallableDef, InferTy, IntTy, Mutability, Obligation, ProjectionPredicate, ProjectionTy, Substs,
|
||||
TraitRef, Ty, TypeCtor, TypeWalk, Uncertain,
|
||||
autoderef,
|
||||
db::HirDatabase,
|
||||
method_resolution, op,
|
||||
traits::InEnvironment,
|
||||
utils::{generics, variant_data, Generics},
|
||||
ApplicationTy, CallableDef, InferTy, IntTy, Mutability, Obligation, Substs, TraitRef, Ty,
|
||||
TypeCtor, TypeWalk, Uncertain,
|
||||
};
|
||||
|
||||
use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch};
|
||||
|
@ -31,13 +35,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
|
||||
);
|
||||
}
|
||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
|
||||
let ty = self.resolve_ty_as_possible(ty);
|
||||
ty
|
||||
}
|
||||
|
||||
/// Infer type of expression with possibly implicit coerce to the expected type.
|
||||
/// Return the type after possible coercion.
|
||||
fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
|
||||
pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
|
||||
let ty = self.infer_expr_inner(expr, &expected);
|
||||
let ty = if !self.coerce(&ty, &expected.ty) {
|
||||
self.result
|
||||
|
@ -52,7 +56,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
expected.ty.clone()
|
||||
};
|
||||
|
||||
self.resolve_ty_as_possible(&mut vec![], ty)
|
||||
self.resolve_ty_as_possible(ty)
|
||||
}
|
||||
|
||||
fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
|
@ -91,27 +95,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
Expr::For { iterable, body, pat } => {
|
||||
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
|
||||
|
||||
let pat_ty = match self.resolve_into_iter_item() {
|
||||
Some(into_iter_item_alias) => {
|
||||
let pat_ty = self.new_type_var();
|
||||
let projection = ProjectionPredicate {
|
||||
ty: pat_ty.clone(),
|
||||
projection_ty: ProjectionTy {
|
||||
associated_ty: into_iter_item_alias,
|
||||
parameters: Substs::single(iterable_ty),
|
||||
},
|
||||
};
|
||||
self.obligations.push(Obligation::Projection(projection));
|
||||
self.resolve_ty_as_possible(&mut vec![], pat_ty)
|
||||
}
|
||||
None => Ty::Unknown,
|
||||
};
|
||||
let pat_ty =
|
||||
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
|
||||
|
||||
self.infer_pat(*pat, &pat_ty, BindingMode::default());
|
||||
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
||||
Ty::unit()
|
||||
}
|
||||
Expr::Lambda { body, args, arg_types } => {
|
||||
Expr::Lambda { body, args, ret_type, arg_types } => {
|
||||
assert_eq!(args.len(), arg_types.len());
|
||||
|
||||
let mut sig_tys = Vec::new();
|
||||
|
@ -127,7 +118,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
|
||||
// add return type
|
||||
let ret_ty = self.new_type_var();
|
||||
let ret_ty = match ret_type {
|
||||
Some(type_ref) => self.make_ty(type_ref),
|
||||
None => self.table.new_type_var(),
|
||||
};
|
||||
sig_tys.push(ret_ty.clone());
|
||||
let sig_ty = Ty::apply(
|
||||
TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 },
|
||||
|
@ -143,7 +137,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
// infer the body.
|
||||
self.coerce(&closure_ty, &expected.ty);
|
||||
|
||||
self.infer_expr(*body, &Expectation::has_type(ret_ty));
|
||||
let prev_ret_ty = std::mem::replace(&mut self.return_ty, ret_ty.clone());
|
||||
|
||||
self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
|
||||
|
||||
self.return_ty = prev_ret_ty;
|
||||
|
||||
closure_ty
|
||||
}
|
||||
Expr::Call { callee, args } => {
|
||||
|
@ -166,7 +165,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
Expr::Match { expr, arms } => {
|
||||
let input_ty = self.infer_expr(*expr, &Expectation::none());
|
||||
|
||||
let mut result_ty = self.new_maybe_never_type_var();
|
||||
let mut result_ty = self.table.new_maybe_never_type_var();
|
||||
|
||||
for arm in arms {
|
||||
for &pat in &arm.pats {
|
||||
|
@ -200,7 +199,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
Expr::Return { expr } => {
|
||||
if let Some(expr) = expr {
|
||||
self.infer_expr(*expr, &Expectation::has_type(self.return_ty.clone()));
|
||||
self.infer_expr_coerce(*expr, &Expectation::has_type(self.return_ty.clone()));
|
||||
} else {
|
||||
let unit = Ty::unit();
|
||||
self.coerce(&unit, &self.return_ty.clone());
|
||||
}
|
||||
Ty::simple(TypeCtor::Never)
|
||||
}
|
||||
|
@ -244,7 +246,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
ty
|
||||
}
|
||||
Expr::Field { expr, name } => {
|
||||
let receiver_ty = self.infer_expr(*expr, &Expectation::none());
|
||||
let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty);
|
||||
let ty = autoderef::autoderef(
|
||||
self.db,
|
||||
|
@ -279,45 +281,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
self.normalize_associated_types_in(ty)
|
||||
}
|
||||
Expr::Await { expr } => {
|
||||
let inner_ty = self.infer_expr(*expr, &Expectation::none());
|
||||
let ty = match self.resolve_future_future_output() {
|
||||
Some(future_future_output_alias) => {
|
||||
let ty = self.new_type_var();
|
||||
let projection = ProjectionPredicate {
|
||||
ty: ty.clone(),
|
||||
projection_ty: ProjectionTy {
|
||||
associated_ty: future_future_output_alias,
|
||||
parameters: Substs::single(inner_ty),
|
||||
},
|
||||
};
|
||||
self.obligations.push(Obligation::Projection(projection));
|
||||
self.resolve_ty_as_possible(&mut vec![], ty)
|
||||
}
|
||||
None => Ty::Unknown,
|
||||
};
|
||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
let ty =
|
||||
self.resolve_associated_type(inner_ty, self.resolve_future_future_output());
|
||||
ty
|
||||
}
|
||||
Expr::Try { expr } => {
|
||||
let inner_ty = self.infer_expr(*expr, &Expectation::none());
|
||||
let ty = match self.resolve_ops_try_ok() {
|
||||
Some(ops_try_ok_alias) => {
|
||||
let ty = self.new_type_var();
|
||||
let projection = ProjectionPredicate {
|
||||
ty: ty.clone(),
|
||||
projection_ty: ProjectionTy {
|
||||
associated_ty: ops_try_ok_alias,
|
||||
parameters: Substs::single(inner_ty),
|
||||
},
|
||||
};
|
||||
self.obligations.push(Obligation::Projection(projection));
|
||||
self.resolve_ty_as_possible(&mut vec![], ty)
|
||||
}
|
||||
None => Ty::Unknown,
|
||||
};
|
||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
let ty = self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok());
|
||||
ty
|
||||
}
|
||||
Expr::Cast { expr, type_ref } => {
|
||||
let _inner_ty = self.infer_expr(*expr, &Expectation::none());
|
||||
let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
let cast_ty = self.make_ty(type_ref);
|
||||
// FIXME check the cast...
|
||||
cast_ty
|
||||
|
@ -333,12 +308,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
} else {
|
||||
Expectation::none()
|
||||
};
|
||||
// FIXME reference coercions etc.
|
||||
let inner_ty = self.infer_expr(*expr, &expectation);
|
||||
let inner_ty = self.infer_expr_inner(*expr, &expectation);
|
||||
Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty)
|
||||
}
|
||||
Expr::Box { expr } => {
|
||||
let inner_ty = self.infer_expr(*expr, &Expectation::none());
|
||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
if let Some(box_) = self.resolve_boxed_box() {
|
||||
Ty::apply_one(TypeCtor::Adt(box_), inner_ty)
|
||||
} else {
|
||||
|
@ -346,7 +320,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
}
|
||||
Expr::UnaryOp { expr, op } => {
|
||||
let inner_ty = self.infer_expr(*expr, &Expectation::none());
|
||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
match op {
|
||||
UnaryOp::Deref => match self.resolver.krate() {
|
||||
Some(krate) => {
|
||||
|
@ -369,31 +343,36 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
},
|
||||
UnaryOp::Neg => {
|
||||
match &inner_ty {
|
||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||
TypeCtor::Int(Uncertain::Unknown)
|
||||
| TypeCtor::Int(Uncertain::Known(IntTy {
|
||||
// Fast path for builtins
|
||||
Ty::Apply(ApplicationTy {
|
||||
ctor:
|
||||
TypeCtor::Int(Uncertain::Known(IntTy {
|
||||
signedness: Signedness::Signed,
|
||||
..
|
||||
}))
|
||||
| TypeCtor::Float(..) => inner_ty,
|
||||
_ => Ty::Unknown,
|
||||
},
|
||||
Ty::Infer(InferTy::IntVar(..)) | Ty::Infer(InferTy::FloatVar(..)) => {
|
||||
inner_ty
|
||||
}
|
||||
// FIXME: resolve ops::Neg trait
|
||||
_ => Ty::Unknown,
|
||||
})),
|
||||
..
|
||||
})
|
||||
| Ty::Apply(ApplicationTy {
|
||||
ctor: TypeCtor::Int(Uncertain::Unknown),
|
||||
..
|
||||
})
|
||||
| Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(_), .. })
|
||||
| Ty::Infer(InferTy::IntVar(..))
|
||||
| Ty::Infer(InferTy::FloatVar(..)) => inner_ty,
|
||||
// Otherwise we resolve via the std::ops::Neg trait
|
||||
_ => self
|
||||
.resolve_associated_type(inner_ty, self.resolve_ops_neg_output()),
|
||||
}
|
||||
}
|
||||
UnaryOp::Not => {
|
||||
match &inner_ty {
|
||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||
TypeCtor::Bool | TypeCtor::Int(_) => inner_ty,
|
||||
_ => Ty::Unknown,
|
||||
},
|
||||
Ty::Infer(InferTy::IntVar(..)) => inner_ty,
|
||||
// FIXME: resolve ops::Not trait for inner_ty
|
||||
_ => Ty::Unknown,
|
||||
// Fast path for builtins
|
||||
Ty::Apply(ApplicationTy { ctor: TypeCtor::Bool, .. })
|
||||
| Ty::Apply(ApplicationTy { ctor: TypeCtor::Int(_), .. })
|
||||
| Ty::Infer(InferTy::IntVar(..)) => inner_ty,
|
||||
// Otherwise we resolve via the std::ops::Not trait
|
||||
_ => self
|
||||
.resolve_associated_type(inner_ty, self.resolve_ops_not_output()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -415,21 +394,63 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
_ => Ty::Unknown,
|
||||
},
|
||||
Expr::Range { lhs, rhs, range_type } => {
|
||||
let lhs_ty = lhs.map(|e| self.infer_expr_inner(e, &Expectation::none()));
|
||||
let rhs_expect = lhs_ty
|
||||
.as_ref()
|
||||
.map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone()));
|
||||
let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect));
|
||||
match (range_type, lhs_ty, rhs_ty) {
|
||||
(RangeOp::Exclusive, None, None) => match self.resolve_range_full() {
|
||||
Some(adt) => Ty::simple(TypeCtor::Adt(adt)),
|
||||
None => Ty::Unknown,
|
||||
},
|
||||
(RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() {
|
||||
Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
|
||||
None => Ty::Unknown,
|
||||
},
|
||||
(RangeOp::Inclusive, None, Some(ty)) => {
|
||||
match self.resolve_range_to_inclusive() {
|
||||
Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
|
||||
None => Ty::Unknown,
|
||||
}
|
||||
}
|
||||
(RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() {
|
||||
Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
|
||||
None => Ty::Unknown,
|
||||
},
|
||||
(RangeOp::Inclusive, Some(_), Some(ty)) => {
|
||||
match self.resolve_range_inclusive() {
|
||||
Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
|
||||
None => Ty::Unknown,
|
||||
}
|
||||
}
|
||||
(RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() {
|
||||
Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
|
||||
None => Ty::Unknown,
|
||||
},
|
||||
(RangeOp::Inclusive, _, None) => Ty::Unknown,
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index } => {
|
||||
let _base_ty = self.infer_expr(*base, &Expectation::none());
|
||||
let _index_ty = self.infer_expr(*index, &Expectation::none());
|
||||
// FIXME: use `std::ops::Index::Output` to figure out the real return type
|
||||
Ty::Unknown
|
||||
let base_ty = self.infer_expr_inner(*base, &Expectation::none());
|
||||
let index_ty = self.infer_expr(*index, &Expectation::none());
|
||||
|
||||
self.resolve_associated_type_with_params(
|
||||
base_ty,
|
||||
self.resolve_ops_index_output(),
|
||||
&[index_ty],
|
||||
)
|
||||
}
|
||||
Expr::Tuple { exprs } => {
|
||||
let mut tys = match &expected.ty {
|
||||
ty_app!(TypeCtor::Tuple { .. }, st) => st
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(repeat_with(|| self.new_type_var()))
|
||||
.chain(repeat_with(|| self.table.new_type_var()))
|
||||
.take(exprs.len())
|
||||
.collect::<Vec<_>>(),
|
||||
_ => (0..exprs.len()).map(|_| self.new_type_var()).collect(),
|
||||
_ => (0..exprs.len()).map(|_| self.table.new_type_var()).collect(),
|
||||
};
|
||||
|
||||
for (expr, ty) in exprs.iter().zip(tys.iter_mut()) {
|
||||
|
@ -443,7 +464,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
ty_app!(TypeCtor::Array, st) | ty_app!(TypeCtor::Slice, st) => {
|
||||
st.as_single().clone()
|
||||
}
|
||||
_ => self.new_type_var(),
|
||||
_ => self.table.new_type_var(),
|
||||
};
|
||||
|
||||
match array {
|
||||
|
@ -485,7 +506,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
};
|
||||
// use a new type variable if we got Ty::Unknown here
|
||||
let ty = self.insert_type_vars_shallow(ty);
|
||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
|
||||
let ty = self.resolve_ty_as_possible(ty);
|
||||
self.write_expr_ty(tgt_expr, ty.clone());
|
||||
ty
|
||||
}
|
||||
|
@ -514,7 +535,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
}
|
||||
|
||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
|
||||
let ty = self.resolve_ty_as_possible(ty);
|
||||
self.infer_pat(*pat, &ty, BindingMode::default());
|
||||
}
|
||||
Statement::Expr(expr) => {
|
||||
|
@ -558,7 +579,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
Some((ty, func)) => {
|
||||
let ty = canonicalized_receiver.decanonicalize_ty(ty);
|
||||
self.write_method_resolution(tgt_expr, func);
|
||||
(ty, self.db.value_ty(func.into()), Some(self.db.generic_params(func.into())))
|
||||
(ty, self.db.value_ty(func.into()), Some(generics(self.db, func.into())))
|
||||
}
|
||||
None => (receiver_ty, Ty::Unknown, None),
|
||||
};
|
||||
|
@ -607,6 +628,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
continue;
|
||||
}
|
||||
|
||||
let param_ty = self.insert_vars_for_impl_trait(param_ty);
|
||||
let param_ty = self.normalize_associated_types_in(param_ty);
|
||||
self.infer_expr_coerce(arg, &Expectation::has_type(param_ty.clone()));
|
||||
}
|
||||
|
@ -615,17 +637,17 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
|
||||
fn substs_for_method_call(
|
||||
&mut self,
|
||||
def_generics: Option<Arc<GenericParams>>,
|
||||
def_generics: Option<Generics>,
|
||||
generic_args: Option<&GenericArgs>,
|
||||
receiver_ty: &Ty,
|
||||
) -> Substs {
|
||||
let (parent_param_count, param_count) =
|
||||
def_generics.as_ref().map_or((0, 0), |g| (g.count_parent_params(), g.params.len()));
|
||||
let mut substs = Vec::with_capacity(parent_param_count + param_count);
|
||||
let (total_len, _parent_len, child_len) =
|
||||
def_generics.as_ref().map_or((0, 0, 0), |g| g.len_split());
|
||||
let mut substs = Vec::with_capacity(total_len);
|
||||
// Parent arguments are unknown, except for the receiver type
|
||||
if let Some(parent_generics) = def_generics.and_then(|p| p.parent_params.clone()) {
|
||||
for param in &parent_generics.params {
|
||||
if param.name == name::SELF_TYPE {
|
||||
if let Some(parent_generics) = def_generics.as_ref().map(|p| p.iter_parent()) {
|
||||
for (_id, param) in parent_generics {
|
||||
if param.name == name![Self] {
|
||||
substs.push(receiver_ty.clone());
|
||||
} else {
|
||||
substs.push(Ty::Unknown);
|
||||
|
@ -635,7 +657,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
// handle provided type arguments
|
||||
if let Some(generic_args) = generic_args {
|
||||
// if args are provided, it should be all of them, but we can't rely on that
|
||||
for arg in generic_args.args.iter().take(param_count) {
|
||||
for arg in generic_args.args.iter().take(child_len) {
|
||||
match arg {
|
||||
GenericArg::Type(type_ref) => {
|
||||
let ty = self.make_ty(type_ref);
|
||||
|
@ -645,10 +667,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
};
|
||||
let supplied_params = substs.len();
|
||||
for _ in supplied_params..parent_param_count + param_count {
|
||||
for _ in supplied_params..total_len {
|
||||
substs.push(Ty::Unknown);
|
||||
}
|
||||
assert_eq!(substs.len(), parent_param_count + param_count);
|
||||
assert_eq!(substs.len(), total_len);
|
||||
Substs(substs.into())
|
||||
}
|
||||
|
||||
|
@ -665,13 +687,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
// add obligation for trait implementation, if this is a trait method
|
||||
match def {
|
||||
CallableDef::FunctionId(f) => {
|
||||
if let ContainerId::TraitId(trait_) = f.lookup(self.db).container {
|
||||
if let AssocContainerId::TraitId(trait_) = f.lookup(self.db).container {
|
||||
// construct a TraitDef
|
||||
let substs = a_ty.parameters.prefix(
|
||||
self.db
|
||||
.generic_params(trait_.into())
|
||||
.count_params_including_parent(),
|
||||
);
|
||||
let substs =
|
||||
a_ty.parameters.prefix(generics(self.db, trait_.into()).len());
|
||||
self.obligations.push(Obligation::Trait(TraitRef {
|
||||
trait_: trait_.into(),
|
||||
substs,
|
||||
|
|
|
@ -170,7 +170,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
BindingMode::Move => inner_ty.clone(),
|
||||
};
|
||||
let bound_ty = self.resolve_ty_as_possible(&mut vec![], bound_ty);
|
||||
let bound_ty = self.resolve_ty_as_possible(bound_ty);
|
||||
self.write_pat_ty(pat, bound_ty);
|
||||
return inner_ty;
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
// use a new type variable if we got Ty::Unknown here
|
||||
let ty = self.insert_type_vars_shallow(ty);
|
||||
self.unify(&ty, expected);
|
||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
|
||||
let ty = self.resolve_ty_as_possible(ty);
|
||||
self.write_pat_ty(pat, ty.clone());
|
||||
ty
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
//! Path expression resolution.
|
||||
|
||||
use std::iter;
|
||||
|
||||
use hir_def::{
|
||||
path::{Path, PathKind, PathSegment},
|
||||
path::{Path, PathSegment},
|
||||
resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||
AssocItemId, ContainerId, Lookup,
|
||||
AssocContainerId, AssocItemId, Lookup,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
||||
|
@ -30,21 +32,21 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
path: &Path,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<Ty> {
|
||||
let (value, self_subst) = if let PathKind::Type(type_ref) = &path.kind {
|
||||
if path.segments.is_empty() {
|
||||
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
|
||||
if path.segments().is_empty() {
|
||||
// This can't actually happen syntax-wise
|
||||
return None;
|
||||
}
|
||||
let ty = self.make_ty(type_ref);
|
||||
let remaining_segments_for_ty = &path.segments[..path.segments.len() - 1];
|
||||
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
|
||||
let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty);
|
||||
self.resolve_ty_assoc_item(
|
||||
ty,
|
||||
&path.segments.last().expect("path had at least one segment").name,
|
||||
&path.segments().last().expect("path had at least one segment").name,
|
||||
id,
|
||||
)?
|
||||
} else {
|
||||
let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?;
|
||||
let value_or_partial = resolver.resolve_path_in_value_ns(self.db, path.mod_path())?;
|
||||
|
||||
match value_or_partial {
|
||||
ResolveValueResult::ValueNs(it) => (it, None),
|
||||
|
@ -57,7 +59,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
let typable: ValueTyDefId = match value {
|
||||
ValueNs::LocalBinding(pat) => {
|
||||
let ty = self.result.type_of_pat.get(pat)?.clone();
|
||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
|
||||
let ty = self.resolve_ty_as_possible(ty);
|
||||
return Some(ty);
|
||||
}
|
||||
ValueNs::FunctionId(it) => it.into(),
|
||||
|
@ -83,13 +85,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
remaining_index: usize,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<(ValueNs, Option<Substs>)> {
|
||||
assert!(remaining_index < path.segments.len());
|
||||
assert!(remaining_index < path.segments().len());
|
||||
// there may be more intermediate segments between the resolved one and
|
||||
// the end. Only the last segment needs to be resolved to a value; from
|
||||
// the segments before that, we need to get either a type or a trait ref.
|
||||
|
||||
let resolved_segment = &path.segments[remaining_index - 1];
|
||||
let remaining_segments = &path.segments[remaining_index..];
|
||||
let resolved_segment = path.segments().get(remaining_index - 1).unwrap();
|
||||
let remaining_segments = path.segments().skip(remaining_index);
|
||||
let is_before_last = remaining_segments.len() == 1;
|
||||
|
||||
match (def, is_before_last) {
|
||||
|
@ -110,7 +112,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
// trait but it's not the last segment, so the next segment
|
||||
// should resolve to an associated type of that trait (e.g. `<T
|
||||
// as Iterator>::Item::default`)
|
||||
let remaining_segments_for_ty = &remaining_segments[..remaining_segments.len() - 1];
|
||||
let remaining_segments_for_ty =
|
||||
remaining_segments.take(remaining_segments.len() - 1);
|
||||
let ty = Ty::from_partly_resolved_hir_path(
|
||||
self.db,
|
||||
&self.resolver,
|
||||
|
@ -136,7 +139,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
fn resolve_trait_assoc_item(
|
||||
&mut self,
|
||||
trait_ref: TraitRef,
|
||||
segment: &PathSegment,
|
||||
segment: PathSegment<'_>,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<(ValueNs, Option<Substs>)> {
|
||||
let trait_ = trait_ref.trait_;
|
||||
|
@ -148,7 +151,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
.map(|(_name, id)| (*id).into())
|
||||
.find_map(|item| match item {
|
||||
AssocItemId::FunctionId(func) => {
|
||||
if segment.name == self.db.function_data(func).name {
|
||||
if segment.name == &self.db.function_data(func).name {
|
||||
Some(AssocItemId::FunctionId(func))
|
||||
} else {
|
||||
None
|
||||
|
@ -156,7 +159,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
|
||||
AssocItemId::ConstId(konst) => {
|
||||
if self.db.const_data(konst).name.as_ref().map_or(false, |n| n == &segment.name)
|
||||
if self.db.const_data(konst).name.as_ref().map_or(false, |n| n == segment.name)
|
||||
{
|
||||
Some(AssocItemId::ConstId(konst))
|
||||
} else {
|
||||
|
@ -206,12 +209,23 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
AssocItemId::TypeAliasId(_) => unreachable!(),
|
||||
};
|
||||
let substs = match container {
|
||||
ContainerId::ImplId(_) => self.find_self_types(&def, ty.clone()),
|
||||
ContainerId::TraitId(trait_) => {
|
||||
AssocContainerId::ImplId(impl_id) => {
|
||||
let impl_substs = Substs::build_for_def(self.db, impl_id)
|
||||
.fill(iter::repeat_with(|| self.table.new_type_var()))
|
||||
.build();
|
||||
let impl_self_ty = self.db.impl_self_ty(impl_id).subst(&impl_substs);
|
||||
let substs = Substs::build_for_def(self.db, item)
|
||||
.use_parent_substs(&impl_substs)
|
||||
.fill_with_params()
|
||||
.build();
|
||||
self.unify(&impl_self_ty, &ty);
|
||||
Some(substs)
|
||||
}
|
||||
AssocContainerId::TraitId(trait_) => {
|
||||
// we're picking this method
|
||||
let trait_substs = Substs::build_for_def(self.db, trait_)
|
||||
.push(ty.clone())
|
||||
.fill(std::iter::repeat_with(|| self.new_type_var()))
|
||||
.fill(std::iter::repeat_with(|| self.table.new_type_var()))
|
||||
.build();
|
||||
let substs = Substs::build_for_def(self.db, item)
|
||||
.use_parent_substs(&trait_substs)
|
||||
|
@ -223,7 +237,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}));
|
||||
Some(substs)
|
||||
}
|
||||
ContainerId::ModuleId(_) => None,
|
||||
AssocContainerId::ContainerId(_) => None,
|
||||
};
|
||||
|
||||
self.write_assoc_resolution(id, item.into());
|
||||
|
@ -231,38 +245,4 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> {
|
||||
if let ValueNs::FunctionId(func) = *def {
|
||||
// We only do the infer if parent has generic params
|
||||
let gen = self.db.generic_params(func.into());
|
||||
if gen.count_parent_params() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let impl_id = match func.lookup(self.db).container {
|
||||
ContainerId::ImplId(it) => it,
|
||||
_ => return None,
|
||||
};
|
||||
let self_ty = self.db.impl_ty(impl_id).self_type().clone();
|
||||
let self_ty_substs = self_ty.substs()?;
|
||||
let actual_substs = actual_def_ty.substs()?;
|
||||
|
||||
let mut new_substs = vec![Ty::Unknown; gen.count_parent_params()];
|
||||
|
||||
// The following code *link up* the function actual parma type
|
||||
// and impl_block type param index
|
||||
self_ty_substs.iter().zip(actual_substs.iter()).for_each(|(param, pty)| {
|
||||
if let Ty::Param { idx, .. } = param {
|
||||
if let Some(s) = new_substs.get_mut(*idx as usize) {
|
||||
*s = pty.clone();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Some(Substs(new_substs.into()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
//! Unification and canonicalization logic.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
|
||||
|
||||
use test_utils::tested_by;
|
||||
|
||||
use super::{InferenceContext, Obligation};
|
||||
use crate::{
|
||||
db::HirDatabase, utils::make_mut_slice, Canonical, InEnvironment, InferTy, ProjectionPredicate,
|
||||
ProjectionTy, Substs, TraitRef, Ty, TypeWalk,
|
||||
ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
};
|
||||
|
||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
|
@ -24,7 +30,7 @@ where
|
|||
/// A stack of type variables that is used to detect recursive types (which
|
||||
/// are an error, but we need to protect against them to avoid stack
|
||||
/// overflows).
|
||||
var_stack: Vec<super::TypeVarId>,
|
||||
var_stack: Vec<TypeVarId>,
|
||||
}
|
||||
|
||||
pub(super) struct Canonicalized<T> {
|
||||
|
@ -53,14 +59,14 @@ where
|
|||
return tv.fallback_value();
|
||||
}
|
||||
if let Some(known_ty) =
|
||||
self.ctx.var_unification_table.inlined_probe_value(inner).known()
|
||||
self.ctx.table.var_unification_table.inlined_probe_value(inner).known()
|
||||
{
|
||||
self.var_stack.push(inner);
|
||||
let result = self.do_canonicalize_ty(known_ty.clone());
|
||||
self.var_stack.pop();
|
||||
result
|
||||
} else {
|
||||
let root = self.ctx.var_unification_table.find(inner);
|
||||
let root = self.ctx.table.var_unification_table.find(inner);
|
||||
let free_var = match tv {
|
||||
InferTy::TypeVar(_) => InferTy::TypeVar(root),
|
||||
InferTy::IntVar(_) => InferTy::IntVar(root),
|
||||
|
@ -153,10 +159,268 @@ impl<T> Canonicalized<T> {
|
|||
solution: Canonical<Vec<Ty>>,
|
||||
) {
|
||||
// the solution may contain new variables, which we need to convert to new inference vars
|
||||
let new_vars = Substs((0..solution.num_vars).map(|_| ctx.new_type_var()).collect());
|
||||
let new_vars = Substs((0..solution.num_vars).map(|_| ctx.table.new_type_var()).collect());
|
||||
for (i, ty) in solution.value.into_iter().enumerate() {
|
||||
let var = self.free_vars[i];
|
||||
ctx.unify(&Ty::Infer(var), &ty.subst_bound_vars(&new_vars));
|
||||
ctx.table.unify(&Ty::Infer(var), &ty.subst_bound_vars(&new_vars));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> {
|
||||
let mut table = InferenceTable::new();
|
||||
let vars =
|
||||
Substs::builder(ty1.num_vars).fill(std::iter::repeat_with(|| table.new_type_var())).build();
|
||||
let ty_with_vars = ty1.value.clone().subst_bound_vars(&vars);
|
||||
if !table.unify(&ty_with_vars, &ty2.value) {
|
||||
return None;
|
||||
}
|
||||
Some(
|
||||
Substs::builder(ty1.num_vars)
|
||||
.fill(vars.iter().map(|v| table.resolve_ty_completely(v.clone())))
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct InferenceTable {
|
||||
pub(super) var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
||||
}
|
||||
|
||||
impl InferenceTable {
|
||||
pub fn new() -> Self {
|
||||
InferenceTable { var_unification_table: InPlaceUnificationTable::new() }
|
||||
}
|
||||
|
||||
pub fn new_type_var(&mut self) -> Ty {
|
||||
Ty::Infer(InferTy::TypeVar(self.var_unification_table.new_key(TypeVarValue::Unknown)))
|
||||
}
|
||||
|
||||
pub fn new_integer_var(&mut self) -> Ty {
|
||||
Ty::Infer(InferTy::IntVar(self.var_unification_table.new_key(TypeVarValue::Unknown)))
|
||||
}
|
||||
|
||||
pub fn new_float_var(&mut self) -> Ty {
|
||||
Ty::Infer(InferTy::FloatVar(self.var_unification_table.new_key(TypeVarValue::Unknown)))
|
||||
}
|
||||
|
||||
pub fn new_maybe_never_type_var(&mut self) -> Ty {
|
||||
Ty::Infer(InferTy::MaybeNeverTypeVar(
|
||||
self.var_unification_table.new_key(TypeVarValue::Unknown),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn resolve_ty_completely(&mut self, ty: Ty) -> Ty {
|
||||
self.resolve_ty_completely_inner(&mut Vec::new(), ty)
|
||||
}
|
||||
|
||||
pub fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty {
|
||||
self.resolve_ty_as_possible_inner(&mut Vec::new(), ty)
|
||||
}
|
||||
|
||||
pub fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
||||
self.unify_inner(ty1, ty2, 0)
|
||||
}
|
||||
|
||||
pub fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs, depth: usize) -> bool {
|
||||
substs1.0.iter().zip(substs2.0.iter()).all(|(t1, t2)| self.unify_inner(t1, t2, depth))
|
||||
}
|
||||
|
||||
fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool {
|
||||
if depth > 1000 {
|
||||
// prevent stackoverflows
|
||||
panic!("infinite recursion in unification");
|
||||
}
|
||||
if ty1 == ty2 {
|
||||
return true;
|
||||
}
|
||||
// try to resolve type vars first
|
||||
let ty1 = self.resolve_ty_shallow(ty1);
|
||||
let ty2 = self.resolve_ty_shallow(ty2);
|
||||
match (&*ty1, &*ty2) {
|
||||
(Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => {
|
||||
self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1)
|
||||
}
|
||||
_ => self.unify_inner_trivial(&ty1, &ty2),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
||||
match (ty1, ty2) {
|
||||
(Ty::Unknown, _) | (_, Ty::Unknown) => true,
|
||||
|
||||
(Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2)))
|
||||
| (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2)))
|
||||
| (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2)))
|
||||
| (
|
||||
Ty::Infer(InferTy::MaybeNeverTypeVar(tv1)),
|
||||
Ty::Infer(InferTy::MaybeNeverTypeVar(tv2)),
|
||||
) => {
|
||||
// both type vars are unknown since we tried to resolve them
|
||||
self.var_unification_table.union(*tv1, *tv2);
|
||||
true
|
||||
}
|
||||
|
||||
// The order of MaybeNeverTypeVar matters here.
|
||||
// Unifying MaybeNeverTypeVar and TypeVar will let the latter become MaybeNeverTypeVar.
|
||||
// Unifying MaybeNeverTypeVar and other concrete type will let the former become it.
|
||||
(Ty::Infer(InferTy::TypeVar(tv)), other)
|
||||
| (other, Ty::Infer(InferTy::TypeVar(tv)))
|
||||
| (Ty::Infer(InferTy::MaybeNeverTypeVar(tv)), other)
|
||||
| (other, Ty::Infer(InferTy::MaybeNeverTypeVar(tv)))
|
||||
| (Ty::Infer(InferTy::IntVar(tv)), other @ ty_app!(TypeCtor::Int(_)))
|
||||
| (other @ ty_app!(TypeCtor::Int(_)), Ty::Infer(InferTy::IntVar(tv)))
|
||||
| (Ty::Infer(InferTy::FloatVar(tv)), other @ ty_app!(TypeCtor::Float(_)))
|
||||
| (other @ ty_app!(TypeCtor::Float(_)), Ty::Infer(InferTy::FloatVar(tv))) => {
|
||||
// the type var is unknown since we tried to resolve it
|
||||
self.var_unification_table.union_value(*tv, TypeVarValue::Known(other.clone()));
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// If `ty` is a type variable with known type, returns that type;
|
||||
/// otherwise, return ty.
|
||||
pub fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
|
||||
let mut ty = Cow::Borrowed(ty);
|
||||
// The type variable could resolve to a int/float variable. Hence try
|
||||
// resolving up to three times; each type of variable shouldn't occur
|
||||
// more than once
|
||||
for i in 0..3 {
|
||||
if i > 0 {
|
||||
tested_by!(type_var_resolves_to_int_var);
|
||||
}
|
||||
match &*ty {
|
||||
Ty::Infer(tv) => {
|
||||
let inner = tv.to_inner();
|
||||
match self.var_unification_table.inlined_probe_value(inner).known() {
|
||||
Some(known_ty) => {
|
||||
// The known_ty can't be a type var itself
|
||||
ty = Cow::Owned(known_ty.clone());
|
||||
}
|
||||
_ => return ty,
|
||||
}
|
||||
}
|
||||
_ => return ty,
|
||||
}
|
||||
}
|
||||
log::error!("Inference variable still not resolved: {:?}", ty);
|
||||
ty
|
||||
}
|
||||
|
||||
/// Resolves the type as far as currently possible, replacing type variables
|
||||
/// by their known types. All types returned by the infer_* functions should
|
||||
/// be resolved as far as possible, i.e. contain no type variables with
|
||||
/// known type.
|
||||
fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
|
||||
ty.fold(&mut |ty| match ty {
|
||||
Ty::Infer(tv) => {
|
||||
let inner = tv.to_inner();
|
||||
if tv_stack.contains(&inner) {
|
||||
tested_by!(type_var_cycles_resolve_as_possible);
|
||||
// recursive type
|
||||
return tv.fallback_value();
|
||||
}
|
||||
if let Some(known_ty) =
|
||||
self.var_unification_table.inlined_probe_value(inner).known()
|
||||
{
|
||||
// known_ty may contain other variables that are known by now
|
||||
tv_stack.push(inner);
|
||||
let result = self.resolve_ty_as_possible_inner(tv_stack, known_ty.clone());
|
||||
tv_stack.pop();
|
||||
result
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
}
|
||||
_ => ty,
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolves the type completely; type variables without known type are
|
||||
/// replaced by Ty::Unknown.
|
||||
fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
|
||||
ty.fold(&mut |ty| match ty {
|
||||
Ty::Infer(tv) => {
|
||||
let inner = tv.to_inner();
|
||||
if tv_stack.contains(&inner) {
|
||||
tested_by!(type_var_cycles_resolve_completely);
|
||||
// recursive type
|
||||
return tv.fallback_value();
|
||||
}
|
||||
if let Some(known_ty) =
|
||||
self.var_unification_table.inlined_probe_value(inner).known()
|
||||
{
|
||||
// known_ty may contain other variables that are known by now
|
||||
tv_stack.push(inner);
|
||||
let result = self.resolve_ty_completely_inner(tv_stack, known_ty.clone());
|
||||
tv_stack.pop();
|
||||
result
|
||||
} else {
|
||||
tv.fallback_value()
|
||||
}
|
||||
}
|
||||
_ => ty,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The ID of a type variable.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct TypeVarId(pub(super) u32);
|
||||
|
||||
impl UnifyKey for TypeVarId {
|
||||
type Value = TypeVarValue;
|
||||
|
||||
fn index(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn from_index(i: u32) -> Self {
|
||||
TypeVarId(i)
|
||||
}
|
||||
|
||||
fn tag() -> &'static str {
|
||||
"TypeVarId"
|
||||
}
|
||||
}
|
||||
|
||||
/// The value of a type variable: either we already know the type, or we don't
|
||||
/// know it yet.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum TypeVarValue {
|
||||
Known(Ty),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl TypeVarValue {
|
||||
fn known(&self) -> Option<&Ty> {
|
||||
match self {
|
||||
TypeVarValue::Known(ty) => Some(ty),
|
||||
TypeVarValue::Unknown => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UnifyValue for TypeVarValue {
|
||||
type Error = NoError;
|
||||
|
||||
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, NoError> {
|
||||
match (value1, value2) {
|
||||
// We should never equate two type variables, both of which have
|
||||
// known types. Instead, we recursively equate those types.
|
||||
(TypeVarValue::Known(t1), TypeVarValue::Known(t2)) => panic!(
|
||||
"equating two type variables, both of which have known types: {:?} and {:?}",
|
||||
t1, t2
|
||||
),
|
||||
|
||||
// If one side is known, prefer that one.
|
||||
(TypeVarValue::Known(..), TypeVarValue::Unknown) => Ok(value1.clone()),
|
||||
(TypeVarValue::Unknown, TypeVarValue::Known(..)) => Ok(value2.clone()),
|
||||
|
||||
(TypeVarValue::Unknown, TypeVarValue::Unknown) => Ok(TypeVarValue::Unknown),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,8 +44,8 @@ use std::sync::Arc;
|
|||
use std::{fmt, iter, mem};
|
||||
|
||||
use hir_def::{
|
||||
expr::ExprId, generics::GenericParams, type_ref::Mutability, AdtId, ContainerId, DefWithBodyId,
|
||||
GenericDefId, HasModule, Lookup, TraitId, TypeAliasId,
|
||||
expr::ExprId, type_ref::Mutability, AdtId, AssocContainerId, DefWithBodyId, GenericDefId,
|
||||
HasModule, Lookup, TraitId, TypeAliasId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use ra_db::{impl_intern_key, salsa, CrateId};
|
||||
|
@ -53,7 +53,7 @@ use ra_db::{impl_intern_key, salsa, CrateId};
|
|||
use crate::{
|
||||
db::HirDatabase,
|
||||
primitive::{FloatTy, IntTy, Uncertain},
|
||||
utils::make_mut_slice,
|
||||
utils::{generics, make_mut_slice, Generics},
|
||||
};
|
||||
use display::{HirDisplay, HirFormatter};
|
||||
|
||||
|
@ -166,16 +166,16 @@ impl TypeCtor {
|
|||
| TypeCtor::Closure { .. } // 1 param representing the signature of the closure
|
||||
=> 1,
|
||||
TypeCtor::Adt(adt) => {
|
||||
let generic_params = db.generic_params(AdtId::from(adt).into());
|
||||
generic_params.count_params_including_parent()
|
||||
let generic_params = generics(db, AdtId::from(adt).into());
|
||||
generic_params.len()
|
||||
}
|
||||
TypeCtor::FnDef(callable) => {
|
||||
let generic_params = db.generic_params(callable.into());
|
||||
generic_params.count_params_including_parent()
|
||||
let generic_params = generics(db, callable.into());
|
||||
generic_params.len()
|
||||
}
|
||||
TypeCtor::AssociatedType(type_alias) => {
|
||||
let generic_params = db.generic_params(type_alias.into());
|
||||
generic_params.count_params_including_parent()
|
||||
let generic_params = generics(db, type_alias.into());
|
||||
generic_params.len()
|
||||
}
|
||||
TypeCtor::FnPtr { num_args } => num_args as usize + 1,
|
||||
TypeCtor::Tuple { cardinality } => cardinality as usize,
|
||||
|
@ -251,7 +251,7 @@ impl ProjectionTy {
|
|||
|
||||
fn trait_(&self, db: &impl HirDatabase) -> TraitId {
|
||||
match self.associated_ty.lookup(db).container {
|
||||
ContainerId::TraitId(it) => it,
|
||||
AssocContainerId::TraitId(it) => it,
|
||||
_ => panic!("projection ty without parent trait"),
|
||||
}
|
||||
}
|
||||
|
@ -364,36 +364,26 @@ impl Substs {
|
|||
}
|
||||
|
||||
/// Return Substs that replace each parameter by itself (i.e. `Ty::Param`).
|
||||
pub fn identity(generic_params: &GenericParams) -> Substs {
|
||||
pub(crate) fn identity(generic_params: &Generics) -> Substs {
|
||||
Substs(
|
||||
generic_params
|
||||
.params_including_parent()
|
||||
.into_iter()
|
||||
.map(|p| Ty::Param { idx: p.idx, name: p.name.clone() })
|
||||
.collect(),
|
||||
generic_params.iter().map(|(idx, p)| Ty::Param { idx, name: p.name.clone() }).collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Return Substs that replace each parameter by a bound variable.
|
||||
pub fn bound_vars(generic_params: &GenericParams) -> Substs {
|
||||
Substs(
|
||||
generic_params
|
||||
.params_including_parent()
|
||||
.into_iter()
|
||||
.map(|p| Ty::Bound(p.idx))
|
||||
.collect(),
|
||||
)
|
||||
pub(crate) fn bound_vars(generic_params: &Generics) -> Substs {
|
||||
Substs(generic_params.iter().map(|(idx, _p)| Ty::Bound(idx)).collect())
|
||||
}
|
||||
|
||||
pub fn build_for_def(db: &impl HirDatabase, def: impl Into<GenericDefId>) -> SubstsBuilder {
|
||||
let def = def.into();
|
||||
let params = db.generic_params(def);
|
||||
let param_count = params.count_params_including_parent();
|
||||
let params = generics(db, def);
|
||||
let param_count = params.len();
|
||||
Substs::builder(param_count)
|
||||
}
|
||||
|
||||
pub fn build_for_generics(generic_params: &GenericParams) -> SubstsBuilder {
|
||||
Substs::builder(generic_params.count_params_including_parent())
|
||||
pub(crate) fn build_for_generics(generic_params: &Generics) -> SubstsBuilder {
|
||||
Substs::builder(generic_params.len())
|
||||
}
|
||||
|
||||
pub fn build_for_type_ctor(db: &impl HirDatabase, type_ctor: TypeCtor) -> SubstsBuilder {
|
||||
|
@ -486,21 +476,6 @@ impl TypeWalk for TraitRef {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum ImplTy {
|
||||
Inherent(Ty),
|
||||
TraitRef(TraitRef),
|
||||
}
|
||||
|
||||
impl ImplTy {
|
||||
pub(crate) fn self_type(&self) -> &Ty {
|
||||
match self {
|
||||
ImplTy::Inherent(it) => it,
|
||||
ImplTy::TraitRef(tr) => &tr.substs[0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `generics::WherePredicate`, but with resolved types: A condition on the
|
||||
/// parameters of a generic item.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -931,13 +906,44 @@ impl HirDisplay for ApplicationTy {
|
|||
write!(f, "{}", name)?;
|
||||
if self.parameters.len() > 0 {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(&*self.parameters.0, ", ")?;
|
||||
|
||||
let mut non_default_parameters = Vec::with_capacity(self.parameters.len());
|
||||
let parameters_to_write = if f.should_display_default_types() {
|
||||
self.parameters.0.as_ref()
|
||||
} else {
|
||||
match self
|
||||
.ctor
|
||||
.as_generic_def()
|
||||
.map(|generic_def_id| f.db.generic_defaults(generic_def_id))
|
||||
.filter(|defaults| !defaults.is_empty())
|
||||
{
|
||||
Option::None => self.parameters.0.as_ref(),
|
||||
Option::Some(default_parameters) => {
|
||||
for (i, parameter) in self.parameters.iter().enumerate() {
|
||||
match (parameter, default_parameters.get(i)) {
|
||||
(&Ty::Unknown, _) | (_, None) => {
|
||||
non_default_parameters.push(parameter.clone())
|
||||
}
|
||||
(_, Some(default_parameter))
|
||||
if parameter != default_parameter =>
|
||||
{
|
||||
non_default_parameters.push(parameter.clone())
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
&non_default_parameters
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
f.write_joined(parameters_to_write, ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
}
|
||||
TypeCtor::AssociatedType(type_alias) => {
|
||||
let trait_ = match type_alias.lookup(f.db).container {
|
||||
ContainerId::TraitId(it) => it,
|
||||
AssocContainerId::TraitId(it) => it,
|
||||
_ => panic!("not an associated type"),
|
||||
};
|
||||
let trait_name = f.db.trait_data(trait_).name.clone();
|
||||
|
|
|
@ -11,10 +11,10 @@ use std::sync::Arc;
|
|||
use hir_def::{
|
||||
builtin_type::BuiltinType,
|
||||
generics::WherePredicate,
|
||||
path::{GenericArg, Path, PathKind, PathSegment},
|
||||
path::{GenericArg, Path, PathSegment, PathSegments},
|
||||
resolver::{HasResolver, Resolver, TypeNs},
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
AdtId, AstItemDef, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId,
|
||||
AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId,
|
||||
LocalStructFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
|
||||
};
|
||||
use ra_arena::map::ArenaMap;
|
||||
|
@ -24,11 +24,11 @@ use crate::{
|
|||
db::HirDatabase,
|
||||
primitive::{FloatTy, IntTy},
|
||||
utils::{
|
||||
all_super_traits, associated_type_by_name_including_super_traits, make_mut_slice,
|
||||
all_super_traits, associated_type_by_name_including_super_traits, generics, make_mut_slice,
|
||||
variant_data,
|
||||
},
|
||||
FnSig, GenericPredicate, ImplTy, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment,
|
||||
TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
FnSig, GenericPredicate, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef,
|
||||
Ty, TypeCtor, TypeWalk,
|
||||
};
|
||||
|
||||
impl Ty {
|
||||
|
@ -101,17 +101,19 @@ impl Ty {
|
|||
TypeRef::Path(path) => path,
|
||||
_ => return None,
|
||||
};
|
||||
if let PathKind::Type(_) = &path.kind {
|
||||
if path.type_anchor().is_some() {
|
||||
return None;
|
||||
}
|
||||
if path.segments.len() > 1 {
|
||||
if path.segments().len() > 1 {
|
||||
return None;
|
||||
}
|
||||
let resolution = match resolver.resolve_path_in_type_ns(db, path) {
|
||||
let resolution = match resolver.resolve_path_in_type_ns(db, path.mod_path()) {
|
||||
Some((it, None)) => it,
|
||||
_ => return None,
|
||||
};
|
||||
if let TypeNs::GenericParam(idx) = resolution {
|
||||
if let TypeNs::GenericParam(param_id) = resolution {
|
||||
let generics = generics(db, resolver.generic_def().expect("generics in scope"));
|
||||
let idx = generics.param_idx(param_id);
|
||||
Some(idx)
|
||||
} else {
|
||||
None
|
||||
|
@ -122,11 +124,11 @@ impl Ty {
|
|||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
ty: Ty,
|
||||
remaining_segments: &[PathSegment],
|
||||
remaining_segments: PathSegments<'_>,
|
||||
) -> Ty {
|
||||
if remaining_segments.len() == 1 {
|
||||
// resolve unselected assoc types
|
||||
let segment = &remaining_segments[0];
|
||||
let segment = remaining_segments.first().unwrap();
|
||||
Ty::select_associated_type(db, resolver, ty, segment)
|
||||
} else if remaining_segments.len() > 1 {
|
||||
// FIXME report error (ambiguous associated type)
|
||||
|
@ -140,15 +142,15 @@ impl Ty {
|
|||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
resolution: TypeNs,
|
||||
resolved_segment: &PathSegment,
|
||||
remaining_segments: &[PathSegment],
|
||||
resolved_segment: PathSegment<'_>,
|
||||
remaining_segments: PathSegments<'_>,
|
||||
) -> Ty {
|
||||
let ty = match resolution {
|
||||
TypeNs::TraitId(trait_) => {
|
||||
let trait_ref =
|
||||
TraitRef::from_resolved_path(db, resolver, trait_, resolved_segment, None);
|
||||
return if remaining_segments.len() == 1 {
|
||||
let segment = &remaining_segments[0];
|
||||
let segment = remaining_segments.first().unwrap();
|
||||
let associated_ty = associated_type_by_name_including_super_traits(
|
||||
db,
|
||||
trait_ref.trait_,
|
||||
|
@ -174,12 +176,14 @@ impl Ty {
|
|||
Ty::Dyn(Arc::new([GenericPredicate::Implemented(trait_ref)]))
|
||||
};
|
||||
}
|
||||
TypeNs::GenericParam(idx) => {
|
||||
TypeNs::GenericParam(param_id) => {
|
||||
let generics = generics(db, resolver.generic_def().expect("generics in scope"));
|
||||
let idx = generics.param_idx(param_id);
|
||||
// FIXME: maybe return name in resolution?
|
||||
let name = resolved_segment.name.clone();
|
||||
let name = generics.param_name(param_id);
|
||||
Ty::Param { idx, name }
|
||||
}
|
||||
TypeNs::SelfType(impl_id) => db.impl_ty(impl_id).self_type().clone(),
|
||||
TypeNs::SelfType(impl_id) => db.impl_self_ty(impl_id).clone(),
|
||||
TypeNs::AdtSelfType(adt) => db.ty(adt.into()),
|
||||
|
||||
TypeNs::AdtId(it) => Ty::from_hir_path_inner(db, resolver, resolved_segment, it.into()),
|
||||
|
@ -198,21 +202,21 @@ impl Ty {
|
|||
|
||||
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty {
|
||||
// Resolve the path (in type namespace)
|
||||
if let PathKind::Type(type_ref) = &path.kind {
|
||||
if let Some(type_ref) = path.type_anchor() {
|
||||
let ty = Ty::from_hir(db, resolver, &type_ref);
|
||||
let remaining_segments = &path.segments[..];
|
||||
return Ty::from_type_relative_path(db, resolver, ty, remaining_segments);
|
||||
return Ty::from_type_relative_path(db, resolver, ty, path.segments());
|
||||
}
|
||||
let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) {
|
||||
let (resolution, remaining_index) =
|
||||
match resolver.resolve_path_in_type_ns(db, path.mod_path()) {
|
||||
Some(it) => it,
|
||||
None => return Ty::Unknown,
|
||||
};
|
||||
let (resolved_segment, remaining_segments) = match remaining_index {
|
||||
None => (
|
||||
path.segments.last().expect("resolved path has at least one element"),
|
||||
&[] as &[PathSegment],
|
||||
path.segments().last().expect("resolved path has at least one element"),
|
||||
PathSegments::EMPTY,
|
||||
),
|
||||
Some(i) => (&path.segments[i - 1], &path.segments[i..]),
|
||||
Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
|
||||
};
|
||||
Ty::from_partly_resolved_hir_path(
|
||||
db,
|
||||
|
@ -227,7 +231,7 @@ impl Ty {
|
|||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
self_ty: Ty,
|
||||
segment: &PathSegment,
|
||||
segment: PathSegment<'_>,
|
||||
) -> Ty {
|
||||
let param_idx = match self_ty {
|
||||
Ty::Param { idx, .. } => idx,
|
||||
|
@ -257,7 +261,7 @@ impl Ty {
|
|||
fn from_hir_path_inner(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
segment: &PathSegment,
|
||||
segment: PathSegment<'_>,
|
||||
typable: TyDefId,
|
||||
) -> Ty {
|
||||
let generic_def = match typable {
|
||||
|
@ -280,7 +284,7 @@ impl Ty {
|
|||
// special-case enum variants
|
||||
resolved: ValueTyDefId,
|
||||
) -> Substs {
|
||||
let last = path.segments.last().expect("path should have at least one segment");
|
||||
let last = path.segments().last().expect("path should have at least one segment");
|
||||
let (segment, generic_def) = match resolved {
|
||||
ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
|
||||
ValueTyDefId::StructId(it) => (last, Some(it.into())),
|
||||
|
@ -292,13 +296,11 @@ impl Ty {
|
|||
// referring to the variant. So `Option::<T>::None` and
|
||||
// `Option::None::<T>` are both allowed (though the former is
|
||||
// preferred). See also `def_ids_for_path_segments` in rustc.
|
||||
let len = path.segments.len();
|
||||
let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() {
|
||||
// Option::<T>::None
|
||||
&path.segments[len - 2]
|
||||
} else {
|
||||
// Option::None::<T>
|
||||
last
|
||||
let len = path.segments().len();
|
||||
let penultimate = if len >= 2 { path.segments().get(len - 2) } else { None };
|
||||
let segment = match penultimate {
|
||||
Some(segment) if segment.args_and_bindings.is_some() => segment,
|
||||
_ => last,
|
||||
};
|
||||
(segment, Some(var.parent.into()))
|
||||
}
|
||||
|
@ -310,16 +312,15 @@ impl Ty {
|
|||
pub(super) fn substs_from_path_segment(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
segment: &PathSegment,
|
||||
segment: PathSegment<'_>,
|
||||
def_generic: Option<GenericDefId>,
|
||||
add_self_param: bool,
|
||||
) -> Substs {
|
||||
let mut substs = Vec::new();
|
||||
let def_generics = def_generic.map(|def| db.generic_params(def.into()));
|
||||
let def_generics = def_generic.map(|def| generics(db, def.into()));
|
||||
|
||||
let (parent_param_count, param_count) =
|
||||
def_generics.map_or((0, 0), |g| (g.count_parent_params(), g.params.len()));
|
||||
substs.extend(iter::repeat(Ty::Unknown).take(parent_param_count));
|
||||
let (total_len, parent_len, child_len) = def_generics.map_or((0, 0, 0), |g| g.len_split());
|
||||
substs.extend(iter::repeat(Ty::Unknown).take(parent_len));
|
||||
if add_self_param {
|
||||
// FIXME this add_self_param argument is kind of a hack: Traits have the
|
||||
// Self type as an implicit first type parameter, but it can't be
|
||||
|
@ -330,8 +331,8 @@ pub(super) fn substs_from_path_segment(
|
|||
if let Some(generic_args) = &segment.args_and_bindings {
|
||||
// if args are provided, it should be all of them, but we can't rely on that
|
||||
let self_param_correction = if add_self_param { 1 } else { 0 };
|
||||
let param_count = param_count - self_param_correction;
|
||||
for arg in generic_args.args.iter().take(param_count) {
|
||||
let child_len = child_len + self_param_correction;
|
||||
for arg in generic_args.args.iter().take(child_len) {
|
||||
match arg {
|
||||
GenericArg::Type(type_ref) => {
|
||||
let ty = Ty::from_hir(db, resolver, type_ref);
|
||||
|
@ -342,10 +343,10 @@ pub(super) fn substs_from_path_segment(
|
|||
}
|
||||
// add placeholders for args that were not provided
|
||||
let supplied_params = substs.len();
|
||||
for _ in supplied_params..parent_param_count + param_count {
|
||||
for _ in supplied_params..total_len {
|
||||
substs.push(Ty::Unknown);
|
||||
}
|
||||
assert_eq!(substs.len(), parent_param_count + param_count);
|
||||
assert_eq!(substs.len(), total_len);
|
||||
|
||||
// handle defaults
|
||||
if let Some(def_generic) = def_generic {
|
||||
|
@ -369,11 +370,11 @@ impl TraitRef {
|
|||
path: &Path,
|
||||
explicit_self_ty: Option<Ty>,
|
||||
) -> Option<Self> {
|
||||
let resolved = match resolver.resolve_path_in_type_ns_fully(db, &path)? {
|
||||
let resolved = match resolver.resolve_path_in_type_ns_fully(db, path.mod_path())? {
|
||||
TypeNs::TraitId(tr) => tr,
|
||||
_ => return None,
|
||||
};
|
||||
let segment = path.segments.last().expect("path should have at least one segment");
|
||||
let segment = path.segments().last().expect("path should have at least one segment");
|
||||
Some(TraitRef::from_resolved_path(db, resolver, resolved.into(), segment, explicit_self_ty))
|
||||
}
|
||||
|
||||
|
@ -381,7 +382,7 @@ impl TraitRef {
|
|||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
resolved: TraitId,
|
||||
segment: &PathSegment,
|
||||
segment: PathSegment<'_>,
|
||||
explicit_self_ty: Option<Ty>,
|
||||
) -> Self {
|
||||
let mut substs = TraitRef::substs_from_path(db, resolver, segment, resolved);
|
||||
|
@ -407,7 +408,7 @@ impl TraitRef {
|
|||
fn substs_from_path(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
segment: &PathSegment,
|
||||
segment: PathSegment<'_>,
|
||||
resolved: TraitId,
|
||||
) -> Substs {
|
||||
let has_self_param =
|
||||
|
@ -461,12 +462,12 @@ fn assoc_type_bindings_from_type_bound<'a>(
|
|||
trait_ref: TraitRef,
|
||||
) -> impl Iterator<Item = GenericPredicate> + 'a {
|
||||
let last_segment = match bound {
|
||||
TypeBound::Path(path) => path.segments.last(),
|
||||
TypeBound::Path(path) => path.segments().last(),
|
||||
TypeBound::Error => None,
|
||||
};
|
||||
last_segment
|
||||
.into_iter()
|
||||
.flat_map(|segment| segment.args_and_bindings.iter())
|
||||
.flat_map(|segment| segment.args_and_bindings.into_iter())
|
||||
.flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
|
||||
.map(move |(name, type_ref)| {
|
||||
let associated_ty =
|
||||
|
@ -532,6 +533,15 @@ pub(crate) fn generic_predicates_for_param_query(
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn generic_predicates_for_param_recover(
|
||||
_db: &impl HirDatabase,
|
||||
_cycle: &[String],
|
||||
_def: &GenericDefId,
|
||||
_param_idx: &u32,
|
||||
) -> Arc<[GenericPredicate]> {
|
||||
Arc::new([])
|
||||
}
|
||||
|
||||
impl TraitEnvironment {
|
||||
pub fn lower(db: &impl HirDatabase, resolver: &Resolver) -> Arc<TraitEnvironment> {
|
||||
let predicates = resolver
|
||||
|
@ -558,12 +568,11 @@ pub(crate) fn generic_predicates_query(
|
|||
/// Resolve the default type params from generics
|
||||
pub(crate) fn generic_defaults_query(db: &impl HirDatabase, def: GenericDefId) -> Substs {
|
||||
let resolver = def.resolver(db);
|
||||
let generic_params = db.generic_params(def.into());
|
||||
let generic_params = generics(db, def.into());
|
||||
|
||||
let defaults = generic_params
|
||||
.params_including_parent()
|
||||
.into_iter()
|
||||
.map(|p| p.default.as_ref().map_or(Ty::Unknown, |t| Ty::from_hir(db, &resolver, t)))
|
||||
.iter()
|
||||
.map(|(_idx, p)| p.default.as_ref().map_or(Ty::Unknown, |t| Ty::from_hir(db, &resolver, t)))
|
||||
.collect();
|
||||
|
||||
Substs(defaults)
|
||||
|
@ -580,7 +589,7 @@ fn fn_sig_for_fn(db: &impl HirDatabase, def: FunctionId) -> FnSig {
|
|||
/// Build the declared type of a function. This should not need to look at the
|
||||
/// function body.
|
||||
fn type_for_fn(db: &impl HirDatabase, def: FunctionId) -> Ty {
|
||||
let generics = db.generic_params(def.into());
|
||||
let generics = generics(db, def.into());
|
||||
let substs = Substs::identity(&generics);
|
||||
Ty::apply(TypeCtor::FnDef(def.into()), substs)
|
||||
}
|
||||
|
@ -630,7 +639,7 @@ fn type_for_struct_constructor(db: &impl HirDatabase, def: StructId) -> Ty {
|
|||
if struct_data.variant_data.is_unit() {
|
||||
return type_for_adt(db, def.into()); // Unit struct
|
||||
}
|
||||
let generics = db.generic_params(def.into());
|
||||
let generics = generics(db, def.into());
|
||||
let substs = Substs::identity(&generics);
|
||||
Ty::apply(TypeCtor::FnDef(def.into()), substs)
|
||||
}
|
||||
|
@ -644,7 +653,7 @@ fn fn_sig_for_enum_variant_constructor(db: &impl HirDatabase, def: EnumVariantId
|
|||
.iter()
|
||||
.map(|(_, field)| Ty::from_hir(db, &resolver, &field.type_ref))
|
||||
.collect::<Vec<_>>();
|
||||
let generics = db.generic_params(def.parent.into());
|
||||
let generics = generics(db, def.parent.into());
|
||||
let substs = Substs::identity(&generics);
|
||||
let ret = type_for_adt(db, def.parent.into()).subst(&substs);
|
||||
FnSig::from_params_and_return(params, ret)
|
||||
|
@ -657,18 +666,18 @@ fn type_for_enum_variant_constructor(db: &impl HirDatabase, def: EnumVariantId)
|
|||
if var_data.is_unit() {
|
||||
return type_for_adt(db, def.parent.into()); // Unit variant
|
||||
}
|
||||
let generics = db.generic_params(def.parent.into());
|
||||
let generics = generics(db, def.parent.into());
|
||||
let substs = Substs::identity(&generics);
|
||||
Ty::apply(TypeCtor::FnDef(EnumVariantId::from(def).into()), substs)
|
||||
}
|
||||
|
||||
fn type_for_adt(db: &impl HirDatabase, adt: AdtId) -> Ty {
|
||||
let generics = db.generic_params(adt.into());
|
||||
let generics = generics(db, adt.into());
|
||||
Ty::apply(TypeCtor::Adt(adt), Substs::identity(&generics))
|
||||
}
|
||||
|
||||
fn type_for_type_alias(db: &impl HirDatabase, t: TypeAliasId) -> Ty {
|
||||
let generics = db.generic_params(t.into());
|
||||
let generics = generics(db, t.into());
|
||||
let resolver = t.resolver(db);
|
||||
let type_ref = &db.type_alias_data(t).type_ref;
|
||||
let substs = Substs::identity(&generics);
|
||||
|
@ -687,10 +696,11 @@ impl_froms!(CallableDef: FunctionId, StructId, EnumVariantId);
|
|||
impl CallableDef {
|
||||
pub fn krate(self, db: &impl HirDatabase) -> CrateId {
|
||||
match self {
|
||||
CallableDef::FunctionId(f) => f.lookup(db).module(db).krate,
|
||||
CallableDef::StructId(s) => s.module(db).krate,
|
||||
CallableDef::EnumVariantId(e) => e.parent.module(db).krate,
|
||||
CallableDef::FunctionId(f) => f.lookup(db).module(db),
|
||||
CallableDef::StructId(s) => s.lookup(db).container.module(db),
|
||||
CallableDef::EnumVariantId(e) => e.parent.lookup(db).container.module(db),
|
||||
}
|
||||
.krate
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -733,6 +743,11 @@ pub(crate) fn ty_query(db: &impl HirDatabase, def: TyDefId) -> Ty {
|
|||
TyDefId::TypeAliasId(it) => type_for_type_alias(db, it),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ty_recover(_db: &impl HirDatabase, _cycle: &[String], _def: &TyDefId) -> Ty {
|
||||
Ty::Unknown
|
||||
}
|
||||
|
||||
pub(crate) fn value_ty_query(db: &impl HirDatabase, def: ValueTyDefId) -> Ty {
|
||||
match def {
|
||||
ValueTyDefId::FunctionId(it) => type_for_fn(db, it),
|
||||
|
@ -743,17 +758,24 @@ pub(crate) fn value_ty_query(db: &impl HirDatabase, def: ValueTyDefId) -> Ty {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn impl_ty_query(db: &impl HirDatabase, impl_id: ImplId) -> ImplTy {
|
||||
pub(crate) fn impl_self_ty_query(db: &impl HirDatabase, impl_id: ImplId) -> Ty {
|
||||
let impl_data = db.impl_data(impl_id);
|
||||
let resolver = impl_id.resolver(db);
|
||||
let self_ty = Ty::from_hir(db, &resolver, &impl_data.target_type);
|
||||
match impl_data.target_trait.as_ref() {
|
||||
Some(trait_ref) => {
|
||||
match TraitRef::from_hir(db, &resolver, trait_ref, Some(self_ty.clone())) {
|
||||
Some(it) => ImplTy::TraitRef(it),
|
||||
None => ImplTy::Inherent(self_ty),
|
||||
Ty::from_hir(db, &resolver, &impl_data.target_type)
|
||||
}
|
||||
|
||||
pub(crate) fn impl_self_ty_recover(
|
||||
_db: &impl HirDatabase,
|
||||
_cycle: &[String],
|
||||
_impl_id: &ImplId,
|
||||
) -> Ty {
|
||||
Ty::Unknown
|
||||
}
|
||||
None => ImplTy::Inherent(self_ty),
|
||||
}
|
||||
|
||||
pub(crate) fn impl_trait_query(db: &impl HirDatabase, impl_id: ImplId) -> Option<TraitRef> {
|
||||
let impl_data = db.impl_data(impl_id);
|
||||
let resolver = impl_id.resolver(db);
|
||||
let self_ty = db.impl_self_ty(impl_id);
|
||||
let target_trait = impl_data.target_trait.as_ref()?;
|
||||
TraitRef::from_hir(db, &resolver, target_trait, Some(self_ty.clone()))
|
||||
}
|
||||
|
|
|
@ -6,4 +6,5 @@ test_utils::marks!(
|
|||
type_var_resolves_to_int_var
|
||||
match_ergonomics_ref
|
||||
coerce_merge_fail_fallback
|
||||
insert_vars_for_impl_trait
|
||||
);
|
||||
|
|
|
@ -6,20 +6,21 @@ use std::sync::Arc;
|
|||
|
||||
use arrayvec::ArrayVec;
|
||||
use hir_def::{
|
||||
lang_item::LangItemTarget, resolver::Resolver, type_ref::Mutability, AssocItemId, AstItemDef,
|
||||
FunctionId, HasModule, ImplId, TraitId,
|
||||
lang_item::LangItemTarget, resolver::Resolver, type_ref::Mutability, AssocContainerId,
|
||||
AssocItemId, FunctionId, HasModule, ImplId, Lookup, TraitId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use ra_db::CrateId;
|
||||
use ra_prof::profile;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use super::Substs;
|
||||
use crate::{
|
||||
autoderef,
|
||||
db::HirDatabase,
|
||||
primitive::{FloatBitness, Uncertain},
|
||||
utils::all_super_traits,
|
||||
Canonical, ImplTy, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor,
|
||||
Canonical, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
};
|
||||
|
||||
/// This is used as a key for indexing impls.
|
||||
|
@ -57,12 +58,13 @@ impl CrateImplBlocks {
|
|||
|
||||
let crate_def_map = db.crate_def_map(krate);
|
||||
for (_module_id, module_data) in crate_def_map.modules.iter() {
|
||||
for &impl_id in module_data.impls.iter() {
|
||||
match db.impl_ty(impl_id) {
|
||||
ImplTy::TraitRef(tr) => {
|
||||
for impl_id in module_data.scope.impls() {
|
||||
match db.impl_trait(impl_id) {
|
||||
Some(tr) => {
|
||||
res.impls_by_trait.entry(tr.trait_).or_default().push(impl_id);
|
||||
}
|
||||
ImplTy::Inherent(self_ty) => {
|
||||
None => {
|
||||
let self_ty = db.impl_self_ty(impl_id);
|
||||
if let Some(self_ty_fp) = TyFingerprint::for_impl(&self_ty) {
|
||||
res.impls.entry(self_ty_fp).or_default().push(impl_id);
|
||||
}
|
||||
|
@ -132,7 +134,7 @@ impl Ty {
|
|||
LangItemTarget::ImplBlockId(it) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.map(|it| it.module(db).krate)
|
||||
.map(|it| it.lookup(db).container.module(db).krate)
|
||||
.collect();
|
||||
Some(res)
|
||||
}
|
||||
|
@ -175,7 +177,6 @@ pub fn iterate_method_candidates<T>(
|
|||
mode: LookupMode,
|
||||
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let krate = resolver.krate()?;
|
||||
match mode {
|
||||
LookupMode::MethodCall => {
|
||||
// For method calls, rust first does any number of autoderef, and then one
|
||||
|
@ -188,57 +189,159 @@ pub fn iterate_method_candidates<T>(
|
|||
// rustc does an autoderef and then autoref again).
|
||||
let environment = TraitEnvironment::lower(db, resolver);
|
||||
let ty = InEnvironment { value: ty.clone(), environment };
|
||||
for derefed_ty in autoderef::autoderef(db, resolver.krate(), ty) {
|
||||
if let Some(result) =
|
||||
iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
}
|
||||
if let Some(result) = iterate_trait_method_candidates(
|
||||
&derefed_ty,
|
||||
let krate = resolver.krate()?;
|
||||
|
||||
// We have to be careful about the order we're looking at candidates
|
||||
// in here. Consider the case where we're resolving `x.clone()`
|
||||
// where `x: &Vec<_>`. This resolves to the clone method with self
|
||||
// type `Vec<_>`, *not* `&_`. I.e. we need to consider methods where
|
||||
// the receiver type exactly matches before cases where we have to
|
||||
// do autoref. But in the autoderef steps, the `&_` self type comes
|
||||
// up *before* the `Vec<_>` self type.
|
||||
//
|
||||
// On the other hand, we don't want to just pick any by-value method
|
||||
// before any by-autoref method; it's just that we need to consider
|
||||
// the methods by autoderef order of *receiver types*, not *self
|
||||
// types*.
|
||||
|
||||
let deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty.clone()).collect();
|
||||
for i in 0..deref_chain.len() {
|
||||
if let Some(result) = iterate_method_candidates_with_autoref(
|
||||
&deref_chain[i..],
|
||||
db,
|
||||
resolver,
|
||||
name,
|
||||
mode,
|
||||
&mut callback,
|
||||
) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
LookupMode::Path => {
|
||||
// No autoderef for path lookups
|
||||
iterate_method_candidates_for_self_ty(&ty, db, resolver, name, &mut callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn iterate_method_candidates_with_autoref<T>(
|
||||
deref_chain: &[Canonical<Ty>],
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
if let Some(result) = iterate_method_candidates_by_receiver(
|
||||
&deref_chain[0],
|
||||
&deref_chain[1..],
|
||||
db,
|
||||
resolver,
|
||||
name,
|
||||
&mut callback,
|
||||
) {
|
||||
return Some(result);
|
||||
}
|
||||
let refed = Canonical {
|
||||
num_vars: deref_chain[0].num_vars,
|
||||
value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()),
|
||||
};
|
||||
if let Some(result) = iterate_method_candidates_by_receiver(
|
||||
&refed,
|
||||
deref_chain,
|
||||
db,
|
||||
resolver,
|
||||
name,
|
||||
&mut callback,
|
||||
) {
|
||||
return Some(result);
|
||||
}
|
||||
let ref_muted = Canonical {
|
||||
num_vars: deref_chain[0].num_vars,
|
||||
value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()),
|
||||
};
|
||||
if let Some(result) = iterate_method_candidates_by_receiver(
|
||||
&ref_muted,
|
||||
deref_chain,
|
||||
db,
|
||||
resolver,
|
||||
name,
|
||||
&mut callback,
|
||||
) {
|
||||
return Some(result);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn iterate_method_candidates_by_receiver<T>(
|
||||
receiver_ty: &Canonical<Ty>,
|
||||
rest_of_deref_chain: &[Canonical<Ty>],
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
// We're looking for methods with *receiver* type receiver_ty. These could
|
||||
// be found in any of the derefs of receiver_ty, so we have to go through
|
||||
// that.
|
||||
let krate = resolver.krate()?;
|
||||
for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) {
|
||||
if let Some(result) =
|
||||
iterate_inherent_methods(&ty, db, name, mode, krate.into(), &mut callback)
|
||||
iterate_inherent_methods(self_ty, db, name, Some(receiver_ty), krate, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
}
|
||||
if let Some(result) =
|
||||
iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
}
|
||||
for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) {
|
||||
if let Some(result) = iterate_trait_method_candidates(
|
||||
self_ty,
|
||||
db,
|
||||
resolver,
|
||||
name,
|
||||
Some(receiver_ty),
|
||||
&mut callback,
|
||||
) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn iterate_trait_method_candidates<T>(
|
||||
ty: &Canonical<Ty>,
|
||||
fn iterate_method_candidates_for_self_ty<T>(
|
||||
self_ty: &Canonical<Ty>,
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
name: Option<&Name>,
|
||||
mode: LookupMode,
|
||||
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let krate = resolver.krate()?;
|
||||
if let Some(result) = iterate_inherent_methods(self_ty, db, name, None, krate, &mut callback) {
|
||||
return Some(result);
|
||||
}
|
||||
if let Some(result) =
|
||||
iterate_trait_method_candidates(self_ty, db, resolver, name, None, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn iterate_trait_method_candidates<T>(
|
||||
self_ty: &Canonical<Ty>,
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
name: Option<&Name>,
|
||||
receiver_ty: Option<&Canonical<Ty>>,
|
||||
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let krate = resolver.krate()?;
|
||||
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
|
||||
let env = TraitEnvironment::lower(db, resolver);
|
||||
// if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope
|
||||
let inherent_trait = ty.value.inherent_trait().into_iter();
|
||||
let inherent_trait = self_ty.value.inherent_trait().into_iter();
|
||||
// if we have `T: Trait` in the param env, the trait doesn't need to be in scope
|
||||
let traits_from_env = env
|
||||
.trait_predicates_for_self_ty(&ty.value)
|
||||
.trait_predicates_for_self_ty(&self_ty.value)
|
||||
.map(|tr| tr.trait_)
|
||||
.flat_map(|t| all_super_traits(db, t));
|
||||
let traits =
|
||||
|
@ -251,17 +354,17 @@ fn iterate_trait_method_candidates<T>(
|
|||
// iteration
|
||||
let mut known_implemented = false;
|
||||
for (_name, item) in data.items.iter() {
|
||||
if !is_valid_candidate(db, name, mode, (*item).into()) {
|
||||
if !is_valid_candidate(db, name, receiver_ty, (*item).into(), self_ty) {
|
||||
continue;
|
||||
}
|
||||
if !known_implemented {
|
||||
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
|
||||
let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone());
|
||||
if db.trait_solve(krate.into(), goal).is_none() {
|
||||
continue 'traits;
|
||||
}
|
||||
}
|
||||
known_implemented = true;
|
||||
if let Some(result) = callback(&ty.value, (*item).into()) {
|
||||
if let Some(result) = callback(&self_ty.value, (*item).into()) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
|
@ -270,22 +373,22 @@ fn iterate_trait_method_candidates<T>(
|
|||
}
|
||||
|
||||
fn iterate_inherent_methods<T>(
|
||||
ty: &Canonical<Ty>,
|
||||
self_ty: &Canonical<Ty>,
|
||||
db: &impl HirDatabase,
|
||||
name: Option<&Name>,
|
||||
mode: LookupMode,
|
||||
receiver_ty: Option<&Canonical<Ty>>,
|
||||
krate: CrateId,
|
||||
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
for krate in ty.value.def_crates(db, krate)? {
|
||||
for krate in self_ty.value.def_crates(db, krate)? {
|
||||
let impls = db.impls_in_crate(krate);
|
||||
|
||||
for impl_block in impls.lookup_impl_blocks(&ty.value) {
|
||||
for impl_block in impls.lookup_impl_blocks(&self_ty.value) {
|
||||
for &item in db.impl_data(impl_block).items.iter() {
|
||||
if !is_valid_candidate(db, name, mode, item) {
|
||||
if !is_valid_candidate(db, name, receiver_ty, item, self_ty) {
|
||||
continue;
|
||||
}
|
||||
if let Some(result) = callback(&ty.value, item.into()) {
|
||||
if let Some(result) = callback(&self_ty.value, item) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
|
@ -297,23 +400,68 @@ fn iterate_inherent_methods<T>(
|
|||
fn is_valid_candidate(
|
||||
db: &impl HirDatabase,
|
||||
name: Option<&Name>,
|
||||
mode: LookupMode,
|
||||
receiver_ty: Option<&Canonical<Ty>>,
|
||||
item: AssocItemId,
|
||||
self_ty: &Canonical<Ty>,
|
||||
) -> bool {
|
||||
match item {
|
||||
AssocItemId::FunctionId(m) => {
|
||||
let data = db.function_data(m);
|
||||
name.map_or(true, |name| &data.name == name)
|
||||
&& (data.has_self_param || mode == LookupMode::Path)
|
||||
if let Some(name) = name {
|
||||
if &data.name != name {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if let Some(receiver_ty) = receiver_ty {
|
||||
if !data.has_self_param {
|
||||
return false;
|
||||
}
|
||||
let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) {
|
||||
Some(ty) => ty,
|
||||
None => return false,
|
||||
};
|
||||
if transformed_receiver_ty != receiver_ty.value {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
AssocItemId::ConstId(c) => {
|
||||
let data = db.const_data(c);
|
||||
name.map_or(true, |name| data.name.as_ref() == Some(name)) && (mode == LookupMode::Path)
|
||||
name.map_or(true, |name| data.name.as_ref() == Some(name)) && receiver_ty.is_none()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn inherent_impl_substs(
|
||||
db: &impl HirDatabase,
|
||||
impl_id: ImplId,
|
||||
self_ty: &Canonical<Ty>,
|
||||
) -> Option<Substs> {
|
||||
let vars = Substs::build_for_def(db, impl_id).fill_with_bound_vars(0).build();
|
||||
let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars);
|
||||
let self_ty_with_vars = Canonical { num_vars: vars.len(), value: self_ty_with_vars };
|
||||
super::infer::unify(&self_ty_with_vars, self_ty)
|
||||
}
|
||||
|
||||
fn transform_receiver_ty(
|
||||
db: &impl HirDatabase,
|
||||
function_id: FunctionId,
|
||||
self_ty: &Canonical<Ty>,
|
||||
) -> Option<Ty> {
|
||||
let substs = match function_id.lookup(db).container {
|
||||
AssocContainerId::TraitId(_) => Substs::build_for_def(db, function_id)
|
||||
.push(self_ty.value.clone())
|
||||
.fill_with_unknown()
|
||||
.build(),
|
||||
AssocContainerId::ImplId(impl_id) => inherent_impl_substs(db, impl_id, &self_ty)?,
|
||||
AssocContainerId::ContainerId(_) => unreachable!(),
|
||||
};
|
||||
let sig = db.callable_item_signature(function_id.into());
|
||||
Some(sig.params()[0].clone().subst(&substs))
|
||||
}
|
||||
|
||||
pub fn implements_trait(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &impl HirDatabase,
|
||||
|
|
|
@ -74,7 +74,7 @@ impl TestDB {
|
|||
for &krate in self.relevant_crates(file_id).iter() {
|
||||
let crate_def_map = self.crate_def_map(krate);
|
||||
for (local_id, data) in crate_def_map.modules.iter() {
|
||||
if data.definition == Some(file_id) {
|
||||
if data.origin.file_id() == Some(file_id) {
|
||||
return ModuleId { krate, local_id };
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ impl TestDB {
|
|||
}
|
||||
}
|
||||
|
||||
for &impl_id in crate_def_map[module_id].impls.iter() {
|
||||
for impl_id in crate_def_map[module_id].scope.impls() {
|
||||
let impl_data = self.impl_data(impl_id);
|
||||
for item in impl_data.items.iter() {
|
||||
if let AssocItemId::FunctionId(f) = item {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,4 @@
|
|||
use super::infer_with_mismatches;
|
||||
use insta::assert_snapshot;
|
||||
use test_utils::covers;
|
||||
|
||||
|
@ -367,3 +368,161 @@ fn test() {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_coerce_unknown() {
|
||||
assert_snapshot!(
|
||||
infer_with_mismatches(r#"
|
||||
fn foo() -> u32 {
|
||||
return unknown;
|
||||
}
|
||||
"#, true),
|
||||
@r###"
|
||||
[17; 40) '{ ...own; }': !
|
||||
[23; 37) 'return unknown': !
|
||||
[30; 37) 'unknown': u32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_autoderef() {
|
||||
assert_snapshot!(
|
||||
infer_with_mismatches(r#"
|
||||
struct Foo;
|
||||
fn takes_ref_foo(x: &Foo) {}
|
||||
fn test() {
|
||||
takes_ref_foo(&Foo);
|
||||
takes_ref_foo(&&Foo);
|
||||
takes_ref_foo(&&&Foo);
|
||||
}
|
||||
"#, true),
|
||||
@r###"
|
||||
[30; 31) 'x': &Foo
|
||||
[39; 41) '{}': ()
|
||||
[52; 133) '{ ...oo); }': ()
|
||||
[58; 71) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> ()
|
||||
[58; 77) 'takes_...(&Foo)': ()
|
||||
[72; 76) '&Foo': &Foo
|
||||
[73; 76) 'Foo': Foo
|
||||
[83; 96) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> ()
|
||||
[83; 103) 'takes_...&&Foo)': ()
|
||||
[97; 102) '&&Foo': &&Foo
|
||||
[98; 102) '&Foo': &Foo
|
||||
[99; 102) 'Foo': Foo
|
||||
[109; 122) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> ()
|
||||
[109; 130) 'takes_...&&Foo)': ()
|
||||
[123; 129) '&&&Foo': &&&Foo
|
||||
[124; 129) '&&Foo': &&Foo
|
||||
[125; 129) '&Foo': &Foo
|
||||
[126; 129) 'Foo': Foo
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_autoderef_generic() {
|
||||
assert_snapshot!(
|
||||
infer_with_mismatches(r#"
|
||||
struct Foo;
|
||||
fn takes_ref<T>(x: &T) -> T { *x }
|
||||
fn test() {
|
||||
takes_ref(&Foo);
|
||||
takes_ref(&&Foo);
|
||||
takes_ref(&&&Foo);
|
||||
}
|
||||
"#, true),
|
||||
@r###"
|
||||
[29; 30) 'x': &T
|
||||
[41; 47) '{ *x }': T
|
||||
[43; 45) '*x': T
|
||||
[44; 45) 'x': &T
|
||||
[58; 127) '{ ...oo); }': ()
|
||||
[64; 73) 'takes_ref': fn takes_ref<Foo>(&T) -> T
|
||||
[64; 79) 'takes_ref(&Foo)': Foo
|
||||
[74; 78) '&Foo': &Foo
|
||||
[75; 78) 'Foo': Foo
|
||||
[85; 94) 'takes_ref': fn takes_ref<&Foo>(&T) -> T
|
||||
[85; 101) 'takes_...&&Foo)': &Foo
|
||||
[95; 100) '&&Foo': &&Foo
|
||||
[96; 100) '&Foo': &Foo
|
||||
[97; 100) 'Foo': Foo
|
||||
[107; 116) 'takes_ref': fn takes_ref<&&Foo>(&T) -> T
|
||||
[107; 124) 'takes_...&&Foo)': &&Foo
|
||||
[117; 123) '&&&Foo': &&&Foo
|
||||
[118; 123) '&&Foo': &&Foo
|
||||
[119; 123) '&Foo': &Foo
|
||||
[120; 123) 'Foo': Foo
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closure_return_coerce() {
|
||||
assert_snapshot!(
|
||||
infer_with_mismatches(r#"
|
||||
fn foo() {
|
||||
let x = || {
|
||||
if true {
|
||||
return &1u32;
|
||||
}
|
||||
&&1u32
|
||||
};
|
||||
}
|
||||
"#, true),
|
||||
@r###"
|
||||
[10; 106) '{ ... }; }': ()
|
||||
[20; 21) 'x': || -> &u32
|
||||
[24; 103) '|| { ... }': || -> &u32
|
||||
[27; 103) '{ ... }': &u32
|
||||
[37; 82) 'if tru... }': ()
|
||||
[40; 44) 'true': bool
|
||||
[45; 82) '{ ... }': !
|
||||
[59; 71) 'return &1u32': !
|
||||
[66; 71) '&1u32': &u32
|
||||
[67; 71) '1u32': u32
|
||||
[91; 97) '&&1u32': &&u32
|
||||
[92; 97) '&1u32': &u32
|
||||
[93; 97) '1u32': u32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_fn_item_to_fn_ptr() {
|
||||
assert_snapshot!(
|
||||
infer_with_mismatches(r#"
|
||||
fn foo(x: u32) -> isize { 1 }
|
||||
fn test() {
|
||||
let f: fn(u32) -> isize = foo;
|
||||
}
|
||||
"#, true),
|
||||
@r###"
|
||||
[8; 9) 'x': u32
|
||||
[25; 30) '{ 1 }': isize
|
||||
[27; 28) '1': isize
|
||||
[41; 79) '{ ...foo; }': ()
|
||||
[51; 52) 'f': fn(u32) -> isize
|
||||
[73; 76) 'foo': fn foo(u32) -> isize
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_closure_to_fn_ptr() {
|
||||
assert_snapshot!(
|
||||
infer_with_mismatches(r#"
|
||||
fn test() {
|
||||
let f: fn(u32) -> isize = |x| { 1 };
|
||||
}
|
||||
"#, true),
|
||||
@r###"
|
||||
[11; 55) '{ ...1 }; }': ()
|
||||
[21; 22) 'f': fn(u32) -> isize
|
||||
[43; 52) '|x| { 1 }': |u32| -> isize
|
||||
[44; 45) 'x': u32
|
||||
[47; 52) '{ 1 }': isize
|
||||
[49; 50) '1': isize
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
390
crates/ra_hir_ty/src/tests/macros.rs
Normal file
390
crates/ra_hir_ty/src/tests/macros.rs
Normal file
|
@ -0,0 +1,390 @@
|
|||
use super::{infer, type_at, type_at_pos};
|
||||
use crate::test_db::TestDB;
|
||||
use insta::assert_snapshot;
|
||||
use ra_db::fixture::WithFixture;
|
||||
|
||||
#[test]
|
||||
fn cfg_impl_block() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:foo cfg:test
|
||||
use foo::S as T;
|
||||
struct S;
|
||||
|
||||
#[cfg(test)]
|
||||
impl S {
|
||||
fn foo1(&self) -> i32 { 0 }
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
impl S {
|
||||
fn foo2(&self) -> i32 { 0 }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let t = (S.foo1(), S.foo2(), T.foo3(), T.foo4());
|
||||
t<|>;
|
||||
}
|
||||
|
||||
//- /foo.rs crate:foo
|
||||
struct S;
|
||||
|
||||
#[cfg(not(test))]
|
||||
impl S {
|
||||
fn foo3(&self) -> i32 { 0 }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl S {
|
||||
fn foo4(&self) -> i32 { 0 }
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("(i32, {unknown}, i32, {unknown})", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_macros_expanded() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
struct Foo(Vec<i32>);
|
||||
|
||||
macro_rules! foo {
|
||||
($($item:expr),*) => {
|
||||
{
|
||||
Foo(vec![$($item,)*])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = foo!(1,2);
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
![0; 17) '{Foo(v...,2,])}': Foo
|
||||
![1; 4) 'Foo': Foo({unknown}) -> Foo
|
||||
![1; 16) 'Foo(vec![1,2,])': Foo
|
||||
![5; 15) 'vec![1,2,]': {unknown}
|
||||
[156; 182) '{ ...,2); }': ()
|
||||
[166; 167) 'x': Foo
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_legacy_textual_scoped_macros_expanded() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
struct Foo(Vec<i32>);
|
||||
|
||||
#[macro_use]
|
||||
mod m {
|
||||
macro_rules! foo {
|
||||
($($item:expr),*) => {
|
||||
{
|
||||
Foo(vec![$($item,)*])
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = foo!(1,2);
|
||||
let y = crate::foo!(1,2);
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
![0; 17) '{Foo(v...,2,])}': Foo
|
||||
![1; 4) 'Foo': Foo({unknown}) -> Foo
|
||||
![1; 16) 'Foo(vec![1,2,])': Foo
|
||||
![5; 15) 'vec![1,2,]': {unknown}
|
||||
[195; 251) '{ ...,2); }': ()
|
||||
[205; 206) 'x': Foo
|
||||
[228; 229) 'y': {unknown}
|
||||
[232; 248) 'crate:...!(1,2)': {unknown}
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_path_qualified_macros_expanded() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
#[macro_export]
|
||||
macro_rules! foo {
|
||||
() => { 42i32 }
|
||||
}
|
||||
|
||||
mod m {
|
||||
pub use super::foo as bar;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = crate::foo!();
|
||||
let y = m::bar!();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
![0; 5) '42i32': i32
|
||||
![0; 5) '42i32': i32
|
||||
[111; 164) '{ ...!(); }': ()
|
||||
[121; 122) 'x': i32
|
||||
[148; 149) 'y': i32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_type_value_macro_having_same_name() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
#[macro_export]
|
||||
macro_rules! foo {
|
||||
() => {
|
||||
mod foo {
|
||||
pub use super::foo;
|
||||
}
|
||||
};
|
||||
($x:tt) => {
|
||||
$x
|
||||
};
|
||||
}
|
||||
|
||||
foo!();
|
||||
|
||||
fn foo() {
|
||||
let foo = foo::foo!(42i32);
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
![0; 5) '42i32': i32
|
||||
[171; 206) '{ ...32); }': ()
|
||||
[181; 184) 'foo': i32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn processes_impls_generated_by_macros() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
macro_rules! m {
|
||||
($ident:ident) => (impl Trait for $ident {})
|
||||
}
|
||||
trait Trait { fn foo(self) -> u128 {} }
|
||||
struct S;
|
||||
m!(S);
|
||||
fn test() { S.foo()<|>; }
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_impl_items_generated_by_macros() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
macro_rules! m {
|
||||
() => (fn foo(&self) -> u128 {0})
|
||||
}
|
||||
struct S;
|
||||
impl S {
|
||||
m!();
|
||||
}
|
||||
|
||||
fn test() { S.foo()<|>; }
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_impl_items_generated_by_macros_chain() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
macro_rules! m_inner {
|
||||
() => {fn foo(&self) -> u128 {0}}
|
||||
}
|
||||
macro_rules! m {
|
||||
() => {m_inner!();}
|
||||
}
|
||||
|
||||
struct S;
|
||||
impl S {
|
||||
m!();
|
||||
}
|
||||
|
||||
fn test() { S.foo()<|>; }
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_macro_with_dollar_crate_is_correct_in_expr() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:foo
|
||||
fn test() {
|
||||
let x = (foo::foo!(1), foo::foo!(2));
|
||||
x<|>;
|
||||
}
|
||||
|
||||
//- /lib.rs crate:foo
|
||||
#[macro_export]
|
||||
macro_rules! foo {
|
||||
(1) => { $crate::bar!() };
|
||||
(2) => { 1 + $crate::baz() };
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bar {
|
||||
() => { 42 }
|
||||
}
|
||||
|
||||
pub fn baz() -> usize { 31usize }
|
||||
"#,
|
||||
);
|
||||
assert_eq!("(i32, usize)", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_type_value_non_legacy_macro_use_as() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
mod m {
|
||||
macro_rules! _foo {
|
||||
($x:ident) => { type $x = u64; }
|
||||
}
|
||||
pub(crate) use _foo as foo;
|
||||
}
|
||||
|
||||
m::foo!(foo);
|
||||
use foo as bar;
|
||||
fn f() -> bar { 0 }
|
||||
fn main() {
|
||||
let _a = f();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[159; 164) '{ 0 }': u64
|
||||
[161; 162) '0': u64
|
||||
[175; 199) '{ ...f(); }': ()
|
||||
[187; 189) '_a': u64
|
||||
[193; 194) 'f': fn f() -> u64
|
||||
[193; 196) 'f()': u64
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_builtin_macros_line() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! line {() => {}}
|
||||
|
||||
fn main() {
|
||||
let x = line!();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
![0; 1) '6': i32
|
||||
[64; 88) '{ ...!(); }': ()
|
||||
[74; 75) 'x': i32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_builtin_macros_file() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! file {() => {}}
|
||||
|
||||
fn main() {
|
||||
let x = file!();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
![0; 2) '""': &str
|
||||
[64; 88) '{ ...!(); }': ()
|
||||
[74; 75) 'x': &str
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_builtin_macros_column() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! column {() => {}}
|
||||
|
||||
fn main() {
|
||||
let x = column!();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
![0; 2) '13': i32
|
||||
[66; 92) '{ ...!(); }': ()
|
||||
[76; 77) 'x': i32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_derive_clone_simple() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
#[derive(Clone)]
|
||||
struct S;
|
||||
fn test() {
|
||||
S.clone()<|>;
|
||||
}
|
||||
|
||||
//- /lib.rs crate:std
|
||||
#[prelude_import]
|
||||
use clone::*;
|
||||
mod clone {
|
||||
trait Clone {
|
||||
fn clone(&self) -> Self;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("S", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_derive_clone_with_params() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
#[derive(Clone)]
|
||||
struct S;
|
||||
#[derive(Clone)]
|
||||
struct Wrapper<T>(T);
|
||||
struct NonClone;
|
||||
fn test() {
|
||||
(Wrapper(S).clone(), Wrapper(NonClone).clone())<|>;
|
||||
}
|
||||
|
||||
//- /lib.rs crate:std
|
||||
#[prelude_import]
|
||||
use clone::*;
|
||||
mod clone {
|
||||
trait Clone {
|
||||
fn clone(&self) -> Self;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("(Wrapper<S>, {unknown})", type_at_pos(&db, pos));
|
||||
}
|
1005
crates/ra_hir_ty/src/tests/method_resolution.rs
Normal file
1005
crates/ra_hir_ty/src/tests/method_resolution.rs
Normal file
File diff suppressed because it is too large
Load diff
238
crates/ra_hir_ty/src/tests/patterns.rs
Normal file
238
crates/ra_hir_ty/src/tests/patterns.rs
Normal file
|
@ -0,0 +1,238 @@
|
|||
use super::infer;
|
||||
use insta::assert_snapshot;
|
||||
use test_utils::covers;
|
||||
|
||||
#[test]
|
||||
fn infer_pattern() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn test(x: &i32) {
|
||||
let y = x;
|
||||
let &z = x;
|
||||
let a = z;
|
||||
let (c, d) = (1, "hello");
|
||||
|
||||
for (e, f) in some_iter {
|
||||
let g = e;
|
||||
}
|
||||
|
||||
if let [val] = opt {
|
||||
let h = val;
|
||||
}
|
||||
|
||||
let lambda = |a: u64, b, c: i32| { a + b; c };
|
||||
|
||||
let ref ref_to_x = x;
|
||||
let mut mut_x = x;
|
||||
let ref mut mut_ref_to_x = x;
|
||||
let k = mut_ref_to_x;
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[9; 10) 'x': &i32
|
||||
[18; 369) '{ ...o_x; }': ()
|
||||
[28; 29) 'y': &i32
|
||||
[32; 33) 'x': &i32
|
||||
[43; 45) '&z': &i32
|
||||
[44; 45) 'z': i32
|
||||
[48; 49) 'x': &i32
|
||||
[59; 60) 'a': i32
|
||||
[63; 64) 'z': i32
|
||||
[74; 80) '(c, d)': (i32, &str)
|
||||
[75; 76) 'c': i32
|
||||
[78; 79) 'd': &str
|
||||
[83; 95) '(1, "hello")': (i32, &str)
|
||||
[84; 85) '1': i32
|
||||
[87; 94) '"hello"': &str
|
||||
[102; 152) 'for (e... }': ()
|
||||
[106; 112) '(e, f)': ({unknown}, {unknown})
|
||||
[107; 108) 'e': {unknown}
|
||||
[110; 111) 'f': {unknown}
|
||||
[116; 125) 'some_iter': {unknown}
|
||||
[126; 152) '{ ... }': ()
|
||||
[140; 141) 'g': {unknown}
|
||||
[144; 145) 'e': {unknown}
|
||||
[158; 205) 'if let... }': ()
|
||||
[165; 170) '[val]': {unknown}
|
||||
[173; 176) 'opt': {unknown}
|
||||
[177; 205) '{ ... }': ()
|
||||
[191; 192) 'h': {unknown}
|
||||
[195; 198) 'val': {unknown}
|
||||
[215; 221) 'lambda': |u64, u64, i32| -> i32
|
||||
[224; 256) '|a: u6...b; c }': |u64, u64, i32| -> i32
|
||||
[225; 226) 'a': u64
|
||||
[233; 234) 'b': u64
|
||||
[236; 237) 'c': i32
|
||||
[244; 256) '{ a + b; c }': i32
|
||||
[246; 247) 'a': u64
|
||||
[246; 251) 'a + b': u64
|
||||
[250; 251) 'b': u64
|
||||
[253; 254) 'c': i32
|
||||
[267; 279) 'ref ref_to_x': &&i32
|
||||
[282; 283) 'x': &i32
|
||||
[293; 302) 'mut mut_x': &i32
|
||||
[305; 306) 'x': &i32
|
||||
[316; 336) 'ref mu...f_to_x': &mut &i32
|
||||
[339; 340) 'x': &i32
|
||||
[350; 351) 'k': &mut &i32
|
||||
[354; 366) 'mut_ref_to_x': &mut &i32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_pattern_match_ergonomics() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
struct A<T>(T);
|
||||
|
||||
fn test() {
|
||||
let A(n) = &A(1);
|
||||
let A(n) = &mut A(1);
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[28; 79) '{ ...(1); }': ()
|
||||
[38; 42) 'A(n)': A<i32>
|
||||
[40; 41) 'n': &i32
|
||||
[45; 50) '&A(1)': &A<i32>
|
||||
[46; 47) 'A': A<i32>(T) -> A<T>
|
||||
[46; 50) 'A(1)': A<i32>
|
||||
[48; 49) '1': i32
|
||||
[60; 64) 'A(n)': A<i32>
|
||||
[62; 63) 'n': &mut i32
|
||||
[67; 76) '&mut A(1)': &mut A<i32>
|
||||
[72; 73) 'A': A<i32>(T) -> A<T>
|
||||
[72; 76) 'A(1)': A<i32>
|
||||
[74; 75) '1': i32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_pattern_match_ergonomics_ref() {
|
||||
covers!(match_ergonomics_ref);
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn test() {
|
||||
let v = &(1, &2);
|
||||
let (_, &w) = v;
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[11; 57) '{ ...= v; }': ()
|
||||
[21; 22) 'v': &(i32, &i32)
|
||||
[25; 33) '&(1, &2)': &(i32, &i32)
|
||||
[26; 33) '(1, &2)': (i32, &i32)
|
||||
[27; 28) '1': i32
|
||||
[30; 32) '&2': &i32
|
||||
[31; 32) '2': i32
|
||||
[43; 50) '(_, &w)': (i32, &i32)
|
||||
[44; 45) '_': i32
|
||||
[47; 49) '&w': &i32
|
||||
[48; 49) 'w': i32
|
||||
[53; 54) 'v': &(i32, &i32)
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_adt_pattern() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
enum E {
|
||||
A { x: usize },
|
||||
B
|
||||
}
|
||||
|
||||
struct S(u32, E);
|
||||
|
||||
fn test() {
|
||||
let e = E::A { x: 3 };
|
||||
|
||||
let S(y, z) = foo;
|
||||
let E::A { x: new_var } = e;
|
||||
|
||||
match e {
|
||||
E::A { x } => x,
|
||||
E::B if foo => 1,
|
||||
E::B => 10,
|
||||
};
|
||||
|
||||
let ref d @ E::A { .. } = e;
|
||||
d;
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[68; 289) '{ ... d; }': ()
|
||||
[78; 79) 'e': E
|
||||
[82; 95) 'E::A { x: 3 }': E
|
||||
[92; 93) '3': usize
|
||||
[106; 113) 'S(y, z)': S
|
||||
[108; 109) 'y': u32
|
||||
[111; 112) 'z': E
|
||||
[116; 119) 'foo': S
|
||||
[129; 148) 'E::A {..._var }': E
|
||||
[139; 146) 'new_var': usize
|
||||
[151; 152) 'e': E
|
||||
[159; 245) 'match ... }': usize
|
||||
[165; 166) 'e': E
|
||||
[177; 187) 'E::A { x }': E
|
||||
[184; 185) 'x': usize
|
||||
[191; 192) 'x': usize
|
||||
[202; 206) 'E::B': E
|
||||
[210; 213) 'foo': bool
|
||||
[217; 218) '1': usize
|
||||
[228; 232) 'E::B': E
|
||||
[236; 238) '10': usize
|
||||
[256; 275) 'ref d ...{ .. }': &E
|
||||
[264; 275) 'E::A { .. }': E
|
||||
[278; 279) 'e': E
|
||||
[285; 286) 'd': &E
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_generics_in_patterns() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
struct A<T> {
|
||||
x: T,
|
||||
}
|
||||
|
||||
enum Option<T> {
|
||||
Some(T),
|
||||
None,
|
||||
}
|
||||
|
||||
fn test(a1: A<u32>, o: Option<u64>) {
|
||||
let A { x: x2 } = a1;
|
||||
let A::<i64> { x: x3 } = A { x: 1 };
|
||||
match o {
|
||||
Option::Some(t) => t,
|
||||
_ => 1,
|
||||
};
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[79; 81) 'a1': A<u32>
|
||||
[91; 92) 'o': Option<u64>
|
||||
[107; 244) '{ ... }; }': ()
|
||||
[117; 128) 'A { x: x2 }': A<u32>
|
||||
[124; 126) 'x2': u32
|
||||
[131; 133) 'a1': A<u32>
|
||||
[143; 161) 'A::<i6...: x3 }': A<i64>
|
||||
[157; 159) 'x3': i64
|
||||
[164; 174) 'A { x: 1 }': A<i64>
|
||||
[171; 172) '1': i64
|
||||
[180; 241) 'match ... }': u64
|
||||
[186; 187) 'o': Option<u64>
|
||||
[198; 213) 'Option::Some(t)': Option<u64>
|
||||
[211; 212) 't': u64
|
||||
[217; 218) 't': u64
|
||||
[228; 229) '_': Option<u64>
|
||||
[233; 234) '1': u64
|
||||
"###
|
||||
);
|
||||
}
|
333
crates/ra_hir_ty/src/tests/regression.rs
Normal file
333
crates/ra_hir_ty/src/tests/regression.rs
Normal file
|
@ -0,0 +1,333 @@
|
|||
use super::infer;
|
||||
use insta::assert_snapshot;
|
||||
use test_utils::covers;
|
||||
|
||||
#[test]
|
||||
fn bug_484() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn test() {
|
||||
let x = if true {};
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[11; 37) '{ l... {}; }': ()
|
||||
[20; 21) 'x': ()
|
||||
[24; 34) 'if true {}': ()
|
||||
[27; 31) 'true': bool
|
||||
[32; 34) '{}': ()
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_panic_on_field_of_enum() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
enum X {}
|
||||
|
||||
fn test(x: X) {
|
||||
x.some_field;
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[20; 21) 'x': X
|
||||
[26; 47) '{ ...eld; }': ()
|
||||
[32; 33) 'x': X
|
||||
[32; 44) 'x.some_field': {unknown}
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bug_585() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn test() {
|
||||
X {};
|
||||
match x {
|
||||
A::B {} => (),
|
||||
A::Y() => (),
|
||||
}
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[11; 89) '{ ... } }': ()
|
||||
[17; 21) 'X {}': {unknown}
|
||||
[27; 87) 'match ... }': ()
|
||||
[33; 34) 'x': {unknown}
|
||||
[45; 52) 'A::B {}': {unknown}
|
||||
[56; 58) '()': ()
|
||||
[68; 74) 'A::Y()': {unknown}
|
||||
[78; 80) '()': ()
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bug_651() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn quux() {
|
||||
let y = 92;
|
||||
1 + y;
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[11; 41) '{ ...+ y; }': ()
|
||||
[21; 22) 'y': i32
|
||||
[25; 27) '92': i32
|
||||
[33; 34) '1': i32
|
||||
[33; 38) '1 + y': i32
|
||||
[37; 38) 'y': i32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursive_vars() {
|
||||
covers!(type_var_cycles_resolve_completely);
|
||||
covers!(type_var_cycles_resolve_as_possible);
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn test() {
|
||||
let y = unknown;
|
||||
[y, &y];
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[11; 48) '{ ...&y]; }': ()
|
||||
[21; 22) 'y': &{unknown}
|
||||
[25; 32) 'unknown': &{unknown}
|
||||
[38; 45) '[y, &y]': [&&{unknown};_]
|
||||
[39; 40) 'y': &{unknown}
|
||||
[42; 44) '&y': &&{unknown}
|
||||
[43; 44) 'y': &{unknown}
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursive_vars_2() {
|
||||
covers!(type_var_cycles_resolve_completely);
|
||||
covers!(type_var_cycles_resolve_as_possible);
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn test() {
|
||||
let x = unknown;
|
||||
let y = unknown;
|
||||
[(x, y), (&y, &x)];
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[11; 80) '{ ...x)]; }': ()
|
||||
[21; 22) 'x': &&{unknown}
|
||||
[25; 32) 'unknown': &&{unknown}
|
||||
[42; 43) 'y': &&{unknown}
|
||||
[46; 53) 'unknown': &&{unknown}
|
||||
[59; 77) '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown});_]
|
||||
[60; 66) '(x, y)': (&&&{unknown}, &&&{unknown})
|
||||
[61; 62) 'x': &&{unknown}
|
||||
[64; 65) 'y': &&{unknown}
|
||||
[68; 76) '(&y, &x)': (&&&{unknown}, &&&{unknown})
|
||||
[69; 71) '&y': &&&{unknown}
|
||||
[70; 71) 'y': &&{unknown}
|
||||
[73; 75) '&x': &&&{unknown}
|
||||
[74; 75) 'x': &&{unknown}
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_std_crash_1() {
|
||||
// caused stack overflow, taken from std
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
enum Maybe<T> {
|
||||
Real(T),
|
||||
Fake,
|
||||
}
|
||||
|
||||
fn write() {
|
||||
match something_unknown {
|
||||
Maybe::Real(ref mut something) => (),
|
||||
}
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[54; 139) '{ ... } }': ()
|
||||
[60; 137) 'match ... }': ()
|
||||
[66; 83) 'someth...nknown': Maybe<{unknown}>
|
||||
[94; 124) 'Maybe:...thing)': Maybe<{unknown}>
|
||||
[106; 123) 'ref mu...ething': &mut {unknown}
|
||||
[128; 130) '()': ()
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_std_crash_2() {
|
||||
covers!(type_var_resolves_to_int_var);
|
||||
// caused "equating two type variables, ...", taken from std
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn test_line_buffer() {
|
||||
&[0, b'\n', 1, b'\n'];
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[23; 53) '{ ...n']; }': ()
|
||||
[29; 50) '&[0, b...b'\n']': &[u8;_]
|
||||
[30; 50) '[0, b'...b'\n']': [u8;_]
|
||||
[31; 32) '0': u8
|
||||
[34; 39) 'b'\n'': u8
|
||||
[41; 42) '1': u8
|
||||
[44; 49) 'b'\n'': u8
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_std_crash_3() {
|
||||
// taken from rustc
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
pub fn compute() {
|
||||
match nope!() {
|
||||
SizeSkeleton::Pointer { non_zero: true, tail } => {}
|
||||
}
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[18; 108) '{ ... } }': ()
|
||||
[24; 106) 'match ... }': ()
|
||||
[30; 37) 'nope!()': {unknown}
|
||||
[48; 94) 'SizeSk...tail }': {unknown}
|
||||
[82; 86) 'true': {unknown}
|
||||
[88; 92) 'tail': {unknown}
|
||||
[98; 100) '{}': ()
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_std_crash_4() {
|
||||
// taken from rustc
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
pub fn primitive_type() {
|
||||
match *self {
|
||||
BorrowedRef { type_: Primitive(p), ..} => {},
|
||||
}
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[25; 106) '{ ... } }': ()
|
||||
[31; 104) 'match ... }': ()
|
||||
[37; 42) '*self': {unknown}
|
||||
[38; 42) 'self': {unknown}
|
||||
[53; 91) 'Borrow...), ..}': {unknown}
|
||||
[74; 86) 'Primitive(p)': {unknown}
|
||||
[84; 85) 'p': {unknown}
|
||||
[95; 97) '{}': ()
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_std_crash_5() {
|
||||
// taken from rustc
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn extra_compiler_flags() {
|
||||
for content in doesnt_matter {
|
||||
let name = if doesnt_matter {
|
||||
first
|
||||
} else {
|
||||
&content
|
||||
};
|
||||
|
||||
let content = if ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.contains(&name) {
|
||||
name
|
||||
} else {
|
||||
content
|
||||
};
|
||||
}
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[27; 323) '{ ... } }': ()
|
||||
[33; 321) 'for co... }': ()
|
||||
[37; 44) 'content': &{unknown}
|
||||
[48; 61) 'doesnt_matter': {unknown}
|
||||
[62; 321) '{ ... }': ()
|
||||
[76; 80) 'name': &&{unknown}
|
||||
[83; 167) 'if doe... }': &&{unknown}
|
||||
[86; 99) 'doesnt_matter': bool
|
||||
[100; 129) '{ ... }': &&{unknown}
|
||||
[114; 119) 'first': &&{unknown}
|
||||
[135; 167) '{ ... }': &&{unknown}
|
||||
[149; 157) '&content': &&{unknown}
|
||||
[150; 157) 'content': &{unknown}
|
||||
[182; 189) 'content': &{unknown}
|
||||
[192; 314) 'if ICE... }': &{unknown}
|
||||
[195; 232) 'ICE_RE..._VALUE': {unknown}
|
||||
[195; 248) 'ICE_RE...&name)': bool
|
||||
[242; 247) '&name': &&&{unknown}
|
||||
[243; 247) 'name': &&{unknown}
|
||||
[249; 277) '{ ... }': &&{unknown}
|
||||
[263; 267) 'name': &&{unknown}
|
||||
[283; 314) '{ ... }': &{unknown}
|
||||
[297; 304) 'content': &{unknown}
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_nested_generics_crash() {
|
||||
// another crash found typechecking rustc
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
struct Canonical<V> {
|
||||
value: V,
|
||||
}
|
||||
struct QueryResponse<V> {
|
||||
value: V,
|
||||
}
|
||||
fn test<R>(query_response: Canonical<QueryResponse<R>>) {
|
||||
&query_response.value;
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[92; 106) 'query_response': Canonical<QueryResponse<R>>
|
||||
[137; 167) '{ ...lue; }': ()
|
||||
[143; 164) '&query....value': &QueryResponse<R>
|
||||
[144; 158) 'query_response': Canonical<QueryResponse<R>>
|
||||
[144; 164) 'query_....value': QueryResponse<R>
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bug_1030() {
|
||||
assert_snapshot!(infer(r#"
|
||||
struct HashSet<T, H>;
|
||||
struct FxHasher;
|
||||
type FxHashSet<T> = HashSet<T, FxHasher>;
|
||||
|
||||
impl<T, H> HashSet<T, H> {
|
||||
fn default() -> HashSet<T, H> {}
|
||||
}
|
||||
|
||||
pub fn main_loop() {
|
||||
FxHashSet::default();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[144; 146) '{}': ()
|
||||
[169; 198) '{ ...t(); }': ()
|
||||
[175; 193) 'FxHash...efault': fn default<{unknown}, FxHasher>() -> HashSet<T, H>
|
||||
[175; 195) 'FxHash...ault()': HashSet<{unknown}, FxHasher>
|
||||
"###
|
||||
);
|
||||
}
|
1663
crates/ra_hir_ty/src/tests/simple.rs
Normal file
1663
crates/ra_hir_ty/src/tests/simple.rs
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue