mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 21:13:37 +00:00
⬆️ rust-analyzer
This commit is contained in:
parent
61c744d4fd
commit
a2a1d99545
126 changed files with 2098 additions and 904 deletions
116
Cargo.lock
generated
116
Cargo.lock
generated
|
@ -221,6 +221,16 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "command-group"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7a8a86f409b4a59df3a3e4bee2de0b83f1755fdd2a25e3a9684c396fc4bed2c"
|
||||||
|
dependencies = [
|
||||||
|
"nix",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "countme"
|
name = "countme"
|
||||||
version = "3.0.1"
|
version = "3.0.1"
|
||||||
|
@ -300,7 +310,7 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot_core 0.9.3",
|
"parking_lot_core 0.9.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -359,14 +369,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetime"
|
name = "filetime"
|
||||||
version = "0.2.17"
|
version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c"
|
checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"windows-sys 0.36.1",
|
"windows-sys 0.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -390,6 +400,7 @@ name = "flycheck"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cargo_metadata",
|
"cargo_metadata",
|
||||||
|
"command-group",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"jod-thread",
|
"jod-thread",
|
||||||
"paths",
|
"paths",
|
||||||
|
@ -963,11 +974,24 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miow"
|
name = "miow"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7377f7792b3afb6a3cba68daa54ca23c032137010460d667fda53a8d66be00e"
|
checksum = "52ffbca2f655e33c08be35d87278e5b18b89550a37dbd598c20db92f6a471123"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.28.0",
|
"windows-sys 0.42.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.22.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1037,7 +1061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"parking_lot_core 0.9.3",
|
"parking_lot_core 0.9.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1056,15 +1080,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-sys 0.36.1",
|
"windows-sys 0.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1979,19 +2003,6 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.28.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_msvc 0.28.0",
|
|
||||||
"windows_i686_gnu 0.28.0",
|
|
||||||
"windows_i686_msvc 0.28.0",
|
|
||||||
"windows_x86_64_gnu 0.28.0",
|
|
||||||
"windows_x86_64_msvc 0.28.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
|
@ -2006,10 +2017,25 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows-sys"
|
||||||
version = "0.28.0"
|
version = "0.42.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2"
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc 0.42.0",
|
||||||
|
"windows_i686_gnu 0.42.0",
|
||||||
|
"windows_i686_msvc 0.42.0",
|
||||||
|
"windows_x86_64_gnu 0.42.0",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc 0.42.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
|
@ -2018,10 +2044,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.28.0"
|
version = "0.42.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a"
|
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
|
@ -2030,10 +2056,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_gnu"
|
||||||
version = "0.28.0"
|
version = "0.42.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64"
|
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
|
@ -2042,10 +2068,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_i686_msvc"
|
||||||
version = "0.28.0"
|
version = "0.42.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954"
|
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
|
@ -2054,10 +2080,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.28.0"
|
version = "0.42.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f"
|
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
|
@ -2065,6 +2097,12 @@ version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "write-json"
|
name = "write-json"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
@ -17,6 +17,7 @@ rustc-hash = "1.1.0"
|
||||||
serde = { version = "1.0.137", features = ["derive"] }
|
serde = { version = "1.0.137", features = ["derive"] }
|
||||||
serde_json = "1.0.86"
|
serde_json = "1.0.86"
|
||||||
jod-thread = "0.1.2"
|
jod-thread = "0.1.2"
|
||||||
|
command-group = "1.0.8"
|
||||||
|
|
||||||
toolchain = { path = "../toolchain", version = "0.0.0" }
|
toolchain = { path = "../toolchain", version = "0.0.0" }
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
|
|
|
@ -10,11 +10,12 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use command_group::{CommandGroup, GroupChild};
|
||||||
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
|
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
|
||||||
use paths::AbsPathBuf;
|
use paths::AbsPathBuf;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use stdx::{process::streaming_output, JodChild};
|
use stdx::process::streaming_output;
|
||||||
|
|
||||||
pub use cargo_metadata::diagnostic::{
|
pub use cargo_metadata::diagnostic::{
|
||||||
Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
|
Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
|
||||||
|
@ -39,7 +40,7 @@ pub enum InvocationLocation {
|
||||||
pub enum FlycheckConfig {
|
pub enum FlycheckConfig {
|
||||||
CargoCommand {
|
CargoCommand {
|
||||||
command: String,
|
command: String,
|
||||||
target_triple: Option<String>,
|
target_triples: Vec<String>,
|
||||||
all_targets: bool,
|
all_targets: bool,
|
||||||
no_default_features: bool,
|
no_default_features: bool,
|
||||||
all_features: bool,
|
all_features: bool,
|
||||||
|
@ -285,7 +286,7 @@ impl FlycheckActor {
|
||||||
let (mut cmd, args) = match &self.config {
|
let (mut cmd, args) = match &self.config {
|
||||||
FlycheckConfig::CargoCommand {
|
FlycheckConfig::CargoCommand {
|
||||||
command,
|
command,
|
||||||
target_triple,
|
target_triples,
|
||||||
no_default_features,
|
no_default_features,
|
||||||
all_targets,
|
all_targets,
|
||||||
all_features,
|
all_features,
|
||||||
|
@ -299,7 +300,7 @@ impl FlycheckActor {
|
||||||
cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
|
cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
|
||||||
.arg(self.root.join("Cargo.toml").as_os_str());
|
.arg(self.root.join("Cargo.toml").as_os_str());
|
||||||
|
|
||||||
if let Some(target) = target_triple {
|
for target in target_triples {
|
||||||
cmd.args(&["--target", target.as_str()]);
|
cmd.args(&["--target", target.as_str()]);
|
||||||
}
|
}
|
||||||
if *all_targets {
|
if *all_targets {
|
||||||
|
@ -359,10 +360,12 @@ impl FlycheckActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct JodChild(GroupChild);
|
||||||
|
|
||||||
/// A handle to a cargo process used for fly-checking.
|
/// A handle to a cargo process used for fly-checking.
|
||||||
struct CargoHandle {
|
struct CargoHandle {
|
||||||
/// The handle to the actual cargo process. As we cannot cancel directly from with
|
/// The handle to the actual cargo process. As we cannot cancel directly from with
|
||||||
/// a read syscall dropping and therefor terminating the process is our best option.
|
/// a read syscall dropping and therefore terminating the process is our best option.
|
||||||
child: JodChild,
|
child: JodChild,
|
||||||
thread: jod_thread::JoinHandle<io::Result<(bool, String)>>,
|
thread: jod_thread::JoinHandle<io::Result<(bool, String)>>,
|
||||||
receiver: Receiver<CargoMessage>,
|
receiver: Receiver<CargoMessage>,
|
||||||
|
@ -371,10 +374,10 @@ struct CargoHandle {
|
||||||
impl CargoHandle {
|
impl CargoHandle {
|
||||||
fn spawn(mut command: Command) -> std::io::Result<CargoHandle> {
|
fn spawn(mut command: Command) -> std::io::Result<CargoHandle> {
|
||||||
command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
|
command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
|
||||||
let mut child = JodChild::spawn(command)?;
|
let mut child = command.group_spawn().map(JodChild)?;
|
||||||
|
|
||||||
let stdout = child.stdout.take().unwrap();
|
let stdout = child.0.inner().stdout.take().unwrap();
|
||||||
let stderr = child.stderr.take().unwrap();
|
let stderr = child.0.inner().stderr.take().unwrap();
|
||||||
|
|
||||||
let (sender, receiver) = unbounded();
|
let (sender, receiver) = unbounded();
|
||||||
let actor = CargoActor::new(sender, stdout, stderr);
|
let actor = CargoActor::new(sender, stdout, stderr);
|
||||||
|
@ -386,13 +389,13 @@ impl CargoHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancel(mut self) {
|
fn cancel(mut self) {
|
||||||
let _ = self.child.kill();
|
let _ = self.child.0.kill();
|
||||||
let _ = self.child.wait();
|
let _ = self.child.0.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn join(mut self) -> io::Result<()> {
|
fn join(mut self) -> io::Result<()> {
|
||||||
let _ = self.child.kill();
|
let _ = self.child.0.kill();
|
||||||
let exit_status = self.child.wait()?;
|
let exit_status = self.child.0.wait()?;
|
||||||
let (read_at_least_one_message, error) = self.thread.join()?;
|
let (read_at_least_one_message, error) = self.thread.join()?;
|
||||||
if read_at_least_one_message || exit_status.success() {
|
if read_at_least_one_message || exit_status.success() {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -236,11 +236,19 @@ impl TraitData {
|
||||||
.by_key("rustc_skip_array_during_method_dispatch")
|
.by_key("rustc_skip_array_during_method_dispatch")
|
||||||
.exists();
|
.exists();
|
||||||
|
|
||||||
let mut collector =
|
let (items, attribute_calls, diagnostics) = match &tr_def.items {
|
||||||
AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr));
|
Some(items) => {
|
||||||
collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items);
|
let mut collector = AssocItemCollector::new(
|
||||||
let (items, attribute_calls, diagnostics) = collector.finish();
|
db,
|
||||||
|
module_id,
|
||||||
|
tree_id.file_id(),
|
||||||
|
ItemContainerId::TraitId(tr),
|
||||||
|
);
|
||||||
|
collector.collect(&item_tree, tree_id.tree_id(), items);
|
||||||
|
collector.finish()
|
||||||
|
}
|
||||||
|
None => Default::default(),
|
||||||
|
};
|
||||||
(
|
(
|
||||||
Arc::new(TraitData {
|
Arc::new(TraitData {
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -666,7 +666,8 @@ pub struct Trait {
|
||||||
pub generic_params: Interned<GenericParams>,
|
pub generic_params: Interned<GenericParams>,
|
||||||
pub is_auto: bool,
|
pub is_auto: bool,
|
||||||
pub is_unsafe: bool,
|
pub is_unsafe: bool,
|
||||||
pub items: Box<[AssocItem]>,
|
/// This is [`None`] if this Trait is a trait alias.
|
||||||
|
pub items: Option<Box<[AssocItem]>>,
|
||||||
pub ast_id: FileAstId<ast::Trait>,
|
pub ast_id: FileAstId<ast::Trait>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -451,15 +451,7 @@ impl<'a> Ctx<'a> {
|
||||||
.collect()
|
.collect()
|
||||||
});
|
});
|
||||||
let ast_id = self.source_ast_id_map.ast_id(trait_def);
|
let ast_id = self.source_ast_id_map.ast_id(trait_def);
|
||||||
let res = Trait {
|
let res = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id };
|
||||||
name,
|
|
||||||
visibility,
|
|
||||||
generic_params,
|
|
||||||
is_auto,
|
|
||||||
is_unsafe,
|
|
||||||
items: items.unwrap_or_default(),
|
|
||||||
ast_id,
|
|
||||||
};
|
|
||||||
Some(id(self.data().traits.alloc(res)))
|
Some(id(self.data().traits.alloc(res)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -375,12 +375,21 @@ impl<'a> Printer<'a> {
|
||||||
}
|
}
|
||||||
w!(self, "trait {}", name);
|
w!(self, "trait {}", name);
|
||||||
self.print_generic_params(generic_params);
|
self.print_generic_params(generic_params);
|
||||||
self.print_where_clause_and_opening_brace(generic_params);
|
match items {
|
||||||
self.indented(|this| {
|
Some(items) => {
|
||||||
for item in &**items {
|
self.print_where_clause_and_opening_brace(generic_params);
|
||||||
this.print_mod_item((*item).into());
|
self.indented(|this| {
|
||||||
|
for item in &**items {
|
||||||
|
this.print_mod_item((*item).into());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
None => {
|
||||||
|
w!(self, " = ");
|
||||||
|
// FIXME: Print the aliased traits
|
||||||
|
self.print_where_clause_and_opening_brace(generic_params);
|
||||||
|
}
|
||||||
|
}
|
||||||
wln!(self, "}}");
|
wln!(self, "}}");
|
||||||
}
|
}
|
||||||
ModItem::Impl(it) => {
|
ModItem::Impl(it) => {
|
||||||
|
|
|
@ -94,11 +94,11 @@ macro_rules! m {
|
||||||
($($s:stmt)*) => (stringify!($($s |)*);)
|
($($s:stmt)*) => (stringify!($($s |)*);)
|
||||||
}
|
}
|
||||||
stringify!(;
|
stringify!(;
|
||||||
|;
|
| ;
|
||||||
|92|;
|
|92| ;
|
||||||
|let x = 92|;
|
|let x = 92| ;
|
||||||
|loop {}
|
|loop {}
|
||||||
|;
|
| ;
|
||||||
|);
|
|);
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -118,7 +118,7 @@ m!(.. .. ..);
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
($($p:pat)*) => (stringify!($($p |)*);)
|
($($p:pat)*) => (stringify!($($p |)*);)
|
||||||
}
|
}
|
||||||
stringify!(.. .. ..|);
|
stringify!(.. .. .. |);
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,14 +82,14 @@ fn attribute_macro_syntax_completion_2() {
|
||||||
#[proc_macros::identity_when_valid]
|
#[proc_macros::identity_when_valid]
|
||||||
fn foo() { bar.; blub }
|
fn foo() { bar.; blub }
|
||||||
"#,
|
"#,
|
||||||
expect![[r##"
|
expect![[r#"
|
||||||
#[proc_macros::identity_when_valid]
|
#[proc_macros::identity_when_valid]
|
||||||
fn foo() { bar.; blub }
|
fn foo() { bar.; blub }
|
||||||
|
|
||||||
fn foo() {
|
fn foo() {
|
||||||
bar.;
|
bar. ;
|
||||||
blub
|
blub
|
||||||
}"##]],
|
}"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -212,6 +212,7 @@ impl Import {
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
struct ImportDirective {
|
struct ImportDirective {
|
||||||
|
/// The module this import directive is in.
|
||||||
module_id: LocalModuleId,
|
module_id: LocalModuleId,
|
||||||
import: Import,
|
import: Import,
|
||||||
status: PartialResolvedImport,
|
status: PartialResolvedImport,
|
||||||
|
@ -963,8 +964,10 @@ impl DefCollector<'_> {
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
// The module for which `resolutions` have been resolve
|
||||||
module_id: LocalModuleId,
|
module_id: LocalModuleId,
|
||||||
resolutions: &[(Option<Name>, PerNs)],
|
resolutions: &[(Option<Name>, PerNs)],
|
||||||
|
// Visibility this import will have
|
||||||
vis: Visibility,
|
vis: Visibility,
|
||||||
import_type: ImportType,
|
import_type: ImportType,
|
||||||
) {
|
) {
|
||||||
|
@ -974,6 +977,7 @@ impl DefCollector<'_> {
|
||||||
|
|
||||||
fn update_recursive(
|
fn update_recursive(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
// The module for which `resolutions` have been resolve
|
||||||
module_id: LocalModuleId,
|
module_id: LocalModuleId,
|
||||||
resolutions: &[(Option<Name>, PerNs)],
|
resolutions: &[(Option<Name>, PerNs)],
|
||||||
// All resolutions are imported with this visibility; the visibilities in
|
// All resolutions are imported with this visibility; the visibilities in
|
||||||
|
|
|
@ -73,7 +73,10 @@ impl DefMap {
|
||||||
pub(crate) fn resolve_visibility(
|
pub(crate) fn resolve_visibility(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
|
// module to import to
|
||||||
original_module: LocalModuleId,
|
original_module: LocalModuleId,
|
||||||
|
// pub(path)
|
||||||
|
// ^^^^ this
|
||||||
visibility: &RawVisibility,
|
visibility: &RawVisibility,
|
||||||
) -> Option<Visibility> {
|
) -> Option<Visibility> {
|
||||||
let mut vis = match visibility {
|
let mut vis = match visibility {
|
||||||
|
@ -115,6 +118,7 @@ impl DefMap {
|
||||||
&self,
|
&self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
mode: ResolveMode,
|
mode: ResolveMode,
|
||||||
|
// module to import to
|
||||||
mut original_module: LocalModuleId,
|
mut original_module: LocalModuleId,
|
||||||
path: &ModPath,
|
path: &ModPath,
|
||||||
shadow: BuiltinShadowMode,
|
shadow: BuiltinShadowMode,
|
||||||
|
@ -361,6 +365,9 @@ impl DefMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
curr_per_ns = curr_per_ns
|
||||||
|
.filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module));
|
||||||
}
|
}
|
||||||
|
|
||||||
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
|
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
|
||||||
|
|
|
@ -58,9 +58,9 @@ extern {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
crate
|
crate
|
||||||
E: t
|
E: _
|
||||||
S: t v
|
S: t v
|
||||||
V: t v
|
V: _
|
||||||
foo: t
|
foo: t
|
||||||
|
|
||||||
crate::foo
|
crate::foo
|
||||||
|
@ -307,7 +307,7 @@ pub struct FromLib;
|
||||||
Bar: t v
|
Bar: t v
|
||||||
|
|
||||||
crate::foo
|
crate::foo
|
||||||
Bar: t v
|
Bar: _
|
||||||
FromLib: t v
|
FromLib: t v
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
|
|
@ -119,7 +119,7 @@ use foo::*;
|
||||||
use foo::bar::*;
|
use foo::bar::*;
|
||||||
|
|
||||||
//- /foo/mod.rs
|
//- /foo/mod.rs
|
||||||
mod bar;
|
pub mod bar;
|
||||||
fn Foo() {};
|
fn Foo() {};
|
||||||
pub struct Foo {};
|
pub struct Foo {};
|
||||||
|
|
||||||
|
@ -132,6 +132,7 @@ pub(crate) struct PubCrateStruct;
|
||||||
crate
|
crate
|
||||||
Foo: t
|
Foo: t
|
||||||
PubCrateStruct: t v
|
PubCrateStruct: t v
|
||||||
|
bar: t
|
||||||
foo: t
|
foo: t
|
||||||
|
|
||||||
crate::foo
|
crate::foo
|
||||||
|
@ -336,3 +337,33 @@ mod d {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn glob_name_collision_check_visibility() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
mod event {
|
||||||
|
mod serenity {
|
||||||
|
pub fn Event() {}
|
||||||
|
}
|
||||||
|
use serenity::*;
|
||||||
|
|
||||||
|
pub struct Event {}
|
||||||
|
}
|
||||||
|
|
||||||
|
use event::Event;
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
crate
|
||||||
|
Event: t
|
||||||
|
event: t
|
||||||
|
|
||||||
|
crate::event
|
||||||
|
Event: t v
|
||||||
|
serenity: t
|
||||||
|
|
||||||
|
crate::event::serenity
|
||||||
|
Event: v
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -580,7 +580,7 @@ fn module_resolution_decl_inside_inline_module_in_crate_root() {
|
||||||
//- /main.rs
|
//- /main.rs
|
||||||
mod foo {
|
mod foo {
|
||||||
#[path = "baz.rs"]
|
#[path = "baz.rs"]
|
||||||
mod bar;
|
pub mod bar;
|
||||||
}
|
}
|
||||||
use self::foo::bar::Baz;
|
use self::foo::bar::Baz;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::mem;
|
||||||
|
|
||||||
use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
|
use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, HasLoopBody},
|
ast::{self, AstNode, HasLoopBody},
|
||||||
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
|
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
|
||||||
|
@ -292,25 +293,34 @@ pub(crate) fn reverse_fixups(
|
||||||
token_map: &TokenMap,
|
token_map: &TokenMap,
|
||||||
undo_info: &SyntaxFixupUndoInfo,
|
undo_info: &SyntaxFixupUndoInfo,
|
||||||
) {
|
) {
|
||||||
tt.token_trees.retain(|tt| match tt {
|
let tts = std::mem::take(&mut tt.token_trees);
|
||||||
tt::TokenTree::Leaf(leaf) => {
|
tt.token_trees = tts
|
||||||
token_map.synthetic_token_id(leaf.id()).is_none()
|
.into_iter()
|
||||||
|| token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID)
|
.filter(|tt| match tt {
|
||||||
}
|
tt::TokenTree::Leaf(leaf) => token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID),
|
||||||
tt::TokenTree::Subtree(st) => st.delimiter.map_or(true, |d| {
|
tt::TokenTree::Subtree(st) => {
|
||||||
token_map.synthetic_token_id(d.id).is_none()
|
st.delimiter.map_or(true, |d| token_map.synthetic_token_id(d.id) != Some(EMPTY_ID))
|
||||||
|| token_map.synthetic_token_id(d.id) != Some(EMPTY_ID)
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
tt.token_trees.iter_mut().for_each(|tt| match tt {
|
|
||||||
tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map, undo_info),
|
|
||||||
tt::TokenTree::Leaf(leaf) => {
|
|
||||||
if let Some(id) = token_map.synthetic_token_id(leaf.id()) {
|
|
||||||
let original = &undo_info.original[id.0 as usize];
|
|
||||||
*tt = tt::TokenTree::Subtree(original.clone());
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.flat_map(|tt| match tt {
|
||||||
|
tt::TokenTree::Subtree(mut tt) => {
|
||||||
|
reverse_fixups(&mut tt, token_map, undo_info);
|
||||||
|
SmallVec::from_const([tt.into()])
|
||||||
|
}
|
||||||
|
tt::TokenTree::Leaf(leaf) => {
|
||||||
|
if let Some(id) = token_map.synthetic_token_id(leaf.id()) {
|
||||||
|
let original = undo_info.original[id.0 as usize].clone();
|
||||||
|
if original.delimiter.is_none() {
|
||||||
|
original.token_trees.into()
|
||||||
|
} else {
|
||||||
|
SmallVec::from_const([original.into()])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SmallVec::from_const([leaf.into()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -319,6 +329,31 @@ mod tests {
|
||||||
|
|
||||||
use super::reverse_fixups;
|
use super::reverse_fixups;
|
||||||
|
|
||||||
|
// The following three functions are only meant to check partial structural equivalence of
|
||||||
|
// `TokenTree`s, see the last assertion in `check()`.
|
||||||
|
fn check_leaf_eq(a: &tt::Leaf, b: &tt::Leaf) -> bool {
|
||||||
|
match (a, b) {
|
||||||
|
(tt::Leaf::Literal(a), tt::Leaf::Literal(b)) => a.text == b.text,
|
||||||
|
(tt::Leaf::Punct(a), tt::Leaf::Punct(b)) => a.char == b.char,
|
||||||
|
(tt::Leaf::Ident(a), tt::Leaf::Ident(b)) => a.text == b.text,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_subtree_eq(a: &tt::Subtree, b: &tt::Subtree) -> bool {
|
||||||
|
a.delimiter.map(|it| it.kind) == b.delimiter.map(|it| it.kind)
|
||||||
|
&& a.token_trees.len() == b.token_trees.len()
|
||||||
|
&& a.token_trees.iter().zip(&b.token_trees).all(|(a, b)| check_tt_eq(a, b))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_tt_eq(a: &tt::TokenTree, b: &tt::TokenTree) -> bool {
|
||||||
|
match (a, b) {
|
||||||
|
(tt::TokenTree::Leaf(a), tt::TokenTree::Leaf(b)) => check_leaf_eq(a, b),
|
||||||
|
(tt::TokenTree::Subtree(a), tt::TokenTree::Subtree(b)) => check_subtree_eq(a, b),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check(ra_fixture: &str, mut expect: Expect) {
|
fn check(ra_fixture: &str, mut expect: Expect) {
|
||||||
let parsed = syntax::SourceFile::parse(ra_fixture);
|
let parsed = syntax::SourceFile::parse(ra_fixture);
|
||||||
|
@ -331,17 +366,15 @@ mod tests {
|
||||||
fixups.append,
|
fixups.append,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut actual = tt.to_string();
|
let actual = format!("{}\n", tt);
|
||||||
actual.push('\n');
|
|
||||||
|
|
||||||
expect.indent(false);
|
expect.indent(false);
|
||||||
expect.assert_eq(&actual);
|
expect.assert_eq(&actual);
|
||||||
|
|
||||||
// the fixed-up tree should be syntactically valid
|
// the fixed-up tree should be syntactically valid
|
||||||
let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems);
|
let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems);
|
||||||
assert_eq!(
|
assert!(
|
||||||
parse.errors(),
|
parse.errors().is_empty(),
|
||||||
&[],
|
|
||||||
"parse has syntax errors. parse tree:\n{:#?}",
|
"parse has syntax errors. parse tree:\n{:#?}",
|
||||||
parse.syntax_node()
|
parse.syntax_node()
|
||||||
);
|
);
|
||||||
|
@ -349,9 +382,12 @@ mod tests {
|
||||||
reverse_fixups(&mut tt, &tmap, &fixups.undo_info);
|
reverse_fixups(&mut tt, &tmap, &fixups.undo_info);
|
||||||
|
|
||||||
// the fixed-up + reversed version should be equivalent to the original input
|
// the fixed-up + reversed version should be equivalent to the original input
|
||||||
// (but token IDs don't matter)
|
// modulo token IDs and `Punct`s' spacing.
|
||||||
let (original_as_tt, _) = mbe::syntax_node_to_token_tree(&parsed.syntax_node());
|
let (original_as_tt, _) = mbe::syntax_node_to_token_tree(&parsed.syntax_node());
|
||||||
assert_eq!(tt.to_string(), original_as_tt.to_string());
|
assert!(
|
||||||
|
check_subtree_eq(&tt, &original_as_tt),
|
||||||
|
"different token tree: {tt:?}, {original_as_tt:?}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -468,7 +504,7 @@ fn foo() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
fn foo () {a .__ra_fixup}
|
fn foo () {a . __ra_fixup}
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -482,7 +518,7 @@ fn foo() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
fn foo () {a .__ra_fixup ;}
|
fn foo () {a . __ra_fixup ;}
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -497,7 +533,7 @@ fn foo() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
fn foo () {a .__ra_fixup ; bar () ;}
|
fn foo () {a . __ra_fixup ; bar () ;}
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -525,7 +561,7 @@ fn foo() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
fn foo () {let x = a .__ra_fixup ;}
|
fn foo () {let x = a . __ra_fixup ;}
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -541,7 +577,7 @@ fn foo() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
fn foo () {a .b ; bar () ;}
|
fn foo () {a . b ; bar () ;}
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -814,7 +814,7 @@ impl<'a> InFile<&'a SyntaxNode> {
|
||||||
|
|
||||||
pub fn original_syntax_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
|
pub fn original_syntax_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
|
||||||
// This kind of upmapping can only be achieved in attribute expanded files,
|
// This kind of upmapping can only be achieved in attribute expanded files,
|
||||||
// as we don't have node inputs otherwise and therefor can't find an `N` node in the input
|
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
|
||||||
if !self.file_id.is_macro() {
|
if !self.file_id.is_macro() {
|
||||||
return Some(self.map(Clone::clone));
|
return Some(self.map(Clone::clone));
|
||||||
} else if !self.file_id.is_attr_macro(db) {
|
} else if !self.file_id.is_attr_macro(db) {
|
||||||
|
@ -926,7 +926,7 @@ impl<N: AstNode> InFile<N> {
|
||||||
|
|
||||||
pub fn original_ast_node(self, db: &dyn db::AstDatabase) -> Option<InFile<N>> {
|
pub fn original_ast_node(self, db: &dyn db::AstDatabase) -> Option<InFile<N>> {
|
||||||
// This kind of upmapping can only be achieved in attribute expanded files,
|
// This kind of upmapping can only be achieved in attribute expanded files,
|
||||||
// as we don't have node inputs otherwise and therefor can't find an `N` node in the input
|
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
|
||||||
if !self.file_id.is_macro() {
|
if !self.file_id.is_macro() {
|
||||||
return Some(self);
|
return Some(self);
|
||||||
} else if !self.file_id.is_attr_macro(db) {
|
} else if !self.file_id.is_attr_macro(db) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -53,7 +53,7 @@ pub use builder::{ParamKind, TyBuilder};
|
||||||
pub use chalk_ext::*;
|
pub use chalk_ext::*;
|
||||||
pub use infer::{
|
pub use infer::{
|
||||||
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
|
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
|
||||||
InferenceResult,
|
InferenceResult, OverloadedDeref, PointerCast,
|
||||||
};
|
};
|
||||||
pub use interner::Interner;
|
pub use interner::Interner;
|
||||||
pub use lower::{
|
pub use lower::{
|
||||||
|
@ -523,7 +523,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn callable_sig_from_fnonce(
|
pub fn callable_sig_from_fnonce(
|
||||||
self_ty: &Canonical<Ty>,
|
self_ty: &Ty,
|
||||||
env: Arc<TraitEnvironment>,
|
env: Arc<TraitEnvironment>,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
) -> Option<CallableSig> {
|
) -> Option<CallableSig> {
|
||||||
|
@ -531,27 +531,28 @@ pub fn callable_sig_from_fnonce(
|
||||||
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
|
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
|
||||||
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
|
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
|
||||||
|
|
||||||
let mut kinds = self_ty.binders.interned().to_vec();
|
|
||||||
let b = TyBuilder::trait_ref(db, fn_once_trait);
|
let b = TyBuilder::trait_ref(db, fn_once_trait);
|
||||||
if b.remaining() != 2 {
|
if b.remaining() != 2 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let fn_once = b
|
let fn_once = b.push(self_ty.clone()).fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build();
|
||||||
.push(self_ty.value.clone())
|
let kinds = fn_once
|
||||||
.fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
|
.substitution
|
||||||
.build();
|
.iter(Interner)
|
||||||
kinds.extend(fn_once.substitution.iter(Interner).skip(1).map(|x| {
|
.skip(1)
|
||||||
let vk = match x.data(Interner) {
|
.map(|x| {
|
||||||
chalk_ir::GenericArgData::Ty(_) => {
|
let vk = match x.data(Interner) {
|
||||||
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
|
chalk_ir::GenericArgData::Ty(_) => {
|
||||||
}
|
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
|
||||||
chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
|
}
|
||||||
chalk_ir::GenericArgData::Const(c) => {
|
chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
|
||||||
chalk_ir::VariableKind::Const(c.data(Interner).ty.clone())
|
chalk_ir::GenericArgData::Const(c) => {
|
||||||
}
|
chalk_ir::VariableKind::Const(c.data(Interner).ty.clone())
|
||||||
};
|
}
|
||||||
chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
|
};
|
||||||
}));
|
chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// FIXME: chalk refuses to solve `<Self as FnOnce<^0.0>>::Output == ^0.1`, so we first solve
|
// FIXME: chalk refuses to solve `<Self as FnOnce<^0.0>>::Output == ^0.1`, so we first solve
|
||||||
// `<Self as FnOnce<^0.0>>` and then replace `^0.0` with the concrete argument tuple.
|
// `<Self as FnOnce<^0.0>>` and then replace `^0.0` with the concrete argument tuple.
|
||||||
|
@ -563,21 +564,16 @@ pub fn callable_sig_from_fnonce(
|
||||||
Some(Solution::Unique(vars)) => vars.value.subst,
|
Some(Solution::Unique(vars)) => vars.value.subst,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
let args = subst.at(Interner, self_ty.binders.interned().len()).ty(Interner)?;
|
let args = subst.at(Interner, 0).ty(Interner)?;
|
||||||
let params = match args.kind(Interner) {
|
let params = match args.kind(Interner) {
|
||||||
chalk_ir::TyKind::Tuple(_, subst) => {
|
chalk_ir::TyKind::Tuple(_, subst) => {
|
||||||
subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::<Vec<_>>()
|
subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
if params.iter().any(|ty| ty.is_unknown()) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let fn_once = TyBuilder::trait_ref(db, fn_once_trait)
|
let fn_once =
|
||||||
.push(self_ty.value.clone())
|
TyBuilder::trait_ref(db, fn_once_trait).push(self_ty.clone()).push(args.clone()).build();
|
||||||
.push(args.clone())
|
|
||||||
.build();
|
|
||||||
let projection =
|
let projection =
|
||||||
TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone()))
|
TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone()))
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -541,7 +541,7 @@ pub struct ReceiverAdjustments {
|
||||||
|
|
||||||
impl ReceiverAdjustments {
|
impl ReceiverAdjustments {
|
||||||
pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec<Adjustment>) {
|
pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec<Adjustment>) {
|
||||||
let mut ty = ty;
|
let mut ty = table.resolve_ty_shallow(&ty);
|
||||||
let mut adjust = Vec::new();
|
let mut adjust = Vec::new();
|
||||||
for _ in 0..self.autoderefs {
|
for _ in 0..self.autoderefs {
|
||||||
match autoderef::autoderef_step(table, ty.clone()) {
|
match autoderef::autoderef_step(table, ty.clone()) {
|
||||||
|
|
|
@ -164,16 +164,16 @@ fn infer_associated_method_with_modules() {
|
||||||
check_infer(
|
check_infer(
|
||||||
r#"
|
r#"
|
||||||
mod a {
|
mod a {
|
||||||
struct A;
|
pub struct A;
|
||||||
impl A { pub fn thing() -> A { A {} }}
|
impl A { pub fn thing() -> A { A {} }}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod b {
|
mod b {
|
||||||
struct B;
|
pub struct B;
|
||||||
impl B { pub fn thing() -> u32 { 99 }}
|
impl B { pub fn thing() -> u32 { 99 }}
|
||||||
|
|
||||||
mod c {
|
pub mod c {
|
||||||
struct C;
|
pub struct C;
|
||||||
impl C { pub fn thing() -> C { C {} }}
|
impl C { pub fn thing() -> C { C {} }}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,22 +186,22 @@ fn infer_associated_method_with_modules() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
55..63 '{ A {} }': A
|
59..67 '{ A {} }': A
|
||||||
57..61 'A {}': A
|
61..65 'A {}': A
|
||||||
125..131 '{ 99 }': u32
|
133..139 '{ 99 }': u32
|
||||||
127..129 '99': u32
|
135..137 '99': u32
|
||||||
201..209 '{ C {} }': C
|
217..225 '{ C {} }': C
|
||||||
203..207 'C {}': C
|
219..223 'C {}': C
|
||||||
240..324 '{ ...g(); }': ()
|
256..340 '{ ...g(); }': ()
|
||||||
250..251 'x': A
|
266..267 'x': A
|
||||||
254..265 'a::A::thing': fn thing() -> A
|
270..281 'a::A::thing': fn thing() -> A
|
||||||
254..267 'a::A::thing()': A
|
270..283 'a::A::thing()': A
|
||||||
277..278 'y': u32
|
293..294 'y': u32
|
||||||
281..292 'b::B::thing': fn thing() -> u32
|
297..308 'b::B::thing': fn thing() -> u32
|
||||||
281..294 'b::B::thing()': u32
|
297..310 'b::B::thing()': u32
|
||||||
304..305 'z': C
|
320..321 'z': C
|
||||||
308..319 'c::C::thing': fn thing() -> C
|
324..335 'c::C::thing': fn thing() -> C
|
||||||
308..321 'c::C::thing()': C
|
324..337 'c::C::thing()': C
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1707,3 +1707,19 @@ impl<T, const N: usize> Trait for [T; N] {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unsize_array_with_inference_variable() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
//- minicore: try, slice
|
||||||
|
use core::ops::ControlFlow;
|
||||||
|
fn foo() -> ControlFlow<(), [usize; 1]> { loop {} }
|
||||||
|
fn bar() -> ControlFlow<(), ()> {
|
||||||
|
let a = foo()?.len();
|
||||||
|
//^ usize
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -214,7 +214,7 @@ fn infer_paths() {
|
||||||
fn a() -> u32 { 1 }
|
fn a() -> u32 { 1 }
|
||||||
|
|
||||||
mod b {
|
mod b {
|
||||||
fn c() -> u32 { 1 }
|
pub fn c() -> u32 { 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
|
@ -225,13 +225,13 @@ fn test() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
14..19 '{ 1 }': u32
|
14..19 '{ 1 }': u32
|
||||||
16..17 '1': u32
|
16..17 '1': u32
|
||||||
47..52 '{ 1 }': u32
|
51..56 '{ 1 }': u32
|
||||||
49..50 '1': u32
|
53..54 '1': u32
|
||||||
66..90 '{ ...c(); }': ()
|
70..94 '{ ...c(); }': ()
|
||||||
72..73 'a': fn a() -> u32
|
76..77 'a': fn a() -> u32
|
||||||
72..75 'a()': u32
|
76..79 'a()': u32
|
||||||
81..85 'b::c': fn c() -> u32
|
85..89 'b::c': fn c() -> u32
|
||||||
81..87 'b::c()': u32
|
85..91 'b::c()': u32
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1856,7 +1856,7 @@ fn not_shadowing_module_by_primitive() {
|
||||||
check_types(
|
check_types(
|
||||||
r#"
|
r#"
|
||||||
//- /str.rs
|
//- /str.rs
|
||||||
fn foo() -> u32 {0}
|
pub fn foo() -> u32 {0}
|
||||||
|
|
||||||
//- /main.rs
|
//- /main.rs
|
||||||
mod str;
|
mod str;
|
||||||
|
|
|
@ -1706,7 +1706,7 @@ fn where_clause_trait_in_scope_for_method_resolution() {
|
||||||
check_types(
|
check_types(
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
trait Trait {
|
pub trait Trait {
|
||||||
fn foo(&self) -> u32 { 0 }
|
fn foo(&self) -> u32 { 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1723,7 +1723,7 @@ fn super_trait_method_resolution() {
|
||||||
check_infer(
|
check_infer(
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
trait SuperTrait {
|
pub trait SuperTrait {
|
||||||
fn foo(&self) -> u32 {}
|
fn foo(&self) -> u32 {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1735,15 +1735,15 @@ fn test<T: Trait1, U: Trait2>(x: T, y: U) {
|
||||||
y.foo();
|
y.foo();
|
||||||
}"#,
|
}"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
49..53 'self': &Self
|
53..57 'self': &Self
|
||||||
62..64 '{}': u32
|
66..68 '{}': u32
|
||||||
181..182 'x': T
|
185..186 'x': T
|
||||||
187..188 'y': U
|
191..192 'y': U
|
||||||
193..222 '{ ...o(); }': ()
|
197..226 '{ ...o(); }': ()
|
||||||
199..200 'x': T
|
203..204 'x': T
|
||||||
199..206 'x.foo()': u32
|
203..210 'x.foo()': u32
|
||||||
212..213 'y': U
|
216..217 'y': U
|
||||||
212..219 'y.foo()': u32
|
216..223 'y.foo()': u32
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1754,7 +1754,7 @@ fn super_trait_impl_trait_method_resolution() {
|
||||||
r#"
|
r#"
|
||||||
//- minicore: sized
|
//- minicore: sized
|
||||||
mod foo {
|
mod foo {
|
||||||
trait SuperTrait {
|
pub trait SuperTrait {
|
||||||
fn foo(&self) -> u32 {}
|
fn foo(&self) -> u32 {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1764,12 +1764,12 @@ fn test(x: &impl Trait1) {
|
||||||
x.foo();
|
x.foo();
|
||||||
}"#,
|
}"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
49..53 'self': &Self
|
53..57 'self': &Self
|
||||||
62..64 '{}': u32
|
66..68 '{}': u32
|
||||||
115..116 'x': &impl Trait1
|
119..120 'x': &impl Trait1
|
||||||
132..148 '{ ...o(); }': ()
|
136..152 '{ ...o(); }': ()
|
||||||
138..139 'x': &impl Trait1
|
142..143 'x': &impl Trait1
|
||||||
138..145 'x.foo()': u32
|
142..149 'x.foo()': u32
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -117,7 +117,7 @@ pub use {
|
||||||
name::{known, Name},
|
name::{known, Name},
|
||||||
ExpandResult, HirFileId, InFile, MacroFile, Origin,
|
ExpandResult, HirFileId, InFile, MacroFile, Origin,
|
||||||
},
|
},
|
||||||
hir_ty::display::HirDisplay,
|
hir_ty::{display::HirDisplay, PointerCast, Safety},
|
||||||
};
|
};
|
||||||
|
|
||||||
// These are negative re-exports: pub using these names is forbidden, they
|
// These are negative re-exports: pub using these names is forbidden, they
|
||||||
|
@ -2997,8 +2997,7 @@ impl Type {
|
||||||
TyKind::Function(_) => Callee::FnPtr,
|
TyKind::Function(_) => Callee::FnPtr,
|
||||||
TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
|
TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
|
||||||
_ => {
|
_ => {
|
||||||
let ty = hir_ty::replace_errors_with_variables(&self.ty);
|
let sig = hir_ty::callable_sig_from_fnonce(&self.ty, self.env.clone(), db)?;
|
||||||
let sig = hir_ty::callable_sig_from_fnonce(&ty, self.env.clone(), db)?;
|
|
||||||
return Some(Callable {
|
return Some(Callable {
|
||||||
ty: self.clone(),
|
ty: self.clone(),
|
||||||
sig,
|
sig,
|
||||||
|
@ -3651,6 +3650,28 @@ impl From<ItemInNs> for ScopeDef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Adjust {
|
||||||
|
/// Go from ! to any type.
|
||||||
|
NeverToAny,
|
||||||
|
/// Dereference once, producing a place.
|
||||||
|
Deref(Option<OverloadedDeref>),
|
||||||
|
/// Take the address and produce either a `&` or `*` pointer.
|
||||||
|
Borrow(AutoBorrow),
|
||||||
|
Pointer(PointerCast),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum AutoBorrow {
|
||||||
|
/// Converts from T to &T.
|
||||||
|
Ref(Mutability),
|
||||||
|
/// Converts from T to *T.
|
||||||
|
RawPtr(Mutability),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct OverloadedDeref(pub Mutability);
|
||||||
|
|
||||||
pub trait HasVisibility {
|
pub trait HasVisibility {
|
||||||
fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
|
fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
|
||||||
fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
|
fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
|
||||||
|
|
|
@ -29,9 +29,10 @@ use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||||
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
||||||
Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function,
|
Access, Adjust, AutoBorrow, BindingMode, BuiltinAttr, Callable, ConstParam, Crate,
|
||||||
HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
|
DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local,
|
||||||
Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
|
Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, ToolModule, Trait, Type,
|
||||||
|
TypeAlias, TypeParam, VariantDef,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -333,9 +334,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
||||||
self.imp.resolve_trait(trait_)
|
self.imp.resolve_trait(trait_)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Figure out a nice interface to inspect adjustments
|
pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjust>> {
|
||||||
pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
|
self.imp.expr_adjustments(expr)
|
||||||
self.imp.is_implicit_reborrow(expr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
|
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
|
||||||
|
@ -1067,8 +1067,29 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
|
fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjust>> {
|
||||||
self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr)
|
let mutability = |m| match m {
|
||||||
|
hir_ty::Mutability::Not => Mutability::Shared,
|
||||||
|
hir_ty::Mutability::Mut => Mutability::Mut,
|
||||||
|
};
|
||||||
|
self.analyze(expr.syntax())?.expr_adjustments(self.db, expr).map(|it| {
|
||||||
|
it.iter()
|
||||||
|
.map(|adjust| match adjust.kind {
|
||||||
|
hir_ty::Adjust::NeverToAny => Adjust::NeverToAny,
|
||||||
|
hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => {
|
||||||
|
Adjust::Deref(Some(OverloadedDeref(mutability(m))))
|
||||||
|
}
|
||||||
|
hir_ty::Adjust::Deref(None) => Adjust::Deref(None),
|
||||||
|
hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
|
||||||
|
Adjust::Borrow(AutoBorrow::RawPtr(mutability(m)))
|
||||||
|
}
|
||||||
|
hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::Ref(m)) => {
|
||||||
|
Adjust::Borrow(AutoBorrow::Ref(mutability(m)))
|
||||||
|
}
|
||||||
|
hir_ty::Adjust::Pointer(pc) => Adjust::Pointer(pc),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
|
fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
|
||||||
|
|
|
@ -38,8 +38,7 @@ use hir_ty::{
|
||||||
UnsafeExpr,
|
UnsafeExpr,
|
||||||
},
|
},
|
||||||
method_resolution::{self, lang_names_for_bin_op},
|
method_resolution::{self, lang_names_for_bin_op},
|
||||||
Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
|
Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext,
|
||||||
TyLoweringContext,
|
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -156,21 +155,14 @@ impl SourceAnalyzer {
|
||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_implicit_reborrow(
|
pub(crate) fn expr_adjustments(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
expr: &ast::Expr,
|
expr: &ast::Expr,
|
||||||
) -> Option<Mutability> {
|
) -> Option<&[Adjustment]> {
|
||||||
let expr_id = self.expr_id(db, expr)?;
|
let expr_id = self.expr_id(db, expr)?;
|
||||||
let infer = self.infer.as_ref()?;
|
let infer = self.infer.as_ref()?;
|
||||||
let adjustments = infer.expr_adjustments.get(&expr_id)?;
|
infer.expr_adjustments.get(&expr_id).map(|v| &**v)
|
||||||
adjustments.windows(2).find_map(|slice| match slice {
|
|
||||||
&[Adjustment {kind: Adjust::Deref(None), ..}, Adjustment {kind: Adjust::Borrow(AutoBorrow::Ref(m)), ..}] => Some(match m {
|
|
||||||
hir_ty::Mutability::Mut => Mutability::Mut,
|
|
||||||
hir_ty::Mutability::Not => Mutability::Shared,
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn type_of_expr(
|
pub(crate) fn type_of_expr(
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -196,6 +196,7 @@ trait Foo {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
const CONST: usize = 42;
|
const CONST: usize = 42;
|
||||||
|
const CONST_2: i32;
|
||||||
|
|
||||||
fn foo(&self);
|
fn foo(&self);
|
||||||
fn bar(&self);
|
fn bar(&self);
|
||||||
|
@ -213,6 +214,7 @@ trait Foo {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
const CONST: usize = 42;
|
const CONST: usize = 42;
|
||||||
|
const CONST_2: i32;
|
||||||
|
|
||||||
fn foo(&self);
|
fn foo(&self);
|
||||||
fn bar(&self);
|
fn bar(&self);
|
||||||
|
@ -226,7 +228,7 @@ impl Foo for S {
|
||||||
|
|
||||||
$0type Output;
|
$0type Output;
|
||||||
|
|
||||||
const CONST: usize = 42;
|
const CONST_2: i32;
|
||||||
|
|
||||||
fn foo(&self) {
|
fn foo(&self) {
|
||||||
todo!()
|
todo!()
|
||||||
|
@ -379,14 +381,14 @@ impl Foo for S {
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
pub struct Bar;
|
pub struct Bar;
|
||||||
trait Foo { fn foo(&self, bar: Bar); }
|
pub trait Foo { fn foo(&self, bar: Bar); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo for S { $0 }"#,
|
impl foo::Foo for S { $0 }"#,
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
pub struct Bar;
|
pub struct Bar;
|
||||||
trait Foo { fn foo(&self, bar: Bar); }
|
pub trait Foo { fn foo(&self, bar: Bar); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo for S {
|
impl foo::Foo for S {
|
||||||
|
@ -439,14 +441,14 @@ impl bar::Foo for S {
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
pub struct Bar<T>;
|
pub struct Bar<T>;
|
||||||
trait Foo { fn foo(&self, bar: Bar<u32>); }
|
pub trait Foo { fn foo(&self, bar: Bar<u32>); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo for S { $0 }"#,
|
impl foo::Foo for S { $0 }"#,
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
pub struct Bar<T>;
|
pub struct Bar<T>;
|
||||||
trait Foo { fn foo(&self, bar: Bar<u32>); }
|
pub trait Foo { fn foo(&self, bar: Bar<u32>); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo for S {
|
impl foo::Foo for S {
|
||||||
|
@ -464,14 +466,14 @@ impl foo::Foo for S {
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
pub struct Bar<T>;
|
pub struct Bar<T>;
|
||||||
trait Foo<T> { fn foo(&self, bar: Bar<T>); }
|
pub trait Foo<T> { fn foo(&self, bar: Bar<T>); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo<u32> for S { $0 }"#,
|
impl foo::Foo<u32> for S { $0 }"#,
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
pub struct Bar<T>;
|
pub struct Bar<T>;
|
||||||
trait Foo<T> { fn foo(&self, bar: Bar<T>); }
|
pub trait Foo<T> { fn foo(&self, bar: Bar<T>); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo<u32> for S {
|
impl foo::Foo<u32> for S {
|
||||||
|
@ -489,7 +491,7 @@ impl foo::Foo<u32> for S {
|
||||||
add_missing_impl_members,
|
add_missing_impl_members,
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
trait Foo<T> { fn foo(&self, bar: T); }
|
pub trait Foo<T> { fn foo(&self, bar: T); }
|
||||||
pub struct Param;
|
pub struct Param;
|
||||||
}
|
}
|
||||||
struct Param;
|
struct Param;
|
||||||
|
@ -497,7 +499,7 @@ struct S;
|
||||||
impl foo::Foo<Param> for S { $0 }"#,
|
impl foo::Foo<Param> for S { $0 }"#,
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
trait Foo<T> { fn foo(&self, bar: T); }
|
pub trait Foo<T> { fn foo(&self, bar: T); }
|
||||||
pub struct Param;
|
pub struct Param;
|
||||||
}
|
}
|
||||||
struct Param;
|
struct Param;
|
||||||
|
@ -518,7 +520,7 @@ impl foo::Foo<Param> for S {
|
||||||
mod foo {
|
mod foo {
|
||||||
pub struct Bar<T>;
|
pub struct Bar<T>;
|
||||||
impl Bar<T> { type Assoc = u32; }
|
impl Bar<T> { type Assoc = u32; }
|
||||||
trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
|
pub trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo for S { $0 }"#,
|
impl foo::Foo for S { $0 }"#,
|
||||||
|
@ -526,7 +528,7 @@ impl foo::Foo for S { $0 }"#,
|
||||||
mod foo {
|
mod foo {
|
||||||
pub struct Bar<T>;
|
pub struct Bar<T>;
|
||||||
impl Bar<T> { type Assoc = u32; }
|
impl Bar<T> { type Assoc = u32; }
|
||||||
trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
|
pub trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo for S {
|
impl foo::Foo for S {
|
||||||
|
@ -545,7 +547,7 @@ impl foo::Foo for S {
|
||||||
mod foo {
|
mod foo {
|
||||||
pub struct Bar<T>;
|
pub struct Bar<T>;
|
||||||
pub struct Baz;
|
pub struct Baz;
|
||||||
trait Foo { fn foo(&self, bar: Bar<Baz>); }
|
pub trait Foo { fn foo(&self, bar: Bar<Baz>); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo for S { $0 }"#,
|
impl foo::Foo for S { $0 }"#,
|
||||||
|
@ -553,7 +555,7 @@ impl foo::Foo for S { $0 }"#,
|
||||||
mod foo {
|
mod foo {
|
||||||
pub struct Bar<T>;
|
pub struct Bar<T>;
|
||||||
pub struct Baz;
|
pub struct Baz;
|
||||||
trait Foo { fn foo(&self, bar: Bar<Baz>); }
|
pub trait Foo { fn foo(&self, bar: Bar<Baz>); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo for S {
|
impl foo::Foo for S {
|
||||||
|
@ -571,14 +573,14 @@ impl foo::Foo for S {
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
pub trait Fn<Args> { type Output; }
|
pub trait Fn<Args> { type Output; }
|
||||||
trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
|
pub trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo for S { $0 }"#,
|
impl foo::Foo for S { $0 }"#,
|
||||||
r#"
|
r#"
|
||||||
mod foo {
|
mod foo {
|
||||||
pub trait Fn<Args> { type Output; }
|
pub trait Fn<Args> { type Output; }
|
||||||
trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
|
pub trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl foo::Foo for S {
|
impl foo::Foo for S {
|
||||||
|
@ -658,6 +660,7 @@ trait Foo {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
const CONST: usize = 42;
|
const CONST: usize = 42;
|
||||||
|
const CONST_2: i32;
|
||||||
|
|
||||||
fn valid(some: u32) -> bool { false }
|
fn valid(some: u32) -> bool { false }
|
||||||
fn foo(some: u32) -> bool;
|
fn foo(some: u32) -> bool;
|
||||||
|
@ -669,13 +672,16 @@ trait Foo {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
const CONST: usize = 42;
|
const CONST: usize = 42;
|
||||||
|
const CONST_2: i32;
|
||||||
|
|
||||||
fn valid(some: u32) -> bool { false }
|
fn valid(some: u32) -> bool { false }
|
||||||
fn foo(some: u32) -> bool;
|
fn foo(some: u32) -> bool;
|
||||||
}
|
}
|
||||||
struct S;
|
struct S;
|
||||||
impl Foo for S {
|
impl Foo for S {
|
||||||
$0fn valid(some: u32) -> bool { false }
|
$0const CONST: usize = 42;
|
||||||
|
|
||||||
|
fn valid(some: u32) -> bool { false }
|
||||||
}"#,
|
}"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,8 +109,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
|
||||||
let params =
|
let params =
|
||||||
body.extracted_function_params(ctx, &container_info, locals_used.iter().copied());
|
body.extracted_function_params(ctx, &container_info, locals_used.iter().copied());
|
||||||
|
|
||||||
let extracted_from_trait_impl = body.extracted_from_trait_impl();
|
|
||||||
|
|
||||||
let name = make_function_name(&semantics_scope);
|
let name = make_function_name(&semantics_scope);
|
||||||
|
|
||||||
let fun = Function {
|
let fun = Function {
|
||||||
|
@ -129,8 +127,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
|
||||||
|
|
||||||
builder.replace(target_range, make_call(ctx, &fun, old_indent));
|
builder.replace(target_range, make_call(ctx, &fun, old_indent));
|
||||||
|
|
||||||
|
let has_impl_wrapper =
|
||||||
|
insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after);
|
||||||
|
|
||||||
let fn_def = match fun.self_param_adt(ctx) {
|
let fn_def = match fun.self_param_adt(ctx) {
|
||||||
Some(adt) if extracted_from_trait_impl => {
|
Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => {
|
||||||
let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1);
|
let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1);
|
||||||
generate_impl_text(&adt, &fn_def).replace("{\n\n", "{")
|
generate_impl_text(&adt, &fn_def).replace("{\n\n", "{")
|
||||||
}
|
}
|
||||||
|
@ -272,7 +273,7 @@ enum FunType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where to put extracted function definition
|
/// Where to put extracted function definition
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
enum Anchor {
|
enum Anchor {
|
||||||
/// Extract free function and put right after current top-level function
|
/// Extract free function and put right after current top-level function
|
||||||
Freestanding,
|
Freestanding,
|
||||||
|
@ -1245,6 +1246,14 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNod
|
||||||
while let Some(next_ancestor) = ancestors.next() {
|
while let Some(next_ancestor) = ancestors.next() {
|
||||||
match next_ancestor.kind() {
|
match next_ancestor.kind() {
|
||||||
SyntaxKind::SOURCE_FILE => break,
|
SyntaxKind::SOURCE_FILE => break,
|
||||||
|
SyntaxKind::IMPL => {
|
||||||
|
if body.extracted_from_trait_impl() && matches!(anchor, Anchor::Method) {
|
||||||
|
let impl_node = find_non_trait_impl(&next_ancestor);
|
||||||
|
if let target_node @ Some(_) = impl_node.as_ref().and_then(last_impl_member) {
|
||||||
|
return target_node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
SyntaxKind::ITEM_LIST if !matches!(anchor, Anchor::Freestanding) => continue,
|
SyntaxKind::ITEM_LIST if !matches!(anchor, Anchor::Freestanding) => continue,
|
||||||
SyntaxKind::ITEM_LIST => {
|
SyntaxKind::ITEM_LIST => {
|
||||||
if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) {
|
if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) {
|
||||||
|
@ -1265,6 +1274,29 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNod
|
||||||
last_ancestor
|
last_ancestor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_non_trait_impl(trait_impl: &SyntaxNode) -> Option<ast::Impl> {
|
||||||
|
let as_impl = ast::Impl::cast(trait_impl.clone())?;
|
||||||
|
let impl_type = Some(impl_type_name(&as_impl)?);
|
||||||
|
|
||||||
|
let sibblings = trait_impl.parent()?.children();
|
||||||
|
sibblings
|
||||||
|
.filter_map(ast::Impl::cast)
|
||||||
|
.find(|s| impl_type_name(s) == impl_type && !is_trait_impl(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_impl_member(impl_node: &ast::Impl) -> Option<SyntaxNode> {
|
||||||
|
let last_child = impl_node.assoc_item_list()?.assoc_items().last()?;
|
||||||
|
Some(last_child.syntax().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_trait_impl(node: &ast::Impl) -> bool {
|
||||||
|
node.trait_().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_type_name(impl_node: &ast::Impl) -> Option<String> {
|
||||||
|
Some(impl_node.self_ty()?.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> String {
|
fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> String {
|
||||||
let ret_ty = fun.return_type(ctx);
|
let ret_ty = fun.return_type(ctx);
|
||||||
|
|
||||||
|
@ -5051,6 +5083,236 @@ impl Struct {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extract_method_from_trait_with_existing_non_empty_impl_block() {
|
||||||
|
check_assist(
|
||||||
|
extract_function,
|
||||||
|
r#"
|
||||||
|
struct Struct(i32);
|
||||||
|
trait Trait {
|
||||||
|
fn bar(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
fn foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Struct {
|
||||||
|
fn bar(&self) -> i32 {
|
||||||
|
$0self.0 + 2$0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Struct(i32);
|
||||||
|
trait Trait {
|
||||||
|
fn bar(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
fn foo() {}
|
||||||
|
|
||||||
|
fn $0fun_name(&self) -> i32 {
|
||||||
|
self.0 + 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Struct {
|
||||||
|
fn bar(&self) -> i32 {
|
||||||
|
self.fun_name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extract_function_from_trait_with_existing_non_empty_impl_block() {
|
||||||
|
check_assist(
|
||||||
|
extract_function,
|
||||||
|
r#"
|
||||||
|
struct Struct(i32);
|
||||||
|
trait Trait {
|
||||||
|
fn bar(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
fn foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Struct {
|
||||||
|
fn bar(&self) -> i32 {
|
||||||
|
let three_squared = $03 * 3$0;
|
||||||
|
self.0 + three_squared
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Struct(i32);
|
||||||
|
trait Trait {
|
||||||
|
fn bar(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
fn foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Struct {
|
||||||
|
fn bar(&self) -> i32 {
|
||||||
|
let three_squared = fun_name();
|
||||||
|
self.0 + three_squared
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn $0fun_name() -> i32 {
|
||||||
|
3 * 3
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extract_method_from_trait_with_multiple_existing_impl_blocks() {
|
||||||
|
check_assist(
|
||||||
|
extract_function,
|
||||||
|
r#"
|
||||||
|
struct Struct(i32);
|
||||||
|
struct StructBefore(i32);
|
||||||
|
struct StructAfter(i32);
|
||||||
|
trait Trait {
|
||||||
|
fn bar(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructBefore {
|
||||||
|
fn foo(){}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
fn foo(){}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructAfter {
|
||||||
|
fn foo(){}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Struct {
|
||||||
|
fn bar(&self) -> i32 {
|
||||||
|
$0self.0 + 2$0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Struct(i32);
|
||||||
|
struct StructBefore(i32);
|
||||||
|
struct StructAfter(i32);
|
||||||
|
trait Trait {
|
||||||
|
fn bar(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructBefore {
|
||||||
|
fn foo(){}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
fn foo(){}
|
||||||
|
|
||||||
|
fn $0fun_name(&self) -> i32 {
|
||||||
|
self.0 + 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructAfter {
|
||||||
|
fn foo(){}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Struct {
|
||||||
|
fn bar(&self) -> i32 {
|
||||||
|
self.fun_name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extract_method_from_trait_with_multiple_existing_trait_impl_blocks() {
|
||||||
|
check_assist(
|
||||||
|
extract_function,
|
||||||
|
r#"
|
||||||
|
struct Struct(i32);
|
||||||
|
trait Trait {
|
||||||
|
fn bar(&self) -> i32;
|
||||||
|
}
|
||||||
|
trait TraitBefore {
|
||||||
|
fn before(&self) -> i32;
|
||||||
|
}
|
||||||
|
trait TraitAfter {
|
||||||
|
fn after(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TraitBefore for Struct {
|
||||||
|
fn before(&self) -> i32 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
fn foo(){}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TraitAfter for Struct {
|
||||||
|
fn after(&self) -> i32 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Struct {
|
||||||
|
fn bar(&self) -> i32 {
|
||||||
|
$0self.0 + 2$0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Struct(i32);
|
||||||
|
trait Trait {
|
||||||
|
fn bar(&self) -> i32;
|
||||||
|
}
|
||||||
|
trait TraitBefore {
|
||||||
|
fn before(&self) -> i32;
|
||||||
|
}
|
||||||
|
trait TraitAfter {
|
||||||
|
fn after(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TraitBefore for Struct {
|
||||||
|
fn before(&self) -> i32 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
fn foo(){}
|
||||||
|
|
||||||
|
fn $0fun_name(&self) -> i32 {
|
||||||
|
self.0 + 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TraitAfter for Struct {
|
||||||
|
fn after(&self) -> i32 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Struct {
|
||||||
|
fn bar(&self) -> i32 {
|
||||||
|
self.fun_name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn closure_arguments() {
|
fn closure_arguments() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
|
use hir::{db::HirDatabase, HasSource, HasVisibility, ModuleDef, PathResolution, ScopeDef};
|
||||||
use ide_db::base_db::FileId;
|
use ide_db::base_db::FileId;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasVisibility as _},
|
ast::{self, HasVisibility as _},
|
||||||
|
@ -18,7 +18,7 @@ use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
|
||||||
// fn frobnicate() {}
|
// fn frobnicate() {}
|
||||||
// }
|
// }
|
||||||
// fn main() {
|
// fn main() {
|
||||||
// m::frobnicate$0() {}
|
// m::frobnicate$0();
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
// ->
|
// ->
|
||||||
|
@ -27,7 +27,7 @@ use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
|
||||||
// $0pub(crate) fn frobnicate() {}
|
// $0pub(crate) fn frobnicate() {}
|
||||||
// }
|
// }
|
||||||
// fn main() {
|
// fn main() {
|
||||||
// m::frobnicate() {}
|
// m::frobnicate();
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
|
@ -37,11 +37,15 @@ pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
|
||||||
|
|
||||||
fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
let path: ast::Path = ctx.find_node_at_offset()?;
|
let path: ast::Path = ctx.find_node_at_offset()?;
|
||||||
let path_res = ctx.sema.resolve_path(&path)?;
|
let qualifier = path.qualifier()?;
|
||||||
let def = match path_res {
|
let name_ref = path.segment()?.name_ref()?;
|
||||||
PathResolution::Def(def) => def,
|
let qualifier_res = ctx.sema.resolve_path(&qualifier)?;
|
||||||
_ => return None,
|
let PathResolution::Def(ModuleDef::Module(module)) = qualifier_res else { return None; };
|
||||||
};
|
let (_, def) = module
|
||||||
|
.scope(ctx.db(), None)
|
||||||
|
.into_iter()
|
||||||
|
.find(|(name, _)| name.to_smol_str() == name_ref.text().as_str())?;
|
||||||
|
let ScopeDef::ModuleDef(def) = def else { return None; };
|
||||||
|
|
||||||
let current_module = ctx.sema.scope(path.syntax())?.module();
|
let current_module = ctx.sema.scope(path.syntax())?.module();
|
||||||
let target_module = def.module(ctx.db())?;
|
let target_module = def.module(ctx.db())?;
|
||||||
|
|
|
@ -261,12 +261,12 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /foo.rs
|
//- /foo.rs
|
||||||
enum Foo {
|
pub enum Foo {
|
||||||
Bar,
|
Bar,
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
enum Foo {
|
pub enum Foo {
|
||||||
Bar,
|
Bar,
|
||||||
Baz,
|
Baz,
|
||||||
}
|
}
|
||||||
|
@ -310,7 +310,7 @@ fn main() {
|
||||||
generate_enum_variant,
|
generate_enum_variant,
|
||||||
r"
|
r"
|
||||||
mod m {
|
mod m {
|
||||||
enum Foo {
|
pub enum Foo {
|
||||||
Bar,
|
Bar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,7 +320,7 @@ fn main() {
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
mod m {
|
mod m {
|
||||||
enum Foo {
|
pub enum Foo {
|
||||||
Bar,
|
Bar,
|
||||||
Baz,
|
Baz,
|
||||||
}
|
}
|
||||||
|
@ -516,10 +516,10 @@ mod foo;
|
||||||
use foo::Foo::Bar$0;
|
use foo::Foo::Bar$0;
|
||||||
|
|
||||||
//- /foo.rs
|
//- /foo.rs
|
||||||
enum Foo {}
|
pub enum Foo {}
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
enum Foo {
|
pub enum Foo {
|
||||||
Bar,
|
Bar,
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
|
|
|
@ -1324,7 +1324,7 @@ fn foo() {
|
||||||
generate_function,
|
generate_function,
|
||||||
r"
|
r"
|
||||||
mod bar {
|
mod bar {
|
||||||
mod baz {}
|
pub mod baz {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn foo() {
|
fn foo() {
|
||||||
|
@ -1333,7 +1333,7 @@ fn foo() {
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
mod bar {
|
mod bar {
|
||||||
mod baz {
|
pub mod baz {
|
||||||
pub(crate) fn my_fn() {
|
pub(crate) fn my_fn() {
|
||||||
${0:todo!()}
|
${0:todo!()}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
NodeOrToken::Node(n) => {
|
NodeOrToken::Node(n) => {
|
||||||
format_to!(current_arg, "{n}");
|
format_to!(current_arg, "{n}");
|
||||||
},
|
},
|
||||||
NodeOrToken::Token(t) if t.kind() == COMMA=> {
|
NodeOrToken::Token(t) if t.kind() == COMMA => {
|
||||||
existing_args.push(current_arg.trim().into());
|
existing_args.push(current_arg.trim().into());
|
||||||
current_arg.clear();
|
current_arg.clear();
|
||||||
},
|
},
|
||||||
|
@ -238,14 +238,14 @@ fn main() {
|
||||||
&add_macro_decl(
|
&add_macro_decl(
|
||||||
r#"
|
r#"
|
||||||
fn main() {
|
fn main() {
|
||||||
print!("{} {x + 1:b} {Struct(1, 2)}$0", 1);
|
print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1);
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
),
|
),
|
||||||
&add_macro_decl(
|
&add_macro_decl(
|
||||||
r#"
|
r#"
|
||||||
fn main() {
|
fn main() {
|
||||||
print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
|
print!("{:b} {:b} {}"$0, 1, x + 1, Struct(1, 2));
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, AstToken},
|
ast::{self, AstNode, AstToken},
|
||||||
match_ast, NodeOrToken, SyntaxElement, TextSize, T,
|
match_ast, NodeOrToken, SyntaxElement, TextRange, TextSize, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||||
|
@ -22,7 +22,36 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
|
let macro_calls = if ctx.has_empty_selection() {
|
||||||
|
vec![ctx.find_node_at_offset::<ast::MacroCall>()?]
|
||||||
|
} else {
|
||||||
|
ctx.covering_element()
|
||||||
|
.as_node()?
|
||||||
|
.descendants()
|
||||||
|
.filter(|node| ctx.selection_trimmed().contains_range(node.text_range()))
|
||||||
|
.filter_map(ast::MacroCall::cast)
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
let replacements =
|
||||||
|
macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
|
||||||
|
if replacements.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.add(
|
||||||
|
AssistId("remove_dbg", AssistKind::Refactor),
|
||||||
|
"Remove dbg!()",
|
||||||
|
ctx.selection_trimmed(),
|
||||||
|
|builder| {
|
||||||
|
for (range, text) in replacements {
|
||||||
|
builder.replace(range, text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, String)> {
|
||||||
let tt = macro_call.token_tree()?;
|
let tt = macro_call.token_tree()?;
|
||||||
let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
|
let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
|
||||||
if macro_call.path()?.segment()?.name_ref()?.text() != "dbg"
|
if macro_call.path()?.segment()?.name_ref()?.text() != "dbg"
|
||||||
|
@ -41,7 +70,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
|
||||||
|
|
||||||
let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?;
|
let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?;
|
||||||
let parent = macro_expr.syntax().parent()?;
|
let parent = macro_expr.syntax().parent()?;
|
||||||
let (range, text) = match &*input_expressions {
|
Some(match &*input_expressions {
|
||||||
// dbg!()
|
// dbg!()
|
||||||
[] => {
|
[] => {
|
||||||
match_ast! {
|
match_ast! {
|
||||||
|
@ -107,10 +136,6 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
|
||||||
}
|
}
|
||||||
// dbg!(expr0, expr1, ...)
|
// dbg!(expr0, expr1, ...)
|
||||||
exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))),
|
exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))),
|
||||||
};
|
|
||||||
|
|
||||||
acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", range, |builder| {
|
|
||||||
builder.replace(range, text);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,4 +263,28 @@ fn foo() {
|
||||||
check(r#"$0dbg!(0, 1)"#, r#"(0, 1)"#);
|
check(r#"$0dbg!(0, 1)"#, r#"(0, 1)"#);
|
||||||
check(r#"$0dbg!(0, (1, 2))"#, r#"(0, (1, 2))"#);
|
check(r#"$0dbg!(0, (1, 2))"#, r#"(0, (1, 2))"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_range() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn f() {
|
||||||
|
dbg!(0) + $0dbg!(1);
|
||||||
|
dbg!(())$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn f() {
|
||||||
|
dbg!(0) + 1;
|
||||||
|
()
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_range_partial() {
|
||||||
|
check_assist_not_applicable(remove_dbg, r#"$0dbg$0!(0)"#);
|
||||||
|
check_assist_not_applicable(remove_dbg, r#"$0dbg!(0$0)"#);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1019,8 +1019,6 @@ struct Foo {
|
||||||
impl foo::Bar for Foo {
|
impl foo::Bar for Foo {
|
||||||
$0type Qux;
|
$0type Qux;
|
||||||
|
|
||||||
const Baz: usize = 42;
|
|
||||||
|
|
||||||
const Fez: usize;
|
const Fez: usize;
|
||||||
|
|
||||||
fn foo() {
|
fn foo() {
|
||||||
|
|
|
@ -741,7 +741,7 @@ mod m {
|
||||||
fn frobnicate() {}
|
fn frobnicate() {}
|
||||||
}
|
}
|
||||||
fn main() {
|
fn main() {
|
||||||
m::frobnicate$0() {}
|
m::frobnicate$0();
|
||||||
}
|
}
|
||||||
"#####,
|
"#####,
|
||||||
r#####"
|
r#####"
|
||||||
|
@ -749,7 +749,7 @@ mod m {
|
||||||
$0pub(crate) fn frobnicate() {}
|
$0pub(crate) fn frobnicate() {}
|
||||||
}
|
}
|
||||||
fn main() {
|
fn main() {
|
||||||
m::frobnicate() {}
|
m::frobnicate();
|
||||||
}
|
}
|
||||||
"#####,
|
"#####,
|
||||||
)
|
)
|
||||||
|
|
|
@ -119,6 +119,10 @@ pub fn filter_assoc_items(
|
||||||
(default_methods, def.body()),
|
(default_methods, def.body()),
|
||||||
(DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
|
(DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
|
||||||
),
|
),
|
||||||
|
ast::AssocItem::Const(def) => matches!(
|
||||||
|
(default_methods, def.body()),
|
||||||
|
(DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
|
||||||
|
),
|
||||||
_ => default_methods == DefaultMethods::No,
|
_ => default_methods == DefaultMethods::No,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -157,7 +157,7 @@ fn complete_trait_impl(
|
||||||
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
|
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
|
||||||
}
|
}
|
||||||
(hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
|
(hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
|
||||||
add_type_alias_impl(acc, ctx, replacement_range, type_alias)
|
add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
|
||||||
}
|
}
|
||||||
(hir::AssocItem::Const(const_), All | Const) => {
|
(hir::AssocItem::Const(const_), All | Const) => {
|
||||||
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
|
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
|
||||||
|
@ -236,9 +236,7 @@ fn get_transformed_assoc_item(
|
||||||
);
|
);
|
||||||
|
|
||||||
transform.apply(assoc_item.syntax());
|
transform.apply(assoc_item.syntax());
|
||||||
if let ast::AssocItem::Fn(func) = &assoc_item {
|
assoc_item.remove_attrs_and_docs();
|
||||||
func.remove_attrs_and_docs();
|
|
||||||
}
|
|
||||||
Some(assoc_item)
|
Some(assoc_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,24 +245,50 @@ fn add_type_alias_impl(
|
||||||
ctx: &CompletionContext<'_>,
|
ctx: &CompletionContext<'_>,
|
||||||
replacement_range: TextRange,
|
replacement_range: TextRange,
|
||||||
type_alias: hir::TypeAlias,
|
type_alias: hir::TypeAlias,
|
||||||
|
impl_def: hir::Impl,
|
||||||
) {
|
) {
|
||||||
let alias_name = type_alias.name(ctx.db);
|
let alias_name = type_alias.name(ctx.db).unescaped().to_smol_str();
|
||||||
let (alias_name, escaped_name) =
|
|
||||||
(alias_name.unescaped().to_smol_str(), alias_name.to_smol_str());
|
|
||||||
|
|
||||||
let label = format!("type {} =", alias_name);
|
let label = format!("type {} =", alias_name);
|
||||||
let replacement = format!("type {} = ", escaped_name);
|
|
||||||
|
|
||||||
let mut item = CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label);
|
let mut item = CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label);
|
||||||
item.lookup_by(format!("type {}", alias_name))
|
item.lookup_by(format!("type {}", alias_name))
|
||||||
.set_documentation(type_alias.docs(ctx.db))
|
.set_documentation(type_alias.docs(ctx.db))
|
||||||
.set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
|
.set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
|
||||||
match ctx.config.snippet_cap {
|
|
||||||
Some(cap) => item
|
if let Some(source) = ctx.sema.source(type_alias) {
|
||||||
.snippet_edit(cap, TextEdit::replace(replacement_range, format!("{}$0;", replacement))),
|
let assoc_item = ast::AssocItem::TypeAlias(source.value);
|
||||||
None => item.text_edit(TextEdit::replace(replacement_range, replacement)),
|
if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
|
||||||
};
|
let transformed_ty = match transformed_item {
|
||||||
item.add_to(acc);
|
ast::AssocItem::TypeAlias(ty) => ty,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let start = transformed_ty.syntax().text_range().start();
|
||||||
|
let Some(end) = transformed_ty
|
||||||
|
.eq_token()
|
||||||
|
.map(|tok| tok.text_range().start())
|
||||||
|
.or(transformed_ty.semicolon_token().map(|tok| tok.text_range().start())) else { return };
|
||||||
|
|
||||||
|
let len = end - start;
|
||||||
|
let mut decl = transformed_ty.syntax().text().slice(..len).to_string();
|
||||||
|
if !decl.ends_with(' ') {
|
||||||
|
decl.push(' ');
|
||||||
|
}
|
||||||
|
decl.push_str("= ");
|
||||||
|
|
||||||
|
match ctx.config.snippet_cap {
|
||||||
|
Some(cap) => {
|
||||||
|
let snippet = format!("{}$0;", decl);
|
||||||
|
item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
item.text_edit(TextEdit::replace(replacement_range, decl));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
item.add_to(acc);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_const_impl(
|
fn add_const_impl(
|
||||||
|
@ -309,7 +333,6 @@ fn add_const_impl(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> String {
|
fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> String {
|
||||||
const_.remove_attrs_and_docs();
|
|
||||||
let const_ = if needs_whitespace {
|
let const_ = if needs_whitespace {
|
||||||
insert_whitespace_into_node::insert_ws_into(const_.syntax().clone())
|
insert_whitespace_into_node::insert_ws_into(const_.syntax().clone())
|
||||||
} else {
|
} else {
|
||||||
|
@ -333,8 +356,6 @@ fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> Strin
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
|
fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
|
||||||
node.remove_attrs_and_docs();
|
|
||||||
|
|
||||||
let node = if needs_whitespace {
|
let node = if needs_whitespace {
|
||||||
insert_whitespace_into_node::insert_ws_into(node.syntax().clone())
|
insert_whitespace_into_node::insert_ws_into(node.syntax().clone())
|
||||||
} else {
|
} else {
|
||||||
|
@ -350,9 +371,7 @@ fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
|
||||||
.map_or(end, |f| f.text_range().start());
|
.map_or(end, |f| f.text_range().start());
|
||||||
|
|
||||||
let len = end - start;
|
let len = end - start;
|
||||||
let range = TextRange::new(0.into(), len);
|
let syntax = node.text().slice(..len).to_string();
|
||||||
|
|
||||||
let syntax = node.text().slice(range).to_string();
|
|
||||||
|
|
||||||
syntax.trim_end().to_owned()
|
syntax.trim_end().to_owned()
|
||||||
}
|
}
|
||||||
|
@ -1160,6 +1179,106 @@ impl Foo for Test {
|
||||||
$0
|
$0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn includes_gat_generics() {
|
||||||
|
check_edit(
|
||||||
|
"type Ty",
|
||||||
|
r#"
|
||||||
|
trait Tr<'b> {
|
||||||
|
type Ty<'a: 'b, T: Copy, const C: usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b> Tr<'b> for () {
|
||||||
|
$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
trait Tr<'b> {
|
||||||
|
type Ty<'a: 'b, T: Copy, const C: usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b> Tr<'b> for () {
|
||||||
|
type Ty<'a: 'b, T: Copy, const C: usize> = $0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn strips_comments() {
|
||||||
|
check_edit(
|
||||||
|
"fn func",
|
||||||
|
r#"
|
||||||
|
trait Tr {
|
||||||
|
/// docs
|
||||||
|
#[attr]
|
||||||
|
fn func();
|
||||||
|
}
|
||||||
|
impl Tr for () {
|
||||||
|
$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
trait Tr {
|
||||||
|
/// docs
|
||||||
|
#[attr]
|
||||||
|
fn func();
|
||||||
|
}
|
||||||
|
impl Tr for () {
|
||||||
|
fn func() {
|
||||||
|
$0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_edit(
|
||||||
|
"const C",
|
||||||
|
r#"
|
||||||
|
trait Tr {
|
||||||
|
/// docs
|
||||||
|
#[attr]
|
||||||
|
const C: usize;
|
||||||
|
}
|
||||||
|
impl Tr for () {
|
||||||
|
$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
trait Tr {
|
||||||
|
/// docs
|
||||||
|
#[attr]
|
||||||
|
const C: usize;
|
||||||
|
}
|
||||||
|
impl Tr for () {
|
||||||
|
const C: usize = $0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check_edit(
|
||||||
|
"type Item",
|
||||||
|
r#"
|
||||||
|
trait Tr {
|
||||||
|
/// docs
|
||||||
|
#[attr]
|
||||||
|
type Item;
|
||||||
|
}
|
||||||
|
impl Tr for () {
|
||||||
|
$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
trait Tr {
|
||||||
|
/// docs
|
||||||
|
#[attr]
|
||||||
|
type Item;
|
||||||
|
}
|
||||||
|
impl Tr for () {
|
||||||
|
type Item = $0;
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -681,9 +681,13 @@ fn classify_name_ref(
|
||||||
ast::Item::ExternBlock(it) => it.extern_item_list().is_none(),
|
ast::Item::ExternBlock(it) => it.extern_item_list().is_none(),
|
||||||
ast::Item::Fn(it) => it.body().is_none(),
|
ast::Item::Fn(it) => it.body().is_none(),
|
||||||
ast::Item::Impl(it) => it.assoc_item_list().is_none(),
|
ast::Item::Impl(it) => it.assoc_item_list().is_none(),
|
||||||
ast::Item::Module(it) => it.item_list().is_none(),
|
ast::Item::Module(it) => {
|
||||||
|
it.item_list().is_none() && it.semicolon_token().is_none()
|
||||||
|
}
|
||||||
ast::Item::Static(it) => it.body().is_none(),
|
ast::Item::Static(it) => it.body().is_none(),
|
||||||
ast::Item::Struct(it) => it.field_list().is_none(),
|
ast::Item::Struct(it) => {
|
||||||
|
it.field_list().is_none() && it.semicolon_token().is_none()
|
||||||
|
}
|
||||||
ast::Item::Trait(it) => it.assoc_item_list().is_none(),
|
ast::Item::Trait(it) => it.assoc_item_list().is_none(),
|
||||||
ast::Item::TypeAlias(it) => it.ty().is_none(),
|
ast::Item::TypeAlias(it) => it.ty().is_none(),
|
||||||
ast::Item::Union(it) => it.record_field_list().is_none(),
|
ast::Item::Union(it) => it.record_field_list().is_none(),
|
||||||
|
|
|
@ -245,3 +245,35 @@ impl Test for () {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn after_unit_struct() {
|
||||||
|
check(
|
||||||
|
r#"struct S; f$0"#,
|
||||||
|
expect![[r#"
|
||||||
|
ma makro!(…) macro_rules! makro
|
||||||
|
md module
|
||||||
|
kw const
|
||||||
|
kw crate::
|
||||||
|
kw enum
|
||||||
|
kw extern
|
||||||
|
kw fn
|
||||||
|
kw impl
|
||||||
|
kw mod
|
||||||
|
kw pub
|
||||||
|
kw pub(crate)
|
||||||
|
kw pub(super)
|
||||||
|
kw self::
|
||||||
|
kw static
|
||||||
|
kw struct
|
||||||
|
kw trait
|
||||||
|
kw type
|
||||||
|
kw union
|
||||||
|
kw unsafe
|
||||||
|
kw use
|
||||||
|
sn macro_rules
|
||||||
|
sn tfn (Test function)
|
||||||
|
sn tmod (Test module)
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -58,8 +58,11 @@ impl LineIndex {
|
||||||
let mut utf16_lines = NoHashHashMap::default();
|
let mut utf16_lines = NoHashHashMap::default();
|
||||||
let mut utf16_chars = Vec::new();
|
let mut utf16_chars = Vec::new();
|
||||||
|
|
||||||
let mut newlines = vec![0.into()];
|
let mut newlines = Vec::with_capacity(16);
|
||||||
let mut curr_row @ mut curr_col = 0.into();
|
newlines.push(TextSize::from(0));
|
||||||
|
|
||||||
|
let mut curr_row = 0.into();
|
||||||
|
let mut curr_col = 0.into();
|
||||||
let mut line = 0;
|
let mut line = 0;
|
||||||
for c in text.chars() {
|
for c in text.chars() {
|
||||||
let c_len = TextSize::of(c);
|
let c_len = TextSize::of(c);
|
||||||
|
|
|
@ -104,6 +104,11 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
|
||||||
extracted_expressions.push(Arg::Placeholder);
|
extracted_expressions.push(Arg::Placeholder);
|
||||||
state = State::NotArg;
|
state = State::NotArg;
|
||||||
}
|
}
|
||||||
|
(State::MaybeArg, ':') => {
|
||||||
|
output.push(chr);
|
||||||
|
extracted_expressions.push(Arg::Placeholder);
|
||||||
|
state = State::FormatOpts;
|
||||||
|
}
|
||||||
(State::MaybeArg, _) => {
|
(State::MaybeArg, _) => {
|
||||||
if matches!(chr, '\\' | '$') {
|
if matches!(chr, '\\' | '$') {
|
||||||
current_expr.push('\\');
|
current_expr.push('\\');
|
||||||
|
@ -118,44 +123,41 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
|
||||||
state = State::Expr;
|
state = State::Expr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(State::Ident | State::Expr, '}') => {
|
|
||||||
if inexpr_open_count == 0 {
|
|
||||||
output.push(chr);
|
|
||||||
|
|
||||||
if matches!(state, State::Expr) {
|
|
||||||
extracted_expressions.push(Arg::Expr(current_expr.trim().into()));
|
|
||||||
} else {
|
|
||||||
extracted_expressions.push(Arg::Ident(current_expr.trim().into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
current_expr = String::new();
|
|
||||||
state = State::NotArg;
|
|
||||||
} else {
|
|
||||||
// We're closing one brace met before inside of the expression.
|
|
||||||
current_expr.push(chr);
|
|
||||||
inexpr_open_count -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(State::Ident | State::Expr, ':') if matches!(chars.peek(), Some(':')) => {
|
(State::Ident | State::Expr, ':') if matches!(chars.peek(), Some(':')) => {
|
||||||
// path separator
|
// path separator
|
||||||
state = State::Expr;
|
state = State::Expr;
|
||||||
current_expr.push_str("::");
|
current_expr.push_str("::");
|
||||||
chars.next();
|
chars.next();
|
||||||
}
|
}
|
||||||
(State::Ident | State::Expr, ':') => {
|
(State::Ident | State::Expr, ':' | '}') => {
|
||||||
if inexpr_open_count == 0 {
|
if inexpr_open_count == 0 {
|
||||||
// We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
|
let trimmed = current_expr.trim();
|
||||||
output.push(chr);
|
|
||||||
|
|
||||||
if matches!(state, State::Expr) {
|
// if the expression consists of a single number, like "0" or "12", it can refer to
|
||||||
extracted_expressions.push(Arg::Expr(current_expr.trim().into()));
|
// format args in the order they are specified.
|
||||||
|
// see: https://doc.rust-lang.org/std/fmt/#positional-parameters
|
||||||
|
if trimmed.chars().fold(true, |only_num, c| c.is_ascii_digit() && only_num) {
|
||||||
|
output.push_str(trimmed);
|
||||||
|
} else if matches!(state, State::Expr) {
|
||||||
|
extracted_expressions.push(Arg::Expr(trimmed.into()));
|
||||||
} else {
|
} else {
|
||||||
extracted_expressions.push(Arg::Ident(current_expr.trim().into()));
|
extracted_expressions.push(Arg::Ident(trimmed.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
current_expr = String::new();
|
output.push(chr);
|
||||||
state = State::FormatOpts;
|
current_expr.clear();
|
||||||
} else {
|
state = if chr == ':' {
|
||||||
|
State::FormatOpts
|
||||||
|
} else if chr == '}' {
|
||||||
|
State::NotArg
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
} else if chr == '}' {
|
||||||
|
// We're closing one brace met before inside of the expression.
|
||||||
|
current_expr.push(chr);
|
||||||
|
inexpr_open_count -= 1;
|
||||||
|
} else if chr == ':' {
|
||||||
// We're inside of braced expression, assume that it's a struct field name/value delimiter.
|
// We're inside of braced expression, assume that it's a struct field name/value delimiter.
|
||||||
current_expr.push(chr);
|
current_expr.push(chr);
|
||||||
}
|
}
|
||||||
|
@ -219,6 +221,10 @@ mod tests {
|
||||||
("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
|
("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
|
||||||
("{expr:?}", expect![["{:?}; expr"]]),
|
("{expr:?}", expect![["{:?}; expr"]]),
|
||||||
("{expr:1$}", expect![[r"{:1\$}; expr"]]),
|
("{expr:1$}", expect![[r"{:1\$}; expr"]]),
|
||||||
|
("{:1$}", expect![[r"{:1\$}; $1"]]),
|
||||||
|
("{:>padding$}", expect![[r"{:>padding\$}; $1"]]),
|
||||||
|
("{}, {}, {0}", expect![[r"{}, {}, {0}; $1, $2"]]),
|
||||||
|
("{}, {}, {0:b}", expect![[r"{}, {}, {0:b}; $1, $2"]]),
|
||||||
("{$0}", expect![[r"{}; \$0"]]),
|
("{$0}", expect![[r"{}; \$0"]]),
|
||||||
("{malformed", expect![["-"]]),
|
("{malformed", expect![["-"]]),
|
||||||
("malformed}", expect![["-"]]),
|
("malformed}", expect![["-"]]),
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -5,10 +5,7 @@ use crate::{Diagnostic, DiagnosticsContext};
|
||||||
// This diagnostic is shown for macro expansion errors.
|
// This diagnostic is shown for macro expansion errors.
|
||||||
pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
|
pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
|
||||||
// Use more accurate position if available.
|
// Use more accurate position if available.
|
||||||
let display_range = d
|
let display_range = ctx.resolve_precise_location(&d.node, d.precise_location);
|
||||||
.precise_location
|
|
||||||
.unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone()).range);
|
|
||||||
|
|
||||||
Diagnostic::new("macro-error", d.message.clone(), display_range).experimental()
|
Diagnostic::new("macro-error", d.message.clone(), display_range).experimental()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -268,12 +268,12 @@ fn main() {
|
||||||
foo::Foo { bar: 3, $0baz: false};
|
foo::Foo { bar: 3, $0baz: false};
|
||||||
}
|
}
|
||||||
//- /foo.rs
|
//- /foo.rs
|
||||||
struct Foo {
|
pub struct Foo {
|
||||||
bar: i32
|
bar: i32
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
struct Foo {
|
pub struct Foo {
|
||||||
bar: i32,
|
bar: i32,
|
||||||
pub(crate) baz: bool
|
pub(crate) baz: bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,7 @@ pub(crate) fn unresolved_macro_call(
|
||||||
d: &hir::UnresolvedMacroCall,
|
d: &hir::UnresolvedMacroCall,
|
||||||
) -> Diagnostic {
|
) -> Diagnostic {
|
||||||
// Use more accurate position if available.
|
// Use more accurate position if available.
|
||||||
let display_range = d
|
let display_range = ctx.resolve_precise_location(&d.macro_call, d.precise_location);
|
||||||
.precise_location
|
|
||||||
.unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.macro_call.clone()).range);
|
|
||||||
|
|
||||||
let bang = if d.is_bang { "!" } else { "" };
|
let bang = if d.is_bang { "!" } else { "" };
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
"unresolved-macro-call",
|
"unresolved-macro-call",
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use hir::db::DefDatabase;
|
use hir::db::DefDatabase;
|
||||||
use syntax::NodeOrToken;
|
|
||||||
|
|
||||||
use crate::{Diagnostic, DiagnosticsContext, Severity};
|
use crate::{Diagnostic, DiagnosticsContext, Severity};
|
||||||
|
|
||||||
|
@ -19,16 +18,7 @@ pub(crate) fn unresolved_proc_macro(
|
||||||
proc_attr_macros_enabled: bool,
|
proc_attr_macros_enabled: bool,
|
||||||
) -> Diagnostic {
|
) -> Diagnostic {
|
||||||
// Use more accurate position if available.
|
// Use more accurate position if available.
|
||||||
let display_range = (|| {
|
let display_range = ctx.resolve_precise_location(&d.node, d.precise_location);
|
||||||
let precise_location = d.precise_location?;
|
|
||||||
let root = ctx.sema.parse_or_expand(d.node.file_id)?;
|
|
||||||
match root.covering_element(precise_location) {
|
|
||||||
NodeOrToken::Node(it) => Some(ctx.sema.original_range(&it)),
|
|
||||||
NodeOrToken::Token(it) => d.node.with_value(it).original_file_range_opt(ctx.sema.db),
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
.unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone()))
|
|
||||||
.range;
|
|
||||||
|
|
||||||
let config_enabled = match d.kind {
|
let config_enabled = match d.kind {
|
||||||
hir::MacroKind::Attr => proc_macros_enabled && proc_attr_macros_enabled,
|
hir::MacroKind::Attr => proc_macros_enabled && proc_attr_macros_enabled,
|
||||||
|
|
|
@ -71,9 +71,9 @@ use a;
|
||||||
use a::{c, d::e};
|
use a::{c, d::e};
|
||||||
|
|
||||||
mod a {
|
mod a {
|
||||||
mod c {}
|
pub mod c {}
|
||||||
mod d {
|
pub mod d {
|
||||||
mod e {}
|
pub mod e {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
@ -87,9 +87,9 @@ use a::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod a {
|
mod a {
|
||||||
mod c {}
|
pub mod c {}
|
||||||
mod d {
|
pub mod d {
|
||||||
mod e {}
|
pub mod e {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
@ -116,11 +116,11 @@ use b;
|
||||||
);
|
);
|
||||||
check_fix(
|
check_fix(
|
||||||
r#"
|
r#"
|
||||||
mod a { mod c {} }
|
mod a { pub mod c {} }
|
||||||
use a::{c$0};
|
use a::{c$0};
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
mod a { mod c {} }
|
mod a { pub mod c {} }
|
||||||
use a::c;
|
use a::c;
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -136,11 +136,11 @@ use a;
|
||||||
);
|
);
|
||||||
check_fix(
|
check_fix(
|
||||||
r#"
|
r#"
|
||||||
mod a { mod c {} mod d { mod e {} } }
|
mod a { pub mod c {} pub mod d { pub mod e {} } }
|
||||||
use a::{c, d::{e$0}};
|
use a::{c, d::{e$0}};
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
mod a { mod c {} mod d { mod e {} } }
|
mod a { pub mod c {} pub mod d { pub mod e {} } }
|
||||||
use a::{c, d::e};
|
use a::{c, d::e};
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -182,6 +182,28 @@ struct DiagnosticsContext<'a> {
|
||||||
resolve: &'a AssistResolveStrategy,
|
resolve: &'a AssistResolveStrategy,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> DiagnosticsContext<'a> {
|
||||||
|
fn resolve_precise_location(
|
||||||
|
&self,
|
||||||
|
node: &InFile<SyntaxNodePtr>,
|
||||||
|
precise_location: Option<TextRange>,
|
||||||
|
) -> TextRange {
|
||||||
|
let sema = &self.sema;
|
||||||
|
(|| {
|
||||||
|
let precise_location = precise_location?;
|
||||||
|
let root = sema.parse_or_expand(node.file_id)?;
|
||||||
|
match root.covering_element(precise_location) {
|
||||||
|
syntax::NodeOrToken::Node(it) => Some(sema.original_range(&it)),
|
||||||
|
syntax::NodeOrToken::Token(it) => {
|
||||||
|
node.with_value(it).original_file_range_opt(sema.db)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
.unwrap_or_else(|| sema.diagnostics_display_range(node.clone()))
|
||||||
|
.range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn diagnostics(
|
pub fn diagnostics(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
config: &DiagnosticsConfig,
|
config: &DiagnosticsConfig,
|
||||||
|
|
|
@ -5,7 +5,7 @@ description = "Structural search and replace of Rust code"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/rust-lang/rust-analyzer"
|
repository = "https://github.com/rust-lang/rust-analyzer"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -289,10 +289,10 @@ mod b;
|
||||||
enum E { X(Foo$0) }
|
enum E { X(Foo$0) }
|
||||||
|
|
||||||
//- /a.rs
|
//- /a.rs
|
||||||
struct Foo;
|
pub struct Foo;
|
||||||
//^^^
|
//^^^
|
||||||
//- /b.rs
|
//- /b.rs
|
||||||
struct Foo;
|
pub struct Foo;
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,14 @@ pub(crate) fn hover(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind())));
|
let in_attr = original_token
|
||||||
|
.parent_ancestors()
|
||||||
|
.filter_map(ast::Item::cast)
|
||||||
|
.any(|item| sema.is_attr_macro_call(&item))
|
||||||
|
&& !matches!(
|
||||||
|
original_token.parent().and_then(ast::TokenTree::cast),
|
||||||
|
Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))
|
||||||
|
);
|
||||||
// prefer descending the same token kind in attribute expansions, in normal macros text
|
// prefer descending the same token kind in attribute expansions, in normal macros text
|
||||||
// equivalency is more important
|
// equivalency is more important
|
||||||
let descended = if in_attr {
|
let descended = if in_attr {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo};
|
use hir::{
|
||||||
|
known, Adjust, AutoBorrow, Callable, HasVisibility, HirDisplay, Mutability, OverloadedDeref,
|
||||||
|
PointerCast, Safety, Semantics, TypeInfo,
|
||||||
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap,
|
base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap,
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
|
@ -22,7 +25,7 @@ pub struct InlayHintsConfig {
|
||||||
pub type_hints: bool,
|
pub type_hints: bool,
|
||||||
pub parameter_hints: bool,
|
pub parameter_hints: bool,
|
||||||
pub chaining_hints: bool,
|
pub chaining_hints: bool,
|
||||||
pub reborrow_hints: ReborrowHints,
|
pub adjustment_hints: AdjustmentHints,
|
||||||
pub closure_return_type_hints: ClosureReturnTypeHints,
|
pub closure_return_type_hints: ClosureReturnTypeHints,
|
||||||
pub binding_mode_hints: bool,
|
pub binding_mode_hints: bool,
|
||||||
pub lifetime_elision_hints: LifetimeElisionHints,
|
pub lifetime_elision_hints: LifetimeElisionHints,
|
||||||
|
@ -48,9 +51,9 @@ pub enum LifetimeElisionHints {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum ReborrowHints {
|
pub enum AdjustmentHints {
|
||||||
Always,
|
Always,
|
||||||
MutableOnly,
|
ReborrowOnly,
|
||||||
Never,
|
Never,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +64,8 @@ pub enum InlayKind {
|
||||||
ClosingBraceHint,
|
ClosingBraceHint,
|
||||||
ClosureReturnTypeHint,
|
ClosureReturnTypeHint,
|
||||||
GenericParamListHint,
|
GenericParamListHint,
|
||||||
ImplicitReborrowHint,
|
AdjustmentHint,
|
||||||
|
AdjustmentHintClosingParenthesis,
|
||||||
LifetimeHint,
|
LifetimeHint,
|
||||||
ParameterHint,
|
ParameterHint,
|
||||||
TypeHint,
|
TypeHint,
|
||||||
|
@ -115,6 +119,12 @@ impl From<String> for InlayHintLabel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&str> for InlayHintLabel {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
Self { parts: vec![InlayHintLabelPart { text: s.into(), linked_location: None }] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for InlayHintLabel {
|
impl fmt::Display for InlayHintLabel {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.parts.iter().map(|part| &part.text).format(""))
|
write!(f, "{}", self.parts.iter().map(|part| &part.text).format(""))
|
||||||
|
@ -180,7 +190,7 @@ impl fmt::Debug for InlayHintLabelPart {
|
||||||
pub(crate) fn inlay_hints(
|
pub(crate) fn inlay_hints(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
range_limit: Option<FileRange>,
|
range_limit: Option<TextRange>,
|
||||||
config: &InlayHintsConfig,
|
config: &InlayHintsConfig,
|
||||||
) -> Vec<InlayHint> {
|
) -> Vec<InlayHint> {
|
||||||
let _p = profile::span("inlay_hints");
|
let _p = profile::span("inlay_hints");
|
||||||
|
@ -195,7 +205,7 @@ pub(crate) fn inlay_hints(
|
||||||
|
|
||||||
let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
|
let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
|
||||||
match range_limit {
|
match range_limit {
|
||||||
Some(FileRange { range, .. }) => match file.covering_element(range) {
|
Some(range) => match file.covering_element(range) {
|
||||||
NodeOrToken::Token(_) => return acc,
|
NodeOrToken::Token(_) => return acc,
|
||||||
NodeOrToken::Node(n) => n
|
NodeOrToken::Node(n) => n
|
||||||
.descendants()
|
.descendants()
|
||||||
|
@ -221,6 +231,7 @@ fn hints(
|
||||||
match node {
|
match node {
|
||||||
ast::Expr(expr) => {
|
ast::Expr(expr) => {
|
||||||
chaining_hints(hints, sema, &famous_defs, config, file_id, &expr);
|
chaining_hints(hints, sema, &famous_defs, config, file_id, &expr);
|
||||||
|
adjustment_hints(hints, sema, config, &expr);
|
||||||
match expr {
|
match expr {
|
||||||
ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)),
|
ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)),
|
||||||
ast::Expr::MethodCallExpr(it) => {
|
ast::Expr::MethodCallExpr(it) => {
|
||||||
|
@ -229,7 +240,7 @@ fn hints(
|
||||||
ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, file_id, it),
|
ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, file_id, it),
|
||||||
// We could show reborrows for all expressions, but usually that is just noise to the user
|
// We could show reborrows for all expressions, but usually that is just noise to the user
|
||||||
// and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
|
// and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
|
||||||
ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
|
// ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -617,30 +628,95 @@ fn closure_ret_hints(
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reborrow_hints(
|
fn adjustment_hints(
|
||||||
acc: &mut Vec<InlayHint>,
|
acc: &mut Vec<InlayHint>,
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
config: &InlayHintsConfig,
|
config: &InlayHintsConfig,
|
||||||
expr: &ast::Expr,
|
expr: &ast::Expr,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
if config.reborrow_hints == ReborrowHints::Never {
|
if config.adjustment_hints == AdjustmentHints::Never {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let ast::Expr::ParenExpr(_) = expr {
|
||||||
|
// These inherit from the inner expression which would result in duplicate hints
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parent = expr.syntax().parent().and_then(ast::Expr::cast);
|
||||||
let descended = sema.descend_node_into_attributes(expr.clone()).pop();
|
let descended = sema.descend_node_into_attributes(expr.clone()).pop();
|
||||||
let desc_expr = descended.as_ref().unwrap_or(expr);
|
let desc_expr = descended.as_ref().unwrap_or(expr);
|
||||||
let mutability = sema.is_implicit_reborrow(desc_expr)?;
|
let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
|
||||||
let label = match mutability {
|
let needs_parens = match parent {
|
||||||
hir::Mutability::Shared if config.reborrow_hints != ReborrowHints::MutableOnly => "&*",
|
Some(parent) => {
|
||||||
hir::Mutability::Mut => "&mut *",
|
match parent {
|
||||||
_ => return None,
|
ast::Expr::AwaitExpr(_)
|
||||||
|
| ast::Expr::CallExpr(_)
|
||||||
|
| ast::Expr::CastExpr(_)
|
||||||
|
| ast::Expr::FieldExpr(_)
|
||||||
|
| ast::Expr::MethodCallExpr(_)
|
||||||
|
| ast::Expr::TryExpr(_) => true,
|
||||||
|
// FIXME: shorthands need special casing, though not sure if adjustments are even valid there
|
||||||
|
ast::Expr::RecordExpr(_) => false,
|
||||||
|
ast::Expr::IndexExpr(index) => index.base().as_ref() == Some(expr),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => false,
|
||||||
};
|
};
|
||||||
acc.push(InlayHint {
|
if needs_parens {
|
||||||
range: expr.syntax().text_range(),
|
acc.push(InlayHint {
|
||||||
kind: InlayKind::ImplicitReborrowHint,
|
range: expr.syntax().text_range(),
|
||||||
label: label.to_string().into(),
|
kind: InlayKind::AdjustmentHint,
|
||||||
tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())),
|
label: "(".into(),
|
||||||
});
|
tooltip: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for adjustment in adjustments.into_iter().rev() {
|
||||||
|
// FIXME: Add some nicer tooltips to each of these
|
||||||
|
let text = match adjustment {
|
||||||
|
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
|
||||||
|
"<never-to-any>"
|
||||||
|
}
|
||||||
|
Adjust::Deref(None) => "*",
|
||||||
|
Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => "*",
|
||||||
|
Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => "*",
|
||||||
|
Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => "&",
|
||||||
|
Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => "&mut ",
|
||||||
|
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => "&raw const ",
|
||||||
|
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => "&raw mut ",
|
||||||
|
// some of these could be represented via `as` casts, but that's not too nice and
|
||||||
|
// handling everything as a prefix expr makes the `(` and `)` insertion easier
|
||||||
|
Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
|
||||||
|
match cast {
|
||||||
|
PointerCast::ReifyFnPointer => "<fn-item-to-fn-pointer>",
|
||||||
|
PointerCast::UnsafeFnPointer => "<safe-fn-pointer-to-unsafe-fn-pointer>",
|
||||||
|
PointerCast::ClosureFnPointer(Safety::Unsafe) => {
|
||||||
|
"<closure-to-unsafe-fn-pointer>"
|
||||||
|
}
|
||||||
|
PointerCast::ClosureFnPointer(Safety::Safe) => "<closure-to-fn-pointer>",
|
||||||
|
PointerCast::MutToConstPointer => "<mut-ptr-to-const-ptr>",
|
||||||
|
PointerCast::ArrayToPointer => "<array-ptr-to-element-ptr>",
|
||||||
|
PointerCast::Unsize => "<unsize>",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
acc.push(InlayHint {
|
||||||
|
range: expr.syntax().text_range(),
|
||||||
|
kind: InlayKind::AdjustmentHint,
|
||||||
|
label: text.into(),
|
||||||
|
tooltip: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if needs_parens {
|
||||||
|
acc.push(InlayHint {
|
||||||
|
range: expr.syntax().text_range(),
|
||||||
|
kind: InlayKind::AdjustmentHintClosingParenthesis,
|
||||||
|
label: ")".into(),
|
||||||
|
tooltip: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1213,12 +1289,11 @@ fn get_callable(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
use ide_db::base_db::FileRange;
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{TextRange, TextSize};
|
use syntax::{TextRange, TextSize};
|
||||||
use test_utils::extract_annotations;
|
use test_utils::extract_annotations;
|
||||||
|
|
||||||
use crate::inlay_hints::ReborrowHints;
|
use crate::inlay_hints::AdjustmentHints;
|
||||||
use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
|
use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
|
||||||
|
|
||||||
use super::ClosureReturnTypeHints;
|
use super::ClosureReturnTypeHints;
|
||||||
|
@ -1230,7 +1305,7 @@ mod tests {
|
||||||
chaining_hints: false,
|
chaining_hints: false,
|
||||||
lifetime_elision_hints: LifetimeElisionHints::Never,
|
lifetime_elision_hints: LifetimeElisionHints::Never,
|
||||||
closure_return_type_hints: ClosureReturnTypeHints::Never,
|
closure_return_type_hints: ClosureReturnTypeHints::Never,
|
||||||
reborrow_hints: ReborrowHints::Always,
|
adjustment_hints: AdjustmentHints::Never,
|
||||||
binding_mode_hints: false,
|
binding_mode_hints: false,
|
||||||
hide_named_constructor_hints: false,
|
hide_named_constructor_hints: false,
|
||||||
hide_closure_initialization_hints: false,
|
hide_closure_initialization_hints: false,
|
||||||
|
@ -1242,7 +1317,6 @@ mod tests {
|
||||||
type_hints: true,
|
type_hints: true,
|
||||||
parameter_hints: true,
|
parameter_hints: true,
|
||||||
chaining_hints: true,
|
chaining_hints: true,
|
||||||
reborrow_hints: ReborrowHints::Always,
|
|
||||||
closure_return_type_hints: ClosureReturnTypeHints::WithBlock,
|
closure_return_type_hints: ClosureReturnTypeHints::WithBlock,
|
||||||
binding_mode_hints: true,
|
binding_mode_hints: true,
|
||||||
lifetime_elision_hints: LifetimeElisionHints::Always,
|
lifetime_elision_hints: LifetimeElisionHints::Always,
|
||||||
|
@ -1838,10 +1912,7 @@ fn main() {
|
||||||
.inlay_hints(
|
.inlay_hints(
|
||||||
&InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
|
&InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
|
||||||
file_id,
|
file_id,
|
||||||
Some(FileRange {
|
Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
|
||||||
file_id,
|
|
||||||
range: TextRange::new(TextSize::from(500), TextSize::from(600)),
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let actual =
|
let actual =
|
||||||
|
@ -2845,48 +2916,6 @@ impl () {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hints_implicit_reborrow() {
|
|
||||||
check_with_config(
|
|
||||||
InlayHintsConfig {
|
|
||||||
reborrow_hints: ReborrowHints::Always,
|
|
||||||
parameter_hints: true,
|
|
||||||
..DISABLED_CONFIG
|
|
||||||
},
|
|
||||||
r#"
|
|
||||||
fn __() {
|
|
||||||
let unique = &mut ();
|
|
||||||
let r_mov = unique;
|
|
||||||
let foo: &mut _ = unique;
|
|
||||||
//^^^^^^ &mut *
|
|
||||||
ref_mut_id(unique);
|
|
||||||
//^^^^^^ mut_ref
|
|
||||||
//^^^^^^ &mut *
|
|
||||||
let shared = ref_id(unique);
|
|
||||||
//^^^^^^ shared_ref
|
|
||||||
//^^^^^^ &*
|
|
||||||
let mov = shared;
|
|
||||||
let r_mov: &_ = shared;
|
|
||||||
ref_id(shared);
|
|
||||||
//^^^^^^ shared_ref
|
|
||||||
|
|
||||||
identity(unique);
|
|
||||||
identity(shared);
|
|
||||||
}
|
|
||||||
fn identity<T>(t: T) -> T {
|
|
||||||
t
|
|
||||||
}
|
|
||||||
fn ref_mut_id(mut_ref: &mut ()) -> &mut () {
|
|
||||||
mut_ref
|
|
||||||
//^^^^^^^ &mut *
|
|
||||||
}
|
|
||||||
fn ref_id(shared_ref: &()) -> &() {
|
|
||||||
shared_ref
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hints_binding_modes() {
|
fn hints_binding_modes() {
|
||||||
check_with_config(
|
check_with_config(
|
||||||
|
@ -2994,4 +3023,76 @@ fn f() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adjustment_hints() {
|
||||||
|
check_with_config(
|
||||||
|
InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized
|
||||||
|
fn main() {
|
||||||
|
let _: u32 = loop {};
|
||||||
|
//^^^^^^^<never-to-any>
|
||||||
|
let _: &u32 = &mut 0;
|
||||||
|
//^^^^^^&
|
||||||
|
//^^^^^^*
|
||||||
|
let _: &mut u32 = &mut 0;
|
||||||
|
//^^^^^^&mut $
|
||||||
|
//^^^^^^*
|
||||||
|
let _: *const u32 = &mut 0;
|
||||||
|
//^^^^^^&raw const $
|
||||||
|
//^^^^^^*
|
||||||
|
let _: *mut u32 = &mut 0;
|
||||||
|
//^^^^^^&raw mut $
|
||||||
|
//^^^^^^*
|
||||||
|
let _: fn() = main;
|
||||||
|
//^^^^<fn-item-to-fn-pointer>
|
||||||
|
let _: unsafe fn() = main;
|
||||||
|
//^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
|
||||||
|
//^^^^<fn-item-to-fn-pointer>
|
||||||
|
let _: unsafe fn() = main as fn();
|
||||||
|
//^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
|
||||||
|
let _: fn() = || {};
|
||||||
|
//^^^^^<closure-to-fn-pointer>
|
||||||
|
let _: unsafe fn() = || {};
|
||||||
|
//^^^^^<closure-to-unsafe-fn-pointer>
|
||||||
|
let _: *const u32 = &mut 0u32 as *mut u32;
|
||||||
|
//^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>
|
||||||
|
let _: &mut [_] = &mut [0; 0];
|
||||||
|
//^^^^^^^^^^^<unsize>
|
||||||
|
//^^^^^^^^^^^&mut $
|
||||||
|
//^^^^^^^^^^^*
|
||||||
|
|
||||||
|
Struct.consume();
|
||||||
|
Struct.by_ref();
|
||||||
|
//^^^^^^(
|
||||||
|
//^^^^^^&
|
||||||
|
//^^^^^^)
|
||||||
|
Struct.by_ref_mut();
|
||||||
|
//^^^^^^(
|
||||||
|
//^^^^^^&mut $
|
||||||
|
//^^^^^^)
|
||||||
|
|
||||||
|
(&Struct).consume();
|
||||||
|
//^^^^^^^*
|
||||||
|
(&Struct).by_ref();
|
||||||
|
|
||||||
|
(&mut Struct).consume();
|
||||||
|
//^^^^^^^^^^^*
|
||||||
|
(&mut Struct).by_ref();
|
||||||
|
//^^^^^^^^^^^&
|
||||||
|
//^^^^^^^^^^^*
|
||||||
|
(&mut Struct).by_ref_mut();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct Struct;
|
||||||
|
impl Struct {
|
||||||
|
fn consume(self) {}
|
||||||
|
fn by_ref(&self) {}
|
||||||
|
fn by_ref_mut(&mut self) {}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,8 +81,8 @@ pub use crate::{
|
||||||
highlight_related::{HighlightRelatedConfig, HighlightedRange},
|
highlight_related::{HighlightRelatedConfig, HighlightedRange},
|
||||||
hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
|
hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
|
||||||
inlay_hints::{
|
inlay_hints::{
|
||||||
ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
|
AdjustmentHints, ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig,
|
||||||
InlayTooltip, LifetimeElisionHints, ReborrowHints,
|
InlayKind, InlayTooltip, LifetimeElisionHints,
|
||||||
},
|
},
|
||||||
join_lines::JoinLinesConfig,
|
join_lines::JoinLinesConfig,
|
||||||
markup::Markup,
|
markup::Markup,
|
||||||
|
@ -367,7 +367,7 @@ impl Analysis {
|
||||||
&self,
|
&self,
|
||||||
config: &InlayHintsConfig,
|
config: &InlayHintsConfig,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
range: Option<FileRange>,
|
range: Option<TextRange>,
|
||||||
) -> Cancellable<Vec<InlayHint>> {
|
) -> Cancellable<Vec<InlayHint>> {
|
||||||
self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config))
|
self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
|
//! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
|
||||||
//! for LSIF and LSP.
|
//! for LSIF and LSP.
|
||||||
|
|
||||||
use hir::{db::DefDatabase, AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
|
use hir::{AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::{CrateOrigin, FileId, FileLoader, FilePosition, LangCrateOrigin},
|
base_db::{CrateOrigin, FilePosition, LangCrateOrigin},
|
||||||
defs::{Definition, IdentClass},
|
defs::{Definition, IdentClass},
|
||||||
helpers::pick_best_token,
|
helpers::pick_best_token,
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
|
@ -11,7 +11,7 @@ use ide_db::{
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{AstNode, SyntaxKind::*, T};
|
use syntax::{AstNode, SyntaxKind::*, T};
|
||||||
|
|
||||||
use crate::{doc_links::token_as_doc_comment, RangeInfo};
|
use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum MonikerDescriptorKind {
|
pub enum MonikerDescriptorKind {
|
||||||
|
@ -77,25 +77,13 @@ pub struct PackageInformation {
|
||||||
pub version: Option<String>,
|
pub version: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option<Crate> {
|
|
||||||
for &krate in db.relevant_crates(file_id).iter() {
|
|
||||||
let crate_def_map = db.crate_def_map(krate);
|
|
||||||
for (_, data) in crate_def_map.modules() {
|
|
||||||
if data.origin.file_id() == Some(file_id) {
|
|
||||||
return Some(krate.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn moniker(
|
pub(crate) fn moniker(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
FilePosition { file_id, offset }: FilePosition,
|
FilePosition { file_id, offset }: FilePosition,
|
||||||
) -> Option<RangeInfo<Vec<MonikerResult>>> {
|
) -> Option<RangeInfo<Vec<MonikerResult>>> {
|
||||||
let sema = &Semantics::new(db);
|
let sema = &Semantics::new(db);
|
||||||
let file = sema.parse(file_id).syntax().clone();
|
let file = sema.parse(file_id).syntax().clone();
|
||||||
let current_crate = crate_for_file(db, file_id)?;
|
let current_crate: hir::Crate = crates_for(db, file_id).pop()?.into();
|
||||||
let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
|
let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
|
||||||
IDENT
|
IDENT
|
||||||
| INT_NUMBER
|
| INT_NUMBER
|
||||||
|
|
|
@ -16,6 +16,7 @@ use ide_db::{
|
||||||
search::{ReferenceCategory, SearchScope, UsageSearchResult},
|
search::{ReferenceCategory, SearchScope, UsageSearchResult},
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
use stdx::hash::NoHashHashMap;
|
use stdx::hash::NoHashHashMap;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::find_node_at_offset,
|
algo::find_node_at_offset,
|
||||||
|
@ -86,6 +87,7 @@ pub(crate) fn find_all_refs(
|
||||||
file_id,
|
file_id,
|
||||||
refs.into_iter()
|
refs.into_iter()
|
||||||
.map(|file_ref| (file_ref.range, file_ref.category))
|
.map(|file_ref| (file_ref.range, file_ref.category))
|
||||||
|
.unique()
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1345,5 +1345,36 @@ fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
|
||||||
^^ ---
|
^^ ---
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn f<T, F: FnOnce(&T, u16) -> &T>(f: F) {
|
||||||
|
f($0)
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(&T, u16) -> &T
|
||||||
|
^^ ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn regression_13579() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn f() {
|
||||||
|
take(2)($0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take<C, Error>(
|
||||||
|
count: C
|
||||||
|
) -> impl Fn() -> C {
|
||||||
|
move || count
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
() -> i32
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,8 @@ use syntax::{AstNode, SyntaxKind::*, SyntaxToken, TextRange, T};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
hover::hover_for_definition,
|
hover::hover_for_definition,
|
||||||
moniker::{crate_for_file, def_to_moniker, MonikerResult},
|
moniker::{def_to_moniker, MonikerResult},
|
||||||
|
parent_module::crates_for,
|
||||||
Analysis, Fold, HoverConfig, HoverDocFormat, HoverResult, InlayHint, InlayHintsConfig,
|
Analysis, Fold, HoverConfig, HoverDocFormat, HoverResult, InlayHint, InlayHintsConfig,
|
||||||
TryToNav,
|
TryToNav,
|
||||||
};
|
};
|
||||||
|
@ -99,7 +100,7 @@ fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
|
||||||
|
|
||||||
impl StaticIndex<'_> {
|
impl StaticIndex<'_> {
|
||||||
fn add_file(&mut self, file_id: FileId) {
|
fn add_file(&mut self, file_id: FileId) {
|
||||||
let current_crate = crate_for_file(self.db, file_id);
|
let current_crate = crates_for(self.db, file_id).pop().map(Into::into);
|
||||||
let folds = self.analysis.folding_ranges(file_id).unwrap();
|
let folds = self.analysis.folding_ranges(file_id).unwrap();
|
||||||
let inlay_hints = self
|
let inlay_hints = self
|
||||||
.analysis
|
.analysis
|
||||||
|
@ -111,7 +112,7 @@ impl StaticIndex<'_> {
|
||||||
chaining_hints: true,
|
chaining_hints: true,
|
||||||
closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock,
|
closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock,
|
||||||
lifetime_elision_hints: crate::LifetimeElisionHints::Never,
|
lifetime_elision_hints: crate::LifetimeElisionHints::Never,
|
||||||
reborrow_hints: crate::ReborrowHints::Never,
|
adjustment_hints: crate::AdjustmentHints::Never,
|
||||||
hide_named_constructor_hints: false,
|
hide_named_constructor_hints: false,
|
||||||
hide_closure_initialization_hints: false,
|
hide_closure_initialization_hints: false,
|
||||||
param_names_for_lifetime_elision_hints: false,
|
param_names_for_lifetime_elision_hints: false,
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tracking = []
|
tracking = []
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -12,6 +12,9 @@ use tt::buffer::{Cursor, TokenBuffer};
|
||||||
|
|
||||||
use crate::{to_parser_input::to_parser_input, tt_iter::TtIter, TokenMap};
|
use crate::{to_parser_input::to_parser_input, tt_iter::TtIter, TokenMap};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
/// Convert the syntax node to a `TokenTree` (what macro
|
/// Convert the syntax node to a `TokenTree` (what macro
|
||||||
/// will consume).
|
/// will consume).
|
||||||
pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> (tt::Subtree, TokenMap) {
|
pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> (tt::Subtree, TokenMap) {
|
||||||
|
@ -35,7 +38,7 @@ pub fn syntax_node_to_token_tree_with_modifications(
|
||||||
append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
|
append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
|
||||||
) -> (tt::Subtree, TokenMap, u32) {
|
) -> (tt::Subtree, TokenMap, u32) {
|
||||||
let global_offset = node.text_range().start();
|
let global_offset = node.text_range().start();
|
||||||
let mut c = Convertor::new(node, global_offset, existing_token_map, next_id, replace, append);
|
let mut c = Converter::new(node, global_offset, existing_token_map, next_id, replace, append);
|
||||||
let subtree = convert_tokens(&mut c);
|
let subtree = convert_tokens(&mut c);
|
||||||
c.id_alloc.map.shrink_to_fit();
|
c.id_alloc.map.shrink_to_fit();
|
||||||
always!(c.replace.is_empty(), "replace: {:?}", c.replace);
|
always!(c.replace.is_empty(), "replace: {:?}", c.replace);
|
||||||
|
@ -100,7 +103,7 @@ pub fn parse_to_token_tree(text: &str) -> Option<(tt::Subtree, TokenMap)> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut conv = RawConvertor {
|
let mut conv = RawConverter {
|
||||||
lexed,
|
lexed,
|
||||||
pos: 0,
|
pos: 0,
|
||||||
id_alloc: TokenIdAlloc {
|
id_alloc: TokenIdAlloc {
|
||||||
|
@ -148,7 +151,7 @@ pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec<tt::Subtree> {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
|
fn convert_tokens<C: TokenConverter>(conv: &mut C) -> tt::Subtree {
|
||||||
struct StackEntry {
|
struct StackEntry {
|
||||||
subtree: tt::Subtree,
|
subtree: tt::Subtree,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
|
@ -228,7 +231,7 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
|
||||||
}
|
}
|
||||||
|
|
||||||
let spacing = match conv.peek().map(|next| next.kind(conv)) {
|
let spacing = match conv.peek().map(|next| next.kind(conv)) {
|
||||||
Some(kind) if !kind.is_trivia() => tt::Spacing::Joint,
|
Some(kind) if is_single_token_op(kind) => tt::Spacing::Joint,
|
||||||
_ => tt::Spacing::Alone,
|
_ => tt::Spacing::Alone,
|
||||||
};
|
};
|
||||||
let char = match token.to_char(conv) {
|
let char = match token.to_char(conv) {
|
||||||
|
@ -307,6 +310,35 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_single_token_op(kind: SyntaxKind) -> bool {
|
||||||
|
matches!(
|
||||||
|
kind,
|
||||||
|
EQ | L_ANGLE
|
||||||
|
| R_ANGLE
|
||||||
|
| BANG
|
||||||
|
| AMP
|
||||||
|
| PIPE
|
||||||
|
| TILDE
|
||||||
|
| AT
|
||||||
|
| DOT
|
||||||
|
| COMMA
|
||||||
|
| SEMICOLON
|
||||||
|
| COLON
|
||||||
|
| POUND
|
||||||
|
| DOLLAR
|
||||||
|
| QUESTION
|
||||||
|
| PLUS
|
||||||
|
| MINUS
|
||||||
|
| STAR
|
||||||
|
| SLASH
|
||||||
|
| PERCENT
|
||||||
|
| CARET
|
||||||
|
// LIFETIME_IDENT will be split into a sequence of `'` (a single quote) and an
|
||||||
|
// identifier.
|
||||||
|
| LIFETIME_IDENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the textual content of a doc comment block as a quoted string
|
/// Returns the textual content of a doc comment block as a quoted string
|
||||||
/// That is, strips leading `///` (or `/**`, etc)
|
/// That is, strips leading `///` (or `/**`, etc)
|
||||||
/// and strips the ending `*/`
|
/// and strips the ending `*/`
|
||||||
|
@ -425,8 +457,8 @@ impl TokenIdAlloc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A raw token (straight from lexer) convertor
|
/// A raw token (straight from lexer) converter
|
||||||
struct RawConvertor<'a> {
|
struct RawConverter<'a> {
|
||||||
lexed: parser::LexedStr<'a>,
|
lexed: parser::LexedStr<'a>,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
id_alloc: TokenIdAlloc,
|
id_alloc: TokenIdAlloc,
|
||||||
|
@ -442,7 +474,7 @@ trait SrcToken<Ctx>: std::fmt::Debug {
|
||||||
fn synthetic_id(&self, ctx: &Ctx) -> Option<SyntheticTokenId>;
|
fn synthetic_id(&self, ctx: &Ctx) -> Option<SyntheticTokenId>;
|
||||||
}
|
}
|
||||||
|
|
||||||
trait TokenConvertor: Sized {
|
trait TokenConverter: Sized {
|
||||||
type Token: SrcToken<Self>;
|
type Token: SrcToken<Self>;
|
||||||
|
|
||||||
fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>>;
|
fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>>;
|
||||||
|
@ -454,25 +486,25 @@ trait TokenConvertor: Sized {
|
||||||
fn id_alloc(&mut self) -> &mut TokenIdAlloc;
|
fn id_alloc(&mut self) -> &mut TokenIdAlloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SrcToken<RawConvertor<'a>> for usize {
|
impl<'a> SrcToken<RawConverter<'a>> for usize {
|
||||||
fn kind(&self, ctx: &RawConvertor<'a>) -> SyntaxKind {
|
fn kind(&self, ctx: &RawConverter<'a>) -> SyntaxKind {
|
||||||
ctx.lexed.kind(*self)
|
ctx.lexed.kind(*self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_char(&self, ctx: &RawConvertor<'a>) -> Option<char> {
|
fn to_char(&self, ctx: &RawConverter<'a>) -> Option<char> {
|
||||||
ctx.lexed.text(*self).chars().next()
|
ctx.lexed.text(*self).chars().next()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_text(&self, ctx: &RawConvertor<'_>) -> SmolStr {
|
fn to_text(&self, ctx: &RawConverter<'_>) -> SmolStr {
|
||||||
ctx.lexed.text(*self).into()
|
ctx.lexed.text(*self).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn synthetic_id(&self, _ctx: &RawConvertor<'a>) -> Option<SyntheticTokenId> {
|
fn synthetic_id(&self, _ctx: &RawConverter<'a>) -> Option<SyntheticTokenId> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TokenConvertor for RawConvertor<'a> {
|
impl<'a> TokenConverter for RawConverter<'a> {
|
||||||
type Token = usize;
|
type Token = usize;
|
||||||
|
|
||||||
fn convert_doc_comment(&self, &token: &usize) -> Option<Vec<tt::TokenTree>> {
|
fn convert_doc_comment(&self, &token: &usize) -> Option<Vec<tt::TokenTree>> {
|
||||||
|
@ -504,7 +536,7 @@ impl<'a> TokenConvertor for RawConvertor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Convertor {
|
struct Converter {
|
||||||
id_alloc: TokenIdAlloc,
|
id_alloc: TokenIdAlloc,
|
||||||
current: Option<SyntaxToken>,
|
current: Option<SyntaxToken>,
|
||||||
current_synthetic: Vec<SyntheticToken>,
|
current_synthetic: Vec<SyntheticToken>,
|
||||||
|
@ -515,7 +547,7 @@ struct Convertor {
|
||||||
punct_offset: Option<(SyntaxToken, TextSize)>,
|
punct_offset: Option<(SyntaxToken, TextSize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Convertor {
|
impl Converter {
|
||||||
fn new(
|
fn new(
|
||||||
node: &SyntaxNode,
|
node: &SyntaxNode,
|
||||||
global_offset: TextSize,
|
global_offset: TextSize,
|
||||||
|
@ -523,11 +555,11 @@ impl Convertor {
|
||||||
next_id: u32,
|
next_id: u32,
|
||||||
mut replace: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
|
mut replace: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
|
||||||
mut append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
|
mut append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
|
||||||
) -> Convertor {
|
) -> Converter {
|
||||||
let range = node.text_range();
|
let range = node.text_range();
|
||||||
let mut preorder = node.preorder_with_tokens();
|
let mut preorder = node.preorder_with_tokens();
|
||||||
let (first, synthetic) = Self::next_token(&mut preorder, &mut replace, &mut append);
|
let (first, synthetic) = Self::next_token(&mut preorder, &mut replace, &mut append);
|
||||||
Convertor {
|
Converter {
|
||||||
id_alloc: { TokenIdAlloc { map: existing_token_map, global_offset, next_id } },
|
id_alloc: { TokenIdAlloc { map: existing_token_map, global_offset, next_id } },
|
||||||
current: first,
|
current: first,
|
||||||
current_synthetic: synthetic,
|
current_synthetic: synthetic,
|
||||||
|
@ -590,15 +622,15 @@ impl SynToken {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SrcToken<Convertor> for SynToken {
|
impl SrcToken<Converter> for SynToken {
|
||||||
fn kind(&self, _ctx: &Convertor) -> SyntaxKind {
|
fn kind(&self, ctx: &Converter) -> SyntaxKind {
|
||||||
match self {
|
match self {
|
||||||
SynToken::Ordinary(token) => token.kind(),
|
SynToken::Ordinary(token) => token.kind(),
|
||||||
SynToken::Punch(token, _) => token.kind(),
|
SynToken::Punch(..) => SyntaxKind::from_char(self.to_char(ctx).unwrap()).unwrap(),
|
||||||
SynToken::Synthetic(token) => token.kind,
|
SynToken::Synthetic(token) => token.kind,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn to_char(&self, _ctx: &Convertor) -> Option<char> {
|
fn to_char(&self, _ctx: &Converter) -> Option<char> {
|
||||||
match self {
|
match self {
|
||||||
SynToken::Ordinary(_) => None,
|
SynToken::Ordinary(_) => None,
|
||||||
SynToken::Punch(it, i) => it.text().chars().nth((*i).into()),
|
SynToken::Punch(it, i) => it.text().chars().nth((*i).into()),
|
||||||
|
@ -606,7 +638,7 @@ impl SrcToken<Convertor> for SynToken {
|
||||||
SynToken::Synthetic(_) => None,
|
SynToken::Synthetic(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn to_text(&self, _ctx: &Convertor) -> SmolStr {
|
fn to_text(&self, _ctx: &Converter) -> SmolStr {
|
||||||
match self {
|
match self {
|
||||||
SynToken::Ordinary(token) => token.text().into(),
|
SynToken::Ordinary(token) => token.text().into(),
|
||||||
SynToken::Punch(token, _) => token.text().into(),
|
SynToken::Punch(token, _) => token.text().into(),
|
||||||
|
@ -614,7 +646,7 @@ impl SrcToken<Convertor> for SynToken {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn synthetic_id(&self, _ctx: &Convertor) -> Option<SyntheticTokenId> {
|
fn synthetic_id(&self, _ctx: &Converter) -> Option<SyntheticTokenId> {
|
||||||
match self {
|
match self {
|
||||||
SynToken::Synthetic(token) => Some(token.id),
|
SynToken::Synthetic(token) => Some(token.id),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -622,7 +654,7 @@ impl SrcToken<Convertor> for SynToken {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenConvertor for Convertor {
|
impl TokenConverter for Converter {
|
||||||
type Token = SynToken;
|
type Token = SynToken;
|
||||||
fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> {
|
fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> {
|
||||||
convert_doc_comment(token.token()?)
|
convert_doc_comment(token.token()?)
|
||||||
|
@ -651,7 +683,7 @@ impl TokenConvertor for Convertor {
|
||||||
}
|
}
|
||||||
|
|
||||||
let curr = self.current.clone()?;
|
let curr = self.current.clone()?;
|
||||||
if !&self.range.contains_range(curr.text_range()) {
|
if !self.range.contains_range(curr.text_range()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let (new_current, new_synth) =
|
let (new_current, new_synth) =
|
||||||
|
@ -809,12 +841,15 @@ impl<'a> TtTreeSink<'a> {
|
||||||
let next = last.bump();
|
let next = last.bump();
|
||||||
if let (
|
if let (
|
||||||
Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(curr), _)),
|
Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(curr), _)),
|
||||||
Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(_), _)),
|
Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(next), _)),
|
||||||
) = (last.token_tree(), next.token_tree())
|
) = (last.token_tree(), next.token_tree())
|
||||||
{
|
{
|
||||||
// Note: We always assume the semi-colon would be the last token in
|
// Note: We always assume the semi-colon would be the last token in
|
||||||
// other parts of RA such that we don't add whitespace here.
|
// other parts of RA such that we don't add whitespace here.
|
||||||
if curr.spacing == tt::Spacing::Alone && curr.char != ';' {
|
//
|
||||||
|
// When `next` is a `Punct` of `'`, that's a part of a lifetime identifier so we don't
|
||||||
|
// need to add whitespace either.
|
||||||
|
if curr.spacing == tt::Spacing::Alone && curr.char != ';' && next.char != '\'' {
|
||||||
self.inner.token(WHITESPACE, " ");
|
self.inner.token(WHITESPACE, " ");
|
||||||
self.text_pos += TextSize::of(' ');
|
self.text_pos += TextSize::of(' ');
|
||||||
}
|
}
|
||||||
|
|
93
crates/mbe/src/syntax_bridge/tests.rs
Normal file
93
crates/mbe/src/syntax_bridge/tests.rs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use syntax::{ast, AstNode};
|
||||||
|
use test_utils::extract_annotations;
|
||||||
|
use tt::{
|
||||||
|
buffer::{TokenBuffer, TokenTreeRef},
|
||||||
|
Leaf, Punct, Spacing,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::syntax_node_to_token_tree;
|
||||||
|
|
||||||
|
fn check_punct_spacing(fixture: &str) {
|
||||||
|
let source_file = ast::SourceFile::parse(fixture).ok().unwrap();
|
||||||
|
let (subtree, token_map) = syntax_node_to_token_tree(source_file.syntax());
|
||||||
|
let mut annotations: HashMap<_, _> = extract_annotations(fixture)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(range, annotation)| {
|
||||||
|
let token = token_map.token_by_range(range).expect("no token found");
|
||||||
|
let spacing = match annotation.as_str() {
|
||||||
|
"Alone" => Spacing::Alone,
|
||||||
|
"Joint" => Spacing::Joint,
|
||||||
|
a => panic!("unknown annotation: {}", a),
|
||||||
|
};
|
||||||
|
(token, spacing)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let buf = TokenBuffer::from_subtree(&subtree);
|
||||||
|
let mut cursor = buf.begin();
|
||||||
|
while !cursor.eof() {
|
||||||
|
while let Some(token_tree) = cursor.token_tree() {
|
||||||
|
if let TokenTreeRef::Leaf(Leaf::Punct(Punct { spacing, id, .. }), _) = token_tree {
|
||||||
|
if let Some(expected) = annotations.remove(&id) {
|
||||||
|
assert_eq!(expected, *spacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor = cursor.bump_subtree();
|
||||||
|
}
|
||||||
|
cursor = cursor.bump();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(annotations.is_empty(), "unchecked annotations: {:?}", annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn punct_spacing() {
|
||||||
|
check_punct_spacing(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
0+0;
|
||||||
|
//^ Alone
|
||||||
|
0+(0);
|
||||||
|
//^ Alone
|
||||||
|
0<=0;
|
||||||
|
//^ Joint
|
||||||
|
// ^ Alone
|
||||||
|
0<=(0);
|
||||||
|
// ^ Alone
|
||||||
|
a=0;
|
||||||
|
//^ Alone
|
||||||
|
a=(0);
|
||||||
|
//^ Alone
|
||||||
|
a+=0;
|
||||||
|
//^ Joint
|
||||||
|
// ^ Alone
|
||||||
|
a+=(0);
|
||||||
|
// ^ Alone
|
||||||
|
a&&b;
|
||||||
|
//^ Joint
|
||||||
|
// ^ Alone
|
||||||
|
a&&(b);
|
||||||
|
// ^ Alone
|
||||||
|
foo::bar;
|
||||||
|
// ^ Joint
|
||||||
|
// ^ Alone
|
||||||
|
use foo::{bar,baz,};
|
||||||
|
// ^ Alone
|
||||||
|
// ^ Alone
|
||||||
|
// ^ Alone
|
||||||
|
struct Struct<'a> {};
|
||||||
|
// ^ Joint
|
||||||
|
// ^ Joint
|
||||||
|
Struct::<0>;
|
||||||
|
// ^ Alone
|
||||||
|
Struct::<{0}>;
|
||||||
|
// ^ Alone
|
||||||
|
;;
|
||||||
|
//^ Joint
|
||||||
|
// ^ Alone
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" }
|
proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" }
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -117,7 +117,7 @@ impl Abi {
|
||||||
let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?;
|
let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?;
|
||||||
Ok(Abi::Abi1_63(inner))
|
Ok(Abi::Abi1_63(inner))
|
||||||
}
|
}
|
||||||
_ => Err(LoadProcMacroDylibError::UnsupportedABI),
|
_ => Err(LoadProcMacroDylibError::UnsupportedABI(info.version_string.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,14 +80,14 @@ fn load_library(file: &Path) -> Result<Library, libloading::Error> {
|
||||||
pub enum LoadProcMacroDylibError {
|
pub enum LoadProcMacroDylibError {
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
LibLoading(libloading::Error),
|
LibLoading(libloading::Error),
|
||||||
UnsupportedABI,
|
UnsupportedABI(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for LoadProcMacroDylibError {
|
impl fmt::Display for LoadProcMacroDylibError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Io(e) => e.fmt(f),
|
Self::Io(e) => e.fmt(f),
|
||||||
Self::UnsupportedABI => write!(f, "unsupported ABI version"),
|
Self::UnsupportedABI(v) => write!(f, "unsupported ABI `{v}`"),
|
||||||
Self::LibLoading(e) => e.fmt(f),
|
Self::LibLoading(e) => e.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,12 +113,12 @@ impl ProcMacroSrv {
|
||||||
|
|
||||||
fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> {
|
fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> {
|
||||||
let time = fs::metadata(path).and_then(|it| it.modified()).map_err(|err| {
|
let time = fs::metadata(path).and_then(|it| it.modified()).map_err(|err| {
|
||||||
format!("Failed to get file metadata for {}: {:?}", path.display(), err)
|
format!("Failed to get file metadata for {}: {}", path.display(), err)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(match self.expanders.entry((path.to_path_buf(), time)) {
|
Ok(match self.expanders.entry((path.to_path_buf(), time)) {
|
||||||
Entry::Vacant(v) => v.insert(dylib::Expander::new(path).map_err(|err| {
|
Entry::Vacant(v) => v.insert(dylib::Expander::new(path).map_err(|err| {
|
||||||
format!("Cannot create expander for {}: {:?}", path.display(), err)
|
format!("Cannot create expander for {}: {}", path.display(), err)
|
||||||
})?),
|
})?),
|
||||||
Entry::Occupied(e) => e.into_mut(),
|
Entry::Occupied(e) => e.into_mut(),
|
||||||
})
|
})
|
||||||
|
|
|
@ -19,7 +19,7 @@ fn test_derive_error() {
|
||||||
expect![[r##"
|
expect![[r##"
|
||||||
SUBTREE $
|
SUBTREE $
|
||||||
IDENT compile_error 4294967295
|
IDENT compile_error 4294967295
|
||||||
PUNCH ! [joint] 4294967295
|
PUNCH ! [alone] 4294967295
|
||||||
SUBTREE () 4294967295
|
SUBTREE () 4294967295
|
||||||
LITERAL "#[derive(DeriveError)] struct S ;" 4294967295
|
LITERAL "#[derive(DeriveError)] struct S ;" 4294967295
|
||||||
PUNCH ; [alone] 4294967295"##]],
|
PUNCH ; [alone] 4294967295"##]],
|
||||||
|
@ -109,7 +109,7 @@ fn test_fn_like_macro_clone_literals() {
|
||||||
PUNCH , [alone] 4294967295
|
PUNCH , [alone] 4294967295
|
||||||
LITERAL 2_u32 4294967295
|
LITERAL 2_u32 4294967295
|
||||||
PUNCH , [alone] 4294967295
|
PUNCH , [alone] 4294967295
|
||||||
PUNCH - [joint] 4294967295
|
PUNCH - [alone] 4294967295
|
||||||
LITERAL 4i64 4294967295
|
LITERAL 4i64 4294967295
|
||||||
PUNCH , [alone] 4294967295
|
PUNCH , [alone] 4294967295
|
||||||
LITERAL 3.14f32 4294967295
|
LITERAL 3.14f32 4294967295
|
||||||
|
@ -130,7 +130,7 @@ fn test_attr_macro() {
|
||||||
expect![[r##"
|
expect![[r##"
|
||||||
SUBTREE $
|
SUBTREE $
|
||||||
IDENT compile_error 4294967295
|
IDENT compile_error 4294967295
|
||||||
PUNCH ! [joint] 4294967295
|
PUNCH ! [alone] 4294967295
|
||||||
SUBTREE () 4294967295
|
SUBTREE () 4294967295
|
||||||
LITERAL "#[attr_error(some arguments)] mod m {}" 4294967295
|
LITERAL "#[attr_error(some arguments)] mod m {}" 4294967295
|
||||||
PUNCH ; [alone] 4294967295"##]],
|
PUNCH ; [alone] 4294967295"##]],
|
||||||
|
|
|
@ -3,7 +3,7 @@ name = "proc-macro-test"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
|
@ -3,7 +3,7 @@ name = "proc-macro-test-impl"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
||||||
description = "TBD"
|
description = "TBD"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl WorkspaceBuildScripts {
|
||||||
cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);
|
cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);
|
||||||
|
|
||||||
// --all-targets includes tests, benches and examples in addition to the
|
// --all-targets includes tests, benches and examples in addition to the
|
||||||
// default lib and bins. This is an independent concept from the --targets
|
// default lib and bins. This is an independent concept from the --target
|
||||||
// flag below.
|
// flag below.
|
||||||
cmd.arg("--all-targets");
|
cmd.arg("--all-targets");
|
||||||
|
|
||||||
|
|
|
@ -270,11 +270,7 @@ impl CargoWorkspace {
|
||||||
config: &CargoConfig,
|
config: &CargoConfig,
|
||||||
progress: &dyn Fn(String),
|
progress: &dyn Fn(String),
|
||||||
) -> Result<cargo_metadata::Metadata> {
|
) -> Result<cargo_metadata::Metadata> {
|
||||||
let target = config
|
let targets = find_list_of_build_targets(config, cargo_toml);
|
||||||
.target
|
|
||||||
.clone()
|
|
||||||
.or_else(|| cargo_config_build_target(cargo_toml, &config.extra_env))
|
|
||||||
.or_else(|| rustc_discover_host_triple(cargo_toml, &config.extra_env));
|
|
||||||
|
|
||||||
let mut meta = MetadataCommand::new();
|
let mut meta = MetadataCommand::new();
|
||||||
meta.cargo_path(toolchain::cargo());
|
meta.cargo_path(toolchain::cargo());
|
||||||
|
@ -294,8 +290,12 @@ impl CargoWorkspace {
|
||||||
}
|
}
|
||||||
meta.current_dir(current_dir.as_os_str());
|
meta.current_dir(current_dir.as_os_str());
|
||||||
|
|
||||||
if let Some(target) = target {
|
if !targets.is_empty() {
|
||||||
meta.other_options(vec![String::from("--filter-platform"), target]);
|
let other_options: Vec<_> = targets
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|target| ["--filter-platform".to_string(), target])
|
||||||
|
.collect();
|
||||||
|
meta.other_options(other_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Fetching metadata is a slow process, as it might require
|
// FIXME: Fetching metadata is a slow process, as it might require
|
||||||
|
@ -469,6 +469,19 @@ impl CargoWorkspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_list_of_build_targets(config: &CargoConfig, cargo_toml: &ManifestPath) -> Vec<String> {
|
||||||
|
if let Some(target) = &config.target {
|
||||||
|
return [target.into()].to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env);
|
||||||
|
if !build_targets.is_empty() {
|
||||||
|
return build_targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
rustc_discover_host_triple(cargo_toml, &config.extra_env).into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn rustc_discover_host_triple(
|
fn rustc_discover_host_triple(
|
||||||
cargo_toml: &ManifestPath,
|
cargo_toml: &ManifestPath,
|
||||||
extra_env: &FxHashMap<String, String>,
|
extra_env: &FxHashMap<String, String>,
|
||||||
|
@ -499,7 +512,7 @@ fn rustc_discover_host_triple(
|
||||||
fn cargo_config_build_target(
|
fn cargo_config_build_target(
|
||||||
cargo_toml: &ManifestPath,
|
cargo_toml: &ManifestPath,
|
||||||
extra_env: &FxHashMap<String, String>,
|
extra_env: &FxHashMap<String, String>,
|
||||||
) -> Option<String> {
|
) -> Vec<String> {
|
||||||
let mut cargo_config = Command::new(toolchain::cargo());
|
let mut cargo_config = Command::new(toolchain::cargo());
|
||||||
cargo_config.envs(extra_env);
|
cargo_config.envs(extra_env);
|
||||||
cargo_config
|
cargo_config
|
||||||
|
@ -507,12 +520,21 @@ fn cargo_config_build_target(
|
||||||
.args(&["-Z", "unstable-options", "config", "get", "build.target"])
|
.args(&["-Z", "unstable-options", "config", "get", "build.target"])
|
||||||
.env("RUSTC_BOOTSTRAP", "1");
|
.env("RUSTC_BOOTSTRAP", "1");
|
||||||
// if successful we receive `build.target = "target-triple"`
|
// if successful we receive `build.target = "target-triple"`
|
||||||
|
// or `build.target = ["<target 1>", ..]`
|
||||||
tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
|
tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
|
||||||
match utf8_stdout(cargo_config) {
|
utf8_stdout(cargo_config).map(parse_output_cargo_config_build_target).unwrap_or_default()
|
||||||
Ok(stdout) => stdout
|
}
|
||||||
.strip_prefix("build.target = \"")
|
|
||||||
.and_then(|stdout| stdout.strip_suffix('"'))
|
fn parse_output_cargo_config_build_target(stdout: String) -> Vec<String> {
|
||||||
.map(ToOwned::to_owned),
|
let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
|
||||||
Err(_) => None,
|
|
||||||
}
|
if !trimmed.starts_with('[') {
|
||||||
|
return [trimmed.to_string()].to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = serde_json::from_str(trimmed);
|
||||||
|
if let Err(e) = &res {
|
||||||
|
tracing::warn!("Failed to parse `build.target` as an array of target: {}`", e);
|
||||||
|
}
|
||||||
|
res.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,14 +128,18 @@ impl Sysroot {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(alloc) = sysroot.by_name("alloc") {
|
if let Some(alloc) = sysroot.by_name("alloc") {
|
||||||
if let Some(core) = sysroot.by_name("core") {
|
for dep in ALLOC_DEPS.trim().lines() {
|
||||||
sysroot.crates[alloc].deps.push(core);
|
if let Some(dep) = sysroot.by_name(dep) {
|
||||||
|
sysroot.crates[alloc].deps.push(dep)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(proc_macro) = sysroot.by_name("proc_macro") {
|
if let Some(proc_macro) = sysroot.by_name("proc_macro") {
|
||||||
if let Some(std) = sysroot.by_name("std") {
|
for dep in PROC_MACRO_DEPS.trim().lines() {
|
||||||
sysroot.crates[proc_macro].deps.push(std);
|
if let Some(dep) = sysroot.by_name(dep) {
|
||||||
|
sysroot.crates[proc_macro].deps.push(dep)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +243,7 @@ fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
|
||||||
|
|
||||||
const SYSROOT_CRATES: &str = "
|
const SYSROOT_CRATES: &str = "
|
||||||
alloc
|
alloc
|
||||||
|
backtrace
|
||||||
core
|
core
|
||||||
panic_abort
|
panic_abort
|
||||||
panic_unwind
|
panic_unwind
|
||||||
|
@ -246,17 +251,19 @@ proc_macro
|
||||||
profiler_builtins
|
profiler_builtins
|
||||||
std
|
std
|
||||||
stdarch/crates/std_detect
|
stdarch/crates/std_detect
|
||||||
term
|
|
||||||
test
|
test
|
||||||
unwind";
|
unwind";
|
||||||
|
|
||||||
|
const ALLOC_DEPS: &str = "core";
|
||||||
|
|
||||||
const STD_DEPS: &str = "
|
const STD_DEPS: &str = "
|
||||||
alloc
|
alloc
|
||||||
core
|
|
||||||
panic_abort
|
|
||||||
panic_unwind
|
panic_unwind
|
||||||
|
panic_abort
|
||||||
|
core
|
||||||
profiler_builtins
|
profiler_builtins
|
||||||
|
unwind
|
||||||
std_detect
|
std_detect
|
||||||
term
|
test";
|
||||||
test
|
|
||||||
unwind";
|
const PROC_MACRO_DEPS: &str = "std";
|
||||||
|
|
|
@ -1566,10 +1566,10 @@ fn rust_project_hello_world_project_model() {
|
||||||
},
|
},
|
||||||
Dependency {
|
Dependency {
|
||||||
crate_id: CrateId(
|
crate_id: CrateId(
|
||||||
1,
|
3,
|
||||||
),
|
),
|
||||||
name: CrateName(
|
name: CrateName(
|
||||||
"core",
|
"panic_unwind",
|
||||||
),
|
),
|
||||||
prelude: true,
|
prelude: true,
|
||||||
},
|
},
|
||||||
|
@ -1584,10 +1584,10 @@ fn rust_project_hello_world_project_model() {
|
||||||
},
|
},
|
||||||
Dependency {
|
Dependency {
|
||||||
crate_id: CrateId(
|
crate_id: CrateId(
|
||||||
3,
|
1,
|
||||||
),
|
),
|
||||||
name: CrateName(
|
name: CrateName(
|
||||||
"panic_unwind",
|
"core",
|
||||||
),
|
),
|
||||||
prelude: true,
|
prelude: true,
|
||||||
},
|
},
|
||||||
|
@ -1600,6 +1600,15 @@ fn rust_project_hello_world_project_model() {
|
||||||
),
|
),
|
||||||
prelude: true,
|
prelude: true,
|
||||||
},
|
},
|
||||||
|
Dependency {
|
||||||
|
crate_id: CrateId(
|
||||||
|
9,
|
||||||
|
),
|
||||||
|
name: CrateName(
|
||||||
|
"unwind",
|
||||||
|
),
|
||||||
|
prelude: true,
|
||||||
|
},
|
||||||
Dependency {
|
Dependency {
|
||||||
crate_id: CrateId(
|
crate_id: CrateId(
|
||||||
7,
|
7,
|
||||||
|
@ -1613,29 +1622,11 @@ fn rust_project_hello_world_project_model() {
|
||||||
crate_id: CrateId(
|
crate_id: CrateId(
|
||||||
8,
|
8,
|
||||||
),
|
),
|
||||||
name: CrateName(
|
|
||||||
"term",
|
|
||||||
),
|
|
||||||
prelude: true,
|
|
||||||
},
|
|
||||||
Dependency {
|
|
||||||
crate_id: CrateId(
|
|
||||||
9,
|
|
||||||
),
|
|
||||||
name: CrateName(
|
name: CrateName(
|
||||||
"test",
|
"test",
|
||||||
),
|
),
|
||||||
prelude: true,
|
prelude: true,
|
||||||
},
|
},
|
||||||
Dependency {
|
|
||||||
crate_id: CrateId(
|
|
||||||
10,
|
|
||||||
),
|
|
||||||
name: CrateName(
|
|
||||||
"unwind",
|
|
||||||
),
|
|
||||||
prelude: true,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
proc_macro: Err(
|
proc_macro: Err(
|
||||||
"no proc macro loaded for sysroot crate",
|
"no proc macro loaded for sysroot crate",
|
||||||
|
@ -1687,40 +1678,6 @@ fn rust_project_hello_world_project_model() {
|
||||||
),
|
),
|
||||||
edition: Edition2018,
|
edition: Edition2018,
|
||||||
version: None,
|
version: None,
|
||||||
display_name: Some(
|
|
||||||
CrateDisplayName {
|
|
||||||
crate_name: CrateName(
|
|
||||||
"term",
|
|
||||||
),
|
|
||||||
canonical_name: "term",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
cfg_options: CfgOptions(
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
potential_cfg_options: CfgOptions(
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
env: Env {
|
|
||||||
entries: {},
|
|
||||||
},
|
|
||||||
dependencies: [],
|
|
||||||
proc_macro: Err(
|
|
||||||
"no proc macro loaded for sysroot crate",
|
|
||||||
),
|
|
||||||
origin: Lang(
|
|
||||||
Other,
|
|
||||||
),
|
|
||||||
is_proc_macro: false,
|
|
||||||
},
|
|
||||||
CrateId(
|
|
||||||
9,
|
|
||||||
): CrateData {
|
|
||||||
root_file_id: FileId(
|
|
||||||
10,
|
|
||||||
),
|
|
||||||
edition: Edition2018,
|
|
||||||
version: None,
|
|
||||||
display_name: Some(
|
display_name: Some(
|
||||||
CrateDisplayName {
|
CrateDisplayName {
|
||||||
crate_name: CrateName(
|
crate_name: CrateName(
|
||||||
|
@ -1748,10 +1705,10 @@ fn rust_project_hello_world_project_model() {
|
||||||
is_proc_macro: false,
|
is_proc_macro: false,
|
||||||
},
|
},
|
||||||
CrateId(
|
CrateId(
|
||||||
10,
|
9,
|
||||||
): CrateData {
|
): CrateData {
|
||||||
root_file_id: FileId(
|
root_file_id: FileId(
|
||||||
11,
|
10,
|
||||||
),
|
),
|
||||||
edition: Edition2018,
|
edition: Edition2018,
|
||||||
version: None,
|
version: None,
|
||||||
|
@ -1782,10 +1739,10 @@ fn rust_project_hello_world_project_model() {
|
||||||
is_proc_macro: false,
|
is_proc_macro: false,
|
||||||
},
|
},
|
||||||
CrateId(
|
CrateId(
|
||||||
11,
|
10,
|
||||||
): CrateData {
|
): CrateData {
|
||||||
root_file_id: FileId(
|
root_file_id: FileId(
|
||||||
12,
|
11,
|
||||||
),
|
),
|
||||||
edition: Edition2018,
|
edition: Edition2018,
|
||||||
version: None,
|
version: None,
|
||||||
|
@ -1836,7 +1793,7 @@ fn rust_project_hello_world_project_model() {
|
||||||
},
|
},
|
||||||
Dependency {
|
Dependency {
|
||||||
crate_id: CrateId(
|
crate_id: CrateId(
|
||||||
9,
|
8,
|
||||||
),
|
),
|
||||||
name: CrateName(
|
name: CrateName(
|
||||||
"test",
|
"test",
|
||||||
|
|
|
@ -377,6 +377,21 @@ impl ProjectWorkspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_sysroot_proc_macro_srv(&self) -> Option<AbsPathBuf> {
|
||||||
|
match self {
|
||||||
|
ProjectWorkspace::Cargo { sysroot: Some(sysroot), .. }
|
||||||
|
| ProjectWorkspace::Json { sysroot: Some(sysroot), .. } => {
|
||||||
|
let standalone_server_name =
|
||||||
|
format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
|
||||||
|
["libexec", "lib"]
|
||||||
|
.into_iter()
|
||||||
|
.map(|segment| sysroot.root().join(segment).join(&standalone_server_name))
|
||||||
|
.find(|server_path| std::fs::metadata(&server_path).is_ok())
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the roots for the current `ProjectWorkspace`
|
/// Returns the roots for the current `ProjectWorkspace`
|
||||||
/// The return type contains the path and whether or not
|
/// The return type contains the path and whether or not
|
||||||
/// the root is a member of the current workspace
|
/// the root is a member of the current workspace
|
||||||
|
@ -509,14 +524,14 @@ impl ProjectWorkspace {
|
||||||
build_scripts,
|
build_scripts,
|
||||||
toolchain: _,
|
toolchain: _,
|
||||||
} => cargo_to_crate_graph(
|
} => cargo_to_crate_graph(
|
||||||
rustc_cfg.clone(),
|
|
||||||
cfg_overrides,
|
|
||||||
load_proc_macro,
|
load_proc_macro,
|
||||||
load,
|
load,
|
||||||
cargo,
|
|
||||||
build_scripts,
|
|
||||||
sysroot.as_ref(),
|
|
||||||
rustc,
|
rustc,
|
||||||
|
cargo,
|
||||||
|
sysroot.as_ref(),
|
||||||
|
rustc_cfg.clone(),
|
||||||
|
cfg_overrides,
|
||||||
|
build_scripts,
|
||||||
),
|
),
|
||||||
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
|
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
|
||||||
detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
|
detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
|
||||||
|
@ -602,7 +617,7 @@ fn project_json_to_crate_graph(
|
||||||
for (from, krate) in project.crates() {
|
for (from, krate) in project.crates() {
|
||||||
if let Some(&from) = crates.get(&from) {
|
if let Some(&from) = crates.get(&from) {
|
||||||
if let Some((public_deps, libproc_macro)) = &sysroot_deps {
|
if let Some((public_deps, libproc_macro)) = &sysroot_deps {
|
||||||
public_deps.add(from, &mut crate_graph);
|
public_deps.add_to_crate_graph(&mut crate_graph, from);
|
||||||
if krate.is_proc_macro {
|
if krate.is_proc_macro {
|
||||||
if let Some(proc_macro) = libproc_macro {
|
if let Some(proc_macro) = libproc_macro {
|
||||||
add_dep(
|
add_dep(
|
||||||
|
@ -626,14 +641,14 @@ fn project_json_to_crate_graph(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cargo_to_crate_graph(
|
fn cargo_to_crate_graph(
|
||||||
rustc_cfg: Vec<CfgFlag>,
|
|
||||||
override_cfg: &CfgOverrides,
|
|
||||||
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
|
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
|
||||||
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
|
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
|
||||||
cargo: &CargoWorkspace,
|
|
||||||
build_scripts: &WorkspaceBuildScripts,
|
|
||||||
sysroot: Option<&Sysroot>,
|
|
||||||
rustc: &Option<CargoWorkspace>,
|
rustc: &Option<CargoWorkspace>,
|
||||||
|
cargo: &CargoWorkspace,
|
||||||
|
sysroot: Option<&Sysroot>,
|
||||||
|
rustc_cfg: Vec<CfgFlag>,
|
||||||
|
override_cfg: &CfgOverrides,
|
||||||
|
build_scripts: &WorkspaceBuildScripts,
|
||||||
) -> CrateGraph {
|
) -> CrateGraph {
|
||||||
let _p = profile::span("cargo_to_crate_graph");
|
let _p = profile::span("cargo_to_crate_graph");
|
||||||
let mut crate_graph = CrateGraph::default();
|
let mut crate_graph = CrateGraph::default();
|
||||||
|
@ -642,13 +657,15 @@ fn cargo_to_crate_graph(
|
||||||
None => (SysrootPublicDeps::default(), None),
|
None => (SysrootPublicDeps::default(), None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut cfg_options = CfgOptions::default();
|
let cfg_options = {
|
||||||
cfg_options.extend(rustc_cfg);
|
let mut cfg_options = CfgOptions::default();
|
||||||
|
cfg_options.extend(rustc_cfg);
|
||||||
|
cfg_options.insert_atom("debug_assertions".into());
|
||||||
|
cfg_options
|
||||||
|
};
|
||||||
|
|
||||||
let mut pkg_to_lib_crate = FxHashMap::default();
|
let mut pkg_to_lib_crate = FxHashMap::default();
|
||||||
|
|
||||||
cfg_options.insert_atom("debug_assertions".into());
|
|
||||||
|
|
||||||
let mut pkg_crates = FxHashMap::default();
|
let mut pkg_crates = FxHashMap::default();
|
||||||
// Does any crate signal to rust-analyzer that they need the rustc_private crates?
|
// Does any crate signal to rust-analyzer that they need the rustc_private crates?
|
||||||
let mut has_private = false;
|
let mut has_private = false;
|
||||||
|
@ -723,7 +740,7 @@ fn cargo_to_crate_graph(
|
||||||
// Set deps to the core, std and to the lib target of the current package
|
// Set deps to the core, std and to the lib target of the current package
|
||||||
for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
|
for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
|
||||||
// Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
|
// Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
|
||||||
public_deps.add(from, &mut crate_graph);
|
public_deps.add_to_crate_graph(&mut crate_graph, from);
|
||||||
|
|
||||||
if let Some((to, name)) = lib_tgt.clone() {
|
if let Some((to, name)) = lib_tgt.clone() {
|
||||||
if to != from && kind != TargetKind::BuildScript {
|
if to != from && kind != TargetKind::BuildScript {
|
||||||
|
@ -767,15 +784,16 @@ fn cargo_to_crate_graph(
|
||||||
if let Some(rustc_workspace) = rustc {
|
if let Some(rustc_workspace) = rustc {
|
||||||
handle_rustc_crates(
|
handle_rustc_crates(
|
||||||
&mut crate_graph,
|
&mut crate_graph,
|
||||||
rustc_workspace,
|
&mut pkg_to_lib_crate,
|
||||||
load,
|
load,
|
||||||
|
load_proc_macro,
|
||||||
|
rustc_workspace,
|
||||||
|
cargo,
|
||||||
|
&public_deps,
|
||||||
|
libproc_macro,
|
||||||
|
&pkg_crates,
|
||||||
&cfg_options,
|
&cfg_options,
|
||||||
override_cfg,
|
override_cfg,
|
||||||
load_proc_macro,
|
|
||||||
&mut pkg_to_lib_crate,
|
|
||||||
&public_deps,
|
|
||||||
cargo,
|
|
||||||
&pkg_crates,
|
|
||||||
build_scripts,
|
build_scripts,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -825,28 +843,29 @@ fn detached_files_to_crate_graph(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
public_deps.add(detached_file_crate, &mut crate_graph);
|
public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
|
||||||
}
|
}
|
||||||
crate_graph
|
crate_graph
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_rustc_crates(
|
fn handle_rustc_crates(
|
||||||
crate_graph: &mut CrateGraph,
|
crate_graph: &mut CrateGraph,
|
||||||
rustc_workspace: &CargoWorkspace,
|
pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
|
||||||
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
|
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
|
||||||
|
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
|
||||||
|
rustc_workspace: &CargoWorkspace,
|
||||||
|
cargo: &CargoWorkspace,
|
||||||
|
public_deps: &SysrootPublicDeps,
|
||||||
|
libproc_macro: Option<CrateId>,
|
||||||
|
pkg_crates: &FxHashMap<Package, Vec<(CrateId, TargetKind)>>,
|
||||||
cfg_options: &CfgOptions,
|
cfg_options: &CfgOptions,
|
||||||
override_cfg: &CfgOverrides,
|
override_cfg: &CfgOverrides,
|
||||||
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
|
|
||||||
pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
|
|
||||||
public_deps: &SysrootPublicDeps,
|
|
||||||
cargo: &CargoWorkspace,
|
|
||||||
pkg_crates: &FxHashMap<Package, Vec<(CrateId, TargetKind)>>,
|
|
||||||
build_scripts: &WorkspaceBuildScripts,
|
build_scripts: &WorkspaceBuildScripts,
|
||||||
) {
|
) {
|
||||||
let mut rustc_pkg_crates = FxHashMap::default();
|
let mut rustc_pkg_crates = FxHashMap::default();
|
||||||
// The root package of the rustc-dev component is rustc_driver, so we match that
|
// The root package of the rustc-dev component is rustc_driver, so we match that
|
||||||
let root_pkg =
|
let root_pkg =
|
||||||
rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
|
rustc_workspace.packages().find(|&package| rustc_workspace[package].name == "rustc_driver");
|
||||||
// The rustc workspace might be incomplete (such as if rustc-dev is not
|
// The rustc workspace might be incomplete (such as if rustc-dev is not
|
||||||
// installed for the current toolchain) and `rustc_source` is set to discover.
|
// installed for the current toolchain) and `rustc_source` is set to discover.
|
||||||
if let Some(root_pkg) = root_pkg {
|
if let Some(root_pkg) = root_pkg {
|
||||||
|
@ -901,7 +920,16 @@ fn handle_rustc_crates(
|
||||||
);
|
);
|
||||||
pkg_to_lib_crate.insert(pkg, crate_id);
|
pkg_to_lib_crate.insert(pkg, crate_id);
|
||||||
// Add dependencies on core / std / alloc for this crate
|
// Add dependencies on core / std / alloc for this crate
|
||||||
public_deps.add(crate_id, crate_graph);
|
public_deps.add_to_crate_graph(crate_graph, crate_id);
|
||||||
|
if let Some(proc_macro) = libproc_macro {
|
||||||
|
add_dep_with_prelude(
|
||||||
|
crate_graph,
|
||||||
|
crate_id,
|
||||||
|
CrateName::new("proc_macro").unwrap(),
|
||||||
|
proc_macro,
|
||||||
|
rustc_workspace[tgt].is_proc_macro,
|
||||||
|
);
|
||||||
|
}
|
||||||
rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
|
rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1009,7 +1037,7 @@ struct SysrootPublicDeps {
|
||||||
|
|
||||||
impl SysrootPublicDeps {
|
impl SysrootPublicDeps {
|
||||||
/// Makes `from` depend on the public sysroot crates.
|
/// Makes `from` depend on the public sysroot crates.
|
||||||
fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
|
fn add_to_crate_graph(&self, crate_graph: &mut CrateGraph, from: CrateId) {
|
||||||
for (name, krate, prelude) in &self.deps {
|
for (name, krate, prelude) in &self.deps {
|
||||||
add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
|
add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ documentation = "https://rust-analyzer.github.io/manual.html"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
autobins = false
|
autobins = false
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use cfg::{CfgAtom, CfgExpr};
|
use cfg::{CfgAtom, CfgExpr};
|
||||||
use ide::{FileId, RunnableKind, TestId};
|
use ide::{Cancellable, FileId, RunnableKind, TestId};
|
||||||
use project_model::{self, CargoFeatures, ManifestPath, TargetKind};
|
use project_model::{self, CargoFeatures, ManifestPath, TargetKind};
|
||||||
use vfs::AbsPathBuf;
|
use vfs::AbsPathBuf;
|
||||||
|
|
||||||
use crate::{global_state::GlobalStateSnapshot, Result};
|
use crate::global_state::GlobalStateSnapshot;
|
||||||
|
|
||||||
/// Abstract representation of Cargo target.
|
/// Abstract representation of Cargo target.
|
||||||
///
|
///
|
||||||
|
@ -29,7 +29,7 @@ impl CargoTargetSpec {
|
||||||
spec: Option<CargoTargetSpec>,
|
spec: Option<CargoTargetSpec>,
|
||||||
kind: &RunnableKind,
|
kind: &RunnableKind,
|
||||||
cfg: &Option<CfgExpr>,
|
cfg: &Option<CfgExpr>,
|
||||||
) -> Result<(Vec<String>, Vec<String>)> {
|
) -> (Vec<String>, Vec<String>) {
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
let mut extra_args = Vec::new();
|
let mut extra_args = Vec::new();
|
||||||
|
|
||||||
|
@ -111,13 +111,13 @@ impl CargoTargetSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((args, extra_args))
|
(args, extra_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn for_file(
|
pub(crate) fn for_file(
|
||||||
global_state_snapshot: &GlobalStateSnapshot,
|
global_state_snapshot: &GlobalStateSnapshot,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
) -> Result<Option<CargoTargetSpec>> {
|
) -> Cancellable<Option<CargoTargetSpec>> {
|
||||||
let crate_id = match &*global_state_snapshot.analysis.crates_for(file_id)? {
|
let crate_id = match &*global_state_snapshot.analysis.crates_for(file_id)? {
|
||||||
&[crate_id, ..] => crate_id,
|
&[crate_id, ..] => crate_id,
|
||||||
_ => return Ok(None),
|
_ => return Ok(None),
|
||||||
|
|
|
@ -60,24 +60,12 @@ pub fn load_workspace(
|
||||||
};
|
};
|
||||||
|
|
||||||
let proc_macro_client = if load_config.with_proc_macro {
|
let proc_macro_client = if load_config.with_proc_macro {
|
||||||
let mut path = AbsPathBuf::assert(std::env::current_exe()?);
|
let (server_path, args): (_, &[_]) = match ws.find_sysroot_proc_macro_srv() {
|
||||||
let mut args = vec!["proc-macro"];
|
Some(server_path) => (server_path, &[]),
|
||||||
|
None => (AbsPathBuf::assert(std::env::current_exe()?), &["proc-macro"]),
|
||||||
|
};
|
||||||
|
|
||||||
if let ProjectWorkspace::Cargo { sysroot, .. } | ProjectWorkspace::Json { sysroot, .. } =
|
ProcMacroServer::spawn(server_path, args).map_err(|e| e.to_string())
|
||||||
&ws
|
|
||||||
{
|
|
||||||
if let Some(sysroot) = sysroot.as_ref() {
|
|
||||||
let standalone_server_name =
|
|
||||||
format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
|
|
||||||
let server_path = sysroot.root().join("libexec").join(&standalone_server_name);
|
|
||||||
if std::fs::metadata(&server_path).is_ok() {
|
|
||||||
path = server_path;
|
|
||||||
args = vec![];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|e| e.to_string())
|
|
||||||
} else {
|
} else {
|
||||||
Err("proc macro server disabled".to_owned())
|
Err("proc macro server disabled".to_owned())
|
||||||
};
|
};
|
||||||
|
|
|
@ -47,30 +47,27 @@ impl flags::Scip {
|
||||||
|
|
||||||
let si = StaticIndex::compute(&analysis);
|
let si = StaticIndex::compute(&analysis);
|
||||||
|
|
||||||
let mut index = scip_types::Index {
|
let metadata = scip_types::Metadata {
|
||||||
metadata: Some(scip_types::Metadata {
|
version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
|
||||||
version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
|
tool_info: Some(scip_types::ToolInfo {
|
||||||
tool_info: Some(scip_types::ToolInfo {
|
name: "rust-analyzer".to_owned(),
|
||||||
name: "rust-analyzer".to_owned(),
|
version: "0.1".to_owned(),
|
||||||
version: "0.1".to_owned(),
|
arguments: vec![],
|
||||||
arguments: vec![],
|
special_fields: Default::default(),
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.into(),
|
|
||||||
project_root: format!(
|
|
||||||
"file://{}",
|
|
||||||
path.normalize()
|
|
||||||
.as_os_str()
|
|
||||||
.to_str()
|
|
||||||
.ok_or(anyhow::anyhow!("Unable to normalize project_root path"))?
|
|
||||||
.to_string()
|
|
||||||
),
|
|
||||||
text_document_encoding: scip_types::TextEncoding::UTF8.into(),
|
|
||||||
..Default::default()
|
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
..Default::default()
|
project_root: format!(
|
||||||
|
"file://{}",
|
||||||
|
path.normalize()
|
||||||
|
.as_os_str()
|
||||||
|
.to_str()
|
||||||
|
.ok_or(anyhow::anyhow!("Unable to normalize project_root path"))?
|
||||||
|
.to_string()
|
||||||
|
),
|
||||||
|
text_document_encoding: scip_types::TextEncoding::UTF8.into(),
|
||||||
|
special_fields: Default::default(),
|
||||||
};
|
};
|
||||||
|
let mut documents = Vec::new();
|
||||||
|
|
||||||
let mut symbols_emitted: HashSet<TokenId> = HashSet::default();
|
let mut symbols_emitted: HashSet<TokenId> = HashSet::default();
|
||||||
let mut tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();
|
let mut tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();
|
||||||
|
@ -95,18 +92,14 @@ impl flags::Scip {
|
||||||
endings: LineEndings::Unix,
|
endings: LineEndings::Unix,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut doc = scip_types::Document {
|
let mut occurrences = Vec::new();
|
||||||
relative_path,
|
let mut symbols = Vec::new();
|
||||||
language: "rust".to_string(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
tokens.into_iter().for_each(|(range, id)| {
|
tokens.into_iter().for_each(|(text_range, id)| {
|
||||||
let token = si.tokens.get(id).unwrap();
|
let token = si.tokens.get(id).unwrap();
|
||||||
|
|
||||||
let mut occurrence = scip_types::Occurrence::default();
|
let range = text_range_to_scip_range(&line_index, text_range);
|
||||||
occurrence.range = text_range_to_scip_range(&line_index, range);
|
let symbol = tokens_to_symbol
|
||||||
occurrence.symbol = tokens_to_symbol
|
|
||||||
.entry(id)
|
.entry(id)
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
let symbol = token_to_symbol(&token).unwrap_or_else(&mut new_local_symbol);
|
let symbol = token_to_symbol(&token).unwrap_or_else(&mut new_local_symbol);
|
||||||
|
@ -114,34 +107,62 @@ impl flags::Scip {
|
||||||
})
|
})
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
|
let mut symbol_roles = Default::default();
|
||||||
|
|
||||||
if let Some(def) = token.definition {
|
if let Some(def) = token.definition {
|
||||||
if def.range == range {
|
if def.range == text_range {
|
||||||
occurrence.symbol_roles |= scip_types::SymbolRole::Definition as i32;
|
symbol_roles |= scip_types::SymbolRole::Definition as i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
if symbols_emitted.insert(id) {
|
if symbols_emitted.insert(id) {
|
||||||
let mut symbol_info = scip_types::SymbolInformation::default();
|
let documentation = token
|
||||||
symbol_info.symbol = occurrence.symbol.clone();
|
.hover
|
||||||
if let Some(hover) = &token.hover {
|
.as_ref()
|
||||||
if !hover.markup.as_str().is_empty() {
|
.map(|hover| hover.markup.as_str())
|
||||||
symbol_info.documentation = vec![hover.markup.as_str().to_string()];
|
.filter(|it| !it.is_empty())
|
||||||
}
|
.map(|it| vec![it.to_owned()]);
|
||||||
}
|
let symbol_info = scip_types::SymbolInformation {
|
||||||
|
symbol: symbol.clone(),
|
||||||
|
documentation: documentation.unwrap_or_default(),
|
||||||
|
relationships: Vec::new(),
|
||||||
|
special_fields: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
doc.symbols.push(symbol_info)
|
symbols.push(symbol_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.occurrences.push(occurrence);
|
occurrences.push(scip_types::Occurrence {
|
||||||
|
range,
|
||||||
|
symbol,
|
||||||
|
symbol_roles,
|
||||||
|
override_documentation: Vec::new(),
|
||||||
|
syntax_kind: Default::default(),
|
||||||
|
diagnostics: Vec::new(),
|
||||||
|
special_fields: Default::default(),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if doc.occurrences.is_empty() {
|
if occurrences.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
index.documents.push(doc);
|
documents.push(scip_types::Document {
|
||||||
|
relative_path,
|
||||||
|
language: "rust".to_string(),
|
||||||
|
occurrences,
|
||||||
|
symbols,
|
||||||
|
special_fields: Default::default(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let index = scip_types::Index {
|
||||||
|
metadata: Some(metadata).into(),
|
||||||
|
documents,
|
||||||
|
external_symbols: Vec::new(),
|
||||||
|
special_fields: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
scip::write_message_to_file("index.scip", index)
|
scip::write_message_to_file("index.scip", index)
|
||||||
.map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?;
|
.map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?;
|
||||||
|
|
||||||
|
@ -181,7 +202,7 @@ fn new_descriptor_str(
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
disambiguator: "".to_string(),
|
disambiguator: "".to_string(),
|
||||||
suffix: suffix.into(),
|
suffix: suffix.into(),
|
||||||
..Default::default()
|
special_fields: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,11 +253,11 @@ fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
|
||||||
manager: "cargo".to_string(),
|
manager: "cargo".to_string(),
|
||||||
name: package_name,
|
name: package_name,
|
||||||
version: version.unwrap_or_else(|| ".".to_string()),
|
version: version.unwrap_or_else(|| ".".to_string()),
|
||||||
..Default::default()
|
special_fields: Default::default(),
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
descriptors,
|
descriptors,
|
||||||
..Default::default()
|
special_fields: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,9 +118,11 @@ config_data! {
|
||||||
/// This option does not take effect until rust-analyzer is restarted.
|
/// This option does not take effect until rust-analyzer is restarted.
|
||||||
cargo_sysroot: Option<String> = "\"discover\"",
|
cargo_sysroot: Option<String> = "\"discover\"",
|
||||||
/// Compilation target override (target triple).
|
/// Compilation target override (target triple).
|
||||||
|
// FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
|
||||||
|
// than `checkOnSave_target`
|
||||||
cargo_target: Option<String> = "null",
|
cargo_target: Option<String> = "null",
|
||||||
/// Unsets `#[cfg(test)]` for the specified crates.
|
/// Unsets `#[cfg(test)]` for the specified crates.
|
||||||
cargo_unsetTest: Vec<String> = "[\"core\"]",
|
cargo_unsetTest: Vec<String> = "[\"core\"]",
|
||||||
|
|
||||||
/// Check all targets and tests (`--all-targets`).
|
/// Check all targets and tests (`--all-targets`).
|
||||||
checkOnSave_allTargets: bool = "true",
|
checkOnSave_allTargets: bool = "true",
|
||||||
|
@ -157,7 +159,7 @@ config_data! {
|
||||||
checkOnSave_noDefaultFeatures: Option<bool> = "null",
|
checkOnSave_noDefaultFeatures: Option<bool> = "null",
|
||||||
/// Override the command rust-analyzer uses instead of `cargo check` for
|
/// Override the command rust-analyzer uses instead of `cargo check` for
|
||||||
/// diagnostics on save. The command is required to output json and
|
/// diagnostics on save. The command is required to output json and
|
||||||
/// should therefor include `--message-format=json` or a similar option.
|
/// should therefore include `--message-format=json` or a similar option.
|
||||||
///
|
///
|
||||||
/// If you're changing this because you're using some tool wrapping
|
/// If you're changing this because you're using some tool wrapping
|
||||||
/// Cargo, you might also want to change
|
/// Cargo, you might also want to change
|
||||||
|
@ -174,9 +176,13 @@ config_data! {
|
||||||
/// ```
|
/// ```
|
||||||
/// .
|
/// .
|
||||||
checkOnSave_overrideCommand: Option<Vec<String>> = "null",
|
checkOnSave_overrideCommand: Option<Vec<String>> = "null",
|
||||||
/// Check for a specific target. Defaults to
|
/// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
|
||||||
/// `#rust-analyzer.cargo.target#`.
|
///
|
||||||
checkOnSave_target: Option<String> = "null",
|
/// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g.
|
||||||
|
/// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`.
|
||||||
|
///
|
||||||
|
/// Aliased as `"checkOnSave.targets"`.
|
||||||
|
checkOnSave_target | checkOnSave_targets: CheckOnSaveTargets = "[]",
|
||||||
|
|
||||||
/// Toggles the additional completions that automatically add imports when completed.
|
/// Toggles the additional completions that automatically add imports when completed.
|
||||||
/// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
|
/// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
|
||||||
|
@ -261,6 +267,7 @@ config_data! {
|
||||||
files_excludeDirs: Vec<PathBuf> = "[]",
|
files_excludeDirs: Vec<PathBuf> = "[]",
|
||||||
/// Controls file watching implementation.
|
/// Controls file watching implementation.
|
||||||
files_watcher: FilesWatcherDef = "\"client\"",
|
files_watcher: FilesWatcherDef = "\"client\"",
|
||||||
|
|
||||||
/// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
|
/// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
|
||||||
highlightRelated_breakPoints_enable: bool = "true",
|
highlightRelated_breakPoints_enable: bool = "true",
|
||||||
/// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
|
/// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
|
||||||
|
@ -320,6 +327,8 @@ config_data! {
|
||||||
inlayHints_closingBraceHints_minLines: usize = "25",
|
inlayHints_closingBraceHints_minLines: usize = "25",
|
||||||
/// Whether to show inlay type hints for return types of closures.
|
/// Whether to show inlay type hints for return types of closures.
|
||||||
inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = "\"never\"",
|
inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = "\"never\"",
|
||||||
|
/// Whether to show inlay hints for type adjustments.
|
||||||
|
inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"",
|
||||||
/// Whether to show inlay type hints for elided lifetimes in function signatures.
|
/// Whether to show inlay type hints for elided lifetimes in function signatures.
|
||||||
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
|
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
|
||||||
/// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
|
/// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
|
||||||
|
@ -329,7 +338,8 @@ config_data! {
|
||||||
/// Whether to show function parameter name inlay hints at the call
|
/// Whether to show function parameter name inlay hints at the call
|
||||||
/// site.
|
/// site.
|
||||||
inlayHints_parameterHints_enable: bool = "true",
|
inlayHints_parameterHints_enable: bool = "true",
|
||||||
/// Whether to show inlay type hints for compiler inserted reborrows.
|
/// Whether to show inlay hints for compiler inserted reborrows.
|
||||||
|
/// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.
|
||||||
inlayHints_reborrowHints_enable: ReborrowHintsDef = "\"never\"",
|
inlayHints_reborrowHints_enable: ReborrowHintsDef = "\"never\"",
|
||||||
/// Whether to render leading colons for type hints, and trailing colons for parameter hints.
|
/// Whether to render leading colons for type hints, and trailing colons for parameter hints.
|
||||||
inlayHints_renderColons: bool = "true",
|
inlayHints_renderColons: bool = "true",
|
||||||
|
@ -1143,11 +1153,10 @@ impl Config {
|
||||||
}
|
}
|
||||||
Some(_) | None => FlycheckConfig::CargoCommand {
|
Some(_) | None => FlycheckConfig::CargoCommand {
|
||||||
command: self.data.checkOnSave_command.clone(),
|
command: self.data.checkOnSave_command.clone(),
|
||||||
target_triple: self
|
target_triples: match &self.data.checkOnSave_target.0[..] {
|
||||||
.data
|
[] => self.data.cargo_target.clone().into_iter().collect(),
|
||||||
.checkOnSave_target
|
targets => targets.into(),
|
||||||
.clone()
|
},
|
||||||
.or_else(|| self.data.cargo_target.clone()),
|
|
||||||
all_targets: self.data.checkOnSave_allTargets,
|
all_targets: self.data.checkOnSave_allTargets,
|
||||||
no_default_features: self
|
no_default_features: self
|
||||||
.data
|
.data
|
||||||
|
@ -1200,10 +1209,15 @@ impl Config {
|
||||||
hide_closure_initialization_hints: self
|
hide_closure_initialization_hints: self
|
||||||
.data
|
.data
|
||||||
.inlayHints_typeHints_hideClosureInitialization,
|
.inlayHints_typeHints_hideClosureInitialization,
|
||||||
reborrow_hints: match self.data.inlayHints_reborrowHints_enable {
|
adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable {
|
||||||
ReborrowHintsDef::Always => ide::ReborrowHints::Always,
|
AdjustmentHintsDef::Always => ide::AdjustmentHints::Always,
|
||||||
ReborrowHintsDef::Never => ide::ReborrowHints::Never,
|
AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable {
|
||||||
ReborrowHintsDef::Mutable => ide::ReborrowHints::MutableOnly,
|
ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => {
|
||||||
|
ide::AdjustmentHints::ReborrowOnly
|
||||||
|
}
|
||||||
|
ReborrowHintsDef::Never => ide::AdjustmentHints::Never,
|
||||||
|
},
|
||||||
|
AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly,
|
||||||
},
|
},
|
||||||
binding_mode_hints: self.data.inlayHints_bindingModeHints_enable,
|
binding_mode_hints: self.data.inlayHints_bindingModeHints_enable,
|
||||||
param_names_for_lifetime_elision_hints: self
|
param_names_for_lifetime_elision_hints: self
|
||||||
|
@ -1538,6 +1552,7 @@ mod de_unit_v {
|
||||||
named_unit_variant!(all);
|
named_unit_variant!(all);
|
||||||
named_unit_variant!(skip_trivial);
|
named_unit_variant!(skip_trivial);
|
||||||
named_unit_variant!(mutable);
|
named_unit_variant!(mutable);
|
||||||
|
named_unit_variant!(reborrow);
|
||||||
named_unit_variant!(with_block);
|
named_unit_variant!(with_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1647,6 +1662,9 @@ enum InvocationStrategy {
|
||||||
PerWorkspace,
|
PerWorkspace,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
struct CheckOnSaveTargets(#[serde(deserialize_with = "single_or_array")] Vec<String>);
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
enum InvocationLocation {
|
enum InvocationLocation {
|
||||||
|
@ -1687,6 +1705,17 @@ enum ReborrowHintsDef {
|
||||||
Mutable,
|
Mutable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum AdjustmentHintsDef {
|
||||||
|
#[serde(deserialize_with = "true_or_always")]
|
||||||
|
Always,
|
||||||
|
#[serde(deserialize_with = "false_or_never")]
|
||||||
|
Never,
|
||||||
|
#[serde(deserialize_with = "de_unit_v::reborrow")]
|
||||||
|
Reborrow,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
enum FilesWatcherDef {
|
enum FilesWatcherDef {
|
||||||
|
@ -1996,6 +2025,19 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
|
||||||
"Only show mutable reborrow hints."
|
"Only show mutable reborrow hints."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"AdjustmentHintsDef" => set! {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"always",
|
||||||
|
"never",
|
||||||
|
"reborrow"
|
||||||
|
],
|
||||||
|
"enumDescriptions": [
|
||||||
|
"Always show all adjustment hints.",
|
||||||
|
"Never show adjustment hints.",
|
||||||
|
"Only show auto borrow and dereference adjustment hints."
|
||||||
|
]
|
||||||
|
},
|
||||||
"CargoFeaturesDef" => set! {
|
"CargoFeaturesDef" => set! {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
|
@ -2084,6 +2126,17 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
|
||||||
"The command will be executed in the project root."
|
"The command will be executed in the project root."
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
"CheckOnSaveTargets" => set! {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
_ => panic!("missing entry for {}: {}", ty, default),
|
_ => panic!("missing entry for {}: {}", ty, default),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -359,14 +359,15 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|primary_span| {
|
.flat_map(|primary_span| {
|
||||||
let primary_location = primary_location(config, workspace_root, primary_span, snap);
|
let primary_location = primary_location(config, workspace_root, primary_span, snap);
|
||||||
|
let message = {
|
||||||
let mut message = message.clone();
|
let mut message = message.clone();
|
||||||
if needs_primary_span_label {
|
if needs_primary_span_label {
|
||||||
if let Some(primary_span_label) = &primary_span.label {
|
if let Some(primary_span_label) = &primary_span.label {
|
||||||
format_to!(message, "\n{}", primary_span_label);
|
format_to!(message, "\n{}", primary_span_label);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
message
|
||||||
|
};
|
||||||
// Each primary diagnostic span may result in multiple LSP diagnostics.
|
// Each primary diagnostic span may result in multiple LSP diagnostics.
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
|
|
||||||
|
@ -417,7 +418,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
message: message.clone(),
|
message: message.clone(),
|
||||||
related_information: Some(information_for_additional_diagnostic),
|
related_information: Some(information_for_additional_diagnostic),
|
||||||
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
||||||
data: None,
|
data: Some(serde_json::json!({ "rendered": rd.rendered })),
|
||||||
};
|
};
|
||||||
diagnostics.push(MappedRustDiagnostic {
|
diagnostics.push(MappedRustDiagnostic {
|
||||||
url: secondary_location.uri,
|
url: secondary_location.uri,
|
||||||
|
@ -449,7 +450,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
||||||
data: None,
|
data: Some(serde_json::json!({ "rendered": rd.rendered })),
|
||||||
},
|
},
|
||||||
fix: None,
|
fix: None,
|
||||||
});
|
});
|
||||||
|
@ -534,7 +535,8 @@ mod tests {
|
||||||
Config::new(workspace_root.to_path_buf(), ClientCapabilities::default()),
|
Config::new(workspace_root.to_path_buf(), ClientCapabilities::default()),
|
||||||
);
|
);
|
||||||
let snap = state.snapshot();
|
let snap = state.snapshot();
|
||||||
let actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap);
|
let mut actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap);
|
||||||
|
actual.iter_mut().for_each(|diag| diag.diagnostic.data = None);
|
||||||
expect.assert_debug_eq(&actual)
|
expect.assert_debug_eq(&actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,10 @@ pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> R
|
||||||
pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Result<TextRange> {
|
pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Result<TextRange> {
|
||||||
let start = offset(line_index, range.start)?;
|
let start = offset(line_index, range.start)?;
|
||||||
let end = offset(line_index, range.end)?;
|
let end = offset(line_index, range.end)?;
|
||||||
let text_range = TextRange::new(start, end);
|
match end < start {
|
||||||
Ok(text_range)
|
true => Err(format_err!("Invalid Range").into()),
|
||||||
|
false => Ok(TextRange::new(start, end)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> {
|
pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> {
|
||||||
|
|
|
@ -100,7 +100,7 @@ pub(crate) struct GlobalState {
|
||||||
/// the user just adds comments or whitespace to Cargo.toml, we do not want
|
/// the user just adds comments or whitespace to Cargo.toml, we do not want
|
||||||
/// to invalidate any salsa caches.
|
/// to invalidate any salsa caches.
|
||||||
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
|
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
|
||||||
pub(crate) fetch_workspaces_queue: OpQueue<Vec<anyhow::Result<ProjectWorkspace>>>,
|
pub(crate) fetch_workspaces_queue: OpQueue<Option<Vec<anyhow::Result<ProjectWorkspace>>>>,
|
||||||
pub(crate) fetch_build_data_queue:
|
pub(crate) fetch_build_data_queue:
|
||||||
OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
|
OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ use std::{
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use ide::{
|
use ide::{
|
||||||
AnnotationConfig, AssistKind, AssistResolveStrategy, FileId, FilePosition, FileRange,
|
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FileId, FilePosition,
|
||||||
HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
|
FileRange, HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable,
|
||||||
SingleResolve, SourceChange, TextEdit,
|
RunnableKind, SingleResolve, SourceChange, TextEdit,
|
||||||
};
|
};
|
||||||
use ide_db::SymbolKind;
|
use ide_db::SymbolKind;
|
||||||
use lsp_server::ErrorCode;
|
use lsp_server::ErrorCode;
|
||||||
|
@ -556,7 +556,7 @@ pub(crate) fn handle_will_rename_files(
|
||||||
if source_change.source_file_edits.is_empty() {
|
if source_change.source_file_edits.is_empty() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
to_proto::workspace_edit(&snap, source_change).map(Some)
|
Ok(Some(to_proto::workspace_edit(&snap, source_change)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1313,7 +1313,7 @@ pub(crate) fn handle_ssr(
|
||||||
position,
|
position,
|
||||||
selections,
|
selections,
|
||||||
)??;
|
)??;
|
||||||
to_proto::workspace_edit(&snap, source_change)
|
to_proto::workspace_edit(&snap, source_change).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn publish_diagnostics(
|
pub(crate) fn publish_diagnostics(
|
||||||
|
@ -1354,13 +1354,12 @@ pub(crate) fn handle_inlay_hints(
|
||||||
) -> Result<Option<Vec<InlayHint>>> {
|
) -> Result<Option<Vec<InlayHint>>> {
|
||||||
let _p = profile::span("handle_inlay_hints");
|
let _p = profile::span("handle_inlay_hints");
|
||||||
let document_uri = ¶ms.text_document.uri;
|
let document_uri = ¶ms.text_document.uri;
|
||||||
let file_id = from_proto::file_id(&snap, document_uri)?;
|
let FileRange { file_id, range } = from_proto::file_range(
|
||||||
let line_index = snap.file_line_index(file_id)?;
|
|
||||||
let range = from_proto::file_range(
|
|
||||||
&snap,
|
&snap,
|
||||||
TextDocumentIdentifier::new(document_uri.to_owned()),
|
TextDocumentIdentifier::new(document_uri.to_owned()),
|
||||||
params.range,
|
params.range,
|
||||||
)?;
|
)?;
|
||||||
|
let line_index = snap.file_line_index(file_id)?;
|
||||||
let inlay_hints_config = snap.config.inlay_hints();
|
let inlay_hints_config = snap.config.inlay_hints();
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
snap.analysis
|
snap.analysis
|
||||||
|
@ -1369,7 +1368,7 @@ pub(crate) fn handle_inlay_hints(
|
||||||
.map(|it| {
|
.map(|it| {
|
||||||
to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it)
|
to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?,
|
.collect::<Cancellable<Vec<_>>>()?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1426,7 +1425,7 @@ pub(crate) fn handle_call_hierarchy_prepare(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|it| it.kind == Some(SymbolKind::Function))
|
.filter(|it| it.kind == Some(SymbolKind::Function))
|
||||||
.map(|it| to_proto::call_hierarchy_item(&snap, it))
|
.map(|it| to_proto::call_hierarchy_item(&snap, it))
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Cancellable<Vec<_>>>()?;
|
||||||
|
|
||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,6 @@ pub(crate) enum LineEndings {
|
||||||
impl LineEndings {
|
impl LineEndings {
|
||||||
/// Replaces `\r\n` with `\n` in-place in `src`.
|
/// Replaces `\r\n` with `\n` in-place in `src`.
|
||||||
pub(crate) fn normalize(src: String) -> (String, LineEndings) {
|
pub(crate) fn normalize(src: String) -> (String, LineEndings) {
|
||||||
if !src.as_bytes().contains(&b'\r') {
|
|
||||||
return (src, LineEndings::Unix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We replace `\r\n` with `\n` in-place, which doesn't break utf-8 encoding.
|
// We replace `\r\n` with `\n` in-place, which doesn't break utf-8 encoding.
|
||||||
// While we *can* call `as_mut_vec` and do surgery on the live string
|
// While we *can* call `as_mut_vec` and do surgery on the live string
|
||||||
// directly, let's rather steal the contents of `src`. This makes the code
|
// directly, let's rather steal the contents of `src`. This makes the code
|
||||||
|
@ -39,10 +35,19 @@ impl LineEndings {
|
||||||
let mut buf = src.into_bytes();
|
let mut buf = src.into_bytes();
|
||||||
let mut gap_len = 0;
|
let mut gap_len = 0;
|
||||||
let mut tail = buf.as_mut_slice();
|
let mut tail = buf.as_mut_slice();
|
||||||
|
let mut crlf_seen = false;
|
||||||
|
|
||||||
|
let find_crlf = |src: &[u8]| src.windows(2).position(|it| it == b"\r\n");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let idx = match find_crlf(&tail[gap_len..]) {
|
let idx = match find_crlf(&tail[gap_len..]) {
|
||||||
None => tail.len(),
|
None if crlf_seen => tail.len(),
|
||||||
Some(idx) => idx + gap_len,
|
// SAFETY: buf is unchanged and therefore still contains utf8 data
|
||||||
|
None => return (unsafe { String::from_utf8_unchecked(buf) }, LineEndings::Unix),
|
||||||
|
Some(idx) => {
|
||||||
|
crlf_seen = true;
|
||||||
|
idx + gap_len
|
||||||
|
}
|
||||||
};
|
};
|
||||||
tail.copy_within(gap_len..idx, 0);
|
tail.copy_within(gap_len..idx, 0);
|
||||||
tail = &mut tail[idx - gap_len..];
|
tail = &mut tail[idx - gap_len..];
|
||||||
|
@ -54,15 +59,48 @@ impl LineEndings {
|
||||||
|
|
||||||
// Account for removed `\r`.
|
// Account for removed `\r`.
|
||||||
// After `set_len`, `buf` is guaranteed to contain utf-8 again.
|
// After `set_len`, `buf` is guaranteed to contain utf-8 again.
|
||||||
let new_len = buf.len() - gap_len;
|
|
||||||
let src = unsafe {
|
let src = unsafe {
|
||||||
|
let new_len = buf.len() - gap_len;
|
||||||
buf.set_len(new_len);
|
buf.set_len(new_len);
|
||||||
String::from_utf8_unchecked(buf)
|
String::from_utf8_unchecked(buf)
|
||||||
};
|
};
|
||||||
return (src, LineEndings::Dos);
|
(src, LineEndings::Dos)
|
||||||
|
}
|
||||||
fn find_crlf(src: &[u8]) -> Option<usize> {
|
}
|
||||||
src.windows(2).position(|it| it == b"\r\n")
|
|
||||||
}
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unix() {
|
||||||
|
let src = "a\nb\nc\n\n\n\n";
|
||||||
|
let (res, endings) = LineEndings::normalize(src.into());
|
||||||
|
assert_eq!(endings, LineEndings::Unix);
|
||||||
|
assert_eq!(res, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dos() {
|
||||||
|
let src = "\r\na\r\n\r\nb\r\nc\r\n\r\n\r\n\r\n";
|
||||||
|
let (res, endings) = LineEndings::normalize(src.into());
|
||||||
|
assert_eq!(endings, LineEndings::Dos);
|
||||||
|
assert_eq!(res, "\na\n\nb\nc\n\n\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mixed() {
|
||||||
|
let src = "a\r\nb\r\nc\r\n\n\r\n\n";
|
||||||
|
let (res, endings) = LineEndings::normalize(src.into());
|
||||||
|
assert_eq!(endings, LineEndings::Dos);
|
||||||
|
assert_eq!(res, "a\nb\nc\n\n\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn none() {
|
||||||
|
let src = "abc";
|
||||||
|
let (res, endings) = LineEndings::normalize(src.into());
|
||||||
|
assert_eq!(endings, LineEndings::Unix);
|
||||||
|
assert_eq!(res, src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//! Utilities for LSP-related boilerplate code.
|
//! Utilities for LSP-related boilerplate code.
|
||||||
use std::{ops::Range, sync::Arc};
|
use std::{mem, ops::Range, sync::Arc};
|
||||||
|
|
||||||
use lsp_server::Notification;
|
use lsp_server::Notification;
|
||||||
|
|
||||||
|
@ -133,11 +133,37 @@ impl GlobalState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn apply_document_changes(
|
pub(crate) fn apply_document_changes(
|
||||||
old_text: &mut String,
|
file_contents: impl FnOnce() -> String,
|
||||||
content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,
|
mut content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,
|
||||||
) {
|
) -> String {
|
||||||
|
// Skip to the last full document change, as it invalidates all previous changes anyways.
|
||||||
|
let mut start = content_changes
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.position(|change| change.range.is_none())
|
||||||
|
.map(|idx| content_changes.len() - idx - 1)
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
let mut text: String = match content_changes.get_mut(start) {
|
||||||
|
// peek at the first content change as an optimization
|
||||||
|
Some(lsp_types::TextDocumentContentChangeEvent { range: None, text, .. }) => {
|
||||||
|
let text = mem::take(text);
|
||||||
|
start += 1;
|
||||||
|
|
||||||
|
// The only change is a full document update
|
||||||
|
if start == content_changes.len() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
text
|
||||||
|
}
|
||||||
|
Some(_) => file_contents(),
|
||||||
|
// we received no content changes
|
||||||
|
None => return file_contents(),
|
||||||
|
};
|
||||||
|
|
||||||
let mut line_index = LineIndex {
|
let mut line_index = LineIndex {
|
||||||
index: Arc::new(ide::LineIndex::new(old_text)),
|
// the index will be overwritten in the bottom loop's first iteration
|
||||||
|
index: Arc::new(ide::LineIndex::new(&text)),
|
||||||
// We don't care about line endings or offset encoding here.
|
// We don't care about line endings or offset encoding here.
|
||||||
endings: LineEndings::Unix,
|
endings: LineEndings::Unix,
|
||||||
encoding: PositionEncoding::Utf16,
|
encoding: PositionEncoding::Utf16,
|
||||||
|
@ -148,38 +174,20 @@ pub(crate) fn apply_document_changes(
|
||||||
// Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
|
// Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
|
||||||
// remember the last valid line in the index and only rebuild it if needed.
|
// remember the last valid line in the index and only rebuild it if needed.
|
||||||
// The VFS will normalize the end of lines to `\n`.
|
// The VFS will normalize the end of lines to `\n`.
|
||||||
enum IndexValid {
|
let mut index_valid = !0u32;
|
||||||
All,
|
|
||||||
UpToLineExclusive(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexValid {
|
|
||||||
fn covers(&self, line: u32) -> bool {
|
|
||||||
match *self {
|
|
||||||
IndexValid::UpToLineExclusive(to) => to > line,
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut index_valid = IndexValid::All;
|
|
||||||
for change in content_changes {
|
for change in content_changes {
|
||||||
match change.range {
|
// The None case can't happen as we have handled it above already
|
||||||
Some(range) => {
|
if let Some(range) = change.range {
|
||||||
if !index_valid.covers(range.end.line) {
|
if index_valid <= range.end.line {
|
||||||
line_index.index = Arc::new(ide::LineIndex::new(old_text));
|
*Arc::make_mut(&mut line_index.index) = ide::LineIndex::new(&text);
|
||||||
}
|
|
||||||
index_valid = IndexValid::UpToLineExclusive(range.start.line);
|
|
||||||
if let Ok(range) = from_proto::text_range(&line_index, range) {
|
|
||||||
old_text.replace_range(Range::<usize>::from(range), &change.text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {
|
index_valid = range.start.line;
|
||||||
*old_text = change.text;
|
if let Ok(range) = from_proto::text_range(&line_index, range) {
|
||||||
index_valid = IndexValid::UpToLineExclusive(0);
|
text.replace_range(Range::<usize>::from(range), &change.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
text
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that the edits inside the completion and the additional edits do not overlap.
|
/// Checks that the edits inside the completion and the additional edits do not overlap.
|
||||||
|
@ -242,11 +250,10 @@ mod tests {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut text = String::new();
|
let text = apply_document_changes(|| String::new(), vec![]);
|
||||||
apply_document_changes(&mut text, vec![]);
|
|
||||||
assert_eq!(text, "");
|
assert_eq!(text, "");
|
||||||
apply_document_changes(
|
let text = apply_document_changes(
|
||||||
&mut text,
|
|| text,
|
||||||
vec![TextDocumentContentChangeEvent {
|
vec![TextDocumentContentChangeEvent {
|
||||||
range: None,
|
range: None,
|
||||||
range_length: None,
|
range_length: None,
|
||||||
|
@ -254,39 +261,39 @@ mod tests {
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
assert_eq!(text, "the");
|
assert_eq!(text, "the");
|
||||||
apply_document_changes(&mut text, c![0, 3; 0, 3 => " quick"]);
|
let text = apply_document_changes(|| text, c![0, 3; 0, 3 => " quick"]);
|
||||||
assert_eq!(text, "the quick");
|
assert_eq!(text, "the quick");
|
||||||
apply_document_changes(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
|
let text = apply_document_changes(|| text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
|
||||||
assert_eq!(text, "quick foxes");
|
assert_eq!(text, "quick foxes");
|
||||||
apply_document_changes(&mut text, c![0, 11; 0, 11 => "\ndream"]);
|
let text = apply_document_changes(|| text, c![0, 11; 0, 11 => "\ndream"]);
|
||||||
assert_eq!(text, "quick foxes\ndream");
|
assert_eq!(text, "quick foxes\ndream");
|
||||||
apply_document_changes(&mut text, c![1, 0; 1, 0 => "have "]);
|
let text = apply_document_changes(|| text, c![1, 0; 1, 0 => "have "]);
|
||||||
assert_eq!(text, "quick foxes\nhave dream");
|
assert_eq!(text, "quick foxes\nhave dream");
|
||||||
apply_document_changes(
|
let text = apply_document_changes(
|
||||||
&mut text,
|
|| text,
|
||||||
c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"],
|
c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"],
|
||||||
);
|
);
|
||||||
assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
|
assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
|
||||||
apply_document_changes(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
|
let text = apply_document_changes(|| text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
|
||||||
assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
|
assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
|
||||||
apply_document_changes(
|
let text = apply_document_changes(
|
||||||
&mut text,
|
|| text,
|
||||||
c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"],
|
c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"],
|
||||||
);
|
);
|
||||||
assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
|
assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
|
||||||
apply_document_changes(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
|
let text = apply_document_changes(|| text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
|
||||||
assert_eq!(text, "the quick \nthey have quiet dreams\n");
|
assert_eq!(text, "the quick \nthey have quiet dreams\n");
|
||||||
|
|
||||||
text = String::from("❤️");
|
let text = String::from("❤️");
|
||||||
apply_document_changes(&mut text, c![0, 0; 0, 0 => "a"]);
|
let text = apply_document_changes(|| text, c![0, 0; 0, 0 => "a"]);
|
||||||
assert_eq!(text, "a❤️");
|
assert_eq!(text, "a❤️");
|
||||||
|
|
||||||
text = String::from("a\nb");
|
let text = String::from("a\nb");
|
||||||
apply_document_changes(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
|
let text = apply_document_changes(|| text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
|
||||||
assert_eq!(text, "adcb");
|
assert_eq!(text, "adcb");
|
||||||
|
|
||||||
text = String::from("a\nb");
|
let text = String::from("a\nb");
|
||||||
apply_document_changes(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
|
let text = apply_document_changes(|| text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
|
||||||
assert_eq!(text, "ațc\ncb");
|
assert_eq!(text, "ațc\ncb");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -451,7 +451,7 @@ impl GlobalState {
|
||||||
ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
|
ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
|
||||||
ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
|
ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
|
||||||
ProjectWorkspaceProgress::End(workspaces) => {
|
ProjectWorkspaceProgress::End(workspaces) => {
|
||||||
self.fetch_workspaces_queue.op_completed(workspaces);
|
self.fetch_workspaces_queue.op_completed(Some(workspaces));
|
||||||
|
|
||||||
let old = Arc::clone(&self.workspaces);
|
let old = Arc::clone(&self.workspaces);
|
||||||
self.switch_workspaces("fetched workspace".to_string());
|
self.switch_workspaces("fetched workspace".to_string());
|
||||||
|
@ -759,8 +759,10 @@ impl GlobalState {
|
||||||
|
|
||||||
let vfs = &mut this.vfs.write().0;
|
let vfs = &mut this.vfs.write().0;
|
||||||
let file_id = vfs.file_id(&path).unwrap();
|
let file_id = vfs.file_id(&path).unwrap();
|
||||||
let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
|
let text = apply_document_changes(
|
||||||
apply_document_changes(&mut text, params.content_changes);
|
|| std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
|
||||||
|
params.content_changes,
|
||||||
|
);
|
||||||
|
|
||||||
vfs.set_file_contents(path, Some(text.into_bytes()));
|
vfs.set_file_contents(path, Some(text.into_bytes()));
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue