mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43: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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "command-group"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7a8a86f409b4a59df3a3e4bee2de0b83f1755fdd2a25e3a9684c396fc4bed2c"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "countme"
|
||||
version = "3.0.1"
|
||||
|
@ -300,7 +310,7 @@ dependencies = [
|
|||
"hashbrown",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core 0.9.3",
|
||||
"parking_lot_core 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -359,14 +369,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.17"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c"
|
||||
checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"windows-sys 0.36.1",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -390,6 +400,7 @@ name = "flycheck"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"cargo_metadata",
|
||||
"command-group",
|
||||
"crossbeam-channel",
|
||||
"jod-thread",
|
||||
"paths",
|
||||
|
@ -963,11 +974,24 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7377f7792b3afb6a3cba68daa54ca23c032137010460d667fda53a8d66be00e"
|
||||
checksum = "52ffbca2f655e33c08be35d87278e5b18b89550a37dbd598c20db92f6a471123"
|
||||
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]]
|
||||
|
@ -1037,7 +1061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.3",
|
||||
"parking_lot_core 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1056,15 +1080,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.3"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
||||
checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys 0.36.1",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1979,19 +2003,6 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
|
@ -2006,10 +2017,25 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.28.0"
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
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]]
|
||||
name = "windows_aarch64_msvc"
|
||||
|
@ -2018,10 +2044,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.28.0"
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
|
@ -2030,10 +2056,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.28.0"
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
|
@ -2042,10 +2068,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.28.0"
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
|
@ -2054,10 +2080,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.28.0"
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
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]]
|
||||
name = "windows_x86_64_msvc"
|
||||
|
@ -2065,6 +2097,12 @@ version = "0.36.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "write-json"
|
||||
version = "0.1.2"
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -17,6 +17,7 @@ rustc-hash = "1.1.0"
|
|||
serde = { version = "1.0.137", features = ["derive"] }
|
||||
serde_json = "1.0.86"
|
||||
jod-thread = "0.1.2"
|
||||
command-group = "1.0.8"
|
||||
|
||||
toolchain = { path = "../toolchain", version = "0.0.0" }
|
||||
stdx = { path = "../stdx", version = "0.0.0" }
|
||||
|
|
|
@ -10,11 +10,12 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use command_group::{CommandGroup, GroupChild};
|
||||
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
|
||||
use paths::AbsPathBuf;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::Deserialize;
|
||||
use stdx::{process::streaming_output, JodChild};
|
||||
use stdx::process::streaming_output;
|
||||
|
||||
pub use cargo_metadata::diagnostic::{
|
||||
Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
|
||||
|
@ -39,7 +40,7 @@ pub enum InvocationLocation {
|
|||
pub enum FlycheckConfig {
|
||||
CargoCommand {
|
||||
command: String,
|
||||
target_triple: Option<String>,
|
||||
target_triples: Vec<String>,
|
||||
all_targets: bool,
|
||||
no_default_features: bool,
|
||||
all_features: bool,
|
||||
|
@ -285,7 +286,7 @@ impl FlycheckActor {
|
|||
let (mut cmd, args) = match &self.config {
|
||||
FlycheckConfig::CargoCommand {
|
||||
command,
|
||||
target_triple,
|
||||
target_triples,
|
||||
no_default_features,
|
||||
all_targets,
|
||||
all_features,
|
||||
|
@ -299,7 +300,7 @@ impl FlycheckActor {
|
|||
cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
|
||||
.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()]);
|
||||
}
|
||||
if *all_targets {
|
||||
|
@ -359,10 +360,12 @@ impl FlycheckActor {
|
|||
}
|
||||
}
|
||||
|
||||
struct JodChild(GroupChild);
|
||||
|
||||
/// A handle to a cargo process used for fly-checking.
|
||||
struct CargoHandle {
|
||||
/// 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,
|
||||
thread: jod_thread::JoinHandle<io::Result<(bool, String)>>,
|
||||
receiver: Receiver<CargoMessage>,
|
||||
|
@ -371,10 +374,10 @@ struct CargoHandle {
|
|||
impl CargoHandle {
|
||||
fn spawn(mut command: Command) -> std::io::Result<CargoHandle> {
|
||||
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 stderr = child.stderr.take().unwrap();
|
||||
let stdout = child.0.inner().stdout.take().unwrap();
|
||||
let stderr = child.0.inner().stderr.take().unwrap();
|
||||
|
||||
let (sender, receiver) = unbounded();
|
||||
let actor = CargoActor::new(sender, stdout, stderr);
|
||||
|
@ -386,13 +389,13 @@ impl CargoHandle {
|
|||
}
|
||||
|
||||
fn cancel(mut self) {
|
||||
let _ = self.child.kill();
|
||||
let _ = self.child.wait();
|
||||
let _ = self.child.0.kill();
|
||||
let _ = self.child.0.wait();
|
||||
}
|
||||
|
||||
fn join(mut self) -> io::Result<()> {
|
||||
let _ = self.child.kill();
|
||||
let exit_status = self.child.wait()?;
|
||||
let _ = self.child.0.kill();
|
||||
let exit_status = self.child.0.wait()?;
|
||||
let (read_at_least_one_message, error) = self.thread.join()?;
|
||||
if read_at_least_one_message || exit_status.success() {
|
||||
Ok(())
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -236,11 +236,19 @@ impl TraitData {
|
|||
.by_key("rustc_skip_array_during_method_dispatch")
|
||||
.exists();
|
||||
|
||||
let mut collector =
|
||||
AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr));
|
||||
collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items);
|
||||
let (items, attribute_calls, diagnostics) = collector.finish();
|
||||
|
||||
let (items, attribute_calls, diagnostics) = match &tr_def.items {
|
||||
Some(items) => {
|
||||
let mut collector = AssocItemCollector::new(
|
||||
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 {
|
||||
name,
|
||||
|
|
|
@ -666,7 +666,8 @@ pub struct Trait {
|
|||
pub generic_params: Interned<GenericParams>,
|
||||
pub is_auto: 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>,
|
||||
}
|
||||
|
||||
|
|
|
@ -451,15 +451,7 @@ impl<'a> Ctx<'a> {
|
|||
.collect()
|
||||
});
|
||||
let ast_id = self.source_ast_id_map.ast_id(trait_def);
|
||||
let res = Trait {
|
||||
name,
|
||||
visibility,
|
||||
generic_params,
|
||||
is_auto,
|
||||
is_unsafe,
|
||||
items: items.unwrap_or_default(),
|
||||
ast_id,
|
||||
};
|
||||
let res = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id };
|
||||
Some(id(self.data().traits.alloc(res)))
|
||||
}
|
||||
|
||||
|
|
|
@ -375,12 +375,21 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
w!(self, "trait {}", name);
|
||||
self.print_generic_params(generic_params);
|
||||
self.print_where_clause_and_opening_brace(generic_params);
|
||||
self.indented(|this| {
|
||||
for item in &**items {
|
||||
this.print_mod_item((*item).into());
|
||||
match items {
|
||||
Some(items) => {
|
||||
self.print_where_clause_and_opening_brace(generic_params);
|
||||
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, "}}");
|
||||
}
|
||||
ModItem::Impl(it) => {
|
||||
|
|
|
@ -94,11 +94,11 @@ macro_rules! m {
|
|||
($($s:stmt)*) => (stringify!($($s |)*);)
|
||||
}
|
||||
stringify!(;
|
||||
|;
|
||||
|92|;
|
||||
|let x = 92|;
|
||||
| ;
|
||||
|92| ;
|
||||
|let x = 92| ;
|
||||
|loop {}
|
||||
|;
|
||||
| ;
|
||||
|);
|
||||
"#]],
|
||||
);
|
||||
|
@ -118,7 +118,7 @@ m!(.. .. ..);
|
|||
macro_rules! m {
|
||||
($($p:pat)*) => (stringify!($($p |)*);)
|
||||
}
|
||||
stringify!(.. .. ..|);
|
||||
stringify!(.. .. .. |);
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -82,14 +82,14 @@ fn attribute_macro_syntax_completion_2() {
|
|||
#[proc_macros::identity_when_valid]
|
||||
fn foo() { bar.; blub }
|
||||
"#,
|
||||
expect![[r##"
|
||||
expect![[r#"
|
||||
#[proc_macros::identity_when_valid]
|
||||
fn foo() { bar.; blub }
|
||||
|
||||
fn foo() {
|
||||
bar.;
|
||||
bar. ;
|
||||
blub
|
||||
}"##]],
|
||||
}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -212,6 +212,7 @@ impl Import {
|
|||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct ImportDirective {
|
||||
/// The module this import directive is in.
|
||||
module_id: LocalModuleId,
|
||||
import: Import,
|
||||
status: PartialResolvedImport,
|
||||
|
@ -963,8 +964,10 @@ impl DefCollector<'_> {
|
|||
|
||||
fn update(
|
||||
&mut self,
|
||||
// The module for which `resolutions` have been resolve
|
||||
module_id: LocalModuleId,
|
||||
resolutions: &[(Option<Name>, PerNs)],
|
||||
// Visibility this import will have
|
||||
vis: Visibility,
|
||||
import_type: ImportType,
|
||||
) {
|
||||
|
@ -974,6 +977,7 @@ impl DefCollector<'_> {
|
|||
|
||||
fn update_recursive(
|
||||
&mut self,
|
||||
// The module for which `resolutions` have been resolve
|
||||
module_id: LocalModuleId,
|
||||
resolutions: &[(Option<Name>, PerNs)],
|
||||
// All resolutions are imported with this visibility; the visibilities in
|
||||
|
|
|
@ -73,7 +73,10 @@ impl DefMap {
|
|||
pub(crate) fn resolve_visibility(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
// module to import to
|
||||
original_module: LocalModuleId,
|
||||
// pub(path)
|
||||
// ^^^^ this
|
||||
visibility: &RawVisibility,
|
||||
) -> Option<Visibility> {
|
||||
let mut vis = match visibility {
|
||||
|
@ -115,6 +118,7 @@ impl DefMap {
|
|||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
mode: ResolveMode,
|
||||
// module to import to
|
||||
mut original_module: LocalModuleId,
|
||||
path: &ModPath,
|
||||
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))
|
||||
|
|
|
@ -58,9 +58,9 @@ extern {
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
E: t
|
||||
E: _
|
||||
S: t v
|
||||
V: t v
|
||||
V: _
|
||||
foo: t
|
||||
|
||||
crate::foo
|
||||
|
@ -307,7 +307,7 @@ pub struct FromLib;
|
|||
Bar: t v
|
||||
|
||||
crate::foo
|
||||
Bar: t v
|
||||
Bar: _
|
||||
FromLib: t v
|
||||
"#]],
|
||||
);
|
||||
|
|
|
@ -119,7 +119,7 @@ use foo::*;
|
|||
use foo::bar::*;
|
||||
|
||||
//- /foo/mod.rs
|
||||
mod bar;
|
||||
pub mod bar;
|
||||
fn Foo() {};
|
||||
pub struct Foo {};
|
||||
|
||||
|
@ -132,6 +132,7 @@ pub(crate) struct PubCrateStruct;
|
|||
crate
|
||||
Foo: t
|
||||
PubCrateStruct: t v
|
||||
bar: t
|
||||
foo: t
|
||||
|
||||
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
|
||||
mod foo {
|
||||
#[path = "baz.rs"]
|
||||
mod bar;
|
||||
pub mod bar;
|
||||
}
|
||||
use self::foo::bar::Baz;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::mem;
|
|||
|
||||
use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use syntax::{
|
||||
ast::{self, AstNode, HasLoopBody},
|
||||
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
|
||||
|
@ -292,25 +293,34 @@ pub(crate) fn reverse_fixups(
|
|||
token_map: &TokenMap,
|
||||
undo_info: &SyntaxFixupUndoInfo,
|
||||
) {
|
||||
tt.token_trees.retain(|tt| match tt {
|
||||
tt::TokenTree::Leaf(leaf) => {
|
||||
token_map.synthetic_token_id(leaf.id()).is_none()
|
||||
|| token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID)
|
||||
}
|
||||
tt::TokenTree::Subtree(st) => st.delimiter.map_or(true, |d| {
|
||||
token_map.synthetic_token_id(d.id).is_none()
|
||||
|| 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());
|
||||
let tts = std::mem::take(&mut tt.token_trees);
|
||||
tt.token_trees = tts
|
||||
.into_iter()
|
||||
.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| token_map.synthetic_token_id(d.id) != Some(EMPTY_ID))
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.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)]
|
||||
|
@ -319,6 +329,31 @@ mod tests {
|
|||
|
||||
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]
|
||||
fn check(ra_fixture: &str, mut expect: Expect) {
|
||||
let parsed = syntax::SourceFile::parse(ra_fixture);
|
||||
|
@ -331,17 +366,15 @@ mod tests {
|
|||
fixups.append,
|
||||
);
|
||||
|
||||
let mut actual = tt.to_string();
|
||||
actual.push('\n');
|
||||
let actual = format!("{}\n", tt);
|
||||
|
||||
expect.indent(false);
|
||||
expect.assert_eq(&actual);
|
||||
|
||||
// the fixed-up tree should be syntactically valid
|
||||
let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems);
|
||||
assert_eq!(
|
||||
parse.errors(),
|
||||
&[],
|
||||
assert!(
|
||||
parse.errors().is_empty(),
|
||||
"parse has syntax errors. parse tree:\n{:#?}",
|
||||
parse.syntax_node()
|
||||
);
|
||||
|
@ -349,9 +382,12 @@ mod tests {
|
|||
reverse_fixups(&mut tt, &tmap, &fixups.undo_info);
|
||||
|
||||
// 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());
|
||||
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]
|
||||
|
@ -468,7 +504,7 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {a .__ra_fixup}
|
||||
fn foo () {a . __ra_fixup}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -482,7 +518,7 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {a .__ra_fixup ;}
|
||||
fn foo () {a . __ra_fixup ;}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -497,7 +533,7 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {a .__ra_fixup ; bar () ;}
|
||||
fn foo () {a . __ra_fixup ; bar () ;}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -525,7 +561,7 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {let x = a .__ra_fixup ;}
|
||||
fn foo () {let x = a . __ra_fixup ;}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -541,7 +577,7 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
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>> {
|
||||
// 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() {
|
||||
return Some(self.map(Clone::clone));
|
||||
} 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>> {
|
||||
// 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() {
|
||||
return Some(self);
|
||||
} else if !self.file_id.is_attr_macro(db) {
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -53,7 +53,7 @@ pub use builder::{ParamKind, TyBuilder};
|
|||
pub use chalk_ext::*;
|
||||
pub use infer::{
|
||||
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
|
||||
InferenceResult,
|
||||
InferenceResult, OverloadedDeref, PointerCast,
|
||||
};
|
||||
pub use interner::Interner;
|
||||
pub use lower::{
|
||||
|
@ -523,7 +523,7 @@ where
|
|||
}
|
||||
|
||||
pub fn callable_sig_from_fnonce(
|
||||
self_ty: &Canonical<Ty>,
|
||||
self_ty: &Ty,
|
||||
env: Arc<TraitEnvironment>,
|
||||
db: &dyn HirDatabase,
|
||||
) -> Option<CallableSig> {
|
||||
|
@ -531,27 +531,28 @@ pub fn callable_sig_from_fnonce(
|
|||
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 mut kinds = self_ty.binders.interned().to_vec();
|
||||
let b = TyBuilder::trait_ref(db, fn_once_trait);
|
||||
if b.remaining() != 2 {
|
||||
return None;
|
||||
}
|
||||
let fn_once = b
|
||||
.push(self_ty.value.clone())
|
||||
.fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
|
||||
.build();
|
||||
kinds.extend(fn_once.substitution.iter(Interner).skip(1).map(|x| {
|
||||
let vk = match x.data(Interner) {
|
||||
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::VariableKind::Const(c.data(Interner).ty.clone())
|
||||
}
|
||||
};
|
||||
chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
|
||||
}));
|
||||
let fn_once = b.push(self_ty.clone()).fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build();
|
||||
let kinds = fn_once
|
||||
.substitution
|
||||
.iter(Interner)
|
||||
.skip(1)
|
||||
.map(|x| {
|
||||
let vk = match x.data(Interner) {
|
||||
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::VariableKind::Const(c.data(Interner).ty.clone())
|
||||
}
|
||||
};
|
||||
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
|
||||
// `<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,
|
||||
_ => 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) {
|
||||
chalk_ir::TyKind::Tuple(_, subst) => {
|
||||
subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::<Vec<_>>()
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
if params.iter().any(|ty| ty.is_unknown()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let fn_once = TyBuilder::trait_ref(db, fn_once_trait)
|
||||
.push(self_ty.value.clone())
|
||||
.push(args.clone())
|
||||
.build();
|
||||
let fn_once =
|
||||
TyBuilder::trait_ref(db, fn_once_trait).push(self_ty.clone()).push(args.clone()).build();
|
||||
let projection =
|
||||
TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone()))
|
||||
.build();
|
||||
|
|
|
@ -541,7 +541,7 @@ pub struct ReceiverAdjustments {
|
|||
|
||||
impl ReceiverAdjustments {
|
||||
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();
|
||||
for _ in 0..self.autoderefs {
|
||||
match autoderef::autoderef_step(table, ty.clone()) {
|
||||
|
|
|
@ -164,16 +164,16 @@ fn infer_associated_method_with_modules() {
|
|||
check_infer(
|
||||
r#"
|
||||
mod a {
|
||||
struct A;
|
||||
pub struct A;
|
||||
impl A { pub fn thing() -> A { A {} }}
|
||||
}
|
||||
|
||||
mod b {
|
||||
struct B;
|
||||
pub struct B;
|
||||
impl B { pub fn thing() -> u32 { 99 }}
|
||||
|
||||
mod c {
|
||||
struct C;
|
||||
pub mod c {
|
||||
pub struct C;
|
||||
impl C { pub fn thing() -> C { C {} }}
|
||||
}
|
||||
}
|
||||
|
@ -186,22 +186,22 @@ fn infer_associated_method_with_modules() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
55..63 '{ A {} }': A
|
||||
57..61 'A {}': A
|
||||
125..131 '{ 99 }': u32
|
||||
127..129 '99': u32
|
||||
201..209 '{ C {} }': C
|
||||
203..207 'C {}': C
|
||||
240..324 '{ ...g(); }': ()
|
||||
250..251 'x': A
|
||||
254..265 'a::A::thing': fn thing() -> A
|
||||
254..267 'a::A::thing()': A
|
||||
277..278 'y': u32
|
||||
281..292 'b::B::thing': fn thing() -> u32
|
||||
281..294 'b::B::thing()': u32
|
||||
304..305 'z': C
|
||||
308..319 'c::C::thing': fn thing() -> C
|
||||
308..321 'c::C::thing()': C
|
||||
59..67 '{ A {} }': A
|
||||
61..65 'A {}': A
|
||||
133..139 '{ 99 }': u32
|
||||
135..137 '99': u32
|
||||
217..225 '{ C {} }': C
|
||||
219..223 'C {}': C
|
||||
256..340 '{ ...g(); }': ()
|
||||
266..267 'x': A
|
||||
270..281 'a::A::thing': fn thing() -> A
|
||||
270..283 'a::A::thing()': A
|
||||
293..294 'y': u32
|
||||
297..308 'b::B::thing': fn thing() -> u32
|
||||
297..310 'b::B::thing()': u32
|
||||
320..321 'z': C
|
||||
324..335 'c::C::thing': fn 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 }
|
||||
|
||||
mod b {
|
||||
fn c() -> u32 { 1 }
|
||||
pub fn c() -> u32 { 1 }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
|
@ -225,13 +225,13 @@ fn test() {
|
|||
expect![[r#"
|
||||
14..19 '{ 1 }': u32
|
||||
16..17 '1': u32
|
||||
47..52 '{ 1 }': u32
|
||||
49..50 '1': u32
|
||||
66..90 '{ ...c(); }': ()
|
||||
72..73 'a': fn a() -> u32
|
||||
72..75 'a()': u32
|
||||
81..85 'b::c': fn c() -> u32
|
||||
81..87 'b::c()': u32
|
||||
51..56 '{ 1 }': u32
|
||||
53..54 '1': u32
|
||||
70..94 '{ ...c(); }': ()
|
||||
76..77 'a': fn a() -> u32
|
||||
76..79 'a()': u32
|
||||
85..89 'b::c': fn c() -> u32
|
||||
85..91 'b::c()': u32
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1856,7 +1856,7 @@ fn not_shadowing_module_by_primitive() {
|
|||
check_types(
|
||||
r#"
|
||||
//- /str.rs
|
||||
fn foo() -> u32 {0}
|
||||
pub fn foo() -> u32 {0}
|
||||
|
||||
//- /main.rs
|
||||
mod str;
|
||||
|
|
|
@ -1706,7 +1706,7 @@ fn where_clause_trait_in_scope_for_method_resolution() {
|
|||
check_types(
|
||||
r#"
|
||||
mod foo {
|
||||
trait Trait {
|
||||
pub trait Trait {
|
||||
fn foo(&self) -> u32 { 0 }
|
||||
}
|
||||
}
|
||||
|
@ -1723,7 +1723,7 @@ fn super_trait_method_resolution() {
|
|||
check_infer(
|
||||
r#"
|
||||
mod foo {
|
||||
trait SuperTrait {
|
||||
pub trait SuperTrait {
|
||||
fn foo(&self) -> u32 {}
|
||||
}
|
||||
}
|
||||
|
@ -1735,15 +1735,15 @@ fn test<T: Trait1, U: Trait2>(x: T, y: U) {
|
|||
y.foo();
|
||||
}"#,
|
||||
expect![[r#"
|
||||
49..53 'self': &Self
|
||||
62..64 '{}': u32
|
||||
181..182 'x': T
|
||||
187..188 'y': U
|
||||
193..222 '{ ...o(); }': ()
|
||||
199..200 'x': T
|
||||
199..206 'x.foo()': u32
|
||||
212..213 'y': U
|
||||
212..219 'y.foo()': u32
|
||||
53..57 'self': &Self
|
||||
66..68 '{}': u32
|
||||
185..186 'x': T
|
||||
191..192 'y': U
|
||||
197..226 '{ ...o(); }': ()
|
||||
203..204 'x': T
|
||||
203..210 'x.foo()': u32
|
||||
216..217 'y': U
|
||||
216..223 'y.foo()': u32
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1754,7 +1754,7 @@ fn super_trait_impl_trait_method_resolution() {
|
|||
r#"
|
||||
//- minicore: sized
|
||||
mod foo {
|
||||
trait SuperTrait {
|
||||
pub trait SuperTrait {
|
||||
fn foo(&self) -> u32 {}
|
||||
}
|
||||
}
|
||||
|
@ -1764,12 +1764,12 @@ fn test(x: &impl Trait1) {
|
|||
x.foo();
|
||||
}"#,
|
||||
expect![[r#"
|
||||
49..53 'self': &Self
|
||||
62..64 '{}': u32
|
||||
115..116 'x': &impl Trait1
|
||||
132..148 '{ ...o(); }': ()
|
||||
138..139 'x': &impl Trait1
|
||||
138..145 'x.foo()': u32
|
||||
53..57 'self': &Self
|
||||
66..68 '{}': u32
|
||||
119..120 'x': &impl Trait1
|
||||
136..152 '{ ...o(); }': ()
|
||||
142..143 'x': &impl Trait1
|
||||
142..149 'x.foo()': u32
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -117,7 +117,7 @@ pub use {
|
|||
name::{known, Name},
|
||||
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
|
||||
|
@ -2997,8 +2997,7 @@ impl Type {
|
|||
TyKind::Function(_) => Callee::FnPtr,
|
||||
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(&ty, self.env.clone(), db)?;
|
||||
let sig = hir_ty::callable_sig_from_fnonce(&self.ty, self.env.clone(), db)?;
|
||||
return Some(Callable {
|
||||
ty: self.clone(),
|
||||
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 {
|
||||
fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
|
||||
fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
|
||||
|
|
|
@ -29,9 +29,10 @@ use crate::{
|
|||
db::HirDatabase,
|
||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
||||
Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function,
|
||||
HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
|
||||
Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
|
||||
Access, Adjust, AutoBorrow, BindingMode, BuiltinAttr, Callable, ConstParam, Crate,
|
||||
DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local,
|
||||
Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, ToolModule, Trait, Type,
|
||||
TypeAlias, TypeParam, VariantDef,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -333,9 +334,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
|||
self.imp.resolve_trait(trait_)
|
||||
}
|
||||
|
||||
// FIXME: Figure out a nice interface to inspect adjustments
|
||||
pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
|
||||
self.imp.is_implicit_reborrow(expr)
|
||||
pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjust>> {
|
||||
self.imp.expr_adjustments(expr)
|
||||
}
|
||||
|
||||
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> {
|
||||
self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr)
|
||||
fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjust>> {
|
||||
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> {
|
||||
|
|
|
@ -38,8 +38,7 @@ use hir_ty::{
|
|||
UnsafeExpr,
|
||||
},
|
||||
method_resolution::{self, lang_names_for_bin_op},
|
||||
Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
|
||||
TyLoweringContext,
|
||||
Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use smallvec::SmallVec;
|
||||
|
@ -156,21 +155,14 @@ impl SourceAnalyzer {
|
|||
Some(res)
|
||||
}
|
||||
|
||||
pub(crate) fn is_implicit_reborrow(
|
||||
pub(crate) fn expr_adjustments(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
expr: &ast::Expr,
|
||||
) -> Option<Mutability> {
|
||||
) -> Option<&[Adjustment]> {
|
||||
let expr_id = self.expr_id(db, expr)?;
|
||||
let infer = self.infer.as_ref()?;
|
||||
let adjustments = infer.expr_adjustments.get(&expr_id)?;
|
||||
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,
|
||||
})
|
||||
infer.expr_adjustments.get(&expr_id).map(|v| &**v)
|
||||
}
|
||||
|
||||
pub(crate) fn type_of_expr(
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -196,6 +196,7 @@ trait Foo {
|
|||
type Output;
|
||||
|
||||
const CONST: usize = 42;
|
||||
const CONST_2: i32;
|
||||
|
||||
fn foo(&self);
|
||||
fn bar(&self);
|
||||
|
@ -213,6 +214,7 @@ trait Foo {
|
|||
type Output;
|
||||
|
||||
const CONST: usize = 42;
|
||||
const CONST_2: i32;
|
||||
|
||||
fn foo(&self);
|
||||
fn bar(&self);
|
||||
|
@ -226,7 +228,7 @@ impl Foo for S {
|
|||
|
||||
$0type Output;
|
||||
|
||||
const CONST: usize = 42;
|
||||
const CONST_2: i32;
|
||||
|
||||
fn foo(&self) {
|
||||
todo!()
|
||||
|
@ -379,14 +381,14 @@ impl Foo for S {
|
|||
r#"
|
||||
mod foo {
|
||||
pub struct Bar;
|
||||
trait Foo { fn foo(&self, bar: Bar); }
|
||||
pub trait Foo { fn foo(&self, bar: Bar); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S { $0 }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar;
|
||||
trait Foo { fn foo(&self, bar: Bar); }
|
||||
pub trait Foo { fn foo(&self, bar: Bar); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
|
@ -439,14 +441,14 @@ impl bar::Foo for S {
|
|||
r#"
|
||||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
trait Foo { fn foo(&self, bar: Bar<u32>); }
|
||||
pub trait Foo { fn foo(&self, bar: Bar<u32>); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S { $0 }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
trait Foo { fn foo(&self, bar: Bar<u32>); }
|
||||
pub trait Foo { fn foo(&self, bar: Bar<u32>); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
|
@ -464,14 +466,14 @@ impl foo::Foo for S {
|
|||
r#"
|
||||
mod foo {
|
||||
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;
|
||||
impl foo::Foo<u32> for S { $0 }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
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;
|
||||
impl foo::Foo<u32> for S {
|
||||
|
@ -489,7 +491,7 @@ impl foo::Foo<u32> for S {
|
|||
add_missing_impl_members,
|
||||
r#"
|
||||
mod foo {
|
||||
trait Foo<T> { fn foo(&self, bar: T); }
|
||||
pub trait Foo<T> { fn foo(&self, bar: T); }
|
||||
pub struct Param;
|
||||
}
|
||||
struct Param;
|
||||
|
@ -497,7 +499,7 @@ struct S;
|
|||
impl foo::Foo<Param> for S { $0 }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
trait Foo<T> { fn foo(&self, bar: T); }
|
||||
pub trait Foo<T> { fn foo(&self, bar: T); }
|
||||
pub struct Param;
|
||||
}
|
||||
struct Param;
|
||||
|
@ -518,7 +520,7 @@ impl foo::Foo<Param> for S {
|
|||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
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;
|
||||
impl foo::Foo for S { $0 }"#,
|
||||
|
@ -526,7 +528,7 @@ impl foo::Foo for S { $0 }"#,
|
|||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
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;
|
||||
impl foo::Foo for S {
|
||||
|
@ -545,7 +547,7 @@ impl foo::Foo for S {
|
|||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
pub struct Baz;
|
||||
trait Foo { fn foo(&self, bar: Bar<Baz>); }
|
||||
pub trait Foo { fn foo(&self, bar: Bar<Baz>); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S { $0 }"#,
|
||||
|
@ -553,7 +555,7 @@ impl foo::Foo for S { $0 }"#,
|
|||
mod foo {
|
||||
pub struct Bar<T>;
|
||||
pub struct Baz;
|
||||
trait Foo { fn foo(&self, bar: Bar<Baz>); }
|
||||
pub trait Foo { fn foo(&self, bar: Bar<Baz>); }
|
||||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
|
@ -571,14 +573,14 @@ impl foo::Foo for S {
|
|||
r#"
|
||||
mod foo {
|
||||
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;
|
||||
impl foo::Foo for S { $0 }"#,
|
||||
r#"
|
||||
mod foo {
|
||||
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;
|
||||
impl foo::Foo for S {
|
||||
|
@ -658,6 +660,7 @@ trait Foo {
|
|||
type Output;
|
||||
|
||||
const CONST: usize = 42;
|
||||
const CONST_2: i32;
|
||||
|
||||
fn valid(some: u32) -> bool { false }
|
||||
fn foo(some: u32) -> bool;
|
||||
|
@ -669,13 +672,16 @@ trait Foo {
|
|||
type Output;
|
||||
|
||||
const CONST: usize = 42;
|
||||
const CONST_2: i32;
|
||||
|
||||
fn valid(some: u32) -> bool { false }
|
||||
fn foo(some: u32) -> bool;
|
||||
}
|
||||
struct 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 =
|
||||
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 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));
|
||||
|
||||
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) {
|
||||
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);
|
||||
generate_impl_text(&adt, &fn_def).replace("{\n\n", "{")
|
||||
}
|
||||
|
@ -272,7 +273,7 @@ enum FunType {
|
|||
}
|
||||
|
||||
/// Where to put extracted function definition
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
enum Anchor {
|
||||
/// Extract free function and put right after current top-level function
|
||||
Freestanding,
|
||||
|
@ -1245,6 +1246,14 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNod
|
|||
while let Some(next_ancestor) = ancestors.next() {
|
||||
match next_ancestor.kind() {
|
||||
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 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
|
||||
}
|
||||
|
||||
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 {
|
||||
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]
|
||||
fn closure_arguments() {
|
||||
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 syntax::{
|
||||
ast::{self, HasVisibility as _},
|
||||
|
@ -18,7 +18,7 @@ use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
|
|||
// fn frobnicate() {}
|
||||
// }
|
||||
// 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() {}
|
||||
// }
|
||||
// fn main() {
|
||||
// m::frobnicate() {}
|
||||
// m::frobnicate();
|
||||
// }
|
||||
// ```
|
||||
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<()> {
|
||||
let path: ast::Path = ctx.find_node_at_offset()?;
|
||||
let path_res = ctx.sema.resolve_path(&path)?;
|
||||
let def = match path_res {
|
||||
PathResolution::Def(def) => def,
|
||||
_ => return None,
|
||||
};
|
||||
let qualifier = path.qualifier()?;
|
||||
let name_ref = path.segment()?.name_ref()?;
|
||||
let qualifier_res = ctx.sema.resolve_path(&qualifier)?;
|
||||
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 target_module = def.module(ctx.db())?;
|
||||
|
|
|
@ -261,12 +261,12 @@ fn main() {
|
|||
}
|
||||
|
||||
//- /foo.rs
|
||||
enum Foo {
|
||||
pub enum Foo {
|
||||
Bar,
|
||||
}
|
||||
",
|
||||
r"
|
||||
enum Foo {
|
||||
pub enum Foo {
|
||||
Bar,
|
||||
Baz,
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ fn main() {
|
|||
generate_enum_variant,
|
||||
r"
|
||||
mod m {
|
||||
enum Foo {
|
||||
pub enum Foo {
|
||||
Bar,
|
||||
}
|
||||
}
|
||||
|
@ -320,7 +320,7 @@ fn main() {
|
|||
",
|
||||
r"
|
||||
mod m {
|
||||
enum Foo {
|
||||
pub enum Foo {
|
||||
Bar,
|
||||
Baz,
|
||||
}
|
||||
|
@ -516,10 +516,10 @@ mod foo;
|
|||
use foo::Foo::Bar$0;
|
||||
|
||||
//- /foo.rs
|
||||
enum Foo {}
|
||||
pub enum Foo {}
|
||||
",
|
||||
r"
|
||||
enum Foo {
|
||||
pub enum Foo {
|
||||
Bar,
|
||||
}
|
||||
",
|
||||
|
|
|
@ -1324,7 +1324,7 @@ fn foo() {
|
|||
generate_function,
|
||||
r"
|
||||
mod bar {
|
||||
mod baz {}
|
||||
pub mod baz {}
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
|
@ -1333,7 +1333,7 @@ fn foo() {
|
|||
",
|
||||
r"
|
||||
mod bar {
|
||||
mod baz {
|
||||
pub mod baz {
|
||||
pub(crate) fn my_fn() {
|
||||
${0:todo!()}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>)
|
|||
NodeOrToken::Node(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());
|
||||
current_arg.clear();
|
||||
},
|
||||
|
@ -238,14 +238,14 @@ fn main() {
|
|||
&add_macro_decl(
|
||||
r#"
|
||||
fn main() {
|
||||
print!("{} {x + 1:b} {Struct(1, 2)}$0", 1);
|
||||
print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1);
|
||||
}
|
||||
"#,
|
||||
),
|
||||
&add_macro_decl(
|
||||
r#"
|
||||
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 syntax::{
|
||||
ast::{self, AstNode, AstToken},
|
||||
match_ast, NodeOrToken, SyntaxElement, TextSize, T,
|
||||
match_ast, NodeOrToken, SyntaxElement, TextRange, TextSize, T,
|
||||
};
|
||||
|
||||
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<()> {
|
||||
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 r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
|
||||
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 parent = macro_expr.syntax().parent()?;
|
||||
let (range, text) = match &*input_expressions {
|
||||
Some(match &*input_expressions {
|
||||
// dbg!()
|
||||
[] => {
|
||||
match_ast! {
|
||||
|
@ -107,10 +136,6 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
|
|||
}
|
||||
// dbg!(expr0, expr1, ...)
|
||||
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, 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 {
|
||||
$0type Qux;
|
||||
|
||||
const Baz: usize = 42;
|
||||
|
||||
const Fez: usize;
|
||||
|
||||
fn foo() {
|
||||
|
|
|
@ -741,7 +741,7 @@ mod m {
|
|||
fn frobnicate() {}
|
||||
}
|
||||
fn main() {
|
||||
m::frobnicate$0() {}
|
||||
m::frobnicate$0();
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
|
@ -749,7 +749,7 @@ mod m {
|
|||
$0pub(crate) fn frobnicate() {}
|
||||
}
|
||||
fn main() {
|
||||
m::frobnicate() {}
|
||||
m::frobnicate();
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
|
|
|
@ -119,6 +119,10 @@ pub fn filter_assoc_items(
|
|||
(default_methods, def.body()),
|
||||
(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,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -157,7 +157,7 @@ fn complete_trait_impl(
|
|||
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
|
||||
}
|
||||
(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) => {
|
||||
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
|
||||
|
@ -236,9 +236,7 @@ fn get_transformed_assoc_item(
|
|||
);
|
||||
|
||||
transform.apply(assoc_item.syntax());
|
||||
if let ast::AssocItem::Fn(func) = &assoc_item {
|
||||
func.remove_attrs_and_docs();
|
||||
}
|
||||
assoc_item.remove_attrs_and_docs();
|
||||
Some(assoc_item)
|
||||
}
|
||||
|
||||
|
@ -247,24 +245,50 @@ fn add_type_alias_impl(
|
|||
ctx: &CompletionContext<'_>,
|
||||
replacement_range: TextRange,
|
||||
type_alias: hir::TypeAlias,
|
||||
impl_def: hir::Impl,
|
||||
) {
|
||||
let alias_name = type_alias.name(ctx.db);
|
||||
let (alias_name, escaped_name) =
|
||||
(alias_name.unescaped().to_smol_str(), alias_name.to_smol_str());
|
||||
let alias_name = type_alias.name(ctx.db).unescaped().to_smol_str();
|
||||
|
||||
let label = format!("type {} =", alias_name);
|
||||
let replacement = format!("type {} = ", escaped_name);
|
||||
|
||||
let mut item = CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label);
|
||||
item.lookup_by(format!("type {}", alias_name))
|
||||
.set_documentation(type_alias.docs(ctx.db))
|
||||
.set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => item
|
||||
.snippet_edit(cap, TextEdit::replace(replacement_range, format!("{}$0;", replacement))),
|
||||
None => item.text_edit(TextEdit::replace(replacement_range, replacement)),
|
||||
};
|
||||
item.add_to(acc);
|
||||
|
||||
if let Some(source) = ctx.sema.source(type_alias) {
|
||||
let assoc_item = ast::AssocItem::TypeAlias(source.value);
|
||||
if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
|
||||
let transformed_ty = match transformed_item {
|
||||
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(
|
||||
|
@ -309,7 +333,6 @@ fn add_const_impl(
|
|||
}
|
||||
|
||||
fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> String {
|
||||
const_.remove_attrs_and_docs();
|
||||
let const_ = if needs_whitespace {
|
||||
insert_whitespace_into_node::insert_ws_into(const_.syntax().clone())
|
||||
} 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 {
|
||||
node.remove_attrs_and_docs();
|
||||
|
||||
let node = if needs_whitespace {
|
||||
insert_whitespace_into_node::insert_ws_into(node.syntax().clone())
|
||||
} else {
|
||||
|
@ -350,9 +371,7 @@ fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
|
|||
.map_or(end, |f| f.text_range().start());
|
||||
|
||||
let len = end - start;
|
||||
let range = TextRange::new(0.into(), len);
|
||||
|
||||
let syntax = node.text().slice(range).to_string();
|
||||
let syntax = node.text().slice(..len).to_string();
|
||||
|
||||
syntax.trim_end().to_owned()
|
||||
}
|
||||
|
@ -1160,6 +1179,106 @@ impl Foo for Test {
|
|||
$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::Fn(it) => it.body().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::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::TypeAlias(it) => it.ty().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"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -58,8 +58,11 @@ impl LineIndex {
|
|||
let mut utf16_lines = NoHashHashMap::default();
|
||||
let mut utf16_chars = Vec::new();
|
||||
|
||||
let mut newlines = vec![0.into()];
|
||||
let mut curr_row @ mut curr_col = 0.into();
|
||||
let mut newlines = Vec::with_capacity(16);
|
||||
newlines.push(TextSize::from(0));
|
||||
|
||||
let mut curr_row = 0.into();
|
||||
let mut curr_col = 0.into();
|
||||
let mut line = 0;
|
||||
for c in text.chars() {
|
||||
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);
|
||||
state = State::NotArg;
|
||||
}
|
||||
(State::MaybeArg, ':') => {
|
||||
output.push(chr);
|
||||
extracted_expressions.push(Arg::Placeholder);
|
||||
state = State::FormatOpts;
|
||||
}
|
||||
(State::MaybeArg, _) => {
|
||||
if matches!(chr, '\\' | '$') {
|
||||
current_expr.push('\\');
|
||||
|
@ -118,44 +123,41 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
|
|||
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(':')) => {
|
||||
// path separator
|
||||
state = State::Expr;
|
||||
current_expr.push_str("::");
|
||||
chars.next();
|
||||
}
|
||||
(State::Ident | State::Expr, ':') => {
|
||||
(State::Ident | State::Expr, ':' | '}') => {
|
||||
if inexpr_open_count == 0 {
|
||||
// We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
|
||||
output.push(chr);
|
||||
let trimmed = current_expr.trim();
|
||||
|
||||
if matches!(state, State::Expr) {
|
||||
extracted_expressions.push(Arg::Expr(current_expr.trim().into()));
|
||||
// if the expression consists of a single number, like "0" or "12", it can refer to
|
||||
// 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 {
|
||||
extracted_expressions.push(Arg::Ident(current_expr.trim().into()));
|
||||
extracted_expressions.push(Arg::Ident(trimmed.into()));
|
||||
}
|
||||
|
||||
current_expr = String::new();
|
||||
state = State::FormatOpts;
|
||||
} else {
|
||||
output.push(chr);
|
||||
current_expr.clear();
|
||||
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.
|
||||
current_expr.push(chr);
|
||||
}
|
||||
|
@ -219,6 +221,10 @@ mod tests {
|
|||
("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
|
||||
("{expr:?}", expect![["{:?}; 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"]]),
|
||||
("{malformed", expect![["-"]]),
|
||||
("malformed}", expect![["-"]]),
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -5,10 +5,7 @@ use crate::{Diagnostic, DiagnosticsContext};
|
|||
// This diagnostic is shown for macro expansion errors.
|
||||
pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
|
||||
// Use more accurate position if available.
|
||||
let display_range = d
|
||||
.precise_location
|
||||
.unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone()).range);
|
||||
|
||||
let display_range = ctx.resolve_precise_location(&d.node, d.precise_location);
|
||||
Diagnostic::new("macro-error", d.message.clone(), display_range).experimental()
|
||||
}
|
||||
|
||||
|
|
|
@ -268,12 +268,12 @@ fn main() {
|
|||
foo::Foo { bar: 3, $0baz: false};
|
||||
}
|
||||
//- /foo.rs
|
||||
struct Foo {
|
||||
pub struct Foo {
|
||||
bar: i32
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo {
|
||||
pub struct Foo {
|
||||
bar: i32,
|
||||
pub(crate) baz: bool
|
||||
}
|
||||
|
|
|
@ -9,10 +9,7 @@ pub(crate) fn unresolved_macro_call(
|
|||
d: &hir::UnresolvedMacroCall,
|
||||
) -> Diagnostic {
|
||||
// Use more accurate position if available.
|
||||
let display_range = d
|
||||
.precise_location
|
||||
.unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.macro_call.clone()).range);
|
||||
|
||||
let display_range = ctx.resolve_precise_location(&d.macro_call, d.precise_location);
|
||||
let bang = if d.is_bang { "!" } else { "" };
|
||||
Diagnostic::new(
|
||||
"unresolved-macro-call",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use hir::db::DefDatabase;
|
||||
use syntax::NodeOrToken;
|
||||
|
||||
use crate::{Diagnostic, DiagnosticsContext, Severity};
|
||||
|
||||
|
@ -19,16 +18,7 @@ pub(crate) fn unresolved_proc_macro(
|
|||
proc_attr_macros_enabled: bool,
|
||||
) -> Diagnostic {
|
||||
// Use more accurate position if available.
|
||||
let display_range = (|| {
|
||||
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 display_range = ctx.resolve_precise_location(&d.node, d.precise_location);
|
||||
|
||||
let config_enabled = match d.kind {
|
||||
hir::MacroKind::Attr => proc_macros_enabled && proc_attr_macros_enabled,
|
||||
|
|
|
@ -71,9 +71,9 @@ use a;
|
|||
use a::{c, d::e};
|
||||
|
||||
mod a {
|
||||
mod c {}
|
||||
mod d {
|
||||
mod e {}
|
||||
pub mod c {}
|
||||
pub mod d {
|
||||
pub mod e {}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
@ -87,9 +87,9 @@ use a::{
|
|||
};
|
||||
|
||||
mod a {
|
||||
mod c {}
|
||||
mod d {
|
||||
mod e {}
|
||||
pub mod c {}
|
||||
pub mod d {
|
||||
pub mod e {}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
@ -116,11 +116,11 @@ use b;
|
|||
);
|
||||
check_fix(
|
||||
r#"
|
||||
mod a { mod c {} }
|
||||
mod a { pub mod c {} }
|
||||
use a::{c$0};
|
||||
"#,
|
||||
r#"
|
||||
mod a { mod c {} }
|
||||
mod a { pub mod c {} }
|
||||
use a::c;
|
||||
"#,
|
||||
);
|
||||
|
@ -136,11 +136,11 @@ use a;
|
|||
);
|
||||
check_fix(
|
||||
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}};
|
||||
"#,
|
||||
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};
|
||||
"#,
|
||||
);
|
||||
|
|
|
@ -182,6 +182,28 @@ struct DiagnosticsContext<'a> {
|
|||
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(
|
||||
db: &RootDatabase,
|
||||
config: &DiagnosticsConfig,
|
||||
|
|
|
@ -5,7 +5,7 @@ description = "Structural search and replace of Rust code"
|
|||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/rust-lang/rust-analyzer"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -289,10 +289,10 @@ mod b;
|
|||
enum E { X(Foo$0) }
|
||||
|
||||
//- /a.rs
|
||||
struct Foo;
|
||||
//^^^
|
||||
pub struct Foo;
|
||||
//^^^
|
||||
//- /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
|
||||
// equivalency is more important
|
||||
let descended = if in_attr {
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use std::fmt;
|
||||
|
||||
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::{
|
||||
base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap,
|
||||
RootDatabase,
|
||||
|
@ -22,7 +25,7 @@ pub struct InlayHintsConfig {
|
|||
pub type_hints: bool,
|
||||
pub parameter_hints: bool,
|
||||
pub chaining_hints: bool,
|
||||
pub reborrow_hints: ReborrowHints,
|
||||
pub adjustment_hints: AdjustmentHints,
|
||||
pub closure_return_type_hints: ClosureReturnTypeHints,
|
||||
pub binding_mode_hints: bool,
|
||||
pub lifetime_elision_hints: LifetimeElisionHints,
|
||||
|
@ -48,9 +51,9 @@ pub enum LifetimeElisionHints {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ReborrowHints {
|
||||
pub enum AdjustmentHints {
|
||||
Always,
|
||||
MutableOnly,
|
||||
ReborrowOnly,
|
||||
Never,
|
||||
}
|
||||
|
||||
|
@ -61,7 +64,8 @@ pub enum InlayKind {
|
|||
ClosingBraceHint,
|
||||
ClosureReturnTypeHint,
|
||||
GenericParamListHint,
|
||||
ImplicitReborrowHint,
|
||||
AdjustmentHint,
|
||||
AdjustmentHintClosingParenthesis,
|
||||
LifetimeHint,
|
||||
ParameterHint,
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.parts.iter().map(|part| &part.text).format(""))
|
||||
|
@ -180,7 +190,7 @@ impl fmt::Debug for InlayHintLabelPart {
|
|||
pub(crate) fn inlay_hints(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
range_limit: Option<FileRange>,
|
||||
range_limit: Option<TextRange>,
|
||||
config: &InlayHintsConfig,
|
||||
) -> Vec<InlayHint> {
|
||||
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);
|
||||
match range_limit {
|
||||
Some(FileRange { range, .. }) => match file.covering_element(range) {
|
||||
Some(range) => match file.covering_element(range) {
|
||||
NodeOrToken::Token(_) => return acc,
|
||||
NodeOrToken::Node(n) => n
|
||||
.descendants()
|
||||
|
@ -221,6 +231,7 @@ fn hints(
|
|||
match node {
|
||||
ast::Expr(expr) => {
|
||||
chaining_hints(hints, sema, &famous_defs, config, file_id, &expr);
|
||||
adjustment_hints(hints, sema, config, &expr);
|
||||
match expr {
|
||||
ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(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),
|
||||
// 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
|
||||
ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
|
||||
// ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
|
@ -617,30 +628,95 @@ fn closure_ret_hints(
|
|||
Some(())
|
||||
}
|
||||
|
||||
fn reborrow_hints(
|
||||
fn adjustment_hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &InlayHintsConfig,
|
||||
expr: &ast::Expr,
|
||||
) -> Option<()> {
|
||||
if config.reborrow_hints == ReborrowHints::Never {
|
||||
if config.adjustment_hints == AdjustmentHints::Never {
|
||||
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 desc_expr = descended.as_ref().unwrap_or(expr);
|
||||
let mutability = sema.is_implicit_reborrow(desc_expr)?;
|
||||
let label = match mutability {
|
||||
hir::Mutability::Shared if config.reborrow_hints != ReborrowHints::MutableOnly => "&*",
|
||||
hir::Mutability::Mut => "&mut *",
|
||||
_ => return None,
|
||||
let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
|
||||
let needs_parens = match parent {
|
||||
Some(parent) => {
|
||||
match parent {
|
||||
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 {
|
||||
range: expr.syntax().text_range(),
|
||||
kind: InlayKind::ImplicitReborrowHint,
|
||||
label: label.to_string().into(),
|
||||
tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())),
|
||||
});
|
||||
if needs_parens {
|
||||
acc.push(InlayHint {
|
||||
range: expr.syntax().text_range(),
|
||||
kind: InlayKind::AdjustmentHint,
|
||||
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(())
|
||||
}
|
||||
|
||||
|
@ -1213,12 +1289,11 @@ fn get_callable(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{expect, Expect};
|
||||
use ide_db::base_db::FileRange;
|
||||
use itertools::Itertools;
|
||||
use syntax::{TextRange, TextSize};
|
||||
use test_utils::extract_annotations;
|
||||
|
||||
use crate::inlay_hints::ReborrowHints;
|
||||
use crate::inlay_hints::AdjustmentHints;
|
||||
use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
|
||||
|
||||
use super::ClosureReturnTypeHints;
|
||||
|
@ -1230,7 +1305,7 @@ mod tests {
|
|||
chaining_hints: false,
|
||||
lifetime_elision_hints: LifetimeElisionHints::Never,
|
||||
closure_return_type_hints: ClosureReturnTypeHints::Never,
|
||||
reborrow_hints: ReborrowHints::Always,
|
||||
adjustment_hints: AdjustmentHints::Never,
|
||||
binding_mode_hints: false,
|
||||
hide_named_constructor_hints: false,
|
||||
hide_closure_initialization_hints: false,
|
||||
|
@ -1242,7 +1317,6 @@ mod tests {
|
|||
type_hints: true,
|
||||
parameter_hints: true,
|
||||
chaining_hints: true,
|
||||
reborrow_hints: ReborrowHints::Always,
|
||||
closure_return_type_hints: ClosureReturnTypeHints::WithBlock,
|
||||
binding_mode_hints: true,
|
||||
lifetime_elision_hints: LifetimeElisionHints::Always,
|
||||
|
@ -1838,10 +1912,7 @@ fn main() {
|
|||
.inlay_hints(
|
||||
&InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
|
||||
file_id,
|
||||
Some(FileRange {
|
||||
file_id,
|
||||
range: TextRange::new(TextSize::from(500), TextSize::from(600)),
|
||||
}),
|
||||
Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
|
||||
)
|
||||
.unwrap();
|
||||
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]
|
||||
fn hints_binding_modes() {
|
||||
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},
|
||||
hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
|
||||
inlay_hints::{
|
||||
ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
|
||||
InlayTooltip, LifetimeElisionHints, ReborrowHints,
|
||||
AdjustmentHints, ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig,
|
||||
InlayKind, InlayTooltip, LifetimeElisionHints,
|
||||
},
|
||||
join_lines::JoinLinesConfig,
|
||||
markup::Markup,
|
||||
|
@ -367,7 +367,7 @@ impl Analysis {
|
|||
&self,
|
||||
config: &InlayHintsConfig,
|
||||
file_id: FileId,
|
||||
range: Option<FileRange>,
|
||||
range: Option<TextRange>,
|
||||
) -> Cancellable<Vec<InlayHint>> {
|
||||
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)
|
||||
//! for LSIF and LSP.
|
||||
|
||||
use hir::{db::DefDatabase, AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
|
||||
use hir::{AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
|
||||
use ide_db::{
|
||||
base_db::{CrateOrigin, FileId, FileLoader, FilePosition, LangCrateOrigin},
|
||||
base_db::{CrateOrigin, FilePosition, LangCrateOrigin},
|
||||
defs::{Definition, IdentClass},
|
||||
helpers::pick_best_token,
|
||||
RootDatabase,
|
||||
|
@ -11,7 +11,7 @@ use ide_db::{
|
|||
use itertools::Itertools;
|
||||
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)]
|
||||
pub enum MonikerDescriptorKind {
|
||||
|
@ -77,25 +77,13 @@ pub struct PackageInformation {
|
|||
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(
|
||||
db: &RootDatabase,
|
||||
FilePosition { file_id, offset }: FilePosition,
|
||||
) -> Option<RangeInfo<Vec<MonikerResult>>> {
|
||||
let sema = &Semantics::new(db);
|
||||
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 {
|
||||
IDENT
|
||||
| INT_NUMBER
|
||||
|
|
|
@ -16,6 +16,7 @@ use ide_db::{
|
|||
search::{ReferenceCategory, SearchScope, UsageSearchResult},
|
||||
RootDatabase,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use stdx::hash::NoHashHashMap;
|
||||
use syntax::{
|
||||
algo::find_node_at_offset,
|
||||
|
@ -86,6 +87,7 @@ pub(crate) fn find_all_refs(
|
|||
file_id,
|
||||
refs.into_iter()
|
||||
.map(|file_ref| (file_ref.range, file_ref.category))
|
||||
.unique()
|
||||
.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::{
|
||||
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,
|
||||
TryToNav,
|
||||
};
|
||||
|
@ -99,7 +100,7 @@ fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
|
|||
|
||||
impl StaticIndex<'_> {
|
||||
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 inlay_hints = self
|
||||
.analysis
|
||||
|
@ -111,7 +112,7 @@ impl StaticIndex<'_> {
|
|||
chaining_hints: true,
|
||||
closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock,
|
||||
lifetime_elision_hints: crate::LifetimeElisionHints::Never,
|
||||
reborrow_hints: crate::ReborrowHints::Never,
|
||||
adjustment_hints: crate::AdjustmentHints::Never,
|
||||
hide_named_constructor_hints: false,
|
||||
hide_closure_initialization_hints: false,
|
||||
param_names_for_lifetime_elision_hints: false,
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[features]
|
||||
tracking = []
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -12,6 +12,9 @@ use tt::buffer::{Cursor, TokenBuffer};
|
|||
|
||||
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
|
||||
/// will consume).
|
||||
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>>,
|
||||
) -> (tt::Subtree, TokenMap, u32) {
|
||||
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);
|
||||
c.id_alloc.map.shrink_to_fit();
|
||||
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;
|
||||
}
|
||||
|
||||
let mut conv = RawConvertor {
|
||||
let mut conv = RawConverter {
|
||||
lexed,
|
||||
pos: 0,
|
||||
id_alloc: TokenIdAlloc {
|
||||
|
@ -148,7 +151,7 @@ pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec<tt::Subtree> {
|
|||
res
|
||||
}
|
||||
|
||||
fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
|
||||
fn convert_tokens<C: TokenConverter>(conv: &mut C) -> tt::Subtree {
|
||||
struct StackEntry {
|
||||
subtree: tt::Subtree,
|
||||
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)) {
|
||||
Some(kind) if !kind.is_trivia() => tt::Spacing::Joint,
|
||||
Some(kind) if is_single_token_op(kind) => tt::Spacing::Joint,
|
||||
_ => tt::Spacing::Alone,
|
||||
};
|
||||
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
|
||||
/// That is, strips leading `///` (or `/**`, etc)
|
||||
/// and strips the ending `*/`
|
||||
|
@ -425,8 +457,8 @@ impl TokenIdAlloc {
|
|||
}
|
||||
}
|
||||
|
||||
/// A raw token (straight from lexer) convertor
|
||||
struct RawConvertor<'a> {
|
||||
/// A raw token (straight from lexer) converter
|
||||
struct RawConverter<'a> {
|
||||
lexed: parser::LexedStr<'a>,
|
||||
pos: usize,
|
||||
id_alloc: TokenIdAlloc,
|
||||
|
@ -442,7 +474,7 @@ trait SrcToken<Ctx>: std::fmt::Debug {
|
|||
fn synthetic_id(&self, ctx: &Ctx) -> Option<SyntheticTokenId>;
|
||||
}
|
||||
|
||||
trait TokenConvertor: Sized {
|
||||
trait TokenConverter: Sized {
|
||||
type Token: SrcToken<Self>;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
impl<'a> SrcToken<RawConvertor<'a>> for usize {
|
||||
fn kind(&self, ctx: &RawConvertor<'a>) -> SyntaxKind {
|
||||
impl<'a> SrcToken<RawConverter<'a>> for usize {
|
||||
fn kind(&self, ctx: &RawConverter<'a>) -> SyntaxKind {
|
||||
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()
|
||||
}
|
||||
|
||||
fn to_text(&self, ctx: &RawConvertor<'_>) -> SmolStr {
|
||||
fn to_text(&self, ctx: &RawConverter<'_>) -> SmolStr {
|
||||
ctx.lexed.text(*self).into()
|
||||
}
|
||||
|
||||
fn synthetic_id(&self, _ctx: &RawConvertor<'a>) -> Option<SyntheticTokenId> {
|
||||
fn synthetic_id(&self, _ctx: &RawConverter<'a>) -> Option<SyntheticTokenId> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TokenConvertor for RawConvertor<'a> {
|
||||
impl<'a> TokenConverter for RawConverter<'a> {
|
||||
type Token = usize;
|
||||
|
||||
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,
|
||||
current: Option<SyntaxToken>,
|
||||
current_synthetic: Vec<SyntheticToken>,
|
||||
|
@ -515,7 +547,7 @@ struct Convertor {
|
|||
punct_offset: Option<(SyntaxToken, TextSize)>,
|
||||
}
|
||||
|
||||
impl Convertor {
|
||||
impl Converter {
|
||||
fn new(
|
||||
node: &SyntaxNode,
|
||||
global_offset: TextSize,
|
||||
|
@ -523,11 +555,11 @@ impl Convertor {
|
|||
next_id: u32,
|
||||
mut replace: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
|
||||
mut append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
|
||||
) -> Convertor {
|
||||
) -> Converter {
|
||||
let range = node.text_range();
|
||||
let mut preorder = node.preorder_with_tokens();
|
||||
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 } },
|
||||
current: first,
|
||||
current_synthetic: synthetic,
|
||||
|
@ -590,15 +622,15 @@ impl SynToken {
|
|||
}
|
||||
}
|
||||
|
||||
impl SrcToken<Convertor> for SynToken {
|
||||
fn kind(&self, _ctx: &Convertor) -> SyntaxKind {
|
||||
impl SrcToken<Converter> for SynToken {
|
||||
fn kind(&self, ctx: &Converter) -> SyntaxKind {
|
||||
match self {
|
||||
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,
|
||||
}
|
||||
}
|
||||
fn to_char(&self, _ctx: &Convertor) -> Option<char> {
|
||||
fn to_char(&self, _ctx: &Converter) -> Option<char> {
|
||||
match self {
|
||||
SynToken::Ordinary(_) => None,
|
||||
SynToken::Punch(it, i) => it.text().chars().nth((*i).into()),
|
||||
|
@ -606,7 +638,7 @@ impl SrcToken<Convertor> for SynToken {
|
|||
SynToken::Synthetic(_) => None,
|
||||
}
|
||||
}
|
||||
fn to_text(&self, _ctx: &Convertor) -> SmolStr {
|
||||
fn to_text(&self, _ctx: &Converter) -> SmolStr {
|
||||
match self {
|
||||
SynToken::Ordinary(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 {
|
||||
SynToken::Synthetic(token) => Some(token.id),
|
||||
_ => None,
|
||||
|
@ -622,7 +654,7 @@ impl SrcToken<Convertor> for SynToken {
|
|||
}
|
||||
}
|
||||
|
||||
impl TokenConvertor for Convertor {
|
||||
impl TokenConverter for Converter {
|
||||
type Token = SynToken;
|
||||
fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> {
|
||||
convert_doc_comment(token.token()?)
|
||||
|
@ -651,7 +683,7 @@ impl TokenConvertor for Convertor {
|
|||
}
|
||||
|
||||
let curr = self.current.clone()?;
|
||||
if !&self.range.contains_range(curr.text_range()) {
|
||||
if !self.range.contains_range(curr.text_range()) {
|
||||
return None;
|
||||
}
|
||||
let (new_current, new_synth) =
|
||||
|
@ -809,12 +841,15 @@ impl<'a> TtTreeSink<'a> {
|
|||
let next = last.bump();
|
||||
if let (
|
||||
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())
|
||||
{
|
||||
// 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.
|
||||
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.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"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[dependencies]
|
||||
proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" }
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -117,7 +117,7 @@ impl Abi {
|
|||
let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?;
|
||||
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 {
|
||||
Io(io::Error),
|
||||
LibLoading(libloading::Error),
|
||||
UnsupportedABI,
|
||||
UnsupportedABI(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for LoadProcMacroDylibError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,12 +113,12 @@ impl ProcMacroSrv {
|
|||
|
||||
fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> {
|
||||
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)) {
|
||||
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(),
|
||||
})
|
||||
|
|
|
@ -19,7 +19,7 @@ fn test_derive_error() {
|
|||
expect![[r##"
|
||||
SUBTREE $
|
||||
IDENT compile_error 4294967295
|
||||
PUNCH ! [joint] 4294967295
|
||||
PUNCH ! [alone] 4294967295
|
||||
SUBTREE () 4294967295
|
||||
LITERAL "#[derive(DeriveError)] struct S ;" 4294967295
|
||||
PUNCH ; [alone] 4294967295"##]],
|
||||
|
@ -109,7 +109,7 @@ fn test_fn_like_macro_clone_literals() {
|
|||
PUNCH , [alone] 4294967295
|
||||
LITERAL 2_u32 4294967295
|
||||
PUNCH , [alone] 4294967295
|
||||
PUNCH - [joint] 4294967295
|
||||
PUNCH - [alone] 4294967295
|
||||
LITERAL 4i64 4294967295
|
||||
PUNCH , [alone] 4294967295
|
||||
LITERAL 3.14f32 4294967295
|
||||
|
@ -130,7 +130,7 @@ fn test_attr_macro() {
|
|||
expect![[r##"
|
||||
SUBTREE $
|
||||
IDENT compile_error 4294967295
|
||||
PUNCH ! [joint] 4294967295
|
||||
PUNCH ! [alone] 4294967295
|
||||
SUBTREE () 4294967295
|
||||
LITERAL "#[attr_error(some arguments)] mod m {}" 4294967295
|
||||
PUNCH ; [alone] 4294967295"##]],
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "proc-macro-test"
|
|||
version = "0.0.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "proc-macro-test-impl"
|
|||
version = "0.0.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.0"
|
|||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -69,7 +69,7 @@ impl WorkspaceBuildScripts {
|
|||
cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);
|
||||
|
||||
// --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.
|
||||
cmd.arg("--all-targets");
|
||||
|
||||
|
|
|
@ -270,11 +270,7 @@ impl CargoWorkspace {
|
|||
config: &CargoConfig,
|
||||
progress: &dyn Fn(String),
|
||||
) -> Result<cargo_metadata::Metadata> {
|
||||
let target = config
|
||||
.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 targets = find_list_of_build_targets(config, cargo_toml);
|
||||
|
||||
let mut meta = MetadataCommand::new();
|
||||
meta.cargo_path(toolchain::cargo());
|
||||
|
@ -294,8 +290,12 @@ impl CargoWorkspace {
|
|||
}
|
||||
meta.current_dir(current_dir.as_os_str());
|
||||
|
||||
if let Some(target) = target {
|
||||
meta.other_options(vec![String::from("--filter-platform"), target]);
|
||||
if !targets.is_empty() {
|
||||
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
|
||||
|
@ -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(
|
||||
cargo_toml: &ManifestPath,
|
||||
extra_env: &FxHashMap<String, String>,
|
||||
|
@ -499,7 +512,7 @@ fn rustc_discover_host_triple(
|
|||
fn cargo_config_build_target(
|
||||
cargo_toml: &ManifestPath,
|
||||
extra_env: &FxHashMap<String, String>,
|
||||
) -> Option<String> {
|
||||
) -> Vec<String> {
|
||||
let mut cargo_config = Command::new(toolchain::cargo());
|
||||
cargo_config.envs(extra_env);
|
||||
cargo_config
|
||||
|
@ -507,12 +520,21 @@ fn cargo_config_build_target(
|
|||
.args(&["-Z", "unstable-options", "config", "get", "build.target"])
|
||||
.env("RUSTC_BOOTSTRAP", "1");
|
||||
// if successful we receive `build.target = "target-triple"`
|
||||
// or `build.target = ["<target 1>", ..]`
|
||||
tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
|
||||
match utf8_stdout(cargo_config) {
|
||||
Ok(stdout) => stdout
|
||||
.strip_prefix("build.target = \"")
|
||||
.and_then(|stdout| stdout.strip_suffix('"'))
|
||||
.map(ToOwned::to_owned),
|
||||
Err(_) => None,
|
||||
}
|
||||
utf8_stdout(cargo_config).map(parse_output_cargo_config_build_target).unwrap_or_default()
|
||||
}
|
||||
|
||||
fn parse_output_cargo_config_build_target(stdout: String) -> Vec<String> {
|
||||
let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
|
||||
|
||||
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(core) = sysroot.by_name("core") {
|
||||
sysroot.crates[alloc].deps.push(core);
|
||||
for dep in ALLOC_DEPS.trim().lines() {
|
||||
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(std) = sysroot.by_name("std") {
|
||||
sysroot.crates[proc_macro].deps.push(std);
|
||||
for dep in PROC_MACRO_DEPS.trim().lines() {
|
||||
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 = "
|
||||
alloc
|
||||
backtrace
|
||||
core
|
||||
panic_abort
|
||||
panic_unwind
|
||||
|
@ -246,17 +251,19 @@ proc_macro
|
|||
profiler_builtins
|
||||
std
|
||||
stdarch/crates/std_detect
|
||||
term
|
||||
test
|
||||
unwind";
|
||||
|
||||
const ALLOC_DEPS: &str = "core";
|
||||
|
||||
const STD_DEPS: &str = "
|
||||
alloc
|
||||
core
|
||||
panic_abort
|
||||
panic_unwind
|
||||
panic_abort
|
||||
core
|
||||
profiler_builtins
|
||||
unwind
|
||||
std_detect
|
||||
term
|
||||
test
|
||||
unwind";
|
||||
test";
|
||||
|
||||
const PROC_MACRO_DEPS: &str = "std";
|
||||
|
|
|
@ -1566,10 +1566,10 @@ fn rust_project_hello_world_project_model() {
|
|||
},
|
||||
Dependency {
|
||||
crate_id: CrateId(
|
||||
1,
|
||||
3,
|
||||
),
|
||||
name: CrateName(
|
||||
"core",
|
||||
"panic_unwind",
|
||||
),
|
||||
prelude: true,
|
||||
},
|
||||
|
@ -1584,10 +1584,10 @@ fn rust_project_hello_world_project_model() {
|
|||
},
|
||||
Dependency {
|
||||
crate_id: CrateId(
|
||||
3,
|
||||
1,
|
||||
),
|
||||
name: CrateName(
|
||||
"panic_unwind",
|
||||
"core",
|
||||
),
|
||||
prelude: true,
|
||||
},
|
||||
|
@ -1600,6 +1600,15 @@ fn rust_project_hello_world_project_model() {
|
|||
),
|
||||
prelude: true,
|
||||
},
|
||||
Dependency {
|
||||
crate_id: CrateId(
|
||||
9,
|
||||
),
|
||||
name: CrateName(
|
||||
"unwind",
|
||||
),
|
||||
prelude: true,
|
||||
},
|
||||
Dependency {
|
||||
crate_id: CrateId(
|
||||
7,
|
||||
|
@ -1613,29 +1622,11 @@ fn rust_project_hello_world_project_model() {
|
|||
crate_id: CrateId(
|
||||
8,
|
||||
),
|
||||
name: CrateName(
|
||||
"term",
|
||||
),
|
||||
prelude: true,
|
||||
},
|
||||
Dependency {
|
||||
crate_id: CrateId(
|
||||
9,
|
||||
),
|
||||
name: CrateName(
|
||||
"test",
|
||||
),
|
||||
prelude: true,
|
||||
},
|
||||
Dependency {
|
||||
crate_id: CrateId(
|
||||
10,
|
||||
),
|
||||
name: CrateName(
|
||||
"unwind",
|
||||
),
|
||||
prelude: true,
|
||||
},
|
||||
],
|
||||
proc_macro: Err(
|
||||
"no proc macro loaded for sysroot crate",
|
||||
|
@ -1687,40 +1678,6 @@ fn rust_project_hello_world_project_model() {
|
|||
),
|
||||
edition: Edition2018,
|
||||
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(
|
||||
CrateDisplayName {
|
||||
crate_name: CrateName(
|
||||
|
@ -1748,10 +1705,10 @@ fn rust_project_hello_world_project_model() {
|
|||
is_proc_macro: false,
|
||||
},
|
||||
CrateId(
|
||||
10,
|
||||
9,
|
||||
): CrateData {
|
||||
root_file_id: FileId(
|
||||
11,
|
||||
10,
|
||||
),
|
||||
edition: Edition2018,
|
||||
version: None,
|
||||
|
@ -1782,10 +1739,10 @@ fn rust_project_hello_world_project_model() {
|
|||
is_proc_macro: false,
|
||||
},
|
||||
CrateId(
|
||||
11,
|
||||
10,
|
||||
): CrateData {
|
||||
root_file_id: FileId(
|
||||
12,
|
||||
11,
|
||||
),
|
||||
edition: Edition2018,
|
||||
version: None,
|
||||
|
@ -1836,7 +1793,7 @@ fn rust_project_hello_world_project_model() {
|
|||
},
|
||||
Dependency {
|
||||
crate_id: CrateId(
|
||||
9,
|
||||
8,
|
||||
),
|
||||
name: CrateName(
|
||||
"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`
|
||||
/// The return type contains the path and whether or not
|
||||
/// the root is a member of the current workspace
|
||||
|
@ -509,14 +524,14 @@ impl ProjectWorkspace {
|
|||
build_scripts,
|
||||
toolchain: _,
|
||||
} => cargo_to_crate_graph(
|
||||
rustc_cfg.clone(),
|
||||
cfg_overrides,
|
||||
load_proc_macro,
|
||||
load,
|
||||
cargo,
|
||||
build_scripts,
|
||||
sysroot.as_ref(),
|
||||
rustc,
|
||||
cargo,
|
||||
sysroot.as_ref(),
|
||||
rustc_cfg.clone(),
|
||||
cfg_overrides,
|
||||
build_scripts,
|
||||
),
|
||||
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
|
||||
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() {
|
||||
if let Some(&from) = crates.get(&from) {
|
||||
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 let Some(proc_macro) = libproc_macro {
|
||||
add_dep(
|
||||
|
@ -626,14 +641,14 @@ fn project_json_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: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
|
||||
cargo: &CargoWorkspace,
|
||||
build_scripts: &WorkspaceBuildScripts,
|
||||
sysroot: Option<&Sysroot>,
|
||||
rustc: &Option<CargoWorkspace>,
|
||||
cargo: &CargoWorkspace,
|
||||
sysroot: Option<&Sysroot>,
|
||||
rustc_cfg: Vec<CfgFlag>,
|
||||
override_cfg: &CfgOverrides,
|
||||
build_scripts: &WorkspaceBuildScripts,
|
||||
) -> CrateGraph {
|
||||
let _p = profile::span("cargo_to_crate_graph");
|
||||
let mut crate_graph = CrateGraph::default();
|
||||
|
@ -642,13 +657,15 @@ fn cargo_to_crate_graph(
|
|||
None => (SysrootPublicDeps::default(), None),
|
||||
};
|
||||
|
||||
let mut cfg_options = CfgOptions::default();
|
||||
cfg_options.extend(rustc_cfg);
|
||||
let cfg_options = {
|
||||
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();
|
||||
|
||||
cfg_options.insert_atom("debug_assertions".into());
|
||||
|
||||
let mut pkg_crates = FxHashMap::default();
|
||||
// Does any crate signal to rust-analyzer that they need the rustc_private crates?
|
||||
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
|
||||
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.
|
||||
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 to != from && kind != TargetKind::BuildScript {
|
||||
|
@ -767,15 +784,16 @@ fn cargo_to_crate_graph(
|
|||
if let Some(rustc_workspace) = rustc {
|
||||
handle_rustc_crates(
|
||||
&mut crate_graph,
|
||||
rustc_workspace,
|
||||
&mut pkg_to_lib_crate,
|
||||
load,
|
||||
load_proc_macro,
|
||||
rustc_workspace,
|
||||
cargo,
|
||||
&public_deps,
|
||||
libproc_macro,
|
||||
&pkg_crates,
|
||||
&cfg_options,
|
||||
override_cfg,
|
||||
load_proc_macro,
|
||||
&mut pkg_to_lib_crate,
|
||||
&public_deps,
|
||||
cargo,
|
||||
&pkg_crates,
|
||||
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
|
||||
}
|
||||
|
||||
fn handle_rustc_crates(
|
||||
crate_graph: &mut CrateGraph,
|
||||
rustc_workspace: &CargoWorkspace,
|
||||
pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
|
||||
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,
|
||||
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,
|
||||
) {
|
||||
let mut rustc_pkg_crates = FxHashMap::default();
|
||||
// The root package of the rustc-dev component is rustc_driver, so we match that
|
||||
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
|
||||
// installed for the current toolchain) and `rustc_source` is set to discover.
|
||||
if let Some(root_pkg) = root_pkg {
|
||||
|
@ -901,7 +920,16 @@ fn handle_rustc_crates(
|
|||
);
|
||||
pkg_to_lib_crate.insert(pkg, crate_id);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
@ -1009,7 +1037,7 @@ struct SysrootPublicDeps {
|
|||
|
||||
impl SysrootPublicDeps {
|
||||
/// 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 {
|
||||
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"
|
||||
autobins = false
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.65"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
use std::mem;
|
||||
|
||||
use cfg::{CfgAtom, CfgExpr};
|
||||
use ide::{FileId, RunnableKind, TestId};
|
||||
use ide::{Cancellable, FileId, RunnableKind, TestId};
|
||||
use project_model::{self, CargoFeatures, ManifestPath, TargetKind};
|
||||
use vfs::AbsPathBuf;
|
||||
|
||||
use crate::{global_state::GlobalStateSnapshot, Result};
|
||||
use crate::global_state::GlobalStateSnapshot;
|
||||
|
||||
/// Abstract representation of Cargo target.
|
||||
///
|
||||
|
@ -29,7 +29,7 @@ impl CargoTargetSpec {
|
|||
spec: Option<CargoTargetSpec>,
|
||||
kind: &RunnableKind,
|
||||
cfg: &Option<CfgExpr>,
|
||||
) -> Result<(Vec<String>, Vec<String>)> {
|
||||
) -> (Vec<String>, Vec<String>) {
|
||||
let mut 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(
|
||||
global_state_snapshot: &GlobalStateSnapshot,
|
||||
file_id: FileId,
|
||||
) -> Result<Option<CargoTargetSpec>> {
|
||||
) -> Cancellable<Option<CargoTargetSpec>> {
|
||||
let crate_id = match &*global_state_snapshot.analysis.crates_for(file_id)? {
|
||||
&[crate_id, ..] => crate_id,
|
||||
_ => return Ok(None),
|
||||
|
|
|
@ -60,24 +60,12 @@ pub fn load_workspace(
|
|||
};
|
||||
|
||||
let proc_macro_client = if load_config.with_proc_macro {
|
||||
let mut path = AbsPathBuf::assert(std::env::current_exe()?);
|
||||
let mut args = vec!["proc-macro"];
|
||||
let (server_path, args): (_, &[_]) = match ws.find_sysroot_proc_macro_srv() {
|
||||
Some(server_path) => (server_path, &[]),
|
||||
None => (AbsPathBuf::assert(std::env::current_exe()?), &["proc-macro"]),
|
||||
};
|
||||
|
||||
if let ProjectWorkspace::Cargo { sysroot, .. } | ProjectWorkspace::Json { sysroot, .. } =
|
||||
&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())
|
||||
ProcMacroServer::spawn(server_path, args).map_err(|e| e.to_string())
|
||||
} else {
|
||||
Err("proc macro server disabled".to_owned())
|
||||
};
|
||||
|
|
|
@ -47,30 +47,27 @@ impl flags::Scip {
|
|||
|
||||
let si = StaticIndex::compute(&analysis);
|
||||
|
||||
let mut index = scip_types::Index {
|
||||
metadata: Some(scip_types::Metadata {
|
||||
version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
|
||||
tool_info: Some(scip_types::ToolInfo {
|
||||
name: "rust-analyzer".to_owned(),
|
||||
version: "0.1".to_owned(),
|
||||
arguments: vec![],
|
||||
..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()
|
||||
let metadata = scip_types::Metadata {
|
||||
version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
|
||||
tool_info: Some(scip_types::ToolInfo {
|
||||
name: "rust-analyzer".to_owned(),
|
||||
version: "0.1".to_owned(),
|
||||
arguments: vec![],
|
||||
special_fields: Default::default(),
|
||||
})
|
||||
.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 tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();
|
||||
|
@ -95,18 +92,14 @@ impl flags::Scip {
|
|||
endings: LineEndings::Unix,
|
||||
};
|
||||
|
||||
let mut doc = scip_types::Document {
|
||||
relative_path,
|
||||
language: "rust".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut occurrences = Vec::new();
|
||||
let mut symbols = Vec::new();
|
||||
|
||||
tokens.into_iter().for_each(|(range, id)| {
|
||||
tokens.into_iter().for_each(|(text_range, id)| {
|
||||
let token = si.tokens.get(id).unwrap();
|
||||
|
||||
let mut occurrence = scip_types::Occurrence::default();
|
||||
occurrence.range = text_range_to_scip_range(&line_index, range);
|
||||
occurrence.symbol = tokens_to_symbol
|
||||
let range = text_range_to_scip_range(&line_index, text_range);
|
||||
let symbol = tokens_to_symbol
|
||||
.entry(id)
|
||||
.or_insert_with(|| {
|
||||
let symbol = token_to_symbol(&token).unwrap_or_else(&mut new_local_symbol);
|
||||
|
@ -114,34 +107,62 @@ impl flags::Scip {
|
|||
})
|
||||
.clone();
|
||||
|
||||
let mut symbol_roles = Default::default();
|
||||
|
||||
if let Some(def) = token.definition {
|
||||
if def.range == range {
|
||||
occurrence.symbol_roles |= scip_types::SymbolRole::Definition as i32;
|
||||
if def.range == text_range {
|
||||
symbol_roles |= scip_types::SymbolRole::Definition as i32;
|
||||
}
|
||||
|
||||
if symbols_emitted.insert(id) {
|
||||
let mut symbol_info = scip_types::SymbolInformation::default();
|
||||
symbol_info.symbol = occurrence.symbol.clone();
|
||||
if let Some(hover) = &token.hover {
|
||||
if !hover.markup.as_str().is_empty() {
|
||||
symbol_info.documentation = vec![hover.markup.as_str().to_string()];
|
||||
}
|
||||
}
|
||||
let documentation = token
|
||||
.hover
|
||||
.as_ref()
|
||||
.map(|hover| hover.markup.as_str())
|
||||
.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;
|
||||
}
|
||||
|
||||
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)
|
||||
.map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?;
|
||||
|
||||
|
@ -181,7 +202,7 @@ fn new_descriptor_str(
|
|||
name: name.to_string(),
|
||||
disambiguator: "".to_string(),
|
||||
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(),
|
||||
name: package_name,
|
||||
version: version.unwrap_or_else(|| ".".to_string()),
|
||||
..Default::default()
|
||||
special_fields: Default::default(),
|
||||
})
|
||||
.into(),
|
||||
descriptors,
|
||||
..Default::default()
|
||||
special_fields: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -118,9 +118,11 @@ config_data! {
|
|||
/// This option does not take effect until rust-analyzer is restarted.
|
||||
cargo_sysroot: Option<String> = "\"discover\"",
|
||||
/// 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",
|
||||
/// Unsets `#[cfg(test)]` for the specified crates.
|
||||
cargo_unsetTest: Vec<String> = "[\"core\"]",
|
||||
cargo_unsetTest: Vec<String> = "[\"core\"]",
|
||||
|
||||
/// Check all targets and tests (`--all-targets`).
|
||||
checkOnSave_allTargets: bool = "true",
|
||||
|
@ -157,7 +159,7 @@ config_data! {
|
|||
checkOnSave_noDefaultFeatures: Option<bool> = "null",
|
||||
/// Override the command rust-analyzer uses instead of `cargo check` for
|
||||
/// 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
|
||||
/// Cargo, you might also want to change
|
||||
|
@ -174,9 +176,13 @@ config_data! {
|
|||
/// ```
|
||||
/// .
|
||||
checkOnSave_overrideCommand: Option<Vec<String>> = "null",
|
||||
/// Check for a specific target. Defaults to
|
||||
/// `#rust-analyzer.cargo.target#`.
|
||||
checkOnSave_target: Option<String> = "null",
|
||||
/// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
|
||||
///
|
||||
/// 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.
|
||||
/// 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> = "[]",
|
||||
/// Controls file watching implementation.
|
||||
files_watcher: FilesWatcherDef = "\"client\"",
|
||||
|
||||
/// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
|
||||
highlightRelated_breakPoints_enable: bool = "true",
|
||||
/// 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",
|
||||
/// Whether to show inlay type hints for return types of closures.
|
||||
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.
|
||||
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
|
||||
/// 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
|
||||
/// site.
|
||||
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\"",
|
||||
/// Whether to render leading colons for type hints, and trailing colons for parameter hints.
|
||||
inlayHints_renderColons: bool = "true",
|
||||
|
@ -1143,11 +1153,10 @@ impl Config {
|
|||
}
|
||||
Some(_) | None => FlycheckConfig::CargoCommand {
|
||||
command: self.data.checkOnSave_command.clone(),
|
||||
target_triple: self
|
||||
.data
|
||||
.checkOnSave_target
|
||||
.clone()
|
||||
.or_else(|| self.data.cargo_target.clone()),
|
||||
target_triples: match &self.data.checkOnSave_target.0[..] {
|
||||
[] => self.data.cargo_target.clone().into_iter().collect(),
|
||||
targets => targets.into(),
|
||||
},
|
||||
all_targets: self.data.checkOnSave_allTargets,
|
||||
no_default_features: self
|
||||
.data
|
||||
|
@ -1200,10 +1209,15 @@ impl Config {
|
|||
hide_closure_initialization_hints: self
|
||||
.data
|
||||
.inlayHints_typeHints_hideClosureInitialization,
|
||||
reborrow_hints: match self.data.inlayHints_reborrowHints_enable {
|
||||
ReborrowHintsDef::Always => ide::ReborrowHints::Always,
|
||||
ReborrowHintsDef::Never => ide::ReborrowHints::Never,
|
||||
ReborrowHintsDef::Mutable => ide::ReborrowHints::MutableOnly,
|
||||
adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable {
|
||||
AdjustmentHintsDef::Always => ide::AdjustmentHints::Always,
|
||||
AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable {
|
||||
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,
|
||||
param_names_for_lifetime_elision_hints: self
|
||||
|
@ -1538,6 +1552,7 @@ mod de_unit_v {
|
|||
named_unit_variant!(all);
|
||||
named_unit_variant!(skip_trivial);
|
||||
named_unit_variant!(mutable);
|
||||
named_unit_variant!(reborrow);
|
||||
named_unit_variant!(with_block);
|
||||
}
|
||||
|
||||
|
@ -1647,6 +1662,9 @@ enum InvocationStrategy {
|
|||
PerWorkspace,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
struct CheckOnSaveTargets(#[serde(deserialize_with = "single_or_array")] Vec<String>);
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum InvocationLocation {
|
||||
|
@ -1687,6 +1705,17 @@ enum ReborrowHintsDef {
|
|||
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)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum FilesWatcherDef {
|
||||
|
@ -1996,6 +2025,19 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
|
|||
"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! {
|
||||
"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."
|
||||
],
|
||||
},
|
||||
"CheckOnSaveTargets" => set! {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
],
|
||||
},
|
||||
_ => panic!("missing entry for {}: {}", ty, default),
|
||||
}
|
||||
|
||||
|
|
|
@ -359,14 +359,15 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
|||
.iter()
|
||||
.flat_map(|primary_span| {
|
||||
let primary_location = primary_location(config, workspace_root, primary_span, snap);
|
||||
|
||||
let mut message = message.clone();
|
||||
if needs_primary_span_label {
|
||||
if let Some(primary_span_label) = &primary_span.label {
|
||||
format_to!(message, "\n{}", primary_span_label);
|
||||
let message = {
|
||||
let mut message = message.clone();
|
||||
if needs_primary_span_label {
|
||||
if let Some(primary_span_label) = &primary_span.label {
|
||||
format_to!(message, "\n{}", primary_span_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message
|
||||
};
|
||||
// Each primary diagnostic span may result in multiple LSP diagnostics.
|
||||
let mut diagnostics = Vec::new();
|
||||
|
||||
|
@ -417,7 +418,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
|||
message: message.clone(),
|
||||
related_information: Some(information_for_additional_diagnostic),
|
||||
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
||||
data: None,
|
||||
data: Some(serde_json::json!({ "rendered": rd.rendered })),
|
||||
};
|
||||
diagnostics.push(MappedRustDiagnostic {
|
||||
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()) },
|
||||
data: None,
|
||||
data: Some(serde_json::json!({ "rendered": rd.rendered })),
|
||||
},
|
||||
fix: None,
|
||||
});
|
||||
|
@ -534,7 +535,8 @@ mod tests {
|
|||
Config::new(workspace_root.to_path_buf(), ClientCapabilities::default()),
|
||||
);
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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> {
|
||||
let start = offset(line_index, range.start)?;
|
||||
let end = offset(line_index, range.end)?;
|
||||
let text_range = TextRange::new(start, end);
|
||||
Ok(text_range)
|
||||
match end < start {
|
||||
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> {
|
||||
|
|
|
@ -100,7 +100,7 @@ pub(crate) struct GlobalState {
|
|||
/// the user just adds comments or whitespace to Cargo.toml, we do not want
|
||||
/// to invalidate any salsa caches.
|
||||
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:
|
||||
OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ use std::{
|
|||
|
||||
use anyhow::Context;
|
||||
use ide::{
|
||||
AnnotationConfig, AssistKind, AssistResolveStrategy, FileId, FilePosition, FileRange,
|
||||
HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
|
||||
SingleResolve, SourceChange, TextEdit,
|
||||
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FileId, FilePosition,
|
||||
FileRange, HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable,
|
||||
RunnableKind, SingleResolve, SourceChange, TextEdit,
|
||||
};
|
||||
use ide_db::SymbolKind;
|
||||
use lsp_server::ErrorCode;
|
||||
|
@ -556,7 +556,7 @@ pub(crate) fn handle_will_rename_files(
|
|||
if source_change.source_file_edits.is_empty() {
|
||||
Ok(None)
|
||||
} 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,
|
||||
selections,
|
||||
)??;
|
||||
to_proto::workspace_edit(&snap, source_change)
|
||||
to_proto::workspace_edit(&snap, source_change).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn publish_diagnostics(
|
||||
|
@ -1354,13 +1354,12 @@ pub(crate) fn handle_inlay_hints(
|
|||
) -> Result<Option<Vec<InlayHint>>> {
|
||||
let _p = profile::span("handle_inlay_hints");
|
||||
let document_uri = ¶ms.text_document.uri;
|
||||
let file_id = from_proto::file_id(&snap, document_uri)?;
|
||||
let line_index = snap.file_line_index(file_id)?;
|
||||
let range = from_proto::file_range(
|
||||
let FileRange { file_id, range } = from_proto::file_range(
|
||||
&snap,
|
||||
TextDocumentIdentifier::new(document_uri.to_owned()),
|
||||
params.range,
|
||||
)?;
|
||||
let line_index = snap.file_line_index(file_id)?;
|
||||
let inlay_hints_config = snap.config.inlay_hints();
|
||||
Ok(Some(
|
||||
snap.analysis
|
||||
|
@ -1369,7 +1368,7 @@ pub(crate) fn handle_inlay_hints(
|
|||
.map(|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()
|
||||
.filter(|it| it.kind == Some(SymbolKind::Function))
|
||||
.map(|it| to_proto::call_hierarchy_item(&snap, it))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
.collect::<Cancellable<Vec<_>>>()?;
|
||||
|
||||
Ok(Some(res))
|
||||
}
|
||||
|
|
|
@ -27,10 +27,6 @@ pub(crate) enum LineEndings {
|
|||
impl LineEndings {
|
||||
/// Replaces `\r\n` with `\n` in-place in `src`.
|
||||
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.
|
||||
// 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
|
||||
|
@ -39,10 +35,19 @@ impl LineEndings {
|
|||
let mut buf = src.into_bytes();
|
||||
let mut gap_len = 0;
|
||||
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 {
|
||||
let idx = match find_crlf(&tail[gap_len..]) {
|
||||
None => tail.len(),
|
||||
Some(idx) => idx + gap_len,
|
||||
None if crlf_seen => tail.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 = &mut tail[idx - gap_len..];
|
||||
|
@ -54,15 +59,48 @@ impl LineEndings {
|
|||
|
||||
// Account for removed `\r`.
|
||||
// After `set_len`, `buf` is guaranteed to contain utf-8 again.
|
||||
let new_len = buf.len() - gap_len;
|
||||
let src = unsafe {
|
||||
let new_len = buf.len() - gap_len;
|
||||
buf.set_len(new_len);
|
||||
String::from_utf8_unchecked(buf)
|
||||
};
|
||||
return (src, LineEndings::Dos);
|
||||
|
||||
fn find_crlf(src: &[u8]) -> Option<usize> {
|
||||
src.windows(2).position(|it| it == b"\r\n")
|
||||
}
|
||||
(src, LineEndings::Dos)
|
||||
}
|
||||
}
|
||||
|
||||
#[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.
|
||||
use std::{ops::Range, sync::Arc};
|
||||
use std::{mem, ops::Range, sync::Arc};
|
||||
|
||||
use lsp_server::Notification;
|
||||
|
||||
|
@ -133,11 +133,37 @@ impl GlobalState {
|
|||
}
|
||||
|
||||
pub(crate) fn apply_document_changes(
|
||||
old_text: &mut String,
|
||||
content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,
|
||||
) {
|
||||
file_contents: impl FnOnce() -> String,
|
||||
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 {
|
||||
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.
|
||||
endings: LineEndings::Unix,
|
||||
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
|
||||
// remember the last valid line in the index and only rebuild it if needed.
|
||||
// The VFS will normalize the end of lines to `\n`.
|
||||
enum IndexValid {
|
||||
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;
|
||||
let mut index_valid = !0u32;
|
||||
for change in content_changes {
|
||||
match change.range {
|
||||
Some(range) => {
|
||||
if !index_valid.covers(range.end.line) {
|
||||
line_index.index = Arc::new(ide::LineIndex::new(old_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);
|
||||
}
|
||||
// The None case can't happen as we have handled it above already
|
||||
if let Some(range) = change.range {
|
||||
if index_valid <= range.end.line {
|
||||
*Arc::make_mut(&mut line_index.index) = ide::LineIndex::new(&text);
|
||||
}
|
||||
None => {
|
||||
*old_text = change.text;
|
||||
index_valid = IndexValid::UpToLineExclusive(0);
|
||||
index_valid = range.start.line;
|
||||
if let Ok(range) = from_proto::text_range(&line_index, range) {
|
||||
text.replace_range(Range::<usize>::from(range), &change.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
text
|
||||
}
|
||||
|
||||
/// 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();
|
||||
apply_document_changes(&mut text, vec![]);
|
||||
let text = apply_document_changes(|| String::new(), vec![]);
|
||||
assert_eq!(text, "");
|
||||
apply_document_changes(
|
||||
&mut text,
|
||||
let text = apply_document_changes(
|
||||
|| text,
|
||||
vec![TextDocumentContentChangeEvent {
|
||||
range: None,
|
||||
range_length: None,
|
||||
|
@ -254,39 +261,39 @@ mod tests {
|
|||
}],
|
||||
);
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
apply_document_changes(
|
||||
&mut text,
|
||||
let text = apply_document_changes(
|
||||
|| text,
|
||||
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");
|
||||
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");
|
||||
apply_document_changes(
|
||||
&mut text,
|
||||
let text = apply_document_changes(
|
||||
|| text,
|
||||
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");
|
||||
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");
|
||||
|
||||
text = String::from("❤️");
|
||||
apply_document_changes(&mut text, c![0, 0; 0, 0 => "a"]);
|
||||
let text = String::from("❤️");
|
||||
let text = apply_document_changes(|| text, c![0, 0; 0, 0 => "a"]);
|
||||
assert_eq!(text, "a❤️");
|
||||
|
||||
text = String::from("a\nb");
|
||||
apply_document_changes(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
|
||||
let text = String::from("a\nb");
|
||||
let text = apply_document_changes(|| text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
|
||||
assert_eq!(text, "adcb");
|
||||
|
||||
text = String::from("a\nb");
|
||||
apply_document_changes(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
|
||||
let text = String::from("a\nb");
|
||||
let text = apply_document_changes(|| text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
|
||||
assert_eq!(text, "ațc\ncb");
|
||||
}
|
||||
|
||||
|
|
|
@ -451,7 +451,7 @@ impl GlobalState {
|
|||
ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
|
||||
ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
|
||||
ProjectWorkspaceProgress::End(workspaces) => {
|
||||
self.fetch_workspaces_queue.op_completed(workspaces);
|
||||
self.fetch_workspaces_queue.op_completed(Some(workspaces));
|
||||
|
||||
let old = Arc::clone(&self.workspaces);
|
||||
self.switch_workspaces("fetched workspace".to_string());
|
||||
|
@ -759,8 +759,10 @@ impl GlobalState {
|
|||
|
||||
let vfs = &mut this.vfs.write().0;
|
||||
let file_id = vfs.file_id(&path).unwrap();
|
||||
let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
|
||||
apply_document_changes(&mut text, params.content_changes);
|
||||
let text = apply_document_changes(
|
||||
|| std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
|
||||
params.content_changes,
|
||||
);
|
||||
|
||||
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