diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5bc41533c4..cb397ae14a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -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 diff --git a/.vscode/launch.json b/.vscode/launch.json index 9aafc8bd33..0cf3984a9a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -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" }, { diff --git a/Cargo.lock b/Cargo.lock index 2557b5e59d..e724c1a76b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)" = "" -"checksum chalk-engine 0.9.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "" -"checksum chalk-ir 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "" -"checksum chalk-macros 0.1.1 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "" -"checksum chalk-rust-ir 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "" -"checksum chalk-solve 0.1.0 (git+https://github.com/jackh726/chalk.git?rev=095cd38a4f16337913bba487f2055b9ca0179f30)" = "" -"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)" = "" +"checksum chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "" +"checksum chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "" +"checksum chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "" +"checksum chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "" +"checksum chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=ff65b5ac9860f3c36bd892c865ab23d5ff0bbae5)" = "" "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" diff --git a/Cargo.toml b/Cargo.toml index 92e3228f05..97508c57b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } \ No newline at end of file diff --git a/README.md b/README.md index 74c971c0d3..deaa147ba9 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 0ea84d5488..993aebc478 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -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, ) -> 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 { diff --git a/crates/ra_assists/src/assists/add_custom_impl.rs b/crates/ra_assists/src/assists/add_custom_impl.rs new file mode 100644 index 0000000000..037306fd67 --- /dev/null +++ b/crates/ra_assists/src/assists/add_custom_impl.rs @@ -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) -> Option { + let input = ctx.find_node_at_offset::()?; + 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::>(); + 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 {} + ", + ) + } +} diff --git a/crates/ra_assists/src/assists/add_import.rs b/crates/ra_assists/src/assists/add_import.rs index 363ade016b..b8752cbad5 100644 --- a/crates/ra_assists/src/assists/add_import.rs +++ b/crates/ra_assists/src/assists/add_import.rs @@ -578,17 +578,21 @@ fn apply_auto_import( fn collect_hir_path_segments(path: &hir::Path) -> Option> { let mut ps = Vec::::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, - } - for s in path.segments.iter() { - ps.push(s.name.to_string().into()); + hir::PathKind::Super(0) => ps.push("self".into()), + hir::PathKind::Super(lvl) => { + let mut chain = "super".to_string(); + for _ in 0..*lvl { + chain += "::super"; + } + ps.push(chain.into()); + } + hir::PathKind::DollarCrate(_) => return None, } + ps.extend(path.segments().iter().map(|it| it.name.to_string().into())); Some(ps) } diff --git a/crates/ra_assists/src/assists/add_new.rs b/crates/ra_assists/src/assists/add_new.rs index 8f68bd5fb1..b2f946fac3 100644 --- a/crates/ra_assists/src/assists/add_new.rs +++ b/crates/ra_assists/src/assists/add_new.rs @@ -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) -> Option { 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 { - buf.push('\n'); - let start = impl_block - .syntax() - .descendants_with_tokens() - .find(|t| t.kind() == T!['{']) - .unwrap() - .text_range() - .end(); + 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!['{'])? + .text_range() + .end(); - (start, TextUnit::from_usize(1)) - } else { - buf = generate_impl_text(&strukt, &buf); - let start = strukt.syntax().text_range().end(); + 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)) - }; + (start, TextUnit::from_usize(3)) + }); edit.set_cursor(start_offset + TextUnit::of_str(&buf) - end_offset); edit.insert(start_offset, buf); @@ -141,44 +138,41 @@ 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; + None + } else { + Some(impl_blk) } - - found_new_fn = has_new_fn(impl_blk); - true }); - if found_new_fn { - None - } else { - Some(block) + 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") { - return true; + if let Some(name) = f.name() { + if name.text().eq_ignore_ascii_case("new") { + return true; + } } } } diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs index 2644125266..023917aca7 100644 --- a/crates/ra_assists/src/assists/early_return.rs +++ b/crates/ra_assists/src/assists/early_return.rs @@ -83,8 +83,8 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> 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) -> 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) -> 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) diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 3c716c2d12..4586eeb592 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs @@ -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( diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index a372bd8b9d..98fb20b227 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -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, diff --git a/crates/ra_assists/src/test_db.rs b/crates/ra_assists/src/test_db.rs index 523259fd46..d5249f3088 100644 --- a/crates/ra_assists/src/test_db.rs +++ b/crates/ra_assists/src/test_db.rs @@ -43,5 +43,3 @@ impl FileLoader for TestDB { FileLoaderDelegate(self).relevant_crates(file_id) } } - -impl hir::debug::HirDebugHelper for TestDB {} diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs index 2c9645c00a..7744ba85a2 100644 --- a/crates/ra_batch/src/lib.rs +++ b/crates/ra_batch/src/lib.rs @@ -22,7 +22,7 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId { pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap)> { 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()); diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index c7e0d0f0f3..12af075f7d 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml @@ -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] diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs index 9b1802a5fc..ac65415a5e 100644 --- a/crates/ra_cli/src/analysis_stats.rs +++ b/crates/ra_cli/src/analysis_stats.rs @@ -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 -> (LineCol, LineCol) directly let original_file = src.file_id.original_file(db); diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index fe847e6112..3808590abe 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs @@ -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 => { diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index b6d8517761..2a7ed20d1c 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -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 diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index e79361e7cc..7dc31ad3cb 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml @@ -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" } diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 38d66c2a7c..bcfc0d03ec 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -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 { 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 { + 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 { - 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 { - 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, - decl_id: Option>, - ) -> 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::( - 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 { - 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 { 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)> { + 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 { 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 { @@ -343,21 +282,8 @@ impl Struct { .collect() } - pub fn field(self, db: &impl HirDatabase, name: &Name) -> Option { - 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 { @@ -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 { @@ -392,15 +318,6 @@ impl Union { .collect() } - pub fn field(self, db: &impl HirDatabase, name: &Name) -> Option { - 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 { 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 { @@ -432,13 +349,8 @@ impl Enum { .collect() } - pub fn variant(self, db: &impl DefDatabase, name: &Name) -> Option { - 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 { - 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 { 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 { - db.body_with_source_map(self.id.into()).1 - } - - pub fn body(self, db: &impl HirDatabase) -> Arc { - 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 { - db.infer(self.id.into()) - } - - /// The containing impl block, if this is a method. - pub fn impl_block(self, db: &impl DefDatabase) -> Option { - 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 { - match self.container(db) { - Some(Container::Trait(it)) => Some(it), - _ => None, - } - } - - pub fn container(self, db: &impl DefDatabase) -> Option { - 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 { db.const_data(self.id).name.clone() } - - pub fn infer(self, db: &impl HirDatabase) -> Arc { - db.infer(self.id.into()) - } - - /// The containing impl block, if this is a type alias. - pub fn impl_block(self, db: &impl DefDatabase) -> Option { - 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 { - match self.container(db) { - Some(Container::Trait(it)) => Some(it), - _ => None, - } - } - - pub fn container(self, db: &impl DefDatabase) -> Option { - 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 { Some(self.module(db).krate()) } - - pub fn infer(self, db: &impl HirDatabase) -> Arc { - 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 { - 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 { - match self.container(db) { - Some(Container::Trait(it)) => Some(it), - _ => None, - } - } - - pub fn container(self, db: &impl DefDatabase) -> Option { - 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 { 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> { + pub fn source(self, db: &impl HirDatabase) -> InFile> { 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 { - 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 { - 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 { @@ -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, ) -> 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) - .into_iter() - .map(|it| (it, self.derived(it.ty(db).subst(&a_ty.parameters)))) - .collect(), + Ty::Apply(a_ty) => { + let field_types = db.field_types(def.into()); + def.fields(db) + .into_iter() + .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 + '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, ) -> Option { - 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 { 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 { - TypeCtor::Ref(..) => match parameters.as_single() { - Ty::Apply(a_ty) => a_original_ty.ctor == a_ty.ctor, - _ => false, - }, - _ => a_original_ty.ctor == *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), diff --git a/crates/ra_hir/src/code_model/src.rs b/crates/ra_hir/src/code_model/src.rs deleted file mode 100644 index bf3ee08341..0000000000 --- a/crates/ra_hir/src/code_model/src.rs +++ /dev/null @@ -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; -} - -/// 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 { - 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> { - 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 { - 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 { - self.id.source(db) - } -} -impl HasSource for Union { - type Ast = ast::UnionDef; - fn source(self, db: &impl DefDatabase) -> Source { - self.id.source(db) - } -} -impl HasSource for Enum { - type Ast = ast::EnumDef; - fn source(self, db: &impl DefDatabase) -> Source { - self.id.source(db) - } -} -impl HasSource for EnumVariant { - type Ast = ast::EnumVariant; - fn source(self, db: &impl DefDatabase) -> Source { - 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 { - self.id.lookup(db).source(db) - } -} -impl HasSource for Const { - type Ast = ast::ConstDef; - fn source(self, db: &impl DefDatabase) -> Source { - self.id.lookup(db).source(db) - } -} -impl HasSource for Static { - type Ast = ast::StaticDef; - fn source(self, db: &impl DefDatabase) -> Source { - self.id.lookup(db).source(db) - } -} -impl HasSource for Trait { - type Ast = ast::TraitDef; - fn source(self, db: &impl DefDatabase) -> Source { - self.id.source(db) - } -} -impl HasSource for TypeAlias { - type Ast = ast::TypeAliasDef; - fn source(self, db: &impl DefDatabase) -> Source { - self.id.lookup(db).source(db) - } -} -impl HasSource for MacroDef { - type Ast = ast::MacroCall; - fn source(self, db: &impl DefDatabase) -> Source { - 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 { - self.id.source(db) - } -} -impl HasSource for Import { - type Ast = Either; - - /// Returns the syntax of the last path segment corresponding to this import - fn source(self, db: &impl DefDatabase) -> Source { - 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))) - } -} diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index bfae3660b4..f5ffd64fa7 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -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, diff --git a/crates/ra_hir/src/debug.rs b/crates/ra_hir/src/debug.rs deleted file mode 100644 index 7a2810f71a..0000000000 --- a/crates/ra_hir/src/debug.rs +++ /dev/null @@ -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 { - None - } - fn file_path(&self, _file_id: FileId) -> Option { - 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 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); - - impl) -> fmt::Result> fmt::Debug for DebugFn { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - (&self.0)(fmt) - } - } - - DebugFn(f) -} diff --git a/crates/ra_hir/src/from_id.rs b/crates/ra_hir/src/from_id.rs index e96a18d12a..75a1a77722 100644 --- a/crates/ra_hir/src/from_id.rs +++ b/crates/ra_hir/src/from_id.rs @@ -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 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), diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs index 9f7c22b211..6314be8d4c 100644 --- a/crates/ra_hir/src/from_source.rs +++ b/crates/ra_hir/src/from_source.rs @@ -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) -> Option; + fn from_source(db: &impl DefDatabase, src: InFile) -> Option; } -impl FromSource for Struct { - type Ast = ast::StructDef; - fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source) -> Option { - let id = from_source(db, src)?; - Some(Struct { id }) - } +pub trait FromSourceByContainer: Sized { + type Ast: AstNode + 'static; + type Id: Copy + 'static; + const KEY: Key; } -impl FromSource for Union { - type Ast = ast::UnionDef; - fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source) -> Option { - 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) -> Option { - 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) -> Option { - 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) -> Option { - 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 FromSource for T +where + T: From<::Id>, +{ + type Ast = ::Ast; + fn from_source(db: &impl DefDatabase, src: InFile) -> Option { + 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) -> Option { - 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) -> Option { - 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 = $key; + } + )*} } -impl FromSource for TypeAlias { - type Ast = ast::TypeAliasDef; - fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source) -> Option { - 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) -> Option { + fn from_source(db: &impl DefDatabase, src: InFile) -> Option { 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) -> Option { - 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) -> Option { + fn from_source(db: &impl DefDatabase, src: InFile) -> Option { 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) -> Option { - let variant_def: VariantDef = match src.value { - FieldSource::Named(ref field) => { + fn from_source(db: &impl DefDatabase, src: InFile) -> Option { + 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) -> Option { + pub fn from_source(db: &impl HirDatabase, src: InFile) -> Option { 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,84 +147,111 @@ impl Local { } } +impl TypeParam { + pub fn from_source(db: &impl HirDatabase, src: InFile) -> Option { + 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) -> Option { + pub fn from_declaration(db: &impl DefDatabase, src: InFile) -> Option { + 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) -> Option { + pub fn from_definition(db: &impl DefDatabase, src: InFile) -> Option { + 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 { + 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(db: &(impl DefDatabase + AstDatabase), src: Source) -> Option -where - N: AstNode, - DEF: AstItemDef, -{ - 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 { - // 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 { + 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(s1: &Source, s2: &Source) -> bool { - s1.as_ref().map(AstPtr::new) == s2.as_ref().map(AstPtr::new) -} diff --git a/crates/ra_hir/src/has_source.rs b/crates/ra_hir/src/has_source.rs new file mode 100644 index 0000000000..5541266e2b --- /dev/null +++ b/crates/ra_hir/src/has_source.rs @@ -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; +} + +/// 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 { + 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> { + 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 { + 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 { + self.id.lookup(db).source(db) + } +} +impl HasSource for Union { + type Ast = ast::UnionDef; + fn source(self, db: &impl DefDatabase) -> InFile { + self.id.lookup(db).source(db) + } +} +impl HasSource for Enum { + type Ast = ast::EnumDef; + fn source(self, db: &impl DefDatabase) -> InFile { + self.id.lookup(db).source(db) + } +} +impl HasSource for EnumVariant { + type Ast = ast::EnumVariant; + fn source(self, db: &impl DefDatabase) -> InFile { + 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 { + self.id.lookup(db).source(db) + } +} +impl HasSource for Const { + type Ast = ast::ConstDef; + fn source(self, db: &impl DefDatabase) -> InFile { + self.id.lookup(db).source(db) + } +} +impl HasSource for Static { + type Ast = ast::StaticDef; + fn source(self, db: &impl DefDatabase) -> InFile { + self.id.lookup(db).source(db) + } +} +impl HasSource for Trait { + type Ast = ast::TraitDef; + fn source(self, db: &impl DefDatabase) -> InFile { + self.id.lookup(db).source(db) + } +} +impl HasSource for TypeAlias { + type Ast = ast::TypeAliasDef; + fn source(self, db: &impl DefDatabase) -> InFile { + self.id.lookup(db).source(db) + } +} +impl HasSource for MacroDef { + type Ast = ast::MacroCall; + fn source(self, db: &impl DefDatabase) -> InFile { + 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 { + self.id.lookup(db).source(db) + } +} + +impl HasSource for TypeParam { + type Ast = Either; + fn source(self, db: &impl DefDatabase) -> InFile { + let child_source = self.id.parent.child_source(db); + child_source.map(|it| it[self.id.local_id].clone()) + } +} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 3c12c61f03..0008a8858f 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -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}; diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 76c493f1a8..85b378483c 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -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 { +fn try_get_resolver_for_node(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> Option { 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 { - 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, body_source_map: Option>, - infer: Option>, + infer: Option>, scopes: Option>, } @@ -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> { + token: InFile<&SyntaxToken>, + ) -> Option> { 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, ) -> 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 { - 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 { - 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 { - 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 { 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,43 +275,42 @@ impl SourceAnalyzer { db: &impl HirDatabase, path: &crate::Path, ) -> Option { - let types = self.resolver.resolve_path_in_type_ns_fully(db, &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::AdtSelfType(it) | TypeNs::AdtId(it) => { - PathResolution::Def(Adt::from(it).into()) - } - TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), - TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), - 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 res = match val { - ValueNs::LocalBinding(pat_id) => { - let var = Local { parent: self.body_owner?, pat_id }; - PathResolution::Local(var) + 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(id) => PathResolution::TypeParam(TypeParam { id }), + TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { + PathResolution::Def(Adt::from(it).into()) } - ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), - ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), - ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), - ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), - ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), - }; - Some(res) - }); + TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), + TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), + 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.mod_path()).and_then(|val| { + let res = match val { + ValueNs::LocalBinding(pat_id) => { + let var = Local { parent: self.body_owner?, pat_id }; + PathResolution::Local(var) + } + ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), + ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), + ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), + ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), + ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), + }; + Some(res) + }); 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 { 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 + '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 { 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 { 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, + offset: InFile, ) -> Option { 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 - } - } -} diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs deleted file mode 100644 index 4ed69c00dc..0000000000 --- a/crates/ra_hir/src/ty.rs +++ /dev/null @@ -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::*; diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index 7e65f4c1d5..2c368f690b 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml @@ -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" } diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 3666529b0c..d9ea693e34 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -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 { - 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 { - 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 { - 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> { - let src = self.source(db); + fn child_source(&self, db: &impl DefDatabase) -> InFile> { + 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; - fn child_source(&self, db: &impl DefDatabase) -> Source> { + fn child_source(&self, db: &impl DefDatabase) -> InFile> { 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()), diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index fffb22201d..9efa4970c1 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs @@ -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, } @@ -103,7 +106,7 @@ pub enum AttrInput { impl Attr { fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option { - 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)) } diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index a57a0176d4..d3e4c50ae0 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -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, current_file_id: HirFileId, hygiene: Hygiene, + ast_id_map: Arc, 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( &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(&self, value: T) -> Source { - Source { file_id: self.current_file_id, value } + pub(crate) fn to_source(&self, value: T) -> InFile { + InFile { file_id: self.current_file_id, value } } fn parse_path(&mut self, path: ast::Path) -> Option { Path::from_src(path, &self.hygiene) } - fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &Path) -> Option { - 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::from_src(path, &self.hygiene) + } + + fn resolve_path_as_macro(&self, db: &impl DefDatabase, path: &ModPath) -> Option { + self.crate_def_map + .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other) + .0 + .take_macros() + } + + fn ast_id(&self, item: &N) -> AstId { + 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, + bomb: DropBomb, } /// The body of an item (function, const etc.). @@ -112,13 +136,14 @@ pub struct Body { pub params: Vec, /// The `ExprId` of the actual body expression. pub body_expr: ExprId, + pub item_scope: ItemScope, } pub type ExprPtr = Either, AstPtr>; -pub type ExprSource = Source; +pub type ExprSource = InFile; pub type PatPtr = Either, AstPtr>; -pub type PatSource = Source; +pub type PatSource = InFile; /// 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, Arc) { + 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, body: Option, ) -> (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 { - let src = node.map(|it| Either::A(AstPtr::new(it))); + pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option { + 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 { + 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 { - let src = node.map(|it| Either::A(AstPtr::new(it))); + pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option { + let src = node.map(|it| Either::Left(AstPtr::new(it))); self.pat_map.get(&src).cloned() } diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 331736cb23..5323af0972 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -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, body: Option, ) -> (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, + 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) -> 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) -> 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) = 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) -> 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) -> PatId { diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs index 625aa39dd0..a635523275 100644 --- a/crates/ra_hir_def/src/body/scope.rs +++ b/crates/ra_hir_def/src/body/scope.rs @@ -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() }; diff --git a/crates/ra_hir_def/src/builtin_type.rs b/crates/ra_hir_def/src/builtin_type.rs index 5e81571443..d14901a9b4 100644 --- a/crates/ra_hir_def/src/builtin_type.rs +++ b/crates/ra_hir_def/src/builtin_type.rs @@ -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)), ]; } diff --git a/crates/ra_hir_def/src/child_by_source.rs b/crates/ra_hir_def/src/child_by_source.rs new file mode 100644 index 0000000000..8b6c773eeb --- /dev/null +++ b/crates/ra_hir_def/src/child_by_source.rs @@ -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) + } +} diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index fee10b2375..1aa9a9b7d7 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -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 { - 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 { - 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)), - } - .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 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, + )); + } 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, + id: ImplId, +) -> Vec { + 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 { + if limit == 0 { + return Vec::new(); + } + + if let Some((mark, items)) = expander.enter_expand(db, m) { + let items: InFile = 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, + file_id: crate::HirFileId, + id: ImplId, +) -> Vec { + 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() +} diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index ef5611ffc0..c55fd41110 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -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) -> StructId; + fn intern_struct(&self, loc: StructLoc) -> StructId; #[salsa::interned] - fn intern_union(&self, loc: ItemLoc) -> UnionId; + fn intern_union(&self, loc: UnionLoc) -> UnionId; #[salsa::interned] - fn intern_enum(&self, loc: ItemLoc) -> 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) -> 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) -> 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, Arc); - #[salsa::invoke(RawItems::raw_items_query)] fn raw_items(&self, file_id: HirFileId) -> Arc; diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs index eda9b2269a..0954984298 100644 --- a/crates/ra_hir_def/src/diagnostics.rs +++ b/crates/ra_hir_def/src/diagnostics.rs @@ -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 { - Source { file_id: self.file, value: self.decl.into() } + fn source(&self) -> InFile { + InFile { file_id: self.file, value: self.decl.into() } } fn as_any(&self) -> &(dyn Any + Send + 'static) { self diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs index 34ed9b7a5e..b29f142e33 100644 --- a/crates/ra_hir_def/src/docs.rs +++ b/crates/ra_hir_def/src/docs.rs @@ -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), diff --git a/crates/ra_hir_def/src/dyn_map.rs b/crates/ra_hir_def/src/dyn_map.rs new file mode 100644 index 0000000000..6f269d7b01 --- /dev/null +++ b/crates/ra_hir_def/src/dyn_map.rs @@ -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 = Key::new(); +//! const U32_TO_VEC: Key> = 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 { + _phantom: PhantomData<(K, V, P)>, +} + +impl Key { + pub(crate) const fn new() -> Key { + Key { _phantom: PhantomData } + } +} + +impl Copy for Key {} + +impl Clone for Key { + fn clone(&self) -> Key { + *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 Policy for (K, V) { + type K = K; + type V = V; + fn insert(map: &mut DynMap, key: K, value: V) { + map.map.entry::>().or_insert_with(Default::default).insert(key, value); + } + fn get<'a>(map: &'a DynMap, key: &K) -> Option<&'a V> { + map.map.get::>()?.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 { + map: DynMap, + _phantom: PhantomData, +} + +impl KeyMap> { + 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 Index> for DynMap { + type Output = KeyMap>; + fn index(&self, _key: Key) -> &Self::Output { + // Safe due to `#[repr(transparent)]`. + unsafe { std::mem::transmute::<&DynMap, &KeyMap>>(self) } + } +} + +impl IndexMut> for DynMap { + fn index_mut(&mut self, _key: Key) -> &mut Self::Output { + // Safe due to `#[repr(transparent)]`. + unsafe { std::mem::transmute::<&mut DynMap, &mut KeyMap>>(self) } + } +} diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs index 04c1d8f69b..a75ef9970d 100644 --- a/crates/ra_hir_def/src/expr.rs +++ b/crates/ra_hir_def/src/expr.rs @@ -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, }, + Range { + lhs: Option, + rhs: Option, + range_type: RangeOp, + }, Index { base: ExprId, index: ExprId, @@ -137,6 +143,7 @@ pub enum Expr { Lambda { args: Vec, arg_types: Vec>, + ret_type: Option, 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); diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs index 3f94e40e4f..e9c28c7309 100644 --- a/crates/ra_hir_def/src/generics.rs +++ b/crates/ra_hir_def/src/generics.rs @@ -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, } @@ -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>, - pub params: Vec, + pub types: Arena, + // lifetimes: Arena, pub where_predicates: Vec, } @@ -40,63 +49,87 @@ pub struct WherePredicate { pub bound: TypeBound, } +type SourceMap = ArenaMap>; + impl GenericParams { pub(crate) fn generic_params_query( db: &impl DefDatabase, def: GenericDefId, ) -> Arc { - 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>, - ) -> 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) { + 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 + (generics, InFile::new(file_id, sm)) } - 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 find_by_name(&self, name: &Name) -> Option { + self.types.iter().find_map(|(id, p)| if &p.name == name { Some(id) } else { None }) } +} - pub fn count_parent_params(&self) -> usize { - self.parent_params.as_ref().map(|p| p.count_params_including_parent()).unwrap_or(0) +impl HasChildSource for GenericDefId { + type ChildId = LocalTypeParamId; + type Value = Either; + fn child_source(&self, db: &impl DefDatabase) -> InFile { + let (_, sm) = GenericParams::new(db, *self); + sm } +} - 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); +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) + } } - 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 - } -} - -fn parent_generic_def(db: &impl DefDatabase, def: GenericDefId) -> Option { - 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, - }; - - match container { - ContainerId::ImplId(it) => Some(it.into()), - ContainerId::TraitId(it) => Some(it.into()), - ContainerId::ModuleId(_) => None, + res } } diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs new file mode 100644 index 0000000000..b0288ee8db --- /dev/null +++ b/crates/ra_hir_def/src/item_scope.rs @@ -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, + defs: Vec, + impls: Vec, + /// 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, +} + +static BUILTIN_SCOPE: Lazy> = 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 + '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 + 'a { + self.visible.iter().map(|(n, def)| (n, *def)) + } + + pub fn declarations(&self) -> impl Iterator + '_ { + self.defs.iter().copied() + } + + pub fn impls(&self) -> impl Iterator + ExactSizeIterator + '_ { + self.impls.iter().copied() + } + + /// Iterate over all module scoped macros + pub(crate) fn macros<'a>(&'a self) -> impl Iterator + '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 + '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 + '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 { + 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 { + self.legacy_macros.clone() + } +} + +impl From 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), + } + } +} diff --git a/crates/ra_hir_def/src/keys.rs b/crates/ra_hir_def/src/keys.rs new file mode 100644 index 0000000000..d844f7a627 --- /dev/null +++ b/crates/ra_hir_def/src/keys.rs @@ -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 = crate::dyn_map::Key, V, AstPtrPolicy>; + +pub const FUNCTION: Key = Key::new(); +pub const CONST: Key = Key::new(); +pub const STATIC: Key = Key::new(); +pub const TYPE_ALIAS: Key = Key::new(); +pub const IMPL: Key = Key::new(); +pub const TRAIT: Key = Key::new(); +pub const STRUCT: Key = Key::new(); +pub const UNION: Key = Key::new(); +pub const ENUM: Key = Key::new(); + +pub const ENUM_VARIANT: Key = Key::new(); +pub const TUPLE_FIELD: Key = Key::new(); +pub const RECORD_FIELD: Key = Key::new(); +pub const TYPE_PARAM: Key = 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 { + _phantom: PhantomData<(AST, ID)>, +} + +impl Policy for AstPtrPolicy { + type K = InFile; + type V = ID; + fn insert(map: &mut DynMap, key: InFile, value: ID) { + let key = key.as_ref().map(AstPtr::new); + map.map + .entry::>, ID>>() + .or_insert_with(Default::default) + .insert(key, value); + } + fn get<'a>(map: &'a DynMap, key: &InFile) -> Option<&'a ID> { + let key = key.as_ref().map(AstPtr::new); + map.map.get::>, ID>>()?.get(&key) + } +} diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs index f4fdbdcfc7..cef061837b 100644 --- a/crates/ra_hir_def/src/lang_item.rs +++ b/crates/ra_hir_def/src/lang_item.rs @@ -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) } diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index bc55308969..f6c7f38d17 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -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 { - pub(crate) module: ModuleId, - ast_id: AstId, + pub container: ContainerId, + pub ast_id: AstId, } -impl PartialEq for ItemLoc { - fn eq(&self, other: &Self) -> bool { - self.module == other.module && self.ast_id == other.ast_id - } -} -impl Eq for ItemLoc {} -impl Hash for ItemLoc { - fn hash(&self, hasher: &mut H) { - self.module.hash(hasher); - self.ast_id.hash(hasher); - } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AssocItemLoc { + pub container: AssocContainerId, + pub ast_id: AstId, } -impl Clone for ItemLoc { - fn clone(&self) -> ItemLoc { - ItemLoc { module: self.module, ast_id: self.ast_id } - } -} +macro_rules! impl_intern { + ($id:ident, $loc:ident, $intern:ident, $lookup:ident) => { + impl_intern_key!($id); -#[derive(Clone, Copy)] -pub struct LocationCtx { - db: DB, - module: ModuleId, - file_id: HirFileId, -} + impl Intern for $loc { + type ID = $id; + fn intern(self, db: &impl db::DefDatabase) -> $id { + db.$intern(self) + } + } -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: salsa::InternKey + Clone { - fn intern(db: &impl InternDatabase, loc: ItemLoc) -> Self; - fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc; - - fn from_ast_id(ctx: LocationCtx<&impl InternDatabase>, ast_id: FileAstId) -> 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 { - 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 - } + impl Lookup for $id { + type Data = $loc; + fn lookup(&self, db: &impl db::DefDatabase) -> $loc { + db.$lookup(*self) + } + } + }; } #[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, -} - -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; +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 for StructId { - fn intern(db: &impl InternDatabase, loc: ItemLoc) -> Self { - db.intern_struct(loc) - } - fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc { - db.lookup_intern_struct(self) - } -} +type StructLoc = ItemLoc; +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 for UnionId { - fn intern(db: &impl InternDatabase, loc: ItemLoc) -> Self { - db.intern_union(loc) - } - fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc { - db.lookup_intern_union(self) - } -} +pub type UnionLoc = ItemLoc; +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 for EnumId { - fn intern(db: &impl InternDatabase, loc: ItemLoc) -> Self { - db.intern_enum(loc) - } - fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc { - db.lookup_intern_enum(self) - } -} +pub type EnumLoc = ItemLoc; +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, -} - -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; +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, -} - -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; +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 for TraitId { - fn intern(db: &impl InternDatabase, loc: ItemLoc) -> Self { - db.intern_trait(loc) - } - fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc { - db.lookup_intern_trait(self) - } -} +pub type TraitLoc = ItemLoc; +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, -} - -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; +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 for ImplId { - fn intern(db: &impl InternDatabase, loc: ItemLoc) -> Self { - db.intern_impl(loc) - } - fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc { - db.lookup_intern_impl(self) - } +type ImplLoc = ItemLoc; +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 HasModule for AssocItemLoc { 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; -} - -impl HasSource for FunctionLoc { - type Value = ast::FnDef; - - fn source(&self, db: &impl db::DefDatabase) -> Source { - 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 { - 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 { - 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 { - 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>; -} diff --git a/crates/ra_hir_def/src/marks.rs b/crates/ra_hir_def/src/marks.rs index 65239ca0a4..457ba4abec 100644 --- a/crates/ra_hir_def/src/marks.rs +++ b/crates/ra_hir_def/src/marks.rs @@ -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 diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index 2359386c28..5d4ca73a39 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -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 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, + definition: FileId, + }, + Inline { + definition: AstId, + }, +} + +impl Default for ModuleOrigin { + fn default() -> Self { + ModuleOrigin::CrateRoot { definition: FileId(0) } + } +} + +impl ModuleOrigin { + pub(crate) fn not_sure_file(file: Option, declaration: AstId) -> Self { + match file { + None => ModuleOrigin::Inline { definition: declaration }, + Some(definition) => ModuleOrigin::File { declaration, definition }, + } + } + + fn declaration(&self) -> Option> { + match self { + ModuleOrigin::File { declaration: module, .. } + | ModuleOrigin::Inline { definition: module, .. } => Some(*module), + ModuleOrigin::CrateRoot { .. } => None, + } + } + + pub fn file_id(&self) -> Option { + 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 { + 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, pub children: FxHashMap, - 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>, - /// None for inline modules. - /// - /// Note that non-inline modules, by definition, live inside non-macro file. - pub definition: Option, - - pub impls: Vec, -} - -#[derive(Default, Debug, PartialEq, Eq)] -pub(crate) struct Declarations { - fns: FxHashMap, FunctionId>, -} - -#[derive(Debug, Default, PartialEq, Eq)] -pub struct ModuleScope { - items: FxHashMap, - /// 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, -} - -static BUILTIN_SCOPE: Lazy> = 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 + 'a { - //FIXME: shadowing - self.items.iter().chain(BUILTIN_SCOPE.iter()) - } - - pub fn declarations(&self) -> impl Iterator + '_ { - 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 + '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 + '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 + '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 { - 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, + /// 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 + '_ { 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) { - 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> { - 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 { + 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> { - let decl = self.declaration?; + /// `None` for the crate root or block. + pub fn declaration_source(&self, db: &impl DefDatabase) -> Option> { + 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::( + 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(), }) diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index fd82451131..b9f40d3dd9 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -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, - - /// Mainly use for test - validator: Option 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; - } - - fn decrease(&mut self, macro_def_id: MacroDefId) { - *self.counts.entry(macro_def_id).or_default() -= 1; - } - - 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 +impl PartialResolvedImport { + fn namespaces(&self) -> PerNs { + match self { + PartialResolvedImport::Unresolved => PerNs::none(), + PartialResolvedImport::Indeterminate(ns) => *ns, + PartialResolvedImport::Resolved(ns) => *ns, } } } +#[derive(Clone, Debug, Eq, PartialEq)] +struct ImportDirective { + module_id: LocalModuleId, + import_id: raw::Import, + import: raw::ImportData, + status: PartialResolvedImport, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +struct MacroDirective { + module_id: LocalModuleId, + ast_id: AstId, + path: ModPath, + legacy: Option, +} + /// Walks the tree of module recursively struct DefCollector<'a, DB> { db: &'a DB, def_map: CrateDefMap, - glob_imports: FxHashMap>, - unresolved_imports: Vec<(LocalModuleId, LocalImportId, raw::ImportData)>, - unexpanded_macros: Vec<(LocalModuleId, AstId, Path)>, + glob_imports: FxHashMap>, + unresolved_imports: Vec, + resolved_imports: Vec, + unexpanded_macros: Vec, + unexpanded_attribute_macros: Vec<(LocalModuleId, AstId, ModPath)>, mod_dirs: FxHashMap, - - /// 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, - 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); + } + } + + // 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, - module_id: LocalModuleId, - def: PerNs, - import_id: LocalImportId, - import: &raw::ImportData, - ) { + 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::>(); + 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::>(); + 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::>(); - 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, - 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, - 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::>(); - 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,44 +509,41 @@ 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; - } - - 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); - let raw_items = self.db.raw_items(file_id); - let mod_dir = self.mod_dirs[&module_id].clone(); - ModCollector { - def_collector: &mut *self, - file_id, - module_id, - raw_items: &raw_items, - mod_dir, + fn resolve_attribute_macro(&self, path: &ModPath) -> Option { + // 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); } - .collect(raw_items.items()); - } else { - log::error!("Too deep macro expansion: {:?}", macro_call_id); - self.poison_macros.insert(macro_def_id); } + None + } - self.macro_stack_monitor.decrease(macro_def_id); + 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 { + def_collector: &mut *self, + file_id, + module_id, + raw_items: &raw_items, + mod_dir, + } + .collect(raw_items.items()); } 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), - ast_id: AstId::new(self.file_id, ast_id), - } - .intern(self.def_collector.db); - - PerNs::values(def.into()) + 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) + .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), - ast_id: AstId::new(self.file_id, ast_id), - } - .intern(self.def_collector.db); - - PerNs::types(def.into()) + 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) + .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) { + 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) { + 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); - } } diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs index b72c55bd10..695014c7bb 100644 --- a/crates/ra_hir_def/src/nameres/path_resolution.rs +++ b/crates/ra_hir_def/src/nameres/path_resolution.rs @@ -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, pub(super) reached_fixedpoint: ReachedFixedPoint, + pub(super) krate: Option, } 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, + krate: Option, ) -> 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() } diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index 6eb106094c..73dc087459 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs @@ -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, - imports: Arena, + imports: Arena, defs: Arena, macros: Arena, impls: Arena, @@ -41,35 +38,15 @@ pub struct RawItems { items: Vec, } -#[derive(Debug, Default, PartialEq, Eq)] -pub struct ImportSourceMap { - map: ArenaMap, -} - -type ImportSourcePtr = Either, AstPtr>; - -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 { - 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, Arc) { + 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 for RawItems { } } -impl Index for RawItems { +impl Index 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, items: Vec }, } +#[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, pub(super) is_glob: bool, pub(super) is_prelude: bool, @@ -184,6 +162,21 @@ pub(super) enum DefKind { TypeAlias(FileAstId), } +impl DefKind { + pub fn ast_id(&self) -> FileAstId { + 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, - pub(super) path: Path, + pub(super) path: ModPath, pub(super) name: Option, pub(super) export: bool, pub(super) builtin: bool, @@ -208,7 +201,6 @@ pub(super) struct ImplData { struct RawItemsCollector { raw_items: RawItems, - imports: Trace, source_ast_id_map: Arc, 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, 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, - attrs: Attrs, - data: ImportData, - source: ImportSourcePtr, - ) { - let import = self.imports.alloc(|| source, || data); + fn push_import(&mut self, current_module: Option, attrs: Attrs, data: ImportData) { + let import = self.raw_items.imports.alloc(data); self.push_item(current_module, attrs, RawItemKind::Import(import)) } diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs index 87fcd617c5..ff474b53b0 100644 --- a/crates/ra_hir_def/src/nameres/tests.rs +++ b/crates/ra_hir_def/src/nameres/tests.rs @@ -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::>(); - 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 +"###); +} diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs index 5b03fe3650..5e24cb94d6 100644 --- a/crates/ra_hir_def/src/nameres/tests/globs.rs +++ b/crates/ra_hir_def/src/nameres/tests/globs.rs @@ -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 + "### + ); +} diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs index 903a227719..ef2e9435cf 100644 --- a/crates/ra_hir_def/src/nameres/tests/incremental.rs +++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs @@ -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) } diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs index 704065633a..d104f5993f 100644 --- a/crates/ra_hir_def/src/nameres/tests/macros.rs +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs @@ -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); +} diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs index e11530062d..e800cc68e4 100644 --- a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs @@ -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, ), diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 6810a26dbb..8e12942011 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -1,35 +1,97 @@ //! A desugared representation of paths like `crate::foo` or `::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, +} + +#[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 { + lower::lower_path(path, hygiene).map(|it| it.mod_path) + } + + pub fn from_simple_segments( + kind: PathKind, + segments: impl IntoIterator, + ) -> ModPath { + let segments = segments.into_iter().collect::>(); + 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, + hygiene: &Hygiene, + mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option), + ) { + 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, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct PathSegment { - pub name: Name, - pub args_and_bindings: Option>, + /// Type based path like `::foo`. + /// Note that paths like `::foo` are desugard to `Trait::::foo`. + type_anchor: Option>, + mod_path: ModPath, + /// Invariant: the same len as self.path.segments + generic_args: Vec>>, } /// Generic arguments to a path segment (e.g. the `i32` in `Option`). This -/// can (in the future) also include bindings of associated types, like in -/// `Iterator`. +/// also includes bindings of associated types, like in `Iterator`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenericArgs { pub args: Vec, @@ -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 `::foo` - Type(Box), - // `$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, - hygiene: &Hygiene, - mut cb: impl FnMut(Path, &ast::UseTree, bool, Option), - ) { - 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, - ) -> 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::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 { - 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 { - // ::foo - None => { - kind = PathKind::Type(Box::new(self_type)); - } - // >::Foo desugars to Trait::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 { - 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 { + 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 { + 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 { - 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 struct PathSegments<'a> { + segments: &'a [Name], + generic_args: &'a [Option>], +} + +impl<'a> PathSegments<'a> { + pub const EMPTY: PathSegments<'static> = PathSegments { segments: &[], generic_args: &[] }; + pub fn is_empty(&self) -> bool { + self.len() == 0 } - - pub fn is_type_relative(&self) -> bool { - match self.kind { - PathKind::Type(_) => true, - _ => false, - } + pub fn len(&self) -> usize { + self.segments.len() + } + pub fn first(&self) -> Option> { + self.get(0) + } + pub fn last(&self) -> Option> { + self.get(self.len().checked_sub(1)?) + } + pub fn get(&self, idx: usize) -> Option> { + 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> { + 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 { - 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, - ret_type: Option, - ) -> Option { - 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 for Path { fn from(name: Name) -> Path { - Path::from_simple_segments(PathKind::Plain, iter::once(name)) - } -} - -fn expand_use_tree( - prefix: Option, - tree: ast::UseTree, - hygiene: &Hygiene, - cb: &mut dyn FnMut(Path, &ast::UseTree, bool, Option), -) { - 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() { - 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 + Path { + type_anchor: None, + mod_path: ModPath::from_simple_segments(PathKind::Plain, iter::once(name)), + generic_args: vec![None], } } } -fn convert_path(prefix: Option, path: ast::Path, hygiene: &Hygiene) -> Option { - let prefix = if let Some(qual) = path.qualifier() { - Some(convert_path(prefix, qual, hygiene)?) - } else { - prefix +impl From 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") }; - - 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), - }); - 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]) - } +#[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],)* + ]) + }); } + +pub use crate::__path as path; diff --git a/crates/ra_hir_def/src/path/lower.rs b/crates/ra_hir_def/src/path/lower.rs new file mode 100644 index 0000000000..62aafd508e --- /dev/null +++ b/crates/ra_hir_def/src/path/lower.rs @@ -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 { + 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 { + // ::foo + None => { + type_anchor = Some(Box::new(self_type)); + kind = PathKind::Plain; + } + // >::Foo desugars to Trait::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 { + 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 { + 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, + ret_type: Option, +) -> Option { + 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 }) + } +} diff --git a/crates/ra_hir_def/src/path/lower/lower_use.rs b/crates/ra_hir_def/src/path/lower/lower_use.rs new file mode 100644 index 0000000000..3218eaf0a2 --- /dev/null +++ b/crates/ra_hir_def/src/path/lower/lower_use.rs @@ -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, + tree: ast::UseTree, + hygiene: &Hygiene, + cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option), +) { + 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, path: ast::Path, hygiene: &Hygiene) -> Option { + 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) +} diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs index 00e866bf9f..3a5105028f 100644 --- a/crates/ra_hir_def/src/per_ns.rs +++ b/crates/ra_hir_def/src/per_ns.rs @@ -11,8 +11,6 @@ use crate::ModuleDefId; pub struct PerNs { pub types: Option, pub values: Option, - /// 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, } diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index 0847f6dcf4..cf3c33d788 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs @@ -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, } @@ -53,12 +56,14 @@ enum Scope { AdtScope(AdtId), /// Local bindings ExprScope(ExprScope), + /// Temporary hack to support local items. + LocalItemsScope(Arc), } #[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 { - let res = self.resolve_module_path(db, path).take_types()?; + pub fn resolve_known_trait(&self, db: &impl DefDatabase, path: &ModPath) -> Option { + 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 { - let res = self.resolve_module_path(db, path).take_types()?; + pub fn resolve_known_struct(&self, db: &impl DefDatabase, path: &ModPath) -> Option { + let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?; match res { ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it), _ => None, @@ -108,87 +113,116 @@ impl Resolver { } /// Resolve known enum from std, like `std::result::Result` - pub fn resolve_known_enum(&self, db: &impl DefDatabase, path: &Path) -> Option { - let res = self.resolve_module_path(db, path).take_types()?; + pub fn resolve_known_enum(&self, db: &impl DefDatabase, path: &ModPath) -> Option { + 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)> { - 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()? { - ModuleDefId::AdtId(it) => TypeNs::AdtId(it), - ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it), - - ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), - ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), - - ModuleDefId::TraitId(it) => TypeNs::TraitId(it), - - ModuleDefId::FunctionId(_) - | ModuleDefId::ConstId(_) - | ModuleDefId::StaticId(_) - | ModuleDefId::ModuleId(_) => return None, - }; + 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)); + } + } } } - None + return None; + fn to_type_ns(per_ns: PerNs) -> Option { + let res = match per_ns.take_types()? { + ModuleDefId::AdtId(it) => TypeNs::AdtId(it), + ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it), + + ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), + ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), + + ModuleDefId::TraitId(it) => TypeNs::TraitId(it), + + ModuleDefId::FunctionId(_) + | ModuleDefId::ConstId(_) + | ModuleDefId::StaticId(_) + | ModuleDefId::ModuleId(_) => return None, + }; + Some(res) + } } pub fn resolve_path_in_type_ns_fully( &self, db: &impl DefDatabase, - path: &Path, + path: &ModPath, ) -> Option { 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 { - 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 { + 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 { 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 { + pub fn resolve_path_as_macro( + &self, + db: &impl DefDatabase, + path: &ModPath, + ) -> Option { 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 + '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 { - 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 { - 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, ) -> 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::>(); 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) -> 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), + } } } diff --git a/crates/ra_hir_def/src/src.rs b/crates/ra_hir_def/src/src.rs new file mode 100644 index 0000000000..499375b803 --- /dev/null +++ b/crates/ra_hir_def/src/src.rs @@ -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; +} + +impl HasSource for AssocItemLoc { + type Value = N; + + fn source(&self, db: &impl DefDatabase) -> InFile { + let node = self.ast_id.to_node(db); + InFile::new(self.ast_id.file_id, node) + } +} + +impl HasSource for ItemLoc { + type Value = N; + + fn source(&self, db: &impl DefDatabase) -> InFile { + 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>; +} diff --git a/crates/ra_hir_def/src/trace.rs b/crates/ra_hir_def/src/trace.rs index 2bcd707bcd..9769e88df5 100644 --- a/crates/ra_hir_def/src/trace.rs +++ b/crates/ra_hir_def/src/trace.rs @@ -18,10 +18,6 @@ pub(crate) struct Trace { } impl Trace { - pub(crate) fn new() -> Trace { - Trace { arena: Some(Arena::default()), map: Some(ArenaMap::default()), len: 0 } - } - pub(crate) fn new_for_arena() -> Trace { Trace { arena: Some(Arena::default()), map: None, len: 0 } } @@ -52,8 +48,4 @@ impl Trace { pub(crate) fn into_map(mut self) -> ArenaMap { self.map.take().unwrap() } - - pub(crate) fn into_arena_and_map(mut self) -> (Arena, ArenaMap) { - (self.arena.take().unwrap(), self.map.take().unwrap()) - } } diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml index c60152a799..3ae4376dc7 100644 --- a/crates/ra_hir_expand/Cargo.toml +++ b/crates/ra_hir_expand/Cargo.toml @@ -9,6 +9,7 @@ doctest = false [dependencies] log = "0.4.5" +either = "1.5" ra_arena = { path = "../ra_arena" } ra_db = { path = "../ra_db" } diff --git a/crates/ra_hir_expand/src/ast_id_map.rs b/crates/ra_hir_expand/src/ast_id_map.rs index cb464c3ff1..a764bdf24e 100644 --- a/crates/ra_hir_expand/src/ast_id_map.rs +++ b/crates/ra_hir_expand/src/ast_id_map.rs @@ -39,6 +39,16 @@ impl Hash for FileAstId { } } +impl FileAstId { + // Can't make this a From implementation because of coherence + pub fn upcast(self) -> FileAstId + where + M: From, + { + 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. diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs new file mode 100644 index 0000000000..62c60e336c --- /dev/null +++ b/crates/ra_hir_expand/src/builtin_derive.rs @@ -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 { + let expander = match *self { + $( BuiltinDeriveExpander::$trait => $expand, )* + }; + expander(db, id, tt) + } + } + + pub fn find_builtin_derive(ident: &name::Name) -> Option { + 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 { + 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) -> Vec { + let mut result = Vec::::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 { + 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 { + expand_simple_derive(tt, quote! { std::marker::Copy }) +} + +fn clone_expand( + _db: &dyn AstDatabase, + _id: MacroCallId, + tt: &tt::Subtree, +) -> Result { + expand_simple_derive(tt, quote! { std::clone::Clone }) +} + +fn default_expand( + _db: &dyn AstDatabase, + _id: MacroCallId, + tt: &tt::Subtree, +) -> Result { + expand_simple_derive(tt, quote! { std::default::Default }) +} + +fn debug_expand( + _db: &dyn AstDatabase, + _id: MacroCallId, + tt: &tt::Subtree, +) -> Result { + expand_simple_derive(tt, quote! { std::fmt::Debug }) +} + +fn hash_expand( + _db: &dyn AstDatabase, + _id: MacroCallId, + tt: &tt::Subtree, +) -> Result { + expand_simple_derive(tt, quote! { std::hash::Hash }) +} + +fn eq_expand( + _db: &dyn AstDatabase, + _id: MacroCallId, + tt: &tt::Subtree, +) -> Result { + expand_simple_derive(tt, quote! { std::cmp::Eq }) +} + +fn partial_eq_expand( + _db: &dyn AstDatabase, + _id: MacroCallId, + tt: &tt::Subtree, +) -> Result { + expand_simple_derive(tt, quote! { std::cmp::PartialEq }) +} + +fn ord_expand( + _db: &dyn AstDatabase, + _id: MacroCallId, + tt: &tt::Subtree, +) -> Result { + expand_simple_derive(tt, quote! { std::cmp::Ord }) +} + +fn partial_ord_expand( + _db: &dyn AstDatabase, + _id: MacroCallId, + tt: &tt::Subtree, +) -> Result { + 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; +"#, + BuiltinDeriveExpander::Copy, + ); + + assert_eq!( + expanded, + "implstd::marker::CopyforFoo{}" + ); + } + + #[test] + fn test_copy_expand_with_lifetimes() { + let expanded = expand_builtin_derive( + r#" + #[derive(Copy)] + struct Foo; +"#, + BuiltinDeriveExpander::Copy, + ); + + // We currently just ignore lifetimes + + assert_eq!( + expanded, + "implstd::marker::CopyforFoo{}" + ); + } + + #[test] + fn test_clone_expand() { + let expanded = expand_builtin_derive( + r#" + #[derive(Clone)] + struct Foo; +"#, + BuiltinDeriveExpander::Clone, + ); + + assert_eq!( + expanded, + "implstd::clone::CloneforFoo{}" + ); + } +} diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index d370dfb34f..2c119269c1 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs @@ -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 { + 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, ) -> Option { - 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 { 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 { 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 { + // dummy implementation for type-checking purposes + let expanded = quote! { "" }; + + Ok(expanded) +} + +fn option_env_expand( + _db: &dyn AstDatabase, + _id: MacroCallId, + _tt: &tt::Subtree, +) -> Result { + // 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 { 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 { - 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 { + // 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::>(); + 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() } @@ -240,25 +316,23 @@ mod tests { fn test_column_expand() { let expanded = expand_builtin_macro( r#" - #[rustc_builtin_macro] - macro_rules! column {() => {}} - column!() -"#, - BuiltinFnLikeExpander::Column, + #[rustc_builtin_macro] + macro_rules! column {() => {}} + column!() + "#, ); - assert_eq!(expanded, "9"); + assert_eq!(expanded, "13"); } #[test] fn test_line_expand() { let expanded = expand_builtin_macro( r#" - #[rustc_builtin_macro] - macro_rules! line {() => {}} - line!() -"#, - BuiltinFnLikeExpander::Line, + #[rustc_builtin_macro] + macro_rules! line {() => {}} + line!() + "#, ); assert_eq!(expanded, "4"); @@ -268,25 +342,49 @@ mod tests { fn test_stringify_expand() { let expanded = expand_builtin_macro( r#" - #[rustc_builtin_macro] - macro_rules! stringify {() => {}} - stringify!(a b c) -"#, - BuiltinFnLikeExpander::Stringify, + #[rustc_builtin_macro] + macro_rules! stringify {() => {}} + stringify!(a b c) + "#, ); 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( r#" - #[rustc_builtin_macro] - macro_rules! file {() => {}} - file!() -"#, - BuiltinFnLikeExpander::File, + #[rustc_builtin_macro] + macro_rules! file {() => {}} + file!() + "#, ); assert_eq!(expanded, "\"\""); @@ -296,16 +394,34 @@ mod tests { fn test_compile_error_expand() { let expanded = expand_builtin_macro( r#" - #[rustc_builtin_macro] - macro_rules! compile_error { - ($msg:expr) => ({ /* compiler built-in */ }); - ($msg:expr,) => ({ /* compiler built-in */ }) - } - compile_error!("error!"); -"#, - BuiltinFnLikeExpander::CompileError, + #[rustc_builtin_macro] + macro_rules! compile_error { + ($msg:expr) => ({ /* compiler built-in */ }); + ($msg:expr,) => ({ /* compiler built-in */ }) + } + compile_error!("error!"); + "#, ); 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),])"# + ); + } } diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index 8e46fa177d..2e12e126f7 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs @@ -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> { 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> { 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 + } + } +} diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs index 3d37e93358..108c1e38c6 100644 --- a/crates/ra_hir_expand/src/diagnostics.rs +++ b/crates/ra_hir_expand/src/diagnostics.rs @@ -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; + fn source(&self) -> InFile; fn highlight_range(&self) -> TextRange { self.source().value.range() } diff --git a/crates/ra_hir_expand/src/either.rs b/crates/ra_hir_expand/src/either.rs deleted file mode 100644 index 83583ef8bd..0000000000 --- a/crates/ra_hir_expand/src/either.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! FIXME: write short doc here - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Either { - A(A), - B(B), -} - -impl Either { - pub fn either(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(self, f1: F1, f2: F2) -> Either - 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(self, f: F) -> Either - where - F: FnOnce(A) -> U, - { - self.map(f, |it| it) - } - pub fn a(self) -> Option { - match self { - Either::A(it) => Some(it), - Either::B(_) => None, - } - } - pub fn b(self) -> Option { - 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), - } - } -} diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs index 379562a2c1..2e8a533f70 100644 --- a/crates/ra_hir_expand/src/hygiene.rs +++ b/crates/ra_hir_expand/src/hygiene.rs @@ -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 { 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()) } } diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index b6a739cda5..2fa5d51402 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -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> { + 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, + // 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, + pub ast_id: Option>, pub kind: MacroDefKind, } impl MacroDefId { - pub fn as_call_id( - self, - db: &dyn db::AstDatabase, - ast_id: AstId, - ) -> 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, + pub(crate) kind: MacroCallKind, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum MacroCallKind { + FnLike(AstId), + Attr(AstId), +} + +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 { + 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 { + 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, - arg: Source, - def: Source, + expanded: InFile, + arg: InFile, + def: InFile, macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, exp_map: Arc, } +pub use mbe::Origin; + impl ExpansionInfo { - pub fn map_token_down(&self, token: Source<&SyntaxToken>) -> Option> { + pub fn call_node(&self) -> Option> { + Some(self.arg.with_value(self.arg.value.parent()?)) + } + + pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option> { 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> { + pub fn map_token_up( + &self, + token: InFile<&SyntaxToken>, + ) -> Option<(InFile, 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(), - ) - .into_token()?; - Some(tt.with_value(token)) + 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), 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>` ? -#[derive(Debug)] -pub struct AstId { - file_id: HirFileId, - file_ast_id: FileAstId, -} - -impl Clone for AstId { - fn clone(&self) -> AstId { - *self - } -} -impl Copy for AstId {} - -impl PartialEq for AstId { - fn eq(&self, other: &Self) -> bool { - (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id) - } -} -impl Eq for AstId {} -impl Hash for AstId { - fn hash(&self, hasher: &mut H) { - (self.file_id, self.file_ast_id).hash(hasher); - } -} +pub type AstId = InFile>; impl AstId { - pub fn new(file_id: HirFileId, file_ast_id: FileAstId) -> AstId { - 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` stores a value of `T` inside a particular file/syntax tree. +/// `InFile` stores a value of `T` inside a particular file/syntax tree. /// /// Typical usages are: /// -/// * `Source` -- syntax node in a file -/// * `Source` -- ast node in a file -/// * `Source` -- offset in a file +/// * `InFile` -- syntax node in a file +/// * `InFile` -- ast node in a file +/// * `InFile` -- offset in a file #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct Source { +pub struct InFile { pub file_id: HirFileId, pub value: T, } -impl Source { - pub fn new(file_id: HirFileId, value: T) -> Source { - Source { file_id, value } +impl InFile { + pub fn new(file_id: HirFileId, value: T) -> InFile { + InFile { file_id, value } } // Similarly, naming here is stupid... - pub fn with_value(&self, value: U) -> Source { - Source::new(self.file_id, value) + pub fn with_value(&self, value: U) -> InFile { + InFile::new(self.file_id, value) } - pub fn map U, U>(self, f: F) -> Source { - Source::new(self.file_id, f(self.value)) + pub fn map U, U>(self, f: F) -> InFile { + 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 InFile<&T> { + pub fn cloned(&self) -> InFile { + self.with_value(self.value.clone()) + } +} + +impl InFile { + pub fn ancestors_with_macros<'a>( + self, + db: &'a impl crate::db::AstDatabase, + ) -> impl Iterator> + '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) + } + }) + } +} diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index 7824489d7f..e62693b685 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs @@ -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 { } } -// 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"); +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()); + )* + }; + } -// 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"); + known_names!( + // Primitives + isize, + i8, + i16, + i32, + i64, + i128, + usize, + u8, + u16, + u32, + u64, + u128, + f32, + f64, + bool, + char, + str, + // Special names + macro_rules, + // Components of known path (value or mod name) + std, + iter, + ops, + future, + result, + boxed, + // Components of known path (type name) + 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, + ); -// 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"); + // 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"); -// 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"); + #[macro_export] + macro_rules! name { + (self) => { + $crate::name::known::SELF_PARAM + }; + (Self) => { + $crate::name::known::SELF_TYPE + }; + ($ident:ident) => { + $crate::name::known::$ident + }; + } +} -// 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"); +pub use crate::name; diff --git a/crates/ra_hir_expand/src/quote.rs b/crates/ra_hir_expand/src/quote.rs index 65a35e52fa..4de219ce46 100644 --- a/crates/ra_hir_expand/src/quote.rs +++ b/crates/ra_hir_expand/src/quote.rs @@ -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::>(); + 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 { 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 { @@ -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 { diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 429242870f..60793db442 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml @@ -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" diff --git a/crates/ra_hir_ty/src/autoderef.rs b/crates/ra_hir_ty/src/autoderef.rs index 9d1d4e48c6..f32d5786af 100644 --- a/crates/ra_hir_ty/src/autoderef.rs +++ b/crates/ra_hir_ty/src/autoderef.rs @@ -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>, ) -> Option> { - 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) => { diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs index 9ce1545939..d52f65b836 100644 --- a/crates/ra_hir_ty/src/db.rs +++ b/crates/ra_hir_ty/src/db.rs @@ -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; #[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; #[salsa::invoke(crate::lower::field_types_query)] fn field_types(&self, var: VariantId) -> Arc>; @@ -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>; + #[salsa::invoke(chalk::associated_ty_data_query)] + fn associated_ty_data(&self, id: chalk::AssocTypeId) -> Arc; - #[salsa::invoke(crate::traits::chalk::trait_datum_query)] - fn trait_datum( - &self, - krate: CrateId, - trait_id: chalk_ir::TraitId, - ) -> Arc>; + #[salsa::invoke(chalk::trait_datum_query)] + fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc; - #[salsa::invoke(crate::traits::chalk::struct_datum_query)] - fn struct_datum( - &self, - krate: CrateId, - struct_id: chalk_ir::StructId, - ) -> Arc>; + #[salsa::invoke(chalk::struct_datum_query)] + fn struct_datum(&self, krate: CrateId, struct_id: chalk::StructId) -> Arc; #[salsa::invoke(crate::traits::chalk::impl_datum_query)] - fn impl_datum( - &self, - krate: CrateId, - impl_id: chalk_ir::ImplId, - ) -> Arc>; + fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc; #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] fn associated_ty_value( &self, krate: CrateId, - id: chalk_rust_ir::AssociatedTyValueId, - ) -> Arc>; + id: chalk::AssociatedTyValueId, + ) -> Arc; #[salsa::invoke(crate::traits::trait_solve_query)] fn trait_solve( diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 4a13fac239..5054189cc1 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs @@ -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 { - Source { file_id: self.file, value: self.field.into() } + fn source(&self) -> InFile { + 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 { - Source { file_id: self.file, value: self.field_list.into() } + fn source(&self) -> InFile { + 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 { - Source { file_id: self.file, value: self.expr.into() } + fn source(&self) -> InFile { + InFile { file_id: self.file, value: self.expr.into() } } fn as_any(&self) -> &(dyn Any + Send + 'static) { self diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index 9bb3ece6c8..dcca1bace6 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs @@ -10,6 +10,7 @@ pub struct HirFormatter<'a, 'b, DB> { buf: String, curr_size: usize, max_size: Option, + 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); +pub struct HirDisplayWrapper<'a, DB, T>(&'a DB, &'a T, Option, 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, }) } } diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs index 5c65f93707..f752a9f09a 100644 --- a/crates/ra_hir_ty/src/expr.rs +++ b/crates/ra_hir_ty/src/expr.rs @@ -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 }); } } diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 1e9f4b208d..e97b814739 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -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, resolver: Resolver, - var_unification_table: InPlaceUnificationTable, + table: unify::InferenceTable, trait_env: Arc, obligations: Vec, 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)) - } - - 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) + /// 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 } - _ => 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, 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); - } - 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, + self.table.resolve_ty_shallow(ty) + } + + fn resolve_associated_type(&mut self, inner_ty: Ty, assoc_ty: Option) -> Ty { + self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[]) + } + + fn resolve_associated_type_with_params( + &mut self, + inner_ty: Ty, + assoc_ty: Option, + 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, } - 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, 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) { 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 { - 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 { - 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 { + 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 { + 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 { - 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 { - 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()) } -} -/// 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_full(&self) -> Option { + let path = path![std::ops::RangeFull]; + let struct_ = self.resolver.resolve_known_struct(self.db, &path)?; + Some(struct_.into()) } - fn from_index(i: u32) -> Self { - TypeVarId(i) + fn resolve_range(&self) -> Option { + let path = path![std::ops::Range]; + let struct_ = self.resolver.resolve_known_struct(self.db, &path)?; + Some(struct_.into()) } - fn tag() -> &'static str { - "TypeVarId" + fn resolve_range_inclusive(&self) -> Option { + let path = path![std::ops::RangeInclusive]; + 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, -} - -impl TypeVarValue { - fn known(&self) -> Option<&Ty> { - match self { - TypeVarValue::Known(ty) => Some(ty), - TypeVarValue::Unknown => None, - } + fn resolve_range_from(&self) -> Option { + let path = path![std::ops::RangeFrom]; + let struct_ = self.resolver.resolve_known_struct(self.db, &path)?; + Some(struct_.into()) } -} -impl UnifyValue for TypeVarValue { - type Error = NoError; + fn resolve_range_to(&self) -> Option { + let path = path![std::ops::RangeTo]; + let struct_ = self.resolver.resolve_known_struct(self.db, &path)?; + Some(struct_.into()) + } - fn unify_values(value1: &Self, value2: &Self) -> Result { - 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 - ), + fn resolve_range_to_inclusive(&self) -> Option { + let path = path![std::ops::RangeToInclusive]; + let struct_ = self.resolver.resolve_known_struct(self.db, &path)?; + Some(struct_.into()) + } - // 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 { + 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}; diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 719a0f3957..83c0c2c3f3 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -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; + } } - _ => {} } } diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 2f9ca4bbbf..3af05394c6 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -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 { - 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, + // Fast path for builtins + Ty::Apply(ApplicationTy { + ctor: + TypeCtor::Int(Uncertain::Known(IntTy { + signedness: Signedness::Signed, + .. + })), + .. + }) + | 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::>(), - _ => (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>, + def_generics: Option, 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, diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs index 1ebb362399..a146628844 100644 --- a/crates/ra_hir_ty/src/infer/pat.rs +++ b/crates/ra_hir_ty/src/infer/pat.rs @@ -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 } diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs index 14be668365..ffd3583675 100644 --- a/crates/ra_hir_ty/src/infer/path.rs +++ b/crates/ra_hir_ty/src/infer/path.rs @@ -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 { - 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)> { - 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. `::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)> { 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 { - 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 - } - } } diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index f3a8756785..fe05642ae3 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs @@ -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, + var_stack: Vec, } pub(super) struct Canonicalized { @@ -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 Canonicalized { solution: Canonical>, ) { // 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, ty2: &Canonical) -> Option { + 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, +} + +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, 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, 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 { + 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), } } } diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index b45c8f82f4..48abf97c92 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -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) -> 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(); diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 091c60f4fe..af3db2e1d7 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -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) { - Some(it) => it, - None => return Ty::Unknown, - }; + 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::::None` and // `Option::None::` 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::::None - &path.segments[len - 2] - } else { - // Option::None:: - 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, 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, ) -> Option { - 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, ) -> 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 + '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 { 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::>(); - 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), - } - } - 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 +} + +pub(crate) fn impl_trait_query(db: &impl HirDatabase, impl_id: ImplId) -> Option { + 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())) } diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs index 0f754eb9c7..fe74acf119 100644 --- a/crates/ra_hir_ty/src/marks.rs +++ b/crates/ra_hir_ty/src/marks.rs @@ -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 ); diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index ee1936b0e5..888dc31166 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -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( mode: LookupMode, mut callback: impl FnMut(&Ty, AssocItemId) -> Option, ) -> Option { - 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( // 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 - if let Some(result) = - iterate_inherent_methods(&ty, db, name, mode, krate.into(), &mut callback) - { - return Some(result); - } - if let Some(result) = - iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback) - { - return Some(result); - } + iterate_method_candidates_for_self_ty(&ty, db, resolver, name, &mut callback) + } + } +} + +fn iterate_method_candidates_with_autoref( + deref_chain: &[Canonical], + db: &impl HirDatabase, + resolver: &Resolver, + name: Option<&Name>, + mut callback: impl FnMut(&Ty, AssocItemId) -> Option, +) -> Option { + 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( + receiver_ty: &Canonical, + rest_of_deref_chain: &[Canonical], + db: &impl HirDatabase, + resolver: &Resolver, + name: Option<&Name>, + mut callback: impl FnMut(&Ty, AssocItemId) -> Option, +) -> Option { + // 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(self_ty, db, name, Some(receiver_ty), krate, &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( - ty: &Canonical, +fn iterate_method_candidates_for_self_ty( + self_ty: &Canonical, db: &impl HirDatabase, resolver: &Resolver, name: Option<&Name>, - mode: LookupMode, + mut callback: impl FnMut(&Ty, AssocItemId) -> Option, +) -> Option { + 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( + self_ty: &Canonical, + db: &impl HirDatabase, + resolver: &Resolver, + name: Option<&Name>, + receiver_ty: Option<&Canonical>, mut callback: impl FnMut(&Ty, AssocItemId) -> Option, ) -> Option { 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( // 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( } fn iterate_inherent_methods( - ty: &Canonical, + self_ty: &Canonical, db: &impl HirDatabase, name: Option<&Name>, - mode: LookupMode, + receiver_ty: Option<&Canonical>, krate: CrateId, mut callback: impl FnMut(&Ty, AssocItemId) -> Option, ) -> Option { - 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( fn is_valid_candidate( db: &impl HirDatabase, name: Option<&Name>, - mode: LookupMode, + receiver_ty: Option<&Canonical>, item: AssocItemId, + self_ty: &Canonical, ) -> 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, +) -> Option { + 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, +) -> Option { + 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, db: &impl HirDatabase, diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 1dc9793f95..1a31b587bb 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs @@ -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 { diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index c8461b447a..d447b45718 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs @@ -1,21 +1,26 @@ mod never_type; mod coercion; +mod regression; +mod simple; +mod patterns; +mod traits; +mod method_resolution; +mod macros; use std::fmt::Write; use std::sync::Arc; use hir_def::{ - body::BodySourceMap, db::DefDatabase, nameres::CrateDefMap, AssocItemId, DefWithBodyId, - LocalModuleId, Lookup, ModuleDefId, + body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, keys, + nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, }; -use hir_expand::Source; +use hir_expand::InFile; use insta::assert_snapshot; use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase}; use ra_syntax::{ algo, ast::{self, AstNode}, }; -use test_utils::covers; use crate::{db::HirDatabase, display::HirDisplay, test_db::TestDB, InferenceResult}; @@ -23,4669 +28,20 @@ use crate::{db::HirDatabase, display::HirDisplay, test_db::TestDB, InferenceResu // against snapshots of the expected results using insta. Use cargo-insta to // update the snapshots. -#[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_await() { - let (db, pos) = TestDB::with_position( - r#" -//- /main.rs crate:main deps:std - -struct IntFuture; - -impl Future for IntFuture { - type Output = u64; -} - -fn test() { - let r = IntFuture; - let v = r.await; - v<|>; -} - -//- /std.rs crate:std -#[prelude_import] use future::*; -mod future { - trait Future { - type Output; - } -} - -"#, - ); - assert_eq!("u64", type_at_pos(&db, pos)); -} - -#[test] -fn infer_box() { - let (db, pos) = TestDB::with_position( - r#" -//- /main.rs crate:main deps:std - -fn test() { - let x = box 1; - let t = (x, box x, box &1, box [1]); - t<|>; -} - -//- /std.rs crate:std -#[prelude_import] use prelude::*; -mod prelude {} - -mod boxed { - pub struct Box { - inner: *mut T, - } -} - -"#, - ); - assert_eq!("(Box, Box>, Box<&i32>, Box<[i32;_]>)", type_at_pos(&db, pos)); -} - -#[test] -fn infer_adt_self() { - let (db, pos) = TestDB::with_position( - r#" -//- /main.rs -enum Nat { Succ(Self), Demo(Nat), Zero } - -fn test() { - let foo: Nat = Nat::Zero; - if let Nat::Succ(x) = foo { - x<|> - } -} - -"#, - ); - assert_eq!("Nat", type_at_pos(&db, pos)); -} - -#[test] -fn infer_try() { - let (db, pos) = TestDB::with_position( - r#" -//- /main.rs crate:main deps:std - -fn test() { - let r: Result = Result::Ok(1); - let v = r?; - v<|>; -} - -//- /std.rs crate:std - -#[prelude_import] use ops::*; -mod ops { - trait Try { - type Ok; - type Error; - } -} - -#[prelude_import] use result::*; -mod result { - enum Result { - Ok(O), - Err(E) - } - - impl crate::ops::Try for Result { - type Ok = O; - type Error = E; - } -} - -"#, - ); - assert_eq!("i32", type_at_pos(&db, pos)); -} - -#[test] -fn infer_for_loop() { - let (db, pos) = TestDB::with_position( - r#" -//- /main.rs crate:main deps:std - -use std::collections::Vec; - -fn test() { - let v = Vec::new(); - v.push("foo"); - for x in v { - x<|>; - } -} - -//- /std.rs crate:std - -#[prelude_import] use iter::*; -mod iter { - trait IntoIterator { - type Item; - } -} - -mod collections { - struct Vec {} - impl Vec { - fn new() -> Self { Vec {} } - fn push(&mut self, t: T) { } - } - - impl crate::iter::IntoIterator for Vec { - type Item=T; - } -} -"#, - ); - assert_eq!("&str", type_at_pos(&db, pos)); -} - -#[test] -fn infer_while_let() { - let (db, pos) = TestDB::with_position( - r#" -//- /main.rs -enum Option { Some(T), None } - -fn test() { - let foo: Option = None; - while let Option::Some(x) = foo { - <|>x - } -} - -"#, - ); - assert_eq!("f32", type_at_pos(&db, pos)); -} - -#[test] -fn infer_basics() { - assert_snapshot!( - infer(r#" -fn test(a: u32, b: isize, c: !, d: &str) { - a; - b; - c; - d; - 1usize; - 1isize; - "test"; - 1.0f32; -}"#), - @r###" - [9; 10) 'a': u32 - [17; 18) 'b': isize - [27; 28) 'c': ! - [33; 34) 'd': &str - [42; 121) '{ ...f32; }': ! - [48; 49) 'a': u32 - [55; 56) 'b': isize - [62; 63) 'c': ! - [69; 70) 'd': &str - [76; 82) '1usize': usize - [88; 94) '1isize': isize - [100; 106) '"test"': &str - [112; 118) '1.0f32': f32 - "### - ); -} - -#[test] -fn infer_let() { - assert_snapshot!( - infer(r#" -fn test() { - let a = 1isize; - let b: usize = 1; - let c = b; - let d: u32; - let e; - let f: i32 = e; -} -"#), - @r###" - [11; 118) '{ ...= e; }': () - [21; 22) 'a': isize - [25; 31) '1isize': isize - [41; 42) 'b': usize - [52; 53) '1': usize - [63; 64) 'c': usize - [67; 68) 'b': usize - [78; 79) 'd': u32 - [94; 95) 'e': i32 - [105; 106) 'f': i32 - [114; 115) 'e': i32 - "### - ); -} - -#[test] -fn infer_paths() { - assert_snapshot!( - infer(r#" -fn a() -> u32 { 1 } - -mod b { - fn c() -> u32 { 1 } -} - -fn test() { - a(); - b::c(); -} -"#), - @r###" - [15; 20) '{ 1 }': u32 - [17; 18) '1': u32 - [48; 53) '{ 1 }': u32 - [50; 51) '1': u32 - [67; 91) '{ ...c(); }': () - [73; 74) 'a': fn a() -> u32 - [73; 76) 'a()': u32 - [82; 86) 'b::c': fn c() -> u32 - [82; 88) 'b::c()': u32 - "### - ); -} - -#[test] -fn infer_path_type() { - assert_snapshot!( - infer(r#" -struct S; - -impl S { - fn foo() -> i32 { 1 } -} - -fn test() { - S::foo(); - ::foo(); -} -"#), - @r###" - [41; 46) '{ 1 }': i32 - [43; 44) '1': i32 - [60; 93) '{ ...o(); }': () - [66; 72) 'S::foo': fn foo() -> i32 - [66; 74) 'S::foo()': i32 - [80; 88) '::foo': fn foo() -> i32 - [80; 90) '::foo()': i32 - "### - ); -} - -#[test] -fn infer_slice_method() { - assert_snapshot!( - infer(r#" -#[lang = "slice"] -impl [T] { - fn foo(&self) -> T { - loop {} - } -} - -#[lang = "slice_alloc"] -impl [T] {} - -fn test() { - <[_]>::foo(b"foo"); -} -"#), - @r###" - [45; 49) 'self': &[T] - [56; 79) '{ ... }': T - [66; 73) 'loop {}': ! - [71; 73) '{}': () - [133; 160) '{ ...o"); }': () - [139; 149) '<[_]>::foo': fn foo(&[T]) -> T - [139; 157) '<[_]>:..."foo")': u8 - [150; 156) 'b"foo"': &[u8] - "### - ); -} - -#[test] -fn infer_struct() { - assert_snapshot!( - infer(r#" -struct A { - b: B, - c: C, -} -struct B; -struct C(usize); - -fn test() { - let c = C(1); - B; - let a: A = A { b: B, c: C(1) }; - a.b; - a.c; -} -"#), - @r###" - [72; 154) '{ ...a.c; }': () - [82; 83) 'c': C - [86; 87) 'C': C(usize) -> C - [86; 90) 'C(1)': C - [88; 89) '1': usize - [96; 97) 'B': B - [107; 108) 'a': A - [114; 133) 'A { b:...C(1) }': A - [121; 122) 'B': B - [127; 128) 'C': C(usize) -> C - [127; 131) 'C(1)': C - [129; 130) '1': usize - [139; 140) 'a': A - [139; 142) 'a.b': B - [148; 149) 'a': A - [148; 151) 'a.c': C - "### - ); -} - -#[test] -fn infer_enum() { - assert_snapshot!( - infer(r#" -enum E { - V1 { field: u32 }, - V2 -} -fn test() { - E::V1 { field: 1 }; - E::V2; -}"#), - @r###" - [48; 82) '{ E:...:V2; }': () - [52; 70) 'E::V1 ...d: 1 }': E - [67; 68) '1': u32 - [74; 79) 'E::V2': E - "### - ); -} - -#[test] -fn infer_refs() { - assert_snapshot!( - infer(r#" -fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { - a; - *a; - &a; - &mut a; - b; - *b; - &b; - c; - *c; - d; - *d; -} -"#), - @r###" - [9; 10) 'a': &u32 - [18; 19) 'b': &mut u32 - [31; 32) 'c': *const u32 - [46; 47) 'd': *mut u32 - [59; 150) '{ ... *d; }': () - [65; 66) 'a': &u32 - [72; 74) '*a': u32 - [73; 74) 'a': &u32 - [80; 82) '&a': &&u32 - [81; 82) 'a': &u32 - [88; 94) '&mut a': &mut &u32 - [93; 94) 'a': &u32 - [100; 101) 'b': &mut u32 - [107; 109) '*b': u32 - [108; 109) 'b': &mut u32 - [115; 117) '&b': &&mut u32 - [116; 117) 'b': &mut u32 - [123; 124) 'c': *const u32 - [130; 132) '*c': u32 - [131; 132) 'c': *const u32 - [138; 139) 'd': *mut u32 - [145; 147) '*d': u32 - [146; 147) 'd': *mut u32 - "### - ); -} - -#[test] -fn infer_literals() { - assert_snapshot!( - infer(r##" -fn test() { - 5i32; - 5f32; - 5f64; - "hello"; - b"bytes"; - 'c'; - b'b'; - 3.14; - 5000; - false; - true; - r#" - //! doc - // non-doc - mod foo {} - "#; - br#"yolo"#; -} -"##), - @r###" - [11; 221) '{ ...o"#; }': () - [17; 21) '5i32': i32 - [27; 31) '5f32': f32 - [37; 41) '5f64': f64 - [47; 54) '"hello"': &str - [60; 68) 'b"bytes"': &[u8] - [74; 77) ''c'': char - [83; 87) 'b'b'': u8 - [93; 97) '3.14': f64 - [103; 107) '5000': i32 - [113; 118) 'false': bool - [124; 128) 'true': bool - [134; 202) 'r#" ... "#': &str - [208; 218) 'br#"yolo"#': &[u8] - "### - ); -} - -#[test] -fn infer_unary_op() { - assert_snapshot!( - infer(r#" -enum SomeType {} - -fn test(x: SomeType) { - let b = false; - let c = !b; - let a = 100; - let d: i128 = -a; - let e = -100; - let f = !!!true; - let g = !42; - let h = !10u32; - let j = !a; - -3.14; - !3; - -x; - !x; - -"hello"; - !"hello"; -} -"#), - @r###" - [27; 28) 'x': SomeType - [40; 272) '{ ...lo"; }': () - [50; 51) 'b': bool - [54; 59) 'false': bool - [69; 70) 'c': bool - [73; 75) '!b': bool - [74; 75) 'b': bool - [85; 86) 'a': i128 - [89; 92) '100': i128 - [102; 103) 'd': i128 - [112; 114) '-a': i128 - [113; 114) 'a': i128 - [124; 125) 'e': i32 - [128; 132) '-100': i32 - [129; 132) '100': i32 - [142; 143) 'f': bool - [146; 153) '!!!true': bool - [147; 153) '!!true': bool - [148; 153) '!true': bool - [149; 153) 'true': bool - [163; 164) 'g': i32 - [167; 170) '!42': i32 - [168; 170) '42': i32 - [180; 181) 'h': u32 - [184; 190) '!10u32': u32 - [185; 190) '10u32': u32 - [200; 201) 'j': i128 - [204; 206) '!a': i128 - [205; 206) 'a': i128 - [212; 217) '-3.14': f64 - [213; 217) '3.14': f64 - [223; 225) '!3': i32 - [224; 225) '3': i32 - [231; 233) '-x': {unknown} - [232; 233) 'x': SomeType - [239; 241) '!x': {unknown} - [240; 241) 'x': SomeType - [247; 255) '-"hello"': {unknown} - [248; 255) '"hello"': &str - [261; 269) '!"hello"': {unknown} - [262; 269) '"hello"': &str - "### - ); -} - -#[test] -fn infer_backwards() { - assert_snapshot!( - infer(r#" -fn takes_u32(x: u32) {} - -struct S { i32_field: i32 } - -fn test() -> &mut &f64 { - let a = unknown_function(); - takes_u32(a); - let b = unknown_function(); - S { i32_field: b }; - let c = unknown_function(); - &mut &c -} -"#), - @r###" - [14; 15) 'x': u32 - [22; 24) '{}': () - [78; 231) '{ ...t &c }': &mut &f64 - [88; 89) 'a': u32 - [92; 108) 'unknow...nction': {unknown} - [92; 110) 'unknow...tion()': u32 - [116; 125) 'takes_u32': fn takes_u32(u32) -> () - [116; 128) 'takes_u32(a)': () - [126; 127) 'a': u32 - [138; 139) 'b': i32 - [142; 158) 'unknow...nction': {unknown} - [142; 160) 'unknow...tion()': i32 - [166; 184) 'S { i3...d: b }': S - [181; 182) 'b': i32 - [194; 195) 'c': f64 - [198; 214) 'unknow...nction': {unknown} - [198; 216) 'unknow...tion()': f64 - [222; 229) '&mut &c': &mut &f64 - [227; 229) '&c': &f64 - [228; 229) 'c': f64 - "### - ); -} - -#[test] -fn infer_self() { - assert_snapshot!( - infer(r#" -struct S; - -impl S { - fn test(&self) { - self; - } - fn test2(self: &Self) { - self; - } - fn test3() -> Self { - S {} - } - fn test4() -> Self { - Self {} - } -} -"#), - @r###" - [34; 38) 'self': &S - [40; 61) '{ ... }': () - [50; 54) 'self': &S - [75; 79) 'self': &S - [88; 109) '{ ... }': () - [98; 102) 'self': &S - [133; 153) '{ ... }': S - [143; 147) 'S {}': S - [177; 200) '{ ... }': S - [187; 194) 'Self {}': S - "### - ); -} - -#[test] -fn infer_binary_op() { - assert_snapshot!( - infer(r#" -fn f(x: bool) -> i32 { - 0i32 -} - -fn test() -> bool { - let x = a && b; - let y = true || false; - let z = x == y; - let t = x != y; - let minus_forty: isize = -40isize; - let h = minus_forty <= CONST_2; - let c = f(z || y) + 5; - let d = b; - let g = minus_forty ^= i; - let ten: usize = 10; - let ten_is_eleven = ten == some_num; - - ten < 3 -} -"#), - @r###" - [6; 7) 'x': bool - [22; 34) '{ 0i32 }': i32 - [28; 32) '0i32': i32 - [54; 370) '{ ... < 3 }': bool - [64; 65) 'x': bool - [68; 69) 'a': bool - [68; 74) 'a && b': bool - [73; 74) 'b': bool - [84; 85) 'y': bool - [88; 92) 'true': bool - [88; 101) 'true || false': bool - [96; 101) 'false': bool - [111; 112) 'z': bool - [115; 116) 'x': bool - [115; 121) 'x == y': bool - [120; 121) 'y': bool - [131; 132) 't': bool - [135; 136) 'x': bool - [135; 141) 'x != y': bool - [140; 141) 'y': bool - [151; 162) 'minus_forty': isize - [172; 180) '-40isize': isize - [173; 180) '40isize': isize - [190; 191) 'h': bool - [194; 205) 'minus_forty': isize - [194; 216) 'minus_...ONST_2': bool - [209; 216) 'CONST_2': isize - [226; 227) 'c': i32 - [230; 231) 'f': fn f(bool) -> i32 - [230; 239) 'f(z || y)': i32 - [230; 243) 'f(z || y) + 5': i32 - [232; 233) 'z': bool - [232; 238) 'z || y': bool - [237; 238) 'y': bool - [242; 243) '5': i32 - [253; 254) 'd': {unknown} - [257; 258) 'b': {unknown} - [268; 269) 'g': () - [272; 283) 'minus_forty': isize - [272; 288) 'minus_...y ^= i': () - [287; 288) 'i': isize - [298; 301) 'ten': usize - [311; 313) '10': usize - [323; 336) 'ten_is_eleven': bool - [339; 342) 'ten': usize - [339; 354) 'ten == some_num': bool - [346; 354) 'some_num': usize - [361; 364) 'ten': usize - [361; 368) 'ten < 3': bool - [367; 368) '3': usize - "### - ); -} - -#[test] -fn infer_field_autoderef() { - assert_snapshot!( - infer(r#" -struct A { - b: B, -} -struct B; - -fn test1(a: A) { - let a1 = a; - a1.b; - let a2 = &a; - a2.b; - let a3 = &mut a; - a3.b; - let a4 = &&&&&&&a; - a4.b; - let a5 = &mut &&mut &&mut a; - a5.b; -} - -fn test2(a1: *const A, a2: *mut A) { - a1.b; - a2.b; -} -"#), - @r###" - [44; 45) 'a': A - [50; 213) '{ ...5.b; }': () - [60; 62) 'a1': A - [65; 66) 'a': A - [72; 74) 'a1': A - [72; 76) 'a1.b': B - [86; 88) 'a2': &A - [91; 93) '&a': &A - [92; 93) 'a': A - [99; 101) 'a2': &A - [99; 103) 'a2.b': B - [113; 115) 'a3': &mut A - [118; 124) '&mut a': &mut A - [123; 124) 'a': A - [130; 132) 'a3': &mut A - [130; 134) 'a3.b': B - [144; 146) 'a4': &&&&&&&A - [149; 157) '&&&&&&&a': &&&&&&&A - [150; 157) '&&&&&&a': &&&&&&A - [151; 157) '&&&&&a': &&&&&A - [152; 157) '&&&&a': &&&&A - [153; 157) '&&&a': &&&A - [154; 157) '&&a': &&A - [155; 157) '&a': &A - [156; 157) 'a': A - [163; 165) 'a4': &&&&&&&A - [163; 167) 'a4.b': B - [177; 179) 'a5': &mut &&mut &&mut A - [182; 200) '&mut &...&mut a': &mut &&mut &&mut A - [187; 200) '&&mut &&mut a': &&mut &&mut A - [188; 200) '&mut &&mut a': &mut &&mut A - [193; 200) '&&mut a': &&mut A - [194; 200) '&mut a': &mut A - [199; 200) 'a': A - [206; 208) 'a5': &mut &&mut &&mut A - [206; 210) 'a5.b': B - [224; 226) 'a1': *const A - [238; 240) 'a2': *mut A - [250; 273) '{ ...2.b; }': () - [256; 258) 'a1': *const A - [256; 260) 'a1.b': B - [266; 268) 'a2': *mut A - [266; 270) 'a2.b': B - "### - ); -} - -#[test] -fn infer_argument_autoderef() { - assert_snapshot!( - infer(r#" -#[lang = "deref"] -pub trait Deref { - type Target; - fn deref(&self) -> &Self::Target; -} - -struct A(T); - -impl A { - fn foo(&self) -> &T { - &self.0 - } -} - -struct B(T); - -impl Deref for B { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -fn test() { - let t = A::foo(&&B(B(A(42)))); -} -"#), - @r###" - [68; 72) 'self': &Self - [139; 143) 'self': &A - [151; 174) '{ ... }': &T - [161; 168) '&self.0': &T - [162; 166) 'self': &A - [162; 168) 'self.0': T - [255; 259) 'self': &B - [278; 301) '{ ... }': &T - [288; 295) '&self.0': &T - [289; 293) 'self': &B - [289; 295) 'self.0': T - [315; 353) '{ ...))); }': () - [325; 326) 't': &i32 - [329; 335) 'A::foo': fn foo(&A) -> &T - [329; 350) 'A::foo...42))))': &i32 - [336; 349) '&&B(B(A(42)))': &&B>> - [337; 349) '&B(B(A(42)))': &B>> - [338; 339) 'B': B>>(T) -> B - [338; 349) 'B(B(A(42)))': B>> - [340; 341) 'B': B>(T) -> B - [340; 348) 'B(A(42))': B> - [342; 343) 'A': A(T) -> A - [342; 347) 'A(42)': A - [344; 346) '42': i32 - "### - ); -} - -#[test] -fn infer_method_argument_autoderef() { - assert_snapshot!( - infer(r#" -#[lang = "deref"] -pub trait Deref { - type Target; - fn deref(&self) -> &Self::Target; -} - -struct A(*mut T); - -impl A { - fn foo(&self, x: &A) -> &T { - &*x.0 - } -} - -struct B(T); - -impl Deref for B { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -fn test(a: A) { - let t = A(0 as *mut _).foo(&&B(B(a))); -} -"#), - @r###" - [68; 72) 'self': &Self - [144; 148) 'self': &A - [150; 151) 'x': &A - [166; 187) '{ ... }': &T - [176; 181) '&*x.0': &T - [177; 181) '*x.0': T - [178; 179) 'x': &A - [178; 181) 'x.0': *mut T - [268; 272) 'self': &B - [291; 314) '{ ... }': &T - [301; 308) '&self.0': &T - [302; 306) 'self': &B - [302; 308) 'self.0': T - [326; 327) 'a': A - [337; 383) '{ ...))); }': () - [347; 348) 't': &i32 - [351; 352) 'A': A(*mut T) -> A - [351; 365) 'A(0 as *mut _)': A - [351; 380) 'A(0 as...B(a)))': &i32 - [353; 354) '0': i32 - [353; 364) '0 as *mut _': *mut i32 - [370; 379) '&&B(B(a))': &&B>> - [371; 379) '&B(B(a))': &B>> - [372; 373) 'B': B>>(T) -> B - [372; 379) 'B(B(a))': B>> - [374; 375) 'B': B>(T) -> B - [374; 378) 'B(a)': B> - [376; 377) 'a': A - "### - ); -} - -#[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 infer_in_elseif() { - assert_snapshot!( - infer(r#" -struct Foo { field: i32 } -fn main(foo: Foo) { - if true { - - } else if false { - foo.field - } -} -"#), - @r###" - [35; 38) 'foo': Foo - [45; 109) '{ ... } }': () - [51; 107) 'if tru... }': () - [54; 58) 'true': bool - [59; 67) '{ }': () - [73; 107) 'if fal... }': () - [76; 81) 'false': bool - [82; 107) '{ ... }': i32 - [92; 95) 'foo': Foo - [92; 101) 'foo.field': i32 - "### - ) -} - -#[test] -fn infer_if_match_with_return() { - assert_snapshot!( - infer(r#" -fn foo() { - let _x1 = if true { - 1 - } else { - return; - }; - let _x2 = if true { - 2 - } else { - return - }; - let _x3 = match true { - true => 3, - _ => { - return; - } - }; - let _x4 = match true { - true => 4, - _ => return - }; -}"#), - @r###" - [10; 323) '{ ... }; }': () - [20; 23) '_x1': i32 - [26; 80) 'if tru... }': i32 - [29; 33) 'true': bool - [34; 51) '{ ... }': i32 - [44; 45) '1': i32 - [57; 80) '{ ... }': ! - [67; 73) 'return': ! - [90; 93) '_x2': i32 - [96; 149) 'if tru... }': i32 - [99; 103) 'true': bool - [104; 121) '{ ... }': i32 - [114; 115) '2': i32 - [127; 149) '{ ... }': ! - [137; 143) 'return': ! - [159; 162) '_x3': i32 - [165; 247) 'match ... }': i32 - [171; 175) 'true': bool - [186; 190) 'true': bool - [194; 195) '3': i32 - [205; 206) '_': bool - [210; 241) '{ ... }': ! - [224; 230) 'return': ! - [257; 260) '_x4': i32 - [263; 320) 'match ... }': i32 - [269; 273) 'true': bool - [284; 288) 'true': bool - [292; 293) '4': i32 - [303; 304) '_': bool - [308; 314) 'return': ! - "### - ) -} - -#[test] -fn infer_inherent_method() { - assert_snapshot!( - infer(r#" -struct A; - -impl A { - fn foo(self, x: u32) -> i32 {} -} - -mod b { - impl super::A { - fn bar(&self, x: u64) -> i64 {} - } -} - -fn test(a: A) { - a.foo(1); - (&a).bar(1); - a.bar(1); -} -"#), - @r###" - [32; 36) 'self': A - [38; 39) 'x': u32 - [53; 55) '{}': () - [103; 107) 'self': &A - [109; 110) 'x': u64 - [124; 126) '{}': () - [144; 145) 'a': A - [150; 198) '{ ...(1); }': () - [156; 157) 'a': A - [156; 164) 'a.foo(1)': i32 - [162; 163) '1': u32 - [170; 181) '(&a).bar(1)': i64 - [171; 173) '&a': &A - [172; 173) 'a': A - [179; 180) '1': u64 - [187; 188) 'a': A - [187; 195) 'a.bar(1)': i64 - [193; 194) '1': u64 - "### - ); -} - -#[test] -fn infer_inherent_method_str() { - assert_snapshot!( - infer(r#" -#[lang = "str"] -impl str { - fn foo(&self) -> i32 {} -} - -fn test() { - "foo".foo(); -} -"#), - @r###" - [40; 44) 'self': &str - [53; 55) '{}': () - [69; 89) '{ ...o(); }': () - [75; 80) '"foo"': &str - [75; 86) '"foo".foo()': i32 - "### - ); -} - -#[test] -fn infer_tuple() { - assert_snapshot!( - infer(r#" -fn test(x: &str, y: isize) { - let a: (u32, &str) = (1, "a"); - let b = (a, x); - let c = (y, x); - let d = (c, x); - let e = (1, "e"); - let f = (e, "d"); -} -"#), - @r###" - [9; 10) 'x': &str - [18; 19) 'y': isize - [28; 170) '{ ...d"); }': () - [38; 39) 'a': (u32, &str) - [55; 63) '(1, "a")': (u32, &str) - [56; 57) '1': u32 - [59; 62) '"a"': &str - [73; 74) 'b': ((u32, &str), &str) - [77; 83) '(a, x)': ((u32, &str), &str) - [78; 79) 'a': (u32, &str) - [81; 82) 'x': &str - [93; 94) 'c': (isize, &str) - [97; 103) '(y, x)': (isize, &str) - [98; 99) 'y': isize - [101; 102) 'x': &str - [113; 114) 'd': ((isize, &str), &str) - [117; 123) '(c, x)': ((isize, &str), &str) - [118; 119) 'c': (isize, &str) - [121; 122) 'x': &str - [133; 134) 'e': (i32, &str) - [137; 145) '(1, "e")': (i32, &str) - [138; 139) '1': i32 - [141; 144) '"e"': &str - [155; 156) 'f': ((i32, &str), &str) - [159; 167) '(e, "d")': ((i32, &str), &str) - [160; 161) 'e': (i32, &str) - [163; 166) '"d"': &str - "### - ); -} - -#[test] -fn infer_array() { - assert_snapshot!( - infer(r#" -fn test(x: &str, y: isize) { - let a = [x]; - let b = [a, a]; - let c = [b, b]; - - let d = [y, 1, 2, 3]; - let d = [1, y, 2, 3]; - let e = [y]; - let f = [d, d]; - let g = [e, e]; - - let h = [1, 2]; - let i = ["a", "b"]; - - let b = [a, ["b"]]; - let x: [u8; 0] = []; -} -"#), - @r###" - [9; 10) 'x': &str - [18; 19) 'y': isize - [28; 293) '{ ... []; }': () - [38; 39) 'a': [&str;_] - [42; 45) '[x]': [&str;_] - [43; 44) 'x': &str - [55; 56) 'b': [[&str;_];_] - [59; 65) '[a, a]': [[&str;_];_] - [60; 61) 'a': [&str;_] - [63; 64) 'a': [&str;_] - [75; 76) 'c': [[[&str;_];_];_] - [79; 85) '[b, b]': [[[&str;_];_];_] - [80; 81) 'b': [[&str;_];_] - [83; 84) 'b': [[&str;_];_] - [96; 97) 'd': [isize;_] - [100; 112) '[y, 1, 2, 3]': [isize;_] - [101; 102) 'y': isize - [104; 105) '1': isize - [107; 108) '2': isize - [110; 111) '3': isize - [122; 123) 'd': [isize;_] - [126; 138) '[1, y, 2, 3]': [isize;_] - [127; 128) '1': isize - [130; 131) 'y': isize - [133; 134) '2': isize - [136; 137) '3': isize - [148; 149) 'e': [isize;_] - [152; 155) '[y]': [isize;_] - [153; 154) 'y': isize - [165; 166) 'f': [[isize;_];_] - [169; 175) '[d, d]': [[isize;_];_] - [170; 171) 'd': [isize;_] - [173; 174) 'd': [isize;_] - [185; 186) 'g': [[isize;_];_] - [189; 195) '[e, e]': [[isize;_];_] - [190; 191) 'e': [isize;_] - [193; 194) 'e': [isize;_] - [206; 207) 'h': [i32;_] - [210; 216) '[1, 2]': [i32;_] - [211; 212) '1': i32 - [214; 215) '2': i32 - [226; 227) 'i': [&str;_] - [230; 240) '["a", "b"]': [&str;_] - [231; 234) '"a"': &str - [236; 239) '"b"': &str - [251; 252) 'b': [[&str;_];_] - [255; 265) '[a, ["b"]]': [[&str;_];_] - [256; 257) 'a': [&str;_] - [259; 264) '["b"]': [&str;_] - [260; 263) '"b"': &str - [275; 276) 'x': [u8;_] - [288; 290) '[]': [u8;_] - "### - ); -} - -#[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); - -fn test() { - let A(n) = &A(1); - let A(n) = &mut A(1); -} -"#), - @r###" - [28; 79) '{ ...(1); }': () - [38; 42) 'A(n)': A - [40; 41) 'n': &i32 - [45; 50) '&A(1)': &A - [46; 47) 'A': A(T) -> A - [46; 50) 'A(1)': A - [48; 49) '1': i32 - [60; 64) 'A(n)': A - [62; 63) 'n': &mut i32 - [67; 76) '&mut A(1)': &mut A - [72; 73) 'A': A(T) -> A - [72; 76) 'A(1)': A - [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_struct_generics() { - assert_snapshot!( - infer(r#" -struct A { - x: T, -} - -fn test(a1: A, i: i32) { - a1.x; - let a2 = A { x: i }; - a2.x; - let a3 = A:: { x: 1 }; - a3.x; -} -"#), - @r###" - [36; 38) 'a1': A - [48; 49) 'i': i32 - [56; 147) '{ ...3.x; }': () - [62; 64) 'a1': A - [62; 66) 'a1.x': u32 - [76; 78) 'a2': A - [81; 91) 'A { x: i }': A - [88; 89) 'i': i32 - [97; 99) 'a2': A - [97; 101) 'a2.x': i32 - [111; 113) 'a3': A - [116; 134) 'A:: - [131; 132) '1': i128 - [140; 142) 'a3': A - [140; 144) 'a3.x': i128 - "### - ); -} - -#[test] -fn infer_tuple_struct_generics() { - assert_snapshot!( - infer(r#" -struct A(T); -enum Option { Some(T), None } -use Option::*; - -fn test() { - A(42); - A(42u128); - Some("x"); - Option::Some("x"); - None; - let x: Option = None; -} -"#), - @r###" - [76; 184) '{ ...one; }': () - [82; 83) 'A': A(T) -> A - [82; 87) 'A(42)': A - [84; 86) '42': i32 - [93; 94) 'A': A(T) -> A - [93; 102) 'A(42u128)': A - [95; 101) '42u128': u128 - [108; 112) 'Some': Some<&str>(T) -> Option - [108; 117) 'Some("x")': Option<&str> - [113; 116) '"x"': &str - [123; 135) 'Option::Some': Some<&str>(T) -> Option - [123; 140) 'Option...e("x")': Option<&str> - [136; 139) '"x"': &str - [146; 150) 'None': Option<{unknown}> - [160; 161) 'x': Option - [177; 181) 'None': Option - "### - ); -} - -#[test] -fn infer_generics_in_patterns() { - assert_snapshot!( - infer(r#" -struct A { - x: T, -} - -enum Option { - Some(T), - None, -} - -fn test(a1: A, o: Option) { - let A { x: x2 } = a1; - let A:: { x: x3 } = A { x: 1 }; - match o { - Option::Some(t) => t, - _ => 1, - }; -} -"#), - @r###" - [79; 81) 'a1': A - [91; 92) 'o': Option - [107; 244) '{ ... }; }': () - [117; 128) 'A { x: x2 }': A - [124; 126) 'x2': u32 - [131; 133) 'a1': A - [143; 161) 'A:: - [157; 159) 'x3': i64 - [164; 174) 'A { x: 1 }': A - [171; 172) '1': i64 - [180; 241) 'match ... }': u64 - [186; 187) 'o': Option - [198; 213) 'Option::Some(t)': Option - [211; 212) 't': u64 - [217; 218) 't': u64 - [228; 229) '_': Option - [233; 234) '1': u64 - "### - ); -} - -#[test] -fn infer_function_generics() { - assert_snapshot!( - infer(r#" -fn id(t: T) -> T { t } - -fn test() { - id(1u32); - id::(1); - let x: u64 = id(1); -} -"#), - @r###" - [10; 11) 't': T - [21; 26) '{ t }': T - [23; 24) 't': T - [38; 98) '{ ...(1); }': () - [44; 46) 'id': fn id(T) -> T - [44; 52) 'id(1u32)': u32 - [47; 51) '1u32': u32 - [58; 68) 'id::': fn id(T) -> T - [58; 71) 'id::(1)': i128 - [69; 70) '1': i128 - [81; 82) 'x': u64 - [90; 92) 'id': fn id(T) -> T - [90; 95) 'id(1)': u64 - [93; 94) '1': u64 - "### - ); -} - -#[test] -fn infer_impl_generics() { - assert_snapshot!( - infer(r#" -struct A { - x: T1, - y: T2, -} -impl A { - fn x(self) -> X { - self.x - } - fn y(self) -> Y { - self.y - } - fn z(self, t: T) -> (X, Y, T) { - (self.x, self.y, t) - } -} - -fn test() -> i128 { - let a = A { x: 1u64, y: 1i64 }; - a.x(); - a.y(); - a.z(1i128); - a.z::(1); -} -"#), - @r###" - [74; 78) 'self': A - [85; 107) '{ ... }': X - [95; 99) 'self': A - [95; 101) 'self.x': X - [117; 121) 'self': A - [128; 150) '{ ... }': Y - [138; 142) 'self': A - [138; 144) 'self.y': Y - [163; 167) 'self': A - [169; 170) 't': T - [188; 223) '{ ... }': (X, Y, T) - [198; 217) '(self.....y, t)': (X, Y, T) - [199; 203) 'self': A - [199; 205) 'self.x': X - [207; 211) 'self': A - [207; 213) 'self.y': Y - [215; 216) 't': T - [245; 342) '{ ...(1); }': () - [255; 256) 'a': A - [259; 281) 'A { x:...1i64 }': A - [266; 270) '1u64': u64 - [275; 279) '1i64': i64 - [287; 288) 'a': A - [287; 292) 'a.x()': u64 - [298; 299) 'a': A - [298; 303) 'a.y()': i64 - [309; 310) 'a': A - [309; 319) 'a.z(1i128)': (u64, i64, i128) - [313; 318) '1i128': i128 - [325; 326) 'a': A - [325; 339) 'a.z::(1)': (u64, i64, u128) - [337; 338) '1': u128 - "### - ); -} - -#[test] -fn infer_impl_generics_with_autoderef() { - assert_snapshot!( - infer(r#" -enum Option { - Some(T), - None, -} -impl Option { - fn as_ref(&self) -> Option<&T> {} -} -fn test(o: Option) { - (&o).as_ref(); - o.as_ref(); -} -"#), - @r###" - [78; 82) 'self': &Option - [98; 100) '{}': () - [111; 112) 'o': Option - [127; 165) '{ ...f(); }': () - [133; 146) '(&o).as_ref()': Option<&u32> - [134; 136) '&o': &Option - [135; 136) 'o': Option - [152; 153) 'o': Option - [152; 162) 'o.as_ref()': Option<&u32> - "### - ); -} - -#[test] -fn infer_generic_chain() { - assert_snapshot!( - infer(r#" -struct A { - x: T, -} -impl A { - fn x(self) -> T2 { - self.x - } -} -fn id(t: T) -> T { t } - -fn test() -> i128 { - let x = 1; - let y = id(x); - let a = A { x: id(y) }; - let z = id(a.x); - let b = A { x: z }; - b.x() -} -"#), - @r###" - [53; 57) 'self': A - [65; 87) '{ ... }': T2 - [75; 79) 'self': A - [75; 81) 'self.x': T2 - [99; 100) 't': T - [110; 115) '{ t }': T - [112; 113) 't': T - [135; 261) '{ ....x() }': i128 - [146; 147) 'x': i128 - [150; 151) '1': i128 - [162; 163) 'y': i128 - [166; 168) 'id': fn id(T) -> T - [166; 171) 'id(x)': i128 - [169; 170) 'x': i128 - [182; 183) 'a': A - [186; 200) 'A { x: id(y) }': A - [193; 195) 'id': fn id(T) -> T - [193; 198) 'id(y)': i128 - [196; 197) 'y': i128 - [211; 212) 'z': i128 - [215; 217) 'id': fn id(T) -> T - [215; 222) 'id(a.x)': i128 - [218; 219) 'a': A - [218; 221) 'a.x': i128 - [233; 234) 'b': A - [237; 247) 'A { x: z }': A - [244; 245) 'z': i128 - [254; 255) 'b': A - [254; 259) 'b.x()': i128 - "### - ); -} - -#[test] -fn infer_associated_const() { - assert_snapshot!( - infer(r#" -struct Struct; - -impl Struct { - const FOO: u32 = 1; -} - -enum Enum {} - -impl Enum { - const BAR: u32 = 2; -} - -trait Trait { - const ID: u32; -} - -struct TraitTest; - -impl Trait for TraitTest { - const ID: u32 = 5; -} - -fn test() { - let x = Struct::FOO; - let y = Enum::BAR; - let z = TraitTest::ID; -} -"#), - @r###" - [52; 53) '1': u32 - [105; 106) '2': u32 - [213; 214) '5': u32 - [229; 307) '{ ...:ID; }': () - [239; 240) 'x': u32 - [243; 254) 'Struct::FOO': u32 - [264; 265) 'y': u32 - [268; 277) 'Enum::BAR': u32 - [287; 288) 'z': u32 - [291; 304) 'TraitTest::ID': u32 - "### - ); -} - -#[test] -fn infer_associated_method_struct() { - assert_snapshot!( - infer(r#" -struct A { x: u32 } - -impl A { - fn new() -> A { - A { x: 0 } - } -} -fn test() { - let a = A::new(); - a.x; -} -"#), - @r###" - [49; 75) '{ ... }': A - [59; 69) 'A { x: 0 }': A - [66; 67) '0': u32 - [88; 122) '{ ...a.x; }': () - [98; 99) 'a': A - [102; 108) 'A::new': fn new() -> A - [102; 110) 'A::new()': A - [116; 117) 'a': A - [116; 119) 'a.x': u32 - "### - ); -} - -#[test] -fn infer_associated_method_enum() { - assert_snapshot!( - infer(r#" -enum A { B, C } - -impl A { - pub fn b() -> A { - A::B - } - pub fn c() -> A { - A::C - } -} -fn test() { - let a = A::b(); - a; - let c = A::c(); - c; -} -"#), - @r###" - [47; 67) '{ ... }': A - [57; 61) 'A::B': A - [88; 108) '{ ... }': A - [98; 102) 'A::C': A - [121; 178) '{ ... c; }': () - [131; 132) 'a': A - [135; 139) 'A::b': fn b() -> A - [135; 141) 'A::b()': A - [147; 148) 'a': A - [158; 159) 'c': A - [162; 166) 'A::c': fn c() -> A - [162; 168) 'A::c()': A - [174; 175) 'c': A - "### - ); -} - -#[test] -fn infer_associated_method_with_modules() { - assert_snapshot!( - infer(r#" -mod a { - struct A; - impl A { pub fn thing() -> A { A {} }} -} - -mod b { - struct B; - impl B { pub fn thing() -> u32 { 99 }} - - mod c { - struct C; - impl C { pub fn thing() -> C { C {} }} - } -} -use b::c; - -fn test() { - let x = a::A::thing(); - let y = b::B::thing(); - let z = c::C::thing(); -} -"#), - @r###" - [56; 64) '{ A {} }': A - [58; 62) 'A {}': A - [126; 132) '{ 99 }': u32 - [128; 130) '99': u32 - [202; 210) '{ C {} }': C - [204; 208) 'C {}': C - [241; 325) '{ ...g(); }': () - [251; 252) 'x': A - [255; 266) 'a::A::thing': fn thing() -> A - [255; 268) 'a::A::thing()': A - [278; 279) 'y': u32 - [282; 293) 'b::B::thing': fn thing() -> u32 - [282; 295) 'b::B::thing()': u32 - [305; 306) 'z': C - [309; 320) 'c::C::thing': fn thing() -> C - [309; 322) 'c::C::thing()': C - "### - ); -} - -#[test] -fn infer_associated_method_generics() { - assert_snapshot!( - infer(r#" -struct Gen { - val: T -} - -impl Gen { - pub fn make(val: T) -> Gen { - Gen { val } - } -} - -fn test() { - let a = Gen::make(0u32); -} -"#), - @r###" - [64; 67) 'val': T - [82; 109) '{ ... }': Gen - [92; 103) 'Gen { val }': Gen - [98; 101) 'val': T - [123; 155) '{ ...32); }': () - [133; 134) 'a': Gen - [137; 146) 'Gen::make': fn make(T) -> Gen - [137; 152) 'Gen::make(0u32)': Gen - [147; 151) '0u32': u32 - "### - ); -} - -#[test] -fn infer_associated_method_generics_with_default_param() { - assert_snapshot!( - infer(r#" -struct Gen { - val: T -} - -impl Gen { - pub fn make() -> Gen { - loop { } - } -} - -fn test() { - let a = Gen::make(); -} -"#), - @r###" - [80; 104) '{ ... }': Gen - [90; 98) 'loop { }': ! - [95; 98) '{ }': () - [118; 146) '{ ...e(); }': () - [128; 129) 'a': Gen - [132; 141) 'Gen::make': fn make() -> Gen - [132; 143) 'Gen::make()': Gen - "### - ); -} - -#[test] -fn infer_associated_method_generics_with_default_tuple_param() { - let t = type_at( - r#" -//- /main.rs -struct Gen { - val: T -} - -impl Gen { - pub fn make() -> Gen { - loop { } - } -} - -fn test() { - let a = Gen::make(); - a.val<|>; -} -"#, - ); - assert_eq!(t, "()"); -} - -#[test] -fn infer_associated_method_generics_without_args() { - assert_snapshot!( - infer(r#" -struct Gen { - val: T -} - -impl Gen { - pub fn make() -> Gen { - loop { } - } -} - -fn test() { - let a = Gen::::make(); -} -"#), - @r###" - [76; 100) '{ ... }': Gen - [86; 94) 'loop { }': ! - [91; 94) '{ }': () - [114; 149) '{ ...e(); }': () - [124; 125) 'a': Gen - [128; 144) 'Gen::<...::make': fn make() -> Gen - [128; 146) 'Gen::<...make()': Gen - "### - ); -} - -#[test] -fn infer_associated_method_generics_2_type_params_without_args() { - assert_snapshot!( - infer(r#" -struct Gen { - val: T, - val2: U, -} - -impl Gen { - pub fn make() -> Gen { - loop { } - } -} - -fn test() { - let a = Gen::::make(); -} -"#), - @r###" - [102; 126) '{ ... }': Gen - [112; 120) 'loop { }': ! - [117; 120) '{ }': () - [140; 180) '{ ...e(); }': () - [150; 151) 'a': Gen - [154; 175) 'Gen::<...::make': fn make() -> Gen - [154; 177) 'Gen::<...make()': Gen - "### - ); -} - -#[test] -fn infer_type_alias() { - assert_snapshot!( - infer(r#" -struct A { x: X, y: Y } -type Foo = A; -type Bar = A; -type Baz = A; -fn test(x: Foo, y: Bar<&str>, z: Baz) { - x.x; - x.y; - y.x; - y.y; - z.x; - z.y; -} -"#), - @r###" - [116; 117) 'x': A - [124; 125) 'y': A<&str, u128> - [138; 139) 'z': A - [154; 211) '{ ...z.y; }': () - [160; 161) 'x': A - [160; 163) 'x.x': u32 - [169; 170) 'x': A - [169; 172) 'x.y': i128 - [178; 179) 'y': A<&str, u128> - [178; 181) 'y.x': &str - [187; 188) 'y': A<&str, u128> - [187; 190) 'y.y': u128 - [196; 197) 'z': A - [196; 199) 'z.x': u8 - [205; 206) 'z': A - [205; 208) 'z.y': i8 - "### - ) -} - -#[test] -#[should_panic] // we currently can't handle this -fn recursive_type_alias() { - assert_snapshot!( - infer(r#" -struct A {} -type Foo = Foo; -type Bar = A; -fn test(x: Foo) {} -"#), - @"" - ) -} - -#[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_type_param() { - assert_snapshot!( - infer(r#" -fn id(x: T) -> T { - x -} - -fn clone(x: &T) -> T { - *x -} - -fn test() { - let y = 10u32; - id(y); - let x: bool = clone(z); - id::(1); -} -"#), - @r###" - [10; 11) 'x': T - [21; 30) '{ x }': T - [27; 28) 'x': T - [44; 45) 'x': &T - [56; 66) '{ *x }': T - [62; 64) '*x': T - [63; 64) 'x': &T - [78; 158) '{ ...(1); }': () - [88; 89) 'y': u32 - [92; 97) '10u32': u32 - [103; 105) 'id': fn id(T) -> T - [103; 108) 'id(y)': u32 - [106; 107) 'y': u32 - [118; 119) 'x': bool - [128; 133) 'clone': fn clone(&T) -> T - [128; 136) 'clone(z)': bool - [134; 135) 'z': &bool - [142; 152) 'id::': fn id(T) -> T - [142; 155) 'id::(1)': i128 - [153; 154) '1': i128 - "### - ); -} - -#[test] -fn infer_std_crash_1() { - // caused stack overflow, taken from std - assert_snapshot!( - infer(r#" -enum Maybe { - 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 { - value: V, -} -struct QueryResponse { - value: V, -} -fn test(query_response: Canonical>) { - &query_response.value; -} -"#), - @r###" - [92; 106) 'query_response': Canonical> - [137; 167) '{ ...lue; }': () - [143; 164) '&query....value': &QueryResponse - [144; 158) 'query_response': Canonical> - [144; 164) 'query_....value': QueryResponse - "### - ); -} - -#[test] -fn bug_1030() { - assert_snapshot!(infer(r#" -struct HashSet; -struct FxHasher; -type FxHashSet = HashSet; - -impl HashSet { - fn default() -> HashSet {} -} - -pub fn main_loop() { - FxHashSet::default(); -} -"#), - @r###" - [144; 146) '{}': () - [169; 198) '{ ...t(); }': () - [175; 193) 'FxHash...efault': fn default<{unknown}, FxHasher>() -> HashSet - [175; 195) 'FxHash...ault()': HashSet<{unknown}, FxHasher> - "### - ); -} - -#[test] -fn cross_crate_associated_method_call() { - let (db, pos) = TestDB::with_position( - r#" -//- /main.rs crate:main deps:other_crate -fn test() { - let x = other_crate::foo::S::thing(); - x<|>; -} - -//- /lib.rs crate:other_crate -mod foo { - struct S; - impl S { - fn thing() -> i128 {} - } -} -"#, - ); - assert_eq!("i128", type_at_pos(&db, pos)); -} - -#[test] -fn infer_const() { - assert_snapshot!( - infer(r#" -struct Foo; -impl Foo { const ASSOC_CONST: u32 = 0; } -const GLOBAL_CONST: u32 = 101; -fn test() { - const LOCAL_CONST: u32 = 99; - let x = LOCAL_CONST; - let z = GLOBAL_CONST; - let id = Foo::ASSOC_CONST; -} -"#), - @r###" - [49; 50) '0': u32 - [80; 83) '101': u32 - [95; 213) '{ ...NST; }': () - [138; 139) 'x': {unknown} - [142; 153) 'LOCAL_CONST': {unknown} - [163; 164) 'z': u32 - [167; 179) 'GLOBAL_CONST': u32 - [189; 191) 'id': u32 - [194; 210) 'Foo::A..._CONST': u32 - "### - ); -} - -#[test] -fn infer_static() { - assert_snapshot!( - infer(r#" -static GLOBAL_STATIC: u32 = 101; -static mut GLOBAL_STATIC_MUT: u32 = 101; -fn test() { - static LOCAL_STATIC: u32 = 99; - static mut LOCAL_STATIC_MUT: u32 = 99; - let x = LOCAL_STATIC; - let y = LOCAL_STATIC_MUT; - let z = GLOBAL_STATIC; - let w = GLOBAL_STATIC_MUT; -} -"#), - @r###" - [29; 32) '101': u32 - [70; 73) '101': u32 - [85; 280) '{ ...MUT; }': () - [173; 174) 'x': {unknown} - [177; 189) 'LOCAL_STATIC': {unknown} - [199; 200) 'y': {unknown} - [203; 219) 'LOCAL_...IC_MUT': {unknown} - [229; 230) 'z': u32 - [233; 246) 'GLOBAL_STATIC': u32 - [256; 257) 'w': u32 - [260; 277) 'GLOBAL...IC_MUT': u32 - "### - ); -} - -#[test] -fn infer_trait_method_simple() { - // the trait implementation is intentionally incomplete -- it shouldn't matter - assert_snapshot!( - infer(r#" -trait Trait1 { - fn method(&self) -> u32; -} -struct S1; -impl Trait1 for S1 {} -trait Trait2 { - fn method(&self) -> i128; -} -struct S2; -impl Trait2 for S2 {} -fn test() { - S1.method(); // -> u32 - S2.method(); // -> i128 -} -"#), - @r###" - [31; 35) 'self': &Self - [110; 114) 'self': &Self - [170; 228) '{ ...i128 }': () - [176; 178) 'S1': S1 - [176; 187) 'S1.method()': u32 - [203; 205) 'S2': S2 - [203; 214) 'S2.method()': i128 - "### - ); -} - -#[test] -fn infer_trait_method_scoped() { - // the trait implementation is intentionally incomplete -- it shouldn't matter - assert_snapshot!( - infer(r#" -struct S; -mod foo { - pub trait Trait1 { - fn method(&self) -> u32; - } - impl Trait1 for super::S {} -} -mod bar { - pub trait Trait2 { - fn method(&self) -> i128; - } - impl Trait2 for super::S {} -} - -mod foo_test { - use super::S; - use super::foo::Trait1; - fn test() { - S.method(); // -> u32 - } -} - -mod bar_test { - use super::S; - use super::bar::Trait2; - fn test() { - S.method(); // -> i128 - } -} -"#), - @r###" - [63; 67) 'self': &Self - [169; 173) 'self': &Self - [300; 337) '{ ... }': () - [310; 311) 'S': S - [310; 320) 'S.method()': u32 - [416; 454) '{ ... }': () - [426; 427) 'S': S - [426; 436) 'S.method()': i128 - "### - ); -} - -#[test] -fn infer_trait_method_generic_1() { - // the trait implementation is intentionally incomplete -- it shouldn't matter - assert_snapshot!( - infer(r#" -trait Trait { - fn method(&self) -> T; -} -struct S; -impl Trait for S {} -fn test() { - S.method(); -} -"#), - @r###" - [33; 37) 'self': &Self - [92; 111) '{ ...d(); }': () - [98; 99) 'S': S - [98; 108) 'S.method()': u32 - "### - ); -} - -#[test] -fn infer_trait_method_generic_more_params() { - // the trait implementation is intentionally incomplete -- it shouldn't matter - assert_snapshot!( - infer(r#" -trait Trait { - fn method1(&self) -> (T1, T2, T3); - fn method2(&self) -> (T3, T2, T1); -} -struct S1; -impl Trait for S1 {} -struct S2; -impl Trait for S2 {} -fn test() { - S1.method1(); // u8, u16, u32 - S1.method2(); // u32, u16, u8 - S2.method1(); // i8, i16, {unknown} - S2.method2(); // {unknown}, i16, i8 -} -"#), - @r###" - [43; 47) 'self': &Self - [82; 86) 'self': &Self - [210; 361) '{ ..., i8 }': () - [216; 218) 'S1': S1 - [216; 228) 'S1.method1()': (u8, u16, u32) - [250; 252) 'S1': S1 - [250; 262) 'S1.method2()': (u32, u16, u8) - [284; 286) 'S2': S2 - [284; 296) 'S2.method1()': (i8, i16, {unknown}) - [324; 326) 'S2': S2 - [324; 336) 'S2.method2()': ({unknown}, i16, i8) - "### - ); -} - -#[test] -fn infer_trait_method_generic_2() { - // the trait implementation is intentionally incomplete -- it shouldn't matter - assert_snapshot!( - infer(r#" -trait Trait { - fn method(&self) -> T; -} -struct S(T); -impl Trait for S {} -fn test() { - S(1u32).method(); -} -"#), - @r###" - [33; 37) 'self': &Self - [102; 127) '{ ...d(); }': () - [108; 109) 'S': S(T) -> S - [108; 115) 'S(1u32)': S - [108; 124) 'S(1u32...thod()': u32 - [110; 114) '1u32': u32 - "### - ); -} - -#[test] -fn infer_trait_assoc_method() { - assert_snapshot!( - infer(r#" -trait Default { - fn default() -> Self; -} -struct S; -impl Default for S {} -fn test() { - let s1: S = Default::default(); - let s2 = S::default(); - let s3 = ::default(); -} -"#), - @r###" - [87; 193) '{ ...t(); }': () - [97; 99) 's1': S - [105; 121) 'Defaul...efault': fn default() -> Self - [105; 123) 'Defaul...ault()': S - [133; 135) 's2': S - [138; 148) 'S::default': fn default() -> Self - [138; 150) 'S::default()': S - [160; 162) 's3': S - [165; 188) '() -> Self - [165; 190) ' { - fn make() -> T; -} -struct S; -impl Trait for S {} -struct G; -impl Trait for G {} -fn test() { - let a = S::make(); - let b = G::::make(); - let c: f64 = G::make(); -} -"#), - @r###" - [127; 211) '{ ...e(); }': () - [137; 138) 'a': u32 - [141; 148) 'S::make': fn make() -> T - [141; 150) 'S::make()': u32 - [160; 161) 'b': u64 - [164; 178) 'G::::make': fn make, u64>() -> T - [164; 180) 'G::, f64>() -> T - [199; 208) 'G::make()': f64 - "### - ); -} - -#[test] -fn infer_trait_assoc_method_generics_2() { - assert_snapshot!( - infer(r#" -trait Trait { - fn make() -> (T, U); -} -struct S; -impl Trait for S {} -struct G; -impl Trait for G {} -fn test() { - let a = S::make::(); - let b: (_, i64) = S::make(); - let c = G::::make::(); - let d: (u32, _) = G::make::(); - let e: (u32, i64) = G::make(); -} -"#), - @r###" - [135; 313) '{ ...e(); }': () - [145; 146) 'a': (u32, i64) - [149; 163) 'S::make::': fn make() -> (T, U) - [149; 165) 'S::mak...i64>()': (u32, i64) - [175; 176) 'b': (u32, i64) - [189; 196) 'S::make': fn make() -> (T, U) - [189; 198) 'S::make()': (u32, i64) - [208; 209) 'c': (u32, i64) - [212; 233) 'G::': fn make, u32, i64>() -> (T, U) - [212; 235) 'G::()': (u32, i64) - [245; 246) 'd': (u32, i64) - [259; 273) 'G::make::': fn make, u32, i64>() -> (T, U) - [259; 275) 'G::mak...i64>()': (u32, i64) - [285; 286) 'e': (u32, i64) - [301; 308) 'G::make': fn make, u32, i64>() -> (T, U) - [301; 310) 'G::make()': (u32, i64) - "### - ); -} - -#[test] -fn infer_trait_assoc_method_generics_3() { - assert_snapshot!( - infer(r#" -trait Trait { - fn make() -> (Self, T); -} -struct S; -impl Trait for S {} -fn test() { - let a = S::make(); -} -"#), - @r###" - [101; 127) '{ ...e(); }': () - [111; 112) 'a': (S, i64) - [115; 122) 'S::make': fn make, i64>() -> (Self, T) - [115; 124) 'S::make()': (S, i64) - "### - ); -} - -#[test] -fn infer_trait_assoc_method_generics_4() { - assert_snapshot!( - infer(r#" -trait Trait { - fn make() -> (Self, T); -} -struct S; -impl Trait for S {} -impl Trait for S {} -fn test() { - let a: (S, _) = S::make(); - let b: (_, i32) = S::make(); -} -"#), - @r###" - [131; 203) '{ ...e(); }': () - [141; 142) 'a': (S, i64) - [158; 165) 'S::make': fn make, i64>() -> (Self, T) - [158; 167) 'S::make()': (S, i64) - [177; 178) 'b': (S, i32) - [191; 198) 'S::make': fn make, i32>() -> (Self, T) - [191; 200) 'S::make()': (S, i32) - "### - ); -} - -#[test] -fn infer_trait_assoc_method_generics_5() { - assert_snapshot!( - infer(r#" -trait Trait { - fn make() -> (Self, T, U); -} -struct S; -impl Trait for S {} -fn test() { - let a = >::make::(); - let b: (S, _, _) = Trait::::make::(); -} -"#), - @r###" - [107; 211) '{ ...>(); }': () - [117; 118) 'a': (S, i64, u8) - [121; 150) '': fn make, i64, u8>() -> (Self, T, U) - [121; 152) '()': (S, i64, u8) - [162; 163) 'b': (S, i64, u8) - [182; 206) 'Trait:...::': fn make, i64, u8>() -> (Self, T, U) - [182; 208) 'Trait:...()': (S, i64, u8) - "### - ); -} - -#[test] -fn infer_from_bound_1() { - assert_snapshot!( - infer(r#" -trait Trait {} -struct S(T); -impl Trait for S {} -fn foo>(t: T) {} -fn test() { - let s = S(unknown); - foo(s); -} -"#), - @r###" - [86; 87) 't': T - [92; 94) '{}': () - [105; 144) '{ ...(s); }': () - [115; 116) 's': S - [119; 120) 'S': S(T) -> S - [119; 129) 'S(unknown)': S - [121; 128) 'unknown': u32 - [135; 138) 'foo': fn foo>(T) -> () - [135; 141) 'foo(s)': () - [139; 140) 's': S - "### - ); -} - -#[test] -fn infer_from_bound_2() { - assert_snapshot!( - infer(r#" -trait Trait {} -struct S(T); -impl Trait for S {} -fn foo>(t: T) -> U {} -fn test() { - let s = S(unknown); - let x: u32 = foo(s); -} -"#), - @r###" - [87; 88) 't': T - [98; 100) '{}': () - [111; 163) '{ ...(s); }': () - [121; 122) 's': S - [125; 126) 'S': S(T) -> S - [125; 135) 'S(unknown)': S - [127; 134) 'unknown': u32 - [145; 146) 'x': u32 - [154; 157) 'foo': fn foo>(T) -> U - [154; 160) 'foo(s)': u32 - [158; 159) 's': S - "### - ); -} - -#[test] -fn infer_call_trait_method_on_generic_param_1() { - assert_snapshot!( - infer(r#" -trait Trait { - fn method(&self) -> u32; -} -fn test(t: T) { - t.method(); -} -"#), - @r###" - [30; 34) 'self': &Self - [64; 65) 't': T - [70; 89) '{ ...d(); }': () - [76; 77) 't': T - [76; 86) 't.method()': u32 - "### - ); -} - -#[test] -fn infer_call_trait_method_on_generic_param_2() { - assert_snapshot!( - infer(r#" -trait Trait { - fn method(&self) -> T; -} -fn test>(t: T) { - t.method(); -} -"#), - @r###" - [33; 37) 'self': &Self - [71; 72) 't': T - [77; 96) '{ ...d(); }': () - [83; 84) 't': T - [83; 93) 't.method()': [missing name] - "### - ); -} - -#[test] -fn infer_with_multiple_trait_impls() { - assert_snapshot!( - infer(r#" -trait Into { - fn into(self) -> T; -} -struct S; -impl Into for S {} -impl Into for S {} -fn test() { - let x: u32 = S.into(); - let y: u64 = S.into(); - let z = Into::::into(S); -} -"#), - @r###" - [29; 33) 'self': Self - [111; 202) '{ ...(S); }': () - [121; 122) 'x': u32 - [130; 131) 'S': S - [130; 138) 'S.into()': u32 - [148; 149) 'y': u64 - [157; 158) 'S': S - [157; 165) 'S.into()': u64 - [175; 176) 'z': u64 - [179; 196) 'Into::...::into': fn into(Self) -> T - [179; 199) 'Into::...nto(S)': u64 - [197; 198) 'S': S - "### - ); -} - -#[test] -fn infer_project_associated_type() { - // y, z, a don't yet work because of https://github.com/rust-lang/chalk/issues/234 - assert_snapshot!( - infer(r#" -trait Iterable { - type Item; -} -struct S; -impl Iterable for S { type Item = u32; } -fn test() { - let x: ::Item = 1; - let y: ::Item = no_matter; - let z: T::Item = no_matter; - let a: ::Item = no_matter; -} -"#), - @r###" - [108; 261) '{ ...ter; }': () - [118; 119) 'x': u32 - [145; 146) '1': u32 - [156; 157) 'y': {unknown} - [183; 192) 'no_matter': {unknown} - [202; 203) 'z': {unknown} - [215; 224) 'no_matter': {unknown} - [234; 235) 'a': {unknown} - [249; 258) 'no_matter': {unknown} - "### - ); -} - -#[test] -fn infer_return_associated_type() { - assert_snapshot!( - infer(r#" -trait Iterable { - type Item; -} -struct S; -impl Iterable for S { type Item = u32; } -fn foo1(t: T) -> T::Item {} -fn foo2(t: T) -> ::Item {} -fn foo3(t: T) -> ::Item {} -fn test() { - let x = foo1(S); - let y = foo2(S); - let z = foo3(S); -} -"#), - @r###" - [106; 107) 't': T - [123; 125) '{}': () - [147; 148) 't': T - [178; 180) '{}': () - [202; 203) 't': T - [221; 223) '{}': () - [234; 300) '{ ...(S); }': () - [244; 245) 'x': u32 - [248; 252) 'foo1': fn foo1(T) -> ::Item - [248; 255) 'foo1(S)': u32 - [253; 254) 'S': S - [265; 266) 'y': u32 - [269; 273) 'foo2': fn foo2(T) -> ::Item - [269; 276) 'foo2(S)': u32 - [274; 275) 'S': S - [286; 287) 'z': u32 - [290; 294) 'foo3': fn foo3(T) -> ::Item - [290; 297) 'foo3(S)': u32 - [295; 296) 'S': S - "### - ); -} - -#[test] -fn infer_associated_type_bound() { - assert_snapshot!( - infer(r#" -trait Iterable { - type Item; -} -fn test>() { - let y: T::Item = unknown; -} -"#), - @r###" - [67; 100) '{ ...own; }': () - [77; 78) 'y': {unknown} - [90; 97) 'unknown': {unknown} - "### - ); -} - -#[test] -fn infer_const_body() { - assert_snapshot!( - infer(r#" -const A: u32 = 1 + 1; -static B: u64 = { let x = 1; x }; -"#), - @r###" - [16; 17) '1': u32 - [16; 21) '1 + 1': u32 - [20; 21) '1': u32 - [39; 55) '{ let ...1; x }': u64 - [45; 46) 'x': u64 - [49; 50) '1': u64 - [52; 53) 'x': u64 - "### - ); -} - -#[test] -fn tuple_struct_fields() { - assert_snapshot!( - infer(r#" -struct S(i32, u64); -fn test() -> u64 { - let a = S(4, 6); - let b = a.0; - a.1 -} -"#), - @r###" - [38; 87) '{ ... a.1 }': u64 - [48; 49) 'a': S - [52; 53) 'S': S(i32, u64) -> S - [52; 59) 'S(4, 6)': S - [54; 55) '4': i32 - [57; 58) '6': u64 - [69; 70) 'b': i32 - [73; 74) 'a': S - [73; 76) 'a.0': i32 - [82; 83) 'a': S - [82; 85) 'a.1': u64 - "### - ); -} - -#[test] -fn tuple_struct_with_fn() { - assert_snapshot!( - infer(r#" -struct S(fn(u32) -> u64); -fn test() -> u64 { - let a = S(|i| 2*i); - let b = a.0(4); - a.0(2) -} -"#), - @r###" - [44; 102) '{ ...0(2) }': u64 - [54; 55) 'a': S - [58; 59) 'S': S(fn(u32) -> u64) -> S - [58; 68) 'S(|i| 2*i)': S - [60; 67) '|i| 2*i': |i32| -> i32 - [61; 62) 'i': i32 - [64; 65) '2': i32 - [64; 67) '2*i': i32 - [66; 67) 'i': i32 - [78; 79) 'b': u64 - [82; 83) 'a': S - [82; 85) 'a.0': fn(u32) -> u64 - [82; 88) 'a.0(4)': u64 - [86; 87) '4': u32 - [94; 95) 'a': S - [94; 97) 'a.0': fn(u32) -> u64 - [94; 100) 'a.0(2)': u64 - [98; 99) '2': u32 - "### - ); -} - -#[test] -fn indexing_arrays() { - assert_snapshot!( - infer("fn main() { &mut [9][2]; }"), - @r###" - [10; 26) '{ &mut...[2]; }': () - [12; 23) '&mut [9][2]': &mut {unknown} - [17; 20) '[9]': [i32;_] - [17; 23) '[9][2]': {unknown} - [18; 19) '9': i32 - [21; 22) '2': i32 - "### - ) -} - -#[test] -fn infer_macros_expanded() { - assert_snapshot!( - infer(r#" -struct Foo(Vec); - -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); - -#[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_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)); -} - -#[ignore] -#[test] -fn method_resolution_trait_before_autoref() { - let t = type_at( - r#" -//- /main.rs -trait Trait { fn foo(self) -> u128; } -struct S; -impl S { fn foo(&self) -> i8 { 0 } } -impl Trait for S { fn foo(self) -> u128 { 0 } } -fn test() { S.foo()<|>; } -"#, - ); - assert_eq!(t, "u128"); -} - -#[ignore] -#[test] -fn method_resolution_by_value_before_autoref() { - let t = type_at( - r#" -//- /main.rs -trait Clone { fn clone(&self) -> Self; } -struct S; -impl Clone for S {} -impl Clone for &S {} -fn test() { (S.clone(), (&S).clone(), (&&S).clone())<|>; } -"#, - ); - assert_eq!(t, "(S, S, &S)"); -} - -#[test] -fn method_resolution_trait_before_autoderef() { - let t = type_at( - r#" -//- /main.rs -trait Trait { fn foo(self) -> u128; } -struct S; -impl S { fn foo(self) -> i8 { 0 } } -impl Trait for &S { fn foo(self) -> u128 { 0 } } -fn test() { (&S).foo()<|>; } -"#, - ); - assert_eq!(t, "u128"); -} - -#[test] -fn method_resolution_impl_before_trait() { - let t = type_at( - r#" -//- /main.rs -trait Trait { fn foo(self) -> u128; } -struct S; -impl S { fn foo(self) -> i8 { 0 } } -impl Trait for S { fn foo(self) -> u128 { 0 } } -fn test() { S.foo()<|>; } -"#, - ); - assert_eq!(t, "i8"); -} - -#[test] -fn method_resolution_trait_autoderef() { - let t = type_at( - r#" -//- /main.rs -trait Trait { fn foo(self) -> u128; } -struct S; -impl Trait for S { fn foo(self) -> u128 { 0 } } -fn test() { (&S).foo()<|>; } -"#, - ); - assert_eq!(t, "u128"); -} - -#[test] -fn method_resolution_trait_from_prelude() { - let (db, pos) = TestDB::with_position( - r#" -//- /main.rs crate:main deps:other_crate -struct S; -impl Clone for S {} - -fn test() { - S.clone()<|>; -} - -//- /lib.rs crate:other_crate -#[prelude_import] use foo::*; - -mod foo { - trait Clone { - fn clone(&self) -> Self; - } -} -"#, - ); - assert_eq!("S", type_at_pos(&db, pos)); -} - -#[test] -fn method_resolution_where_clause_for_unknown_trait() { - // The blanket impl shouldn't apply because we can't even resolve UnknownTrait - let t = type_at( - r#" -//- /main.rs -trait Trait { fn foo(self) -> u128; } -struct S; -impl Trait for T where T: UnknownTrait {} -fn test() { (&S).foo()<|>; } -"#, - ); - assert_eq!(t, "{unknown}"); -} - -#[test] -fn method_resolution_where_clause_not_met() { - // The blanket impl shouldn't apply because we can't prove S: Clone - let t = type_at( - r#" -//- /main.rs -trait Clone {} -trait Trait { fn foo(self) -> u128; } -struct S; -impl Trait for T where T: Clone {} -fn test() { (&S).foo()<|>; } -"#, - ); - // This is also to make sure that we don't resolve to the foo method just - // because that's the only method named foo we can find, which would make - // the below tests not work - assert_eq!(t, "{unknown}"); -} - -#[test] -fn method_resolution_where_clause_inline_not_met() { - // The blanket impl shouldn't apply because we can't prove S: Clone - let t = type_at( - r#" -//- /main.rs -trait Clone {} -trait Trait { fn foo(self) -> u128; } -struct S; -impl Trait for T {} -fn test() { (&S).foo()<|>; } -"#, - ); - assert_eq!(t, "{unknown}"); -} - -#[test] -fn method_resolution_where_clause_1() { - let t = type_at( - r#" -//- /main.rs -trait Clone {} -trait Trait { fn foo(self) -> u128; } -struct S; -impl Clone for S {} -impl Trait for T where T: Clone {} -fn test() { S.foo()<|>; } -"#, - ); - assert_eq!(t, "u128"); -} - -#[test] -fn method_resolution_where_clause_2() { - let t = type_at( - r#" -//- /main.rs -trait Into { fn into(self) -> T; } -trait From { fn from(other: T) -> Self; } -struct S1; -struct S2; -impl From for S1 {} -impl Into for T where U: From {} -fn test() { S2.into()<|>; } -"#, - ); - assert_eq!(t, "{unknown}"); -} - -#[test] -fn method_resolution_where_clause_inline() { - let t = type_at( - r#" -//- /main.rs -trait Into { fn into(self) -> T; } -trait From { fn from(other: T) -> Self; } -struct S1; -struct S2; -impl From for S1 {} -impl> Into for T {} -fn test() { S2.into()<|>; } -"#, - ); - assert_eq!(t, "{unknown}"); -} - -#[test] -fn method_resolution_encountering_fn_type() { - type_at( - r#" -//- /main.rs -fn foo() {} -trait FnOnce { fn call(self); } -fn test() { foo.call()<|>; } -"#, - ); -} - -#[test] -fn method_resolution_slow() { - // this can get quite slow if we set the solver size limit too high - let t = type_at( - r#" -//- /main.rs -trait SendX {} - -struct S1; impl SendX for S1 {} -struct S2; impl SendX for S2 {} -struct U1; - -trait Trait { fn method(self); } - -struct X1 {} -impl SendX for X1 where A: SendX, B: SendX {} - -struct S {} - -trait FnX {} - -impl Trait for S where C: FnX, B: SendX {} - -fn test() { (S {}).method()<|>; } -"#, - ); - assert_eq!(t, "()"); -} - -#[test] -fn shadowing_primitive() { - let t = type_at( - r#" -//- /main.rs -struct i32; -struct Foo; - -impl i32 { fn foo(&self) -> Foo { Foo } } - -fn main() { - let x: i32 = i32; - x.foo()<|>; -}"#, - ); - assert_eq!(t, "Foo"); -} - -#[test] -fn deref_trait() { - let t = type_at( - r#" -//- /main.rs -#[lang = "deref"] -trait Deref { - type Target; - fn deref(&self) -> &Self::Target; -} - -struct Arc; -impl Deref for Arc { - type Target = T; -} - -struct S; -impl S { - fn foo(&self) -> u128 {} -} - -fn test(s: Arc) { - (*s, s.foo())<|>; -} -"#, - ); - assert_eq!(t, "(S, u128)"); -} - -#[test] -fn deref_trait_with_inference_var() { - let t = type_at( - r#" -//- /main.rs -#[lang = "deref"] -trait Deref { - type Target; - fn deref(&self) -> &Self::Target; -} - -struct Arc; -fn new_arc() -> Arc {} -impl Deref for Arc { - type Target = T; -} - -struct S; -fn foo(a: Arc) {} - -fn test() { - let a = new_arc(); - let b = (*a)<|>; - foo(a); -} -"#, - ); - assert_eq!(t, "S"); -} - -#[test] -fn deref_trait_infinite_recursion() { - let t = type_at( - r#" -//- /main.rs -#[lang = "deref"] -trait Deref { - type Target; - fn deref(&self) -> &Self::Target; -} - -struct S; - -impl Deref for S { - type Target = S; -} - -fn test(s: S) { - s.foo()<|>; -} -"#, - ); - assert_eq!(t, "{unknown}"); -} - -#[test] -fn deref_trait_with_question_mark_size() { - let t = type_at( - r#" -//- /main.rs -#[lang = "deref"] -trait Deref { - type Target; - fn deref(&self) -> &Self::Target; -} - -struct Arc; -impl Deref for Arc { - type Target = T; -} - -struct S; -impl S { - fn foo(&self) -> u128 {} -} - -fn test(s: Arc) { - (*s, s.foo())<|>; -} -"#, - ); - assert_eq!(t, "(S, u128)"); -} - -#[test] -fn obligation_from_function_clause() { - let t = type_at( - r#" -//- /main.rs -struct S; - -trait Trait {} -impl Trait for S {} - -fn foo, U>(t: T) -> U {} - -fn test(s: S) { - foo(s)<|>; -} -"#, - ); - assert_eq!(t, "u32"); -} - -#[test] -fn obligation_from_method_clause() { - let t = type_at( - r#" -//- /main.rs -struct S; - -trait Trait {} -impl Trait for S {} - -struct O; -impl O { - fn foo, U>(&self, t: T) -> U {} -} - -fn test() { - O.foo(S)<|>; -} -"#, - ); - assert_eq!(t, "isize"); -} - -#[test] -fn obligation_from_self_method_clause() { - let t = type_at( - r#" -//- /main.rs -struct S; - -trait Trait {} -impl Trait for S {} - -impl S { - fn foo(&self) -> U where Self: Trait {} -} - -fn test() { - S.foo()<|>; -} -"#, - ); - assert_eq!(t, "i64"); -} - -#[test] -fn obligation_from_impl_clause() { - let t = type_at( - r#" -//- /main.rs -struct S; - -trait Trait {} -impl Trait<&str> for S {} - -struct O; -impl> O { - fn foo(&self) -> U {} -} - -fn test(o: O) { - o.foo()<|>; -} -"#, - ); - assert_eq!(t, "&str"); -} - -#[test] -fn generic_param_env_1() { - let t = type_at( - r#" -//- /main.rs -trait Clone {} -trait Trait { fn foo(self) -> u128; } -struct S; -impl Clone for S {} -impl Trait for T where T: Clone {} -fn test(t: T) { t.foo()<|>; } -"#, - ); - assert_eq!(t, "u128"); -} - -#[test] -fn generic_param_env_1_not_met() { - let t = type_at( - r#" -//- /main.rs -trait Clone {} -trait Trait { fn foo(self) -> u128; } -struct S; -impl Clone for S {} -impl Trait for T where T: Clone {} -fn test(t: T) { t.foo()<|>; } -"#, - ); - assert_eq!(t, "{unknown}"); -} - -#[test] -fn generic_param_env_2() { - let t = type_at( - r#" -//- /main.rs -trait Trait { fn foo(self) -> u128; } -struct S; -impl Trait for S {} -fn test(t: T) { t.foo()<|>; } -"#, - ); - assert_eq!(t, "u128"); -} - -#[test] -fn generic_param_env_2_not_met() { - let t = type_at( - r#" -//- /main.rs -trait Trait { fn foo(self) -> u128; } -struct S; -impl Trait for S {} -fn test(t: T) { t.foo()<|>; } -"#, - ); - assert_eq!(t, "{unknown}"); -} - -#[test] -fn generic_param_env_deref() { - let t = type_at( - r#" -//- /main.rs -#[lang = "deref"] -trait Deref { - type Target; -} -trait Trait {} -impl Deref for T where T: Trait { - type Target = i128; -} -fn test(t: T) { (*t)<|>; } -"#, - ); - assert_eq!(t, "i128"); -} - -#[test] -fn associated_type_placeholder() { - let t = type_at( - r#" -//- /main.rs -pub trait ApplyL { - type Out; -} - -pub struct RefMutL; - -impl ApplyL for RefMutL { - type Out = ::Out; -} - -fn test() { - let y: as ApplyL>::Out = no_matter; - y<|>; -} -"#, - ); - // inside the generic function, the associated type gets normalized to a placeholder `ApplL::Out` [https://rust-lang.github.io/rustc-guide/traits/associated-types.html#placeholder-associated-types]. - // FIXME: fix type parameter names going missing when going through Chalk - assert_eq!(t, "ApplyL::Out<[missing name]>"); -} - -#[test] -fn associated_type_placeholder_2() { - let t = type_at( - r#" -//- /main.rs -pub trait ApplyL { - type Out; -} -fn foo(t: T) -> ::Out; - -fn test(t: T) { - let y = foo(t); - y<|>; -} -"#, - ); - // FIXME here Chalk doesn't normalize the type to a placeholder. I think we - // need to add a rule like Normalize(::Out -> ApplyL::Out) - // to the trait env ourselves here; probably Chalk can't do this by itself. - // assert_eq!(t, "ApplyL::Out<[missing name]>"); - assert_eq!(t, "{unknown}"); -} - -#[test] -fn impl_trait() { - assert_snapshot!( - infer(r#" -trait Trait { - fn foo(&self) -> T; - fn foo2(&self) -> i64; -} -fn bar() -> impl Trait {} - -fn test(x: impl Trait, y: &impl Trait) { - x; - y; - let z = bar(); - x.foo(); - y.foo(); - z.foo(); - x.foo2(); - y.foo2(); - z.foo2(); -} -"#), - @r###" - [30; 34) 'self': &Self - [55; 59) 'self': &Self - [99; 101) '{}': () - [111; 112) 'x': impl Trait - [131; 132) 'y': &impl Trait - [152; 269) '{ ...2(); }': () - [158; 159) 'x': impl Trait - [165; 166) 'y': &impl Trait - [176; 177) 'z': impl Trait - [180; 183) 'bar': fn bar() -> impl Trait - [180; 185) 'bar()': impl Trait - [191; 192) 'x': impl Trait - [191; 198) 'x.foo()': u64 - [204; 205) 'y': &impl Trait - [204; 211) 'y.foo()': u64 - [217; 218) 'z': impl Trait - [217; 224) 'z.foo()': u64 - [230; 231) 'x': impl Trait - [230; 238) 'x.foo2()': i64 - [244; 245) 'y': &impl Trait - [244; 252) 'y.foo2()': i64 - [258; 259) 'z': impl Trait - [258; 266) 'z.foo2()': i64 - "### - ); -} - -#[test] -fn dyn_trait() { - assert_snapshot!( - infer(r#" -trait Trait { - fn foo(&self) -> T; - fn foo2(&self) -> i64; -} -fn bar() -> dyn Trait {} - -fn test(x: dyn Trait, y: &dyn Trait) { - x; - y; - let z = bar(); - x.foo(); - y.foo(); - z.foo(); - x.foo2(); - y.foo2(); - z.foo2(); -} -"#), - @r###" - [30; 34) 'self': &Self - [55; 59) 'self': &Self - [98; 100) '{}': () - [110; 111) 'x': dyn Trait - [129; 130) 'y': &dyn Trait - [149; 266) '{ ...2(); }': () - [155; 156) 'x': dyn Trait - [162; 163) 'y': &dyn Trait - [173; 174) 'z': dyn Trait - [177; 180) 'bar': fn bar() -> dyn Trait - [177; 182) 'bar()': dyn Trait - [188; 189) 'x': dyn Trait - [188; 195) 'x.foo()': u64 - [201; 202) 'y': &dyn Trait - [201; 208) 'y.foo()': u64 - [214; 215) 'z': dyn Trait - [214; 221) 'z.foo()': u64 - [227; 228) 'x': dyn Trait - [227; 235) 'x.foo2()': i64 - [241; 242) 'y': &dyn Trait - [241; 249) 'y.foo2()': i64 - [255; 256) 'z': dyn Trait - [255; 263) 'z.foo2()': i64 - "### - ); -} - -#[test] -fn dyn_trait_bare() { - assert_snapshot!( - infer(r#" -trait Trait { - fn foo(&self) -> u64; -} -fn bar() -> Trait {} - -fn test(x: Trait, y: &Trait) -> u64 { - x; - y; - let z = bar(); - x.foo(); - y.foo(); - z.foo(); -} -"#), - @r###" - [27; 31) 'self': &Self - [61; 63) '{}': () - [73; 74) 'x': dyn Trait - [83; 84) 'y': &dyn Trait - [101; 176) '{ ...o(); }': () - [107; 108) 'x': dyn Trait - [114; 115) 'y': &dyn Trait - [125; 126) 'z': dyn Trait - [129; 132) 'bar': fn bar() -> dyn Trait - [129; 134) 'bar()': dyn Trait - [140; 141) 'x': dyn Trait - [140; 147) 'x.foo()': u64 - [153; 154) 'y': &dyn Trait - [153; 160) 'y.foo()': u64 - [166; 167) 'z': dyn Trait - [166; 173) 'z.foo()': u64 - "### - ); -} - -#[test] -fn weird_bounds() { - assert_snapshot!( - infer(r#" -trait Trait {} -fn test() { - let a: impl Trait + 'lifetime = foo; - let b: impl 'lifetime = foo; - let b: impl (Trait) = foo; - let b: impl ('lifetime) = foo; - let d: impl ?Sized = foo; - let e: impl Trait + ?Sized = foo; -} -"#), - @r###" - [26; 237) '{ ...foo; }': () - [36; 37) 'a': impl Trait + {error} - [64; 67) 'foo': impl Trait + {error} - [77; 78) 'b': impl {error} - [97; 100) 'foo': impl {error} - [110; 111) 'b': impl Trait - [128; 131) 'foo': impl Trait - [141; 142) 'b': impl {error} - [163; 166) 'foo': impl {error} - [176; 177) 'd': impl {error} - [193; 196) 'foo': impl {error} - [206; 207) 'e': impl Trait + {error} - [231; 234) 'foo': impl Trait + {error} - "### - ); -} - -#[test] -fn assoc_type_bindings() { - assert_snapshot!( - infer(r#" -trait Trait { - type Type; -} - -fn get(t: T) -> ::Type {} -fn get2>(t: T) -> U {} -fn set>(t: T) -> T {t} - -struct S; -impl Trait for S { type Type = T; } - -fn test>(x: T, y: impl Trait) { - get(x); - get2(x); - get(y); - get2(y); - get(set(S)); - get2(set(S)); - get2(S::); -} -"#), - @r###" - [50; 51) 't': T - [78; 80) '{}': () - [112; 113) 't': T - [123; 125) '{}': () - [155; 156) 't': T - [166; 169) '{t}': T - [167; 168) 't': T - [257; 258) 'x': T - [263; 264) 'y': impl Trait - [290; 398) '{ ...r>); }': () - [296; 299) 'get': fn get(T) -> ::Type - [296; 302) 'get(x)': {unknown} - [300; 301) 'x': T - [308; 312) 'get2': fn get2<{unknown}, T>(T) -> U - [308; 315) 'get2(x)': {unknown} - [313; 314) 'x': T - [321; 324) 'get': fn get>(T) -> ::Type - [321; 327) 'get(y)': {unknown} - [325; 326) 'y': impl Trait - [333; 337) 'get2': fn get2<{unknown}, impl Trait>(T) -> U - [333; 340) 'get2(y)': {unknown} - [338; 339) 'y': impl Trait - [346; 349) 'get': fn get>(T) -> ::Type - [346; 357) 'get(set(S))': u64 - [350; 353) 'set': fn set>(T) -> T - [350; 356) 'set(S)': S - [354; 355) 'S': S - [363; 367) 'get2': fn get2>(T) -> U - [363; 375) 'get2(set(S))': u64 - [368; 371) 'set': fn set>(T) -> T - [368; 374) 'set(S)': S - [372; 373) 'S': S - [381; 385) 'get2': fn get2>(T) -> U - [381; 395) 'get2(S::)': str - [386; 394) 'S::': S - "### - ); -} - -#[test] -fn impl_trait_assoc_binding_projection_bug() { - let (db, pos) = TestDB::with_position( - r#" -//- /main.rs crate:main deps:std -pub trait Language { - type Kind; -} -pub enum RustLanguage {} -impl Language for RustLanguage { - type Kind = SyntaxKind; -} -struct SyntaxNode {} -fn foo() -> impl Iterator> {} - -trait Clone { - fn clone(&self) -> Self; -} - -fn api_walkthrough() { - for node in foo() { - node.clone()<|>; - } -} - -//- /std.rs crate:std -#[prelude_import] use iter::*; -mod iter { - trait IntoIterator { - type Item; - } - trait Iterator { - type Item; - } - impl IntoIterator for T { - type Item = ::Item; - } -} -"#, - ); - assert_eq!("{unknown}", type_at_pos(&db, pos)); -} - -#[test] -fn projection_eq_within_chalk() { - // std::env::set_var("CHALK_DEBUG", "1"); - assert_snapshot!( - infer(r#" -trait Trait1 { - type Type; -} -trait Trait2 { - fn foo(self) -> T; -} -impl Trait2 for U where U: Trait1 {} - -fn test>(x: T) { - x.foo(); -} -"#), - @r###" - [62; 66) 'self': Self - [164; 165) 'x': T - [170; 186) '{ ...o(); }': () - [176; 177) 'x': T - [176; 183) 'x.foo()': {unknown} - "### - ); -} - -#[test] -fn where_clause_trait_in_scope_for_method_resolution() { - let t = type_at( - r#" -//- /main.rs -mod foo { - trait Trait { - fn foo(&self) -> u32 {} - } -} - -fn test(x: T) { - x.foo()<|>; -} -"#, - ); - assert_eq!(t, "u32"); -} - -#[test] -fn super_trait_method_resolution() { - assert_snapshot!( - infer(r#" -mod foo { - trait SuperTrait { - fn foo(&self) -> u32 {} - } -} -trait Trait1: foo::SuperTrait {} -trait Trait2 where Self: foo::SuperTrait {} - -fn test(x: T, y: U) { - x.foo(); - y.foo(); -} -"#), - @r###" - [50; 54) 'self': &Self - [63; 65) '{}': () - [182; 183) 'x': T - [188; 189) 'y': U - [194; 223) '{ ...o(); }': () - [200; 201) 'x': T - [200; 207) 'x.foo()': u32 - [213; 214) 'y': U - [213; 220) 'y.foo()': u32 - "### - ); -} - -#[test] -fn super_trait_cycle() { - // This just needs to not crash - assert_snapshot!( - infer(r#" -trait A: B {} -trait B: A {} - -fn test(x: T) { - x.foo(); -} -"#), - @r###" - [44; 45) 'x': T - [50; 66) '{ ...o(); }': () - [56; 57) 'x': T - [56; 63) 'x.foo()': {unknown} - "### - ); -} - -#[test] -fn super_trait_assoc_type_bounds() { - assert_snapshot!( - infer(r#" -trait SuperTrait { type Type; } -trait Trait where Self: SuperTrait {} - -fn get2>(t: T) -> U {} -fn set>(t: T) -> T {t} - -struct S; -impl SuperTrait for S { type Type = T; } -impl Trait for S {} - -fn test() { - get2(set(S)); -} -"#), - @r###" - [103; 104) 't': T - [114; 116) '{}': () - [146; 147) 't': T - [157; 160) '{t}': T - [158; 159) 't': T - [259; 280) '{ ...S)); }': () - [265; 269) 'get2': fn get2>(T) -> U - [265; 277) 'get2(set(S))': u64 - [270; 273) 'set': fn set>(T) -> T - [270; 276) 'set(S)': S - [274; 275) 'S': S - "### - ); -} - -#[test] -fn fn_trait() { - assert_snapshot!( - infer(r#" -trait FnOnce { - type Output; - - fn call_once(self, args: Args) -> >::Output; -} - -fn test u128>(f: F) { - f.call_once((1, 2)); -} -"#), - @r###" - [57; 61) 'self': Self - [63; 67) 'args': Args - [150; 151) 'f': F - [156; 184) '{ ...2)); }': () - [162; 163) 'f': F - [162; 181) 'f.call...1, 2))': {unknown} - [174; 180) '(1, 2)': (u32, u64) - [175; 176) '1': u32 - [178; 179) '2': u64 - "### - ); -} - -#[test] -fn closure_1() { - assert_snapshot!( - infer(r#" -#[lang = "fn_once"] -trait FnOnce { - type Output; -} - -enum Option { Some(T), None } -impl Option { - fn map U>(self, f: F) -> Option {} -} - -fn test() { - let x = Option::Some(1u32); - x.map(|v| v + 1); - x.map(|_v| 1u64); - let y: Option = x.map(|_v| 1); -} -"#), - @r###" - [148; 152) 'self': Option - [154; 155) 'f': F - [173; 175) '{}': () - [189; 308) '{ ... 1); }': () - [199; 200) 'x': Option - [203; 215) 'Option::Some': Some(T) -> Option - [203; 221) 'Option...(1u32)': Option - [216; 220) '1u32': u32 - [227; 228) 'x': Option - [227; 243) 'x.map(...v + 1)': Option - [233; 242) '|v| v + 1': |u32| -> u32 - [234; 235) 'v': u32 - [237; 238) 'v': u32 - [237; 242) 'v + 1': u32 - [241; 242) '1': u32 - [249; 250) 'x': Option - [249; 265) 'x.map(... 1u64)': Option - [255; 264) '|_v| 1u64': |u32| -> u64 - [256; 258) '_v': u32 - [260; 264) '1u64': u64 - [275; 276) 'y': Option - [292; 293) 'x': Option - [292; 305) 'x.map(|_v| 1)': Option - [298; 304) '|_v| 1': |u32| -> i64 - [299; 301) '_v': u32 - [303; 304) '1': i64 - "### - ); -} - -#[test] -fn closure_2() { - assert_snapshot!( - infer(r#" -trait FnOnce { - type Output; -} - -fn test u64>(f: F) { - f(1); - let g = |v| v + 1; - g(1u64); - let h = |v| 1u128 + v; -} -"#), - @r###" - [73; 74) 'f': F - [79; 155) '{ ...+ v; }': () - [85; 86) 'f': F - [85; 89) 'f(1)': {unknown} - [87; 88) '1': i32 - [99; 100) 'g': |u64| -> i32 - [103; 112) '|v| v + 1': |u64| -> i32 - [104; 105) 'v': u64 - [107; 108) 'v': u64 - [107; 112) 'v + 1': i32 - [111; 112) '1': i32 - [118; 119) 'g': |u64| -> i32 - [118; 125) 'g(1u64)': i32 - [120; 124) '1u64': u64 - [135; 136) 'h': |u128| -> u128 - [139; 152) '|v| 1u128 + v': |u128| -> u128 - [140; 141) 'v': u128 - [143; 148) '1u128': u128 - [143; 152) '1u128 + v': u128 - [151; 152) 'v': u128 - "### - ); -} - -#[test] -fn closure_as_argument_inference_order() { - assert_snapshot!( - infer(r#" -#[lang = "fn_once"] -trait FnOnce { - type Output; -} - -fn foo1 U>(x: T, f: F) -> U {} -fn foo2 U>(f: F, x: T) -> U {} - -struct S; -impl S { - fn method(self) -> u64; - - fn foo1 U>(self, x: T, f: F) -> U {} - fn foo2 U>(self, f: F, x: T) -> U {} -} - -fn test() { - let x1 = foo1(S, |s| s.method()); - let x2 = foo2(|s| s.method(), S); - let x3 = S.foo1(S, |s| s.method()); - let x4 = S.foo2(|s| s.method(), S); -} -"#), - @r###" - [95; 96) 'x': T - [101; 102) 'f': F - [112; 114) '{}': () - [148; 149) 'f': F - [154; 155) 'x': T - [165; 167) '{}': () - [202; 206) 'self': S - [254; 258) 'self': S - [260; 261) 'x': T - [266; 267) 'f': F - [277; 279) '{}': () - [317; 321) 'self': S - [323; 324) 'f': F - [329; 330) 'x': T - [340; 342) '{}': () - [356; 515) '{ ... S); }': () - [366; 368) 'x1': u64 - [371; 375) 'foo1': fn foo1 u64>(T, F) -> U - [371; 394) 'foo1(S...hod())': u64 - [376; 377) 'S': S - [379; 393) '|s| s.method()': |S| -> u64 - [380; 381) 's': S - [383; 384) 's': S - [383; 393) 's.method()': u64 - [404; 406) 'x2': u64 - [409; 413) 'foo2': fn foo2 u64>(F, T) -> U - [409; 432) 'foo2(|...(), S)': u64 - [414; 428) '|s| s.method()': |S| -> u64 - [415; 416) 's': S - [418; 419) 's': S - [418; 428) 's.method()': u64 - [430; 431) 'S': S - [442; 444) 'x3': u64 - [447; 448) 'S': S - [447; 472) 'S.foo1...hod())': u64 - [454; 455) 'S': S - [457; 471) '|s| s.method()': |S| -> u64 - [458; 459) 's': S - [461; 462) 's': S - [461; 471) 's.method()': u64 - [482; 484) 'x4': u64 - [487; 488) 'S': S - [487; 512) 'S.foo2...(), S)': u64 - [494; 508) '|s| s.method()': |S| -> u64 - [495; 496) 's': S - [498; 499) 's': S - [498; 508) 's.method()': u64 - [510; 511) 'S': S - "### - ); -} - -#[test] -fn unselected_projection_in_trait_env_1() { - let t = type_at( - r#" -//- /main.rs -trait Trait { - type Item; -} - -trait Trait2 { - fn foo(&self) -> u32; -} - -fn test() where T::Item: Trait2 { - let x: T::Item = no_matter; - x.foo()<|>; -} -"#, - ); - assert_eq!(t, "u32"); -} - -#[test] -fn unselected_projection_in_trait_env_2() { - let t = type_at( - r#" -//- /main.rs -trait Trait { - type Item; -} - -trait Trait2 { - fn foo(&self) -> u32; -} - -fn test() where T::Item: Trait2, T: Trait, U: Trait<()> { - let x: T::Item = no_matter; - x.foo()<|>; -} -"#, - ); - assert_eq!(t, "u32"); -} - -#[test] -// FIXME this is currently a Salsa panic; it would be nicer if it just returned -// in Unknown, and we should be able to do that once Salsa allows us to handle -// the cycle. But at least it doesn't overflow for now. -#[should_panic] -fn unselected_projection_in_trait_env_cycle_1() { - let t = type_at( - r#" -//- /main.rs -trait Trait { - type Item; -} - -trait Trait2 {} - -fn test() where T: Trait2 { - let x: T::Item = no_matter<|>; -} -"#, - ); - // this is a legitimate cycle - assert_eq!(t, "{unknown}"); -} - -#[test] -// FIXME this is currently a Salsa panic; it would be nicer if it just returned -// in Unknown, and we should be able to do that once Salsa allows us to handle -// the cycle. But at least it doesn't overflow for now. -#[should_panic] -fn unselected_projection_in_trait_env_cycle_2() { - let t = type_at( - r#" -//- /main.rs -trait Trait { - type Item; -} - -fn test() where T: Trait, U: Trait { - let x: T::Item = no_matter<|>; -} -"#, - ); - // this is a legitimate cycle - assert_eq!(t, "{unknown}"); -} - fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { let file = db.parse(pos.file_id).ok().unwrap(); let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); - + let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); let module = db.module_for_file(pos.file_id); - let crate_def_map = db.crate_def_map(module.krate); - for decl in crate_def_map[module.local_id].scope.declarations() { - if let ModuleDefId::FunctionId(func) = decl { - let (_body, source_map) = db.body_with_source_map(func.into()); - if let Some(expr_id) = source_map.node_expr(Source::new(pos.file_id.into(), &expr)) { - let infer = db.infer(func.into()); - let ty = &infer[expr_id]; - return ty.display(db).to_string(); - } - } + let func = *module.child_by_source(db)[keys::FUNCTION] + .get(&InFile::new(pos.file_id.into(), fn_def)) + .unwrap(); + + let (_body, source_map) = db.body_with_source_map(func.into()); + if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { + let infer = db.infer(func.into()); + let ty = &infer[expr_id]; + return ty.display(db).to_string(); } panic!("Can't find expression") } @@ -4696,6 +52,10 @@ fn type_at(content: &str) -> String { } fn infer(content: &str) -> String { + infer_with_mismatches(content, false) +} + +fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let (db, file_id) = TestDB::with_single_file(content); let mut acc = String::new(); @@ -4703,6 +63,7 @@ fn infer(content: &str) -> String { let mut infer_def = |inference_result: Arc, body_source_map: Arc| { let mut types = Vec::new(); + let mut mismatches = Vec::new(); for (pat, ty) in inference_result.type_of_pat.iter() { let syntax_ptr = match body_source_map.pat_syntax(pat) { @@ -4722,6 +83,9 @@ fn infer(content: &str) -> String { None => continue, }; types.push((syntax_ptr, ty)); + if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { + mismatches.push((syntax_ptr, mismatch)); + } } // sort ranges for consistency @@ -4747,6 +111,24 @@ fn infer(content: &str) -> String { ) .unwrap(); } + if include_mismatches { + mismatches.sort_by_key(|(src_ptr, _)| { + (src_ptr.value.range().start(), src_ptr.value.range().end()) + }); + for (src_ptr, mismatch) in &mismatches { + let range = src_ptr.value.range(); + let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; + write!( + acc, + "{}{}: expected {}, got {}\n", + macro_prefix, + range, + mismatch.expected.display(&db), + mismatch.actual.display(&db), + ) + .unwrap(); + } + } }; let module = db.module_for_file(file_id); @@ -4800,7 +182,7 @@ fn visit_module( _ => (), } } - 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 = db.impl_data(impl_id); for &item in impl_data.items.iter() { match item { @@ -4899,60 +281,3 @@ fn no_such_field_diagnostics() { "### ); } - -#[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 - "### - ); -} diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 1530fcc637..7e99a42edc 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -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(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(&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 + "### + ); +} diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs new file mode 100644 index 0000000000..69c695cc8f --- /dev/null +++ b/crates/ra_hir_ty/src/tests/macros.rs @@ -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); + +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); + +#[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); +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, {unknown})", type_at_pos(&db, pos)); +} diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs new file mode 100644 index 0000000000..ce9a06fde4 --- /dev/null +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -0,0 +1,1005 @@ +use super::{infer, type_at, type_at_pos}; +use crate::test_db::TestDB; +use insta::assert_snapshot; +use ra_db::fixture::WithFixture; + +#[test] +fn infer_slice_method() { + assert_snapshot!( + infer(r#" +#[lang = "slice"] +impl [T] { + fn foo(&self) -> T { + loop {} + } +} + +#[lang = "slice_alloc"] +impl [T] {} + +fn test() { + <[_]>::foo(b"foo"); +} +"#), + @r###" + [45; 49) 'self': &[T] + [56; 79) '{ ... }': T + [66; 73) 'loop {}': ! + [71; 73) '{}': () + [133; 160) '{ ...o"); }': () + [139; 149) '<[_]>::foo': fn foo(&[T]) -> T + [139; 157) '<[_]>:..."foo")': u8 + [150; 156) 'b"foo"': &[u8] + "### + ); +} + +#[test] +fn infer_associated_method_struct() { + assert_snapshot!( + infer(r#" +struct A { x: u32 } + +impl A { + fn new() -> A { + A { x: 0 } + } +} +fn test() { + let a = A::new(); + a.x; +} +"#), + @r###" + [49; 75) '{ ... }': A + [59; 69) 'A { x: 0 }': A + [66; 67) '0': u32 + [88; 122) '{ ...a.x; }': () + [98; 99) 'a': A + [102; 108) 'A::new': fn new() -> A + [102; 110) 'A::new()': A + [116; 117) 'a': A + [116; 119) 'a.x': u32 + "### + ); +} + +#[test] +fn infer_associated_method_enum() { + assert_snapshot!( + infer(r#" +enum A { B, C } + +impl A { + pub fn b() -> A { + A::B + } + pub fn c() -> A { + A::C + } +} +fn test() { + let a = A::b(); + a; + let c = A::c(); + c; +} +"#), + @r###" + [47; 67) '{ ... }': A + [57; 61) 'A::B': A + [88; 108) '{ ... }': A + [98; 102) 'A::C': A + [121; 178) '{ ... c; }': () + [131; 132) 'a': A + [135; 139) 'A::b': fn b() -> A + [135; 141) 'A::b()': A + [147; 148) 'a': A + [158; 159) 'c': A + [162; 166) 'A::c': fn c() -> A + [162; 168) 'A::c()': A + [174; 175) 'c': A + "### + ); +} + +#[test] +fn infer_associated_method_with_modules() { + assert_snapshot!( + infer(r#" +mod a { + struct A; + impl A { pub fn thing() -> A { A {} }} +} + +mod b { + struct B; + impl B { pub fn thing() -> u32 { 99 }} + + mod c { + struct C; + impl C { pub fn thing() -> C { C {} }} + } +} +use b::c; + +fn test() { + let x = a::A::thing(); + let y = b::B::thing(); + let z = c::C::thing(); +} +"#), + @r###" + [56; 64) '{ A {} }': A + [58; 62) 'A {}': A + [126; 132) '{ 99 }': u32 + [128; 130) '99': u32 + [202; 210) '{ C {} }': C + [204; 208) 'C {}': C + [241; 325) '{ ...g(); }': () + [251; 252) 'x': A + [255; 266) 'a::A::thing': fn thing() -> A + [255; 268) 'a::A::thing()': A + [278; 279) 'y': u32 + [282; 293) 'b::B::thing': fn thing() -> u32 + [282; 295) 'b::B::thing()': u32 + [305; 306) 'z': C + [309; 320) 'c::C::thing': fn thing() -> C + [309; 322) 'c::C::thing()': C + "### + ); +} + +#[test] +fn infer_associated_method_generics() { + assert_snapshot!( + infer(r#" +struct Gen { + val: T +} + +impl Gen { + pub fn make(val: T) -> Gen { + Gen { val } + } +} + +fn test() { + let a = Gen::make(0u32); +} +"#), + @r###" + [64; 67) 'val': T + [82; 109) '{ ... }': Gen + [92; 103) 'Gen { val }': Gen + [98; 101) 'val': T + [123; 155) '{ ...32); }': () + [133; 134) 'a': Gen + [137; 146) 'Gen::make': fn make(T) -> Gen + [137; 152) 'Gen::make(0u32)': Gen + [147; 151) '0u32': u32 + "### + ); +} + +#[test] +fn infer_associated_method_generics_with_default_param() { + assert_snapshot!( + infer(r#" +struct Gen { + val: T +} + +impl Gen { + pub fn make() -> Gen { + loop { } + } +} + +fn test() { + let a = Gen::make(); +} +"#), + @r###" + [80; 104) '{ ... }': Gen + [90; 98) 'loop { }': ! + [95; 98) '{ }': () + [118; 146) '{ ...e(); }': () + [128; 129) 'a': Gen + [132; 141) 'Gen::make': fn make() -> Gen + [132; 143) 'Gen::make()': Gen + "### + ); +} + +#[test] +fn infer_associated_method_generics_with_default_tuple_param() { + let t = type_at( + r#" +//- /main.rs +struct Gen { + val: T +} + +impl Gen { + pub fn make() -> Gen { + loop { } + } +} + +fn test() { + let a = Gen::make(); + a.val<|>; +} +"#, + ); + assert_eq!(t, "()"); +} + +#[test] +fn infer_associated_method_generics_without_args() { + assert_snapshot!( + infer(r#" +struct Gen { + val: T +} + +impl Gen { + pub fn make() -> Gen { + loop { } + } +} + +fn test() { + let a = Gen::::make(); +} +"#), + @r###" + [76; 100) '{ ... }': Gen + [86; 94) 'loop { }': ! + [91; 94) '{ }': () + [114; 149) '{ ...e(); }': () + [124; 125) 'a': Gen + [128; 144) 'Gen::<...::make': fn make() -> Gen + [128; 146) 'Gen::<...make()': Gen + "### + ); +} + +#[test] +fn infer_associated_method_generics_2_type_params_without_args() { + assert_snapshot!( + infer(r#" +struct Gen { + val: T, + val2: U, +} + +impl Gen { + pub fn make() -> Gen { + loop { } + } +} + +fn test() { + let a = Gen::::make(); +} +"#), + @r###" + [102; 126) '{ ... }': Gen + [112; 120) 'loop { }': ! + [117; 120) '{ }': () + [140; 180) '{ ...e(); }': () + [150; 151) 'a': Gen + [154; 175) 'Gen::<...::make': fn make() -> Gen + [154; 177) 'Gen::<...make()': Gen + "### + ); +} + +#[test] +fn cross_crate_associated_method_call() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:other_crate +fn test() { + let x = other_crate::foo::S::thing(); + x<|>; +} + +//- /lib.rs crate:other_crate +mod foo { + struct S; + impl S { + fn thing() -> i128 {} + } +} +"#, + ); + assert_eq!("i128", type_at_pos(&db, pos)); +} + +#[test] +fn infer_trait_method_simple() { + // the trait implementation is intentionally incomplete -- it shouldn't matter + assert_snapshot!( + infer(r#" +trait Trait1 { + fn method(&self) -> u32; +} +struct S1; +impl Trait1 for S1 {} +trait Trait2 { + fn method(&self) -> i128; +} +struct S2; +impl Trait2 for S2 {} +fn test() { + S1.method(); // -> u32 + S2.method(); // -> i128 +} +"#), + @r###" + [31; 35) 'self': &Self + [110; 114) 'self': &Self + [170; 228) '{ ...i128 }': () + [176; 178) 'S1': S1 + [176; 187) 'S1.method()': u32 + [203; 205) 'S2': S2 + [203; 214) 'S2.method()': i128 + "### + ); +} + +#[test] +fn infer_trait_method_scoped() { + // the trait implementation is intentionally incomplete -- it shouldn't matter + assert_snapshot!( + infer(r#" +struct S; +mod foo { + pub trait Trait1 { + fn method(&self) -> u32; + } + impl Trait1 for super::S {} +} +mod bar { + pub trait Trait2 { + fn method(&self) -> i128; + } + impl Trait2 for super::S {} +} + +mod foo_test { + use super::S; + use super::foo::Trait1; + fn test() { + S.method(); // -> u32 + } +} + +mod bar_test { + use super::S; + use super::bar::Trait2; + fn test() { + S.method(); // -> i128 + } +} +"#), + @r###" + [63; 67) 'self': &Self + [169; 173) 'self': &Self + [300; 337) '{ ... }': () + [310; 311) 'S': S + [310; 320) 'S.method()': u32 + [416; 454) '{ ... }': () + [426; 427) 'S': S + [426; 436) 'S.method()': i128 + "### + ); +} + +#[test] +fn infer_trait_method_generic_1() { + // the trait implementation is intentionally incomplete -- it shouldn't matter + assert_snapshot!( + infer(r#" +trait Trait { + fn method(&self) -> T; +} +struct S; +impl Trait for S {} +fn test() { + S.method(); +} +"#), + @r###" + [33; 37) 'self': &Self + [92; 111) '{ ...d(); }': () + [98; 99) 'S': S + [98; 108) 'S.method()': u32 + "### + ); +} + +#[test] +fn infer_trait_method_generic_more_params() { + // the trait implementation is intentionally incomplete -- it shouldn't matter + assert_snapshot!( + infer(r#" +trait Trait { + fn method1(&self) -> (T1, T2, T3); + fn method2(&self) -> (T3, T2, T1); +} +struct S1; +impl Trait for S1 {} +struct S2; +impl Trait for S2 {} +fn test() { + S1.method1(); // u8, u16, u32 + S1.method2(); // u32, u16, u8 + S2.method1(); // i8, i16, {unknown} + S2.method2(); // {unknown}, i16, i8 +} +"#), + @r###" + [43; 47) 'self': &Self + [82; 86) 'self': &Self + [210; 361) '{ ..., i8 }': () + [216; 218) 'S1': S1 + [216; 228) 'S1.method1()': (u8, u16, u32) + [250; 252) 'S1': S1 + [250; 262) 'S1.method2()': (u32, u16, u8) + [284; 286) 'S2': S2 + [284; 296) 'S2.method1()': (i8, i16, {unknown}) + [324; 326) 'S2': S2 + [324; 336) 'S2.method2()': ({unknown}, i16, i8) + "### + ); +} + +#[test] +fn infer_trait_method_generic_2() { + // the trait implementation is intentionally incomplete -- it shouldn't matter + assert_snapshot!( + infer(r#" +trait Trait { + fn method(&self) -> T; +} +struct S(T); +impl Trait for S {} +fn test() { + S(1u32).method(); +} +"#), + @r###" + [33; 37) 'self': &Self + [102; 127) '{ ...d(); }': () + [108; 109) 'S': S(T) -> S + [108; 115) 'S(1u32)': S + [108; 124) 'S(1u32...thod()': u32 + [110; 114) '1u32': u32 + "### + ); +} + +#[test] +fn infer_trait_assoc_method() { + assert_snapshot!( + infer(r#" +trait Default { + fn default() -> Self; +} +struct S; +impl Default for S {} +fn test() { + let s1: S = Default::default(); + let s2 = S::default(); + let s3 = ::default(); +} +"#), + @r###" + [87; 193) '{ ...t(); }': () + [97; 99) 's1': S + [105; 121) 'Defaul...efault': fn default() -> Self + [105; 123) 'Defaul...ault()': S + [133; 135) 's2': S + [138; 148) 'S::default': fn default() -> Self + [138; 150) 'S::default()': S + [160; 162) 's3': S + [165; 188) '() -> Self + [165; 190) ' { + fn make() -> T; +} +struct S; +impl Trait for S {} +struct G; +impl Trait for G {} +fn test() { + let a = S::make(); + let b = G::::make(); + let c: f64 = G::make(); +} +"#), + @r###" + [127; 211) '{ ...e(); }': () + [137; 138) 'a': u32 + [141; 148) 'S::make': fn make() -> T + [141; 150) 'S::make()': u32 + [160; 161) 'b': u64 + [164; 178) 'G::::make': fn make, u64>() -> T + [164; 180) 'G::, f64>() -> T + [199; 208) 'G::make()': f64 + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_2() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (T, U); +} +struct S; +impl Trait for S {} +struct G; +impl Trait for G {} +fn test() { + let a = S::make::(); + let b: (_, i64) = S::make(); + let c = G::::make::(); + let d: (u32, _) = G::make::(); + let e: (u32, i64) = G::make(); +} +"#), + @r###" + [135; 313) '{ ...e(); }': () + [145; 146) 'a': (u32, i64) + [149; 163) 'S::make::': fn make() -> (T, U) + [149; 165) 'S::mak...i64>()': (u32, i64) + [175; 176) 'b': (u32, i64) + [189; 196) 'S::make': fn make() -> (T, U) + [189; 198) 'S::make()': (u32, i64) + [208; 209) 'c': (u32, i64) + [212; 233) 'G::': fn make, u32, i64>() -> (T, U) + [212; 235) 'G::()': (u32, i64) + [245; 246) 'd': (u32, i64) + [259; 273) 'G::make::': fn make, u32, i64>() -> (T, U) + [259; 275) 'G::mak...i64>()': (u32, i64) + [285; 286) 'e': (u32, i64) + [301; 308) 'G::make': fn make, u32, i64>() -> (T, U) + [301; 310) 'G::make()': (u32, i64) + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_3() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (Self, T); +} +struct S; +impl Trait for S {} +fn test() { + let a = S::make(); +} +"#), + @r###" + [101; 127) '{ ...e(); }': () + [111; 112) 'a': (S, i64) + [115; 122) 'S::make': fn make, i64>() -> (Self, T) + [115; 124) 'S::make()': (S, i64) + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_4() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (Self, T); +} +struct S; +impl Trait for S {} +impl Trait for S {} +fn test() { + let a: (S, _) = S::make(); + let b: (_, i32) = S::make(); +} +"#), + @r###" + [131; 203) '{ ...e(); }': () + [141; 142) 'a': (S, i64) + [158; 165) 'S::make': fn make, i64>() -> (Self, T) + [158; 167) 'S::make()': (S, i64) + [177; 178) 'b': (S, i32) + [191; 198) 'S::make': fn make, i32>() -> (Self, T) + [191; 200) 'S::make()': (S, i32) + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_5() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (Self, T, U); +} +struct S; +impl Trait for S {} +fn test() { + let a = >::make::(); + let b: (S, _, _) = Trait::::make::(); +} +"#), + @r###" + [107; 211) '{ ...>(); }': () + [117; 118) 'a': (S, i64, u8) + [121; 150) '': fn make, i64, u8>() -> (Self, T, U) + [121; 152) '()': (S, i64, u8) + [162; 163) 'b': (S, i64, u8) + [182; 206) 'Trait:...::': fn make, i64, u8>() -> (Self, T, U) + [182; 208) 'Trait:...()': (S, i64, u8) + "### + ); +} + +#[test] +fn infer_call_trait_method_on_generic_param_1() { + assert_snapshot!( + infer(r#" +trait Trait { + fn method(&self) -> u32; +} +fn test(t: T) { + t.method(); +} +"#), + @r###" + [30; 34) 'self': &Self + [64; 65) 't': T + [70; 89) '{ ...d(); }': () + [76; 77) 't': T + [76; 86) 't.method()': u32 + "### + ); +} + +#[test] +fn infer_call_trait_method_on_generic_param_2() { + assert_snapshot!( + infer(r#" +trait Trait { + fn method(&self) -> T; +} +fn test>(t: T) { + t.method(); +} +"#), + @r###" + [33; 37) 'self': &Self + [71; 72) 't': T + [77; 96) '{ ...d(); }': () + [83; 84) 't': T + [83; 93) 't.method()': [missing name] + "### + ); +} + +#[test] +fn infer_with_multiple_trait_impls() { + assert_snapshot!( + infer(r#" +trait Into { + fn into(self) -> T; +} +struct S; +impl Into for S {} +impl Into for S {} +fn test() { + let x: u32 = S.into(); + let y: u64 = S.into(); + let z = Into::::into(S); +} +"#), + @r###" + [29; 33) 'self': Self + [111; 202) '{ ...(S); }': () + [121; 122) 'x': u32 + [130; 131) 'S': S + [130; 138) 'S.into()': u32 + [148; 149) 'y': u64 + [157; 158) 'S': S + [157; 165) 'S.into()': u64 + [175; 176) 'z': u64 + [179; 196) 'Into::...::into': fn into(Self) -> T + [179; 199) 'Into::...nto(S)': u64 + [197; 198) 'S': S + "### + ); +} + +#[test] +fn method_resolution_unify_impl_self_type() { + let t = type_at( + r#" +//- /main.rs +struct S; +impl S { fn foo(&self) -> u8 {} } +impl S { fn foo(&self) -> i8 {} } +fn test() { (S::.foo(), S::.foo())<|>; } +"#, + ); + assert_eq!(t, "(u8, i8)"); +} + +#[test] +fn method_resolution_trait_before_autoref() { + let t = type_at( + r#" +//- /main.rs +trait Trait { fn foo(self) -> u128; } +struct S; +impl S { fn foo(&self) -> i8 { 0 } } +impl Trait for S { fn foo(self) -> u128 { 0 } } +fn test() { S.foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + +#[test] +fn method_resolution_by_value_before_autoref() { + let t = type_at( + r#" +//- /main.rs +trait Clone { fn clone(&self) -> Self; } +struct S; +impl Clone for S {} +impl Clone for &S {} +fn test() { (S.clone(), (&S).clone(), (&&S).clone())<|>; } +"#, + ); + assert_eq!(t, "(S, S, &S)"); +} + +#[test] +fn method_resolution_trait_before_autoderef() { + let t = type_at( + r#" +//- /main.rs +trait Trait { fn foo(self) -> u128; } +struct S; +impl S { fn foo(self) -> i8 { 0 } } +impl Trait for &S { fn foo(self) -> u128 { 0 } } +fn test() { (&S).foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + +#[test] +fn method_resolution_impl_before_trait() { + let t = type_at( + r#" +//- /main.rs +trait Trait { fn foo(self) -> u128; } +struct S; +impl S { fn foo(self) -> i8 { 0 } } +impl Trait for S { fn foo(self) -> u128 { 0 } } +fn test() { S.foo()<|>; } +"#, + ); + assert_eq!(t, "i8"); +} + +#[test] +fn method_resolution_impl_ref_before_trait() { + let t = type_at( + r#" +//- /main.rs +trait Trait { fn foo(self) -> u128; } +struct S; +impl S { fn foo(&self) -> i8 { 0 } } +impl Trait for &S { fn foo(self) -> u128 { 0 } } +fn test() { S.foo()<|>; } +"#, + ); + assert_eq!(t, "i8"); +} + +#[test] +fn method_resolution_trait_autoderef() { + let t = type_at( + r#" +//- /main.rs +trait Trait { fn foo(self) -> u128; } +struct S; +impl Trait for S { fn foo(self) -> u128 { 0 } } +fn test() { (&S).foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + +#[test] +fn method_resolution_trait_from_prelude() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:other_crate +struct S; +impl Clone for S {} + +fn test() { + S.clone()<|>; +} + +//- /lib.rs crate:other_crate +#[prelude_import] use foo::*; + +mod foo { + trait Clone { + fn clone(&self) -> Self; + } +} +"#, + ); + assert_eq!("S", type_at_pos(&db, pos)); +} + +#[test] +fn method_resolution_where_clause_for_unknown_trait() { + // The blanket impl currently applies because we ignore the unresolved where clause + let t = type_at( + r#" +//- /main.rs +trait Trait { fn foo(self) -> u128; } +struct S; +impl Trait for T where T: UnknownTrait {} +fn test() { (&S).foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + +#[test] +fn method_resolution_where_clause_not_met() { + // The blanket impl shouldn't apply because we can't prove S: Clone + let t = type_at( + r#" +//- /main.rs +trait Clone {} +trait Trait { fn foo(self) -> u128; } +struct S; +impl Trait for T where T: Clone {} +fn test() { (&S).foo()<|>; } +"#, + ); + // This is also to make sure that we don't resolve to the foo method just + // because that's the only method named foo we can find, which would make + // the below tests not work + assert_eq!(t, "{unknown}"); +} + +#[test] +fn method_resolution_where_clause_inline_not_met() { + // The blanket impl shouldn't apply because we can't prove S: Clone + let t = type_at( + r#" +//- /main.rs +trait Clone {} +trait Trait { fn foo(self) -> u128; } +struct S; +impl Trait for T {} +fn test() { (&S).foo()<|>; } +"#, + ); + assert_eq!(t, "{unknown}"); +} + +#[test] +fn method_resolution_where_clause_1() { + let t = type_at( + r#" +//- /main.rs +trait Clone {} +trait Trait { fn foo(self) -> u128; } +struct S; +impl Clone for S {} +impl Trait for T where T: Clone {} +fn test() { S.foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + +#[test] +fn method_resolution_where_clause_2() { + let t = type_at( + r#" +//- /main.rs +trait Into { fn into(self) -> T; } +trait From { fn from(other: T) -> Self; } +struct S1; +struct S2; +impl From for S1 {} +impl Into for T where U: From {} +fn test() { S2.into()<|>; } +"#, + ); + assert_eq!(t, "{unknown}"); +} + +#[test] +fn method_resolution_where_clause_inline() { + let t = type_at( + r#" +//- /main.rs +trait Into { fn into(self) -> T; } +trait From { fn from(other: T) -> Self; } +struct S1; +struct S2; +impl From for S1 {} +impl> Into for T {} +fn test() { S2.into()<|>; } +"#, + ); + assert_eq!(t, "{unknown}"); +} + +#[test] +fn method_resolution_encountering_fn_type() { + type_at( + r#" +//- /main.rs +fn foo() {} +trait FnOnce { fn call(self); } +fn test() { foo.call()<|>; } +"#, + ); +} + +#[test] +fn method_resolution_slow() { + // this can get quite slow if we set the solver size limit too high + let t = type_at( + r#" +//- /main.rs +trait SendX {} + +struct S1; impl SendX for S1 {} +struct S2; impl SendX for S2 {} +struct U1; + +trait Trait { fn method(self); } + +struct X1 {} +impl SendX for X1 where A: SendX, B: SendX {} + +struct S {} + +trait FnX {} + +impl Trait for S where C: FnX, B: SendX {} + +fn test() { (S {}).method()<|>; } +"#, + ); + assert_eq!(t, "()"); +} diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs new file mode 100644 index 0000000000..cb3890b423 --- /dev/null +++ b/crates/ra_hir_ty/src/tests/patterns.rs @@ -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); + +fn test() { + let A(n) = &A(1); + let A(n) = &mut A(1); +} +"#), + @r###" + [28; 79) '{ ...(1); }': () + [38; 42) 'A(n)': A + [40; 41) 'n': &i32 + [45; 50) '&A(1)': &A + [46; 47) 'A': A(T) -> A + [46; 50) 'A(1)': A + [48; 49) '1': i32 + [60; 64) 'A(n)': A + [62; 63) 'n': &mut i32 + [67; 76) '&mut A(1)': &mut A + [72; 73) 'A': A(T) -> A + [72; 76) 'A(1)': A + [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 { + x: T, +} + +enum Option { + Some(T), + None, +} + +fn test(a1: A, o: Option) { + let A { x: x2 } = a1; + let A:: { x: x3 } = A { x: 1 }; + match o { + Option::Some(t) => t, + _ => 1, + }; +} +"#), + @r###" + [79; 81) 'a1': A + [91; 92) 'o': Option + [107; 244) '{ ... }; }': () + [117; 128) 'A { x: x2 }': A + [124; 126) 'x2': u32 + [131; 133) 'a1': A + [143; 161) 'A:: + [157; 159) 'x3': i64 + [164; 174) 'A { x: 1 }': A + [171; 172) '1': i64 + [180; 241) 'match ... }': u64 + [186; 187) 'o': Option + [198; 213) 'Option::Some(t)': Option + [211; 212) 't': u64 + [217; 218) 't': u64 + [228; 229) '_': Option + [233; 234) '1': u64 + "### + ); +} diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs new file mode 100644 index 0000000000..09d684ac2a --- /dev/null +++ b/crates/ra_hir_ty/src/tests/regression.rs @@ -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 { + 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 { + value: V, +} +struct QueryResponse { + value: V, +} +fn test(query_response: Canonical>) { + &query_response.value; +} +"#), + @r###" + [92; 106) 'query_response': Canonical> + [137; 167) '{ ...lue; }': () + [143; 164) '&query....value': &QueryResponse + [144; 158) 'query_response': Canonical> + [144; 164) 'query_....value': QueryResponse + "### + ); +} + +#[test] +fn bug_1030() { + assert_snapshot!(infer(r#" +struct HashSet; +struct FxHasher; +type FxHashSet = HashSet; + +impl HashSet { + fn default() -> HashSet {} +} + +pub fn main_loop() { + FxHashSet::default(); +} +"#), + @r###" + [144; 146) '{}': () + [169; 198) '{ ...t(); }': () + [175; 193) 'FxHash...efault': fn default<{unknown}, FxHasher>() -> HashSet + [175; 195) 'FxHash...ault()': HashSet<{unknown}, FxHasher> + "### + ); +} diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs new file mode 100644 index 0000000000..3e5e163e34 --- /dev/null +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -0,0 +1,1663 @@ +use super::{infer, type_at, type_at_pos}; +use crate::test_db::TestDB; +use insta::assert_snapshot; +use ra_db::fixture::WithFixture; + +#[test] +fn infer_box() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std + +fn test() { + let x = box 1; + let t = (x, box x, box &1, box [1]); + t<|>; +} + +//- /std.rs crate:std +#[prelude_import] use prelude::*; +mod prelude {} + +mod boxed { + pub struct Box { + inner: *mut T, + } +} + +"#, + ); + assert_eq!("(Box, Box>, Box<&i32>, Box<[i32;_]>)", type_at_pos(&db, pos)); +} + +#[test] +fn infer_adt_self() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs +enum Nat { Succ(Self), Demo(Nat), Zero } + +fn test() { + let foo: Nat = Nat::Zero; + if let Nat::Succ(x) = foo { + x<|> + } +} + +"#, + ); + assert_eq!("Nat", type_at_pos(&db, pos)); +} + +#[test] +fn infer_ranges() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std +fn test() { + let a = ..; + let b = 1..; + let c = ..2u32; + let d = 1..2usize; + let e = ..=10; + let f = 'a'..='z'; + + let t = (a, b, c, d, e, f); + t<|>; +} + +//- /std.rs crate:std +#[prelude_import] use prelude::*; +mod prelude {} + +pub mod ops { + pub struct Range { + pub start: Idx, + pub end: Idx, + } + pub struct RangeFrom { + pub start: Idx, + } + struct RangeFull; + pub struct RangeInclusive { + start: Idx, + end: Idx, + is_empty: u8, + } + pub struct RangeTo { + pub end: Idx, + } + pub struct RangeToInclusive { + pub end: Idx, + } +} +"#, + ); + assert_eq!( + "(RangeFull, RangeFrom, RangeTo, Range, RangeToInclusive, RangeInclusive)", + type_at_pos(&db, pos), + ); +} + +#[test] +fn infer_while_let() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs +enum Option { Some(T), None } + +fn test() { + let foo: Option = None; + while let Option::Some(x) = foo { + <|>x + } +} + +"#, + ); + assert_eq!("f32", type_at_pos(&db, pos)); +} + +#[test] +fn infer_basics() { + assert_snapshot!( + infer(r#" +fn test(a: u32, b: isize, c: !, d: &str) { + a; + b; + c; + d; + 1usize; + 1isize; + "test"; + 1.0f32; +}"#), + @r###" + [9; 10) 'a': u32 + [17; 18) 'b': isize + [27; 28) 'c': ! + [33; 34) 'd': &str + [42; 121) '{ ...f32; }': ! + [48; 49) 'a': u32 + [55; 56) 'b': isize + [62; 63) 'c': ! + [69; 70) 'd': &str + [76; 82) '1usize': usize + [88; 94) '1isize': isize + [100; 106) '"test"': &str + [112; 118) '1.0f32': f32 + "### + ); +} + +#[test] +fn infer_let() { + assert_snapshot!( + infer(r#" +fn test() { + let a = 1isize; + let b: usize = 1; + let c = b; + let d: u32; + let e; + let f: i32 = e; +} +"#), + @r###" + [11; 118) '{ ...= e; }': () + [21; 22) 'a': isize + [25; 31) '1isize': isize + [41; 42) 'b': usize + [52; 53) '1': usize + [63; 64) 'c': usize + [67; 68) 'b': usize + [78; 79) 'd': u32 + [94; 95) 'e': i32 + [105; 106) 'f': i32 + [114; 115) 'e': i32 + "### + ); +} + +#[test] +fn infer_paths() { + assert_snapshot!( + infer(r#" +fn a() -> u32 { 1 } + +mod b { + fn c() -> u32 { 1 } +} + +fn test() { + a(); + b::c(); +} +"#), + @r###" + [15; 20) '{ 1 }': u32 + [17; 18) '1': u32 + [48; 53) '{ 1 }': u32 + [50; 51) '1': u32 + [67; 91) '{ ...c(); }': () + [73; 74) 'a': fn a() -> u32 + [73; 76) 'a()': u32 + [82; 86) 'b::c': fn c() -> u32 + [82; 88) 'b::c()': u32 + "### + ); +} + +#[test] +fn infer_path_type() { + assert_snapshot!( + infer(r#" +struct S; + +impl S { + fn foo() -> i32 { 1 } +} + +fn test() { + S::foo(); + ::foo(); +} +"#), + @r###" + [41; 46) '{ 1 }': i32 + [43; 44) '1': i32 + [60; 93) '{ ...o(); }': () + [66; 72) 'S::foo': fn foo() -> i32 + [66; 74) 'S::foo()': i32 + [80; 88) '::foo': fn foo() -> i32 + [80; 90) '::foo()': i32 + "### + ); +} + +#[test] +fn infer_struct() { + assert_snapshot!( + infer(r#" +struct A { + b: B, + c: C, +} +struct B; +struct C(usize); + +fn test() { + let c = C(1); + B; + let a: A = A { b: B, c: C(1) }; + a.b; + a.c; +} +"#), + @r###" + [72; 154) '{ ...a.c; }': () + [82; 83) 'c': C + [86; 87) 'C': C(usize) -> C + [86; 90) 'C(1)': C + [88; 89) '1': usize + [96; 97) 'B': B + [107; 108) 'a': A + [114; 133) 'A { b:...C(1) }': A + [121; 122) 'B': B + [127; 128) 'C': C(usize) -> C + [127; 131) 'C(1)': C + [129; 130) '1': usize + [139; 140) 'a': A + [139; 142) 'a.b': B + [148; 149) 'a': A + [148; 151) 'a.c': C + "### + ); +} + +#[test] +fn infer_enum() { + assert_snapshot!( + infer(r#" +enum E { + V1 { field: u32 }, + V2 +} +fn test() { + E::V1 { field: 1 }; + E::V2; +}"#), + @r###" + [48; 82) '{ E:...:V2; }': () + [52; 70) 'E::V1 ...d: 1 }': E + [67; 68) '1': u32 + [74; 79) 'E::V2': E + "### + ); +} + +#[test] +fn infer_refs() { + assert_snapshot!( + infer(r#" +fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { + a; + *a; + &a; + &mut a; + b; + *b; + &b; + c; + *c; + d; + *d; +} +"#), + @r###" + [9; 10) 'a': &u32 + [18; 19) 'b': &mut u32 + [31; 32) 'c': *const u32 + [46; 47) 'd': *mut u32 + [59; 150) '{ ... *d; }': () + [65; 66) 'a': &u32 + [72; 74) '*a': u32 + [73; 74) 'a': &u32 + [80; 82) '&a': &&u32 + [81; 82) 'a': &u32 + [88; 94) '&mut a': &mut &u32 + [93; 94) 'a': &u32 + [100; 101) 'b': &mut u32 + [107; 109) '*b': u32 + [108; 109) 'b': &mut u32 + [115; 117) '&b': &&mut u32 + [116; 117) 'b': &mut u32 + [123; 124) 'c': *const u32 + [130; 132) '*c': u32 + [131; 132) 'c': *const u32 + [138; 139) 'd': *mut u32 + [145; 147) '*d': u32 + [146; 147) 'd': *mut u32 + "### + ); +} + +#[test] +fn infer_literals() { + assert_snapshot!( + infer(r##" +fn test() { + 5i32; + 5f32; + 5f64; + "hello"; + b"bytes"; + 'c'; + b'b'; + 3.14; + 5000; + false; + true; + r#" + //! doc + // non-doc + mod foo {} + "#; + br#"yolo"#; +} +"##), + @r###" + [11; 221) '{ ...o"#; }': () + [17; 21) '5i32': i32 + [27; 31) '5f32': f32 + [37; 41) '5f64': f64 + [47; 54) '"hello"': &str + [60; 68) 'b"bytes"': &[u8] + [74; 77) ''c'': char + [83; 87) 'b'b'': u8 + [93; 97) '3.14': f64 + [103; 107) '5000': i32 + [113; 118) 'false': bool + [124; 128) 'true': bool + [134; 202) 'r#" ... "#': &str + [208; 218) 'br#"yolo"#': &[u8] + "### + ); +} + +#[test] +fn infer_unary_op() { + assert_snapshot!( + infer(r#" +enum SomeType {} + +fn test(x: SomeType) { + let b = false; + let c = !b; + let a = 100; + let d: i128 = -a; + let e = -100; + let f = !!!true; + let g = !42; + let h = !10u32; + let j = !a; + -3.14; + !3; + -x; + !x; + -"hello"; + !"hello"; +} +"#), + @r###" + [27; 28) 'x': SomeType + [40; 272) '{ ...lo"; }': () + [50; 51) 'b': bool + [54; 59) 'false': bool + [69; 70) 'c': bool + [73; 75) '!b': bool + [74; 75) 'b': bool + [85; 86) 'a': i128 + [89; 92) '100': i128 + [102; 103) 'd': i128 + [112; 114) '-a': i128 + [113; 114) 'a': i128 + [124; 125) 'e': i32 + [128; 132) '-100': i32 + [129; 132) '100': i32 + [142; 143) 'f': bool + [146; 153) '!!!true': bool + [147; 153) '!!true': bool + [148; 153) '!true': bool + [149; 153) 'true': bool + [163; 164) 'g': i32 + [167; 170) '!42': i32 + [168; 170) '42': i32 + [180; 181) 'h': u32 + [184; 190) '!10u32': u32 + [185; 190) '10u32': u32 + [200; 201) 'j': i128 + [204; 206) '!a': i128 + [205; 206) 'a': i128 + [212; 217) '-3.14': f64 + [213; 217) '3.14': f64 + [223; 225) '!3': i32 + [224; 225) '3': i32 + [231; 233) '-x': {unknown} + [232; 233) 'x': SomeType + [239; 241) '!x': {unknown} + [240; 241) 'x': SomeType + [247; 255) '-"hello"': {unknown} + [248; 255) '"hello"': &str + [261; 269) '!"hello"': {unknown} + [262; 269) '"hello"': &str + "### + ); +} + +#[test] +fn infer_backwards() { + assert_snapshot!( + infer(r#" +fn takes_u32(x: u32) {} + +struct S { i32_field: i32 } + +fn test() -> &mut &f64 { + let a = unknown_function(); + takes_u32(a); + let b = unknown_function(); + S { i32_field: b }; + let c = unknown_function(); + &mut &c +} +"#), + @r###" + [14; 15) 'x': u32 + [22; 24) '{}': () + [78; 231) '{ ...t &c }': &mut &f64 + [88; 89) 'a': u32 + [92; 108) 'unknow...nction': {unknown} + [92; 110) 'unknow...tion()': u32 + [116; 125) 'takes_u32': fn takes_u32(u32) -> () + [116; 128) 'takes_u32(a)': () + [126; 127) 'a': u32 + [138; 139) 'b': i32 + [142; 158) 'unknow...nction': {unknown} + [142; 160) 'unknow...tion()': i32 + [166; 184) 'S { i3...d: b }': S + [181; 182) 'b': i32 + [194; 195) 'c': f64 + [198; 214) 'unknow...nction': {unknown} + [198; 216) 'unknow...tion()': f64 + [222; 229) '&mut &c': &mut &f64 + [227; 229) '&c': &f64 + [228; 229) 'c': f64 + "### + ); +} + +#[test] +fn infer_self() { + assert_snapshot!( + infer(r#" +struct S; + +impl S { + fn test(&self) { + self; + } + fn test2(self: &Self) { + self; + } + fn test3() -> Self { + S {} + } + fn test4() -> Self { + Self {} + } +} +"#), + @r###" + [34; 38) 'self': &S + [40; 61) '{ ... }': () + [50; 54) 'self': &S + [75; 79) 'self': &S + [88; 109) '{ ... }': () + [98; 102) 'self': &S + [133; 153) '{ ... }': S + [143; 147) 'S {}': S + [177; 200) '{ ... }': S + [187; 194) 'Self {}': S + "### + ); +} + +#[test] +fn infer_binary_op() { + assert_snapshot!( + infer(r#" +fn f(x: bool) -> i32 { + 0i32 +} + +fn test() -> bool { + let x = a && b; + let y = true || false; + let z = x == y; + let t = x != y; + let minus_forty: isize = -40isize; + let h = minus_forty <= CONST_2; + let c = f(z || y) + 5; + let d = b; + let g = minus_forty ^= i; + let ten: usize = 10; + let ten_is_eleven = ten == some_num; + + ten < 3 +} +"#), + @r###" + [6; 7) 'x': bool + [22; 34) '{ 0i32 }': i32 + [28; 32) '0i32': i32 + [54; 370) '{ ... < 3 }': bool + [64; 65) 'x': bool + [68; 69) 'a': bool + [68; 74) 'a && b': bool + [73; 74) 'b': bool + [84; 85) 'y': bool + [88; 92) 'true': bool + [88; 101) 'true || false': bool + [96; 101) 'false': bool + [111; 112) 'z': bool + [115; 116) 'x': bool + [115; 121) 'x == y': bool + [120; 121) 'y': bool + [131; 132) 't': bool + [135; 136) 'x': bool + [135; 141) 'x != y': bool + [140; 141) 'y': bool + [151; 162) 'minus_forty': isize + [172; 180) '-40isize': isize + [173; 180) '40isize': isize + [190; 191) 'h': bool + [194; 205) 'minus_forty': isize + [194; 216) 'minus_...ONST_2': bool + [209; 216) 'CONST_2': isize + [226; 227) 'c': i32 + [230; 231) 'f': fn f(bool) -> i32 + [230; 239) 'f(z || y)': i32 + [230; 243) 'f(z || y) + 5': i32 + [232; 233) 'z': bool + [232; 238) 'z || y': bool + [237; 238) 'y': bool + [242; 243) '5': i32 + [253; 254) 'd': {unknown} + [257; 258) 'b': {unknown} + [268; 269) 'g': () + [272; 283) 'minus_forty': isize + [272; 288) 'minus_...y ^= i': () + [287; 288) 'i': isize + [298; 301) 'ten': usize + [311; 313) '10': usize + [323; 336) 'ten_is_eleven': bool + [339; 342) 'ten': usize + [339; 354) 'ten == some_num': bool + [346; 354) 'some_num': usize + [361; 364) 'ten': usize + [361; 368) 'ten < 3': bool + [367; 368) '3': usize + "### + ); +} + +#[test] +fn infer_field_autoderef() { + assert_snapshot!( + infer(r#" +struct A { + b: B, +} +struct B; + +fn test1(a: A) { + let a1 = a; + a1.b; + let a2 = &a; + a2.b; + let a3 = &mut a; + a3.b; + let a4 = &&&&&&&a; + a4.b; + let a5 = &mut &&mut &&mut a; + a5.b; +} + +fn test2(a1: *const A, a2: *mut A) { + a1.b; + a2.b; +} +"#), + @r###" + [44; 45) 'a': A + [50; 213) '{ ...5.b; }': () + [60; 62) 'a1': A + [65; 66) 'a': A + [72; 74) 'a1': A + [72; 76) 'a1.b': B + [86; 88) 'a2': &A + [91; 93) '&a': &A + [92; 93) 'a': A + [99; 101) 'a2': &A + [99; 103) 'a2.b': B + [113; 115) 'a3': &mut A + [118; 124) '&mut a': &mut A + [123; 124) 'a': A + [130; 132) 'a3': &mut A + [130; 134) 'a3.b': B + [144; 146) 'a4': &&&&&&&A + [149; 157) '&&&&&&&a': &&&&&&&A + [150; 157) '&&&&&&a': &&&&&&A + [151; 157) '&&&&&a': &&&&&A + [152; 157) '&&&&a': &&&&A + [153; 157) '&&&a': &&&A + [154; 157) '&&a': &&A + [155; 157) '&a': &A + [156; 157) 'a': A + [163; 165) 'a4': &&&&&&&A + [163; 167) 'a4.b': B + [177; 179) 'a5': &mut &&mut &&mut A + [182; 200) '&mut &...&mut a': &mut &&mut &&mut A + [187; 200) '&&mut &&mut a': &&mut &&mut A + [188; 200) '&mut &&mut a': &mut &&mut A + [193; 200) '&&mut a': &&mut A + [194; 200) '&mut a': &mut A + [199; 200) 'a': A + [206; 208) 'a5': &mut &&mut &&mut A + [206; 210) 'a5.b': B + [224; 226) 'a1': *const A + [238; 240) 'a2': *mut A + [250; 273) '{ ...2.b; }': () + [256; 258) 'a1': *const A + [256; 260) 'a1.b': B + [266; 268) 'a2': *mut A + [266; 270) 'a2.b': B + "### + ); +} + +#[test] +fn infer_argument_autoderef() { + assert_snapshot!( + infer(r#" +#[lang = "deref"] +pub trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +struct A(T); + +impl A { + fn foo(&self) -> &T { + &self.0 + } +} + +struct B(T); + +impl Deref for B { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn test() { + let t = A::foo(&&B(B(A(42)))); +} +"#), + @r###" + [68; 72) 'self': &Self + [139; 143) 'self': &A + [151; 174) '{ ... }': &T + [161; 168) '&self.0': &T + [162; 166) 'self': &A + [162; 168) 'self.0': T + [255; 259) 'self': &B + [278; 301) '{ ... }': &T + [288; 295) '&self.0': &T + [289; 293) 'self': &B + [289; 295) 'self.0': T + [315; 353) '{ ...))); }': () + [325; 326) 't': &i32 + [329; 335) 'A::foo': fn foo(&A) -> &T + [329; 350) 'A::foo...42))))': &i32 + [336; 349) '&&B(B(A(42)))': &&B>> + [337; 349) '&B(B(A(42)))': &B>> + [338; 339) 'B': B>>(T) -> B + [338; 349) 'B(B(A(42)))': B>> + [340; 341) 'B': B>(T) -> B + [340; 348) 'B(A(42))': B> + [342; 343) 'A': A(T) -> A + [342; 347) 'A(42)': A + [344; 346) '42': i32 + "### + ); +} + +#[test] +fn infer_method_argument_autoderef() { + assert_snapshot!( + infer(r#" +#[lang = "deref"] +pub trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +struct A(*mut T); + +impl A { + fn foo(&self, x: &A) -> &T { + &*x.0 + } +} + +struct B(T); + +impl Deref for B { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn test(a: A) { + let t = A(0 as *mut _).foo(&&B(B(a))); +} +"#), + @r###" + [68; 72) 'self': &Self + [144; 148) 'self': &A + [150; 151) 'x': &A + [166; 187) '{ ... }': &T + [176; 181) '&*x.0': &T + [177; 181) '*x.0': T + [178; 179) 'x': &A + [178; 181) 'x.0': *mut T + [268; 272) 'self': &B + [291; 314) '{ ... }': &T + [301; 308) '&self.0': &T + [302; 306) 'self': &B + [302; 308) 'self.0': T + [326; 327) 'a': A + [337; 383) '{ ...))); }': () + [347; 348) 't': &i32 + [351; 352) 'A': A(*mut T) -> A + [351; 365) 'A(0 as *mut _)': A + [351; 380) 'A(0 as...B(a)))': &i32 + [353; 354) '0': i32 + [353; 364) '0 as *mut _': *mut i32 + [370; 379) '&&B(B(a))': &&B>> + [371; 379) '&B(B(a))': &B>> + [372; 373) 'B': B>>(T) -> B + [372; 379) 'B(B(a))': B>> + [374; 375) 'B': B>(T) -> B + [374; 378) 'B(a)': B> + [376; 377) 'a': A + "### + ); +} + +#[test] +fn infer_in_elseif() { + assert_snapshot!( + infer(r#" +struct Foo { field: i32 } +fn main(foo: Foo) { + if true { + + } else if false { + foo.field + } +} +"#), + @r###" + [35; 38) 'foo': Foo + [45; 109) '{ ... } }': () + [51; 107) 'if tru... }': () + [54; 58) 'true': bool + [59; 67) '{ }': () + [73; 107) 'if fal... }': () + [76; 81) 'false': bool + [82; 107) '{ ... }': i32 + [92; 95) 'foo': Foo + [92; 101) 'foo.field': i32 + "### + ) +} + +#[test] +fn infer_if_match_with_return() { + assert_snapshot!( + infer(r#" +fn foo() { + let _x1 = if true { + 1 + } else { + return; + }; + let _x2 = if true { + 2 + } else { + return + }; + let _x3 = match true { + true => 3, + _ => { + return; + } + }; + let _x4 = match true { + true => 4, + _ => return + }; +}"#), + @r###" + [10; 323) '{ ... }; }': () + [20; 23) '_x1': i32 + [26; 80) 'if tru... }': i32 + [29; 33) 'true': bool + [34; 51) '{ ... }': i32 + [44; 45) '1': i32 + [57; 80) '{ ... }': ! + [67; 73) 'return': ! + [90; 93) '_x2': i32 + [96; 149) 'if tru... }': i32 + [99; 103) 'true': bool + [104; 121) '{ ... }': i32 + [114; 115) '2': i32 + [127; 149) '{ ... }': ! + [137; 143) 'return': ! + [159; 162) '_x3': i32 + [165; 247) 'match ... }': i32 + [171; 175) 'true': bool + [186; 190) 'true': bool + [194; 195) '3': i32 + [205; 206) '_': bool + [210; 241) '{ ... }': ! + [224; 230) 'return': ! + [257; 260) '_x4': i32 + [263; 320) 'match ... }': i32 + [269; 273) 'true': bool + [284; 288) 'true': bool + [292; 293) '4': i32 + [303; 304) '_': bool + [308; 314) 'return': ! + "### + ) +} + +#[test] +fn infer_inherent_method() { + assert_snapshot!( + infer(r#" +struct A; + +impl A { + fn foo(self, x: u32) -> i32 {} +} + +mod b { + impl super::A { + fn bar(&self, x: u64) -> i64 {} + } +} + +fn test(a: A) { + a.foo(1); + (&a).bar(1); + a.bar(1); +} +"#), + @r###" + [32; 36) 'self': A + [38; 39) 'x': u32 + [53; 55) '{}': () + [103; 107) 'self': &A + [109; 110) 'x': u64 + [124; 126) '{}': () + [144; 145) 'a': A + [150; 198) '{ ...(1); }': () + [156; 157) 'a': A + [156; 164) 'a.foo(1)': i32 + [162; 163) '1': u32 + [170; 181) '(&a).bar(1)': i64 + [171; 173) '&a': &A + [172; 173) 'a': A + [179; 180) '1': u64 + [187; 188) 'a': A + [187; 195) 'a.bar(1)': i64 + [193; 194) '1': u64 + "### + ); +} + +#[test] +fn infer_inherent_method_str() { + assert_snapshot!( + infer(r#" +#[lang = "str"] +impl str { + fn foo(&self) -> i32 {} +} + +fn test() { + "foo".foo(); +} +"#), + @r###" + [40; 44) 'self': &str + [53; 55) '{}': () + [69; 89) '{ ...o(); }': () + [75; 80) '"foo"': &str + [75; 86) '"foo".foo()': i32 + "### + ); +} + +#[test] +fn infer_tuple() { + assert_snapshot!( + infer(r#" +fn test(x: &str, y: isize) { + let a: (u32, &str) = (1, "a"); + let b = (a, x); + let c = (y, x); + let d = (c, x); + let e = (1, "e"); + let f = (e, "d"); +} +"#), + @r###" + [9; 10) 'x': &str + [18; 19) 'y': isize + [28; 170) '{ ...d"); }': () + [38; 39) 'a': (u32, &str) + [55; 63) '(1, "a")': (u32, &str) + [56; 57) '1': u32 + [59; 62) '"a"': &str + [73; 74) 'b': ((u32, &str), &str) + [77; 83) '(a, x)': ((u32, &str), &str) + [78; 79) 'a': (u32, &str) + [81; 82) 'x': &str + [93; 94) 'c': (isize, &str) + [97; 103) '(y, x)': (isize, &str) + [98; 99) 'y': isize + [101; 102) 'x': &str + [113; 114) 'd': ((isize, &str), &str) + [117; 123) '(c, x)': ((isize, &str), &str) + [118; 119) 'c': (isize, &str) + [121; 122) 'x': &str + [133; 134) 'e': (i32, &str) + [137; 145) '(1, "e")': (i32, &str) + [138; 139) '1': i32 + [141; 144) '"e"': &str + [155; 156) 'f': ((i32, &str), &str) + [159; 167) '(e, "d")': ((i32, &str), &str) + [160; 161) 'e': (i32, &str) + [163; 166) '"d"': &str + "### + ); +} + +#[test] +fn infer_array() { + assert_snapshot!( + infer(r#" +fn test(x: &str, y: isize) { + let a = [x]; + let b = [a, a]; + let c = [b, b]; + + let d = [y, 1, 2, 3]; + let d = [1, y, 2, 3]; + let e = [y]; + let f = [d, d]; + let g = [e, e]; + + let h = [1, 2]; + let i = ["a", "b"]; + + let b = [a, ["b"]]; + let x: [u8; 0] = []; +} +"#), + @r###" + [9; 10) 'x': &str + [18; 19) 'y': isize + [28; 293) '{ ... []; }': () + [38; 39) 'a': [&str;_] + [42; 45) '[x]': [&str;_] + [43; 44) 'x': &str + [55; 56) 'b': [[&str;_];_] + [59; 65) '[a, a]': [[&str;_];_] + [60; 61) 'a': [&str;_] + [63; 64) 'a': [&str;_] + [75; 76) 'c': [[[&str;_];_];_] + [79; 85) '[b, b]': [[[&str;_];_];_] + [80; 81) 'b': [[&str;_];_] + [83; 84) 'b': [[&str;_];_] + [96; 97) 'd': [isize;_] + [100; 112) '[y, 1, 2, 3]': [isize;_] + [101; 102) 'y': isize + [104; 105) '1': isize + [107; 108) '2': isize + [110; 111) '3': isize + [122; 123) 'd': [isize;_] + [126; 138) '[1, y, 2, 3]': [isize;_] + [127; 128) '1': isize + [130; 131) 'y': isize + [133; 134) '2': isize + [136; 137) '3': isize + [148; 149) 'e': [isize;_] + [152; 155) '[y]': [isize;_] + [153; 154) 'y': isize + [165; 166) 'f': [[isize;_];_] + [169; 175) '[d, d]': [[isize;_];_] + [170; 171) 'd': [isize;_] + [173; 174) 'd': [isize;_] + [185; 186) 'g': [[isize;_];_] + [189; 195) '[e, e]': [[isize;_];_] + [190; 191) 'e': [isize;_] + [193; 194) 'e': [isize;_] + [206; 207) 'h': [i32;_] + [210; 216) '[1, 2]': [i32;_] + [211; 212) '1': i32 + [214; 215) '2': i32 + [226; 227) 'i': [&str;_] + [230; 240) '["a", "b"]': [&str;_] + [231; 234) '"a"': &str + [236; 239) '"b"': &str + [251; 252) 'b': [[&str;_];_] + [255; 265) '[a, ["b"]]': [[&str;_];_] + [256; 257) 'a': [&str;_] + [259; 264) '["b"]': [&str;_] + [260; 263) '"b"': &str + [275; 276) 'x': [u8;_] + [288; 290) '[]': [u8;_] + "### + ); +} + +#[test] +fn infer_struct_generics() { + assert_snapshot!( + infer(r#" +struct A { + x: T, +} + +fn test(a1: A, i: i32) { + a1.x; + let a2 = A { x: i }; + a2.x; + let a3 = A:: { x: 1 }; + a3.x; +} +"#), + @r###" + [36; 38) 'a1': A + [48; 49) 'i': i32 + [56; 147) '{ ...3.x; }': () + [62; 64) 'a1': A + [62; 66) 'a1.x': u32 + [76; 78) 'a2': A + [81; 91) 'A { x: i }': A + [88; 89) 'i': i32 + [97; 99) 'a2': A + [97; 101) 'a2.x': i32 + [111; 113) 'a3': A + [116; 134) 'A:: + [131; 132) '1': i128 + [140; 142) 'a3': A + [140; 144) 'a3.x': i128 + "### + ); +} + +#[test] +fn infer_tuple_struct_generics() { + assert_snapshot!( + infer(r#" +struct A(T); +enum Option { Some(T), None } +use Option::*; + +fn test() { + A(42); + A(42u128); + Some("x"); + Option::Some("x"); + None; + let x: Option = None; +} +"#), + @r###" + [76; 184) '{ ...one; }': () + [82; 83) 'A': A(T) -> A + [82; 87) 'A(42)': A + [84; 86) '42': i32 + [93; 94) 'A': A(T) -> A + [93; 102) 'A(42u128)': A + [95; 101) '42u128': u128 + [108; 112) 'Some': Some<&str>(T) -> Option + [108; 117) 'Some("x")': Option<&str> + [113; 116) '"x"': &str + [123; 135) 'Option::Some': Some<&str>(T) -> Option + [123; 140) 'Option...e("x")': Option<&str> + [136; 139) '"x"': &str + [146; 150) 'None': Option<{unknown}> + [160; 161) 'x': Option + [177; 181) 'None': Option + "### + ); +} + +#[test] +fn infer_function_generics() { + assert_snapshot!( + infer(r#" +fn id(t: T) -> T { t } + +fn test() { + id(1u32); + id::(1); + let x: u64 = id(1); +} +"#), + @r###" + [10; 11) 't': T + [21; 26) '{ t }': T + [23; 24) 't': T + [38; 98) '{ ...(1); }': () + [44; 46) 'id': fn id(T) -> T + [44; 52) 'id(1u32)': u32 + [47; 51) '1u32': u32 + [58; 68) 'id::': fn id(T) -> T + [58; 71) 'id::(1)': i128 + [69; 70) '1': i128 + [81; 82) 'x': u64 + [90; 92) 'id': fn id(T) -> T + [90; 95) 'id(1)': u64 + [93; 94) '1': u64 + "### + ); +} + +#[test] +fn infer_impl_generics() { + assert_snapshot!( + infer(r#" +struct A { + x: T1, + y: T2, +} +impl A { + fn x(self) -> X { + self.x + } + fn y(self) -> Y { + self.y + } + fn z(self, t: T) -> (X, Y, T) { + (self.x, self.y, t) + } +} + +fn test() -> i128 { + let a = A { x: 1u64, y: 1i64 }; + a.x(); + a.y(); + a.z(1i128); + a.z::(1); +} +"#), + @r###" + [74; 78) 'self': A + [85; 107) '{ ... }': X + [95; 99) 'self': A + [95; 101) 'self.x': X + [117; 121) 'self': A + [128; 150) '{ ... }': Y + [138; 142) 'self': A + [138; 144) 'self.y': Y + [163; 167) 'self': A + [169; 170) 't': T + [188; 223) '{ ... }': (X, Y, T) + [198; 217) '(self.....y, t)': (X, Y, T) + [199; 203) 'self': A + [199; 205) 'self.x': X + [207; 211) 'self': A + [207; 213) 'self.y': Y + [215; 216) 't': T + [245; 342) '{ ...(1); }': () + [255; 256) 'a': A + [259; 281) 'A { x:...1i64 }': A + [266; 270) '1u64': u64 + [275; 279) '1i64': i64 + [287; 288) 'a': A + [287; 292) 'a.x()': u64 + [298; 299) 'a': A + [298; 303) 'a.y()': i64 + [309; 310) 'a': A + [309; 319) 'a.z(1i128)': (u64, i64, i128) + [313; 318) '1i128': i128 + [325; 326) 'a': A + [325; 339) 'a.z::(1)': (u64, i64, u128) + [337; 338) '1': u128 + "### + ); +} + +#[test] +fn infer_impl_generics_with_autoderef() { + assert_snapshot!( + infer(r#" +enum Option { + Some(T), + None, +} +impl Option { + fn as_ref(&self) -> Option<&T> {} +} +fn test(o: Option) { + (&o).as_ref(); + o.as_ref(); +} +"#), + @r###" + [78; 82) 'self': &Option + [98; 100) '{}': () + [111; 112) 'o': Option + [127; 165) '{ ...f(); }': () + [133; 146) '(&o).as_ref()': Option<&u32> + [134; 136) '&o': &Option + [135; 136) 'o': Option + [152; 153) 'o': Option + [152; 162) 'o.as_ref()': Option<&u32> + "### + ); +} + +#[test] +fn infer_generic_chain() { + assert_snapshot!( + infer(r#" +struct A { + x: T, +} +impl A { + fn x(self) -> T2 { + self.x + } +} +fn id(t: T) -> T { t } + +fn test() -> i128 { + let x = 1; + let y = id(x); + let a = A { x: id(y) }; + let z = id(a.x); + let b = A { x: z }; + b.x() +} +"#), + @r###" + [53; 57) 'self': A + [65; 87) '{ ... }': T2 + [75; 79) 'self': A + [75; 81) 'self.x': T2 + [99; 100) 't': T + [110; 115) '{ t }': T + [112; 113) 't': T + [135; 261) '{ ....x() }': i128 + [146; 147) 'x': i128 + [150; 151) '1': i128 + [162; 163) 'y': i128 + [166; 168) 'id': fn id(T) -> T + [166; 171) 'id(x)': i128 + [169; 170) 'x': i128 + [182; 183) 'a': A + [186; 200) 'A { x: id(y) }': A + [193; 195) 'id': fn id(T) -> T + [193; 198) 'id(y)': i128 + [196; 197) 'y': i128 + [211; 212) 'z': i128 + [215; 217) 'id': fn id(T) -> T + [215; 222) 'id(a.x)': i128 + [218; 219) 'a': A + [218; 221) 'a.x': i128 + [233; 234) 'b': A + [237; 247) 'A { x: z }': A + [244; 245) 'z': i128 + [254; 255) 'b': A + [254; 259) 'b.x()': i128 + "### + ); +} + +#[test] +fn infer_associated_const() { + assert_snapshot!( + infer(r#" +struct Struct; + +impl Struct { + const FOO: u32 = 1; +} + +enum Enum {} + +impl Enum { + const BAR: u32 = 2; +} + +trait Trait { + const ID: u32; +} + +struct TraitTest; + +impl Trait for TraitTest { + const ID: u32 = 5; +} + +fn test() { + let x = Struct::FOO; + let y = Enum::BAR; + let z = TraitTest::ID; +} +"#), + @r###" + [52; 53) '1': u32 + [105; 106) '2': u32 + [213; 214) '5': u32 + [229; 307) '{ ...:ID; }': () + [239; 240) 'x': u32 + [243; 254) 'Struct::FOO': u32 + [264; 265) 'y': u32 + [268; 277) 'Enum::BAR': u32 + [287; 288) 'z': u32 + [291; 304) 'TraitTest::ID': u32 + "### + ); +} + +#[test] +fn infer_type_alias() { + assert_snapshot!( + infer(r#" +struct A { x: X, y: Y } +type Foo = A; +type Bar = A; +type Baz = A; +fn test(x: Foo, y: Bar<&str>, z: Baz) { + x.x; + x.y; + y.x; + y.y; + z.x; + z.y; +} +"#), + @r###" + [116; 117) 'x': A + [124; 125) 'y': A<&str, u128> + [138; 139) 'z': A + [154; 211) '{ ...z.y; }': () + [160; 161) 'x': A + [160; 163) 'x.x': u32 + [169; 170) 'x': A + [169; 172) 'x.y': i128 + [178; 179) 'y': A<&str, u128> + [178; 181) 'y.x': &str + [187; 188) 'y': A<&str, u128> + [187; 190) 'y.y': u128 + [196; 197) 'z': A + [196; 199) 'z.x': u8 + [205; 206) 'z': A + [205; 208) 'z.y': i8 + "### + ) +} + +#[test] +fn recursive_type_alias() { + assert_snapshot!( + infer(r#" +struct A {} +type Foo = Foo; +type Bar = A; +fn test(x: Foo) {} +"#), + @r###" + [59; 60) 'x': {unknown} + [67; 69) '{}': () + "### + ) +} + +#[test] +fn infer_type_param() { + assert_snapshot!( + infer(r#" +fn id(x: T) -> T { + x +} + +fn clone(x: &T) -> T { + *x +} + +fn test() { + let y = 10u32; + id(y); + let x: bool = clone(z); + id::(1); +} +"#), + @r###" + [10; 11) 'x': T + [21; 30) '{ x }': T + [27; 28) 'x': T + [44; 45) 'x': &T + [56; 66) '{ *x }': T + [62; 64) '*x': T + [63; 64) 'x': &T + [78; 158) '{ ...(1); }': () + [88; 89) 'y': u32 + [92; 97) '10u32': u32 + [103; 105) 'id': fn id(T) -> T + [103; 108) 'id(y)': u32 + [106; 107) 'y': u32 + [118; 119) 'x': bool + [128; 133) 'clone': fn clone(&T) -> T + [128; 136) 'clone(z)': bool + [134; 135) 'z': &bool + [142; 152) 'id::': fn id(T) -> T + [142; 155) 'id::(1)': i128 + [153; 154) '1': i128 + "### + ); +} + +#[test] +fn infer_const() { + assert_snapshot!( + infer(r#" +struct Foo; +impl Foo { const ASSOC_CONST: u32 = 0; } +const GLOBAL_CONST: u32 = 101; +fn test() { + const LOCAL_CONST: u32 = 99; + let x = LOCAL_CONST; + let z = GLOBAL_CONST; + let id = Foo::ASSOC_CONST; +} +"#), + @r###" + [49; 50) '0': u32 + [80; 83) '101': u32 + [95; 213) '{ ...NST; }': () + [138; 139) 'x': u32 + [142; 153) 'LOCAL_CONST': u32 + [163; 164) 'z': u32 + [167; 179) 'GLOBAL_CONST': u32 + [189; 191) 'id': u32 + [194; 210) 'Foo::A..._CONST': u32 + "### + ); +} + +#[test] +fn infer_static() { + assert_snapshot!( + infer(r#" +static GLOBAL_STATIC: u32 = 101; +static mut GLOBAL_STATIC_MUT: u32 = 101; +fn test() { + static LOCAL_STATIC: u32 = 99; + static mut LOCAL_STATIC_MUT: u32 = 99; + let x = LOCAL_STATIC; + let y = LOCAL_STATIC_MUT; + let z = GLOBAL_STATIC; + let w = GLOBAL_STATIC_MUT; +} +"#), + @r###" + [29; 32) '101': u32 + [70; 73) '101': u32 + [85; 280) '{ ...MUT; }': () + [173; 174) 'x': u32 + [177; 189) 'LOCAL_STATIC': u32 + [199; 200) 'y': u32 + [203; 219) 'LOCAL_...IC_MUT': u32 + [229; 230) 'z': u32 + [233; 246) 'GLOBAL_STATIC': u32 + [256; 257) 'w': u32 + [260; 277) 'GLOBAL...IC_MUT': u32 + "### + ); +} + +#[test] +fn shadowing_primitive() { + let t = type_at( + r#" +//- /main.rs +struct i32; +struct Foo; + +impl i32 { fn foo(&self) -> Foo { Foo } } + +fn main() { + let x: i32 = i32; + x.foo()<|>; +}"#, + ); + assert_eq!(t, "Foo"); +} + +#[test] +fn not_shadowing_primitive_by_module() { + let t = type_at( + r#" +//- /str.rs +fn foo() {} + +//- /main.rs +mod str; +fn foo() -> &'static str { "" } + +fn main() { + foo()<|>; +}"#, + ); + assert_eq!(t, "&str"); +} + +#[test] +fn not_shadowing_module_by_primitive() { + let t = type_at( + r#" +//- /str.rs +fn foo() -> u32 {0} + +//- /main.rs +mod str; +fn foo() -> &'static str { "" } + +fn main() { + str::foo()<|>; +}"#, + ); + assert_eq!(t, "u32"); +} + +#[test] +fn closure_return() { + assert_snapshot!( + infer(r#" +fn foo() -> u32 { + let x = || -> usize { return 1; }; +} +"#), + @r###" + [17; 59) '{ ...; }; }': () + [27; 28) 'x': || -> usize + [31; 56) '|| -> ...n 1; }': || -> usize + [43; 56) '{ return 1; }': ! + [45; 53) 'return 1': ! + [52; 53) '1': usize + "### + ); +} + +#[test] +fn closure_return_unit() { + assert_snapshot!( + infer(r#" +fn foo() -> u32 { + let x = || { return; }; +} +"#), + @r###" + [17; 48) '{ ...; }; }': () + [27; 28) 'x': || -> () + [31; 45) '|| { return; }': || -> () + [34; 45) '{ return; }': ! + [36; 42) 'return': ! + "### + ); +} + +#[test] +fn closure_return_inferred() { + assert_snapshot!( + infer(r#" +fn foo() -> u32 { + let x = || { "test" }; +} +"#), + @r###" + [17; 47) '{ ..." }; }': () + [27; 28) 'x': || -> &str + [31; 44) '|| { "test" }': || -> &str + [34; 44) '{ "test" }': &str + [36; 42) '"test"': &str + "### + ); +} diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs new file mode 100644 index 0000000000..ae316922bd --- /dev/null +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -0,0 +1,1598 @@ +use insta::assert_snapshot; + +use ra_db::fixture::WithFixture; +use test_utils::covers; + +use super::{infer, infer_with_mismatches, type_at, type_at_pos}; +use crate::test_db::TestDB; + +#[test] +fn infer_await() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std + +struct IntFuture; + +impl Future for IntFuture { + type Output = u64; +} + +fn test() { + let r = IntFuture; + let v = r.await; + v<|>; +} + +//- /std.rs crate:std +#[prelude_import] use future::*; +mod future { + trait Future { + type Output; + } +} + +"#, + ); + assert_eq!("u64", type_at_pos(&db, pos)); +} + +#[test] +fn infer_try() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std + +fn test() { + let r: Result = Result::Ok(1); + let v = r?; + v<|>; +} + +//- /std.rs crate:std + +#[prelude_import] use ops::*; +mod ops { + trait Try { + type Ok; + type Error; + } +} + +#[prelude_import] use result::*; +mod result { + enum Result { + Ok(O), + Err(E) + } + + impl crate::ops::Try for Result { + type Ok = O; + type Error = E; + } +} + +"#, + ); + assert_eq!("i32", type_at_pos(&db, pos)); +} + +#[test] +fn infer_for_loop() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std + +use std::collections::Vec; + +fn test() { + let v = Vec::new(); + v.push("foo"); + for x in v { + x<|>; + } +} + +//- /std.rs crate:std + +#[prelude_import] use iter::*; +mod iter { + trait IntoIterator { + type Item; + } +} + +mod collections { + struct Vec {} + impl Vec { + fn new() -> Self { Vec {} } + fn push(&mut self, t: T) { } + } + + impl crate::iter::IntoIterator for Vec { + type Item=T; + } +} +"#, + ); + assert_eq!("&str", type_at_pos(&db, pos)); +} + +#[test] +fn infer_ops_neg() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std + +struct Bar; +struct Foo; + +impl std::ops::Neg for Bar { + type Output = Foo; +} + +fn test() { + let a = Bar; + let b = -a; + b<|>; +} + +//- /std.rs crate:std + +#[prelude_import] use ops::*; +mod ops { + pub trait Neg { + type Output; + } +} +"#, + ); + assert_eq!("Foo", type_at_pos(&db, pos)); +} + +#[test] +fn infer_ops_not() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std + +struct Bar; +struct Foo; + +impl std::ops::Not for Bar { + type Output = Foo; +} + +fn test() { + let a = Bar; + let b = !a; + b<|>; +} + +//- /std.rs crate:std + +#[prelude_import] use ops::*; +mod ops { + pub trait Not { + type Output; + } +} +"#, + ); + assert_eq!("Foo", type_at_pos(&db, pos)); +} + +#[test] +fn infer_from_bound_1() { + assert_snapshot!( + infer(r#" +trait Trait {} +struct S(T); +impl Trait for S {} +fn foo>(t: T) {} +fn test() { + let s = S(unknown); + foo(s); +} +"#), + @r###" + [86; 87) 't': T + [92; 94) '{}': () + [105; 144) '{ ...(s); }': () + [115; 116) 's': S + [119; 120) 'S': S(T) -> S + [119; 129) 'S(unknown)': S + [121; 128) 'unknown': u32 + [135; 138) 'foo': fn foo>(T) -> () + [135; 141) 'foo(s)': () + [139; 140) 's': S + "### + ); +} + +#[test] +fn infer_from_bound_2() { + assert_snapshot!( + infer(r#" +trait Trait {} +struct S(T); +impl Trait for S {} +fn foo>(t: T) -> U {} +fn test() { + let s = S(unknown); + let x: u32 = foo(s); +} +"#), + @r###" + [87; 88) 't': T + [98; 100) '{}': () + [111; 163) '{ ...(s); }': () + [121; 122) 's': S + [125; 126) 'S': S(T) -> S + [125; 135) 'S(unknown)': S + [127; 134) 'unknown': u32 + [145; 146) 'x': u32 + [154; 157) 'foo': fn foo>(T) -> U + [154; 160) 'foo(s)': u32 + [158; 159) 's': S + "### + ); +} + +#[test] +fn infer_project_associated_type() { + // y, z, a don't yet work because of https://github.com/rust-lang/chalk/issues/234 + assert_snapshot!( + infer(r#" +trait Iterable { + type Item; +} +struct S; +impl Iterable for S { type Item = u32; } +fn test() { + let x: ::Item = 1; + let y: ::Item = no_matter; + let z: T::Item = no_matter; + let a: ::Item = no_matter; +} +"#), + @r###" + [108; 261) '{ ...ter; }': () + [118; 119) 'x': u32 + [145; 146) '1': u32 + [156; 157) 'y': {unknown} + [183; 192) 'no_matter': {unknown} + [202; 203) 'z': {unknown} + [215; 224) 'no_matter': {unknown} + [234; 235) 'a': {unknown} + [249; 258) 'no_matter': {unknown} + "### + ); +} + +#[test] +fn infer_return_associated_type() { + assert_snapshot!( + infer(r#" +trait Iterable { + type Item; +} +struct S; +impl Iterable for S { type Item = u32; } +fn foo1(t: T) -> T::Item {} +fn foo2(t: T) -> ::Item {} +fn foo3(t: T) -> ::Item {} +fn test() { + let x = foo1(S); + let y = foo2(S); + let z = foo3(S); +} +"#), + @r###" + [106; 107) 't': T + [123; 125) '{}': () + [147; 148) 't': T + [178; 180) '{}': () + [202; 203) 't': T + [221; 223) '{}': () + [234; 300) '{ ...(S); }': () + [244; 245) 'x': u32 + [248; 252) 'foo1': fn foo1(T) -> ::Item + [248; 255) 'foo1(S)': u32 + [253; 254) 'S': S + [265; 266) 'y': u32 + [269; 273) 'foo2': fn foo2(T) -> ::Item + [269; 276) 'foo2(S)': u32 + [274; 275) 'S': S + [286; 287) 'z': u32 + [290; 294) 'foo3': fn foo3(T) -> ::Item + [290; 297) 'foo3(S)': u32 + [295; 296) 'S': S + "### + ); +} + +#[test] +fn infer_associated_type_bound() { + assert_snapshot!( + infer(r#" +trait Iterable { + type Item; +} +fn test>() { + let y: T::Item = unknown; +} +"#), + @r###" + [67; 100) '{ ...own; }': () + [77; 78) 'y': {unknown} + [90; 97) 'unknown': {unknown} + "### + ); +} + +#[test] +fn infer_const_body() { + assert_snapshot!( + infer(r#" +const A: u32 = 1 + 1; +static B: u64 = { let x = 1; x }; +"#), + @r###" + [16; 17) '1': u32 + [16; 21) '1 + 1': u32 + [20; 21) '1': u32 + [39; 55) '{ let ...1; x }': u64 + [45; 46) 'x': u64 + [49; 50) '1': u64 + [52; 53) 'x': u64 + "### + ); +} + +#[test] +fn tuple_struct_fields() { + assert_snapshot!( + infer(r#" +struct S(i32, u64); +fn test() -> u64 { + let a = S(4, 6); + let b = a.0; + a.1 +} +"#), + @r###" + [38; 87) '{ ... a.1 }': u64 + [48; 49) 'a': S + [52; 53) 'S': S(i32, u64) -> S + [52; 59) 'S(4, 6)': S + [54; 55) '4': i32 + [57; 58) '6': u64 + [69; 70) 'b': i32 + [73; 74) 'a': S + [73; 76) 'a.0': i32 + [82; 83) 'a': S + [82; 85) 'a.1': u64 + "### + ); +} + +#[test] +fn tuple_struct_with_fn() { + assert_snapshot!( + infer(r#" +struct S(fn(u32) -> u64); +fn test() -> u64 { + let a = S(|i| 2*i); + let b = a.0(4); + a.0(2) +} +"#), + @r###" + [44; 102) '{ ...0(2) }': u64 + [54; 55) 'a': S + [58; 59) 'S': S(fn(u32) -> u64) -> S + [58; 68) 'S(|i| 2*i)': S + [60; 67) '|i| 2*i': |u32| -> u64 + [61; 62) 'i': u32 + [64; 65) '2': u32 + [64; 67) '2*i': u32 + [66; 67) 'i': u32 + [78; 79) 'b': u64 + [82; 83) 'a': S + [82; 85) 'a.0': fn(u32) -> u64 + [82; 88) 'a.0(4)': u64 + [86; 87) '4': u32 + [94; 95) 'a': S + [94; 97) 'a.0': fn(u32) -> u64 + [94; 100) 'a.0(2)': u64 + [98; 99) '2': u32 + "### + ); +} + +#[test] +fn indexing_arrays() { + assert_snapshot!( + infer("fn main() { &mut [9][2]; }"), + @r###" + [10; 26) '{ &mut...[2]; }': () + [12; 23) '&mut [9][2]': &mut {unknown} + [17; 20) '[9]': [i32;_] + [17; 23) '[9][2]': {unknown} + [18; 19) '9': i32 + [21; 22) '2': i32 + "### + ) +} + +#[test] +fn infer_ops_index() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std + +struct Bar; +struct Foo; + +impl std::ops::Index for Bar { + type Output = Foo; +} + +fn test() { + let a = Bar; + let b = a[1]; + b<|>; +} + +//- /std.rs crate:std + +#[prelude_import] use ops::*; +mod ops { + pub trait Index { + type Output; + } +} +"#, + ); + assert_eq!("Foo", type_at_pos(&db, pos)); +} + +#[test] +fn deref_trait() { + let t = type_at( + r#" +//- /main.rs +#[lang = "deref"] +trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +struct Arc; +impl Deref for Arc { + type Target = T; +} + +struct S; +impl S { + fn foo(&self) -> u128 {} +} + +fn test(s: Arc) { + (*s, s.foo())<|>; +} +"#, + ); + assert_eq!(t, "(S, u128)"); +} + +#[test] +fn deref_trait_with_inference_var() { + let t = type_at( + r#" +//- /main.rs +#[lang = "deref"] +trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +struct Arc; +fn new_arc() -> Arc {} +impl Deref for Arc { + type Target = T; +} + +struct S; +fn foo(a: Arc) {} + +fn test() { + let a = new_arc(); + let b = (*a)<|>; + foo(a); +} +"#, + ); + assert_eq!(t, "S"); +} + +#[test] +fn deref_trait_infinite_recursion() { + let t = type_at( + r#" +//- /main.rs +#[lang = "deref"] +trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +struct S; + +impl Deref for S { + type Target = S; +} + +fn test(s: S) { + s.foo()<|>; +} +"#, + ); + assert_eq!(t, "{unknown}"); +} + +#[test] +fn deref_trait_with_question_mark_size() { + let t = type_at( + r#" +//- /main.rs +#[lang = "deref"] +trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +struct Arc; +impl Deref for Arc { + type Target = T; +} + +struct S; +impl S { + fn foo(&self) -> u128 {} +} + +fn test(s: Arc) { + (*s, s.foo())<|>; +} +"#, + ); + assert_eq!(t, "(S, u128)"); +} + +#[test] +fn obligation_from_function_clause() { + let t = type_at( + r#" +//- /main.rs +struct S; + +trait Trait {} +impl Trait for S {} + +fn foo, U>(t: T) -> U {} + +fn test(s: S) { + foo(s)<|>; +} +"#, + ); + assert_eq!(t, "u32"); +} + +#[test] +fn obligation_from_method_clause() { + let t = type_at( + r#" +//- /main.rs +struct S; + +trait Trait {} +impl Trait for S {} + +struct O; +impl O { + fn foo, U>(&self, t: T) -> U {} +} + +fn test() { + O.foo(S)<|>; +} +"#, + ); + assert_eq!(t, "isize"); +} + +#[test] +fn obligation_from_self_method_clause() { + let t = type_at( + r#" +//- /main.rs +struct S; + +trait Trait {} +impl Trait for S {} + +impl S { + fn foo(&self) -> U where Self: Trait {} +} + +fn test() { + S.foo()<|>; +} +"#, + ); + assert_eq!(t, "i64"); +} + +#[test] +fn obligation_from_impl_clause() { + let t = type_at( + r#" +//- /main.rs +struct S; + +trait Trait {} +impl Trait<&str> for S {} + +struct O; +impl> O { + fn foo(&self) -> U {} +} + +fn test(o: O) { + o.foo()<|>; +} +"#, + ); + assert_eq!(t, "&str"); +} + +#[test] +fn generic_param_env_1() { + let t = type_at( + r#" +//- /main.rs +trait Clone {} +trait Trait { fn foo(self) -> u128; } +struct S; +impl Clone for S {} +impl Trait for T where T: Clone {} +fn test(t: T) { t.foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + +#[test] +fn generic_param_env_1_not_met() { + let t = type_at( + r#" +//- /main.rs +trait Clone {} +trait Trait { fn foo(self) -> u128; } +struct S; +impl Clone for S {} +impl Trait for T where T: Clone {} +fn test(t: T) { t.foo()<|>; } +"#, + ); + assert_eq!(t, "{unknown}"); +} + +#[test] +fn generic_param_env_2() { + let t = type_at( + r#" +//- /main.rs +trait Trait { fn foo(self) -> u128; } +struct S; +impl Trait for S {} +fn test(t: T) { t.foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + +#[test] +fn generic_param_env_2_not_met() { + let t = type_at( + r#" +//- /main.rs +trait Trait { fn foo(self) -> u128; } +struct S; +impl Trait for S {} +fn test(t: T) { t.foo()<|>; } +"#, + ); + assert_eq!(t, "{unknown}"); +} + +#[test] +fn generic_param_env_deref() { + let t = type_at( + r#" +//- /main.rs +#[lang = "deref"] +trait Deref { + type Target; +} +trait Trait {} +impl Deref for T where T: Trait { + type Target = i128; +} +fn test(t: T) { (*t)<|>; } +"#, + ); + assert_eq!(t, "i128"); +} + +#[test] +fn associated_type_placeholder() { + let t = type_at( + r#" +//- /main.rs +pub trait ApplyL { + type Out; +} + +pub struct RefMutL; + +impl ApplyL for RefMutL { + type Out = ::Out; +} + +fn test() { + let y: as ApplyL>::Out = no_matter; + y<|>; +} +"#, + ); + // inside the generic function, the associated type gets normalized to a placeholder `ApplL::Out` [https://rust-lang.github.io/rustc-guide/traits/associated-types.html#placeholder-associated-types]. + // FIXME: fix type parameter names going missing when going through Chalk + assert_eq!(t, "ApplyL::Out<[missing name]>"); +} + +#[test] +fn associated_type_placeholder_2() { + let t = type_at( + r#" +//- /main.rs +pub trait ApplyL { + type Out; +} +fn foo(t: T) -> ::Out; + +fn test(t: T) { + let y = foo(t); + y<|>; +} +"#, + ); + // FIXME here Chalk doesn't normalize the type to a placeholder. I think we + // need to add a rule like Normalize(::Out -> ApplyL::Out) + // to the trait env ourselves here; probably Chalk can't do this by itself. + // assert_eq!(t, "ApplyL::Out<[missing name]>"); + assert_eq!(t, "{unknown}"); +} + +#[test] +fn impl_trait() { + assert_snapshot!( + infer(r#" +trait Trait { + fn foo(&self) -> T; + fn foo2(&self) -> i64; +} +fn bar() -> impl Trait {} + +fn test(x: impl Trait, y: &impl Trait) { + x; + y; + let z = bar(); + x.foo(); + y.foo(); + z.foo(); + x.foo2(); + y.foo2(); + z.foo2(); +} +"#), + @r###" + [30; 34) 'self': &Self + [55; 59) 'self': &Self + [99; 101) '{}': () + [111; 112) 'x': impl Trait + [131; 132) 'y': &impl Trait + [152; 269) '{ ...2(); }': () + [158; 159) 'x': impl Trait + [165; 166) 'y': &impl Trait + [176; 177) 'z': impl Trait + [180; 183) 'bar': fn bar() -> impl Trait + [180; 185) 'bar()': impl Trait + [191; 192) 'x': impl Trait + [191; 198) 'x.foo()': u64 + [204; 205) 'y': &impl Trait + [204; 211) 'y.foo()': u64 + [217; 218) 'z': impl Trait + [217; 224) 'z.foo()': u64 + [230; 231) 'x': impl Trait + [230; 238) 'x.foo2()': i64 + [244; 245) 'y': &impl Trait + [244; 252) 'y.foo2()': i64 + [258; 259) 'z': impl Trait + [258; 266) 'z.foo2()': i64 + "### + ); +} + +#[test] +fn dyn_trait() { + assert_snapshot!( + infer(r#" +trait Trait { + fn foo(&self) -> T; + fn foo2(&self) -> i64; +} +fn bar() -> dyn Trait {} + +fn test(x: dyn Trait, y: &dyn Trait) { + x; + y; + let z = bar(); + x.foo(); + y.foo(); + z.foo(); + x.foo2(); + y.foo2(); + z.foo2(); +} +"#), + @r###" + [30; 34) 'self': &Self + [55; 59) 'self': &Self + [98; 100) '{}': () + [110; 111) 'x': dyn Trait + [129; 130) 'y': &dyn Trait + [149; 266) '{ ...2(); }': () + [155; 156) 'x': dyn Trait + [162; 163) 'y': &dyn Trait + [173; 174) 'z': dyn Trait + [177; 180) 'bar': fn bar() -> dyn Trait + [177; 182) 'bar()': dyn Trait + [188; 189) 'x': dyn Trait + [188; 195) 'x.foo()': u64 + [201; 202) 'y': &dyn Trait + [201; 208) 'y.foo()': u64 + [214; 215) 'z': dyn Trait + [214; 221) 'z.foo()': u64 + [227; 228) 'x': dyn Trait + [227; 235) 'x.foo2()': i64 + [241; 242) 'y': &dyn Trait + [241; 249) 'y.foo2()': i64 + [255; 256) 'z': dyn Trait + [255; 263) 'z.foo2()': i64 + "### + ); +} + +#[test] +fn dyn_trait_bare() { + assert_snapshot!( + infer(r#" +trait Trait { + fn foo(&self) -> u64; +} +fn bar() -> Trait {} + +fn test(x: Trait, y: &Trait) -> u64 { + x; + y; + let z = bar(); + x.foo(); + y.foo(); + z.foo(); +} +"#), + @r###" + [27; 31) 'self': &Self + [61; 63) '{}': () + [73; 74) 'x': dyn Trait + [83; 84) 'y': &dyn Trait + [101; 176) '{ ...o(); }': () + [107; 108) 'x': dyn Trait + [114; 115) 'y': &dyn Trait + [125; 126) 'z': dyn Trait + [129; 132) 'bar': fn bar() -> dyn Trait + [129; 134) 'bar()': dyn Trait + [140; 141) 'x': dyn Trait + [140; 147) 'x.foo()': u64 + [153; 154) 'y': &dyn Trait + [153; 160) 'y.foo()': u64 + [166; 167) 'z': dyn Trait + [166; 173) 'z.foo()': u64 + "### + ); +} + +#[test] +fn weird_bounds() { + assert_snapshot!( + infer(r#" +trait Trait {} +fn test() { + let a: impl Trait + 'lifetime = foo; + let b: impl 'lifetime = foo; + let b: impl (Trait) = foo; + let b: impl ('lifetime) = foo; + let d: impl ?Sized = foo; + let e: impl Trait + ?Sized = foo; +} +"#), + @r###" + [26; 237) '{ ...foo; }': () + [36; 37) 'a': impl Trait + {error} + [64; 67) 'foo': impl Trait + {error} + [77; 78) 'b': impl {error} + [97; 100) 'foo': impl {error} + [110; 111) 'b': impl Trait + [128; 131) 'foo': impl Trait + [141; 142) 'b': impl {error} + [163; 166) 'foo': impl {error} + [176; 177) 'd': impl {error} + [193; 196) 'foo': impl {error} + [206; 207) 'e': impl Trait + {error} + [231; 234) 'foo': impl Trait + {error} + "### + ); +} + +#[test] +fn error_bound_chalk() { + let t = type_at( + r#" +//- /main.rs +trait Trait { + fn foo(&self) -> u32 {} +} + +fn test(x: (impl Trait + UnknownTrait)) { + x.foo()<|>; +} +"#, + ); + assert_eq!(t, "u32"); +} + +#[test] +fn assoc_type_bindings() { + assert_snapshot!( + infer(r#" +trait Trait { + type Type; +} + +fn get(t: T) -> ::Type {} +fn get2>(t: T) -> U {} +fn set>(t: T) -> T {t} + +struct S; +impl Trait for S { type Type = T; } + +fn test>(x: T, y: impl Trait) { + get(x); + get2(x); + get(y); + get2(y); + get(set(S)); + get2(set(S)); + get2(S::); +} +"#), + @r###" + [50; 51) 't': T + [78; 80) '{}': () + [112; 113) 't': T + [123; 125) '{}': () + [155; 156) 't': T + [166; 169) '{t}': T + [167; 168) 't': T + [257; 258) 'x': T + [263; 264) 'y': impl Trait + [290; 398) '{ ...r>); }': () + [296; 299) 'get': fn get(T) -> ::Type + [296; 302) 'get(x)': {unknown} + [300; 301) 'x': T + [308; 312) 'get2': fn get2<{unknown}, T>(T) -> U + [308; 315) 'get2(x)': {unknown} + [313; 314) 'x': T + [321; 324) 'get': fn get>(T) -> ::Type + [321; 327) 'get(y)': {unknown} + [325; 326) 'y': impl Trait + [333; 337) 'get2': fn get2<{unknown}, impl Trait>(T) -> U + [333; 340) 'get2(y)': {unknown} + [338; 339) 'y': impl Trait + [346; 349) 'get': fn get>(T) -> ::Type + [346; 357) 'get(set(S))': u64 + [350; 353) 'set': fn set>(T) -> T + [350; 356) 'set(S)': S + [354; 355) 'S': S + [363; 367) 'get2': fn get2>(T) -> U + [363; 375) 'get2(set(S))': u64 + [368; 371) 'set': fn set>(T) -> T + [368; 374) 'set(S)': S + [372; 373) 'S': S + [381; 385) 'get2': fn get2>(T) -> U + [381; 395) 'get2(S::)': str + [386; 394) 'S::': S + "### + ); +} + +#[test] +fn impl_trait_assoc_binding_projection_bug() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std +pub trait Language { + type Kind; +} +pub enum RustLanguage {} +impl Language for RustLanguage { + type Kind = SyntaxKind; +} +struct SyntaxNode {} +fn foo() -> impl Iterator> {} + +trait Clone { + fn clone(&self) -> Self; +} + +fn api_walkthrough() { + for node in foo() { + node.clone()<|>; + } +} + +//- /std.rs crate:std +#[prelude_import] use iter::*; +mod iter { + trait IntoIterator { + type Item; + } + trait Iterator { + type Item; + } + impl IntoIterator for T { + type Item = ::Item; + } +} +"#, + ); + assert_eq!("{unknown}", type_at_pos(&db, pos)); +} + +#[test] +fn projection_eq_within_chalk() { + // std::env::set_var("CHALK_DEBUG", "1"); + assert_snapshot!( + infer(r#" +trait Trait1 { + type Type; +} +trait Trait2 { + fn foo(self) -> T; +} +impl Trait2 for U where U: Trait1 {} + +fn test>(x: T) { + x.foo(); +} +"#), + @r###" + [62; 66) 'self': Self + [164; 165) 'x': T + [170; 186) '{ ...o(); }': () + [176; 177) 'x': T + [176; 183) 'x.foo()': {unknown} + "### + ); +} + +#[test] +fn where_clause_trait_in_scope_for_method_resolution() { + let t = type_at( + r#" +//- /main.rs +mod foo { + trait Trait { + fn foo(&self) -> u32 {} + } +} + +fn test(x: T) { + x.foo()<|>; +} +"#, + ); + assert_eq!(t, "u32"); +} + +#[test] +fn super_trait_method_resolution() { + assert_snapshot!( + infer(r#" +mod foo { + trait SuperTrait { + fn foo(&self) -> u32 {} + } +} +trait Trait1: foo::SuperTrait {} +trait Trait2 where Self: foo::SuperTrait {} + +fn test(x: T, y: U) { + x.foo(); + y.foo(); +} +"#), + @r###" + [50; 54) 'self': &Self + [63; 65) '{}': () + [182; 183) 'x': T + [188; 189) 'y': U + [194; 223) '{ ...o(); }': () + [200; 201) 'x': T + [200; 207) 'x.foo()': u32 + [213; 214) 'y': U + [213; 220) 'y.foo()': u32 + "### + ); +} + +#[test] +fn super_trait_cycle() { + // This just needs to not crash + assert_snapshot!( + infer(r#" +trait A: B {} +trait B: A {} + +fn test(x: T) { + x.foo(); +} +"#), + @r###" + [44; 45) 'x': T + [50; 66) '{ ...o(); }': () + [56; 57) 'x': T + [56; 63) 'x.foo()': {unknown} + "### + ); +} + +#[test] +fn super_trait_assoc_type_bounds() { + assert_snapshot!( + infer(r#" +trait SuperTrait { type Type; } +trait Trait where Self: SuperTrait {} + +fn get2>(t: T) -> U {} +fn set>(t: T) -> T {t} + +struct S; +impl SuperTrait for S { type Type = T; } +impl Trait for S {} + +fn test() { + get2(set(S)); +} +"#), + @r###" + [103; 104) 't': T + [114; 116) '{}': () + [146; 147) 't': T + [157; 160) '{t}': T + [158; 159) 't': T + [259; 280) '{ ...S)); }': () + [265; 269) 'get2': fn get2>(T) -> U + [265; 277) 'get2(set(S))': u64 + [270; 273) 'set': fn set>(T) -> T + [270; 276) 'set(S)': S + [274; 275) 'S': S + "### + ); +} + +#[test] +fn fn_trait() { + assert_snapshot!( + infer(r#" +trait FnOnce { + type Output; + + fn call_once(self, args: Args) -> >::Output; +} + +fn test u128>(f: F) { + f.call_once((1, 2)); +} +"#), + @r###" + [57; 61) 'self': Self + [63; 67) 'args': Args + [150; 151) 'f': F + [156; 184) '{ ...2)); }': () + [162; 163) 'f': F + [162; 181) 'f.call...1, 2))': {unknown} + [174; 180) '(1, 2)': (u32, u64) + [175; 176) '1': u32 + [178; 179) '2': u64 + "### + ); +} + +#[test] +fn closure_1() { + assert_snapshot!( + infer(r#" +#[lang = "fn_once"] +trait FnOnce { + type Output; +} + +enum Option { Some(T), None } +impl Option { + fn map U>(self, f: F) -> Option {} +} + +fn test() { + let x = Option::Some(1u32); + x.map(|v| v + 1); + x.map(|_v| 1u64); + let y: Option = x.map(|_v| 1); +} +"#), + @r###" + [148; 152) 'self': Option + [154; 155) 'f': F + [173; 175) '{}': () + [189; 308) '{ ... 1); }': () + [199; 200) 'x': Option + [203; 215) 'Option::Some': Some(T) -> Option + [203; 221) 'Option...(1u32)': Option + [216; 220) '1u32': u32 + [227; 228) 'x': Option + [227; 243) 'x.map(...v + 1)': Option + [233; 242) '|v| v + 1': |u32| -> u32 + [234; 235) 'v': u32 + [237; 238) 'v': u32 + [237; 242) 'v + 1': u32 + [241; 242) '1': u32 + [249; 250) 'x': Option + [249; 265) 'x.map(... 1u64)': Option + [255; 264) '|_v| 1u64': |u32| -> u64 + [256; 258) '_v': u32 + [260; 264) '1u64': u64 + [275; 276) 'y': Option + [292; 293) 'x': Option + [292; 305) 'x.map(|_v| 1)': Option + [298; 304) '|_v| 1': |u32| -> i64 + [299; 301) '_v': u32 + [303; 304) '1': i64 + "### + ); +} + +#[test] +fn closure_2() { + assert_snapshot!( + infer(r#" +trait FnOnce { + type Output; +} + +fn test u64>(f: F) { + f(1); + let g = |v| v + 1; + g(1u64); + let h = |v| 1u128 + v; +} +"#), + @r###" + [73; 74) 'f': F + [79; 155) '{ ...+ v; }': () + [85; 86) 'f': F + [85; 89) 'f(1)': {unknown} + [87; 88) '1': i32 + [99; 100) 'g': |u64| -> i32 + [103; 112) '|v| v + 1': |u64| -> i32 + [104; 105) 'v': u64 + [107; 108) 'v': u64 + [107; 112) 'v + 1': i32 + [111; 112) '1': i32 + [118; 119) 'g': |u64| -> i32 + [118; 125) 'g(1u64)': i32 + [120; 124) '1u64': u64 + [135; 136) 'h': |u128| -> u128 + [139; 152) '|v| 1u128 + v': |u128| -> u128 + [140; 141) 'v': u128 + [143; 148) '1u128': u128 + [143; 152) '1u128 + v': u128 + [151; 152) 'v': u128 + "### + ); +} + +#[test] +fn closure_as_argument_inference_order() { + assert_snapshot!( + infer(r#" +#[lang = "fn_once"] +trait FnOnce { + type Output; +} + +fn foo1 U>(x: T, f: F) -> U {} +fn foo2 U>(f: F, x: T) -> U {} + +struct S; +impl S { + fn method(self) -> u64; + + fn foo1 U>(self, x: T, f: F) -> U {} + fn foo2 U>(self, f: F, x: T) -> U {} +} + +fn test() { + let x1 = foo1(S, |s| s.method()); + let x2 = foo2(|s| s.method(), S); + let x3 = S.foo1(S, |s| s.method()); + let x4 = S.foo2(|s| s.method(), S); +} +"#), + @r###" + [95; 96) 'x': T + [101; 102) 'f': F + [112; 114) '{}': () + [148; 149) 'f': F + [154; 155) 'x': T + [165; 167) '{}': () + [202; 206) 'self': S + [254; 258) 'self': S + [260; 261) 'x': T + [266; 267) 'f': F + [277; 279) '{}': () + [317; 321) 'self': S + [323; 324) 'f': F + [329; 330) 'x': T + [340; 342) '{}': () + [356; 515) '{ ... S); }': () + [366; 368) 'x1': u64 + [371; 375) 'foo1': fn foo1 u64>(T, F) -> U + [371; 394) 'foo1(S...hod())': u64 + [376; 377) 'S': S + [379; 393) '|s| s.method()': |S| -> u64 + [380; 381) 's': S + [383; 384) 's': S + [383; 393) 's.method()': u64 + [404; 406) 'x2': u64 + [409; 413) 'foo2': fn foo2 u64>(F, T) -> U + [409; 432) 'foo2(|...(), S)': u64 + [414; 428) '|s| s.method()': |S| -> u64 + [415; 416) 's': S + [418; 419) 's': S + [418; 428) 's.method()': u64 + [430; 431) 'S': S + [442; 444) 'x3': u64 + [447; 448) 'S': S + [447; 472) 'S.foo1...hod())': u64 + [454; 455) 'S': S + [457; 471) '|s| s.method()': |S| -> u64 + [458; 459) 's': S + [461; 462) 's': S + [461; 471) 's.method()': u64 + [482; 484) 'x4': u64 + [487; 488) 'S': S + [487; 512) 'S.foo2...(), S)': u64 + [494; 508) '|s| s.method()': |S| -> u64 + [495; 496) 's': S + [498; 499) 's': S + [498; 508) 's.method()': u64 + [510; 511) 'S': S + "### + ); +} + +#[test] +fn unselected_projection_in_trait_env_1() { + let t = type_at( + r#" +//- /main.rs +trait Trait { + type Item; +} + +trait Trait2 { + fn foo(&self) -> u32; +} + +fn test() where T::Item: Trait2 { + let x: T::Item = no_matter; + x.foo()<|>; +} +"#, + ); + assert_eq!(t, "u32"); +} + +#[test] +fn unselected_projection_in_trait_env_2() { + let t = type_at( + r#" +//- /main.rs +trait Trait { + type Item; +} + +trait Trait2 { + fn foo(&self) -> u32; +} + +fn test() where T::Item: Trait2, T: Trait, U: Trait<()> { + let x: T::Item = no_matter; + x.foo()<|>; +} +"#, + ); + assert_eq!(t, "u32"); +} + +#[test] +fn trait_impl_self_ty() { + let t = type_at( + r#" +//- /main.rs +trait Trait { + fn foo(&self); +} + +struct S; + +impl Trait for S {} + +fn test() { + S.foo()<|>; +} +"#, + ); + assert_eq!(t, "()"); +} + +#[test] +fn trait_impl_self_ty_cycle() { + let t = type_at( + r#" +//- /main.rs +trait Trait { + fn foo(&self); +} + +struct S; + +impl Trait for S {} + +fn test() { + S.foo()<|>; +} +"#, + ); + assert_eq!(t, "{unknown}"); +} + +#[test] +fn unselected_projection_in_trait_env_cycle_1() { + let t = type_at( + r#" +//- /main.rs +trait Trait { + type Item; +} + +trait Trait2 {} + +fn test() where T: Trait2 { + let x: T::Item = no_matter<|>; +} +"#, + ); + // this is a legitimate cycle + assert_eq!(t, "{unknown}"); +} + +#[test] +fn unselected_projection_in_trait_env_cycle_2() { + let t = type_at( + r#" +//- /main.rs +trait Trait { + type Item; +} + +fn test() where T: Trait, U: Trait { + let x: T::Item = no_matter<|>; +} +"#, + ); + // this is a legitimate cycle + assert_eq!(t, "{unknown}"); +} + +#[test] +fn unify_impl_trait() { + covers!(insert_vars_for_impl_trait); + assert_snapshot!( + infer_with_mismatches(r#" +trait Trait {} + +fn foo(x: impl Trait) { loop {} } +fn bar(x: impl Trait) -> T { loop {} } + +struct S(T); +impl Trait for S {} + +fn default() -> T { loop {} } + +fn test() -> impl Trait { + let s1 = S(default()); + foo(s1); + let x: i32 = bar(S(default())); + S(default()) +} +"#, true), + @r###" + [27; 28) 'x': impl Trait + [47; 58) '{ loop {} }': () + [49; 56) 'loop {}': ! + [54; 56) '{}': () + [69; 70) 'x': impl Trait + [92; 103) '{ loop {} }': T + [94; 101) 'loop {}': ! + [99; 101) '{}': () + [172; 183) '{ loop {} }': T + [174; 181) 'loop {}': ! + [179; 181) '{}': () + [214; 310) '{ ...t()) }': S + [224; 226) 's1': S + [229; 230) 'S': S(T) -> S + [229; 241) 'S(default())': S + [231; 238) 'default': fn default() -> T + [231; 240) 'default()': u32 + [247; 250) 'foo': fn foo(impl Trait) -> () + [247; 254) 'foo(s1)': () + [251; 253) 's1': S + [264; 265) 'x': i32 + [273; 276) 'bar': fn bar(impl Trait) -> T + [273; 290) 'bar(S(...lt()))': i32 + [277; 278) 'S': S(T) -> S + [277; 289) 'S(default())': S + [279; 286) 'default': fn default() -> T + [279; 288) 'default()': i32 + [296; 297) 'S': S(T) -> S + [296; 308) 'S(default())': S + [298; 305) 'default': fn default() -> T + [298; 307) 'default()': i32 + "### + ); +} diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 76189a60b1..c4dc857bc7 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs @@ -1,7 +1,7 @@ //! Trait solving using Chalk. use std::sync::{Arc, Mutex}; -use chalk_ir::{cast::Cast, family::ChalkIr}; +use chalk_ir::cast::Cast; use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId}; use log::debug; use ra_db::{impl_intern_key, salsa, CrateId}; @@ -12,14 +12,15 @@ use crate::db::HirDatabase; use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; -use self::chalk::{from_chalk, ToChalk}; +use self::chalk::{from_chalk, ToChalk, TypeFamily}; pub(crate) mod chalk; +mod builtin; #[derive(Debug, Clone)] pub struct TraitSolver { krate: CrateId, - inner: Arc>>, + inner: Arc>>, } /// We need eq for salsa @@ -35,8 +36,8 @@ impl TraitSolver { fn solve( &self, db: &impl HirDatabase, - goal: &chalk_ir::UCanonical>>, - ) -> Option> { + goal: &chalk_ir::UCanonical>>, + ) -> Option> { let context = ChalkContext { db, krate: self.krate }; debug!("solve goal: {:?}", goal); let mut solver = match self.inner.lock() { @@ -200,17 +201,17 @@ pub(crate) fn trait_solve_query( fn solution_from_chalk( db: &impl HirDatabase, - solution: chalk_solve::Solution, + solution: chalk_solve::Solution, ) -> Solution { - let convert_subst = |subst: chalk_ir::Canonical>| { + let convert_subst = |subst: chalk_ir::Canonical>| { let value = subst .value .parameters .into_iter() .map(|p| { - let ty = match p { - chalk_ir::Parameter(chalk_ir::ParameterKind::Ty(ty)) => from_chalk(db, ty), - chalk_ir::Parameter(chalk_ir::ParameterKind::Lifetime(_)) => unimplemented!(), + let ty = match p.ty() { + Some(ty) => from_chalk(db, ty.clone()), + None => unimplemented!(), }; ty }) @@ -290,7 +291,7 @@ impl FnTrait { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ClosureFnTraitImplData { def: DefWithBodyId, expr: ExprId, @@ -299,7 +300,7 @@ pub struct ClosureFnTraitImplData { /// An impl. Usually this comes from an impl block, but some built-in types get /// synthetic impls. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Impl { /// A normal impl from an impl block. ImplBlock(ImplId), diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs new file mode 100644 index 0000000000..dd41176f0d --- /dev/null +++ b/crates/ra_hir_ty/src/traits/builtin.rs @@ -0,0 +1,178 @@ +//! This module provides the built-in trait implementations, e.g. to make +//! closures implement `Fn`. +use hir_def::{expr::Expr, lang_item::LangItemTarget, TraitId, TypeAliasId}; +use hir_expand::name::name; +use ra_db::CrateId; + +use super::{AssocTyValue, Impl}; +use crate::{db::HirDatabase, ApplicationTy, Substs, TraitRef, Ty, TypeCtor}; + +pub(super) struct BuiltinImplData { + pub num_vars: usize, + pub trait_ref: TraitRef, + pub where_clauses: Vec, + pub assoc_ty_values: Vec, +} + +pub(super) struct BuiltinImplAssocTyValueData { + pub impl_: Impl, + pub assoc_ty_id: TypeAliasId, + pub num_vars: usize, + pub value: Ty, +} + +pub(super) fn get_builtin_impls( + db: &impl HirDatabase, + krate: CrateId, + ty: &Ty, + trait_: TraitId, + mut callback: impl FnMut(Impl), +) { + // Note: since impl_datum needs to be infallible, we need to make sure here + // that we have all prerequisites to build the respective impls. + if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty { + for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter() + { + if let Some(actual_trait) = get_fn_trait(db, krate, fn_trait) { + if trait_ == actual_trait { + let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait }; + if check_closure_fn_trait_impl_prerequisites(db, krate, impl_) { + callback(Impl::ClosureFnTraitImpl(impl_)); + } + } + } + } + } +} + +pub(super) fn impl_datum(db: &impl HirDatabase, krate: CrateId, impl_: Impl) -> BuiltinImplData { + match impl_ { + Impl::ImplBlock(_) => unreachable!(), + Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data), + } +} + +pub(super) fn associated_ty_value( + db: &impl HirDatabase, + krate: CrateId, + data: AssocTyValue, +) -> BuiltinImplAssocTyValueData { + match data { + AssocTyValue::TypeAlias(_) => unreachable!(), + AssocTyValue::ClosureFnTraitImplOutput(data) => { + closure_fn_trait_output_assoc_ty_value(db, krate, data) + } + } +} + +fn check_closure_fn_trait_impl_prerequisites( + db: &impl HirDatabase, + krate: CrateId, + data: super::ClosureFnTraitImplData, +) -> bool { + // the respective Fn/FnOnce/FnMut trait needs to exist + if get_fn_trait(db, krate, data.fn_trait).is_none() { + return false; + } + + // FIXME: there are more assumptions that we should probably check here: + // the traits having no type params, FnOnce being a supertrait + + // the FnOnce trait needs to exist and have an assoc type named Output + let fn_once_trait = match get_fn_trait(db, krate, super::FnTrait::FnOnce) { + Some(t) => t, + None => return false, + }; + db.trait_data(fn_once_trait).associated_type_by_name(&name![Output]).is_some() +} + +fn closure_fn_trait_impl_datum( + db: &impl HirDatabase, + krate: CrateId, + data: super::ClosureFnTraitImplData, +) -> BuiltinImplData { + // for some closure |X, Y| -> Z: + // impl Fn<(T, U)> for closure V> { Output = V } + + let trait_ = get_fn_trait(db, krate, data.fn_trait) // get corresponding fn trait + // the existence of the Fn trait has been checked before + .expect("fn trait for closure impl missing"); + + let num_args: u16 = match &db.body(data.def.into())[data.expr] { + Expr::Lambda { args, .. } => args.len() as u16, + _ => { + log::warn!("closure for closure type {:?} not found", data); + 0 + } + }; + + let arg_ty = Ty::apply( + TypeCtor::Tuple { cardinality: num_args }, + Substs::builder(num_args as usize).fill_with_bound_vars(0).build(), + ); + let sig_ty = Ty::apply( + TypeCtor::FnPtr { num_args }, + Substs::builder(num_args as usize + 1).fill_with_bound_vars(0).build(), + ); + + let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty); + + let trait_ref = TraitRef { + trait_: trait_.into(), + substs: Substs::build_for_def(db, trait_).push(self_ty).push(arg_ty).build(), + }; + + let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data.clone()); + + BuiltinImplData { + num_vars: num_args as usize + 1, + trait_ref, + where_clauses: Vec::new(), + assoc_ty_values: vec![output_ty_id], + } +} + +fn closure_fn_trait_output_assoc_ty_value( + db: &impl HirDatabase, + krate: CrateId, + data: super::ClosureFnTraitImplData, +) -> BuiltinImplAssocTyValueData { + let impl_ = Impl::ClosureFnTraitImpl(data.clone()); + + let num_args: u16 = match &db.body(data.def.into())[data.expr] { + Expr::Lambda { args, .. } => args.len() as u16, + _ => { + log::warn!("closure for closure type {:?} not found", data); + 0 + } + }; + + let output_ty = Ty::Bound(num_args.into()); + + let fn_once_trait = + get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist"); + + let output_ty_id = db + .trait_data(fn_once_trait) + .associated_type_by_name(&name![Output]) + .expect("assoc ty value should not exist"); + + BuiltinImplAssocTyValueData { + impl_, + assoc_ty_id: output_ty_id, + num_vars: num_args as usize + 1, + value: output_ty, + } +} + +fn get_fn_trait( + db: &impl HirDatabase, + krate: CrateId, + fn_trait: super::FnTrait, +) -> Option { + let target = db.lang_item(krate, fn_trait.lang_item_name().into())?; + match target { + LangItemTarget::TraitId(t) => Some(t), + _ => None, + } +} diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 35de37e6b6..555930c9bf 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -1,32 +1,99 @@ //! Conversion code from/to Chalk. -use std::sync::Arc; +use std::{fmt, sync::Arc}; use log::debug; -use chalk_ir::{ - cast::Cast, family::ChalkIr, Identifier, Parameter, PlaceholderIndex, TypeId, TypeKindId, - TypeName, UniverseIndex, +use chalk_ir::{cast::Cast, Parameter, PlaceholderIndex, TypeName, UniverseIndex}; + +use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId}; +use ra_db::{ + salsa::{InternId, InternKey}, + CrateId, }; -use chalk_rust_ir::{AssociatedTyDatum, AssociatedTyValue, ImplDatum, StructDatum, TraitDatum}; -use ra_db::CrateId; -use hir_def::{ - expr::Expr, lang_item::LangItemTarget, AssocItemId, AstItemDef, ContainerId, GenericDefId, - ImplId, Lookup, TraitId, TypeAliasId, -}; -use hir_expand::name; - -use ra_db::salsa::{InternId, InternKey}; - -use super::{AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; +use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; use crate::{ - db::HirDatabase, display::HirDisplay, ApplicationTy, GenericPredicate, ImplTy, ProjectionTy, - Substs, TraitRef, Ty, TypeCtor, TypeWalk, + db::HirDatabase, display::HirDisplay, utils::generics, ApplicationTy, GenericPredicate, + ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk, }; -/// This represents a trait whose name we could not resolve. -const UNKNOWN_TRAIT: chalk_ir::TraitId = - chalk_ir::TraitId(chalk_ir::RawId { index: u32::max_value() }); +#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct TypeFamily {} + +impl chalk_ir::family::TypeFamily for TypeFamily { + type InternedType = Box>; + type InternedLifetime = chalk_ir::LifetimeData; + type InternedParameter = chalk_ir::ParameterData; + type DefId = InternId; + + // FIXME: implement these + fn debug_struct_id( + _type_kind_id: chalk_ir::StructId, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option { + None + } + + fn debug_trait_id( + _type_kind_id: chalk_ir::TraitId, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option { + None + } + + fn debug_assoc_type_id( + _id: chalk_ir::AssocTypeId, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option { + None + } + + fn debug_projection( + _projection: &chalk_ir::ProjectionTy, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option { + None + } + + fn intern_ty(ty: chalk_ir::TyData) -> Box> { + Box::new(ty) + } + + fn ty_data(ty: &Box>) -> &chalk_ir::TyData { + ty + } + + fn intern_lifetime(lifetime: chalk_ir::LifetimeData) -> chalk_ir::LifetimeData { + lifetime + } + + fn lifetime_data(lifetime: &chalk_ir::LifetimeData) -> &chalk_ir::LifetimeData { + lifetime + } + + fn intern_parameter(parameter: chalk_ir::ParameterData) -> chalk_ir::ParameterData { + parameter + } + + fn parameter_data(parameter: &chalk_ir::ParameterData) -> &chalk_ir::ParameterData { + parameter + } +} + +impl chalk_ir::family::HasTypeFamily for TypeFamily { + type TypeFamily = Self; +} + +pub type AssocTypeId = chalk_ir::AssocTypeId; +pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum; +pub type TraitId = chalk_ir::TraitId; +pub type TraitDatum = chalk_rust_ir::TraitDatum; +pub type StructId = chalk_ir::StructId; +pub type StructDatum = chalk_rust_ir::StructDatum; +pub type ImplId = chalk_ir::ImplId; +pub type ImplDatum = chalk_rust_ir::ImplDatum; +pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId; +pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue; pub(super) trait ToChalk { type Chalk; @@ -42,21 +109,11 @@ where } impl ToChalk for Ty { - type Chalk = chalk_ir::Ty; - fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty { + type Chalk = chalk_ir::Ty; + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty { match self { Ty::Apply(apply_ty) => { - let name = match apply_ty.ctor { - TypeCtor::AssociatedType(type_alias) => { - let type_id = type_alias.to_chalk(db); - TypeName::AssociatedType(type_id) - } - _ => { - // other TypeCtors get interned and turned into a chalk StructId - let struct_id = apply_ty.ctor.to_chalk(db); - TypeName::TypeKindId(struct_id.into()) - } - }; + let name = apply_ty.ctor.to_chalk(db); let parameters = apply_ty.parameters.to_chalk(db); chalk_ir::ApplicationTy { name, parameters }.cast().intern() } @@ -66,17 +123,30 @@ impl ToChalk for Ty { chalk_ir::ProjectionTy { associated_ty_id, parameters }.cast().intern() } Ty::Param { idx, .. } => { - PlaceholderIndex { ui: UniverseIndex::ROOT, idx: idx as usize }.to_ty::() + PlaceholderIndex { ui: UniverseIndex::ROOT, idx: idx as usize } + .to_ty::() } Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx as usize).intern(), Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"), Ty::Dyn(predicates) => { - let where_clauses = predicates.iter().cloned().map(|p| p.to_chalk(db)).collect(); - chalk_ir::TyData::Dyn(make_binders(where_clauses, 1)).intern() + let where_clauses = predicates + .iter() + .filter(|p| !p.is_error()) + .cloned() + .map(|p| p.to_chalk(db)) + .collect(); + let bounded_ty = chalk_ir::BoundedTy { bounds: make_binders(where_clauses, 1) }; + chalk_ir::TyData::Dyn(bounded_ty).intern() } Ty::Opaque(predicates) => { - let where_clauses = predicates.iter().cloned().map(|p| p.to_chalk(db)).collect(); - chalk_ir::TyData::Opaque(make_binders(where_clauses, 1)).intern() + let where_clauses = predicates + .iter() + .filter(|p| !p.is_error()) + .cloned() + .map(|p| p.to_chalk(db)) + .collect(); + let bounded_ty = chalk_ir::BoundedTy { bounds: make_binders(where_clauses, 1) }; + chalk_ir::TyData::Opaque(bounded_ty).intern() } Ty::Unknown => { let parameters = Vec::new(); @@ -85,30 +155,19 @@ impl ToChalk for Ty { } } } - fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty) -> Self { + fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty) -> Self { match chalk.data().clone() { - chalk_ir::TyData::Apply(apply_ty) => { - // FIXME this is kind of hacky due to the fact that - // TypeName::Placeholder is a Ty::Param on our side - match apply_ty.name { - TypeName::TypeKindId(TypeKindId::StructId(struct_id)) => { - let ctor = from_chalk(db, struct_id); - let parameters = from_chalk(db, apply_ty.parameters); - Ty::Apply(ApplicationTy { ctor, parameters }) - } - TypeName::AssociatedType(type_id) => { - let ctor = TypeCtor::AssociatedType(from_chalk(db, type_id)); - let parameters = from_chalk(db, apply_ty.parameters); - Ty::Apply(ApplicationTy { ctor, parameters }) - } - TypeName::Error => Ty::Unknown, - // FIXME handle TypeKindId::Trait/Type here - TypeName::TypeKindId(_) => unimplemented!(), - TypeName::Placeholder(idx) => { - assert_eq!(idx.ui, UniverseIndex::ROOT); - Ty::Param { idx: idx.idx as u32, name: crate::Name::missing() } - } + chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name { + TypeName::Error => Ty::Unknown, + _ => { + let ctor = from_chalk(db, apply_ty.name); + let parameters = from_chalk(db, apply_ty.parameters); + Ty::Apply(ApplicationTy { ctor, parameters }) } + }, + chalk_ir::TyData::Placeholder(idx) => { + assert_eq!(idx.ui, UniverseIndex::ROOT); + Ty::Param { idx: idx.idx as u32, name: crate::Name::missing() } } chalk_ir::TyData::Projection(proj) => { let associated_ty = from_chalk(db, proj.associated_ty_id); @@ -119,15 +178,15 @@ impl ToChalk for Ty { chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx as u32), chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown, chalk_ir::TyData::Dyn(where_clauses) => { - assert_eq!(where_clauses.binders.len(), 1); + assert_eq!(where_clauses.bounds.binders.len(), 1); let predicates = - where_clauses.value.into_iter().map(|c| from_chalk(db, c)).collect(); + where_clauses.bounds.value.into_iter().map(|c| from_chalk(db, c)).collect(); Ty::Dyn(predicates) } chalk_ir::TyData::Opaque(where_clauses) => { - assert_eq!(where_clauses.binders.len(), 1); + assert_eq!(where_clauses.bounds.binders.len(), 1); let predicates = - where_clauses.value.into_iter().map(|c| from_chalk(db, c)).collect(); + where_clauses.bounds.value.into_iter().map(|c| from_chalk(db, c)).collect(); Ty::Opaque(predicates) } } @@ -135,18 +194,21 @@ impl ToChalk for Ty { } impl ToChalk for Substs { - type Chalk = Vec>; + type Chalk = Vec>; - fn to_chalk(self, db: &impl HirDatabase) -> Vec> { + fn to_chalk(self, db: &impl HirDatabase) -> Vec> { self.iter().map(|ty| ty.clone().to_chalk(db).cast()).collect() } - fn from_chalk(db: &impl HirDatabase, parameters: Vec>) -> Substs { + fn from_chalk( + db: &impl HirDatabase, + parameters: Vec>, + ) -> Substs { let tys = parameters .into_iter() - .map(|p| match p { - chalk_ir::Parameter(chalk_ir::ParameterKind::Ty(ty)) => from_chalk(db, ty), - chalk_ir::Parameter(chalk_ir::ParameterKind::Lifetime(_)) => unimplemented!(), + .map(|p| match p.ty() { + Some(ty) => from_chalk(db, ty.clone()), + None => unimplemented!(), }) .collect(); Substs(tys) @@ -154,88 +216,102 @@ impl ToChalk for Substs { } impl ToChalk for TraitRef { - type Chalk = chalk_ir::TraitRef; + type Chalk = chalk_ir::TraitRef; - fn to_chalk(self: TraitRef, db: &impl HirDatabase) -> chalk_ir::TraitRef { + fn to_chalk(self: TraitRef, db: &impl HirDatabase) -> chalk_ir::TraitRef { let trait_id = self.trait_.to_chalk(db); let parameters = self.substs.to_chalk(db); chalk_ir::TraitRef { trait_id, parameters } } - fn from_chalk(db: &impl HirDatabase, trait_ref: chalk_ir::TraitRef) -> Self { + fn from_chalk(db: &impl HirDatabase, trait_ref: chalk_ir::TraitRef) -> Self { let trait_ = from_chalk(db, trait_ref.trait_id); let substs = from_chalk(db, trait_ref.parameters); TraitRef { trait_, substs } } } -impl ToChalk for TraitId { - type Chalk = chalk_ir::TraitId; +impl ToChalk for hir_def::TraitId { + type Chalk = TraitId; - fn to_chalk(self, _db: &impl HirDatabase) -> chalk_ir::TraitId { - chalk_ir::TraitId(id_to_chalk(self)) + fn to_chalk(self, _db: &impl HirDatabase) -> TraitId { + chalk_ir::TraitId(self.as_intern_id()) } - fn from_chalk(_db: &impl HirDatabase, trait_id: chalk_ir::TraitId) -> TraitId { - id_from_chalk(trait_id.0) + fn from_chalk(_db: &impl HirDatabase, trait_id: TraitId) -> hir_def::TraitId { + InternKey::from_intern_id(trait_id.0) } } impl ToChalk for TypeCtor { - type Chalk = chalk_ir::StructId; + type Chalk = TypeName; - fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::StructId { - db.intern_type_ctor(self).into() + fn to_chalk(self, db: &impl HirDatabase) -> TypeName { + match self { + TypeCtor::AssociatedType(type_alias) => { + let type_id = type_alias.to_chalk(db); + TypeName::AssociatedType(type_id) + } + _ => { + // other TypeCtors get interned and turned into a chalk StructId + let struct_id = db.intern_type_ctor(self).into(); + TypeName::Struct(struct_id) + } + } } - fn from_chalk(db: &impl HirDatabase, struct_id: chalk_ir::StructId) -> TypeCtor { - db.lookup_intern_type_ctor(struct_id.into()) + fn from_chalk(db: &impl HirDatabase, type_name: TypeName) -> TypeCtor { + match type_name { + TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), + TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), + TypeName::Error => { + // this should not be reached, since we don't represent TypeName::Error with TypeCtor + unreachable!() + } + } } } impl ToChalk for Impl { - type Chalk = chalk_ir::ImplId; + type Chalk = ImplId; - fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::ImplId { + fn to_chalk(self, db: &impl HirDatabase) -> ImplId { db.intern_chalk_impl(self).into() } - fn from_chalk(db: &impl HirDatabase, impl_id: chalk_ir::ImplId) -> Impl { + fn from_chalk(db: &impl HirDatabase, impl_id: ImplId) -> Impl { db.lookup_intern_chalk_impl(impl_id.into()) } } impl ToChalk for TypeAliasId { - type Chalk = chalk_ir::TypeId; + type Chalk = AssocTypeId; - fn to_chalk(self, _db: &impl HirDatabase) -> chalk_ir::TypeId { - chalk_ir::TypeId(id_to_chalk(self)) + fn to_chalk(self, _db: &impl HirDatabase) -> AssocTypeId { + chalk_ir::AssocTypeId(self.as_intern_id()) } - fn from_chalk(_db: &impl HirDatabase, type_alias_id: chalk_ir::TypeId) -> TypeAliasId { - id_from_chalk(type_alias_id.0) + fn from_chalk(_db: &impl HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId { + InternKey::from_intern_id(type_alias_id.0) } } impl ToChalk for AssocTyValue { - type Chalk = chalk_rust_ir::AssociatedTyValueId; + type Chalk = AssociatedTyValueId; - fn to_chalk(self, db: &impl HirDatabase) -> chalk_rust_ir::AssociatedTyValueId { + fn to_chalk(self, db: &impl HirDatabase) -> AssociatedTyValueId { db.intern_assoc_ty_value(self).into() } - fn from_chalk( - db: &impl HirDatabase, - assoc_ty_value_id: chalk_rust_ir::AssociatedTyValueId, - ) -> AssocTyValue { + fn from_chalk(db: &impl HirDatabase, assoc_ty_value_id: AssociatedTyValueId) -> AssocTyValue { db.lookup_intern_assoc_ty_value(assoc_ty_value_id.into()) } } impl ToChalk for GenericPredicate { - type Chalk = chalk_ir::QuantifiedWhereClause; + type Chalk = chalk_ir::QuantifiedWhereClause; - fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::QuantifiedWhereClause { + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::QuantifiedWhereClause { match self { GenericPredicate::Implemented(trait_ref) => { make_binders(chalk_ir::WhereClause::Implemented(trait_ref.to_chalk(db)), 0) @@ -247,26 +323,16 @@ impl ToChalk for GenericPredicate { }), 0, ), - GenericPredicate::Error => { - let impossible_trait_ref = chalk_ir::TraitRef { - trait_id: UNKNOWN_TRAIT, - parameters: vec![Ty::Unknown.to_chalk(db).cast()], - }; - make_binders(chalk_ir::WhereClause::Implemented(impossible_trait_ref), 0) - } + GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"), } } fn from_chalk( db: &impl HirDatabase, - where_clause: chalk_ir::QuantifiedWhereClause, + where_clause: chalk_ir::QuantifiedWhereClause, ) -> GenericPredicate { match where_clause.value { chalk_ir::WhereClause::Implemented(tr) => { - if tr.trait_id == UNKNOWN_TRAIT { - // FIXME we need an Error enum on the Chalk side to avoid this - return GenericPredicate::Error; - } GenericPredicate::Implemented(from_chalk(db, tr)) } chalk_ir::WhereClause::ProjectionEq(projection_eq) => { @@ -279,9 +345,9 @@ impl ToChalk for GenericPredicate { } impl ToChalk for ProjectionTy { - type Chalk = chalk_ir::ProjectionTy; + type Chalk = chalk_ir::ProjectionTy; - fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::ProjectionTy { + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::ProjectionTy { chalk_ir::ProjectionTy { associated_ty_id: self.associated_ty.to_chalk(db), parameters: self.parameters.to_chalk(db), @@ -290,7 +356,7 @@ impl ToChalk for ProjectionTy { fn from_chalk( db: &impl HirDatabase, - projection_ty: chalk_ir::ProjectionTy, + projection_ty: chalk_ir::ProjectionTy, ) -> ProjectionTy { ProjectionTy { associated_ty: from_chalk(db, projection_ty.associated_ty_id), @@ -300,31 +366,31 @@ impl ToChalk for ProjectionTy { } impl ToChalk for super::ProjectionPredicate { - type Chalk = chalk_ir::Normalize; + type Chalk = chalk_ir::Normalize; - fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Normalize { + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Normalize { chalk_ir::Normalize { projection: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db), } } - fn from_chalk(_db: &impl HirDatabase, _normalize: chalk_ir::Normalize) -> Self { + fn from_chalk(_db: &impl HirDatabase, _normalize: chalk_ir::Normalize) -> Self { unimplemented!() } } impl ToChalk for Obligation { - type Chalk = chalk_ir::DomainGoal; + type Chalk = chalk_ir::DomainGoal; - fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::DomainGoal { + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::DomainGoal { match self { Obligation::Trait(tr) => tr.to_chalk(db).cast(), Obligation::Projection(pr) => pr.to_chalk(db).cast(), } } - fn from_chalk(_db: &impl HirDatabase, _goal: chalk_ir::DomainGoal) -> Self { + fn from_chalk(_db: &impl HirDatabase, _goal: chalk_ir::DomainGoal) -> Self { unimplemented!() } } @@ -348,16 +414,17 @@ where } impl ToChalk for Arc { - type Chalk = chalk_ir::Environment; + type Chalk = chalk_ir::Environment; - fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Environment { + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Environment { let mut clauses = Vec::new(); for pred in &self.predicates { if pred.is_error() { // for env, we just ignore errors continue; } - let program_clause: chalk_ir::ProgramClause = pred.clone().to_chalk(db).cast(); + let program_clause: chalk_ir::ProgramClause = + pred.clone().to_chalk(db).cast(); clauses.push(program_clause.into_from_env_clause()); } chalk_ir::Environment::new().add_clauses(clauses) @@ -365,7 +432,7 @@ impl ToChalk for Arc { fn from_chalk( _db: &impl HirDatabase, - _env: chalk_ir::Environment, + _env: chalk_ir::Environment, ) -> Arc { unimplemented!() } @@ -373,7 +440,7 @@ impl ToChalk for Arc { impl ToChalk for super::InEnvironment where - T::Chalk: chalk_ir::family::HasTypeFamily, + T::Chalk: chalk_ir::family::HasTypeFamily, { type Chalk = chalk_ir::InEnvironment; @@ -395,6 +462,51 @@ where } } +impl ToChalk for builtin::BuiltinImplData { + type Chalk = ImplDatum; + + fn to_chalk(self, db: &impl HirDatabase) -> ImplDatum { + let impl_type = chalk_rust_ir::ImplType::External; + let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect(); + + let impl_datum_bound = + chalk_rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses }; + let associated_ty_value_ids = + self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect(); + chalk_rust_ir::ImplDatum { + binders: make_binders(impl_datum_bound, self.num_vars), + impl_type, + polarity: chalk_rust_ir::Polarity::Positive, + associated_ty_value_ids, + } + } + + fn from_chalk(_db: &impl HirDatabase, _data: ImplDatum) -> Self { + unimplemented!() + } +} + +impl ToChalk for builtin::BuiltinImplAssocTyValueData { + type Chalk = AssociatedTyValue; + + fn to_chalk(self, db: &impl HirDatabase) -> AssociatedTyValue { + let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty: self.value.to_chalk(db) }; + + chalk_rust_ir::AssociatedTyValue { + associated_ty_id: self.assoc_ty_id.to_chalk(db), + impl_id: self.impl_.to_chalk(db), + value: make_binders(value_bound, self.num_vars), + } + } + + fn from_chalk( + _db: &impl HirDatabase, + _data: AssociatedTyValue, + ) -> builtin::BuiltinImplAssocTyValueData { + unimplemented!() + } +} + fn make_binders(value: T, num_vars: usize) -> chalk_ir::Binders { chalk_ir::Binders { value, @@ -406,46 +518,46 @@ fn convert_where_clauses( db: &impl HirDatabase, def: GenericDefId, substs: &Substs, -) -> Vec> { +) -> Vec> { let generic_predicates = db.generic_predicates(def); let mut result = Vec::with_capacity(generic_predicates.len()); for pred in generic_predicates.iter() { if pred.is_error() { - // HACK: Return just the single predicate (which is always false - // anyway), otherwise Chalk can easily get into slow situations - return vec![pred.clone().subst(substs).to_chalk(db)]; + // skip errored predicates completely + continue; } result.push(pred.clone().subst(substs).to_chalk(db)); } result } -impl<'a, DB> chalk_solve::RustIrDatabase for ChalkContext<'a, DB> +impl<'a, DB> chalk_solve::RustIrDatabase for ChalkContext<'a, DB> where DB: HirDatabase, { - fn associated_ty_data(&self, id: TypeId) -> Arc> { + fn associated_ty_data(&self, id: AssocTypeId) -> Arc { self.db.associated_ty_data(id) } - fn trait_datum(&self, trait_id: chalk_ir::TraitId) -> Arc> { + fn trait_datum(&self, trait_id: TraitId) -> Arc { self.db.trait_datum(self.krate, trait_id) } - fn struct_datum(&self, struct_id: chalk_ir::StructId) -> Arc> { + fn struct_datum(&self, struct_id: StructId) -> Arc { self.db.struct_datum(self.krate, struct_id) } - fn impl_datum(&self, impl_id: chalk_ir::ImplId) -> Arc> { + fn impl_datum(&self, impl_id: ImplId) -> Arc { self.db.impl_datum(self.krate, impl_id) } fn impls_for_trait( &self, - trait_id: chalk_ir::TraitId, - parameters: &[Parameter], - ) -> Vec { + trait_id: TraitId, + parameters: &[Parameter], + ) -> Vec { debug!("impls_for_trait {:?}", trait_id); - if trait_id == UNKNOWN_TRAIT { - return Vec::new(); - } - let trait_: TraitId = from_chalk(self.db, trait_id); + let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); + + // Note: Since we're using impls_for_trait, only impls where the trait + // can be resolved should ever reach Chalk. `impl_datum` relies on that + // and will panic if the trait can't be resolved. let mut result: Vec<_> = self .db .impls_for_trait(self.krate, trait_.into()) @@ -456,62 +568,47 @@ where .collect(); let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref().clone()); - if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty { - for &fn_trait in - [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter() - { - if let Some(actual_trait) = get_fn_trait(self.db, self.krate, fn_trait) { - if trait_ == actual_trait { - let impl_ = super::ClosureFnTraitImplData { def, expr, fn_trait }; - result.push(Impl::ClosureFnTraitImpl(impl_).to_chalk(self.db)); - } - } - } - } + + builtin::get_builtin_impls(self.db, self.krate, &ty, trait_, |i| { + result.push(i.to_chalk(self.db)) + }); debug!("impls_for_trait returned {} impls", result.len()); result } - fn impl_provided_for( - &self, - auto_trait_id: chalk_ir::TraitId, - struct_id: chalk_ir::StructId, - ) -> bool { + fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: StructId) -> bool { debug!("impl_provided_for {:?}, {:?}", auto_trait_id, struct_id); false // FIXME } - fn type_name(&self, _id: TypeKindId) -> Identifier { - unimplemented!() - } - fn associated_ty_value( - &self, - id: chalk_rust_ir::AssociatedTyValueId, - ) -> Arc> { + fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc { self.db.associated_ty_value(self.krate.into(), id) } - fn custom_clauses(&self) -> Vec> { + fn custom_clauses(&self) -> Vec> { vec![] } - fn local_impls_to_coherence_check( - &self, - _trait_id: chalk_ir::TraitId, - ) -> Vec { + fn local_impls_to_coherence_check(&self, _trait_id: TraitId) -> Vec { // We don't do coherence checking (yet) unimplemented!() } + fn as_struct_id(&self, id: &TypeName) -> Option { + match id { + TypeName::Struct(struct_id) => Some(*struct_id), + _ => None, + } + } } pub(crate) fn associated_ty_data_query( db: &impl HirDatabase, - id: TypeId, -) -> Arc> { + id: AssocTypeId, +) -> Arc { debug!("associated_ty_data {:?}", id); let type_alias: TypeAliasId = from_chalk(db, id); let trait_ = match type_alias.lookup(db).container { - ContainerId::TraitId(t) => t, + AssocContainerId::TraitId(t) => t, _ => panic!("associated type not in trait"), }; - let generic_params = db.generic_params(type_alias.into()); + let generic_params = generics(db, type_alias.into()); let bound_data = chalk_rust_ir::AssociatedTyDatumBound { // FIXME add bounds and where clauses bounds: vec![], @@ -521,7 +618,7 @@ pub(crate) fn associated_ty_data_query( trait_id: trait_.to_chalk(db), id, name: lalrpop_intern::intern(&db.type_alias_data(type_alias).name.to_string()), - binders: make_binders(bound_data, generic_params.count_params_including_parent()), + binders: make_binders(bound_data, generic_params.len()), }; Arc::new(datum) } @@ -529,35 +626,17 @@ pub(crate) fn associated_ty_data_query( pub(crate) fn trait_datum_query( db: &impl HirDatabase, krate: CrateId, - trait_id: chalk_ir::TraitId, -) -> Arc> { + trait_id: TraitId, +) -> Arc { debug!("trait_datum {:?}", trait_id); - if trait_id == UNKNOWN_TRAIT { - let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses: Vec::new() }; - - let flags = chalk_rust_ir::TraitFlags { - auto: false, - marker: false, - upstream: true, - fundamental: false, - non_enumerable: true, - coinductive: false, - }; - return Arc::new(TraitDatum { - id: trait_id, - binders: make_binders(trait_datum_bound, 1), - flags, - associated_ty_ids: vec![], - }); - } - let trait_: TraitId = from_chalk(db, trait_id); + let trait_: hir_def::TraitId = from_chalk(db, trait_id); let trait_data = db.trait_data(trait_); debug!("trait {:?} = {:?}", trait_id, trait_data.name); - let generic_params = db.generic_params(trait_.into()); + let generic_params = generics(db, trait_.into()); let bound_vars = Substs::bound_vars(&generic_params); let flags = chalk_rust_ir::TraitFlags { auto: trait_data.auto, - upstream: trait_.module(db).krate != krate, + upstream: trait_.lookup(db).container.module(db).krate != krate, non_enumerable: true, coinductive: false, // only relevant for Chalk testing // FIXME set these flags correctly @@ -580,17 +659,17 @@ pub(crate) fn trait_datum_query( pub(crate) fn struct_datum_query( db: &impl HirDatabase, krate: CrateId, - struct_id: chalk_ir::StructId, -) -> Arc> { + struct_id: StructId, +) -> Arc { debug!("struct_datum {:?}", struct_id); - let type_ctor: TypeCtor = from_chalk(db, struct_id); + let type_ctor: TypeCtor = from_chalk(db, TypeName::Struct(struct_id)); debug!("struct {:?} = {:?}", struct_id, type_ctor); let num_params = type_ctor.num_ty_params(db); let upstream = type_ctor.krate(db) != Some(krate); let where_clauses = type_ctor .as_generic_def() .map(|generic_def| { - let generic_params = db.generic_params(generic_def.into()); + let generic_params = generics(db, generic_def.into()); let bound_vars = Substs::bound_vars(&generic_params); convert_where_clauses(db, generic_def, &bound_vars) }) @@ -612,35 +691,34 @@ pub(crate) fn struct_datum_query( pub(crate) fn impl_datum_query( db: &impl HirDatabase, krate: CrateId, - impl_id: chalk_ir::ImplId, -) -> Arc> { + impl_id: ImplId, +) -> Arc { let _p = ra_prof::profile("impl_datum"); debug!("impl_datum {:?}", impl_id); let impl_: Impl = from_chalk(db, impl_id); match impl_ { Impl::ImplBlock(impl_block) => impl_block_datum(db, krate, impl_id, impl_block), - Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data), + _ => Arc::new(builtin::impl_datum(db, krate, impl_).to_chalk(db)), } - .unwrap_or_else(invalid_impl_datum) } fn impl_block_datum( db: &impl HirDatabase, krate: CrateId, - chalk_id: chalk_ir::ImplId, - impl_id: ImplId, -) -> Option>> { - let trait_ref = match db.impl_ty(impl_id) { - ImplTy::TraitRef(it) => it, - ImplTy::Inherent(_) => return None, - }; + chalk_id: ImplId, + impl_id: hir_def::ImplId, +) -> Arc { + let trait_ref = db + .impl_trait(impl_id) + // ImplIds for impls where the trait ref can't be resolved should never reach Chalk + .expect("invalid impl passed to Chalk"); let impl_data = db.impl_data(impl_id); - let generic_params = db.generic_params(impl_id.into()); + let generic_params = generics(db, impl_id.into()); let bound_vars = Substs::bound_vars(&generic_params); let trait_ref = trait_ref.subst(&bound_vars); let trait_ = trait_ref.trait_; - let impl_type = if impl_id.module(db).krate == krate { + let impl_type = if impl_id.lookup(db).container.module(db).krate == krate { chalk_rust_ir::ImplType::Local } else { chalk_rust_ir::ImplType::External @@ -685,94 +763,20 @@ fn impl_block_datum( polarity, associated_ty_value_ids, }; - Some(Arc::new(impl_datum)) -} - -fn invalid_impl_datum() -> Arc> { - let trait_ref = chalk_ir::TraitRef { - trait_id: UNKNOWN_TRAIT, - parameters: vec![chalk_ir::TyData::BoundVar(0).cast().intern().cast()], - }; - let impl_datum_bound = chalk_rust_ir::ImplDatumBound { trait_ref, where_clauses: Vec::new() }; - let impl_datum = ImplDatum { - binders: make_binders(impl_datum_bound, 1), - impl_type: chalk_rust_ir::ImplType::External, - polarity: chalk_rust_ir::Polarity::Positive, - associated_ty_value_ids: Vec::new(), - }; Arc::new(impl_datum) } -fn closure_fn_trait_impl_datum( - db: &impl HirDatabase, - krate: CrateId, - data: super::ClosureFnTraitImplData, -) -> Option>> { - // for some closure |X, Y| -> Z: - // impl Fn<(T, U)> for closure V> { Output = V } - - let trait_ = get_fn_trait(db, krate, data.fn_trait)?; // get corresponding fn trait - - // validate FnOnce trait, since we need it in the assoc ty value definition - // and don't want to return a valid value only to find out later that FnOnce - // is broken - let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?; - let _output = db.trait_data(fn_once_trait).associated_type_by_name(&name::OUTPUT_TYPE)?; - - let num_args: u16 = match &db.body(data.def.into())[data.expr] { - Expr::Lambda { args, .. } => args.len() as u16, - _ => { - log::warn!("closure for closure type {:?} not found", data); - 0 - } - }; - - let arg_ty = Ty::apply( - TypeCtor::Tuple { cardinality: num_args }, - Substs::builder(num_args as usize).fill_with_bound_vars(0).build(), - ); - let sig_ty = Ty::apply( - TypeCtor::FnPtr { num_args }, - Substs::builder(num_args as usize + 1).fill_with_bound_vars(0).build(), - ); - - let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty); - - let trait_ref = TraitRef { - trait_: trait_.into(), - substs: Substs::build_for_def(db, trait_).push(self_ty).push(arg_ty).build(), - }; - - let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data.clone()).to_chalk(db); - - let impl_type = chalk_rust_ir::ImplType::External; - - let impl_datum_bound = chalk_rust_ir::ImplDatumBound { - trait_ref: trait_ref.to_chalk(db), - where_clauses: Vec::new(), - }; - let impl_datum = ImplDatum { - binders: make_binders(impl_datum_bound, num_args as usize + 1), - impl_type, - polarity: chalk_rust_ir::Polarity::Positive, - associated_ty_value_ids: vec![output_ty_id], - }; - Some(Arc::new(impl_datum)) -} - pub(crate) fn associated_ty_value_query( db: &impl HirDatabase, krate: CrateId, - id: chalk_rust_ir::AssociatedTyValueId, -) -> Arc> { + id: AssociatedTyValueId, +) -> Arc { let data: AssocTyValue = from_chalk(db, id); match data { AssocTyValue::TypeAlias(type_alias) => { type_alias_associated_ty_value(db, krate, type_alias) } - AssocTyValue::ClosureFnTraitImplOutput(data) => { - closure_fn_trait_output_assoc_ty_value(db, krate, data) - } + _ => Arc::new(builtin::associated_ty_value(db, krate, data).to_chalk(db)), } } @@ -780,24 +784,20 @@ fn type_alias_associated_ty_value( db: &impl HirDatabase, _krate: CrateId, type_alias: TypeAliasId, -) -> Arc> { +) -> Arc { let type_alias_data = db.type_alias_data(type_alias); let impl_id = match type_alias.lookup(db).container { - ContainerId::ImplId(it) => it, + AssocContainerId::ImplId(it) => it, _ => panic!("assoc ty value should be in impl"), }; - let trait_ref = match db.impl_ty(impl_id) { - ImplTy::TraitRef(it) => it, - // we don't return any assoc ty values if the impl'd trait can't be resolved - ImplTy::Inherent(_) => panic!("assoc ty value should not exist"), - }; + let trait_ref = db.impl_trait(impl_id).expect("assoc ty value should not exist"); // we don't return any assoc ty values if the impl'd trait can't be resolved let assoc_ty = db .trait_data(trait_ref.trait_) .associated_type_by_name(&type_alias_data.name) .expect("assoc ty value should not exist"); // validated when building the impl data as well - let generic_params = db.generic_params(impl_id.into()); + let generic_params = generics(db, impl_id.into()); let bound_vars = Substs::bound_vars(&generic_params); let ty = db.ty(type_alias.into()).subst(&bound_vars); let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty: ty.to_chalk(db) }; @@ -809,53 +809,6 @@ fn type_alias_associated_ty_value( Arc::new(value) } -fn closure_fn_trait_output_assoc_ty_value( - db: &impl HirDatabase, - krate: CrateId, - data: super::ClosureFnTraitImplData, -) -> Arc> { - let impl_id = Impl::ClosureFnTraitImpl(data.clone()).to_chalk(db); - - let num_args: u16 = match &db.body(data.def.into())[data.expr] { - Expr::Lambda { args, .. } => args.len() as u16, - _ => { - log::warn!("closure for closure type {:?} not found", data); - 0 - } - }; - - let output_ty = Ty::Bound(num_args.into()); - - let fn_once_trait = - get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist"); - - let output_ty_id = db - .trait_data(fn_once_trait) - .associated_type_by_name(&name::OUTPUT_TYPE) - .expect("assoc ty value should not exist"); - - let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty: output_ty.to_chalk(db) }; - - let value = chalk_rust_ir::AssociatedTyValue { - associated_ty_id: output_ty_id.to_chalk(db), - impl_id, - value: make_binders(value_bound, num_args as usize + 1), - }; - Arc::new(value) -} - -fn get_fn_trait( - db: &impl HirDatabase, - krate: CrateId, - fn_trait: super::FnTrait, -) -> Option { - let target = db.lang_item(krate, fn_trait.lang_item_name().into())?; - match target { - LangItemTarget::TraitId(t) => Some(t), - _ => None, - } -} - fn id_from_chalk(chalk_id: chalk_ir::RawId) -> T { T::from_intern_id(InternId::from(chalk_id.index)) } @@ -863,27 +816,27 @@ fn id_to_chalk(salsa_id: T) -> chalk_ir::RawId { chalk_ir::RawId { index: salsa_id.as_intern_id().as_u32() } } -impl From for crate::TypeCtorId { - fn from(struct_id: chalk_ir::StructId) -> Self { - id_from_chalk(struct_id.0) +impl From for crate::TypeCtorId { + fn from(struct_id: StructId) -> Self { + InternKey::from_intern_id(struct_id.0) } } -impl From for chalk_ir::StructId { +impl From for StructId { fn from(type_ctor_id: crate::TypeCtorId) -> Self { - chalk_ir::StructId(id_to_chalk(type_ctor_id)) + chalk_ir::StructId(type_ctor_id.as_intern_id()) } } -impl From for crate::traits::GlobalImplId { - fn from(impl_id: chalk_ir::ImplId) -> Self { - id_from_chalk(impl_id.0) +impl From for crate::traits::GlobalImplId { + fn from(impl_id: ImplId) -> Self { + InternKey::from_intern_id(impl_id.0) } } -impl From for chalk_ir::ImplId { +impl From for ImplId { fn from(impl_id: crate::traits::GlobalImplId) -> Self { - chalk_ir::ImplId(id_to_chalk(impl_id)) + chalk_ir::ImplId(impl_id.as_intern_id()) } } diff --git a/crates/ra_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs index e4ba890efa..0b1806a84f 100644 --- a/crates/ra_hir_ty/src/utils.rs +++ b/crates/ra_hir_ty/src/utils.rs @@ -5,14 +5,14 @@ use std::sync::Arc; use hir_def::{ adt::VariantData, db::DefDatabase, + generics::{GenericParams, TypeParamData}, + path::Path, resolver::{HasResolver, TypeNs}, type_ref::TypeRef, - TraitId, TypeAliasId, VariantId, + AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId, VariantId, }; -use hir_expand::name::{self, Name}; +use hir_expand::name::{name, Name}; -// FIXME: this is wrong, b/c it can't express `trait T: PartialEq<()>`. -// We should return a `TraitREf` here. fn direct_super_traits(db: &impl DefDatabase, trait_: TraitId) -> Vec { let resolver = trait_.resolver(db); // returning the iterator directly doesn't easily work because of @@ -23,10 +23,10 @@ fn direct_super_traits(db: &impl DefDatabase, trait_: TraitId) -> Vec { .where_predicates .iter() .filter_map(|pred| match &pred.type_ref { - TypeRef::Path(p) if p.as_ident() == Some(&name::SELF_TYPE) => pred.bound.as_path(), + TypeRef::Path(p) if p == &Path::from(name![Self]) => pred.bound.as_path(), _ => None, }) - .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path) { + .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { Some(TypeNs::TraitId(t)) => Some(t), _ => None, }) @@ -82,3 +82,81 @@ pub(crate) fn make_mut_slice(a: &mut Arc<[T]>) -> &mut [T] { } Arc::get_mut(a).unwrap() } + +pub(crate) fn generics(db: &impl DefDatabase, def: GenericDefId) -> Generics { + let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); + Generics { def, params: db.generic_params(def), parent_generics } +} + +pub(crate) struct Generics { + def: GenericDefId, + pub(crate) params: Arc, + parent_generics: Option>, +} + +impl Generics { + pub(crate) fn iter<'a>(&'a self) -> impl Iterator + 'a { + self.parent_generics + .as_ref() + .into_iter() + .flat_map(|it| it.params.types.iter()) + .chain(self.params.types.iter()) + .enumerate() + .map(|(i, (_local_id, p))| (i as u32, p)) + } + + pub(crate) fn iter_parent<'a>(&'a self) -> impl Iterator + 'a { + self.parent_generics + .as_ref() + .into_iter() + .flat_map(|it| it.params.types.iter()) + .enumerate() + .map(|(i, (_local_id, p))| (i as u32, p)) + } + + pub(crate) fn len(&self) -> usize { + self.len_split().0 + } + /// (total, parents, child) + pub(crate) fn len_split(&self) -> (usize, usize, usize) { + let parent = self.parent_generics.as_ref().map_or(0, |p| p.len()); + let child = self.params.types.len(); + (parent + child, parent, child) + } + pub(crate) fn param_idx(&self, param: TypeParamId) -> u32 { + self.find_param(param).0 + } + pub(crate) fn param_name(&self, param: TypeParamId) -> Name { + self.find_param(param).1.name.clone() + } + fn find_param(&self, param: TypeParamId) -> (u32, &TypeParamData) { + if param.parent == self.def { + let (idx, (_local_id, data)) = self + .params + .types + .iter() + .enumerate() + .find(|(_, (idx, _))| *idx == param.local_id) + .unwrap(); + let (_total, parent_len, _child) = self.len_split(); + return ((parent_len + idx) as u32, data); + } + self.parent_generics.as_ref().unwrap().find_param(param) + } +} + +fn parent_generic_def(db: &impl DefDatabase, def: GenericDefId) -> Option { + 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, + }; + + match container { + AssocContainerId::ImplId(it) => Some(it.into()), + AssocContainerId::TraitId(it) => Some(it.into()), + AssocContainerId::ContainerId(_) => None, + } +} diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index e6383dd352..e3439ae313 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml @@ -11,6 +11,7 @@ doctest = false wasm = [] [dependencies] +either = "1.5" format-buf = "1.0.0" itertools = "0.8.0" join_to_string = "0.1.3" diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index d559dc4d0e..2c2b6fa489 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs @@ -1,24 +1,26 @@ //! FIXME: write short doc here -use ra_db::SourceDatabase; +use hir::db::AstDatabase; use ra_syntax::{ - algo::ancestors_at_offset, ast::{self, ArgListOwner}, - match_ast, AstNode, SyntaxNode, TextUnit, + match_ast, AstNode, SyntaxNode, }; use test_utils::tested_by; -use crate::{db::RootDatabase, CallInfo, FilePosition, FunctionSignature}; +use crate::{ + db::RootDatabase, expand::descend_into_macros, CallInfo, FilePosition, FunctionSignature, +}; /// Computes parameter information for the given call expression. pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option { - let parse = db.parse(position.file_id); - let syntax = parse.tree().syntax().clone(); + let file = db.parse_or_expand(position.file_id.into())?; + let token = file.token_at_offset(position.offset).next()?; + let token = descend_into_macros(db, position.file_id, token); // Find the calling expression and it's NameRef - let calling_node = FnCallNode::with_node(&syntax, position.offset)?; + let calling_node = FnCallNode::with_node(&token.value.parent())?; let name_ref = calling_node.name_ref()?; - let name_ref = hir::Source::new(position.file_id.into(), name_ref.syntax()); + let name_ref = token.with_value(name_ref.syntax()); let analyzer = hir::SourceAnalyzer::new(db, name_ref, None); let (mut call_info, has_self) = match &calling_node { @@ -93,8 +95,8 @@ enum FnCallNode { } impl FnCallNode { - fn with_node(syntax: &SyntaxNode, offset: TextUnit) -> Option { - ancestors_at_offset(syntax, offset).find_map(|node| { + fn with_node(syntax: &SyntaxNode) -> Option { + syntax.ancestors().find_map(|node| { match_ast! { match node { ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) }, @@ -589,4 +591,25 @@ fn f() { assert_eq!(info.label(), "foo!()"); assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string())); } + + #[test] + fn fn_signature_for_call_in_macro() { + let info = call_info( + r#" + macro_rules! id { + ($($tt:tt)*) => { $($tt)* } + } + fn foo() { + + } + id! { + fn bar() { + foo(<|>); + } + } + "#, + ); + + assert_eq!(info.label(), "fn foo()"); + } } diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs index 4a76d1dd83..387a9cafb6 100644 --- a/crates/ra_ide/src/change.rs +++ b/crates/ra_ide/src/change.rs @@ -270,7 +270,6 @@ impl RootDatabase { self.query(hir::db::AstIdMapQuery).sweep(sweep); - self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep); self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); self.query(hir::db::ExprScopesQuery).sweep(sweep); @@ -309,7 +308,6 @@ impl RootDatabase { hir::db::StructDataQuery hir::db::EnumDataQuery hir::db::TraitDataQuery - hir::db::RawItemsWithSourceMapQuery hir::db::RawItemsQuery hir::db::CrateDefMapQuery hir::db::GenericParamsQuery diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index b6fe486270..294964887a 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -27,7 +27,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { complete_methods(acc, ctx, &receiver_ty); // Suggest .await syntax for types that implement Future trait - if ctx.analyzer.impls_future(ctx.db, receiver_ty.into_ty()) { + if ctx.analyzer.impls_future(ctx.db, receiver_ty) { CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") .detail("expr.await") .insert_text("await") @@ -216,6 +216,39 @@ mod tests { ); } + #[test] + fn test_method_completion_only_fitting_impls() { + assert_debug_snapshot!( + do_ref_completion( + r" + struct A {} + impl A { + fn the_method(&self) {} + } + impl A { + fn the_other_method(&self) {} + } + fn foo(a: A) { + a.<|> + } + ", + ), + @r###" + [ + CompletionItem { + label: "the_method()", + source_range: [243; 243), + delete: [243; 243), + insert: "the_method()$0", + kind: Method, + lookup: "the_method", + detail: "fn the_method(&self)", + }, + ] + "### + ); + } + #[test] fn test_trait_method_completion() { assert_debug_snapshot!( diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index 89e0009a17..cc1f7c8305 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -use hir::{Adt, Either, HasSource, PathResolution}; +use hir::{Adt, PathResolution, ScopeDef}; use ra_syntax::AstNode; use test_utils::tested_by; @@ -18,17 +18,15 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { match def { hir::ModuleDef::Module(module) => { let module_scope = module.scope(ctx.db); - for (name, def, import) in module_scope { - if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def { - if ctx.use_item_syntax.is_some() { + for (name, def) in module_scope { + if ctx.use_item_syntax.is_some() { + if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def { tested_by!(dont_complete_primitive_in_use); continue; } - } - if Some(module) == ctx.module { - if let Some(import) = import { - if let Either::A(use_tree) = import.source(ctx.db).value { - if use_tree.syntax().text_range().contains_inclusive(ctx.offset) { + if let ScopeDef::Unknown = def { + if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { + if &name_ref.syntax().text() == name.to_string().as_str() { // for `use self::foo<|>`, don't suggest `foo` as a completion tested_by!(dont_complete_current_use); continue; @@ -36,6 +34,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { } } } + acc.add_resolution(ctx, name.to_string(), &def); } } diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 646a30c76e..5470dc291f 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs @@ -12,7 +12,7 @@ use crate::{ }; pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { - if ctx.db.feature_flags.get("completion.enable-postfix") == false { + if !ctx.db.feature_flags.get("completion.enable-postfix") { return; } diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index d5739b58a6..458d7525ec 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs @@ -873,4 +873,41 @@ mod tests { "### ); } + + #[test] + fn completes_local_item() { + assert_debug_snapshot!( + do_reference_completion( + " + //- /main.rs + fn main() { + return f<|>; + fn frobnicate() {} + } + " + ), + @r###" + [ + CompletionItem { + label: "frobnicate()", + source_range: [23; 24), + delete: [23; 24), + insert: "frobnicate()$0", + kind: Function, + lookup: "frobnicate", + detail: "fn frobnicate()", + }, + CompletionItem { + label: "main()", + source_range: [23; 24), + delete: [23; 24), + insert: "main()$0", + kind: Function, + lookup: "main", + detail: "fn main()", + }, + ] + "### + ) + } } diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index b8345c91d5..48d69f7e54 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -19,6 +19,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) offset: TextUnit, pub(super) token: SyntaxToken, pub(super) module: Option, + pub(super) name_ref_syntax: Option, pub(super) function_syntax: Option, pub(super) use_item_syntax: Option, pub(super) record_lit_syntax: Option, @@ -54,13 +55,13 @@ impl<'a> CompletionContext<'a> { let src = hir::ModuleSource::from_position(db, position); let module = hir::Module::from_definition( db, - hir::Source { file_id: position.file_id.into(), value: src }, + hir::InFile { file_id: position.file_id.into(), value: src }, ); let token = original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?; let analyzer = hir::SourceAnalyzer::new( db, - hir::Source::new(position.file_id.into(), &token.parent()), + hir::InFile::new(position.file_id.into(), &token.parent()), Some(position.offset), ); let mut ctx = CompletionContext { @@ -69,6 +70,7 @@ impl<'a> CompletionContext<'a> { token, offset: position.offset, module, + name_ref_syntax: None, function_syntax: None, use_item_syntax: None, record_lit_syntax: None, @@ -142,6 +144,8 @@ impl<'a> CompletionContext<'a> { } fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) { + self.name_ref_syntax = + find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); let name_range = name_ref.syntax().text_range(); if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); @@ -188,10 +192,9 @@ impl<'a> CompletionContext<'a> { self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); self.has_type_args = segment.type_arg_list().is_some(); - if let Some(mut path) = hir::Path::from_ast(path.clone()) { - if !path.is_ident() { - path.segments.pop().unwrap(); - self.path_prefix = Some(path); + if let Some(path) = hir::Path::from_ast(path.clone()) { + if let Some(path_prefix) = path.qualifier() { + self.path_prefix = Some(path_prefix); return; } } @@ -240,16 +243,15 @@ impl<'a> CompletionContext<'a> { .expr() .map(|e| e.syntax().text_range()) .and_then(|r| find_node_with_range(original_file.syntax(), r)); - self.dot_receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = - &self.dot_receiver - { - match l.kind() { - ast::LiteralKind::FloatNumber { suffix: _ } => l.token().text().ends_with('.'), - _ => false, + self.dot_receiver_is_ambiguous_float_literal = + if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { + match l.kind() { + ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'), + _ => false, + } + } else { + false } - } else { - false - } } if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { // As above diff --git a/crates/ra_ide/src/db.rs b/crates/ra_ide/src/db.rs index f739ebecd3..47d0aed6fb 100644 --- a/crates/ra_ide/src/db.rs +++ b/crates/ra_ide/src/db.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use ra_db::{ salsa::{self, Database, Durability}, Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, - SourceDatabase, SourceDatabaseExt, SourceRootId, + SourceDatabase, SourceRootId, }; use rustc_hash::FxHashMap; @@ -49,18 +49,6 @@ impl FileLoader for RootDatabase { } } -impl hir::debug::HirDebugHelper for RootDatabase { - fn crate_name(&self, krate: CrateId) -> Option { - self.debug_data.crate_names.get(&krate).cloned() - } - fn file_path(&self, file_id: FileId) -> Option { - let source_root_id = self.file_source_root(file_id); - let source_root_path = self.debug_data.root_paths.get(&source_root_id)?; - let file_path = self.file_relative_path(file_id); - Some(format!("{}/{}", source_root_path, file_path)) - } -} - impl salsa::Database for RootDatabase { fn salsa_runtime(&self) -> &salsa::Runtime { &self.runtime diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index cc1ccab4b5..c50a70d990 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -96,7 +96,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec }); let source_file = db.parse(file_id).tree(); let src = - hir::Source { file_id: file_id.into(), value: hir::ModuleSource::SourceFile(source_file) }; + hir::InFile { file_id: file_id.into(), value: hir::ModuleSource::SourceFile(source_file) }; if let Some(m) = hir::Module::from_definition(db, src) { m.diagnostics(db, &mut sink); }; diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs index 30617412a6..fbe89841b0 100644 --- a/crates/ra_ide/src/display.rs +++ b/crates/ra_ide/src/display.rs @@ -15,7 +15,7 @@ pub use function_signature::FunctionSignature; pub use navigation_target::NavigationTarget; pub use structure::{file_structure, StructureNode}; -pub(crate) use navigation_target::{description_from_symbol, docs_from_symbol, ToNav}; +pub(crate) use navigation_target::ToNav; pub(crate) use short_label::ShortLabel; pub(crate) fn function_label(node: &ast::FnDef) -> String { diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index 6ac60722b9..b9ae678284 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs @@ -1,11 +1,12 @@ //! FIXME: write short doc here -use hir::{AssocItem, Either, FieldSource, HasSource, ModuleSource, Source}; +use either::Either; +use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource}; use ra_db::{FileId, SourceDatabase}; use ra_syntax::{ ast::{self, DocCommentsOwner, NameOwner}, match_ast, AstNode, SmolStr, - SyntaxKind::{self, BIND_PAT}, + SyntaxKind::{self, BIND_PAT, TYPE_PARAM}, TextRange, }; @@ -141,7 +142,7 @@ impl NavigationTarget { /// Allows `NavigationTarget` to be created from a `NameOwner` pub(crate) fn from_named( db: &RootDatabase, - node: Source<&dyn ast::NameOwner>, + node: InFile<&dyn ast::NameOwner>, docs: Option, description: Option, ) -> NavigationTarget { @@ -230,34 +231,20 @@ impl ToNav for hir::Module { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { let src = self.definition_source(db); let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default(); - match &src.value { - ModuleSource::SourceFile(node) => { - let frange = original_range(db, src.with_value(node.syntax())); - - NavigationTarget::from_syntax( - frange.file_id, - name, - None, - frange.range, - node.syntax().kind(), - None, - None, - ) - } - ModuleSource::Module(node) => { - let frange = original_range(db, src.with_value(node.syntax())); - - NavigationTarget::from_syntax( - frange.file_id, - name, - None, - frange.range, - node.syntax().kind(), - node.doc_comment_text(), - node.short_label(), - ) - } - } + let syntax = match &src.value { + ModuleSource::SourceFile(node) => node.syntax(), + ModuleSource::Module(node) => node.syntax(), + }; + let frange = original_range(db, src.with_value(syntax)); + NavigationTarget::from_syntax( + frange.file_id, + name, + None, + frange.range, + syntax.kind(), + None, + None, + ) } } @@ -341,22 +328,43 @@ impl ToNav for hir::AssocItem { impl ToNav for hir::Local { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { let src = self.source(db); - let (full_range, focus_range) = match src.value { - Either::A(it) => { - (it.syntax().text_range(), it.name().map(|it| it.syntax().text_range())) + let node = match &src.value { + Either::Left(bind_pat) => { + bind_pat.name().map_or_else(|| bind_pat.syntax().clone(), |it| it.syntax().clone()) } - Either::B(it) => (it.syntax().text_range(), Some(it.self_kw_token().text_range())), + Either::Right(it) => it.syntax().clone(), }; + let full_range = original_range(db, src.with_value(&node)); let name = match self.name(db) { Some(it) => it.to_string().into(), None => "".into(), }; NavigationTarget { - file_id: src.file_id.original_file(db), + file_id: full_range.file_id, name, kind: BIND_PAT, - full_range, - focus_range, + full_range: full_range.range, + focus_range: None, + container_name: None, + description: None, + docs: None, + } + } +} + +impl ToNav for hir::TypeParam { + fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + let src = self.source(db); + let range = match src.value { + Either::Left(it) => it.syntax().text_range(), + Either::Right(it) => it.syntax().text_range(), + }; + NavigationTarget { + file_id: src.file_id.original_file(db), + name: self.name(db).to_string().into(), + kind: TYPE_PARAM, + full_range: range, + focus_range: None, container_name: None, description: None, docs: None, diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs index 2f1abf509a..7a22bb0a4e 100644 --- a/crates/ra_ide/src/expand.rs +++ b/crates/ra_ide/src/expand.rs @@ -1,42 +1,71 @@ //! Utilities to work with files, produced by macros. use std::iter::successors; -use hir::Source; +use hir::{InFile, Origin}; use ra_db::FileId; -use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken}; +use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange}; use crate::{db::RootDatabase, FileRange}; -pub(crate) fn original_range(db: &RootDatabase, node: Source<&SyntaxNode>) -> FileRange { - let expansion = match node.file_id.expansion_info(db) { - None => { +pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange { + if let Some((range, Origin::Call)) = original_range_and_origin(db, node) { + return range; + } + + if let Some(expansion) = node.file_id.expansion_info(db) { + if let Some(call_node) = expansion.call_node() { return FileRange { - file_id: node.file_id.original_file(db), - range: node.value.text_range(), - } + file_id: call_node.file_id.original_file(db), + range: call_node.value.text_range(), + }; } - Some(it) => it, - }; - // FIXME: the following completely wrong. - // - // *First*, we should try to map first and last tokens of node, and, if that - // fails, return the range of the overall macro expansions. - // - // *Second*, we should handle recurside macro expansions + } - let token = node - .value - .descendants_with_tokens() - .filter_map(|it| it.into_token()) - .find_map(|it| expansion.map_token_up(node.with_value(&it))); + FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } +} - match token { - Some(it) => { - FileRange { file_id: it.file_id.original_file(db), range: it.value.text_range() } +fn original_range_and_origin( + db: &RootDatabase, + node: InFile<&SyntaxNode>, +) -> Option<(FileRange, Origin)> { + let expansion = node.file_id.expansion_info(db)?; + + // the input node has only one token ? + let single = node.value.first_token()? == node.value.last_token()?; + + // FIXME: We should handle recurside macro expansions + let (range, origin) = node.value.descendants().find_map(|it| { + let first = it.first_token()?; + let last = it.last_token()?; + + if !single && first == last { + return None; } - None => { - FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } + + // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens + let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; + let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; + + if first.file_id != last.file_id || first_origin != last_origin { + return None; } + + // FIXME: Add union method in TextRange + Some(( + first.with_value(union_range(first.value.text_range(), last.value.text_range())), + first_origin, + )) + })?; + + return Some(( + FileRange { file_id: range.file_id.original_file(db), range: range.value }, + origin, + )); + + fn union_range(a: TextRange, b: TextRange) -> TextRange { + let start = a.start().min(b.start()); + let end = a.end().max(b.end()); + TextRange::from_to(start, end) } } @@ -44,8 +73,8 @@ pub(crate) fn descend_into_macros( db: &RootDatabase, file_id: FileId, token: SyntaxToken, -) -> Source { - let src = Source::new(file_id.into(), token); +) -> InFile { + let src = InFile::new(file_id.into(), token); successors(Some(src), |token| { let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs index abc602244c..bdbc31704b 100644 --- a/crates/ra_ide/src/expand_macro.rs +++ b/crates/ra_ide/src/expand_macro.rs @@ -22,7 +22,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< let name_ref = find_node_at_offset::(file.syntax(), position.offset)?; let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; - let source = hir::Source::new(position.file_id.into(), mac.syntax()); + let source = hir::InFile::new(position.file_id.into(), mac.syntax()); let expanded = expand_macro_recur(db, source, source.with_value(&mac))?; // FIXME: @@ -34,8 +34,8 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< fn expand_macro_recur( db: &RootDatabase, - source: hir::Source<&SyntaxNode>, - macro_call: hir::Source<&ast::MacroCall>, + source: hir::InFile<&SyntaxNode>, + macro_call: hir::InFile<&ast::MacroCall>, ) -> Option { let analyzer = hir::SourceAnalyzer::new(db, source, None); let expansion = analyzer.expand(db, macro_call)?; @@ -46,7 +46,7 @@ fn expand_macro_recur( let mut replaces = FxHashMap::default(); for child in children.into_iter() { - let node = hir::Source::new(macro_file_id, &child); + let node = hir::InFile::new(macro_file_id, &child); if let Some(new_node) = expand_macro_recur(db, source, node) { // Replace the whole node if it is root // `replace_descendants` will not replace the parent node @@ -86,21 +86,18 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { let mut is_next = |f: fn(SyntaxKind) -> bool, default| -> bool { token_iter.peek().map(|it| f(it.kind())).unwrap_or(default) }; - let is_last = |f: fn(SyntaxKind) -> bool, default| -> bool { - last.map(|it| f(it)).unwrap_or(default) - }; + let is_last = + |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) }; res += &match token.kind() { - k @ _ if is_text(k) && is_next(|it| !it.is_punct(), true) => { - token.text().to_string() + " " - } + k if is_text(k) && is_next(|it| !it.is_punct(), true) => token.text().to_string() + " ", L_CURLY if is_next(|it| it != R_CURLY, true) => { indent += 1; - let leading_space = if is_last(|it| is_text(it), false) { " " } else { "" }; + let leading_space = if is_last(is_text, false) { " " } else { "" }; format!("{}{{\n{}", leading_space, " ".repeat(indent)) } R_CURLY if is_last(|it| it != L_CURLY, true) => { - indent = indent.checked_sub(1).unwrap_or(0); + indent = indent.saturating_sub(1); format!("\n{}}}", " ".repeat(indent)) } R_CURLY => format!("}}\n{}", " ".repeat(indent)), diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs index 4b7bfc0b11..1ec41a117b 100644 --- a/crates/ra_ide/src/extend_selection.rs +++ b/crates/ra_ide/src/extend_selection.rs @@ -34,6 +34,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option TextRange ws.text_range() } -fn pick_best<'a>(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken { +fn pick_best(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken { return if priority(&r) > priority(&l) { r } else { l }; fn priority(n: &SyntaxToken) -> usize { match n.kind() { @@ -174,12 +175,7 @@ fn extend_list_item(node: &SyntaxNode) -> Option { TYPE_BOUND => T![+], _ => T![,], }; - if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Prev) { - return Some(TextRange::from_to( - delimiter_node.text_range().start(), - node.text_range().end(), - )); - } + if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Next) { // Include any following whitespace when delimiter is after list item. let final_node = delimiter_node @@ -190,6 +186,12 @@ fn extend_list_item(node: &SyntaxNode) -> Option { return Some(TextRange::from_to(node.text_range().start(), final_node.text_range().end())); } + if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Prev) { + return Some(TextRange::from_to( + delimiter_node.text_range().start(), + node.text_range().end(), + )); + } None } @@ -250,14 +252,14 @@ mod tests { fn test_extend_selection_list() { do_check(r#"fn foo(<|>x: i32) {}"#, &["x", "x: i32"]); do_check(r#"fn foo(<|>x: i32, y: i32) {}"#, &["x", "x: i32", "x: i32, "]); - do_check(r#"fn foo(<|>x: i32,y: i32) {}"#, &["x", "x: i32", "x: i32,"]); + do_check(r#"fn foo(<|>x: i32,y: i32) {}"#, &["x", "x: i32", "x: i32,", "(x: i32,y: i32)"]); do_check(r#"fn foo(x: i32, <|>y: i32) {}"#, &["y", "y: i32", ", y: i32"]); - do_check(r#"fn foo(x: i32, <|>y: i32, ) {}"#, &["y", "y: i32", ", y: i32"]); + do_check(r#"fn foo(x: i32, <|>y: i32, ) {}"#, &["y", "y: i32", "y: i32, "]); do_check(r#"fn foo(x: i32,<|>y: i32) {}"#, &["y", "y: i32", ",y: i32"]); do_check(r#"const FOO: [usize; 2] = [ 22<|> , 33];"#, &["22", "22 , "]); do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|>];"#, &["33", ", 33"]); - do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|> ,];"#, &["33", ", 33"]); + do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|> ,];"#, &["33", "33 ,", "[ 22 , 33 ,]"]); do_check(r#"fn main() { (1, 2<|>) }"#, &["2", ", 2", "(1, 2)"]); @@ -276,7 +278,7 @@ const FOO: [usize; 2] = [ 22 , 33<|>, ]"#, - &["33", ", 33"], + &["33", "33,"], ); } @@ -424,7 +426,7 @@ fn foo() do_check(r#"fn foo() where T: <|>Copy +Display"#, &["Copy", "Copy +"]); do_check(r#"fn foo() where T: <|>Copy+Display"#, &["Copy", "Copy+"]); do_check(r#"fn foo() where T: Copy + <|>Display"#, &["Display", "+ Display"]); - do_check(r#"fn foo() where T: Copy + <|>Display + Sync"#, &["Display", "+ Display"]); + do_check(r#"fn foo() where T: Copy + <|>Display + Sync"#, &["Display", "Display + "]); do_check(r#"fn foo() where T: Copy +<|>Display"#, &["Display", "+Display"]); } @@ -435,7 +437,7 @@ fn foo() do_check(r#"fn fooCopy +Display>() {}"#, &["Copy", "Copy +"]); do_check(r#"fn fooCopy+Display>() {}"#, &["Copy", "Copy+"]); do_check(r#"fn fooDisplay>() {}"#, &["Display", "+ Display"]); - do_check(r#"fn fooDisplay + Sync>() {}"#, &["Display", "+ Display"]); + do_check(r#"fn fooDisplay + Sync>() {}"#, &["Display", "Display + "]); do_check(r#"fn fooDisplay>() {}"#, &["Display", "+Display"]); do_check( r#"fn foo + Display, U: Copy>() {}"#, @@ -449,4 +451,56 @@ fn foo() ], ); } + + #[test] + fn test_extend_selection_on_tuple_in_type() { + do_check( + r#"fn main() { let _: (krate, <|>_crate_def_map, module_id) = (); }"#, + &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"], + ); + // white space variations + do_check( + r#"fn main() { let _: (krate,<|>_crate_def_map,module_id) = (); }"#, + &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"], + ); + do_check( + r#" +fn main() { let _: ( + krate, + _crate<|>_def_map, + module_id +) = (); }"#, + &[ + "_crate_def_map", + "_crate_def_map,", + "(\n krate,\n _crate_def_map,\n module_id\n)", + ], + ); + } + + #[test] + fn test_extend_selection_on_tuple_in_rvalue() { + do_check( + r#"fn main() { let var = (krate, _crate_def_map<|>, module_id); }"#, + &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"], + ); + // white space variations + do_check( + r#"fn main() { let var = (krate,_crate<|>_def_map,module_id); }"#, + &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"], + ); + do_check( + r#" +fn main() { let var = ( + krate, + _crate_def_map<|>, + module_id +); }"#, + &[ + "_crate_def_map", + "_crate_def_map,", + "(\n krate,\n _crate_def_map,\n module_id\n)", + ], + ); + } } diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index c10a6c8448..79d332e8ce 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -1,9 +1,11 @@ //! FIXME: write short doc here -use hir::{db::AstDatabase, Source}; +use hir::{db::AstDatabase, InFile}; use ra_syntax::{ ast::{self, DocCommentsOwner}, - match_ast, AstNode, SyntaxNode, + match_ast, AstNode, + SyntaxKind::*, + SyntaxNode, SyntaxToken, TokenAtOffset, }; use crate::{ @@ -19,25 +21,33 @@ pub(crate) fn goto_definition( position: FilePosition, ) -> Option>> { let file = db.parse_or_expand(position.file_id.into())?; - let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; - let token = descend_into_macros(db, position.file_id, token); + let original_token = pick_best(file.token_at_offset(position.offset))?; + let token = descend_into_macros(db, position.file_id, original_token.clone()); - let res = match_ast! { + let nav_targets = match_ast! { match (token.value.parent()) { ast::NameRef(name_ref) => { - let navs = reference_definition(db, token.with_value(&name_ref)).to_vec(); - RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec()) + reference_definition(db, token.with_value(&name_ref)).to_vec() }, ast::Name(name) => { - let navs = name_definition(db, token.with_value(&name))?; - RangeInfo::new(name.syntax().text_range(), navs) - + name_definition(db, token.with_value(&name))? }, _ => return None, } }; - Some(res) + Some(RangeInfo::new(original_token.text_range(), nav_targets)) +} + +fn pick_best(tokens: TokenAtOffset) -> Option { + return tokens.max_by_key(priority); + fn priority(n: &SyntaxToken) -> usize { + match n.kind() { + IDENT | INT_NUMBER => 2, + kind if kind.is_trivia() => 0, + _ => 1, + } + } } #[derive(Debug)] @@ -58,15 +68,17 @@ impl ReferenceResult { pub(crate) fn reference_definition( db: &RootDatabase, - name_ref: Source<&ast::NameRef>, + name_ref: InFile<&ast::NameRef>, ) -> ReferenceResult { use self::ReferenceResult::*; let name_kind = classify_name_ref(db, name_ref).map(|d| d.kind); match name_kind { - Some(Macro(mac)) => return Exact(mac.to_nav(db)), - Some(Field(field)) => return Exact(field.to_nav(db)), - Some(AssocItem(assoc)) => return Exact(assoc.to_nav(db)), + Some(Macro(it)) => return Exact(it.to_nav(db)), + Some(Field(it)) => return Exact(it.to_nav(db)), + Some(TypeParam(it)) => return Exact(it.to_nav(db)), + Some(AssocItem(it)) => return Exact(it.to_nav(db)), + Some(Local(it)) => return Exact(it.to_nav(db)), Some(Def(def)) => match NavigationTarget::from_def(db, def) { Some(nav) => return Exact(nav), None => return Approximate(vec![]), @@ -77,10 +89,6 @@ pub(crate) fn reference_definition( // us to the actual type return Exact(imp.to_nav(db)); } - Some(Local(local)) => return Exact(local.to_nav(db)), - Some(GenericParam(_)) => { - // FIXME: go to the generic param def - } None => {} }; @@ -94,7 +102,7 @@ pub(crate) fn reference_definition( pub(crate) fn name_definition( db: &RootDatabase, - name: Source<&ast::Name>, + name: InFile<&ast::Name>, ) -> Option> { let parent = name.value.syntax().parent()?; @@ -115,7 +123,7 @@ pub(crate) fn name_definition( None } -fn named_target(db: &RootDatabase, node: Source<&SyntaxNode>) -> Option { +fn named_target(db: &RootDatabase, node: InFile<&SyntaxNode>) -> Option { match_ast! { match (node.value) { ast::StructDef(it) => { @@ -213,21 +221,44 @@ fn named_target(db: &RootDatabase, node: Source<&SyntaxNode>) -> Option) } ", "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", + "struct Foo;|Foo", + ); + } + + #[test] + fn goto_def_at_start_of_item() { + check_goto( + " + //- /lib.rs + struct Foo; + enum E { X(<|>Foo) } + ", + "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", + "struct Foo;|Foo", ); } @@ -247,61 +292,65 @@ mod tests { mod a; mod b; enum E { X(Foo<|>) } + //- /a.rs struct Foo; + //- /b.rs struct Foo; ", "Foo STRUCT_DEF FileId(2) [0; 11) [7; 10)", + "struct Foo;|Foo", ); } #[test] - fn goto_definition_works_for_module_declaration() { + fn goto_def_for_module_declaration() { check_goto( " //- /lib.rs mod <|>foo; + //- /foo.rs // empty ", "foo SOURCE_FILE FileId(2) [0; 10)", + "// empty\n\n", ); check_goto( " //- /lib.rs mod <|>foo; + //- /foo/mod.rs // empty ", "foo SOURCE_FILE FileId(2) [0; 10)", + "// empty\n\n", ); } #[test] - fn goto_definition_works_for_macros() { - covers!(goto_definition_works_for_macros); + fn goto_def_for_macros() { + covers!(goto_def_for_macros); check_goto( " //- /lib.rs - macro_rules! foo { - () => { - {} - }; - } + macro_rules! foo { () => { () } } fn bar() { <|>foo!(); } ", - "foo MACRO_CALL FileId(1) [0; 50) [13; 16)", + "foo MACRO_CALL FileId(1) [0; 33) [13; 16)", + "macro_rules! foo { () => { () } }|foo", ); } #[test] - fn goto_definition_works_for_macros_from_other_crates() { - covers!(goto_definition_works_for_macros); + fn goto_def_for_macros_from_other_crates() { + covers!(goto_def_for_macros); check_goto( " //- /lib.rs @@ -312,18 +361,15 @@ mod tests { //- /foo/lib.rs #[macro_export] - macro_rules! foo { - () => { - {} - }; - } + macro_rules! foo { () => { () } } ", - "foo MACRO_CALL FileId(2) [0; 66) [29; 32)", + "foo MACRO_CALL FileId(2) [0; 49) [29; 32)", + "#[macro_export]\nmacro_rules! foo { () => { () } }|foo", ); } #[test] - fn goto_definition_works_for_macros_in_use_tree() { + fn goto_def_for_macros_in_use_tree() { check_goto( " //- /lib.rs @@ -331,18 +377,15 @@ mod tests { //- /foo/lib.rs #[macro_export] - macro_rules! foo { - () => { - {} - }; - } + macro_rules! foo { () => { () } } ", - "foo MACRO_CALL FileId(2) [0; 66) [29; 32)", + "foo MACRO_CALL FileId(2) [0; 49) [29; 32)", + "#[macro_export]\nmacro_rules! foo { () => { () } }|foo", ); } #[test] - fn goto_definition_works_for_macro_defined_fn_with_arg() { + fn goto_def_for_macro_defined_fn_with_arg() { check_goto( " //- /lib.rs @@ -350,20 +393,19 @@ mod tests { ($name:ident) => (fn $name() {}) } - define_fn!( - foo - ) + define_fn!(foo); fn bar() { <|>foo(); } ", - "foo FN_DEF FileId(1) [80; 83) [80; 83)", + "foo FN_DEF FileId(1) [64; 80) [75; 78)", + "define_fn!(foo);|foo", ); } #[test] - fn goto_definition_works_for_macro_defined_fn_no_arg() { + fn goto_def_for_macro_defined_fn_no_arg() { check_goto( " //- /lib.rs @@ -377,32 +419,70 @@ mod tests { <|>foo(); } ", - "foo FN_DEF FileId(1) [39; 42) [39; 42)", + "foo FN_DEF FileId(1) [51; 64) [51; 64)", + "define_fn!();|define_fn!();", ); } #[test] - fn goto_definition_works_for_methods() { - covers!(goto_definition_works_for_methods); + fn goto_definition_works_for_macro_inside_pattern() { + check_goto( + " + //- /lib.rs + macro_rules! foo {() => {0}} + + fn bar() { + match (0,1) { + (<|>foo!(), _) => {} + } + } + ", + "foo MACRO_CALL FileId(1) [0; 28) [13; 16)", + "macro_rules! foo {() => {0}}|foo", + ); + } + + #[test] + fn goto_definition_works_for_macro_inside_match_arm_lhs() { + check_goto( + " + //- /lib.rs + macro_rules! foo {() => {0}} + + fn bar() { + match 0 { + <|>foo!() => {} + } + } + ", + "foo MACRO_CALL FileId(1) [0; 28) [13; 16)", + "macro_rules! foo {() => {0}}|foo", + ); + } + + #[test] + fn goto_def_for_methods() { + covers!(goto_def_for_methods); check_goto( " //- /lib.rs struct Foo; impl Foo { - fn frobnicate(&self) { } + fn frobnicate(&self) { } } fn bar(foo: &Foo) { foo.frobnicate<|>(); } ", - "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)", + "frobnicate FN_DEF FileId(1) [27; 51) [30; 40)", + "fn frobnicate(&self) { }|frobnicate", ); } #[test] - fn goto_definition_works_for_fields() { - covers!(goto_definition_works_for_fields); + fn goto_def_for_fields() { + covers!(goto_def_for_fields); check_goto( " //- /lib.rs @@ -415,12 +495,13 @@ mod tests { } ", "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", + "spam: u32|spam", ); } #[test] - fn goto_definition_works_for_record_fields() { - covers!(goto_definition_works_for_record_fields); + fn goto_def_for_record_fields() { + covers!(goto_def_for_record_fields); check_goto( " //- /lib.rs @@ -435,29 +516,48 @@ mod tests { } ", "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", + "spam: u32|spam", ); } #[test] - fn goto_definition_works_for_ufcs_inherent_methods() { + fn goto_for_tuple_fields() { + check_goto( + " + //- /lib.rs + struct Foo(u32); + + fn bar() { + let foo = Foo(0); + foo.<|>0; + } + ", + "TUPLE_FIELD_DEF FileId(1) [11; 14)", + "u32", + ); + } + + #[test] + fn goto_def_for_ufcs_inherent_methods() { check_goto( " //- /lib.rs struct Foo; impl Foo { - fn frobnicate() { } + fn frobnicate() { } } fn bar(foo: &Foo) { Foo::frobnicate<|>(); } ", - "frobnicate FN_DEF FileId(1) [27; 47) [30; 40)", + "frobnicate FN_DEF FileId(1) [27; 46) [30; 40)", + "fn frobnicate() { }|frobnicate", ); } #[test] - fn goto_definition_works_for_ufcs_trait_methods_through_traits() { + fn goto_def_for_ufcs_trait_methods_through_traits() { check_goto( " //- /lib.rs @@ -470,11 +570,12 @@ mod tests { } ", "frobnicate FN_DEF FileId(1) [16; 32) [19; 29)", + "fn frobnicate();|frobnicate", ); } #[test] - fn goto_definition_works_for_ufcs_trait_methods_through_self() { + fn goto_def_for_ufcs_trait_methods_through_self() { check_goto( " //- /lib.rs @@ -489,6 +590,7 @@ mod tests { } ", "frobnicate FN_DEF FileId(1) [30; 46) [33; 43)", + "fn frobnicate();|frobnicate", ); } @@ -505,6 +607,7 @@ mod tests { } ", "impl IMPL_BLOCK FileId(1) [12; 73)", + "impl Foo {...}", ); check_goto( @@ -518,6 +621,7 @@ mod tests { } ", "impl IMPL_BLOCK FileId(1) [12; 73)", + "impl Foo {...}", ); check_goto( @@ -531,6 +635,7 @@ mod tests { } ", "impl IMPL_BLOCK FileId(1) [15; 75)", + "impl Foo {...}", ); check_goto( @@ -543,6 +648,7 @@ mod tests { } ", "impl IMPL_BLOCK FileId(1) [15; 62)", + "impl Foo {...}", ); } @@ -562,6 +668,7 @@ mod tests { } ", "impl IMPL_BLOCK FileId(1) [49; 115)", + "impl Make for Foo {...}", ); check_goto( @@ -578,17 +685,19 @@ mod tests { } ", "impl IMPL_BLOCK FileId(1) [49; 115)", + "impl Make for Foo {...}", ); } #[test] - fn goto_definition_works_when_used_on_definition_name_itself() { + fn goto_def_when_used_on_definition_name_itself() { check_goto( " //- /lib.rs struct Foo<|> { value: u32 } ", "Foo STRUCT_DEF FileId(1) [0; 25) [7; 10)", + "struct Foo { value: u32 }|Foo", ); check_goto( @@ -599,15 +708,16 @@ mod tests { } "#, "field RECORD_FIELD_DEF FileId(1) [17; 30) [17; 22)", + "field: string|field", ); check_goto( " //- /lib.rs - fn foo_test<|>() { - } + fn foo_test<|>() { } ", "foo_test FN_DEF FileId(1) [0; 17) [3; 11)", + "fn foo_test() { }|foo_test", ); check_goto( @@ -618,6 +728,7 @@ mod tests { } ", "Foo ENUM_DEF FileId(1) [0; 25) [5; 8)", + "enum Foo {...}|Foo", ); check_goto( @@ -630,22 +741,25 @@ mod tests { } ", "Variant2 ENUM_VARIANT FileId(1) [29; 37) [29; 37)", + "Variant2|Variant2", ); check_goto( r#" //- /lib.rs - static inner<|>: &str = ""; + static INNER<|>: &str = ""; "#, - "inner STATIC_DEF FileId(1) [0; 24) [7; 12)", + "INNER STATIC_DEF FileId(1) [0; 24) [7; 12)", + "static INNER: &str = \"\";|INNER", ); check_goto( r#" //- /lib.rs - const inner<|>: &str = ""; + const INNER<|>: &str = ""; "#, - "inner CONST_DEF FileId(1) [0; 23) [6; 11)", + "INNER CONST_DEF FileId(1) [0; 23) [6; 11)", + "const INNER: &str = \"\";|INNER", ); check_goto( @@ -654,24 +768,25 @@ mod tests { type Thing<|> = Option<()>; "#, "Thing TYPE_ALIAS_DEF FileId(1) [0; 24) [5; 10)", + "type Thing = Option<()>;|Thing", ); check_goto( r#" //- /lib.rs - trait Foo<|> { - } + trait Foo<|> { } "#, "Foo TRAIT_DEF FileId(1) [0; 13) [6; 9)", + "trait Foo { }|Foo", ); check_goto( r#" //- /lib.rs - mod bar<|> { - } + mod bar<|> { } "#, "bar MODULE FileId(1) [0; 11) [4; 7)", + "mod bar { }|bar", ); } @@ -689,8 +804,128 @@ mod tests { fo<|>o(); } } + mod confuse_index { fn foo(); } ", "foo FN_DEF FileId(1) [52; 63) [55; 58)", + "fn foo() {}|foo", ); } + + #[test] + fn goto_through_format() { + check_goto( + " + //- /lib.rs + #[macro_export] + macro_rules! format { + ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) + } + #[rustc_builtin_macro] + #[macro_export] + macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) + } + pub mod __export { + pub use crate::format_args; + fn foo() {} // for index confusion + } + fn foo() -> i8 {} + fn test() { + format!(\"{}\", fo<|>o()) + } + ", + "foo FN_DEF FileId(1) [398; 415) [401; 404)", + "fn foo() -> i8 {}|foo", + ); + } + + #[test] + fn goto_for_type_param() { + check_goto( + " + //- /lib.rs + struct Foo { + t: <|>T, + } + ", + "T TYPE_PARAM FileId(1) [11; 12)", + "T", + ); + } + + #[test] + fn goto_within_macro() { + check_goto( + " + //- /lib.rs + macro_rules! id { + ($($tt:tt)*) => ($($tt)*) + } + + fn foo() { + let x = 1; + id!({ + let y = <|>x; + let z = y; + }); + } + ", + "x BIND_PAT FileId(1) [69; 70)", + "x", + ); + + check_goto( + " + //- /lib.rs + macro_rules! id { + ($($tt:tt)*) => ($($tt)*) + } + + fn foo() { + let x = 1; + id!({ + let y = x; + let z = <|>y; + }); + } + ", + "y BIND_PAT FileId(1) [98; 99)", + "y", + ); + } + + #[test] + fn goto_def_in_local_fn() { + check_goto( + " + //- /lib.rs + fn main() { + fn foo() { + let x = 92; + <|>x; + } + } + ", + "x BIND_PAT FileId(1) [39; 40)", + "x", + ); + } + + #[test] + fn goto_def_for_field_init_shorthand() { + covers!(goto_def_for_field_init_shorthand); + check_goto( + " + //- /lib.rs + struct Foo { x: i32 } + fn main() { + let x = 92; + Foo { x<|> }; + } + ", + "x RECORD_FIELD_DEF FileId(1) [13; 19) [13; 14)", + "x: i32|x", + ) + } } diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index 992a088090..ce8b6c72a1 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs @@ -1,7 +1,7 @@ //! FIXME: write short doc here use hir::db::AstDatabase; -use ra_syntax::{ast, AstNode}; +use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; use crate::{ db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, @@ -13,7 +13,7 @@ pub(crate) fn goto_type_definition( position: FilePosition, ) -> Option>> { let file = db.parse_or_expand(position.file_id.into())?; - let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; + let token = pick_best(file.token_at_offset(position.offset))?; let token = descend_into_macros(db, position.file_id, token); let node = token.value.ancestors().find_map(|token| { @@ -41,6 +41,17 @@ pub(crate) fn goto_type_definition( Some(RangeInfo::new(node.text_range(), vec![nav])) } +fn pick_best(tokens: TokenAtOffset) -> Option { + return tokens.max_by_key(priority); + fn priority(n: &SyntaxToken) -> usize { + match n.kind() { + IDENT | INT_NUMBER => 2, + kind if kind.is_trivia() => 0, + _ => 1, + } + } +} + #[cfg(test)] mod tests { use crate::mock_analysis::analysis_and_position; @@ -102,4 +113,32 @@ mod tests { "Foo STRUCT_DEF FileId(1) [52; 65) [59; 62)", ); } + + #[test] + fn goto_type_definition_for_param() { + check_goto( + " + //- /lib.rs + struct Foo; + fn foo(<|>f: Foo) {} + ", + "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", + ); + } + + #[test] + fn goto_type_definition_for_tuple_field() { + check_goto( + " + //- /lib.rs + struct Foo; + struct Bar(Foo); + fn foo() { + let bar = Bar(Foo); + bar.<|>0; + } + ", + "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", + ); + } } diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 260a7b8694..35e39f9652 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -6,14 +6,13 @@ use ra_syntax::{ algo::find_covering_element, ast::{self, DocCommentsOwner}, match_ast, AstNode, + SyntaxKind::*, + SyntaxToken, TokenAtOffset, }; use crate::{ db::RootDatabase, - display::{ - description_from_symbol, docs_from_symbol, macro_label, rust_code_markup, - rust_code_markup_with_doc, ShortLabel, - }, + display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, expand::descend_into_macros, references::{classify_name, classify_name_ref, NameKind, NameKind::*}, FilePosition, FileRange, RangeInfo, @@ -93,11 +92,7 @@ fn hover_text(docs: Option, desc: Option) -> Option { } } -fn hover_text_from_name_kind( - db: &RootDatabase, - name_kind: NameKind, - no_fallback: &mut bool, -) -> Option { +fn hover_text_from_name_kind(db: &RootDatabase, name_kind: NameKind) -> Option { return match name_kind { Macro(it) => { let src = it.source(db); @@ -133,12 +128,8 @@ fn hover_text_from_name_kind( hir::ModuleDef::TypeAlias(it) => from_def_source(db, it), hir::ModuleDef::BuiltinType(it) => Some(it.to_string()), }, - Local(_) => { - // Hover for these shows type names - *no_fallback = true; - None - } - GenericParam(_) | SelfType(_) => { + Local(_) => None, + TypeParam(_) | SelfType(_) => { // FIXME: Hover for generic param None } @@ -156,68 +147,55 @@ fn hover_text_from_name_kind( pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option> { let file = db.parse_or_expand(position.file_id.into())?; - let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; + let token = pick_best(file.token_at_offset(position.offset))?; let token = descend_into_macros(db, position.file_id, token); let mut res = HoverResult::new(); - let mut range = match_ast! { + if let Some((range, name_kind)) = match_ast! { match (token.value.parent()) { ast::NameRef(name_ref) => { - let mut no_fallback = false; - if let Some(name_kind) = - classify_name_ref(db, token.with_value(&name_ref)).map(|d| d.kind) - { - res.extend(hover_text_from_name_kind(db, name_kind, &mut no_fallback)) - } - - if res.is_empty() && !no_fallback { - // Fallback index based approach: - let symbols = crate::symbol_index::index_resolve(db, &name_ref); - for sym in symbols { - let docs = docs_from_symbol(db, &sym); - let desc = description_from_symbol(db, &sym); - res.extend(hover_text(docs, desc)); - } - } - - if !res.is_empty() { - Some(name_ref.syntax().text_range()) - } else { - None - } + classify_name_ref(db, token.with_value(&name_ref)).map(|d| (name_ref.syntax().text_range(), d.kind)) }, ast::Name(name) => { - if let Some(name_kind) = classify_name(db, token.with_value(&name)).map(|d| d.kind) { - res.extend(hover_text_from_name_kind(db, name_kind, &mut true)); - } - - if !res.is_empty() { - Some(name.syntax().text_range()) - } else { - None - } + classify_name(db, token.with_value(&name)).map(|d| (name.syntax().text_range(), d.kind)) }, _ => None, } - }; + } { + res.extend(hover_text_from_name_kind(db, name_kind)); - if range.is_none() { - let node = token.value.ancestors().find(|n| { - ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some() - })?; - let frange = FileRange { file_id: position.file_id, range: node.text_range() }; - res.extend(type_of(db, frange).map(rust_code_markup)); - range = Some(node.text_range()); - }; + if !res.is_empty() { + return Some(RangeInfo::new(range, res)); + } + } - let range = range?; + let node = token + .value + .ancestors() + .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; + let frange = FileRange { file_id: position.file_id, range: node.text_range() }; + res.extend(type_of(db, frange).map(rust_code_markup)); if res.is_empty() { return None; } + let range = node.text_range(); + Some(RangeInfo::new(range, res)) } +fn pick_best(tokens: TokenAtOffset) -> Option { + return tokens.max_by_key(priority); + fn priority(n: &SyntaxToken) -> usize { + match n.kind() { + IDENT | INT_NUMBER => 3, + L_PAREN | R_PAREN => 2, + kind if kind.is_trivia() => 0, + _ => 1, + } + } +} + pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option { let parse = db.parse(frange.file_id); let leaf_node = find_covering_element(parse.tree().syntax(), frange.range); @@ -227,7 +205,7 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option { .take_while(|it| it.text_range() == leaf_node.text_range()) .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?; let analyzer = - hir::SourceAnalyzer::new(db, hir::Source::new(frange.file_id.into(), &node), None); + hir::SourceAnalyzer::new(db, hir::InFile::new(frange.file_id.into(), &node), None); let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e)) { ty @@ -236,7 +214,7 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option { } else { return None; }; - Some(ty.display(db).to_string()) + Some(ty.display_truncated(db, None).to_string()) } #[cfg(test)] @@ -300,7 +278,7 @@ mod tests { &["pub fn foo() -> u32"], ); - // Multiple results + // Multiple candidates but results are ambiguous. check_hover_result( r#" //- /a.rs @@ -321,7 +299,7 @@ mod tests { let foo_test = fo<|>o(); } "#, - &["pub fn foo() -> &str", "pub fn foo() -> u32", "pub fn foo(a: u32, b: u32)"], + &["{unknown}"], ); } @@ -410,6 +388,23 @@ mod tests { ); } + #[test] + fn hover_omits_default_generic_types() { + check_hover_result( + r#" +//- /main.rs +struct Test { + k: K, + t: T, +} + +fn main() { + let zz<|> = Test { t: 23, k: 33 }; +}"#, + &["Test"], + ); + } + #[test] fn hover_some() { let (analysis, position) = single_file_with_position( @@ -504,6 +499,13 @@ fn func(foo: i32) { if true { <|>foo; }; } assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); } + #[test] + fn hover_for_param_edge() { + let (analysis, position) = single_file_with_position("fn func(<|>foo: i32) {}"); + let hover = analysis.hover(position).unwrap().unwrap(); + assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); + } + #[test] fn test_type_of_for_function() { let (analysis, range) = single_file_with_range( diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs index aa480e399b..9b165ee2aa 100644 --- a/crates/ra_ide/src/impls.rs +++ b/crates/ra_ide/src/impls.rs @@ -16,7 +16,7 @@ pub(crate) fn goto_implementation( let src = hir::ModuleSource::from_position(db, position); let module = hir::Module::from_definition( db, - hir::Source { file_id: position.file_id.into(), value: src }, + hir::InFile { file_id: position.file_id.into(), value: src }, )?; if let Some(nominal_def) = find_node_at_offset::(&syntax, position.offset) { @@ -42,15 +42,15 @@ fn impls_for_def( ) -> Option> { let ty = match node { ast::NominalDef::StructDef(def) => { - let src = hir::Source { file_id: position.file_id.into(), value: def.clone() }; + let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; hir::Struct::from_source(db, src)?.ty(db) } ast::NominalDef::EnumDef(def) => { - let src = hir::Source { file_id: position.file_id.into(), value: def.clone() }; + let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; hir::Enum::from_source(db, src)?.ty(db) } ast::NominalDef::UnionDef(def) => { - let src = hir::Source { file_id: position.file_id.into(), value: def.clone() }; + let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; hir::Union::from_source(db, src)?.ty(db) } }; @@ -73,7 +73,7 @@ fn impls_for_trait( node: &ast::TraitDef, module: hir::Module, ) -> Option> { - let src = hir::Source { file_id: position.file_id.into(), value: node.clone() }; + let src = hir::InFile { file_id: position.file_id.into(), value: node.clone() }; let tr = hir::Trait::from_source(db, src)?; let krate = module.krate(); diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 45149bf0cc..c5e4069773 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -1,12 +1,15 @@ //! FIXME: write short doc here -use crate::{db::RootDatabase, FileId}; use hir::{HirDisplay, SourceAnalyzer}; +use once_cell::unsync::Lazy; +use ra_prof::profile; use ra_syntax::{ ast::{self, AstNode, TypeAscriptionOwner}, match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, }; +use crate::{db::RootDatabase, FileId}; + #[derive(Debug, PartialEq, Eq)] pub enum InlayKind { TypeHint, @@ -27,7 +30,7 @@ pub(crate) fn inlay_hints( ) -> Vec { file.syntax() .descendants() - .map(|node| get_inlay_hints(db, file_id, &node, max_inlay_hint_length).unwrap_or_default()) + .flat_map(|node| get_inlay_hints(db, file_id, &node, max_inlay_hint_length)) .flatten() .collect() } @@ -38,7 +41,9 @@ fn get_inlay_hints( node: &SyntaxNode, max_inlay_hint_length: Option, ) -> Option> { - let analyzer = SourceAnalyzer::new(db, hir::Source::new(file_id.into(), node), None); + let _p = profile("get_inlay_hints"); + let analyzer = + Lazy::new(|| SourceAnalyzer::new(db, hir::InFile::new(file_id.into(), node), None)); match_ast! { match node { ast::LetStmt(it) => { @@ -122,18 +127,11 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec { while let Some(maybe_leaf_pat) = pats_to_process.pop_front() { match &maybe_leaf_pat { - ast::Pat::BindPat(bind_pat) => { - if let Some(pat) = bind_pat.pat() { - pats_to_process.push_back(pat); - } else { - leaf_pats.push(maybe_leaf_pat); - } - } - ast::Pat::TuplePat(tuple_pat) => { - for arg_pat in tuple_pat.args() { - pats_to_process.push_back(arg_pat); - } - } + ast::Pat::BindPat(bind_pat) => match bind_pat.pat() { + Some(pat) => pats_to_process.push_back(pat), + _ => leaf_pats.push(maybe_leaf_pat), + }, + ast::Pat::TuplePat(tuple_pat) => pats_to_process.extend(tuple_pat.args()), ast::Pat::RecordPat(record_pat) => { if let Some(pat_list) = record_pat.record_field_pat_list() { pats_to_process.extend( @@ -151,10 +149,9 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec { } } ast::Pat::TupleStructPat(tuple_struct_pat) => { - for arg_pat in tuple_struct_pat.args() { - pats_to_process.push_back(arg_pat); - } + pats_to_process.extend(tuple_struct_pat.args()) } + ast::Pat::RefPat(ref_pat) => pats_to_process.extend(ref_pat.pat()), _ => (), } } @@ -163,9 +160,36 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec { #[cfg(test)] mod tests { - use crate::mock_analysis::single_file; use insta::assert_debug_snapshot; + use crate::mock_analysis::single_file; + + #[test] + fn default_generic_types_should_not_be_displayed() { + let (analysis, file_id) = single_file( + r#" +struct Test { + k: K, + t: T, +} + +fn main() { + let zz = Test { t: 23, k: 33 }; +}"#, + ); + + assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" + [ + InlayHint { + range: [69; 71), + kind: TypeHint, + label: "Test", + }, + ] + "### + ); + } + #[test] fn let_statement() { let (analysis, file_id) = single_file( @@ -202,6 +226,7 @@ fn main() { let test = (42, 'a'); let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); + let &x = &92; }"#, ); @@ -257,6 +282,11 @@ fn main() { kind: TypeHint, label: "f64", }, + InlayHint { + range: [627; 628), + kind: TypeHint, + label: "i32", + }, ] "### ); diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index d1bff4a761..779a81b2c0 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -422,6 +422,11 @@ impl Analysis { self.with_db(|db| parent_module::crate_for(db, file_id)) } + /// Returns the edition of the given crate. + pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable { + self.with_db(|db| db.crate_graph().edition(crate_id)) + } + /// Returns the root file of the given crate. pub fn crate_root(&self, crate_id: CrateId) -> Cancelable { self.with_db(|db| db.crate_graph().crate_root(crate_id)) diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs index 848ae4dc74..077a44473b 100644 --- a/crates/ra_ide/src/marks.rs +++ b/crates/ra_ide/src/marks.rs @@ -3,10 +3,11 @@ test_utils::marks!( inserts_angle_brackets_for_generics inserts_parens_for_function_calls - goto_definition_works_for_macros - goto_definition_works_for_methods - goto_definition_works_for_fields - goto_definition_works_for_record_fields + goto_def_for_macros + goto_def_for_methods + goto_def_for_fields + goto_def_for_record_fields + goto_def_for_field_init_shorthand call_info_bad_offset dont_complete_current_use dont_complete_primitive_in_use diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index 6027e7d54d..f5a788c07c 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -use ra_db::{CrateId, FileId, FilePosition}; +use ra_db::{CrateId, FileId, FilePosition, SourceDatabase}; use crate::{db::RootDatabase, NavigationTarget}; @@ -10,7 +10,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec return Vec::new(), Some(it) => it, @@ -21,15 +21,16 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec Vec { - let src = hir::ModuleSource::from_file_id(db, file_id); + let source_file = db.parse(file_id).tree(); + let src = hir::ModuleSource::SourceFile(source_file); let module = - match hir::Module::from_definition(db, hir::Source { file_id: file_id.into(), value: src }) + match hir::Module::from_definition(db, hir::InFile { file_id: file_id.into(), value: src }) { Some(it) => it, None => return Vec::new(), }; let krate = module.krate(); - vec![krate.crate_id()] + vec![krate.into()] } #[cfg(test)] diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 21a1ea69eb..e3ecde50dd 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -14,7 +14,7 @@ mod name_definition; mod rename; mod search_scope; -use hir::Source; +use hir::InFile; use once_cell::unsync::Lazy; use ra_db::{SourceDatabase, SourceDatabaseExt}; use ra_prof::profile; @@ -85,7 +85,7 @@ pub(crate) fn find_all_refs( NameKind::Def(def) => NavigationTarget::from_def(db, def)?, NameKind::SelfType(imp) => imp.to_nav(db), NameKind::Local(local) => local.to_nav(db), - NameKind::GenericParam(_) => return None, + NameKind::TypeParam(_) => return None, }; let search_scope = { @@ -107,12 +107,12 @@ fn find_name<'a>( position: FilePosition, ) -> Option> { if let Some(name) = find_node_at_offset::(&syntax, position.offset) { - let def = classify_name(db, Source::new(position.file_id.into(), &name))?; + let def = classify_name(db, InFile::new(position.file_id.into(), &name))?; let range = name.syntax().text_range(); return Some(RangeInfo::new(range, (name.text().to_string(), def))); } let name_ref = find_node_at_offset::(&syntax, position.offset)?; - let def = classify_name_ref(db, Source::new(position.file_id.into(), &name_ref))?; + let def = classify_name_ref(db, InFile::new(position.file_id.into(), &name_ref))?; let range = name_ref.syntax().text_range(); Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) } @@ -144,7 +144,7 @@ fn process_definition( continue; } } - if let Some(d) = classify_name_ref(db, Source::new(file_id.into(), &name_ref)) { + if let Some(d) = classify_name_ref(db, InFile::new(file_id.into(), &name_ref)) { if d == def { refs.push(FileRange { file_id, range }); } diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs index 5cea805ec9..3483a7176c 100644 --- a/crates/ra_ide/src/references/classify.rs +++ b/crates/ra_ide/src/references/classify.rs @@ -1,6 +1,6 @@ //! Functions that are used to classify an element from its definition or reference. -use hir::{FromSource, Module, ModuleSource, PathResolution, Source, SourceAnalyzer}; +use hir::{FromSource, InFile, Module, ModuleSource, PathResolution, SourceAnalyzer}; use ra_prof::profile; use ra_syntax::{ast, match_ast, AstNode}; use test_utils::tested_by; @@ -11,7 +11,7 @@ use super::{ }; use crate::db::RootDatabase; -pub(crate) fn classify_name(db: &RootDatabase, name: Source<&ast::Name>) -> Option { +pub(crate) fn classify_name(db: &RootDatabase, name: InFile<&ast::Name>) -> Option { let _p = profile("classify_name"); let parent = name.value.syntax().parent()?; @@ -110,6 +110,15 @@ pub(crate) fn classify_name(db: &RootDatabase, name: Source<&ast::Name>) -> Opti kind: NameKind::Macro(def), }) }, + ast::TypeParam(it) => { + let src = name.with_value(it); + let def = hir::TypeParam::from_source(db, src)?; + Some(NameDefinition { + visibility: None, + container: def.module(db), + kind: NameKind::TypeParam(def), + }) + }, _ => None, } } @@ -117,7 +126,7 @@ pub(crate) fn classify_name(db: &RootDatabase, name: Source<&ast::Name>) -> Opti pub(crate) fn classify_name_ref( db: &RootDatabase, - name_ref: Source<&ast::NameRef>, + name_ref: InFile<&ast::NameRef>, ) -> Option { let _p = profile("classify_name_ref"); @@ -125,21 +134,22 @@ pub(crate) fn classify_name_ref( let analyzer = SourceAnalyzer::new(db, name_ref.map(|it| it.syntax()), None); if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { - tested_by!(goto_definition_works_for_methods); + tested_by!(goto_def_for_methods); if let Some(func) = analyzer.resolve_method_call(&method_call) { return Some(from_assoc_item(db, func.into())); } } if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { - tested_by!(goto_definition_works_for_fields); + tested_by!(goto_def_for_fields); if let Some(field) = analyzer.resolve_field(&field_expr) { return Some(from_struct_field(db, field)); } } if let Some(record_field) = ast::RecordField::cast(parent.clone()) { - tested_by!(goto_definition_works_for_record_fields); + tested_by!(goto_def_for_record_fields); + tested_by!(goto_def_for_field_init_shorthand); if let Some(field_def) = analyzer.resolve_record_field(&record_field) { return Some(from_struct_field(db, field_def)); } @@ -151,7 +161,7 @@ pub(crate) fn classify_name_ref( let visibility = None; if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { - tested_by!(goto_definition_works_for_macros); + tested_by!(goto_def_for_macros); if let Some(macro_def) = analyzer.resolve_macro_call(db, name_ref.with_value(¯o_call)) { let kind = NameKind::Macro(macro_def); return Some(NameDefinition { kind, container, visibility }); @@ -168,9 +178,8 @@ pub(crate) fn classify_name_ref( let kind = NameKind::Local(local); Some(NameDefinition { kind, container, visibility: None }) } - PathResolution::GenericParam(par) => { - // FIXME: get generic param def - let kind = NameKind::GenericParam(par); + PathResolution::TypeParam(par) => { + let kind = NameKind::TypeParam(par); Some(NameDefinition { kind, container, visibility }) } PathResolution::Macro(def) => { diff --git a/crates/ra_ide/src/references/name_definition.rs b/crates/ra_ide/src/references/name_definition.rs index 10d3a2364c..8c67c88639 100644 --- a/crates/ra_ide/src/references/name_definition.rs +++ b/crates/ra_ide/src/references/name_definition.rs @@ -4,8 +4,8 @@ //! Note that the reference search is possible for not all of the classified items. use hir::{ - Adt, AssocItem, GenericParam, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, - StructField, VariantDef, + Adt, AssocItem, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, StructField, + TypeParam, VariantDef, }; use ra_syntax::{ast, ast::VisibilityOwner}; @@ -19,7 +19,7 @@ pub enum NameKind { Def(ModuleDef), SelfType(ImplBlock), Local(Local), - GenericParam(GenericParam), + TypeParam(TypeParam), } #[derive(PartialEq, Eq)] diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index d58496049e..b804d5f6da 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs @@ -2,7 +2,7 @@ use hir::ModuleSource; use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; -use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode}; +use ra_syntax::{algo::find_node_at_offset, ast, tokenize, AstNode, SyntaxKind, SyntaxNode}; use ra_text_edit::TextEdit; use crate::{ @@ -17,6 +17,13 @@ pub(crate) fn rename( position: FilePosition, new_name: &str, ) -> Option> { + let tokens = tokenize(new_name); + if tokens.len() != 1 + || (tokens[0].kind != SyntaxKind::IDENT && tokens[0].kind != SyntaxKind::UNDERSCORE) + { + return None; + } + let parse = db.parse(position.file_id); if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(parse.tree().syntax(), position) @@ -55,7 +62,7 @@ fn rename_mod( ) -> Option { let mut source_file_edits = Vec::new(); let mut file_system_edits = Vec::new(); - let module_src = hir::Source { file_id: position.file_id.into(), value: ast_module.clone() }; + let module_src = hir::InFile { file_id: position.file_id.into(), value: ast_module.clone() }; if let Some(module) = hir::Module::from_declaration(db, module_src) { let src = module.definition_source(db); let file_id = src.file_id.original_file(db); @@ -123,6 +130,49 @@ mod tests { mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, }; + #[test] + fn test_rename_to_underscore() { + test_rename( + r#" + fn main() { + let i<|> = 1; + }"#, + "_", + r#" + fn main() { + let _ = 1; + }"#, + ); + } + + #[test] + fn test_rename_to_raw_identifier() { + test_rename( + r#" + fn main() { + let i<|> = 1; + }"#, + "r#fn", + r#" + fn main() { + let r#fn = 1; + }"#, + ); + } + + #[test] + fn test_rename_to_invalid_identifier() { + let (analysis, position) = single_file_with_position( + " + fn main() { + let i<|> = 1; + }", + ); + let new_name = "invalid!"; + let source_change = analysis.rename(position, new_name).unwrap(); + assert!(source_change.is_none()); + } + #[test] fn test_rename_for_local() { test_rename( diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs index f5c9589f4d..241dd358f9 100644 --- a/crates/ra_ide/src/references/search_scope.rs +++ b/crates/ra_ide/src/references/search_scope.rs @@ -5,7 +5,7 @@ use std::mem; use hir::{DefWithBody, HasSource, ModuleSource}; -use ra_db::{FileId, SourceDatabase, SourceDatabaseExt}; +use ra_db::{FileId, SourceDatabaseExt}; use ra_prof::profile; use ra_syntax::{AstNode, TextRange}; use rustc_hash::FxHashMap; @@ -120,15 +120,11 @@ impl NameDefinition { } if vis.as_str() == "pub" { let krate = self.container.krate(); - let crate_graph = db.crate_graph(); - for crate_id in crate_graph.iter() { - let mut crate_deps = crate_graph.dependencies(crate_id); - if crate_deps.any(|dep| dep.crate_id() == krate.crate_id()) { - let root_file = crate_graph.crate_root(crate_id); - let source_root_id = db.file_source_root(root_file); - let source_root = db.source_root(source_root_id); - res.extend(source_root.walk().map(|id| (id, None))); - } + for rev_dep in krate.reverse_dependencies(db) { + let root_file = rev_dep.root_file(db); + let source_root_id = db.file_source_root(root_file); + let source_root = db.source_root(source_root_id); + res.extend(source_root.walk().map(|id| (id, None))); } return SearchScope::new(res); } diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 8039a5164e..e213e1a06d 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -use hir::Source; +use hir::InFile; use itertools::Itertools; use ra_db::SourceDatabase; use ra_syntax::{ @@ -66,8 +66,8 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti return None; } let range = module.syntax().text_range(); - let src = hir::ModuleSource::from_child_node(db, Source::new(file_id.into(), &module.syntax())); - let module = hir::Module::from_definition(db, Source::new(file_id.into(), src))?; + let src = hir::ModuleSource::from_child_node(db, InFile::new(file_id.into(), &module.syntax())); + let module = hir::Module::from_definition(db, InFile::new(file_id.into(), src))?; let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); Some(Runnable { range, kind: RunnableKind::TestMod { path } }) diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index b39c4d3717..a097cf8e84 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html @@ -5,12 +5,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .comment { color: #7F9F7F; } .string { color: #CC9393; } +.field { color: #94BFF3; } .function { color: #93E0E3; } .parameter { color: #94BFF3; } -.builtin { color: #DD6718; } .text { color: #DCDCCC; } +.type { color: #7CB8BB; } +.type\.builtin { color: #8CD0D3; } +.type\.param { color: #20999D; } .attribute { color: #94BFF3; } .literal { color: #BFEBBF; } +.literal\.numeric { color: #6A8759; } .macro { color: #94BFF3; } .variable { color: #DCDCCC; } .variable\.mut { color: #DCDCCC; text-decoration: underline; } @@ -21,28 +25,37 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
#[derive(Clone, Debug)]
 struct Foo {
-    pub x: i32,
-    pub y: i32,
+    pub x: i32,
+    pub y: i32,
 }
 
-fn foo<T>() -> T {
+fn foo<T>() -> T {
     unimplemented!();
-    foo::<i32>();
+    foo::<i32>();
 }
 
 // comment
 fn main() {
-    println!("Hello, {}!", 92);
+    println!("Hello, {}!", 92);
 
     let mut vec = Vec::new();
     if true {
-        vec.push(Foo { x: 0, y: 1 });
+        let x = 92;
+        vec.push(Foo { x, y: 1 });
     }
-    unsafe { vec.set_len(0); }
+    unsafe { vec.set_len(0); }
 
-    let mut x = 42;
+    let mut x = 42;
     let y = &mut x;
     let z = &y;
 
     y;
+}
+
+enum E<X> {
+    V(X)
+}
+
+impl<X> E<X> {
+    fn new<T>() -> E<T> {}
 }
\ No newline at end of file diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 79f11ea80c..110556c092 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html @@ -5,12 +5,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .comment { color: #7F9F7F; } .string { color: #CC9393; } +.field { color: #94BFF3; } .function { color: #93E0E3; } .parameter { color: #94BFF3; } -.builtin { color: #DD6718; } .text { color: #DCDCCC; } +.type { color: #7CB8BB; } +.type\.builtin { color: #8CD0D3; } +.type\.param { color: #20999D; } .attribute { color: #94BFF3; } .literal { color: #BFEBBF; } +.literal\.numeric { color: #6A8759; } .macro { color: #94BFF3; } .variable { color: #DCDCCC; } .variable\.mut { color: #DCDCCC; text-decoration: underline; } diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 9a3e4c82f6..0228ee7e90 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -2,7 +2,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; -use hir::{Name, Source}; +use hir::{InFile, Name}; use ra_db::SourceDatabase; use ra_prof::profile; use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; @@ -16,6 +16,34 @@ use crate::{ FileId, }; +pub mod tags { + pub(crate) const FIELD: &str = "field"; + pub(crate) const FUNCTION: &str = "function"; + pub(crate) const MODULE: &str = "module"; + pub(crate) const TYPE: &str = "type"; + pub(crate) const CONSTANT: &str = "constant"; + pub(crate) const MACRO: &str = "macro"; + pub(crate) const VARIABLE: &str = "variable"; + pub(crate) const VARIABLE_MUT: &str = "variable.mut"; + pub(crate) const TEXT: &str = "text"; + + pub(crate) const TYPE_BUILTIN: &str = "type.builtin"; + pub(crate) const TYPE_SELF: &str = "type.self"; + pub(crate) const TYPE_PARAM: &str = "type.param"; + pub(crate) const TYPE_LIFETIME: &str = "type.lifetime"; + + pub(crate) const LITERAL_BYTE: &str = "literal.byte"; + pub(crate) const LITERAL_NUMERIC: &str = "literal.numeric"; + pub(crate) const LITERAL_CHAR: &str = "literal.char"; + pub(crate) const LITERAL_COMMENT: &str = "comment"; + pub(crate) const LITERAL_STRING: &str = "string"; + pub(crate) const LITERAL_ATTRIBUTE: &str = "attribute"; + + pub(crate) const KEYWORD_UNSAFE: &str = "keyword.unsafe"; + pub(crate) const KEYWORD_CONTROL: &str = "keyword.control"; + pub(crate) const KEYWORD: &str = "keyword"; +} + #[derive(Debug)] pub struct HighlightedRange { pub range: TextRange, @@ -71,17 +99,16 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec "comment", - STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", - ATTR => "attribute", + COMMENT => tags::LITERAL_COMMENT, + STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, + ATTR => tags::LITERAL_ATTRIBUTE, + // Special-case field init shorthand + NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, + NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => continue, NAME_REF => { - if node.ancestors().any(|it| it.kind() == ATTR) { - continue; - } - let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); let name_kind = - classify_name_ref(db, Source::new(file_id.into(), &name_ref)).map(|d| d.kind); + classify_name_ref(db, InFile::new(file_id.into(), &name_ref)).map(|d| d.kind); if let Some(Local(local)) = &name_kind { if let Some(name) = local.name(db) { @@ -90,12 +117,12 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec { let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); let name_kind = - classify_name(db, Source::new(file_id.into(), &name)).map(|d| d.kind); + classify_name(db, InFile::new(file_id.into(), &name)).map(|d| d.kind); if let Some(Local(local)) = &name_kind { if let Some(name) = local.name(db) { @@ -107,18 +134,21 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec highlight_name(db, name_kind), - None => name.syntax().parent().map_or("function", |x| match x.kind() { - TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => "type", - RECORD_FIELD_DEF => "field", - _ => "function", + None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() { + STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE, + TYPE_PARAM => tags::TYPE_PARAM, + RECORD_FIELD_DEF => tags::FIELD, + _ => tags::FUNCTION, }), } } - INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", - LIFETIME => "parameter", - T![unsafe] => "keyword.unsafe", - k if is_control_keyword(k) => "keyword.control", - k if k.is_keyword() => "keyword", + INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC, + BYTE => tags::LITERAL_BYTE, + CHAR => tags::LITERAL_CHAR, + LIFETIME => tags::TYPE_LIFETIME, + T![unsafe] => tags::KEYWORD_UNSAFE, + k if is_control_keyword(k) => tags::KEYWORD_CONTROL, + k if k.is_keyword() => tags::KEYWORD, _ => { if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) { if let Some(path) = macro_call.path() { @@ -135,7 +165,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec &'static str { match name_kind { - Macro(_) => "macro", - Field(_) => "field", - AssocItem(hir::AssocItem::Function(_)) => "function", - AssocItem(hir::AssocItem::Const(_)) => "constant", - AssocItem(hir::AssocItem::TypeAlias(_)) => "type", - Def(hir::ModuleDef::Module(_)) => "module", - Def(hir::ModuleDef::Function(_)) => "function", - Def(hir::ModuleDef::Adt(_)) => "type", - Def(hir::ModuleDef::EnumVariant(_)) => "constant", - Def(hir::ModuleDef::Const(_)) => "constant", - Def(hir::ModuleDef::Static(_)) => "constant", - Def(hir::ModuleDef::Trait(_)) => "type", - Def(hir::ModuleDef::TypeAlias(_)) => "type", - Def(hir::ModuleDef::BuiltinType(_)) => "type", - SelfType(_) => "type", - GenericParam(_) => "type", + Macro(_) => tags::MACRO, + Field(_) => tags::FIELD, + AssocItem(hir::AssocItem::Function(_)) => tags::FUNCTION, + AssocItem(hir::AssocItem::Const(_)) => tags::CONSTANT, + AssocItem(hir::AssocItem::TypeAlias(_)) => tags::TYPE, + Def(hir::ModuleDef::Module(_)) => tags::MODULE, + Def(hir::ModuleDef::Function(_)) => tags::FUNCTION, + Def(hir::ModuleDef::Adt(_)) => tags::TYPE, + Def(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT, + Def(hir::ModuleDef::Const(_)) => tags::CONSTANT, + Def(hir::ModuleDef::Static(_)) => tags::CONSTANT, + Def(hir::ModuleDef::Trait(_)) => tags::TYPE, + Def(hir::ModuleDef::TypeAlias(_)) => tags::TYPE, + Def(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN, + SelfType(_) => tags::TYPE_SELF, + TypeParam(_) => tags::TYPE_PARAM, Local(local) => { - if local.is_mut(db) { - "variable.mut" - } else if local.ty(db).is_mutable_reference() { - "variable.mut" + if local.is_mut(db) || local.ty(db).is_mutable_reference() { + tags::VARIABLE_MUT } else { - "variable" + tags::VARIABLE } } } @@ -251,12 +279,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .comment { color: #7F9F7F; } .string { color: #CC9393; } +.field { color: #94BFF3; } .function { color: #93E0E3; } .parameter { color: #94BFF3; } -.builtin { color: #DD6718; } .text { color: #DCDCCC; } +.type { color: #7CB8BB; } +.type\\.builtin { color: #8CD0D3; } +.type\\.param { color: #20999D; } .attribute { color: #94BFF3; } .literal { color: #BFEBBF; } +.literal\\.numeric { color: #6A8759; } .macro { color: #94BFF3; } .variable { color: #DCDCCC; } .variable\\.mut { color: #DCDCCC; text-decoration: underline; } @@ -293,7 +325,8 @@ fn main() { let mut vec = Vec::new(); if true { - vec.push(Foo { x: 0, y: 1 }); + let x = 92; + vec.push(Foo { x, y: 1 }); } unsafe { vec.set_len(0); } @@ -303,6 +336,14 @@ fn main() { y; } + +enum E { + V(X) +} + +impl E { + fn new() -> E {} +} "# .trim(), ); diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 21aef842ce..60cbc38a95 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml @@ -13,9 +13,8 @@ relative-path = "1.0.0" serde_json = "1.0.34" serde = { version = "1.0.83", features = ["derive"] } crossbeam-channel = "0.4" -flexi_logger = "0.14.0" log = "0.4.3" -lsp-types = { version = "0.61.0", features = ["proposed"] } +lsp-types = { version = "0.66.0", features = ["proposed"] } rustc-hash = "1.0" parking_lot = "0.10.0" jod-thread = "0.1.0" @@ -27,6 +26,7 @@ lsp-server = "0.3.0" ra_project_model = { path = "../ra_project_model" } ra_prof = { path = "../ra_prof" } ra_vfs_glob = { path = "../ra_vfs_glob" } +env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } [dev-dependencies] tempfile = "3" diff --git a/crates/ra_lsp_server/build.rs b/crates/ra_lsp_server/build.rs new file mode 100644 index 0000000000..05f9772c04 --- /dev/null +++ b/crates/ra_lsp_server/build.rs @@ -0,0 +1,15 @@ +//! Just embed git-hash to `--version` + +use std::process::Command; + +fn main() { + let rev = rev().unwrap_or_else(|| "???????".to_string()); + println!("cargo:rustc-env=REV={}", rev) +} + +fn rev() -> Option { + let output = Command::new("git").args(&["rev-parse", "HEAD"]).output().ok()?; + let stdout = String::from_utf8(output.stdout).ok()?; + let short_hash = stdout.get(0..7)?; + Some(short_hash.to_owned()) +} diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs index eea0965edb..eeca67ee19 100644 --- a/crates/ra_lsp_server/src/caps.rs +++ b/crates/ra_lsp_server/src/caps.rs @@ -1,11 +1,12 @@ -//! FIXME: write short doc here +//! Advertizes the capabilities of the LSP Server. use lsp_types::{ CodeActionProviderCapability, CodeLensOptions, CompletionOptions, - DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, GenericCapability, - ImplementationProviderCapability, RenameOptions, RenameProviderCapability, ServerCapabilities, - SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, - TextDocumentSyncOptions, TypeDefinitionProviderCapability, + DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, + ImplementationProviderCapability, RenameOptions, RenameProviderCapability, + SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, + TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, + TypeDefinitionProviderCapability, WorkDoneProgressOptions, }; pub fn server_capabilities() -> ServerCapabilities { @@ -21,10 +22,14 @@ pub fn server_capabilities() -> ServerCapabilities { completion_provider: Some(CompletionOptions { resolve_provider: None, trigger_characters: Some(vec![":".to_string(), ".".to_string()]), + work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, }), signature_help_provider: Some(SignatureHelpOptions { - trigger_characters: Some(vec!["(".to_string(), ",".to_string(), ")".to_string()]), + trigger_characters: Some(vec!["(".to_string(), ",".to_string()]), + retrigger_characters: None, + work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, }), + declaration_provider: None, definition_provider: Some(true), type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)), implementation_provider: Some(ImplementationProviderCapability::Simple(true)), @@ -40,10 +45,11 @@ pub fn server_capabilities() -> ServerCapabilities { first_trigger_character: "=".to_string(), more_trigger_character: Some(vec![".".to_string(), ">".to_string()]), }), - selection_range_provider: Some(GenericCapability::default()), + selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), rename_provider: Some(RenameProviderCapability::Options(RenameOptions { prepare_provider: Some(true), + work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, })), document_link_provider: None, color_provider: None, diff --git a/crates/ra_lsp_server/src/config.rs b/crates/ra_lsp_server/src/config.rs index 8045f3d60d..67942aa414 100644 --- a/crates/ra_lsp_server/src/config.rs +++ b/crates/ra_lsp_server/src/config.rs @@ -9,6 +9,7 @@ use rustc_hash::FxHashMap; +use ra_project_model::CargoFeatures; use serde::{Deserialize, Deserializer}; /// Client provided initialization options @@ -37,6 +38,9 @@ pub struct ServerConfig { /// Fine grained feature flags to disable specific features. pub feature_flags: FxHashMap, + + /// Cargo feature configurations. + pub cargo_features: CargoFeatures, } impl Default for ServerConfig { @@ -49,6 +53,7 @@ impl Default for ServerConfig { max_inlay_hint_length: None, with_sysroot: true, feature_flags: FxHashMap::default(), + cargo_features: Default::default(), } } } diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index b13093cfe6..e93d4ea33d 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -1,4 +1,4 @@ -//! FIXME: write short doc here +//! Convenience module responsible for translating between rust-analyzer's types and LSP types. use lsp_types::{ self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation, @@ -130,6 +130,11 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { deprecated: Some(self.deprecated()), ..Default::default() }; + + if self.deprecated() { + res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) + } + res.insert_text_format = Some(match self.insert_text_format() { InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet, InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText, diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index e13c8ca144..cdd925c9f3 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs @@ -1,24 +1,22 @@ //! `ra_lsp_server` binary -use flexi_logger::{Duplicate, Logger}; use lsp_server::Connection; use ra_lsp_server::{show_message, Result, ServerConfig}; use ra_prof; fn main() -> Result<()> { setup_logging()?; - run_server()?; + match Args::parse()? { + Args::Version => println!("rust-analyzer {}", env!("REV")), + Args::Run => run_server()?, + } Ok(()) } fn setup_logging() -> Result<()> { std::env::set_var("RUST_BACKTRACE", "short"); - let logger = Logger::with_env_or_str("error").duplicate_to_stderr(Duplicate::All); - match std::env::var("RA_LOG_DIR") { - Ok(ref v) if v == "1" => logger.log_to_file().directory("log").start()?, - _ => logger.start()?, - }; + env_logger::try_init()?; ra_prof::set_filter(match std::env::var("RA_PROFILE") { Ok(spec) => ra_prof::Filter::from_spec(&spec), @@ -27,6 +25,19 @@ fn setup_logging() -> Result<()> { Ok(()) } +enum Args { + Version, + Run, +} + +impl Args { + fn parse() -> Result { + let res = + if std::env::args().any(|it| it == "--version") { Args::Version } else { Args::Run }; + Ok(res) + } +} + fn run_server() -> Result<()> { log::info!("lifecycle: server started"); diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 83845f1e00..dda318e43e 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs @@ -1,4 +1,5 @@ -//! FIXME: write short doc here +//! The main loop of `ra_lsp_server` responsible for dispatching LSP requests/replies and +//! notifications back to the client. mod handlers; mod subscriptions; @@ -67,6 +68,7 @@ pub fn main_loop( let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( ws_root.as_path(), config.with_sysroot, + &config.cargo_features, ); match workspace { Ok(workspace) => loaded_workspaces.push(workspace), @@ -130,7 +132,7 @@ pub fn main_loop( let feature_flags = { let mut ff = FeatureFlags::default(); for (flag, value) in config.feature_flags { - if let Err(_) = ff.set(flag.as_str(), value) { + if ff.set(flag.as_str(), value).is_err() { log::error!("unknown feature flag: {:?}", flag); show_message( req::MessageType::Error, @@ -303,7 +305,6 @@ fn loop_turn( log::info!("queued count = {}", queue_count); } - let mut state_changed = false; match event { Event::Task(task) => { on_task(task, &connection.sender, &mut loop_state.pending_requests, world_state); @@ -311,7 +312,6 @@ fn loop_turn( } Event::Vfs(task) => { world_state.vfs.write().handle_task(task); - state_changed = true; } Event::Lib(lib) => { world_state.add_lib(lib); @@ -336,7 +336,6 @@ fn loop_turn( &mut loop_state.subscriptions, not, )?; - state_changed = true; } Message::Response(resp) => { let removed = loop_state.pending_responses.remove(&resp.id); @@ -347,7 +346,12 @@ fn loop_turn( }, }; - loop_state.pending_libraries.extend(world_state.process_changes()); + let mut state_changed = false; + if let Some(changes) = world_state.process_changes() { + state_changed = true; + loop_state.pending_libraries.extend(changes); + } + while loop_state.in_flight_libraries < MAX_IN_FLIGHT_LIBS && !loop_state.pending_libraries.is_empty() { @@ -520,7 +524,8 @@ fn on_notification( if let Some(file_id) = state.vfs.write().remove_file_overlay(path.as_path()) { subs.remove_sub(FileId(file_id.0)); } - let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() }; + let params = + req::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None }; let not = notification_new::(params); msg_sender.send(not.into()).unwrap(); return Ok(()); diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index c81fa7f679..39eb3df3e5 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -1,4 +1,5 @@ -//! FIXME: write short doc here +//! This module is responsible for implementing handlers for Lanuage Server Protocol. +//! The majority of requests are fulfilled by calling into the `ra_ide` crate. use std::{fmt::Write as _, io::Write as _}; @@ -164,7 +165,7 @@ pub fn handle_on_type_formatting( // in `ra_ide`, the `on_type` invariant is that // `text.char_at(position) == typed_char`. - position.offset = position.offset - TextUnit::of_char('.'); + position.offset -= TextUnit::of_char('.'); let char_typed = params.ch.chars().next().unwrap_or('\0'); // We have an assist that inserts ` ` after typing `->` in `fn foo() ->{`, @@ -480,8 +481,6 @@ pub fn handle_prepare_rename( let _p = profile("handle_prepare_rename"); let position = params.try_conv_with(&world)?; - // We support renaming references like handle_rename does. - // In the future we may want to reject the renaming of things like keywords here too. let optional_change = world.analysis().rename(position, "dummy")?; let range = match optional_change { None => return Ok(None), @@ -557,12 +556,18 @@ pub fn handle_formatting( let _p = profile("handle_formatting"); let file_id = params.text_document.try_conv_with(&world)?; let file = world.analysis().file_text(file_id)?; + let crate_ids = world.analysis().crate_for(file_id)?; let file_line_index = world.analysis().file_line_index(file_id)?; let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); use std::process; let mut rustfmt = process::Command::new("rustfmt"); + if let Some(&crate_id) = crate_ids.first() { + // Assume all crates are in the same edition + let edition = world.analysis().crate_edition(crate_id)?; + rustfmt.args(&["--edition", &edition.to_string()]); + } rustfmt.stdin(process::Stdio::piped()).stdout(process::Stdio::piped()); if let Ok(path) = params.text_document.uri.to_file_path() { @@ -644,6 +649,7 @@ pub fn handle_code_action( diagnostics: None, edit: None, command: Some(command), + is_preferred: None, }; res.push(action.into()); } @@ -666,6 +672,7 @@ pub fn handle_code_action( diagnostics: None, edit: None, command: Some(command), + is_preferred: None, }; res.push(action.into()); } @@ -824,9 +831,10 @@ pub fn publish_diagnostics( source: Some("rust-analyzer".to_string()), message: d.message, related_information: None, + tags: None, }) .collect(); - Ok(req::PublishDiagnosticsParams { uri, diagnostics }) + Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None }) } pub fn publish_decorations( diff --git a/crates/ra_lsp_server/src/main_loop/pending_requests.rs b/crates/ra_lsp_server/src/main_loop/pending_requests.rs index e7ea7aa5b8..2d22134641 100644 --- a/crates/ra_lsp_server/src/main_loop/pending_requests.rs +++ b/crates/ra_lsp_server/src/main_loop/pending_requests.rs @@ -1,4 +1,4 @@ -//! FIXME: write short doc here +//! Datastructures that keep track of inflight requests. use std::time::{Duration, Instant}; diff --git a/crates/ra_lsp_server/src/main_loop/subscriptions.rs b/crates/ra_lsp_server/src/main_loop/subscriptions.rs index 609b2adcc9..b0bae90f5c 100644 --- a/crates/ra_lsp_server/src/main_loop/subscriptions.rs +++ b/crates/ra_lsp_server/src/main_loop/subscriptions.rs @@ -1,4 +1,4 @@ -//! FIXME: write short doc here +//! Keeps track of file subscriptions. use ra_ide::FileId; use rustc_hash::FxHashSet; diff --git a/crates/ra_lsp_server/src/markdown.rs b/crates/ra_lsp_server/src/markdown.rs index f51fc4ade3..76bef45ccc 100644 --- a/crates/ra_lsp_server/src/markdown.rs +++ b/crates/ra_lsp_server/src/markdown.rs @@ -1,4 +1,4 @@ -//! FIXME: write short doc here +//! Transforms markdown pub(crate) fn format_docs(src: &str) -> String { let mut processed_lines = Vec::new(); diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index 39361b7e8f..b34e6f9b89 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs @@ -1,4 +1,4 @@ -//! FIXME: write short doc here +//! Defines `rust-analyzer` specific custom messages. use lsp_types::{Location, Position, Range, TextDocumentIdentifier, Url}; use rustc_hash::FxHashMap; @@ -10,8 +10,9 @@ pub use lsp_types::{ DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType, PublishDiagnosticsParams, - ReferenceParams, Registration, RegistrationParams, ShowMessageParams, SignatureHelp, - TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, + ReferenceParams, Registration, RegistrationParams, SelectionRange, SelectionRangeParams, + ShowMessageParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, TextEdit, + WorkspaceEdit, WorkspaceSymbolParams, }; pub enum AnalyzerStatus {} @@ -67,28 +68,6 @@ pub struct ExpandMacroParams { pub position: Option, } -pub enum SelectionRangeRequest {} - -impl Request for SelectionRangeRequest { - type Params = SelectionRangeParams; - type Result = Vec; - const METHOD: &'static str = "textDocument/selectionRange"; -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct SelectionRangeParams { - pub text_document: TextDocumentIdentifier, - pub positions: Vec, -} - -#[derive(Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct SelectionRange { - pub range: Range, - pub parent: Option>, -} - pub enum FindMatchingBrace {} impl Request for FindMatchingBrace { diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 927449b45d..79431e7e6f 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -1,4 +1,7 @@ -//! FIXME: write short doc here +//! The context or environment in which the language server functions. +//! In our server implementation this is know as the `WorldState`. +//! +//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. use std::{ path::{Path, PathBuf}, @@ -17,11 +20,13 @@ use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace}; use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; use relative_path::RelativePathBuf; +use std::path::{Component, Prefix}; use crate::{ main_loop::pending_requests::{CompletedRequest, LatestRequests}, LspError, Result, }; +use std::str::FromStr; #[derive(Debug, Clone)] pub struct Options { @@ -140,10 +145,10 @@ impl WorldState { /// FIXME: better API here pub fn process_changes( &mut self, - ) -> Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc)>)> { + ) -> Option)>)>> { let changes = self.vfs.write().commit_changes(); if changes.is_empty() { - return Vec::new(); + return None; } let mut libs = Vec::new(); let mut change = AnalysisChange::new(); @@ -177,7 +182,7 @@ impl WorldState { } } self.analysis_host.apply_change(change); - libs + Some(libs) } pub fn add_lib(&mut self, data: LibraryData) { @@ -233,8 +238,8 @@ impl WorldSnapshot { pub fn file_id_to_uri(&self, id: FileId) -> Result { let path = self.vfs.read().file2path(VfsFile(id.0)); - let url = Url::from_file_path(&path) - .map_err(|_| format!("can't convert path to url: {}", path.display()))?; + let url = url_from_path_with_drive_lowercasing(path)?; + Ok(url) } @@ -279,3 +284,61 @@ impl WorldSnapshot { self.analysis.feature_flags() } } + +/// Returns a `Url` object from a given path, will lowercase drive letters if present. +/// This will only happen when processing windows paths. +/// +/// When processing non-windows path, this is essentially the same as `Url::from_file_path`. +fn url_from_path_with_drive_lowercasing(path: impl AsRef) -> Result { + let component_has_windows_drive = path.as_ref().components().any(|comp| { + if let Component::Prefix(c) = comp { + match c.kind() { + Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true, + _ => return false, + } + } + false + }); + + // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters. + if component_has_windows_drive { + let url_original = Url::from_file_path(&path) + .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?; + + let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect(); + + // There is a drive partition, but we never found a colon. + // This should not happen, but in this case we just pass it through. + if drive_partition.len() == 1 { + return Ok(url_original); + } + + let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0]; + let url = Url::from_str(&joined).expect("This came from a valid `Url`"); + + Ok(url) + } else { + Ok(Url::from_file_path(&path) + .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?) + } +} + +// `Url` is not able to parse windows paths on unix machines. +#[cfg(target_os = "windows")] +#[cfg(test)] +mod path_conversion_windows_tests { + use super::url_from_path_with_drive_lowercasing; + #[test] + fn test_lowercase_drive_letter_with_drive() { + let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap(); + + assert_eq!(url.to_string(), "file:///c:/Test"); + } + + #[test] + fn test_drive_without_colon_passthrough() { + let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap(); + + assert_eq!(url.to_string(), "file://localhost/C$/my_dir"); + } +} diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 2ba82ab051..dff63a12d4 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs @@ -4,7 +4,8 @@ use std::{collections::HashMap, time::Instant}; use lsp_types::{ CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions, - Position, Range, TextDocumentItem, TextDocumentPositionParams, + PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, + WorkDoneProgressParams, }; use ra_lsp_server::req::{ CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument, @@ -12,15 +13,19 @@ use ra_lsp_server::req::{ }; use serde_json::json; use tempfile::TempDir; +use test_utils::skip_slow_tests; use crate::support::{project, Project}; -const LOG: &'static str = ""; const PROFILE: &'static str = ""; // const PROFILE: &'static str = "*@3>100"; #[test] fn completes_items_from_standard_library() { + if skip_slow_tests() { + return; + } + let project_start = Instant::now(); let server = Project::with_fixture( r#" @@ -44,6 +49,8 @@ use std::collections::Spam; Position::new(0, 23), ), context: None, + partial_result_params: PartialResultParams::default(), + work_done_progress_params: WorkDoneProgressParams::default(), }); assert!(format!("{}", res).contains("HashMap")); eprintln!("completion took {:?}", completion_start.elapsed()); @@ -51,6 +58,10 @@ use std::collections::Spam; #[test] fn test_runnables_no_project() { + if skip_slow_tests() { + return; + } + let server = project( r" //- lib.rs @@ -100,6 +111,10 @@ fn foo() { #[test] fn test_runnables_project() { + if skip_slow_tests() { + return; + } + let code = r#" //- foo/Cargo.toml [package] @@ -171,8 +186,13 @@ fn main() {} #[test] fn test_format_document() { + if skip_slow_tests() { + return; + } + let server = project( r#" +//- Cargo.toml [package] name = "foo" version = "0.0.0" @@ -194,8 +214,12 @@ pub use std::collections::HashMap; options: FormattingOptions { tab_size: 4, insert_spaces: false, + insert_final_newline: None, + trim_final_newlines: None, + trim_trailing_whitespace: None, properties: HashMap::new(), }, + work_done_progress_params: WorkDoneProgressParams::default(), }, json!([ { @@ -220,8 +244,78 @@ pub use std::collections::HashMap; ); } +#[test] +fn test_format_document_2018() { + if skip_slow_tests() { + return; + } + + let server = project( + r#" +//- Cargo.toml +[package] +name = "foo" +version = "0.0.0" +edition = "2018" + +//- src/lib.rs +mod bar; + +async fn test() { +} + +fn main() { +} + +pub use std::collections::HashMap; +"#, + ); + server.wait_until_workspace_is_loaded(); + + server.request::( + DocumentFormattingParams { + text_document: server.doc_id("src/lib.rs"), + options: FormattingOptions { + tab_size: 4, + insert_spaces: false, + properties: HashMap::new(), + insert_final_newline: None, + trim_final_newlines: None, + trim_trailing_whitespace: None, + }, + work_done_progress_params: WorkDoneProgressParams::default(), + }, + json!([ + { + "newText": r#"mod bar; + +async fn test() {} + +fn main() {} + +pub use std::collections::HashMap; +"#, + "range": { + "end": { + "character": 0, + "line": 10 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ]), + ); +} + #[test] fn test_missing_module_code_action() { + if skip_slow_tests() { + return; + } + let server = project( r#" //- Cargo.toml @@ -242,6 +336,8 @@ fn main() {} text_document: server.doc_id("src/lib.rs"), range: Range::new(Position::new(0, 4), Position::new(0, 7)), context: empty_context(), + partial_result_params: PartialResultParams::default(), + work_done_progress_params: WorkDoneProgressParams::default(), }, json!([ { @@ -273,6 +369,8 @@ fn main() {} text_document: server.doc_id("src/lib.rs"), range: Range::new(Position::new(2, 4), Position::new(2, 7)), context: empty_context(), + partial_result_params: PartialResultParams::default(), + work_done_progress_params: WorkDoneProgressParams::default(), }, json!([]), ); @@ -280,6 +378,10 @@ fn main() {} #[test] fn test_missing_module_code_action_in_json_project() { + if skip_slow_tests() { + return; + } + let tmp_dir = TempDir::new().unwrap(); let path = tmp_dir.path(); @@ -317,6 +419,8 @@ fn main() {{}} text_document: server.doc_id("src/lib.rs"), range: Range::new(Position::new(0, 4), Position::new(0, 7)), context: empty_context(), + partial_result_params: PartialResultParams::default(), + work_done_progress_params: WorkDoneProgressParams::default(), }, json!([ { @@ -348,6 +452,8 @@ fn main() {{}} text_document: server.doc_id("src/lib.rs"), range: Range::new(Position::new(2, 4), Position::new(2, 7)), context: empty_context(), + partial_result_params: PartialResultParams::default(), + work_done_progress_params: WorkDoneProgressParams::default(), }, json!([]), ); @@ -355,6 +461,10 @@ fn main() {{}} #[test] fn diagnostics_dont_block_typing() { + if skip_slow_tests() { + return; + } + let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect(); let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect(); let server = Project::with_fixture(&format!( @@ -423,6 +533,10 @@ fn main() {{}} #[test] fn preserves_dos_line_endings() { + if skip_slow_tests() { + return; + } + let server = Project::with_fixture( &" //- Cargo.toml diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index 86073b57df..d5ea52fa95 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs @@ -7,7 +7,6 @@ use std::{ }; use crossbeam_channel::{after, select, Receiver}; -use flexi_logger::Logger; use lsp_server::{Connection, Message, Notification, Request}; use lsp_types::{ notification::{DidOpenTextDocument, Exit}, @@ -53,7 +52,7 @@ impl<'a> Project<'a> { let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap()); static INIT: Once = Once::new(); INIT.call_once(|| { - let _ = Logger::with_env_or_str(crate::LOG).start().unwrap(); + let _ = env_logger::builder().is_test(true).try_init().unwrap(); ra_prof::set_filter(if crate::PROFILE.is_empty() { ra_prof::Filter::disabled() } else { diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index bbddebe67f..2c6ae56589 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs @@ -67,7 +67,15 @@ impl Shift { .token_trees .iter() .filter_map(|tt| match tt { - tt::TokenTree::Subtree(subtree) => max_id(subtree), + tt::TokenTree::Subtree(subtree) => { + let tree_id = max_id(subtree); + match subtree.delimiter { + Some(it) if it.id != tt::TokenId::unspecified() => { + Some(tree_id.map_or(it.id.0, |t| t.max(it.id.0))) + } + _ => tree_id, + } + } tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) if ident.id != tt::TokenId::unspecified() => { @@ -85,9 +93,15 @@ impl Shift { match t { tt::TokenTree::Leaf(leaf) => match leaf { tt::Leaf::Ident(ident) => ident.id = self.shift(ident.id), - _ => (), + tt::Leaf::Punct(punct) => punct.id = self.shift(punct.id), + tt::Leaf::Literal(lit) => lit.id = self.shift(lit.id), }, - tt::TokenTree::Subtree(tt) => self.shift_all(tt), + tt::TokenTree::Subtree(tt) => { + if let Some(it) = tt.delimiter.as_mut() { + it.id = self.shift(it.id); + }; + self.shift_all(tt) + } } } } @@ -104,6 +118,7 @@ impl Shift { } } +#[derive(Debug, Eq, PartialEq)] pub enum Origin { Def, Call, @@ -159,14 +174,14 @@ impl Rule { .expect_subtree() .map_err(|()| ParseError::Expected("expected subtree".to_string()))? .clone(); - lhs.delimiter = tt::Delimiter::None; + lhs.delimiter = None; src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; let mut rhs = src .expect_subtree() .map_err(|()| ParseError::Expected("expected subtree".to_string()))? .clone(); - rhs.delimiter = tt::Delimiter::None; + rhs.delimiter = None; Ok(crate::Rule { lhs, rhs }) } } diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs index 33b9d483d2..e36b5a412b 100644 --- a/crates/ra_mbe/src/mbe_expander/matcher.rs +++ b/crates/ra_mbe/src/mbe_expander/matcher.rs @@ -16,7 +16,7 @@ impl Bindings { fn push_optional(&mut self, name: &SmolStr) { // FIXME: Do we have a better way to represent an empty token ? // Insert an empty subtree for empty token - let tt = tt::Subtree { delimiter: tt::Delimiter::None, token_trees: vec![] }.into(); + let tt = tt::Subtree::default().into(); self.inner.insert(name.clone(), Binding::Fragment(Fragment::Tokens(tt))); } @@ -65,7 +65,7 @@ macro_rules! bail { } pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result { - assert!(pattern.delimiter == tt::Delimiter::None); + assert!(pattern.delimiter == None); let mut res = Bindings::default(); let mut src = TtIter::new(src); @@ -106,7 +106,7 @@ fn match_subtree( } Op::TokenTree(tt::TokenTree::Subtree(lhs)) => { let rhs = src.expect_subtree().map_err(|()| err!("expected subtree"))?; - if lhs.delimiter != rhs.delimiter { + if lhs.delimiter_kind() != rhs.delimiter_kind() { bail!("mismatched delimiter") } let mut src = TtIter::new(rhs); @@ -210,7 +210,7 @@ impl<'a> TtIter<'a> { 0 => Err(()), 1 => Ok(res[0].clone()), _ => Ok(tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::None, + delimiter: None, token_trees: res.into_iter().cloned().collect(), })), } diff --git a/crates/ra_mbe/src/mbe_expander/transcriber.rs b/crates/ra_mbe/src/mbe_expander/transcriber.rs index ed094d5bb4..eda66cd506 100644 --- a/crates/ra_mbe/src/mbe_expander/transcriber.rs +++ b/crates/ra_mbe/src/mbe_expander/transcriber.rs @@ -50,7 +50,7 @@ pub(super) fn transcribe( template: &tt::Subtree, bindings: &Bindings, ) -> Result { - assert!(template.delimiter == tt::Delimiter::None); + assert!(template.delimiter == None); let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new(), var_expanded: false }; expand_subtree(&mut ctx, template) } @@ -106,9 +106,14 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> Result // ``` // We just treat it a normal tokens let tt = tt::Subtree { - delimiter: tt::Delimiter::None, + delimiter: None, token_trees: vec![ - tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone }).into(), + tt::Leaf::from(tt::Punct { + char: '$', + spacing: tt::Spacing::Alone, + id: tt::TokenId::unspecified(), + }) + .into(), tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() }) .into(), ], @@ -147,7 +152,7 @@ fn expand_repeat( ctx.var_expanded = false; while let Ok(mut t) = expand_subtree(ctx, template) { - t.delimiter = tt::Delimiter::None; + t.delimiter = None; // if no var expanded in the child, we count it as a fail if !ctx.var_expanded { break; @@ -212,7 +217,7 @@ fn expand_repeat( // Check if it is a single token subtree without any delimiter // e.g {Delimiter:None> ['>'] /Delimiter:None>} - let tt = tt::Subtree { delimiter: tt::Delimiter::None, token_trees: buf }.into(); + let tt = tt::Subtree { delimiter: None, token_trees: buf }.into(); Ok(Fragment::Tokens(tt)) } @@ -225,7 +230,7 @@ fn push_fragment(buf: &mut Vec, fragment: Fragment) { fn push_subtree(buf: &mut Vec, tt: tt::Subtree) { match tt.delimiter { - tt::Delimiter::None => buf.extend(tt.token_trees), + None => buf.extend(tt.token_trees), _ => buf.push(tt.into()), } } diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs index 7ef45f6dc4..b841c39d31 100644 --- a/crates/ra_mbe/src/subtree_source.rs +++ b/crates/ra_mbe/src/subtree_source.rs @@ -70,11 +70,11 @@ impl<'a> SubtreeTokenSource<'a> { } Some(tt::TokenTree::Subtree(subtree)) => { self.cached_cursor.set(cursor.subtree().unwrap()); - cached.push(Some(convert_delim(subtree.delimiter, false))); + cached.push(Some(convert_delim(subtree.delimiter_kind(), false))); } None => { if let Some(subtree) = cursor.end() { - cached.push(Some(convert_delim(subtree.delimiter, true))); + cached.push(Some(convert_delim(subtree.delimiter_kind(), true))); self.cached_cursor.set(cursor.bump()); } } @@ -114,12 +114,12 @@ impl<'a> TokenSource for SubtreeTokenSource<'a> { } } -fn convert_delim(d: tt::Delimiter, closing: bool) -> TtToken { +fn convert_delim(d: Option, closing: bool) -> TtToken { let (kinds, texts) = match d { - tt::Delimiter::Parenthesis => ([T!['('], T![')']], "()"), - tt::Delimiter::Brace => ([T!['{'], T!['}']], "{}"), - tt::Delimiter::Bracket => ([T!['['], T![']']], "[]"), - tt::Delimiter::None => ([L_DOLLAR, R_DOLLAR], ""), + Some(tt::DelimiterKind::Parenthesis) => ([T!['('], T![')']], "()"), + Some(tt::DelimiterKind::Brace) => ([T!['{'], T!['}']], "{}"), + Some(tt::DelimiterKind::Bracket) => ([T!['['], T![']']], "[]"), + None => ([L_DOLLAR, R_DOLLAR], ""), }; let idx = closing as usize; diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 1de399fee4..ea2cac069e 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs @@ -2,25 +2,45 @@ use ra_parser::{FragmentKind, ParseError, TreeSink}; use ra_syntax::{ - ast, AstNode, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxNode, + ast, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxNode, SyntaxTreeBuilder, TextRange, TextUnit, T, }; +use rustc_hash::FxHashMap; use std::iter::successors; use tt::buffer::{Cursor, TokenBuffer}; use crate::subtree_source::SubtreeTokenSource; use crate::ExpandError; +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum TokenTextRange { + Token(TextRange), + Delimiter(TextRange, TextRange), +} + +impl TokenTextRange { + pub fn by_kind(self, kind: SyntaxKind) -> Option { + match self { + TokenTextRange::Token(it) => Some(it), + TokenTextRange::Delimiter(open, close) => match kind { + T!['{'] | T!['('] | T!['['] => Some(open), + T!['}'] | T![')'] | T![']'] => Some(close), + _ => None, + }, + } + } +} + /// Maps `tt::TokenId` to the relative range of the original token. #[derive(Debug, PartialEq, Eq, Default)] pub struct TokenMap { /// Maps `tt::TokenId` to the *relative* source range. - entries: Vec<(tt::TokenId, TextRange)>, + entries: Vec<(tt::TokenId, TokenTextRange)>, } /// Convert the syntax tree (what user has written) to a `TokenTree` (what macro /// will consume). -pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> { +pub fn ast_to_token_tree(ast: &impl ast::AstNode) -> Option<(tt::Subtree, TokenMap)> { syntax_node_to_token_tree(ast.syntax()) } @@ -51,7 +71,7 @@ pub fn token_tree_to_syntax_node( ) -> Result<(Parse, TokenMap), ExpandError> { let tmp; let tokens = match tt { - tt::Subtree { delimiter: tt::Delimiter::None, token_trees } => token_trees.as_slice(), + tt::Subtree { delimiter: None, token_trees } => token_trees.as_slice(), _ => { tmp = [tt.clone().into()]; &tmp[..] @@ -71,17 +91,32 @@ pub fn token_tree_to_syntax_node( impl TokenMap { pub fn token_by_range(&self, relative_range: TextRange) -> Option { - let &(token_id, _) = self.entries.iter().find(|(_, range)| *range == relative_range)?; + let &(token_id, _) = self.entries.iter().find(|(_, range)| match range { + TokenTextRange::Token(it) => *it == relative_range, + TokenTextRange::Delimiter(open, close) => { + *open == relative_range || *close == relative_range + } + })?; Some(token_id) } - pub fn range_by_token(&self, token_id: tt::TokenId) -> Option { + pub fn range_by_token(&self, token_id: tt::TokenId) -> Option { let &(_, range) = self.entries.iter().find(|(tid, _)| *tid == token_id)?; Some(range) } fn insert(&mut self, token_id: tt::TokenId, relative_range: TextRange) { - self.entries.push((token_id, relative_range)); + self.entries.push((token_id, TokenTextRange::Token(relative_range))); + } + + fn insert_delim( + &mut self, + token_id: tt::TokenId, + open_relative_range: TextRange, + close_relative_range: TextRange, + ) { + self.entries + .push((token_id, TokenTextRange::Delimiter(open_relative_range, close_relative_range))); } } @@ -121,7 +156,10 @@ fn convert_doc_comment(token: &ra_syntax::SyntaxToken) -> Option Option tt::TokenTree { - tt::TokenTree::from(tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone })) + tt::TokenTree::from(tt::Leaf::from(tt::Punct { + char: c, + spacing: tt::Spacing::Alone, + id: tt::TokenId::unspecified(), + })) } fn mk_doc_literal(comment: &ast::Comment) -> tt::TokenTree { - let lit = tt::Literal { text: doc_comment_text(comment) }; + let lit = tt::Literal { text: doc_comment_text(comment), id: tt::TokenId::unspecified() }; tt::TokenTree::from(tt::Leaf::from(lit)) } @@ -156,7 +198,7 @@ impl Convertor { fn go(&mut self, tt: &SyntaxNode) -> Option { // This tree is empty if tt.first_child_or_token().is_none() { - return Some(tt::Subtree { token_trees: vec![], delimiter: tt::Delimiter::None }); + return Some(tt::Subtree { token_trees: vec![], delimiter: None }); } let first_child = tt.first_child_or_token()?; @@ -173,7 +215,7 @@ impl Convertor { .last() .unwrap(); if first_child.kind().is_trivia() { - return Some(tt::Subtree { token_trees: vec![], delimiter: tt::Delimiter::None }); + return Some(tt::Subtree { token_trees: vec![], delimiter: None }); } let last_child = successors(Some(last_child), |it| { @@ -186,12 +228,16 @@ impl Convertor { .last() .unwrap(); - let (delimiter, skip_first) = match (first_child.kind(), last_child.kind()) { - (T!['('], T![')']) => (tt::Delimiter::Parenthesis, true), - (T!['{'], T!['}']) => (tt::Delimiter::Brace, true), - (T!['['], T![']']) => (tt::Delimiter::Bracket, true), - _ => (tt::Delimiter::None, false), + let (delimiter_kind, skip_first) = match (first_child.kind(), last_child.kind()) { + (T!['('], T![')']) => (Some(tt::DelimiterKind::Parenthesis), true), + (T!['{'], T!['}']) => (Some(tt::DelimiterKind::Brace), true), + (T!['['], T![']']) => (Some(tt::DelimiterKind::Bracket), true), + _ => (None, false), }; + let delimiter = delimiter_kind.map(|kind| tt::Delimiter { + kind, + id: self.alloc_delim(first_child.text_range(), last_child.text_range()), + }); let mut token_trees = Vec::new(); let mut child_iter = tt.children_with_tokens().skip(skip_first as usize).peekable(); @@ -208,13 +254,8 @@ impl Convertor { } else if token.kind().is_trivia() { continue; } else if token.kind().is_punct() { - assert!( - token.text().len() == 1, - "Input ast::token punct must be single char." - ); - let char = token.text().chars().next().unwrap(); - - let spacing = match child_iter.peek() { + // we need to pull apart joined punctuation tokens + let last_spacing = match child_iter.peek() { Some(NodeOrToken::Token(token)) => { if token.kind().is_punct() { tt::Spacing::Joint @@ -224,30 +265,47 @@ impl Convertor { } _ => tt::Spacing::Alone, }; - - token_trees.push(tt::Leaf::from(tt::Punct { char, spacing }).into()); + let spacing_iter = std::iter::repeat(tt::Spacing::Joint) + .take(token.text().len() - 1) + .chain(std::iter::once(last_spacing)); + for (char, spacing) in token.text().chars().zip(spacing_iter) { + token_trees.push( + tt::Leaf::from(tt::Punct { + char, + spacing, + id: self.alloc(token.text_range()), + }) + .into(), + ); + } } else { - let child: tt::TokenTree = - if token.kind() == T![true] || token.kind() == T![false] { - tt::Leaf::from(tt::Literal { text: token.text().clone() }).into() - } else if token.kind().is_keyword() - || token.kind() == IDENT - || token.kind() == LIFETIME - { - let id = self.alloc(token.text_range()); - let text = token.text().clone(); - tt::Leaf::from(tt::Ident { text, id }).into() - } else if token.kind().is_literal() { - tt::Leaf::from(tt::Literal { text: token.text().clone() }).into() - } else { - return None; + macro_rules! make_leaf { + ($i:ident) => { + tt::$i { + id: self.alloc(token.text_range()), + text: token.text().clone(), + } + .into() }; - token_trees.push(child); + } + + let child: tt::Leaf = match token.kind() { + T![true] | T![false] => make_leaf!(Literal), + IDENT | LIFETIME => make_leaf!(Ident), + k if k.is_keyword() => make_leaf!(Ident), + k if k.is_literal() => make_leaf!(Literal), + _ => return None, + }; + token_trees.push(child.into()); } } NodeOrToken::Node(node) => { - let child = self.go(&node)?.into(); - token_trees.push(child); + let child_subtree = self.go(&node)?; + if child_subtree.delimiter.is_none() && node.kind() != SyntaxKind::TOKEN_TREE { + token_trees.extend(child_subtree.token_trees); + } else { + token_trees.push(child_subtree.into()); + } } }; } @@ -263,11 +321,26 @@ impl Convertor { self.map.insert(token_id, relative_range); token_id } + + fn alloc_delim( + &mut self, + open_abs_range: TextRange, + close_abs_range: TextRange, + ) -> tt::TokenId { + let open_relative_range = open_abs_range - self.global_offset; + let close_relative_range = close_abs_range - self.global_offset; + let token_id = tt::TokenId(self.next_id); + self.next_id += 1; + + self.map.insert_delim(token_id, open_relative_range, close_relative_range); + token_id + } } struct TtTreeSink<'a> { buf: String, cursor: Cursor<'a>, + open_delims: FxHashMap, text_pos: TextUnit, inner: SyntaxTreeBuilder, token_map: TokenMap, @@ -282,6 +355,7 @@ impl<'a> TtTreeSink<'a> { TtTreeSink { buf: String::new(), cursor, + open_delims: FxHashMap::default(), text_pos: 0.into(), inner: SyntaxTreeBuilder::default(), roots: smallvec::SmallVec::new(), @@ -294,16 +368,16 @@ impl<'a> TtTreeSink<'a> { } } -fn delim_to_str(d: tt::Delimiter, closing: bool) -> SmolStr { +fn delim_to_str(d: Option, closing: bool) -> SmolStr { let texts = match d { - tt::Delimiter::Parenthesis => "()", - tt::Delimiter::Brace => "{}", - tt::Delimiter::Bracket => "[]", - tt::Delimiter::None => "", + Some(tt::DelimiterKind::Parenthesis) => "()", + Some(tt::DelimiterKind::Brace) => "{}", + Some(tt::DelimiterKind::Bracket) => "[]", + None => return "".into(), }; let idx = closing as usize; - let text = if !texts.is_empty() { &texts[idx..texts.len() - (1 - idx)] } else { "" }; + let text = &texts[idx..texts.len() - (1 - idx)]; text.into() } @@ -319,34 +393,49 @@ impl<'a> TreeSink for TtTreeSink<'a> { break; } - match self.cursor.token_tree() { + let text: SmolStr = match self.cursor.token_tree() { Some(tt::TokenTree::Leaf(leaf)) => { // Mark the range if needed - if let tt::Leaf::Ident(ident) = leaf { - if kind == IDENT { - let range = - TextRange::offset_len(self.text_pos, TextUnit::of_str(&ident.text)); - self.token_map.insert(ident.id, range); - } - } - + let id = match leaf { + tt::Leaf::Ident(ident) => ident.id, + tt::Leaf::Punct(punct) => punct.id, + tt::Leaf::Literal(lit) => lit.id, + }; + let text = SmolStr::new(format!("{}", leaf)); + let range = TextRange::offset_len(self.text_pos, TextUnit::of_str(&text)); + self.token_map.insert(id, range); self.cursor = self.cursor.bump(); - self.buf += &format!("{}", leaf); + text } Some(tt::TokenTree::Subtree(subtree)) => { self.cursor = self.cursor.subtree().unwrap(); - self.buf += &delim_to_str(subtree.delimiter, false); + if let Some(id) = subtree.delimiter.map(|it| it.id) { + self.open_delims.insert(id, self.text_pos); + } + delim_to_str(subtree.delimiter_kind(), false) } None => { if let Some(parent) = self.cursor.end() { self.cursor = self.cursor.bump(); - self.buf += &delim_to_str(parent.delimiter, true); + if let Some(id) = parent.delimiter.map(|it| it.id) { + if let Some(open_delim) = self.open_delims.get(&id) { + let open_range = + TextRange::offset_len(*open_delim, TextUnit::from_usize(1)); + let close_range = + TextRange::offset_len(self.text_pos, TextUnit::from_usize(1)); + self.token_map.insert_delim(id, open_range, close_range); + } + } + delim_to_str(parent.delimiter_kind(), true) + } else { + continue; } } }; + self.buf += &text; + self.text_pos += TextUnit::of_str(&text); } - self.text_pos += TextUnit::of_str(&self.buf); let text = SmolStr::new(self.buf.as_str()); self.buf.clear(); self.inner.token(kind, text); @@ -387,13 +476,16 @@ impl<'a> TreeSink for TtTreeSink<'a> { #[cfg(test)] mod tests { use super::*; - use crate::tests::{create_rules, expand}; + use crate::tests::parse_macro; use ra_parser::TokenSource; - use ra_syntax::algo::{insert_children, InsertPosition}; + use ra_syntax::{ + algo::{insert_children, InsertPosition}, + ast::AstNode, + }; #[test] fn convert_tt_token_source() { - let rules = create_rules( + let expansion = parse_macro( r#" macro_rules! literals { ($i:ident) => { @@ -406,8 +498,8 @@ mod tests { } } "#, - ); - let expansion = expand(&rules, "literals!(foo);"); + ) + .expand_tt("literals!(foo);"); let tts = &[expansion.into()]; let buffer = tt::buffer::TokenBuffer::new(tts); let mut tt_src = SubtreeTokenSource::new(&buffer); @@ -435,7 +527,7 @@ mod tests { #[test] fn stmts_token_trees_to_expr_is_err() { - let rules = create_rules( + let expansion = parse_macro( r#" macro_rules! stmts { () => { @@ -446,8 +538,8 @@ mod tests { } } "#, - ); - let expansion = expand(&rules, "stmts!();"); + ) + .expand_tt("stmts!();"); assert!(token_tree_to_syntax_node(&expansion, FragmentKind::Expr).is_err()); } @@ -489,6 +581,14 @@ mod tests { let token_tree = ast::TokenTree::cast(token_tree).unwrap(); let tt = ast_to_token_tree(&token_tree).unwrap().0; - assert_eq!(tt.delimiter, tt::Delimiter::Brace); + assert_eq!(tt.delimiter_kind(), Some(tt::DelimiterKind::Brace)); + } + + #[test] + fn test_token_tree_multi_char_punct() { + let source_file = ast::SourceFile::parse("struct Foo { a: x::Y }").ok().unwrap(); + let struct_def = source_file.syntax().descendants().find_map(ast::StructDef::cast).unwrap(); + let tt = ast_to_token_tree(&struct_def).unwrap().0; + token_tree_to_syntax_node(&tt, FragmentKind::Item).unwrap(); } } diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index 0109a4d980..e640d115b4 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs @@ -1,5 +1,7 @@ +use std::fmt::Write; + use ra_parser::FragmentKind; -use ra_syntax::{ast, AstNode, NodeOrToken, WalkEvent}; +use ra_syntax::{ast, AstNode, NodeOrToken, SyntaxKind::IDENT, SyntaxNode, WalkEvent, T}; use test_utils::assert_eq_text; use super::*; @@ -61,13 +63,14 @@ mod rule_parsing { #[test] fn test_token_id_shift() { - let macro_definition = r#" + let expansion = parse_macro( + r#" macro_rules! foobar { ($e:ident) => { foo bar $e } } -"#; - let rules = create_rules(macro_definition); - let expansion = expand(&rules, "foobar!(baz);"); +"#, + ) + .expand_tt("foobar!(baz);"); fn get_id(t: &tt::TokenTree) -> Option { if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = t { @@ -77,18 +80,47 @@ macro_rules! foobar { } assert_eq!(expansion.token_trees.len(), 3); - // ($e:ident) => { foo bar $e } - // 0 1 2 3 4 - assert_eq!(get_id(&expansion.token_trees[0]), Some(2)); - assert_eq!(get_id(&expansion.token_trees[1]), Some(3)); + // {($e:ident) => { foo bar $e }} + // 012345 67 8 9 T 12 + assert_eq!(get_id(&expansion.token_trees[0]), Some(9)); + assert_eq!(get_id(&expansion.token_trees[1]), Some(10)); - // So baz should be 5 - assert_eq!(get_id(&expansion.token_trees[2]), Some(5)); + // The input args of macro call include parentheses: + // (baz) + // So baz should be 12+1+1 + assert_eq!(get_id(&expansion.token_trees[2]), Some(14)); +} + +#[test] +fn test_token_map() { + let expanded = parse_macro( + r#" +macro_rules! foobar { + ($e:ident) => { fn $e() {} } +} +"#, + ) + .expand_tt("foobar!(baz);"); + + let (node, token_map) = token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap(); + let content = node.syntax_node().to_string(); + + let get_text = |id, kind| -> String { + content[token_map.range_by_token(id).unwrap().by_kind(kind).unwrap()].to_string() + }; + + assert_eq!(expanded.token_trees.len(), 4); + // {($e:ident) => { fn $e() {} }} + // 012345 67 8 9 T12 3 + + assert_eq!(get_text(tt::TokenId(9), IDENT), "fn"); + assert_eq!(get_text(tt::TokenId(12), T!['(']), "("); + assert_eq!(get_text(tt::TokenId(13), T!['{']), "{"); } #[test] fn test_convert_tt() { - let macro_definition = r#" + parse_macro(r#" macro_rules! impl_froms { ($e:ident: $($v:ident),*) => { $( @@ -100,24 +132,17 @@ macro_rules! impl_froms { )* } } -"#; - - let macro_invocation = r#" -impl_froms!(TokenTree: Leaf, Subtree); -"#; - - let rules = create_rules(macro_definition); - let expansion = expand(&rules, macro_invocation); - assert_eq!( - expansion.to_string(), - "impl From for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ - impl From for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree ::Subtree (it)}}" - ) +"#) + .assert_expand_tt( + "impl_froms!(TokenTree: Leaf, Subtree);", + "impl From for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ + impl From for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree ::Subtree (it)}}" + ); } #[test] fn test_expr_order() { - let rules = create_rules( + let expanded = parse_macro( r#" macro_rules! foo { ($ i:expr) => { @@ -125,11 +150,10 @@ fn test_expr_order() { } } "#, - ); - let expanded = expand(&rules, "foo! { 1 + 1}"); - let tree = token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node(); + ) + .expand_items("foo! { 1 + 1}"); - let dump = format!("{:#?}", tree); + let dump = format!("{:#?}", expanded); assert_eq_text!( dump.trim(), r#"MACRO_ITEMS@[0; 15) @@ -161,7 +185,7 @@ fn test_expr_order() { #[test] fn test_fail_match_pattern_by_first_token() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:ident) => ( @@ -175,16 +199,15 @@ fn test_fail_match_pattern_by_first_token() { ) } "#, - ); - - assert_expansion(MacroKind::Items, &rules, "foo! { foo }", "mod foo {}"); - assert_expansion(MacroKind::Items, &rules, "foo! { = bar }", "fn bar () {}"); - assert_expansion(MacroKind::Items, &rules, "foo! { + Baz }", "struct Baz ;"); + ) + .assert_expand_items("foo! { foo }", "mod foo {}") + .assert_expand_items("foo! { = bar }", "fn bar () {}") + .assert_expand_items("foo! { + Baz }", "struct Baz ;"); } #[test] fn test_fail_match_pattern_by_last_token() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:ident) => ( @@ -198,16 +221,15 @@ fn test_fail_match_pattern_by_last_token() { ) } "#, - ); - - assert_expansion(MacroKind::Items, &rules, "foo! { foo }", "mod foo {}"); - assert_expansion(MacroKind::Items, &rules, "foo! { bar = }", "fn bar () {}"); - assert_expansion(MacroKind::Items, &rules, "foo! { Baz + }", "struct Baz ;"); + ) + .assert_expand_items("foo! { foo }", "mod foo {}") + .assert_expand_items("foo! { bar = }", "fn bar () {}") + .assert_expand_items("foo! { Baz + }", "struct Baz ;"); } #[test] fn test_fail_match_pattern_by_word_token() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:ident) => ( @@ -221,16 +243,15 @@ fn test_fail_match_pattern_by_word_token() { ) } "#, - ); - - assert_expansion(MacroKind::Items, &rules, "foo! { foo }", "mod foo {}"); - assert_expansion(MacroKind::Items, &rules, "foo! { spam bar }", "fn bar () {}"); - assert_expansion(MacroKind::Items, &rules, "foo! { eggs Baz }", "struct Baz ;"); + ) + .assert_expand_items("foo! { foo }", "mod foo {}") + .assert_expand_items("foo! { spam bar }", "fn bar () {}") + .assert_expand_items("foo! { eggs Baz }", "struct Baz ;"); } #[test] fn test_match_group_pattern_by_separator_token() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ ($ i:ident),*) => ($ ( @@ -245,16 +266,15 @@ fn test_match_group_pattern_by_separator_token() { ) } "#, - ); - - assert_expansion(MacroKind::Items, &rules, "foo! { foo, bar }", "mod foo {} mod bar {}"); - assert_expansion(MacroKind::Items, &rules, "foo! { foo# bar }", "fn foo () {} fn bar () {}"); - assert_expansion(MacroKind::Items, &rules, "foo! { Foo,# Bar }", "struct Foo ; struct Bar ;"); + ) + .assert_expand_items("foo! { foo, bar }", "mod foo {} mod bar {}") + .assert_expand_items("foo! { foo# bar }", "fn foo () {} fn bar () {}") + .assert_expand_items("foo! { Foo,# Bar }", "struct Foo ; struct Bar ;"); } #[test] fn test_match_group_pattern_with_multiple_defs() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ ($ i:ident),*) => ( struct Bar { $ ( @@ -262,19 +282,13 @@ fn test_match_group_pattern_with_multiple_defs() { )*} ); } "#, - ); - - assert_expansion( - MacroKind::Items, - &rules, - "foo! { foo, bar }", - "struct Bar {fn foo {} fn bar {}}", - ); + ) + .assert_expand_items("foo! { foo, bar }", "struct Bar {fn foo {} fn bar {}}"); } #[test] fn test_match_group_pattern_with_multiple_statement() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ ($ i:ident),*) => ( fn baz { $ ( @@ -282,14 +296,13 @@ fn test_match_group_pattern_with_multiple_statement() { )*} ); } "#, - ); - - assert_expansion(MacroKind::Items, &rules, "foo! { foo, bar }", "fn baz {foo () ; bar () ;}"); + ) + .assert_expand_items("foo! { foo, bar }", "fn baz {foo () ; bar () ;}"); } #[test] fn test_match_group_pattern_with_multiple_statement_without_semi() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ ($ i:ident),*) => ( fn baz { $ ( @@ -297,14 +310,13 @@ fn test_match_group_pattern_with_multiple_statement_without_semi() { );*} ); } "#, - ); - - assert_expansion(MacroKind::Items, &rules, "foo! { foo, bar }", "fn baz {foo () ;bar ()}"); + ) + .assert_expand_items("foo! { foo, bar }", "fn baz {foo () ;bar ()}"); } #[test] fn test_match_group_empty_fixed_token() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ ($ i:ident)* #abc) => ( fn baz { $ ( @@ -312,69 +324,59 @@ fn test_match_group_empty_fixed_token() { )*} ); } "#, - ); - - assert_expansion(MacroKind::Items, &rules, "foo! {#abc}", "fn baz {}"); + ) + .assert_expand_items("foo! {#abc}", "fn baz {}"); } #[test] fn test_match_group_in_subtree() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { (fn $name:ident {$($i:ident)*} ) => ( fn $name() { $ ( $ i (); )*} ); }"#, - ); - - assert_expansion(MacroKind::Items, &rules, "foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}"); + ) + .assert_expand_items("foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}"); } #[test] fn test_match_group_with_multichar_sep() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { (fn $name:ident {$($i:literal)*} ) => ( fn $name() -> bool { $($i)&&*} ); }"#, - ); - - assert_expansion( - MacroKind::Items, - &rules, - "foo! (fn baz {true true} );", - "fn baz () -> bool {true &&true}", - ); + ) + .assert_expand_items("foo! (fn baz {true true} );", "fn baz () -> bool {true &&true}"); } #[test] fn test_match_group_zero_match() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ( $($i:ident)* ) => (); }"#, - ); - - assert_expansion(MacroKind::Items, &rules, "foo! ();", ""); + ) + .assert_expand_items("foo! ();", ""); } #[test] fn test_match_group_in_group() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { { $( ( $($i:ident)* ) )* } => ( $( ( $($i)* ) )* ); }"#, - ); - - assert_expansion(MacroKind::Items, &rules, "foo! ( (a b) );", "(a b)"); + ) + .assert_expand_items("foo! ( (a b) );", "(a b)"); } #[test] fn test_expand_to_item_list() { - let rules = create_rules( + let tree = parse_macro( " macro_rules! structs { ($($i:ident),*) => { @@ -382,9 +384,8 @@ fn test_expand_to_item_list() { } } ", - ); - let expansion = expand(&rules, "structs!(Foo, Bar);"); - let tree = token_tree_to_syntax_node(&expansion, FragmentKind::Items).unwrap().0.syntax_node(); + ) + .expand_items("structs!(Foo, Bar);"); assert_eq!( format!("{:#?}", tree).trim(), r#" @@ -441,7 +442,7 @@ fn test_expand_literals_to_token_tree() { unreachable!("It is not a literal"); } - let rules = create_rules( + let expansion = parse_macro( r#" macro_rules! literals { ($i:ident) => { @@ -454,8 +455,8 @@ fn test_expand_literals_to_token_tree() { } } "#, - ); - let expansion = expand(&rules, "literals!(foo);"); + ) + .expand_tt("literals!(foo);"); let stm_tokens = &to_subtree(&expansion.token_trees[0]).token_trees; // [let] [a] [=] ['c'] [;] @@ -470,7 +471,7 @@ fn test_expand_literals_to_token_tree() { #[test] fn test_two_idents() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:ident, $ j:ident) => { @@ -478,18 +479,13 @@ fn test_two_idents() { } } "#, - ); - assert_expansion( - MacroKind::Items, - &rules, - "foo! { foo, bar }", - "fn foo () {let a = foo ; let b = bar ;}", - ); + ) + .assert_expand_items("foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); } #[test] fn test_tt_to_stmts() { - let rules = create_rules( + let stmts = parse_macro( r#" macro_rules! foo { () => { @@ -499,11 +495,8 @@ fn test_tt_to_stmts() { } } "#, - ); - - let expanded = expand(&rules, "foo!{}"); - let stmts = - token_tree_to_syntax_node(&expanded, FragmentKind::Statements).unwrap().0.syntax_node(); + ) + .expand_statements("foo!{}"); assert_eq!( format!("{:#?}", stmts).trim(), @@ -543,7 +536,7 @@ fn test_tt_to_stmts() { #[test] fn test_match_literal() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ('(') => { @@ -551,8 +544,8 @@ fn test_match_literal() { } } "#, - ); - assert_expansion(MacroKind::Items, &rules, "foo! ['('];", "fn foo () {}"); + ) + .assert_expand_items("foo! ['('];", "fn foo () {}"); } // The following tests are port from intellij-rust directly @@ -560,7 +553,7 @@ fn test_match_literal() { #[test] fn test_path() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:path) => { @@ -568,11 +561,9 @@ fn test_path() { } } "#, - ); - assert_expansion(MacroKind::Items, &rules, "foo! { foo }", "fn foo () {let a = foo ;}"); - assert_expansion( - MacroKind::Items, - &rules, + ) + .assert_expand_items("foo! { foo }", "fn foo () {let a = foo ;}") + .assert_expand_items( "foo! { bar::::baz:: }", "fn foo () {let a = bar ::< u8 >:: baz ::< u8 > ;}", ); @@ -580,7 +571,7 @@ fn test_path() { #[test] fn test_two_paths() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:path, $ j:path) => { @@ -588,18 +579,13 @@ fn test_two_paths() { } } "#, - ); - assert_expansion( - MacroKind::Items, - &rules, - "foo! { foo, bar }", - "fn foo () {let a = foo ; let b = bar ;}", - ); + ) + .assert_expand_items("foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); } #[test] fn test_path_with_path() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:path) => { @@ -607,13 +593,13 @@ fn test_path_with_path() { } } "#, - ); - assert_expansion(MacroKind::Items, &rules, "foo! { foo }", "fn foo () {let a = foo :: bar ;}"); + ) + .assert_expand_items("foo! { foo }", "fn foo () {let a = foo :: bar ;}"); } #[test] fn test_expr() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:expr) => { @@ -621,11 +607,8 @@ fn test_expr() { } } "#, - ); - - assert_expansion( - MacroKind::Items, - &rules, + ) + .assert_expand_items( "foo! { 2 + 2 * baz(3).quux() }", "fn bar () {2 + 2 * baz (3) . quux () ;}", ); @@ -633,7 +616,7 @@ fn test_expr() { #[test] fn test_last_expr() { - let rules = create_rules( + parse_macro( r#" macro_rules! vec { ($($item:expr),*) => { @@ -647,10 +630,8 @@ fn test_last_expr() { }; } "#, - ); - assert_expansion( - MacroKind::Items, - &rules, + ) + .assert_expand_items( "vec!(1,2,3);", "{let mut v = Vec :: new () ; v . push (1) ; v . push (2) ; v . push (3) ; v}", ); @@ -658,7 +639,7 @@ fn test_last_expr() { #[test] fn test_ty() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:ty) => ( @@ -666,18 +647,13 @@ fn test_ty() { ) } "#, - ); - assert_expansion( - MacroKind::Items, - &rules, - "foo! { Baz }", - "fn bar () -> Baz < u8 > {unimplemented ! ()}", - ); + ) + .assert_expand_items("foo! { Baz }", "fn bar () -> Baz < u8 > {unimplemented ! ()}"); } #[test] fn test_ty_with_complex_type() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:ty) => ( @@ -685,20 +661,14 @@ fn test_ty_with_complex_type() { ) } "#, - ); - + ) // Reference lifetime struct with generic type - assert_expansion( - MacroKind::Items, - &rules, + .assert_expand_items( "foo! { &'a Baz }", "fn bar () -> & 'a Baz < u8 > {unimplemented ! ()}", - ); - + ) // extern "Rust" func type - assert_expansion( - MacroKind::Items, - &rules, + .assert_expand_items( r#"foo! { extern "Rust" fn() -> Ret }"#, r#"fn bar () -> extern "Rust" fn () -> Ret {unimplemented ! ()}"#, ); @@ -706,19 +676,19 @@ fn test_ty_with_complex_type() { #[test] fn test_pat_() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:pat) => { fn foo() { let $ i; } } } "#, - ); - assert_expansion(MacroKind::Items, &rules, "foo! { (a, b) }", "fn foo () {let (a , b) ;}"); + ) + .assert_expand_items("foo! { (a, b) }", "fn foo () {let (a , b) ;}"); } #[test] fn test_stmt() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:stmt) => ( @@ -726,14 +696,14 @@ fn test_stmt() { ) } "#, - ); - assert_expansion(MacroKind::Items, &rules, "foo! { 2 }", "fn bar () {2 ;}"); - assert_expansion(MacroKind::Items, &rules, "foo! { let a = 0 }", "fn bar () {let a = 0 ;}"); + ) + .assert_expand_items("foo! { 2 }", "fn bar () {2 ;}") + .assert_expand_items("foo! { let a = 0 }", "fn bar () {let a = 0 ;}"); } #[test] fn test_single_item() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:item) => ( @@ -741,13 +711,13 @@ fn test_single_item() { ) } "#, - ); - assert_expansion(MacroKind::Items, &rules, "foo! {mod c {}}", "mod c {}"); + ) + .assert_expand_items("foo! {mod c {}}", "mod c {}"); } #[test] fn test_all_items() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ ($ i:item)*) => ($ ( @@ -755,10 +725,8 @@ fn test_all_items() { )*) } "#, - ); - assert_expansion( - MacroKind::Items, - &rules, + ). + assert_expand_items( r#" foo! { extern crate a; @@ -782,19 +750,19 @@ fn test_all_items() { #[test] fn test_block() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:block) => { fn foo() $ i } } "#, - ); - assert_expansion(MacroKind::Stmts, &rules, "foo! { { 1; } }", "fn foo () {1 ;}"); + ) + .assert_expand_statements("foo! { { 1; } }", "fn foo () {1 ;}"); } #[test] fn test_meta() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:meta) => ( @@ -803,10 +771,8 @@ fn test_meta() { ) } "#, - ); - assert_expansion( - MacroKind::Items, - &rules, + ) + .assert_expand_items( r#"foo! { cfg(target_os = "windows") }"#, r#"# [cfg (target_os = "windows")] fn bar () {}"#, ); @@ -814,7 +780,7 @@ fn test_meta() { #[test] fn test_meta_doc_comments() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($(#[$ i:meta])+) => ( @@ -823,10 +789,8 @@ fn test_meta_doc_comments() { ) } "#, - ); - assert_expansion( - MacroKind::Items, - &rules, + ). + assert_expand_items( r#"foo! { /// Single Line Doc 1 /** @@ -839,69 +803,68 @@ fn test_meta_doc_comments() { #[test] fn test_tt_block() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ i:tt) => { fn foo() $ i } } "#, - ); - assert_expansion(MacroKind::Items, &rules, r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); + ) + .assert_expand_items(r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); } #[test] fn test_tt_group() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($($ i:tt)*) => { $($ i)* } } "#, - ); - assert_expansion(MacroKind::Items, &rules, r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); + ) + .assert_expand_items(r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); } #[test] fn test_lifetime() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ lt:lifetime) => { struct Ref<$ lt>{ s: &$ lt str } } } "#, - ); - assert_expansion(MacroKind::Items, &rules, r#"foo!{'a}"#, r#"struct Ref <'a > {s : &'a str}"#); + ) + .assert_expand_items(r#"foo!{'a}"#, r#"struct Ref <'a > {s : &'a str}"#); } #[test] fn test_literal() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ type:ty $ lit:literal) => { const VALUE: $ type = $ lit;}; } "#, - ); - assert_expansion(MacroKind::Items, &rules, r#"foo!(u8 0);"#, r#"const VALUE : u8 = 0 ;"#); + ) + .assert_expand_items(r#"foo!(u8 0);"#, r#"const VALUE : u8 = 0 ;"#); } #[test] fn test_vis() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($ vis:vis $ name:ident) => { $ vis fn $ name() {}}; } "#, - ); - assert_expansion(MacroKind::Items, &rules, r#"foo!(pub foo);"#, r#"pub fn foo () {}"#); - - // test optional casse - assert_expansion(MacroKind::Items, &rules, r#"foo!(foo);"#, r#"fn foo () {}"#); + ) + .assert_expand_items(r#"foo!(pub foo);"#, r#"pub fn foo () {}"#) + // test optional cases + .assert_expand_items(r#"foo!(foo);"#, r#"fn foo () {}"#); } #[test] fn test_inner_macro_rules() { - let rules = create_rules( + parse_macro( r#" macro_rules! foo { ($a:ident, $b:ident, $c:tt) => { @@ -917,10 +880,8 @@ macro_rules! foo { } } "#, - ); - assert_expansion( - MacroKind::Items, - &rules, + ). + assert_expand_items( r#"foo!(x,y, 1);"#, r#"macro_rules ! bar {($ bi : ident) => {fn $ bi () -> u8 {1}}} bar ! (x) ; fn y () -> u8 {1}"#, ); @@ -929,7 +890,7 @@ macro_rules! foo { // The following tests are based on real world situations #[test] fn test_vec() { - let rules = create_rules( + let fixture = parse_macro( r#" macro_rules! vec { ($($item:expr),*) => { @@ -944,16 +905,14 @@ fn test_vec() { } "#, ); - assert_expansion(MacroKind::Items, &rules, r#"vec!();"#, r#"{let mut v = Vec :: new () ; v}"#); - assert_expansion( - MacroKind::Items, - &rules, - r#"vec![1u32,2];"#, - r#"{let mut v = Vec :: new () ; v . push (1u32) ; v . push (2) ; v}"#, - ); + fixture + .assert_expand_items(r#"vec!();"#, r#"{let mut v = Vec :: new () ; v}"#) + .assert_expand_items( + r#"vec![1u32,2];"#, + r#"{let mut v = Vec :: new () ; v . push (1u32) ; v . push (2) ; v}"#, + ); - let expansion = expand(&rules, r#"vec![1u32,2];"#); - let tree = token_tree_to_syntax_node(&expansion, FragmentKind::Expr).unwrap().0.syntax_node(); + let tree = fixture.expand_expr(r#"vec![1u32,2];"#); assert_eq!( format!("{:#?}", tree).trim(), @@ -1027,7 +986,7 @@ fn test_vec() { fn test_winapi_struct() { // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366 - let rules = create_rules( + parse_macro( r#" macro_rules! STRUCT { ($(#[$attrs:meta])* struct $name:ident { @@ -1049,17 +1008,19 @@ macro_rules! STRUCT { ); } "#, - ); + ). // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/shared/d3d9caps.rs - assert_expansion(MacroKind::Items, &rules, r#"STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}}"#, - "# [repr (C)] # [derive (Copy)] pub struct D3DVSHADERCAPS2_0 {pub Caps : u8 ,} impl Clone for D3DVSHADERCAPS2_0 {# [inline] fn clone (& self) -> D3DVSHADERCAPS2_0 {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DVSHADERCAPS2_0 {# [inline] fn default () -> D3DVSHADERCAPS2_0 {unsafe {$crate :: _core :: mem :: zeroed ()}}}"); - assert_expansion(MacroKind::Items, &rules, r#"STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}}"#, - "# [repr (C)] # [derive (Copy)] # [cfg_attr (target_arch = \"x86\" , repr (packed))] pub struct D3DCONTENTPROTECTIONCAPS {pub Caps : u8 ,} impl Clone for D3DCONTENTPROTECTIONCAPS {# [inline] fn clone (& self) -> D3DCONTENTPROTECTIONCAPS {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DCONTENTPROTECTIONCAPS {# [inline] fn default () -> D3DCONTENTPROTECTIONCAPS {unsafe {$crate :: _core :: mem :: zeroed ()}}}"); + assert_expand_items(r#"STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}}"#, + "# [repr (C)] # [derive (Copy)] pub struct D3DVSHADERCAPS2_0 {pub Caps : u8 ,} impl Clone for D3DVSHADERCAPS2_0 {# [inline] fn clone (& self) -> D3DVSHADERCAPS2_0 {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DVSHADERCAPS2_0 {# [inline] fn default () -> D3DVSHADERCAPS2_0 {unsafe {$crate :: _core :: mem :: zeroed ()}}}" + ) + .assert_expand_items(r#"STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}}"#, + "# [repr (C)] # [derive (Copy)] # [cfg_attr (target_arch = \"x86\" , repr (packed))] pub struct D3DCONTENTPROTECTIONCAPS {pub Caps : u8 ,} impl Clone for D3DCONTENTPROTECTIONCAPS {# [inline] fn clone (& self) -> D3DCONTENTPROTECTIONCAPS {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DCONTENTPROTECTIONCAPS {# [inline] fn default () -> D3DCONTENTPROTECTIONCAPS {unsafe {$crate :: _core :: mem :: zeroed ()}}}" + ); } #[test] fn test_int_base() { - let rules = create_rules( + parse_macro( r#" macro_rules! int_base { ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { @@ -1072,17 +1033,15 @@ macro_rules! int_base { } } "#, - ); - - assert_expansion(MacroKind::Items, &rules, r#" int_base!{Binary for isize as usize -> Binary}"#, + ).assert_expand_items(r#" int_base!{Binary for isize as usize -> Binary}"#, "# [stable (feature = \"rust1\" , since = \"1.0.0\")] impl fmt ::Binary for isize {fn fmt (& self , f : & mut fmt :: Formatter < \'_ >) -> fmt :: Result {Binary . fmt_int (* self as usize , f)}}" - ); + ); } #[test] fn test_generate_pattern_iterators() { // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs - let rules = create_rules( + parse_macro( r#" macro_rules! generate_pattern_iterators { { double ended; with $(#[$common_stability_attribute:meta])*, @@ -1093,11 +1052,7 @@ macro_rules! generate_pattern_iterators { } } "#, - ); - - assert_expansion( - MacroKind::Items, - &rules, + ).assert_expand_items( r#"generate_pattern_iterators ! ( double ended ; with # [ stable ( feature = "rust1" , since = "1.0.0" ) ] , Split , RSplit , & 'a str );"#, "fn foo () {}", ); @@ -1106,7 +1061,7 @@ macro_rules! generate_pattern_iterators { #[test] fn test_impl_fn_for_zst() { // from https://github.com/rust-lang/rust/blob/5d20ff4d2718c820632b38c1e49d4de648a9810b/src/libcore/internal_macros.rs - let rules = create_rules( + parse_macro( r#" macro_rules! impl_fn_for_zst { { $( $( #[$attr: meta] )* @@ -1147,9 +1102,7 @@ $body: block; )+ } } "#, - ); - - assert_expansion(MacroKind::Items, &rules, r#" + ).assert_expand_items(r#" impl_fn_for_zst ! { # [ derive ( Clone ) ] struct CharEscapeDebugContinue impl Fn = | c : char | -> char :: EscapeDebug { @@ -1166,13 +1119,14 @@ impl_fn_for_zst ! { } ; } "#, - "# [derive (Clone)] struct CharEscapeDebugContinue ; impl Fn < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDebug {{c . escape_debug_ext (false)}}} impl FnMut < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDebugContinue {type Output = char :: EscapeDebug ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeUnicode ; impl Fn < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeUnicode {{c . escape_unicode ()}}} impl FnMut < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeUnicode {type Output = char :: EscapeUnicode ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeDefault ; impl Fn < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDefault {{c . escape_default ()}}} impl FnMut < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDefault {type Output = char :: EscapeDefault ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (& self , (c ,))}}"); + "# [derive (Clone)] struct CharEscapeDebugContinue ; impl Fn < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDebug {{c . escape_debug_ext (false)}}} impl FnMut < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDebugContinue {type Output = char :: EscapeDebug ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeUnicode ; impl Fn < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeUnicode {{c . escape_unicode ()}}} impl FnMut < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeUnicode {type Output = char :: EscapeUnicode ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeDefault ; impl Fn < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDefault {{c . escape_default ()}}} impl FnMut < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDefault {type Output = char :: EscapeDefault ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (& self , (c ,))}}" + ); } #[test] fn test_impl_nonzero_fmt() { // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/num/mod.rs#L12 - let rules = create_rules( + parse_macro( r#" macro_rules! impl_nonzero_fmt { ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { @@ -1180,11 +1134,7 @@ fn test_impl_nonzero_fmt() { } } "#, - ); - - assert_expansion( - MacroKind::Items, - &rules, + ).assert_expand_items( r#"impl_nonzero_fmt! { # [stable(feature= "nonzero",since="1.28.0")] (Debug,Display,Binary,Octal,LowerHex,UpperHex) for NonZeroU8}"#, "fn foo () {}", ); @@ -1193,7 +1143,7 @@ fn test_impl_nonzero_fmt() { #[test] fn test_cfg_if_items() { // from https://github.com/rust-lang/rust/blob/33fe1131cadba69d317156847be9a402b89f11bb/src/libstd/macros.rs#L986 - let rules = create_rules( + parse_macro( r#" macro_rules! __cfg_if_items { (($($not:meta,)*) ; ) => {}; @@ -1202,11 +1152,7 @@ fn test_cfg_if_items() { } } "#, - ); - - assert_expansion( - MacroKind::Items, - &rules, + ).assert_expand_items( r#"__cfg_if_items ! { ( rustdoc , ) ; ( ( ) ( # [ cfg ( any ( target_os = "redox" , unix ) ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as unix ; # [ cfg ( windows ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as windows ; # [ cfg ( any ( target_os = "linux" , target_os = "l4re" ) ) ] pub mod linux ; ) ) , }"#, "__cfg_if_items ! {(rustdoc ,) ;}", ); @@ -1215,7 +1161,7 @@ fn test_cfg_if_items() { #[test] fn test_cfg_if_main() { // from https://github.com/rust-lang/rust/blob/3d211248393686e0f73851fc7548f6605220fbe1/src/libpanic_unwind/macros.rs#L9 - let rules = create_rules( + parse_macro( r#" macro_rules! cfg_if { ($( @@ -1236,9 +1182,7 @@ fn test_cfg_if_main() { }; } "#, - ); - - assert_expansion(MacroKind::Items, &rules, r#" + ).assert_expand_items(r#" cfg_if ! { if # [ cfg ( target_env = "msvc" ) ] { // no extra unwinder support needed @@ -1250,11 +1194,8 @@ cfg_if ! { } } "#, - "__cfg_if_items ! {() ; ((target_env = \"msvc\") ()) , ((all (target_arch = \"wasm32\" , not (target_os = \"emscripten\"))) ()) , (() (mod libunwind ; pub use libunwind :: * ;)) ,}"); - - assert_expansion( - MacroKind::Items, - &rules, + "__cfg_if_items ! {() ; ((target_env = \"msvc\") ()) , ((all (target_arch = \"wasm32\" , not (target_os = \"emscripten\"))) ()) , (() (mod libunwind ; pub use libunwind :: * ;)) ,}" + ).assert_expand_items( r#" cfg_if ! { @ __apply cfg ( all ( not ( any ( not ( any ( target_os = "solaris" , target_os = "illumos" ) ) ) ) ) ) , } "#, @@ -1265,7 +1206,7 @@ cfg_if ! { @ __apply cfg ( all ( not ( any ( not ( any ( target_os = "solaris" , #[test] fn test_proptest_arbitrary() { // from https://github.com/AltSysrq/proptest/blob/d1c4b049337d2f75dd6f49a095115f7c532e5129/proptest/src/arbitrary/macros.rs#L16 - let rules = create_rules( + parse_macro( r#" macro_rules! arbitrary { ([$($bounds : tt)*] $typ: ty, $strat: ty, $params: ty; @@ -1280,22 +1221,21 @@ macro_rules! arbitrary { }; }"#, - ); - - assert_expansion(MacroKind::Items, &rules, r#"arbitrary ! ( [ A : Arbitrary ] + ).assert_expand_items(r#"arbitrary ! ( [ A : Arbitrary ] Vec < A > , VecStrategy < A :: Strategy > , RangedParams1 < A :: Parameters > ; args => { let product_unpack ! [ range , a ] = args ; vec ( any_with :: < A > ( a ) , range ) } ) ;"#, - "impl
$crate :: arbitrary :: Arbitrary for Vec < A > {type Parameters = RangedParams1 < A :: Parameters > ; type Strategy = VecStrategy < A :: Strategy > ; fn arbitrary_with (args : Self :: Parameters) -> Self :: Strategy {{let product_unpack ! [range , a] = args ; vec (any_with :: < A > (a) , range)}}}"); + "impl $crate :: arbitrary :: Arbitrary for Vec < A > {type Parameters = RangedParams1 < A :: Parameters > ; type Strategy = VecStrategy < A :: Strategy > ; fn arbitrary_with (args : Self :: Parameters) -> Self :: Strategy {{let product_unpack ! [range , a] = args ; vec (any_with :: < A > (a) , range)}}}" + ); } #[test] fn test_old_ridl() { // This is from winapi 2.8, which do not have a link from github // - let rules = create_rules( + let expanded = parse_macro( r#" #[macro_export] macro_rules! RIDL { @@ -1311,21 +1251,17 @@ macro_rules! RIDL { } }; }"#, - ); + ).expand_tt(r#" + RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID3D11DeviceChildVtbl) { + fn GetDataSize(&mut self) -> UINT + }}"#); - let expanded = expand( - &rules, - r#" -RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID3D11DeviceChildVtbl) { - fn GetDataSize(&mut self) -> UINT -}}"#, - ); assert_eq!(expanded.to_string(), "impl ID3D11Asynchronous {pub unsafe fn GetDataSize (& mut self) -> UINT {((* self . lpVtbl) .GetDataSize) (self)}}"); } #[test] fn test_quick_error() { - let rules = create_rules( + let expanded = parse_macro( r#" macro_rules! quick_error { @@ -1348,10 +1284,8 @@ macro_rules! quick_error { } "#, - ); - - let expanded = expand( - &rules, + ) + .expand_tt( r#" quick_error ! (SORT [enum Wrapped # [derive (Debug)]] items [ => One : UNIT [] {} @@ -1365,7 +1299,7 @@ quick_error ! (SORT [enum Wrapped # [derive (Debug)]] items [ #[test] fn test_empty_repeat_vars_in_empty_repeat_vars() { - let rules = create_rules( + parse_macro( r#" macro_rules! delegate_impl { ([$self_type:ident, $self_wrap:ty, $self_map:ident] @@ -1412,103 +1346,117 @@ macro_rules! delegate_impl { } } "#, - ); - - assert_expansion( - MacroKind::Items, - &rules, + ).assert_expand_items( r#"delegate_impl ! {[G , & 'a mut G , deref] pub trait Data : GraphBase {@ section type type NodeWeight ;}}"#, "impl <> Data for & \'a mut G where G : Data {}", ); } -pub(crate) fn create_rules(macro_definition: &str) -> MacroRules { +#[test] +fn expr_interpolation() { + let expanded = parse_macro( + r#" + macro_rules! id { + ($expr:expr) => { + map($expr) + } + } + "#, + ) + .expand_expr("id!(x + foo);"); + + assert_eq!(expanded.to_string(), "map(x+foo)"); +} + +pub(crate) struct MacroFixture { + rules: MacroRules, +} + +impl MacroFixture { + pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree { + let source_file = ast::SourceFile::parse(invocation).ok().unwrap(); + let macro_invocation = + source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); + + let (invocation_tt, _) = + ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap(); + + self.rules.expand(&invocation_tt).unwrap() + } + + fn expand_items(&self, invocation: &str) -> SyntaxNode { + let expanded = self.expand_tt(invocation); + token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node() + } + + fn expand_statements(&self, invocation: &str) -> SyntaxNode { + let expanded = self.expand_tt(invocation); + token_tree_to_syntax_node(&expanded, FragmentKind::Statements).unwrap().0.syntax_node() + } + + fn expand_expr(&self, invocation: &str) -> SyntaxNode { + let expanded = self.expand_tt(invocation); + token_tree_to_syntax_node(&expanded, FragmentKind::Expr).unwrap().0.syntax_node() + } + + fn assert_expand_tt(&self, invocation: &str, expected: &str) { + let expansion = self.expand_tt(invocation); + assert_eq!(expansion.to_string(), expected); + } + + fn assert_expand_items(&self, invocation: &str, expected: &str) -> &MacroFixture { + self.assert_expansion(FragmentKind::Items, invocation, expected); + self + } + + fn assert_expand_statements(&self, invocation: &str, expected: &str) -> &MacroFixture { + self.assert_expansion(FragmentKind::Statements, invocation, expected); + self + } + + fn assert_expansion(&self, kind: FragmentKind, invocation: &str, expected: &str) { + let expanded = self.expand_tt(invocation); + assert_eq!(expanded.to_string(), expected); + + let expected = expected.replace("$crate", "C_C__C"); + + // wrap the given text to a macro call + let expected = { + let wrapped = format!("wrap_macro!( {} )", expected); + let wrapped = ast::SourceFile::parse(&wrapped); + let wrapped = + wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0; + wrapped.delimiter = None; + wrapped + }; + + let expanded_tree = token_tree_to_syntax_node(&expanded, kind).unwrap().0.syntax_node(); + let expanded_tree = debug_dump_ignore_spaces(&expanded_tree).trim().to_string(); + + let expected_tree = token_tree_to_syntax_node(&expected, kind).unwrap().0.syntax_node(); + let expected_tree = debug_dump_ignore_spaces(&expected_tree).trim().to_string(); + + let expected_tree = expected_tree.replace("C_C__C", "$crate"); + assert_eq!( + expanded_tree, expected_tree, + "\nleft:\n{}\nright:\n{}", + expanded_tree, expected_tree, + ); + } +} + +pub(crate) fn parse_macro(macro_definition: &str) -> MacroFixture { let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap(); let macro_definition = source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); - crate::MacroRules::parse(&definition_tt).unwrap() + let rules = MacroRules::parse(&definition_tt).unwrap(); + MacroFixture { rules } } -pub(crate) fn expand(rules: &MacroRules, invocation: &str) -> tt::Subtree { - let source_file = ast::SourceFile::parse(invocation).ok().unwrap(); - let macro_invocation = - source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); - - let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap(); - - rules.expand(&invocation_tt).unwrap() -} - -pub(crate) enum MacroKind { - Items, - Stmts, -} - -pub(crate) fn assert_expansion( - kind: MacroKind, - rules: &MacroRules, - invocation: &str, - expected: &str, -) -> tt::Subtree { - let expanded = expand(rules, invocation); - assert_eq!(expanded.to_string(), expected); - - let expected = expected.replace("$crate", "C_C__C"); - - // wrap the given text to a macro call - let expected = { - let wrapped = format!("wrap_macro!( {} )", expected); - let wrapped = ast::SourceFile::parse(&wrapped); - let wrapped = wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0; - wrapped.delimiter = tt::Delimiter::None; - wrapped - }; - let (expanded_tree, expected_tree) = match kind { - MacroKind::Items => { - let expanded_tree = - token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node(); - let expected_tree = - token_tree_to_syntax_node(&expected, FragmentKind::Items).unwrap().0.syntax_node(); - - ( - debug_dump_ignore_spaces(&expanded_tree).trim().to_string(), - debug_dump_ignore_spaces(&expected_tree).trim().to_string(), - ) - } - - MacroKind::Stmts => { - let expanded_tree = token_tree_to_syntax_node(&expanded, FragmentKind::Statements) - .unwrap() - .0 - .syntax_node(); - let expected_tree = token_tree_to_syntax_node(&expected, FragmentKind::Statements) - .unwrap() - .0 - .syntax_node(); - - ( - debug_dump_ignore_spaces(&expanded_tree).trim().to_string(), - debug_dump_ignore_spaces(&expected_tree).trim().to_string(), - ) - } - }; - - let expected_tree = expected_tree.replace("C_C__C", "$crate"); - assert_eq!( - expanded_tree, expected_tree, - "\nleft:\n{}\nright:\n{}", - expanded_tree, expected_tree, - ); - - expanded -} - -pub fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String { - use std::fmt::Write; - +fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String { let mut level = 0; let mut buf = String::new(); macro_rules! indent { diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs index 6e9e212b76..22f64a9f4a 100644 --- a/crates/ra_parser/src/grammar.rs +++ b/crates/ra_parser/src/grammar.rs @@ -264,7 +264,7 @@ fn name_r(p: &mut Parser, recovery: TokenSet) { } fn name(p: &mut Parser) { - name_r(p, TokenSet::empty()) + name_r(p, TokenSet::EMPTY) } fn name_ref(p: &mut Parser) { diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs index f06191963f..4ac1d6334a 100644 --- a/crates/ra_parser/src/grammar/expressions/atom.rs +++ b/crates/ra_parser/src/grammar/expressions/atom.rs @@ -43,6 +43,7 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet = T!['('], T!['{'], T!['['], + L_DOLLAR, T![|], T![move], T![box], @@ -248,7 +249,12 @@ fn lambda_expr(p: &mut Parser) -> CompletedMarker { p.error("expected `{`"); } } - expr(p); + + if p.at_ts(EXPR_FIRST) { + expr(p); + } else { + p.error("expected expression"); + } m.complete(p, LAMBDA_EXPR) } @@ -438,7 +444,7 @@ fn match_arm(p: &mut Parser) -> BlockLike { // } attributes::outer_attributes(p); - patterns::pattern_list_r(p, TokenSet::empty()); + patterns::pattern_list_r(p, TokenSet::EMPTY); if p.at(T![if]) { match_guard(p); } diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index 370990e21f..6e23d9b72d 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs @@ -33,7 +33,7 @@ pub(super) enum ItemFlavor { pub(super) const ITEM_RECOVERY_SET: TokenSet = token_set![ FN_KW, STRUCT_KW, ENUM_KW, IMPL_KW, TRAIT_KW, CONST_KW, STATIC_KW, LET_KW, MOD_KW, PUB_KW, - CRATE_KW, USE_KW + CRATE_KW, USE_KW, MACRO_KW ]; pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemFlavor) { @@ -249,6 +249,11 @@ fn items_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> { // } adt::struct_def(p, m); } + // test pub_macro_def + // pub macro m($:ident) {} + T![macro] => { + macro_def(p, m); + } IDENT if p.at_contextual_kw("union") && p.nth(1) == IDENT => { // test union_items // union Foo {} @@ -379,6 +384,29 @@ pub(crate) fn mod_item_list(p: &mut Parser) { m.complete(p, ITEM_LIST); } +// test macro_def +// macro m { ($i:ident) => {} } +// macro m($i:ident) {} +fn macro_def(p: &mut Parser, m: Marker) { + p.expect(T![macro]); + name_r(p, ITEM_RECOVERY_SET); + if p.at(T!['{']) { + token_tree(p); + } else if !p.at(T!['(']) { + p.error("unmatched `(`"); + } else { + let m = p.start(); + token_tree(p); + match p.current() { + T!['{'] | T!['['] | T!['('] => token_tree(p), + _ => p.error("expected `{`, `[`, `(`"), + } + m.complete(p, TOKEN_TREE); + } + + m.complete(p, MACRO_DEF); +} + fn macro_call(p: &mut Parser) -> BlockLike { assert!(paths::is_use_path_start(p)); paths::use_path(p); diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs index f5d12278c8..422a4e3dc1 100644 --- a/crates/ra_parser/src/grammar/patterns.rs +++ b/crates/ra_parser/src/grammar/patterns.rs @@ -50,7 +50,7 @@ pub(super) fn pattern_r(p: &mut Parser, recovery_set: TokenSet) { // let m!(x) = 0; // } if lhs.kind() == PATH_PAT && p.at(T![!]) { - let m = lhs.precede(p); + let m = lhs.undo_completion(p); items::macro_call_after_excl(p); m.complete(p, MACRO_CALL); } diff --git a/crates/ra_parser/src/grammar/type_params.rs b/crates/ra_parser/src/grammar/type_params.rs index 34406b5bd5..50e4900c31 100644 --- a/crates/ra_parser/src/grammar/type_params.rs +++ b/crates/ra_parser/src/grammar/type_params.rs @@ -25,6 +25,7 @@ fn type_param_list(p: &mut Parser) { match p.current() { LIFETIME => lifetime_param(p, m), IDENT => type_param(p, m), + CONST_KW => type_const_param(p, m), _ => { m.abandon(p); p.err_and_bump("expected type parameter") @@ -62,6 +63,16 @@ fn type_param(p: &mut Parser, m: Marker) { m.complete(p, TYPE_PARAM); } +// test const_param +// struct S; +fn type_const_param(p: &mut Parser, m: Marker) { + assert!(p.at(CONST_KW)); + p.bump(T![const]); + name(p); + types::ascription(p); + m.complete(p, CONST_PARAM); +} + // test type_param_bounds // struct S; pub(super) fn bounds(p: &mut Parser) { diff --git a/crates/ra_parser/src/lib.rs b/crates/ra_parser/src/lib.rs index 45241e5664..65134277e1 100644 --- a/crates/ra_parser/src/lib.rs +++ b/crates/ra_parser/src/lib.rs @@ -83,6 +83,7 @@ pub fn parse(token_source: &mut dyn TokenSource, tree_sink: &mut dyn TreeSink) { parse_from_tokens(token_source, tree_sink, grammar::root); } +#[derive(Clone, Copy)] pub enum FragmentKind { Path, Expr, diff --git a/crates/ra_parser/src/parser.rs b/crates/ra_parser/src/parser.rs index dafd5247bf..1071c46dc8 100644 --- a/crates/ra_parser/src/parser.rs +++ b/crates/ra_parser/src/parser.rs @@ -208,7 +208,7 @@ impl<'t> Parser<'t> { /// Create an error node and consume the next token. pub(crate) fn err_and_bump(&mut self, message: &str) { - self.err_recover(message, TokenSet::empty()); + self.err_recover(message, TokenSet::EMPTY); } /// Create an error node and consume the next token. diff --git a/crates/ra_parser/src/syntax_kind/generated.rs b/crates/ra_parser/src/syntax_kind/generated.rs index fe0fcdb33f..af2945f571 100644 --- a/crates/ra_parser/src/syntax_kind/generated.rs +++ b/crates/ra_parser/src/syntax_kind/generated.rs @@ -100,6 +100,7 @@ pub enum SyntaxKind { TRY_KW, BOX_KW, AWAIT_KW, + MACRO_KW, AUTO_KW, DEFAULT_KW, EXISTENTIAL_KW, @@ -136,6 +137,7 @@ pub enum SyntaxKind { TYPE_ALIAS_DEF, MACRO_CALL, TOKEN_TREE, + MACRO_DEF, PAREN_TYPE, TUPLE_TYPE, NEVER_TYPE, @@ -227,6 +229,7 @@ pub enum SyntaxKind { TYPE_PARAM_LIST, LIFETIME_PARAM, TYPE_PARAM, + CONST_PARAM, TYPE_ARG_LIST, LIFETIME_ARG, TYPE_ARG, @@ -251,7 +254,7 @@ impl SyntaxKind { | SUPER_KW | IN_KW | WHERE_KW | FOR_KW | LOOP_KW | WHILE_KW | CONTINUE_KW | BREAK_KW | IF_KW | ELSE_KW | MATCH_KW | CONST_KW | STATIC_KW | MUT_KW | UNSAFE_KW | TYPE_KW | REF_KW | LET_KW | MOVE_KW | RETURN_KW | TRY_KW | BOX_KW | AWAIT_KW - | AUTO_KW | DEFAULT_KW | EXISTENTIAL_KW | UNION_KW => true, + | MACRO_KW | AUTO_KW | DEFAULT_KW | EXISTENTIAL_KW | UNION_KW => true, _ => false, } } @@ -314,6 +317,7 @@ impl SyntaxKind { "try" => TRY_KW, "box" => BOX_KW, "await" => AWAIT_KW, + "macro" => MACRO_KW, _ => return None, }; Some(kw) @@ -628,6 +632,9 @@ macro_rules! T { ( await ) => { $crate::SyntaxKind::AWAIT_KW }; + ( macro ) => { + $crate::SyntaxKind::MACRO_KW + }; ( auto ) => { $crate::SyntaxKind::AUTO_KW }; diff --git a/crates/ra_parser/src/token_set.rs b/crates/ra_parser/src/token_set.rs index 2a6952c013..994017acfd 100644 --- a/crates/ra_parser/src/token_set.rs +++ b/crates/ra_parser/src/token_set.rs @@ -1,4 +1,4 @@ -//! FIXME: write short doc here +//! A bit-set of `SyntaxKind`s. use crate::SyntaxKind; @@ -7,9 +7,7 @@ use crate::SyntaxKind; pub(crate) struct TokenSet(u128); impl TokenSet { - pub(crate) const fn empty() -> TokenSet { - TokenSet(0) - } + pub(crate) const EMPTY: TokenSet = TokenSet(0); pub(crate) const fn singleton(kind: SyntaxKind) -> TokenSet { TokenSet(mask(kind)) @@ -30,7 +28,7 @@ const fn mask(kind: SyntaxKind) -> u128 { #[macro_export] macro_rules! token_set { - ($($t:expr),*) => { TokenSet::empty()$(.union(TokenSet::singleton($t)))* }; + ($($t:expr),*) => { TokenSet::EMPTY$(.union(TokenSet::singleton($t)))* }; ($($t:expr),* ,) => { token_set!($($t),*) }; } diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs index 845b2221c8..f260c40a32 100644 --- a/crates/ra_prof/src/lib.rs +++ b/crates/ra_prof/src/lib.rs @@ -217,7 +217,7 @@ fn print( total: Option, ) { let mut last = 0; - let indent = repeat(" ").take(lvl + 1).collect::(); + let indent = repeat(" ").take(lvl).collect::(); // We output hierarchy for long calls, but sum up all short calls let mut short = Vec::new(); let mut accounted_for = Duration::default(); @@ -227,7 +227,7 @@ fn print( } accounted_for += duration; if duration >= longer_than { - writeln!(out, "{} {:6}ms - {}", indent, duration.as_millis(), msg) + writeln!(out, "{}{:5}ms - {}", indent, duration.as_millis(), msg) .expect("printing profiling info to stdout"); print(lvl + 1, &msgs[last..i], out, longer_than, Some(duration)); @@ -245,14 +245,14 @@ fn print( count += 1; total_duration += *time; }); - writeln!(out, "{} {:6}ms - {} ({} calls)", indent, total_duration.as_millis(), msg, count) + writeln!(out, "{}{:5}ms - {} ({} calls)", indent, total_duration.as_millis(), msg, count) .expect("printing profiling info to stdout"); } if let Some(total) = total { if let Some(unaccounted) = total.checked_sub(accounted_for) { if unaccounted >= longer_than && last > 0 { - writeln!(out, "{} {:6}ms - ???", indent, unaccounted.as_millis()) + writeln!(out, "{}{:5}ms - ???", indent, unaccounted.as_millis()) .expect("printing profiling info to stdout"); } } diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 351997dcdb..c862d3912c 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -6,6 +6,7 @@ use cargo_metadata::{CargoOpt, MetadataCommand}; use ra_arena::{impl_arena_id, Arena, RawId}; use ra_db::Edition; use rustc_hash::FxHashMap; +use serde::Deserialize; use crate::Result; @@ -23,6 +24,26 @@ pub struct CargoWorkspace { pub(crate) workspace_root: PathBuf, } +#[derive(Deserialize, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase", default)] +pub struct CargoFeatures { + /// Do not activate the `default` feature. + pub no_default_features: bool, + + /// Activate all available features + pub all_features: bool, + + /// List of features to activate. + /// This will be ignored if `cargo_all_features` is true. + pub features: Vec, +} + +impl Default for CargoFeatures { + fn default() -> Self { + CargoFeatures { no_default_features: false, all_features: true, features: Vec::new() } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Package(RawId); impl_arena_id!(Package); @@ -132,9 +153,21 @@ impl Target { } impl CargoWorkspace { - pub fn from_cargo_metadata(cargo_toml: &Path) -> Result { + pub fn from_cargo_metadata( + cargo_toml: &Path, + cargo_features: &CargoFeatures, + ) -> Result { let mut meta = MetadataCommand::new(); - meta.manifest_path(cargo_toml).features(CargoOpt::AllFeatures); + meta.manifest_path(cargo_toml); + if cargo_features.all_features { + meta.features(CargoOpt::AllFeatures); + } else if cargo_features.no_default_features { + // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` + // https://github.com/oli-obk/cargo_metadata/issues/79 + meta.features(CargoOpt::NoDefaultFeatures); + } else { + meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); + } if let Some(parent) = cargo_toml.parent() { meta.current_dir(parent); } diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 55ff4d6efc..d71b7031a8 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -18,7 +18,7 @@ use rustc_hash::FxHashMap; use serde_json::from_reader; pub use crate::{ - cargo_workspace::{CargoWorkspace, Package, Target, TargetKind}, + cargo_workspace::{CargoFeatures, CargoWorkspace, Package, Target, TargetKind}, json_project::JsonProject, sysroot::Sysroot, }; @@ -60,11 +60,15 @@ impl PackageRoot { } impl ProjectWorkspace { - pub fn discover(path: &Path) -> Result { - ProjectWorkspace::discover_with_sysroot(path, true) + pub fn discover(path: &Path, cargo_features: &CargoFeatures) -> Result { + ProjectWorkspace::discover_with_sysroot(path, true, cargo_features) } - pub fn discover_with_sysroot(path: &Path, with_sysroot: bool) -> Result { + pub fn discover_with_sysroot( + path: &Path, + with_sysroot: bool, + cargo_features: &CargoFeatures, + ) -> Result { match find_rust_project_json(path) { Some(json_path) => { let file = File::open(json_path)?; @@ -73,7 +77,7 @@ impl ProjectWorkspace { } None => { let cargo_toml = find_cargo_toml(path)?; - let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?; + let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)?; let sysroot = if with_sysroot { Sysroot::discover(&cargo_toml)? } else { Sysroot::default() }; Ok(ProjectWorkspace::Cargo { cargo, sysroot }) diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml index 5db2b58c0b..b6ebb129d0 100644 --- a/crates/ra_syntax/Cargo.toml +++ b/crates/ra_syntax/Cargo.toml @@ -12,19 +12,20 @@ doctest = false [dependencies] itertools = "0.8.0" -rowan = "0.7.0" +rowan = "0.8.0" rustc_lexer = "0.1.0" rustc-hash = "1.0.1" arrayvec = "0.5.1" once_cell = "1.2.0" +ra_text_edit = { path = "../ra_text_edit" } +ra_parser = { path = "../ra_parser" } + # This crate transitively depends on `smol_str` via `rowan`. # ideally, `serde` should be enabled by `ra_lsp_server`, but we enable it here # to reduce number of compilations smol_str = { version = "0.1.12", features = ["serde"] } - -ra_text_edit = { path = "../ra_text_edit" } -ra_parser = { path = "../ra_parser" } +serde = { version = "1", features = ["derive"] } [dev-dependencies] test_utils = { path = "../test_utils" } diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 1c075082a4..e4061e9947 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs @@ -140,13 +140,13 @@ pub fn insert_children( }); let new_children = match &position { - InsertPosition::First => to_insert.chain(old_children).collect::>(), - InsertPosition::Last => old_children.chain(to_insert).collect::>(), + InsertPosition::First => to_insert.chain(old_children).collect::>(), + InsertPosition::Last => old_children.chain(to_insert).collect::>(), InsertPosition::Before(anchor) | InsertPosition::After(anchor) => { let take_anchor = if let InsertPosition::After(_) = position { 1 } else { 0 }; let split_at = position_of_child(parent, anchor.clone()) + take_anchor; let before = old_children.by_ref().take(split_at).collect::>(); - before.into_iter().chain(to_insert).chain(old_children).collect::>() + before.into_iter().chain(to_insert).chain(old_children).collect::>() } }; @@ -174,7 +174,7 @@ pub fn replace_children( .into_iter() .chain(to_insert.map(to_green_element)) .chain(old_children.skip(end + 1 - start)) - .collect::>(); + .collect::>(); with_children(parent, new_children) } @@ -187,7 +187,7 @@ pub fn replace_descendants( map: &FxHashMap, ) -> SyntaxNode { // FIXME: this could be made much faster. - let new_children = parent.children_with_tokens().map(|it| go(map, it)).collect::>(); + let new_children = parent.children_with_tokens().map(|it| go(map, it)).collect::>(); return with_children(parent, new_children); fn go( @@ -211,7 +211,7 @@ pub fn replace_descendants( fn with_children( parent: &SyntaxNode, - new_children: Box<[NodeOrToken]>, + new_children: Vec>, ) -> SyntaxNode { let len = new_children.iter().map(|it| it.text_len()).sum::(); let new_node = diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 95bf9db147..ae5d639276 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -104,7 +104,7 @@ impl ast::ItemList { } }; - let indent = leading_indent(self.syntax()).unwrap_or("".into()); + let indent = leading_indent(self.syntax()).unwrap_or_default(); let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); let to_insert = iter::once(ws.ws().into()); match existing_ws { @@ -133,7 +133,7 @@ impl ast::RecordFieldList { let space = if is_multiline { ws = tokens::WsBuilder::new(&format!( "\n{} ", - leading_indent(self.syntax()).unwrap_or("".into()) + leading_indent(self.syntax()).unwrap_or_default() )); ws.ws() } else { diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index c06076e3d7..9f9d6e63cb 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -312,6 +312,7 @@ impl AstNode for Block { } } impl ast::AttrsOwner for Block {} +impl ast::ModuleItemOwner for Block {} impl Block { pub fn statements(&self) -> AstChildren { AstChildren::new(&self.syntax) @@ -550,6 +551,36 @@ impl ConstDef { } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ConstParam { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for ConstParam { + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + CONST_PARAM => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +impl ast::NameOwner for ConstParam {} +impl ast::AttrsOwner for ConstParam {} +impl ast::TypeAscriptionOwner for ConstParam {} +impl ConstParam { + pub fn default_val(&self) -> Option { + AstChildren::new(&self.syntax).next() + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ContinueExpr { pub(crate) syntax: SyntaxNode, } @@ -1425,6 +1456,9 @@ impl LambdaExpr { pub fn param_list(&self) -> Option { AstChildren::new(&self.syntax).next() } + pub fn ret_type(&self) -> Option { + AstChildren::new(&self.syntax).next() + } pub fn body(&self) -> Option { AstChildren::new(&self.syntax).next() } diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 40db570da0..04a5408fef 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs @@ -168,8 +168,7 @@ pub fn let_stmt(pattern: ast::Pat, initializer: Option) -> ast::LetSt fn ast_from_text(text: &str) -> N { let parse = SourceFile::parse(text); - let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap(); - res + parse.tree().syntax().descendants().find_map(N::cast).unwrap() } pub mod tokens { diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index d1be40abeb..08aafb610d 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -94,7 +94,8 @@ Grammar( "return", "try", "box", - "await" + "await", + "macro" ], contextual_keywords: [ "auto", @@ -140,6 +141,7 @@ Grammar( "TYPE_ALIAS_DEF", "MACRO_CALL", "TOKEN_TREE", + "MACRO_DEF", "PAREN_TYPE", "TUPLE_TYPE", @@ -243,6 +245,7 @@ Grammar( "TYPE_PARAM_LIST", "LIFETIME_PARAM", "TYPE_PARAM", + "CONST_PARAM", "TYPE_ARG_LIST", "LIFETIME_ARG", "TYPE_ARG", @@ -426,7 +429,7 @@ Grammar( "PathExpr": (options: ["Path"]), "LambdaExpr": ( options: [ - "ParamList", + "ParamList", "RetType", ["body", "Expr"], ] ), @@ -602,6 +605,10 @@ Grammar( options: [("default_type", "TypeRef")], traits: ["NameOwner", "AttrsOwner", "TypeBoundsOwner"], ), + "ConstParam": ( + options: [("default_val", "Expr")], + traits: ["NameOwner", "AttrsOwner", "TypeAscriptionOwner"], + ), "LifetimeParam": ( traits: ["AttrsOwner"], ), @@ -653,6 +660,7 @@ Grammar( ], traits: [ "AttrsOwner", + "ModuleItemOwner", ] ), "ParamList": ( @@ -664,14 +672,14 @@ Grammar( "SelfParam": ( traits: [ "TypeAscriptionOwner", - "AttrsOwner", + "AttrsOwner", ] ), "Param": ( options: [ "Pat" ], traits: [ "TypeAscriptionOwner", - "AttrsOwner", + "AttrsOwner", ] ), "UseItem": ( diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs index e049fce611..db6230aab9 100644 --- a/crates/ra_syntax/src/ptr.rs +++ b/crates/ra_syntax/src/ptr.rs @@ -1,6 +1,10 @@ //! FIXME: write short doc here -use std::{iter::successors, marker::PhantomData}; +use std::{ + hash::{Hash, Hasher}, + iter::successors, + marker::PhantomData, +}; use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange}; @@ -43,7 +47,7 @@ impl SyntaxNodePtr { } /// Like `SyntaxNodePtr`, but remembers the type of node -#[derive(Debug, Hash)] +#[derive(Debug)] pub struct AstPtr { raw: SyntaxNodePtr, _ty: PhantomData N>, @@ -64,6 +68,12 @@ impl PartialEq for AstPtr { } } +impl Hash for AstPtr { + fn hash(&self, state: &mut H) { + self.raw.hash(state) + } +} + impl AstPtr { pub fn new(node: &N) -> AstPtr { AstPtr { raw: SyntaxNodePtr::new(node.syntax()), _ty: PhantomData } diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs index b2f5b8c646..041c6ea8d6 100644 --- a/crates/ra_syntax/src/syntax_node.rs +++ b/crates/ra_syntax/src/syntax_node.rs @@ -40,7 +40,7 @@ pub use rowan::{Direction, NodeOrToken}; pub struct SyntaxTreeBuilder { errors: Vec, - inner: GreenNodeBuilder, + inner: GreenNodeBuilder<'static>, } impl Default for SyntaxTreeBuilder { diff --git a/crates/ra_syntax/test_data/parser/err/0028_macro_2.0.txt b/crates/ra_syntax/test_data/parser/err/0028_macro_2.0.txt deleted file mode 100644 index c5be73a5a0..0000000000 --- a/crates/ra_syntax/test_data/parser/err/0028_macro_2.0.txt +++ /dev/null @@ -1,328 +0,0 @@ -SOURCE_FILE@[0; 349) - MACRO_CALL@[0; 41) - PATH@[0; 5) - PATH_SEGMENT@[0; 5) - NAME_REF@[0; 5) - IDENT@[0; 5) "macro" - WHITESPACE@[5; 6) " " - NAME@[6; 21) - IDENT@[6; 21) "parse_use_trees" - TOKEN_TREE@[21; 41) - L_PAREN@[21; 22) "(" - DOLLAR@[22; 23) "$" - TOKEN_TREE@[23; 32) - L_PAREN@[23; 24) "(" - DOLLAR@[24; 25) "$" - IDENT@[25; 26) "s" - COLON@[26; 27) ":" - IDENT@[27; 31) "expr" - R_PAREN@[31; 32) ")" - COMMA@[32; 33) "," - STAR@[33; 34) "*" - WHITESPACE@[34; 35) " " - DOLLAR@[35; 36) "$" - TOKEN_TREE@[36; 39) - L_PAREN@[36; 37) "(" - COMMA@[37; 38) "," - R_PAREN@[38; 39) ")" - STAR@[39; 40) "*" - R_PAREN@[40; 41) ")" - WHITESPACE@[41; 42) " " - ERROR@[42; 93) - L_CURLY@[42; 43) "{" - WHITESPACE@[43; 48) "\n " - MACRO_CALL@[48; 91) - PATH@[48; 51) - PATH_SEGMENT@[48; 51) - NAME_REF@[48; 51) - IDENT@[48; 51) "vec" - EXCL@[51; 52) "!" - TOKEN_TREE@[52; 91) - L_BRACK@[52; 53) "[" - WHITESPACE@[53; 62) "\n " - DOLLAR@[62; 63) "$" - TOKEN_TREE@[63; 84) - L_PAREN@[63; 64) "(" - IDENT@[64; 78) "parse_use_tree" - TOKEN_TREE@[78; 82) - L_PAREN@[78; 79) "(" - DOLLAR@[79; 80) "$" - IDENT@[80; 81) "s" - R_PAREN@[81; 82) ")" - COMMA@[82; 83) "," - R_PAREN@[83; 84) ")" - STAR@[84; 85) "*" - WHITESPACE@[85; 90) "\n " - R_BRACK@[90; 91) "]" - WHITESPACE@[91; 92) "\n" - R_CURLY@[92; 93) "}" - WHITESPACE@[93; 95) "\n\n" - FN_DEF@[95; 348) - ATTR@[95; 102) - POUND@[95; 96) "#" - L_BRACK@[96; 97) "[" - PATH@[97; 101) - PATH_SEGMENT@[97; 101) - NAME_REF@[97; 101) - IDENT@[97; 101) "test" - R_BRACK@[101; 102) "]" - WHITESPACE@[102; 103) "\n" - FN_KW@[103; 105) "fn" - WHITESPACE@[105; 106) " " - NAME@[106; 125) - IDENT@[106; 125) "test_use_tree_merge" - PARAM_LIST@[125; 127) - L_PAREN@[125; 126) "(" - R_PAREN@[126; 127) ")" - WHITESPACE@[127; 128) " " - BLOCK_EXPR@[128; 348) - BLOCK@[128; 348) - L_CURLY@[128; 129) "{" - WHITESPACE@[129; 134) "\n " - EXPR_STMT@[134; 139) - PATH_EXPR@[134; 139) - PATH@[134; 139) - PATH_SEGMENT@[134; 139) - NAME_REF@[134; 139) - IDENT@[134; 139) "macro" - WHITESPACE@[139; 140) " " - EXPR_STMT@[140; 154) - CALL_EXPR@[140; 154) - PATH_EXPR@[140; 150) - PATH@[140; 150) - PATH_SEGMENT@[140; 150) - NAME_REF@[140; 150) - IDENT@[140; 150) "test_merge" - ARG_LIST@[150; 154) - L_PAREN@[150; 151) "(" - ARRAY_EXPR@[151; 154) - L_BRACK@[151; 152) "[" - ERROR@[152; 153) - DOLLAR@[152; 153) "$" - PAREN_EXPR@[153; 154) - L_PAREN@[153; 154) "(" - EXPR_STMT@[154; 155) - ERROR@[154; 155) - DOLLAR@[154; 155) "$" - EXPR_STMT@[155; 160) - PATH_EXPR@[155; 160) - PATH@[155; 160) - PATH_SEGMENT@[155; 160) - NAME_REF@[155; 160) - IDENT@[155; 160) "input" - EXPR_STMT@[160; 161) - ERROR@[160; 161) - COLON@[160; 161) ":" - EXPR_STMT@[161; 165) - PATH_EXPR@[161; 165) - PATH@[161; 165) - PATH_SEGMENT@[161; 165) - NAME_REF@[161; 165) - IDENT@[161; 165) "expr" - EXPR_STMT@[165; 166) - ERROR@[165; 166) - R_PAREN@[165; 166) ")" - EXPR_STMT@[166; 167) - ERROR@[166; 167) - COMMA@[166; 167) "," - EXPR_STMT@[167; 170) - PREFIX_EXPR@[167; 170) - STAR@[167; 168) "*" - WHITESPACE@[168; 169) " " - ERROR@[169; 170) - DOLLAR@[169; 170) "$" - EXPR_STMT@[170; 171) - PAREN_EXPR@[170; 171) - L_PAREN@[170; 171) "(" - EXPR_STMT@[171; 172) - ERROR@[171; 172) - COMMA@[171; 172) "," - EXPR_STMT@[172; 173) - ERROR@[172; 173) - R_PAREN@[172; 173) ")" - EXPR_STMT@[173; 175) - PREFIX_EXPR@[173; 175) - STAR@[173; 174) "*" - ERROR@[174; 175) - R_BRACK@[174; 175) "]" - EXPR_STMT@[175; 176) - ERROR@[175; 176) - COMMA@[175; 176) "," - WHITESPACE@[176; 177) " " - EXPR_STMT@[177; 180) - ARRAY_EXPR@[177; 180) - L_BRACK@[177; 178) "[" - ERROR@[178; 179) - DOLLAR@[178; 179) "$" - PAREN_EXPR@[179; 180) - L_PAREN@[179; 180) "(" - EXPR_STMT@[180; 181) - ERROR@[180; 181) - DOLLAR@[180; 181) "$" - EXPR_STMT@[181; 187) - PATH_EXPR@[181; 187) - PATH@[181; 187) - PATH_SEGMENT@[181; 187) - NAME_REF@[181; 187) - IDENT@[181; 187) "output" - EXPR_STMT@[187; 188) - ERROR@[187; 188) - COLON@[187; 188) ":" - EXPR_STMT@[188; 192) - PATH_EXPR@[188; 192) - PATH@[188; 192) - PATH_SEGMENT@[188; 192) - NAME_REF@[188; 192) - IDENT@[188; 192) "expr" - EXPR_STMT@[192; 193) - ERROR@[192; 193) - R_PAREN@[192; 193) ")" - EXPR_STMT@[193; 194) - ERROR@[193; 194) - COMMA@[193; 194) "," - EXPR_STMT@[194; 197) - PREFIX_EXPR@[194; 197) - STAR@[194; 195) "*" - WHITESPACE@[195; 196) " " - ERROR@[196; 197) - DOLLAR@[196; 197) "$" - EXPR_STMT@[197; 198) - PAREN_EXPR@[197; 198) - L_PAREN@[197; 198) "(" - EXPR_STMT@[198; 199) - ERROR@[198; 199) - COMMA@[198; 199) "," - EXPR_STMT@[199; 200) - ERROR@[199; 200) - R_PAREN@[199; 200) ")" - EXPR_STMT@[200; 202) - PREFIX_EXPR@[200; 202) - STAR@[200; 201) "*" - ERROR@[201; 202) - R_BRACK@[201; 202) "]" - EXPR_STMT@[202; 203) - ERROR@[202; 203) - R_PAREN@[202; 203) ")" - WHITESPACE@[203; 204) " " - BLOCK_EXPR@[204; 346) - BLOCK@[204; 346) - L_CURLY@[204; 205) "{" - WHITESPACE@[205; 214) "\n " - EXPR_STMT@[214; 340) - MACRO_CALL@[214; 339) - PATH@[214; 223) - PATH_SEGMENT@[214; 223) - NAME_REF@[214; 223) - IDENT@[214; 223) "assert_eq" - EXCL@[223; 224) "!" - TOKEN_TREE@[224; 339) - L_PAREN@[224; 225) "(" - WHITESPACE@[225; 238) "\n " - IDENT@[238; 253) "merge_use_trees" - TOKEN_TREE@[253; 284) - L_PAREN@[253; 254) "(" - IDENT@[254; 269) "parse_use_trees" - EXCL@[269; 270) "!" - TOKEN_TREE@[270; 283) - L_PAREN@[270; 271) "(" - DOLLAR@[271; 272) "$" - TOKEN_TREE@[272; 281) - L_PAREN@[272; 273) "(" - DOLLAR@[273; 274) "$" - IDENT@[274; 279) "input" - COMMA@[279; 280) "," - R_PAREN@[280; 281) ")" - STAR@[281; 282) "*" - R_PAREN@[282; 283) ")" - R_PAREN@[283; 284) ")" - COMMA@[284; 285) "," - WHITESPACE@[285; 298) "\n " - IDENT@[298; 313) "parse_use_trees" - EXCL@[313; 314) "!" - TOKEN_TREE@[314; 328) - L_PAREN@[314; 315) "(" - DOLLAR@[315; 316) "$" - TOKEN_TREE@[316; 326) - L_PAREN@[316; 317) "(" - DOLLAR@[317; 318) "$" - IDENT@[318; 324) "output" - COMMA@[324; 325) "," - R_PAREN@[325; 326) ")" - STAR@[326; 327) "*" - R_PAREN@[327; 328) ")" - COMMA@[328; 329) "," - WHITESPACE@[329; 338) "\n " - R_PAREN@[338; 339) ")" - SEMI@[339; 340) ";" - WHITESPACE@[340; 345) "\n " - R_CURLY@[345; 346) "}" - WHITESPACE@[346; 347) "\n" - R_CURLY@[347; 348) "}" - WHITESPACE@[348; 349) "\n" -error 5: expected EXCL -error 41: expected SEMI -error 42: expected an item -error 139: expected SEMI -error 152: expected expression -error 153: expected COMMA -error 154: expected expression -error 154: expected R_PAREN -error 154: expected COMMA -error 154: expected expression -error 154: expected R_BRACK -error 154: expected COMMA -error 154: expected SEMI -error 154: expected expression -error 155: expected SEMI -error 160: expected SEMI -error 160: expected expression -error 161: expected SEMI -error 165: expected SEMI -error 165: expected expression -error 166: expected SEMI -error 166: expected expression -error 167: expected SEMI -error 169: expected expression -error 170: expected SEMI -error 171: expected expression -error 171: expected R_PAREN -error 171: expected SEMI -error 171: expected expression -error 172: expected SEMI -error 172: expected expression -error 173: expected SEMI -error 174: expected expression -error 175: expected SEMI -error 175: expected expression -error 176: expected SEMI -error 178: expected expression -error 179: expected COMMA -error 180: expected expression -error 180: expected R_PAREN -error 180: expected COMMA -error 180: expected expression -error 180: expected R_BRACK -error 180: expected SEMI -error 180: expected expression -error 181: expected SEMI -error 187: expected SEMI -error 187: expected expression -error 188: expected SEMI -error 192: expected SEMI -error 192: expected expression -error 193: expected SEMI -error 193: expected expression -error 194: expected SEMI -error 196: expected expression -error 197: expected SEMI -error 198: expected expression -error 198: expected R_PAREN -error 198: expected SEMI -error 198: expected expression -error 199: expected SEMI -error 199: expected expression -error 200: expected SEMI -error 201: expected expression -error 202: expected SEMI -error 202: expected expression -error 203: expected SEMI diff --git a/crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.rs b/crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.rs new file mode 100644 index 0000000000..a2f74bd879 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.rs @@ -0,0 +1,5 @@ +fn foo() -> i32 { + [1, 2, 3].iter() + .map(|it|) + .max::(); +} diff --git a/crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.txt b/crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.txt new file mode 100644 index 0000000000..d1544634ce --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0039_lambda_recovery.txt @@ -0,0 +1,83 @@ +SOURCE_FILE@[0; 83) + FN_DEF@[0; 82) + FN_KW@[0; 2) "fn" + WHITESPACE@[2; 3) " " + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) "(" + R_PAREN@[7; 8) ")" + WHITESPACE@[8; 9) " " + RET_TYPE@[9; 15) + THIN_ARROW@[9; 11) "->" + WHITESPACE@[11; 12) " " + PATH_TYPE@[12; 15) + PATH@[12; 15) + PATH_SEGMENT@[12; 15) + NAME_REF@[12; 15) + IDENT@[12; 15) "i32" + WHITESPACE@[15; 16) " " + BLOCK_EXPR@[16; 82) + BLOCK@[16; 82) + L_CURLY@[16; 17) "{" + WHITESPACE@[17; 22) "\n " + EXPR_STMT@[22; 80) + METHOD_CALL_EXPR@[22; 79) + METHOD_CALL_EXPR@[22; 57) + METHOD_CALL_EXPR@[22; 38) + ARRAY_EXPR@[22; 31) + L_BRACK@[22; 23) "[" + LITERAL@[23; 24) + INT_NUMBER@[23; 24) "1" + COMMA@[24; 25) "," + WHITESPACE@[25; 26) " " + LITERAL@[26; 27) + INT_NUMBER@[26; 27) "2" + COMMA@[27; 28) "," + WHITESPACE@[28; 29) " " + LITERAL@[29; 30) + INT_NUMBER@[29; 30) "3" + R_BRACK@[30; 31) "]" + DOT@[31; 32) "." + NAME_REF@[32; 36) + IDENT@[32; 36) "iter" + ARG_LIST@[36; 38) + L_PAREN@[36; 37) "(" + R_PAREN@[37; 38) ")" + WHITESPACE@[38; 47) "\n " + DOT@[47; 48) "." + NAME_REF@[48; 51) + IDENT@[48; 51) "map" + ARG_LIST@[51; 57) + L_PAREN@[51; 52) "(" + LAMBDA_EXPR@[52; 56) + PARAM_LIST@[52; 56) + PIPE@[52; 53) "|" + PARAM@[53; 55) + BIND_PAT@[53; 55) + NAME@[53; 55) + IDENT@[53; 55) "it" + PIPE@[55; 56) "|" + R_PAREN@[56; 57) ")" + WHITESPACE@[57; 66) "\n " + DOT@[66; 67) "." + NAME_REF@[67; 70) + IDENT@[67; 70) "max" + TYPE_ARG_LIST@[70; 77) + COLONCOLON@[70; 72) "::" + L_ANGLE@[72; 73) "<" + TYPE_ARG@[73; 76) + PATH_TYPE@[73; 76) + PATH@[73; 76) + PATH_SEGMENT@[73; 76) + NAME_REF@[73; 76) + IDENT@[73; 76) "i32" + R_ANGLE@[76; 77) ">" + ARG_LIST@[77; 79) + L_PAREN@[77; 78) "(" + R_PAREN@[78; 79) ")" + SEMI@[79; 80) ";" + WHITESPACE@[80; 81) "\n" + R_CURLY@[81; 82) "}" + WHITESPACE@[82; 83) "\n" +error 56: expected expression diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0129_marco_pat.txt b/crates/ra_syntax/test_data/parser/inline/ok/0129_marco_pat.txt index 4a714ad6b1..b05ccc0ed5 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0129_marco_pat.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0129_marco_pat.txt @@ -16,11 +16,10 @@ SOURCE_FILE@[0; 33) LET_KW@[16; 19) "let" WHITESPACE@[19; 20) " " MACRO_CALL@[20; 25) - PATH_PAT@[20; 21) - PATH@[20; 21) - PATH_SEGMENT@[20; 21) - NAME_REF@[20; 21) - IDENT@[20; 21) "m" + PATH@[20; 21) + PATH_SEGMENT@[20; 21) + NAME_REF@[20; 21) + IDENT@[20; 21) "m" EXCL@[21; 22) "!" TOKEN_TREE@[22; 25) L_PAREN@[22; 23) "(" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0147_const_param.rs b/crates/ra_syntax/test_data/parser/inline/ok/0147_const_param.rs new file mode 100644 index 0000000000..8cdb3b7036 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0147_const_param.rs @@ -0,0 +1 @@ +struct S; diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0147_const_param.txt b/crates/ra_syntax/test_data/parser/inline/ok/0147_const_param.txt new file mode 100644 index 0000000000..f81de7bac7 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0147_const_param.txt @@ -0,0 +1,23 @@ +SOURCE_FILE@[0; 24) + STRUCT_DEF@[0; 23) + STRUCT_KW@[0; 6) "struct" + WHITESPACE@[6; 7) " " + NAME@[7; 8) + IDENT@[7; 8) "S" + TYPE_PARAM_LIST@[8; 22) + L_ANGLE@[8; 9) "<" + CONST_PARAM@[9; 21) + CONST_KW@[9; 14) "const" + WHITESPACE@[14; 15) " " + NAME@[15; 16) + IDENT@[15; 16) "N" + COLON@[16; 17) ":" + WHITESPACE@[17; 18) " " + PATH_TYPE@[18; 21) + PATH@[18; 21) + PATH_SEGMENT@[18; 21) + NAME_REF@[18; 21) + IDENT@[18; 21) "u32" + R_ANGLE@[21; 22) ">" + SEMI@[22; 23) ";" + WHITESPACE@[23; 24) "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0147_macro_def.rs b/crates/ra_syntax/test_data/parser/inline/ok/0147_macro_def.rs new file mode 100644 index 0000000000..319a4e2aad --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0147_macro_def.rs @@ -0,0 +1,2 @@ +macro m { ($i:ident) => {} } +macro m($i:ident) {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0147_macro_def.txt b/crates/ra_syntax/test_data/parser/inline/ok/0147_macro_def.txt new file mode 100644 index 0000000000..3556099bd0 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0147_macro_def.txt @@ -0,0 +1,45 @@ +SOURCE_FILE@[0; 50) + MACRO_DEF@[0; 28) + MACRO_KW@[0; 5) "macro" + WHITESPACE@[5; 6) " " + NAME@[6; 7) + IDENT@[6; 7) "m" + WHITESPACE@[7; 8) " " + TOKEN_TREE@[8; 28) + L_CURLY@[8; 9) "{" + WHITESPACE@[9; 10) " " + TOKEN_TREE@[10; 20) + L_PAREN@[10; 11) "(" + DOLLAR@[11; 12) "$" + IDENT@[12; 13) "i" + COLON@[13; 14) ":" + IDENT@[14; 19) "ident" + R_PAREN@[19; 20) ")" + WHITESPACE@[20; 21) " " + EQ@[21; 22) "=" + R_ANGLE@[22; 23) ">" + WHITESPACE@[23; 24) " " + TOKEN_TREE@[24; 26) + L_CURLY@[24; 25) "{" + R_CURLY@[25; 26) "}" + WHITESPACE@[26; 27) " " + R_CURLY@[27; 28) "}" + WHITESPACE@[28; 29) "\n" + MACRO_DEF@[29; 49) + MACRO_KW@[29; 34) "macro" + WHITESPACE@[34; 35) " " + NAME@[35; 36) + IDENT@[35; 36) "m" + TOKEN_TREE@[36; 49) + TOKEN_TREE@[36; 46) + L_PAREN@[36; 37) "(" + DOLLAR@[37; 38) "$" + IDENT@[38; 39) "i" + COLON@[39; 40) ":" + IDENT@[40; 45) "ident" + R_PAREN@[45; 46) ")" + WHITESPACE@[46; 47) " " + TOKEN_TREE@[47; 49) + L_CURLY@[47; 48) "{" + R_CURLY@[48; 49) "}" + WHITESPACE@[49; 50) "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0148_pub_macro_def.rs b/crates/ra_syntax/test_data/parser/inline/ok/0148_pub_macro_def.rs new file mode 100644 index 0000000000..3b2be597fd --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0148_pub_macro_def.rs @@ -0,0 +1 @@ +pub macro m($:ident) {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0148_pub_macro_def.txt b/crates/ra_syntax/test_data/parser/inline/ok/0148_pub_macro_def.txt new file mode 100644 index 0000000000..cfd79d9c26 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0148_pub_macro_def.txt @@ -0,0 +1,21 @@ +SOURCE_FILE@[0; 24) + MACRO_DEF@[0; 23) + VISIBILITY@[0; 3) + PUB_KW@[0; 3) "pub" + WHITESPACE@[3; 4) " " + MACRO_KW@[4; 9) "macro" + WHITESPACE@[9; 10) " " + NAME@[10; 11) + IDENT@[10; 11) "m" + TOKEN_TREE@[11; 23) + TOKEN_TREE@[11; 20) + L_PAREN@[11; 12) "(" + DOLLAR@[12; 13) "$" + COLON@[13; 14) ":" + IDENT@[14; 19) "ident" + R_PAREN@[19; 20) ")" + WHITESPACE@[20; 21) " " + TOKEN_TREE@[21; 23) + L_CURLY@[21; 22) "{" + R_CURLY@[22; 23) "}" + WHITESPACE@[23; 24) "\n" diff --git a/crates/ra_syntax/test_data/parser/err/0028_macro_2.0.rs b/crates/ra_syntax/test_data/parser/ok/0062_macro_2.0.rs similarity index 100% rename from crates/ra_syntax/test_data/parser/err/0028_macro_2.0.rs rename to crates/ra_syntax/test_data/parser/ok/0062_macro_2.0.rs diff --git a/crates/ra_syntax/test_data/parser/ok/0062_macro_2.0.txt b/crates/ra_syntax/test_data/parser/ok/0062_macro_2.0.txt new file mode 100644 index 0000000000..2be523fc35 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0062_macro_2.0.txt @@ -0,0 +1,176 @@ +SOURCE_FILE@[0; 349) + MACRO_DEF@[0; 93) + MACRO_KW@[0; 5) "macro" + WHITESPACE@[5; 6) " " + NAME@[6; 21) + IDENT@[6; 21) "parse_use_trees" + TOKEN_TREE@[21; 93) + TOKEN_TREE@[21; 41) + L_PAREN@[21; 22) "(" + DOLLAR@[22; 23) "$" + TOKEN_TREE@[23; 32) + L_PAREN@[23; 24) "(" + DOLLAR@[24; 25) "$" + IDENT@[25; 26) "s" + COLON@[26; 27) ":" + IDENT@[27; 31) "expr" + R_PAREN@[31; 32) ")" + COMMA@[32; 33) "," + STAR@[33; 34) "*" + WHITESPACE@[34; 35) " " + DOLLAR@[35; 36) "$" + TOKEN_TREE@[36; 39) + L_PAREN@[36; 37) "(" + COMMA@[37; 38) "," + R_PAREN@[38; 39) ")" + STAR@[39; 40) "*" + R_PAREN@[40; 41) ")" + WHITESPACE@[41; 42) " " + TOKEN_TREE@[42; 93) + L_CURLY@[42; 43) "{" + WHITESPACE@[43; 48) "\n " + IDENT@[48; 51) "vec" + EXCL@[51; 52) "!" + TOKEN_TREE@[52; 91) + L_BRACK@[52; 53) "[" + WHITESPACE@[53; 62) "\n " + DOLLAR@[62; 63) "$" + TOKEN_TREE@[63; 84) + L_PAREN@[63; 64) "(" + IDENT@[64; 78) "parse_use_tree" + TOKEN_TREE@[78; 82) + L_PAREN@[78; 79) "(" + DOLLAR@[79; 80) "$" + IDENT@[80; 81) "s" + R_PAREN@[81; 82) ")" + COMMA@[82; 83) "," + R_PAREN@[83; 84) ")" + STAR@[84; 85) "*" + WHITESPACE@[85; 90) "\n " + R_BRACK@[90; 91) "]" + WHITESPACE@[91; 92) "\n" + R_CURLY@[92; 93) "}" + WHITESPACE@[93; 95) "\n\n" + FN_DEF@[95; 348) + ATTR@[95; 102) + POUND@[95; 96) "#" + L_BRACK@[96; 97) "[" + PATH@[97; 101) + PATH_SEGMENT@[97; 101) + NAME_REF@[97; 101) + IDENT@[97; 101) "test" + R_BRACK@[101; 102) "]" + WHITESPACE@[102; 103) "\n" + FN_KW@[103; 105) "fn" + WHITESPACE@[105; 106) " " + NAME@[106; 125) + IDENT@[106; 125) "test_use_tree_merge" + PARAM_LIST@[125; 127) + L_PAREN@[125; 126) "(" + R_PAREN@[126; 127) ")" + WHITESPACE@[127; 128) " " + BLOCK_EXPR@[128; 348) + BLOCK@[128; 348) + L_CURLY@[128; 129) "{" + WHITESPACE@[129; 134) "\n " + MACRO_DEF@[134; 346) + MACRO_KW@[134; 139) "macro" + WHITESPACE@[139; 140) " " + NAME@[140; 150) + IDENT@[140; 150) "test_merge" + TOKEN_TREE@[150; 346) + TOKEN_TREE@[150; 203) + L_PAREN@[150; 151) "(" + TOKEN_TREE@[151; 175) + L_BRACK@[151; 152) "[" + DOLLAR@[152; 153) "$" + TOKEN_TREE@[153; 166) + L_PAREN@[153; 154) "(" + DOLLAR@[154; 155) "$" + IDENT@[155; 160) "input" + COLON@[160; 161) ":" + IDENT@[161; 165) "expr" + R_PAREN@[165; 166) ")" + COMMA@[166; 167) "," + STAR@[167; 168) "*" + WHITESPACE@[168; 169) " " + DOLLAR@[169; 170) "$" + TOKEN_TREE@[170; 173) + L_PAREN@[170; 171) "(" + COMMA@[171; 172) "," + R_PAREN@[172; 173) ")" + STAR@[173; 174) "*" + R_BRACK@[174; 175) "]" + COMMA@[175; 176) "," + WHITESPACE@[176; 177) " " + TOKEN_TREE@[177; 202) + L_BRACK@[177; 178) "[" + DOLLAR@[178; 179) "$" + TOKEN_TREE@[179; 193) + L_PAREN@[179; 180) "(" + DOLLAR@[180; 181) "$" + IDENT@[181; 187) "output" + COLON@[187; 188) ":" + IDENT@[188; 192) "expr" + R_PAREN@[192; 193) ")" + COMMA@[193; 194) "," + STAR@[194; 195) "*" + WHITESPACE@[195; 196) " " + DOLLAR@[196; 197) "$" + TOKEN_TREE@[197; 200) + L_PAREN@[197; 198) "(" + COMMA@[198; 199) "," + R_PAREN@[199; 200) ")" + STAR@[200; 201) "*" + R_BRACK@[201; 202) "]" + R_PAREN@[202; 203) ")" + WHITESPACE@[203; 204) " " + TOKEN_TREE@[204; 346) + L_CURLY@[204; 205) "{" + WHITESPACE@[205; 214) "\n " + IDENT@[214; 223) "assert_eq" + EXCL@[223; 224) "!" + TOKEN_TREE@[224; 339) + L_PAREN@[224; 225) "(" + WHITESPACE@[225; 238) "\n " + IDENT@[238; 253) "merge_use_trees" + TOKEN_TREE@[253; 284) + L_PAREN@[253; 254) "(" + IDENT@[254; 269) "parse_use_trees" + EXCL@[269; 270) "!" + TOKEN_TREE@[270; 283) + L_PAREN@[270; 271) "(" + DOLLAR@[271; 272) "$" + TOKEN_TREE@[272; 281) + L_PAREN@[272; 273) "(" + DOLLAR@[273; 274) "$" + IDENT@[274; 279) "input" + COMMA@[279; 280) "," + R_PAREN@[280; 281) ")" + STAR@[281; 282) "*" + R_PAREN@[282; 283) ")" + R_PAREN@[283; 284) ")" + COMMA@[284; 285) "," + WHITESPACE@[285; 298) "\n " + IDENT@[298; 313) "parse_use_trees" + EXCL@[313; 314) "!" + TOKEN_TREE@[314; 328) + L_PAREN@[314; 315) "(" + DOLLAR@[315; 316) "$" + TOKEN_TREE@[316; 326) + L_PAREN@[316; 317) "(" + DOLLAR@[317; 318) "$" + IDENT@[318; 324) "output" + COMMA@[324; 325) "," + R_PAREN@[325; 326) ")" + STAR@[326; 327) "*" + R_PAREN@[327; 328) ")" + COMMA@[328; 329) "," + WHITESPACE@[329; 338) "\n " + R_PAREN@[338; 339) ")" + SEMI@[339; 340) ";" + WHITESPACE@[340; 345) "\n " + R_CURLY@[345; 346) "}" + WHITESPACE@[346; 347) "\n" + R_CURLY@[347; 348) "}" + WHITESPACE@[348; 349) "\n" diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs index 20c251ff45..10f424aae9 100644 --- a/crates/ra_tt/src/lib.rs +++ b/crates/ra_tt/src/lib.rs @@ -33,14 +33,14 @@ impl TokenId { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TokenTree { Leaf(Leaf), Subtree(Subtree), } impl_froms!(TokenTree: Leaf, Subtree); -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Leaf { Literal(Literal), Punct(Punct), @@ -48,38 +48,45 @@ pub enum Leaf { } impl_froms!(Leaf: Literal, Punct, Ident); -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] pub struct Subtree { - pub delimiter: Delimiter, + pub delimiter: Option, pub token_trees: Vec, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Delimiter { +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Delimiter { + pub id: TokenId, + pub kind: DelimiterKind, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum DelimiterKind { Parenthesis, Brace, Bracket, - None, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Literal { pub text: SmolStr, + pub id: TokenId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Punct { pub char: char, pub spacing: Spacing, + pub id: TokenId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Spacing { Alone, Joint, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Ident { pub text: SmolStr, pub id: TokenId, @@ -96,11 +103,11 @@ impl fmt::Display for TokenTree { impl fmt::Display for Subtree { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let (l, r) = match self.delimiter { - Delimiter::Parenthesis => ("(", ")"), - Delimiter::Brace => ("{", "}"), - Delimiter::Bracket => ("[", "]"), - Delimiter::None => ("", ""), + let (l, r) = match self.delimiter_kind() { + Some(DelimiterKind::Parenthesis) => ("(", ")"), + Some(DelimiterKind::Brace) => ("{", "}"), + Some(DelimiterKind::Bracket) => ("[", "]"), + None => ("", ""), }; f.write_str(l)?; let mut needs_space = false; @@ -164,6 +171,10 @@ impl Subtree { self.token_trees.len() + children_count } + + pub fn delimiter_kind(&self) -> Option { + self.delimiter.map(|it| it.kind) + } } pub mod buffer; diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 1244ea8cf1..659f77b71a 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -207,8 +207,8 @@ pub fn lines_match(expected: &str, actual: &str) -> bool { // Let's not deal with / vs \ (windows...) // First replace backslash-escaped backslashes with forward slashes // which can occur in, for example, JSON output - let expected = expected.replace("\\\\", "/").replace("\\", "/"); - let mut actual: &str = &actual.replace("\\\\", "/").replace("\\", "/"); + let expected = expected.replace(r"\\", "/").replace(r"\", "/"); + let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/"); for (i, part) in expected.split("[..]").enumerate() { match actual.find(part) { Some(j) => { @@ -356,6 +356,17 @@ pub fn read_text(path: &Path) -> String { .replace("\r\n", "\n") } +pub fn skip_slow_tests() -> bool { + let should_skip = std::env::var("CI").is_err() && std::env::var("RUN_SLOW_TESTS").is_err(); + if should_skip { + eprintln!("ignoring slow test") + } else { + let path = project_dir().join("./target/.slow_tests_cookie"); + fs::write(&path, ".").unwrap(); + } + should_skip +} + const REWRITE: bool = false; fn assert_equal_text(expected: &str, actual: &str, path: &Path) { diff --git a/docs/dev/README.md b/docs/dev/README.md index 0f64d7e5f8..2f6215d6bb 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -124,9 +124,8 @@ Logging is done by both rust-analyzer and VS Code, so it might be tricky to figure out where logs go. Inside rust-analyzer, we use the standard `log` crate for logging, and -`flexi_logger` for logging frotend. By default, log goes to stderr (the same as -with `env_logger`), but the stderr itself is processed by VS Code. To mirror -logs to a `./log` directory, set `RA_LOG_DIR=1` environmental variable. +`env_logger` for logging frontend. By default, log goes to stderr, but the +stderr itself is processed by VS Code. To see stderr in the running VS Code instance, go to the "Output" tab of the panel and select `rust-analyzer`. This shows `eprintln!` as well. Note that diff --git a/docs/user/README.md b/docs/user/README.md index 1b2d98608c..968d2e34c8 100644 --- a/docs/user/README.md +++ b/docs/user/README.md @@ -1,14 +1,19 @@ The main interface to rust-analyzer is the [LSP](https://microsoft.github.io/language-server-protocol/) implementation. To -install lsp server, use `cargo xtask install --server`, which is a shorthand for `cargo -install --package ra_lsp_server`. The binary is named `ra_lsp_server`, you -should be able to use it with any LSP-compatible editor. We use custom +install lsp server, clone the repository and then run `cargo xtask install +--server` (which is shorthand for `cargo install --path +./crates/ra_lsp_server`). This will produce a binary named `ra_lsp_server` which +you should be able to use it with any LSP-compatible editor. We use custom extensions to LSP, so special client-side support is required to take full advantage of rust-analyzer. This repository contains support code for VS Code and Emacs. -Rust Analyzer needs sources of rust standard library to work, so you might need -to execute +``` +$ git clone git@github.com:rust-analyzer/rust-analyzer && cd rust-analyzer +$ cargo xtask install --server +``` +Rust Analyzer needs sources of rust standard library to work, so +you might also need to execute ``` $ rustup component add rust-src @@ -133,43 +138,31 @@ Prerequisites: Installation: * add -[ra-emacs-lsp.el](https://github.com/rust-analyzer/rust-analyzer/blob/69ee5c9c5ef212f7911028c9ddf581559e6565c3/editors/emacs/ra-emacs-lsp.el) +[ra-emacs-lsp.el](../../editors/emacs/ra-emacs-lsp.el) to load path and require it in `init.el` * run `lsp` in a rust buffer * (Optionally) bind commands like `rust-analyzer-join-lines`, `rust-analyzer-extend-selection` and `rust-analyzer-expand-macro` to keys, and enable `rust-analyzer-inlay-hints-mode` to get inline type hints -## Vim and NeoVim +## Vim and NeoVim (coc-rust-analyzer) -Neovim 0.5 has a built in language server. For a quick start configuration of -rust-analyzer, use [neovim/nvim-lsp](https://github.com/neovim/nvim-lsp#rust_analyzer). -Once `neovim/nvim-lsp` is installed, you can use `call nvim_lsp#setup("rust_analyzer", {})` -or `lua require'nvim_lsp'.rust_analyzer.setup({})` to quickly get set up. - -* Install coc.nvim by following the instructions at [coc.nvim] - - You will need nodejs installed. - - You may want to include some of the sample vim configurations [from here][coc-vim-conf] - - Note that if you use a plugin manager other than `vim-plug`, you may need to manually - checkout the `release` branch wherever your plugin manager cloned it. Otherwise you will - get errors about a missing javascript file. -* Run `:CocInstall coc-rust-analyzer` to install [coc-rust-analyzer], this extension implemented _most_ of the features supported in the VSCode extension: +* Install coc.nvim by following the instructions at [coc.nvim][] (nodejs required) +* Run `:CocInstall coc-rust-analyzer` to install [coc-rust-analyzer], this extension implements _most_ of the features supported in the VSCode extension: - same configurations as VSCode extension, `rust-analyzer.raLspServerPath`, `rust-analyzer.enableCargoWatchOnStartup` etc. - same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.startCargoWatch` etc. - highlighting and inlay_hints are not implemented yet [coc.nvim]: https://github.com/neoclide/coc.nvim -[coc-vim-conf]: https://github.com/neoclide/coc.nvim/#example-vim-configuration [coc-rust-analyzer]: https://github.com/fannheyward/coc-rust-analyzer -## Vim and NeoVim Alternative +## Vim and NeoVim (LanguageClient-neovim) * Install LanguageClient-neovim by following the instructions [here][lang-client-neovim] - - No extra run-time is required as this server is written in Rust - The github project wiki has extra tips on configuration * Configure by adding this to your vim/neovim config file (replacing the existing rust specific line if it exists): -``` +```vim let g:LanguageClient_serverCommands = { \ 'rust': ['ra_lsp_server'], \ } @@ -177,6 +170,13 @@ let g:LanguageClient_serverCommands = { [lang-client-neovim]: https://github.com/autozimu/LanguageClient-neovim +## NeoVim (nvim-lsp) + +NeoVim 0.5 (not yet released) has built in language server support. For a quick start configuration +of rust-analyzer, use [neovim/nvim-lsp](https://github.com/neovim/nvim-lsp#rust_analyzer). +Once `neovim/nvim-lsp` is installed, you can use `call nvim_lsp#setup("rust_analyzer", {})` +or `lua require'nvim_lsp'.rust_analyzer.setup({})` to quickly get set up. + ## Sublime Text 3 @@ -208,4 +208,19 @@ Installation: * You can now invoke the command palette and type LSP enable to locally/globally enable the rust-analyzer LSP (type LSP enable, then choose either locally or globally, then select rust-analyzer) -* Note that `ra_lsp_server` binary must be in `$PATH` for this to work. If it's not the case, you can specify full path to the binary, which is typically `.cargo/bin/ra_lsp_server`. +### Setting up the `PATH` variable + +On Unix systems, `rustup` adds `~/.cargo/bin` to `PATH` by modifying the shell's +startup file. Depending on your configuration, your Desktop Environment might not +actually load it. If you find that `rust-analyzer` only runs when starting the +editor from the terminal, you will have to set up your `PATH` variable manually. + +There are a couple of ways to do that: + +- for Code, set `rust-analyzer.raLspServerPath` to `~/.cargo/bin` (the `~` is + automatically resolved by the extension) +- copy the binary to a location that is already in `PATH`, e.g. `/usr/local/bin` +- on Linux, use PAM to configure the `PATH` variable, by e.g. putting + `PATH DEFAULT=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:@{HOME}/.cargo/bin:@{HOME}/.local/bin` + in your `~/.pam_environment` file; note that this might interfere with other + defaults set by the system administrator via `/etc/environment`. diff --git a/docs/user/assists.md b/docs/user/assists.md index 6f4c30bee3..334ba450f5 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -3,6 +3,24 @@ Cursor position or selection is signified by `┃` character. +## `add_custom_impl` + +Adds impl block for derived trait. + +```rust +// BEFORE +#[derive(Deb┃ug, Display)] +struct S; + +// AFTER +#[derive(Display)] +struct S; + +impl Debug for S { + +} +``` + ## `add_derive` Adds a new `#[derive()]` clause to a struct or enum. diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index a41497a239..4c5c13646c 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -60,9 +60,9 @@ "dev": true }, "@types/node": { - "version": "10.14.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.13.tgz", - "integrity": "sha512-yN/FNNW1UYsRR1wwAoyOwqvDuLDtVXnaJTZ898XIw/Q5cCaeVAlVwvsmXLX5PuiScBYwZsZU4JYSHB3TvfdwvQ==", + "version": "12.12.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.21.tgz", + "integrity": "sha512-8sRGhbpU+ck1n0PGAUgVrWrWdjSW2aqNeyC15W88GRsMpSwzv6RJGlLhE7s2RhVSOdyDmxbqlWSeThq4/7xqlA==", "dev": true }, "@types/resolve": { @@ -81,9 +81,9 @@ "dev": true }, "@types/vscode": { - "version": "1.37.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.37.0.tgz", - "integrity": "sha512-PRfeuqYuzk3vjf+puzxltIUWC+AhEGYpFX29/37w30DQSQnpf5AgMVf7GDBAdmTbWTBou+EMFz/Ne6XCM/KxzQ==", + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.41.0.tgz", + "integrity": "sha512-7SfeY5u9jgiELwxyLB3z7l6l/GbN9CqpCQGkcRlB7tKRFBxzbz2PoBfGrLxI1vRfUCIq5+hg5vtDHExwq5j3+A==", "dev": true }, "acorn": { @@ -131,6 +131,12 @@ "sprintf-js": "~1.0.2" } }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, "azure-devops-node-api": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-7.2.0.tgz", @@ -198,6 +204,17 @@ "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "cheerio": { @@ -215,22 +232,44 @@ } }, "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -247,9 +286,9 @@ "dev": true }, "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, "concat-map": { @@ -258,19 +297,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "css-select": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", @@ -290,12 +316,12 @@ "dev": true }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "decamelize": { @@ -304,6 +330,12 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -372,15 +404,6 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", @@ -388,23 +411,27 @@ "dev": true }, "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.3.tgz", + "integrity": "sha512-WtY7Fx5LiOnSYgF5eg/1T+GONaGmpvpPdCpSnYij+U2gDTL0UPfWrhDw7b2IYb+9NQJsYpCA0wOQvZfsd6YwRw==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", + "has-symbols": "^1.0.1", "is-callable": "^1.1.4", "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" } }, "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -439,6 +466,16 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "eslint-plugin-prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz", + "integrity": "sha512-CStQYJgALoQBw3FsBzH0VOVDRnJ/ZimUlpLm226U8qgqYJfPOY/CPK6wyRInMxh73HSKg5wyRwdS4BVYYHwokA==", + "dev": true, + "requires": { + "fast-diff": "^1.1.1", + "jest-docblock": "^21.0.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -452,25 +489,16 @@ "dev": true }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true }, "fd-slicer": { "version": "1.1.0", @@ -517,19 +545,10 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -562,9 +581,9 @@ "dev": true }, "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, "he": { @@ -595,12 +614,29 @@ "requires": { "agent-base": "4", "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "https-proxy-agent": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.3.tgz", - "integrity": "sha512-Ytgnz23gm2DVftnzqRRz2dOXZbGd2uiajSw/95bPp6v53zPRspQjLm/AfBgqbJ2qfeRXWIOMVLpp86+/5yX39Q==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { "agent-base": "^4.3.0", @@ -618,9 +654,9 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "interpret": { @@ -629,16 +665,10 @@ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", "dev": true }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", "dev": true }, "is-callable": { @@ -683,19 +713,13 @@ "has": "^1.0.1" } }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "^1.0.1" } }, "isexe": { @@ -704,6 +728,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "jest-docblock": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz", + "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -720,19 +750,11 @@ "esprima": "^4.0.0" } }, - "jsonc-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.0.tgz", - "integrity": "sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA==" - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true }, "linkify-it": { "version": "2.2.0", @@ -768,6 +790,11 @@ "chalk": "^2.0.1" } }, + "lookpath": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lookpath/-/lookpath-1.0.4.tgz", + "integrity": "sha512-xVFrWlfo7n8VZs1YjBWKkbSIJU7DKE/0Mep62KeT94V1Ui1IY9w5fXfgiCsDIDZkakIYSXSeaW2FLbCqfw9/Cw==" + }, "magic-string": { "version": "0.25.3", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", @@ -777,15 +804,6 @@ "sourcemap-codec": "^1.4.4" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, "markdown-it": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", @@ -805,29 +823,12 @@ "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", "dev": true }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -853,9 +854,9 @@ } }, "mocha": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.0.tgz", - "integrity": "sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.2.tgz", + "integrity": "sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A==", "dev": true, "requires": { "ansi-colors": "3.2.3", @@ -878,20 +879,11 @@ "supports-color": "6.0.0", "which": "1.3.1", "wide-align": "1.1.3", - "yargs": "13.2.2", - "yargs-parser": "13.0.0", - "yargs-unparser": "1.5.0" + "yargs": "13.3.0", + "yargs-parser": "13.1.1", + "yargs-unparser": "1.6.0" }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -905,28 +897,13 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "mute-stream": { @@ -935,12 +912,6 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "node-environment-flags": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", @@ -951,15 +922,6 @@ "semver": "^5.7.0" } }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -969,10 +931,10 @@ "boolbase": "~1.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", "dev": true }, "object-keys": { @@ -1024,17 +986,6 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -1051,24 +1002,6 @@ "os-tmpdir": "^1.0.0" } }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, "p-limit": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", @@ -1123,12 +1056,6 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", @@ -1142,21 +1069,11 @@ "dev": true }, "prettier": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", - "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -1207,6 +1124,12 @@ "path-parse": "^1.0.6" } }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -1217,9 +1140,9 @@ } }, "rollup": { - "version": "1.23.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.23.1.tgz", - "integrity": "sha512-95C1GZQpr/NIA0kMUQmSjuMDQ45oZfPgDBcN0yZwBG7Kee//m7H68vgIyg+SPuyrTZ5PrXfyLK80OzXeKG5dAA==", + "version": "1.27.13", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.27.13.tgz", + "integrity": "sha512-hDi7M07MpmNSDE8YVwGVFA8L7n8jTLJ4lG65nMAijAyqBe//rtu4JdxjUBE7JqXfdpqxqDTbCDys9WcqdpsQvw==", "dev": true, "requires": { "@types/estree": "*", @@ -1281,6 +1204,16 @@ } } }, + "rollup-plugin-sourcemaps": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz", + "integrity": "sha1-YhJaqUCHqt97g+9N+vYptHMTXoc=", + "dev": true, + "requires": { + "rollup-pluginutils": "^2.0.1", + "source-map-resolve": "^0.5.0" + } + }, "rollup-plugin-typescript": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rollup-plugin-typescript/-/rollup-plugin-typescript-1.0.1.tgz", @@ -1318,14 +1251,15 @@ "dev": true }, "seedrandom": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.1.tgz", - "integrity": "sha512-1/02Y/rUeU1CJBAGLebiC5Lbo5FnB22gQbIFFYTLkwvp1xdABZJH1sn4ZT1MzXmPpzv+Rf/Lu2NcsLJiK4rcDg==" + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true }, "set-blocking": { "version": "2.0.0", @@ -1333,21 +1267,6 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, "shelljs": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", @@ -1378,10 +1297,23 @@ } } }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, "sourcemap-codec": { @@ -1406,6 +1338,26 @@ "strip-ansi": "^4.0.0" } }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -1424,12 +1376,6 @@ "ansi-regex": "^3.0.0" } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -1437,9 +1383,9 @@ "dev": true }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -1461,16 +1407,16 @@ "dev": true }, "tslint": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.18.0.tgz", - "integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", + "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", "chalk": "^2.3.0", "commander": "^2.12.1", - "diff": "^3.2.0", + "diff": "^4.0.1", "glob": "^7.1.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", @@ -1479,6 +1425,14 @@ "semver": "^5.3.0", "tslib": "^1.8.0", "tsutils": "^2.29.0" + }, + "dependencies": { + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + } } }, "tslint-config-prettier": { @@ -1487,6 +1441,17 @@ "integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==", "dev": true }, + "tslint-plugin-prettier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tslint-plugin-prettier/-/tslint-plugin-prettier-2.0.1.tgz", + "integrity": "sha512-4FX9JIx/1rKHIPJNfMb+ooX1gPk5Vg3vNi7+dyFYpLO+O57F4g+b/fo1+W/G0SUOkBLHB/YKScxjX/P+7ZT/Tw==", + "dev": true, + "requires": { + "eslint-plugin-prettier": "^2.2.0", + "lines-and-columns": "^1.1.6", + "tslib": "^1.7.1" + } + }, "tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", @@ -1513,9 +1478,9 @@ } }, "typescript": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", - "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", + "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", "dev": true }, "uc.micro": { @@ -1530,6 +1495,12 @@ "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "dev": true }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, "url-join": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/url-join/-/url-join-1.1.0.tgz", @@ -1543,9 +1514,9 @@ "dev": true }, "vsce": { - "version": "1.67.1", - "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.67.1.tgz", - "integrity": "sha512-Y/0fnfaLs2cCfytTGmy4Cp1bf9BaxHO7020YePdUwxjAlPlZ9+lm74M9yEFEWXTIug0L0sMax1WMz0TnozIqxg==", + "version": "1.71.0", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.71.0.tgz", + "integrity": "sha512-7k+LPC4oJYPyyxs0a5nh4A8CleQ6+2EMPiAiX/bDyN+PmwJFm2FFPqLRxdIsIWfFnkW4ZMQBf10+W62dCRd9kQ==", "dev": true, "requires": { "azure-devops-node-api": "^7.2.0", @@ -1571,41 +1542,48 @@ } }, "vscode-jsonrpc": { - "version": "4.1.0-next.3", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.3.tgz", - "integrity": "sha512-Z6oxBiMks2+UADV1QHXVooSakjyhI+eHTnXzDyVvVMmegvSfkXk2w6mPEdSkaNHFBdtWW7n20H1yw2nA3A17mg==" + "version": "5.0.0-next.5", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.5.tgz", + "integrity": "sha512-k9akfglxWgr0dtLNscq2uBq48XJwnhf4EaDxn05KQowRwR0DkNML0zeYqFRLtXZe6x5vpL5ppyu4o6GqL+23YQ==" }, "vscode-languageclient": { - "version": "5.3.0-next.4", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-5.3.0-next.4.tgz", - "integrity": "sha512-RODuzXErVpJRSgHv+Xei8fwQtZ/iZOWPCqlLl07NTtkzgTAepJf9r4EioZVuTviGJ5DEJ9xs0bjrit8shKtW6Q==", + "version": "6.0.0-next.9", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.0.0-next.9.tgz", + "integrity": "sha512-NEpeeFM9FKrrRqlBHXGfwpkhtnjruDz3zfFBP+Cymr10qigAEtE/JsODJsIG/ErGqjh3/JXxu8SUOVTGu5oK+w==", "requires": { - "semver": "^5.5.0", - "vscode-languageserver-protocol": "3.15.0-next.4" + "semver": "^6.3.0", + "vscode-languageserver-protocol": "^3.15.0-next.14" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } } }, "vscode-languageserver-protocol": { - "version": "3.15.0-next.4", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.4.tgz", - "integrity": "sha512-4AgisQ8GWa3irdRu3/UNr3brcSSm0oobmoV1eSOnV7JM32lYyXDnSKB7RuTTXvaAjD/0xQJLEGhkyGHS5gbywA==", + "version": "3.15.0-next.14", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.14.tgz", + "integrity": "sha512-xUwwno6Q6RFd2Z2EWV9D3dQlsKPnHyiZMNWq+EC7JJdp2WH1gRlD+KPX4UGRCnJK0WI5omqHV313IESPwRY5xA==", "requires": { - "vscode-jsonrpc": "^4.1.0-next.1", - "vscode-languageserver-types": "3.15.0-next.1" + "vscode-jsonrpc": "^5.0.0-next.5", + "vscode-languageserver-types": "^3.15.0-next.9" } }, "vscode-languageserver-types": { - "version": "3.15.0-next.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.1.tgz", - "integrity": "sha512-R0kzmaI8gOGEoU7b9huYQAzgZzRQ/5Q8HKjsIUdfz0MjXcBZ4tr1ik1So1p1O5kGrI1VTCd22Fw/wI7ECGoIPw==" + "version": "3.15.0-next.9", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.9.tgz", + "integrity": "sha512-Rl/8qJ6932nrHCdPn+9y0x08uLVQaSLRG+U4JzhyKpWU4eJbVaDRoAcz1Llj7CErJGbPr6kdBvShPy5fRfR+Uw==" }, "vscode-test": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.2.0.tgz", - "integrity": "sha512-aowqgc8gZe0eflzVUXsBjBrlsJ8eC35kfgfSEeHu9PKA1vQKm/3rVK43TlbxGue8hKtZBElNAJ5QuYklR/vLJA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.3.0.tgz", + "integrity": "sha512-LddukcBiSU2FVTDr3c1D8lwkiOvwlJdDL2hqVbn6gIz+rpTqUCkMZSKYm94Y1v0WXlHSDQBsXyY+tchWQgGVsw==", "dev": true, "requires": { "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", + "https-proxy-agent": "^2.2.4", "rimraf": "^2.6.3" } }, @@ -1634,48 +1612,40 @@ } }, "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" }, "dependencies": { "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^4.1.0" } } } @@ -1693,22 +1663,21 @@ "dev": true }, "yargs": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", - "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", "dev": true, "requires": { - "cliui": "^4.0.0", + "cliui": "^5.0.0", "find-up": "^3.0.0", "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.0.0" + "yargs-parser": "^13.1.1" }, "dependencies": { "ansi-regex": { @@ -1740,9 +1709,9 @@ } }, "yargs-parser": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", - "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -1750,58 +1719,14 @@ } }, "yargs-unparser": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", - "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", "dev": true, "requires": { "flat": "^4.1.0", - "lodash": "^4.17.11", - "yargs": "^12.0.5" - }, - "dependencies": { - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "lodash": "^4.17.15", + "yargs": "^13.3.0" } }, "yauzl": { diff --git a/editors/code/package.json b/editors/code/package.json index e21dfa1745..8e7046418b 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -13,7 +13,7 @@ "Other" ], "engines": { - "vscode": "^1.37.0" + "vscode": "^1.41.0" }, "scripts": { "vscode:prepublish": "npm run compile", @@ -27,33 +27,37 @@ "travis": "npm run compile && npm run test && npm run lint && npm run prettier -- --write && git diff --exit-code" }, "prettier": { + "singleQuote": true, "tabWidth": 4, - "singleQuote": true + "trailingComma": "all" }, "dependencies": { - "seedrandom": "^3.0.1", - "vscode-languageclient": "^5.3.0-next.4", + "lookpath": "^1.0.4", + "seedrandom": "^3.0.5", + "vscode-languageclient": "^6.0.0-next.9", "jsonc-parser": "^2.1.0" }, "devDependencies": { "@types/glob": "^7.1.1", "@types/mocha": "^5.2.7", - "@types/node": "^10.14.13", + "@types/node": "^12.12.21", "@types/seedrandom": "^2.4.28", - "@types/vscode": "^1.37.0", - "glob": "^7.1.4", - "mocha": "^6.2.0", - "prettier": "^1.18.2", - "rollup": "^1.23.1", + "@types/vscode": "^1.41.0", + "glob": "^7.1.6", + "mocha": "^6.2.2", + "prettier": "^1.19.1", + "rollup": "^1.27.13", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-sourcemaps": "^0.4.2", "rollup-plugin-typescript": "^1.0.1", "shx": "^0.3.1", - "tslint": "^5.18.0", + "tslint": "^5.20.1", "tslint-config-prettier": "^1.18.0", - "typescript": "^3.5.3", - "vsce": "^1.67.0", - "vscode-test": "^1.2.0" + "tslint-plugin-prettier": "^2.0.1", + "typescript": "^3.7.3", + "vsce": "^1.71.0", + "vscode-test": "^1.3.0" }, "activationEvents": [ "onLanguage:rust", @@ -279,7 +283,7 @@ }, "rust-analyzer.useClientWatching": { "type": "boolean", - "default": false, + "default": true, "description": "client provided file watching instead of notify watching." }, "rust-analyzer.cargo-watch.arguments": { @@ -297,6 +301,11 @@ "description": "A list of patterns for cargo-watch to ignore (will be passed as `--ignore`)", "default": [] }, + "rust-analyzer.cargo-watch.allTargets": { + "type": "boolean", + "description": "Check all targets and tests (will be passed as `--all-targets`)", + "default": true + }, "rust-analyzer.trace.server": { "type": "string", "scope": "window", @@ -338,6 +347,21 @@ "type": "number", "default": 20, "description": "Maximum length for inlay hints" + }, + "rust-analyzer.cargoFeatures.noDefaultFeatures": { + "type": "boolean", + "default": false, + "description": "Do not activate the `default` feature" + }, + "rust-analyzer.cargoFeatures.allFeatures": { + "type": "boolean", + "default": true, + "description": "Activate all available features" + }, + "rust-analyzer.cargoFeatures.features": { + "type": "array", + "default": [], + "description": "List of features to activate" } } }, @@ -358,6 +382,20 @@ "column": 3 } ] + }, + { + "name": "rustc-json", + "patterns": [ + { + "regexp": "^.*\"message\":{\"message\":\"([^\"]*).*?\"file_name\":\"([^\"]+).*?\"line_start\":(\\d+).*?\"line_end\":(\\d+).*?\"column_start\":(\\d+).*?\"column_end\":(\\d+).*}$", + "message": 1, + "file": 2, + "line": 3, + "endLine": 4, + "column": 5, + "endColumn": 6 + } + ] } ], "problemMatchers": [ @@ -369,6 +407,14 @@ ], "pattern": "$rustc" }, + { + "name": "rustc-json", + "fileLocation": [ + "relative", + "${workspaceRoot}" + ], + "pattern": "$rustc-json" + }, { "name": "rustc-watch", "fileLocation": [ @@ -482,6 +528,33 @@ "highContrast": "#B5CEA8" } }, + { + "id": "ralsp.literal.numeric", + "description": "Color for numeric literals", + "defaults": { + "dark": "#BECEA8", + "light": "#09885A", + "highContrast": "#B5CEA8" + } + }, + { + "id": "ralsp.literal.char", + "description": "Color for character literals", + "defaults": { + "dark": "#BECEA8", + "light": "#09885A", + "highContrast": "#B5CEA8" + } + }, + { + "id": "ralsp.literal.byte", + "description": "Color for byte literals", + "defaults": { + "dark": "#BECEA8", + "light": "#09885A", + "highContrast": "#B5CEA8" + } + }, { "id": "ralsp.macro", "description": "Color for macros", @@ -502,7 +575,43 @@ }, { "id": "ralsp.type", - "description": "Color for types", + "description": "Color for other types (traits, aliases..)", + "defaults": { + "dark": "#4EC9B0", + "light": "#267F99", + "highContrast": "#4EC9B0" + } + }, + { + "id": "ralsp.type.builtin", + "description": "Color for built-in types (&str, bool, u16, u32)", + "defaults": { + "dark": "#4EC9B0", + "light": "#267F99", + "highContrast": "#4EC9B0" + } + }, + { + "id": "ralsp.type.lifetime", + "description": "Color for lifetimes parameters", + "defaults": { + "dark": "#4EC9B0", + "light": "#267F99", + "highContrast": "#4EC9B0" + } + }, + { + "id": "ralsp.type.self", + "description": "Color for `Self` param type", + "defaults": { + "dark": "#4EC9B0", + "light": "#267F99", + "highContrast": "#4EC9B0" + } + }, + { + "id": "ralsp.type.param", + "description": "Color for type parameters", "defaults": { "dark": "#4EC9B0", "light": "#267F99", diff --git a/editors/code/rollup.config.js b/editors/code/rollup.config.js index a023b8c3fe..1b222bbe78 100644 --- a/editors/code/rollup.config.js +++ b/editors/code/rollup.config.js @@ -1,12 +1,14 @@ import typescript from 'rollup-plugin-typescript'; import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; +import sourcemaps from 'rollup-plugin-sourcemaps' import nodeBuiltins from 'builtin-modules'; export default { input: './src/extension.ts', plugins: [ typescript(), + sourcemaps(), resolve(), commonjs({ namedExports: { @@ -22,6 +24,7 @@ export default { ], output: { file: './bundle/extension.js', + sourcemap: true, format: 'cjs', } }; diff --git a/editors/code/src/commands/analyzer_status.ts b/editors/code/src/commands/analyzer_status.ts index 63f82c92d8..2777ced247 100644 --- a/editors/code/src/commands/analyzer_status.ts +++ b/editors/code/src/commands/analyzer_status.ts @@ -9,7 +9,7 @@ export class TextDocumentContentProvider public syntaxTree: string = 'Not available'; public provideTextDocumentContent( - uri: vscode.Uri + _uri: vscode.Uri, ): vscode.ProviderResult { const editor = vscode.window.activeTextEditor; if (editor == null) { @@ -17,7 +17,7 @@ export class TextDocumentContentProvider } return Server.client.sendRequest( 'rust-analyzer/analyzerStatus', - null + null, ); } @@ -35,8 +35,8 @@ export function makeCommand(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.workspace.registerTextDocumentContentProvider( 'rust-analyzer-status', - textDocumentContentProvider - ) + textDocumentContentProvider, + ), ); context.subscriptions.push({ @@ -44,21 +44,21 @@ export function makeCommand(context: vscode.ExtensionContext) { if (poller != null) { clearInterval(poller); } - } + }, }); return async function handle() { if (poller == null) { poller = setInterval( () => textDocumentContentProvider.eventEmitter.fire(statusUri), - 1000 + 1000, ); } const document = await vscode.workspace.openTextDocument(statusUri); return vscode.window.showTextDocument( document, vscode.ViewColumn.Two, - true + true, ); }; } diff --git a/editors/code/src/commands/apply_source_change.ts b/editors/code/src/commands/apply_source_change.ts index dcd074b8bc..8167398b1c 100644 --- a/editors/code/src/commands/apply_source_change.ts +++ b/editors/code/src/commands/apply_source_change.ts @@ -11,7 +11,7 @@ export interface SourceChange { export async function handle(change: SourceChange) { const wsEdit = Server.client.protocol2CodeConverter.asWorkspaceEdit( - change.workspaceEdit + change.workspaceEdit, ); let created; let moved; @@ -33,10 +33,10 @@ export async function handle(change: SourceChange) { await vscode.window.showTextDocument(doc); } else if (toReveal) { const uri = Server.client.protocol2CodeConverter.asUri( - toReveal.textDocument.uri + toReveal.textDocument.uri, ); const position = Server.client.protocol2CodeConverter.asPosition( - toReveal.position + toReveal.position, ); const editor = vscode.window.activeTextEditor; if (!editor || editor.document.uri.toString() !== uri.toString()) { @@ -48,7 +48,7 @@ export async function handle(change: SourceChange) { editor.selection = new vscode.Selection(position, position); editor.revealRange( new vscode.Range(position, position), - vscode.TextEditorRevealType.Default + vscode.TextEditorRevealType.Default, ); } } diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts index 59d4ba97a2..ac62bdd48d 100644 --- a/editors/code/src/commands/cargo_watch.ts +++ b/editors/code/src/commands/cargo_watch.ts @@ -9,13 +9,13 @@ import { StatusDisplay } from './watch_status'; import { mapRustDiagnosticToVsCode, - RustDiagnostic + RustDiagnostic, } from '../utils/diagnostics/rust'; import SuggestedFixCollection from '../utils/diagnostics/SuggestedFixCollection'; import { areDiagnosticsEqual } from '../utils/diagnostics/vscode'; export async function registerCargoWatchProvider( - subscriptions: vscode.Disposable[] + subscriptions: vscode.Disposable[], ): Promise { let cargoExists = false; @@ -30,7 +30,7 @@ export async function registerCargoWatchProvider( if (!cargoExists) { vscode.window.showErrorMessage( - `Couldn\'t find \'Cargo.toml\' at ${cargoTomlPath}` + `Couldn\'t find \'Cargo.toml\' at ${cargoTomlPath}`, ); return; } @@ -52,13 +52,13 @@ export class CargoWatchProvider implements vscode.Disposable { constructor() { this.diagnosticCollection = vscode.languages.createDiagnosticCollection( - 'rustc' + 'rustc', ); this.statusDisplay = new StatusDisplay( - Server.config.cargoWatchOptions.command + Server.config.cargoWatchOptions.command, ); this.outputChannel = vscode.window.createOutputChannel( - 'Cargo Watch Trace' + 'Cargo Watch Trace', ); // Track `rustc`'s suggested fixes so we can convert them to code actions @@ -68,22 +68,24 @@ export class CargoWatchProvider implements vscode.Disposable { this.suggestedFixCollection, { providedCodeActionKinds: - SuggestedFixCollection.PROVIDED_CODE_ACTION_KINDS - } + SuggestedFixCollection.PROVIDED_CODE_ACTION_KINDS, + }, ); } public start() { if (this.cargoProcess) { vscode.window.showInformationMessage( - 'Cargo Watch is already running' + 'Cargo Watch is already running', ); return; } let args = - Server.config.cargoWatchOptions.command + - ' --all-targets --message-format json'; + Server.config.cargoWatchOptions.command + ' --message-format json'; + if (Server.config.cargoWatchOptions.allTargets) { + args += ' --all-targets'; + } if (Server.config.cargoWatchOptions.command.length > 0) { // Excape the double quote string: args += ' ' + Server.config.cargoWatchOptions.arguments; @@ -95,7 +97,7 @@ export class CargoWatchProvider implements vscode.Disposable { const ignoreFlags = Server.config.cargoWatchOptions.ignore.reduce( (flags, pattern) => [...flags, '--ignore', pattern], - [] as string[] + [] as string[], ); // Start the cargo watch with json message @@ -105,12 +107,17 @@ export class CargoWatchProvider implements vscode.Disposable { { stdio: ['ignore', 'pipe', 'pipe'], cwd: vscode.workspace.rootPath, - windowsVerbatimArguments: true - } + windowsVerbatimArguments: true, + }, ); + if (!this.cargoProcess) { + vscode.window.showErrorMessage('Cargo Watch failed to start'); + return; + } + const stdoutData = new LineBuffer(); - this.cargoProcess.stdout.on('data', (s: string) => { + this.cargoProcess.stdout?.on('data', (s: string) => { stdoutData.processOutput(s, line => { this.logInfo(line); try { @@ -122,7 +129,7 @@ export class CargoWatchProvider implements vscode.Disposable { }); const stderrData = new LineBuffer(); - this.cargoProcess.stderr.on('data', (s: string) => { + this.cargoProcess.stderr?.on('data', (s: string) => { stderrData.processOutput(s, line => { this.logError('Error on cargo-watch : {\n' + line + '}\n'); }); @@ -130,7 +137,7 @@ export class CargoWatchProvider implements vscode.Disposable { this.cargoProcess.on('error', (err: Error) => { this.logError( - 'Error on cargo-watch process : {\n' + err.message + '}\n' + 'Error on cargo-watch process : {\n' + err.message + '}\n', ); }); @@ -223,12 +230,12 @@ export class CargoWatchProvider implements vscode.Disposable { const fileUri = location.uri; const diagnostics: vscode.Diagnostic[] = [ - ...(this.diagnosticCollection!.get(fileUri) || []) + ...(this.diagnosticCollection!.get(fileUri) || []), ]; // If we're building multiple targets it's possible we've already seen this diagnostic const isDuplicate = diagnostics.some(d => - areDiagnosticsEqual(d, diagnostic) + areDiagnosticsEqual(d, diagnostic), ); if (isDuplicate) { return; @@ -241,7 +248,7 @@ export class CargoWatchProvider implements vscode.Disposable { for (const suggestedFix of suggestedFixes) { this.suggestedFixCollection.addSuggestedFixForDiagnostic( suggestedFix, - diagnostic + diagnostic, ); } @@ -249,7 +256,7 @@ export class CargoWatchProvider implements vscode.Disposable { vscode.commands.executeCommand( 'vscode.executeCodeActionProvider', fileUri, - diagnostic.range + diagnostic.range, ); } } diff --git a/editors/code/src/commands/expand_macro.ts b/editors/code/src/commands/expand_macro.ts index 34e0c8fb33..17c78280a8 100644 --- a/editors/code/src/commands/expand_macro.ts +++ b/editors/code/src/commands/expand_macro.ts @@ -3,7 +3,7 @@ import { Position, TextDocumentIdentifier } from 'vscode-languageclient'; import { Server } from '../server'; export const expandMacroUri = vscode.Uri.parse( - 'rust-analyzer://expandMacro/[EXPANSION].rs' + 'rust-analyzer://expandMacro/[EXPANSION].rs', ); export class ExpandMacroContentProvider @@ -11,7 +11,7 @@ export class ExpandMacroContentProvider public eventEmitter = new vscode.EventEmitter(); public provideTextDocumentContent( - uri: vscode.Uri + _uri: vscode.Uri, ): vscode.ProviderResult { async function handle() { const editor = vscode.window.activeTextEditor; @@ -22,11 +22,11 @@ export class ExpandMacroContentProvider const position = editor.selection.active; const request: MacroExpandParams = { textDocument: { uri: editor.document.uri.toString() }, - position + position, }; const expanded = await Server.client.sendRequest( 'rust-analyzer/expandMacro', - request + request, ); if (expanded == null) { @@ -58,7 +58,7 @@ export function createHandle(provider: ExpandMacroContentProvider) { return vscode.window.showTextDocument( document, vscode.ViewColumn.Two, - true + true, ); }; } diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index 2ade6d331a..13a696758b 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts @@ -19,5 +19,5 @@ export { runnables, syntaxTree, onEnter, - inlayHints + inlayHints, }; diff --git a/editors/code/src/commands/inlay_hints.ts b/editors/code/src/commands/inlay_hints.ts index 0dbdd94fba..ac7dcce604 100644 --- a/editors/code/src/commands/inlay_hints.ts +++ b/editors/code/src/commands/inlay_hints.ts @@ -15,8 +15,8 @@ interface InlayHint { const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ after: { - color: new vscode.ThemeColor('ralsp.inlayHint') - } + color: new vscode.ThemeColor('ralsp.inlayHint'), + }, }); export class HintsUpdater { @@ -26,13 +26,13 @@ export class HintsUpdater { if (this.displayHints !== displayHints) { this.displayHints = displayHints; return this.refreshVisibleEditorsHints( - displayHints ? undefined : [] + displayHints ? undefined : [], ); } } public async refreshHintsForVisibleEditors( - cause?: TextDocumentChangeEvent + cause?: TextDocumentChangeEvent, ): Promise { if (!this.displayHints) { return; @@ -48,21 +48,21 @@ export class HintsUpdater { } private async refreshVisibleEditorsHints( - newDecorations?: vscode.DecorationOptions[] + newDecorations?: vscode.DecorationOptions[], ) { const promises: Array> = []; for (const rustEditor of vscode.window.visibleTextEditors.filter( - editor => this.isRustDocument(editor.document) + editor => this.isRustDocument(editor.document), )) { if (newDecorations !== undefined) { promises.push( Promise.resolve( rustEditor.setDecorations( typeHintDecorationType, - newDecorations - ) - ) + newDecorations, + ), + ), ); } else { promises.push(this.updateDecorationsFromServer(rustEditor)); @@ -79,7 +79,7 @@ export class HintsUpdater { } private async updateDecorationsFromServer( - editor: TextEditor + editor: TextEditor, ): Promise { const newHints = await this.queryHints(editor.document.uri.toString()); if (newHints !== null) { @@ -87,20 +87,20 @@ export class HintsUpdater { range: hint.range, renderOptions: { after: { - contentText: `: ${hint.label}` - } - } + contentText: `: ${hint.label}`, + }, + }, })); return editor.setDecorations( typeHintDecorationType, - newDecorations + newDecorations, ); } } private async queryHints(documentUri: string): Promise { const request: InlayHintsParams = { - textDocument: { uri: documentUri } + textDocument: { uri: documentUri }, }; const client = Server.client; return client @@ -108,8 +108,8 @@ export class HintsUpdater { .then(() => client.sendRequest( 'rust-analyzer/inlayHints', - request - ) + request, + ), ); } } diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts index 0d4b12f4d8..134ddc8016 100644 --- a/editors/code/src/commands/join_lines.ts +++ b/editors/code/src/commands/join_lines.ts @@ -4,7 +4,7 @@ import { Range, TextDocumentIdentifier } from 'vscode-languageclient'; import { Server } from '../server'; import { handle as applySourceChange, - SourceChange + SourceChange, } from './apply_source_change'; interface JoinLinesParams { @@ -19,11 +19,11 @@ export async function handle() { } const request: JoinLinesParams = { range: Server.client.code2ProtocolConverter.asRange(editor.selection), - textDocument: { uri: editor.document.uri.toString() } + textDocument: { uri: editor.document.uri.toString() }, }; const change = await Server.client.sendRequest( 'rust-analyzer/joinLines', - request + request, ); await applySourceChange(change); } diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts index d86faf4051..364208cc75 100644 --- a/editors/code/src/commands/matching_brace.ts +++ b/editors/code/src/commands/matching_brace.ts @@ -17,15 +17,15 @@ export async function handle() { textDocument: { uri: editor.document.uri.toString() }, offsets: editor.selections.map(s => { return Server.client.code2ProtocolConverter.asPosition(s.active); - }) + }), }; const response = await Server.client.sendRequest( 'rust-analyzer/findMatchingBrace', - request + request, ); editor.selections = editor.selections.map((sel, idx) => { const active = Server.client.protocol2CodeConverter.asPosition( - response[idx] + response[idx], ); const anchor = sel.isEmpty ? active : sel.anchor; return new vscode.Selection(anchor, active); diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts index 16dcb70c81..772c64b3c7 100644 --- a/editors/code/src/commands/on_enter.ts +++ b/editors/code/src/commands/on_enter.ts @@ -3,7 +3,7 @@ import * as lc from 'vscode-languageclient'; import { Server } from '../server'; import { handle as applySourceChange, - SourceChange + SourceChange, } from './apply_source_change'; export async function handle(event: { text: string }): Promise { @@ -18,12 +18,12 @@ export async function handle(event: { text: string }): Promise { const request: lc.TextDocumentPositionParams = { textDocument: { uri: editor.document.uri.toString() }, position: Server.client.code2ProtocolConverter.asPosition( - editor.selection.active - ) + editor.selection.active, + ), }; const change = await Server.client.sendRequest( 'rust-analyzer/onEnter', - request + request, ); if (!change) { return false; diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts index 9d30b7b59a..ad49e1bdbb 100644 --- a/editors/code/src/commands/parent_module.ts +++ b/editors/code/src/commands/parent_module.ts @@ -11,12 +11,12 @@ export async function handle() { const request: lc.TextDocumentPositionParams = { textDocument: { uri: editor.document.uri.toString() }, position: Server.client.code2ProtocolConverter.asPosition( - editor.selection.active - ) + editor.selection.active, + ), }; const response = await Server.client.sendRequest( 'rust-analyzer/parentModule', - request + request, ); const loc = response[0]; if (loc == null) { diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index ac59bf60db..cf980e2578 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -46,17 +46,17 @@ function createTask(spec: Runnable): vscode.Task { label: spec.label, command: spec.bin, args: spec.args, - env: spec.env + env: spec.env, }; const execOption: vscode.ShellExecutionOptions = { cwd: spec.cwd || '.', - env: definition.env + env: definition.env, }; const exec = new vscode.ShellExecution( definition.command, definition.args, - execOption + execOption, ); const f = vscode.workspace.workspaceFolders![0]; @@ -66,30 +66,30 @@ function createTask(spec: Runnable): vscode.Task { definition.label, TASK_SOURCE, exec, - ['$rustc'] + ['$rustc'], ); t.presentationOptions.clear = true; return t; } let prevRunnable: RunnableQuickPick | undefined; -export async function handle() { +export async function handle(): Promise { const editor = vscode.window.activeTextEditor; if (editor == null || editor.document.languageId !== 'rust') { return; } const textDocument: lc.TextDocumentIdentifier = { - uri: editor.document.uri.toString() + uri: editor.document.uri.toString(), }; const params: RunnablesParams = { textDocument, position: Server.client.code2ProtocolConverter.asPosition( - editor.selection.active - ) + editor.selection.active, + ), }; const runnables = await Server.client.sendRequest( 'rust-analyzer/runnables', - params + params, ); const items: RunnableQuickPick[] = []; if (prevRunnable) { @@ -105,12 +105,14 @@ export async function handle() { items.push(new RunnableQuickPick(r)); } const item = await vscode.window.showQuickPick(items); - if (item) { - item.detail = 'rerun'; - prevRunnable = item; - const task = createTask(item.runnable); - return await vscode.tasks.executeTask(task); + if (!item) { + return; } + + item.detail = 'rerun'; + prevRunnable = item; + const task = createTask(item.runnable); + return await vscode.tasks.executeTask(task); } export async function handleSingle(runnable: Runnable) { @@ -124,7 +126,7 @@ export async function handleSingle(runnable: Runnable) { task.presentationOptions = { reveal: vscode.TaskRevealKind.Always, panel: vscode.TaskPanelKind.Dedicated, - clear: true + clear: true, }; return vscode.tasks.executeTask(task); @@ -136,7 +138,7 @@ export async function handleSingle(runnable: Runnable) { * that, when accepted, allow us to `cargo install cargo-watch` and then run it. */ export async function interactivelyStartCargoWatch( - context: vscode.ExtensionContext + context: vscode.ExtensionContext, ): Promise { if (Server.config.cargoWatchOptions.enableOnStartup === 'disabled') { return; @@ -146,7 +148,7 @@ export async function interactivelyStartCargoWatch( const watch = await vscode.window.showInformationMessage( 'Start watching changes with cargo? (Executes `cargo watch`, provides inline diagnostics)', 'yes', - 'no' + 'no', ); if (watch !== 'yes') { return; @@ -157,12 +159,12 @@ export async function interactivelyStartCargoWatch( } export async function startCargoWatch( - context: vscode.ExtensionContext + context: vscode.ExtensionContext, ): Promise { const execPromise = util.promisify(child_process.exec); const { stderr, code = 0 } = await execPromise( - 'cargo watch --version' + 'cargo watch --version', ).catch(e => e); if (stderr.includes('no such subcommand: `watch`')) { @@ -171,14 +173,14 @@ export async function startCargoWatch( const install = await vscode.window.showInformationMessage( msg, 'yes', - 'no' + 'no', ); if (install !== 'yes') { return; } const label = 'install-cargo-watch'; - const taskFinished = new Promise((resolve, reject) => { + const taskFinished = new Promise((resolve, _reject) => { const disposable = vscode.tasks.onDidEndTask(({ execution }) => { if (execution.task.name === label) { disposable.dispose(); @@ -192,20 +194,20 @@ export async function startCargoWatch( label, bin: 'cargo', args: ['install', 'cargo-watch'], - env: {} - }) + env: {}, + }), ); await taskFinished; const output = await execPromise('cargo watch --version').catch(e => e); if (output.stderr !== '') { vscode.window.showErrorMessage( - `Couldn't install \`cargo-\`watch: ${output.stderr}` + `Couldn't install \`cargo-\`watch: ${output.stderr}`, ); return; } } else if (code !== 0) { vscode.window.showErrorMessage( - `\`cargo watch\` failed with ${code}: ${stderr}` + `\`cargo watch\` failed with ${code}: ${stderr}`, ); return; } diff --git a/editors/code/src/commands/syntaxTree.ts b/editors/code/src/commands/syntaxTree.ts index 2f50fe14b3..89a80550ce 100644 --- a/editors/code/src/commands/syntaxTree.ts +++ b/editors/code/src/commands/syntaxTree.ts @@ -11,7 +11,7 @@ export class SyntaxTreeContentProvider public syntaxTree: string = 'Not available'; public provideTextDocumentContent( - uri: vscode.Uri + uri: vscode.Uri, ): vscode.ProviderResult { const editor = vscode.window.activeTextEditor; if (editor == null) { @@ -25,17 +25,17 @@ export class SyntaxTreeContentProvider range = editor.selection.isEmpty ? undefined : Server.client.code2ProtocolConverter.asRange( - editor.selection + editor.selection, ); } const request: SyntaxTreeParams = { textDocument: { uri: editor.document.uri.toString() }, - range + range, }; return Server.client.sendRequest( 'rust-analyzer/syntaxTree', - request + request, ); } @@ -70,7 +70,7 @@ export function createHandle(provider: SyntaxTreeContentProvider) { return vscode.window.showTextDocument( document, vscode.ViewColumn.Two, - true + true, ); }; } diff --git a/editors/code/src/commands/watch_status.ts b/editors/code/src/commands/watch_status.ts index 6c1f9041ba..8d64394c7b 100644 --- a/editors/code/src/commands/watch_status.ts +++ b/editors/code/src/commands/watch_status.ts @@ -13,7 +13,7 @@ export class StatusDisplay implements vscode.Disposable { constructor(command: string) { this.statusBarItem = vscode.window.createStatusBarItem( vscode.StatusBarAlignment.Left, - 10 + 10, ); this.command = command; this.statusBarItem.hide(); diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 4cedbea466..c06dddb1c6 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -14,6 +14,13 @@ export interface CargoWatchOptions { command: string; trace: CargoWatchTraceOptions; ignore: string[]; + allTargets: boolean; +} + +export interface CargoFeatures { + noDefaultFeatures: boolean; + allFeatures: boolean; + features: string[]; } export class Config { @@ -25,21 +32,30 @@ export class Config { public displayInlayHints = true; public maxInlayHintLength: null | number = null; public excludeGlobs = []; - public useClientWatching = false; + public useClientWatching = true; public featureFlags = {}; + // for internal use + public withSysroot: null | boolean = null; public cargoWatchOptions: CargoWatchOptions = { enableOnStartup: 'ask', trace: 'off', arguments: '', command: '', - ignore: [] + ignore: [], + allTargets: true, + }; + public cargoFeatures: CargoFeatures = { + noDefaultFeatures: false, + allFeatures: true, + features: [], }; private prevEnhancedTyping: null | boolean = null; + private prevCargoFeatures: null | CargoFeatures = null; constructor() { vscode.workspace.onDidChangeConfiguration(_ => - this.userConfigChanged() + this.userConfigChanged(), ); this.userConfigChanged(); } @@ -49,6 +65,8 @@ export class Config { Server.highlighter.removeHighlights(); + let requireReloadMessage = null; + if (config.has('highlightingOn')) { this.highlightingOn = config.get('highlightingOn') as boolean; if (this.highlightingOn) { @@ -59,13 +77,13 @@ export class Config { if (config.has('rainbowHighlightingOn')) { this.rainbowHighlightingOn = config.get( - 'rainbowHighlightingOn' + 'rainbowHighlightingOn', ) as boolean; } if (config.has('enableEnhancedTyping')) { this.enableEnhancedTyping = config.get( - 'enableEnhancedTyping' + 'enableEnhancedTyping', ) as boolean; if (this.prevEnhancedTyping === null) { @@ -76,19 +94,8 @@ export class Config { } if (this.prevEnhancedTyping !== this.enableEnhancedTyping) { - const reloadAction = 'Reload now'; - vscode.window - .showInformationMessage( - 'Changing enhanced typing setting requires a reload', - reloadAction - ) - .then(selectedAction => { - if (selectedAction === reloadAction) { - vscode.commands.executeCommand( - 'workbench.action.reloadWindow' - ); - } - }); + requireReloadMessage = + 'Changing enhanced typing setting requires a reload'; this.prevEnhancedTyping = this.enableEnhancedTyping; } @@ -106,28 +113,35 @@ export class Config { if (config.has('trace.cargo-watch')) { this.cargoWatchOptions.trace = config.get( 'trace.cargo-watch', - 'off' + 'off', ); } if (config.has('cargo-watch.arguments')) { this.cargoWatchOptions.arguments = config.get( 'cargo-watch.arguments', - '' + '', ); } if (config.has('cargo-watch.command')) { this.cargoWatchOptions.command = config.get( 'cargo-watch.command', - '' + '', ); } if (config.has('cargo-watch.ignore')) { this.cargoWatchOptions.ignore = config.get( 'cargo-watch.ignore', - [] + [], + ); + } + + if (config.has('cargo-watch.allTargets')) { + this.cargoWatchOptions.allTargets = config.get( + 'cargo-watch.allTargets', + true, ); } @@ -140,17 +154,68 @@ export class Config { } if (config.has('maxInlayHintLength')) { this.maxInlayHintLength = config.get( - 'maxInlayHintLength' + 'maxInlayHintLength', ) as number; } if (config.has('excludeGlobs')) { this.excludeGlobs = config.get('excludeGlobs') || []; } if (config.has('useClientWatching')) { - this.useClientWatching = config.get('useClientWatching') || false; + this.useClientWatching = config.get('useClientWatching') || true; } if (config.has('featureFlags')) { this.featureFlags = config.get('featureFlags') || {}; } + if (config.has('withSysroot')) { + this.withSysroot = config.get('withSysroot') || false; + } + + if (config.has('cargoFeatures.noDefaultFeatures')) { + this.cargoFeatures.noDefaultFeatures = config.get( + 'cargoFeatures.noDefaultFeatures', + false, + ); + } + if (config.has('cargoFeatures.allFeatures')) { + this.cargoFeatures.allFeatures = config.get( + 'cargoFeatures.allFeatures', + true, + ); + } + if (config.has('cargoFeatures.features')) { + this.cargoFeatures.features = config.get( + 'cargoFeatures.features', + [], + ); + } + + if ( + this.prevCargoFeatures !== null && + (this.cargoFeatures.allFeatures !== + this.prevCargoFeatures.allFeatures || + this.cargoFeatures.noDefaultFeatures !== + this.prevCargoFeatures.noDefaultFeatures || + this.cargoFeatures.features.length !== + this.prevCargoFeatures.features.length || + this.cargoFeatures.features.some( + (v, i) => v !== this.prevCargoFeatures!.features[i], + )) + ) { + requireReloadMessage = 'Changing cargo features requires a reload'; + } + this.prevCargoFeatures = { ...this.cargoFeatures }; + + if (requireReloadMessage !== null) { + const reloadAction = 'Reload now'; + vscode.window + .showInformationMessage(requireReloadMessage, reloadAction) + .then(selectedAction => { + if (selectedAction === reloadAction) { + vscode.commands.executeCommand( + 'workbench.action.reloadWindow', + ); + } + }); + } } } diff --git a/editors/code/src/events/change_active_text_editor.ts b/editors/code/src/events/change_active_text_editor.ts index 64be562250..74b91bd487 100644 --- a/editors/code/src/events/change_active_text_editor.ts +++ b/editors/code/src/events/change_active_text_editor.ts @@ -3,7 +3,7 @@ import { TextDocumentIdentifier } from 'vscode-languageclient'; import { SyntaxTreeContentProvider, - syntaxTreeUri + syntaxTreeUri, } from '../commands/syntaxTree'; import { Decoration } from '../highlighting'; import { Server } from '../server'; @@ -21,11 +21,11 @@ export function makeHandler(syntaxTreeProvider: SyntaxTreeContentProvider) { } const params: TextDocumentIdentifier = { - uri: editor.document.uri.toString() + uri: editor.document.uri.toString(), }; const decorations = await Server.client.sendRequest( 'rust-analyzer/decorationsRequest', - params + params, ); Server.highlighter.setHighlights(editor, decorations); }; diff --git a/editors/code/src/events/change_text_document.ts b/editors/code/src/events/change_text_document.ts index 89488bc61e..2e998e889b 100644 --- a/editors/code/src/events/change_text_document.ts +++ b/editors/code/src/events/change_text_document.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import { SyntaxTreeContentProvider, - syntaxTreeUri + syntaxTreeUri, } from '../commands/syntaxTree'; export function createHandler(syntaxTreeProvider: SyntaxTreeContentProvider) { diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index 683497dfd4..815f3692c0 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts @@ -7,14 +7,14 @@ import { ExpandMacroContentProvider } from './commands/expand_macro'; import { HintsUpdater } from './commands/inlay_hints'; import { interactivelyStartCargoWatch, - startCargoWatch + startCargoWatch, } from './commands/runnables'; import { SyntaxTreeContentProvider } from './commands/syntaxTree'; import * as events from './events'; import * as notifications from './notifications'; import { Server } from './server'; -export function activate(context: vscode.ExtensionContext) { +export async function activate(context: vscode.ExtensionContext) { function disposeOnDeactivation(disposable: vscode.Disposable) { context.subscriptions.push(disposable); } @@ -24,7 +24,7 @@ export function activate(context: vscode.ExtensionContext) { } function overrideCommand( name: string, - f: (...args: any[]) => Promise + f: (...args: any[]) => Promise, ) { const defaultCmd = `default:${name}`; const original = (...args: any[]) => @@ -46,7 +46,7 @@ export function activate(context: vscode.ExtensionContext) { }); } catch (_) { vscode.window.showWarningMessage( - 'Enhanced typing feature is disabled because of incompatibility with VIM extension, consider turning off rust-analyzer.enableEnhancedTyping: https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/README.md#settings' + 'Enhanced typing feature is disabled because of incompatibility with VIM extension, consider turning off rust-analyzer.enableEnhancedTyping: https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/README.md#settings', ); } } @@ -54,14 +54,14 @@ export function activate(context: vscode.ExtensionContext) { // Commands are requests from vscode to the language server registerCommand( 'rust-analyzer.analyzerStatus', - commands.analyzerStatus.makeCommand(context) + commands.analyzerStatus.makeCommand(context), ); registerCommand('rust-analyzer.collectGarbage', () => - Server.client.sendRequest('rust-analyzer/collectGarbage', null) + Server.client.sendRequest('rust-analyzer/collectGarbage', null), ); registerCommand( 'rust-analyzer.matchingBrace', - commands.matchingBrace.handle + commands.matchingBrace.handle, ); registerCommand('rust-analyzer.joinLines', commands.joinLines.handle); registerCommand('rust-analyzer.parentModule', commands.parentModule.handle); @@ -70,7 +70,7 @@ export function activate(context: vscode.ExtensionContext) { registerCommand('rust-analyzer.runSingle', commands.runnables.handleSingle); registerCommand( 'rust-analyzer.applySourceChange', - commands.applySourceChange.handle + commands.applySourceChange.handle, ); registerCommand( 'rust-analyzer.showReferences', @@ -79,9 +79,9 @@ export function activate(context: vscode.ExtensionContext) { 'editor.action.showReferences', vscode.Uri.parse(uri), Server.client.protocol2CodeConverter.asPosition(position), - locations.map(Server.client.protocol2CodeConverter.asLocation) + locations.map(Server.client.protocol2CodeConverter.asLocation), ); - } + }, ); if (Server.config.enableEnhancedTyping) { @@ -89,48 +89,49 @@ export function activate(context: vscode.ExtensionContext) { } // Notifications are events triggered by the language server - const allNotifications: Iterable< - [string, lc.GenericNotificationHandler] - > = [ + const allNotifications: Iterable<[ + string, + lc.GenericNotificationHandler, + ]> = [ [ 'rust-analyzer/publishDecorations', - notifications.publishDecorations.handle - ] + notifications.publishDecorations.handle, + ], ]; const syntaxTreeContentProvider = new SyntaxTreeContentProvider(); const expandMacroContentProvider = new ExpandMacroContentProvider(); // The events below are plain old javascript events, triggered and handled by vscode vscode.window.onDidChangeActiveTextEditor( - events.changeActiveTextEditor.makeHandler(syntaxTreeContentProvider) + events.changeActiveTextEditor.makeHandler(syntaxTreeContentProvider), ); disposeOnDeactivation( vscode.workspace.registerTextDocumentContentProvider( 'rust-analyzer', - syntaxTreeContentProvider - ) + syntaxTreeContentProvider, + ), ); disposeOnDeactivation( vscode.workspace.registerTextDocumentContentProvider( 'rust-analyzer', - expandMacroContentProvider - ) + expandMacroContentProvider, + ), ); registerCommand( 'rust-analyzer.syntaxTree', - commands.syntaxTree.createHandle(syntaxTreeContentProvider) + commands.syntaxTree.createHandle(syntaxTreeContentProvider), ); registerCommand( 'rust-analyzer.expandMacro', - commands.expandMacro.createHandle(expandMacroContentProvider) + commands.expandMacro.createHandle(expandMacroContentProvider), ); vscode.workspace.onDidChangeTextDocument( events.changeTextDocument.createHandler(syntaxTreeContentProvider), null, - context.subscriptions + context.subscriptions, ); const startServer = () => Server.start(allNotifications); @@ -159,7 +160,11 @@ export function activate(context: vscode.ExtensionContext) { }); // Start the language server, finally! - startServer(); + try { + await startServer(); + } catch (e) { + vscode.window.showErrorMessage(e.message); + } if (Server.config.displayInlayHints) { const hintsUpdater = new HintsUpdater(); @@ -173,25 +178,25 @@ export function activate(context: vscode.ExtensionContext) { editorChangeDisposable.dispose(); } return hintsUpdater.refreshHintsForVisibleEditors(); - } + }, ); disposeOnDeactivation( vscode.window.onDidChangeVisibleTextEditors(_ => - hintsUpdater.refreshHintsForVisibleEditors() - ) + hintsUpdater.refreshHintsForVisibleEditors(), + ), ); disposeOnDeactivation( vscode.workspace.onDidChangeTextDocument(e => - hintsUpdater.refreshHintsForVisibleEditors(e) - ) + hintsUpdater.refreshHintsForVisibleEditors(e), + ), ); disposeOnDeactivation( vscode.workspace.onDidChangeConfiguration(_ => hintsUpdater.toggleHintsDisplay( - Server.config.displayInlayHints - ) - ) + Server.config.displayInlayHints, + ), + ), ); }); } @@ -204,10 +209,10 @@ export function deactivate(): Thenable { return Server.client.stop(); } -async function reloadServer(startServer: () => void) { +async function reloadServer(startServer: () => Promise) { if (Server.client != null) { vscode.window.showInformationMessage('Reloading rust-analyzer...'); await Server.client.stop(); - startServer(); + await startServer(); } } diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts index 0a38c9ef67..68eae09419 100644 --- a/editors/code/src/highlighting.ts +++ b/editors/code/src/highlighting.ts @@ -67,7 +67,7 @@ export class Highlighter { > { const decoration = ( tag: string, - textDecoration?: string + textDecoration?: string, ): [string, vscode.TextEditorDecorationType] => { const rule = scopesMapper.toRule(tag, scopes.find); @@ -90,9 +90,10 @@ export class Highlighter { } }; - const decorations: Iterable< - [string, vscode.TextEditorDecorationType] - > = [ + const decorations: Iterable<[ + string, + vscode.TextEditorDecorationType, + ]> = [ decoration('comment'), decoration('string'), decoration('keyword'), @@ -101,16 +102,23 @@ export class Highlighter { decoration('function'), decoration('parameter'), decoration('constant'), + decoration('type.builtin'), + decoration('type.generic'), + decoration('type.lifetime'), + decoration('type.param'), + decoration('type.self'), decoration('type'), - decoration('builtin'), decoration('text'), decoration('attribute'), decoration('literal'), + decoration('literal.numeric'), + decoration('literal.char'), + decoration('literal.byte'), decoration('macro'), decoration('variable'), decoration('variable.mut', 'underline'), decoration('field'), - decoration('module') + decoration('module'), ]; return new Map(decorations); @@ -139,7 +147,6 @@ export class Highlighter { // // Note: decoration objects need to be kept around so we can dispose them // if the user disables syntax highlighting - if (this.decorations == null) { this.decorations = Highlighter.initDecorations(); } @@ -168,23 +175,22 @@ export class Highlighter { colorfulIdents .get(d.bindingHash)![0] .push( - Server.client.protocol2CodeConverter.asRange(d.range) + Server.client.protocol2CodeConverter.asRange(d.range), ); } else { byTag .get(d.tag)! .push( - Server.client.protocol2CodeConverter.asRange(d.range) + Server.client.protocol2CodeConverter.asRange(d.range), ); } } for (const tag of byTag.keys()) { const dec = this.decorations.get( - tag + tag, ) as vscode.TextEditorDecorationType; const ranges = byTag.get(tag)!; - editor.setDecorations(dec, ranges); } @@ -192,7 +198,7 @@ export class Highlighter { const textDecoration = mut ? 'underline' : undefined; const dec = vscode.window.createTextEditorDecorationType({ light: { color: fancify(hash, 'light'), textDecoration }, - dark: { color: fancify(hash, 'dark'), textDecoration } + dark: { color: fancify(hash, 'dark'), textDecoration }, }); editor.setDecorations(dec, ranges); } diff --git a/editors/code/src/notifications/publish_decorations.ts b/editors/code/src/notifications/publish_decorations.ts index 3180019b74..f23e286ad5 100644 --- a/editors/code/src/notifications/publish_decorations.ts +++ b/editors/code/src/notifications/publish_decorations.ts @@ -9,11 +9,16 @@ export interface PublishDecorationsParams { } export function handle(params: PublishDecorationsParams) { - const targetEditor = vscode.window.visibleTextEditors.find( - editor => editor.document.uri.toString() === params.uri - ); + const targetEditor = vscode.window.visibleTextEditors.find(editor => { + const unescapedUri = unescape(editor.document.uri.toString()); + // Unescaped URI looks like: + // file:///c:/Workspace/ra-test/src/main.rs + return unescapedUri === params.uri; + }); + if (!Server.config.highlightingOn || !targetEditor) { return; } + Server.highlighter.setHighlights(targetEditor, params.decorations); } diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts index 7907b70bc5..5ace1d0fae 100644 --- a/editors/code/src/server.ts +++ b/editors/code/src/server.ts @@ -1,4 +1,5 @@ -import { homedir } from 'os'; +import { lookpath } from 'lookpath'; +import { homedir, platform } from 'os'; import * as lc from 'vscode-languageclient'; import { window, workspace } from 'vscode'; @@ -17,8 +18,8 @@ export class Server { public static config = new Config(); public static client: lc.LanguageClient; - public static start( - notificationHandlers: Iterable<[string, lc.GenericNotificationHandler]> + public static async start( + notificationHandlers: Iterable<[string, lc.GenericNotificationHandler]>, ) { // '.' Is the fallback if no folder is open // TODO?: Workspace folders support Uri's (eg: file://test.txt). It might be a good idea to test if the uri points to a file. @@ -27,16 +28,26 @@ export class Server { folder = workspace.workspaceFolders[0].uri.fsPath.toString(); } + const command = expandPathResolving(this.config.raLspServerPath); + // FIXME: remove check when the following issue is fixed: + // https://github.com/otiai10/lookpath/issues/4 + if (platform() !== 'win32') { + if (!(await lookpath(command))) { + throw new Error( + `Cannot find rust-analyzer server \`${command}\` in PATH.`, + ); + } + } const run: lc.Executable = { - command: expandPathResolving(this.config.raLspServerPath), - options: { cwd: folder } + command, + options: { cwd: folder }, }; const serverOptions: lc.ServerOptions = { run, - debug: run + debug: run, }; const traceOutputChannel = window.createOutputChannel( - 'Rust Analyzer Language Server Trace' + 'Rust Analyzer Language Server Trace', ); const clientOptions: lc.LanguageClientOptions = { documentSelector: [{ scheme: 'file', language: 'rust' }], @@ -46,16 +57,18 @@ export class Server { maxInlayHintLength: Server.config.maxInlayHintLength, excludeGlobs: Server.config.excludeGlobs, useClientWatching: Server.config.useClientWatching, - featureFlags: Server.config.featureFlags + featureFlags: Server.config.featureFlags, + withSysroot: Server.config.withSysroot, + cargoFeatures: Server.config.cargoFeatures, }, - traceOutputChannel + traceOutputChannel, }; Server.client = new lc.LanguageClient( 'rust-analyzer', 'Rust Analyzer Language Server', serverOptions, - clientOptions + clientOptions, ); // HACK: This is an awful way of filtering out the decorations notifications // However, pending proper support, this is the most effecitve approach @@ -68,10 +81,10 @@ export class Server { if (typeof messageOrDataObject === 'string') { if ( messageOrDataObject.includes( - 'rust-analyzer/publishDecorations' + 'rust-analyzer/publishDecorations', ) || messageOrDataObject.includes( - 'rust-analyzer/decorationsRequest' + 'rust-analyzer/decorationsRequest', ) ) { // Don't log publish decorations requests @@ -83,7 +96,7 @@ export class Server { // @ts-ignore Server.client.logObjectTrace(messageOrDataObject); } - } + }, }; Server.client.registerProposedFeatures(); Server.client.onReady().then(() => { diff --git a/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json b/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json new file mode 100644 index 0000000000..bfef33c7de --- /dev/null +++ b/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json @@ -0,0 +1,261 @@ +{ + "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n", + "children": [ + { + "children": [], + "code": null, + "level": "help", + "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", + "rendered": null, + "spans": [] + } + ], + "code": { + "code": "E0277", + "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n" + }, + "level": "error", + "message": "can't compare `{integer}` with `&str`", + "spans": [ + { + "byte_end": 155, + "byte_start": 153, + "column_end": 33, + "column_start": 31, + "expansion": { + "def_site_span": { + "byte_end": 940, + "byte_start": 0, + "column_end": 6, + "column_start": 1, + "expansion": null, + "file_name": "<::core::macros::assert_eq macros>", + "is_primary": false, + "label": null, + "line_end": 36, + "line_start": 1, + "suggested_replacement": null, + "suggestion_applicability": null, + "text": [ + { + "highlight_end": 35, + "highlight_start": 1, + "text": "($ left : expr, $ right : expr) =>" + }, + { + "highlight_end": 3, + "highlight_start": 1, + "text": "({" + }, + { + "highlight_end": 33, + "highlight_start": 1, + "text": " match (& $ left, & $ right)" + }, + { + "highlight_end": 7, + "highlight_start": 1, + "text": " {" + }, + { + "highlight_end": 34, + "highlight_start": 1, + "text": " (left_val, right_val) =>" + }, + { + "highlight_end": 11, + "highlight_start": 1, + "text": " {" + }, + { + "highlight_end": 46, + "highlight_start": 1, + "text": " if ! (* left_val == * right_val)" + }, + { + "highlight_end": 15, + "highlight_start": 1, + "text": " {" + }, + { + "highlight_end": 25, + "highlight_start": 1, + "text": " panic !" + }, + { + "highlight_end": 57, + "highlight_start": 1, + "text": " (r#\"assertion failed: `(left == right)`" + }, + { + "highlight_end": 16, + "highlight_start": 1, + "text": " left: `{:?}`," + }, + { + "highlight_end": 18, + "highlight_start": 1, + "text": " right: `{:?}`\"#," + }, + { + "highlight_end": 47, + "highlight_start": 1, + "text": " & * left_val, & * right_val)" + }, + { + "highlight_end": 15, + "highlight_start": 1, + "text": " }" + }, + { + "highlight_end": 11, + "highlight_start": 1, + "text": " }" + }, + { + "highlight_end": 7, + "highlight_start": 1, + "text": " }" + }, + { + "highlight_end": 42, + "highlight_start": 1, + "text": " }) ; ($ left : expr, $ right : expr,) =>" + }, + { + "highlight_end": 49, + "highlight_start": 1, + "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;" + }, + { + "highlight_end": 53, + "highlight_start": 1, + "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>" + }, + { + "highlight_end": 3, + "highlight_start": 1, + "text": "({" + }, + { + "highlight_end": 37, + "highlight_start": 1, + "text": " match (& ($ left), & ($ right))" + }, + { + "highlight_end": 7, + "highlight_start": 1, + "text": " {" + }, + { + "highlight_end": 34, + "highlight_start": 1, + "text": " (left_val, right_val) =>" + }, + { + "highlight_end": 11, + "highlight_start": 1, + "text": " {" + }, + { + "highlight_end": 46, + "highlight_start": 1, + "text": " if ! (* left_val == * right_val)" + }, + { + "highlight_end": 15, + "highlight_start": 1, + "text": " {" + }, + { + "highlight_end": 25, + "highlight_start": 1, + "text": " panic !" + }, + { + "highlight_end": 57, + "highlight_start": 1, + "text": " (r#\"assertion failed: `(left == right)`" + }, + { + "highlight_end": 16, + "highlight_start": 1, + "text": " left: `{:?}`," + }, + { + "highlight_end": 22, + "highlight_start": 1, + "text": " right: `{:?}`: {}\"#," + }, + { + "highlight_end": 72, + "highlight_start": 1, + "text": " & * left_val, & * right_val, $ crate :: format_args !" + }, + { + "highlight_end": 33, + "highlight_start": 1, + "text": " ($ ($ arg) +))" + }, + { + "highlight_end": 15, + "highlight_start": 1, + "text": " }" + }, + { + "highlight_end": 11, + "highlight_start": 1, + "text": " }" + }, + { + "highlight_end": 7, + "highlight_start": 1, + "text": " }" + }, + { + "highlight_end": 6, + "highlight_start": 1, + "text": " }) ;" + } + ] + }, + "macro_decl_name": "assert_eq!", + "span": { + "byte_end": 38, + "byte_start": 16, + "column_end": 27, + "column_start": 5, + "expansion": null, + "file_name": "src/main.rs", + "is_primary": false, + "label": null, + "line_end": 2, + "line_start": 2, + "suggested_replacement": null, + "suggestion_applicability": null, + "text": [ + { + "highlight_end": 27, + "highlight_start": 5, + "text": " assert_eq!(1, \"love\");" + } + ] + } + }, + "file_name": "<::core::macros::assert_eq macros>", + "is_primary": true, + "label": "no implementation for `{integer} == &str`", + "line_end": 7, + "line_start": 7, + "suggested_replacement": null, + "suggestion_applicability": null, + "text": [ + { + "highlight_end": 33, + "highlight_start": 31, + "text": " if ! (* left_val == * right_val)" + } + ] + } + ] +} diff --git a/editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts b/editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts index 6c7f436f35..2b25eb705d 100644 --- a/editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts +++ b/editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts @@ -6,12 +6,12 @@ import SuggestedFix from '../../../utils/diagnostics/SuggestedFix'; const location1 = new vscode.Location( vscode.Uri.file('/file/1'), - new vscode.Range(new vscode.Position(1, 2), new vscode.Position(3, 4)) + new vscode.Range(new vscode.Position(1, 2), new vscode.Position(3, 4)), ); const location2 = new vscode.Location( vscode.Uri.file('/file/2'), - new vscode.Range(new vscode.Position(5, 6), new vscode.Position(7, 8)) + new vscode.Range(new vscode.Position(5, 6), new vscode.Position(7, 8)), ); describe('SuggestedFix', () => { @@ -20,13 +20,13 @@ describe('SuggestedFix', () => { const suggestion1 = new SuggestedFix( 'Replace me!', location1, - 'With this!' + 'With this!', ); const suggestion2 = new SuggestedFix( 'Replace me!', location1, - 'With this!' + 'With this!', ); assert(suggestion1.isEqual(suggestion2)); @@ -36,13 +36,13 @@ describe('SuggestedFix', () => { const suggestion1 = new SuggestedFix( 'Replace me!', location1, - 'With this!' + 'With this!', ); const suggestion2 = new SuggestedFix( 'Not the same title!', location1, - 'With this!' + 'With this!', ); assert(!suggestion1.isEqual(suggestion2)); @@ -52,13 +52,13 @@ describe('SuggestedFix', () => { const suggestion1 = new SuggestedFix( 'Replace me!', location1, - 'With this!' + 'With this!', ); const suggestion2 = new SuggestedFix( 'Replace me!', location1, - 'With something else!' + 'With something else!', ); assert(!suggestion1.isEqual(suggestion2)); @@ -68,13 +68,13 @@ describe('SuggestedFix', () => { const suggestion1 = new SuggestedFix( 'Replace me!', location1, - 'With this!' + 'With this!', ); const suggestion2 = new SuggestedFix( 'Replace me!', location2, - 'With this!' + 'With this!', ); assert(!suggestion1.isEqual(suggestion2)); @@ -85,14 +85,14 @@ describe('SuggestedFix', () => { 'Replace me!', location1, 'With this!', - SuggestionApplicability.MachineApplicable + SuggestionApplicability.MachineApplicable, ); const suggestion2 = new SuggestedFix( 'Replace me!', location2, 'With this!', - SuggestionApplicability.HasPlaceholders + SuggestionApplicability.HasPlaceholders, ); assert(!suggestion1.isEqual(suggestion2)); @@ -104,7 +104,7 @@ describe('SuggestedFix', () => { const suggestion = new SuggestedFix( 'Replace me!', location1, - 'With this!' + 'With this!', ); const codeAction = suggestion.toCodeAction(); @@ -114,7 +114,8 @@ describe('SuggestedFix', () => { const edit = codeAction.edit; if (!edit) { - return assert.fail('Code Action edit unexpectedly missing'); + assert.fail('Code Action edit unexpectedly missing'); + return; } const editEntries = edit.entries(); diff --git a/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts b/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts index f0328893e7..ef09013f41 100644 --- a/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts +++ b/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts @@ -8,20 +8,20 @@ const uri1 = vscode.Uri.file('/file/1'); const uri2 = vscode.Uri.file('/file/2'); const mockDocument1 = ({ - uri: uri1 + uri: uri1, } as unknown) as vscode.TextDocument; const mockDocument2 = ({ - uri: uri2 + uri: uri2, } as unknown) as vscode.TextDocument; const range1 = new vscode.Range( new vscode.Position(1, 2), - new vscode.Position(3, 4) + new vscode.Position(3, 4), ); const range2 = new vscode.Range( new vscode.Position(5, 6), - new vscode.Position(7, 8) + new vscode.Position(7, 8), ); const diagnostic1 = new vscode.Diagnostic(range1, 'First diagnostic'); @@ -32,7 +32,7 @@ function suggestion1(): SuggestedFix { return new SuggestedFix( 'Replace me!', new vscode.Location(uri1, range1), - 'With this!' + 'With this!', ); } @@ -44,7 +44,7 @@ describe('SuggestedFixCollection', () => { // Specify the document and range that exactly matches const codeActions = suggestedFixes.provideCodeActions( mockDocument1, - range1 + range1, ); assert.strictEqual(codeActions.length, 1); @@ -53,7 +53,8 @@ describe('SuggestedFixCollection', () => { const { diagnostics } = codeAction; if (!diagnostics) { - return assert.fail('Diagnostics unexpectedly missing'); + assert.fail('Diagnostics unexpectedly missing'); + return; } assert.strictEqual(diagnostics.length, 1); @@ -66,7 +67,7 @@ describe('SuggestedFixCollection', () => { const codeActions = suggestedFixes.provideCodeActions( mockDocument1, - range2 + range2, ); assert(!codeActions || codeActions.length === 0); @@ -78,7 +79,7 @@ describe('SuggestedFixCollection', () => { const codeActions = suggestedFixes.provideCodeActions( mockDocument2, - range1 + range1, ); assert(!codeActions || codeActions.length === 0); @@ -91,7 +92,7 @@ describe('SuggestedFixCollection', () => { const codeActions = suggestedFixes.provideCodeActions( mockDocument1, - range1 + range1, ); assert(!codeActions || codeActions.length === 0); @@ -106,7 +107,7 @@ describe('SuggestedFixCollection', () => { const codeActions = suggestedFixes.provideCodeActions( mockDocument1, - range1 + range1, ); assert.strictEqual(codeActions.length, 1); @@ -114,7 +115,8 @@ describe('SuggestedFixCollection', () => { const { diagnostics } = codeAction; if (!diagnostics) { - return assert.fail('Diagnostics unexpectedly missing'); + assert.fail('Diagnostics unexpectedly missing'); + return; } // We should be associated with both diagnostics diff --git a/editors/code/src/test/utils/diagnotics/rust.test.ts b/editors/code/src/test/utils/diagnotics/rust.test.ts index 327d15046f..358325cc8d 100644 --- a/editors/code/src/test/utils/diagnotics/rust.test.ts +++ b/editors/code/src/test/utils/diagnotics/rust.test.ts @@ -6,14 +6,14 @@ import { MappedRustDiagnostic, mapRustDiagnosticToVsCode, RustDiagnostic, - SuggestionApplicability + SuggestionApplicability, } from '../../../utils/diagnostics/rust'; function loadDiagnosticFixture(name: string): RustDiagnostic { const jsonText = fs .readFileSync( // We're actually in our JavaScript output directory, climb out - `${__dirname}/../../../../src/test/fixtures/rust-diagnostics/${name}.json` + `${__dirname}/../../../../src/test/fixtures/rust-diagnostics/${name}.json`, ) .toString(); @@ -33,12 +33,12 @@ function mapFixtureToVsCode(name: string): MappedRustDiagnostic { describe('mapRustDiagnosticToVsCode', () => { it('should map an incompatible type for trait error', () => { const { diagnostic, suggestedFixes } = mapFixtureToVsCode( - 'error/E0053' + 'error/E0053', ); assert.strictEqual( diagnostic.severity, - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); assert.strictEqual(diagnostic.source, 'rustc'); assert.strictEqual( @@ -46,8 +46,8 @@ describe('mapRustDiagnosticToVsCode', () => { [ `method \`next\` has an incompatible type for trait`, `expected type \`fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref>\``, - ` found type \`fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref>\`` - ].join('\n') + ` found type \`fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref>\``, + ].join('\n'), ); assert.strictEqual(diagnostic.code, 'E0053'); assert.deepStrictEqual(diagnostic.tags, []); @@ -61,24 +61,24 @@ describe('mapRustDiagnosticToVsCode', () => { it('should map an unused variable warning', () => { const { diagnostic, suggestedFixes } = mapFixtureToVsCode( - 'warning/unused_variables' + 'warning/unused_variables', ); assert.strictEqual( diagnostic.severity, - vscode.DiagnosticSeverity.Warning + vscode.DiagnosticSeverity.Warning, ); assert.strictEqual( diagnostic.message, [ 'unused variable: `foo`', - '#[warn(unused_variables)] on by default' - ].join('\n') + '#[warn(unused_variables)] on by default', + ].join('\n'), ); assert.strictEqual(diagnostic.code, 'unused_variables'); assert.strictEqual(diagnostic.source, 'rustc'); assert.deepStrictEqual(diagnostic.tags, [ - vscode.DiagnosticTag.Unnecessary + vscode.DiagnosticTag.Unnecessary, ]); // No related information @@ -89,29 +89,29 @@ describe('mapRustDiagnosticToVsCode', () => { const [suggestedFix] = suggestedFixes; assert.strictEqual( suggestedFix.title, - 'consider prefixing with an underscore: `_foo`' + 'consider prefixing with an underscore: `_foo`', ); assert.strictEqual( suggestedFix.applicability, - SuggestionApplicability.MachineApplicable + SuggestionApplicability.MachineApplicable, ); }); it('should map a wrong number of parameters error', () => { const { diagnostic, suggestedFixes } = mapFixtureToVsCode( - 'error/E0061' + 'error/E0061', ); assert.strictEqual( diagnostic.severity, - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); assert.strictEqual( diagnostic.message, [ 'this function takes 2 parameters but 3 parameters were supplied', - 'expected 2 parameters' - ].join('\n') + 'expected 2 parameters', + ].join('\n'), ); assert.strictEqual(diagnostic.code, 'E0061'); assert.strictEqual(diagnostic.source, 'rustc'); @@ -120,7 +120,8 @@ describe('mapRustDiagnosticToVsCode', () => { // One related information for the original definition const relatedInformation = diagnostic.relatedInformation; if (!relatedInformation) { - return assert.fail('Related information unexpectedly undefined'); + assert.fail('Related information unexpectedly undefined'); + return; } assert.strictEqual(relatedInformation.length, 1); const [related] = relatedInformation; @@ -132,12 +133,12 @@ describe('mapRustDiagnosticToVsCode', () => { it('should map a Clippy copy pass by ref warning', () => { const { diagnostic, suggestedFixes } = mapFixtureToVsCode( - 'clippy/trivially_copy_pass_by_ref' + 'clippy/trivially_copy_pass_by_ref', ); assert.strictEqual( diagnostic.severity, - vscode.DiagnosticSeverity.Warning + vscode.DiagnosticSeverity.Warning, ); assert.strictEqual(diagnostic.source, 'clippy'); assert.strictEqual( @@ -145,8 +146,8 @@ describe('mapRustDiagnosticToVsCode', () => { [ 'this argument is passed by reference, but would be more efficient if passed by value', '#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]', - 'for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref' - ].join('\n') + 'for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref', + ].join('\n'), ); assert.strictEqual(diagnostic.code, 'trivially_copy_pass_by_ref'); assert.deepStrictEqual(diagnostic.tags, []); @@ -154,7 +155,8 @@ describe('mapRustDiagnosticToVsCode', () => { // One related information for the lint definition const relatedInformation = diagnostic.relatedInformation; if (!relatedInformation) { - return assert.fail('Related information unexpectedly undefined'); + assert.fail('Related information unexpectedly undefined'); + return; } assert.strictEqual(relatedInformation.length, 1); const [related] = relatedInformation; @@ -165,27 +167,27 @@ describe('mapRustDiagnosticToVsCode', () => { const [suggestedFix] = suggestedFixes; assert.strictEqual( suggestedFix.title, - 'consider passing by value instead: `self`' + 'consider passing by value instead: `self`', ); // Clippy does not mark this with any applicability assert.strictEqual( suggestedFix.applicability, - SuggestionApplicability.Unspecified + SuggestionApplicability.Unspecified, ); }); it('should map a mismatched type error', () => { const { diagnostic, suggestedFixes } = mapFixtureToVsCode( - 'error/E0308' + 'error/E0308', ); assert.strictEqual( diagnostic.severity, - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); assert.strictEqual( diagnostic.message, - ['mismatched types', 'expected usize, found u32'].join('\n') + ['mismatched types', 'expected usize, found u32'].join('\n'), ); assert.strictEqual(diagnostic.code, 'E0308'); assert.strictEqual(diagnostic.source, 'rustc'); @@ -197,4 +199,38 @@ describe('mapRustDiagnosticToVsCode', () => { // There are no suggested fixes assert.strictEqual(suggestedFixes.length, 0); }); + + it('should map a macro invocation location to normal file path', () => { + const { location, diagnostic, suggestedFixes } = mapFixtureToVsCode( + 'error/E0277', + ); + + assert.strictEqual( + diagnostic.severity, + vscode.DiagnosticSeverity.Error, + ); + assert.strictEqual( + diagnostic.message, + [ + "can't compare `{integer}` with `&str`", + 'the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`', + ].join('\n'), + ); + assert.strictEqual(diagnostic.code, 'E0277'); + assert.strictEqual(diagnostic.source, 'rustc'); + assert.deepStrictEqual(diagnostic.tags, []); + + // No related information + assert.deepStrictEqual(diagnostic.relatedInformation, []); + + // There are no suggested fixes + assert.strictEqual(suggestedFixes.length, 0); + + // The file url should be normal file + // Ignore the first part because it depends on vs workspace location + assert.strictEqual( + location.uri.path.substr(-'src/main.rs'.length), + 'src/main.rs', + ); + }); }); diff --git a/editors/code/src/test/utils/diagnotics/vscode.test.ts b/editors/code/src/test/utils/diagnotics/vscode.test.ts index 542dec1f54..4944dd0328 100644 --- a/editors/code/src/test/utils/diagnotics/vscode.test.ts +++ b/editors/code/src/test/utils/diagnotics/vscode.test.ts @@ -5,12 +5,12 @@ import { areDiagnosticsEqual } from '../../../utils/diagnostics/vscode'; const range1 = new vscode.Range( new vscode.Position(1, 2), - new vscode.Position(3, 4) + new vscode.Position(3, 4), ); const range2 = new vscode.Range( new vscode.Position(5, 6), - new vscode.Position(7, 8) + new vscode.Position(7, 8), ); describe('areDiagnosticsEqual', () => { @@ -18,13 +18,13 @@ describe('areDiagnosticsEqual', () => { const diagnostic1 = new vscode.Diagnostic( range1, 'Hello, world!', - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); const diagnostic2 = new vscode.Diagnostic( range1, 'Hello, world!', - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); assert(areDiagnosticsEqual(diagnostic1, diagnostic2)); @@ -34,14 +34,14 @@ describe('areDiagnosticsEqual', () => { const diagnostic1 = new vscode.Diagnostic( range1, 'Hello, world!', - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); diagnostic1.source = 'rustc'; const diagnostic2 = new vscode.Diagnostic( range1, 'Hello, world!', - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); diagnostic2.source = 'clippy'; @@ -52,13 +52,13 @@ describe('areDiagnosticsEqual', () => { const diagnostic1 = new vscode.Diagnostic( range1, 'Hello, world!', - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); const diagnostic2 = new vscode.Diagnostic( range2, 'Hello, world!', - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); assert(!areDiagnosticsEqual(diagnostic1, diagnostic2)); @@ -68,13 +68,13 @@ describe('areDiagnosticsEqual', () => { const diagnostic1 = new vscode.Diagnostic( range1, 'Hello, world!', - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); const diagnostic2 = new vscode.Diagnostic( range1, 'Goodbye!, world!', - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); assert(!areDiagnosticsEqual(diagnostic1, diagnostic2)); @@ -84,13 +84,13 @@ describe('areDiagnosticsEqual', () => { const diagnostic1 = new vscode.Diagnostic( range1, 'Hello, world!', - vscode.DiagnosticSeverity.Warning + vscode.DiagnosticSeverity.Warning, ); const diagnostic2 = new vscode.Diagnostic( range1, 'Hello, world!', - vscode.DiagnosticSeverity.Error + vscode.DiagnosticSeverity.Error, ); assert(!areDiagnosticsEqual(diagnostic1, diagnostic2)); diff --git a/editors/code/src/test/utils/index.ts b/editors/code/src/test/utils/index.ts index 16715a286c..9927daaf6a 100644 --- a/editors/code/src/test/utils/index.ts +++ b/editors/code/src/test/utils/index.ts @@ -17,7 +17,7 @@ import * as path from 'path'; export function run(): Promise { // Create the mocha test const mocha = new Mocha({ - ui: 'bdd' + ui: 'bdd', }); mocha.useColors(true); diff --git a/editors/code/src/utils/diagnostics/SuggestedFix.ts b/editors/code/src/utils/diagnostics/SuggestedFix.ts index b1be2a225b..6e660bb61d 100644 --- a/editors/code/src/utils/diagnostics/SuggestedFix.ts +++ b/editors/code/src/utils/diagnostics/SuggestedFix.ts @@ -24,7 +24,7 @@ export default class SuggestedFix { title: string, location: vscode.Location, replacement: string, - applicability: SuggestionApplicability = SuggestionApplicability.Unspecified + applicability: SuggestionApplicability = SuggestionApplicability.Unspecified, ) { this.title = title; this.location = location; @@ -51,7 +51,7 @@ export default class SuggestedFix { public toCodeAction(): vscode.CodeAction { const codeAction = new vscode.CodeAction( this.title, - vscode.CodeActionKind.QuickFix + vscode.CodeActionKind.QuickFix, ); const edit = new vscode.WorkspaceEdit(); diff --git a/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts b/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts index 132ce12f89..57c9856cfa 100644 --- a/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts +++ b/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts @@ -38,13 +38,13 @@ export default class SuggestedFixCollection */ public addSuggestedFixForDiagnostic( suggestedFix: SuggestedFix, - diagnostic: vscode.Diagnostic + diagnostic: vscode.Diagnostic, ): void { const fileUriString = suggestedFix.location.uri.toString(); const fileSuggestions = this.suggestedFixes.get(fileUriString) || []; const existingSuggestion = fileSuggestions.find(s => - s.isEqual(suggestedFix) + s.isEqual(suggestedFix), ); if (existingSuggestion) { @@ -65,7 +65,7 @@ export default class SuggestedFixCollection */ public provideCodeActions( document: vscode.TextDocument, - range: vscode.Range + range: vscode.Range, ): vscode.CodeAction[] { const documentUriString = document.uri.toString(); diff --git a/editors/code/src/utils/diagnostics/rust.ts b/editors/code/src/utils/diagnostics/rust.ts index 0550d03724..1f0c0d3e40 100644 --- a/editors/code/src/utils/diagnostics/rust.ts +++ b/editors/code/src/utils/diagnostics/rust.ts @@ -7,7 +7,13 @@ export enum SuggestionApplicability { MachineApplicable = 'MachineApplicable', HasPlaceholders = 'HasPlaceholders', MaybeIncorrect = 'MaybeIncorrect', - Unspecified = 'Unspecified' + Unspecified = 'Unspecified', +} + +export interface RustDiagnosticSpanMacroExpansion { + span: RustDiagnosticSpan; + macro_decl_name: string; + def_site_span?: RustDiagnosticSpan; } // Reference: @@ -20,6 +26,7 @@ export interface RustDiagnosticSpan { is_primary: boolean; file_name: string; label?: string; + expansion?: RustDiagnosticSpanMacroExpansion; suggested_replacement?: string; suggestion_applicability?: SuggestionApplicability; } @@ -60,16 +67,47 @@ function mapLevelToSeverity(s: string): vscode.DiagnosticSeverity { return vscode.DiagnosticSeverity.Information; } +/** + * Check whether a file name is from macro invocation + */ +function isFromMacro(fileName: string): boolean { + return fileName.startsWith('<') && fileName.endsWith('>'); +} + +/** + * Converts a Rust macro span to a VsCode location recursively + */ +function mapMacroSpanToLocation( + spanMacro: RustDiagnosticSpanMacroExpansion, +): vscode.Location | undefined { + if (!isFromMacro(spanMacro.span.file_name)) { + return mapSpanToLocation(spanMacro.span); + } + + if (spanMacro.span.expansion) { + return mapMacroSpanToLocation(spanMacro.span.expansion); + } + + return; +} + /** * Converts a Rust span to a VsCode location */ function mapSpanToLocation(span: RustDiagnosticSpan): vscode.Location { + if (isFromMacro(span.file_name) && span.expansion) { + const macroLoc = mapMacroSpanToLocation(span.expansion); + if (macroLoc) { + return macroLoc; + } + } + const fileName = path.join(vscode.workspace.rootPath || '', span.file_name); const fileUri = vscode.Uri.file(fileName); const range = new vscode.Range( new vscode.Position(span.line_start - 1, span.column_start - 1), - new vscode.Position(span.line_end - 1, span.column_end - 1) + new vscode.Position(span.line_end - 1, span.column_end - 1), ); return new vscode.Location(fileUri, range); @@ -81,7 +119,7 @@ function mapSpanToLocation(span: RustDiagnosticSpan): vscode.Location { * If the span is unlabelled this will return `undefined`. */ function mapSecondarySpanToRelated( - span: RustDiagnosticSpan + span: RustDiagnosticSpan, ): vscode.DiagnosticRelatedInformation | undefined { if (!span.label) { // Nothing to label this with @@ -107,7 +145,7 @@ function isUnusedOrUnnecessary(rd: RustDiagnostic): boolean { 'unused_attributes', 'unused_imports', 'unused_macros', - 'unused_variables' + 'unused_variables', ].includes(rd.code.code); } @@ -157,13 +195,13 @@ function mapRustChildDiagnostic(rd: RustDiagnostic): MappedRustChildDiagnostic { title, location, span.suggested_replacement, - span.suggestion_applicability - ) + span.suggestion_applicability, + ), }; } else { const related = new vscode.DiagnosticRelatedInformation( location, - rd.message + rd.message, ); return { related }; @@ -183,7 +221,7 @@ function mapRustChildDiagnostic(rd: RustDiagnostic): MappedRustChildDiagnostic { * If the diagnostic has no primary span this will return `undefined` */ export function mapRustDiagnosticToVsCode( - rd: RustDiagnostic + rd: RustDiagnostic, ): MappedRustDiagnostic | undefined { const primarySpan = rd.spans.find(s => s.is_primary); if (!primarySpan) { @@ -223,7 +261,7 @@ export function mapRustDiagnosticToVsCode( const suggestedFixes = []; for (const child of rd.children) { const { related, suggestedFix, messageLine } = mapRustChildDiagnostic( - child + child, ); if (related) { @@ -256,6 +294,6 @@ export function mapRustDiagnosticToVsCode( return { location, diagnostic: vd, - suggestedFixes + suggestedFixes, }; } diff --git a/editors/code/src/utils/diagnostics/vscode.ts b/editors/code/src/utils/diagnostics/vscode.ts index d8b85b7200..f4a5450e2b 100644 --- a/editors/code/src/utils/diagnostics/vscode.ts +++ b/editors/code/src/utils/diagnostics/vscode.ts @@ -3,7 +3,7 @@ import * as vscode from 'vscode'; /** Compares two `vscode.Diagnostic`s for equality */ export function areDiagnosticsEqual( left: vscode.Diagnostic, - right: vscode.Diagnostic + right: vscode.Diagnostic, ): boolean { return ( left.source === right.source && diff --git a/editors/code/src/utils/processes.ts b/editors/code/src/utils/processes.ts index da8be9eb17..a1d6b7eafb 100644 --- a/editors/code/src/utils/processes.ts +++ b/editors/code/src/utils/processes.ts @@ -22,7 +22,7 @@ export function terminate(process: ChildProcess, cwd?: string): boolean { // Ignore stderr since this is otherwise piped to parent.stderr // which might be already closed. const options: any = { - stdio: ['pipe', 'pipe', 'ignore'] + stdio: ['pipe', 'pipe', 'ignore'], }; if (cwd) { options.cwd = cwd; @@ -30,7 +30,7 @@ export function terminate(process: ChildProcess, cwd?: string): boolean { cp.execFileSync( 'taskkill', ['/T', '/F', '/PID', process.pid.toString()], - options + options, ); return true; } catch (err) { diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json index 9ad2e967b2..5e11c37751 100644 --- a/editors/code/tsconfig.json +++ b/editors/code/tsconfig.json @@ -1,13 +1,16 @@ { "compilerOptions": { "module": "commonjs", - "target": "es6", + "target": "es2018", "outDir": "out", - "lib": ["es6"], + "lib": ["es2018"], "sourceMap": true, "rootDir": "src", "strict": true, - "noUnusedLocals": true + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true }, "exclude": ["node_modules", ".vscode-test"] } diff --git a/editors/code/tslint.json b/editors/code/tslint.json index bdeb4895ee..f06fa5fab7 100644 --- a/editors/code/tslint.json +++ b/editors/code/tslint.json @@ -1,9 +1,15 @@ { "defaultSeverity": "error", - "extends": ["tslint:recommended", "tslint-config-prettier"], + "extends": [ + "tslint:recommended", + "tslint-config-prettier", + "tslint-plugin-prettier" + ], "rules": { - "quotemark": [true, "single"], "interface-name": false, - "object-literal-sort-keys": false + "prettier": true, + "object-literal-sort-keys": false, + // Allow `_bar` to sort with tsc's `noUnusedParameters` option + "variable-name": [true, "allow-leading-underscore"] } } diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 7332a40729..40a6682be9 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -79,7 +79,7 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> { } pub fn install_rustfmt() -> Result<()> { - run(&format!("rustup install {}", TOOLCHAIN), ".")?; + run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; run(&format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), ".") } @@ -125,7 +125,7 @@ pub fn run_clippy() -> Result<()> { } pub fn install_clippy() -> Result<()> { - run(&format!("rustup install {}", TOOLCHAIN), ".")?; + run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; run(&format!("rustup component add clippy --toolchain {}", TOOLCHAIN), ".") } @@ -144,6 +144,7 @@ pub fn run_fuzzer() -> Result<()> { } pub fn reformat_staged_files() -> Result<()> { + run_rustfmt(Mode::Overwrite)?; let root = project_root(); let output = Command::new("git") .arg("diff") diff --git a/xtask/src/main.rs b/xtask/src/main.rs index c97bfec977..4201c6a6a7 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -20,7 +20,7 @@ use xtask::{ }; // Latest stable, feel free to send a PR if this lags behind. -const REQUIRED_RUST_VERSION: u32 = 39; +const REQUIRED_RUST_VERSION: u32 = 40; struct InstallOpt { client: Option, @@ -182,7 +182,8 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { eprintln!("\nERROR: `npm --version` failed, `npm` is required to build the VS Code plugin") } - Cmd { unix: r"npm ci", windows: r"cmd.exe /c npm ci", work_dir: "./editors/code" }.run()?; + Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" } + .run()?; Cmd { unix: r"npm run package --scripts-prepend-node-path", windows: r"cmd.exe /c npm run package",