summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates')
-rw-r--r--src/tools/rust-analyzer/crates/base-db/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/cfg/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/flycheck/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/crates/flycheck/src/lib.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs33
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/pretty.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs96
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs108
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs143
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs86
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs157
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tls.rs28
-rw-r--r--src/tools/rust-analyzer/crates/hir/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs39
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs37
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs81
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs413
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs70
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs318
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs31
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs90
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs29
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs69
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs44
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs159
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs29
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs32
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/line_index.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs70
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs60
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs88
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs243
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs48
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/rename.rs35
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs58
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs7
-rw-r--r--src/tools/rust-analyzer/crates/limit/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/mbe/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs89
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs93
-rw-r--r--src/tools/rust-analyzer/crates/parser/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/paths/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs6
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-test/imp/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/profile/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs50
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs27
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs71
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs84
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs14
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs22
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs159
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs95
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs38
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs19
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs64
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs11
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs115
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs54
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs64
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs68
-rw-r--r--src/tools/rust-analyzer/crates/sourcegen/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/stdx/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/fuzz/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram9
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs56
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs20
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs46
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/validation.rs6
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/text-edit/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/toolchain/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/tt/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/vfs/Cargo.toml2
180 files changed, 3603 insertions, 1413 deletions
diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
index f02a51ab6..a484ecec6 100644
--- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml
index ee1ad677a..2857420c2 100644
--- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
index 2ad32d248..514d567fc 100644
--- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
@@ -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" }
diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
index 73c3a48b4..8f93dad06 100644
--- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
@@ -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,
@@ -295,9 +296,11 @@ impl FlycheckActor {
} => {
let mut cmd = Command::new(toolchain::cargo());
cmd.arg(command);
- cmd.args(&["--workspace", "--message-format=json"]);
+ cmd.current_dir(&self.root);
+ 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 {
@@ -357,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>,
@@ -369,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);
@@ -384,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(())
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index 4ad8e7597..22f98ea7c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index 2dc69b00a..9c7696908 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -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,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 570344596..0aa531eff 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -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>,
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index 077a1b619..b25274bcc 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -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)))
}
@@ -662,8 +654,12 @@ fn desugar_future_path(orig: TypeRef) -> Path {
let mut generic_args: Vec<_> =
std::iter::repeat(None).take(path.segments().len() - 1).collect();
let mut last = GenericArgs::empty();
- let binding =
- AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() };
+ let binding = AssociatedTypeBinding {
+ name: name![Output],
+ args: None,
+ type_ref: Some(orig),
+ bounds: Vec::new(),
+ };
last.bindings.push(binding);
generic_args.push(Some(Interned::new(last)));
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index da1643152..48c40df22 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -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) => {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
index bc162d0fa..fc90c6e9f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
@@ -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!(.. .. .. |);
"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index 029821e5e..118c14ed8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -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
-}"##]],
+}"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index 9ffc21881..b0dd01f9d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -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
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
index 8dfda6df6..20d39ec6c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
@@ -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))
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
index 70dd2eb3a..0d90047c2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
@@ -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
"#]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs
index b2a6a592c..88a3c7639 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs
@@ -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
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
index ba3bf8b5a..c575bf7ca 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
@@ -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;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index 2f13a9fbf..592223f7d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -68,6 +68,9 @@ pub struct GenericArgs {
pub struct AssociatedTypeBinding {
/// The name of the associated type.
pub name: Name,
+ /// The generic arguments to the associated type. e.g. For `Trait<Assoc<'a, T> = &'a T>`, this
+ /// would be `['a, T]`.
+ pub args: Option<Interned<GenericArgs>>,
/// The type bound to this associated type (in `Item = T`, this would be the
/// `T`). This can be `None` if there are bounds instead.
pub type_ref: Option<TypeRef>,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
index 0428f1a39..cfa3a6baa 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
@@ -163,6 +163,10 @@ pub(super) fn lower_generic_args(
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
if let Some(name_ref) = assoc_type_arg.name_ref() {
let name = name_ref.as_name();
+ let args = assoc_type_arg
+ .generic_arg_list()
+ .and_then(|args| lower_generic_args(lower_ctx, args))
+ .map(Interned::new);
let type_ref = assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it));
let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
l.bounds()
@@ -171,7 +175,7 @@ pub(super) fn lower_generic_args(
} else {
Vec::new()
};
- bindings.push(AssociatedTypeBinding { name, type_ref, bounds });
+ bindings.push(AssociatedTypeBinding { name, args, type_ref, bounds });
}
}
ast::GenericArg::LifetimeArg(lifetime_arg) => {
@@ -214,6 +218,7 @@ fn lower_generic_args_from_fn_path(
let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty());
bindings.push(AssociatedTypeBinding {
name: name![Output],
+ args: None,
type_ref: Some(type_ref),
bounds: Vec::new(),
});
@@ -222,6 +227,7 @@ fn lower_generic_args_from_fn_path(
let type_ref = TypeRef::Tuple(Vec::new());
bindings.push(AssociatedTypeBinding {
name: name![Output],
+ args: None,
type_ref: Some(type_ref),
bounds: Vec::new(),
});
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
index 6636c8a23..933970d10 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
@@ -143,9 +143,12 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
print_type_ref(elem, buf)?;
write!(buf, "]")?;
}
- TypeRef::Fn(args_and_ret, varargs) => {
+ TypeRef::Fn(args_and_ret, varargs, is_unsafe) => {
let ((_, return_type), args) =
args_and_ret.split_last().expect("TypeRef::Fn is missing return type");
+ if *is_unsafe {
+ write!(buf, "unsafe ")?;
+ }
write!(buf, "fn(")?;
for (i, (_, typeref)) in args.iter().enumerate() {
if i != 0 {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
index 5b4c71be7..f8bb78ddc 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
@@ -119,7 +119,7 @@ pub enum TypeRef {
Array(Box<TypeRef>, ConstScalarOrPath),
Slice(Box<TypeRef>),
/// A fn pointer. Last element of the vector is the return type.
- Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/),
+ Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
ImplTrait(Vec<Interned<TypeBound>>),
DynTrait(Vec<Interned<TypeBound>>),
Macro(AstId<ast::MacroCall>),
@@ -229,7 +229,7 @@ impl TypeRef {
Vec::new()
};
params.push((None, ret_ty));
- TypeRef::Fn(params, is_varargs)
+ TypeRef::Fn(params, is_varargs, inner.unsafe_token().is_some())
}
// for types are close enough for our purposes to the inner type for now...
ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
@@ -263,7 +263,7 @@ impl TypeRef {
fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
f(type_ref);
match type_ref {
- TypeRef::Fn(params, _) => {
+ TypeRef::Fn(params, _, _) => {
params.iter().for_each(|(_, param_type)| go(param_type, f))
}
TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
index 3359c99b3..77eb1fd45 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
index 893e6fe4b..a4abe7562 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
@@ -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 () ;}
"#]],
)
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index a5b499fe8..7352b003a 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -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) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index ed13275ba..a1d6835bf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
index e2099d7e5..996b42f5b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
@@ -11,9 +11,9 @@ use syntax::SmolStr;
use crate::{
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
- from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId,
- CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause,
- Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
+ from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
+ CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
+ QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
};
pub trait TyExt {
@@ -338,10 +338,13 @@ pub trait ProjectionTyExt {
impl ProjectionTyExt for ProjectionTy {
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
- TraitRef {
- trait_id: to_chalk_trait_id(self.trait_(db)),
- substitution: self.substitution.clone(),
- }
+ // FIXME: something like `Split` trait from chalk-solve might be nice.
+ let generics = generics(db.upcast(), from_assoc_type_id(self.associated_ty_id).into());
+ let substitution = Substitution::from_iter(
+ Interner,
+ self.substitution.iter(Interner).skip(generics.len_self()),
+ );
+ TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution }
}
fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 0221f922f..a22a4b170 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -289,16 +289,18 @@ impl HirDisplay for ProjectionTy {
return write!(f, "{}", TYPE_HINT_TRUNCATION);
}
- let trait_ = f.db.trait_data(self.trait_(f.db));
+ let trait_ref = self.trait_ref(f.db);
write!(f, "<")?;
- self.self_type_parameter(f.db).hir_fmt(f)?;
- write!(f, " as {}", trait_.name)?;
- if self.substitution.len(Interner) > 1 {
+ fmt_trait_ref(&trait_ref, f, true)?;
+ write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
+ let proj_params_count =
+ self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
+ let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
+ if !proj_params.is_empty() {
write!(f, "<")?;
- f.write_joined(&self.substitution.as_slice(Interner)[1..], ", ")?;
+ f.write_joined(proj_params, ", ")?;
write!(f, ">")?;
}
- write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
Ok(())
}
}
@@ -641,9 +643,12 @@ impl HirDisplay for Ty {
// Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
if f.display_target.is_test() {
write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
+ // Note that the generic args for the associated type come before those for the
+ // trait (including the self type).
+ // FIXME: reconsider the generic args order upon formatting?
if parameters.len(Interner) > 0 {
write!(f, "<")?;
- f.write_joined(&*parameters.as_slice(Interner), ", ")?;
+ f.write_joined(parameters.as_slice(Interner), ", ")?;
write!(f, ">")?;
}
} else {
@@ -972,9 +977,20 @@ fn write_bounds_like_dyn_trait(
angle_open = true;
}
if let AliasTy::Projection(proj) = alias {
- let type_alias =
- f.db.type_alias_data(from_assoc_type_id(proj.associated_ty_id));
- write!(f, "{} = ", type_alias.name)?;
+ let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id);
+ let type_alias = f.db.type_alias_data(assoc_ty_id);
+ write!(f, "{}", type_alias.name)?;
+
+ let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
+ if proj_arg_count > 0 {
+ write!(f, "<")?;
+ f.write_joined(
+ &proj.substitution.as_slice(Interner)[..proj_arg_count],
+ ", ",
+ )?;
+ write!(f, ">")?;
+ }
+ write!(f, " = ")?;
}
ty.hir_fmt(f)?;
}
@@ -1171,8 +1187,11 @@ impl HirDisplay for TypeRef {
inner.hir_fmt(f)?;
write!(f, "]")?;
}
- TypeRef::Fn(parameters, is_varargs) => {
+ &TypeRef::Fn(ref parameters, is_varargs, is_unsafe) => {
// FIXME: Function pointer qualifiers.
+ if is_unsafe {
+ write!(f, "unsafe ")?;
+ }
write!(f, "fn(")?;
if let Some(((_, return_type), function_parameters)) = parameters.split_last() {
for index in 0..function_parameters.len() {
@@ -1187,7 +1206,7 @@ impl HirDisplay for TypeRef {
write!(f, ", ")?;
}
}
- if *is_varargs {
+ if is_varargs {
write!(f, "{}...", if parameters.len() == 1 { "" } else { ", " })?;
}
write!(f, ")")?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 0efff651c..0b3c23f57 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -1020,7 +1020,7 @@ impl Expectation {
/// The primary use case is where the expected type is a fat pointer,
/// like `&[isize]`. For example, consider the following statement:
///
- /// let x: &[isize] = &[1, 2, 3];
+ /// let x: &[isize] = &[1, 2, 3];
///
/// In this case, the expected type for the `&[1, 2, 3]` expression is
/// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index f56108b26..b1f4de826 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -85,6 +85,7 @@ impl<'a> InferenceContext<'a> {
let ty = match &self.body[tgt_expr] {
Expr::Missing => self.err_ty(),
&Expr::If { condition, then_branch, else_branch } => {
+ let expected = &expected.adjust_for_branches(&mut self.table);
self.infer_expr(
condition,
&Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index 7a4754cdc..ebe9d6fb5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -157,7 +157,7 @@ impl<'a> InferenceContext<'a> {
remaining_segments_for_ty,
true,
);
- if let TyKind::Error = ty.kind(Interner) {
+ if ty.is_unknown() {
return None;
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index b00e3216b..12f45f00f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -340,8 +340,8 @@ impl<'a> InferenceTable<'a> {
self.resolve_with_fallback(t, &|_, _, d, _| d)
}
- /// Unify two types and register new trait goals that arise from that.
- pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
+ /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
+ pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
let result = match self.try_unify(ty1, ty2) {
Ok(r) => r,
Err(_) => return false,
@@ -350,9 +350,13 @@ impl<'a> InferenceTable<'a> {
true
}
- /// Unify two types and return new trait goals arising from it, so the
+ /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the
/// caller needs to deal with them.
- pub(crate) fn try_unify<T: Zip<Interner>>(&mut self, t1: &T, t2: &T) -> InferResult<()> {
+ pub(crate) fn try_unify<T: ?Sized + Zip<Interner>>(
+ &mut self,
+ t1: &T,
+ t2: &T,
+ ) -> InferResult<()> {
match self.var_unification_table.relate(
Interner,
&self.db,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index c4b700cbc..39514fc44 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -38,10 +38,12 @@ use std::sync::Arc;
use chalk_ir::{
fold::{Shift, TypeFoldable},
interner::HasInterner,
- NoSolution,
+ NoSolution, UniverseIndex,
};
use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
+use hir_expand::name;
use itertools::Either;
+use traits::FnTrait;
use utils::Generics;
use crate::{consteval::unknown_const, db::HirDatabase, utils::generics};
@@ -51,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::{
@@ -81,7 +83,20 @@ pub type PlaceholderIndex = chalk_ir::PlaceholderIndex;
pub type VariableKind = chalk_ir::VariableKind<Interner>;
pub type VariableKinds = chalk_ir::VariableKinds<Interner>;
pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>;
+/// Represents generic parameters and an item bound by them. When the item has parent, the binders
+/// also contain the generic parameters for its parent. See chalk's documentation for details.
+///
+/// One thing to keep in mind when working with `Binders` (and `Substitution`s, which represent
+/// generic arguments) in rust-analyzer is that the ordering within *is* significant - the generic
+/// parameters/arguments for an item MUST come before those for its parent. This is to facilitate
+/// the integration with chalk-solve, which mildly puts constraints as such. See #13335 for its
+/// motivation in detail.
pub type Binders<T> = chalk_ir::Binders<T>;
+/// Interned list of generic arguments for an item. When an item has parent, the `Substitution` for
+/// it contains generic arguments for both its parent and itself. See chalk's documentation for
+/// details.
+///
+/// See `Binders` for the constraint on the ordering.
pub type Substitution = chalk_ir::Substitution<Interner>;
pub type GenericArg = chalk_ir::GenericArg<Interner>;
pub type GenericArgData = chalk_ir::GenericArgData<Interner>;
@@ -124,14 +139,6 @@ pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;
pub type Guidance = chalk_solve::Guidance<Interner>;
pub type WhereClause = chalk_ir::WhereClause<Interner>;
-// FIXME: get rid of this
-pub fn subst_prefix(s: &Substitution, n: usize) -> Substitution {
- Substitution::from_iter(
- Interner,
- s.as_slice(Interner)[..std::cmp::min(s.len(Interner), n)].iter().cloned(),
- )
-}
-
/// Return an index of a parameter in the generic type parameter list by it's id.
pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
generics(db.upcast(), id.parent).param_idx(id)
@@ -203,6 +210,7 @@ pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
pub struct CallableSig {
params_and_return: Arc<[Ty]>,
is_varargs: bool,
+ safety: Safety,
}
has_interner!(CallableSig);
@@ -211,9 +219,14 @@ has_interner!(CallableSig);
pub type PolyFnSig = Binders<CallableSig>;
impl CallableSig {
- pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty, is_varargs: bool) -> CallableSig {
+ pub fn from_params_and_return(
+ mut params: Vec<Ty>,
+ ret: Ty,
+ is_varargs: bool,
+ safety: Safety,
+ ) -> CallableSig {
params.push(ret);
- CallableSig { params_and_return: params.into(), is_varargs }
+ CallableSig { params_and_return: params.into(), is_varargs, safety }
}
pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig {
@@ -230,13 +243,14 @@ impl CallableSig {
.map(|arg| arg.assert_ty_ref(Interner).clone())
.collect(),
is_varargs: fn_ptr.sig.variadic,
+ safety: fn_ptr.sig.safety,
}
}
pub fn to_fn_ptr(&self) -> FnPointer {
FnPointer {
num_binders: 0,
- sig: FnSig { abi: (), safety: Safety::Safe, variadic: self.is_varargs },
+ sig: FnSig { abi: (), safety: self.safety, variadic: self.is_varargs },
substitution: FnSubst(Substitution::from_iter(
Interner,
self.params_and_return.iter().cloned(),
@@ -261,7 +275,11 @@ impl TypeFoldable<Interner> for CallableSig {
) -> Result<Self, E> {
let vec = self.params_and_return.to_vec();
let folded = vec.try_fold_with(folder, outer_binder)?;
- Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs })
+ Ok(CallableSig {
+ params_and_return: folded.into(),
+ is_varargs: self.is_varargs,
+ safety: self.safety,
+ })
}
}
@@ -382,7 +400,6 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
pub fn replace_errors_with_variables<T>(t: &T) -> Canonical<T>
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + Clone,
- T: HasInterner<Interner = Interner>,
{
use chalk_ir::{
fold::{FallibleTypeFolder, TypeSuperFoldable},
@@ -504,3 +521,64 @@ where
});
Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) }
}
+
+pub fn callable_sig_from_fnonce(
+ self_ty: &Ty,
+ env: Arc<TraitEnvironment>,
+ db: &dyn HirDatabase,
+) -> Option<CallableSig> {
+ let krate = env.krate;
+ let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
+ let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
+
+ let b = TyBuilder::trait_ref(db, fn_once_trait);
+ if b.remaining() != 2 {
+ return None;
+ }
+ 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.
+ let trait_env = env.env.clone();
+ let obligation = InEnvironment { goal: fn_once.cast(Interner), environment: trait_env };
+ let canonical =
+ Canonical { binders: CanonicalVarKinds::from_iter(Interner, kinds), value: obligation };
+ let subst = match db.trait_solve(krate, canonical) {
+ Some(Solution::Unique(vars)) => vars.value.subst,
+ _ => return None,
+ };
+ 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,
+ };
+
+ 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();
+
+ let ret_ty = db.normalize_projection(projection, env);
+
+ Some(CallableSig::from_params_and_return(params, ret_ty.clone(), false, Safety::Safe))
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 223d705b1..baf9842d5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -227,13 +227,17 @@ impl<'a> TyLoweringContext<'a> {
.intern(Interner)
}
TypeRef::Placeholder => TyKind::Error.intern(Interner),
- TypeRef::Fn(params, is_varargs) => {
+ &TypeRef::Fn(ref params, variadic, is_unsafe) => {
let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
Substitution::from_iter(Interner, params.iter().map(|(_, tr)| ctx.lower_ty(tr)))
});
TyKind::Function(FnPointer {
num_binders: 0, // FIXME lower `for<'a> fn()` correctly
- sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs },
+ sig: FnSig {
+ abi: (),
+ safety: if is_unsafe { Safety::Unsafe } else { Safety::Safe },
+ variadic,
+ },
substitution: FnSubst(substs),
})
.intern(Interner)
@@ -447,12 +451,31 @@ impl<'a> TyLoweringContext<'a> {
.db
.trait_data(trait_ref.hir_trait_id())
.associated_type_by_name(segment.name);
+
match found {
Some(associated_ty) => {
- // FIXME handle type parameters on the segment
+ // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+ // generic params. It's inefficient to splice the `Substitution`s, so we may want
+ // that method to optionally take parent `Substitution` as we already know them at
+ // this point (`trait_ref.substitution`).
+ let substitution = self.substs_from_path_segment(
+ segment,
+ Some(associated_ty.into()),
+ false,
+ None,
+ );
+ let len_self =
+ generics(self.db.upcast(), associated_ty.into()).len_self();
+ let substitution = Substitution::from_iter(
+ Interner,
+ substitution
+ .iter(Interner)
+ .take(len_self)
+ .chain(trait_ref.substitution.iter(Interner)),
+ );
TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
- substitution: trait_ref.substitution,
+ substitution,
}))
.intern(Interner)
}
@@ -590,36 +613,48 @@ impl<'a> TyLoweringContext<'a> {
res,
Some(segment.name.clone()),
move |name, t, associated_ty| {
- if name == segment.name {
- let substs = match self.type_param_mode {
- ParamLoweringMode::Placeholder => {
- // if we're lowering to placeholders, we have to put
- // them in now
- let generics = generics(
- self.db.upcast(),
- self.resolver
- .generic_def()
- .expect("there should be generics if there's a generic param"),
- );
- let s = generics.placeholder_subst(self.db);
- s.apply(t.substitution.clone(), Interner)
- }
- ParamLoweringMode::Variable => t.substitution.clone(),
- };
- // We need to shift in the bound vars, since
- // associated_type_shorthand_candidates does not do that
- let substs = substs.shifted_in_from(Interner, self.in_binders);
- // FIXME handle type parameters on the segment
- Some(
- TyKind::Alias(AliasTy::Projection(ProjectionTy {
- associated_ty_id: to_assoc_type_id(associated_ty),
- substitution: substs,
- }))
- .intern(Interner),
- )
- } else {
- None
+ if name != segment.name {
+ return None;
}
+
+ // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+ // generic params. It's inefficient to splice the `Substitution`s, so we may want
+ // that method to optionally take parent `Substitution` as we already know them at
+ // this point (`t.substitution`).
+ let substs = self.substs_from_path_segment(
+ segment.clone(),
+ Some(associated_ty.into()),
+ false,
+ None,
+ );
+
+ let len_self = generics(self.db.upcast(), associated_ty.into()).len_self();
+
+ let substs = Substitution::from_iter(
+ Interner,
+ substs.iter(Interner).take(len_self).chain(t.substitution.iter(Interner)),
+ );
+
+ let substs = match self.type_param_mode {
+ ParamLoweringMode::Placeholder => {
+ // if we're lowering to placeholders, we have to put
+ // them in now
+ let generics = generics(self.db.upcast(), def);
+ let s = generics.placeholder_subst(self.db);
+ s.apply(substs, Interner)
+ }
+ ParamLoweringMode::Variable => substs,
+ };
+ // We need to shift in the bound vars, since
+ // associated_type_shorthand_candidates does not do that
+ let substs = substs.shifted_in_from(Interner, self.in_binders);
+ Some(
+ TyKind::Alias(AliasTy::Projection(ProjectionTy {
+ associated_ty_id: to_assoc_type_id(associated_ty),
+ substitution: substs,
+ }))
+ .intern(Interner),
+ )
},
);
@@ -777,7 +812,15 @@ impl<'a> TyLoweringContext<'a> {
// handle defaults. In expression or pattern path segments without
// explicitly specified type arguments, missing type arguments are inferred
// (i.e. defaults aren't used).
- if !infer_args || had_explicit_args {
+ // Generic parameters for associated types are not supposed to have defaults, so we just
+ // ignore them.
+ let is_assoc_ty = if let GenericDefId::TypeAliasId(id) = def {
+ let container = id.lookup(self.db.upcast()).container;
+ matches!(container, ItemContainerId::TraitId(_))
+ } else {
+ false
+ };
+ if !is_assoc_ty && (!infer_args || had_explicit_args) {
let defaults = self.db.generic_defaults(def);
assert_eq!(total_len, defaults.len());
let parent_from = item_len - substs.len();
@@ -966,9 +1009,28 @@ impl<'a> TyLoweringContext<'a> {
None => return SmallVec::new(),
Some(t) => t,
};
+ // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+ // generic params. It's inefficient to splice the `Substitution`s, so we may want
+ // that method to optionally take parent `Substitution` as we already know them at
+ // this point (`super_trait_ref.substitution`).
+ let substitution = self.substs_from_path_segment(
+ // FIXME: This is hack. We shouldn't really build `PathSegment` directly.
+ PathSegment { name: &binding.name, args_and_bindings: binding.args.as_deref() },
+ Some(associated_ty.into()),
+ false, // this is not relevant
+ Some(super_trait_ref.self_type_parameter(Interner)),
+ );
+ let self_params = generics(self.db.upcast(), associated_ty.into()).len_self();
+ let substitution = Substitution::from_iter(
+ Interner,
+ substitution
+ .iter(Interner)
+ .take(self_params)
+ .chain(super_trait_ref.substitution.iter(Interner)),
+ );
let projection_ty = ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
- substitution: super_trait_ref.substitution,
+ substitution,
};
let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
@@ -1515,7 +1577,12 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
.with_type_param_mode(ParamLoweringMode::Variable);
let ret = ctx_ret.lower_ty(&data.ret_type);
let generics = generics(db.upcast(), def.into());
- let sig = CallableSig::from_params_and_return(params, ret, data.is_varargs());
+ let sig = CallableSig::from_params_and_return(
+ params,
+ ret,
+ data.is_varargs(),
+ if data.has_unsafe_kw() { Safety::Unsafe } else { Safety::Safe },
+ );
make_binders(db, &generics, sig)
}
@@ -1559,7 +1626,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
- Binders::new(binders, CallableSig::from_params_and_return(params, ret, false))
+ Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
}
/// Build the type of a tuple struct constructor.
@@ -1586,7 +1653,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders();
- Binders::new(binders, CallableSig::from_params_and_return(params, ret, false))
+ Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
}
/// Build the type of a tuple enum variant constructor.
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index 3a1a3f4fd..8bcfa2728 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -22,10 +22,10 @@ use crate::{
from_foreign_def_id,
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
primitive::{FloatTy, IntTy, UintTy},
- static_lifetime,
+ static_lifetime, to_chalk_trait_id,
utils::all_super_traits,
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner,
- Scalar, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
+ Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
};
/// This is used as a key for indexing impls.
@@ -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()) {
@@ -624,52 +624,76 @@ pub(crate) fn iterate_method_candidates<T>(
slot
}
+/// Looks up the impl method that actually runs for the trait method `func`.
+///
+/// Returns `func` if it's not a method defined in a trait or the lookup failed.
pub fn lookup_impl_method(
- self_ty: &Ty,
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
- trait_: TraitId,
+ func: FunctionId,
+ fn_subst: Substitution,
+) -> FunctionId {
+ let trait_id = match func.lookup(db.upcast()).container {
+ ItemContainerId::TraitId(id) => id,
+ _ => return func,
+ };
+ let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
+ let fn_params = fn_subst.len(Interner) - trait_params;
+ let trait_ref = TraitRef {
+ trait_id: to_chalk_trait_id(trait_id),
+ substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).skip(fn_params)),
+ };
+
+ let name = &db.function_data(func).name;
+ lookup_impl_method_for_trait_ref(trait_ref, db, env, name).unwrap_or(func)
+}
+
+fn lookup_impl_method_for_trait_ref(
+ trait_ref: TraitRef,
+ db: &dyn HirDatabase,
+ env: Arc<TraitEnvironment>,
name: &Name,
) -> Option<FunctionId> {
- let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?;
- let trait_impls = db.trait_impls_in_deps(env.krate);
- let impls = trait_impls.for_trait_and_self_ty(trait_, self_ty_fp);
- let mut table = InferenceTable::new(db, env.clone());
- find_matching_impl(impls, &mut table, &self_ty).and_then(|data| {
- data.items.iter().find_map(|it| match it {
- AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
- _ => None,
- })
+ let self_ty = trait_ref.self_type_parameter(Interner);
+ let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
+ let impls = db.trait_impls_in_deps(env.krate);
+ let impls = impls.for_trait_and_self_ty(trait_ref.hir_trait_id(), self_ty_fp);
+
+ let table = InferenceTable::new(db, env);
+
+ let impl_data = find_matching_impl(impls, table, trait_ref)?;
+ impl_data.items.iter().find_map(|it| match it {
+ AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
+ _ => None,
})
}
fn find_matching_impl(
mut impls: impl Iterator<Item = ImplId>,
- table: &mut InferenceTable<'_>,
- self_ty: &Ty,
+ mut table: InferenceTable<'_>,
+ actual_trait_ref: TraitRef,
) -> Option<Arc<ImplData>> {
let db = table.db;
loop {
let impl_ = impls.next()?;
let r = table.run_in_snapshot(|table| {
let impl_data = db.impl_data(impl_);
- let substs =
+ let impl_substs =
TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build();
- let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs);
-
- table
- .unify(self_ty, &impl_ty)
- .then(|| {
- let wh_goals =
- crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs)
- .into_iter()
- .map(|b| b.cast(Interner));
+ let trait_ref = db
+ .impl_trait(impl_)
+ .expect("non-trait method in find_matching_impl")
+ .substitute(Interner, &impl_substs);
- let goal = crate::Goal::all(Interner, wh_goals);
+ if !table.unify(&trait_ref, &actual_trait_ref) {
+ return None;
+ }
- table.try_obligation(goal).map(|_| impl_data)
- })
- .flatten()
+ let wcs = crate::chalk_db::convert_where_clauses(db, impl_.into(), &impl_substs)
+ .into_iter()
+ .map(|b| b.cast(Interner));
+ let goal = crate::Goal::all(Interner, wcs);
+ table.try_obligation(goal).map(|_| impl_data)
});
if r.is_some() {
break r;
@@ -1214,7 +1238,7 @@ fn is_valid_fn_candidate(
let expected_receiver =
sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
- check_that!(table.unify(&receiver_ty, &expected_receiver));
+ check_that!(table.unify(receiver_ty, &expected_receiver));
}
if let ItemContainerId::ImplId(impl_id) = container {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
index d301595bc..7e3aecc2a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
@@ -123,6 +123,23 @@ fn test() {
}
#[test]
+fn if_else_adjust_for_branches_discard_type_var() {
+ check_no_mismatches(
+ r#"
+fn test() {
+ let f = || {
+ if true {
+ &""
+ } else {
+ ""
+ }
+ };
+}
+"#,
+ );
+}
+
+#[test]
fn match_first_coerce() {
check_no_mismatches(
r#"
@@ -183,6 +200,22 @@ fn test() {
}
#[test]
+fn match_adjust_for_branches_discard_type_var() {
+ check_no_mismatches(
+ r#"
+fn test() {
+ let f = || {
+ match 0i32 {
+ 0i32 => &"",
+ _ => "",
+ }
+ };
+}
+"#,
+ );
+}
+
+#[test]
fn return_coerce_unknown() {
check_types(
r"
@@ -357,7 +390,7 @@ fn test() {
let f: fn(u32) -> isize = foo;
// ^^^ adjustments: Pointer(ReifyFnPointer)
let f: unsafe fn(u32) -> isize = foo;
- // ^^^ adjustments: Pointer(ReifyFnPointer)
+ // ^^^ adjustments: Pointer(ReifyFnPointer), Pointer(UnsafeFnPointer)
}",
);
}
@@ -388,7 +421,10 @@ fn coerce_closure_to_fn_ptr() {
check_no_mismatches(
r"
fn test() {
- let f: fn(u32) -> isize = |x| { 1 };
+ let f: fn(u32) -> u32 = |x| x;
+ // ^^^^^ adjustments: Pointer(ClosureFnPointer(Safe))
+ let f: unsafe fn(u32) -> u32 = |x| x;
+ // ^^^^^ adjustments: Pointer(ClosureFnPointer(Unsafe))
}",
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
index 8a8ff08cf..425432479 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
@@ -196,3 +196,34 @@ fn test(
"#,
);
}
+
+#[test]
+fn projection_type_correct_arguments_order() {
+ check_types_source_code(
+ r#"
+trait Foo<T> {
+ type Assoc<U>;
+}
+fn f<T: Foo<i32>>(a: T::Assoc<usize>) {
+ a;
+ //^ <T as Foo<i32>>::Assoc<usize>
+}
+"#,
+ );
+}
+
+#[test]
+fn generic_associated_type_binding_in_impl_trait() {
+ check_types_source_code(
+ r#"
+//- minicore: sized
+trait Foo<T> {
+ type Assoc<U>;
+}
+fn f(a: impl Foo<i8, Assoc<i16> = i32>) {
+ a;
+ //^ impl Foo<i8, Assoc<i16> = i32>
+}
+ "#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
index ac8edb841..5d76d185f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
@@ -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
"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index a155adcec..4e4639745 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -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(())
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 080e2ac1b..d7431443b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -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;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index 555b6972f..3d7194b6f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -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
"#]],
);
}
@@ -3963,3 +3963,124 @@ fn g(t: &(dyn T + Send)) {
"#,
);
}
+
+#[test]
+fn gats_in_path() {
+ check_types(
+ r#"
+//- minicore: deref
+use core::ops::Deref;
+trait PointerFamily {
+ type Pointer<T>: Deref<Target = T>;
+}
+
+fn f<P: PointerFamily>(p: P::Pointer<i32>) {
+ let a = *p;
+ //^ i32
+}
+fn g<P: PointerFamily>(p: <P as PointerFamily>::Pointer<i32>) {
+ let a = *p;
+ //^ i32
+}
+ "#,
+ );
+}
+
+#[test]
+fn gats_with_impl_trait() {
+ // FIXME: the last function (`fn i()`) is not valid Rust as of this writing because you cannot
+ // specify the same associated type multiple times even if their arguments are different (c.f.
+ // `fn h()`, which is valid). Reconsider how to treat these invalid types.
+ check_types(
+ r#"
+//- minicore: deref
+use core::ops::Deref;
+
+trait Trait {
+ type Assoc<T>: Deref<Target = T>;
+ fn get<U>(&self) -> Self::Assoc<U>;
+}
+
+fn f<T>(v: impl Trait) {
+ let a = v.get::<i32>().deref();
+ //^ &i32
+ let a = v.get::<T>().deref();
+ //^ &T
+}
+fn g<'a, T: 'a>(v: impl Trait<Assoc<T> = &'a T>) {
+ let a = v.get::<T>();
+ //^ &T
+ let a = v.get::<()>();
+ //^ Trait::Assoc<(), impl Trait<Assoc<T> = &T>>
+}
+fn h<'a>(v: impl Trait<Assoc<i32> = &'a i32> + Trait<Assoc<i64> = &'a i64>) {
+ let a = v.get::<i32>();
+ //^ &i32
+ let a = v.get::<i64>();
+ //^ &i64
+}
+fn i<'a>(v: impl Trait<Assoc<i32> = &'a i32, Assoc<i64> = &'a i64>) {
+ let a = v.get::<i32>();
+ //^ &i32
+ let a = v.get::<i64>();
+ //^ &i64
+}
+ "#,
+ );
+}
+
+#[test]
+fn gats_with_dyn() {
+ // This test is here to keep track of how we infer things despite traits with GATs being not
+ // object-safe currently.
+ // FIXME: reconsider how to treat these invalid types.
+ check_infer_with_mismatches(
+ r#"
+//- minicore: deref
+use core::ops::Deref;
+
+trait Trait {
+ type Assoc<T>: Deref<Target = T>;
+ fn get<U>(&self) -> Self::Assoc<U>;
+}
+
+fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) {
+ v.get::<i32>().deref();
+}
+ "#,
+ expect![[r#"
+ 90..94 'self': &Self
+ 127..128 'v': &(dyn Trait<Assoc<i32> = &i32>)
+ 164..195 '{ ...f(); }': ()
+ 170..171 'v': &(dyn Trait<Assoc<i32> = &i32>)
+ 170..184 'v.get::<i32>()': &i32
+ 170..192 'v.get:...eref()': &i32
+ "#]],
+ );
+}
+
+#[test]
+fn gats_in_associated_type_binding() {
+ check_types(
+ r#"
+trait Trait {
+ type Assoc<T>;
+ fn get<U>(&self) -> Self::Assoc<U>;
+}
+
+fn f<T>(t: T)
+where
+ T: Trait<Assoc<i32> = u32>,
+ T: Trait<Assoc<isize> = usize>,
+{
+ let a = t.get::<i32>();
+ //^ u32
+ let a = t.get::<isize>();
+ //^ usize
+ let a = t.get::<()>();
+ //^ Trait::Assoc<(), T>
+}
+
+ "#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
index 547850b02..92711a24f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
@@ -5,7 +5,7 @@ use itertools::Itertools;
use crate::{
chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk,
- CallableDefId, Interner,
+ CallableDefId, Interner, ProjectionTyExt,
};
use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId};
@@ -63,17 +63,31 @@ impl DebugContext<'_> {
ItemContainerId::TraitId(t) => t,
_ => panic!("associated type not in trait"),
};
- let trait_data = self.0.trait_data(trait_);
- let params = projection_ty.substitution.as_slice(Interner);
- write!(fmt, "<{:?} as {}", &params[0], trait_data.name,)?;
- if params.len() > 1 {
+ let trait_name = &self.0.trait_data(trait_).name;
+ let trait_ref = projection_ty.trait_ref(self.0);
+ let trait_params = trait_ref.substitution.as_slice(Interner);
+ let self_ty = trait_ref.self_type_parameter(Interner);
+ write!(fmt, "<{:?} as {}", self_ty, trait_name)?;
+ if trait_params.len() > 1 {
+ write!(
+ fmt,
+ "<{}>",
+ trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
+ )?;
+ }
+ write!(fmt, ">::{}", type_alias_data.name)?;
+
+ let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len();
+ let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count];
+ if !proj_params.is_empty() {
write!(
fmt,
"<{}>",
- &params[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
+ proj_params.iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
)?;
}
- write!(fmt, ">::{}", type_alias_data.name)
+
+ Ok(())
}
pub(crate) fn debug_fn_def_id(
diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml
index e1418de3c..f780e3f53 100644
--- a/src/tools/rust-analyzer/crates/hir/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index f5324208c..cbbcaebb4 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -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
@@ -2995,7 +2995,16 @@ impl Type {
let callee = match self.ty.kind(Interner) {
TyKind::Closure(id, _) => Callee::Closure(*id),
TyKind::Function(_) => Callee::FnPtr,
- _ => Callee::Def(self.ty.callable_def(db)?),
+ TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
+ _ => {
+ let sig = hir_ty::callable_sig_from_fnonce(&self.ty, self.env.clone(), db)?;
+ return Some(Callable {
+ ty: self.clone(),
+ sig,
+ callee: Callee::Other,
+ is_bound_method: false,
+ });
+ }
};
let sig = self.ty.callable_sig(db)?;
@@ -3464,6 +3473,7 @@ enum Callee {
Def(CallableDefId),
Closure(ClosureId),
FnPtr,
+ Other,
}
pub enum CallableKind {
@@ -3472,6 +3482,8 @@ pub enum CallableKind {
TupleEnumVariant(Variant),
Closure,
FnPtr,
+ /// Some other type that implements `FnOnce`.
+ Other,
}
impl Callable {
@@ -3483,6 +3495,7 @@ impl Callable {
Def(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
Closure(_) => CallableKind::Closure,
FnPtr => CallableKind::FnPtr,
+ Other => CallableKind::Other,
}
}
pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
@@ -3637,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 {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 119ec3210..2e1f88ba0 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -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> {
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 07bae2b38..91ea1c24d 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -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(
@@ -270,7 +262,7 @@ impl SourceAnalyzer {
let expr_id = self.expr_id(db, &call.clone().into())?;
let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
- Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, &substs))
+ Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))
}
pub(crate) fn resolve_await_to_poll(
@@ -311,7 +303,7 @@ impl SourceAnalyzer {
// HACK: subst for `poll()` coincides with that for `Future` because `poll()` itself
// doesn't have any generic parameters, so we skip building another subst for `poll()`.
let substs = hir_ty::TyBuilder::subst_for_def(db, future_trait, None).push(ty).build();
- Some(self.resolve_impl_method_or_trait_def(db, poll_fn, &substs))
+ Some(self.resolve_impl_method_or_trait_def(db, poll_fn, substs))
}
pub(crate) fn resolve_prefix_expr(
@@ -331,7 +323,7 @@ impl SourceAnalyzer {
// don't have any generic parameters, so we skip building another subst for the methods.
let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build();
- Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
+ Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
}
pub(crate) fn resolve_index_expr(
@@ -351,7 +343,7 @@ impl SourceAnalyzer {
.push(base_ty.clone())
.push(index_ty.clone())
.build();
- Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
+ Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
}
pub(crate) fn resolve_bin_expr(
@@ -372,7 +364,7 @@ impl SourceAnalyzer {
.push(rhs.clone())
.build();
- Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
+ Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
}
pub(crate) fn resolve_try_expr(
@@ -392,7 +384,7 @@ impl SourceAnalyzer {
// doesn't have any generic parameters, so we skip building another subst for `branch()`.
let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build();
- Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
+ Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
}
pub(crate) fn resolve_field(
@@ -487,9 +479,9 @@ impl SourceAnalyzer {
let mut prefer_value_ns = false;
let resolved = (|| {
+ let infer = self.infer.as_deref()?;
if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
let expr_id = self.expr_id(db, &path_expr.into())?;
- let infer = self.infer.as_ref()?;
if let Some(assoc) = infer.assoc_resolutions_for_expr(expr_id) {
let assoc = match assoc {
AssocItemId::FunctionId(f_in_trait) => {
@@ -497,9 +489,12 @@ impl SourceAnalyzer {
None => assoc,
Some(func_ty) => {
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) {
- self.resolve_impl_method(db, f_in_trait, subs)
- .map(AssocItemId::FunctionId)
- .unwrap_or(assoc)
+ self.resolve_impl_method_or_trait_def(
+ db,
+ f_in_trait,
+ subs.clone(),
+ )
+ .into()
} else {
assoc
}
@@ -520,18 +515,18 @@ impl SourceAnalyzer {
prefer_value_ns = true;
} else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
let pat_id = self.pat_id(&path_pat.into())?;
- if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) {
+ if let Some(assoc) = infer.assoc_resolutions_for_pat(pat_id) {
return Some(PathResolution::Def(AssocItem::from(assoc).into()));
}
if let Some(VariantId::EnumVariantId(variant)) =
- self.infer.as_ref()?.variant_resolution_for_pat(pat_id)
+ infer.variant_resolution_for_pat(pat_id)
{
return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
}
} else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) {
let expr_id = self.expr_id(db, &rec_lit.into())?;
if let Some(VariantId::EnumVariantId(variant)) =
- self.infer.as_ref()?.variant_resolution_for_expr(expr_id)
+ infer.variant_resolution_for_expr(expr_id)
{
return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
}
@@ -541,8 +536,7 @@ impl SourceAnalyzer {
|| parent().and_then(ast::TupleStructPat::cast).map(ast::Pat::from);
if let Some(pat) = record_pat.or_else(tuple_struct_pat) {
let pat_id = self.pat_id(&pat)?;
- let variant_res_for_pat =
- self.infer.as_ref()?.variant_resolution_for_pat(pat_id);
+ let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id);
if let Some(VariantId::EnumVariantId(variant)) = variant_res_for_pat {
return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
}
@@ -780,37 +774,22 @@ impl SourceAnalyzer {
false
}
- fn resolve_impl_method(
+ fn resolve_impl_method_or_trait_def(
&self,
db: &dyn HirDatabase,
func: FunctionId,
- substs: &Substitution,
- ) -> Option<FunctionId> {
- let impled_trait = match func.lookup(db.upcast()).container {
- ItemContainerId::TraitId(trait_id) => trait_id,
- _ => return None,
- };
- if substs.is_empty(Interner) {
- return None;
- }
- let self_ty = substs.at(Interner, 0).ty(Interner)?;
+ substs: Substitution,
+ ) -> FunctionId {
let krate = self.resolver.krate();
- let trait_env = self.resolver.body_owner()?.as_generic_def_id().map_or_else(
+ let owner = match self.resolver.body_owner() {
+ Some(it) => it,
+ None => return func,
+ };
+ let env = owner.as_generic_def_id().map_or_else(
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|d| db.trait_environment(d),
);
-
- let fun_data = db.function_data(func);
- method_resolution::lookup_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name)
- }
-
- fn resolve_impl_method_or_trait_def(
- &self,
- db: &dyn HirDatabase,
- func: FunctionId,
- substs: &Substitution,
- ) -> FunctionId {
- self.resolve_impl_method(db, func, substs).unwrap_or(func)
+ method_resolution::lookup_impl_method(db, env, func, substs)
}
fn lang_trait_fn(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml
index 57a41f3d9..e781c0a01 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
index 60d1588a4..b273ebc85 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
@@ -14,4 +14,5 @@ pub struct AssistConfig {
pub allowed: Option<Vec<AssistKind>>,
pub insert_use: InsertUseConfig,
pub prefer_no_std: bool,
+ pub assist_emit_must_use: bool,
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
index bfa9759ec..b5f99726f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
@@ -69,14 +69,14 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
acc.add(
AssistId("add_explicit_type", AssistKind::RefactorRewrite),
- format!("Insert explicit type `{}`", inferred_type),
+ format!("Insert explicit type `{inferred_type}`"),
pat_range,
|builder| match ascribed_ty {
Some(ascribed_ty) => {
builder.replace(ascribed_ty.syntax().text_range(), inferred_type);
}
None => {
- builder.insert(pat_range.end(), format!(": {}", inferred_type));
+ builder.insert(pat_range.end(), format!(": {inferred_type}"));
}
},
)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 62cf5ab4f..2b3793659 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -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 }
}"#,
)
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
index f858d7a15..89040a856 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
@@ -35,16 +35,16 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
match builder_edit_pos {
InsertOrReplace::Insert(insert_pos, needs_whitespace) => {
let preceeding_whitespace = if needs_whitespace { " " } else { "" };
- builder.insert(insert_pos, &format!("{}-> {} ", preceeding_whitespace, ty))
+ builder.insert(insert_pos, &format!("{preceeding_whitespace}-> {ty} "))
}
InsertOrReplace::Replace(text_range) => {
- builder.replace(text_range, &format!("-> {}", ty))
+ builder.replace(text_range, &format!("-> {ty}"))
}
}
if let FnType::Closure { wrap_expr: true } = fn_type {
cov_mark::hit!(wrap_closure_non_block_expr);
// `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
- builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr));
+ builder.replace(tail_expr.syntax().text_range(), &format!("{{{tail_expr}}}"));
}
},
)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
index c0bf238db..acf82e4b2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
@@ -93,12 +93,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
builder.trigger_signature_help();
match ctx.config.snippet_cap {
Some(cap) => {
- let snip = format!("::<{}>", get_snippet_fish_head(number_of_arguments));
+ let fish_head = get_snippet_fish_head(number_of_arguments);
+ let snip = format!("::<{fish_head}>");
builder.insert_snippet(cap, ident.text_range().end(), snip)
}
None => {
let fish_head = std::iter::repeat("_").take(number_of_arguments).format(", ");
- let snip = format!("::<{}>", fish_head);
+ let snip = format!("::<{fish_head}>");
builder.insert(ident.text_range().end(), snip);
}
}
@@ -109,7 +110,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
/// This will create a snippet string with tabstops marked
fn get_snippet_fish_head(number_of_arguments: usize) -> String {
let mut fish_head = (1..number_of_arguments)
- .format_with("", |i, f| f(&format_args!("${{{}:_}}, ", i)))
+ .format_with("", |i, f| f(&format_args!("${{{i}:_}}, ")))
.to_string();
// tabstop 0 is a special case and always the last one
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
index 2853d1d1b..57cfa17cc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -123,20 +123,20 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
let lhs_range = lhs.syntax().text_range();
let not_lhs = invert_boolean_expression(lhs);
- edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
+ edit.replace(lhs_range, format!("!({not_lhs}"));
}
if let Some(rhs) = terms.pop_back() {
let rhs_range = rhs.syntax().text_range();
let not_rhs = invert_boolean_expression(rhs);
- edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
+ edit.replace(rhs_range, format!("{not_rhs})"));
}
for term in terms {
let term_range = term.syntax().text_range();
let not_term = invert_boolean_expression(term);
- edit.replace(term_range, not_term.syntax().text());
+ edit.replace(term_range, not_term.to_string());
}
}
},
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
index 678dc877d..a689270bc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
@@ -127,10 +127,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
.sort_by_key(|import| Reverse(relevance_score(ctx, import, current_module.as_ref())));
for import in proposed_imports {
+ let import_path = import.import_path;
+
acc.add_group(
&group_label,
AssistId("auto_import", AssistKind::QuickFix),
- format!("Import `{}`", import.import_path),
+ format!("Import `{import_path}`"),
range,
|builder| {
let scope = match scope.clone() {
@@ -138,7 +140,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
};
- insert_use(&scope, mod_path_to_ast(&import.import_path), &ctx.config.insert_use);
+ insert_use(&scope, mod_path_to_ast(&import_path), &ctx.config.insert_use);
},
);
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs
index f171dd81a..312cb65ab 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs
@@ -54,16 +54,17 @@ fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
let indent_spaces = indentation.to_string();
let output = lines
- .map(|l| l.trim_start_matches(&indent_spaces))
- .map(|l| {
+ .map(|line| {
+ let line = line.trim_start_matches(&indent_spaces);
+
// Don't introduce trailing whitespace
- if l.is_empty() {
+ if line.is_empty() {
line_prefix.to_string()
} else {
- format!("{} {}", line_prefix, l.trim_start_matches(&indent_spaces))
+ format!("{line_prefix} {line}")
}
})
- .join(&format!("\n{}", indent_spaces));
+ .join(&format!("\n{indent_spaces}"));
edit.replace(target, output)
},
@@ -96,7 +97,7 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
let block_prefix =
CommentKind { shape: CommentShape::Block, ..comment.kind() }.prefix();
- let output = format!("{}\n{}\n{}*/", block_prefix, block_comment_body, indentation);
+ let output = format!("{block_prefix}\n{block_comment_body}\n{indentation}*/");
edit.replace(target, output)
},
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs
index 9060696cd..ff2195f7e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs
@@ -32,19 +32,19 @@ pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_>
}
let mut converted = match target_radix {
- Radix::Binary => format!("0b{:b}", value),
- Radix::Octal => format!("0o{:o}", value),
+ Radix::Binary => format!("0b{value:b}"),
+ Radix::Octal => format!("0o{value:o}"),
Radix::Decimal => value.to_string(),
- Radix::Hexadecimal => format!("0x{:X}", value),
+ Radix::Hexadecimal => format!("0x{value:X}"),
};
- let label = format!("Convert {} to {}{}", literal, converted, suffix.unwrap_or_default());
-
// Appends the type suffix back into the new literal if it exists.
if let Some(suffix) = suffix {
converted.push_str(suffix);
}
+ let label = format!("Convert {literal} to {converted}");
+
acc.add_group(
&group_id,
AssistId("convert_integer_literal", AssistKind::RefactorInline),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
index 95d11abe8..872b52c98 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
@@ -86,9 +86,9 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -
impl_.syntax().text_range(),
|builder| {
builder.replace(src_type.syntax().text_range(), dest_type.to_string());
- builder.replace(ast_trait.syntax().text_range(), format!("From<{}>", src_type));
+ builder.replace(ast_trait.syntax().text_range(), format!("From<{src_type}>"));
builder.replace(into_fn_return.syntax().text_range(), "-> Self");
- builder.replace(into_fn_params.syntax().text_range(), format!("(val: {})", src_type));
+ builder.replace(into_fn_params.syntax().text_range(), format!("(val: {src_type})"));
builder.replace(into_fn_name.syntax().text_range(), "from");
for s in selfs {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
index 2cf370c09..80eecf4a0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -119,19 +119,19 @@ pub(crate) fn convert_for_loop_with_for_each(
{
// We have either "for x in &col" and col implements a method called iter
// or "for x in &mut col" and col implements a method called iter_mut
- format_to!(buf, "{}.{}()", expr_behind_ref, method);
+ format_to!(buf, "{expr_behind_ref}.{method}()");
} else if let ast::Expr::RangeExpr(..) = iterable {
// range expressions need to be parenthesized for the syntax to be correct
- format_to!(buf, "({})", iterable);
+ format_to!(buf, "({iterable})");
} else if impls_core_iter(&ctx.sema, &iterable) {
- format_to!(buf, "{}", iterable);
+ format_to!(buf, "{iterable}");
} else if let ast::Expr::RefExpr(_) = iterable {
- format_to!(buf, "({}).into_iter()", iterable);
+ format_to!(buf, "({iterable}).into_iter()");
} else {
- format_to!(buf, "{}.into_iter()", iterable);
+ format_to!(buf, "{iterable}.into_iter()");
}
- format_to!(buf, ".for_each(|{}| {});", pat, body);
+ format_to!(buf, ".for_each(|{pat}| {body});");
builder.replace(for_loop.syntax().text_range(), buf)
},
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
index 00095de25..c82a3b530 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
@@ -80,7 +80,7 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String {
.map(
|(ident, ismut)| {
if *ismut && addmut {
- format!("mut {}", ident)
+ format!("mut {ident}")
} else {
ident.to_string()
}
@@ -93,7 +93,7 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String {
} else if binders.len() == 1 {
vars
} else {
- format!("({})", vars)
+ format!("({vars})")
}
}
@@ -153,7 +153,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'
let only_expr = let_else_block.statements().next().is_none();
let branch2 = match &let_else_block.tail_expr() {
- Some(tail) if only_expr => format!("{},", tail.syntax().text()),
+ Some(tail) if only_expr => format!("{tail},"),
_ => let_else_block.syntax().text().to_string(),
};
let replace = if binders.is_empty() {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
new file mode 100644
index 000000000..5bf04a3ad
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -0,0 +1,413 @@
+use ide_db::defs::{Definition, NameRefClass};
+use syntax::{
+ ast::{self, HasName},
+ ted, AstNode, SyntaxNode,
+};
+
+use crate::{
+ assist_context::{AssistContext, Assists},
+ AssistId, AssistKind,
+};
+
+// Assist: convert_match_to_let_else
+//
+// Converts let statement with match initializer to let-else statement.
+//
+// ```
+// # //- minicore: option
+// fn foo(opt: Option<()>) {
+// let val = $0match opt {
+// Some(it) => it,
+// None => return,
+// };
+// }
+// ```
+// ->
+// ```
+// fn foo(opt: Option<()>) {
+// let Some(val) = opt else { return };
+// }
+// ```
+pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let let_stmt: ast::LetStmt = ctx.find_node_at_offset()?;
+ let binding = find_binding(let_stmt.pat()?)?;
+
+ let initializer = match let_stmt.initializer() {
+ Some(ast::Expr::MatchExpr(it)) => it,
+ _ => return None,
+ };
+ let initializer_expr = initializer.expr()?;
+
+ let (extracting_arm, diverging_arm) = match find_arms(ctx, &initializer) {
+ Some(it) => it,
+ None => return None,
+ };
+ if extracting_arm.guard().is_some() {
+ cov_mark::hit!(extracting_arm_has_guard);
+ return None;
+ }
+
+ let diverging_arm_expr = diverging_arm.expr()?;
+ let extracting_arm_pat = extracting_arm.pat()?;
+ let extracted_variable = find_extracted_variable(ctx, &extracting_arm)?;
+
+ acc.add(
+ AssistId("convert_match_to_let_else", AssistKind::RefactorRewrite),
+ "Convert match to let-else",
+ let_stmt.syntax().text_range(),
+ |builder| {
+ let extracting_arm_pat = rename_variable(&extracting_arm_pat, extracted_variable, binding);
+ builder.replace(
+ let_stmt.syntax().text_range(),
+ format!("let {extracting_arm_pat} = {initializer_expr} else {{ {diverging_arm_expr} }};")
+ )
+ },
+ )
+}
+
+// Given a pattern, find the name introduced to the surrounding scope.
+fn find_binding(pat: ast::Pat) -> Option<ast::IdentPat> {
+ if let ast::Pat::IdentPat(ident) = pat {
+ Some(ident)
+ } else {
+ None
+ }
+}
+
+// Given a match expression, find extracting and diverging arms.
+fn find_arms(
+ ctx: &AssistContext<'_>,
+ match_expr: &ast::MatchExpr,
+) -> Option<(ast::MatchArm, ast::MatchArm)> {
+ let arms = match_expr.match_arm_list()?.arms().collect::<Vec<_>>();
+ if arms.len() != 2 {
+ return None;
+ }
+
+ let mut extracting = None;
+ let mut diverging = None;
+ for arm in arms {
+ if ctx.sema.type_of_expr(&arm.expr().unwrap()).unwrap().original().is_never() {
+ diverging = Some(arm);
+ } else {
+ extracting = Some(arm);
+ }
+ }
+
+ match (extracting, diverging) {
+ (Some(extracting), Some(diverging)) => Some((extracting, diverging)),
+ _ => {
+ cov_mark::hit!(non_diverging_match);
+ None
+ }
+ }
+}
+
+// Given an extracting arm, find the extracted variable.
+fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option<ast::Name> {
+ match arm.expr()? {
+ ast::Expr::PathExpr(path) => {
+ let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?;
+ match NameRefClass::classify(&ctx.sema, &name_ref)? {
+ NameRefClass::Definition(Definition::Local(local)) => {
+ let source = local.source(ctx.db()).value.left()?;
+ Some(source.name()?)
+ }
+ _ => None,
+ }
+ }
+ _ => {
+ cov_mark::hit!(extracting_arm_is_not_an_identity_expr);
+ return None;
+ }
+ }
+}
+
+// Rename `extracted` with `binding` in `pat`.
+fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::IdentPat) -> SyntaxNode {
+ let syntax = pat.syntax().clone_for_update();
+ let extracted_syntax = syntax.covering_element(extracted.syntax().text_range());
+
+ // If `extracted` variable is a record field, we should rename it to `binding`,
+ // otherwise we just need to replace `extracted` with `binding`.
+
+ if let Some(record_pat_field) = extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
+ {
+ if let Some(name_ref) = record_pat_field.field_name() {
+ ted::replace(
+ record_pat_field.syntax(),
+ ast::make::record_pat_field(ast::make::name_ref(&name_ref.text()), binding.into())
+ .syntax()
+ .clone_for_update(),
+ );
+ }
+ } else {
+ ted::replace(extracted_syntax, binding.syntax().clone_for_update());
+ }
+
+ syntax
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ use super::*;
+
+ #[test]
+ fn should_not_be_applicable_for_non_diverging_match() {
+ cov_mark::check!(non_diverging_match);
+ check_assist_not_applicable(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+ let val = $0match opt {
+ Some(it) => it,
+ None => (),
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn should_not_be_applicable_if_extracting_arm_is_not_an_identity_expr() {
+ cov_mark::check_count!(extracting_arm_is_not_an_identity_expr, 2);
+ check_assist_not_applicable(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+fn foo(opt: Option<i32>) {
+ let val = $0match opt {
+ Some(it) => it + 1,
+ None => return,
+ };
+}
+"#,
+ );
+
+ check_assist_not_applicable(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+ let val = $0match opt {
+ Some(it) => {
+ let _ = 1 + 1;
+ it
+ },
+ None => return,
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn should_not_be_applicable_if_extracting_arm_has_guard() {
+ cov_mark::check!(extracting_arm_has_guard);
+ check_assist_not_applicable(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+ let val = $0match opt {
+ Some(it) if 2 > 1 => it,
+ None => return,
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn basic_pattern() {
+ check_assist(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+ let val = $0match opt {
+ Some(it) => it,
+ None => return,
+ };
+}
+ "#,
+ r#"
+fn foo(opt: Option<()>) {
+ let Some(val) = opt else { return };
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn keeps_modifiers() {
+ check_assist(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+ let ref mut val = $0match opt {
+ Some(it) => it,
+ None => return,
+ };
+}
+ "#,
+ r#"
+fn foo(opt: Option<()>) {
+ let Some(ref mut val) = opt else { return };
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn nested_pattern() {
+ check_assist(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option, result
+fn foo(opt: Option<Result<()>>) {
+ let val = $0match opt {
+ Some(Ok(it)) => it,
+ _ => return,
+ };
+}
+ "#,
+ r#"
+fn foo(opt: Option<Result<()>>) {
+ let Some(Ok(val)) = opt else { return };
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn works_with_any_diverging_block() {
+ check_assist(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+ loop {
+ let val = $0match opt {
+ Some(it) => it,
+ None => break,
+ };
+ }
+}
+ "#,
+ r#"
+fn foo(opt: Option<()>) {
+ loop {
+ let Some(val) = opt else { break };
+ }
+}
+ "#,
+ );
+
+ check_assist(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+fn foo(opt: Option<()>) {
+ loop {
+ let val = $0match opt {
+ Some(it) => it,
+ None => continue,
+ };
+ }
+}
+ "#,
+ r#"
+fn foo(opt: Option<()>) {
+ loop {
+ let Some(val) = opt else { continue };
+ }
+}
+ "#,
+ );
+
+ check_assist(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+fn panic() -> ! {}
+
+fn foo(opt: Option<()>) {
+ loop {
+ let val = $0match opt {
+ Some(it) => it,
+ None => panic(),
+ };
+ }
+}
+ "#,
+ r#"
+fn panic() -> ! {}
+
+fn foo(opt: Option<()>) {
+ loop {
+ let Some(val) = opt else { panic() };
+ }
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn struct_pattern() {
+ check_assist(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn foo(opt: Option<Point>) {
+ let val = $0match opt {
+ Some(Point { x: 0, y }) => y,
+ _ => return,
+ };
+}
+ "#,
+ r#"
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn foo(opt: Option<Point>) {
+ let Some(Point { x: 0, y: val }) = opt else { return };
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn renames_whole_binding() {
+ check_assist(
+ convert_match_to_let_else,
+ r#"
+//- minicore: option
+fn foo(opt: Option<i32>) -> Option<i32> {
+ let val = $0match opt {
+ it @ Some(42) => it,
+ _ => return None,
+ };
+ val
+}
+ "#,
+ r#"
+fn foo(opt: Option<i32>) -> Option<i32> {
+ let val @ Some(42) = opt else { return None };
+ val
+}
+ "#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
index cb75619ce..b97be34c5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -129,32 +129,15 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
}
Some((path, bound_ident)) => {
// If-let.
- let match_expr = {
- let happy_arm = {
- let pat = make::tuple_struct_pat(
- path,
- once(make::ext::simple_ident_pat(make::name("it")).into()),
- );
- let expr = {
- let path = make::ext::ident_path("it");
- make::expr_path(path)
- };
- make::match_arm(once(pat.into()), None, expr)
- };
-
- let sad_arm = make::match_arm(
- // FIXME: would be cool to use `None` or `Err(_)` if appropriate
- once(make::wildcard_pat().into()),
- None,
- early_expression,
- );
-
- make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
- };
-
- let let_stmt = make::let_stmt(bound_ident, None, Some(match_expr));
- let let_stmt = let_stmt.indent(if_indent_level);
- let_stmt.syntax().clone_for_update()
+ let pat = make::tuple_struct_pat(path, once(bound_ident));
+ let let_else_stmt = make::let_else_stmt(
+ pat.into(),
+ None,
+ cond_expr,
+ ast::make::tail_only_block_expr(early_expression),
+ );
+ let let_else_stmt = let_else_stmt.indent(if_indent_level);
+ let_else_stmt.syntax().clone_for_update()
}
};
@@ -238,10 +221,7 @@ fn main(n: Option<String>) {
r#"
fn main(n: Option<String>) {
bar();
- let n = match n {
- Some(it) => it,
- _ => return,
- };
+ let Some(n) = n else { return };
foo(n);
// comment
@@ -264,10 +244,7 @@ fn main() {
"#,
r#"
fn main() {
- let x = match Err(92) {
- Ok(it) => it,
- _ => return,
- };
+ let Ok(x) = Err(92) else { return };
foo(x);
}
"#,
@@ -292,10 +269,7 @@ fn main(n: Option<String>) {
r#"
fn main(n: Option<String>) {
bar();
- let n = match n {
- Some(it) => it,
- _ => return,
- };
+ let Some(n) = n else { return };
foo(n);
// comment
@@ -323,10 +297,7 @@ fn main(n: Option<String>) {
r#"
fn main(n: Option<String>) {
bar();
- let mut n = match n {
- Some(it) => it,
- _ => return,
- };
+ let Some(mut n) = n else { return };
foo(n);
// comment
@@ -354,10 +325,7 @@ fn main(n: Option<&str>) {
r#"
fn main(n: Option<&str>) {
bar();
- let ref n = match n {
- Some(it) => it,
- _ => return,
- };
+ let Some(ref n) = n else { return };
foo(n);
// comment
@@ -412,10 +380,7 @@ fn main() {
r#"
fn main() {
while true {
- let n = match n {
- Some(it) => it,
- _ => continue,
- };
+ let Some(n) = n else { continue };
foo(n);
bar();
}
@@ -469,10 +434,7 @@ fn main() {
r#"
fn main() {
loop {
- let n = match n {
- Some(it) => it,
- _ => continue,
- };
+ let Some(n) = n else { continue };
foo(n);
bar();
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index d8f522708..92e091fca 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -226,7 +226,13 @@ fn edit_field_references(
}
fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Name> {
- fields.enumerate().map(|(i, _)| ast::make::name(&format!("field{}", i + 1))).collect()
+ fields
+ .enumerate()
+ .map(|(i, _)| {
+ let idx = i + 1;
+ ast::make::name(&format!("field{idx}"))
+ })
+ .collect()
}
#[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
index 54a7f480a..b1b0f587c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
@@ -58,16 +58,16 @@ pub(crate) fn convert_two_arm_bool_match_to_matches_macro(
target_range,
|builder| {
let mut arm_str = String::new();
- if let Some(ref pat) = first_arm.pat() {
+ if let Some(pat) = &first_arm.pat() {
arm_str += &pat.to_string();
}
- if let Some(ref guard) = first_arm.guard() {
- arm_str += &format!(" {}", &guard.to_string());
+ if let Some(guard) = &first_arm.guard() {
+ arm_str += &format!(" {guard}");
}
if invert_matches {
- builder.replace(target_range, format!("!matches!({}, {})", expr, arm_str));
+ builder.replace(target_range, format!("!matches!({expr}, {arm_str})"));
} else {
- builder.replace(target_range, format!("matches!({}, {})", expr, arm_str));
+ builder.replace(target_range, format!("matches!({expr}, {arm_str})"));
}
},
)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
index dc581ff3b..31c2ce7c1 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
@@ -133,7 +133,7 @@ fn generate_name(
_usages: &Option<UsageSearchResult>,
) -> String {
// FIXME: detect if name already used
- format!("_{}", index)
+ format!("_{index}")
}
enum RefType {
@@ -168,12 +168,12 @@ fn edit_tuple_assignment(
let add_cursor = |text: &str| {
// place cursor on first tuple item
let first_tuple = &data.field_names[0];
- text.replacen(first_tuple, &format!("$0{}", first_tuple), 1)
+ text.replacen(first_tuple, &format!("$0{first_tuple}"), 1)
};
// with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)`
if in_sub_pattern {
- let text = format!(" @ {}", tuple_pat);
+ let text = format!(" @ {tuple_pat}");
match ctx.config.snippet_cap {
Some(cap) => {
let snip = add_cursor(&text);
@@ -314,9 +314,9 @@ struct RefData {
impl RefData {
fn format(&self, field_name: &str) -> String {
match (self.needs_deref, self.needs_parentheses) {
- (true, true) => format!("(*{})", field_name),
- (true, false) => format!("*{}", field_name),
- (false, true) => format!("({})", field_name),
+ (true, true) => format!("(*{field_name})"),
+ (true, false) => format!("*{field_name}"),
+ (false, true) => format!("({field_name})"),
(false, false) => field_name.to_string(),
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index d6c8ea785..c1e2f19ab 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -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", "{")
}
@@ -181,7 +182,7 @@ fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef
let mut counter = 0;
while names_in_scope.contains(&name) {
counter += 1;
- name = format!("{}{}", &default_name, counter)
+ name = format!("{default_name}{counter}")
}
make::name_ref(&name)
}
@@ -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);
@@ -1291,19 +1323,23 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St
match fun.outliving_locals.as_slice() {
[] => {}
[var] => {
- format_to!(buf, "let {}{} = ", mut_modifier(var), var.local.name(ctx.db()))
+ let modifier = mut_modifier(var);
+ let name = var.local.name(ctx.db());
+ format_to!(buf, "let {modifier}{name} = ")
}
vars => {
buf.push_str("let (");
let bindings = vars.iter().format_with(", ", |local, f| {
- f(&format_args!("{}{}", mut_modifier(local), local.local.name(ctx.db())))
+ let modifier = mut_modifier(local);
+ let name = local.local.name(ctx.db());
+ f(&format_args!("{modifier}{name}"))
});
- format_to!(buf, "{}", bindings);
+ format_to!(buf, "{bindings}");
buf.push_str(") = ");
}
}
- format_to!(buf, "{}", expr);
+ format_to!(buf, "{expr}");
let insert_comma = fun
.body
.parent()
@@ -1447,6 +1483,8 @@ fn format_function(
new_indent: IndentLevel,
) -> String {
let mut fn_def = String::new();
+
+ let fun_name = &fun.name;
let params = fun.make_param_list(ctx, module);
let ret_ty = fun.make_ret_ty(ctx, module);
let body = make_body(ctx, old_indent, new_indent, fun);
@@ -1454,42 +1492,28 @@ fn format_function(
let async_kw = if fun.control_flow.is_async { "async " } else { "" };
let unsafe_kw = if fun.control_flow.is_unsafe { "unsafe " } else { "" };
let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun);
+
+ format_to!(fn_def, "\n\n{new_indent}{const_kw}{async_kw}{unsafe_kw}");
match ctx.config.snippet_cap {
- Some(_) => format_to!(
- fn_def,
- "\n\n{}{}{}{}fn $0{}",
- new_indent,
- const_kw,
- async_kw,
- unsafe_kw,
- fun.name,
- ),
- None => format_to!(
- fn_def,
- "\n\n{}{}{}{}fn {}",
- new_indent,
- const_kw,
- async_kw,
- unsafe_kw,
- fun.name,
- ),
+ Some(_) => format_to!(fn_def, "fn $0{fun_name}"),
+ None => format_to!(fn_def, "fn {fun_name}"),
}
if let Some(generic_params) = generic_params {
- format_to!(fn_def, "{}", generic_params);
+ format_to!(fn_def, "{generic_params}");
}
- format_to!(fn_def, "{}", params);
+ format_to!(fn_def, "{params}");
if let Some(ret_ty) = ret_ty {
- format_to!(fn_def, " {}", ret_ty);
+ format_to!(fn_def, " {ret_ty}");
}
if let Some(where_clause) = where_clause {
- format_to!(fn_def, " {}", where_clause);
+ format_to!(fn_def, " {where_clause}");
}
- format_to!(fn_def, " {}", body);
+ format_to!(fn_def, " {body}");
fn_def
}
@@ -5060,6 +5084,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(
extract_function,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
index 897980c66..56834394a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
@@ -127,7 +127,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
for item in items_to_be_processed {
let item = item.indent(IndentLevel(1));
let mut indented_item = String::new();
- format_to!(indented_item, "{}{}", new_item_indent, item.to_string());
+ format_to!(indented_item, "{new_item_indent}{item}");
body_items.push(indented_item);
}
@@ -137,30 +137,28 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
let mut impl_body_def = String::new();
if let Some(self_ty) = impl_.self_ty() {
- format_to!(
- impl_body_def,
- "{}impl {} {{\n{}\n{}}}",
- old_item_indent + 1,
- self_ty.to_string(),
- body,
- old_item_indent + 1
- );
-
+ {
+ let impl_indent = old_item_indent + 1;
+ format_to!(
+ impl_body_def,
+ "{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}",
+ );
+ }
body = impl_body_def;
// Add the import for enum/struct corresponding to given impl block
module.make_use_stmt_of_node_with_super(self_ty.syntax());
for item in module.use_items {
- let mut indented_item = String::new();
- format_to!(indented_item, "{}{}", old_item_indent + 1, item.to_string());
- body = format!("{}\n\n{}", indented_item, body);
+ let item_indent = old_item_indent + 1;
+ body = format!("{item_indent}{item}\n\n{body}");
}
}
}
let mut module_def = String::new();
- format_to!(module_def, "mod {} {{\n{}\n{}}}", module.name, body, old_item_indent);
+ let module_name = module.name;
+ format_to!(module_def, "mod {module_name} {{\n{body}\n{old_item_indent}}}");
let mut usages_to_be_updated_for_curr_file = vec![];
for usages_to_be_updated_for_file in usages_to_be_processed {
@@ -199,7 +197,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
builder.delete(range);
}
- builder.insert(impl_.syntax().text_range().end(), format!("\n\n{}", module_def));
+ builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}"));
} else {
builder.replace(module.text_range, module_def)
}
@@ -343,9 +341,10 @@ impl Module {
&& !self.text_range.contains_range(desc.text_range())
{
if let Some(name_ref) = ast::NameRef::cast(desc) {
+ let mod_name = self.name;
return Some((
name_ref.syntax().text_range(),
- format!("{}::{}", self.name, name_ref),
+ format!("{mod_name}::{name_ref}"),
));
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
index 970e948df..b4e10667b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -296,10 +296,14 @@ fn create_struct_def(
fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList>) -> Option<()> {
let name = variant.name()?;
- let ty = generics
+ let generic_args = generics
.filter(|generics| generics.generic_params().count() > 0)
- .map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args())))
- .unwrap_or_else(|| make::ty(&name.text()));
+ .map(|generics| generics.to_generic_args());
+ // FIXME: replace with a `ast::make` constructor
+ let ty = match generic_args {
+ Some(generic_args) => make::ty(&format!("{name}{generic_args}")),
+ None => make::ty(&name.text()),
+ };
// change from a record to a tuple field list
let tuple_field = make::tuple_field(None, ty);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
index 03aa8601d..3116935fc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -1,8 +1,7 @@
use either::Either;
use ide_db::syntax_helpers::node_ext::walk_ty;
-use itertools::Itertools;
use syntax::{
- ast::{self, edit::IndentLevel, AstNode, HasGenericParams, HasName},
+ ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName},
match_ast,
};
@@ -64,41 +63,29 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
known_generics.extend(it.generic_params());
}
let generics = collect_used_generics(&ty, &known_generics);
+ let generic_params =
+ generics.map(|it| make::generic_param_list(it.into_iter().cloned()));
- let replacement = if !generics.is_empty() {
- format!(
- "Type<{}>",
- generics.iter().format_with(", ", |generic, f| {
- match generic {
- ast::GenericParam::ConstParam(cp) => f(&cp.name().unwrap()),
- ast::GenericParam::LifetimeParam(lp) => f(&lp.lifetime().unwrap()),
- ast::GenericParam::TypeParam(tp) => f(&tp.name().unwrap()),
- }
- })
- )
- } else {
- String::from("Type")
- };
+ let ty_args = generic_params
+ .as_ref()
+ .map_or(String::new(), |it| it.to_generic_args().to_string());
+ let replacement = format!("Type{ty_args}");
builder.replace(target, replacement);
let indent = IndentLevel::from_node(node);
- let generics = if !generics.is_empty() {
- format!("<{}>", generics.iter().format(", "))
- } else {
- String::new()
- };
+ let generic_params = generic_params.map_or(String::new(), |it| it.to_string());
match ctx.config.snippet_cap {
Some(cap) => {
builder.insert_snippet(
cap,
insert_pos,
- format!("type $0Type{} = {};\n\n{}", generics, ty, indent),
+ format!("type $0Type{generic_params} = {ty};\n\n{indent}"),
);
}
None => {
builder.insert(
insert_pos,
- format!("type Type{} = {};\n\n{}", generics, ty, indent),
+ format!("type Type{generic_params} = {ty};\n\n{indent}"),
);
}
}
@@ -109,7 +96,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
fn collect_used_generics<'gp>(
ty: &ast::Type,
known_generics: &'gp [ast::GenericParam],
-) -> Vec<&'gp ast::GenericParam> {
+) -> Option<Vec<&'gp ast::GenericParam>> {
// can't use a closure -> closure here cause lifetime inference fails for that
fn find_lifetime(text: &str) -> impl Fn(&&ast::GenericParam) -> bool + '_ {
move |gp: &&ast::GenericParam| match gp {
@@ -198,7 +185,8 @@ fn collect_used_generics<'gp>(
ast::GenericParam::LifetimeParam(_) => 0,
ast::GenericParam::TypeParam(_) => 1,
});
- generics
+
+ Some(generics).filter(|it| it.len() > 0)
}
#[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index 3596b6f82..a738deffb 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -91,13 +91,13 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
match anchor {
Anchor::Before(_) | Anchor::Replace(_) => {
- format_to!(buf, "let {}{} = {}", var_modifier, var_name, reference_modifier)
+ format_to!(buf, "let {var_modifier}{var_name} = {reference_modifier}")
}
Anchor::WrapInBlock(_) => {
- format_to!(buf, "{{ let {} = {}", var_name, reference_modifier)
+ format_to!(buf, "{{ let {var_name} = {reference_modifier}")
}
};
- format_to!(buf, "{}", to_extract.syntax());
+ format_to!(buf, "{to_extract}");
if let Anchor::Replace(stmt) = anchor {
cov_mark::hit!(test_extract_var_expr_stmt);
@@ -107,8 +107,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
match ctx.config.snippet_cap {
Some(cap) => {
let snip = buf.replace(
- &format!("let {}{}", var_modifier, var_name),
- &format!("let {}$0{}", var_modifier, var_name),
+ &format!("let {var_modifier}{var_name}"),
+ &format!("let {var_modifier}$0{var_name}"),
);
edit.replace_snippet(cap, expr_range, snip)
}
@@ -135,8 +135,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
match ctx.config.snippet_cap {
Some(cap) => {
let snip = buf.replace(
- &format!("let {}{}", var_modifier, var_name),
- &format!("let {}$0{}", var_modifier, var_name),
+ &format!("let {var_modifier}{var_name}"),
+ &format!("let {var_modifier}$0{var_name}"),
);
edit.insert_snippet(cap, offset, snip)
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
index b33846f54..d9e00435e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
@@ -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())?;
@@ -57,8 +61,8 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
let assist_label = match target_name {
- None => format!("Change visibility to {}", missing_visibility),
- Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
+ None => format!("Change visibility to {missing_visibility}"),
+ Some(name) => format!("Change visibility of {name} to {missing_visibility}"),
};
acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
@@ -68,15 +72,15 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
Some(current_visibility) => builder.replace_snippet(
cap,
current_visibility.syntax().text_range(),
- format!("$0{}", missing_visibility),
+ format!("$0{missing_visibility}"),
),
- None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
+ None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")),
},
None => match current_visibility {
Some(current_visibility) => {
builder.replace(current_visibility.syntax().text_range(), missing_visibility)
}
- None => builder.insert(offset, format!("{} ", missing_visibility)),
+ None => builder.insert(offset, format!("{missing_visibility} ")),
},
}
})
@@ -114,7 +118,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>
let target_name = record_field_def.name(ctx.db());
let assist_label =
- format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
+ format!("Change visibility of {parent_name}.{target_name} to {missing_visibility}");
acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
builder.edit_file(target_file);
@@ -123,15 +127,15 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>
Some(current_visibility) => builder.replace_snippet(
cap,
current_visibility.syntax().text_range(),
- format!("$0{}", missing_visibility),
+ format!("$0{missing_visibility}"),
),
- None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
+ None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")),
},
None => match current_visibility {
Some(current_visibility) => {
builder.replace(current_visibility.syntax().text_range(), missing_visibility)
}
- None => builder.insert(offset, format!("{} ", missing_visibility)),
+ None => builder.insert(offset, format!("{missing_visibility} ")),
},
}
})
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
index bdd3cf4f0..c9aa41c84 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
@@ -124,6 +124,7 @@ fn generate_enum_projection_method(
happy_case,
sad_case,
} = props;
+
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
let variant_name = variant.name()?;
let parent_enum = ast::Adt::Enum(variant.parent_enum());
@@ -144,7 +145,7 @@ fn generate_enum_projection_method(
ast::StructKind::Unit => return None,
};
- let fn_name = format!("{}_{}", fn_name_prefix, &to_lower_snake_case(&variant_name.text()));
+ let fn_name = format!("{fn_name_prefix}_{}", &to_lower_snake_case(&variant_name.text()));
// Return early if we've found an existing new fn
let impl_def = find_struct_impl(ctx, &parent_enum, &[fn_name.clone()])?;
@@ -156,15 +157,25 @@ fn generate_enum_projection_method(
assist_description,
target,
|builder| {
- let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} "));
+ let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v));
+
+ let field_type_syntax = field_type.syntax();
+
+ let must_use = if ctx.config.assist_emit_must_use {
+ "#[must_use]\n "
+ } else {
+ ""
+ };
+
let method = format!(
- " {vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
+ " {must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type_syntax}{return_suffix} {{
if let Self::{variant_name}{pattern_suffix} = self {{
{happy_case}({bound_name})
}} else {{
{sad_case}
}}
- }}");
+ }}"
+ );
add_method_to_adt(builder, &parent_enum, impl_def, &method);
},
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs
index 35cd42908..0bcb57283 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs
@@ -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,
}
",
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index c229127e4..57f198748 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -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!()}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
index 9f51cdaf8..0c546ce5d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
@@ -1,3 +1,5 @@
+use std::collections::BTreeSet;
+
use ast::make;
use either::Either;
use hir::{db::HirDatabase, PathResolution, Semantics, TypeInfo};
@@ -190,10 +192,10 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
PathResolution::Def(hir::ModuleDef::Function(f)) => f,
_ => return None,
};
- (function, format!("Inline `{}`", path))
+ (function, format!("Inline `{path}`"))
}
ast::CallableExpr::MethodCall(call) => {
- (ctx.sema.resolve_method_call(call)?, format!("Inline `{}`", name_ref))
+ (ctx.sema.resolve_method_call(call)?, format!("Inline `{name_ref}`"))
}
};
@@ -373,8 +375,44 @@ fn inline(
})
}
}
+
+ let mut func_let_vars: BTreeSet<String> = BTreeSet::new();
+
+ // grab all of the local variable declarations in the function
+ for stmt in fn_body.statements() {
+ if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) {
+ for has_token in let_stmt.syntax().children_with_tokens() {
+ if let Some(node) = has_token.as_node() {
+ if let Some(ident_pat) = ast::IdentPat::cast(node.to_owned()) {
+ func_let_vars.insert(ident_pat.syntax().text().to_string());
+ }
+ }
+ }
+ }
+ }
+
// Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() {
+ // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
+ let usages: &[ast::PathExpr] = &*usages;
+ let expr: &ast::Expr = expr;
+
+ let insert_let_stmt = || {
+ let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
+ if let Some(stmt_list) = body.stmt_list() {
+ stmt_list.push_front(
+ make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(),
+ )
+ }
+ };
+
+ // check if there is a local var in the function that conflicts with parameter
+ // if it does then emit a let statement and continue
+ if func_let_vars.contains(&expr.syntax().text().to_string()) {
+ insert_let_stmt();
+ continue;
+ }
+
let inline_direct = |usage, replacement: &ast::Expr| {
if let Some(field) = path_expr_as_record_field(usage) {
cov_mark::hit!(inline_call_inline_direct_field);
@@ -383,9 +421,7 @@ fn inline(
ted::replace(usage.syntax(), &replacement.syntax().clone_for_update());
}
};
- // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
- let usages: &[ast::PathExpr] = &*usages;
- let expr: &ast::Expr = expr;
+
match usages {
// inline single use closure arguments
[usage]
@@ -408,18 +444,11 @@ fn inline(
}
// can't inline, emit a let statement
_ => {
- let ty =
- sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
- if let Some(stmt_list) = body.stmt_list() {
- stmt_list.push_front(
- make::let_stmt(pat.clone(), ty, Some(expr.clone()))
- .clone_for_update()
- .into(),
- )
- }
+ insert_let_stmt();
}
}
}
+
if let Some(generic_arg_list) = generic_arg_list.clone() {
if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax()))
{
@@ -1256,4 +1285,37 @@ impl A {
"#,
)
}
+
+ #[test]
+ fn local_variable_shadowing_callers_argument() {
+ check_assist(
+ inline_call,
+ r#"
+fn foo(bar: u32, baz: u32) -> u32 {
+ let a = 1;
+ bar * baz * a * 6
+}
+fn main() {
+ let a = 7;
+ let b = 1;
+ let res = foo$0(a, b);
+}
+"#,
+ r#"
+fn foo(bar: u32, baz: u32) -> u32 {
+ let a = 1;
+ bar * baz * a * 6
+}
+fn main() {
+ let a = 7;
+ let b = 1;
+ let res = {
+ let bar = a;
+ let a = 1;
+ bar * b * a * 6
+ };
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
index 7259d6781..ce44100e3 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
@@ -113,7 +113,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
.collect::<Option<Vec<_>>>()?;
let init_str = initializer_expr.syntax().text().to_string();
- let init_in_paren = format!("({})", &init_str);
+ let init_in_paren = format!("({init_str})");
let target = match target {
ast::NameOrNameRef::Name(it) => it.syntax().text_range(),
@@ -132,7 +132,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
let replacement = if should_wrap { &init_in_paren } else { &init_str };
if ast::RecordExprField::for_field_name(&name).is_some() {
cov_mark::hit!(inline_field_shorthand);
- builder.insert(range.end(), format!(": {}", replacement));
+ builder.insert(range.end(), format!(": {replacement}"));
} else {
builder.replace(range, replacement.clone())
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
index 2fc754e3e..a54dc4f96 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
@@ -127,7 +127,7 @@ fn generate_unique_lifetime_param_name(
Some(type_params) => {
let used_lifetime_params: FxHashSet<_> =
type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect();
- ('a'..='z').map(|it| format!("'{}", it)).find(|it| !used_lifetime_params.contains(it))
+ ('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it))
}
None => Some("'a".to_string()),
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
index c24015b1c..641c90885 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
@@ -78,7 +78,7 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
.join(" | ")
};
- let arm = format!("{} => {},", pats, current_expr.syntax().text());
+ let arm = format!("{pats} => {current_expr},");
if let [first, .., last] = &*arms_to_merge {
let start = first.syntax().text_range().start();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs
index aa710d2ce..11db6ae7f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs
@@ -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));
}
"#,
),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
index a6c85a2b1..1728c03cd 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
@@ -40,11 +40,11 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
let target = source_file.syntax().text_range();
let module_name = module.name(ctx.db())?.to_string();
- let path = format!("../{}.rs", module_name);
+ let path = format!("../{module_name}.rs");
let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
acc.add(
AssistId("move_from_mod_rs", AssistKind::Refactor),
- format!("Convert {}/mod.rs to {}.rs", module_name, module_name),
+ format!("Convert {module_name}/mod.rs to {module_name}.rs"),
target,
|builder| {
builder.move_file(ctx.file_id(), dst);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
index b8f1b36de..ec3281619 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
@@ -133,16 +133,16 @@ pub(crate) fn move_arm_cond_to_match_guard(
};
let then_arm_end = match_arm.syntax().text_range().end();
let indent_level = match_arm.indent_level();
- let spaces = " ".repeat(indent_level.0 as _);
+ let spaces = indent_level;
let mut first = true;
for (cond, block) in conds_blocks {
if !first {
- edit.insert(then_arm_end, format!("\n{}", spaces));
+ edit.insert(then_arm_end, format!("\n{spaces}"));
} else {
first = false;
}
- let guard = format!("{} if {} => ", match_pat, cond.syntax().text());
+ let guard = format!("{match_pat} if {cond} => ");
edit.insert(then_arm_end, guard);
let only_expr = block.statements().next().is_none();
match &block.tail_expr() {
@@ -158,7 +158,7 @@ pub(crate) fn move_arm_cond_to_match_guard(
}
if let Some(e) = tail {
cov_mark::hit!(move_guard_ifelse_else_tail);
- let guard = format!("\n{}{} => ", spaces, match_pat);
+ let guard = format!("\n{spaces}{match_pat} => ");
edit.insert(then_arm_end, guard);
let only_expr = e.statements().next().is_none();
match &e.tail_expr() {
@@ -183,7 +183,7 @@ pub(crate) fn move_arm_cond_to_match_guard(
{
cov_mark::hit!(move_guard_ifelse_has_wildcard);
}
- _ => edit.insert(then_arm_end, format!("\n{}{} => {{}}", spaces, match_pat)),
+ _ => edit.insert(then_arm_end, format!("\n{spaces}{match_pat} => {{}}")),
}
}
},
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
index 7468318a5..a7c605325 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
@@ -52,7 +52,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let mut buf = String::from("./");
match parent_module.name(ctx.db()) {
Some(name) if !parent_module.is_mod_rs(ctx.db()) => {
- format_to!(buf, "{}/", name)
+ format_to!(buf, "{name}/")
}
_ => (),
}
@@ -82,7 +82,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) ->
items
};
- let buf = format!("mod {};", module_name);
+ let buf = format!("mod {module_name};");
let replacement_start = match module_ast.mod_token() {
Some(mod_token) => mod_token.text_range(),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
index a909ce8b2..076d25411 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
@@ -40,11 +40,11 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
let target = source_file.syntax().text_range();
let module_name = module.name(ctx.db())?.to_string();
- let path = format!("./{}/mod.rs", module_name);
+ let path = format!("./{module_name}/mod.rs");
let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
acc.add(
AssistId("move_to_mod_rs", AssistKind::Refactor),
- format!("Convert {}.rs to {}/mod.rs", module_name, module_name),
+ format!("Convert {module_name}.rs to {module_name}/mod.rs"),
target,
|builder| {
builder.move_file(ctx.file_id(), dst);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs
index 424db7437..7e3fef516 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs
@@ -38,7 +38,7 @@ pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext<'_>
converted.push_str(suffix);
let group_id = GroupLabel("Reformat number literal".into());
- let label = format!("Convert {} to {}", literal, converted);
+ let label = format!("Convert {literal} to {converted}");
let range = literal.syntax().text_range();
acc.add_group(
&group_id,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
index e57d1d065..1ea87429c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -54,7 +54,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) ->
acc.add(
AssistId("qualify_method_call", AssistKind::RefactorInline),
- format!("Qualify `{}` method call", ident.text()),
+ format!("Qualify `{ident}` method call"),
range,
|builder| {
qualify_candidate.qualify(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
index 4b2af550b..e759e1561 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
@@ -118,14 +118,14 @@ impl QualifyCandidate<'_> {
match self {
QualifyCandidate::QualifierStart(segment, generics) => {
let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
- replacer(format!("{}{}::{}", import, generics, segment));
+ replacer(format!("{import}{generics}::{segment}"));
}
QualifyCandidate::UnqualifiedName(generics) => {
let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
- replacer(format!("{}{}", import, generics));
+ replacer(format!("{import}{generics}"));
}
QualifyCandidate::TraitAssocItem(qualifier, segment) => {
- replacer(format!("<{} as {}>::{}", qualifier, import, segment));
+ replacer(format!("<{qualifier} as {import}>::{segment}"));
}
QualifyCandidate::TraitMethod(db, mcall_expr) => {
Self::qualify_trait_method(db, mcall_expr, replacer, import, item);
@@ -155,16 +155,11 @@ impl QualifyCandidate<'_> {
hir::Access::Exclusive => make::expr_ref(receiver, true),
hir::Access::Owned => receiver,
};
- replacer(format!(
- "{}::{}{}{}",
- import,
- method_name,
- generics,
- match arg_list {
- Some(args) => make::arg_list(iter::once(receiver).chain(args)),
- None => make::arg_list(iter::once(receiver)),
- }
- ));
+ let arg_list = match arg_list {
+ Some(args) => make::arg_list(iter::once(receiver).chain(args)),
+ None => make::arg_list(iter::once(receiver)),
+ };
+ replacer(format!("{import}::{method_name}{generics}{arg_list}"));
}
Some(())
}
@@ -218,15 +213,17 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
}
}
.text();
- GroupLabel(format!("Qualify {}", name))
+ GroupLabel(format!("Qualify {name}"))
}
fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String {
+ let import_path = &import.import_path;
+
match candidate {
ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => {
- format!("Qualify as `{}`", import.import_path)
+ format!("Qualify as `{import_path}`")
}
- _ => format!("Qualify with `{}`", import.import_path),
+ _ => format!("Qualify with `{import_path}`"),
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
index dbe8cb7bf..c9bc25b27 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
@@ -34,13 +34,10 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
let hashes = "#".repeat(required_hashes(&value).max(1));
if matches!(value, Cow::Borrowed(_)) {
// Avoid replacing the whole string to better position the cursor.
- edit.insert(token.syntax().text_range().start(), format!("r{}", hashes));
+ edit.insert(token.syntax().text_range().start(), format!("r{hashes}"));
edit.insert(token.syntax().text_range().end(), hashes);
} else {
- edit.replace(
- token.syntax().text_range(),
- format!("r{}\"{}\"{}", hashes, value, hashes),
- );
+ edit.replace(token.syntax().text_range(), format!("r{hashes}\"{value}\"{hashes}"));
}
},
)
@@ -83,7 +80,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
}
}
- edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
+ edit.replace(token.syntax().text_range(), format!("\"{escaped}\""));
},
)
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
index afaa7c933..99ae60e07 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -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! {
@@ -102,15 +131,11 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
};
(
macro_call.syntax().text_range(),
- if wrap { format!("({})", expr) } else { expr.to_string() },
+ if wrap { format!("({expr})") } else { expr.to_string() },
)
}
// 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);
})
}
@@ -127,8 +152,8 @@ mod tests {
fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
check_assist(
remove_dbg,
- &format!("fn main() {{\n{}\n}}", ra_fixture_before),
- &format!("fn main() {{\n{}\n}}", ra_fixture_after),
+ &format!("fn main() {{\n{ra_fixture_before}\n}}"),
+ &format!("fn main() {{\n{ra_fixture_after}\n}}"),
);
}
@@ -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)"#);
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 9fd5e1886..6fa15b28e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -124,7 +124,7 @@ fn add_assist(
) -> Option<()> {
let target = attr.syntax().text_range();
let annotated_name = adt.name()?;
- let label = format!("Convert to manual `impl {} for {}`", replace_trait_path, annotated_name);
+ let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`");
acc.add(
AssistId("replace_derive_with_manual_impl", AssistKind::Refactor),
@@ -158,11 +158,8 @@ fn add_assist(
}
}
- builder.insert_snippet(
- cap,
- insert_pos,
- format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)),
- )
+ let rendered = render_snippet(cap, impl_def.syntax(), cursor);
+ builder.insert_snippet(cap, insert_pos, format!("\n\n{rendered}"))
}
};
},
@@ -1022,8 +1019,6 @@ struct Foo {
impl foo::Bar for Foo {
$0type Qux;
- const Baz: usize = 42;
-
const Fez: usize;
fn foo() {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
index 7d91be621..77382056c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
@@ -62,7 +62,7 @@ pub(crate) fn replace_or_with_or_else(acc: &mut Assists, ctx: &AssistContext<'_>
acc.add(
AssistId("replace_or_with_or_else", AssistKind::RefactorRewrite),
- format!("Replace {} with {}", name.text(), replace),
+ format!("Replace {name} with {replace}"),
call.syntax().text_range(),
|builder| {
builder.replace(name.syntax().text_range(), replace);
@@ -138,7 +138,7 @@ pub(crate) fn replace_or_else_with_or(acc: &mut Assists, ctx: &AssistContext<'_>
acc.add(
AssistId("replace_or_else_with_or", AssistKind::RefactorRewrite),
- format!("Replace {} with {}", name.text(), replace),
+ format!("Replace {name} with {replace}"),
call.syntax().text_range(),
|builder| {
builder.replace(name.syntax().text_range(), replace);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
index 521447c26..c177adc7a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
@@ -79,7 +79,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
"Replace turbofish with explicit type",
TextRange::new(initializer_start, turbofish_range.end()),
|builder| {
- builder.insert(ident_range.end(), format!(": {}", returned_type));
+ builder.insert(ident_range.end(), format!(": {returned_type}"));
builder.delete(turbofish_range);
},
);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs
index d5cd2d551..043988322 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs
@@ -44,6 +44,12 @@ pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
if function.body()?.syntax().descendants().find_map(ast::AwaitExpr::cast).is_some() {
return None;
}
+ // Do nothing if the method is a member of trait.
+ if let Some(impl_) = function.syntax().ancestors().nth(2).and_then(ast::Impl::cast) {
+ if let Some(_) = impl_.trait_() {
+ return None;
+ }
+ }
// Remove the `async` keyword plus whitespace after it, if any.
let async_range = {
@@ -254,4 +260,18 @@ pub async fn f(s: &S) { s.f2() }"#,
fn does_not_apply_when_not_on_prototype() {
check_assist_not_applicable(unnecessary_async, "pub async fn f() { $0f2() }")
}
+
+ #[test]
+ fn does_not_apply_on_async_trait_method() {
+ check_assist_not_applicable(
+ unnecessary_async,
+ r#"
+trait Trait {
+ async fn foo();
+}
+impl Trait for () {
+ $0async fn foo() {}
+}"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs
index 25c58d086..d09614c51 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs
@@ -69,13 +69,13 @@ pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
for (pat, ty, expr) in
itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
{
- zipped_decls.push_str(&format!("{}let {pat}: {ty} = {expr};\n", indents))
+ zipped_decls.push_str(&format!("{indents}let {pat}: {ty} = {expr};\n"))
}
edit.replace(parent.text_range(), zipped_decls.trim());
} else {
let mut zipped_decls = String::new();
for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
- zipped_decls.push_str(&format!("{}let {pat} = {expr};\n", indents));
+ zipped_decls.push_str(&format!("{indents}let {pat} = {expr};\n"));
}
edit.replace(parent.text_range(), zipped_decls.trim());
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
index 83446387d..b6c489eb6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
@@ -76,11 +76,11 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext<
match ctx.config.snippet_cap {
Some(cap) => {
- let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
+ let snippet = format!("Result<{type_ref}, ${{0:_}}>");
builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
}
None => builder
- .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
+ .replace(type_ref.syntax().text_range(), format!("Result<{type_ref}, _>")),
}
},
)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index a07318cef..387cc6314 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -120,6 +120,7 @@ mod handlers {
mod convert_into_to_from;
mod convert_iter_for_each_to_for;
mod convert_let_else_to_match;
+ mod convert_match_to_let_else;
mod convert_tuple_struct_to_named_struct;
mod convert_named_struct_to_tuple_struct;
mod convert_to_guarded_return;
@@ -220,6 +221,7 @@ mod handlers {
convert_iter_for_each_to_for::convert_for_loop_with_for_each,
convert_let_else_to_match::convert_let_else_to_match,
convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
+ convert_match_to_let_else::convert_match_to_let_else,
convert_to_guarded_return::convert_to_guarded_return,
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
index f7f2417d0..92ced27c7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
@@ -30,6 +30,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
skip_glob_imports: true,
},
prefer_no_std: false,
+ assist_emit_must_use: false,
};
pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index 2c4000efe..c09317572 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -408,6 +408,27 @@ fn main() {
}
#[test]
+fn doctest_convert_match_to_let_else() {
+ check_doc_test(
+ "convert_match_to_let_else",
+ r#####"
+//- minicore: option
+fn foo(opt: Option<()>) {
+ let val = $0match opt {
+ Some(it) => it,
+ None => return,
+ };
+}
+"#####,
+ r#####"
+fn foo(opt: Option<()>) {
+ let Some(val) = opt else { return };
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_convert_named_struct_to_tuple_struct() {
check_doc_test(
"convert_named_struct_to_tuple_struct",
@@ -720,7 +741,7 @@ mod m {
fn frobnicate() {}
}
fn main() {
- m::frobnicate$0() {}
+ m::frobnicate$0();
}
"#####,
r#####"
@@ -728,7 +749,7 @@ mod m {
$0pub(crate) fn frobnicate() {}
}
fn main() {
- m::frobnicate() {}
+ m::frobnicate();
}
"#####,
)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index db32e7182..68c31b4f8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -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<_>>()
@@ -189,8 +193,8 @@ pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor
let mut placeholder = cursor.node().to_string();
escape(&mut placeholder);
let tab_stop = match cursor {
- Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder),
- Cursor::Before(placeholder) => format!("$0{}", placeholder),
+ Cursor::Replace(placeholder) => format!("${{0:{placeholder}}}"),
+ Cursor::Before(placeholder) => format!("$0{placeholder}"),
};
let mut buf = node.to_string();
@@ -539,17 +543,17 @@ impl ReferenceConversion {
ReferenceConversionType::AsRefSlice => {
let type_argument_name =
self.ty.type_arguments().next().unwrap().display(db).to_string();
- format!("&[{}]", type_argument_name)
+ format!("&[{type_argument_name}]")
}
ReferenceConversionType::Dereferenced => {
let type_argument_name =
self.ty.type_arguments().next().unwrap().display(db).to_string();
- format!("&{}", type_argument_name)
+ format!("&{type_argument_name}")
}
ReferenceConversionType::Option => {
let type_argument_name =
self.ty.type_arguments().next().unwrap().display(db).to_string();
- format!("Option<&{}>", type_argument_name)
+ format!("Option<&{type_argument_name}>")
}
ReferenceConversionType::Result => {
let mut type_arguments = self.ty.type_arguments();
@@ -557,19 +561,19 @@ impl ReferenceConversion {
type_arguments.next().unwrap().display(db).to_string();
let second_type_argument_name =
type_arguments.next().unwrap().display(db).to_string();
- format!("Result<&{}, &{}>", first_type_argument_name, second_type_argument_name)
+ format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>")
}
}
}
pub(crate) fn getter(&self, field_name: String) -> String {
match self.conversion {
- ReferenceConversionType::Copy => format!("self.{}", field_name),
+ ReferenceConversionType::Copy => format!("self.{field_name}"),
ReferenceConversionType::AsRefStr
| ReferenceConversionType::AsRefSlice
| ReferenceConversionType::Dereferenced
| ReferenceConversionType::Option
- | ReferenceConversionType::Result => format!("self.{}.as_ref()", field_name),
+ | ReferenceConversionType::Result => format!("self.{field_name}.as_ref()"),
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
index 7a0c91295..6c87e66c1 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -41,7 +41,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let mut arms = vec![];
for variant in list.variants() {
let name = variant.name()?;
- let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?;
+ let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?;
match variant.field_list() {
// => match self { Self::Name { x } => Self::Name { x: x.clone() } }
@@ -70,7 +70,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let mut pats = vec![];
let mut fields = vec![];
for (i, _) in list.fields().enumerate() {
- let field_name = format!("arg{}", i);
+ let field_name = format!("arg{i}");
let pat = make::ident_pat(false, false, make::name(&field_name));
pats.push(pat.into());
@@ -118,7 +118,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let mut fields = vec![];
for (i, _) in field_list.fields().enumerate() {
let f_path = make::expr_path(make::ext::ident_path("self"));
- let target = make::expr_field(f_path, &format!("{}", i));
+ let target = make::expr_field(f_path, &format!("{i}"));
fields.push(gen_clone_call(target));
}
let struct_name = make::expr_path(make::ext::ident_path("Self"));
@@ -151,7 +151,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let mut arms = vec![];
for variant in list.variants() {
let name = variant.name()?;
- let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?;
+ let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?;
let target = make::expr_path(make::ext::ident_path("f"));
match variant.field_list() {
@@ -159,7 +159,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
// => f.debug_struct(name)
let target = make::expr_path(make::ext::ident_path("f"));
let method = make::name_ref("debug_struct");
- let struct_name = format!("\"{}\"", name);
+ let struct_name = format!("\"{name}\"");
let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
let mut expr = make::expr_method_call(target, method, args);
@@ -173,8 +173,8 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
// => <expr>.field("field_name", field)
let method_name = make::name_ref("field");
- let name = make::expr_literal(&(format!("\"{}\"", field_name))).into();
- let path = &format!("{}", field_name);
+ let name = make::expr_literal(&(format!("\"{field_name}\""))).into();
+ let path = &format!("{field_name}");
let path = make::expr_path(make::ext::ident_path(path));
let args = make::arg_list(vec![name, path]);
expr = make::expr_method_call(expr, method_name, args);
@@ -192,13 +192,13 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
// => f.debug_tuple(name)
let target = make::expr_path(make::ext::ident_path("f"));
let method = make::name_ref("debug_tuple");
- let struct_name = format!("\"{}\"", name);
+ let struct_name = format!("\"{name}\"");
let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
let mut expr = make::expr_method_call(target, method, args);
let mut pats = vec![];
for (i, _) in list.fields().enumerate() {
- let name = format!("arg{}", i);
+ let name = format!("arg{i}");
// create a field pattern for use in `MyStruct(fields..)`
let field_name = make::name(&name);
@@ -222,7 +222,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
arms.push(make::match_arm(Some(pat.into()), None, expr));
}
None => {
- let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into();
+ let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into();
let args = make::arg_list([target, fmt_string]);
let macro_name = make::expr_path(make::ext::ident_path("write"));
let macro_call = make::expr_macro_call(macro_name, args);
@@ -244,7 +244,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
}
ast::Adt::Struct(strukt) => {
- let name = format!("\"{}\"", annotated_name);
+ let name = format!("\"{annotated_name}\"");
let args = make::arg_list(Some(make::expr_literal(&name).into()));
let target = make::expr_path(make::ext::ident_path("f"));
@@ -258,10 +258,10 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let mut expr = make::expr_method_call(target, method, args);
for field in field_list.fields() {
let name = field.name()?;
- let f_name = make::expr_literal(&(format!("\"{}\"", name))).into();
+ let f_name = make::expr_literal(&(format!("\"{name}\""))).into();
let f_path = make::expr_path(make::ext::ident_path("self"));
let f_path = make::expr_ref(f_path, false);
- let f_path = make::expr_field(f_path, &format!("{}", name));
+ let f_path = make::expr_field(f_path, &format!("{name}"));
let args = make::arg_list([f_name, f_path]);
expr = make::expr_method_call(expr, make::name_ref("field"), args);
}
@@ -275,7 +275,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
for (i, _) in field_list.fields().enumerate() {
let f_path = make::expr_path(make::ext::ident_path("self"));
let f_path = make::expr_ref(f_path, false);
- let f_path = make::expr_field(f_path, &format!("{}", i));
+ let f_path = make::expr_field(f_path, &format!("{i}"));
let method = make::name_ref("field");
expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
}
@@ -379,7 +379,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let mut stmts = vec![];
for (i, _) in field_list.fields().enumerate() {
let base = make::expr_path(make::ext::ident_path("self"));
- let target = make::expr_field(base, &format!("{}", i));
+ let target = make::expr_field(base, &format!("{i}"));
stmts.push(gen_hash_call(target));
}
make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
@@ -453,10 +453,10 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
for field in list.fields() {
let field_name = field.name()?.to_string();
- let l_name = &format!("l_{}", field_name);
+ let l_name = &format!("l_{field_name}");
l_fields.push(gen_record_pat_field(&field_name, l_name));
- let r_name = &format!("r_{}", field_name);
+ let r_name = &format!("r_{field_name}");
r_fields.push(gen_record_pat_field(&field_name, r_name));
let lhs = make::expr_path(make::ext::ident_path(l_name));
@@ -484,12 +484,12 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let mut r_fields = vec![];
for (i, _) in list.fields().enumerate() {
- let field_name = format!("{}", i);
+ let field_name = format!("{i}");
- let l_name = format!("l{}", field_name);
+ let l_name = format!("l{field_name}");
l_fields.push(gen_tuple_field(&l_name));
- let r_name = format!("r{}", field_name);
+ let r_name = format!("r{field_name}");
r_fields.push(gen_tuple_field(&r_name));
let lhs = make::expr_path(make::ext::ident_path(&l_name));
@@ -548,7 +548,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
Some(ast::FieldList::TupleFieldList(field_list)) => {
let mut expr = None;
for (i, _) in field_list.fields().enumerate() {
- let idx = format!("{}", i);
+ let idx = format!("{i}");
let lhs = make::expr_path(make::ext::ident_path("self"));
let lhs = make::expr_field(lhs, &idx);
let rhs = make::expr_path(make::ext::ident_path("other"));
@@ -628,7 +628,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
Some(ast::FieldList::TupleFieldList(field_list)) => {
let mut exprs = vec![];
for (i, _) in field_list.fields().enumerate() {
- let idx = format!("{}", i);
+ let idx = format!("{i}");
let lhs = make::expr_path(make::ext::ident_path("self"));
let lhs = make::expr_field(lhs, &idx);
let rhs = make::expr_path(make::ext::ident_path("other"));
diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml
index 75835bce9..11310e2f1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index e82cbfdcb..7384a3f2d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -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()
}
@@ -1163,4 +1182,104 @@ impl Foo for Test {
"#,
);
}
+
+ #[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;
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 9a891cea2..b9bd47f7d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -69,10 +69,6 @@ pub(crate) fn complete_postfix(
}
}
- if !ctx.config.snippets.is_empty() {
- add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
- }
-
let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references());
if let Some(try_enum) = &try_enum {
match try_enum {
@@ -140,6 +136,10 @@ pub(crate) fn complete_postfix(
None => return,
};
+ if !ctx.config.snippets.is_empty() {
+ add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
+ }
+
match try_enum {
Some(try_enum) => match try_enum {
TryEnum::Result => {
@@ -613,4 +613,25 @@ fn main() {
r#"fn main() { log::error!("{}", 2+2) }"#,
);
}
+
+ #[test]
+ fn postfix_custom_snippets_completion_for_references() {
+ check_edit_with_config(
+ CompletionConfig {
+ snippets: vec![Snippet::new(
+ &[],
+ &["ok".into()],
+ &["Ok(${receiver})".into()],
+ "",
+ &[],
+ crate::SnippetScope::Expr,
+ )
+ .unwrap()],
+ ..TEST_CONFIG
+ },
+ "ok",
+ r#"fn main() { &&42.$0 }"#,
+ r#"fn main() { Ok(&&42) }"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 04111ec7e..c142a7305 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -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(),
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
index 5076c6e86..8ed6cb3cf 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
@@ -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)
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
index cf0bcd5c9..f48cce58c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs
index 75d49ff2f..1b8f56187 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs
@@ -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);
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 82b85f2fa..aa5d7e9be 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -446,33 +446,47 @@ impl<'a> FindUsages<'a> {
})
}
- // FIXME: There should be optimization potential here
- // Currently we try to descend everything we find which
- // means we call `Semantics::descend_into_macros` on
- // every textual hit. That function is notoriously
- // expensive even for things that do not get down mapped
- // into macros.
+ let find_nodes = move |name: &str, node: &syntax::SyntaxNode, offset: TextSize| {
+ node.token_at_offset(offset).find(|it| it.text() == name).map(|token| {
+ // FIXME: There should be optimization potential here
+ // Currently we try to descend everything we find which
+ // means we call `Semantics::descend_into_macros` on
+ // every textual hit. That function is notoriously
+ // expensive even for things that do not get down mapped
+ // into macros.
+ sema.descend_into_macros(token).into_iter().filter_map(|it| it.parent())
+ })
+ };
+
for (text, file_id, search_range) in scope_files(sema, &search_scope) {
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
// Search for occurrences of the items name
for offset in match_indices(&text, finder, search_range) {
- for name in sema.find_nodes_at_offset_with_descend(&tree, offset) {
- if match name {
- ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
- ast::NameLike::Name(name) => self.found_name(&name, sink),
- ast::NameLike::Lifetime(lifetime) => self.found_lifetime(&lifetime, sink),
- } {
- return;
+ if let Some(iter) = find_nodes(name, &tree, offset) {
+ for name in iter.filter_map(ast::NameLike::cast) {
+ if match name {
+ ast::NameLike::NameRef(name_ref) => {
+ self.found_name_ref(&name_ref, sink)
+ }
+ ast::NameLike::Name(name) => self.found_name(&name, sink),
+ ast::NameLike::Lifetime(lifetime) => {
+ self.found_lifetime(&lifetime, sink)
+ }
+ } {
+ return;
+ }
}
}
}
// Search for occurrences of the `Self` referring to our type
if let Some((self_ty, finder)) = &include_self_kw_refs {
for offset in match_indices(&text, finder, search_range) {
- for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
- if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
- return;
+ if let Some(iter) = find_nodes("Self", &tree, offset) {
+ for name_ref in iter.filter_map(ast::NameRef::cast) {
+ if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
+ return;
+ }
}
}
}
@@ -493,17 +507,21 @@ impl<'a> FindUsages<'a> {
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
for offset in match_indices(&text, finder, search_range) {
- for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
- if self.found_name_ref(&name_ref, sink) {
- return;
+ if let Some(iter) = find_nodes("super", &tree, offset) {
+ for name_ref in iter.filter_map(ast::NameRef::cast) {
+ if self.found_name_ref(&name_ref, sink) {
+ return;
+ }
}
}
}
if let Some(finder) = &is_crate_root {
for offset in match_indices(&text, finder, search_range) {
- for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
- if self.found_name_ref(&name_ref, sink) {
- return;
+ if let Some(iter) = find_nodes("crate", &tree, offset) {
+ for name_ref in iter.filter_map(ast::NameRef::cast) {
+ if self.found_name_ref(&name_ref, sink) {
+ return;
+ }
}
}
}
@@ -544,9 +562,11 @@ impl<'a> FindUsages<'a> {
let finder = &Finder::new("self");
for offset in match_indices(&text, finder, search_range) {
- for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
- if self.found_self_module_name_ref(&name_ref, sink) {
- return;
+ if let Some(iter) = find_nodes("self", &tree, offset) {
+ for name_ref in iter.filter_map(ast::NameRef::cast) {
+ if self.found_self_module_name_ref(&name_ref, sink) {
+ return;
+ }
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
index ac6c6e8fe..313346ee1 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
@@ -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![["-"]]),
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
index e1d146f4e..7e9a1125d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
index 43ff4ed5a..870c78d1f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -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()
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
index a80299106..d8f2a9de9 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -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
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
index 4b4312475..87531f4ac 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
@@ -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",
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
index 760f51f90..23818d883 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
@@ -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,
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
index 8b9330e04..289ed0458 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
@@ -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};
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index ae299f058..d81e36a1f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -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,
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
index 4baf786c4..7be62a8d9 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml
index 712459a7e..73f202630 100644
--- a/src/tools/rust-analyzer/crates/ide/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index d0be1b3f4..43f7a529b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -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;
"#,
);
}
@@ -1834,4 +1834,86 @@ fn f() {
"#,
);
}
+
+ #[test]
+ fn goto_bin_op_multiple_impl() {
+ check(
+ r#"
+//- minicore: add
+struct S;
+impl core::ops::Add for S {
+ fn add(
+ //^^^
+ ) {}
+}
+impl core::ops::Add<usize> for S {
+ fn add(
+ ) {}
+}
+
+fn f() {
+ S +$0 S
+}
+"#,
+ );
+
+ check(
+ r#"
+//- minicore: add
+struct S;
+impl core::ops::Add for S {
+ fn add(
+ ) {}
+}
+impl core::ops::Add<usize> for S {
+ fn add(
+ //^^^
+ ) {}
+}
+
+fn f() {
+ S +$0 0usize
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn path_call_multiple_trait_impl() {
+ check(
+ r#"
+trait Trait<T> {
+ fn f(_: T);
+}
+impl Trait<i32> for usize {
+ fn f(_: i32) {}
+ //^
+}
+impl Trait<i64> for usize {
+ fn f(_: i64) {}
+}
+fn main() {
+ usize::f$0(0i32);
+}
+"#,
+ );
+
+ check(
+ r#"
+trait Trait<T> {
+ fn f(_: T);
+}
+impl Trait<i32> for usize {
+ fn f(_: i32) {}
+}
+impl Trait<i64> for usize {
+ fn f(_: i64) {}
+ //^
+}
+fn main() {
+ usize::f$0(0i64);
+}
+"#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 3687b597f..838fb18c3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -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 {
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index 34d8bf67a..37384c4e7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -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 =
@@ -2846,48 +2917,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(
InlayHintsConfig { binding_mode_hints: true, ..DISABLED_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) {}
+}
+"#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index 416817ca0..7402e86f3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -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))
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 852a8fd83..fcbf6d8e5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -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 {
@@ -73,20 +73,8 @@ impl MonikerResult {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PackageInformation {
pub name: String,
- pub repo: String,
- pub version: 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 repo: Option<String>,
+ pub version: Option<String>,
}
pub(crate) fn moniker(
@@ -95,7 +83,7 @@ pub(crate) fn moniker(
) -> 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
@@ -256,18 +244,18 @@ pub(crate) fn def_to_moniker(
let (name, repo, version) = match krate.origin(db) {
CrateOrigin::CratesIo { repo, name } => (
name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()),
- repo?,
- krate.version(db)?,
+ repo,
+ krate.version(db),
),
CrateOrigin::Lang(lang) => (
krate.display_name(db)?.canonical_name().to_string(),
- "https://github.com/rust-lang/rust/".to_string(),
- match lang {
+ Some("https://github.com/rust-lang/rust/".to_string()),
+ Some(match lang {
LangCrateOrigin::Other => {
"https://github.com/rust-lang/rust/library/".into()
}
lang => format!("https://github.com/rust-lang/rust/library/{lang}",),
- },
+ }),
),
};
PackageInformation { name, repo, version }
@@ -315,7 +303,7 @@ pub mod module {
}
"#,
"foo::module::func",
- r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+ r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
MonikerKind::Import,
);
check_moniker(
@@ -331,7 +319,7 @@ pub mod module {
}
"#,
"foo::module::func",
- r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+ r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
MonikerKind::Export,
);
}
@@ -348,7 +336,7 @@ pub mod module {
}
"#,
"foo::module::MyTrait::func",
- r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+ r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
MonikerKind::Export,
);
}
@@ -365,7 +353,7 @@ pub mod module {
}
"#,
"foo::module::MyTrait::MY_CONST",
- r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+ r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
MonikerKind::Export,
);
}
@@ -382,7 +370,7 @@ pub mod module {
}
"#,
"foo::module::MyTrait::MyType",
- r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+ r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
MonikerKind::Export,
);
}
@@ -405,7 +393,7 @@ pub mod module {
}
"#,
"foo::module::MyStruct::MyTrait::func",
- r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+ r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
MonikerKind::Export,
);
}
@@ -425,7 +413,7 @@ pub struct St {
}
"#,
"foo::St::a",
- r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
+ r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
MonikerKind::Import,
);
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index e942413c1..0f758cfa2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -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(),
)
})
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index fe44856dc..b4df04370 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -40,7 +40,9 @@ pub(crate) fn prepare_rename(
if def.range_for_rename(&sema).is_none() {
bail!("No references found at position")
}
- let frange = sema.original_range(name_like.syntax());
+ let Some(frange) = sema.original_range_opt(name_like.syntax()) else {
+ bail!("No references found at position");
+ };
always!(
frange.range.contains_inclusive(position.offset)
@@ -51,7 +53,7 @@ pub(crate) fn prepare_rename(
.reduce(|acc, cur| match (acc, cur) {
// ensure all ranges are the same
(Ok(acc_inner), Ok(cur_inner)) if acc_inner == cur_inner => Ok(acc_inner),
- (Err(e), _) => Err(e),
+ (e @ Err(_), _) | (_, e @ Err(_)) => e,
_ => bail!("inconsistent text range"),
});
@@ -2249,4 +2251,33 @@ fn foo((bar | bar | bar): ()) {
"#,
);
}
+
+ #[test]
+ fn regression_13498() {
+ check(
+ "Testing",
+ r"
+mod foo {
+ pub struct Test$0;
+}
+
+use foo::Test as Tester;
+
+fn main() {
+ let t = Tester;
+}
+",
+ r"
+mod foo {
+ pub struct Testing;
+}
+
+use foo::Testing as Tester;
+
+fn main() {
+ let t = Tester;
+}
+",
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index fedc1a435..e7412d27f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -149,7 +149,7 @@ fn signature_help_for_call(
variant.name(db)
);
}
- hir::CallableKind::Closure | hir::CallableKind::FnPtr => (),
+ hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
}
res.signature.push('(');
@@ -189,9 +189,10 @@ fn signature_help_for_call(
hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => {
render(func.ret_type(db))
}
- hir::CallableKind::Function(_) | hir::CallableKind::Closure | hir::CallableKind::FnPtr => {
- render(callable.return_type())
- }
+ hir::CallableKind::Function(_)
+ | hir::CallableKind::Closure
+ | hir::CallableKind::FnPtr
+ | hir::CallableKind::Other => render(callable.return_type()),
hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
}
Some(res)
@@ -387,10 +388,9 @@ mod tests {
}
fn check(ra_fixture: &str, expect: Expect) {
- // Implicitly add `Sized` to avoid noisy `T: ?Sized` in the results.
let fixture = format!(
r#"
-#[lang = "sized"] trait Sized {{}}
+//- minicore: sized, fn
{ra_fixture}
"#
);
@@ -1331,4 +1331,50 @@ fn f() {
"#]],
);
}
+
+ #[test]
+ fn help_for_generic_call() {
+ check(
+ r#"
+fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
+ f($0)
+}
+"#,
+ expect![[r#"
+ (u8, u16) -> i32
+ ^^ ---
+ "#]],
+ );
+ 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
+ "#]],
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index 27ad1a948..2380cf738 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -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,
diff --git a/src/tools/rust-analyzer/crates/limit/Cargo.toml b/src/tools/rust-analyzer/crates/limit/Cargo.toml
index 893db436d..3536f73da 100644
--- a/src/tools/rust-analyzer/crates/limit/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/limit/Cargo.toml
@@ -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 = []
diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml
index 13cd89010..bce2fc9a7 100644
--- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
index e4c56565b..cf53c1672 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
@@ -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(' ');
}
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs
new file mode 100644
index 000000000..4e04d2bc1
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs
@@ -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
+}
+ "#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml
index a286a6bcd..d1420de89 100644
--- a/src/tools/rust-analyzer/crates/parser/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml
index 5e83de7d9..d23a63d2a 100644
--- a/src/tools/rust-analyzer/crates/paths/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
index 54879c187..f261f3def 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
index 9d0da5dee..7991e125a 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
@@ -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" }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
index e39026ac7..a136abc12 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
index 2f854bc15..0ce099ae0 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
@@ -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())),
}
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
index 7aba74e53..0722cd89d 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
@@ -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),
}
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
index 72a2dfe72..b4f5ebd15 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
@@ -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(),
})
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
index b46cdddcf..cc0fc91fe 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
@@ -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"##]],
diff --git a/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml
index 684477191..d2a79f910 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml
@@ -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]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-test/imp/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-test/imp/Cargo.toml
index 2d1fc3c5c..1bd14070e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-test/imp/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-test/imp/Cargo.toml
@@ -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]
diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml
index 5697aea96..01d1735bf 100644
--- a/src/tools/rust-analyzer/crates/profile/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml
index cf9868740..39902a532 100644
--- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
index a26a7c57a..ae2b41f27 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
@@ -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");
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index b4c2ba436..02ec7a4f6 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -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()
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index fa8d76f3f..f6c09a27c 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -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";
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index e2444e249..a1cb438bd 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -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,
},
@@ -1602,40 +1602,31 @@ fn rust_project_hello_world_project_model() {
},
Dependency {
crate_id: CrateId(
- 7,
+ 9,
),
name: CrateName(
- "std_detect",
+ "unwind",
),
prelude: true,
},
Dependency {
crate_id: CrateId(
- 8,
+ 7,
),
name: CrateName(
- "term",
+ "std_detect",
),
prelude: true,
},
Dependency {
crate_id: CrateId(
- 9,
+ 8,
),
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",
@@ -1690,40 +1681,6 @@ fn rust_project_hello_world_project_model() {
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(
"test",
),
canonical_name: "test",
@@ -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",
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 2780c62ed..3d199ed24 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -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,
+ rustc,
cargo,
- build_scripts,
sysroot.as_ref(),
- rustc,
+ 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>,
+ rustc: &Option<CargoWorkspace>,
cargo: &CargoWorkspace,
- build_scripts: &WorkspaceBuildScripts,
sysroot: Option<&Sysroot>,
- rustc: &Option<CargoWorkspace>,
+ 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,
- &cfg_options,
- override_cfg,
load_proc_macro,
- &mut pkg_to_lib_crate,
- &public_deps,
+ rustc_workspace,
cargo,
+ &public_deps,
+ libproc_macro,
&pkg_crates,
+ &cfg_options,
+ override_cfg,
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>,
- 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,
+ 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,
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);
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index 544502853..56f14fe18 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -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
@@ -23,7 +23,7 @@ crossbeam-channel = "0.5.5"
dissimilar = "1.0.4"
itertools = "0.10.5"
scip = "0.1.1"
-lsp-types = { version = "0.93.1", features = ["proposed"] }
+lsp-types = { version = "=0.93.2", features = ["proposed"] }
parking_lot = "0.12.1"
xflags = "0.3.0"
oorandom = "11.1.3"
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index eabfcf194..7bf595d2a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -11,7 +11,7 @@ use std::{env, fs, path::Path, process};
use lsp_server::Connection;
use project_model::ProjectManifest;
-use rust_analyzer::{cli::flags, config::Config, from_json, lsp_ext::supports_utf8, Result};
+use rust_analyzer::{cli::flags, config::Config, from_json, Result};
use vfs::AbsPathBuf;
#[cfg(all(feature = "mimalloc"))]
@@ -191,11 +191,7 @@ fn run_server() -> Result<()> {
name: String::from("rust-analyzer"),
version: Some(rust_analyzer::version().to_string()),
}),
- offset_encoding: if supports_utf8(config.caps()) {
- Some("utf-8".to_string())
- } else {
- None
- },
+ offset_encoding: None,
};
let initialize_result = serde_json::to_value(initialize_result).unwrap();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
index cda95cd86..723b888d9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
@@ -6,19 +6,25 @@ use lsp_types::{
FileOperationFilter, FileOperationPattern, FileOperationPatternKind,
FileOperationRegistrationOptions, FoldingRangeProviderCapability, HoverProviderCapability,
ImplementationProviderCapability, InlayHintOptions, InlayHintServerCapabilities, OneOf,
- RenameOptions, SaveOptions, SelectionRangeProviderCapability, SemanticTokensFullOptions,
- SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions,
- TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
- TypeDefinitionProviderCapability, WorkDoneProgressOptions,
+ PositionEncodingKind, RenameOptions, SaveOptions, SelectionRangeProviderCapability,
+ SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities,
+ SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
+ TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions,
WorkspaceFileOperationsServerCapabilities, WorkspaceServerCapabilities,
};
use serde_json::json;
use crate::config::{Config, RustfmtConfig};
+use crate::lsp_ext::supports_utf8;
use crate::semantic_tokens;
pub fn server_capabilities(config: &Config) -> ServerCapabilities {
ServerCapabilities {
+ position_encoding: if supports_utf8(config.caps()) {
+ Some(PositionEncodingKind::UTF8)
+ } else {
+ None
+ },
text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
open_close: Some(true),
change: Some(TextDocumentSyncKind::INCREMENTAL),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
index 6ede194ba..cf51cf15a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -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),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
index 5dba545b8..762d7d3a1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -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"];
-
- 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![];
- }
- }
- }
+ 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"]),
+ };
- 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())
};
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index 748306ea5..c74ddabb1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -20,7 +20,7 @@ use crate::cli::{
load_cargo::{load_workspace, LoadCargoConfig},
Result,
};
-use crate::line_index::{LineEndings, LineIndex, OffsetEncoding};
+use crate::line_index::{LineEndings, LineIndex, PositionEncoding};
use crate::to_proto;
use crate::version::version;
@@ -106,12 +106,12 @@ impl LsifManager<'_> {
manager: "cargo".to_string(),
uri: None,
content: None,
- repository: Some(lsif::Repository {
- url: pi.repo,
+ repository: pi.repo.map(|url| lsif::Repository {
+ url,
r#type: "git".to_string(),
commit_id: None,
}),
- version: Some(pi.version),
+ version: pi.version,
}));
self.package_map.insert(package_information, result_set_id);
result_set_id
@@ -126,7 +126,7 @@ impl LsifManager<'_> {
let line_index = self.db.line_index(file_id);
let line_index = LineIndex {
index: line_index,
- encoding: OffsetEncoding::Utf16,
+ encoding: PositionEncoding::Utf16,
endings: LineEndings::Unix,
};
let range_id = self.add_vertex(lsif::Vertex::Range {
@@ -248,7 +248,7 @@ impl LsifManager<'_> {
let line_index = self.db.line_index(file_id);
let line_index = LineIndex {
index: line_index,
- encoding: OffsetEncoding::Utf16,
+ encoding: PositionEncoding::Utf16,
endings: LineEndings::Unix,
};
let result = folds
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 8b77ccde0..9edd045ab 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -5,7 +5,7 @@ use std::{
time::Instant,
};
-use crate::line_index::{LineEndings, LineIndex, OffsetEncoding};
+use crate::line_index::{LineEndings, LineIndex, PositionEncoding};
use hir::Name;
use ide::{
LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
@@ -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();
@@ -91,22 +88,18 @@ impl flags::Scip {
let line_index = LineIndex {
index: db.line_index(file_id),
- encoding: OffsetEncoding::Utf8,
+ encoding: PositionEncoding::Utf8,
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()];
- }
- }
-
- doc.symbols.push(symbol_info)
+ 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(),
+ };
+
+ 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(),
}
}
@@ -231,12 +252,12 @@ fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
package: Some(scip_types::Package {
manager: "cargo".to_string(),
name: package_name,
- version,
- ..Default::default()
+ version: version.unwrap_or_else(|| ".".to_string()),
+ special_fields: Default::default(),
})
.into(),
descriptors,
- ..Default::default()
+ special_fields: Default::default(),
})
}
@@ -415,4 +436,42 @@ pub mod module {
"",
);
}
+
+ #[test]
+ fn global_symbol_for_pub_struct() {
+ check_symbol(
+ r#"
+ //- /lib.rs crate:main
+ mod foo;
+
+ fn main() {
+ let _bar = foo::Bar { i: 0 };
+ }
+ //- /foo.rs
+ pub struct Bar$0 {
+ pub i: i32,
+ }
+ "#,
+ "rust-analyzer cargo main . foo/Bar#",
+ );
+ }
+
+ #[test]
+ fn global_symbol_for_pub_struct_reference() {
+ check_symbol(
+ r#"
+ //- /lib.rs crate:main
+ mod foo;
+
+ fn main() {
+ let _bar = foo::Bar$0 { i: 0 };
+ }
+ //- /foo.rs
+ pub struct Bar {
+ pub i: i32,
+ }
+ "#,
+ "rust-analyzer cargo main . foo/Bar#",
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 85322f12a..6b2f22faa 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -32,7 +32,7 @@ use vfs::AbsPathBuf;
use crate::{
caps::completion_item_edit_resolve,
diagnostics::DiagnosticsMapConfig,
- line_index::OffsetEncoding,
+ line_index::PositionEncoding,
lsp_ext::{self, supports_utf8, WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
};
@@ -56,6 +56,9 @@ mod patch_old_style;
// parsing the old name.
config_data! {
struct ConfigData {
+ /// Whether to insert #[must_use] when generating `as_` methods
+ /// for enum variants.
+ assist_emitMustUse: bool = "false",
/// Placeholder expression to use for missing expressions in assists.
assist_expressionFillDefault: ExprFillDefaultDef = "\"todo\"",
@@ -115,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",
@@ -154,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
@@ -171,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.
@@ -258,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 (`->`).
@@ -317,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.
@@ -326,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",
@@ -948,11 +961,11 @@ impl Config {
.is_some()
}
- pub fn offset_encoding(&self) -> OffsetEncoding {
+ pub fn position_encoding(&self) -> PositionEncoding {
if supports_utf8(&self.caps) {
- OffsetEncoding::Utf8
+ PositionEncoding::Utf8
} else {
- OffsetEncoding::Utf16
+ PositionEncoding::Utf16
}
}
@@ -1140,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
@@ -1197,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
@@ -1276,6 +1293,7 @@ impl Config {
allowed: None,
insert_use: self.insert_use_config(),
prefer_no_std: self.data.imports_prefer_no_std,
+ assist_emit_must_use: self.data.assist_emitMustUse,
}
}
@@ -1534,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);
}
@@ -1644,6 +1663,9 @@ enum InvocationStrategy {
}
#[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 {
Root,
@@ -1684,6 +1706,17 @@ enum ReborrowHintsDef {
}
#[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 {
Client,
@@ -1992,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": [
{
@@ -2080,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),
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 74689fd87..beb23c54c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -8,7 +8,7 @@ use stdx::format_to;
use vfs::{AbsPath, AbsPathBuf};
use crate::{
- global_state::GlobalStateSnapshot, line_index::OffsetEncoding, lsp_ext,
+ global_state::GlobalStateSnapshot, line_index::PositionEncoding, lsp_ext,
to_proto::url_from_abs_path,
};
@@ -66,17 +66,17 @@ fn location(
let uri = url_from_abs_path(&file_name);
let range = {
- let offset_encoding = snap.config.offset_encoding();
+ let position_encoding = snap.config.position_encoding();
lsp_types::Range::new(
- position(&offset_encoding, span, span.line_start, span.column_start),
- position(&offset_encoding, span, span.line_end, span.column_end),
+ position(&position_encoding, span, span.line_start, span.column_start),
+ position(&position_encoding, span, span.line_end, span.column_end),
)
};
lsp_types::Location::new(uri, range)
}
fn position(
- offset_encoding: &OffsetEncoding,
+ position_encoding: &PositionEncoding,
span: &DiagnosticSpan,
line_offset: usize,
column_offset: usize,
@@ -93,9 +93,9 @@ fn position(
};
}
let mut char_offset = 0;
- let len_func = match offset_encoding {
- OffsetEncoding::Utf8 => char::len_utf8,
- OffsetEncoding::Utf16 => char::len_utf16,
+ let len_func = match position_encoding {
+ PositionEncoding::Utf8 => char::len_utf8,
+ PositionEncoding::Utf16 => char::len_utf16,
};
for c in line.text.chars() {
char_offset += 1;
@@ -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)
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
index f2db9a273..dd433b0f4 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
@@ -8,7 +8,7 @@ use vfs::AbsPathBuf;
use crate::{
from_json,
global_state::GlobalStateSnapshot,
- line_index::{LineIndex, OffsetEncoding},
+ line_index::{LineIndex, PositionEncoding},
lsp_ext,
lsp_utils::invalid_params_error,
Result,
@@ -25,10 +25,10 @@ pub(crate) fn vfs_path(url: &lsp_types::Url) -> Result<vfs::VfsPath> {
pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> Result<TextSize> {
let line_col = match line_index.encoding {
- OffsetEncoding::Utf8 => {
+ PositionEncoding::Utf8 => {
LineCol { line: position.line as u32, col: position.character as u32 }
}
- OffsetEncoding::Utf16 => {
+ PositionEncoding::Utf16 => {
let line_col =
LineColUtf16 { line: position.line as u32, col: position.character as u32 };
line_index.index.to_utf8(line_col)
@@ -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> {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index 3fb06c31f..4e8bc8d64 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -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>>)>,
@@ -383,7 +383,7 @@ impl GlobalStateSnapshot {
pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
let endings = self.vfs.read().1[&file_id];
let index = self.analysis.file_line_index(file_id)?;
- let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
+ let res = LineIndex { index, endings, encoding: self.config.position_encoding() };
Ok(res)
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
index 34795a8eb..d190a9f4e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
@@ -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 = &params.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))
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
index c116414da..2945dba12 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
@@ -7,7 +7,7 @@
use std::sync::Arc;
-pub enum OffsetEncoding {
+pub enum PositionEncoding {
Utf8,
Utf16,
}
@@ -15,7 +15,7 @@ pub enum OffsetEncoding {
pub(crate) struct LineIndex {
pub(crate) index: Arc<ide::LineIndex>,
pub(crate) endings: LineEndings,
- pub(crate) encoding: OffsetEncoding,
+ pub(crate) encoding: PositionEncoding,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -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);
+ (src, LineEndings::Dos)
+ }
+}
- fn find_crlf(src: &[u8]) -> Option<usize> {
- src.windows(2).position(|it| it == b"\r\n")
- }
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn unix() {
+ let src = "a\nb\nc\n\n\n\n";
+ let (res, endings) = LineEndings::normalize(src.into());
+ assert_eq!(endings, LineEndings::Unix);
+ assert_eq!(res, src);
+ }
+
+ #[test]
+ fn dos() {
+ let src = "\r\na\r\n\r\nb\r\nc\r\n\r\n\r\n\r\n";
+ let (res, endings) = LineEndings::normalize(src.into());
+ assert_eq!(endings, LineEndings::Dos);
+ assert_eq!(res, "\na\n\nb\nc\n\n\n\n");
+ }
+
+ #[test]
+ fn mixed() {
+ let src = "a\r\nb\r\nc\r\n\n\r\n\n";
+ let (res, endings) = LineEndings::normalize(src.into());
+ assert_eq!(endings, LineEndings::Dos);
+ assert_eq!(res, "a\nb\nc\n\n\n\n");
+ }
+
+ #[test]
+ fn none() {
+ let src = "abc";
+ let (res, endings) = LineEndings::normalize(src.into());
+ assert_eq!(endings, LineEndings::Unix);
+ assert_eq!(res, src);
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
index e61c8b643..8cc5648f3 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
@@ -3,6 +3,7 @@
use std::{collections::HashMap, path::PathBuf};
use lsp_types::request::Request;
+use lsp_types::PositionEncodingKind;
use lsp_types::{
notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams,
PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams,
@@ -455,7 +456,15 @@ pub(crate) enum CodeLensResolveData {
}
pub fn supports_utf8(caps: &lsp_types::ClientCapabilities) -> bool {
- caps.offset_encoding.as_deref().unwrap_or_default().iter().any(|it| it == "utf-8")
+ match &caps.general {
+ Some(general) => general
+ .position_encodings
+ .as_deref()
+ .unwrap_or_default()
+ .iter()
+ .any(|it| it == &PositionEncodingKind::UTF8),
+ _ => false,
+ }
}
pub enum MoveItem {}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
index b3cea64d4..0971dc36f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
@@ -1,12 +1,12 @@
//! Utilities for LSP-related boilerplate code.
-use std::{ops::Range, sync::Arc};
+use std::{mem, ops::Range, sync::Arc};
use lsp_server::Notification;
use crate::{
from_proto,
global_state::GlobalState,
- line_index::{LineEndings, LineIndex, OffsetEncoding},
+ line_index::{LineEndings, LineIndex, PositionEncoding},
LspError,
};
@@ -133,14 +133,40 @@ 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: OffsetEncoding::Utf16,
+ encoding: PositionEncoding::Utf16,
};
// The changes we got must be applied sequentially, but can cross lines so we
@@ -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");
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 2c928a580..274588ce0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -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());
@@ -607,30 +607,34 @@ impl GlobalState {
/// Handles a request.
fn on_request(&mut self, req: Request) {
- if self.shutdown_requested {
- self.respond(lsp_server::Response::new_err(
- req.id,
- lsp_server::ErrorCode::InvalidRequest as i32,
- "Shutdown already requested.".to_owned(),
- ));
- return;
- }
+ let mut dispatcher = RequestDispatcher { req: Some(req), global_state: self };
+ dispatcher.on_sync_mut::<lsp_types::request::Shutdown>(|s, ()| {
+ s.shutdown_requested = true;
+ Ok(())
+ });
+
+ if let RequestDispatcher { req: Some(req), global_state: this } = &mut dispatcher {
+ if this.shutdown_requested {
+ this.respond(lsp_server::Response::new_err(
+ req.id.clone(),
+ lsp_server::ErrorCode::InvalidRequest as i32,
+ "Shutdown already requested.".to_owned(),
+ ));
+ return;
+ }
- // Avoid flashing a bunch of unresolved references during initial load.
- if self.workspaces.is_empty() && !self.is_quiescent() {
- self.respond(lsp_server::Response::new_err(
- req.id,
- lsp_server::ErrorCode::ContentModified as i32,
- "waiting for cargo metadata or cargo check".to_owned(),
- ));
- return;
+ // Avoid flashing a bunch of unresolved references during initial load.
+ if this.workspaces.is_empty() && !this.is_quiescent() {
+ this.respond(lsp_server::Response::new_err(
+ req.id.clone(),
+ lsp_server::ErrorCode::ContentModified as i32,
+ "waiting for cargo metadata or cargo check".to_owned(),
+ ));
+ return;
+ }
}
- RequestDispatcher { req: Some(req), global_state: self }
- .on_sync_mut::<lsp_types::request::Shutdown>(|s, ()| {
- s.shutdown_requested = true;
- Ok(())
- })
+ dispatcher
.on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)
.on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)
.on_sync_mut::<lsp_ext::ShuffleCrateGraph>(handlers::handle_shuffle_crate_graph)
@@ -755,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()));
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs
index f86a0f66a..45a1dab97 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs
@@ -7,7 +7,7 @@ use vfs::VfsPath;
/// Holds the set of in-memory documents.
///
-/// For these document, there true contents is maintained by the client. It
+/// For these document, their true contents is maintained by the client. It
/// might be different from what's on disk.
#[derive(Default, Clone)]
pub(crate) struct MemDocs {
@@ -19,6 +19,7 @@ impl MemDocs {
pub(crate) fn contains(&self, path: &VfsPath) -> bool {
self.mem_docs.contains_key(path)
}
+
pub(crate) fn insert(&mut self, path: VfsPath, data: DocumentData) -> Result<(), ()> {
self.added_or_removed = true;
match self.mem_docs.insert(path, data) {
@@ -26,6 +27,7 @@ impl MemDocs {
None => Ok(()),
}
}
+
pub(crate) fn remove(&mut self, path: &VfsPath) -> Result<(), ()> {
self.added_or_removed = true;
match self.mem_docs.remove(path) {
@@ -33,17 +35,21 @@ impl MemDocs {
None => Err(()),
}
}
+
pub(crate) fn get(&self, path: &VfsPath) -> Option<&DocumentData> {
self.mem_docs.get(path)
}
+
pub(crate) fn get_mut(&mut self, path: &VfsPath) -> Option<&mut DocumentData> {
// NB: don't set `self.added_or_removed` here, as that purposefully only
// tracks changes to the key set.
self.mem_docs.get_mut(path)
}
+
pub(crate) fn iter(&self) -> impl Iterator<Item = &VfsPath> {
self.mem_docs.keys()
}
+
pub(crate) fn take_changes(&mut self) -> bool {
mem::replace(&mut self.added_or_removed, false)
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index e1f651786..fcfe4be0b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -106,6 +106,14 @@ impl GlobalState {
status.health = lsp_ext::Health::Error;
status.message = Some(error)
}
+
+ if self.config.linked_projects().is_empty()
+ && self.config.detached_files().is_empty()
+ && self.config.notifications().cargo_toml_not_found
+ {
+ status.health = lsp_ext::Health::Warning;
+ status.message = Some("Workspace reload required".to_string())
+ }
status
}
@@ -198,12 +206,9 @@ impl GlobalState {
self.show_and_log_error("failed to run build scripts".to_string(), Some(error));
}
- let workspaces = self
- .fetch_workspaces_queue
- .last_op_result()
- .iter()
- .filter_map(|res| res.as_ref().ok().cloned())
- .collect::<Vec<_>>();
+ let Some(workspaces) = self.fetch_workspaces_queue.last_op_result() else { return; };
+ let workspaces =
+ workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>();
fn eq_ignore_build_data<'a>(
left: &'a ProjectWorkspace,
@@ -300,9 +305,6 @@ impl GlobalState {
let files_config = self.config.files();
let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
- let standalone_server_name =
- format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
-
if self.proc_macro_clients.is_empty() {
if let Some((path, path_manually_set)) = self.config.proc_macro_srv() {
tracing::info!("Spawning proc-macro servers");
@@ -310,40 +312,17 @@ impl GlobalState {
.workspaces
.iter()
.map(|ws| {
- let (path, args) = if path_manually_set {
+ let (path, args): (_, &[_]) = if path_manually_set {
tracing::debug!(
"Pro-macro server path explicitly set: {}",
path.display()
);
- (path.clone(), vec![])
+ (path.clone(), &[])
} else {
- let mut sysroot_server = None;
- if let ProjectWorkspace::Cargo { sysroot, .. }
- | ProjectWorkspace::Json { sysroot, .. } = ws
- {
- if let Some(sysroot) = sysroot.as_ref() {
- let server_path = sysroot
- .root()
- .join("libexec")
- .join(&standalone_server_name);
- if std::fs::metadata(&server_path).is_ok() {
- tracing::debug!(
- "Sysroot proc-macro server exists at {}",
- server_path.display()
- );
- sysroot_server = Some(server_path);
- } else {
- tracing::debug!(
- "Sysroot proc-macro server does not exist at {}",
- server_path.display()
- );
- }
- }
+ match ws.find_sysroot_proc_macro_srv() {
+ Some(server_path) => (server_path, &[]),
+ None => (path.clone(), &["proc-macro"]),
}
- sysroot_server.map_or_else(
- || (path.clone(), vec!["proc-macro".to_owned()]),
- |path| (path, vec![]),
- )
};
tracing::info!(?args, "Using proc-macro server at {}", path.display(),);
@@ -427,9 +406,14 @@ impl GlobalState {
fn fetch_workspace_error(&self) -> Result<(), String> {
let mut buf = String::new();
- for ws in self.fetch_workspaces_queue.last_op_result() {
- if let Err(err) = ws {
- stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
+ let Some(last_op_result) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) };
+ if last_op_result.is_empty() {
+ stdx::format_to!(buf, "rust-analyzer failed to discover workspace");
+ } else {
+ for ws in last_op_result {
+ if let Err(err) = ws {
+ stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
+ }
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
index 5936454a7..81cc1952b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
@@ -21,17 +21,17 @@ use crate::{
cargo_target_spec::CargoTargetSpec,
config::{CallInfoConfig, Config},
global_state::GlobalStateSnapshot,
- line_index::{LineEndings, LineIndex, OffsetEncoding},
+ line_index::{LineEndings, LineIndex, PositionEncoding},
lsp_ext,
lsp_utils::invalid_params_error,
- semantic_tokens, Result,
+ semantic_tokens,
};
pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
let line_col = line_index.index.line_col(offset);
match line_index.encoding {
- OffsetEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col),
- OffsetEncoding::Utf16 => {
+ PositionEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col),
+ PositionEncoding::Utf16 => {
let line_col = line_index.index.to_utf16(line_col);
lsp_types::Position::new(line_col.line, line_col.col)
}
@@ -429,7 +429,7 @@ pub(crate) fn inlay_hint(
line_index: &LineIndex,
render_colons: bool,
mut inlay_hint: InlayHint,
-) -> Result<lsp_types::InlayHint> {
+) -> Cancellable<lsp_types::InlayHint> {
match inlay_hint.kind {
InlayKind::ParameterHint if render_colons => inlay_hint.label.append_str(":"),
InlayKind::TypeHint if render_colons => inlay_hint.label.prepend_str(": "),
@@ -440,32 +440,35 @@ pub(crate) fn inlay_hint(
Ok(lsp_types::InlayHint {
position: match inlay_hint.kind {
// before annotated thing
- InlayKind::ParameterHint
- | InlayKind::ImplicitReborrowHint
- | InlayKind::BindingModeHint => position(line_index, inlay_hint.range.start()),
+ InlayKind::ParameterHint | InlayKind::AdjustmentHint | InlayKind::BindingModeHint => {
+ position(line_index, inlay_hint.range.start())
+ }
// after annotated thing
InlayKind::ClosureReturnTypeHint
| InlayKind::TypeHint
| InlayKind::ChainingHint
| InlayKind::GenericParamListHint
+ | InlayKind::AdjustmentHintClosingParenthesis
| InlayKind::LifetimeHint
| InlayKind::ClosingBraceHint => position(line_index, inlay_hint.range.end()),
},
padding_left: Some(match inlay_hint.kind {
InlayKind::TypeHint => !render_colons,
InlayKind::ChainingHint | InlayKind::ClosingBraceHint => true,
- InlayKind::BindingModeHint
+ InlayKind::AdjustmentHintClosingParenthesis
+ | InlayKind::BindingModeHint
| InlayKind::ClosureReturnTypeHint
| InlayKind::GenericParamListHint
- | InlayKind::ImplicitReborrowHint
+ | InlayKind::AdjustmentHint
| InlayKind::LifetimeHint
| InlayKind::ParameterHint => false,
}),
padding_right: Some(match inlay_hint.kind {
- InlayKind::ChainingHint
+ InlayKind::AdjustmentHintClosingParenthesis
+ | InlayKind::ChainingHint
| InlayKind::ClosureReturnTypeHint
| InlayKind::GenericParamListHint
- | InlayKind::ImplicitReborrowHint
+ | InlayKind::AdjustmentHint
| InlayKind::TypeHint
| InlayKind::ClosingBraceHint => false,
InlayKind::BindingModeHint => inlay_hint.label.as_simple_str() != Some("&"),
@@ -476,10 +479,11 @@ pub(crate) fn inlay_hint(
InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
Some(lsp_types::InlayHintKind::TYPE)
}
- InlayKind::BindingModeHint
+ InlayKind::AdjustmentHintClosingParenthesis
+ | InlayKind::BindingModeHint
| InlayKind::GenericParamListHint
| InlayKind::LifetimeHint
- | InlayKind::ImplicitReborrowHint
+ | InlayKind::AdjustmentHint
| InlayKind::ClosingBraceHint => None,
},
text_edits: None,
@@ -518,7 +522,7 @@ pub(crate) fn inlay_hint(
fn inlay_hint_label(
snap: &GlobalStateSnapshot,
label: InlayHintLabel,
-) -> Result<lsp_types::InlayHintLabel> {
+) -> Cancellable<lsp_types::InlayHintLabel> {
Ok(match label.as_simple_str() {
Some(s) => lsp_types::InlayHintLabel::String(s.into()),
None => lsp_types::InlayHintLabel::LabelParts(
@@ -536,7 +540,7 @@ fn inlay_hint_label(
command: None,
})
})
- .collect::<Result<Vec<_>>>()?,
+ .collect::<Cancellable<Vec<_>>>()?,
),
})
}
@@ -794,7 +798,7 @@ pub(crate) fn optional_versioned_text_document_identifier(
pub(crate) fn location(
snap: &GlobalStateSnapshot,
frange: FileRange,
-) -> Result<lsp_types::Location> {
+) -> Cancellable<lsp_types::Location> {
let url = url(snap, frange.file_id);
let line_index = snap.file_line_index(frange.file_id)?;
let range = range(&line_index, frange.range);
@@ -806,7 +810,7 @@ pub(crate) fn location(
pub(crate) fn location_from_nav(
snap: &GlobalStateSnapshot,
nav: NavigationTarget,
-) -> Result<lsp_types::Location> {
+) -> Cancellable<lsp_types::Location> {
let url = url(snap, nav.file_id);
let line_index = snap.file_line_index(nav.file_id)?;
let range = range(&line_index, nav.full_range);
@@ -818,7 +822,7 @@ pub(crate) fn location_link(
snap: &GlobalStateSnapshot,
src: Option<FileRange>,
target: NavigationTarget,
-) -> Result<lsp_types::LocationLink> {
+) -> Cancellable<lsp_types::LocationLink> {
let origin_selection_range = match src {
Some(src) => {
let line_index = snap.file_line_index(src.file_id)?;
@@ -840,7 +844,7 @@ pub(crate) fn location_link(
fn location_info(
snap: &GlobalStateSnapshot,
target: NavigationTarget,
-) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> {
+) -> Cancellable<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> {
let line_index = snap.file_line_index(target.file_id)?;
let target_uri = url(snap, target.file_id);
@@ -854,12 +858,12 @@ pub(crate) fn goto_definition_response(
snap: &GlobalStateSnapshot,
src: Option<FileRange>,
targets: Vec<NavigationTarget>,
-) -> Result<lsp_types::GotoDefinitionResponse> {
+) -> Cancellable<lsp_types::GotoDefinitionResponse> {
if snap.config.location_link() {
let links = targets
.into_iter()
.map(|nav| location_link(snap, src, nav))
- .collect::<Result<Vec<_>>>()?;
+ .collect::<Cancellable<Vec<_>>>()?;
Ok(links.into())
} else {
let locations = targets
@@ -867,7 +871,7 @@ pub(crate) fn goto_definition_response(
.map(|nav| {
location(snap, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
})
- .collect::<Result<Vec<_>>>()?;
+ .collect::<Cancellable<Vec<_>>>()?;
Ok(locations.into())
}
}
@@ -881,7 +885,7 @@ pub(crate) fn snippet_text_document_edit(
is_snippet: bool,
file_id: FileId,
edit: TextEdit,
-) -> Result<lsp_ext::SnippetTextDocumentEdit> {
+) -> Cancellable<lsp_ext::SnippetTextDocumentEdit> {
let text_document = optional_versioned_text_document_identifier(snap, file_id);
let line_index = snap.file_line_index(file_id)?;
let mut edits: Vec<_> =
@@ -958,7 +962,7 @@ pub(crate) fn snippet_text_document_ops(
pub(crate) fn snippet_workspace_edit(
snap: &GlobalStateSnapshot,
source_change: SourceChange,
-) -> Result<lsp_ext::SnippetWorkspaceEdit> {
+) -> Cancellable<lsp_ext::SnippetWorkspaceEdit> {
let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
for op in source_change.file_system_edits {
@@ -995,7 +999,7 @@ pub(crate) fn snippet_workspace_edit(
pub(crate) fn workspace_edit(
snap: &GlobalStateSnapshot,
source_change: SourceChange,
-) -> Result<lsp_types::WorkspaceEdit> {
+) -> Cancellable<lsp_types::WorkspaceEdit> {
assert!(!source_change.is_snippet);
snippet_workspace_edit(snap, source_change).map(|it| it.into())
}
@@ -1048,7 +1052,7 @@ impl From<lsp_ext::SnippetTextEdit>
pub(crate) fn call_hierarchy_item(
snap: &GlobalStateSnapshot,
target: NavigationTarget,
-) -> Result<lsp_types::CallHierarchyItem> {
+) -> Cancellable<lsp_types::CallHierarchyItem> {
let name = target.name.to_string();
let detail = target.description.clone();
let kind = target.kind.map(symbol_kind).unwrap_or(lsp_types::SymbolKind::FUNCTION);
@@ -1080,7 +1084,7 @@ pub(crate) fn code_action(
snap: &GlobalStateSnapshot,
assist: Assist,
resolve_data: Option<(usize, lsp_types::CodeActionParams)>,
-) -> Result<lsp_ext::CodeAction> {
+) -> Cancellable<lsp_ext::CodeAction> {
let mut res = lsp_ext::CodeAction {
title: assist.label.to_string(),
group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
@@ -1113,13 +1117,13 @@ pub(crate) fn code_action(
pub(crate) fn runnable(
snap: &GlobalStateSnapshot,
runnable: Runnable,
-) -> Result<lsp_ext::Runnable> {
+) -> Cancellable<lsp_ext::Runnable> {
let config = snap.config.runnables();
let spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id)?;
let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone());
let target = spec.as_ref().map(|s| s.target.clone());
let (cargo_args, executable_args) =
- CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg)?;
+ CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg);
let label = runnable.label(target);
let location = location_link(snap, None, runnable.nav)?;
@@ -1142,7 +1146,7 @@ pub(crate) fn code_lens(
acc: &mut Vec<lsp_types::CodeLens>,
snap: &GlobalStateSnapshot,
annotation: Annotation,
-) -> Result<()> {
+) -> Cancellable<()> {
let client_commands_config = snap.config.client_commands();
match annotation.kind {
AnnotationKind::Runnable(run) => {
@@ -1394,7 +1398,7 @@ fn main() {
let line_index = LineIndex {
index: Arc::new(ide::LineIndex::new(text)),
endings: LineEndings::Unix,
- encoding: OffsetEncoding::Utf16,
+ encoding: PositionEncoding::Utf16,
};
let converted: Vec<lsp_types::FoldingRange> =
folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect();
diff --git a/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml b/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml
index e75867e2d..593dc4e55 100644
--- a/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
index e0657ab0f..f7b7d0964 100644
--- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
@@ -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
@@ -16,7 +16,7 @@ always-assert = { version = "0.1.2", features = ["log"] }
# Think twice before adding anything here
[target.'cfg(windows)'.dependencies]
-miow = "0.4.0"
+miow = "0.5.0"
winapi = { version = "0.3.9", features = ["winerror"] }
[features]
diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
index 1ef903371..00743cca5 100644
--- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
@@ -5,7 +5,7 @@ description = "Comment and whitespace preserving parser for the Rust language"
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
diff --git a/src/tools/rust-analyzer/crates/syntax/fuzz/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/fuzz/Cargo.toml
index ba2f515b0..f295c4006 100644
--- a/src/tools/rust-analyzer/crates/syntax/fuzz/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/syntax/fuzz/Cargo.toml
@@ -4,7 +4,7 @@ name = "syntax-fuzz"
version = "0.0.1"
publish = false
edition = "2021"
-rust-version = "1.57"
+rust-version = "1.65"
[package.metadata]
cargo-fuzz = true
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 894795435..0a0cb0290 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -51,7 +51,7 @@ TypeArg =
Type
AssocTypeArg =
- NameRef GenericParamList? (':' TypeBoundList | ('=' Type | ConstArg))
+ NameRef GenericArgList? (':' TypeBoundList | ('=' Type | ConstArg))
LifetimeArg =
Lifetime
@@ -239,8 +239,11 @@ Static =
Trait =
Attr* Visibility?
'unsafe'? 'auto'?
- 'trait' Name GenericParamList? (':' TypeBoundList?)? WhereClause?
- AssocItemList
+ 'trait' Name GenericParamList?
+ (
+ (':' TypeBoundList?)? WhereClause? AssocItemList
+ | '=' TypeBoundList? WhereClause? ';'
+ )
AssocItemList =
'{' Attr* AssocItem* '}'
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 449402e5f..2ea715f47 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -120,7 +120,7 @@ pub struct AssocTypeArg {
impl ast::HasTypeBounds for AssocTypeArg {}
impl AssocTypeArg {
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
- pub fn generic_param_list(&self) -> Option<GenericParamList> { support::child(&self.syntax) }
+ pub fn generic_arg_list(&self) -> Option<GenericArgList> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
pub fn const_arg(&self) -> Option<ConstArg> { support::child(&self.syntax) }
@@ -143,16 +143,6 @@ impl ConstArg {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct GenericParamList {
- pub(crate) syntax: SyntaxNode,
-}
-impl GenericParamList {
- pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
- pub fn generic_params(&self) -> AstChildren<GenericParam> { support::children(&self.syntax) }
- pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TypeBoundList {
pub(crate) syntax: SyntaxNode,
}
@@ -417,6 +407,8 @@ impl Trait {
pub fn auto_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![auto]) }
pub fn trait_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![trait]) }
pub fn assoc_item_list(&self) -> Option<AssocItemList> { support::child(&self.syntax) }
+ pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+ pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -528,6 +520,16 @@ impl Abi {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct GenericParamList {
+ pub(crate) syntax: SyntaxNode,
+}
+impl GenericParamList {
+ pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
+ pub fn generic_params(&self) -> AstChildren<GenericParam> { support::children(&self.syntax) }
+ pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct WhereClause {
pub(crate) syntax: SyntaxNode,
}
@@ -1834,17 +1836,6 @@ impl AstNode for ConstArg {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl AstNode for GenericParamList {
- fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_PARAM_LIST }
- fn cast(syntax: SyntaxNode) -> Option<Self> {
- if Self::can_cast(syntax.kind()) {
- Some(Self { syntax })
- } else {
- None
- }
- }
- fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
impl AstNode for TypeBoundList {
fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -2153,6 +2144,17 @@ impl AstNode for Abi {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for GenericParamList {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_PARAM_LIST }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for WhereClause {
fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_CLAUSE }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -4263,11 +4265,6 @@ impl std::fmt::Display for ConstArg {
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for GenericParamList {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- std::fmt::Display::fmt(self.syntax(), f)
- }
-}
impl std::fmt::Display for TypeBoundList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -4408,6 +4405,11 @@ impl std::fmt::Display for Abi {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for GenericParamList {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for WhereClause {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 4057a75e7..8c26009ad 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -334,6 +334,10 @@ pub fn block_expr(
ast_from_text(&format!("fn f() {buf}"))
}
+pub fn tail_only_block_expr(tail_expr: ast::Expr) -> ast::BlockExpr {
+ ast_from_text(&format!("fn f() {{ {tail_expr} }}"))
+}
+
/// Ideally this function wouldn't exist since it involves manual indenting.
/// It differs from `make::block_expr` by also supporting comments.
///
@@ -656,6 +660,22 @@ pub fn let_stmt(
};
ast_from_text(&format!("fn f() {{ {text} }}"))
}
+
+pub fn let_else_stmt(
+ pattern: ast::Pat,
+ ty: Option<ast::Type>,
+ expr: ast::Expr,
+ diverging: ast::BlockExpr,
+) -> ast::LetStmt {
+ let mut text = String::new();
+ format_to!(text, "let {pattern}");
+ if let Some(ty) = ty {
+ format_to!(text, ": {ty}");
+ }
+ format_to!(text, " = {expr} else {diverging};");
+ ast_from_text(&format!("fn f() {{ {text} }}"))
+}
+
pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
let semi = if expr.is_block_like() { "" } else { ";" };
ast_from_text(&format!("fn f() {{ {expr}{semi} (); }}"))
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
index ba72e6442..8990f7a7d 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
@@ -209,17 +209,19 @@ impl ast::String {
let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
let mut buf = String::new();
- let mut text_iter = text.chars();
+ let mut prev_end = 0;
let mut has_error = false;
unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match (
unescaped_char,
buf.capacity() == 0,
) {
(Ok(c), false) => buf.push(c),
- (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (),
+ (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
+ prev_end = char_range.end
+ }
(Ok(c), true) => {
buf.reserve_exact(text.len());
- buf.push_str(&text[..char_range.start]);
+ buf.push_str(&text[..prev_end]);
buf.push(c);
}
(Err(_), _) => has_error = true,
@@ -252,17 +254,19 @@ impl ast::ByteString {
let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
let mut buf: Vec<u8> = Vec::new();
- let mut text_iter = text.chars();
+ let mut prev_end = 0;
let mut has_error = false;
unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match (
unescaped_char,
buf.capacity() == 0,
) {
(Ok(c), false) => buf.push(c as u8),
- (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (),
+ (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
+ prev_end = char_range.end
+ }
(Ok(c), true) => {
buf.reserve_exact(text.len());
- buf.extend_from_slice(text[..char_range.start].as_bytes());
+ buf.extend_from_slice(text[..prev_end].as_bytes());
buf.push(c as u8);
}
(Err(_), _) => has_error = true,
@@ -445,6 +449,36 @@ mod tests {
check_string_value(r"\foobar", None);
check_string_value(r"\nfoobar", "\nfoobar");
check_string_value(r"C:\\Windows\\System32\\", "C:\\Windows\\System32\\");
+ check_string_value(r"\x61bcde", "abcde");
+ check_string_value(
+ r"a\
+bcde", "abcde",
+ );
+ }
+
+ fn check_byte_string_value<'a, const N: usize>(
+ lit: &str,
+ expected: impl Into<Option<&'a [u8; N]>>,
+ ) {
+ assert_eq!(
+ ast::ByteString { syntax: make::tokens::literal(&format!("b\"{}\"", lit)) }
+ .value()
+ .as_deref(),
+ expected.into().map(|value| &value[..])
+ );
+ }
+
+ #[test]
+ fn test_byte_string_escape() {
+ check_byte_string_value(r"foobar", b"foobar");
+ check_byte_string_value(r"\foobar", None::<&[u8; 0]>);
+ check_byte_string_value(r"\nfoobar", b"\nfoobar");
+ check_byte_string_value(r"C:\\Windows\\System32\\", b"C:\\Windows\\System32\\");
+ check_byte_string_value(r"\x61bcde", b"abcde");
+ check_byte_string_value(
+ r"a\
+bcde", b"abcde",
+ );
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
index 70b54843d..712ef5f63 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
@@ -86,7 +86,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> String {
.traits
.iter()
.filter(|trait_name| {
- // Loops have two expressions so this might collide, therefor manual impl it
+ // Loops have two expressions so this might collide, therefore manual impl it
node.name != "ForExpr" && node.name != "WhileExpr"
|| trait_name.as_str() != "HasLoopBody"
})
diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
index b9f2b5132..1eea23464 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
@@ -5,9 +5,7 @@
mod block;
use rowan::Direction;
-use rustc_lexer::unescape::{
- self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
-};
+use rustc_lexer::unescape::{self, unescape_byte, unescape_char, unescape_literal, Mode};
use crate::{
algo,
@@ -143,7 +141,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
ast::LiteralKind::ByteString(s) => {
if !s.is_raw() {
if let Some(without_quotes) = unquote(text, 2, '"') {
- unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
+ unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
if let Err(err) = char {
push_err(2, (range.start, err));
}
diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml
index cceafe04e..1047373b1 100644
--- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
index 7a90d64a9..8df7e1af6 100644
--- a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml
index 3e0f31f19..a6a3ae742 100644
--- a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml
index 52dfb8608..4f2103f3a 100644
--- a/src/tools/rust-analyzer/crates/tt/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
index df5dc24e2..061f3c157 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
@@ -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
diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml
index d7549a284..e55bf6f29 100644
--- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml
@@ -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