From 12b67ef33e213bb015f08afc5d454907b32de40a Mon Sep 17 00:00:00 2001 From: Mohammad Mustakim Ali Date: Wed, 6 Dec 2023 23:51:14 +0000 Subject: [PATCH 01/62] basic test --- crates/ide-completion/src/completions/dot.rs | 34 +++++++++++++++++++- crates/ide-completion/src/tests.rs | 5 +++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 57e06461099e..89726acf9532 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -155,7 +155,8 @@ mod tests { use expect_test::{expect, Expect}; use crate::tests::{ - check_edit, completion_list_no_kw, completion_list_no_kw_with_private_editable, + check_edit, completion_list_exact_order, completion_list_no_kw, + completion_list_no_kw_with_private_editable, }; fn check(ra_fixture: &str, expect: Expect) { @@ -163,6 +164,11 @@ mod tests { expect.assert_eq(&actual); } + fn check_exact_order(ra_fixture: &str, expect: Expect) { + let actual = completion_list_exact_order(ra_fixture); + expect.assert_eq(&actual); + } + fn check_with_private_editable(ra_fixture: &str, expect: Expect) { let actual = completion_list_no_kw_with_private_editable(ra_fixture); expect.assert_eq(&actual); @@ -265,6 +271,32 @@ fn foo(a: A) { a.$0() } ); } + #[test] + fn test_usable_types_first() { + check_exact_order( + r#" +struct A; +impl A { + fn foo(&self) {} + fn new_1(input: u32) -> Self { A } + fn new_2() -> A { A } +} + +fn test() { + let a = A::$0; +} +"#, + // preference: + // fn with no param that returns itself + // fn with param that returns itself + expect![[r#" + fn new_2() fn() -> A + fn new_1(…) fn(u32) -> A + me foo(…) fn(&self) + "#]], + ); + } + #[test] fn test_visibility_filtering() { check( diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index f28afacc586f..b03439eef732 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -88,6 +88,11 @@ pub(crate) fn completion_list_no_kw(ra_fixture: &str) -> String { completion_list_with_config(TEST_CONFIG, ra_fixture, false, None) } +pub(crate) fn completion_list_exact_order(ra_fixture: &str) -> String { + let items = get_all_items(TEST_CONFIG, ra_fixture, None); + render_completion_list(items) +} + pub(crate) fn completion_list_no_kw_with_private_editable(ra_fixture: &str) -> String { let mut config = TEST_CONFIG; config.enable_private_editable = true; From 983bc9ad01390996bebd4b411657085b9b8beef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 5 Dec 2023 13:07:52 +0200 Subject: [PATCH 02/62] Fix runnable cwd on Windows --- editors/code/src/toolchain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index 1037e513aa10..a0b34406c1b0 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -74,7 +74,7 @@ export class Cargo { artifacts.push({ fileName: message.executable, name: message.target.name, - workspace: message.manifest_path.replace(/\/Cargo\.toml$/, ""), + workspace: path.dirname(message.manifest_path), kind: message.target.kind[0], isTest: message.profile.test, }); From 7218995a8a4c71f2702739788c682e20e8142180 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 5 Dec 2023 11:35:09 +0100 Subject: [PATCH 03/62] Don't explicitly warn against `semicolon_in_expressions_from_macros` This has been warn-by-default for two years now and has already been added to the future-incompat lints in 1.68. --- crates/base-db/src/lib.rs | 2 +- crates/cfg/src/lib.rs | 2 +- crates/flycheck/src/lib.rs | 2 +- crates/hir-def/src/lib.rs | 2 +- crates/hir-expand/src/lib.rs | 2 +- crates/hir-ty/src/lib.rs | 2 +- crates/hir/src/lib.rs | 2 +- crates/ide-assists/src/lib.rs | 2 +- crates/ide-completion/src/lib.rs | 2 +- crates/ide-db/src/lib.rs | 2 +- crates/ide-diagnostics/src/lib.rs | 2 +- crates/ide-ssr/src/lib.rs | 2 +- crates/ide/src/lib.rs | 2 +- crates/limit/src/lib.rs | 2 +- crates/mbe/src/lib.rs | 2 +- crates/parser/src/lib.rs | 2 +- crates/paths/src/lib.rs | 2 +- crates/proc-macro-api/src/lib.rs | 2 +- crates/proc-macro-srv/src/lib.rs | 2 +- crates/proc-macro-test/imp/src/lib.rs | 2 +- crates/proc-macro-test/src/lib.rs | 2 +- crates/profile/src/lib.rs | 2 +- crates/project-model/src/lib.rs | 2 +- crates/rust-analyzer/src/bin/main.rs | 2 +- crates/rust-analyzer/src/lib.rs | 2 +- crates/rust-analyzer/tests/slow-tests/main.rs | 2 +- crates/sourcegen/src/lib.rs | 2 +- crates/stdx/src/lib.rs | 2 +- crates/syntax/src/lib.rs | 2 +- crates/test-utils/src/lib.rs | 2 +- crates/text-edit/src/lib.rs | 2 +- crates/toolchain/src/lib.rs | 2 +- crates/tt/src/lib.rs | 2 +- crates/vfs-notify/src/lib.rs | 2 +- crates/vfs/src/lib.rs | 2 +- lib/la-arena/src/lib.rs | 2 +- lib/lsp-server/src/lib.rs | 2 +- xtask/src/main.rs | 2 +- 38 files changed, 38 insertions(+), 38 deletions(-) diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index cddd5bdf48ee..57e7934367bb 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -1,6 +1,6 @@ //! base_db defines basic database traits. The concrete DB is defined by ide. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod input; mod change; diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 8bbe5e2a8c29..6b178e7b04a7 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -1,6 +1,6 @@ //! cfg defines conditional compiling options, `cfg` attribute parser and evaluator -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod cfg_expr; mod dnf; diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 0749d91eb32a..68faca51e826 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -2,7 +2,7 @@ //! another compatible command (f.x. clippy) in a background thread and provide //! LSP diagnostics based on the output of the command. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::{ ffi::OsString, diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 35fb10e465e0..7cf13a202e02 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -7,7 +7,7 @@ //! Note that `hir_def` is a work in progress, so not all of the above is //! actually true. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[allow(unused)] diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index bc7b5fb211d1..602babcc9928 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -4,7 +4,7 @@ //! tree originates not from the text of some `FileId`, but from some macro //! expansion. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] pub mod db; pub mod ast_id_map; diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index bcf7bfa0d273..907a30301964 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -1,6 +1,6 @@ //! The type system. We currently use this to infer types for completion, hover //! information and various assists. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[allow(unused)] diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index c95c038afc96..22e14b618126 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -17,7 +17,7 @@ //! from the ide with completions, hovers, etc. It is a (soft, internal) boundary: //! . -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![recursion_limit = "512"] diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index e6f03214ed30..1e4d1c94f5be 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -58,7 +58,7 @@ //! See also this post: //! -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #[allow(unused)] macro_rules! eprintln { diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index aaf7cd7843af..37a2828e8dc8 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -1,6 +1,6 @@ //! `completions` crate provides utilities for generating completions of user input. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod completions; mod config; diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index cbd51e67810c..fefc05e53550 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -2,7 +2,7 @@ //! //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod apply_change; diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 6744895f3cd2..f9fb921f4053 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -23,7 +23,7 @@ //! There are also a couple of ad-hoc diagnostics implemented directly here, we //! don't yet have a great pattern for how to do them properly. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod handlers { pub(crate) mod break_outside_of_loop; diff --git a/crates/ide-ssr/src/lib.rs b/crates/ide-ssr/src/lib.rs index 66832a0bee49..d756e7a63eb9 100644 --- a/crates/ide-ssr/src/lib.rs +++ b/crates/ide-ssr/src/lib.rs @@ -3,7 +3,7 @@ //! Allows searching the AST for code that matches one or more patterns and then replacing that code //! based on a template. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] // Feature: Structural Search and Replace // diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 2320c95b4a1a..3390331e0e8a 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -8,7 +8,7 @@ //! in this crate. // For proving that RootDatabase is RefUnwindSafe. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![recursion_limit = "128"] diff --git a/crates/limit/src/lib.rs b/crates/limit/src/lib.rs index 7fb4b513a715..7f4b00df0bac 100644 --- a/crates/limit/src/lib.rs +++ b/crates/limit/src/lib.rs @@ -1,6 +1,6 @@ //! limit defines a struct to enforce limits. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #[cfg(feature = "tracking")] use std::sync::atomic::AtomicUsize; diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 2b52e04b251e..9331798589fc 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -6,7 +6,7 @@ //! The tests for this functionality live in another crate: //! `hir_def::macro_expansion_tests::mbe`. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod parser; mod expander; diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index fcfd1a50719b..d9b3f46f2001 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -17,7 +17,7 @@ //! //! [`Parser`]: crate::parser::Parser -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #![allow(rustdoc::private_intra_doc_links)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 88b8d0aee3a4..db705a7b69ec 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs @@ -1,7 +1,7 @@ //! Thin wrappers around `std::path`, distinguishing between absolute and //! relative paths. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::{ borrow::Borrow, diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index 9fc5a53d935f..f697ecd3518f 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -5,7 +5,7 @@ //! is used to provide basic infrastructure for communication between two //! processes: Client (RA itself), Server (the external program) -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] pub mod msg; mod process; diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs index dd327681c4c0..790e7936cdc5 100644 --- a/crates/proc-macro-srv/src/lib.rs +++ b/crates/proc-macro-srv/src/lib.rs @@ -12,7 +12,7 @@ #![cfg(any(feature = "sysroot-abi", rust_analyzer))] #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unreachable_pub)] extern crate proc_macro; diff --git a/crates/proc-macro-test/imp/src/lib.rs b/crates/proc-macro-test/imp/src/lib.rs index feeacdb6407a..32510fba2f8c 100644 --- a/crates/proc-macro-test/imp/src/lib.rs +++ b/crates/proc-macro-test/imp/src/lib.rs @@ -1,6 +1,6 @@ //! Exports a few trivial procedural macros for testing. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree}; diff --git a/crates/proc-macro-test/src/lib.rs b/crates/proc-macro-test/src/lib.rs index 6d57bc81e0e3..739c6ec6f449 100644 --- a/crates/proc-macro-test/src/lib.rs +++ b/crates/proc-macro-test/src/lib.rs @@ -1,6 +1,6 @@ //! Exports a few trivial procedural macros for testing. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] pub static PROC_MACRO_TEST_LOCATION: &str = include_str!(concat!(env!("OUT_DIR"), "/proc_macro_test_location.txt")); diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs index e7fc3d970bff..fdd724e2aab4 100644 --- a/crates/profile/src/lib.rs +++ b/crates/profile/src/lib.rs @@ -1,6 +1,6 @@ //! A collection of tools for profiling rust-analyzer. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod stop_watch; mod memory_usage; diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 901dcfd2b110..5f9b708289d1 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -15,7 +15,7 @@ //! procedural macros). //! * Lowering of concrete model to a [`base_db::CrateGraph`] -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod manifest_path; mod cargo_workspace; diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index a7d0a0b0dfc4..29bd02f92da7 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -2,7 +2,7 @@ //! //! Based on cli flags, either spawns an LSP server, or runs a batch analysis -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[cfg(feature = "in-rust-tree")] #[allow(unused_extern_crates)] diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 6c62577f696e..29bc0b80d8a1 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs @@ -9,7 +9,7 @@ //! The `cli` submodule implements some batch-processing analysis, primarily as //! a debugging aid. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] pub mod cli; diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 5cd02f784046..ec8e5c6dd968 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -8,7 +8,7 @@ //! specific JSON shapes here -- there's little value in such tests, as we can't //! be sure without a real client anyway. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #[cfg(not(feature = "in-rust-tree"))] mod sourcegen; diff --git a/crates/sourcegen/src/lib.rs b/crates/sourcegen/src/lib.rs index 1514c6c7d4c1..18fa77fd9743 100644 --- a/crates/sourcegen/src/lib.rs +++ b/crates/sourcegen/src/lib.rs @@ -6,7 +6,7 @@ //! //! This crate contains utilities to make this kind of source-gen easy. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::{ fmt, fs, mem, diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index af7846d49c64..71e269f74bc7 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -1,6 +1,6 @@ //! Missing batteries for standard libraries. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::io as sio; use std::process::Command; diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index 1d7b7de390e4..d600698040dc 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs @@ -20,7 +20,7 @@ //! [Swift]: #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #[allow(unused)] macro_rules! eprintln { diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index fd3e68e2d2c6..e48b27313068 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -6,7 +6,7 @@ //! * Extracting markup (mainly, `$0` markers) out of fixture strings. //! * marks (see the eponymous module). -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod assert_linear; pub mod bench_fixture; diff --git a/crates/text-edit/src/lib.rs b/crates/text-edit/src/lib.rs index 4705d18187af..fb52a50f0b3f 100644 --- a/crates/text-edit/src/lib.rs +++ b/crates/text-edit/src/lib.rs @@ -4,7 +4,7 @@ //! so `TextEdit` is the ultimate representation of the work done by //! rust-analyzer. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] use itertools::Itertools; use std::cmp::max; diff --git a/crates/toolchain/src/lib.rs b/crates/toolchain/src/lib.rs index 729f84a8150c..997f339edc4d 100644 --- a/crates/toolchain/src/lib.rs +++ b/crates/toolchain/src/lib.rs @@ -1,6 +1,6 @@ //! Discovery of `cargo` & `rustc` executables. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::{env, iter, path::PathBuf}; diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index 10e05862189f..481d575403ac 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs @@ -2,7 +2,7 @@ //! input and output) of macros. It closely mirrors `proc_macro` crate's //! `TokenTree`. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::fmt; diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index abfc51dfec66..030650437146 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -7,7 +7,7 @@ //! Hopefully, one day a reliable file watching/walking crate appears on //! crates.io, and we can reduce this to trivial glue code. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::fs; diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 128559727bdc..7360f68c735e 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -38,7 +38,7 @@ //! [`Handle`]: loader::Handle //! [`Entries`]: loader::Entry -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod anchored_path; pub mod file_set; diff --git a/lib/la-arena/src/lib.rs b/lib/la-arena/src/lib.rs index f39c3a3e4ca1..d195bdd156be 100644 --- a/lib/la-arena/src/lib.rs +++ b/lib/la-arena/src/lib.rs @@ -1,6 +1,6 @@ //! Yet another index-based arena. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #![warn(missing_docs)] use std::{ diff --git a/lib/lsp-server/src/lib.rs b/lib/lsp-server/src/lib.rs index b190c0af73d7..2797a6b60de4 100644 --- a/lib/lsp-server/src/lib.rs +++ b/lib/lsp-server/src/lib.rs @@ -4,7 +4,7 @@ //! //! Run with `RUST_LOG=lsp_server=debug` to see all the messages. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod msg; mod stdio; diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 6a45033ada3b..49f8ae79baf0 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -8,7 +8,7 @@ //! This binary is integrated into the `cargo` command line by using an alias in //! `.cargo/config`. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod flags; From 4c15158843724b335212ebced262b723446c3d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 5 Dec 2023 13:38:35 +0200 Subject: [PATCH 04/62] Bump ra-ap-rustc_lexer --- Cargo.lock | 14 ++++++++++++-- crates/rustc-dependencies/Cargo.toml | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e51017c6ab6b..8736bf6796c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1448,6 +1448,16 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "ra-ap-rustc_lexer" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc741c7a78103efab416b562e35bd73c8d4967478575010c86c6062f8d3cbf29" +dependencies = [ + "unicode-properties", + "unicode-xid", +] + [[package]] name = "ra-ap-rustc_parse_format" version = "0.20.0" @@ -1455,7 +1465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6b325ee1ec90e4dbd4394913adf4ef32e4fcf2b311ec9563a0fa50cd549af6" dependencies = [ "ra-ap-rustc_index", - "ra-ap-rustc_lexer", + "ra-ap-rustc_lexer 0.20.0", ] [[package]] @@ -1607,7 +1617,7 @@ version = "0.0.0" dependencies = [ "ra-ap-rustc_abi", "ra-ap-rustc_index", - "ra-ap-rustc_lexer", + "ra-ap-rustc_lexer 0.21.0", "ra-ap-rustc_parse_format", ] diff --git a/crates/rustc-dependencies/Cargo.toml b/crates/rustc-dependencies/Cargo.toml index cd7ec3059365..af86170efdd9 100644 --- a/crates/rustc-dependencies/Cargo.toml +++ b/crates/rustc-dependencies/Cargo.toml @@ -11,7 +11,7 @@ authors.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ra-ap-rustc_lexer = { version = "0.20.0" } +ra-ap-rustc_lexer = { version = "0.21.0" } ra-ap-rustc_parse_format = { version = "0.20.0", default-features = false } ra-ap-rustc_index = { version = "0.20.0", default-features = false } ra-ap-rustc_abi = { version = "0.20.0", default-features = false } From e9bc90cf2f2b3be69d4eefeda882cc4af594e8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 5 Dec 2023 13:39:17 +0200 Subject: [PATCH 05/62] Bump ra-ap-rustc_parse_format --- Cargo.lock | 39 ++++++++++++++++++---------- crates/rustc-dependencies/Cargo.toml | 2 +- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8736bf6796c7..14fe22b74edd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1411,7 +1411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5f38444d48da534b3bb612713fce9b0aeeffb2e0dfa242764f55482acc5b52d" dependencies = [ "bitflags 1.3.2", - "ra-ap-rustc_index", + "ra-ap-rustc_index 0.20.0", "tracing", ] @@ -1422,7 +1422,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69fb5da07e1a39222d9c311203123c3b6a86420fa06dc695aa1661b0aecf8d16" dependencies = [ "arrayvec", - "ra-ap-rustc_index_macros", + "ra-ap-rustc_index_macros 0.20.0", + "smallvec", +] + +[[package]] +name = "ra-ap-rustc_index" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8352918d61aa4afab9f2ed7314cf638976b20949b3d61d2f468c975b0d251f24" +dependencies = [ + "arrayvec", + "ra-ap-rustc_index_macros 0.21.0", "smallvec", ] @@ -1439,13 +1450,15 @@ dependencies = [ ] [[package]] -name = "ra-ap-rustc_lexer" -version = "0.20.0" +name = "ra-ap-rustc_index_macros" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5e8650195795c4023d8321846466994a975bc457cb8a91c0b3b17a5fc8ba40" +checksum = "66a9424018828155a3e3596515598f90e68427d8f35eff6df7f0856c73fc58a8" dependencies = [ - "unicode-properties", - "unicode-xid", + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] @@ -1460,12 +1473,12 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6b325ee1ec90e4dbd4394913adf4ef32e4fcf2b311ec9563a0fa50cd549af6" +checksum = "d557201d71792487bd2bab637ab5be9aa6fff59b88e25e12de180b0f9d2df60f" dependencies = [ - "ra-ap-rustc_index", - "ra-ap-rustc_lexer 0.20.0", + "ra-ap-rustc_index 0.21.0", + "ra-ap-rustc_lexer", ] [[package]] @@ -1616,8 +1629,8 @@ name = "rustc-dependencies" version = "0.0.0" dependencies = [ "ra-ap-rustc_abi", - "ra-ap-rustc_index", - "ra-ap-rustc_lexer 0.21.0", + "ra-ap-rustc_index 0.20.0", + "ra-ap-rustc_lexer", "ra-ap-rustc_parse_format", ] diff --git a/crates/rustc-dependencies/Cargo.toml b/crates/rustc-dependencies/Cargo.toml index af86170efdd9..3e260b175b6b 100644 --- a/crates/rustc-dependencies/Cargo.toml +++ b/crates/rustc-dependencies/Cargo.toml @@ -12,7 +12,7 @@ authors.workspace = true [dependencies] ra-ap-rustc_lexer = { version = "0.21.0" } -ra-ap-rustc_parse_format = { version = "0.20.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.21.0", default-features = false } ra-ap-rustc_index = { version = "0.20.0", default-features = false } ra-ap-rustc_abi = { version = "0.20.0", default-features = false } From 41a8497878807f98ff5562e820db5f789691c690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 5 Dec 2023 13:39:45 +0200 Subject: [PATCH 06/62] Revert "Temporarily revert delay_bug to delayed_bug change" This reverts commit 6d2543b62256676718f1936a182f92b10f2ca8ac. --- crates/hir-ty/src/layout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index b2591f016d42..27c794998687 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -110,7 +110,7 @@ struct LayoutCx<'a> { impl<'a> LayoutCalculator for LayoutCx<'a> { type TargetDataLayoutRef = &'a TargetDataLayout; - fn delay_bug(&self, txt: String) { + fn delayed_bug(&self, txt: String) { never!("{}", txt); } From 23fb593dfed16843649c0a27080c5a49338ad2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 5 Dec 2023 13:40:29 +0200 Subject: [PATCH 07/62] Bump ra-ap-rustc_index and ra-ap-rustc_abi --- Cargo.lock | 35 +++++----------------------- crates/rustc-dependencies/Cargo.toml | 4 ++-- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 14fe22b74edd..5ba5c15a1d3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1406,26 +1406,15 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5f38444d48da534b3bb612713fce9b0aeeffb2e0dfa242764f55482acc5b52d" +checksum = "7816f980fab89e878ff2e916e2077d484e3aa1c619a3cc982c8a417c3dfe45fa" dependencies = [ "bitflags 1.3.2", - "ra-ap-rustc_index 0.20.0", + "ra-ap-rustc_index", "tracing", ] -[[package]] -name = "ra-ap-rustc_index" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fb5da07e1a39222d9c311203123c3b6a86420fa06dc695aa1661b0aecf8d16" -dependencies = [ - "arrayvec", - "ra-ap-rustc_index_macros 0.20.0", - "smallvec", -] - [[package]] name = "ra-ap-rustc_index" version = "0.21.0" @@ -1433,22 +1422,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8352918d61aa4afab9f2ed7314cf638976b20949b3d61d2f468c975b0d251f24" dependencies = [ "arrayvec", - "ra-ap-rustc_index_macros 0.21.0", + "ra-ap-rustc_index_macros", "smallvec", ] -[[package]] -name = "ra-ap-rustc_index_macros" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d69f9f6af58124f2da0cb8b0c3d8494e0d883a5fe0c6732258bde81ac5a87cc" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "ra-ap-rustc_index_macros" version = "0.21.0" @@ -1477,7 +1454,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d557201d71792487bd2bab637ab5be9aa6fff59b88e25e12de180b0f9d2df60f" dependencies = [ - "ra-ap-rustc_index 0.21.0", + "ra-ap-rustc_index", "ra-ap-rustc_lexer", ] @@ -1629,7 +1606,7 @@ name = "rustc-dependencies" version = "0.0.0" dependencies = [ "ra-ap-rustc_abi", - "ra-ap-rustc_index 0.20.0", + "ra-ap-rustc_index", "ra-ap-rustc_lexer", "ra-ap-rustc_parse_format", ] diff --git a/crates/rustc-dependencies/Cargo.toml b/crates/rustc-dependencies/Cargo.toml index 3e260b175b6b..1b3b6ec735e7 100644 --- a/crates/rustc-dependencies/Cargo.toml +++ b/crates/rustc-dependencies/Cargo.toml @@ -13,8 +13,8 @@ authors.workspace = true [dependencies] ra-ap-rustc_lexer = { version = "0.21.0" } ra-ap-rustc_parse_format = { version = "0.21.0", default-features = false } -ra-ap-rustc_index = { version = "0.20.0", default-features = false } -ra-ap-rustc_abi = { version = "0.20.0", default-features = false } +ra-ap-rustc_index = { version = "0.21.0", default-features = false } +ra-ap-rustc_abi = { version = "0.21.0", default-features = false } [features] in-rust-tree = [] From 136470596a2e6b2eab75d6b967c3488bcb31ac21 Mon Sep 17 00:00:00 2001 From: dfireBird Date: Mon, 4 Dec 2023 22:06:19 +0530 Subject: [PATCH 08/62] Insert fn call parens only if the parens inserted around field name --- .../ide-completion/src/completions/record.rs | 25 +++++++++++++++++++ crates/ide-completion/src/render.rs | 12 ++++----- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index 945c3945bfa3..46213deb0afe 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -427,6 +427,31 @@ fn foo() { ..Default::default() }; } +"#, + ); + } + + #[test] + fn callable_field_struct_init() { + check_edit( + "field", + r#" +struct S { + field: fn(), +} + +fn main() { + S {fi$0 +} +"#, + r#" +struct S { + field: fn(), +} + +fn main() { + S {field +} "#, ); } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 048730c078d7..830d7cabab58 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -169,14 +169,14 @@ pub(crate) fn render_field( if let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone()) { builder.insert(receiver.syntax().text_range().start(), "(".to_string()); builder.insert(ctx.source_range().end(), ")".to_string()); - } - } - let is_parens_needed = - !matches!(dot_access.kind, DotAccessKind::Method { has_parens: true }); + let is_parens_needed = + !matches!(dot_access.kind, DotAccessKind::Method { has_parens: true }); - if is_parens_needed { - builder.insert(ctx.source_range().end(), "()".to_string()); + if is_parens_needed { + builder.insert(ctx.source_range().end(), "()".to_string()); + } + } } } From 99e4fc432272cba87e29cf289bb588a71431db03 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 3 Dec 2023 20:20:38 +0100 Subject: [PATCH 09/62] Improve macro descension API --- crates/hir-expand/src/lib.rs | 2 - crates/hir/src/lib.rs | 4 +- crates/hir/src/semantics.rs | 176 ++++++++++-------- .../extract_expressions_from_format_string.rs | 9 +- .../src/handlers/extract_function.rs | 8 +- crates/ide-db/src/helpers.rs | 4 +- crates/ide-db/src/search.rs | 8 +- crates/ide/src/call_hierarchy.rs | 4 +- crates/ide/src/doc_links.rs | 8 +- crates/ide/src/expand_macro.rs | 8 +- crates/ide/src/extend_selection.rs | 19 +- crates/ide/src/goto_declaration.rs | 4 +- crates/ide/src/goto_definition.rs | 4 +- crates/ide/src/goto_implementation.rs | 4 +- crates/ide/src/goto_type_definition.rs | 3 +- crates/ide/src/highlight_related.rs | 4 +- crates/ide/src/hover.rs | 12 +- crates/ide/src/moniker.rs | 4 +- crates/ide/src/references.rs | 4 +- crates/ide/src/signature_help.rs | 7 +- crates/ide/src/syntax_highlighting.rs | 18 +- 21 files changed, 177 insertions(+), 137 deletions(-) diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 602babcc9928..71c98b2770aa 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -629,8 +629,6 @@ impl ExpansionInfo { pub fn map_range_down<'a>( &'a self, span: SpanData, - // FIXME: use this for range mapping, so that we can resolve inline format args - _relative_token_offset: Option, ) -> Option> + 'a> { let tokens = self .exp_map diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 22e14b618126..53e60c5862ad 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -92,7 +92,9 @@ pub use crate::{ attrs::{resolve_doc_path_on, HasAttrs}, diagnostics::*, has_source::HasSource, - semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, + semantics::{ + DescendPreference, PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits, + }, }; // Be careful with these re-exports. diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index ed3d3f1a3b69..7d3c89ddb61f 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -2,7 +2,11 @@ mod source_to_def; -use std::{cell::RefCell, fmt, iter, mem, ops}; +use std::{ + cell::RefCell, + fmt, iter, mem, + ops::{self, ControlFlow}, +}; use base_db::{FileId, FileRange}; use either::Either; @@ -39,6 +43,12 @@ use crate::{ TypeAlias, TypeParam, VariantDef, }; +pub enum DescendPreference { + SameText, + SameKind, + None, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum PathResolution { /// An item @@ -397,6 +407,7 @@ impl<'db> SemanticsImpl<'db> { // This might not be the correct way to do this, but it works for now let mut res = smallvec![]; let tokens = (|| { + // FIXME: the trivia skipping should not be necessary let first = skip_trivia_token(node.syntax().first_token()?, Direction::Next)?; let last = skip_trivia_token(node.syntax().last_token()?, Direction::Prev)?; Some((first, last)) @@ -407,18 +418,19 @@ impl<'db> SemanticsImpl<'db> { }; if first == last { + // node is just the token, so descend the token self.descend_into_macros_impl(first, 0.into(), &mut |InFile { value, .. }| { if let Some(node) = value.parent_ancestors().find_map(N::cast) { res.push(node) } - false + ControlFlow::Continue(()) }); } else { // Descend first and last token, then zip them to look for the node they belong to let mut scratch: SmallVec<[_; 1]> = smallvec![]; self.descend_into_macros_impl(first, 0.into(), &mut |token| { scratch.push(token); - false + ControlFlow::Continue(()) }); let mut scratch = scratch.into_iter(); @@ -441,7 +453,7 @@ impl<'db> SemanticsImpl<'db> { } } } - false + ControlFlow::Continue(()) }, ); } @@ -453,32 +465,43 @@ impl<'db> SemanticsImpl<'db> { /// be considered for the mapping in case of inline format args. pub fn descend_into_macros( &self, + mode: DescendPreference, token: SyntaxToken, offset: TextSize, ) -> SmallVec<[SyntaxToken; 1]> { - let mut res = smallvec![]; - self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| { - res.push(value); - false - }); - res - } - - /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token. - /// - /// Returns the original non descended token if none of the mapped counterparts have the same text. - pub fn descend_into_macros_with_same_text( - &self, - token: SyntaxToken, - offset: TextSize, - ) -> SmallVec<[SyntaxToken; 1]> { - let text = token.text(); + enum Dp<'t> { + SameText(&'t str), + SameKind(SyntaxKind), + None, + } + let fetch_kind = |token: &SyntaxToken| match token.parent() { + Some(node) => match node.kind() { + kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => kind, + _ => token.kind(), + }, + None => token.kind(), + }; + let mode = match mode { + DescendPreference::SameText => Dp::SameText(token.text()), + DescendPreference::SameKind => Dp::SameKind(fetch_kind(&token)), + DescendPreference::None => Dp::None, + }; let mut res = smallvec![]; self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| { - if value.text() == text { + let is_a_match = match mode { + Dp::SameText(text) => value.text() == text, + Dp::SameKind(preferred_kind) => { + let kind = fetch_kind(&value); + kind == preferred_kind + // special case for derive macros + || (preferred_kind == SyntaxKind::IDENT && kind == SyntaxKind::NAME_REF) + } + Dp::None => true, + }; + if is_a_match { res.push(value); } - false + ControlFlow::Continue(()) }); if res.is_empty() { res.push(token); @@ -486,45 +509,48 @@ impl<'db> SemanticsImpl<'db> { res } - pub fn descend_into_macros_with_kind_preference( + pub fn descend_into_macros_single( &self, + mode: DescendPreference, token: SyntaxToken, offset: TextSize, ) -> SyntaxToken { + enum Dp<'t> { + SameText(&'t str), + SameKind(SyntaxKind), + None, + } let fetch_kind = |token: &SyntaxToken| match token.parent() { Some(node) => match node.kind() { - kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => { - node.parent().map_or(kind, |it| it.kind()) - } + kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => kind, _ => token.kind(), }, None => token.kind(), }; - let preferred_kind = fetch_kind(&token); - let mut res = None; + let mode = match mode { + DescendPreference::SameText => Dp::SameText(token.text()), + DescendPreference::SameKind => Dp::SameKind(fetch_kind(&token)), + DescendPreference::None => Dp::None, + }; + let mut res = token.clone(); self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| { - if fetch_kind(&value) == preferred_kind { - res = Some(value); - true - } else { - if let None = res { - res = Some(value) + let is_a_match = match mode { + Dp::SameText(text) => value.text() == text, + Dp::SameKind(preferred_kind) => { + let kind = fetch_kind(&value); + kind == preferred_kind + // special case for derive macros + || (preferred_kind == SyntaxKind::IDENT && kind == SyntaxKind::NAME_REF) } - false + Dp::None => true, + }; + if is_a_match { + res = value; + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } }); - res.unwrap_or(token) - } - - /// Descend the token into its macro call if it is part of one, returning the token in the - /// expansion that it is associated with. If `offset` points into the token's range, it will - /// be considered for the mapping in case of inline format args. - pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken { - let mut res = token.clone(); - self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| { - res = value; - true - }); res } @@ -535,7 +561,7 @@ impl<'db> SemanticsImpl<'db> { // FIXME: We might want this to be Option to be able to opt out of subrange // mapping, specifically for node downmapping _offset: TextSize, - f: &mut dyn FnMut(InFile) -> bool, + f: &mut dyn FnMut(InFile) -> ControlFlow<()>, ) { // FIXME: Clean this up let _p = profile::span("descend_into_macros"); @@ -560,25 +586,24 @@ impl<'db> SemanticsImpl<'db> { let def_map = sa.resolver.def_map(); let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)]; - let mut process_expansion_for_token = - |stack: &mut SmallVec<_>, macro_file, _token: InFile<&_>| { - let expansion_info = cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(self.db.upcast())); + let mut process_expansion_for_token = |stack: &mut SmallVec<_>, macro_file| { + let expansion_info = cache + .entry(macro_file) + .or_insert_with(|| macro_file.expansion_info(self.db.upcast())); - { - let InFile { file_id, value } = expansion_info.expanded(); - self.cache(value, file_id); - } + { + let InFile { file_id, value } = expansion_info.expanded(); + self.cache(value, file_id); + } - let mapped_tokens = expansion_info.map_range_down(span, None)?; - let len = stack.len(); + let mapped_tokens = expansion_info.map_range_down(span)?; + let len = stack.len(); - // requeue the tokens we got from mapping our current token down - stack.extend(mapped_tokens.map(Into::into)); - // if the length changed we have found a mapping for the token - (stack.len() != len).then_some(()) - }; + // requeue the tokens we got from mapping our current token down + stack.extend(mapped_tokens.map(Into::into)); + // if the length changed we have found a mapping for the token + (stack.len() != len).then_some(()) + }; // Remap the next token in the queue into a macro call its in, if it is not being remapped // either due to not being in a macro-call or because its unused push it into the result vec, @@ -598,7 +623,7 @@ impl<'db> SemanticsImpl<'db> { }); if let Some(call_id) = containing_attribute_macro_call { let file_id = call_id.as_macro_file(); - return process_expansion_for_token(&mut stack, file_id, token.as_ref()); + return process_expansion_for_token(&mut stack, file_id); } // Then check for token trees, that means we are either in a function-like macro or @@ -624,7 +649,7 @@ impl<'db> SemanticsImpl<'db> { it } }; - process_expansion_for_token(&mut stack, file_id, token.as_ref()) + process_expansion_for_token(&mut stack, file_id) } else if let Some(meta) = ast::Meta::cast(parent) { // attribute we failed expansion for earlier, this might be a derive invocation // or derive helper attribute @@ -646,11 +671,7 @@ impl<'db> SemanticsImpl<'db> { Some(call_id) => { // resolved to a derive let file_id = call_id.as_macro_file(); - return process_expansion_for_token( - &mut stack, - file_id, - token.as_ref(), - ); + return process_expansion_for_token(&mut stack, file_id); } None => Some(adt), } @@ -682,11 +703,8 @@ impl<'db> SemanticsImpl<'db> { def_map.derive_helpers_in_scope(InFile::new(token.file_id, id))?; let mut res = None; for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) { - res = res.or(process_expansion_for_token( - &mut stack, - derive.as_macro_file(), - token.as_ref(), - )); + res = + res.or(process_expansion_for_token(&mut stack, derive.as_macro_file())); } res } else { @@ -695,7 +713,7 @@ impl<'db> SemanticsImpl<'db> { })() .is_none(); - if was_not_remapped && f(token) { + if was_not_remapped && f(token).is_break() { break; } } @@ -711,7 +729,7 @@ impl<'db> SemanticsImpl<'db> { offset: TextSize, ) -> impl Iterator + '_> + '_ { node.token_at_offset(offset) - .map(move |token| self.descend_into_macros(token, offset)) + .map(move |token| self.descend_into_macros(DescendPreference::None, token, offset)) .map(|descendants| { descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it)) }) diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index 31a1ff496e13..55f2fd9f6ce8 100644 --- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -1,4 +1,5 @@ use crate::{AssistContext, Assists}; +use hir::DescendPreference; use ide_db::{ assists::{AssistId, AssistKind}, syntax_helpers::{ @@ -34,9 +35,11 @@ pub(crate) fn extract_expressions_from_format_string( let fmt_string = ctx.find_token_at_offset::()?; let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?; - let expanded_t = ast::String::cast( - ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone(), 0.into()), - )?; + let expanded_t = ast::String::cast(ctx.sema.descend_into_macros_single( + DescendPreference::SameKind, + fmt_string.syntax().clone(), + 0.into(), + ))?; if !is_format_string(&expanded_t) { return None; } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 6b48d1588152..9b892ac1e981 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -3,8 +3,8 @@ use std::iter; use ast::make; use either::Either; use hir::{ - HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, PathResolution, Semantics, - TypeInfo, TypeParam, + DescendPreference, HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, + PathResolution, Semantics, TypeInfo, TypeParam, }; use ide_db::{ defs::{Definition, NameRefClass}, @@ -751,7 +751,9 @@ impl FunctionBody { .descendants_with_tokens() .filter_map(SyntaxElement::into_token) .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self])) - .flat_map(|t| sema.descend_into_macros(t, 0.into())) + .flat_map(|t| { + sema.descend_into_macros(DescendPreference::None, t, 0.into()) + }) .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast))); } } diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index 330af442f754..d4b031879d11 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs @@ -3,7 +3,7 @@ use std::collections::VecDeque; use base_db::{FileId, SourceDatabaseExt}; -use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics}; +use hir::{Crate, DescendPreference, ItemInNs, ModuleDef, Name, Semantics}; use syntax::{ ast::{self, make}, AstToken, SyntaxKind, SyntaxToken, TokenAtOffset, @@ -117,7 +117,7 @@ pub fn get_definition( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, ) -> Option { - for token in sema.descend_into_macros(token, 0.into()) { + for token in sema.descend_into_macros(DescendPreference::None, token, 0.into()) { let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); if let Some(&[x]) = def.as_deref() { return Some(x); diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 2ce036c044ad..c1ed17503fe9 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -8,8 +8,8 @@ use std::mem; use base_db::{salsa::Database, FileId, FileRange, SourceDatabase, SourceDatabaseExt}; use hir::{ - AsAssocItem, DefWithBody, HasAttrs, HasSource, HirFileIdExt, InFile, InRealFile, ModuleSource, - Semantics, Visibility, + AsAssocItem, DefWithBody, DescendPreference, HasAttrs, HasSource, HirFileIdExt, InFile, + InRealFile, ModuleSource, Semantics, Visibility, }; use memchr::memmem::Finder; use nohash_hasher::IntMap; @@ -467,7 +467,9 @@ impl<'a> FindUsages<'a> { // every textual hit. That function is notoriously // expensive even for things that do not get down mapped // into macros. - sema.descend_into_macros(token, offset).into_iter().filter_map(|it| it.parent()) + sema.descend_into_macros(DescendPreference::None, token, offset) + .into_iter() + .filter_map(|it| it.parent()) }) }; diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index 6f41f51f80ec..70391cd847b9 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs @@ -1,6 +1,6 @@ //! Entry point for call-hierarchy -use hir::Semantics; +use hir::{DescendPreference, Semantics}; use ide_db::{ defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, @@ -87,7 +87,7 @@ pub(crate) fn outgoing_calls( })?; let mut calls = CallLocations::default(); - sema.descend_into_macros(token, offset) + sema.descend_into_macros(DescendPreference::None, token, offset) .into_iter() .filter_map(|it| it.parent_ancestors().nth(1).and_then(ast::Item::cast)) .filter_map(|item| match item { diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index ac15b6aba618..97fa7dee306a 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -12,7 +12,9 @@ use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions use stdx::format_to; use url::Url; -use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasAttrs}; +use hir::{ + db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, DescendPreference, HasAttrs, +}; use ide_db::{ base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase}, defs::{Definition, NameClass, NameRefClass}, @@ -144,7 +146,7 @@ pub(crate) fn external_docs( kind if kind.is_trivia() => 0, _ => 1, })?; - let token = sema.descend_into_macros_single(token, offset); + let token = sema.descend_into_macros_single(DescendPreference::None, token, offset); let node = token.parent()?; let definition = match_ast! { @@ -286,7 +288,7 @@ impl DocCommentToken { let original_start = doc_token.text_range().start(); let relative_comment_offset = offset - original_start - prefix_len; - sema.descend_into_macros(doc_token, offset).into_iter().find_map(|t| { + sema.descend_into_macros(DescendPreference::None,doc_token, offset).into_iter().find_map(|t| { let (node, descended_prefix_len) = match_ast! { match t { ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?), diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index a70f335ada3c..cc878dc7196b 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -1,4 +1,4 @@ -use hir::{HirFileIdExt, InFile, Semantics}; +use hir::{DescendPreference, HirFileIdExt, InFile, Semantics}; use ide_db::{ base_db::FileId, helpers::pick_best_token, syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase, @@ -40,8 +40,10 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< // struct Bar; // ``` - let derive = - sema.descend_into_macros(tok.clone(), 0.into()).into_iter().find_map(|descended| { + let derive = sema + .descend_into_macros(DescendPreference::None, tok.clone(), 0.into()) + .into_iter() + .find_map(|descended| { let hir_file = sema.hir_file_for(&descended.parent()?); if !hir_file.is_derive_attr_pseudo_expansion(db) { return None; diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index 9b2ff070c74b..1cdbf7840e75 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs @@ -1,6 +1,6 @@ use std::iter::successors; -use hir::Semantics; +use hir::{DescendPreference, Semantics}; use ide_db::RootDatabase; use syntax::{ algo::{self, skip_trivia_token}, @@ -140,10 +140,16 @@ fn extend_tokens_from_range( // compute original mapped token range let extended = { - let fst_expanded = - sema.descend_into_macros_single(first_token.clone(), original_range.start()); - let lst_expanded = - sema.descend_into_macros_single(last_token.clone(), original_range.end()); + let fst_expanded = sema.descend_into_macros_single( + DescendPreference::None, + first_token.clone(), + original_range.start(), + ); + let lst_expanded = sema.descend_into_macros_single( + DescendPreference::None, + last_token.clone(), + original_range.end(), + ); let mut lca = algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?; lca = shallowest_node(&lca); @@ -157,7 +163,8 @@ fn extend_tokens_from_range( let validate = |offset: TextSize| { let extended = &extended; move |token: &SyntaxToken| -> bool { - let expanded = sema.descend_into_macros_single(token.clone(), offset); + let expanded = + sema.descend_into_macros_single(DescendPreference::None, token.clone(), offset); let parent = match expanded.parent() { Some(it) => it, None => return false, diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs index 7e0fab42608b..ee94dff5fb30 100644 --- a/crates/ide/src/goto_declaration.rs +++ b/crates/ide/src/goto_declaration.rs @@ -1,4 +1,4 @@ -use hir::{AsAssocItem, Semantics}; +use hir::{AsAssocItem, DescendPreference, Semantics}; use ide_db::{ defs::{Definition, NameClass, NameRefClass}, RootDatabase, @@ -29,7 +29,7 @@ pub(crate) fn goto_declaration( .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?; let range = original_token.text_range(); let info: Vec = sema - .descend_into_macros(original_token, offset) + .descend_into_macros(DescendPreference::None, original_token, offset) .iter() .filter_map(|token| { let parent = token.parent()?; diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 816f1bebee42..635f82686206 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -4,7 +4,7 @@ use crate::{ doc_links::token_as_doc_comment, navigation_target::ToNav, FilePosition, NavigationTarget, RangeInfo, TryToNav, }; -use hir::{AsAssocItem, AssocItem, Semantics}; +use hir::{AsAssocItem, AssocItem, DescendPreference, Semantics}; use ide_db::{ base_db::{AnchoredPath, FileId, FileLoader}, defs::{Definition, IdentClass}, @@ -56,7 +56,7 @@ pub(crate) fn goto_definition( }); } let navs = sema - .descend_into_macros(original_token.clone(), offset) + .descend_into_macros(DescendPreference::None, original_token.clone(), offset) .into_iter() .filter_map(|token| { let parent = token.parent()?; diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 3593c5c7dd9c..6c1b9966a88d 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -1,4 +1,4 @@ -use hir::{AsAssocItem, Impl, Semantics}; +use hir::{AsAssocItem, DescendPreference, Impl, Semantics}; use ide_db::{ defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, @@ -34,7 +34,7 @@ pub(crate) fn goto_implementation( })?; let range = original_token.text_range(); let navs = - sema.descend_into_macros(original_token, offset) + sema.descend_into_macros(DescendPreference::None, original_token, offset) .into_iter() .filter_map(|token| token.parent().and_then(ast::NameLike::cast)) .filter_map(|node| match &node { diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 955923d76910..4bd8bfdacb86 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs @@ -1,3 +1,4 @@ +use hir::DescendPreference; use ide_db::{base_db::Upcast, defs::Definition, helpers::pick_best_token, RootDatabase}; use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, T}; @@ -37,7 +38,7 @@ pub(crate) fn goto_type_definition( } }; let range = token.text_range(); - sema.descend_into_macros(token, offset) + sema.descend_into_macros(DescendPreference::None,token, offset) .into_iter() .filter_map(|token| { let ty = sema diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index a7f5ae92a4ca..620e59a71bf5 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -1,4 +1,4 @@ -use hir::Semantics; +use hir::{DescendPreference, Semantics}; use ide_db::{ base_db::{FileId, FilePosition, FileRange}, defs::{Definition, IdentClass}, @@ -461,7 +461,7 @@ fn find_defs( token: SyntaxToken, offset: TextSize, ) -> FxHashSet { - sema.descend_into_macros(token, offset) + sema.descend_into_macros(DescendPreference::None, token, offset) .into_iter() .filter_map(|token| IdentClass::classify_token(sema, &token)) .map(IdentClass::definitions_no_ops) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index e0b64fe7988e..7f2783df3d05 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -6,7 +6,7 @@ mod tests; use std::iter; use either::Either; -use hir::{db::DefDatabase, HasSource, LangItem, Semantics}; +use hir::{db::DefDatabase, DescendPreference, HasSource, LangItem, Semantics}; use ide_db::{ base_db::FileRange, defs::{Definition, IdentClass, NameRefClass, OperatorClass}, @@ -161,11 +161,11 @@ fn hover_simple( // prefer descending the same token kind in attribute expansions, in normal macros text // equivalency is more important - let descended = if in_attr { - [sema.descend_into_macros_with_kind_preference(original_token.clone(), offset)].into() - } else { - sema.descend_into_macros_with_same_text(original_token.clone(), offset) - }; + let descended = sema.descend_into_macros( + if in_attr { DescendPreference::SameKind } else { DescendPreference::SameText }, + original_token.clone(), + offset, + ); let descended = || descended.iter(); let result = descended() diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 2ca2b5b1d5f3..28d455f4e591 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -1,7 +1,7 @@ //! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports) //! for LSIF and LSP. -use hir::{AsAssocItem, AssocItemContainer, Crate, Semantics}; +use hir::{AsAssocItem, AssocItemContainer, Crate, DescendPreference, Semantics}; use ide_db::{ base_db::{CrateOrigin, FilePosition, LangCrateOrigin}, defs::{Definition, IdentClass}, @@ -99,7 +99,7 @@ pub(crate) fn moniker( }); } let navs = sema - .descend_into_macros(original_token.clone(), offset) + .descend_into_macros(DescendPreference::None, original_token.clone(), offset) .into_iter() .filter_map(|token| { IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| { diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index f387bbf6b014..2285ab199343 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -9,7 +9,7 @@ //! at the index that the match starts at and its tree parent is //! resolved to the search element definition, we get a reference. -use hir::{PathResolution, Semantics}; +use hir::{DescendPreference, PathResolution, Semantics}; use ide_db::{ base_db::FileId, defs::{Definition, NameClass, NameRefClass}, @@ -126,7 +126,7 @@ pub(crate) fn find_defs<'a>( ) }); token.map(|token| { - sema.descend_into_macros_with_same_text(token, offset) + sema.descend_into_macros(DescendPreference::SameText, token, offset) .into_iter() .filter_map(|it| ast::NameLike::cast(it.parent()?)) .filter_map(move |name_like| { diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index e020b52e1710..c95f9994615d 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -4,7 +4,10 @@ use std::collections::BTreeSet; use either::Either; -use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait}; +use hir::{ + AssocItem, DescendPreference, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, + Trait, +}; use ide_db::{ active_parameter::{callable_for_node, generic_def_for_node}, base_db::FilePosition, @@ -79,7 +82,7 @@ pub(crate) fn signature_help( // if the cursor is sandwiched between two space tokens and the call is unclosed // this prevents us from leaving the CallExpression .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?; - let token = sema.descend_into_macros_single(token, offset); + let token = sema.descend_into_macros_single(DescendPreference::None, token, offset); for node in token.parent_ancestors() { match_ast! { diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index dd72484b3807..b2db6bb5c43f 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -13,7 +13,7 @@ mod html; #[cfg(test)] mod tests; -use hir::{Name, Semantics}; +use hir::{DescendPreference, Name, Semantics}; use ide_db::{FxHashMap, RootDatabase, SymbolKind}; use syntax::{ ast::{self, IsString}, @@ -393,14 +393,14 @@ fn traverse( // Attempt to descend tokens into macro-calls. let res = match element { NodeOrToken::Token(token) if token.kind() != COMMENT => { - let token = match attr_or_derive_item { - Some(AttrOrDerive::Attr(_)) => { - sema.descend_into_macros_with_kind_preference(token, 0.into()) - } - Some(AttrOrDerive::Derive(_)) | None => { - sema.descend_into_macros_single(token, 0.into()) - } - }; + let token = sema.descend_into_macros_single( + match attr_or_derive_item { + Some(AttrOrDerive::Attr(_)) => DescendPreference::SameKind, + Some(AttrOrDerive::Derive(_)) | None => DescendPreference::None, + }, + token, + 0.into(), + ); match token.parent().and_then(ast::NameLike::cast) { // Remap the token into the wrapping single token nodes Some(parent) => match (token.kind(), parent.syntax().kind()) { From 903086c616f780acb85efc1a12b66477a41016f1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 5 Dec 2023 15:42:39 +0100 Subject: [PATCH 10/62] Implicit format args support --- crates/hir-def/src/body.rs | 12 ++ crates/hir-def/src/body/lower.rs | 24 ++-- crates/hir-def/src/body/tests.rs | 6 +- crates/hir-def/src/hir/format_args.rs | 14 ++- crates/hir/src/semantics.rs | 64 +++++++--- crates/hir/src/source_analyzer.rs | 66 +++++++--- .../ide-assists/src/handlers/bool_to_enum.rs | 2 + .../convert_tuple_return_type_to_struct.rs | 2 + .../extract_expressions_from_format_string.rs | 9 +- .../src/handlers/extract_function.rs | 4 +- .../ide-assists/src/handlers/inline_call.rs | 8 +- .../src/handlers/inline_local_variable.rs | 4 +- .../replace_named_generic_with_impl.rs | 5 +- .../src/handlers/unnecessary_async.rs | 6 +- crates/ide-db/src/helpers.rs | 2 +- crates/ide-db/src/rename.rs | 10 +- crates/ide-db/src/search.rs | 117 +++++++++++++++--- crates/ide/src/call_hierarchy.rs | 2 +- crates/ide/src/doc_links.rs | 4 +- crates/ide/src/expand_macro.rs | 2 +- crates/ide/src/extend_selection.rs | 23 ++-- crates/ide/src/goto_declaration.rs | 2 +- crates/ide/src/goto_definition.rs | 38 ++++-- crates/ide/src/goto_implementation.rs | 2 +- crates/ide/src/goto_type_definition.rs | 85 ++++++++++--- crates/ide/src/highlight_related.rs | 39 ++++-- crates/ide/src/hover.rs | 18 ++- crates/ide/src/hover/tests.rs | 60 +++++++++ crates/ide/src/moniker.rs | 2 +- crates/ide/src/references.rs | 50 ++++++-- crates/ide/src/rename.rs | 91 +++++++++++--- crates/ide/src/runnables.rs | 4 +- crates/ide/src/signature_help.rs | 2 +- crates/ide/src/syntax_highlighting.rs | 1 - crates/mbe/src/token_map.rs | 1 + crates/syntax/src/ast/token_ext.rs | 6 + crates/syntax/src/token_text.rs | 2 +- 37 files changed, 615 insertions(+), 174 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 1942c60c075d..db28c6731ece 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -95,6 +95,8 @@ pub struct BodySourceMap { field_map_back: FxHashMap, pat_field_map_back: FxHashMap, + format_args_template_map: FxHashMap>, + expansions: FxHashMap>, HirFileId>, /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in @@ -387,6 +389,14 @@ impl BodySourceMap { self.expr_map.get(&src).copied() } + pub fn implicit_format_args( + &self, + node: InFile<&ast::FormatArgsExpr>, + ) -> Option<&[(syntax::TextRange, Name)]> { + let src = node.map(AstPtr::new).map(AstPtr::upcast::); + self.format_args_template_map.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref) + } + /// Get a reference to the body source map's diagnostics. pub fn diagnostics(&self) -> &[BodyDiagnostic] { &self.diagnostics @@ -403,8 +413,10 @@ impl BodySourceMap { field_map_back, pat_field_map_back, expansions, + format_args_template_map, diagnostics, } = self; + format_args_template_map.shrink_to_fit(); expr_map.shrink_to_fit(); expr_map_back.shrink_to_fit(); pat_map.shrink_to_fit(); diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 0466068ec810..c22bd6e2e57c 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -1597,12 +1597,20 @@ impl ExprCollector<'_> { }); let template = f.template(); let fmt_snippet = template.as_ref().map(ToString::to_string); + let mut mappings = vec![]; let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) { - Some((s, is_direct_literal)) => { - format_args::parse(&s, fmt_snippet, args, is_direct_literal, |name| { - self.alloc_expr_desugared(Expr::Path(Path::from(name))) - }) - } + Some((s, is_direct_literal)) => format_args::parse( + &s, + fmt_snippet, + args, + is_direct_literal, + |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))), + |name, span| { + if let Some(span) = span { + mappings.push((span, name.clone())) + } + }, + ), None => FormatArgs { template: Default::default(), arguments: args.finish() }, }; @@ -1746,14 +1754,16 @@ impl ExprCollector<'_> { tail: Some(unsafe_arg_new), }); - self.alloc_expr( + let idx = self.alloc_expr( Expr::Call { callee: new_v1_formatted, args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]), is_assignee_expr: false, }, syntax_ptr, - ) + ); + self.source_map.format_args_template_map.insert(idx, mappings); + idx } /// Generate a hir expression for a format_args placeholder specification. diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs index 5517abb1ab70..2b432dfbb92b 100644 --- a/crates/hir-def/src/body/tests.rs +++ b/crates/hir-def/src/body/tests.rs @@ -160,7 +160,7 @@ fn main() { let count = 10; builtin#lang(Arguments::new_v1_formatted)( &[ - "\"hello ", " ", " friends, we ", " ", "", "\"", + "hello ", " ", " friends, we ", " ", "", ], &[ builtin#lang(Argument::new_display)( @@ -261,7 +261,7 @@ impl SsrError { _ = $crate::error::SsrError::new( builtin#lang(Arguments::new_v1_formatted)( &[ - "\"Failed to resolve path `", "`\"", + "Failed to resolve path `", "`", ], &[ builtin#lang(Argument::new_display)( @@ -320,7 +320,7 @@ fn f() { $crate::panicking::panic_fmt( builtin#lang(Arguments::new_v1_formatted)( &[ - "\"cc\"", + "cc", ], &[], &[], diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs index 46d24bd4a614..068abb27a25c 100644 --- a/crates/hir-def/src/hir/format_args.rs +++ b/crates/hir-def/src/hir/format_args.rs @@ -5,7 +5,7 @@ use hir_expand::name::Name; use rustc_dependencies::parse_format as parse; use syntax::{ ast::{self, IsString}, - AstToken, SmolStr, TextRange, + SmolStr, TextRange, TextSize, }; use crate::hir::ExprId; @@ -170,15 +170,18 @@ pub(crate) fn parse( mut args: FormatArgumentsCollector, is_direct_literal: bool, mut synth: impl FnMut(Name) -> ExprId, + mut record_usage: impl FnMut(Name, Option), ) -> FormatArgs { - let text = s.text(); + let text = s.text_without_quotes(); let str_style = match s.quote_offsets() { Some(offsets) => { let raw = u32::from(offsets.quotes.0.len()) - 1; - (raw != 0).then_some(raw as usize) + // subtract 1 for the `r` prefix + (raw != 0).then(|| raw as usize - 1) } None => None, }; + let mut parser = parse::Parser::new(text, str_style, fmt_snippet, false, parse::ParseMode::Format); @@ -199,6 +202,7 @@ pub(crate) fn parse( let to_span = |inner_span: parse::InnerSpan| { is_source_literal.then(|| { TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap()) + - TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1) }) }; @@ -230,9 +234,10 @@ pub(crate) fn parse( Err(index) } } - ArgRef::Name(name, _span) => { + ArgRef::Name(name, span) => { let name = Name::new_text_dont_use(SmolStr::new(name)); if let Some((index, _)) = args.by_name(&name) { + record_usage(name, span); // Name found in `args`, so we resolve it to its index. if index < args.explicit_args().len() { // Mark it as used, if it was an explicit argument. @@ -246,6 +251,7 @@ pub(crate) fn parse( // disabled (see RFC #2795) // FIXME: Diagnose } + record_usage(name.clone(), span); Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(name.clone()), // FIXME: This is problematic, we might want to synthesize a dummy diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 7d3c89ddb61f..b0e0f969d98f 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -29,8 +29,9 @@ use smallvec::{smallvec, SmallVec}; use stdx::TupleExt; use syntax::{ algo::skip_trivia_token, - ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody}, - match_ast, AstNode, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize, + ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody, IsString as _}, + match_ast, AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, + TextRange, TextSize, }; use crate::{ @@ -49,7 +50,7 @@ pub enum DescendPreference { None, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum PathResolution { /// An item Def(ModuleDef), @@ -402,6 +403,41 @@ impl<'db> SemanticsImpl<'db> { ) } + pub fn resolve_offset_in_format_args( + &self, + string: ast::String, + offset: TextSize, + ) -> Option<(TextRange, Option)> { + debug_assert!(offset <= string.syntax().text_range().len()); + let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; + let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; + let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + source_analyzer.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + } + + pub fn check_for_format_args_template( + &self, + original_token: SyntaxToken, + offset: TextSize, + ) -> Option<(TextRange, Option)> { + if let Some(original_string) = ast::String::cast(original_token.clone()) { + if let Some(quote) = original_string.open_quote_text_range() { + return self + .descend_into_macros(DescendPreference::SameText, original_token.clone()) + .into_iter() + .find_map(|token| { + self.resolve_offset_in_format_args( + ast::String::cast(token)?, + offset - quote.end(), + ) + }) + .map(|(range, res)| (range + quote.end(), res)); + } + } + None + } + /// Maps a node down by mapping its first and last token down. pub fn descend_node_into_attributes(&self, node: N) -> SmallVec<[N; 1]> { // This might not be the correct way to do this, but it works for now @@ -419,8 +455,12 @@ impl<'db> SemanticsImpl<'db> { if first == last { // node is just the token, so descend the token - self.descend_into_macros_impl(first, 0.into(), &mut |InFile { value, .. }| { - if let Some(node) = value.parent_ancestors().find_map(N::cast) { + self.descend_into_macros_impl(first, &mut |InFile { value, .. }| { + if let Some(node) = value + .parent_ancestors() + .take_while(|it| it.text_range() == value.text_range()) + .find_map(N::cast) + { res.push(node) } ControlFlow::Continue(()) @@ -428,7 +468,7 @@ impl<'db> SemanticsImpl<'db> { } else { // Descend first and last token, then zip them to look for the node they belong to let mut scratch: SmallVec<[_; 1]> = smallvec![]; - self.descend_into_macros_impl(first, 0.into(), &mut |token| { + self.descend_into_macros_impl(first, &mut |token| { scratch.push(token); ControlFlow::Continue(()) }); @@ -436,7 +476,6 @@ impl<'db> SemanticsImpl<'db> { let mut scratch = scratch.into_iter(); self.descend_into_macros_impl( last, - 0.into(), &mut |InFile { value: last, file_id: last_fid }| { if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() { if first_fid == last_fid { @@ -467,7 +506,6 @@ impl<'db> SemanticsImpl<'db> { &self, mode: DescendPreference, token: SyntaxToken, - offset: TextSize, ) -> SmallVec<[SyntaxToken; 1]> { enum Dp<'t> { SameText(&'t str), @@ -487,7 +525,7 @@ impl<'db> SemanticsImpl<'db> { DescendPreference::None => Dp::None, }; let mut res = smallvec![]; - self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| { + self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| { let is_a_match = match mode { Dp::SameText(text) => value.text() == text, Dp::SameKind(preferred_kind) => { @@ -513,7 +551,6 @@ impl<'db> SemanticsImpl<'db> { &self, mode: DescendPreference, token: SyntaxToken, - offset: TextSize, ) -> SyntaxToken { enum Dp<'t> { SameText(&'t str), @@ -533,7 +570,7 @@ impl<'db> SemanticsImpl<'db> { DescendPreference::None => Dp::None, }; let mut res = token.clone(); - self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| { + self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| { let is_a_match = match mode { Dp::SameText(text) => value.text() == text, Dp::SameKind(preferred_kind) => { @@ -558,9 +595,6 @@ impl<'db> SemanticsImpl<'db> { fn descend_into_macros_impl( &self, token: SyntaxToken, - // FIXME: We might want this to be Option to be able to opt out of subrange - // mapping, specifically for node downmapping - _offset: TextSize, f: &mut dyn FnMut(InFile) -> ControlFlow<()>, ) { // FIXME: Clean this up @@ -729,7 +763,7 @@ impl<'db> SemanticsImpl<'db> { offset: TextSize, ) -> impl Iterator + '_> + '_ { node.token_at_offset(offset) - .map(move |token| self.descend_into_macros(DescendPreference::None, token, offset)) + .map(move |token| self.descend_into_macros(DescendPreference::None, token)) .map(|descendants| { descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it)) }) diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 8b1148368961..9fdee209cfbc 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -820,6 +820,29 @@ impl SourceAnalyzer { false } + pub(crate) fn resolve_offset_in_format_args( + &self, + db: &dyn HirDatabase, + format_args: InFile<&ast::FormatArgsExpr>, + offset: TextSize, + ) -> Option<(TextRange, Option)> { + let implicits = self.body_source_map()?.implicit_format_args(format_args)?; + implicits.iter().find(|(range, _)| range.contains_inclusive(offset)).map(|(range, name)| { + ( + *range, + resolve_hir_value_path( + db, + &self.resolver, + self.resolver.body_owner(), + &Path::from_known_path_with_no_generic(ModPath::from_segments( + PathKind::Plain, + Some(name.clone()), + )), + ), + ) + }) + } + fn resolve_impl_method_or_trait_def( &self, db: &dyn HirDatabase, @@ -1038,24 +1061,7 @@ fn resolve_hir_path_( }; let body_owner = resolver.body_owner(); - let values = || { - resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| { - let res = match val { - ValueNs::LocalBinding(binding_id) => { - let var = Local { parent: body_owner?, binding_id }; - PathResolution::Local(var) - } - ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), - ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), - ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), - ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), - ValueNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()), - ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), - ValueNs::GenericParam(id) => PathResolution::ConstParam(id.into()), - }; - Some(res) - }) - }; + let values = || resolve_hir_value_path(db, resolver, body_owner, path); let items = || { resolver @@ -1075,6 +1081,30 @@ fn resolve_hir_path_( .or_else(macros) } +fn resolve_hir_value_path( + db: &dyn HirDatabase, + resolver: &Resolver, + body_owner: Option, + path: &Path, +) -> Option { + resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| { + let res = match val { + ValueNs::LocalBinding(binding_id) => { + let var = Local { parent: body_owner?, binding_id }; + PathResolution::Local(var) + } + ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), + ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), + ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), + ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), + ValueNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()), + ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), + ValueNs::GenericParam(id) => PathResolution::ConstParam(id.into()), + }; + Some(res) + }) +} + /// Resolves a path where we know it is a qualifier of another path. /// /// For example, if we have: diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/bool_to_enum.rs index 11facc5bee2a..0f2d1057c0a4 100644 --- a/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -328,6 +328,7 @@ fn augment_references_with_imports( references .iter() .filter_map(|FileReference { range, name, .. }| { + let name = name.clone().into_name_like()?; ctx.sema.scope(name.syntax()).map(|scope| (*range, name, scope.module())) }) .map(|(range, name, ref_module)| { @@ -455,6 +456,7 @@ fn add_enum_def( .iter() .flat_map(|(_, refs)| refs) .filter_map(|FileReference { name, .. }| { + let name = name.clone().into_name_like()?; ctx.sema.scope(name.syntax()).map(|scope| scope.module()) }) .any(|module| module.nearest_non_block_module(ctx.db()) != *target_module); diff --git a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 1f3caa7db33f..79b46d66121e 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -186,6 +186,7 @@ fn augment_references_with_imports( references .iter() .filter_map(|FileReference { name, .. }| { + let name = name.clone().into_name_like()?; ctx.sema.scope(name.syntax()).map(|scope| (name, scope.module())) }) .map(|(name, ref_module)| { @@ -238,6 +239,7 @@ fn add_tuple_struct_def( .iter() .flat_map(|(_, refs)| refs) .filter_map(|FileReference { name, .. }| { + let name = name.clone().into_name_like()?; ctx.sema.scope(name.syntax()).map(|scope| scope.module()) }) .any(|module| module.nearest_non_block_module(ctx.db()) != *target_module); diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index 55f2fd9f6ce8..9d72d3af096a 100644 --- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -35,11 +35,10 @@ pub(crate) fn extract_expressions_from_format_string( let fmt_string = ctx.find_token_at_offset::()?; let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?; - let expanded_t = ast::String::cast(ctx.sema.descend_into_macros_single( - DescendPreference::SameKind, - fmt_string.syntax().clone(), - 0.into(), - ))?; + let expanded_t = ast::String::cast( + ctx.sema + .descend_into_macros_single(DescendPreference::SameKind, fmt_string.syntax().clone()), + )?; if !is_format_string(&expanded_t) { return None; } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 9b892ac1e981..d4a12307790b 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -751,9 +751,7 @@ impl FunctionBody { .descendants_with_tokens() .filter_map(SyntaxElement::into_token) .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self])) - .flat_map(|t| { - sema.descend_into_macros(DescendPreference::None, t, 0.into()) - }) + .flat_map(|t| sema.descend_into_macros(DescendPreference::None, t)) .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast))); } } diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index f8c75bdb0de1..5b9cc5f66cde 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -8,7 +8,7 @@ use ide_db::{ defs::Definition, imports::insert_use::remove_path_if_in_use_stmt, path_transform::PathTransform, - search::{FileReference, SearchScope}, + search::{FileReference, FileReferenceNode, SearchScope}, source_change::SourceChangeBuilder, syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref}, RootDatabase, @@ -148,7 +148,7 @@ pub(super) fn split_refs_and_uses( ) -> (Vec, Vec) { iter.into_iter() .filter_map(|file_ref| match file_ref.name { - ast::NameLike::NameRef(name_ref) => Some(name_ref), + FileReferenceNode::NameRef(name_ref) => Some(name_ref), _ => None, }) .filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) { @@ -346,7 +346,7 @@ fn inline( match param.as_local(sema.db) { Some(l) => usages_for_locals(l) .map(|FileReference { name, range, .. }| match name { - ast::NameLike::NameRef(_) => body + FileReferenceNode::NameRef(_) => body .syntax() .covering_element(range) .ancestors() @@ -372,7 +372,7 @@ fn inline( if let Some(self_local) = params[0].2.as_local(sema.db) { usages_for_locals(self_local) .filter_map(|FileReference { name, range, .. }| match name { - ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)), + FileReferenceNode::NameRef(_) => Some(body.syntax().covering_element(range)), _ => None, }) .for_each(|usage| { diff --git a/crates/ide-assists/src/handlers/inline_local_variable.rs b/crates/ide-assists/src/handlers/inline_local_variable.rs index 49dcde75d2b3..5d8ba43ec846 100644 --- a/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -2,7 +2,7 @@ use hir::{PathResolution, Semantics}; use ide_db::{ base_db::FileId, defs::Definition, - search::{FileReference, UsageSearchResult}, + search::{FileReference, FileReferenceNode, UsageSearchResult}, RootDatabase, }; use syntax::{ @@ -63,7 +63,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) let wrap_in_parens = references .into_iter() .filter_map(|FileReference { range, name, .. }| match name { - ast::NameLike::NameRef(name) => Some((range, name)), + FileReferenceNode::NameRef(name) => Some((range, name)), _ => None, }) .map(|(range, name_ref)| { diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index c7c0be4c7d4f..e61ce481727a 100644 --- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -59,7 +59,10 @@ pub(crate) fn replace_named_generic_with_impl( let mut path_types_to_replace = Vec::new(); for (_a, refs) in usage_refs.iter() { for usage_ref in refs { - let param_node = find_path_type(&ctx.sema, &type_param_name, &usage_ref.name)?; + let Some(name_like) = usage_ref.name.clone().into_name_like() else { + continue; + }; + let param_node = find_path_type(&ctx.sema, &type_param_name, &name_like)?; path_types_to_replace.push(param_node); } } diff --git a/crates/ide-assists/src/handlers/unnecessary_async.rs b/crates/ide-assists/src/handlers/unnecessary_async.rs index 7f612c2a142c..1cfa291a29d8 100644 --- a/crates/ide-assists/src/handlers/unnecessary_async.rs +++ b/crates/ide-assists/src/handlers/unnecessary_async.rs @@ -2,11 +2,11 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::FileId, defs::Definition, - search::FileReference, + search::{FileReference, FileReferenceNode}, syntax_helpers::node_ext::full_path_of_name_ref, }; use syntax::{ - ast::{self, NameLike, NameRef}, + ast::{self, NameRef}, AstNode, SyntaxKind, TextRange, }; @@ -76,7 +76,7 @@ pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> O for await_expr in find_all_references(ctx, &Definition::Function(fn_def)) // Keep only references that correspond NameRefs. .filter_map(|(_, reference)| match reference.name { - NameLike::NameRef(nameref) => Some(nameref), + FileReferenceNode::NameRef(nameref) => Some(nameref), _ => None, }) // Keep only references that correspond to await expressions diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index d4b031879d11..9363bdfa14b2 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs @@ -117,7 +117,7 @@ pub fn get_definition( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, ) -> Option { - for token in sema.descend_into_macros(DescendPreference::None, token, 0.into()) { + for token in sema.descend_into_macros(DescendPreference::None, token) { let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); if let Some(&[x]) = def.as_deref() { return Some(x); diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 676d286b8dcc..d2b6a732689c 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -34,7 +34,7 @@ use text_edit::{TextEdit, TextEditBuilder}; use crate::{ defs::Definition, - search::FileReference, + search::{FileReference, FileReferenceNode}, source_change::{FileSystemEdit, SourceChange}, syntax_helpers::node_ext::expr_as_name_ref, traits::convert_to_def_in_trait, @@ -361,7 +361,7 @@ pub fn source_edit_from_references( // macros can cause multiple refs to occur for the same text range, so keep track of what we have edited so far let mut edited_ranges = Vec::new(); for &FileReference { range, ref name, .. } in references { - let name_range = name.syntax().text_range(); + let name_range = name.text_range(); if name_range.len() != range.len() { // This usage comes from a different token kind that was downmapped to a NameLike in a macro // Renaming this will most likely break things syntax-wise @@ -371,17 +371,17 @@ pub fn source_edit_from_references( // if the ranges differ then the node is inside a macro call, we can't really attempt // to make special rewrites like shorthand syntax and such, so just rename the node in // the macro input - ast::NameLike::NameRef(name_ref) if name_range == range => { + FileReferenceNode::NameRef(name_ref) if name_range == range => { source_edit_from_name_ref(&mut edit, name_ref, new_name, def) } - ast::NameLike::Name(name) if name_range == range => { + FileReferenceNode::Name(name) if name_range == range => { source_edit_from_name(&mut edit, name, new_name) } _ => false, }; if !has_emitted_edit && !edited_ranges.contains(&range.start()) { let (range, new_name) = match name { - ast::NameLike::Lifetime(_) => ( + FileReferenceNode::Lifetime(_) => ( TextRange::new(range.start() + syntax::TextSize::from(1), range.end()), new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(), ), diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index c1ed17503fe9..dbef36026822 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -9,13 +9,13 @@ use std::mem; use base_db::{salsa::Database, FileId, FileRange, SourceDatabase, SourceDatabaseExt}; use hir::{ AsAssocItem, DefWithBody, DescendPreference, HasAttrs, HasSource, HirFileIdExt, InFile, - InRealFile, ModuleSource, Semantics, Visibility, + InRealFile, ModuleSource, PathResolution, Semantics, Visibility, }; use memchr::memmem::Finder; use nohash_hasher::IntMap; use once_cell::unsync::Lazy; use parser::SyntaxKind; -use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; +use syntax::{ast, match_ast, AstNode, AstToken, SyntaxElement, TextRange, TextSize}; use triomphe::Arc; use crate::{ @@ -63,10 +63,67 @@ pub struct FileReference { /// The range of the reference in the original file pub range: TextRange, /// The node of the reference in the (macro-)file - pub name: ast::NameLike, + pub name: FileReferenceNode, pub category: Option, } +#[derive(Debug, Clone)] +pub enum FileReferenceNode { + Name(ast::Name), + NameRef(ast::NameRef), + Lifetime(ast::Lifetime), + FormatStringEntry(ast::String, TextRange), +} + +impl FileReferenceNode { + pub fn text_range(&self) -> TextRange { + match self { + FileReferenceNode::Name(it) => it.syntax().text_range(), + FileReferenceNode::NameRef(it) => it.syntax().text_range(), + FileReferenceNode::Lifetime(it) => it.syntax().text_range(), + FileReferenceNode::FormatStringEntry(_, range) => *range, + } + } + pub fn syntax(&self) -> SyntaxElement { + match self { + FileReferenceNode::Name(it) => it.syntax().clone().into(), + FileReferenceNode::NameRef(it) => it.syntax().clone().into(), + FileReferenceNode::Lifetime(it) => it.syntax().clone().into(), + FileReferenceNode::FormatStringEntry(it, _) => it.syntax().clone().into(), + } + } + pub fn into_name_like(self) -> Option { + match self { + FileReferenceNode::Name(it) => Some(ast::NameLike::Name(it)), + FileReferenceNode::NameRef(it) => Some(ast::NameLike::NameRef(it)), + FileReferenceNode::Lifetime(it) => Some(ast::NameLike::Lifetime(it)), + FileReferenceNode::FormatStringEntry(_, _) => None, + } + } + pub fn as_name_ref(&self) -> Option<&ast::NameRef> { + match self { + FileReferenceNode::NameRef(name_ref) => Some(name_ref), + _ => None, + } + } + pub fn as_lifetime(&self) -> Option<&ast::Lifetime> { + match self { + FileReferenceNode::Lifetime(lifetime) => Some(lifetime), + _ => None, + } + } + pub fn text(&self) -> syntax::TokenText<'_> { + match self { + FileReferenceNode::NameRef(name_ref) => name_ref.text(), + FileReferenceNode::Name(name) => name.text(), + FileReferenceNode::Lifetime(lifetime) => lifetime.text(), + FileReferenceNode::FormatStringEntry(it, range) => { + syntax::TokenText::borrowed(&it.text()[*range - it.syntax().text_range().start()]) + } + } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ReferenceCategory { // FIXME: Add this variant and delete the `retain_adt_literal_usages` function. @@ -467,7 +524,7 @@ impl<'a> FindUsages<'a> { // every textual hit. That function is notoriously // expensive even for things that do not get down mapped // into macros. - sema.descend_into_macros(DescendPreference::None, token, offset) + sema.descend_into_macros(DescendPreference::None, token) .into_iter() .filter_map(|it| it.parent()) }) @@ -479,6 +536,17 @@ impl<'a> FindUsages<'a> { // Search for occurrences of the items name for offset in match_indices(&text, finder, search_range) { + tree.token_at_offset(offset).into_iter().for_each(|token| { + let Some(str_token) = ast::String::cast(token.clone()) else { return }; + if let Some((range, nameres)) = + sema.check_for_format_args_template(token.clone(), offset) + { + if self.found_format_args_ref(file_id, range, str_token, nameres, sink) { + return; + } + } + }); + for name in find_nodes(name, &tree, offset).filter_map(ast::NameLike::cast) { if match name { ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink), @@ -593,7 +661,7 @@ impl<'a> FindUsages<'a> { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { range, - name: ast::NameLike::NameRef(name_ref.clone()), + name: FileReferenceNode::NameRef(name_ref.clone()), category: None, }; sink(file_id, reference) @@ -612,7 +680,7 @@ impl<'a> FindUsages<'a> { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { range, - name: ast::NameLike::NameRef(name_ref.clone()), + name: FileReferenceNode::NameRef(name_ref.clone()), category: is_name_ref_in_import(name_ref).then_some(ReferenceCategory::Import), }; sink(file_id, reference) @@ -621,6 +689,27 @@ impl<'a> FindUsages<'a> { } } + fn found_format_args_ref( + &self, + file_id: FileId, + range: TextRange, + token: ast::String, + res: Option, + sink: &mut dyn FnMut(FileId, FileReference) -> bool, + ) -> bool { + match res.map(Definition::from) { + Some(def) if def == self.def => { + let reference = FileReference { + range, + name: FileReferenceNode::FormatStringEntry(token, range), + category: Some(ReferenceCategory::Read), + }; + sink(file_id, reference) + } + _ => false, + } + } + fn found_lifetime( &self, lifetime: &ast::Lifetime, @@ -631,7 +720,7 @@ impl<'a> FindUsages<'a> { let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); let reference = FileReference { range, - name: ast::NameLike::Lifetime(lifetime.clone()), + name: FileReferenceNode::Lifetime(lifetime.clone()), category: None, }; sink(file_id, reference) @@ -655,7 +744,7 @@ impl<'a> FindUsages<'a> { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { range, - name: ast::NameLike::NameRef(name_ref.clone()), + name: FileReferenceNode::NameRef(name_ref.clone()), category: ReferenceCategory::new(&def, name_ref), }; sink(file_id, reference) @@ -671,7 +760,7 @@ impl<'a> FindUsages<'a> { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { range, - name: ast::NameLike::NameRef(name_ref.clone()), + name: FileReferenceNode::NameRef(name_ref.clone()), category: ReferenceCategory::new(&def, name_ref), }; sink(file_id, reference) @@ -681,7 +770,7 @@ impl<'a> FindUsages<'a> { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { range, - name: ast::NameLike::NameRef(name_ref.clone()), + name: FileReferenceNode::NameRef(name_ref.clone()), category: ReferenceCategory::new(&def, name_ref), }; sink(file_id, reference) @@ -705,7 +794,7 @@ impl<'a> FindUsages<'a> { }; let reference = FileReference { range, - name: ast::NameLike::NameRef(name_ref.clone()), + name: FileReferenceNode::NameRef(name_ref.clone()), category: access, }; sink(file_id, reference) @@ -728,7 +817,7 @@ impl<'a> FindUsages<'a> { let FileRange { file_id, range } = self.sema.original_range(name.syntax()); let reference = FileReference { range, - name: ast::NameLike::Name(name.clone()), + name: FileReferenceNode::Name(name.clone()), // FIXME: mutable patterns should have `Write` access category: Some(ReferenceCategory::Read), }; @@ -738,7 +827,7 @@ impl<'a> FindUsages<'a> { let FileRange { file_id, range } = self.sema.original_range(name.syntax()); let reference = FileReference { range, - name: ast::NameLike::Name(name.clone()), + name: FileReferenceNode::Name(name.clone()), category: None, }; sink(file_id, reference) @@ -763,7 +852,7 @@ impl<'a> FindUsages<'a> { let FileRange { file_id, range } = self.sema.original_range(name.syntax()); let reference = FileReference { range, - name: ast::NameLike::Name(name.clone()), + name: FileReferenceNode::Name(name.clone()), category: None, }; sink(file_id, reference) diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index 70391cd847b9..5cc64e60ed85 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs @@ -87,7 +87,7 @@ pub(crate) fn outgoing_calls( })?; let mut calls = CallLocations::default(); - sema.descend_into_macros(DescendPreference::None, token, offset) + sema.descend_into_macros(DescendPreference::None, token) .into_iter() .filter_map(|it| it.parent_ancestors().nth(1).and_then(ast::Item::cast)) .filter_map(|item| match item { diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 97fa7dee306a..9760f9daf0a3 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -146,7 +146,7 @@ pub(crate) fn external_docs( kind if kind.is_trivia() => 0, _ => 1, })?; - let token = sema.descend_into_macros_single(DescendPreference::None, token, offset); + let token = sema.descend_into_macros_single(DescendPreference::None, token); let node = token.parent()?; let definition = match_ast! { @@ -288,7 +288,7 @@ impl DocCommentToken { let original_start = doc_token.text_range().start(); let relative_comment_offset = offset - original_start - prefix_len; - sema.descend_into_macros(DescendPreference::None,doc_token, offset).into_iter().find_map(|t| { + sema.descend_into_macros(DescendPreference::None, doc_token).into_iter().find_map(|t| { let (node, descended_prefix_len) = match_ast! { match t { ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?), diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index cc878dc7196b..653d9f088384 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -41,7 +41,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< // ``` let derive = sema - .descend_into_macros(DescendPreference::None, tok.clone(), 0.into()) + .descend_into_macros(DescendPreference::None, tok.clone()) .into_iter() .find_map(|descended| { let hir_file = sema.hir_file_for(&descended.parent()?); diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index 1cdbf7840e75..b706e959d34e 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs @@ -140,16 +140,10 @@ fn extend_tokens_from_range( // compute original mapped token range let extended = { - let fst_expanded = sema.descend_into_macros_single( - DescendPreference::None, - first_token.clone(), - original_range.start(), - ); - let lst_expanded = sema.descend_into_macros_single( - DescendPreference::None, - last_token.clone(), - original_range.end(), - ); + let fst_expanded = + sema.descend_into_macros_single(DescendPreference::None, first_token.clone()); + let lst_expanded = + sema.descend_into_macros_single(DescendPreference::None, last_token.clone()); let mut lca = algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?; lca = shallowest_node(&lca); @@ -160,11 +154,10 @@ fn extend_tokens_from_range( }; // Compute parent node range - let validate = |offset: TextSize| { + let validate = || { let extended = &extended; move |token: &SyntaxToken| -> bool { - let expanded = - sema.descend_into_macros_single(DescendPreference::None, token.clone(), offset); + let expanded = sema.descend_into_macros_single(DescendPreference::None, token.clone()); let parent = match expanded.parent() { Some(it) => it, None => return false, @@ -178,14 +171,14 @@ fn extend_tokens_from_range( let token = token.prev_token()?; skip_trivia_token(token, Direction::Prev) }) - .take_while(validate(original_range.start())) + .take_while(validate()) .last()?; let last = successors(Some(last_token), |token| { let token = token.next_token()?; skip_trivia_token(token, Direction::Next) }) - .take_while(validate(original_range.end())) + .take_while(validate()) .last()?; let range = first.text_range().cover(last.text_range()); diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs index ee94dff5fb30..ad7ec1964567 100644 --- a/crates/ide/src/goto_declaration.rs +++ b/crates/ide/src/goto_declaration.rs @@ -29,7 +29,7 @@ pub(crate) fn goto_declaration( .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?; let range = original_token.text_range(); let info: Vec = sema - .descend_into_macros(DescendPreference::None, original_token, offset) + .descend_into_macros(DescendPreference::None, original_token) .iter() .filter_map(|token| { let parent = token.parent()?; diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 635f82686206..5ca82a362f53 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -55,8 +55,21 @@ pub(crate) fn goto_definition( Some(RangeInfo::new(link_range, vec![nav])) }); } + + if let Some((range, resolution)) = + sema.check_for_format_args_template(original_token.clone(), offset) + { + return Some(RangeInfo::new( + range, + match resolution { + Some(res) => def_to_nav(db, Definition::from(res)), + None => vec![], + }, + )); + } + let navs = sema - .descend_into_macros(DescendPreference::None, original_token.clone(), offset) + .descend_into_macros(DescendPreference::None, original_token.clone()) .into_iter() .filter_map(|token| { let parent = token.parent()?; @@ -809,18 +822,13 @@ mod confuse_index { fn foo(); } fn goto_through_format() { check( r#" +//- minicore: fmt #[macro_export] macro_rules! format { ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) } -#[rustc_builtin_macro] -#[macro_export] -macro_rules! format_args { - ($fmt:expr) => ({ /* compiler built-in */ }); - ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) -} pub mod __export { - pub use crate::format_args; + pub use core::format_args; fn foo() {} // for index confusion } fn foo() -> i8 {} @@ -2056,6 +2064,20 @@ fn f2() { struct S2; S1::e$0(); } +"#, + ); + } + + #[test] + fn implicit_format_args() { + check( + r#" +//- minicore: fmt +fn test() { + let a = "world"; + // ^ + format_args!("hello {a$0}"); +} "#, ); } diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 6c1b9966a88d..bb474282dd7c 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -34,7 +34,7 @@ pub(crate) fn goto_implementation( })?; let range = original_token.text_range(); let navs = - sema.descend_into_macros(DescendPreference::None, original_token, offset) + sema.descend_into_macros(DescendPreference::None, original_token) .into_iter() .filter_map(|token| token.parent().and_then(ast::NameLike::cast)) .filter_map(|node| match &node { diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 4bd8bfdacb86..83f134aaaf57 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs @@ -1,4 +1,4 @@ -use hir::DescendPreference; +use hir::{DescendPreference, GenericParam}; use ide_db::{base_db::Upcast, defs::Definition, helpers::pick_best_token, RootDatabase}; use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, T}; @@ -37,8 +37,37 @@ pub(crate) fn goto_type_definition( } } }; + let mut process_ty = |ty: hir::Type| { + // collect from each `ty` into the `res` result vec + let ty = ty.strip_references(); + ty.walk(db, |t| { + if let Some(adt) = t.as_adt() { + push(adt.into()); + } else if let Some(trait_) = t.as_dyn_trait() { + push(trait_.into()); + } else if let Some(traits) = t.as_impl_traits(db) { + traits.for_each(|it| push(it.into())); + } else if let Some(trait_) = t.as_associated_type_parent_trait(db) { + push(trait_.into()); + } + }); + }; + if let Some((range, resolution)) = sema.check_for_format_args_template(token.clone(), offset) { + if let Some(ty) = resolution.and_then(|res| match Definition::from(res) { + Definition::Const(it) => Some(it.ty(db)), + Definition::Static(it) => Some(it.ty(db)), + Definition::GenericParam(GenericParam::ConstParam(it)) => Some(it.ty(db)), + Definition::Local(it) => Some(it.ty(db)), + Definition::Adt(hir::Adt::Struct(it)) => Some(it.ty(db)), + _ => None, + }) { + process_ty(ty); + } + return Some(RangeInfo::new(range, res)); + } + let range = token.text_range(); - sema.descend_into_macros(DescendPreference::None,token, offset) + sema.descend_into_macros(DescendPreference::None, token) .into_iter() .filter_map(|token| { let ty = sema @@ -76,21 +105,7 @@ pub(crate) fn goto_type_definition( }); ty }) - .for_each(|ty| { - // collect from each `ty` into the `res` result vec - let ty = ty.strip_references(); - ty.walk(db, |t| { - if let Some(adt) = t.as_adt() { - push(adt.into()); - } else if let Some(trait_) = t.as_dyn_trait() { - push(trait_.into()); - } else if let Some(traits) = t.as_impl_traits(db) { - traits.for_each(|it| push(it.into())); - } else if let Some(trait_) = t.as_associated_type_parent_trait(db) { - push(trait_.into()); - } - }); - }); + .for_each(process_ty); Some(RangeInfo::new(range, res)) } @@ -326,6 +341,42 @@ struct Baz(T); //^^^ fn foo(x$0: Bar, Baz) {} +"#, + ); + } + + #[test] + fn implicit_format_args() { + check( + r#" +//- minicore: fmt +struct Bar; + // ^^^ + fn test() { + let a = Bar; + format_args!("hello {a$0}"); +} +"#, + ); + check( + r#" +//- minicore: fmt +struct Bar; + // ^^^ + fn test() { + format_args!("hello {Bar$0}"); +} +"#, + ); + check( + r#" +//- minicore: fmt +struct Bar; + // ^^^ +const BAR: Bar = Bar; +fn test() { + format_args!("hello {BAR$0}"); +} "#, ); } diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 620e59a71bf5..8daff8c2ebe3 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -1,3 +1,5 @@ +use std::iter; + use hir::{DescendPreference, Semantics}; use ide_db::{ base_db::{FileId, FilePosition, FileRange}, @@ -15,7 +17,6 @@ use syntax::{ SyntaxKind::{self, IDENT, INT_NUMBER}, SyntaxNode, SyntaxToken, TextRange, T, }; -use text_edit::TextSize; use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav}; @@ -132,7 +133,16 @@ fn highlight_references( token: SyntaxToken, FilePosition { file_id, offset }: FilePosition, ) -> Option> { - let defs = find_defs(sema, token.clone(), offset); + let defs = if let Some((range, resolution)) = + sema.check_for_format_args_template(token.clone(), offset) + { + match resolution.map(Definition::from) { + Some(def) => iter::once(def).collect(), + None => return Some(vec![HighlightedRange { range, category: None }]), + } + } else { + find_defs(sema, token.clone()) + }; let usages = defs .iter() .filter_map(|&d| { @@ -456,12 +466,8 @@ fn cover_range(r0: Option, r1: Option) -> Option, - token: SyntaxToken, - offset: TextSize, -) -> FxHashSet { - sema.descend_into_macros(DescendPreference::None, token, offset) +fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet { + sema.descend_into_macros(DescendPreference::None, token) .into_iter() .filter_map(|token| IdentClass::classify_token(sema, &token)) .map(IdentClass::definitions_no_ops) @@ -1620,6 +1626,23 @@ fn f2(t: T) { T::C; T::f(); } +"#, + ); + } + + #[test] + fn implicit_format_args() { + check( + r#" +//- minicore: fmt +fn test() { + let a = "foo"; + // ^ + format_args!("hello {a} {a$0} {}", a); + // ^read + // ^read + // ^read +} "#, ); } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 7f2783df3d05..88a5b623425d 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -150,6 +150,19 @@ fn hover_simple( }); } + if let Some((range, resolution)) = + sema.check_for_format_args_template(original_token.clone(), offset) + { + let res = hover_for_definition( + sema, + file_id, + Definition::from(resolution?), + &original_token.parent()?, + config, + )?; + return Some(RangeInfo::new(range, res)); + } + let in_attr = original_token .parent_ancestors() .filter_map(ast::Item::cast) @@ -164,7 +177,6 @@ fn hover_simple( let descended = sema.descend_into_macros( if in_attr { DescendPreference::SameKind } else { DescendPreference::SameText }, original_token.clone(), - offset, ); let descended = || descended.iter(); @@ -298,11 +310,11 @@ pub(crate) fn hover_for_definition( sema: &Semantics<'_, RootDatabase>, file_id: FileId, definition: Definition, - node: &SyntaxNode, + scope_node: &SyntaxNode, config: &HoverConfig, ) -> Option { let famous_defs = match &definition { - Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())), + Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(scope_node)?.krate())), _ => None, }; render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index d3d492f3fdef..53585624b68a 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -6613,3 +6613,63 @@ fn test() { "#]], ); } + +#[test] +fn format_args_implicit() { + check( + r#" +//- minicore: fmt +fn test() { +let aaaaa = "foo"; +format_args!("{aaaaa$0}"); +} +"#, + expect![[r#" + *aaaaa* + + ```rust + let aaaaa: &str // size = 16 (0x10), align = 8, niches = 1 + ``` + "#]], + ); +} + +#[test] +fn format_args_implicit2() { + check( + r#" +//- minicore: fmt +fn test() { +let aaaaa = "foo"; +format_args!("{$0aaaaa}"); +} +"#, + expect![[r#" + *aaaaa* + + ```rust + let aaaaa: &str // size = 16 (0x10), align = 8, niches = 1 + ``` + "#]], + ); +} + +#[test] +fn format_args_implicit_raw() { + check( + r#" +//- minicore: fmt +fn test() { +let aaaaa = "foo"; +format_args!(r"{$0aaaaa}"); +} +"#, + expect![[r#" + *aaaaa* + + ```rust + let aaaaa: &str // size = 16 (0x10), align = 8, niches = 1 + ``` + "#]], + ); +} diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 28d455f4e591..8e8bb5e0139e 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -99,7 +99,7 @@ pub(crate) fn moniker( }); } let navs = sema - .descend_into_macros(DescendPreference::None, original_token.clone(), offset) + .descend_into_macros(DescendPreference::None, original_token.clone()) .into_iter() .filter_map(|token| { IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| { diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 2285ab199343..b805ddfa2678 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -109,7 +109,7 @@ pub(crate) fn find_all_refs( } None => { let search = make_searcher(false); - Some(find_defs(sema, &syntax, position.offset)?.map(search).collect()) + Some(find_defs(sema, &syntax, position.offset)?.into_iter().map(search).collect()) } } } @@ -118,15 +118,27 @@ pub(crate) fn find_defs<'a>( sema: &'a Semantics<'_, RootDatabase>, syntax: &SyntaxNode, offset: TextSize, -) -> Option + 'a> { +) -> Option + 'a> { let token = syntax.token_at_offset(offset).find(|t| { matches!( t.kind(), - IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] + IDENT + | INT_NUMBER + | LIFETIME_IDENT + | STRING + | T![self] + | T![super] + | T![crate] + | T![Self] ) - }); - token.map(|token| { - sema.descend_into_macros(DescendPreference::SameText, token, offset) + })?; + + if let Some((_, resolution)) = sema.check_for_format_args_template(token.clone(), offset) { + return resolution.map(Definition::from).map(|it| vec![it]); + } + + Some( + sema.descend_into_macros(DescendPreference::SameText, token) .into_iter() .filter_map(|it| ast::NameLike::cast(it.parent()?)) .filter_map(move |name_like| { @@ -162,7 +174,8 @@ pub(crate) fn find_defs<'a>( }; Some(def) }) - }) + .collect(), + ) } pub(crate) fn decl_mutability(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> bool { @@ -2092,4 +2105,27 @@ fn main() { r#fn(); } "#]], ); } + + #[test] + fn implicit_format_args() { + check( + r#" +//- minicore: fmt +fn test() { + let a = "foo"; + format_args!("hello {a} {a$0} {}", a); + // ^ + // ^ + // ^ +} +"#, + expect![[r#" + a Local FileId(0) 20..21 20..21 + + FileId(0) 56..57 Read + FileId(0) 60..61 Read + FileId(0) 68..69 Read + "#]], + ); + } } diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index ad1eb2499718..1febfabfcb7f 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -6,14 +6,16 @@ use hir::{AsAssocItem, HirFileIdExt, InFile, Semantics}; use ide_db::{ - base_db::FileId, + base_db::{FileId, FileRange}, defs::{Definition, NameClass, NameRefClass}, rename::{bail, format_err, source_edit_from_references, IdentifierKind}, RootDatabase, }; use itertools::Itertools; use stdx::{always, never}; -use syntax::{ast, utils::is_raw_identifier, AstNode, SmolStr, SyntaxNode, TextRange, TextSize}; +use syntax::{ + ast, utils::is_raw_identifier, AstNode, SmolStr, SyntaxKind, SyntaxNode, TextRange, TextSize, +}; use text_edit::TextEdit; @@ -34,23 +36,20 @@ pub(crate) fn prepare_rename( let syntax = source_file.syntax(); let res = find_definitions(&sema, syntax, position)? - .map(|(name_like, def)| { + .map(|(frange, kind, def)| { // ensure all ranges are valid if def.range_for_rename(&sema).is_none() { bail!("No references found at position") } - let Some(frange) = sema.original_range_opt(name_like.syntax()) else { - bail!("No references found at position"); - }; always!( frange.range.contains_inclusive(position.offset) && frange.file_id == position.file_id ); - Ok(match name_like { - ast::NameLike::Lifetime(_) => { + Ok(match kind { + SyntaxKind::LIFETIME => { TextRange::new(frange.range.start() + TextSize::from(1), frange.range.end()) } _ => frange.range, @@ -93,7 +92,7 @@ pub(crate) fn rename( let defs = find_definitions(&sema, syntax, position)?; let ops: RenameResult> = defs - .map(|(_namelike, def)| { + .map(|(.., def)| { if let Definition::Local(local) = def { if let Some(self_param) = local.as_self_param(sema.db) { cov_mark::hit!(rename_self_to_param); @@ -134,11 +133,27 @@ pub(crate) fn will_rename_file( fn find_definitions( sema: &Semantics<'_, RootDatabase>, syntax: &SyntaxNode, - position: FilePosition, -) -> RenameResult> { - let symbols = sema - .find_nodes_at_offset_with_descend::(syntax, position.offset) - .map(|name_like| { + FilePosition { file_id, offset }: FilePosition, +) -> RenameResult> { + let token = syntax.token_at_offset(offset).find(|t| matches!(t.kind(), SyntaxKind::STRING)); + + if let Some((range, Some(resolution))) = + token.and_then(|token| sema.check_for_format_args_template(token, offset)) + { + return Ok(vec![( + FileRange { file_id, range }, + SyntaxKind::STRING, + Definition::from(resolution), + )] + .into_iter()); + } + + let symbols = + sema.find_nodes_at_offset_with_descend::(syntax, offset).map(|name_like| { + let kind = name_like.syntax().kind(); + let range = sema + .original_range_opt(name_like.syntax()) + .ok_or_else(|| format_err!("No references found at position"))?; let res = match &name_like { // renaming aliases would rename the item being aliased as the HIR doesn't track aliases yet ast::NameLike::Name(name) @@ -163,7 +178,6 @@ fn find_definitions( Definition::Local(local_def) } }) - .map(|def| (name_like.clone(), def)) .ok_or_else(|| format_err!("No references found at position")), ast::NameLike::NameRef(name_ref) => { NameRefClass::classify(sema, name_ref) @@ -187,7 +201,7 @@ fn find_definitions( { Err(format_err!("Renaming aliases is currently unsupported")) } else { - Ok((name_like.clone(), def)) + Ok(def) } }) } @@ -203,11 +217,10 @@ fn find_definitions( _ => None, }) }) - .map(|def| (name_like, def)) .ok_or_else(|| format_err!("No references found at position")) } }; - res + res.map(|def| (range, kind, def)) }); let res: RenameResult> = symbols.collect(); @@ -218,7 +231,7 @@ fn find_definitions( Err(format_err!("No references found at position")) } else { // remove duplicates, comparing `Definition`s - Ok(v.into_iter().unique_by(|t| t.1)) + Ok(v.into_iter().unique_by(|&(.., def)| def).collect::>().into_iter()) } } Err(e) => Err(e), @@ -2663,4 +2676,44 @@ struct A; "error: Cannot rename a non-local definition.", ) } + + #[test] + fn implicit_format_args() { + check( + "fbar", + r#" +//- minicore: fmt +fn test() { + let foo = "foo"; + format_args!("hello {foo} {foo$0} {}", foo); +} +"#, + r#" +fn test() { + let fbar = "foo"; + format_args!("hello {fbar} {fbar} {}", fbar); +} +"#, + ); + } + + #[test] + fn implicit_format_args2() { + check( + "fo", + r#" +//- minicore: fmt +fn test() { + let foo = "foo"; + format_args!("hello {foo} {foo$0} {}", foo); +} +"#, + r#" +fn test() { + let fo = "foo"; + format_args!("hello {fo} {fo} {}", fo); +} +"#, + ); + } } diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 954a364c7870..d487a538bb57 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -9,7 +9,7 @@ use ide_db::{ defs::Definition, documentation::docs_from_attrs, helpers::visit_file_defs, - search::SearchScope, + search::{FileReferenceNode, SearchScope}, FxHashMap, FxHashSet, RootDatabase, SymbolKind, }; use itertools::Itertools; @@ -240,7 +240,7 @@ fn find_related_tests( .flatten(); for ref_ in defs { let name_ref = match ref_.name { - ast::NameLike::NameRef(name_ref) => name_ref, + FileReferenceNode::NameRef(name_ref) => name_ref, _ => continue, }; if let Some(fn_def) = diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index c95f9994615d..990376a49659 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -82,7 +82,7 @@ pub(crate) fn signature_help( // if the cursor is sandwiched between two space tokens and the call is unclosed // this prevents us from leaving the CallExpression .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?; - let token = sema.descend_into_macros_single(DescendPreference::None, token, offset); + let token = sema.descend_into_macros_single(DescendPreference::None, token); for node in token.parent_ancestors() { match_ast! { diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index b2db6bb5c43f..ac14afa13208 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -399,7 +399,6 @@ fn traverse( Some(AttrOrDerive::Derive(_)) | None => DescendPreference::None, }, token, - 0.into(), ); match token.parent().and_then(ast::NameLike::cast) { // Remap the token into the wrapping single token nodes diff --git a/crates/mbe/src/token_map.rs b/crates/mbe/src/token_map.rs index b51d7575a113..28b39b4f1eec 100644 --- a/crates/mbe/src/token_map.rs +++ b/crates/mbe/src/token_map.rs @@ -35,6 +35,7 @@ impl SpanMap { /// /// Note this does a linear search through the entire backing vector. pub fn ranges_with_span(&self, span: S) -> impl Iterator + '_ { + // FIXME: This should ignore the syntax context! self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| { if s != span { return None; diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 8cc271d226c4..d5d565a015a0 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -121,6 +121,7 @@ impl ast::Whitespace { } } +#[derive(Debug)] pub struct QuoteOffsets { pub quotes: (TextRange, TextRange), pub contents: TextRange, @@ -167,6 +168,11 @@ pub trait IsString: AstToken { fn text_range_between_quotes(&self) -> Option { self.quote_offsets().map(|it| it.contents) } + fn text_without_quotes(&self) -> &str { + let text = self.text(); + let Some(offsets) = self.text_range_between_quotes() else { return text }; + &text[offsets - self.syntax().text_range().start()] + } fn open_quote_text_range(&self) -> Option { self.quote_offsets().map(|it| it.quotes.0) } diff --git a/crates/syntax/src/token_text.rs b/crates/syntax/src/token_text.rs index 09c080c0c230..e69deb49ce14 100644 --- a/crates/syntax/src/token_text.rs +++ b/crates/syntax/src/token_text.rs @@ -13,7 +13,7 @@ pub(crate) enum Repr<'a> { } impl<'a> TokenText<'a> { - pub(crate) fn borrowed(text: &'a str) -> Self { + pub fn borrowed(text: &'a str) -> Self { TokenText(Repr::Borrowed(text)) } From 918ed508820ab780720b9ae6c6c0860ff0afa050 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 5 Dec 2023 16:30:57 +0100 Subject: [PATCH 11/62] Resolve implicit format args in syntax highlighting --- crates/hir/src/semantics.rs | 45 ++++++++++++++----- crates/hir/src/source_analyzer.rs | 23 ++++++++++ crates/ide/src/syntax_highlighting.rs | 2 +- crates/ide/src/syntax_highlighting/format.rs | 20 ++++++++- .../ide/src/syntax_highlighting/highlight.rs | 2 +- .../test_data/highlight_macros.html | 10 +---- .../test_data/highlight_strings.html | 43 +++++++----------- crates/ide/src/syntax_highlighting/tests.rs | 36 +++++---------- crates/test-utils/src/minicore.rs | 29 ++++++++++++ 9 files changed, 136 insertions(+), 74 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index b0e0f969d98f..d2fd63428b43 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -403,17 +403,29 @@ impl<'db> SemanticsImpl<'db> { ) } - pub fn resolve_offset_in_format_args( + pub fn as_format_args_parts( &self, - string: ast::String, - offset: TextSize, - ) -> Option<(TextRange, Option)> { - debug_assert!(offset <= string.syntax().text_range().len()); - let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; - let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; - let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; - let format_args = self.wrap_node_infile(format_args); - source_analyzer.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + string: &ast::String, + ) -> Option)>> { + if let Some(quote) = string.open_quote_text_range() { + return self + .descend_into_macros(DescendPreference::SameText, string.syntax().clone()) + .into_iter() + .find_map(|token| { + let string = ast::String::cast(token)?; + let literal = + string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; + let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; + let source_analyzer = self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + let res = source_analyzer + .as_format_args_parts(self.db, format_args.as_ref())? + .map(|(range, res)| (range + quote.end(), res)) + .collect(); + Some(res) + }); + } + None } pub fn check_for_format_args_template( @@ -438,6 +450,19 @@ impl<'db> SemanticsImpl<'db> { None } + fn resolve_offset_in_format_args( + &self, + string: ast::String, + offset: TextSize, + ) -> Option<(TextRange, Option)> { + debug_assert!(offset <= string.syntax().text_range().len()); + let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; + let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; + let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + source_analyzer.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + } + /// Maps a node down by mapping its first and last token down. pub fn descend_node_into_attributes(&self, node: N) -> SmallVec<[N; 1]> { // This might not be the correct way to do this, but it works for now diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 9fdee209cfbc..8afa7e065947 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -843,6 +843,29 @@ impl SourceAnalyzer { }) } + pub(crate) fn as_format_args_parts<'a>( + &'a self, + db: &'a dyn HirDatabase, + format_args: InFile<&ast::FormatArgsExpr>, + ) -> Option)> + 'a> { + Some(self.body_source_map()?.implicit_format_args(format_args)?.iter().map( + move |(range, name)| { + ( + *range, + resolve_hir_value_path( + db, + &self.resolver, + self.resolver.body_owner(), + &Path::from_known_path_with_no_generic(ModPath::from_segments( + PathKind::Plain, + Some(name.clone()), + )), + ), + ) + }, + )) + } + fn resolve_impl_method_or_trait_def( &self, db: &dyn HirDatabase, diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index ac14afa13208..45582f54b46c 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -440,7 +440,7 @@ fn traverse( { continue; } - highlight_format_string(hl, &string, &expanded_string, range); + highlight_format_string(hl, sema, krate, &string, &expanded_string, range); if !string.is_raw() { highlight_escape_string(hl, &string, range.start()); diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs index 2ef1315945a0..518e71454798 100644 --- a/crates/ide/src/syntax_highlighting/format.rs +++ b/crates/ide/src/syntax_highlighting/format.rs @@ -1,14 +1,20 @@ //! Syntax highlighting for format macro strings. use ide_db::{ + defs::Definition, syntax_helpers::format_string::{is_format_string, lex_format_specifiers, FormatSpecifier}, SymbolKind, }; use syntax::{ast, TextRange}; -use crate::{syntax_highlighting::highlights::Highlights, HlRange, HlTag}; +use crate::{ + syntax_highlighting::{highlight::highlight_def, highlights::Highlights}, + HlRange, HlTag, +}; pub(super) fn highlight_format_string( stack: &mut Highlights, + sema: &hir::Semantics<'_, ide_db::RootDatabase>, + krate: hir::Crate, string: &ast::String, expanded_string: &ast::String, range: TextRange, @@ -27,6 +33,18 @@ pub(super) fn highlight_format_string( }); } }); + + if let Some(parts) = sema.as_format_args_parts(string) { + parts.into_iter().for_each(|(range, res)| { + if let Some(res) = res { + stack.add(HlRange { + range, + highlight: highlight_def(sema, krate, Definition::from(res)), + binding_hash: None, + }) + } + }) + } } fn highlight_format_specifier(kind: FormatSpecifier) -> Option { diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 1bffab29cf93..d510c11c3d5a 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -348,7 +348,7 @@ fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 { hash((name, shadow_count)) } -fn highlight_def( +pub(super) fn highlight_def( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, def: Definition, diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 06b66b302ae0..b8d38a60fc01 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -90,17 +90,11 @@ } } -#[rustc_builtin_macro] -macro_rules! concat {} -#[rustc_builtin_macro] -macro_rules! include {} -#[rustc_builtin_macro] -macro_rules! format_args {} -include!(concat!("foo/", "foo.rs")); +include!(concat!("foo/", "foo.rs")); fn main() { - format_args!("Hello, {}!", 92); + format_args!("Hello, {}!", 92); dont_color_me_braces!(); noop!(noop!(1)); } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index aa79cd9b829f..b40295684df9 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -48,42 +48,29 @@ $crate::io::_print(format_args_nl!($($arg)*)); }) } -#[rustc_builtin_macro] -#[macro_export] -macro_rules! format_args_nl {} mod panic { pub macro panic_2015 { () => ( - $crate::panicking::panic("explicit panic") + panic("explicit panic") ), ($msg:literal $(,)?) => ( - $crate::panicking::panic($msg) + panic($msg) ), // Use `panic_str` instead of `panic_display::<&str>` for non_fmt_panic lint. ($msg:expr $(,)?) => ( - $crate::panicking::panic_str($msg) + panic_str($msg) ), // Special-case the single-argument case for const_panic. ("{}", $arg:expr $(,)?) => ( - $crate::panicking::panic_display(&$arg) + panic_display(&$arg) ), ($fmt:expr, $($arg:tt)+) => ( - $crate::panicking::panic_fmt(const_format_args!($fmt, $($arg)+)) + panic_fmt(const_format_args!($fmt, $($arg)+)) ), } } -#[rustc_builtin_macro(std_panic)] -#[macro_export] -macro_rules! panic {} -#[rustc_builtin_macro] -macro_rules! assert {} -#[rustc_builtin_macro] -macro_rules! asm {} -#[rustc_builtin_macro] -macro_rules! concat {} - macro_rules! toho { () => ($crate::panic!("not yet implemented")); ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+))); @@ -165,20 +152,22 @@ println!("{ничоси}", ничоси = 92); println!("{:x?} {} ", thingy, n2); - panic!("{}", 0); - panic!("more {}", 1); - assert!(true, "{}", 1); - assert!(true, "{} asdasd", 1); + panic!("{}", 0); + panic!("more {}", 1); + assert!(true, "{}", 1); + assert!(true, "{} asdasd", 1); toho!("{}fmt", 0); let i: u64 = 3; let o: u64; - asm!( - "mov {0}, {1}", - "add {0}, 5", + asm!( + "mov {0}, {1}", + "add {0}, 5", out(reg) o, in(reg) i, ); - format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + const CONSTANT: () = (): + let mut m = (); + format_args!(concat!("{}"), "{}"); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 542d8992531f..935b6b2cb9b1 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -48,6 +48,7 @@ fn macros() { check_highlighting( r#" //- proc_macros: mirror +//- minicore: fmt, include, concat //- /lib.rs crate:lib proc_macros::mirror! { { @@ -96,12 +97,6 @@ macro without_args { } } -#[rustc_builtin_macro] -macro_rules! concat {} -#[rustc_builtin_macro] -macro_rules! include {} -#[rustc_builtin_macro] -macro_rules! format_args {} include!(concat!("foo/", "foo.rs")); @@ -401,48 +396,35 @@ fn test_string_highlighting() { // thus, we have to copy the macro definition from `std` check_highlighting( r#" -//- minicore: fmt +//- minicore: fmt, assert, asm, concat, panic macro_rules! println { ($($arg:tt)*) => ({ $crate::io::_print(format_args_nl!($($arg)*)); }) } -#[rustc_builtin_macro] -#[macro_export] -macro_rules! format_args_nl {} mod panic { pub macro panic_2015 { () => ( - $crate::panicking::panic("explicit panic") + panic("explicit panic") ), ($msg:literal $(,)?) => ( - $crate::panicking::panic($msg) + panic($msg) ), // Use `panic_str` instead of `panic_display::<&str>` for non_fmt_panic lint. ($msg:expr $(,)?) => ( - $crate::panicking::panic_str($msg) + panic_str($msg) ), // Special-case the single-argument case for const_panic. ("{}", $arg:expr $(,)?) => ( - $crate::panicking::panic_display(&$arg) + panic_display(&$arg) ), ($fmt:expr, $($arg:tt)+) => ( - $crate::panicking::panic_fmt(const_format_args!($fmt, $($arg)+)) + panic_fmt(const_format_args!($fmt, $($arg)+)) ), } } -#[rustc_builtin_macro(std_panic)] -#[macro_export] -macro_rules! panic {} -#[rustc_builtin_macro] -macro_rules! assert {} -#[rustc_builtin_macro] -macro_rules! asm {} -#[rustc_builtin_macro] -macro_rules! concat {} - macro_rules! toho { () => ($crate::panic!("not yet implemented")); ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+))); @@ -538,8 +520,10 @@ fn main() { in(reg) i, ); + const CONSTANT: () = (): + let mut m = (); format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); }"#, expect_file!["./test_data/highlight_strings.html"], false, diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index d39d62f3620d..f766747d707c 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -9,6 +9,8 @@ //! //! Available flags: //! add: +//! asm: +//! assert: //! as_ref: sized //! bool_impl: option, fn //! builtin_impls: @@ -1366,6 +1368,26 @@ mod macros { } // endregion:panic + // region:asm + #[macro_export] + #[rustc_builtin_macro] + macro_rules! asm { + ($($arg:tt)*) => { + /* compiler built-in */ + }; + } + // endregion:asm + + // region:assert + #[macro_export] + #[rustc_builtin_macro] + macro_rules! assert { + ($($arg:tt)*) => { + /* compiler built-in */ + }; + } + // endregion:assert + // region:fmt #[macro_export] #[rustc_builtin_macro] @@ -1381,6 +1403,13 @@ mod macros { ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; } + #[macro_export] + #[rustc_builtin_macro] + macro_rules! format_args_nl { + ($fmt:expr) => {{ /* compiler built-in */ }}; + ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; + } + #[macro_export] macro_rules! print { ($($arg:tt)*) => {{ From 42eaebeee66a459e4554c2f64fd7036b5adbb522 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 5 Dec 2023 16:32:49 +0100 Subject: [PATCH 12/62] Add test for implicit format args support through nested macro call --- crates/ide/src/hover/tests.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 53585624b68a..8c9d58671e69 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -6673,3 +6673,28 @@ format_args!(r"{$0aaaaa}"); "#]], ); } + +#[test] +fn format_args_implicit_nested() { + check( + r#" +//- minicore: fmt +macro_rules! foo { + ($($tt:tt)*) => { + format_args!($($tt)*) + } +} +fn test() { +let aaaaa = "foo"; +foo!(r"{$0aaaaa}"); +} +"#, + expect![[r#" + *aaaaa* + + ```rust + let aaaaa: &str // size = 16 (0x10), align = 8, niches = 1 + ``` + "#]], + ); +} From 7132bf53064a772687117cf60490dd994512e3b1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 5 Dec 2023 17:04:59 +0100 Subject: [PATCH 13/62] Ignore strings in token trees in syntax highlighting --- crates/hir/src/semantics.rs | 2 +- crates/ide/src/syntax_highlighting.rs | 26 ++++++++++++++----- .../test_data/highlight_strings.html | 5 ++++ crates/ide/src/syntax_highlighting/tests.rs | 5 ++++ 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index d2fd63428b43..ac70c27785c5 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -606,8 +606,8 @@ impl<'db> SemanticsImpl<'db> { } Dp::None => true, }; + res = value; if is_a_match { - res = value; ControlFlow::Break(()) } else { ControlFlow::Continue(()) diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 45582f54b46c..366a3c969f93 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -393,13 +393,25 @@ fn traverse( // Attempt to descend tokens into macro-calls. let res = match element { NodeOrToken::Token(token) if token.kind() != COMMENT => { - let token = sema.descend_into_macros_single( - match attr_or_derive_item { - Some(AttrOrDerive::Attr(_)) => DescendPreference::SameKind, - Some(AttrOrDerive::Derive(_)) | None => DescendPreference::None, - }, - token, - ); + let token = if token.kind() == STRING { + // for strings, try to prefer a string that has not been lost in a token + // tree + // FIXME: This should be done for everything, but check perf first + sema.descend_into_macros(DescendPreference::SameKind, token) + .into_iter() + .max_by_key(|it| { + it.parent().map_or(false, |it| it.kind() != TOKEN_TREE) + }) + .unwrap() + } else { + sema.descend_into_macros_single( + match attr_or_derive_item { + Some(AttrOrDerive::Attr(_)) => DescendPreference::SameKind, + Some(AttrOrDerive::Derive(_)) | None => DescendPreference::None, + }, + token, + ) + }; match token.parent().and_then(ast::NameLike::cast) { // Remap the token into the wrapping single token nodes Some(parent) => match (token.kind(), parent.syntax().kind()) { diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index b40295684df9..75cb6223e0e6 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -76,6 +76,10 @@ ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+))); } +macro_rules! reuse_twice { + ($literal:literal) => {{stringify!($literal); format_args!($literal)}}; +} + fn main() { let a = '\n'; let a = '\t'; @@ -170,4 +174,5 @@ let mut m = (); format_args!(concat!("{}"), "{}"); format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + reuse_twice!("{backslash}"); } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 935b6b2cb9b1..fcfd3c92571b 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -430,6 +430,10 @@ macro_rules! toho { ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+))); } +macro_rules! reuse_twice { + ($literal:literal) => {{stringify!($literal); format_args!($literal)}}; +} + fn main() { let a = '\n'; let a = '\t'; @@ -524,6 +528,7 @@ fn main() { let mut m = (); format_args!(concat!("{}"), "{}"); format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + reuse_twice!("{backslash}"); }"#, expect_file!["./test_data/highlight_strings.html"], false, From 28e26b57225e57f4824ff58cf5dd71394cb97243 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Dec 2023 11:53:28 +0100 Subject: [PATCH 14/62] Allow navigation targets to be duplicated when the focus range lies in the macro definition site --- Cargo.lock | 1 + crates/base-db/src/fixture.rs | 7 +- crates/base-db/src/input.rs | 20 +- crates/base-db/src/span.rs | 20 +- crates/hir-def/src/attr/tests.rs | 2 +- crates/hir-def/src/generics.rs | 2 +- crates/hir-expand/src/files.rs | 34 + crates/hir-expand/src/fixup.rs | 12 +- crates/hir-expand/src/hygiene.rs | 6 + crates/hir-expand/src/lib.rs | 15 + crates/hir-expand/src/quote.rs | 4 +- crates/hir/src/attrs.rs | 2 +- crates/hir/src/lib.rs | 1 + crates/hir/src/symbols.rs | 10 +- .../ide-db/src/test_data/test_doc_alias.txt | 70 +- .../test_symbol_index_collection.txt | 290 +++++---- crates/ide/Cargo.toml | 1 + crates/ide/src/call_hierarchy.rs | 10 +- crates/ide/src/doc_links/tests.rs | 8 +- crates/ide/src/goto_declaration.rs | 1 + crates/ide/src/goto_definition.rs | 18 +- crates/ide/src/goto_implementation.rs | 8 +- crates/ide/src/goto_type_definition.rs | 8 +- crates/ide/src/highlight_related.rs | 36 +- crates/ide/src/hover.rs | 21 +- crates/ide/src/inlay_hints.rs | 1 + crates/ide/src/lib.rs | 5 +- crates/ide/src/navigation_target.rs | 616 +++++++++++------- crates/ide/src/parent_module.rs | 4 +- crates/ide/src/references.rs | 51 +- crates/ide/src/runnables.rs | 12 +- crates/ide/src/static_index.rs | 8 +- crates/proc-macro-api/src/msg.rs | 6 +- crates/project-model/src/tests.rs | 4 +- crates/rust-analyzer/src/handlers/request.rs | 2 +- crates/rust-analyzer/src/lsp/to_proto.rs | 2 +- crates/syntax/src/ptr.rs | 7 +- crates/vfs/src/lib.rs | 14 +- 38 files changed, 851 insertions(+), 488 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ba5c15a1d3d..876ba2546a38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -608,6 +608,7 @@ dependencies = [ name = "ide" version = "0.0.0" dependencies = [ + "arrayvec", "cfg", "cov-mark", "crossbeam-channel", diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index cfba01a03298..bfdd21555f0a 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -135,7 +135,7 @@ impl ChangeFixture { let mut file_set = FileSet::default(); let mut current_source_root_kind = SourceRootKind::Local; - let mut file_id = FileId(0); + let mut file_id = FileId::from_raw(0); let mut roots = Vec::new(); let mut file_position = None; @@ -210,7 +210,7 @@ impl ChangeFixture { let path = VfsPath::new_virtual_path(meta.path); file_set.insert(file_id, path); files.push(file_id); - file_id.0 += 1; + file_id = FileId::from_raw(file_id.index() + 1); } if crates.is_empty() { @@ -255,7 +255,7 @@ impl ChangeFixture { if let Some(mini_core) = mini_core { let core_file = file_id; - file_id.0 += 1; + file_id = FileId::from_raw(file_id.index() + 1); let mut fs = FileSet::default(); fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string())); @@ -296,7 +296,6 @@ impl ChangeFixture { let mut proc_macros = ProcMacros::default(); if !proc_macro_names.is_empty() { let proc_lib_file = file_id; - file_id.0 += 1; proc_macro_defs.extend(default_test_proc_macros()); let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs); diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 12b449932dd7..c2472363aacd 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -880,7 +880,7 @@ mod tests { fn detect_cyclic_dependency_indirect() { let mut graph = CrateGraph::default(); let crate1 = graph.add_crate_root( - FileId(1u32), + FileId::from_raw(1u32), Edition2018, None, None, @@ -893,7 +893,7 @@ mod tests { None, ); let crate2 = graph.add_crate_root( - FileId(2u32), + FileId::from_raw(2u32), Edition2018, None, None, @@ -906,7 +906,7 @@ mod tests { None, ); let crate3 = graph.add_crate_root( - FileId(3u32), + FileId::from_raw(3u32), Edition2018, None, None, @@ -942,7 +942,7 @@ mod tests { fn detect_cyclic_dependency_direct() { let mut graph = CrateGraph::default(); let crate1 = graph.add_crate_root( - FileId(1u32), + FileId::from_raw(1u32), Edition2018, None, None, @@ -955,7 +955,7 @@ mod tests { None, ); let crate2 = graph.add_crate_root( - FileId(2u32), + FileId::from_raw(2u32), Edition2018, None, None, @@ -985,7 +985,7 @@ mod tests { fn it_works() { let mut graph = CrateGraph::default(); let crate1 = graph.add_crate_root( - FileId(1u32), + FileId::from_raw(1u32), Edition2018, None, None, @@ -998,7 +998,7 @@ mod tests { None, ); let crate2 = graph.add_crate_root( - FileId(2u32), + FileId::from_raw(2u32), Edition2018, None, None, @@ -1011,7 +1011,7 @@ mod tests { None, ); let crate3 = graph.add_crate_root( - FileId(3u32), + FileId::from_raw(3u32), Edition2018, None, None, @@ -1041,7 +1041,7 @@ mod tests { fn dashes_are_normalized() { let mut graph = CrateGraph::default(); let crate1 = graph.add_crate_root( - FileId(1u32), + FileId::from_raw(1u32), Edition2018, None, None, @@ -1054,7 +1054,7 @@ mod tests { None, ); let crate2 = graph.add_crate_root( - FileId(2u32), + FileId::from_raw(2u32), Edition2018, None, None, diff --git a/crates/base-db/src/span.rs b/crates/base-db/src/span.rs index 6723bf97fea5..3464f4cb6d1c 100644 --- a/crates/base-db/src/span.rs +++ b/crates/base-db/src/span.rs @@ -70,7 +70,7 @@ impl fmt::Debug for SpanAnchor { } impl tt::SpanAnchor for SpanAnchor { - const DUMMY: Self = SpanAnchor { file_id: FileId(0), ast_id: ROOT_ERASED_FILE_AST_ID }; + const DUMMY: Self = SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID }; } /// Input to the analyzer is a set of files, where each file is identified by @@ -99,12 +99,6 @@ impl From for u32 { } } -impl From for HirFileId { - fn from(value: u32) -> Self { - HirFileId(value) - } -} - impl From for HirFileId { fn from(value: MacroCallId) -> Self { value.as_file() @@ -147,7 +141,7 @@ pub enum HirFileIdRepr { impl fmt::Debug for HirFileIdRepr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::FileId(arg0) => f.debug_tuple("FileId").field(&arg0.0).finish(), + Self::FileId(arg0) => f.debug_tuple("FileId").field(&arg0.index()).finish(), Self::MacroFile(arg0) => { f.debug_tuple("MacroFile").field(&arg0.macro_call_id.0).finish() } @@ -156,9 +150,9 @@ impl fmt::Debug for HirFileIdRepr { } impl From for HirFileId { - fn from(FileId(id): FileId) -> Self { - assert!(id < Self::MAX_FILE_ID); - HirFileId(id) + fn from(id: FileId) -> Self { + assert!(id.index() < Self::MAX_FILE_ID); + HirFileId(id.index()) } } @@ -192,7 +186,7 @@ impl HirFileId { #[inline] pub fn file_id(self) -> Option { match self.0 & Self::MACRO_FILE_TAG_MASK { - 0 => Some(FileId(self.0)), + 0 => Some(FileId::from_raw(self.0)), _ => None, } } @@ -200,7 +194,7 @@ impl HirFileId { #[inline] pub fn repr(self) -> HirFileIdRepr { match self.0 & Self::MACRO_FILE_TAG_MASK { - 0 => HirFileIdRepr::FileId(FileId(self.0)), + 0 => HirFileIdRepr::FileId(FileId::from_raw(self.0)), _ => HirFileIdRepr::MacroFile(MacroFileId { macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)), }), diff --git a/crates/hir-def/src/attr/tests.rs b/crates/hir-def/src/attr/tests.rs index 796f165c7c87..0f98a4ec93c6 100644 --- a/crates/hir-def/src/attr/tests.rs +++ b/crates/hir-def/src/attr/tests.rs @@ -13,7 +13,7 @@ fn assert_parse_result(input: &str, expected: DocExpr) { let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree( tt.syntax(), - SpanMapRef::RealSpanMap(&RealSpanMap::absolute(FileId(0))), + SpanMapRef::RealSpanMap(&RealSpanMap::absolute(FileId::from_raw(0))), ); let cfg = DocExpr::parse(&tt); assert_eq!(cfg, expected); diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index 1c1c481a8eed..0d95d916ff99 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -524,7 +524,7 @@ fn file_id_and_params_of( (src.file_id, src.value.generic_param_list()) } // We won't be using this ID anyway - GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => (FileId(!0).into(), None), + GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => (FileId::BOGUS.into(), None), } } diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs index ed8639d7a1d9..174e59056981 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs @@ -307,6 +307,40 @@ impl InFile { }; range } + + pub fn original_node_file_range( + self, + db: &dyn db::ExpandDatabase, + ) -> (FileRange, SyntaxContextId) { + match self.file_id.repr() { + HirFileIdRepr::FileId(file_id) => { + (FileRange { file_id, range: self.value }, SyntaxContextId::ROOT) + } + HirFileIdRepr::MacroFile(mac_file) => { + match ExpansionInfo::new(db, mac_file).map_node_range_up(db, self.value) { + Some(it) => it, + None => { + let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); + (loc.kind.original_call_range(db), SyntaxContextId::ROOT) + } + } + } + } + } + + pub fn original_node_file_range_opt( + self, + db: &dyn db::ExpandDatabase, + ) -> Option<(FileRange, SyntaxContextId)> { + match self.file_id.repr() { + HirFileIdRepr::FileId(file_id) => { + Some((FileRange { file_id, range: self.value }, SyntaxContextId::ROOT)) + } + HirFileIdRepr::MacroFile(mac_file) => { + ExpansionInfo::new(db, mac_file).map_node_range_up(db, self.value) + } + } + } } impl InFile { diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 77ddf7a48c30..11775c531d4c 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -54,8 +54,10 @@ pub(crate) fn fixup_syntax(span_map: SpanMapRef<'_>, node: &SyntaxNode) -> Synta let dummy_range = TextRange::empty(TextSize::new(0)); // we use a file id of `FileId(!0)` to signal a fake node, and the text range's start offset as // the index into the replacement vec but only if the end points to !0 - let dummy_anchor = - SpanAnchor { file_id: FileId(!0), ast_id: ErasedFileAstId::from_raw(RawIdx::from(!0)) }; + let dummy_anchor = SpanAnchor { + file_id: FileId::from_raw(!0), + ast_id: ErasedFileAstId::from_raw(RawIdx::from(!0)), + }; let fake_span = |range| SpanData { range: dummy_range, anchor: dummy_anchor, @@ -308,7 +310,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { .filter(|tt| match tt { tt::TokenTree::Leaf(leaf) => { let span = leaf.span(); - span.anchor.file_id != FileId(!0) || span.range.end() == TextSize::new(!0) + span.anchor.file_id != FileId::from_raw(!0) || span.range.end() == TextSize::new(!0) } tt::TokenTree::Subtree(_) => true, }) @@ -318,7 +320,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { SmallVec::from_const([tt.into()]) } tt::TokenTree::Leaf(leaf) => { - if leaf.span().anchor.file_id == FileId(!0) { + if leaf.span().anchor.file_id == FileId::from_raw(!0) { let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone(); if original.delimiter.kind == tt::DelimiterKind::Invisible { original.token_trees.into() @@ -373,7 +375,7 @@ mod tests { #[track_caller] fn check(ra_fixture: &str, mut expect: Expect) { let parsed = syntax::SourceFile::parse(ra_fixture); - let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId(0)))); + let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0)))); let fixups = super::fixup_syntax(span_map.as_ref(), &parsed.syntax_node()); let mut tt = mbe::syntax_node_to_token_tree_modified( &parsed.syntax_node(), diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs index a809e92d6222..7b03709aced0 100644 --- a/crates/hir-expand/src/hygiene.rs +++ b/crates/hir-expand/src/hygiene.rs @@ -197,6 +197,7 @@ pub trait SyntaxContextExt { fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self; fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self; fn parent_ctxt(self, db: &dyn ExpandDatabase) -> Self; + fn remove_mark(&mut self, db: &dyn ExpandDatabase) -> (Option, Transparency); fn outer_mark(self, db: &dyn ExpandDatabase) -> (Option, Transparency); fn marks(self, db: &dyn ExpandDatabase) -> Vec<(Option, Transparency)>; } @@ -223,6 +224,11 @@ impl SyntaxContextExt for SyntaxContextId { let data = db.lookup_intern_syntax_context(self); (data.outer_expn, data.outer_transparency) } + fn remove_mark(&mut self, db: &dyn ExpandDatabase) -> (Option, Transparency) { + let data = db.lookup_intern_syntax_context(*self); + *self = data.parent; + (data.outer_expn, data.outer_transparency) + } fn marks(self, db: &dyn ExpandDatabase) -> Vec<(Option, Transparency)> { let mut marks = marks_rev(self, db).collect::>(); marks.reverse(); diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 71c98b2770aa..167ba0eb5da1 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -380,6 +380,21 @@ impl MacroDefId { db.intern_macro_call(MacroCallLoc { def: self, krate, eager: None, kind, call_site }) } + pub fn definition_range(&self, db: &dyn db::ExpandDatabase) -> InFile { + match self.kind { + MacroDefKind::Declarative(id) + | MacroDefKind::BuiltIn(_, id) + | MacroDefKind::BuiltInAttr(_, id) + | MacroDefKind::BuiltInDerive(_, id) + | MacroDefKind::BuiltInEager(_, id) => { + id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range()) + } + MacroDefKind::ProcMacro(_, _, id) => { + id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range()) + } + } + } + pub fn ast_id(&self) -> Either, AstId> { match self.kind { MacroDefKind::ProcMacro(.., id) => return Either::Right(id), diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs index 0950f5d28755..acbde26c8ddf 100644 --- a/crates/hir-expand/src/quote.rs +++ b/crates/hir-expand/src/quote.rs @@ -261,8 +261,8 @@ mod tests { assert_eq!(quoted.to_string(), "hello"); let t = format!("{quoted:?}"); expect![[r#" - SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(4294967295), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(4294967295), 0), ctx: SyntaxContextId(0) } - IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(4294967295), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t); + SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } + IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t); } #[test] diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 0ac1db9311b1..185853353181 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -241,7 +241,7 @@ fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option { ModPath::from_src( db.upcast(), ast_path, - SpanMapRef::RealSpanMap(&RealSpanMap::absolute(FileId(0))), + SpanMapRef::RealSpanMap(&RealSpanMap::absolute(FileId::BOGUS)), ) }; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 53e60c5862ad..7e210740eac0 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -125,6 +125,7 @@ pub use { }, hir_expand::{ attrs::{Attr, AttrId}, + hygiene::{marks_rev, SyntaxContextExt}, name::{known, Name}, tt, ExpandResult, HirFileId, HirFileIdExt, InFile, InMacroFile, InRealFile, MacroFileId, }, diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index a392070fd88e..2b03d575cb27 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -9,7 +9,7 @@ use hir_def::{ }; use hir_expand::{HirFileId, InFile}; use hir_ty::db::HirDatabase; -use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr}; +use syntax::{ast::HasName, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr}; use crate::{Module, ModuleDef, Semantics}; @@ -32,7 +32,7 @@ pub struct DeclarationLocation { /// This points to the whole syntax node of the declaration. pub ptr: SyntaxNodePtr, /// This points to the [`syntax::ast::Name`] identifier of the declaration. - pub name_ptr: SyntaxNodePtr, + pub name_ptr: AstPtr, } impl DeclarationLocation { @@ -185,7 +185,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr: SyntaxNodePtr::new(name.syntax()), + name_ptr: AstPtr::new(&name), }; self.symbols.push(FileSymbol { @@ -289,7 +289,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), + name_ptr: AstPtr::new(&name_node), }; if let Some(attrs) = def.attrs(self.db) { @@ -322,7 +322,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: declaration.file_id, ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), + name_ptr: AstPtr::new(&name_node), }; let def = ModuleDef::Module(module_id.into()); diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt index 72a6eb5eabd1..4a72881fe5e4 100644 --- a/crates/ide-db/src/test_data/test_doc_alias.txt +++ b/crates/ide-db/src/test_data/test_doc_alias.txt @@ -27,10 +27,12 @@ kind: STRUCT, range: 83..119, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 109..118, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + ), }, container_name: None, is_alias: false, @@ -54,10 +56,12 @@ kind: STRUCT, range: 0..81, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), }, container_name: None, is_alias: false, @@ -81,10 +85,12 @@ kind: STRUCT, range: 0..81, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), }, container_name: None, is_alias: true, @@ -108,10 +114,12 @@ kind: STRUCT, range: 0..81, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), }, container_name: None, is_alias: true, @@ -135,10 +143,12 @@ kind: STRUCT, range: 0..81, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), }, container_name: None, is_alias: true, @@ -162,10 +172,12 @@ kind: STRUCT, range: 83..119, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 109..118, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + ), }, container_name: None, is_alias: true, @@ -189,10 +201,12 @@ kind: STRUCT, range: 0..81, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), }, container_name: None, is_alias: true, diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 375ac5598152..da1f3167d7d4 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -25,10 +25,12 @@ kind: TYPE_ALIAS, range: 397..417, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 402..407, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 402..407, + }, + ), }, container_name: None, is_alias: false, @@ -50,10 +52,12 @@ kind: CONST, range: 340..361, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 346..351, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 346..351, + }, + ), }, container_name: None, is_alias: false, @@ -75,10 +79,12 @@ kind: CONST, range: 520..592, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 526..542, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 526..542, + }, + ), }, container_name: None, is_alias: false, @@ -102,10 +108,12 @@ kind: ENUM, range: 185..207, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 190..194, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 190..194, + }, + ), }, container_name: None, is_alias: false, @@ -129,10 +137,12 @@ kind: USE_TREE, range: 654..676, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 663..676, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 663..676, + }, + ), }, container_name: None, is_alias: false, @@ -156,10 +166,12 @@ kind: MACRO_DEF, range: 153..168, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 159..164, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 159..164, + }, + ), }, container_name: None, is_alias: false, @@ -181,10 +193,12 @@ kind: STATIC, range: 362..396, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 369..375, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 369..375, + }, + ), }, container_name: None, is_alias: false, @@ -208,10 +222,12 @@ kind: STRUCT, range: 170..184, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 177..183, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 177..183, + }, + ), }, container_name: None, is_alias: false, @@ -235,10 +251,12 @@ kind: STRUCT, range: 0..22, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 6..21, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 6..21, + }, + ), }, container_name: None, is_alias: false, @@ -262,10 +280,12 @@ kind: STRUCT, range: 318..336, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 325..335, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 325..335, + }, + ), }, container_name: Some( "main", @@ -291,10 +311,12 @@ kind: STRUCT, range: 555..581, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 562..580, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 562..580, + }, + ), }, container_name: Some( "CONST_WITH_INNER", @@ -320,10 +342,12 @@ kind: STRUCT, range: 479..507, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 486..506, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 486..506, + }, + ), }, container_name: None, is_alias: false, @@ -345,10 +369,12 @@ kind: TRAIT, range: 261..300, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 267..272, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 267..272, + }, + ), }, container_name: None, is_alias: false, @@ -372,10 +398,12 @@ kind: USE_TREE, range: 682..696, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 691..696, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 691..696, + }, + ), }, container_name: None, is_alias: false, @@ -399,10 +427,12 @@ kind: UNION, range: 208..222, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 214..219, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 214..219, + }, + ), }, container_name: None, is_alias: false, @@ -426,10 +456,12 @@ kind: MODULE, range: 419..457, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 423..428, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 423..428, + }, + ), }, container_name: None, is_alias: false, @@ -453,10 +485,12 @@ kind: MODULE, range: 594..604, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 598..603, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 598..603, + }, + ), }, container_name: None, is_alias: false, @@ -480,10 +514,12 @@ kind: MACRO_RULES, range: 51..131, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 64..77, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 64..77, + }, + ), }, container_name: None, is_alias: false, @@ -505,10 +541,12 @@ kind: FN, range: 242..257, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 245..252, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 245..252, + }, + ), }, container_name: None, is_alias: false, @@ -532,10 +570,12 @@ kind: MACRO_RULES, range: 1..48, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 14..31, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 14..31, + }, + ), }, container_name: None, is_alias: false, @@ -557,10 +597,12 @@ kind: FN, range: 302..338, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 305..309, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 305..309, + }, + ), }, container_name: None, is_alias: false, @@ -584,10 +626,12 @@ kind: USE_TREE, range: 611..648, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 628..648, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 628..648, + }, + ), }, container_name: None, is_alias: false, @@ -609,10 +653,12 @@ kind: FN, range: 279..298, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 282..290, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 282..290, + }, + ), }, container_name: Some( "Trait", @@ -649,10 +695,12 @@ kind: STRUCT, range: 435..455, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 442..454, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 442..454, + }, + ), }, container_name: None, is_alias: false, @@ -687,10 +735,12 @@ kind: USE_TREE, range: 111..143, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 127..143, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 127..143, + }, + ), }, container_name: None, is_alias: false, @@ -714,10 +764,12 @@ kind: STRUCT, range: 0..20, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 7..19, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 7..19, + }, + ), }, container_name: None, is_alias: false, @@ -741,10 +793,12 @@ kind: USE_TREE, range: 25..59, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 41..59, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 41..59, + }, + ), }, container_name: None, is_alias: false, @@ -768,10 +822,12 @@ kind: USE_TREE, range: 65..105, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 95..105, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 95..105, + }, + ), }, container_name: None, is_alias: false, @@ -795,10 +851,12 @@ kind: USE_TREE, range: 65..105, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 95..105, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 95..105, + }, + ), }, container_name: None, is_alias: false, diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index d5c3439f9563..0943574ec1b5 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml @@ -14,6 +14,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" crossbeam-channel = "0.5.5" +arrayvec = "0.7.4" either.workspace = true itertools.workspace = true tracing.workspace = true diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index 5cc64e60ed85..458b852e2a1e 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs @@ -1,5 +1,7 @@ //! Entry point for call-hierarchy +use std::iter; + use hir::{DescendPreference, Semantics}; use ide_db::{ defs::{Definition, NameClass, NameRefClass}, @@ -66,7 +68,10 @@ pub(crate) fn incoming_calls( def.try_to_nav(sema.db) }); if let Some(nav) = nav { - calls.add(nav, sema.original_range(name.syntax()).range); + calls.add(nav.call_site, sema.original_range(name.syntax()).range); + if let Some(other) = nav.def_site { + calls.add(other, sema.original_range(name.syntax()).range); + } } } } @@ -117,8 +122,9 @@ pub(crate) fn outgoing_calls( function.try_to_nav(db).zip(Some(range)) } }?; - Some((nav_target, range)) + Some(nav_target.into_iter().zip(iter::repeat(range))) }) + .flatten() .for_each(|(nav, range)| calls.add(nav, range)); Some(calls.into_items()) diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs index 9ae70ae66f50..f388aea4c379 100644 --- a/crates/ide/src/doc_links/tests.rs +++ b/crates/ide/src/doc_links/tests.rs @@ -1,4 +1,4 @@ -use std::ffi::OsStr; +use std::{ffi::OsStr, iter}; use expect_test::{expect, Expect}; use hir::Semantics; @@ -63,10 +63,12 @@ fn check_doc_links(ra_fixture: &str) { let defs = extract_definitions_from_docs(&docs); let actual: Vec<_> = defs .into_iter() - .map(|(_, link, ns)| { + .flat_map(|(_, link, ns)| { let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns) .unwrap_or_else(|| panic!("Failed to resolve {link}")); - let nav_target = def.try_to_nav(sema.db).unwrap(); + def.try_to_nav(sema.db).unwrap().into_iter().zip(iter::repeat(link)) + }) + .map(|(nav_target, link)| { let range = FileRange { file_id: nav_target.file_id, range: nav_target.focus_or_full_range() }; (range, link) diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs index ad7ec1964567..fae100743543 100644 --- a/crates/ide/src/goto_declaration.rs +++ b/crates/ide/src/goto_declaration.rs @@ -66,6 +66,7 @@ pub(crate) fn goto_declaration( let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?; item.try_to_nav(db) }) + .flatten() .collect(); if info.is_empty() { diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 5ca82a362f53..7491879a67fb 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -52,7 +52,7 @@ pub(crate) fn goto_definition( if let Some(doc_comment) = token_as_doc_comment(&original_token) { return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| { let nav = def.try_to_nav(db)?; - Some(RangeInfo::new(link_range, vec![nav])) + Some(RangeInfo::new(link_range, nav.collect())) }); } @@ -88,6 +88,7 @@ pub(crate) fn goto_definition( .resolved_crate(db) .map(|it| it.root_module().to_nav(sema.db)) .into_iter() + .flatten() .collect(); } try_filter_trait_item_definition(sema, &def) @@ -138,6 +139,7 @@ fn try_lookup_include_path( docs: None, }) } + /// finds the trait definition of an impl'd item, except function /// e.g. /// ```rust @@ -166,13 +168,13 @@ fn try_filter_trait_item_definition( .iter() .filter(|itm| discriminant(*itm) == discri_value) .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) - .map(|it| vec![it]) + .map(|it| it.collect()) } } } fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec { - def.try_to_nav(db).map(|it| vec![it]).unwrap_or_default() + def.try_to_nav(db).map(|it| it.collect()).unwrap_or_default() } #[cfg(test)] @@ -405,8 +407,6 @@ fn bar() { ); } - // FIXME: We should emit two targets here, one for the identifier in the declaration, one for - // the macro call #[test] fn goto_def_for_macro_defined_fn_no_arg() { check( @@ -414,7 +414,7 @@ fn bar() { //- /lib.rs macro_rules! define_fn { () => (fn foo() {}) - + //^^^ } define_fn!(); @@ -1748,9 +1748,9 @@ macro_rules! foo { fn $ident(Foo { $ident }: Foo) {} } } -foo!(foo$0); - //^^^ - //^^^ + foo!(foo$0); + //^^^ + //^^^ "#, ); check( diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index bb474282dd7c..6384db39d7c6 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -82,7 +82,11 @@ pub(crate) fn goto_implementation( } fn impls_for_ty(sema: &Semantics<'_, RootDatabase>, ty: hir::Type) -> Vec { - Impl::all_for_type(sema.db, ty).into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect() + Impl::all_for_type(sema.db, ty) + .into_iter() + .filter_map(|imp| imp.try_to_nav(sema.db)) + .flatten() + .collect() } fn impls_for_trait( @@ -92,6 +96,7 @@ fn impls_for_trait( Impl::all_for_trait(sema.db, trait_) .into_iter() .filter_map(|imp| imp.try_to_nav(sema.db)) + .flatten() .collect() } @@ -109,6 +114,7 @@ fn impls_for_trait_item( })?; item.try_to_nav(sema.db) }) + .flatten() .collect() } diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 83f134aaaf57..ad393d98001b 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs @@ -31,9 +31,11 @@ pub(crate) fn goto_type_definition( let mut res = Vec::new(); let mut push = |def: Definition| { - if let Some(nav) = def.try_to_nav(db) { - if !res.contains(&nav) { - res.push(nav); + if let Some(navs) = def.try_to_nav(db) { + for nav in navs { + if !res.contains(&nav) { + res.push(nav); + } } } }; diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 8daff8c2ebe3..3aed007f3ea5 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -117,7 +117,7 @@ fn highlight_closure_captures( local .sources(sema.db) .into_iter() - .map(|x| x.to_nav(sema.db)) + .flat_map(|x| x.to_nav(sema.db)) .filter(|decl| decl.file_id == file_id) .filter_map(|decl| decl.focus_range) .map(move |range| HighlightedRange { range, category }) @@ -216,7 +216,7 @@ fn highlight_references( local .sources(sema.db) .into_iter() - .map(|x| x.to_nav(sema.db)) + .flat_map(|x| x.to_nav(sema.db)) .filter(|decl| decl.file_id == file_id) .filter_map(|decl| decl.focus_range) .map(|range| HighlightedRange { range, category }) @@ -225,21 +225,27 @@ fn highlight_references( }); } def => { - let hl_range = match def { + let navs = match def { Definition::Module(module) => { - Some(NavigationTarget::from_module_to_decl(sema.db, module)) + NavigationTarget::from_module_to_decl(sema.db, module) + } + def => match def.try_to_nav(sema.db) { + Some(it) => it, + None => continue, + }, + }; + for nav in navs { + if nav.file_id != file_id { + continue; + } + let hl_range = nav.focus_range.map(|range| { + let category = references::decl_mutability(&def, node, range) + .then_some(ReferenceCategory::Write); + HighlightedRange { range, category } + }); + if let Some(hl_range) = hl_range { + res.insert(hl_range); } - def => def.try_to_nav(sema.db), - } - .filter(|decl| decl.file_id == file_id) - .and_then(|decl| decl.focus_range) - .map(|range| { - let category = references::decl_mutability(&def, node, range) - .then_some(ReferenceCategory::Write); - HighlightedRange { range, category } - }); - if let Some(hl_range) = hl_range { - res.insert(hl_range); } } } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 88a5b623425d..5ad119ace89d 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -21,6 +21,7 @@ use crate::{ doc_links::token_as_doc_comment, markdown_remove::remove_markdown, markup::Markup, + navigation_target::UpmappingResult, runnables::{runnable_fn, runnable_mod}, FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav, }; @@ -73,7 +74,7 @@ impl HoverAction { it.module(db)?, it.name(db).map(|name| name.display(db).to_string()), ), - nav: it.try_to_nav(db)?, + nav: it.try_to_nav(db)?.call_site(), }) }) .collect(); @@ -342,22 +343,26 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option return it.try_to_nav(db).map(to_action), + Definition::Trait(it) => { + return it.try_to_nav(db).map(UpmappingResult::call_site).map(to_action) + } Definition::Adt(it) => Some(it), Definition::SelfType(it) => it.self_ty(db).as_adt(), _ => None, }?; - adt.try_to_nav(db).map(to_action) + adt.try_to_nav(db).map(UpmappingResult::call_site).map(to_action) } fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option { match def { - Definition::Function(it) => it.try_to_nav(db).map(|nav_target| { - HoverAction::Reference(FilePosition { - file_id: nav_target.file_id, - offset: nav_target.focus_or_full_range().start(), + Definition::Function(it) => { + it.try_to_nav(db).map(UpmappingResult::call_site).map(|nav_target| { + HoverAction::Reference(FilePosition { + file_id: nav_target.file_id, + offset: nav_target.focus_or_full_range().start(), + }) }) - }), + } _ => None, } } diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 7ea9d4f1038b..ca334e915797 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -315,6 +315,7 @@ impl HirWrite for InlayHintLabelBuilder<'_> { } self.make_new_part(); let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; + let location = location.call_site(); let location = FileRange { file_id: location.file_id, range: location.focus_or_full_range() }; self.location = Some(location); diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 3390331e0e8a..d8f6e4e1b1b1 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -100,7 +100,7 @@ pub use crate::{ markup::Markup, moniker::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation}, move_item::Direction, - navigation_target::NavigationTarget, + navigation_target::{NavigationTarget, UpmappingResult}, prime_caches::ParallelPrimeCachesProgress, references::ReferenceSearchResult, rename::RenameError, @@ -230,7 +230,7 @@ impl Analysis { // `AnalysisHost` for creating a fully-featured analysis. pub fn from_single_file(text: String) -> (Analysis, FileId) { let mut host = AnalysisHost::default(); - let file_id = FileId(0); + let file_id = FileId::from_raw(0); let mut file_set = FileSet::default(); file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_string())); let source_root = SourceRoot::new_local(file_set); @@ -413,6 +413,7 @@ impl Analysis { symbol_index::world_symbols(db, query) .into_iter() // xx: should we make this a par iter? .filter_map(|s| s.try_to_nav(db)) + .map(UpmappingResult::call_site) .collect::>() }) } diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index df0c4a6adee2..31f4aad41e52 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -2,6 +2,7 @@ use std::fmt; +use arrayvec::ArrayVec; use either::Either; use hir::{ db::ExpandDatabase, symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasSource, @@ -72,15 +73,15 @@ impl fmt::Debug for NavigationTarget { } pub(crate) trait ToNav { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget; + fn to_nav(&self, db: &RootDatabase) -> UpmappingResult; } pub(crate) trait TryToNav { - fn try_to_nav(&self, db: &RootDatabase) -> Option; + fn try_to_nav(&self, db: &RootDatabase) -> Option>; } impl TryToNav for Either { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { match self { Either::Left(it) => it.try_to_nav(db), Either::Right(it) => it.try_to_nav(db), @@ -93,23 +94,30 @@ impl NavigationTarget { self.focus_range.unwrap_or(self.full_range) } - pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { + pub(crate) fn from_module_to_decl( + db: &RootDatabase, + module: hir::Module, + ) -> UpmappingResult { let name = module.name(db).map(|it| it.to_smol_str()).unwrap_or_default(); - if let Some(InFile { value, file_id }) = &module.declaration_source(db) { - let (file_id, full_range, focus_range) = - orig_range_with_focus(db, *file_id, value.syntax(), value.name()); - let mut res = NavigationTarget::from_syntax( - file_id, - name, - focus_range, - full_range, - SymbolKind::Module, - ); - res.docs = module.docs(db); - res.description = Some(module.display(db).to_string()); - return res; + match module.declaration_source(db) { + Some(InFile { value, file_id }) => { + orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + let mut res = NavigationTarget::from_syntax( + file_id, + name.clone(), + focus_range, + full_range, + SymbolKind::Module, + ); + res.docs = module.docs(db); + res.description = Some(module.display(db).to_string()); + res + }, + ) + } + _ => module.to_nav(db), } - module.to_nav(db) } #[cfg(test)] @@ -135,13 +143,14 @@ impl NavigationTarget { db: &RootDatabase, InFile { file_id, value }: InFile<&dyn ast::HasName>, kind: SymbolKind, - ) -> NavigationTarget { - let name = value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into()); - - let (file_id, full_range, focus_range) = - orig_range_with_focus(db, file_id, value.syntax(), value.name()); + ) -> UpmappingResult { + let name: SmolStr = value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into()); - NavigationTarget::from_syntax(file_id, name, focus_range, full_range, kind) + orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + NavigationTarget::from_syntax(file_id, name.clone(), focus_range, full_range, kind) + }, + ) } fn from_syntax( @@ -166,49 +175,51 @@ impl NavigationTarget { } impl TryToNav for FileSymbol { - fn try_to_nav(&self, db: &RootDatabase) -> Option { - let full_range = self.loc.original_range(db); - let focus_range = self.loc.original_name_range(db); - let focus_range = if focus_range.file_id == full_range.file_id - && full_range.range.contains_range(focus_range.range) - { - Some(focus_range.range) - } else { - None - }; - - Some(NavigationTarget { - file_id: full_range.file_id, - name: self - .is_alias - .then(|| self.def.name(db)) - .flatten() - .map_or_else(|| self.name.clone(), |it| it.to_smol_str()), - alias: self.is_alias.then(|| self.name.clone()), - kind: Some(hir::ModuleDefId::from(self.def).into()), - full_range: full_range.range, - focus_range, - container_name: self.container_name.clone(), - description: match self.def { - hir::ModuleDef::Module(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Function(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Adt(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Variant(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Const(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Static(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Trait(it) => Some(it.display(db).to_string()), - hir::ModuleDef::TraitAlias(it) => Some(it.display(db).to_string()), - hir::ModuleDef::TypeAlias(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Macro(it) => Some(it.display(db).to_string()), - hir::ModuleDef::BuiltinType(_) => None, - }, - docs: None, - }) + fn try_to_nav(&self, db: &RootDatabase) -> Option> { + let root = db.parse_or_expand(self.loc.hir_file_id); + self.loc.ptr.to_node(&root); + Some( + orig_range_with_focus( + db, + self.loc.hir_file_id, + &self.loc.ptr.to_node(&root), + Some(self.loc.name_ptr.to_node(&root)), + ) + .map(|(FileRange { file_id, range: full_range }, focus_range)| { + NavigationTarget { + file_id, + name: self + .is_alias + .then(|| self.def.name(db)) + .flatten() + .map_or_else(|| self.name.clone(), |it| it.to_smol_str()), + alias: self.is_alias.then(|| self.name.clone()), + kind: Some(hir::ModuleDefId::from(self.def).into()), + full_range, + focus_range, + container_name: self.container_name.clone(), + description: match self.def { + hir::ModuleDef::Module(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Function(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Adt(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Variant(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Const(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Static(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Trait(it) => Some(it.display(db).to_string()), + hir::ModuleDef::TraitAlias(it) => Some(it.display(db).to_string()), + hir::ModuleDef::TypeAlias(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Macro(it) => Some(it.display(db).to_string()), + hir::ModuleDef::BuiltinType(_) => None, + }, + docs: None, + } + }), + ) } } impl TryToNav for Definition { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { match self { Definition::Local(it) => Some(it.to_nav(db)), Definition::Label(it) => Some(it.to_nav(db)), @@ -236,7 +247,7 @@ impl TryToNav for Definition { } impl TryToNav for hir::ModuleDef { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { match self { hir::ModuleDef::Module(it) => Some(it.to_nav(db)), hir::ModuleDef::Function(it) => it.try_to_nav(db), @@ -334,22 +345,26 @@ where D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay, D::Ast: ast::HasName, { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { let src = self.source(db)?; - let mut res = NavigationTarget::from_named( - db, - src.as_ref().map(|it| it as &dyn ast::HasName), - D::KIND, - ); - res.docs = self.docs(db); - res.description = Some(self.display(db).to_string()); - res.container_name = self.container_name(db); - Some(res) + Some( + NavigationTarget::from_named( + db, + src.as_ref().map(|it| it as &dyn ast::HasName), + D::KIND, + ) + .map(|mut res| { + res.docs = self.docs(db); + res.description = Some(self.display(db).to_string()); + res.container_name = self.container_name(db); + res + }), + ) } } impl ToNav for hir::Module { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + fn to_nav(&self, db: &RootDatabase) -> UpmappingResult { let InFile { file_id, value } = self.definition_source(db); let name = self.name(db).map(|it| it.to_smol_str()).unwrap_or_default(); @@ -358,13 +373,23 @@ impl ToNav for hir::Module { ModuleSource::Module(node) => (node.syntax(), node.name()), ModuleSource::BlockExpr(node) => (node.syntax(), None), }; - let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); - NavigationTarget::from_syntax(file_id, name, focus_range, full_range, SymbolKind::Module) + + orig_range_with_focus(db, file_id, syntax, focus).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + NavigationTarget::from_syntax( + file_id, + name.clone(), + focus_range, + full_range, + SymbolKind::Module, + ) + }, + ) } } impl TryToNav for hir::Impl { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { let InFile { file_id, value } = self.source(db)?; let derive_path = self.as_builtin_derive_path(db); @@ -373,82 +398,100 @@ impl TryToNav for hir::Impl { None => (file_id, value.self_ty(), value.syntax()), }; - let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); - Some(NavigationTarget::from_syntax( - file_id, - "impl".into(), - focus_range, - full_range, - SymbolKind::Impl, + Some(orig_range_with_focus(db, file_id, syntax, focus).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + NavigationTarget::from_syntax( + file_id, + "impl".into(), + focus_range, + full_range, + SymbolKind::Impl, + ) + }, )) } } impl TryToNav for hir::ExternCrateDecl { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { let src = self.source(db)?; let InFile { file_id, value } = src; let focus = value .rename() .map_or_else(|| value.name_ref().map(Either::Left), |it| it.name().map(Either::Right)); - let (file_id, full_range, focus_range) = - orig_range_with_focus(db, file_id, value.syntax(), focus); - let mut res = NavigationTarget::from_syntax( - file_id, - self.alias_or_name(db).unwrap_or_else(|| self.name(db)).to_smol_str(), - focus_range, - full_range, - SymbolKind::Module, - ); - res.docs = self.docs(db); - res.description = Some(self.display(db).to_string()); - res.container_name = container_name(db, *self); - Some(res) + Some(orig_range_with_focus(db, file_id, value.syntax(), focus).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + let mut res = NavigationTarget::from_syntax( + file_id, + self.alias_or_name(db).unwrap_or_else(|| self.name(db)).to_smol_str(), + focus_range, + full_range, + SymbolKind::Module, + ); + + res.docs = self.docs(db); + res.description = Some(self.display(db).to_string()); + res.container_name = container_name(db, *self); + res + }, + )) } } impl TryToNav for hir::Field { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { let src = self.source(db)?; let field_source = match &src.value { FieldSource::Named(it) => { - let mut res = - NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field); - res.docs = self.docs(db); - res.description = Some(self.display(db).to_string()); - res - } - FieldSource::Pos(it) => { - let FileRange { file_id, range } = - src.with_value(it.syntax()).original_file_range(db); - NavigationTarget::from_syntax(file_id, "".into(), None, range, SymbolKind::Field) + NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map( + |mut res| { + res.docs = self.docs(db); + res.description = Some(self.display(db).to_string()); + res + }, + ) } + FieldSource::Pos(it) => orig_range(db, src.file_id, it.syntax()).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + NavigationTarget::from_syntax( + file_id, + format!("{}", self.index()).into(), + focus_range, + full_range, + SymbolKind::Field, + ) + }, + ), }; Some(field_source) } } impl TryToNav for hir::Macro { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { let src = self.source(db)?; let name_owner: &dyn ast::HasName = match &src.value { Either::Left(it) => it, Either::Right(it) => it, }; - let mut res = NavigationTarget::from_named( - db, - src.as_ref().with_value(name_owner), - self.kind(db).into(), - ); - res.docs = self.docs(db); - Some(res) + Some( + NavigationTarget::from_named( + db, + src.as_ref().with_value(name_owner), + self.kind(db).into(), + ) + .map(|mut res| { + res.docs = self.docs(db); + res + }), + ) } } impl TryToNav for hir::Adt { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { match self { hir::Adt::Struct(it) => it.try_to_nav(db), hir::Adt::Union(it) => it.try_to_nav(db), @@ -458,7 +501,7 @@ impl TryToNav for hir::Adt { } impl TryToNav for hir::AssocItem { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { match self { AssocItem::Function(it) => it.try_to_nav(db), AssocItem::Const(it) => it.try_to_nav(db), @@ -468,7 +511,7 @@ impl TryToNav for hir::AssocItem { } impl TryToNav for hir::GenericParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { match self { hir::GenericParam::TypeParam(it) => it.try_to_nav(db), hir::GenericParam::ConstParam(it) => it.try_to_nav(db), @@ -478,7 +521,7 @@ impl TryToNav for hir::GenericParam { } impl ToNav for LocalSource { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + fn to_nav(&self, db: &RootDatabase) -> UpmappingResult { let InFile { file_id, value } = &self.source; let file_id = *file_id; let local = self.local; @@ -487,60 +530,61 @@ impl ToNav for LocalSource { Either::Right(it) => (it.syntax(), it.name()), }; - let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, node, name); - - let name = local.name(db).to_smol_str(); - let kind = if local.is_self(db) { - SymbolKind::SelfParam - } else if local.is_param(db) { - SymbolKind::ValueParam - } else { - SymbolKind::Local - }; - NavigationTarget { - file_id, - name, - alias: None, - kind: Some(kind), - full_range, - focus_range, - container_name: None, - description: None, - docs: None, - } + orig_range_with_focus(db, file_id, node, name).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + let name = local.name(db).to_smol_str(); + let kind = if local.is_self(db) { + SymbolKind::SelfParam + } else if local.is_param(db) { + SymbolKind::ValueParam + } else { + SymbolKind::Local + }; + NavigationTarget { + file_id, + name, + alias: None, + kind: Some(kind), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + } + }, + ) } } impl ToNav for hir::Local { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + fn to_nav(&self, db: &RootDatabase) -> UpmappingResult { self.primary_source(db).to_nav(db) } } impl ToNav for hir::Label { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + fn to_nav(&self, db: &RootDatabase) -> UpmappingResult { let InFile { file_id, value } = self.source(db); let name = self.name(db).to_smol_str(); - let (file_id, full_range, focus_range) = - orig_range_with_focus(db, file_id, value.syntax(), value.lifetime()); - - NavigationTarget { - file_id, - name, - alias: None, - kind: Some(SymbolKind::Label), - full_range, - focus_range, - container_name: None, - description: None, - docs: None, - } + orig_range_with_focus(db, file_id, value.syntax(), value.lifetime()).map( + |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget { + file_id, + name: name.clone(), + alias: None, + kind: Some(SymbolKind::Label), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + }, + ) } } impl TryToNav for hir::TypeParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { let InFile { file_id, value } = self.merge().source(db)?; let name = self.name(db).to_smol_str(); @@ -559,51 +603,51 @@ impl TryToNav for hir::TypeParam { }; let focus = value.as_ref().either(|it| it.name(), |it| it.name()); - let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); - - Some(NavigationTarget { - file_id, - name, - alias: None, - kind: Some(SymbolKind::TypeParam), - full_range, - focus_range, - container_name: None, - description: None, - docs: None, - }) + Some(orig_range_with_focus(db, file_id, syntax, focus).map( + |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget { + file_id, + name: name.clone(), + alias: None, + kind: Some(SymbolKind::TypeParam), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + }, + )) } } impl TryToNav for hir::TypeOrConstParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { self.split(db).try_to_nav(db) } } impl TryToNav for hir::LifetimeParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { let InFile { file_id, value } = self.source(db)?; let name = self.name(db).to_smol_str(); - let FileRange { file_id, range } = - InFile::new(file_id, value.syntax()).original_file_range(db); - Some(NavigationTarget { - file_id, - name, - alias: None, - kind: Some(SymbolKind::LifetimeParam), - full_range: range, - focus_range: Some(range), - container_name: None, - description: None, - docs: None, - }) + Some(orig_range(db, file_id, value.syntax()).map( + |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget { + file_id, + name: name.clone(), + alias: None, + kind: Some(SymbolKind::LifetimeParam), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + }, + )) } } impl TryToNav for hir::ConstParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { let InFile { file_id, value } = self.merge().source(db)?; let name = self.name(db).to_smol_str(); @@ -615,46 +659,180 @@ impl TryToNav for hir::ConstParam { } }; - let (file_id, full_range, focus_range) = - orig_range_with_focus(db, file_id, value.syntax(), value.name()); - Some(NavigationTarget { - file_id, - name, - alias: None, - kind: Some(SymbolKind::ConstParam), - full_range, - focus_range, - container_name: None, - description: None, - docs: None, - }) + Some(orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( + |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget { + file_id, + name: name.clone(), + alias: None, + kind: Some(SymbolKind::ConstParam), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + }, + )) + } +} + +#[derive(Debug)] +pub struct UpmappingResult { + /// The macro call site. + pub call_site: T, + /// The macro definition site, if relevant. + pub def_site: Option, +} + +impl UpmappingResult { + pub fn call_site(self) -> T { + self.call_site + } + + pub fn collect>(self) -> FI { + FI::from_iter(self.into_iter()) + } +} + +impl IntoIterator for UpmappingResult { + type Item = T; + + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.def_site + .into_iter() + .chain(Some(self.call_site)) + .collect::>() + .into_iter() + } +} + +impl UpmappingResult { + fn map(self, f: impl Fn(T) -> U) -> UpmappingResult { + UpmappingResult { call_site: f(self.call_site), def_site: self.def_site.map(f) } } } /// Returns the original range of the syntax node, and the range of the name mapped out of macro expansions -/// Additionally verifies that the name span is in bounds and related to the original range. +/// May return two results if the mapped node originates from a macro definition in which case the +/// second result is the creating macro call. fn orig_range_with_focus( db: &RootDatabase, hir_file: HirFileId, value: &SyntaxNode, name: Option, -) -> (FileId, TextRange, Option) { - let FileRange { file_id, range } = - match InFile::new(hir_file, value).original_file_range_opt(db) { - Some((range, ctxt)) if ctxt.is_root() => range, - _ => db - .lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id) - .kind - .original_call_range(db), +) -> UpmappingResult<(FileRange, Option)> { + let Some(name) = name else { return orig_range(db, hir_file, value) }; + + let call_range = || { + db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id) + .kind + .original_call_range(db) + }; + + let def_range = || { + db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id) + .def + .definition_range(db) + }; + + // FIXME What about include!d things + + let value_range = InFile::new(hir_file, value).original_file_range_opt(db); + let ((call_site_range, call_site_focus), def_site) = + match InFile::new(hir_file, name.syntax()).original_file_range_opt(db) { + // call site name + Some((focus_range, ctxt)) if ctxt.is_root() => { + // Try to upmap the node as well, if it ends up in the def site, go back to the call site + ( + ( + match value_range { + // name is in the node in the macro input so we can return it + Some((range, ctxt)) + if ctxt.is_root() + && range.file_id == focus_range.file_id + && range.range.contains_range(focus_range.range) => + { + range + } + // name lies outside the node, so instead point to the macro call which + // *should* contain the name + _ => call_range(), + }, + Some(focus_range), + ), + // no def site relevant + None, + ) + } + + // def site name + // FIXME: This can be de improved + Some((focus_range, _ctxt)) => { + match value_range { + // but overall node is in macro input + Some((range, ctxt)) if ctxt.is_root() => ( + // node mapped up in call site, show the node + (range, None), + // def site, if the name is in the (possibly) upmapped def site range, show the + // def site + { + let (def_site, _) = def_range().original_node_file_range(db); + (def_site.file_id == focus_range.file_id + && def_site.range.contains_range(focus_range.range)) + .then_some((def_site, Some(focus_range))) + }, + ), + // node is in macro def, just show the focus + _ => ( + // show the macro call + (call_range(), None), + Some((focus_range, Some(focus_range))), + ), + } + } + // lost name? can't happen for single tokens + None => return orig_range(db, hir_file, value), }; - let focus_range = name - .and_then(|it| InFile::new(hir_file, it.syntax()).original_file_range_opt(db)) - .filter(|(frange, ctxt)| { - ctxt.is_root() && frange.file_id == file_id && frange.range.contains_range(frange.range) - }) - .map(|(frange, _ctxt)| frange.range); - - (file_id, range, focus_range) + + UpmappingResult { + call_site: ( + call_site_range, + call_site_focus.and_then(|FileRange { file_id, range }| { + if call_site_range.file_id == file_id && call_site_range.range.contains_range(range) + { + Some(range) + } else { + None + } + }), + ), + def_site: def_site.map(|(def_site_range, def_site_focus)| { + ( + def_site_range, + def_site_focus.and_then(|FileRange { file_id, range }| { + if def_site_range.file_id == file_id + && def_site_range.range.contains_range(range) + { + Some(range) + } else { + None + } + }), + ) + }), + } +} + +fn orig_range( + db: &RootDatabase, + hir_file: HirFileId, + value: &SyntaxNode, +) -> UpmappingResult<(FileRange, Option)> { + UpmappingResult { + call_site: (InFile::new(hir_file, value).original_file_range(db), None), + def_site: None, + } } #[cfg(test)] diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index 506f9452cf19..413dbf9c5dfc 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs @@ -45,11 +45,11 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec sema .to_def(&module) .into_iter() - .map(|module| NavigationTarget::from_module_to_decl(db, module)) + .flat_map(|module| NavigationTarget::from_module_to_decl(db, module)) .collect(), None => sema .to_module_defs(position.file_id) - .map(|module| NavigationTarget::from_module_to_decl(db, module)) + .flat_map(|module| NavigationTarget::from_module_to_decl(db, module)) .collect(), } } diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index b805ddfa2678..6c0fb0baf2e2 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -9,6 +9,8 @@ //! at the index that the match starts at and its tree parent is //! resolved to the search element definition, we get a reference. +use std::collections::HashMap; + use hir::{DescendPreference, PathResolution, Semantics}; use ide_db::{ base_db::FileId, @@ -60,19 +62,6 @@ pub(crate) fn find_all_refs( let syntax = sema.parse(position.file_id).syntax().clone(); let make_searcher = |literal_search: bool| { move |def: Definition| { - let declaration = match def { - Definition::Module(module) => { - Some(NavigationTarget::from_module_to_decl(sema.db, module)) - } - def => def.try_to_nav(sema.db), - } - .map(|nav| { - let decl_range = nav.focus_or_full_range(); - Declaration { - is_mut: decl_mutability(&def, sema.parse(nav.file_id).syntax(), decl_range), - nav, - } - }); let mut usages = def.usages(sema).set_scope(search_scope.as_ref()).include_self_refs().all(); @@ -80,7 +69,7 @@ pub(crate) fn find_all_refs( retain_adt_literal_usages(&mut usages, def, sema); } - let references = usages + let mut references = usages .into_iter() .map(|(file_id, refs)| { ( @@ -91,8 +80,30 @@ pub(crate) fn find_all_refs( .collect(), ) }) - .collect(); - + .collect::, _>>(); + let declaration = match def { + Definition::Module(module) => { + Some(NavigationTarget::from_module_to_decl(sema.db, module)) + } + def => def.try_to_nav(sema.db), + } + .map(|nav| { + let (nav, extra_ref) = match nav.def_site { + Some(call) => (call, Some(nav.call_site)), + None => (nav.call_site, None), + }; + if let Some(extra_ref) = extra_ref { + references + .entry(extra_ref.file_id) + .or_default() + .push((extra_ref.focus_or_full_range(), None)); + } + let decl_range = nav.focus_or_full_range(); + Declaration { + is_mut: decl_mutability(&def, sema.parse(nav.file_id).syntax(), decl_range), + nav, + } + }); ReferenceSearchResult { declaration, references } } }; @@ -882,7 +893,7 @@ pub(super) struct Foo$0 { check_with_scope( code, - Some(SearchScope::single_file(FileId(2))), + Some(SearchScope::single_file(FileId::from_raw(2))), expect![[r#" quux Function FileId(0) 19..35 26..30 @@ -1181,7 +1192,7 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { } "#, expect![[r#" - 'a LifetimeParam FileId(0) 55..57 55..57 + 'a LifetimeParam FileId(0) 55..57 FileId(0) 63..65 FileId(0) 71..73 @@ -1199,7 +1210,7 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { type Foo<'a, T> where T: 'a$0 = &'a T; "#, expect![[r#" - 'a LifetimeParam FileId(0) 9..11 9..11 + 'a LifetimeParam FileId(0) 9..11 FileId(0) 25..27 FileId(0) 31..33 @@ -1221,7 +1232,7 @@ impl<'a> Foo<'a> for &'a () { } "#, expect![[r#" - 'a LifetimeParam FileId(0) 47..49 47..49 + 'a LifetimeParam FileId(0) 47..49 FileId(0) 55..57 FileId(0) 64..66 diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index d487a538bb57..c0b556f5441e 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -335,7 +335,8 @@ pub(crate) fn runnable_fn( sema.db, def.source(sema.db)?.as_ref().map(|it| it as &dyn ast::HasName), SymbolKind::Function, - ); + ) + .call_site(); let cfg = def.attrs(sema.db).cfg(); Some(Runnable { use_name_in_title: false, nav, kind, cfg }) } @@ -357,7 +358,7 @@ pub(crate) fn runnable_mod( let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); - let nav = NavigationTarget::from_module_to_decl(sema.db, def); + let nav = NavigationTarget::from_module_to_decl(sema.db, def).call_site(); Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::TestMod { path }, cfg }) } @@ -370,7 +371,7 @@ pub(crate) fn runnable_impl( return None; } let cfg = attrs.cfg(); - let nav = def.try_to_nav(sema.db)?; + let nav = def.try_to_nav(sema.db)?.call_site(); let ty = def.self_ty(sema.db); let adt_name = ty.as_adt()?.name(sema.db); let mut ty_args = ty.generic_parameters(sema.db).peekable(); @@ -407,7 +408,7 @@ fn runnable_mod_outline_definition( match def.definition_source(sema.db).value { hir::ModuleSource::SourceFile(_) => Some(Runnable { use_name_in_title: false, - nav: def.to_nav(sema.db), + nav: def.to_nav(sema.db).call_site(), kind: RunnableKind::TestMod { path }, cfg, }), @@ -465,7 +466,8 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { let mut nav = match def { Definition::Module(def) => NavigationTarget::from_module_to_decl(db, def), def => def.try_to_nav(db)?, - }; + } + .call_site(); nav.focus_range = None; nav.description = None; nav.docs = None; diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 49e165d3dc50..3724dc282211 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -13,6 +13,7 @@ use ide_db::{ use syntax::{AstNode, SyntaxKind::*, TextRange, T}; use crate::inlay_hints::InlayFieldsToResolve; +use crate::navigation_target::UpmappingResult; use crate::{ hover::hover_for_definition, inlay_hints::AdjustmentHintsMode, @@ -166,9 +167,8 @@ impl StaticIndex<'_> { } else { let it = self.tokens.insert(TokenStaticData { hover: hover_for_definition(&sema, file_id, def, &node, &hover_config), - definition: def.try_to_nav(self.db).map(|it| FileRange { - file_id: it.file_id, - range: it.focus_or_full_range(), + definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| { + FileRange { file_id: it.file_id, range: it.focus_or_full_range() } }), references: vec![], moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), @@ -179,7 +179,7 @@ impl StaticIndex<'_> { let token = self.tokens.get_mut(id).unwrap(); token.references.push(ReferenceData { range: FileRange { range, file_id }, - is_definition: match def.try_to_nav(self.db) { + is_definition: match def.try_to_nav(self.db).map(UpmappingResult::call_site) { Some(it) => it.file_id == file_id && it.focus_or_full_range() == range, None => false, }, diff --git a/crates/proc-macro-api/src/msg.rs b/crates/proc-macro-api/src/msg.rs index dd882d82fb64..1d3e45aff385 100644 --- a/crates/proc-macro-api/src/msg.rs +++ b/crates/proc-macro-api/src/msg.rs @@ -147,8 +147,10 @@ mod tests { use super::*; fn fixture_token_tree() -> Subtree { - let anchor = - SpanAnchor { file_id: FileId(0), ast_id: ErasedFileAstId::from_raw(RawIdx::from(0)) }; + let anchor = SpanAnchor { + file_id: FileId::from_raw(0), + ast_id: ErasedFileAstId::from_raw(RawIdx::from(0)), + }; let mut subtree = Subtree { delimiter: Delimiter { open: SpanData { diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 98f3063bb982..4887b29815ae 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -58,7 +58,7 @@ fn load_cargo_with_sysroot( &mut { |path| { let len = file_map.len(); - Some(*file_map.entry(path.to_path_buf()).or_insert(FileId(len as u32))) + Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) } }, &Default::default(), @@ -142,7 +142,7 @@ fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacro let mut counter = 0; move |_path| { counter += 1; - Some(FileId(counter)) + Some(FileId::from_raw(counter)) } }, &Default::default(), diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 06c27332d440..49c88702faad 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1438,7 +1438,7 @@ pub(crate) fn handle_inlay_hints_resolve( }; let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?; - let file_id = FileId(resolve_data.file_id); + let file_id = FileId::from_raw(resolve_data.file_id); anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data"); let line_index = snap.file_line_index(file_id)?; diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index fb366fd5cc41..dae560c5de12 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -457,7 +457,7 @@ pub(crate) fn inlay_hint( inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) }; let data = if needs_resolve && something_to_resolve { - Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.0 }).unwrap()) + Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index() }).unwrap()) } else { None }; diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs index 71762996cd7d..07641b203bb8 100644 --- a/crates/syntax/src/ptr.rs +++ b/crates/syntax/src/ptr.rs @@ -22,12 +22,17 @@ use crate::{syntax_node::RustLanguage, AstNode, SyntaxNode}; pub type SyntaxNodePtr = rowan::ast::SyntaxNodePtr; /// Like `SyntaxNodePtr`, but remembers the type of node. -#[derive(Debug)] pub struct AstPtr { raw: SyntaxNodePtr, _ty: PhantomData N>, } +impl std::fmt::Debug for AstPtr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("AstPtr").field(&self.raw).finish() + } +} + impl Clone for AstPtr { fn clone(&self) -> AstPtr { AstPtr { raw: self.raw.clone(), _ty: PhantomData } diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 7360f68c735e..8ffda5d78d13 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -60,11 +60,21 @@ pub use paths::{AbsPath, AbsPathBuf}; /// /// Most functions in rust-analyzer use this when they need to refer to a file. #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct FileId(pub u32); +pub struct FileId(u32); impl FileId { /// Think twice about using this outside of tests. If this ends up in a wrong place it will cause panics! - pub const BOGUS: FileId = FileId(u32::MAX); + pub const BOGUS: FileId = FileId(0xe4e4e); + + #[inline] + pub fn from_raw(raw: u32) -> FileId { + FileId(raw) + } + + #[inline] + pub fn index(self) -> u32 { + self.0 + } } /// safe because `FileId` is a newtype of `u32` From ee812314cbbed74b14805f026ad7bca71de33f01 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Dec 2023 14:36:39 +0100 Subject: [PATCH 15/62] Simplify --- .../hir-def/src/macro_expansion_tests/mod.rs | 52 ++++--- crates/hir-def/src/nameres/mod_resolution.rs | 4 +- crates/hir-expand/src/files.rs | 52 +++---- crates/hir-expand/src/lib.rs | 146 +++++++----------- crates/hir-ty/src/diagnostics/decl_check.rs | 4 +- crates/hir/src/lib.rs | 3 +- crates/hir/src/semantics.rs | 6 +- crates/hir/src/source_analyzer.rs | 13 +- crates/hir/src/symbols.rs | 4 - .../replace_derive_with_manual_impl.rs | 10 +- crates/ide/src/expand_macro.rs | 8 +- crates/ide/src/runnables.rs | 2 +- .../ide/src/syntax_highlighting/highlight.rs | 7 +- 13 files changed, 136 insertions(+), 175 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index bcbf4047caa2..be2a503d82b1 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -18,7 +18,7 @@ use std::{iter, ops::Range, sync}; use base_db::{fixture::WithFixture, span::SpanData, ProcMacro, SourceDatabase}; use expect_test::Expect; -use hir_expand::{db::ExpandDatabase, span::SpanMapRef, HirFileIdExt, InFile, MacroFileId}; +use hir_expand::{db::ExpandDatabase, span::SpanMapRef, InFile, MacroFileId, MacroFileIdExt}; use stdx::format_to; use syntax::{ ast::{self, edit::IndentLevel}, @@ -172,35 +172,41 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream }; if let Some(src) = src { - if src.file_id.is_attr_macro(&db) || src.file_id.is_custom_derive(&db) { - let call = src.file_id.call_node(&db).expect("macro file"); - let mut show_spans = false; - let mut show_ctxt = false; - for comment in call.value.children_with_tokens().filter(|it| it.kind() == COMMENT) { - show_spans |= comment.to_string().contains("+spans"); - show_ctxt |= comment.to_string().contains("+syntaxctxt"); + if let Some(file_id) = src.file_id.macro_file() { + if file_id.is_attr_macro(&db) || file_id.is_custom_derive(&db) { + let call = file_id.call_node(&db); + let mut show_spans = false; + let mut show_ctxt = false; + for comment in + call.value.children_with_tokens().filter(|it| it.kind() == COMMENT) + { + show_spans |= comment.to_string().contains("+spans"); + show_ctxt |= comment.to_string().contains("+syntaxctxt"); + } + let pp = pretty_print_macro_expansion( + src.value, + db.span_map(src.file_id).as_ref(), + show_spans, + show_ctxt, + ); + format_to!(expanded_text, "\n{}", pp) } - let pp = pretty_print_macro_expansion( - src.value, - db.span_map(src.file_id).as_ref(), - show_spans, - show_ctxt, - ); - format_to!(expanded_text, "\n{}", pp) } } } for impl_id in def_map[local_id].scope.impls() { let src = impl_id.lookup(&db).source(&db); - if src.file_id.is_builtin_derive(&db) { - let pp = pretty_print_macro_expansion( - src.value.syntax().clone(), - db.span_map(src.file_id).as_ref(), - false, - false, - ); - format_to!(expanded_text, "\n{}", pp) + if let Some(macro_file) = src.file_id.macro_file() { + if macro_file.is_builtin_derive(&db) { + let pp = pretty_print_macro_expansion( + src.value.syntax().clone(), + db.span_map(macro_file.into()).as_ref(), + false, + false, + ); + format_to!(expanded_text, "\n{}", pp) + } } } diff --git a/crates/hir-def/src/nameres/mod_resolution.rs b/crates/hir-def/src/nameres/mod_resolution.rs index 9dcb9717297f..c45200e2de9d 100644 --- a/crates/hir-def/src/nameres/mod_resolution.rs +++ b/crates/hir-def/src/nameres/mod_resolution.rs @@ -1,7 +1,7 @@ //! This module resolves `mod foo;` declaration to file. use arrayvec::ArrayVec; use base_db::{AnchoredPath, FileId}; -use hir_expand::{name::Name, HirFileIdExt}; +use hir_expand::{name::Name, HirFileIdExt, MacroFileIdExt}; use limit::Limit; use syntax::SmolStr; @@ -73,7 +73,7 @@ impl ModDir { Some(attr_path) => { candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) } - None if file_id.is_include_macro(db.upcast()) => { + None if file_id.macro_file().map_or(false, |it| it.is_include_macro(db.upcast())) => { candidate_files.push(format!("{}.rs", name.display(db.upcast()))); candidate_files.push(format!("{}/mod.rs", name.display(db.upcast()))); } diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs index 174e59056981..7e55f6be1e41 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs @@ -6,9 +6,9 @@ use base_db::{ FileId, FileRange, }; use either::Either; -use syntax::{AstNode, SyntaxNode, SyntaxToken, TextRange}; +use syntax::{AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize}; -use crate::{db, ExpansionInfo, HirFileIdExt as _}; +use crate::{db, ExpansionInfo, MacroFileIdExt}; /// `InFile` stores a value of `T` inside a particular file/syntax tree. /// @@ -119,16 +119,6 @@ impl InFileWrapper { // region:specific impls impl InFile<&SyntaxNode> { - pub fn ancestors_with_macros( - self, - db: &dyn db::ExpandDatabase, - ) -> impl Iterator> + Clone + '_ { - iter::successors(Some(self.cloned()), move |node| match node.value.parent() { - Some(parent) => Some(node.with_value(parent)), - None => node.file_id.call_node(db), - }) - } - /// Skips the attributed item that caused the macro invocation we are climbing up pub fn ancestors_with_macros_skip_attr_item( self, @@ -137,8 +127,9 @@ impl InFile<&SyntaxNode> { let succ = move |node: &InFile| match node.value.parent() { Some(parent) => Some(node.with_value(parent)), None => { - let parent_node = node.file_id.call_node(db)?; - if node.file_id.is_attr_macro(db) { + let macro_file_id = node.file_id.macro_file()?; + let parent_node = macro_file_id.call_node(db); + if macro_file_id.is_attr_macro(db) { // macro call was an attributed item, skip it // FIXME: does this fail if this is a direct expansion of another macro? parent_node.map(|node| node.parent()).transpose() @@ -222,7 +213,7 @@ impl InFile<&SyntaxNode> { } HirFileIdRepr::MacroFile(m) => m, }; - if !self.file_id.is_attr_macro(db) { + if !file_id.is_attr_macro(db) { return None; } @@ -243,21 +234,23 @@ impl InFile<&SyntaxNode> { } } -impl InFile { +impl InMacroFile { pub fn upmap_once( self, db: &dyn db::ExpandDatabase, - ) -> Option>> { - Some(self.file_id.expansion_info(db)?.map_range_up_once(db, self.value.text_range())) + ) -> InFile> { + self.file_id.expansion_info(db).map_range_up_once(db, self.value.text_range()) } +} +impl InFile { /// Falls back to the macro call range if the node cannot be mapped up fully. pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange { match self.file_id.repr() { HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, HirFileIdRepr::MacroFile(mac_file) => { let (range, ctxt) = ExpansionInfo::new(db, mac_file) - .map_token_range_up(db, self.value.text_range()); + .span_for_offset(db, self.value.text_range().start()); // FIXME: Figure out an API that makes proper use of ctx, this only exists to // keep pre-token map rewrite behaviour. @@ -280,7 +273,7 @@ impl InFile { } HirFileIdRepr::MacroFile(mac_file) => { let (range, ctxt) = ExpansionInfo::new(db, mac_file) - .map_token_range_up(db, self.value.text_range()); + .span_for_offset(db, self.value.text_range().start()); // FIXME: Figure out an API that makes proper use of ctx, this only exists to // keep pre-token map rewrite behaviour. @@ -294,20 +287,13 @@ impl InFile { } } -impl InFile { - /// Attempts to map the syntax node back up its macro calls. - pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange { - let (range, _ctxt) = match self.file_id.repr() { - HirFileIdRepr::FileId(file_id) => { - (FileRange { file_id, range: self.value }, SyntaxContextId::ROOT) - } - HirFileIdRepr::MacroFile(m) => { - ExpansionInfo::new(db, m).map_token_range_up(db, self.value) - } - }; - range +impl InMacroFile { + pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> (FileRange, SyntaxContextId) { + ExpansionInfo::new(db, self.file_id).span_for_offset(db, self.value) } +} +impl InFile { pub fn original_node_file_range( self, db: &dyn db::ExpandDatabase, @@ -353,7 +339,7 @@ impl InFile { } HirFileIdRepr::MacroFile(m) => m, }; - if !self.file_id.is_attr_macro(db) { + if !file_id.is_attr_macro(db) { return None; } diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 167ba0eb5da1..fe336aa1421e 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -179,9 +179,6 @@ pub trait HirFileIdExt { /// one of the calls comes from an `include!``. fn original_file_respecting_includes(self, db: &dyn db::ExpandDatabase) -> FileId; - /// If this is a macro call, returns the syntax node of the call. - fn call_node(self, db: &dyn db::ExpandDatabase) -> Option>; - /// If this is a macro call, returns the syntax node of the very first macro call this file resides in. fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option>; @@ -190,19 +187,6 @@ pub trait HirFileIdExt { fn as_builtin_derive_attr_node(&self, db: &dyn db::ExpandDatabase) -> Option>; - fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool; - fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool; - - /// Return whether this file is an include macro - fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool; - - fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool; - /// Return whether this file is an attr macro - fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool; - - /// Return whether this file is the pseudo expansion of the derive attribute. - /// See [`crate::builtin_attr_macro::derive_attr_expand`]. - fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool; } impl HirFileIdExt for HirFileId { @@ -241,12 +225,6 @@ impl HirFileIdExt for HirFileId { } } - fn call_node(self, db: &dyn db::ExpandDatabase) -> Option> { - let macro_file = self.macro_file()?; - let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - Some(loc.to_node(db)) - } - fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option> { let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db); loop { @@ -278,77 +256,34 @@ impl HirFileIdExt for HirFileId { }; Some(attr.with_value(ast::Attr::cast(attr.value.clone())?)) } +} - fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool { - match self.macro_file() { - Some(macro_file) => { - matches!( - db.lookup_intern_macro_call(macro_file.macro_call_id).def.kind, - MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _) - ) - } - None => false, - } - } - - fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool { - match self.macro_file() { - Some(macro_file) => { - matches!( - db.lookup_intern_macro_call(macro_file.macro_call_id).def.kind, - MacroDefKind::BuiltInDerive(..) - ) - } - None => false, - } - } +pub trait MacroFileIdExt { + fn expansion_level(self, db: &dyn db::ExpandDatabase) -> u32; + /// If this is a macro call, returns the syntax node of the call. + fn call_node(self, db: &dyn db::ExpandDatabase) -> InFile; - fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool { - match self.macro_file() { - Some(macro_file) => { - db.lookup_intern_macro_call(macro_file.macro_call_id).def.is_include() - } - _ => false, - } - } + fn expansion_info(self, db: &dyn db::ExpandDatabase) -> ExpansionInfo; - fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool { - match self.macro_file() { - Some(macro_file) => { - let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) - } - _ => false, - } - } + fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool; + fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool; - fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool { - match self.macro_file() { - Some(macro_file) => { - let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - matches!(loc.kind, MacroCallKind::Attr { .. }) - } - _ => false, - } - } + /// Return whether this file is an include macro + fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool; - fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool { - match self.macro_file() { - Some(macro_file) => { - let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - loc.def.is_attribute_derive() - } - None => false, - } - } -} + fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool; + /// Return whether this file is an attr macro + fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool; -pub trait MacroFileIdExt { - fn expansion_level(self, db: &dyn db::ExpandDatabase) -> u32; - fn expansion_info(self, db: &dyn db::ExpandDatabase) -> ExpansionInfo; + /// Return whether this file is the pseudo expansion of the derive attribute. + /// See [`crate::builtin_attr_macro::derive_attr_expand`]. + fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool; } impl MacroFileIdExt for MacroFileId { + fn call_node(self, db: &dyn db::ExpandDatabase) -> InFile { + db.lookup_intern_macro_call(self.macro_call_id).to_node(db) + } fn expansion_level(self, db: &dyn db::ExpandDatabase) -> u32 { let mut level = 0; let mut macro_file = self; @@ -367,6 +302,39 @@ impl MacroFileIdExt for MacroFileId { fn expansion_info(self, db: &dyn db::ExpandDatabase) -> ExpansionInfo { ExpansionInfo::new(db, self) } + + fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool { + matches!( + db.lookup_intern_macro_call(self.macro_call_id).def.kind, + MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _) + ) + } + + fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool { + matches!( + db.lookup_intern_macro_call(self.macro_call_id).def.kind, + MacroDefKind::BuiltInDerive(..) + ) + } + + fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool { + db.lookup_intern_macro_call(self.macro_call_id).def.is_include() + } + + fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool { + let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); + matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) + } + + fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool { + let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); + matches!(loc.kind, MacroCallKind::Attr { .. }) + } + + fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool { + let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); + loc.def.is_attribute_derive() + } } impl MacroDefId { @@ -653,14 +621,14 @@ impl ExpansionInfo { Some(tokens.map(move |token| InMacroFile::new(self.expanded.file_id, token))) } - /// Maps up the text range out of the expansion hierarchy back into the original file its from. - pub fn map_token_range_up( + /// Looks up the span at the given offset. + pub fn span_for_offset( &self, db: &dyn db::ExpandDatabase, - range: TextRange, + offset: TextSize, ) -> (FileRange, SyntaxContextId) { - debug_assert!(self.expanded.value.text_range().contains_range(range)); - let span = self.exp_map.span_at(range.start()); + debug_assert!(self.expanded.value.text_range().contains(offset)); + let span = self.exp_map.span_at(offset); let anchor_offset = db .ast_id_map(span.anchor.file_id.into()) .get_erased(span.anchor.ast_id) diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index c2ff487ef91d..51a044d8ef56 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -24,7 +24,7 @@ use hir_def::{ }; use hir_expand::{ name::{AsName, Name}, - HirFileId, HirFileIdExt, + HirFileId, MacroFileIdExt, }; use stdx::{always, never}; use syntax::{ @@ -196,7 +196,7 @@ impl<'a> DeclValidator<'a> { AttrDefId::GenericParamId(_) => None, } .map_or(false, |file_id| { - file_id.is_custom_derive(db.upcast()) || file_id.is_builtin_derive(db.upcast()) + matches!(file_id.macro_file(), Some(file_id) if file_id.is_custom_derive(db.upcast()) || file_id.is_builtin_derive(db.upcast())) }) }; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 7e210740eac0..4a0c384e8a30 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -128,6 +128,7 @@ pub use { hygiene::{marks_rev, SyntaxContextExt}, name::{known, Name}, tt, ExpandResult, HirFileId, HirFileIdExt, InFile, InMacroFile, InRealFile, MacroFileId, + MacroFileIdExt, }, hir_ty::{ display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, @@ -607,7 +608,7 @@ impl Module { let tree = loc.id.item_tree(db.upcast()); let node = &tree[loc.id.value]; let file_id = loc.id.file_id(); - if file_id.is_builtin_derive(db.upcast()) { + if file_id.macro_file().map_or(false, |it| it.is_builtin_derive(db.upcast())) { // these expansion come from us, diagnosing them is a waste of resources // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow continue; diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index ac70c27785c5..46835ec04e01 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -20,8 +20,8 @@ use hir_def::{ AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId, }; use hir_expand::{ - db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo, HirFileIdExt, MacroCallId, - MacroFileId, MacroFileIdExt, + db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo, MacroCallId, MacroFileId, + MacroFileIdExt, }; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; @@ -865,7 +865,7 @@ impl<'db> SemanticsImpl<'db> { Some(parent) => Some(InFile::new(file_id, parent)), None => { self.cache(value.clone(), file_id); - file_id.call_node(db) + Some(file_id.macro_file()?.call_node(db)) } } }) diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 8afa7e065947..7a31e6df1fae 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -29,7 +29,7 @@ use hir_expand::{ mod_path::path, name, name::{AsName, Name}, - HirFileId, HirFileIdExt, InFile, MacroFileId, MacroFileIdExt, + HirFileId, InFile, MacroFileId, MacroFileIdExt, }; use hir_ty::{ diagnostics::{ @@ -939,11 +939,12 @@ fn scope_for_offset( } // FIXME handle attribute expansion - let source = iter::successors(file_id.call_node(db.upcast()), |it| { - it.file_id.call_node(db.upcast()) - }) - .find(|it| it.file_id == from_file) - .filter(|it| it.value.kind() == SyntaxKind::MACRO_CALL)?; + let source = + iter::successors(file_id.macro_file().map(|it| it.call_node(db.upcast())), |it| { + Some(it.file_id.macro_file()?.call_node(db.upcast())) + }) + .find(|it| it.file_id == from_file) + .filter(|it| it.value.kind() == SyntaxKind::MACRO_CALL)?; Some((source.value.text_range(), scope)) }) .filter(|(expr_range, _scope)| expr_range.start() <= offset && offset <= expr_range.end()) diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 2b03d575cb27..03112f6de5af 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -49,10 +49,6 @@ impl DeclarationLocation { let node = resolve_node(db, self.hir_file_id, &self.ptr); node.as_ref().original_file_range(db.upcast()) } - - pub fn original_name_range(&self, db: &dyn HirDatabase) -> FileRange { - InFile::new(self.hir_file_id, self.name_ptr.text_range()).original_file_range(db.upcast()) - } } fn resolve_node( diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 1b373bcb8ce9..b54e4204e3f3 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1,4 +1,4 @@ -use hir::{HirFileIdExt, InFile, ModuleDef}; +use hir::{InFile, MacroFileIdExt, ModuleDef}; use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator}; use itertools::Itertools; use syntax::{ @@ -43,12 +43,12 @@ pub(crate) fn replace_derive_with_manual_impl( ) -> Option<()> { let attr = ctx.find_node_at_offset_with_descend::()?; let path = attr.path()?; - let hir_file = ctx.sema.hir_file_for(attr.syntax()); - if !hir_file.is_derive_attr_pseudo_expansion(ctx.db()) { + let macro_file = ctx.sema.hir_file_for(attr.syntax()).macro_file()?; + if !macro_file.is_derive_attr_pseudo_expansion(ctx.db()) { return None; } - let InFile { file_id, value } = hir_file.call_node(ctx.db())?; + let InFile { file_id, value } = macro_file.call_node(ctx.db()); if file_id.is_macro() { // FIXME: make this work in macro files return None; @@ -56,7 +56,7 @@ pub(crate) fn replace_derive_with_manual_impl( // collect the derive paths from the #[derive] expansion let current_derives = ctx .sema - .parse_or_expand(hir_file) + .parse_or_expand(macro_file.into()) .descendants() .filter_map(ast::Attr::cast) .filter_map(|attr| attr.path()) diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 653d9f088384..024053effe42 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -1,4 +1,4 @@ -use hir::{DescendPreference, HirFileIdExt, InFile, Semantics}; +use hir::{DescendPreference, InFile, MacroFileIdExt, Semantics}; use ide_db::{ base_db::FileId, helpers::pick_best_token, syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase, @@ -44,15 +44,15 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< .descend_into_macros(DescendPreference::None, tok.clone()) .into_iter() .find_map(|descended| { - let hir_file = sema.hir_file_for(&descended.parent()?); - if !hir_file.is_derive_attr_pseudo_expansion(db) { + let macro_file = sema.hir_file_for(&descended.parent()?).macro_file()?; + if !macro_file.is_derive_attr_pseudo_expansion(db) { return None; } let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string(); // up map out of the #[derive] expansion let InFile { file_id, value: tokens } = - hir::InFile::new(hir_file, descended).upmap_once(db)?; + hir::InMacroFile::new(macro_file, descended).upmap_once(db); let token = sema.parse_or_expand(file_id).covering_element(tokens[0]).into_token()?; let attr = token.parent_ancestors().find_map(ast::Attr::cast)?; let expansions = sema.expand_derive_macro(&attr)?; diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index c0b556f5441e..d334e66d3dd6 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -142,7 +142,7 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { Definition::Function(it) => it.source(db).map(|src| src.file_id), _ => None, }; - if let Some(file_id) = file_id.filter(|file| file.call_node(db).is_some()) { + if let Some(file_id) = file_id.filter(|file| file.macro_file().is_some()) { in_macro_expansion.entry(file_id).or_default().push(runnable); return; } diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index d510c11c3d5a..0558f658fd19 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -1,6 +1,6 @@ //! Computes color for a single element. -use hir::{AsAssocItem, HasVisibility, HirFileIdExt, Semantics}; +use hir::{AsAssocItem, HasVisibility, MacroFileIdExt, Semantics}; use ide_db::{ defs::{Definition, IdentClass, NameClass, NameRefClass}, FxHashMap, RootDatabase, SymbolKind, @@ -218,7 +218,10 @@ fn highlight_name_ref( // We can fix this for derive attributes since derive helpers are recorded, but not for // general attributes. None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR) - && !sema.hir_file_for(name_ref.syntax()).is_derive_attr_pseudo_expansion(sema.db) => + && !sema + .hir_file_for(name_ref.syntax()) + .macro_file() + .map_or(false, |it| it.is_derive_attr_pseudo_expansion(sema.db)) => { return HlTag::Symbol(SymbolKind::Attribute).into(); } From 6f7dfacb37b98d96fbc6244ed2dfa7d33aa9e367 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Dec 2023 14:36:45 +0100 Subject: [PATCH 16/62] Fix diagnostics panicking when resolving to different files due to macros --- .../src/handlers/field_shorthand.rs | 9 ++- .../src/handlers/inactive_code.rs | 2 +- .../src/handlers/invalid_derive_target.rs | 2 +- .../src/handlers/json_is_not_rust.rs | 4 +- .../src/handlers/macro_error.rs | 20 ++++++ .../src/handlers/malformed_derive.rs | 2 +- .../src/handlers/mismatched_arg_count.rs | 5 +- .../src/handlers/type_mismatch.rs | 21 +++--- .../src/handlers/typed_hole.rs | 2 +- .../src/handlers/unlinked_file.rs | 10 ++- .../src/handlers/unresolved_module.rs | 7 +- .../src/handlers/useless_braces.rs | 7 +- crates/ide-diagnostics/src/lib.rs | 26 ++++--- crates/ide-diagnostics/src/tests.rs | 49 +++++++------ crates/rust-analyzer/src/diagnostics.rs | 69 +++++++++++++++---- 15 files changed, 158 insertions(+), 77 deletions(-) diff --git a/crates/ide-diagnostics/src/handlers/field_shorthand.rs b/crates/ide-diagnostics/src/handlers/field_shorthand.rs index 9ed8199ae4d0..45fc6f8e68d0 100644 --- a/crates/ide-diagnostics/src/handlers/field_shorthand.rs +++ b/crates/ide-diagnostics/src/handlers/field_shorthand.rs @@ -1,7 +1,10 @@ //! Suggests shortening `Foo { field: field }` to `Foo { field }` in both //! expressions and patterns. -use ide_db::{base_db::FileId, source_change::SourceChange}; +use ide_db::{ + base_db::{FileId, FileRange}, + source_change::SourceChange, +}; use syntax::{ast, match_ast, AstNode, SyntaxNode}; use text_edit::TextEdit; @@ -49,7 +52,7 @@ fn check_expr_field_shorthand( Diagnostic::new( DiagnosticCode::Clippy("redundant_field_names"), "Shorthand struct initialization", - field_range, + FileRange { file_id, range: field_range }, ) .with_fixes(Some(vec![fix( "use_expr_field_shorthand", @@ -93,7 +96,7 @@ fn check_pat_field_shorthand( Diagnostic::new( DiagnosticCode::Clippy("redundant_field_names"), "Shorthand struct pattern", - field_range, + FileRange { file_id, range: field_range }, ) .with_fixes(Some(vec![fix( "use_pat_field_shorthand", diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs index 9eb763d3e2c2..3b2e15a17887 100644 --- a/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -31,7 +31,7 @@ pub(crate) fn inactive_code( let res = Diagnostic::new( DiagnosticCode::Ra("inactive-code", Severity::WeakWarning), message, - ctx.sema.diagnostics_display_range(d.node.clone()).range, + ctx.sema.diagnostics_display_range(d.node.clone()), ) .with_unused(true); Some(res) diff --git a/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs b/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs index 1ec17952b238..f68f5b44b11b 100644 --- a/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs +++ b/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs @@ -8,7 +8,7 @@ pub(crate) fn invalid_derive_target( ctx: &DiagnosticsContext<'_>, d: &hir::InvalidDeriveTarget, ) -> Diagnostic { - let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range; + let display_range = ctx.sema.diagnostics_display_range(d.node.clone()); Diagnostic::new( DiagnosticCode::RustcHardError("E0774"), diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 659b74445f8f..d330973aaaa3 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -3,7 +3,7 @@ use hir::{PathResolution, Semantics}; use ide_db::{ - base_db::FileId, + base_db::{FileId, FileRange}, helpers::mod_path_to_ast, imports::insert_use::{insert_use, ImportScope}, source_change::SourceChangeBuilder, @@ -119,7 +119,7 @@ pub(crate) fn json_in_items( Diagnostic::new( DiagnosticCode::Ra("json-is-not-rust", Severity::WeakWarning), "JSON syntax is not valid as a Rust item", - range, + FileRange { file_id, range }, ) .with_fixes(Some(vec![{ let mut scb = SourceChangeBuilder::new(file_id); diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs index 2993950be04a..099de4528d46 100644 --- a/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -264,4 +264,24 @@ fn f() { "#, ) } + + #[test] + fn include_does_not_break_diagnostics() { + let mut config = DiagnosticsConfig::test_sample(); + config.disabled.insert("inactive-code".to_string()); + config.disabled.insert("unlinked-file".to_string()); + check_diagnostics_with_config( + config, + r#" +//- minicore: include +//- /lib.rs crate:lib +include!("include-me.rs"); +//- /include-me.rs +/// long doc that pushes the diagnostic range beyond the first file's text length + #[err] +//^^^^^^error: unresolved macro `err` +mod prim_never {} +"#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/malformed_derive.rs b/crates/ide-diagnostics/src/handlers/malformed_derive.rs index fc57dde69f2a..6202d1585396 100644 --- a/crates/ide-diagnostics/src/handlers/malformed_derive.rs +++ b/crates/ide-diagnostics/src/handlers/malformed_derive.rs @@ -7,7 +7,7 @@ pub(crate) fn malformed_derive( ctx: &DiagnosticsContext<'_>, d: &hir::MalformedDerive, ) -> Diagnostic { - let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range; + let display_range = ctx.sema.diagnostics_display_range(d.node.clone()); Diagnostic::new( DiagnosticCode::RustcHardError("E0777"), diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 06ba13bcc55c..8296018022cb 100644 --- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -1,8 +1,9 @@ use either::Either; use hir::InFile; +use ide_db::base_db::FileRange; use syntax::{ ast::{self, HasArgList}, - AstNode, SyntaxNodePtr, TextRange, + AstNode, SyntaxNodePtr, }; use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext}; @@ -48,7 +49,7 @@ fn invalid_args_range( source: InFile, expected: usize, found: usize, -) -> TextRange { +) -> FileRange { adjusted_display_range::>(ctx, source, &|expr| { let (text_range, r_paren_token, expected_arg) = match expr { Either::Left(ast::Expr::CallExpr(call)) => { diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index fd00535d0c34..70beb9468938 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -35,14 +35,10 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) Some(salient_token_range) }, ), - pat => { - ctx.sema - .diagnostics_display_range(InFile { - file_id: d.expr_or_pat.file_id, - value: pat.syntax_node_ptr(), - }) - .range - } + pat => ctx.sema.diagnostics_display_range(InFile { + file_id: d.expr_or_pat.file_id, + value: pat.syntax_node_ptr(), + }), }; let mut diag = Diagnostic::new( DiagnosticCode::RustcHardError("E0308"), @@ -84,7 +80,7 @@ fn add_reference( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let range = ctx.sema.diagnostics_display_range(expr_ptr.clone().map(|it| it.into())).range; + let range = ctx.sema.diagnostics_display_range(expr_ptr.clone().map(|it| it.into())); let (_, mutability) = d.expected.as_reference()?; let actual_with_ref = Type::reference(&d.actual, mutability); @@ -94,10 +90,9 @@ fn add_reference( let ampersands = format!("&{}", mutability.as_keyword_for_ref()); - let edit = TextEdit::insert(range.start(), ampersands); - let source_change = - SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit); - acc.push(fix("add_reference_here", "Add reference here", source_change, range)); + let edit = TextEdit::insert(range.range.start(), ampersands); + let source_change = SourceChange::from_text_edit(range.file_id, edit); + acc.push(fix("add_reference_here", "Add reference here", source_change, range.range)); Some(()) } diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index ea5c7564d343..a740e332bbdd 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -26,7 +26,7 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di ) }; - Diagnostic::new(DiagnosticCode::RustcHardError("typed-hole"), message, display_range.range) + Diagnostic::new(DiagnosticCode::RustcHardError("typed-hole"), message, display_range) .with_fixes(fixes) } diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index e04f27c27fdf..becc24ab21ec 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -4,7 +4,7 @@ use std::iter; use hir::{db::DefDatabase, DefMap, InFile, ModuleSource}; use ide_db::{ - base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, + base_db::{FileId, FileLoader, FileRange, SourceDatabase, SourceDatabaseExt}, source_change::SourceChange, RootDatabase, }; @@ -46,8 +46,12 @@ pub(crate) fn unlinked_file( .unwrap_or(range); acc.push( - Diagnostic::new(DiagnosticCode::Ra("unlinked-file", Severity::WeakWarning), message, range) - .with_fixes(fixes), + Diagnostic::new( + DiagnosticCode::Ra("unlinked-file", Severity::WeakWarning), + message, + FileRange { file_id, range }, + ) + .with_fixes(fixes), ); } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 4f81ec051258..e90d385bab8c 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_module.rs @@ -87,7 +87,12 @@ mod baz {} "E0583", ), message: "unresolved module, can't find module file: foo.rs, or foo/mod.rs", - range: 0..8, + range: FileRange { + file_id: FileId( + 0, + ), + range: 0..8, + }, severity: Error, unused: false, experimental: false, diff --git a/crates/ide-diagnostics/src/handlers/useless_braces.rs b/crates/ide-diagnostics/src/handlers/useless_braces.rs index c4ac59ec2a4d..8dce2af23e32 100644 --- a/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -1,5 +1,8 @@ use hir::InFile; -use ide_db::{base_db::FileId, source_change::SourceChange}; +use ide_db::{ + base_db::{FileId, FileRange}, + source_change::SourceChange, +}; use itertools::Itertools; use syntax::{ast, AstNode, SyntaxNode}; use text_edit::TextEdit; @@ -38,7 +41,7 @@ pub(crate) fn useless_braces( Diagnostic::new( DiagnosticCode::RustcLint("unused_braces"), "Unnecessary braces in use statement".to_string(), - use_range, + FileRange { file_id, range: use_range }, ) .with_main_node(InFile::new(file_id.into(), node.clone())) .with_fixes(Some(vec![fix( diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index f9fb921f4053..35272e872645 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -133,7 +133,7 @@ impl DiagnosticCode { pub struct Diagnostic { pub code: DiagnosticCode, pub message: String, - pub range: TextRange, + pub range: FileRange, pub severity: Severity, pub unused: bool, pub experimental: bool, @@ -143,7 +143,7 @@ pub struct Diagnostic { } impl Diagnostic { - fn new(code: DiagnosticCode, message: impl Into, range: TextRange) -> Diagnostic { + fn new(code: DiagnosticCode, message: impl Into, range: FileRange) -> Diagnostic { let message = message.into(); Diagnostic { code, @@ -172,7 +172,7 @@ impl Diagnostic { node: InFile, ) -> Diagnostic { let file_id = node.file_id; - Diagnostic::new(code, message, ctx.sema.diagnostics_display_range(node.clone()).range) + Diagnostic::new(code, message, ctx.sema.diagnostics_display_range(node.clone())) .with_main_node(node.map(|x| x.to_node(&ctx.sema.parse_or_expand(file_id)))) } @@ -267,7 +267,7 @@ impl DiagnosticsContext<'_> { &self, node: &InFile, precise_location: Option, - ) -> TextRange { + ) -> FileRange { let sema = &self.sema; (|| { let precise_location = precise_location?; @@ -280,10 +280,11 @@ impl DiagnosticsContext<'_> { } })() .unwrap_or_else(|| sema.diagnostics_display_range(node.clone())) - .range } } +/// Request diagnostics for the given [`FileId`]. The produced diagnostics may point to other files +/// due to macros. pub fn diagnostics( db: &RootDatabase, config: &DiagnosticsConfig, @@ -300,7 +301,7 @@ pub fn diagnostics( Diagnostic::new( DiagnosticCode::RustcHardError("syntax-error"), format!("Syntax Error: {err}"), - err.range(), + FileRange { file_id, range: err.range() }, ) })); @@ -569,12 +570,15 @@ fn adjusted_display_range( ctx: &DiagnosticsContext<'_>, diag_ptr: InFile, adj: &dyn Fn(N) -> Option, -) -> TextRange { +) -> FileRange { let FileRange { file_id, range } = ctx.sema.diagnostics_display_range(diag_ptr); let source_file = ctx.sema.db.parse(file_id); - find_node_at_range::(&source_file.syntax_node(), range) - .filter(|it| it.syntax().text_range() == range) - .and_then(adj) - .unwrap_or(range) + FileRange { + file_id, + range: find_node_at_range::(&source_file.syntax_node(), range) + .filter(|it| it.syntax().text_range() == range) + .and_then(adj) + .unwrap_or(range), + } } diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index c766a018bfd0..48e0363c9ca8 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -7,6 +7,7 @@ use ide_db::{ base_db::{fixture::WithFixture, SourceDatabaseExt}, LineIndexDatabase, RootDatabase, }; +use itertools::Itertools; use stdx::trim_indent; use test_utils::{assert_eq_text, extract_annotations, MiniCore}; @@ -103,33 +104,39 @@ pub(crate) fn check_diagnostics(ra_fixture: &str) { #[track_caller] pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) { let (db, files) = RootDatabase::with_many_files(ra_fixture); + let mut annotations = files + .iter() + .copied() + .flat_map(|file_id| { + super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id).into_iter().map( + |d| { + let mut annotation = String::new(); + if let Some(fixes) = &d.fixes { + assert!(!fixes.is_empty()); + annotation.push_str("💡 ") + } + annotation.push_str(match d.severity { + Severity::Error => "error", + Severity::WeakWarning => "weak", + Severity::Warning => "warn", + Severity::Allow => "allow", + }); + annotation.push_str(": "); + annotation.push_str(&d.message); + (d.range, annotation) + }, + ) + }) + .map(|(diagnostic, annotation)| (diagnostic.file_id, (diagnostic.range, annotation))) + .into_group_map(); for file_id in files { let line_index = db.line_index(file_id); - let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); + let mut actual = annotations.remove(&file_id).unwrap_or_default(); let expected = extract_annotations(&db.file_text(file_id)); - let mut actual = diagnostics - .into_iter() - .map(|d| { - let mut annotation = String::new(); - if let Some(fixes) = &d.fixes { - assert!(!fixes.is_empty()); - annotation.push_str("💡 ") - } - annotation.push_str(match d.severity { - Severity::Error => "error", - Severity::WeakWarning => "weak", - Severity::Warning => "warn", - Severity::Allow => "allow", - }); - annotation.push_str(": "); - annotation.push_str(&d.message); - (d.range, annotation) - }) - .collect::>(); actual.sort_by_key(|(range, _)| range.start()); if expected.is_empty() { - // makes minicore smoke test debugable + // makes minicore smoke test debuggable for (e, _) in &actual { eprintln!( "Code in range {e:?} = {}", diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 71701ef16179..f80beb9caadd 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs @@ -5,6 +5,7 @@ use std::mem; use ide::FileId; use ide_db::FxHashMap; +use itertools::Itertools; use nohash_hasher::{IntMap, IntSet}; use rustc_hash::FxHashSet; use triomphe::Arc; @@ -129,8 +130,28 @@ pub(crate) fn fetch_native_diagnostics( ) -> Vec<(FileId, Vec)> { let _p = profile::span("fetch_native_diagnostics"); let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned()); - subscriptions - .into_iter() + + let convert_diagnostic = + |line_index: &crate::line_index::LineIndex, d: ide::Diagnostic| lsp_types::Diagnostic { + range: lsp::to_proto::range(&line_index, d.range.range), + severity: Some(lsp::to_proto::diagnostic_severity(d.severity)), + code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_string())), + code_description: Some(lsp_types::CodeDescription { + href: lsp_types::Url::parse(&d.code.url()).unwrap(), + }), + source: Some("rust-analyzer".to_string()), + message: d.message, + related_information: None, + tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]), + data: None, + }; + + // the diagnostics produced may point to different files not requested by the concrete request, + // put those into here and filter later + let mut odd_ones = Vec::new(); + let mut diagnostics = subscriptions + .iter() + .copied() .filter_map(|file_id| { let line_index = snapshot.file_line_index(file_id).ok()?; let diagnostics = snapshot @@ -142,21 +163,39 @@ pub(crate) fn fetch_native_diagnostics( ) .ok()? .into_iter() - .map(move |d| lsp_types::Diagnostic { - range: lsp::to_proto::range(&line_index, d.range), - severity: Some(lsp::to_proto::diagnostic_severity(d.severity)), - code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_string())), - code_description: Some(lsp_types::CodeDescription { - href: lsp_types::Url::parse(&d.code.url()).unwrap(), - }), - source: Some("rust-analyzer".to_string()), - message: d.message, - related_information: None, - tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]), - data: None, + .filter_map(|d| { + if d.range.file_id == file_id { + Some(convert_diagnostic(&line_index, d)) + } else { + odd_ones.push(d); + None + } }) .collect::>(); Some((file_id, diagnostics)) }) - .collect() + .collect::>(); + + // Add back any diagnostics that point to files we are subscribed to + for (file_id, group) in odd_ones + .into_iter() + .sorted_by_key(|it| it.range.file_id) + .group_by(|it| it.range.file_id) + .into_iter() + { + if !subscriptions.contains(&file_id) { + continue; + } + let Some((_, diagnostics)) = diagnostics.iter_mut().find(|&&mut (id, _)| id == file_id) + else { + continue; + }; + let Some(line_index) = snapshot.file_line_index(file_id).ok() else { + break; + }; + for diagnostic in group { + diagnostics.push(convert_diagnostic(&line_index, diagnostic)); + } + } + diagnostics } From 747e8a5eeffedbfae23245e3fb76244d05c463b0 Mon Sep 17 00:00:00 2001 From: Johannes Hostert Date: Wed, 6 Dec 2023 15:11:45 +0100 Subject: [PATCH 17/62] make ParamLoweringMode accessible --- crates/hir-ty/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 907a30301964..33dffafa2600 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -73,8 +73,8 @@ pub use infer::{ }; pub use interner::Interner; pub use lower::{ - associated_type_shorthand_candidates, CallableDefId, ImplTraitLoweringMode, TyDefId, - TyLoweringContext, ValueTyDefId, + associated_type_shorthand_candidates, CallableDefId, ImplTraitLoweringMode, ParamLoweringMode, + TyDefId, TyLoweringContext, ValueTyDefId, }; pub use mapping::{ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, From 597cc1f16d02489e17b64f1f8ac7669a8af6caed Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Dec 2023 17:39:41 +0100 Subject: [PATCH 18/62] fix: Don't print proc-macro panic backtraces in the logs --- crates/hir-expand/src/builtin_fn_macro.rs | 2 + crates/hir-expand/src/lib.rs | 5 ++ crates/hir-expand/src/proc_macro.rs | 2 +- crates/ide/src/navigation_target.rs | 2 - crates/ide/src/syntax_highlighting.rs | 8 +--- .../test_data/highlight_macros.html | 4 +- .../test_data/highlight_strings.html | 2 +- crates/ide/src/syntax_highlighting/tests.rs | 6 ++- crates/proc-macro-srv/src/dylib.rs | 2 +- crates/proc-macro-srv/src/lib.rs | 4 +- crates/proc-macro-srv/src/proc_macros.rs | 46 +++++++++---------- 11 files changed, 41 insertions(+), 42 deletions(-) diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 4b2f27bd4650..903c21c84eec 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -152,6 +152,8 @@ fn line_expand( span: SpanData, ) -> ExpandResult { // dummy implementation for type-checking purposes + // Note that `line!` and `column!` will never be implemented properly, as they are by definition + // not incremental ExpandResult::ok(tt::Subtree { delimiter: tt::Delimiter::dummy_invisible(), token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index fe336aa1421e..a159cf92a7ac 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -78,6 +78,7 @@ pub enum ExpandError { Mbe(mbe::ExpandError), RecursionOverflowPoisoned, Other(Box>), + ProcMacroPanic(Box>), } impl ExpandError { @@ -100,6 +101,10 @@ impl fmt::Display for ExpandError { ExpandError::RecursionOverflowPoisoned => { f.write_str("overflow expanding the original macro") } + ExpandError::ProcMacroPanic(it) => { + f.write_str("proc-macro panicked: ")?; + f.write_str(it) + } ExpandError::Other(it) => f.write_str(it), } } diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs index ccae4c288e65..de577796831f 100644 --- a/crates/hir-expand/src/proc_macro.rs +++ b/crates/hir-expand/src/proc_macro.rs @@ -85,7 +85,7 @@ impl ProcMacroExpander { ProcMacroExpansionError::System(text) | ProcMacroExpansionError::Panic(text) => ExpandResult::new( tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), - ExpandError::other(text), + ExpandError::ProcMacroPanic(Box::new(text.into_boxed_str())), ), }, } diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 31f4aad41e52..6cb7d7724d5f 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -736,8 +736,6 @@ fn orig_range_with_focus( .definition_range(db) }; - // FIXME What about include!d things - let value_range = InFile::new(hir_file, value).original_file_range_opt(db); let ((call_site_range, call_site_focus), def_site) = match InFile::new(hir_file, name.syntax()).original_file_range_opt(db) { diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 366a3c969f93..307812156e92 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -404,13 +404,7 @@ fn traverse( }) .unwrap() } else { - sema.descend_into_macros_single( - match attr_or_derive_item { - Some(AttrOrDerive::Attr(_)) => DescendPreference::SameKind, - Some(AttrOrDerive::Derive(_)) | None => DescendPreference::None, - }, - token, - ) + sema.descend_into_macros_single(DescendPreference::SameKind, token) }; match token.parent().and_then(ast::NameLike::cast) { // Remap the token into the wrapping single token nodes diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index b8d38a60fc01..e8b3a38c9e0f 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -43,7 +43,9 @@ .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
proc_macros::mirror! {
+
use proc_macros::{mirror, identity, DeriveIdentity};
+
+mirror! {
     {
         ,i32 :x pub
         ,i32 :y pub
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 75cb6223e0e6..84a823363f68 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -118,7 +118,7 @@
     println!("Hello {:+}!", 5);
     println!("{:#x}!", 27);
     println!("Hello {:05}!", 5);
-    println!("Hello {:05}!", -5);
+    println!("Hello {:05}!", -5);
     println!("{:#010x}!", 27);
     println!("Hello {0} is {1:.5}", "x", 0.01);
     println!("Hello {1} is {2:.0$}", 5, "x", 0.01);
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index fcfd3c92571b..afb6c555b4af 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -47,10 +47,12 @@ struct Foo;
 fn macros() {
     check_highlighting(
         r#"
-//- proc_macros: mirror
+//- proc_macros: mirror, identity, derive_identity
 //- minicore: fmt, include, concat
 //- /lib.rs crate:lib
-proc_macros::mirror! {
+use proc_macros::{mirror, identity, DeriveIdentity};
+
+mirror! {
     {
         ,i32 :x pub
         ,i32 :y pub
diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs
index 80bce3af1a00..f20e6832f6e9 100644
--- a/crates/proc-macro-srv/src/dylib.rs
+++ b/crates/proc-macro-srv/src/dylib.rs
@@ -160,7 +160,7 @@ impl Expander {
             .inner
             .proc_macros
             .expand(macro_name, macro_body, attributes, def_site, call_site, mixed_site);
-        result.map_err(|e| e.as_str().unwrap_or_else(|| "".to_string()))
+        result.map_err(|e| e.into_string().unwrap_or_default())
     }
 
     pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs
index 790e7936cdc5..5b03813b19a8 100644
--- a/crates/proc-macro-srv/src/lib.rs
+++ b/crates/proc-macro-srv/src/lib.rs
@@ -160,8 +160,8 @@ pub struct PanicMessage {
 }
 
 impl PanicMessage {
-    pub fn as_str(&self) -> Option {
-        self.message.clone()
+    pub fn into_string(self) -> Option {
+        self.message
     }
 }
 
diff --git a/crates/proc-macro-srv/src/proc_macros.rs b/crates/proc-macro-srv/src/proc_macros.rs
index 4f87fa281b77..716b85d096d0 100644
--- a/crates/proc-macro-srv/src/proc_macros.rs
+++ b/crates/proc-macro-srv/src/proc_macros.rs
@@ -1,16 +1,17 @@
 //! Proc macro ABI
 
 use libloading::Library;
+use proc_macro::bridge;
 use proc_macro_api::{msg::TokenId, ProcMacroKind, RustCInfo};
 
 use crate::{dylib::LoadProcMacroDylibError, server::SYMBOL_INTERNER, tt};
 
 pub(crate) struct ProcMacros {
-    exported_macros: Vec,
+    exported_macros: Vec,
 }
 
-impl From for crate::PanicMessage {
-    fn from(p: proc_macro::bridge::PanicMessage) -> Self {
+impl From for crate::PanicMessage {
+    fn from(p: bridge::PanicMessage) -> Self {
         Self { message: p.as_str().map(|s| s.to_string()) }
     }
 }
@@ -31,9 +32,8 @@ impl ProcMacros {
         info: RustCInfo,
     ) -> Result {
         if info.version_string == crate::RUSTC_VERSION_STRING {
-            let macros = unsafe {
-                lib.get::<&&[proc_macro::bridge::client::ProcMacro]>(symbol_name.as_bytes())
-            }?;
+            let macros =
+                unsafe { lib.get::<&&[bridge::client::ProcMacro]>(symbol_name.as_bytes()) }?;
 
             return Ok(Self { exported_macros: macros.to_vec() });
         }
@@ -57,11 +57,11 @@ impl ProcMacros {
 
         for proc_macro in &self.exported_macros {
             match proc_macro {
-                proc_macro::bridge::client::ProcMacro::CustomDerive {
-                    trait_name, client, ..
-                } if *trait_name == macro_name => {
+                bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
+                    if *trait_name == macro_name =>
+                {
                     let res = client.run(
-                        &proc_macro::bridge::server::SameThread,
+                        &bridge::server::SameThread,
                         crate::server::RustAnalyzer {
                             interner: &SYMBOL_INTERNER,
                             call_site,
@@ -69,17 +69,15 @@ impl ProcMacros {
                             mixed_site,
                         },
                         parsed_body,
-                        true,
+                        false,
                     );
                     return res
                         .map(|it| it.into_subtree(call_site))
                         .map_err(crate::PanicMessage::from);
                 }
-                proc_macro::bridge::client::ProcMacro::Bang { name, client }
-                    if *name == macro_name =>
-                {
+                bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
                     let res = client.run(
-                        &proc_macro::bridge::server::SameThread,
+                        &bridge::server::SameThread,
                         crate::server::RustAnalyzer {
                             interner: &SYMBOL_INTERNER,
                             call_site,
@@ -87,17 +85,15 @@ impl ProcMacros {
                             mixed_site,
                         },
                         parsed_body,
-                        true,
+                        false,
                     );
                     return res
                         .map(|it| it.into_subtree(call_site))
                         .map_err(crate::PanicMessage::from);
                 }
-                proc_macro::bridge::client::ProcMacro::Attr { name, client }
-                    if *name == macro_name =>
-                {
+                bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
                     let res = client.run(
-                        &proc_macro::bridge::server::SameThread,
+                        &bridge::server::SameThread,
                         crate::server::RustAnalyzer {
                             interner: &SYMBOL_INTERNER,
 
@@ -107,7 +103,7 @@ impl ProcMacros {
                         },
                         parsed_attributes,
                         parsed_body,
-                        true,
+                        false,
                     );
                     return res
                         .map(|it| it.into_subtree(call_site))
@@ -117,20 +113,20 @@ impl ProcMacros {
             }
         }
 
-        Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
+        Err(bridge::PanicMessage::String("Nothing to expand".to_string()).into())
     }
 
     pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
         self.exported_macros
             .iter()
             .map(|proc_macro| match proc_macro {
-                proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
+                bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
                     (trait_name.to_string(), ProcMacroKind::CustomDerive)
                 }
-                proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
+                bridge::client::ProcMacro::Bang { name, .. } => {
                     (name.to_string(), ProcMacroKind::FuncLike)
                 }
-                proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
+                bridge::client::ProcMacro::Attr { name, .. } => {
                     (name.to_string(), ProcMacroKind::Attr)
                 }
             })

From a2d1c9c4a6df200c4e04ec02522ba515f17b971d Mon Sep 17 00:00:00 2001
From: David Barsky 
Date: Tue, 28 Nov 2023 10:36:01 -0500
Subject: [PATCH 19/62] internal: switch to `Arc::from_iter`

---
 crates/hir-expand/src/attrs.rs               | 123 ++++++++-----------
 crates/hir-ty/src/lib.rs                     |   6 +-
 crates/hir-ty/src/lower.rs                   |  90 ++++++--------
 crates/hir-ty/src/method_resolution.rs       |   9 +-
 crates/rust-analyzer/src/global_state.rs     |   8 +-
 crates/rust-analyzer/src/handlers/request.rs |   6 +-
 crates/rust-analyzer/src/reload.rs           |  38 +++---
 7 files changed, 117 insertions(+), 163 deletions(-)

diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index edaf2f06a4e8..b8fc30c91189 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -44,21 +44,18 @@ impl RawAttrs {
         owner: &dyn ast::HasAttrs,
         span_map: SpanMapRef<'_>,
     ) -> Self {
-        let entries = collect_attrs(owner)
-            .filter_map(|(id, attr)| match attr {
-                Either::Left(attr) => {
-                    attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
-                }
-                Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
-                    id,
-                    input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
-                    path: Interned::new(ModPath::from(crate::name!(doc))),
-                    ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx,
-                }),
-            })
-            .collect::>();
-        // FIXME: use `Arc::from_iter` when it becomes available
-        let entries: Arc<[Attr]> = Arc::from(entries);
+        let entries = collect_attrs(owner).filter_map(|(id, attr)| match attr {
+            Either::Left(attr) => {
+                attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
+            }
+            Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
+                id,
+                input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
+                path: Interned::new(ModPath::from(crate::name!(doc))),
+                ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx,
+            }),
+        });
+        let entries: Arc<[Attr]> = Arc::from_iter(entries);
 
         Self { entries: if entries.is_empty() { None } else { Some(entries) } }
     }
@@ -79,19 +76,13 @@ impl RawAttrs {
             (Some(a), Some(b)) => {
                 let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
                 Self {
-                    entries: Some(Arc::from(
-                        a.iter()
-                            .cloned()
-                            .chain(b.iter().map(|it| {
-                                let mut it = it.clone();
-                                it.id.id = it.id.ast_index() as u32 + last_ast_index
-                                    | (it.id.cfg_attr_index().unwrap_or(0) as u32)
-                                        << AttrId::AST_INDEX_BITS;
-                                it
-                            }))
-                            // FIXME: use `Arc::from_iter` when it becomes available
-                            .collect::>(),
-                    )),
+                    entries: Some(Arc::from_iter(a.iter().cloned().chain(b.iter().map(|it| {
+                        let mut it = it.clone();
+                        it.id.id = it.id.ast_index() as u32 + last_ast_index
+                            | (it.id.cfg_attr_index().unwrap_or(0) as u32)
+                                << AttrId::AST_INDEX_BITS;
+                        it
+                    })))),
                 }
             }
         }
@@ -108,49 +99,43 @@ impl RawAttrs {
         }
 
         let crate_graph = db.crate_graph();
-        let new_attrs = Arc::from(
-            self.iter()
-                .flat_map(|attr| -> SmallVec<[_; 1]> {
-                    let is_cfg_attr =
-                        attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
-                    if !is_cfg_attr {
-                        return smallvec![attr.clone()];
-                    }
-
-                    let subtree = match attr.token_tree_value() {
-                        Some(it) => it,
-                        _ => return smallvec![attr.clone()],
-                    };
+        let new_attrs = Arc::from_iter(self.iter().flat_map(|attr| -> SmallVec<[_; 1]> {
+            let is_cfg_attr =
+                attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
+            if !is_cfg_attr {
+                return smallvec![attr.clone()];
+            }
 
-                    let (cfg, parts) = match parse_cfg_attr_input(subtree) {
-                        Some(it) => it,
-                        None => return smallvec![attr.clone()],
+            let subtree = match attr.token_tree_value() {
+                Some(it) => it,
+                _ => return smallvec![attr.clone()],
+            };
+
+            let (cfg, parts) = match parse_cfg_attr_input(subtree) {
+                Some(it) => it,
+                None => return smallvec![attr.clone()],
+            };
+            let index = attr.id;
+            let attrs =
+                parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(|(idx, attr)| {
+                    let tree = Subtree {
+                        delimiter: tt::Delimiter::dummy_invisible(),
+                        token_trees: attr.to_vec(),
                     };
-                    let index = attr.id;
-                    let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
-                        |(idx, attr)| {
-                            let tree = Subtree {
-                                delimiter: tt::Delimiter::dummy_invisible(),
-                                token_trees: attr.to_vec(),
-                            };
-                            Attr::from_tt(db, &tree, index.with_cfg_attr(idx))
-                        },
-                    );
-
-                    let cfg_options = &crate_graph[krate].cfg_options;
-                    let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
-                    let cfg = CfgExpr::parse(&cfg);
-                    if cfg_options.check(&cfg) == Some(false) {
-                        smallvec![]
-                    } else {
-                        cov_mark::hit!(cfg_attr_active);
-
-                        attrs.collect()
-                    }
-                })
-                // FIXME: use `Arc::from_iter` when it becomes available
-                .collect::>(),
-        );
+                    Attr::from_tt(db, &tree, index.with_cfg_attr(idx))
+                });
+
+            let cfg_options = &crate_graph[krate].cfg_options;
+            let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
+            let cfg = CfgExpr::parse(&cfg);
+            if cfg_options.check(&cfg) == Some(false) {
+                smallvec![]
+            } else {
+                cov_mark::hit!(cfg_attr_active);
+
+                attrs.collect()
+            }
+        }));
 
         RawAttrs { entries: Some(new_attrs) }
     }
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 33dffafa2600..5a3e423f152a 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -322,8 +322,7 @@ impl CallableSig {
     pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig {
         CallableSig {
             // FIXME: what to do about lifetime params? -> return PolyFnSig
-            // FIXME: use `Arc::from_iter` when it becomes available
-            params_and_return: Arc::from(
+            params_and_return: Arc::from_iter(
                 fn_ptr
                     .substitution
                     .clone()
@@ -332,8 +331,7 @@ impl CallableSig {
                     .0
                     .as_slice(Interner)
                     .iter()
-                    .map(|arg| arg.assert_ty_ref(Interner).clone())
-                    .collect::>(),
+                    .map(|arg| arg.assert_ty_ref(Interner).clone()),
             ),
             is_varargs: fn_ptr.sig.variadic,
             safety: fn_ptr.sig.safety,
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 5122021d6d2b..30ebd1f92e07 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -1459,8 +1459,7 @@ pub(crate) fn generic_predicates_for_param_recover(
     _param_id: &TypeOrConstParamId,
     _assoc_name: &Option,
 ) -> Arc<[Binders]> {
-    // FIXME: use `Arc::from_iter` when it becomes available
-    Arc::from(vec![])
+    Arc::from_iter(None)
 }
 
 pub(crate) fn trait_environment_for_body_query(
@@ -1603,44 +1602,35 @@ pub(crate) fn generic_defaults_query(
     let generic_params = generics(db.upcast(), def);
     let parent_start_idx = generic_params.len_self();
 
-    let defaults = Arc::from(
-        generic_params
-            .iter()
-            .enumerate()
-            .map(|(idx, (id, p))| {
-                match p {
-                    TypeOrConstParamData::TypeParamData(p) => {
-                        let mut ty = p
-                            .default
-                            .as_ref()
-                            .map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
-                        // Each default can only refer to previous parameters.
-                        // Type variable default referring to parameter coming
-                        // after it is forbidden (FIXME: report diagnostic)
-                        ty = fallback_bound_vars(ty, idx, parent_start_idx);
-                        crate::make_binders(db, &generic_params, ty.cast(Interner))
-                    }
-                    TypeOrConstParamData::ConstParamData(p) => {
-                        let mut val = p.default.as_ref().map_or_else(
-                            || {
-                                unknown_const_as_generic(
-                                    db.const_param_ty(ConstParamId::from_unchecked(id)),
-                                )
-                            },
-                            |c| {
-                                let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
-                                c.cast(Interner)
-                            },
-                        );
-                        // Each default can only refer to previous parameters, see above.
-                        val = fallback_bound_vars(val, idx, parent_start_idx);
-                        make_binders(db, &generic_params, val)
-                    }
-                }
-            })
-            // FIXME: use `Arc::from_iter` when it becomes available
-            .collect::>(),
-    );
+    let defaults = Arc::from_iter(generic_params.iter().enumerate().map(|(idx, (id, p))| {
+        match p {
+            TypeOrConstParamData::TypeParamData(p) => {
+                let mut ty =
+                    p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
+                // Each default can only refer to previous parameters.
+                // Type variable default referring to parameter coming
+                // after it is forbidden (FIXME: report diagnostic)
+                ty = fallback_bound_vars(ty, idx, parent_start_idx);
+                crate::make_binders(db, &generic_params, ty.cast(Interner))
+            }
+            TypeOrConstParamData::ConstParamData(p) => {
+                let mut val = p.default.as_ref().map_or_else(
+                    || {
+                        unknown_const_as_generic(
+                            db.const_param_ty(ConstParamId::from_unchecked(id)),
+                        )
+                    },
+                    |c| {
+                        let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
+                        c.cast(Interner)
+                    },
+                );
+                // Each default can only refer to previous parameters, see above.
+                val = fallback_bound_vars(val, idx, parent_start_idx);
+                make_binders(db, &generic_params, val)
+            }
+        }
+    }));
 
     defaults
 }
@@ -1653,19 +1643,13 @@ pub(crate) fn generic_defaults_recover(
     let generic_params = generics(db.upcast(), *def);
     // FIXME: this code is not covered in tests.
     // we still need one default per parameter
-    let defaults = Arc::from(
-        generic_params
-            .iter_id()
-            .map(|id| {
-                let val = match id {
-                    Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
-                    Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
-                };
-                crate::make_binders(db, &generic_params, val)
-            })
-            // FIXME: use `Arc::from_iter` when it becomes available
-            .collect::>(),
-    );
+    let defaults = Arc::from_iter(generic_params.iter_id().map(|id| {
+        let val = match id {
+            Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
+            Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
+        };
+        crate::make_binders(db, &generic_params, val)
+    }));
 
     defaults
 }
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 732643566a2d..041d61c1b153 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -168,12 +168,9 @@ impl TraitImpls {
     ) -> Arc<[Arc]> {
         let _p = profile::span("trait_impls_in_deps_query").detail(|| format!("{krate:?}"));
         let crate_graph = db.crate_graph();
-        // FIXME: use `Arc::from_iter` when it becomes available
-        Arc::from(
-            crate_graph
-                .transitive_deps(krate)
-                .map(|krate| db.trait_impls_in_crate(krate))
-                .collect::>(),
+
+        Arc::from_iter(
+            crate_graph.transitive_deps(krate).map(|krate| db.trait_impls_in_crate(krate)),
         )
     }
 
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index c09f57252ce9..0f31fe16054a 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -187,11 +187,9 @@ impl GlobalState {
             config_errors: Default::default(),
 
             proc_macro_changed: false,
-            // FIXME: use `Arc::from_iter` when it becomes available
-            proc_macro_clients: Arc::from(Vec::new()),
+            proc_macro_clients: Arc::from_iter([]),
 
-            // FIXME: use `Arc::from_iter` when it becomes available
-            flycheck: Arc::from(Vec::new()),
+            flycheck: Arc::from_iter([]),
             flycheck_sender,
             flycheck_receiver,
             last_flycheck_error: None,
@@ -202,7 +200,7 @@ impl GlobalState {
             vfs_progress_n_total: 0,
             vfs_progress_n_done: 0,
 
-            workspaces: Arc::new(Vec::new()),
+            workspaces: Arc::from(Vec::new()),
             crate_graph_file_dependencies: FxHashSet::default(),
             fetch_workspaces_queue: OpQueue::default(),
             fetch_build_data_queue: OpQueue::default(),
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 49c88702faad..57955ebf897e 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -51,8 +51,7 @@ use crate::{
 };
 
 pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> {
-    // FIXME: use `Arc::from_iter` when it becomes available
-    state.proc_macro_clients = Arc::from(Vec::new());
+    state.proc_macro_clients = Arc::from_iter([]);
     state.proc_macro_changed = false;
 
     state.fetch_workspaces_queue.request_op("reload workspace request".to_string(), false);
@@ -60,8 +59,7 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow:
 }
 
 pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> anyhow::Result<()> {
-    // FIXME: use `Arc::from_iter` when it becomes available
-    state.proc_macro_clients = Arc::from(Vec::new());
+    state.proc_macro_clients = Arc::from_iter([]);
     state.proc_macro_changed = false;
 
     state.fetch_build_data_queue.request_op("rebuild proc macros request".to_string(), ());
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index abe2191f4002..7ab528f49751 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -437,28 +437,22 @@ impl GlobalState {
             if self.config.expand_proc_macros() {
                 tracing::info!("Spawning proc-macro servers");
 
-                // FIXME: use `Arc::from_iter` when it becomes available
-                self.proc_macro_clients = Arc::from(
-                    self.workspaces
-                        .iter()
-                        .map(|ws| {
-                            let path = match self.config.proc_macro_srv() {
-                                Some(path) => path,
-                                None => ws.find_sysroot_proc_macro_srv()?,
-                            };
-
-                            tracing::info!("Using proc-macro server at {path}");
-                            ProcMacroServer::spawn(path.clone()).map_err(|err| {
-                                tracing::error!(
-                                    "Failed to run proc-macro server from path {path}, error: {err:?}",
-                                );
-                                anyhow::format_err!(
-                                    "Failed to run proc-macro server from path {path}, error: {err:?}",
-                                )
-                            })
-                        })
-                        .collect::>(),
-                )
+                self.proc_macro_clients = Arc::from_iter(self.workspaces.iter().map(|ws| {
+                    let path = match self.config.proc_macro_srv() {
+                        Some(path) => path,
+                        None => ws.find_sysroot_proc_macro_srv()?,
+                    };
+
+                    tracing::info!("Using proc-macro server at {path}");
+                    ProcMacroServer::spawn(path.clone()).map_err(|err| {
+                        tracing::error!(
+                            "Failed to run proc-macro server from path {path}, error: {err:?}",
+                        );
+                        anyhow::format_err!(
+                            "Failed to run proc-macro server from path {path}, error: {err:?}",
+                        )
+                    })
+                }))
             };
         }
 

From f0100e3fba1a250b648fd8fd865167d4309dda31 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Wed, 6 Dec 2023 21:56:04 +0100
Subject: [PATCH 20/62] Publish lsp-server 0.7.5

---
 Cargo.lock                | 12 ++++++------
 lib/lsp-server/Cargo.toml |  2 +-
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 876ba2546a38..51db45fc9be4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -939,23 +939,23 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
 [[package]]
 name = "lsp-server"
 version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b52dccdf3302eefab8c8a1273047f0a3c3dca4b527c8458d00c09484c8371928"
 dependencies = [
  "crossbeam-channel",
- "ctrlc",
  "log",
- "lsp-types",
  "serde",
  "serde_json",
 ]
 
 [[package]]
 name = "lsp-server"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b52dccdf3302eefab8c8a1273047f0a3c3dca4b527c8458d00c09484c8371928"
+version = "0.7.5"
 dependencies = [
  "crossbeam-channel",
+ "ctrlc",
  "log",
+ "lsp-types",
  "serde",
  "serde_json",
 ]
@@ -1529,7 +1529,7 @@ dependencies = [
  "ide-ssr",
  "itertools",
  "load-cargo",
- "lsp-server 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lsp-server 0.7.4",
  "lsp-types",
  "mbe",
  "mimalloc",
diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml
index be1573913ff2..2a70aedbe8ec 100644
--- a/lib/lsp-server/Cargo.toml
+++ b/lib/lsp-server/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "lsp-server"
-version = "0.7.4"
+version = "0.7.5"
 description = "Generic LSP server scaffold."
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server"

From 037c10abf49577311515e8084fd093c5f53c68a4 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Thu, 7 Dec 2023 10:57:51 +0100
Subject: [PATCH 21/62] internal: Bump salsa

---
 Cargo.lock                                |  9 ++++-----
 Cargo.toml                                | 19 +++++++++++--------
 crates/base-db/Cargo.toml                 |  8 +++-----
 crates/hir-ty/src/consteval.rs            |  8 ++++----
 crates/hir-ty/src/layout.rs               |  3 ++-
 crates/hir-ty/src/layout/adt.rs           |  3 ++-
 crates/hir-ty/src/lower.rs                | 10 +++++-----
 crates/hir-ty/src/mir/lower.rs            |  4 ++--
 crates/hir-ty/src/mir/monomorphization.rs |  3 ++-
 9 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 51db45fc9be4..87bb788d0f26 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1569,11 +1569,10 @@ dependencies = [
 
 [[package]]
 name = "rust-analyzer-salsa"
-version = "0.17.0-pre.3"
+version = "0.17.0-pre.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ca92b657d614d076800aa7bf5d5ba33564e71fa7f16cd79eacdfe301a50ab1c"
+checksum = "16c42b8737c320578b441a82daf7cdf8d897468de64e8a774fa54b53a50b6cc0"
 dependencies = [
- "crossbeam-utils",
  "indexmap",
  "lock_api",
  "log",
@@ -1586,9 +1585,9 @@ dependencies = [
 
 [[package]]
 name = "rust-analyzer-salsa-macros"
-version = "0.17.0-pre.3"
+version = "0.17.0-pre.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b190359266d293f2ee13eaa502a766dc8b77b63fbaa5d460d24fd0210675ceef"
+checksum = "db72b0883f3592ade2be15a10583c75e0b269ec26e1190800fda2e2ce5ae6634"
 dependencies = [
  "heck",
  "proc-macro2",
diff --git a/Cargo.toml b/Cargo.toml
index 272f456bf9f8..17810d0f299d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -42,7 +42,7 @@ debug = 0
 
 # ungrammar = { path = "../ungrammar" }
 
-# salsa = { path = "../salsa" }
+# rust-analyzer-salsa = { path = "../salsa" }
 
 [workspace.dependencies]
 # local crates
@@ -98,11 +98,19 @@ either = "1.9.0"
 indexmap = "2.1.0"
 itertools = "0.12.0"
 libc = "0.2.150"
+nohash-hasher = "0.2.0"
+rayon = "1.8.0"
+rust-analyzer-salsa = "0.17.0-pre.4"
+rustc-hash = "1.1.0"
+serde = { version = "1.0.192", features = ["derive"] }
+serde_json = "1.0.108"
 smallvec = { version = "1.10.0", features = [
   "const_new",
   "union",
   "const_generics",
 ] }
+smol_str = "0.2.0"
+text-size = "1.1.1"
 tracing = "0.1.40"
 tracing-tree = "0.3.0"
 tracing-subscriber = { version = "0.3.18", default-features = false, features = [
@@ -110,15 +118,10 @@ tracing-subscriber = { version = "0.3.18", default-features = false, features =
     "fmt",
     "tracing-log",
 ] }
-smol_str = "0.2.0"
-nohash-hasher = "0.2.0"
-text-size = "1.1.1"
-rayon = "1.8.0"
-serde = { version = "1.0.192", features = ["derive"] }
-serde_json = "1.0.108"
 triomphe = { version = "0.1.10", default-features = false, features = ["std"] }
+xshell = "0.2.5"
+
 # can't upgrade due to dashmap depending on 0.12.3 currently
 hashbrown = { version = "0.12.3", features = [
   "inline-more",
 ], default-features = false }
-xshell = "0.2.5"
diff --git a/crates/base-db/Cargo.toml b/crates/base-db/Cargo.toml
index 5ad88f65188e..393ffe155ba8 100644
--- a/crates/base-db/Cargo.toml
+++ b/crates/base-db/Cargo.toml
@@ -12,12 +12,10 @@ rust-version.workspace = true
 doctest = false
 
 [dependencies]
-rust-analyzer-salsa = "0.17.0-pre.3"
-rustc-hash = "1.1.0"
-
-triomphe.workspace = true
-
 la-arena.workspace = true
+rust-analyzer-salsa.workspace = true
+rustc-hash.workspace = true
+triomphe.workspace = true
 
 # local deps
 cfg.workspace = true
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 0348680e5da1..576a07d4fb69 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -1,6 +1,6 @@
 //! Constant evaluation details
 
-use base_db::CrateId;
+use base_db::{salsa::Cycle, CrateId};
 use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
 use hir_def::{
     hir::Expr,
@@ -184,7 +184,7 @@ pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option {
 
 pub(crate) fn const_eval_recover(
     _: &dyn HirDatabase,
-    _: &[String],
+    _: &Cycle,
     _: &GeneralConstId,
     _: &Substitution,
     _: &Option>,
@@ -194,7 +194,7 @@ pub(crate) fn const_eval_recover(
 
 pub(crate) fn const_eval_static_recover(
     _: &dyn HirDatabase,
-    _: &[String],
+    _: &Cycle,
     _: &StaticId,
 ) -> Result {
     Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
@@ -202,7 +202,7 @@ pub(crate) fn const_eval_static_recover(
 
 pub(crate) fn const_eval_discriminant_recover(
     _: &dyn HirDatabase,
-    _: &[String],
+    _: &Cycle,
     _: &EnumVariantId,
 ) -> Result {
     Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index 27c794998687..bfc4f1383ec6 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -2,6 +2,7 @@
 
 use std::fmt;
 
+use base_db::salsa::Cycle;
 use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy};
 use hir_def::{
     layout::{
@@ -431,7 +432,7 @@ pub fn layout_of_ty_query(
 
 pub fn layout_of_ty_recover(
     _: &dyn HirDatabase,
-    _: &[String],
+    _: &Cycle,
     _: &Ty,
     _: &Arc,
 ) -> Result, LayoutError> {
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
index 58a06dc64354..39788a950299 100644
--- a/crates/hir-ty/src/layout/adt.rs
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -2,6 +2,7 @@
 
 use std::{cmp, ops::Bound};
 
+use base_db::salsa::Cycle;
 use hir_def::{
     data::adt::VariantData,
     layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout},
@@ -140,7 +141,7 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound,
 
 pub fn layout_of_adt_recover(
     _: &dyn HirDatabase,
-    _: &[String],
+    _: &Cycle,
     _: &AdtId,
     _: &Substitution,
     _: &Arc,
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 30ebd1f92e07..2a6d69e7fc62 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -10,7 +10,7 @@ use std::{
     iter,
 };
 
-use base_db::CrateId;
+use base_db::{salsa::Cycle, CrateId};
 use chalk_ir::{
     cast::Cast, fold::Shift, fold::TypeFoldable, interner::HasInterner, Mutability, Safety,
 };
@@ -1454,7 +1454,7 @@ pub(crate) fn generic_predicates_for_param_query(
 
 pub(crate) fn generic_predicates_for_param_recover(
     _db: &dyn HirDatabase,
-    _cycle: &[String],
+    _cycle: &Cycle,
     _def: &GenericDefId,
     _param_id: &TypeOrConstParamId,
     _assoc_name: &Option,
@@ -1637,7 +1637,7 @@ pub(crate) fn generic_defaults_query(
 
 pub(crate) fn generic_defaults_recover(
     db: &dyn HirDatabase,
-    _cycle: &[String],
+    _cycle: &Cycle,
     def: &GenericDefId,
 ) -> Arc<[Binders]> {
     let generic_params = generics(db.upcast(), *def);
@@ -1865,7 +1865,7 @@ pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders {
     }
 }
 
-pub(crate) fn ty_recover(db: &dyn HirDatabase, _cycle: &[String], def: &TyDefId) -> Binders {
+pub(crate) fn ty_recover(db: &dyn HirDatabase, _cycle: &Cycle, def: &TyDefId) -> Binders {
     let generics = match *def {
         TyDefId::BuiltinType(_) => return Binders::empty(Interner, TyKind::Error.intern(Interner)),
         TyDefId::AdtId(it) => generics(db.upcast(), it.into()),
@@ -1915,7 +1915,7 @@ pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> T
 
 pub(crate) fn impl_self_ty_recover(
     db: &dyn HirDatabase,
-    _cycle: &[String],
+    _cycle: &Cycle,
     impl_id: &ImplId,
 ) -> Binders {
     let generics = generics(db.upcast(), (*impl_id).into());
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 922aee011cf3..639fabc198c1 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -2,7 +2,7 @@
 
 use std::{fmt::Write, iter, mem};
 
-use base_db::FileId;
+use base_db::{salsa::Cycle, FileId};
 use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
 use hir_def::{
     body::Body,
@@ -2110,7 +2110,7 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result Result> {
     Err(MirLowerError::Loop)
diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs
index 7d2bb95d931c..8da03eef2e0c 100644
--- a/crates/hir-ty/src/mir/monomorphization.rs
+++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -9,6 +9,7 @@
 
 use std::mem;
 
+use base_db::salsa::Cycle;
 use chalk_ir::{
     fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
     ConstData, DebruijnIndex,
@@ -300,7 +301,7 @@ pub fn monomorphized_mir_body_query(
 
 pub fn monomorphized_mir_body_recover(
     _: &dyn HirDatabase,
-    _: &[String],
+    _: &Cycle,
     _: &DefWithBodyId,
     _: &Substitution,
     _: &Arc,

From 44abccd9e5e6246b24dcc1df57c3896cbcc3e4cd Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Thu, 7 Dec 2023 11:48:58 +0100
Subject: [PATCH 22/62] Bump and unlock some dependencies

---
 Cargo.lock                      | 67 ++++++++++++---------------------
 Cargo.toml                      |  9 +++--
 crates/hir-def/Cargo.toml       |  3 +-
 crates/intern/Cargo.toml        |  2 +-
 crates/rust-analyzer/Cargo.toml |  5 ---
 crates/stdx/Cargo.toml          |  2 +-
 6 files changed, 33 insertions(+), 55 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 87bb788d0f26..c6c1e1e3c968 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -276,7 +276,7 @@ dependencies = [
  "autocfg",
  "cfg-if",
  "crossbeam-utils",
- "memoffset 0.9.0",
+ "memoffset",
  "scopeguard",
 ]
 
@@ -301,12 +301,12 @@ dependencies = [
 
 [[package]]
 name = "dashmap"
-version = "5.4.0"
+version = "5.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
+checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
 dependencies = [
  "cfg-if",
- "hashbrown 0.12.3",
+ "hashbrown",
  "lock_api",
  "once_cell",
  "parking_lot_core",
@@ -448,15 +448,9 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
 
 [[package]]
 name = "hashbrown"
-version = "0.12.3"
+version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
-[[package]]
-name = "hashbrown"
-version = "0.14.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
 
 [[package]]
 name = "heck"
@@ -509,7 +503,7 @@ dependencies = [
  "either",
  "expect-test",
  "fst",
- "hashbrown 0.12.3",
+ "hashbrown",
  "hir-expand",
  "indexmap",
  "intern",
@@ -539,7 +533,7 @@ dependencies = [
  "cov-mark",
  "either",
  "expect-test",
- "hashbrown 0.12.3",
+ "hashbrown",
  "intern",
  "itertools",
  "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -765,7 +759,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
 dependencies = [
  "equivalent",
- "hashbrown 0.14.2",
+ "hashbrown",
 ]
 
 [[package]]
@@ -793,7 +787,7 @@ name = "intern"
 version = "0.0.0"
 dependencies = [
  "dashmap",
- "hashbrown 0.12.3",
+ "hashbrown",
  "rustc-hash",
  "triomphe",
 ]
@@ -1003,15 +997,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "memoffset"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
-dependencies = [
- "autocfg",
-]
-
 [[package]]
 name = "memoffset"
 version = "0.9.0"
@@ -1062,11 +1047,11 @@ dependencies = [
 
 [[package]]
 name = "miow"
-version = "0.5.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52ffbca2f655e33c08be35d87278e5b18b89550a37dbd598c20db92f6a471123"
+checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044"
 dependencies = [
- "windows-sys 0.42.0",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1178,15 +1163,15 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.6"
+version = "0.9.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall 0.2.16",
+ "redox_syscall 0.4.1",
  "smallvec",
- "windows-sys 0.42.0",
+ "windows-targets",
 ]
 
 [[package]]
@@ -1481,31 +1466,31 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.16"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
 dependencies = [
  "bitflags 1.3.2",
 ]
 
 [[package]]
 name = "redox_syscall"
-version = "0.3.5"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
 dependencies = [
  "bitflags 1.3.2",
 ]
 
 [[package]]
 name = "rowan"
-version = "0.15.11"
+version = "0.15.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64449cfef9483a475ed56ae30e2da5ee96448789fb2aa240a04beb6a055078bf"
+checksum = "a9672ea408d491b517a4dc370159ec6dd7cb5c5fd2f41b02883830339109ac76"
 dependencies = [
  "countme",
- "hashbrown 0.12.3",
- "memoffset 0.8.0",
+ "hashbrown",
+ "memoffset",
  "rustc-hash",
  "text-size",
 ]
@@ -1533,12 +1518,10 @@ dependencies = [
  "lsp-types",
  "mbe",
  "mimalloc",
- "mio",
  "nohash-hasher",
  "num_cpus",
  "oorandom",
  "parking_lot",
- "parking_lot_core",
  "parser",
  "proc-macro-api",
  "profile",
diff --git a/Cargo.toml b/Cargo.toml
index 17810d0f299d..f3f01aab8eee 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -95,6 +95,9 @@ bitflags = "2.4.1"
 cargo_metadata = "0.18.1"
 dissimilar = "1.0.7"
 either = "1.9.0"
+hashbrown = { version = "0.14", features = [
+  "inline-more",
+], default-features = false }
 indexmap = "2.1.0"
 itertools = "0.12.0"
 libc = "0.2.150"
@@ -121,7 +124,5 @@ tracing-subscriber = { version = "0.3.18", default-features = false, features =
 triomphe = { version = "0.1.10", default-features = false, features = ["std"] }
 xshell = "0.2.5"
 
-# can't upgrade due to dashmap depending on 0.12.3 currently
-hashbrown = { version = "0.12.3", features = [
-  "inline-more",
-], default-features = false }
+# We need to freeze the version of the crate, as the raw-api feature is considered unstable
+dashmap = { version = "=5.5.3", features = ["raw-api"] }
diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml
index e4f2e14c51c7..2d174517605f 100644
--- a/crates/hir-def/Cargo.toml
+++ b/crates/hir-def/Cargo.toml
@@ -15,8 +15,7 @@ doctest = false
 arrayvec = "0.7.2"
 bitflags.workspace = true
 cov-mark = "2.0.0-pre.1"
-# We need to freeze the version of the crate, as the raw-api feature is considered unstable
-dashmap = { version = "=5.4.0", features = ["raw-api"] }
+dashmap.workspace = true
 drop_bomb = "0.1.5"
 either.workspace = true
 fst = { version = "0.4.7", default-features = false }
diff --git a/crates/intern/Cargo.toml b/crates/intern/Cargo.toml
index 89b302c796b5..d9184b0fb6fe 100644
--- a/crates/intern/Cargo.toml
+++ b/crates/intern/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
 
 [dependencies]
 # We need to freeze the version of the crate, as the raw-api feature is considered unstable
-dashmap = { version = "=5.4.0", features = ["raw-api"] }
+dashmap.workspace = true
 hashbrown.workspace = true
 rustc-hash = "1.1.0"
 triomphe.workspace = true
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 76f764460329..408c1fb6f39b 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -43,11 +43,6 @@ triomphe.workspace = true
 nohash-hasher.workspace = true
 always-assert = "0.1.2"
 
-# These 3 deps are not used by r-a directly, but we list them here to lock in their versions
-# in our transitive deps to prevent them from pulling in windows-sys 0.45.0
-mio = "=0.8.5"
-parking_lot_core = "=0.9.6"
-
 cfg.workspace = true
 flycheck.workspace = true
 hir-def.workspace = true
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
index ea6c11ac0d56..c914ae2144b5 100644
--- a/crates/stdx/Cargo.toml
+++ b/crates/stdx/Cargo.toml
@@ -21,7 +21,7 @@ itertools.workspace = true
 # Think twice before adding anything here
 
 [target.'cfg(windows)'.dependencies]
-miow = "0.5.0"
+miow = "0.6.0"
 winapi = { version = "0.3.9", features = ["winerror"] }
 
 [features]

From dc5ea83ed5c2beec1d5e56c5f218f85066a8aef2 Mon Sep 17 00:00:00 2001
From: Young-Flash <871946895@qq.com>
Date: Tue, 28 Nov 2023 21:15:45 +0800
Subject: [PATCH 23/62] feat: add trait_impl_reduntant_assoc_item diagnostic

---
 crates/hir/src/diagnostics.rs                 |  8 +++
 crates/hir/src/lib.rs                         | 19 +++++++
 .../trait_impl_reduntant_assoc_item.rs        | 56 +++++++++++++++++++
 crates/ide-diagnostics/src/lib.rs             |  2 +
 4 files changed, 85 insertions(+)
 create mode 100644 crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs

diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index cf9a2b73d9b1..74c1b97a2e37 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -55,6 +55,7 @@ diagnostics![
     ReplaceFilterMapNextWithFindMap,
     TraitImplIncorrectSafety,
     TraitImplMissingAssocItems,
+    TraitImplReduntantAssocItems,
     TraitImplOrphan,
     TypedHole,
     TypeMismatch,
@@ -310,3 +311,10 @@ pub struct TraitImplMissingAssocItems {
     pub impl_: AstPtr,
     pub missing: Vec<(Name, AssocItem)>,
 }
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct TraitImplReduntantAssocItems {
+    pub file_id: HirFileId,
+    pub impl_: AstPtr,
+    pub reduntant: Vec<(Name, AssocItem)>,
+}
\ No newline at end of file
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 4a0c384e8a30..5e6f3c7a99fd 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -693,6 +693,25 @@ impl Module {
                     },
                 ));
 
+                let reduntant: Vec<_> = impl_assoc_items_scratch.iter()
+                .filter(|(id, name)| {
+                    !required_items.clone().any(|(impl_name, impl_item)| {
+                        discriminant(impl_item) == discriminant(id) && impl_name == name
+                    })
+                })
+                .map(|(item, name)| (name.clone(), AssocItem::from(*item)))
+                .collect();
+                if !reduntant.is_empty() {
+                    acc.push(
+                        TraitImplReduntantAssocItems {
+                            impl_: ast_id_map.get(node.ast_id()),
+                            file_id,
+                            reduntant,
+                        }
+                        .into(),
+                    )
+                }
+
                 let missing: Vec<_> = required_items
                     .filter(|(name, id)| {
                         !impl_assoc_items_scratch.iter().any(|(impl_item, impl_name)| {
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs
new file mode 100644
index 000000000000..446ce7d9fe18
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs
@@ -0,0 +1,56 @@
+use hir::InFile;
+use itertools::Itertools;
+use syntax::{ast, AstNode};
+
+use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: trait-impl-reduntant-assoc_item
+//
+// Diagnoses reduntant trait items in a trait impl.
+pub(crate) fn trait_impl_reduntant_assoc_item(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::TraitImplReduntantAssocItems,
+) -> Diagnostic {
+    let reduntant = d.reduntant.iter().format_with(", ", |(name, item), f| {
+        f(&match *item {
+            hir::AssocItem::Function(_) => "`fn ",
+            hir::AssocItem::Const(_) => "`const ",
+            hir::AssocItem::TypeAlias(_) => "`type ",
+        })?;
+        f(&name.display(ctx.sema.db))?;
+        f(&"`")
+    });
+    Diagnostic::new(
+        DiagnosticCode::RustcHardError("E0407"),
+        format!("{reduntant} is not a member of trait"),
+        adjusted_display_range::(
+            ctx,
+            InFile { file_id: d.file_id, value: d.impl_.syntax_node_ptr() },
+            &|impl_| impl_.trait_().map(|t| t.syntax().text_range()),
+        ),
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn trait_with_default_value() {
+        check_diagnostics(
+            r#"
+trait Marker {
+    fn boo();
+}
+struct Foo;
+impl Marker for Foo {
+   //^^^^^^ error: `type T`, `const FLAG`, `fn bar` is not a member of trait
+    type T = i32;
+    const FLAG: bool = false;
+    fn bar() {}
+    fn boo() {}
+}
+            "#,
+        )
+    }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 35272e872645..c39e572b42d4 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -47,6 +47,7 @@ mod handlers {
     pub(crate) mod trait_impl_orphan;
     pub(crate) mod trait_impl_incorrect_safety;
     pub(crate) mod trait_impl_missing_assoc_item;
+    pub(crate) mod trait_impl_reduntant_assoc_item;
     pub(crate) mod typed_hole;
     pub(crate) mod type_mismatch;
     pub(crate) mod unimplemented_builtin_macro;
@@ -364,6 +365,7 @@ pub fn diagnostics(
             AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
             AnyDiagnostic::TraitImplIncorrectSafety(d) => handlers::trait_impl_incorrect_safety::trait_impl_incorrect_safety(&ctx, &d),
             AnyDiagnostic::TraitImplMissingAssocItems(d) => handlers::trait_impl_missing_assoc_item::trait_impl_missing_assoc_item(&ctx, &d),
+            AnyDiagnostic::TraitImplReduntantAssocItems(d) => handlers::trait_impl_reduntant_assoc_item::trait_impl_reduntant_assoc_item(&ctx, &d),
             AnyDiagnostic::TraitImplOrphan(d) => handlers::trait_impl_orphan::trait_impl_orphan(&ctx, &d),
             AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d),
             AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),

From 288f9c6219dbd887ac3b6cc330e1234828d9a220 Mon Sep 17 00:00:00 2001
From: Young-Flash <871946895@qq.com>
Date: Fri, 1 Dec 2023 19:09:42 +0800
Subject: [PATCH 24/62] update: make each trait_impl_reduntant_assoc_item into
 individual diagnostic

---
 crates/hir/src/diagnostics.rs                 | 12 ++--
 crates/hir/src/lib.rs                         | 22 +++---
 .../trait_impl_redundant_assoc_item.rs        | 72 +++++++++++++++++++
 .../trait_impl_reduntant_assoc_item.rs        | 56 ---------------
 crates/ide-diagnostics/src/lib.rs             |  4 +-
 5 files changed, 91 insertions(+), 75 deletions(-)
 create mode 100644 crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
 delete mode 100644 crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs

diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 74c1b97a2e37..52c1c27a7fa9 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -12,7 +12,7 @@ use hir_def::path::ModPath;
 use hir_expand::{name::Name, HirFileId, InFile};
 use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange};
 
-use crate::{AssocItem, Field, Local, MacroKind, Type};
+use crate::{AssocItem, Field, Local, MacroKind, Trait, Type};
 
 macro_rules! diagnostics {
     ($($diag:ident,)*) => {
@@ -55,7 +55,7 @@ diagnostics![
     ReplaceFilterMapNextWithFindMap,
     TraitImplIncorrectSafety,
     TraitImplMissingAssocItems,
-    TraitImplReduntantAssocItems,
+    TraitImplRedundantAssocItems,
     TraitImplOrphan,
     TypedHole,
     TypeMismatch,
@@ -313,8 +313,8 @@ pub struct TraitImplMissingAssocItems {
 }
 
 #[derive(Debug, PartialEq, Eq)]
-pub struct TraitImplReduntantAssocItems {
+pub struct TraitImplRedundantAssocItems {
     pub file_id: HirFileId,
-    pub impl_: AstPtr,
-    pub reduntant: Vec<(Name, AssocItem)>,
-}
\ No newline at end of file
+    pub trait_: Trait,
+    pub assoc_item: (Name, AssocItem),
+}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 5e6f3c7a99fd..4db1a02c0cd2 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -693,20 +693,20 @@ impl Module {
                     },
                 ));
 
-                let reduntant: Vec<_> = impl_assoc_items_scratch.iter()
-                .filter(|(id, name)| {
-                    !required_items.clone().any(|(impl_name, impl_item)| {
-                        discriminant(impl_item) == discriminant(id) && impl_name == name
+                let redundant = impl_assoc_items_scratch
+                    .iter()
+                    .filter(|(id, name)| {
+                        !items.iter().any(|(impl_name, impl_item)| {
+                            discriminant(impl_item) == discriminant(id) && impl_name == name
+                        })
                     })
-                })
-                .map(|(item, name)| (name.clone(), AssocItem::from(*item)))
-                .collect();
-                if !reduntant.is_empty() {
+                    .map(|(item, name)| (name.clone(), AssocItem::from(*item)));
+                for (name, assoc_item) in redundant {
                     acc.push(
-                        TraitImplReduntantAssocItems {
-                            impl_: ast_id_map.get(node.ast_id()),
+                        TraitImplRedundantAssocItems {
+                            trait_,
                             file_id,
-                            reduntant,
+                            assoc_item: (name, assoc_item),
                         }
                         .into(),
                     )
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
new file mode 100644
index 000000000000..6aded11382cb
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -0,0 +1,72 @@
+use hir::{db::ExpandDatabase, Const, Function, HasSource, TypeAlias};
+
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: trait-impl-redundant-assoc_item
+//
+// Diagnoses redundant trait items in a trait impl.
+pub(crate) fn trait_impl_redundant_assoc_item(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::TraitImplRedundantAssocItems,
+) -> Diagnostic {
+    let name = d.assoc_item.0.clone();
+    let assoc_item = d.assoc_item.1;
+    let db = ctx.sema.db;
+
+    let range = db.parse_or_expand(d.file_id).text_range();
+    let trait_name = d.trait_.name(db).to_smol_str();
+
+    let (redundant_item_name, diagnostic_range) = match assoc_item {
+        hir::AssocItem::Function(id) => (
+            format!("`fn {}`", name.display(db)),
+            Function::from(id).source(db).map(|it| it.syntax().value.text_range()).unwrap_or(range),
+        ),
+        hir::AssocItem::Const(id) => (
+            format!("`const {}`", name.display(db)),
+            Const::from(id).source(db).map(|it| it.syntax().value.text_range()).unwrap_or(range),
+        ),
+        hir::AssocItem::TypeAlias(id) => (
+            format!("`type {}`", name.display(db)),
+            TypeAlias::from(id)
+                .source(db)
+                .map(|it| it.syntax().value.text_range())
+                .unwrap_or(range),
+        ),
+    };
+
+    Diagnostic::new(
+        DiagnosticCode::RustcHardError("E0407"),
+        format!("{redundant_item_name} is not a member of trait `{trait_name}`"),
+        diagnostic_range,
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn trait_with_default_value() {
+        check_diagnostics(
+            r#"
+trait Marker {
+    const FLAG: bool = false;
+    fn boo();
+    fn foo () {}
+}
+struct Foo;
+impl Marker for Foo {
+    type T = i32;
+  //^^^^^^^^^^^^^ error: `type T` is not a member of trait `Marker`
+
+    const FLAG: bool = true;
+
+    fn bar() {}
+  //^^^^^^^^^^^ error: `fn bar` is not a member of trait `Marker`
+
+    fn boo() {}
+}
+            "#,
+        )
+    }
+}
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs
deleted file mode 100644
index 446ce7d9fe18..000000000000
--- a/crates/ide-diagnostics/src/handlers/trait_impl_reduntant_assoc_item.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use hir::InFile;
-use itertools::Itertools;
-use syntax::{ast, AstNode};
-
-use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
-
-// Diagnostic: trait-impl-reduntant-assoc_item
-//
-// Diagnoses reduntant trait items in a trait impl.
-pub(crate) fn trait_impl_reduntant_assoc_item(
-    ctx: &DiagnosticsContext<'_>,
-    d: &hir::TraitImplReduntantAssocItems,
-) -> Diagnostic {
-    let reduntant = d.reduntant.iter().format_with(", ", |(name, item), f| {
-        f(&match *item {
-            hir::AssocItem::Function(_) => "`fn ",
-            hir::AssocItem::Const(_) => "`const ",
-            hir::AssocItem::TypeAlias(_) => "`type ",
-        })?;
-        f(&name.display(ctx.sema.db))?;
-        f(&"`")
-    });
-    Diagnostic::new(
-        DiagnosticCode::RustcHardError("E0407"),
-        format!("{reduntant} is not a member of trait"),
-        adjusted_display_range::(
-            ctx,
-            InFile { file_id: d.file_id, value: d.impl_.syntax_node_ptr() },
-            &|impl_| impl_.trait_().map(|t| t.syntax().text_range()),
-        ),
-    )
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::check_diagnostics;
-
-    #[test]
-    fn trait_with_default_value() {
-        check_diagnostics(
-            r#"
-trait Marker {
-    fn boo();
-}
-struct Foo;
-impl Marker for Foo {
-   //^^^^^^ error: `type T`, `const FLAG`, `fn bar` is not a member of trait
-    type T = i32;
-    const FLAG: bool = false;
-    fn bar() {}
-    fn boo() {}
-}
-            "#,
-        )
-    }
-}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index c39e572b42d4..6cfd5f183208 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -47,7 +47,7 @@ mod handlers {
     pub(crate) mod trait_impl_orphan;
     pub(crate) mod trait_impl_incorrect_safety;
     pub(crate) mod trait_impl_missing_assoc_item;
-    pub(crate) mod trait_impl_reduntant_assoc_item;
+    pub(crate) mod trait_impl_redundant_assoc_item;
     pub(crate) mod typed_hole;
     pub(crate) mod type_mismatch;
     pub(crate) mod unimplemented_builtin_macro;
@@ -365,7 +365,7 @@ pub fn diagnostics(
             AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
             AnyDiagnostic::TraitImplIncorrectSafety(d) => handlers::trait_impl_incorrect_safety::trait_impl_incorrect_safety(&ctx, &d),
             AnyDiagnostic::TraitImplMissingAssocItems(d) => handlers::trait_impl_missing_assoc_item::trait_impl_missing_assoc_item(&ctx, &d),
-            AnyDiagnostic::TraitImplReduntantAssocItems(d) => handlers::trait_impl_reduntant_assoc_item::trait_impl_reduntant_assoc_item(&ctx, &d),
+            AnyDiagnostic::TraitImplRedundantAssocItems(d) => handlers::trait_impl_redundant_assoc_item::trait_impl_redundant_assoc_item(&ctx, &d),
             AnyDiagnostic::TraitImplOrphan(d) => handlers::trait_impl_orphan::trait_impl_orphan(&ctx, &d),
             AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d),
             AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),

From 95f9d96155af9409fa4c518a6d915488007611ea Mon Sep 17 00:00:00 2001
From: Young-Flash <871946895@qq.com>
Date: Thu, 7 Dec 2023 20:45:42 +0800
Subject: [PATCH 25/62] fix: change default diagnostic range into impl body

---
 crates/hir/src/diagnostics.rs                 |  1 +
 crates/hir/src/lib.rs                         |  1 +
 .../trait_impl_redundant_assoc_item.rs        | 19 +++++++++++++------
 3 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 52c1c27a7fa9..1cb36f9b021f 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -316,5 +316,6 @@ pub struct TraitImplMissingAssocItems {
 pub struct TraitImplRedundantAssocItems {
     pub file_id: HirFileId,
     pub trait_: Trait,
+    pub impl_: AstPtr,
     pub assoc_item: (Name, AssocItem),
 }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 4db1a02c0cd2..5137bff055a4 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -706,6 +706,7 @@ impl Module {
                         TraitImplRedundantAssocItems {
                             trait_,
                             file_id,
+                            impl_: ast_id_map.get(node.ast_id()),
                             assoc_item: (name, assoc_item),
                         }
                         .into(),
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
index 6aded11382cb..820014391467 100644
--- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -1,4 +1,5 @@
-use hir::{db::ExpandDatabase, Const, Function, HasSource, TypeAlias};
+use hir::{Const, Function, HasSource, TypeAlias};
+use ide_db::base_db::FileRange;
 
 use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
 
@@ -13,31 +14,37 @@ pub(crate) fn trait_impl_redundant_assoc_item(
     let assoc_item = d.assoc_item.1;
     let db = ctx.sema.db;
 
-    let range = db.parse_or_expand(d.file_id).text_range();
+    let default_range = d.impl_.syntax_node_ptr().text_range();
     let trait_name = d.trait_.name(db).to_smol_str();
 
     let (redundant_item_name, diagnostic_range) = match assoc_item {
         hir::AssocItem::Function(id) => (
             format!("`fn {}`", name.display(db)),
-            Function::from(id).source(db).map(|it| it.syntax().value.text_range()).unwrap_or(range),
+            Function::from(id)
+                .source(db)
+                .map(|it| it.syntax().value.text_range())
+                .unwrap_or(default_range),
         ),
         hir::AssocItem::Const(id) => (
             format!("`const {}`", name.display(db)),
-            Const::from(id).source(db).map(|it| it.syntax().value.text_range()).unwrap_or(range),
+            Const::from(id)
+                .source(db)
+                .map(|it| it.syntax().value.text_range())
+                .unwrap_or(default_range),
         ),
         hir::AssocItem::TypeAlias(id) => (
             format!("`type {}`", name.display(db)),
             TypeAlias::from(id)
                 .source(db)
                 .map(|it| it.syntax().value.text_range())
-                .unwrap_or(range),
+                .unwrap_or(default_range),
         ),
     };
 
     Diagnostic::new(
         DiagnosticCode::RustcHardError("E0407"),
         format!("{redundant_item_name} is not a member of trait `{trait_name}`"),
-        diagnostic_range,
+        FileRange { file_id: d.file_id.file_id().unwrap(), range: diagnostic_range },
     )
 }
 

From 578af74d32a7ef9a2a3b14f5c1e7dd70c74aa138 Mon Sep 17 00:00:00 2001
From: roife 
Date: Thu, 7 Dec 2023 15:30:00 +0800
Subject: [PATCH 26/62] fix: correct calculation for fields in WideChar for
 line-specific positions

---
 lib/line-index/src/lib.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/lib/line-index/src/lib.rs b/lib/line-index/src/lib.rs
index 03371c9c87af..58f266d67f62 100644
--- a/lib/line-index/src/lib.rs
+++ b/lib/line-index/src/lib.rs
@@ -363,7 +363,10 @@ fn analyze_source_file_generic(
             let c = src[i..].chars().next().unwrap();
             char_len = c.len_utf8();
 
-            let pos = TextSize::from(i as u32) + output_offset;
+            // The last element of `lines` represents the offset of the start of
+            // current line. To get the offset inside the line, we subtract it.
+            let pos = TextSize::from(i as u32) + output_offset
+                - lines.last().unwrap_or(&TextSize::default());
 
             if char_len > 1 {
                 assert!((2..=4).contains(&char_len));

From 397b68a40947964f48349b3ce8a937478688acb7 Mon Sep 17 00:00:00 2001
From: roife 
Date: Thu, 7 Dec 2023 15:31:15 +0800
Subject: [PATCH 27/62] fix: correct existing tests for WideChar in lib
 'line-index' and add more tests

---
 lib/line-index/src/tests.rs | 28 ++++++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/lib/line-index/src/tests.rs b/lib/line-index/src/tests.rs
index 8f3762d19106..981008e346ba 100644
--- a/lib/line-index/src/tests.rs
+++ b/lib/line-index/src/tests.rs
@@ -1,4 +1,4 @@
-use crate::{LineIndex, TextSize, WideChar};
+use crate::{LineCol, LineIndex, TextSize, WideChar, WideEncoding, WideLineCol};
 
 macro_rules! test {
     (
@@ -102,7 +102,7 @@ test!(
     case: multi_byte_with_new_lines,
     text: "01\t345\n789abcΔf01234567\u{07}9\nbcΔf",
     lines: vec![7, 27],
-    multi_byte_chars: vec![(1, (13, 15)), (2, (29, 31))],
+    multi_byte_chars: vec![(1, (6, 8)), (2, (2, 4))],
 );
 
 test!(
@@ -118,3 +118,27 @@ test!(
     lines: vec![16],
     multi_byte_chars: vec![],
 );
+
+#[test]
+fn test_try_line_col() {
+    let text = "\n\n\n\n\n宽3456";
+    assert_eq!(&text[5..8], "宽");
+    assert_eq!(&text[11..12], "6");
+    let line_index = LineIndex::new(text);
+    let before_6 = TextSize::from(11);
+    let line_col = line_index.try_line_col(before_6);
+    assert_eq!(line_col, Some(LineCol { line: 5, col: 6 }));
+}
+
+#[test]
+fn test_to_wide() {
+    let text = "\n\n\n\n\n宽3456";
+    assert_eq!(&text[5..8], "宽");
+    assert_eq!(&text[11..12], "6");
+    let line_index = LineIndex::new(text);
+    let before_6 = TextSize::from(11);
+    let line_col = line_index.try_line_col(before_6);
+    assert_eq!(line_col, Some(LineCol { line: 5, col: 6 }));
+    let wide_line_col = line_index.to_wide(WideEncoding::Utf16, line_col.unwrap());
+    assert_eq!(wide_line_col, Some(WideLineCol { line: 5, col: 4 }));
+}

From 7ecc0cafb9cee871e6e23bd87c65efae4ff2e7e3 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Thu, 7 Dec 2023 17:34:40 +0000
Subject: [PATCH 28/62] feat: suggest `new` like fn first

---
 crates/ide-completion/src/completions.rs     | 23 ++++++++++++++++++++
 crates/ide-completion/src/completions/dot.rs |  2 +-
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 7d38c638a8ed..af6e28cab2cc 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -593,6 +593,28 @@ impl Completions {
         }
         self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
     }
+
+    /// Sort the suggestions with `new` like functions first.
+    /// That means:
+    /// fn with no param that returns itself
+    /// fn with param that returns itself
+    pub(crate) fn sort_new_first(&mut self) {
+        fn creates_self(item: &CompletionItem) -> Option {
+            item.detail.as_ref().filter(|d| d.starts_with("fn() -> ")).map(|_| false)
+        }
+        fn creates_self_given_args(item: &CompletionItem) -> Option {
+            item.detail
+                .as_ref()
+                .filter(|d| d.starts_with("fn(") && d.contains("->") && !d.contains("&self"))
+                .map(|_| false)
+        }
+
+        self.buf.sort_by(|a, b| {
+            creates_self(b)
+                .cmp(&creates_self(a))
+                .then(creates_self_given_args(b).cmp(&creates_self_given_args(a)))
+        });
+    }
 }
 
 /// Calls the callback for each variant of the provided enum with the path to the variant.
@@ -694,6 +716,7 @@ pub(super) fn complete_name_ref(
                     dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx);
                     item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx);
                     snippet::complete_expr_snippet(acc, ctx, path_ctx, expr_ctx);
+                    acc.sort_new_first();
                 }
                 PathKind::Type { location } => {
                     r#type::complete_type_path(acc, ctx, path_ctx, location);
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 89726acf9532..4c40909621d6 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -272,7 +272,7 @@ fn foo(a: A) { a.$0() }
     }
 
     #[test]
-    fn test_usable_types_first() {
+    fn test_suggest_new_first() {
         check_exact_order(
             r#"
 struct A;

From d271682abe6c231f033de33615a40db2f21cf41b Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Thu, 7 Dec 2023 17:37:02 +0000
Subject: [PATCH 29/62] todo

---
 crates/ide-completion/src/completions.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index af6e28cab2cc..417c1927c979 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -599,6 +599,7 @@ impl Completions {
     /// fn with no param that returns itself
     /// fn with param that returns itself
     pub(crate) fn sort_new_first(&mut self) {
+        // ToDo: Ensure these fn returns Self
         fn creates_self(item: &CompletionItem) -> Option {
             item.detail.as_ref().filter(|d| d.starts_with("fn() -> ")).map(|_| false)
         }

From 544245454166c57efb5119a2b8fd12f546179a17 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Thu, 7 Dec 2023 21:34:37 +0000
Subject: [PATCH 30/62] fix: test

---
 crates/ide-completion/src/completions.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 417c1927c979..5ec42018dfdd 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -717,7 +717,10 @@ pub(super) fn complete_name_ref(
                     dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx);
                     item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx);
                     snippet::complete_expr_snippet(acc, ctx, path_ctx, expr_ctx);
-                    acc.sort_new_first();
+
+                    if matches!(ctx.token.kind(), syntax::SyntaxKind::COLON2) {
+                        acc.sort_new_first();
+                    }
                 }
                 PathKind::Type { location } => {
                     r#type::complete_type_path(acc, ctx, path_ctx, location);

From 1c8b95cb1b5e2b75d91c10826a0b545ed1333fd8 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Fri, 8 Dec 2023 00:31:18 +0000
Subject: [PATCH 31/62] save

---
 Cargo.toml                                   | 2 +-
 crates/ide-completion/src/completions.rs     | 8 ++++++++
 crates/ide-completion/src/completions/dot.rs | 4 ++--
 crates/ide-completion/src/item.rs            | 3 +++
 crates/rust-analyzer/src/lsp/to_proto.rs     | 3 ++-
 5 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index f3f01aab8eee..d17fde0c1563 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@ authors = ["rust-analyzer team"]
 [profile.dev]
 # Disabling debug info speeds up builds a bunch,
 # and we don't rely on it for debugging that much.
-debug = 0
+debug = 2
 
 [profile.dev.package]
 # These speed up local tests.
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 5ec42018dfdd..af53c525c16d 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -610,6 +610,14 @@ impl Completions {
                 .map(|_| false)
         }
 
+        for item in self.buf.iter_mut() {
+            if creates_self(&item) == Some(true) {
+                item.sort_text = Some(format!("{0:08x}", 0));
+            } else if creates_self_given_args(&item) == Some(true) {
+                item.sort_text = Some(format!("{0:08x}", 1));
+            }
+        }
+
         self.buf.sort_by(|a, b| {
             creates_self(b)
                 .cmp(&creates_self(a))
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 4c40909621d6..bb30f5f6c51f 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -278,8 +278,8 @@ fn foo(a: A) { a.$0() }
 struct A;
 impl A {
     fn foo(&self) {}
-    fn new_1(input: u32) -> Self { A }
-    fn new_2() -> A { A }
+    fn new_1(input: u32) -> A { A }
+    fn new_2() -> Self { A }
 }
 
 fn test() {
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 99b895eed4d2..c18eb69620dd 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -85,6 +85,8 @@ pub struct CompletionItem {
     /// The import data to add to completion's edits.
     /// (ImportPath, LastSegment)
     pub import_to_add: SmallVec<[(String, String); 1]>,
+
+    pub sort_text: Option,
 }
 
 // We use custom debug for CompletionItem to make snapshot tests more readable.
@@ -503,6 +505,7 @@ impl Builder {
             relevance: self.relevance,
             ref_match: self.ref_match,
             import_to_add,
+            sort_text: None,
         }
     }
     pub(crate) fn lookup_by(&mut self, lookup: impl Into) -> &mut Builder {
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index dae560c5de12..0190dd4d7f72 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -308,7 +308,7 @@ fn completion_item(
         lsp_item.label.push_str(label_detail.as_str());
     }
 
-    set_score(&mut lsp_item, max_relevance, item.relevance);
+    set_score(&mut lsp_item, max_relevance, item.relevance); // TODO: copy sort_text from CompletionItem if present (+1 instance below)
 
     if config.completion().enable_imports_on_the_fly {
         if !item.import_to_add.is_empty() {
@@ -349,6 +349,7 @@ fn completion_item(
         if relevance.is_relevant() && relevance.score() == max_relevance {
             res.preselect = Some(true);
         }
+
         // The relevance needs to be inverted to come up with a sort score
         // because the client will sort ascending.
         let sort_score = relevance.score() ^ 0xFF_FF_FF_FF;

From 7b73f60937dcc324a9ee4b746899b714918b6bf4 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Fri, 8 Dec 2023 21:41:15 +0000
Subject: [PATCH 32/62] ops new in this codebase!

---
 crates/ide-completion/src/completions.rs     | 36 +++++++++++---------
 crates/ide-completion/src/completions/dot.rs | 34 +-----------------
 crates/ide-completion/src/item.rs            |  3 --
 crates/ide-completion/src/render.rs          | 26 ++++++++++++++
 crates/ide-completion/src/tests.rs           |  5 ---
 crates/rust-analyzer/src/lsp/to_proto.rs     |  2 +-
 6 files changed, 48 insertions(+), 58 deletions(-)

diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index af53c525c16d..285ef2f07ea2 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -45,7 +45,7 @@ use crate::{
         union_literal::render_union_literal,
         RenderContext,
     },
-    CompletionContext, CompletionItem, CompletionItemKind,
+    CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
 };
 
 /// Represents an in-progress set of completions being built.
@@ -600,29 +600,33 @@ impl Completions {
     /// fn with param that returns itself
     pub(crate) fn sort_new_first(&mut self) {
         // ToDo: Ensure these fn returns Self
-        fn creates_self(item: &CompletionItem) -> Option {
-            item.detail.as_ref().filter(|d| d.starts_with("fn() -> ")).map(|_| false)
+        fn creates_self(item: &CompletionItem) -> bool {
+            item.detail.as_ref().map(|d| d.starts_with("fn() -> ")).unwrap_or_default()
         }
-        fn creates_self_given_args(item: &CompletionItem) -> Option {
+        fn creates_self_given_args(item: &CompletionItem) -> bool {
             item.detail
                 .as_ref()
-                .filter(|d| d.starts_with("fn(") && d.contains("->") && !d.contains("&self"))
-                .map(|_| false)
+                .map(|d| d.starts_with("fn(") && d.contains("->") && !d.contains("&self"))
+                .unwrap_or_default()
         }
 
         for item in self.buf.iter_mut() {
-            if creates_self(&item) == Some(true) {
-                item.sort_text = Some(format!("{0:08x}", 0));
-            } else if creates_self_given_args(&item) == Some(true) {
-                item.sort_text = Some(format!("{0:08x}", 1));
+            if creates_self(&item) {
+                //item.sort_text = Some(format!("{0:08x}", 0));
+                item.relevance = CompletionRelevance {
+                    exact_name_match: true,
+                    is_definite: true,
+                    ..Default::default()
+                };
+            } else if creates_self_given_args(&item) {
+                //item.sort_text = Some(format!("{0:08x}", 1));
+                item.relevance = CompletionRelevance {
+                    exact_name_match: true,
+                    is_local: true,
+                    ..Default::default()
+                };
             }
         }
-
-        self.buf.sort_by(|a, b| {
-            creates_self(b)
-                .cmp(&creates_self(a))
-                .then(creates_self_given_args(b).cmp(&creates_self_given_args(a)))
-        });
     }
 }
 
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index bb30f5f6c51f..57e06461099e 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -155,8 +155,7 @@ mod tests {
     use expect_test::{expect, Expect};
 
     use crate::tests::{
-        check_edit, completion_list_exact_order, completion_list_no_kw,
-        completion_list_no_kw_with_private_editable,
+        check_edit, completion_list_no_kw, completion_list_no_kw_with_private_editable,
     };
 
     fn check(ra_fixture: &str, expect: Expect) {
@@ -164,11 +163,6 @@ mod tests {
         expect.assert_eq(&actual);
     }
 
-    fn check_exact_order(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list_exact_order(ra_fixture);
-        expect.assert_eq(&actual);
-    }
-
     fn check_with_private_editable(ra_fixture: &str, expect: Expect) {
         let actual = completion_list_no_kw_with_private_editable(ra_fixture);
         expect.assert_eq(&actual);
@@ -271,32 +265,6 @@ fn foo(a: A) { a.$0() }
         );
     }
 
-    #[test]
-    fn test_suggest_new_first() {
-        check_exact_order(
-            r#"
-struct A;
-impl A {
-    fn foo(&self) {}
-    fn new_1(input: u32) -> A { A }
-    fn new_2() -> Self { A }
-}
-
-fn test() {
-    let a = A::$0;
-}
-"#,
-            // preference:
-            // fn with no param that returns itself
-            // fn with param that returns itself
-            expect![[r#"
-                fn new_2()  fn() -> A
-                fn new_1(…) fn(u32) -> A
-                me foo(…)   fn(&self)
-            "#]],
-        );
-    }
-
     #[test]
     fn test_visibility_filtering() {
         check(
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index c18eb69620dd..99b895eed4d2 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -85,8 +85,6 @@ pub struct CompletionItem {
     /// The import data to add to completion's edits.
     /// (ImportPath, LastSegment)
     pub import_to_add: SmallVec<[(String, String); 1]>,
-
-    pub sort_text: Option,
 }
 
 // We use custom debug for CompletionItem to make snapshot tests more readable.
@@ -505,7 +503,6 @@ impl Builder {
             relevance: self.relevance,
             ref_match: self.ref_match,
             import_to_add,
-            sort_text: None,
         }
     }
     pub(crate) fn lookup_by(&mut self, lookup: impl Into) -> &mut Builder {
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 830d7cabab58..4a9bb02a7f08 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -1632,6 +1632,32 @@ fn main() {
         );
     }
 
+    #[test]
+    fn new_like_fns() {
+        check_relevance(
+            r#"
+struct A;
+impl A {
+    fn foo(&self) {}
+    fn new_1(input: u32) -> A { A }
+    fn new_2() -> Self { A }
+}
+
+fn test() {
+    let a = A::$0;
+}
+"#,
+            // preference:
+            // fn with no param that returns itself
+            // fn with param that returns itself
+            expect![[r#"
+                fn new_2() [name]
+                fn new_1(…) [name+local]
+                me foo(…) [type_could_unify]
+            "#]],
+        );
+    }
+
     #[test]
     fn struct_field_method_ref() {
         check_kinds(
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index b03439eef732..f28afacc586f 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -88,11 +88,6 @@ pub(crate) fn completion_list_no_kw(ra_fixture: &str) -> String {
     completion_list_with_config(TEST_CONFIG, ra_fixture, false, None)
 }
 
-pub(crate) fn completion_list_exact_order(ra_fixture: &str) -> String {
-    let items = get_all_items(TEST_CONFIG, ra_fixture, None);
-    render_completion_list(items)
-}
-
 pub(crate) fn completion_list_no_kw_with_private_editable(ra_fixture: &str) -> String {
     let mut config = TEST_CONFIG;
     config.enable_private_editable = true;
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index 0190dd4d7f72..cd8befcc0868 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -308,7 +308,7 @@ fn completion_item(
         lsp_item.label.push_str(label_detail.as_str());
     }
 
-    set_score(&mut lsp_item, max_relevance, item.relevance); // TODO: copy sort_text from CompletionItem if present (+1 instance below)
+    set_score(&mut lsp_item, max_relevance, item.relevance);
 
     if config.completion().enable_imports_on_the_fly {
         if !item.import_to_add.is_empty() {

From eb7d6b75dc386120bf7236474e3b6a55c073f206 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Fri, 8 Dec 2023 22:10:50 +0000
Subject: [PATCH 33/62] builder methods

---
 crates/ide-completion/src/completions.rs | 33 ++++++++++++------------
 crates/ide-completion/src/item.rs        | 11 +++++++-
 crates/ide-completion/src/render.rs      |  8 ++++--
 3 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 285ef2f07ea2..febc788c40a5 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -45,7 +45,7 @@ use crate::{
         union_literal::render_union_literal,
         RenderContext,
     },
-    CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
+    CompletionContext, CompletionItem, CompletionItemKind,
 };
 
 /// Represents an in-progress set of completions being built.
@@ -600,31 +600,30 @@ impl Completions {
     /// fn with param that returns itself
     pub(crate) fn sort_new_first(&mut self) {
         // ToDo: Ensure these fn returns Self
-        fn creates_self(item: &CompletionItem) -> bool {
+        fn maybe_new(item: &CompletionItem) -> bool {
             item.detail.as_ref().map(|d| d.starts_with("fn() -> ")).unwrap_or_default()
         }
-        fn creates_self_given_args(item: &CompletionItem) -> bool {
+        fn maybe_new_with_args(item: &CompletionItem) -> bool {
             item.detail
                 .as_ref()
                 .map(|d| d.starts_with("fn(") && d.contains("->") && !d.contains("&self"))
                 .unwrap_or_default()
         }
 
+        fn maybe_builder(item: &CompletionItem) -> bool {
+            item.detail
+                .as_ref()
+                .map(|d| d.starts_with("fn() -> ") && d.contains("Builder"))
+                .unwrap_or_default()
+        }
+
         for item in self.buf.iter_mut() {
-            if creates_self(&item) {
-                //item.sort_text = Some(format!("{0:08x}", 0));
-                item.relevance = CompletionRelevance {
-                    exact_name_match: true,
-                    is_definite: true,
-                    ..Default::default()
-                };
-            } else if creates_self_given_args(&item) {
-                //item.sort_text = Some(format!("{0:08x}", 1));
-                item.relevance = CompletionRelevance {
-                    exact_name_match: true,
-                    is_local: true,
-                    ..Default::default()
-                };
+            if maybe_new(&item) {
+                item.bump_relevance_by(30);
+            } else if maybe_builder(&item) {
+                item.bump_relevance_by(20);
+            } else if maybe_new_with_args(&item) {
+                item.bump_relevance_by(10);
             }
         }
     }
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 99b895eed4d2..fdb2c97a8f28 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -164,6 +164,9 @@ pub struct CompletionRelevance {
     pub postfix_match: Option,
     /// This is set for type inference results
     pub is_definite: bool,
+    /// Any other bonuses we want to add,
+    /// eg. bonus for good behavior!
+    pub bonus_score: u32,
 }
 
 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
@@ -228,6 +231,7 @@ impl CompletionRelevance {
             is_private_editable,
             postfix_match,
             is_definite,
+            bonus_score,
         } = self;
 
         // lower rank private things
@@ -269,7 +273,8 @@ impl CompletionRelevance {
         if is_definite {
             score += 10;
         }
-        score
+
+        score + bonus_score
     }
 
     /// Returns true when the score for this threshold is above
@@ -386,6 +391,10 @@ impl CompletionItem {
             )
         })
     }
+
+    pub fn bump_relevance_by(&mut self, bonus: u32) {
+        self.relevance.bonus_score += bonus;
+    }
 }
 
 /// A helper to make `CompletionItem`s.
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 4a9bb02a7f08..bf2a21fc6037 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -1637,10 +1637,12 @@ fn main() {
         check_relevance(
             r#"
 struct A;
+struct ABuilder;
 impl A {
     fn foo(&self) {}
     fn new_1(input: u32) -> A { A }
     fn new_2() -> Self { A }
+    fn aaaabuilder() -> ABuilder { A }
 }
 
 fn test() {
@@ -1649,10 +1651,12 @@ fn test() {
 "#,
             // preference:
             // fn with no param that returns itself
+            // builder like fn
             // fn with param that returns itself
             expect![[r#"
-                fn new_2() [name]
-                fn new_1(…) [name+local]
+                fn new_2() [type_could_unify]
+                fn aaaabuilder() [type_could_unify]
+                fn new_1(…) [type_could_unify]
                 me foo(…) [type_could_unify]
             "#]],
         );

From c754f9f68cd976b9894dfa5c634c22cc4551017a Mon Sep 17 00:00:00 2001
From: werifu 
Date: Mon, 4 Dec 2023 16:23:18 +0800
Subject: [PATCH 34/62] fix: bug in extract_function: should not import
 ControlFlow in some cases

---
 .../src/handlers/extract_function.rs          | 28 ++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index d4a12307790b..347a3e9ba074 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -147,7 +147,12 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
                 _ => format_function(ctx, module, &fun, old_indent, new_indent),
             };
 
-            if fn_def.contains("ControlFlow") {
+            // There are external control flows
+            if fun
+                .control_flow
+                .kind
+                .is_some_and(|kind| matches!(kind, FlowKind::Break(_, _) | FlowKind::Continue(_)))
+            {
                 let scope = match scope {
                     ImportScope::File(it) => ImportScope::File(builder.make_mut(it)),
                     ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
@@ -4968,6 +4973,27 @@ pub fn testfn(arg: &mut Foo) {
 fn $0fun_name(arg: &mut Foo) {
     arg.field = 8;
 }
+"#,
+        );
+    }
+    #[test]
+    fn does_not_import_control_flow() {
+        check_assist(
+            extract_function,
+            r#"
+//- minicore: try
+fn func() {
+    $0let cf = "I'm ControlFlow";$0
+}
+"#,
+            r#"
+fn func() {
+    fun_name();
+}
+
+fn $0fun_name() {
+    let cf = "I'm ControlFlow";
+}
 "#,
         );
     }

From 16d8c3fd0321c12cff46af11c6cbc5310ad14e16 Mon Sep 17 00:00:00 2001
From: hkalbasi 
Date: Fri, 8 Dec 2023 00:44:45 +0330
Subject: [PATCH 35/62] Fix panic with closure inside array len

---
 crates/hir-ty/src/consteval.rs        | 18 +++++++++++++++---
 crates/hir-ty/src/tests/regression.rs | 12 ++++++++++++
 2 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 576a07d4fb69..ddeb9f14b5dc 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -3,7 +3,8 @@
 use base_db::{salsa::Cycle, CrateId};
 use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
 use hir_def::{
-    hir::Expr,
+    body::Body,
+    hir::{Expr, ExprId},
     path::Path,
     resolver::{Resolver, ValueNs},
     type_ref::LiteralConstRef,
@@ -280,7 +281,7 @@ pub(crate) fn const_eval_discriminant_variant(
 // get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here
 // and make this function private. See the fixme comment on `InferenceContext::resolve_all`.
 pub(crate) fn eval_to_const(
-    expr: Idx,
+    expr: ExprId,
     mode: ParamLoweringMode,
     ctx: &mut InferenceContext<'_>,
     args: impl FnOnce() -> Generics,
@@ -288,13 +289,24 @@ pub(crate) fn eval_to_const(
 ) -> Const {
     let db = ctx.db;
     let infer = ctx.clone().resolve_all();
+    fn has_closure(body: &Body, expr: ExprId) -> bool {
+        if matches!(body[expr], Expr::Closure { .. }) {
+            return true;
+        }
+        let mut r = false;
+        body[expr].walk_child_exprs(|idx| r |= has_closure(body, idx));
+        r
+    }
+    if has_closure(&ctx.body, expr) {
+        // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic.
+        return unknown_const(infer[expr].clone());
+    }
     if let Expr::Path(p) = &ctx.body.exprs[expr] {
         let resolver = &ctx.resolver;
         if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) {
             return c;
         }
     }
-    let infer = ctx.clone().resolve_all();
     if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
         if let Ok(result) = interpret_mir(db, Arc::new(mir_body), true, None).0 {
             return result;
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 6ea059065e93..35079e70946d 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -2000,3 +2000,15 @@ fn test() {
 "#,
     );
 }
+
+#[test]
+fn rustc_test_issue_52437() {
+    check_types(
+        r#"
+    fn main() {
+        let x = [(); &(&'static: loop { |x| {}; }) as *const _ as usize]
+          //^ [(); _]
+    }
+    "#,
+    );
+}

From daaf0b3d8ee068a3aa44ef375574c783ab60ee64 Mon Sep 17 00:00:00 2001
From: Young-Flash <871946895@qq.com>
Date: Tue, 5 Dec 2023 22:56:51 +0800
Subject: [PATCH 36/62] fix: make drop inlay hint more readable

---
 crates/ide/src/inlay_hints/implicit_drop.rs | 22 +++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs
index 60f1f3496f6c..9cbaed090dc7 100644
--- a/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -62,7 +62,11 @@ pub(super) fn hints(
                         match_ast! {
                             match expr {
                                 ast::BlockExpr(x) => x.stmt_list().and_then(|x| x.r_curly_token()).map(|x| x.text_range()).unwrap_or_else(|| expr.text_range()),
-                                _ => expr.text_range(),
+                                // make the inlay hint appear after the semicolon if there is
+                                _ => {
+                                    let nearest_semicolon = nearest_token_after_node(expr, syntax::SyntaxKind::SEMICOLON);
+                                    nearest_semicolon.map(|x| x.text_range()).unwrap_or_else(|| expr.text_range())
+                                },
                             }
                         }
                     }
@@ -95,7 +99,7 @@ pub(super) fn hints(
             label.append_str(")");
             acc.push(InlayHint {
                 range,
-                position: InlayHintPosition::Before,
+                position: InlayHintPosition::After,
                 pad_left: true,
                 pad_right: true,
                 kind: InlayKind::Drop,
@@ -109,6 +113,16 @@ pub(super) fn hints(
     Some(())
 }
 
+fn nearest_token_after_node(
+    node: &syntax::SyntaxNode,
+    token_type: syntax::SyntaxKind,
+) -> Option {
+    node.siblings_with_tokens(syntax::Direction::Next)
+        .filter_map(|it| it.as_token().map(|it| it.clone()))
+        .filter(|it| it.kind() == token_type)
+        .next()
+}
+
 #[cfg(test)]
 mod tests {
     use crate::{
@@ -129,7 +143,7 @@ mod tests {
         let x = X;
         if 2 == 5 {
             return;
-          //^^^^^^ drop(x)
+                //^ drop(x)
         }
     }
   //^ drop(x)
@@ -176,7 +190,7 @@ mod tests {
         let x = X;
         let t_opt = Some(2);
         let t = t_opt?;
-              //^^^^^^ drop(x)
+                    //^ drop(x)
         Some(())
     }
   //^ drop(x)

From c818c3952ef4496c2b64824a6cef0efeb58d772e Mon Sep 17 00:00:00 2001
From: Mine Starks 
Date: Tue, 14 Nov 2023 14:39:17 -0800
Subject: [PATCH 37/62] Show placeholder while run command gets runnables from
 server

---
 editors/code/src/run.ts | 151 +++++++++++++++++++++++++---------------
 1 file changed, 94 insertions(+), 57 deletions(-)

diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index 57881803a6a0..778cbc5762ae 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -7,6 +7,8 @@ import type { CtxInit } from "./ctx";
 import { makeDebugConfig } from "./debug";
 import type { Config, RunnableEnvCfg, RunnableEnvCfgItem } from "./config";
 import { unwrapUndefinable } from "./undefinable";
+import type { LanguageClient } from "vscode-languageclient/node";
+import type { RustEditor } from "./util";
 
 const quickPickButtons = [
     { iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." },
@@ -21,73 +23,36 @@ export async function selectRunnable(
     const editor = ctx.activeRustEditor;
     if (!editor) return;
 
-    const client = ctx.client;
-    const textDocument: lc.TextDocumentIdentifier = {
-        uri: editor.document.uri.toString(),
-    };
-
-    const runnables = await client.sendRequest(ra.runnables, {
-        textDocument,
-        position: client.code2ProtocolConverter.asPosition(editor.selection.active),
-    });
-    const items: RunnableQuickPick[] = [];
-    if (prevRunnable) {
-        items.push(prevRunnable);
+    // show a placeholder while we get the runnables from the server
+    const quickPick = vscode.window.createQuickPick();
+    quickPick.title = "Select Runnable";
+    if (showButtons) {
+        quickPick.buttons = quickPickButtons;
     }
-    for (const r of runnables) {
-        if (prevRunnable && JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)) {
-            continue;
-        }
+    quickPick.items = [{ label: "Looking for runnables..." }];
+    quickPick.activeItems = [];
+    quickPick.show();
 
-        if (debuggeeOnly && (r.label.startsWith("doctest") || r.label.startsWith("cargo"))) {
-            continue;
-        }
-        items.push(new RunnableQuickPick(r));
-    }
+    const runnables = await getRunnables(ctx.client, editor, prevRunnable, debuggeeOnly);
 
-    if (items.length === 0) {
+    if (runnables.length === 0) {
         // it is the debug case, run always has at least 'cargo check ...'
         // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables
         await vscode.window.showErrorMessage("There's no debug target!");
+        quickPick.dispose();
         return;
     }
 
-    return await new Promise((resolve) => {
-        const disposables: vscode.Disposable[] = [];
-        const close = (result?: RunnableQuickPick) => {
-            resolve(result);
-            disposables.forEach((d) => d.dispose());
-        };
+    // clear the list before we hook up listeners to to avoid invoking them
+    // if the user happens to accept the placeholder item
+    quickPick.items = [];
 
-        const quickPick = vscode.window.createQuickPick();
-        quickPick.items = items;
-        quickPick.title = "Select Runnable";
-        if (showButtons) {
-            quickPick.buttons = quickPickButtons;
-        }
-        disposables.push(
-            quickPick.onDidHide(() => close()),
-            quickPick.onDidAccept(() => close(quickPick.selectedItems[0])),
-            quickPick.onDidTriggerButton(async (_button) => {
-                const runnable = unwrapUndefinable(quickPick.activeItems[0]).runnable;
-                await makeDebugConfig(ctx, runnable);
-                close();
-            }),
-            quickPick.onDidChangeActive((activeList) => {
-                if (showButtons && activeList.length > 0) {
-                    const active = unwrapUndefinable(activeList[0]);
-                    if (active.label.startsWith("cargo")) {
-                        // save button makes no sense for `cargo test` or `cargo check`
-                        quickPick.buttons = [];
-                    } else if (quickPick.buttons.length === 0) {
-                        quickPick.buttons = quickPickButtons;
-                    }
-                }
-            }),
-            quickPick,
-        );
-        quickPick.show();
-    });
+    return await populateAndGetSelection(
+        quickPick as vscode.QuickPick,
+        runnables,
+        ctx,
+        showButtons,
+    );
 }
 
 export class RunnableQuickPick implements vscode.QuickPickItem {
@@ -187,3 +152,75 @@ export function createArgs(runnable: ra.Runnable): string[] {
     }
     return args;
 }
+
+async function getRunnables(
+    client: LanguageClient,
+    editor: RustEditor,
+    prevRunnable?: RunnableQuickPick,
+    debuggeeOnly = false,
+): Promise {
+    const textDocument: lc.TextDocumentIdentifier = {
+        uri: editor.document.uri.toString(),
+    };
+
+    const runnables = await client.sendRequest(ra.runnables, {
+        textDocument,
+        position: client.code2ProtocolConverter.asPosition(editor.selection.active),
+    });
+    const items: RunnableQuickPick[] = [];
+    if (prevRunnable) {
+        items.push(prevRunnable);
+    }
+    for (const r of runnables) {
+        if (prevRunnable && JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)) {
+            continue;
+        }
+
+        if (debuggeeOnly && (r.label.startsWith("doctest") || r.label.startsWith("cargo"))) {
+            continue;
+        }
+        items.push(new RunnableQuickPick(r));
+    }
+
+    return items;
+}
+
+async function populateAndGetSelection(
+    quickPick: vscode.QuickPick,
+    runnables: RunnableQuickPick[],
+    ctx: CtxInit,
+    showButtons: boolean,
+): Promise {
+    return new Promise((resolve) => {
+        const disposables: vscode.Disposable[] = [];
+        const close = (result?: RunnableQuickPick) => {
+            resolve(result);
+            disposables.forEach((d) => d.dispose());
+        };
+        disposables.push(
+            quickPick.onDidHide(() => close()),
+            quickPick.onDidAccept(() => close(quickPick.selectedItems[0] as RunnableQuickPick)),
+            quickPick.onDidTriggerButton(async (_button) => {
+                const runnable = unwrapUndefinable(
+                    quickPick.activeItems[0] as RunnableQuickPick,
+                ).runnable;
+                await makeDebugConfig(ctx, runnable);
+                close();
+            }),
+            quickPick.onDidChangeActive((activeList) => {
+                if (showButtons && activeList.length > 0) {
+                    const active = unwrapUndefinable(activeList[0]);
+                    if (active.label.startsWith("cargo")) {
+                        // save button makes no sense for `cargo test` or `cargo check`
+                        quickPick.buttons = [];
+                    } else if (quickPick.buttons.length === 0) {
+                        quickPick.buttons = quickPickButtons;
+                    }
+                }
+            }),
+            quickPick,
+        );
+        // populate the list with the actual runnables
+        quickPick.items = runnables;
+    });
+}

From 476f711354c0f4d17ff3bf995f6cd1db50ad162c Mon Sep 17 00:00:00 2001
From: Matheus Cardoso 
Date: Fri, 25 Aug 2023 15:47:27 -0300
Subject: [PATCH 38/62] Flip binexpr works for lhs binexpr

---
 .../ide-assists/src/handlers/flip_binexpr.rs   | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/crates/ide-assists/src/handlers/flip_binexpr.rs b/crates/ide-assists/src/handlers/flip_binexpr.rs
index 2ea6f58fa0f1..4b1e4165965c 100644
--- a/crates/ide-assists/src/handlers/flip_binexpr.rs
+++ b/crates/ide-assists/src/handlers/flip_binexpr.rs
@@ -33,6 +33,15 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
         return None;
     }
 
+    // If the lhs is a binary expression we check if its rhs can be used as the lhs of the current expression
+    let lhs = match BinExpr::cast(lhs.clone()) {
+        Some(lhs) => match lhs.rhs() {
+            Some(lhs) => lhs,
+            None => lhs,
+        },
+        None => lhs,
+    };
+
     acc.add(
         AssistId("flip_binexpr", AssistKind::RefactorRewrite),
         "Flip binary expression",
@@ -114,6 +123,15 @@ mod tests {
         )
     }
 
+    #[test]
+    fn flip_binexpr_works_for_lhs_binexpr() {
+        check_assist(
+            flip_binexpr,
+            r"fn f() { let res = 1 + (2 - 3) +$0 4 + 5; }",
+            r"fn f() { let res = 1 + 4 + (2 - 3) + 5; }",
+        )
+    }
+
     #[test]
     fn flip_binexpr_works_inside_match() {
         check_assist(

From d593e4ba378321fae090afe9f09a9e890ce8d730 Mon Sep 17 00:00:00 2001
From: Matheus Cardoso 
Date: Fri, 25 Aug 2023 20:14:35 -0300
Subject: [PATCH 39/62] flip binexpr works for lhs cmp

---
 .../ide-assists/src/handlers/flip_binexpr.rs  | 33 ++++++++++++-------
 1 file changed, 22 insertions(+), 11 deletions(-)

diff --git a/crates/ide-assists/src/handlers/flip_binexpr.rs b/crates/ide-assists/src/handlers/flip_binexpr.rs
index 4b1e4165965c..8b46a23f9a64 100644
--- a/crates/ide-assists/src/handlers/flip_binexpr.rs
+++ b/crates/ide-assists/src/handlers/flip_binexpr.rs
@@ -19,8 +19,19 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 // ```
 pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let expr = ctx.find_node_at_offset::()?;
-    let lhs = expr.lhs()?.syntax().clone();
     let rhs = expr.rhs()?.syntax().clone();
+    let lhs = expr.lhs()?.syntax().clone();
+
+    let lhs = if let Some(bin_expr) = BinExpr::cast(lhs.clone()) {
+        if bin_expr.op_kind() == expr.op_kind() {
+            bin_expr.rhs()?.syntax().clone()
+        } else {
+            lhs
+        }
+    } else {
+        lhs
+    };
+
     let op_range = expr.op_token()?.text_range();
     // The assist should be applied only if the cursor is on the operator
     let cursor_in_range = op_range.contains_range(ctx.selection_trimmed());
@@ -33,15 +44,6 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
         return None;
     }
 
-    // If the lhs is a binary expression we check if its rhs can be used as the lhs of the current expression
-    let lhs = match BinExpr::cast(lhs.clone()) {
-        Some(lhs) => match lhs.rhs() {
-            Some(lhs) => lhs,
-            None => lhs,
-        },
-        None => lhs,
-    };
-
     acc.add(
         AssistId("flip_binexpr", AssistKind::RefactorRewrite),
         "Flip binary expression",
@@ -124,7 +126,7 @@ mod tests {
     }
 
     #[test]
-    fn flip_binexpr_works_for_lhs_binexpr() {
+    fn flip_binexpr_works_for_lhs_arith() {
         check_assist(
             flip_binexpr,
             r"fn f() { let res = 1 + (2 - 3) +$0 4 + 5; }",
@@ -132,6 +134,15 @@ mod tests {
         )
     }
 
+    #[test]
+    fn flip_binexpr_works_for_lhs_cmp() {
+        check_assist(
+            flip_binexpr,
+            r"fn f() { let res = 1 + (2 - 3) >$0 4 + 5; }",
+            r"fn f() { let res = 4 + 5 < 1 + (2 - 3); }",
+        )
+    }
+
     #[test]
     fn flip_binexpr_works_inside_match() {
         check_assist(

From 477f45c6968227162cbcfa7217181250686a8410 Mon Sep 17 00:00:00 2001
From: petr-tik 
Date: Sun, 20 Aug 2023 18:31:25 +0100
Subject: [PATCH 40/62] Stop offering private functions in completions

Before
Private functions have RawVisibility module, but were
missed because take_types returned None early. After resolve_visibility
returned None, Visibility::Public was set instead and private functions
ended up being offered in autocompletion.

Choosing such a function results in an immediate error diagnostic
about using a private function.

After
Pattern match of take_types that returns None and
query for Module-level visibility from the original_module

Fix #15134 - tested with a unit test and a manual end-to-end
test of building rust-analyzer from my branch and opening
the reproduction repository

REVIEW
Refactor to move scope_def_applicable and check function visibility
from a module

Please let me know what's the best way to add a unit tests to
nameres, which is where the root cause was
---
 crates/hir-def/src/nameres/path_resolution.rs | 14 +++++----
 crates/ide-completion/src/completions/expr.rs | 29 +++++++++++++------
 crates/ide-completion/src/tests/special.rs    | 24 +++++++++++++++
 3 files changed, 53 insertions(+), 14 deletions(-)

diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index 4c1b8f306c50..7c3231913d32 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -93,11 +93,15 @@ impl DefMap {
                 if remaining.is_some() {
                     return None;
                 }
-                let types = result.take_types()?;
-                match types {
-                    ModuleDefId::ModuleId(m) => Visibility::Module(m),
-                    _ => {
-                        // error: visibility needs to refer to module
+                let types = result.take_types();
+
+                match (types, path.kind) {
+                    (Some(ModuleDefId::ModuleId(m)), _) => Visibility::Module(m),
+                    // resolve_path doesn't find any values for a plan pathkind of a private function
+                    (None, PathKind::Plain | PathKind::Crate) => {
+                        Visibility::Module(self.module_id(original_module))
+                    }
+                    (_, _) => {
                         return None;
                     }
                 }
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index d3c817d4b43a..17a52787b8a8 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -1,6 +1,6 @@
 //! Completion of names from the current scope in expression position.
 
-use hir::ScopeDef;
+use hir::{HasVisibility, Module, ScopeDef};
 use syntax::ast;
 
 use crate::{
@@ -9,6 +9,23 @@ use crate::{
     CompletionContext, Completions,
 };
 
+fn scope_def_applicable(
+    def: ScopeDef,
+    ctx: &CompletionContext<'_>,
+    module: Option<&Module>,
+) -> bool {
+    match (def, module) {
+        (ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_), _) => {
+            false
+        }
+        (ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)), _) => mac.is_fn_like(ctx.db),
+        (ScopeDef::ModuleDef(hir::ModuleDef::Function(f)), Some(m)) => {
+            f.is_visible_from(ctx.db, *m)
+        }
+        _ => true,
+    }
+}
+
 pub(crate) fn complete_expr_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
@@ -37,12 +54,6 @@ pub(crate) fn complete_expr_path(
     let wants_mut_token =
         ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
 
-    let scope_def_applicable = |def| match def {
-        ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => false,
-        ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
-        _ => true,
-    };
-
     let add_assoc_item = |acc: &mut Completions, item| match item {
         hir::AssocItem::Function(func) => acc.add_function(ctx, path_ctx, func, None),
         hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
@@ -87,7 +98,7 @@ pub(crate) fn complete_expr_path(
                 hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
                     let module_scope = module.scope(ctx.db, Some(ctx.module));
                     for (name, def) in module_scope {
-                        if scope_def_applicable(def) {
+                        if scope_def_applicable(def, ctx, Some(module)) {
                             acc.add_path_resolution(
                                 ctx,
                                 path_ctx,
@@ -233,7 +244,7 @@ pub(crate) fn complete_expr_path(
                         [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases),
                     }
                 }
-                _ if scope_def_applicable(def) => {
+                _ if scope_def_applicable(def, ctx, None) => {
                     acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases)
                 }
                 _ => (),
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index d3dbd7cc2277..8d82b5f02bb8 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -1286,6 +1286,30 @@ fn here_we_go() {
     );
 }
 
+#[test]
+fn completes_only_public() {
+    check(
+        r#"
+//- /e.rs
+pub(self) fn i_should_be_hidden() {}
+pub(in crate::krate) fn i_should_also_be_hidden() {}
+pub fn i_am_public () {}
+
+//- /lib.rs crate:krate
+pub mod e;
+
+//- /main.rs deps:krate crate:main
+use krate::e;
+fn main() {
+    e::$0
+}"#,
+        expect![
+            "fn i_am_public() fn()
+"
+        ],
+    )
+}
+
 #[test]
 fn completion_filtering_excludes_non_identifier_doc_aliases() {
     check_edit(

From 38b82e05ac9ae681b956b09ffd9e5919de0287ec Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 8 Dec 2023 11:59:44 +0100
Subject: [PATCH 41/62] fix: Fix item tree lowering pub(self) to pub()

---
 crates/hir-def/src/item_tree/tests.rs         | 12 ++++++++
 crates/hir-def/src/nameres/path_resolution.rs | 14 ++++-----
 crates/hir-def/src/visibility.rs              |  2 +-
 crates/ide-completion/src/completions/expr.rs | 29 ++++++-------------
 crates/ide-completion/src/tests/special.rs    |  2 +-
 5 files changed, 28 insertions(+), 31 deletions(-)

diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index 4180f817209e..96c65b941c1d 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -370,3 +370,15 @@ struct S<#[cfg(never)] T>;
         "#]],
     )
 }
+
+#[test]
+fn pub_self() {
+    check(
+        r#"
+pub(self) struct S;
+        "#,
+        expect![[r#"
+            pub(self) struct S;
+        "#]],
+    )
+}
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index 7c3231913d32..be3438e427db 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -93,15 +93,11 @@ impl DefMap {
                 if remaining.is_some() {
                     return None;
                 }
-                let types = result.take_types();
-
-                match (types, path.kind) {
-                    (Some(ModuleDefId::ModuleId(m)), _) => Visibility::Module(m),
-                    // resolve_path doesn't find any values for a plan pathkind of a private function
-                    (None, PathKind::Plain | PathKind::Crate) => {
-                        Visibility::Module(self.module_id(original_module))
-                    }
-                    (_, _) => {
+                let types = result.take_types()?;
+                match types {
+                    ModuleDefId::ModuleId(m) => Visibility::Module(m),
+                    // error: visibility needs to refer to module
+                    _ => {
                         return None;
                     }
                 }
diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs
index ab9266aa60f1..f5803653c73b 100644
--- a/crates/hir-def/src/visibility.rs
+++ b/crates/hir-def/src/visibility.rs
@@ -73,7 +73,7 @@ impl RawVisibility {
                 RawVisibility::Module(path)
             }
             ast::VisibilityKind::PubSelf => {
-                let path = ModPath::from_kind(PathKind::Plain);
+                let path = ModPath::from_kind(PathKind::Super(0));
                 RawVisibility::Module(path)
             }
             ast::VisibilityKind::Pub => RawVisibility::Public,
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 17a52787b8a8..d3c817d4b43a 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -1,6 +1,6 @@
 //! Completion of names from the current scope in expression position.
 
-use hir::{HasVisibility, Module, ScopeDef};
+use hir::ScopeDef;
 use syntax::ast;
 
 use crate::{
@@ -9,23 +9,6 @@ use crate::{
     CompletionContext, Completions,
 };
 
-fn scope_def_applicable(
-    def: ScopeDef,
-    ctx: &CompletionContext<'_>,
-    module: Option<&Module>,
-) -> bool {
-    match (def, module) {
-        (ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_), _) => {
-            false
-        }
-        (ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)), _) => mac.is_fn_like(ctx.db),
-        (ScopeDef::ModuleDef(hir::ModuleDef::Function(f)), Some(m)) => {
-            f.is_visible_from(ctx.db, *m)
-        }
-        _ => true,
-    }
-}
-
 pub(crate) fn complete_expr_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
@@ -54,6 +37,12 @@ pub(crate) fn complete_expr_path(
     let wants_mut_token =
         ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
 
+    let scope_def_applicable = |def| match def {
+        ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => false,
+        ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
+        _ => true,
+    };
+
     let add_assoc_item = |acc: &mut Completions, item| match item {
         hir::AssocItem::Function(func) => acc.add_function(ctx, path_ctx, func, None),
         hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
@@ -98,7 +87,7 @@ pub(crate) fn complete_expr_path(
                 hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
                     let module_scope = module.scope(ctx.db, Some(ctx.module));
                     for (name, def) in module_scope {
-                        if scope_def_applicable(def, ctx, Some(module)) {
+                        if scope_def_applicable(def) {
                             acc.add_path_resolution(
                                 ctx,
                                 path_ctx,
@@ -244,7 +233,7 @@ pub(crate) fn complete_expr_path(
                         [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases),
                     }
                 }
-                _ if scope_def_applicable(def, ctx, None) => {
+                _ if scope_def_applicable(def) => {
                     acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases)
                 }
                 _ => (),
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index 8d82b5f02bb8..28c9bffc5eca 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -1292,7 +1292,7 @@ fn completes_only_public() {
         r#"
 //- /e.rs
 pub(self) fn i_should_be_hidden() {}
-pub(in crate::krate) fn i_should_also_be_hidden() {}
+pub(in crate::e) fn i_should_also_be_hidden() {}
 pub fn i_am_public () {}
 
 //- /lib.rs crate:krate

From b09a052fe8b93ac7c5d12da0c0469324a2c78666 Mon Sep 17 00:00:00 2001
From: Ryan Mehri 
Date: Mon, 2 Oct 2023 22:04:59 -0700
Subject: [PATCH 42/62] fix: resolve Self type references in delegate method
 assist

---
 .../src/handlers/generate_delegate_methods.rs | 168 +++++++++++++++++-
 crates/ide-db/src/path_transform.rs           |  32 +++-
 2 files changed, 197 insertions(+), 3 deletions(-)

diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index bbac0a26ea4c..92fbdf53f6b7 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -1,6 +1,7 @@
 use std::collections::HashSet;
 
 use hir::{self, HasCrate, HasSource, HasVisibility};
+use ide_db::path_transform::PathTransform;
 use syntax::{
     ast::{
         self, edit_in_place::Indent, make, AstNode, HasGenericParams, HasName, HasVisibility as _,
@@ -106,7 +107,10 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
             |edit| {
                 // Create the function
                 let method_source = match method.source(ctx.db()) {
-                    Some(source) => source.value,
+                    Some(source) => {
+                        ctx.sema.parse_or_expand(source.file_id);
+                        source.value
+                    }
                     None => return,
                 };
                 let vis = method_source.visibility();
@@ -183,6 +187,12 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                 let assoc_items = impl_def.get_or_create_assoc_item_list();
                 assoc_items.add_item(f.clone().into());
 
+                PathTransform::generic_transformation(
+                    &ctx.sema.scope(strukt.syntax()).unwrap(),
+                    &ctx.sema.scope(method_source.syntax()).unwrap(),
+                )
+                .apply(f.syntax());
+
                 if let Some(cap) = ctx.config.snippet_cap {
                     edit.add_tabstop_before(cap, f)
                 }
@@ -454,6 +464,162 @@ impl Person {
         );
     }
 
+    #[test]
+    fn test_fixes_basic_self_references() {
+        check_assist(
+            generate_delegate_methods,
+            r#"
+struct Foo {
+    field: $0Bar,
+}
+
+struct Bar;
+
+impl Bar {
+    fn bar(&self, other: Self) -> Self {
+        other
+    }
+}
+"#,
+            r#"
+struct Foo {
+    field: Bar,
+}
+
+impl Foo {
+    $0fn bar(&self, other: Bar) -> Bar {
+        self.field.bar(other)
+    }
+}
+
+struct Bar;
+
+impl Bar {
+    fn bar(&self, other: Self) -> Self {
+        other
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fixes_nested_self_references() {
+        check_assist(
+            generate_delegate_methods,
+            r#"
+struct Foo {
+    field: $0Bar,
+}
+
+struct Bar;
+
+impl Bar {
+    fn bar(&mut self, a: (Self, [Self; 4]), b: Vec) {}
+}
+"#,
+            r#"
+struct Foo {
+    field: Bar,
+}
+
+impl Foo {
+    $0fn bar(&mut self, a: (Bar, [Bar; 4]), b: Vec) {
+        self.field.bar(a, b)
+    }
+}
+
+struct Bar;
+
+impl Bar {
+    fn bar(&mut self, a: (Self, [Self; 4]), b: Vec) {}
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fixes_self_references_with_lifetimes_and_generics() {
+        check_assist(
+            generate_delegate_methods,
+            r#"
+struct Foo<'a, T> {
+    $0field: Bar<'a, T>,
+}
+
+struct Bar<'a, T>(&'a T);
+
+impl<'a, T> Bar<'a, T> {
+    fn bar(self, mut b: Vec<&'a Self>) -> &'a Self {
+        b.pop().unwrap()
+    }
+}
+"#,
+            r#"
+struct Foo<'a, T> {
+    field: Bar<'a, T>,
+}
+
+impl<'a, T> Foo<'a, T> {
+    $0fn bar(self, mut b: Vec<&'a Bar<'_, T>>) -> &'a Bar<'_, T> {
+        self.field.bar(b)
+    }
+}
+
+struct Bar<'a, T>(&'a T);
+
+impl<'a, T> Bar<'a, T> {
+    fn bar(self, mut b: Vec<&'a Self>) -> &'a Self {
+        b.pop().unwrap()
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fixes_self_references_across_macros() {
+        check_assist(
+            generate_delegate_methods,
+            r#"
+//- /bar.rs
+macro_rules! test_method {
+    () => {
+        pub fn test(self, b: Bar) -> Self {
+            self
+        }
+    };
+}
+
+pub struct Bar;
+
+impl Bar {
+    test_method!();
+}
+
+//- /main.rs
+mod bar;
+
+struct Foo {
+    $0bar: bar::Bar,
+}
+"#,
+            r#"
+mod bar;
+
+struct Foo {
+    bar: bar::Bar,
+}
+
+impl Foo {
+    $0pub fn test(self,b:bar::Bar) ->bar::Bar {
+        self.bar.test(b)
+    }
+}
+"#,
+        );
+    }
+
     #[test]
     fn test_generate_delegate_visibility() {
         check_assist_not_applicable(
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index fa9339f30f20..49b990172a54 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -2,7 +2,7 @@
 
 use crate::helpers::mod_path_to_ast;
 use either::Either;
-use hir::{AsAssocItem, HirDisplay, SemanticsScope};
+use hir::{AsAssocItem, HirDisplay, ModuleDef, SemanticsScope};
 use rustc_hash::FxHashMap;
 use syntax::{
     ast::{self, make, AstNode},
@@ -332,8 +332,36 @@ impl Ctx<'_> {
                     ted::replace(path.syntax(), subst.clone_subtree().clone_for_update());
                 }
             }
+            hir::PathResolution::SelfType(imp) => {
+                let ty = imp.self_ty(self.source_scope.db);
+                let ty_str = &ty
+                    .display_source_code(
+                        self.source_scope.db,
+                        self.source_scope.module().into(),
+                        true,
+                    )
+                    .ok()?;
+                let ast_ty = make::ty(&ty_str).clone_for_update();
+
+                if let Some(adt) = ty.as_adt() {
+                    if let ast::Type::PathType(path_ty) = &ast_ty {
+                        let found_path = self.target_module.find_use_path(
+                            self.source_scope.db.upcast(),
+                            ModuleDef::from(adt),
+                            false,
+                        )?;
+
+                        if let Some(qual) = mod_path_to_ast(&found_path).qualifier() {
+                            let res = make::path_concat(qual, path_ty.path()?).clone_for_update();
+                            ted::replace(path.syntax(), res.syntax());
+                            return Some(());
+                        }
+                    }
+                }
+
+                ted::replace(path.syntax(), ast_ty.syntax());
+            }
             hir::PathResolution::Local(_)
-            | hir::PathResolution::SelfType(_)
             | hir::PathResolution::Def(_)
             | hir::PathResolution::BuiltinAttr(_)
             | hir::PathResolution::ToolModule(_)

From 7ca6ae5819c409415ab69db2468c5cd3878ab7c8 Mon Sep 17 00:00:00 2001
From: Ryan Mehri 
Date: Thu, 5 Oct 2023 12:45:26 -0700
Subject: [PATCH 43/62] fix: preserve where clause in delegate method

---
 .../src/handlers/generate_delegate_methods.rs | 49 ++++++++++++++++++-
 1 file changed, 48 insertions(+), 1 deletion(-)

diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 92fbdf53f6b7..7a5d3d0859c6 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -134,7 +134,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                     vis,
                     fn_name,
                     type_params,
-                    None,
+                    method_source.where_clause(),
                     params,
                     body,
                     ret_type,
@@ -464,6 +464,53 @@ impl Person {
         );
     }
 
+    #[test]
+    fn test_preserve_where_clause() {
+        check_assist(
+            generate_delegate_methods,
+            r#"
+struct Inner(T);
+impl Inner {
+    fn get(&self) -> T
+    where
+        T: Copy,
+        T: PartialEq,
+    {
+        self.0
+    }
+}
+
+struct Struct {
+    $0field: Inner,
+}
+"#,
+            r#"
+struct Inner(T);
+impl Inner {
+    fn get(&self) -> T
+    where
+        T: Copy,
+        T: PartialEq,
+    {
+        self.0
+    }
+}
+
+struct Struct {
+    field: Inner,
+}
+
+impl Struct {
+    $0fn get(&self) -> T where
+            T: Copy,
+            T: PartialEq, {
+        self.field.get()
+    }
+}
+"#,
+        );
+    }
+
     #[test]
     fn test_fixes_basic_self_references() {
         check_assist(

From e1cbcf0907948f6007921ddaa71987d763f55e36 Mon Sep 17 00:00:00 2001
From: Ryan Mehri 
Date: Mon, 9 Oct 2023 08:30:34 -0700
Subject: [PATCH 44/62] fix: prefer keeping Self if it is in the same impl def

---
 crates/hir-def/src/resolver.rs                |  8 ++++++++
 crates/hir/src/semantics.rs                   |  4 ++++
 .../src/handlers/generate_delegate_methods.rs | 19 ++++++++-----------
 crates/ide-db/src/path_transform.rs           |  8 ++++++++
 4 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 50da9ed06a0d..ba0a2c0224a0 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -588,6 +588,14 @@ impl Resolver {
             _ => None,
         })
     }
+
+    pub fn impl_def(&self) -> Option {
+        self.scopes().find_map(|scope| match scope {
+            Scope::ImplDefScope(def) => Some(*def),
+            _ => None,
+        })
+    }
+
     /// `expr_id` is required to be an expression id that comes after the top level expression scope in the given resolver
     #[must_use]
     pub fn update_to_inner_scope(
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 46835ec04e01..55c14312071a 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1556,6 +1556,10 @@ impl SemanticsScope<'_> {
     pub fn extern_crate_decls(&self) -> impl Iterator + '_ {
         self.resolver.extern_crate_decls_in_scope(self.db.upcast())
     }
+
+    pub fn has_same_self_type(&self, other: &SemanticsScope<'_>) -> bool {
+        self.resolver.impl_def() == other.resolver.impl_def()
+    }
 }
 
 #[derive(Debug)]
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 7a5d3d0859c6..db1e0ceaec1e 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -1,6 +1,6 @@
 use std::collections::HashSet;
 
-use hir::{self, HasCrate, HasSource, HasVisibility};
+use hir::{self, HasCrate, HasVisibility};
 use ide_db::path_transform::PathTransform;
 use syntax::{
     ast::{
@@ -106,11 +106,8 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
             target,
             |edit| {
                 // Create the function
-                let method_source = match method.source(ctx.db()) {
-                    Some(source) => {
-                        ctx.sema.parse_or_expand(source.file_id);
-                        source.value
-                    }
+                let method_source = match ctx.sema.source(method) {
+                    Some(source) => source.value,
                     None => return,
                 };
                 let vis = method_source.visibility();
@@ -187,11 +184,11 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                 let assoc_items = impl_def.get_or_create_assoc_item_list();
                 assoc_items.add_item(f.clone().into());
 
-                PathTransform::generic_transformation(
-                    &ctx.sema.scope(strukt.syntax()).unwrap(),
-                    &ctx.sema.scope(method_source.syntax()).unwrap(),
-                )
-                .apply(f.syntax());
+                if let Some((target, source)) =
+                    ctx.sema.scope(strukt.syntax()).zip(ctx.sema.scope(method_source.syntax()))
+                {
+                    PathTransform::generic_transformation(&target, &source).apply(f.syntax());
+                }
 
                 if let Some(cap) = ctx.config.snippet_cap {
                     edit.add_tabstop_before(cap, f)
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index 49b990172a54..fb4c0c12691d 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -183,6 +183,7 @@ impl<'a> PathTransform<'a> {
             lifetime_substs,
             target_module,
             source_scope: self.source_scope,
+            same_self_type: self.target_scope.has_same_self_type(self.source_scope),
         };
         ctx.transform_default_values(defaulted_params);
         ctx
@@ -195,6 +196,7 @@ struct Ctx<'a> {
     lifetime_substs: FxHashMap,
     target_module: hir::Module,
     source_scope: &'a SemanticsScope<'a>,
+    same_self_type: bool,
 }
 
 fn postorder(item: &SyntaxNode) -> impl Iterator {
@@ -333,6 +335,11 @@ impl Ctx<'_> {
                 }
             }
             hir::PathResolution::SelfType(imp) => {
+                // keep Self type if it does not need to be replaced
+                if self.same_self_type {
+                    return None;
+                }
+
                 let ty = imp.self_ty(self.source_scope.db);
                 let ty_str = &ty
                     .display_source_code(
@@ -349,6 +356,7 @@ impl Ctx<'_> {
                             self.source_scope.db.upcast(),
                             ModuleDef::from(adt),
                             false,
+                            true,
                         )?;
 
                         if let Some(qual) = mod_path_to_ast(&found_path).qualifier() {

From ee5e1e77ebab4d0dacf4d1aec3068bd8211bfbe1 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 8 Dec 2023 10:47:36 +0100
Subject: [PATCH 45/62] Make TraitEnvironment's constructor private

---
 crates/hir-ty/src/consteval.rs  |  2 +-
 crates/hir-ty/src/display.rs    |  5 ++---
 crates/hir-ty/src/infer/expr.rs |  6 ++---
 crates/hir-ty/src/lib.rs        |  2 +-
 crates/hir-ty/src/lower.rs      |  9 ++------
 crates/hir-ty/src/mir.rs        |  3 +--
 crates/hir-ty/src/traits.rs     | 22 +++++++++++++++----
 crates/hir/src/lib.rs           | 39 +++++++++++++++------------------
 8 files changed, 46 insertions(+), 42 deletions(-)

diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index ddeb9f14b5dc..9792d945eb8f 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -137,7 +137,7 @@ pub fn intern_const_ref(
     ty: Ty,
     krate: CrateId,
 ) -> Const {
-    let layout = db.layout_of_ty(ty.clone(), Arc::new(TraitEnvironment::empty(krate)));
+    let layout = db.layout_of_ty(ty.clone(), TraitEnvironment::empty(krate));
     let bytes = match value {
         LiteralConstRef::Int(i) => {
             // FIXME: We should handle failure of layout better.
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index a324129b351c..d81926f7c976 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -448,9 +448,8 @@ fn render_const_scalar(
 ) -> Result<(), HirDisplayError> {
     // FIXME: We need to get krate from the final callers of the hir display
     // infrastructure and have it here as a field on `f`.
-    let trait_env = Arc::new(TraitEnvironment::empty(
-        *f.db.crate_graph().crates_in_topological_order().last().unwrap(),
-    ));
+    let trait_env =
+        TraitEnvironment::empty(*f.db.crate_graph().crates_in_topological_order().last().unwrap());
     match ty.kind(Interner) {
         TyKind::Scalar(s) => match s {
             Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }),
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 0c3c725a7c74..24026202b74d 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -18,7 +18,6 @@ use hir_def::{
 use hir_expand::name::{name, Name};
 use stdx::always;
 use syntax::ast::RangeOp;
-use triomphe::Arc;
 
 use crate::{
     autoderef::{builtin_deref, deref_by_trait, Autoderef},
@@ -40,7 +39,8 @@ use crate::{
     traits::FnTrait,
     utils::{generics, Generics},
     Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
-    Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
+    Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt,
+    TyKind,
 };
 
 use super::{
@@ -1291,7 +1291,7 @@ impl InferenceContext<'_> {
         let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr);
         let prev_env = block_id.map(|block_id| {
             let prev_env = self.table.trait_env.clone();
-            Arc::make_mut(&mut self.table.trait_env).block = Some(block_id);
+            TraitEnvironment::with_block(&mut self.table.trait_env, block_id);
             prev_env
         });
 
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 5a3e423f152a..cf174feed24b 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -122,7 +122,7 @@ pub type TyKind = chalk_ir::TyKind;
 pub type TypeFlags = chalk_ir::TypeFlags;
 pub type DynTy = chalk_ir::DynTy;
 pub type FnPointer = chalk_ir::FnPointer;
-// pub type FnSubst = chalk_ir::FnSubst;
+// pub type FnSubst = chalk_ir::FnSubst; // a re-export so we don't lose the tuple constructor
 pub use chalk_ir::FnSubst;
 pub type ProjectionTy = chalk_ir::ProjectionTy;
 pub type AliasTy = chalk_ir::AliasTy;
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 2a6d69e7fc62..c86fe9adff86 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -1468,7 +1468,7 @@ pub(crate) fn trait_environment_for_body_query(
 ) -> Arc {
     let Some(def) = def.as_generic_def_id() else {
         let krate = def.module(db.upcast()).krate();
-        return Arc::new(TraitEnvironment::empty(krate));
+        return TraitEnvironment::empty(krate);
     };
     db.trait_environment(def)
 }
@@ -1528,12 +1528,7 @@ pub(crate) fn trait_environment_query(
 
     let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
 
-    Arc::new(TraitEnvironment {
-        krate,
-        block: None,
-        traits_from_clauses: traits_in_scope.into_boxed_slice(),
-        env,
-    })
+    TraitEnvironment::new(krate, None, traits_in_scope.into_boxed_slice(), env)
 }
 
 /// Resolve the where clause(s) of an item with generics.
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 2e6fe59d3bd8..f1795e71d945 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -40,7 +40,6 @@ pub use monomorphization::{
 use rustc_hash::FxHashMap;
 use smallvec::{smallvec, SmallVec};
 use stdx::{impl_from, never};
-use triomphe::Arc;
 
 use super::consteval::{intern_const_scalar, try_const_usize};
 
@@ -147,7 +146,7 @@ impl ProjectionElem {
             base = normalize(
                 db,
                 // FIXME: we should get this from caller
-                Arc::new(TraitEnvironment::empty(krate)),
+                TraitEnvironment::empty(krate),
                 base,
             );
         }
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 467b94a2662a..b6bc76bc98d5 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -48,18 +48,32 @@ pub struct TraitEnvironment {
     pub krate: CrateId,
     pub block: Option,
     // FIXME make this a BTreeMap
-    pub(crate) traits_from_clauses: Box<[(Ty, TraitId)]>,
+    traits_from_clauses: Box<[(Ty, TraitId)]>,
     pub env: chalk_ir::Environment,
 }
 
 impl TraitEnvironment {
-    pub fn empty(krate: CrateId) -> Self {
-        TraitEnvironment {
+    pub fn empty(krate: CrateId) -> Arc {
+        Arc::new(TraitEnvironment {
             krate,
             block: None,
             traits_from_clauses: Box::default(),
             env: chalk_ir::Environment::new(Interner),
-        }
+        })
+    }
+
+    pub fn new(
+        krate: CrateId,
+        block: Option,
+        traits_from_clauses: Box<[(Ty, TraitId)]>,
+        env: chalk_ir::Environment,
+    ) -> Arc {
+        Arc::new(TraitEnvironment { krate, block, traits_from_clauses, env })
+    }
+
+    // pub fn with_block(self: &mut Arc, block: BlockId) {
+    pub fn with_block(this: &mut Arc, block: BlockId) {
+        Arc::make_mut(this).block = Some(block);
     }
 
     pub fn traits_in_scope_from_clauses(&self, ty: Ty) -> impl Iterator + '_ {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 5137bff055a4..ca838c7a51e1 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -3564,10 +3564,9 @@ impl TraitRef {
         resolver: &Resolver,
         trait_ref: hir_ty::TraitRef,
     ) -> TraitRef {
-        let env = resolver.generic_def().map_or_else(
-            || Arc::new(TraitEnvironment::empty(resolver.krate())),
-            |d| db.trait_environment(d),
-        );
+        let env = resolver
+            .generic_def()
+            .map_or_else(|| TraitEnvironment::empty(resolver.krate()), |d| db.trait_environment(d));
         TraitRef { env, trait_ref }
     }
 
@@ -3707,15 +3706,14 @@ impl Type {
         resolver: &Resolver,
         ty: Ty,
     ) -> Type {
-        let environment = resolver.generic_def().map_or_else(
-            || Arc::new(TraitEnvironment::empty(resolver.krate())),
-            |d| db.trait_environment(d),
-        );
+        let environment = resolver
+            .generic_def()
+            .map_or_else(|| TraitEnvironment::empty(resolver.krate()), |d| db.trait_environment(d));
         Type { env: environment, ty }
     }
 
     pub(crate) fn new_for_crate(krate: CrateId, ty: Ty) -> Type {
-        Type { env: Arc::new(TraitEnvironment::empty(krate)), ty }
+        Type { env: TraitEnvironment::empty(krate), ty }
     }
 
     pub fn reference(inner: &Type, m: Mutability) -> Type {
@@ -3731,10 +3729,9 @@ impl Type {
 
     fn new(db: &dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty) -> Type {
         let resolver = lexical_env.resolver(db.upcast());
-        let environment = resolver.generic_def().map_or_else(
-            || Arc::new(TraitEnvironment::empty(resolver.krate())),
-            |d| db.trait_environment(d),
-        );
+        let environment = resolver
+            .generic_def()
+            .map_or_else(|| TraitEnvironment::empty(resolver.krate()), |d| db.trait_environment(d));
         Type { env: environment, ty }
     }
 
@@ -4304,10 +4301,10 @@ impl Type {
         let canonical = hir_ty::replace_errors_with_variables(&self.ty);
 
         let krate = scope.krate();
-        let environment = scope.resolver().generic_def().map_or_else(
-            || Arc::new(TraitEnvironment::empty(krate.id)),
-            |d| db.trait_environment(d),
-        );
+        let environment = scope
+            .resolver()
+            .generic_def()
+            .map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d));
 
         method_resolution::iterate_method_candidates_dyn(
             &canonical,
@@ -4361,10 +4358,10 @@ impl Type {
         let canonical = hir_ty::replace_errors_with_variables(&self.ty);
 
         let krate = scope.krate();
-        let environment = scope.resolver().generic_def().map_or_else(
-            || Arc::new(TraitEnvironment::empty(krate.id)),
-            |d| db.trait_environment(d),
-        );
+        let environment = scope
+            .resolver()
+            .generic_def()
+            .map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d));
 
         method_resolution::iterate_path_candidates(
             &canonical,

From 5febc8f3d7106f59d690d632984ad541d7ee27c8 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 8 Dec 2023 11:26:22 +0100
Subject: [PATCH 46/62] fix: Fix `concat_bytes!` expansion

---
 .../macro_expansion_tests/builtin_fn_macro.rs |  4 +--
 .../macro_expansion_tests/mbe/regression.rs   | 26 +++++++++++++++++++
 crates/hir-expand/src/builtin_fn_macro.rs     | 22 ++++++++++++++--
 crates/mbe/src/token_map.rs                   | 16 +++++++++---
 4 files changed, 61 insertions(+), 7 deletions(-)

diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 106ead83fad7..514219ee7150 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -468,12 +468,12 @@ macro_rules! concat_bytes {}
 
 fn main() { concat_bytes!(b'A', b"BC", [68, b'E', 70]); }
 "##,
-        expect![[r##"
+        expect![[r#"
 #[rustc_builtin_macro]
 macro_rules! concat_bytes {}
 
 fn main() { [b'A', 66, 67, 68, b'E', 70]; }
-"##]],
+"#]],
     );
 }
 
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index 2886b2a366c0..9010050ee678 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -1004,3 +1004,29 @@ fn main() {
 "##]],
     );
 }
+
+#[test]
+fn eager_concat_bytes_panic() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! concat_bytes {}
+
+fn main() {
+    let x = concat_bytes!(2);
+}
+
+"#,
+        expect![[r#"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! concat_bytes {}
+
+fn main() {
+    let x = /* error: unexpected token in input */[];
+}
+
+"#]],
+    );
+}
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index 903c21c84eec..c8f04bfee54f 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -6,6 +6,7 @@ use base_db::{
 };
 use cfg::CfgExpr;
 use either::Either;
+use itertools::Itertools;
 use mbe::{parse_exprs_with_sep, parse_to_token_tree};
 use syntax::{
     ast::{self, AstToken},
@@ -491,8 +492,25 @@ fn concat_bytes_expand(
             }
         }
     }
-    let ident = tt::Ident { text: bytes.join(", ").into(), span };
-    ExpandResult { value: quote!(span =>[#ident]), err }
+    let value = tt::Subtree {
+        delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket },
+        token_trees: {
+            Itertools::intersperse_with(
+                bytes.into_iter().map(|it| {
+                    tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text: it.into(), span }))
+                }),
+                || {
+                    tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
+                        char: ',',
+                        spacing: tt::Spacing::Alone,
+                        span,
+                    }))
+                },
+            )
+            .collect()
+        },
+    };
+    ExpandResult { value, err }
 }
 
 fn concat_bytes_expand_subtree(
diff --git a/crates/mbe/src/token_map.rs b/crates/mbe/src/token_map.rs
index 28b39b4f1eec..7d15812f8cb6 100644
--- a/crates/mbe/src/token_map.rs
+++ b/crates/mbe/src/token_map.rs
@@ -2,7 +2,7 @@
 
 use std::hash::Hash;
 
-use stdx::itertools::Itertools;
+use stdx::{always, itertools::Itertools};
 use syntax::{TextRange, TextSize};
 use tt::Span;
 
@@ -21,13 +21,23 @@ impl SpanMap {
     /// Finalizes the [`SpanMap`], shrinking its backing storage and validating that the offsets are
     /// in order.
     pub fn finish(&mut self) {
-        assert!(self.spans.iter().tuple_windows().all(|(a, b)| a.0 < b.0));
+        always!(
+            self.spans.iter().tuple_windows().all(|(a, b)| a.0 < b.0),
+            "spans are not in order"
+        );
         self.spans.shrink_to_fit();
     }
 
     /// Pushes a new span onto the [`SpanMap`].
     pub fn push(&mut self, offset: TextSize, span: S) {
-        debug_assert!(self.spans.last().map_or(true, |&(last_offset, _)| last_offset < offset));
+        if cfg!(debug_assertions) {
+            if let Some(&(last_offset, _)) = self.spans.last() {
+                assert!(
+                    last_offset < offset,
+                    "last_offset({last_offset:?}) must be smaller than offset({offset:?})"
+                );
+            }
+        }
         self.spans.push((offset, span));
     }
 

From f0c9f9bc97f2a59a504c6974d239500cbc346b75 Mon Sep 17 00:00:00 2001
From: Jessie Chatham Spencer 
Date: Wed, 13 Sep 2023 04:42:59 +0000
Subject: [PATCH 47/62] WIP - Sort suggested imports by type for data types

---
 crates/hir/src/lib.rs                        |   4 +-
 crates/ide-completion/src/render.rs          | 369 ++++++++++++++++++-
 crates/ide-completion/src/render/function.rs |  55 ++-
 3 files changed, 418 insertions(+), 10 deletions(-)

diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index ca838c7a51e1..0ad84014553c 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -4564,8 +4564,8 @@ impl Type {
 // FIXME: Document this
 #[derive(Debug)]
 pub struct Callable {
-    ty: Type,
-    sig: CallableSig,
+    pub ty: Type,
+    pub sig: CallableSig,
     callee: Callee,
     /// Whether this is a method that was called with method call syntax.
     pub(crate) is_bound_method: bool,
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index bf2a21fc6037..e8880927746c 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -10,7 +10,9 @@ pub(crate) mod variant;
 pub(crate) mod union_literal;
 pub(crate) mod literal;
 
-use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
+use core::panic;
+
+use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
 use ide_db::{
     documentation::{Documentation, HasDocs},
     helpers::item_name,
@@ -340,6 +342,7 @@ fn render_resolution_path(
     let cap = ctx.snippet_cap();
     let db = completion.db;
     let config = completion.config;
+    let requires_import = import_to_add.is_some();
 
     let name = local_name.to_smol_str();
     let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);
@@ -370,8 +373,8 @@ fn render_resolution_path(
             }
         }
     }
-    if let ScopeDef::Local(local) = resolution {
-        let ty = local.ty(db);
+
+    let mut set_item_relevance = |ty: Type| {
         if !ty.is_unknown() {
             item.detail(ty.display(db).to_string());
         }
@@ -379,12 +382,40 @@ fn render_resolution_path(
         item.set_relevance(CompletionRelevance {
             type_match: compute_type_match(completion, &ty),
             exact_name_match: compute_exact_name_match(completion, &name),
-            is_local: true,
+            is_local: matches!(resolution, ScopeDef::Local(_)),
+            requires_import,
             ..CompletionRelevance::default()
         });
 
         path_ref_match(completion, path_ctx, &ty, &mut item);
     };
+
+    match resolution {
+        ScopeDef::Local(local) => set_item_relevance(local.ty(db)),
+        ScopeDef::ModuleDef(ModuleDef::Adt(adt)) | ScopeDef::AdtSelfType(adt) => {
+            set_item_relevance(adt.ty(db))
+        }
+        ScopeDef::ModuleDef(ModuleDef::Function(func)) => {
+            set_item_relevance(func.ty(db).as_callable(db).unwrap().ty)
+        }
+        ScopeDef::ModuleDef(ModuleDef::Variant(variant)) => {
+            set_item_relevance(variant.parent_enum(db).ty(db))
+        }
+        ScopeDef::ModuleDef(ModuleDef::Const(konst)) => set_item_relevance(konst.ty(db)),
+        ScopeDef::ModuleDef(ModuleDef::Static(stat)) => set_item_relevance(stat.ty(db)),
+        ScopeDef::ModuleDef(ModuleDef::BuiltinType(bt)) => set_item_relevance(bt.ty(db)),
+        ScopeDef::ImplSelfType(imp) => set_item_relevance(imp.self_ty(db)),
+
+        ScopeDef::GenericParam(_)
+        | ScopeDef::Label(_)
+        | ScopeDef::Unknown
+        | ScopeDef::ModuleDef(ModuleDef::Trait(_))
+        | ScopeDef::ModuleDef(ModuleDef::TraitAlias(_))
+        | ScopeDef::ModuleDef(ModuleDef::Macro(_))
+        | ScopeDef::ModuleDef(ModuleDef::Module(_))
+        | ScopeDef::ModuleDef(ModuleDef::TypeAlias(_)) => (),
+    };
+
     item
 }
 
@@ -492,6 +523,28 @@ fn compute_type_match(
     }
 }
 
+fn compute_type_match2(
+    ctx: &CompletionContext<'_>,
+    completion_ty1: &hir::Type,
+    completion_ty2: &hir::Type,
+) -> Option {
+    let expected_type = completion_ty1;
+
+    // We don't ever consider unit type to be an exact type match, since
+    // nearly always this is not meaningful to the user.
+    if expected_type.is_unit() {
+        return None;
+    }
+
+    if completion_ty2 == expected_type {
+        Some(CompletionRelevanceTypeMatch::Exact)
+    } else if expected_type.could_unify_with(ctx.db, completion_ty2) {
+        Some(CompletionRelevanceTypeMatch::CouldUnify)
+    } else {
+        None
+    }
+}
+
 fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool {
     ctx.expected_name.as_ref().map_or(false, |name| name.text() == completion_name)
 }
@@ -635,6 +688,314 @@ mod tests {
         }
     }
 
+    #[test]
+    fn set_struct_type_completion_info() {
+        check_relevance(
+            r#"
+//- /lib.rs crate:dep
+
+pub mod test_mod_b {
+    pub struct Struct {}
+}
+
+pub mod test_mod_a {
+    pub struct Struct {}
+}
+
+//- /main.rs crate:main deps:dep
+
+fn test(input: dep::test_mod_b::Struct) { }
+
+fn main() {
+    test(Struct$0);
+}
+"#,
+            expect![[r#"
+                st dep::test_mod_b::Struct {…} [type_could_unify]
+                st Struct (use dep::test_mod_b::Struct) [type_could_unify+requires_import]
+                fn main() []
+                fn test(…) []
+                md dep []
+                st Struct (use dep::test_mod_a::Struct) [requires_import]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn set_union_type_completion_info() {
+        check_relevance(
+            r#"
+//- /lib.rs crate:dep
+
+pub mod test_mod_b {
+    pub union Union {
+            a: i32,
+            b: i32
+            }
+}
+
+pub mod test_mod_a {
+    pub enum Union {
+                a: i32,
+                b: i32
+            }
+}
+
+//- /main.rs crate:main deps:dep
+
+fn test(input: dep::test_mod_b::Union) { }
+
+fn main() {
+    test(Union$0);
+}
+"#,
+            expect![[r#"
+                un Union (use dep::test_mod_b::Union) [type_could_unify+requires_import]
+                fn main() []
+                fn test(…) []
+                md dep []
+                en Union (use dep::test_mod_a::Union) [requires_import]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn set_enum_type_completion_info() {
+        check_relevance(
+            r#"
+//- /lib.rs crate:dep
+
+pub mod test_mod_b {
+    pub enum Enum {
+                variant
+            }
+}
+
+pub mod test_mod_a {
+    pub enum Enum {
+                variant
+            }
+}
+
+//- /main.rs crate:main deps:dep
+
+fn test(input: dep::test_mod_b::Enum) { }
+
+fn main() {
+    test(Enum$0);
+}
+"#,
+            expect![[r#"
+                ev dep::test_mod_b::Enum::variant [type_could_unify]
+                en Enum (use dep::test_mod_b::Enum) [type_could_unify+requires_import]
+                fn main() []
+                fn test(…) []
+                md dep []
+                en Enum (use dep::test_mod_a::Enum) [requires_import]
+            "#]],
+        );
+    }
+
+    // TODO: does this test even make sense?
+    #[test]
+    fn set_enum_variant_type_completion_info() {
+        check_relevance(
+            r#"
+//- /lib.rs crate:dep
+
+pub mod test_mod_b {
+    pub enum Enum {
+                Variant
+            }
+}
+
+pub mod test_mod_a {
+    pub enum Enum {
+                Variant
+            }
+}
+
+//- /main.rs crate:main deps:dep
+
+fn test(input: dep::test_mod_b::Enum) { }
+
+fn main() {
+    test(Enum$0);
+}
+"#,
+            expect![[r#"
+                ev dep::test_mod_b::Enum::Variant [type_could_unify]
+                en Enum (use dep::test_mod_b::Enum) [type_could_unify+requires_import]
+                fn main() []
+                fn test(…) []
+                md dep []
+                en Enum (use dep::test_mod_a::Enum) [requires_import]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn set_fn_type_completion_info() {
+        check_relevance(
+            r#"
+//- /lib.rs crate:dep
+
+pub mod test_mod_b {
+    pub fn Function(j: isize) -> i32 {
+            }
+}
+
+            pub mod test_mod_a {
+    pub fn Function(i: usize) -> i32 {
+            }
+}
+
+//- /main.rs crate:main deps:dep
+
+fn test(input: fn(usize) -> i32) { }
+
+fn main() {
+    test(Function$0);
+}
+"#,
+            expect![[r#"
+                fn Function (use dep::test_mod_a::Function) [type_could_unify+requires_import]
+                fn main []
+                fn test []
+                md dep []
+                fn Function (use dep::test_mod_b::Function) [requires_import]
+            "#]],
+        );
+    }
+
+    // TODO This test does not trigger the const case
+    #[test]
+    fn set_const_type_completion_info() {
+        check_relevance(
+            r#"
+//- /lib.rs crate:dep
+
+pub mod test_mod_b {
+            pub const CONST: i32 = 1;
+}
+
+pub mod test_mod_a {
+            pub const CONST: i64 = 2;
+}
+
+//- /main.rs crate:main deps:dep
+
+fn test(input: i32) { }
+
+fn main() {
+    test(CONST$0);
+}
+"#,
+            expect![[r#"
+                ct CONST (use dep::test_mod_b::CONST) [type_could_unify+requires_import]
+                fn main() []
+                fn test(…) []
+                md dep []
+                ct CONST (use dep::test_mod_a::CONST) [requires_import]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn set_static_type_completion_info() {
+        check_relevance(
+            r#"
+//- /lib.rs crate:dep
+
+pub mod test_mod_b {
+            pub static STATIC: i32 = 5;
+}
+
+pub mod test_mod_a {
+            pub static STATIC: i64 = 5;
+}
+
+//- /main.rs crate:main deps:dep
+
+fn test(input: i32) { }
+
+fn main() {
+    test(STATIC$0);
+}
+"#,
+            expect![[r#"
+                sc STATIC (use dep::test_mod_b::STATIC) [type_could_unify+requires_import]
+                fn main() []
+                fn test(…) []
+                md dep []
+                sc STATIC (use dep::test_mod_a::STATIC) [requires_import]
+            "#]],
+        );
+    }
+
+    // TODO: seems like something is going wrong here. Exapt type match has no effect
+    // EDIT: maybe it is actually working
+    #[test]
+    fn set_self_type_completion_info() {
+        check_relevance(
+            r#"
+//- /main.rs crate:main
+
+struct Struct;
+
+impl Struct {
+fn test(&self) {
+        func(Self$0);            
+    }
+}
+
+fn func(input: Struct) { }
+
+"#,
+            expect![[r#"
+                st Struct [type]
+                st Self [type]
+                sp Self [type]
+                st Struct [type]
+                lc self [local]
+                fn func(…) []
+                me self.test() []
+            "#]],
+        );
+    }
+
+    // TODO: how do we actually test builtins?
+
+    #[test]
+    fn set_builtin_type_completion_info() {
+        check_relevance(
+            r#"
+//- /lib.rs crate:dep
+
+pub mod test_mod_b {
+            static STATIC: i32 = 5;
+}
+
+            pub mod test_mod_a {
+            static STATIC: &str = "test";
+}
+
+//- /main.rs crate:main deps:dep
+
+fn test(input: i32) { }
+
+fn main() {
+    test(STATIC$0);
+}
+"#,
+            expect![[r#"
+                fn main() []
+                fn test(…) []
+                md dep []
+            "#]],
+        );
+    }
+
     #[test]
     fn enum_detail_includes_record_fields() {
         check(
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index dfae715afe36..c2e06c926f12 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -1,6 +1,6 @@
 //! Renderer for function calls.
 
-use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
+use hir::{db::HirDatabase, AsAssocItem, Callable, HirDisplay, Type};
 use ide_db::{SnippetCap, SymbolKind};
 use itertools::Itertools;
 use stdx::{format_to, to_lower_snake_case};
@@ -8,8 +8,14 @@ use syntax::{AstNode, SmolStr};
 
 use crate::{
     context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind},
-    item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
-    render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
+    item::{
+        Builder, CompletionItem, CompletionItemKind, CompletionRelevance,
+        CompletionRelevanceTypeMatch,
+    },
+    render::{
+        compute_exact_name_match, compute_ref_match, compute_type_match, compute_type_match2,
+        RenderContext,
+    },
     CallableSnippets,
 };
 
@@ -62,6 +68,7 @@ fn render(
         ),
         _ => (name.unescaped().to_smol_str(), name.to_smol_str()),
     };
+
     let mut item = CompletionItem::new(
         if func.self_param(db).is_some() {
             CompletionItemKind::Method
@@ -77,8 +84,48 @@ fn render(
         .as_assoc_item(ctx.db())
         .and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
         .map_or(false, |trait_| completion.is_ops_trait(trait_));
+
+    // TODO next step figure out how to unify function typesk, we need to convert fndef to actual callable type
+
+    let type_match = if let Some(ref t) = completion.expected_type {
+        if let Some(t) = t.as_callable(db) {
+            let (mut param_types_exp, ret_type_exp) = (
+                t.params(db).into_iter().map(|(_, ty)| ty).collect::>(),
+                t.return_type(),
+            );
+
+            param_types_exp.push(ret_type_exp);
+
+            let mut param_types = func
+                .ty(db)
+                .as_callable(db)
+                .unwrap()
+                .params(db)
+                .into_iter()
+                .map(|(_, ty)| ty)
+                .collect::>();
+            param_types.push(ret_type.clone());
+
+            if param_types.len() != param_types_exp.len() {
+                None
+            } else {
+                if param_types_exp.iter().zip(param_types).all(|(expected_type, item_type)| {
+                    compute_type_match2(completion, &expected_type, &item_type).is_some()
+                }) {
+                    Some(CompletionRelevanceTypeMatch::CouldUnify)
+                } else {
+                    None
+                }
+            }
+        } else {
+            None
+        }
+    } else {
+        None
+    };
+
     item.set_relevance(CompletionRelevance {
-        type_match: compute_type_match(completion, &ret_type),
+        type_match,
         exact_name_match: compute_exact_name_match(completion, &call),
         is_op_method,
         ..ctx.completion_relevance()

From 3935da3046e48574a3ce2a926e4a93d9862f1db4 Mon Sep 17 00:00:00 2001
From: Jessie Chatham Spencer 
Date: Sat, 4 Nov 2023 12:31:42 +0000
Subject: [PATCH 48/62] Implement function type matching

---
 crates/hir/src/lib.rs                        |   4 +-
 crates/ide-completion/src/render.rs          | 141 ++++++++++++-------
 crates/ide-completion/src/render/function.rs |  51 +------
 3 files changed, 95 insertions(+), 101 deletions(-)

diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 0ad84014553c..ca838c7a51e1 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -4564,8 +4564,8 @@ impl Type {
 // FIXME: Document this
 #[derive(Debug)]
 pub struct Callable {
-    pub ty: Type,
-    pub sig: CallableSig,
+    ty: Type,
+    sig: CallableSig,
     callee: Callee,
     /// Whether this is a method that was called with method call syntax.
     pub(crate) is_bound_method: bool,
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index e8880927746c..cbfdda5585c9 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -10,9 +10,7 @@ pub(crate) mod variant;
 pub(crate) mod union_literal;
 pub(crate) mod literal;
 
-use core::panic;
-
-use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
+use hir::{AsAssocItem, Function, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
 use ide_db::{
     documentation::{Documentation, HasDocs},
     helpers::item_name,
@@ -395,17 +393,14 @@ fn render_resolution_path(
         ScopeDef::ModuleDef(ModuleDef::Adt(adt)) | ScopeDef::AdtSelfType(adt) => {
             set_item_relevance(adt.ty(db))
         }
-        ScopeDef::ModuleDef(ModuleDef::Function(func)) => {
-            set_item_relevance(func.ty(db).as_callable(db).unwrap().ty)
-        }
-        ScopeDef::ModuleDef(ModuleDef::Variant(variant)) => {
-            set_item_relevance(variant.parent_enum(db).ty(db))
-        }
+        // Functions are handled at the start of the function.
+        ScopeDef::ModuleDef(ModuleDef::Function(_)) => (), // TODO: Should merge with the match case earlier in the function?
+        // Enum variants are handled at the start of the function.
+        ScopeDef::ModuleDef(ModuleDef::Variant(_)) => (),
         ScopeDef::ModuleDef(ModuleDef::Const(konst)) => set_item_relevance(konst.ty(db)),
         ScopeDef::ModuleDef(ModuleDef::Static(stat)) => set_item_relevance(stat.ty(db)),
         ScopeDef::ModuleDef(ModuleDef::BuiltinType(bt)) => set_item_relevance(bt.ty(db)),
         ScopeDef::ImplSelfType(imp) => set_item_relevance(imp.self_ty(db)),
-
         ScopeDef::GenericParam(_)
         | ScopeDef::Label(_)
         | ScopeDef::Unknown
@@ -502,6 +497,20 @@ fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> boo
     }
 }
 
+fn match_types(
+    ctx: &CompletionContext<'_>,
+    ty1: &hir::Type,
+    ty2: &hir::Type,
+) -> Option {
+    if ty1 == ty2 {
+        Some(CompletionRelevanceTypeMatch::Exact)
+    } else if ty1.could_unify_with(ctx.db, ty2) {
+        Some(CompletionRelevanceTypeMatch::CouldUnify)
+    } else {
+        None
+    }
+}
+
 fn compute_type_match(
     ctx: &CompletionContext<'_>,
     completion_ty: &hir::Type,
@@ -514,35 +523,42 @@ fn compute_type_match(
         return None;
     }
 
-    if completion_ty == expected_type {
-        Some(CompletionRelevanceTypeMatch::Exact)
-    } else if expected_type.could_unify_with(ctx.db, completion_ty) {
-        Some(CompletionRelevanceTypeMatch::CouldUnify)
-    } else {
-        None
-    }
+    match_types(ctx, expected_type, completion_ty)
 }
 
-fn compute_type_match2(
+fn compute_function_type_match(
     ctx: &CompletionContext<'_>,
-    completion_ty1: &hir::Type,
-    completion_ty2: &hir::Type,
+    func: &Function,
 ) -> Option {
-    let expected_type = completion_ty1;
+    // We compute a vec of function parameters + the return type for the expected
+    // type as well as the function we are matching with. Doing this allows for
+    // matching all of the types in one iterator.
 
-    // We don't ever consider unit type to be an exact type match, since
-    // nearly always this is not meaningful to the user.
-    if expected_type.is_unit() {
+    let expected_callable = ctx.expected_type.as_ref()?.as_callable(ctx.db)?;
+    let expected_types = expected_callable.params(ctx.db).into_iter().map(|param| param.1);
+    let actual_types =
+        func.ty(ctx.db).as_callable(ctx.db)?.params(ctx.db).into_iter().map(|param| param.1);
+
+    if expected_types.len() != actual_types.len() {
         return None;
     }
 
-    if completion_ty2 == expected_type {
-        Some(CompletionRelevanceTypeMatch::Exact)
-    } else if expected_type.could_unify_with(ctx.db, completion_ty2) {
-        Some(CompletionRelevanceTypeMatch::CouldUnify)
-    } else {
-        None
+    let mut matches = expected_types
+        .zip(actual_types)
+        .chain([(expected_callable.return_type(), func.ret_type(ctx.db))])
+        .map(|(expected_type, actual_type)| match_types(ctx, &expected_type, &actual_type));
+
+    // Any missing type match indicates that these types can not be unified.
+    if matches.any(|type_match| type_match.is_none()) {
+        return None;
     }
+
+    // If any of the types are unifiable but not exact we consider the function types as a whole
+    // to be unifiable. Otherwise if every pair of types is an exact match the functions are an
+    // exact type match.
+    matches
+        .find(|type_match| matches!(type_match, Some(CompletionRelevanceTypeMatch::CouldUnify)))
+        .unwrap_or(Some(CompletionRelevanceTypeMatch::Exact))
 }
 
 fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool {
@@ -796,7 +812,7 @@ fn main() {
         );
     }
 
-    // TODO: does this test even make sense?
+    // TODO: How dowe test ModuleDef::Variant(Variant?)
     #[test]
     fn set_enum_variant_type_completion_info() {
         check_relevance(
@@ -820,7 +836,7 @@ pub mod test_mod_a {
 fn test(input: dep::test_mod_b::Enum) { }
 
 fn main() {
-    test(Enum$0);
+    test(Enum::Variant$0);
 }
 "#,
             expect![[r#"
@@ -859,7 +875,7 @@ fn main() {
 }
 "#,
             expect![[r#"
-                fn Function (use dep::test_mod_a::Function) [type_could_unify+requires_import]
+                fn Function (use dep::test_mod_a::Function) [type+requires_import]
                 fn main []
                 fn test []
                 md dep []
@@ -868,7 +884,6 @@ fn main() {
         );
     }
 
-    // TODO This test does not trigger the const case
     #[test]
     fn set_const_type_completion_info() {
         check_relevance(
@@ -933,8 +948,38 @@ fn main() {
         );
     }
 
-    // TODO: seems like something is going wrong here. Exapt type match has no effect
-    // EDIT: maybe it is actually working
+    #[test]
+    fn set_self_type_completion_info_with_params() {
+        check_relevance(
+            r#"
+//- /lib.rs crate:dep
+pub struct Struct;
+
+impl Struct {
+    pub fn Function(&self, input: i32) -> bool {
+                false
+    }
+}
+
+
+//- /main.rs crate:main deps:dep
+
+use dep::Struct;
+
+
+fn test(input: fn(&dep::Struct, i32) -> bool) { }
+
+fn main() {
+    test(Struct::Function$0);
+}
+
+"#,
+            expect![[r#"
+                me Function [type]
+            "#]],
+        );
+    }
+
     #[test]
     fn set_self_type_completion_info() {
         check_relevance(
@@ -964,34 +1009,26 @@ fn func(input: Struct) { }
         );
     }
 
-    // TODO: how do we actually test builtins?
-
     #[test]
     fn set_builtin_type_completion_info() {
         check_relevance(
             r#"
-//- /lib.rs crate:dep
-
-pub mod test_mod_b {
-            static STATIC: i32 = 5;
-}
+//- /main.rs crate:main 
 
-            pub mod test_mod_a {
-            static STATIC: &str = "test";
-}
-
-//- /main.rs crate:main deps:dep
-
-fn test(input: i32) { }
+fn test(input: bool) { }
+    pub Input: bool = false; 
 
 fn main() {
-    test(STATIC$0);
+    let input = false; 
+    let inputbad = 3; 
+    test(inp$0);
 }
 "#,
             expect![[r#"
+                lc input [type+name+local]
+                lc inputbad [local]
                 fn main() []
                 fn test(…) []
-                md dep []
             "#]],
         );
     }
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index c2e06c926f12..ff84aa8742ea 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -1,6 +1,6 @@
 //! Renderer for function calls.
 
-use hir::{db::HirDatabase, AsAssocItem, Callable, HirDisplay, Type};
+use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
 use ide_db::{SnippetCap, SymbolKind};
 use itertools::Itertools;
 use stdx::{format_to, to_lower_snake_case};
@@ -8,13 +8,9 @@ use syntax::{AstNode, SmolStr};
 
 use crate::{
     context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind},
-    item::{
-        Builder, CompletionItem, CompletionItemKind, CompletionRelevance,
-        CompletionRelevanceTypeMatch,
-    },
+    item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
     render::{
-        compute_exact_name_match, compute_ref_match, compute_type_match, compute_type_match2,
-        RenderContext,
+        compute_exact_name_match, compute_function_type_match, compute_ref_match, RenderContext,
     },
     CallableSnippets,
 };
@@ -85,47 +81,8 @@ fn render(
         .and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
         .map_or(false, |trait_| completion.is_ops_trait(trait_));
 
-    // TODO next step figure out how to unify function typesk, we need to convert fndef to actual callable type
-
-    let type_match = if let Some(ref t) = completion.expected_type {
-        if let Some(t) = t.as_callable(db) {
-            let (mut param_types_exp, ret_type_exp) = (
-                t.params(db).into_iter().map(|(_, ty)| ty).collect::>(),
-                t.return_type(),
-            );
-
-            param_types_exp.push(ret_type_exp);
-
-            let mut param_types = func
-                .ty(db)
-                .as_callable(db)
-                .unwrap()
-                .params(db)
-                .into_iter()
-                .map(|(_, ty)| ty)
-                .collect::>();
-            param_types.push(ret_type.clone());
-
-            if param_types.len() != param_types_exp.len() {
-                None
-            } else {
-                if param_types_exp.iter().zip(param_types).all(|(expected_type, item_type)| {
-                    compute_type_match2(completion, &expected_type, &item_type).is_some()
-                }) {
-                    Some(CompletionRelevanceTypeMatch::CouldUnify)
-                } else {
-                    None
-                }
-            }
-        } else {
-            None
-        }
-    } else {
-        None
-    };
-
     item.set_relevance(CompletionRelevance {
-        type_match,
+        type_match: compute_function_type_match(completion, &func),
         exact_name_match: compute_exact_name_match(completion, &call),
         is_op_method,
         ..ctx.completion_relevance()

From 303edff24aa5ff703e0000fc99eb773830883e57 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 8 Dec 2023 13:19:34 +0100
Subject: [PATCH 49/62] Cleanup

---
 crates/ide-completion/src/completions/dot.rs  |  12 +-
 .../src/completions/item_list/trait_impl.rs   |  30 +-
 crates/ide-completion/src/completions/use_.rs |   4 +-
 crates/ide-completion/src/item.rs             |   9 +-
 crates/ide-completion/src/render.rs           | 142 ++---
 crates/ide-completion/src/render/function.rs  |  67 +--
 crates/ide-completion/src/tests/expression.rs |  98 ++--
 crates/ide-completion/src/tests/flyimport.rs  |  54 +-
 crates/ide-completion/src/tests/item.rs       |  24 +-
 crates/ide-completion/src/tests/pattern.rs    |   4 +-
 crates/ide-completion/src/tests/predicate.rs  |  42 +-
 crates/ide-completion/src/tests/record.rs     |   4 +-
 crates/ide-completion/src/tests/special.rs    |  70 +--
 crates/ide-completion/src/tests/type_pos.rs   | 486 +++++++++---------
 crates/ide-completion/src/tests/use_tree.rs   |  20 +-
 15 files changed, 516 insertions(+), 550 deletions(-)

diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 57e06461099e..613a35dcb108 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -940,9 +940,9 @@ impl Foo { fn foo(&self) { $0 } }"#,
             expect![[r#"
                 fd self.field i32
                 lc self       &Foo
-                sp Self
-                st Foo
-                bt u32
+                sp Self       Foo
+                st Foo        Foo
+                bt u32        u32
                 me self.foo() fn(&self)
             "#]],
         );
@@ -954,9 +954,9 @@ impl Foo { fn foo(&mut self) { $0 } }"#,
             expect![[r#"
                 fd self.0     i32
                 lc self       &mut Foo
-                sp Self
-                st Foo
-                bt u32
+                sp Self       Foo
+                st Foo        Foo
+                bt u32        u32
                 me self.foo() fn(&mut self)
             "#]],
         );
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 42dfbfc7d9a1..b0e4d8a5acd1 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -417,10 +417,10 @@ impl Test for T {
 }
 ",
             expect![[r#"
-                sp Self
-                st T
+                sp Self T
+                st T    T
                 tt Test
-                bt u32
+                bt u32  u32
             "#]],
         );
 
@@ -526,10 +526,10 @@ impl Test for T {
 }
 ",
             expect![[r#"
-                sp Self
-                st T
+                sp Self T
+                st T    T
                 tt Test
-                bt u32
+                bt u32  u32
             "#]],
         );
 
@@ -543,10 +543,10 @@ impl Test for T {
 }
 ",
             expect![[r#"
-                sp Self
-                st T
+                sp Self T
+                st T    T
                 tt Test
-                bt u32
+                bt u32  u32
             "#]],
         );
 
@@ -562,10 +562,10 @@ impl Test for T {
 }
 ",
             expect![[r#"
-                sp Self
-                st T
+                sp Self T
+                st T    T
                 tt Test
-                bt u32
+                bt u32  u32
             "#]],
         );
 
@@ -610,10 +610,10 @@ impl Test for T {
 }
 ",
             expect![[r#"
-                sp Self
-                st T
+                sp Self T
+                st T    T
                 tt Test
-                bt u32
+                bt u32  u32
             "#]],
         );
 
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index 7a60030e9ef7..81107c1f419d 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -71,9 +71,9 @@ pub(crate) fn complete_use_path(
 
                         if add_resolution {
                             let mut builder = Builder::from_resolution(ctx, path_ctx, name, def);
-                            builder.set_relevance(CompletionRelevance {
+                            builder.with_relevance(|r| CompletionRelevance {
                                 is_name_already_imported,
-                                ..Default::default()
+                                ..r
                             });
                             acc.add(builder.build(ctx.db));
                         }
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index fdb2c97a8f28..ad1bd603579d 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -1,6 +1,6 @@
 //! See `CompletionItem` structure.
 
-use std::fmt;
+use std::{fmt, mem};
 
 use hir::Mutability;
 use ide_db::{
@@ -579,6 +579,13 @@ impl Builder {
         self.relevance = relevance;
         self
     }
+    pub(crate) fn with_relevance(
+        &mut self,
+        relevance: impl FnOnce(CompletionRelevance) -> CompletionRelevance,
+    ) -> &mut Builder {
+        self.relevance = relevance(mem::take(&mut self.relevance));
+        self
+    }
     pub(crate) fn trigger_call_info(&mut self) -> &mut Builder {
         self.trigger_call_info = true;
         self
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index cbfdda5585c9..272999fe336d 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -10,7 +10,7 @@ pub(crate) mod variant;
 pub(crate) mod union_literal;
 pub(crate) mod literal;
 
-use hir::{AsAssocItem, Function, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
+use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
 use ide_db::{
     documentation::{Documentation, HasDocs},
     helpers::item_name,
@@ -393,10 +393,10 @@ fn render_resolution_path(
         ScopeDef::ModuleDef(ModuleDef::Adt(adt)) | ScopeDef::AdtSelfType(adt) => {
             set_item_relevance(adt.ty(db))
         }
-        // Functions are handled at the start of the function.
-        ScopeDef::ModuleDef(ModuleDef::Function(_)) => (), // TODO: Should merge with the match case earlier in the function?
-        // Enum variants are handled at the start of the function.
-        ScopeDef::ModuleDef(ModuleDef::Variant(_)) => (),
+        // Filtered out above
+        ScopeDef::ModuleDef(
+            ModuleDef::Function(_) | ModuleDef::Variant(_) | ModuleDef::Macro(_),
+        ) => (),
         ScopeDef::ModuleDef(ModuleDef::Const(konst)) => set_item_relevance(konst.ty(db)),
         ScopeDef::ModuleDef(ModuleDef::Static(stat)) => set_item_relevance(stat.ty(db)),
         ScopeDef::ModuleDef(ModuleDef::BuiltinType(bt)) => set_item_relevance(bt.ty(db)),
@@ -404,11 +404,12 @@ fn render_resolution_path(
         ScopeDef::GenericParam(_)
         | ScopeDef::Label(_)
         | ScopeDef::Unknown
-        | ScopeDef::ModuleDef(ModuleDef::Trait(_))
-        | ScopeDef::ModuleDef(ModuleDef::TraitAlias(_))
-        | ScopeDef::ModuleDef(ModuleDef::Macro(_))
-        | ScopeDef::ModuleDef(ModuleDef::Module(_))
-        | ScopeDef::ModuleDef(ModuleDef::TypeAlias(_)) => (),
+        | ScopeDef::ModuleDef(
+            ModuleDef::Trait(_)
+            | ModuleDef::TraitAlias(_)
+            | ModuleDef::Module(_)
+            | ModuleDef::TypeAlias(_),
+        ) => (),
     };
 
     item
@@ -497,6 +498,7 @@ fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> boo
     }
 }
 
+// FIXME: This checks types without possible coercions which some completions might want to do
 fn match_types(
     ctx: &CompletionContext<'_>,
     ty1: &hir::Type,
@@ -526,41 +528,6 @@ fn compute_type_match(
     match_types(ctx, expected_type, completion_ty)
 }
 
-fn compute_function_type_match(
-    ctx: &CompletionContext<'_>,
-    func: &Function,
-) -> Option {
-    // We compute a vec of function parameters + the return type for the expected
-    // type as well as the function we are matching with. Doing this allows for
-    // matching all of the types in one iterator.
-
-    let expected_callable = ctx.expected_type.as_ref()?.as_callable(ctx.db)?;
-    let expected_types = expected_callable.params(ctx.db).into_iter().map(|param| param.1);
-    let actual_types =
-        func.ty(ctx.db).as_callable(ctx.db)?.params(ctx.db).into_iter().map(|param| param.1);
-
-    if expected_types.len() != actual_types.len() {
-        return None;
-    }
-
-    let mut matches = expected_types
-        .zip(actual_types)
-        .chain([(expected_callable.return_type(), func.ret_type(ctx.db))])
-        .map(|(expected_type, actual_type)| match_types(ctx, &expected_type, &actual_type));
-
-    // Any missing type match indicates that these types can not be unified.
-    if matches.any(|type_match| type_match.is_none()) {
-        return None;
-    }
-
-    // If any of the types are unifiable but not exact we consider the function types as a whole
-    // to be unifiable. Otherwise if every pair of types is an exact match the functions are an
-    // exact type match.
-    matches
-        .find(|type_match| matches!(type_match, Some(CompletionRelevanceTypeMatch::CouldUnify)))
-        .unwrap_or(Some(CompletionRelevanceTypeMatch::Exact))
-}
-
 fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool {
     ctx.expected_name.as_ref().map_or(false, |name| name.text() == completion_name)
 }
@@ -745,16 +712,16 @@ fn main() {
 
 pub mod test_mod_b {
     pub union Union {
-            a: i32,
-            b: i32
-            }
+        a: i32,
+        b: i32
+    }
 }
 
 pub mod test_mod_a {
     pub enum Union {
-                a: i32,
-                b: i32
-            }
+        a: i32,
+        b: i32
+    }
 }
 
 //- /main.rs crate:main deps:dep
@@ -783,14 +750,14 @@ fn main() {
 
 pub mod test_mod_b {
     pub enum Enum {
-                variant
-            }
+        variant
+    }
 }
 
 pub mod test_mod_a {
     pub enum Enum {
-                variant
-            }
+        variant
+    }
 }
 
 //- /main.rs crate:main deps:dep
@@ -812,7 +779,6 @@ fn main() {
         );
     }
 
-    // TODO: How dowe test ModuleDef::Variant(Variant?)
     #[test]
     fn set_enum_variant_type_completion_info() {
         check_relevance(
@@ -821,14 +787,14 @@ fn main() {
 
 pub mod test_mod_b {
     pub enum Enum {
-                Variant
-            }
+        Variant
+    }
 }
 
 pub mod test_mod_a {
     pub enum Enum {
-                Variant
-            }
+        Variant
+    }
 }
 
 //- /main.rs crate:main deps:dep
@@ -836,16 +802,14 @@ pub mod test_mod_a {
 fn test(input: dep::test_mod_b::Enum) { }
 
 fn main() {
-    test(Enum::Variant$0);
+    test(Variant$0);
 }
 "#,
             expect![[r#"
                 ev dep::test_mod_b::Enum::Variant [type_could_unify]
-                en Enum (use dep::test_mod_b::Enum) [type_could_unify+requires_import]
                 fn main() []
                 fn test(…) []
                 md dep []
-                en Enum (use dep::test_mod_a::Enum) [requires_import]
             "#]],
         );
     }
@@ -857,13 +821,11 @@ fn main() {
 //- /lib.rs crate:dep
 
 pub mod test_mod_b {
-    pub fn Function(j: isize) -> i32 {
-            }
+    pub fn function(j: isize) -> i32 {}
 }
 
-            pub mod test_mod_a {
-    pub fn Function(i: usize) -> i32 {
-            }
+pub mod test_mod_a {
+    pub fn function(i: usize) -> i32 {}
 }
 
 //- /main.rs crate:main deps:dep
@@ -871,15 +833,15 @@ pub mod test_mod_b {
 fn test(input: fn(usize) -> i32) { }
 
 fn main() {
-    test(Function$0);
+    test(function$0);
 }
 "#,
             expect![[r#"
-                fn Function (use dep::test_mod_a::Function) [type+requires_import]
                 fn main []
                 fn test []
                 md dep []
-                fn Function (use dep::test_mod_b::Function) [requires_import]
+                fn function (use dep::test_mod_a::function) [requires_import]
+                fn function (use dep::test_mod_b::function) [requires_import]
             "#]],
         );
     }
@@ -891,11 +853,11 @@ fn main() {
 //- /lib.rs crate:dep
 
 pub mod test_mod_b {
-            pub const CONST: i32 = 1;
+    pub const CONST: i32 = 1;
 }
 
 pub mod test_mod_a {
-            pub const CONST: i64 = 2;
+    pub const CONST: i64 = 2;
 }
 
 //- /main.rs crate:main deps:dep
@@ -923,11 +885,11 @@ fn main() {
 //- /lib.rs crate:dep
 
 pub mod test_mod_b {
-            pub static STATIC: i32 = 5;
+    pub static STATIC: i32 = 5;
 }
 
 pub mod test_mod_a {
-            pub static STATIC: i64 = 5;
+    pub static STATIC: i64 = 5;
 }
 
 //- /main.rs crate:main deps:dep
@@ -975,7 +937,7 @@ fn main() {
 
 "#,
             expect![[r#"
-                me Function [type]
+                me Function []
             "#]],
         );
     }
@@ -990,7 +952,7 @@ struct Struct;
 
 impl Struct {
 fn test(&self) {
-        func(Self$0);            
+        func(Self$0);
     }
 }
 
@@ -1013,14 +975,14 @@ fn func(input: Struct) { }
     fn set_builtin_type_completion_info() {
         check_relevance(
             r#"
-//- /main.rs crate:main 
+//- /main.rs crate:main
 
 fn test(input: bool) { }
-    pub Input: bool = false; 
+    pub Input: bool = false;
 
 fn main() {
-    let input = false; 
-    let inputbad = 3; 
+    let input = false;
+    let inputbad = 3;
     test(inp$0);
 }
 "#,
@@ -1424,6 +1386,7 @@ use self::E::*;
                         kind: SymbolKind(
                             Enum,
                         ),
+                        detail: "E",
                         documentation: Documentation(
                             "enum docs",
                         ),
@@ -1668,6 +1631,7 @@ fn go(world: &WorldSnapshot) { go(w$0) }
                 st WorldSnapshot {…} []
                 st &WorldSnapshot {…} [type]
                 st WorldSnapshot []
+                st &WorldSnapshot [type]
                 fn go(…) []
             "#]],
         );
@@ -1767,6 +1731,7 @@ fn main() {
                 st S []
                 st &mut S [type]
                 st S []
+                st &mut S [type]
                 fn foo(…) []
                 fn main() []
             "#]],
@@ -1783,7 +1748,7 @@ fn main() {
             expect![[r#"
                 lc s [type+name+local]
                 st S [type]
-                st S []
+                st S [type]
                 fn foo(…) []
                 fn main() []
             "#]],
@@ -1800,7 +1765,7 @@ fn main() {
             expect![[r#"
                 lc ssss [type+local]
                 st S [type]
-                st S []
+                st S [type]
                 fn foo(…) []
                 fn main() []
             "#]],
@@ -1839,7 +1804,9 @@ fn main() {
                 st S []
                 st &S [type]
                 st S []
+                st &S [type]
                 st T []
+                st &T [type]
                 fn foo(…) []
                 fn main() []
                 md core []
@@ -1885,7 +1852,9 @@ fn main() {
                 st S []
                 st &mut S [type]
                 st S []
+                st &mut S [type]
                 st T []
+                st &mut T [type]
                 fn foo(…) []
                 fn main() []
                 md core []
@@ -1924,7 +1893,7 @@ fn bar(t: Foo) {}
             expect![[r#"
                 ev Foo::A [type]
                 ev Foo::B [type]
-                en Foo []
+                en Foo [type]
                 fn bar(…) []
                 fn foo() []
             "#]],
@@ -1947,6 +1916,7 @@ fn bar(t: &Foo) {}
                 ev Foo::B []
                 ev &Foo::B [type]
                 en Foo []
+                en &Foo [type]
                 fn bar(…) []
                 fn foo() []
             "#]],
@@ -1980,7 +1950,9 @@ fn main() {
                 st S []
                 st &S [type]
                 st S []
+                st &S [type]
                 st T []
+                st &T [type]
                 fn bar() []
                 fn &bar() [type]
                 fn foo(…) []
@@ -2219,8 +2191,8 @@ fn foo() {
                 lc foo [type+local]
                 ev Foo::A(…) [type_could_unify]
                 ev Foo::B [type_could_unify]
+                en Foo [type_could_unify]
                 fn foo() []
-                en Foo []
                 fn bar() []
                 fn baz() []
             "#]],
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index ff84aa8742ea..d23ed71fdcc6 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -9,9 +9,7 @@ use syntax::{AstNode, SmolStr};
 use crate::{
     context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind},
     item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
-    render::{
-        compute_exact_name_match, compute_function_type_match, compute_ref_match, RenderContext,
-    },
+    render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
     CallableSnippets,
 };
 
@@ -81,8 +79,30 @@ fn render(
         .and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
         .map_or(false, |trait_| completion.is_ops_trait(trait_));
 
+    let (has_dot_receiver, has_call_parens, cap) = match func_kind {
+        FuncKind::Function(&PathCompletionCtx {
+            kind: PathKind::Expr { .. },
+            has_call_parens,
+            ..
+        }) => (false, has_call_parens, ctx.completion.config.snippet_cap),
+        FuncKind::Method(&DotAccess { kind: DotAccessKind::Method { has_parens }, .. }, _) => {
+            (true, has_parens, ctx.completion.config.snippet_cap)
+        }
+        FuncKind::Method(DotAccess { kind: DotAccessKind::Field { .. }, .. }, _) => {
+            (true, false, ctx.completion.config.snippet_cap)
+        }
+        _ => (false, false, None),
+    };
+    let complete_call_parens = cap
+        .filter(|_| !has_call_parens)
+        .and_then(|cap| Some((cap, params(ctx.completion, func, &func_kind, has_dot_receiver)?)));
+
     item.set_relevance(CompletionRelevance {
-        type_match: compute_function_type_match(completion, &func),
+        type_match: if has_call_parens || complete_call_parens.is_some() {
+            compute_type_match(completion, &ret_type)
+        } else {
+            compute_type_match(completion, &func.ty(db))
+        },
         exact_name_match: compute_exact_name_match(completion, &call),
         is_op_method,
         ..ctx.completion_relevance()
@@ -112,42 +132,9 @@ fn render(
         .detail(detail)
         .lookup_by(name.unescaped().to_smol_str());
 
-    match ctx.completion.config.snippet_cap {
-        Some(cap) => {
-            let complete_params = match func_kind {
-                FuncKind::Function(PathCompletionCtx {
-                    kind: PathKind::Expr { .. },
-                    has_call_parens: false,
-                    ..
-                }) => Some(false),
-                FuncKind::Method(
-                    DotAccess {
-                        kind:
-                            DotAccessKind::Method { has_parens: false } | DotAccessKind::Field { .. },
-                        ..
-                    },
-                    _,
-                ) => Some(true),
-                _ => None,
-            };
-            if let Some(has_dot_receiver) = complete_params {
-                if let Some((self_param, params)) =
-                    params(ctx.completion, func, &func_kind, has_dot_receiver)
-                {
-                    add_call_parens(
-                        &mut item,
-                        completion,
-                        cap,
-                        call,
-                        escaped_call,
-                        self_param,
-                        params,
-                    );
-                }
-            }
-        }
-        _ => (),
-    };
+    if let Some((cap, (self_param, params))) = complete_call_parens {
+        add_call_parens(&mut item, completion, cap, call, escaped_call, self_param, params);
+    }
 
     match ctx.import_to_add {
         Some(import_to_add) => {
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index be5b7f8a3404..e6969c8db300 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -26,22 +26,22 @@ fn baz() {
             "#,
         // This should not contain `FooDesc {…}`.
         expect![[r#"
-            ct CONST
-            en Enum
+            ct CONST         Unit
+            en Enum          Enum
             fn baz()         fn()
             fn create_foo(…) fn(&FooDesc)
             fn function()    fn()
             ma makro!(…)     macro_rules! makro
             md _69latrick
             md module
-            sc STATIC
-            st FooDesc
-            st Record
-            st Tuple
-            st Unit
-            un Union
+            sc STATIC        Unit
+            st FooDesc       FooDesc
+            st Record        Record
+            st Tuple         Tuple
+            st Unit          Unit
+            un Union         Union
             ev TupleV(…)     TupleV(u32)
-            bt u32
+            bt u32           u32
             kw crate::
             kw false
             kw for
@@ -83,7 +83,7 @@ fn func(param0 @ (param1, param2): (i32, i32)) {
             lc param0     (i32, i32)
             lc param1     i32
             lc param2     i32
-            bt u32
+            bt u32        u32
             kw crate::
             kw false
             kw for
@@ -117,24 +117,24 @@ impl Unit {
 "#,
         // `self` is in here twice, once as the module, once as the local
         expect![[r#"
-            ct CONST
+            ct CONST        Unit
             cp CONST_PARAM
-            en Enum
+            en Enum         Enum
             fn function()   fn()
             fn local_func() fn()
             lc self         Unit
             ma makro!(…)    macro_rules! makro
             md module
             md qualified
-            sp Self
-            sc STATIC
-            st Record
-            st Tuple
-            st Unit
+            sp Self         Unit
+            sc STATIC       Unit
+            st Record       Record
+            st Tuple        Tuple
+            st Unit         Unit
             tp TypeParam
-            un Union
+            un Union        Union
             ev TupleV(…)    TupleV(u32)
-            bt u32
+            bt u32          u32
             kw const
             kw crate::
             kw enum
@@ -181,18 +181,18 @@ impl Unit {
 }
 "#,
         expect![[r#"
-            ct CONST
-            en Enum
+            ct CONST      Unit
+            en Enum       Enum
             fn function() fn()
             ma makro!(…)  macro_rules! makro
             md module
             md qualified
-            sc STATIC
-            st Record
-            st Tuple
-            st Unit
+            sc STATIC     Unit
+            st Record     Record
+            st Tuple      Tuple
+            st Unit       Unit
             tt Trait
-            un Union
+            un Union      Union
             ev TupleV(…)  TupleV(u32)
             ?? Unresolved
         "#]],
@@ -211,7 +211,7 @@ fn complete_in_block() {
 "#,
         expect![[r#"
             fn foo()       fn()
-            bt u32
+            bt u32         u32
             kw const
             kw crate::
             kw enum
@@ -256,7 +256,7 @@ fn complete_after_if_expr() {
 "#,
         expect![[r#"
             fn foo()       fn()
-            bt u32
+            bt u32         u32
             kw const
             kw crate::
             kw else
@@ -304,7 +304,7 @@ fn complete_in_match_arm() {
 "#,
         expect![[r#"
             fn foo()     fn()
-            bt u32
+            bt u32       u32
             kw crate::
             kw false
             kw for
@@ -328,7 +328,7 @@ fn completes_in_loop_ctx() {
         r"fn my() { loop { $0 } }",
         expect![[r#"
             fn my()        fn()
-            bt u32
+            bt u32         u32
             kw break
             kw const
             kw continue
@@ -370,7 +370,7 @@ fn completes_in_let_initializer() {
         r#"fn main() { let _ = $0 }"#,
         expect![[r#"
             fn main()    fn()
-            bt u32
+            bt u32       u32
             kw crate::
             kw false
             kw for
@@ -403,8 +403,8 @@ fn foo() {
 "#,
         expect![[r#"
             fn foo()     fn()
-            st Foo
-            bt u32
+            st Foo       Foo
+            bt u32       u32
             kw crate::
             kw false
             kw for
@@ -439,7 +439,7 @@ fn foo() {
         expect![[r#"
             fn foo()     fn()
             lc bar       i32
-            bt u32
+            bt u32       u32
             kw crate::
             kw false
             kw for
@@ -470,7 +470,7 @@ fn quux(x: i32) {
             fn quux(…)   fn(i32)
             lc x         i32
             ma m!(…)     macro_rules! m
-            bt u32
+            bt u32       u32
             kw crate::
             kw false
             kw for
@@ -497,7 +497,7 @@ fn quux(x: i32) {
             fn quux(…)   fn(i32)
             lc x         i32
             ma m!(…)     macro_rules! m
-            bt u32
+            bt u32       u32
             kw crate::
             kw false
             kw for
@@ -683,11 +683,11 @@ fn brr() {
 }
 "#,
         expect![[r#"
-            en HH
+            en HH              HH
             fn brr()           fn()
-            st YoloVariant
+            st YoloVariant     YoloVariant
             st YoloVariant {…} YoloVariant { f: usize }
-            bt u32
+            bt u32             u32
             kw crate::
             kw false
             kw for
@@ -749,7 +749,7 @@ fn foo() { if foo {} $0 }
 "#,
         expect![[r#"
             fn foo()       fn()
-            bt u32
+            bt u32         u32
             kw const
             kw crate::
             kw else
@@ -789,7 +789,7 @@ fn foo() { if foo {} el$0 }
 "#,
         expect![[r#"
             fn foo()       fn()
-            bt u32
+            bt u32         u32
             kw const
             kw crate::
             kw else
@@ -829,7 +829,7 @@ fn foo() { bar(if foo {} $0) }
 "#,
         expect![[r#"
             fn foo()     fn()
-            bt u32
+            bt u32       u32
             kw crate::
             kw else
             kw else if
@@ -853,7 +853,7 @@ fn foo() { bar(if foo {} el$0) }
 "#,
         expect![[r#"
             fn foo()     fn()
-            bt u32
+            bt u32       u32
             kw crate::
             kw else
             kw else if
@@ -877,7 +877,7 @@ fn foo() { if foo {} $0 let x = 92; }
 "#,
         expect![[r#"
             fn foo()       fn()
-            bt u32
+            bt u32         u32
             kw const
             kw crate::
             kw else
@@ -917,7 +917,7 @@ fn foo() { if foo {} el$0 let x = 92; }
 "#,
         expect![[r#"
             fn foo()       fn()
-            bt u32
+            bt u32         u32
             kw const
             kw crate::
             kw else
@@ -957,7 +957,7 @@ fn foo() { if foo {} el$0 { let x = 92; } }
 "#,
         expect![[r#"
             fn foo()       fn()
-            bt u32
+            bt u32         u32
             kw const
             kw crate::
             kw else
@@ -1009,7 +1009,7 @@ pub struct UnstableThisShouldNotBeListed;
         expect![[r#"
             fn main()      fn()
             md std
-            bt u32
+            bt u32         u32
             kw const
             kw crate::
             kw enum
@@ -1060,8 +1060,8 @@ pub struct UnstableButWeAreOnNightlyAnyway;
         expect![[r#"
             fn main()                 fn()
             md std
-            st UnstableButWeAreOnNightlyAnyway
-            bt u32
+            st UnstableButWeAreOnNightlyAnyway UnstableButWeAreOnNightlyAnyway
+            bt u32                    u32
             kw const
             kw crate::
             kw enum
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index 21f693d79f1d..9a4a94a24566 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -139,10 +139,10 @@ fn main() {
 }
 "#,
         expect![[r#"
-            st Rc (use dep::Rc)
-            st Rcar (use dep::Rcar)
-            st Rc (use dep::some_module::Rc)
-            st Rcar (use dep::some_module::Rcar)
+            st Rc (use dep::Rc)       Rc
+            st Rcar (use dep::Rcar)   Rcar
+            st Rc (use dep::some_module::Rc) Rc
+            st Rcar (use dep::some_module::Rcar) Rcar
         "#]],
     );
     check(
@@ -165,12 +165,12 @@ fn main() {
 }
 "#,
         expect![[r#"
-            ct RC (use dep::RC)
-            st Rc (use dep::Rc)
-            st Rcar (use dep::Rcar)
-            ct RC (use dep::some_module::RC)
-            st Rc (use dep::some_module::Rc)
-            st Rcar (use dep::some_module::Rcar)
+            ct RC (use dep::RC)       ()
+            st Rc (use dep::Rc)       Rc
+            st Rcar (use dep::Rcar)   Rcar
+            ct RC (use dep::some_module::RC) ()
+            st Rc (use dep::some_module::Rc) Rc
+            st Rcar (use dep::some_module::Rcar) Rcar
         "#]],
     );
     check(
@@ -193,8 +193,8 @@ fn main() {
 }
 "#,
         expect![[r#"
-            ct RC (use dep::RC)
-            ct RC (use dep::some_module::RC)
+            ct RC (use dep::RC)       ()
+            ct RC (use dep::some_module::RC) ()
         "#]],
     );
 }
@@ -227,10 +227,10 @@ fn main() {
 }
 "#,
         expect![[r#"
-                st ThirdStruct (use dep::some_module::ThirdStruct)
-                st AfterThirdStruct (use dep::some_module::AfterThirdStruct)
-                st ThiiiiiirdStruct (use dep::some_module::ThiiiiiirdStruct)
-            "#]],
+            st ThirdStruct (use dep::some_module::ThirdStruct) ThirdStruct
+            st AfterThirdStruct (use dep::some_module::AfterThirdStruct) AfterThirdStruct
+            st ThiiiiiirdStruct (use dep::some_module::ThiiiiiirdStruct) ThiiiiiirdStruct
+        "#]],
     );
 }
 
@@ -309,7 +309,7 @@ fn trait_const_fuzzy_completion() {
     check(
         fixture,
         expect![[r#"
-            ct SPECIAL_CONST (use dep::test_mod::TestTrait)
+            ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8
         "#]],
     );
 
@@ -597,7 +597,7 @@ fn main() {
 }
 "#,
         expect![[r#"
-            ct SPECIAL_CONST (use dep::test_mod::TestTrait) DEPRECATED
+            ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED
             fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED
         "#]],
     );
@@ -717,7 +717,7 @@ fn main() {
     check(
         fixture,
         expect![[r#"
-        st Item (use foo::bar::baz::Item)
+            st Item (use foo::bar::baz::Item) Item
         "#]],
     );
 
@@ -759,7 +759,7 @@ fn main() {
     check(
         fixture,
         expect![[r#"
-        ct TEST_ASSOC (use foo::Item)
+            ct TEST_ASSOC (use foo::Item) usize
         "#]],
     );
 
@@ -803,8 +803,8 @@ fn main() {
     check(
         fixture,
         expect![[r#"
-        ct TEST_ASSOC (use foo::bar::Item)
-    "#]],
+            ct TEST_ASSOC (use foo::bar::Item) usize
+        "#]],
     );
 
     check_edit(
@@ -897,7 +897,7 @@ fn main() {
     TES$0
 }"#,
         expect![[r#"
-            ct TEST_CONST (use foo::TEST_CONST)
+            ct TEST_CONST (use foo::TEST_CONST) usize
         "#]],
     );
 
@@ -914,7 +914,7 @@ fn main() {
     tes$0
 }"#,
         expect![[r#"
-            ct TEST_CONST (use foo::TEST_CONST)
+            ct TEST_CONST (use foo::TEST_CONST) usize
             fn test_function() (use foo::test_function) fn() -> i32
         "#]],
     );
@@ -1138,8 +1138,8 @@ mod mud {
 }
 "#,
         expect![[r#"
-                st Struct (use crate::Struct)
-            "#]],
+            st Struct (use crate::Struct) Struct
+        "#]],
     );
 }
 
@@ -1250,7 +1250,7 @@ enum Foo {
 }
 }"#,
         expect![[r#"
-            st Barbara (use foo::Barbara)
+            st Barbara (use foo::Barbara) Barbara
         "#]],
     )
 }
diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs
index 3ef2a7c942bc..de3fd05189f3 100644
--- a/crates/ide-completion/src/tests/item.rs
+++ b/crates/ide-completion/src/tests/item.rs
@@ -18,15 +18,15 @@ fn target_type_or_trait_in_impl_block() {
 impl Tra$0
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             kw crate::
             kw self::
         "#]],
@@ -40,15 +40,15 @@ fn target_type_in_trait_impl_block() {
 impl Trait for Str$0
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             kw crate::
             kw self::
         "#]],
diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs
index b2e8274a84d7..67cf551fce84 100644
--- a/crates/ide-completion/src/tests/pattern.rs
+++ b/crates/ide-completion/src/tests/pattern.rs
@@ -435,7 +435,7 @@ fn foo() {
 }
 "#,
         expect![[r#"
-            st Bar
+            st Bar     Bar
             kw crate::
             kw self::
         "#]],
@@ -450,7 +450,7 @@ fn foo() {
 }
 "#,
         expect![[r#"
-            st Foo
+            st Foo     Foo
             kw crate::
             kw self::
         "#]],
diff --git a/crates/ide-completion/src/tests/predicate.rs b/crates/ide-completion/src/tests/predicate.rs
index 789ad66345b1..46a3e97d3e92 100644
--- a/crates/ide-completion/src/tests/predicate.rs
+++ b/crates/ide-completion/src/tests/predicate.rs
@@ -16,16 +16,16 @@ fn predicate_start() {
 struct Foo<'lt, T, const C: usize> where $0 {}
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Foo<…>
-            st Record
-            st Tuple
-            st Unit
+            st Foo<…>    Foo<'_, {unknown}, _>
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             kw crate::
             kw self::
         "#]],
@@ -89,16 +89,16 @@ fn param_list_for_for_pred() {
 struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Foo<…>
-            st Record
-            st Tuple
-            st Unit
+            st Foo<…>    Foo<'_, {unknown}, _>
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             kw crate::
             kw self::
         "#]],
@@ -114,16 +114,16 @@ impl Record {
 }
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            sp Self
-            st Record
-            st Tuple
-            st Unit
+            sp Self      Record
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             kw crate::
             kw self::
         "#]],
diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs
index 65cefdb0856d..18afde1b7cef 100644
--- a/crates/ide-completion/src/tests/record.rs
+++ b/crates/ide-completion/src/tests/record.rs
@@ -186,10 +186,10 @@ fn main() {
             lc foo                  Foo
             lc thing                i32
             md core
-            st Foo
+            st Foo                  Foo
             st Foo {…}              Foo { foo1: u32, foo2: u32 }
             tt Default
-            bt u32
+            bt u32                  u32
             kw crate::
             kw self::
         "#]],
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index 28c9bffc5eca..f96fb71f2893 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -84,10 +84,10 @@ pub mod prelude {
 }
 "#,
         expect![[r#"
-                md std
-                st Option
-                bt u32
-            "#]],
+            md std
+            st Option Option
+            bt u32    u32
+        "#]],
     );
 }
 
@@ -112,11 +112,11 @@ mod macros {
 }
 "#,
         expect![[r#"
-                fn f()        fn()
-                ma concat!(…) macro_rules! concat
-                md std
-                bt u32
-            "#]],
+            fn f()        fn()
+            ma concat!(…) macro_rules! concat
+            md std
+            bt u32        u32
+        "#]],
     );
 }
 
@@ -142,11 +142,11 @@ pub mod prelude {
 }
 "#,
         expect![[r#"
-                md core
-                md std
-                st String
-                bt u32
-            "#]],
+            md core
+            md std
+            st String String
+            bt u32    u32
+        "#]],
     );
 }
 
@@ -171,10 +171,10 @@ pub mod prelude {
 }
             "#,
         expect![[r#"
-                fn f() fn()
-                md std
-                bt u32
-            "#]],
+            fn f() fn()
+            md std
+            bt u32 u32
+        "#]],
     );
 }
 
@@ -446,10 +446,10 @@ mod p {
 }
 "#,
         expect![[r#"
-                ct RIGHT_CONST
-                fn right_fn()  fn()
-                st RightType
-            "#]],
+            ct RIGHT_CONST u32
+            fn right_fn()  fn()
+            st RightType   WrongType
+        "#]],
     );
 
     check_edit(
@@ -881,7 +881,7 @@ fn main() {
             fn main() fn()
             lc foobar i32
             ma x!(…)  macro_rules! x
-            bt u32
+            bt u32    u32
         "#]],
     )
 }
@@ -1008,8 +1008,8 @@ fn here_we_go() {
 "#,
         expect![[r#"
             fn here_we_go()    fn()
-            st Foo (alias Bar)
-            bt u32
+            st Foo (alias Bar) Foo
+            bt u32             u32
             kw const
             kw crate::
             kw enum
@@ -1057,8 +1057,8 @@ fn here_we_go() {
 "#,
         expect![[r#"
             fn here_we_go()           fn()
-            st Foo (alias Bar, Qux, Baz)
-            bt u32
+            st Foo (alias Bar, Qux, Baz) Foo
+            bt u32                    u32
             kw const
             kw crate::
             kw enum
@@ -1178,7 +1178,7 @@ fn bar() { qu$0 }
         expect![[r#"
             fn bar()             fn()
             fn foo() (alias qux) fn()
-            bt u32
+            bt u32               u32
             kw const
             kw crate::
             kw enum
@@ -1227,7 +1227,7 @@ fn here_we_go() {
 }
 "#,
         expect![[r#"
-            st Bar (alias Qux)
+            st Bar (alias Qux) Bar
         "#]],
     );
 }
@@ -1246,7 +1246,7 @@ fn here_we_go() {
 }
 "#,
         expect![[r#"
-            st Bar (alias Qux)
+            st Bar (alias Qux) Bar
         "#]],
     );
 }
@@ -1267,8 +1267,8 @@ fn here_we_go() {
         expect![[r#"
             fn here_we_go()           fn()
             md foo
-            st Bar (alias Qux) (use foo::Bar)
-            bt u32
+            st Bar (alias Qux) (use foo::Bar) Bar
+            bt u32                    u32
             kw crate::
             kw false
             kw for
@@ -1433,7 +1433,7 @@ fn foo() {
         Some('_'),
         expect![[r#"
             fn foo()       fn()
-            bt u32
+            bt u32         u32
             kw const
             kw crate::
             kw enum
@@ -1485,7 +1485,7 @@ fn foo(_: a_$0) { }
 "#,
         Some('_'),
         expect![[r#"
-            bt u32
+            bt u32     u32
             kw crate::
             kw self::
         "#]],
@@ -1499,7 +1499,7 @@ fn foo() {
         Some('_'),
         expect![[r#"
             tp T
-            bt u32
+            bt u32     u32
             kw crate::
             kw self::
         "#]],
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index d518dd764102..c7161f82ce74 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -17,18 +17,18 @@ struct Foo<'lt, T, const C: usize> {
 }
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            sp Self
-            st Foo<…>
-            st Record
-            st Tuple
-            st Unit
+            sp Self      Foo<'_, {unknown}, _>
+            st Foo<…>    Foo<'_, {unknown}, _>
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
             tp T
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             kw crate::
             kw self::
         "#]],
@@ -42,18 +42,18 @@ fn tuple_struct_field() {
 struct Foo<'lt, T, const C: usize>(f$0);
 "#,
         expect![[r#"
-            en Enum
+            en Enum       Enum
             ma makro!(…)  macro_rules! makro
             md module
-            sp Self
-            st Foo<…>
-            st Record
-            st Tuple
-            st Unit
+            sp Self       Foo<'_, {unknown}, _>
+            st Foo<…>     Foo<'_, {unknown}, _>
+            st Record     Record
+            st Tuple      Tuple
+            st Unit       Unit
             tt Trait
             tp T
-            un Union
-            bt u32
+            un Union      Union
+            bt u32        u32
             kw crate::
             kw pub
             kw pub(crate)
@@ -70,16 +70,16 @@ fn fn_return_type() {
 fn x<'lt, T, const C: usize>() -> $0
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
             tp T
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             kw crate::
             kw self::
         "#]],
@@ -100,19 +100,19 @@ fn foo() -> B$0 {
 }
 "#,
         expect![[r#"
-        en Enum
-        ma makro!(…) macro_rules! makro
-        md module
-        st Record
-        st Tuple
-        st Unit
-        tt Trait
-        un Union
-        bt u32
-        it ()
-        kw crate::
-        kw self::
-    "#]],
+            en Enum      Enum
+            ma makro!(…) macro_rules! makro
+            md module
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
+            tt Trait
+            un Union     Union
+            bt u32       u32
+            it ()
+            kw crate::
+            kw self::
+        "#]],
     )
 }
 
@@ -124,16 +124,16 @@ struct Foo(T);
 const FOO: $0 = Foo(2);
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Foo<…>
-            st Record
-            st Tuple
-            st Unit
+            st Foo<…>    Foo<{unknown}>
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             it Foo
             kw crate::
             kw self::
@@ -151,15 +151,15 @@ fn f2() {
 }
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             it i32
             kw crate::
             kw self::
@@ -179,15 +179,15 @@ fn f2() {
 }
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             it u64
             kw crate::
             kw self::
@@ -204,15 +204,15 @@ fn f2(x: u64) -> $0 {
 }
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             it u64
             kw crate::
             kw self::
@@ -230,15 +230,15 @@ fn f2(x: $0) {
 }
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             it i32
             kw crate::
             kw self::
@@ -262,17 +262,17 @@ fn foo<'lt, T, const C: usize>() {
 }
 "#,
         expect![[r#"
-            en Enum
+            en Enum                Enum
             ma makro!(…)           macro_rules! makro
             md a
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record              Record
+            st Tuple               Tuple
+            st Unit                Unit
             tt Trait
             tp T
-            un Union
-            bt u32
+            un Union               Union
+            bt u32                 u32
             it a::Foo>
             kw crate::
             kw self::
@@ -291,17 +291,17 @@ fn foo<'lt, T, const C: usize>() {
 }
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Foo<…>
-            st Record
-            st Tuple
-            st Unit
+            st Foo<…>    Foo<{unknown}>
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
             tp T
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             it Foo
             kw crate::
             kw self::
@@ -319,16 +319,16 @@ fn foo<'lt, T, const C: usize>() {
 }
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
             tp T
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             kw crate::
             kw self::
         "#]],
@@ -341,14 +341,14 @@ fn foo<'lt, T, const C: usize>() {
 }
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
-            un Union
+            un Union     Union
         "#]],
     );
 }
@@ -384,18 +384,18 @@ trait Trait2: Trait1 {
 fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
 "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
             tt Trait1
             tt Trait2
             tp T
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             kw crate::
             kw self::
         "#]],
@@ -409,15 +409,15 @@ trait Trait2 {
 fn foo<'lt, T: Trait2, const CONST_PARAM: usize>(_: T) {}
     "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Record
-            st Tuple
-            st Unit
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
             tt Trait
             tt Trait2
-            un Union
+            un Union     Union
         "#]],
     );
 }
@@ -434,18 +434,18 @@ trait Tr {
 impl Tr<$0
     "#,
         expect![[r#"
-            en Enum
+            en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            sp Self
-            st Record
-            st S
-            st Tuple
-            st Unit
+            sp Self      dyn Tr<{unknown}>
+            st Record    Record
+            st S         S
+            st Tuple     Tuple
+            st Unit      Unit
             tt Tr
             tt Trait
-            un Union
-            bt u32
+            un Union     Union
+            bt u32       u32
             kw crate::
             kw self::
         "#]],
@@ -481,16 +481,16 @@ trait MyTrait {
 fn f(t: impl MyTrait {
 fn f(t: impl MyTrait {
 fn f(t: impl MyTrait {
 fn f(t: impl MyTrait = ()>) {}
             "#,
         expect![[r#"
-                en Enum
-                ma makro!(…) macro_rules! makro
-                md module
-                st Foo
-                st Record
-                st Tuple
-                st Unit
-                tt Bar
-                tt Trait
-                un Union
-                bt u32
-                kw crate::
-                kw self::
-            "#]],
+            en Enum      Enum
+            ma makro!(…) macro_rules! makro
+            md module
+            st Foo       Foo
+            st Record    Record
+            st Tuple     Tuple
+            st Unit      Unit
+            tt Bar
+            tt Trait
+            un Union     Union
+            bt u32       u32
+            kw crate::
+            kw self::
+        "#]],
     );
     check(
         r#"
@@ -853,12 +853,12 @@ fn completes_const_and_type_generics_separately() {
     fn foo = ()>>() {}
             "#,
         expect![[r#"
-                ct CONST
-                ct X
-                ma makro!(…) macro_rules! makro
-                kw crate::
-                kw self::
-            "#]],
+            ct CONST     Unit
+            ct X         usize
+            ma makro!(…) macro_rules! makro
+            kw crate::
+            kw self::
+        "#]],
     );
 
     // Type generic params
@@ -871,12 +871,12 @@ fn completes_const_and_type_generics_separately() {
     }
             "#,
         expect![[r#"
-                ct CONST
-                ct X
-                ma makro!(…) macro_rules! makro
-                kw crate::
-                kw self::
-            "#]],
+            ct CONST     Unit
+            ct X         usize
+            ma makro!(…) macro_rules! makro
+            kw crate::
+            kw self::
+        "#]],
     );
 
     // Type alias generic params
@@ -890,12 +890,12 @@ fn completes_const_and_type_generics_separately() {
     }
             "#,
         expect![[r#"
-                ct CONST
-                ct X
-                ma makro!(…) macro_rules! makro
-                kw crate::
-                kw self::
-            "#]],
+            ct CONST     Unit
+            ct X         usize
+            ma makro!(…) macro_rules! makro
+            kw crate::
+            kw self::
+        "#]],
     );
 
     // Enum variant params
@@ -908,12 +908,12 @@ fn completes_const_and_type_generics_separately() {
     }
             "#,
         expect![[r#"
-                ct CONST
-                ct X
-                ma makro!(…) macro_rules! makro
-                kw crate::
-                kw self::
-            "#]],
+            ct CONST     Unit
+            ct X         usize
+            ma makro!(…) macro_rules! makro
+            kw crate::
+            kw self::
+        "#]],
     );
 
     // Trait params
@@ -924,12 +924,12 @@ fn completes_const_and_type_generics_separately() {
     impl Foo<(), $0> for () {}
             "#,
         expect![[r#"
-                ct CONST
-                ct X
-                ma makro!(…) macro_rules! makro
-                kw crate::
-                kw self::
-            "#]],
+            ct CONST     Unit
+            ct X         usize
+            ma makro!(…) macro_rules! makro
+            kw crate::
+            kw self::
+        "#]],
     );
 
     // Trait alias params
@@ -942,12 +942,12 @@ fn completes_const_and_type_generics_separately() {
     fn foo>() {}
             "#,
         expect![[r#"
-                ct CONST
-                ct X
-                ma makro!(…) macro_rules! makro
-                kw crate::
-                kw self::
-            "#]],
+            ct CONST     Unit
+            ct X         usize
+            ma makro!(…) macro_rules! makro
+            kw crate::
+            kw self::
+        "#]],
     );
 
     // Omitted lifetime params
@@ -957,7 +957,7 @@ struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
 fn foo<'a>() { S::; }
         "#,
         expect![[r#"
-            ct CONST
+            ct CONST     Unit
             ma makro!(…) macro_rules! makro
             kw crate::
             kw self::
@@ -970,7 +970,7 @@ struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
 fn foo<'a>() { S::<'static, 'static, F$0, _>; }
         "#,
         expect![[r#"
-            ct CONST
+            ct CONST     Unit
             ma makro!(…) macro_rules! makro
             kw crate::
             kw self::
diff --git a/crates/ide-completion/src/tests/use_tree.rs b/crates/ide-completion/src/tests/use_tree.rs
index 4c74dba526b6..167bdec546d6 100644
--- a/crates/ide-completion/src/tests/use_tree.rs
+++ b/crates/ide-completion/src/tests/use_tree.rs
@@ -65,7 +65,7 @@ use self::{foo::*, bar$0};
 "#,
         expect![[r#"
             md foo
-            st S
+            st S   S
         "#]],
     );
 }
@@ -82,7 +82,7 @@ mod foo {
 use foo::{bar::$0}
 "#,
         expect![[r#"
-            st FooBar
+            st FooBar FooBar
         "#]],
     );
     check(
@@ -115,7 +115,7 @@ mod foo {
 use foo::{bar::{baz::$0}}
 "#,
         expect![[r#"
-            st FooBarBaz
+            st FooBarBaz FooBarBaz
         "#]],
     );
     check(
@@ -152,7 +152,7 @@ struct Bar;
 "#,
         expect![[r#"
             ma foo macro_rules! foo_
-            st Foo
+            st Foo Foo
         "#]],
     );
 }
@@ -193,7 +193,7 @@ struct Bar;
 "#,
         expect![[r#"
             md foo
-            st Bar
+            st Bar Bar
         "#]],
     );
 }
@@ -212,7 +212,7 @@ struct Bar;
         expect![[r#"
             md bar
             md foo
-            st Bar
+            st Bar Bar
         "#]],
     );
 }
@@ -230,7 +230,7 @@ mod a {
 }
 "#,
         expect![[r#"
-            ct A
+            ct A       usize
             md b
             kw super::
         "#]],
@@ -248,7 +248,7 @@ struct Bar;
 "#,
         expect![[r#"
             md foo
-            st Bar
+            st Bar Bar
         "#]],
     );
 }
@@ -265,7 +265,7 @@ pub mod foo {}
 "#,
         expect![[r#"
             md foo
-            st Foo
+            st Foo Foo
         "#]],
     );
 }
@@ -425,7 +425,7 @@ marco_rules! m { () => {} }
         expect![[r#"
             fn foo  fn()
             md simd
-            st S
+            st S    S
         "#]],
     );
 }

From 61036ec12bde4a9050196f0226b6540c096c1603 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 8 Dec 2023 15:26:38 +0100
Subject: [PATCH 50/62] fix: Fix token downmapping being quadratic

---
 crates/hir-expand/src/lib.rs |   8 +-
 crates/hir/src/semantics.rs  | 287 +++++++++++++++++++----------------
 2 files changed, 164 insertions(+), 131 deletions(-)

diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index a159cf92a7ac..74089593ac03 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -605,8 +605,8 @@ pub struct ExpansionInfo {
 }
 
 impl ExpansionInfo {
-    pub fn expanded(&self) -> InFile {
-        self.expanded.clone().into()
+    pub fn expanded(&self) -> InMacroFile {
+        self.expanded.clone()
     }
 
     pub fn call_node(&self) -> Option> {
@@ -617,13 +617,13 @@ impl ExpansionInfo {
     pub fn map_range_down<'a>(
         &'a self,
         span: SpanData,
-    ) -> Option> + 'a> {
+    ) -> Option + 'a>> {
         let tokens = self
             .exp_map
             .ranges_with_span(span)
             .flat_map(move |range| self.expanded.value.covering_element(range).into_token());
 
-        Some(tokens.map(move |token| InMacroFile::new(self.expanded.file_id, token)))
+        Some(InMacroFile::new(self.expanded.file_id, tokens))
     }
 
     /// Looks up the span at the given offset.
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 55c14312071a..cb52a71d9675 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -5,7 +5,7 @@ mod source_to_def;
 use std::{
     cell::RefCell,
     fmt, iter, mem,
-    ops::{self, ControlFlow},
+    ops::{self, ControlFlow, Not},
 };
 
 use base_db::{FileId, FileRange};
@@ -20,8 +20,8 @@ use hir_def::{
     AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId,
 };
 use hir_expand::{
-    db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo, MacroCallId, MacroFileId,
-    MacroFileIdExt,
+    db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo, InMacroFile, MacroCallId,
+    MacroFileId, MacroFileIdExt,
 };
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -29,7 +29,7 @@ use smallvec::{smallvec, SmallVec};
 use stdx::TupleExt;
 use syntax::{
     algo::skip_trivia_token,
-    ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody, IsString as _},
+    ast::{self, HasAttrs as _, HasDocComments, HasGenericParams, HasLoopBody, IsString as _},
     match_ast, AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken,
     TextRange, TextSize,
 };
@@ -129,9 +129,10 @@ pub struct Semantics<'db, DB> {
 pub struct SemanticsImpl<'db> {
     pub db: &'db dyn HirDatabase,
     s2d_cache: RefCell,
-    expansion_info_cache: RefCell>,
     /// Rootnode to HirFileId cache
     cache: RefCell>,
+    // These 2 caches are mainly useful for semantic highlighting as nothing else descends a lot of tokens
+    expansion_info_cache: RefCell>,
     /// MacroCall to its expansion's MacroFileId cache
     macro_call_cache: RefCell, MacroFileId>>,
 }
@@ -616,164 +617,196 @@ impl<'db> SemanticsImpl<'db> {
         res
     }
 
-    // FIXME: should only take real file inputs for simplicity
     fn descend_into_macros_impl(
         &self,
         token: SyntaxToken,
         f: &mut dyn FnMut(InFile) -> ControlFlow<()>,
     ) {
-        // FIXME: Clean this up
         let _p = profile::span("descend_into_macros");
         let sa = match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) {
             Some(it) => it,
             None => return,
         };
 
-        let mut cache = self.expansion_info_cache.borrow_mut();
-        let mut mcache = self.macro_call_cache.borrow_mut();
-        let span = match sa.file_id.repr() {
-            base_db::span::HirFileIdRepr::FileId(file_id) => {
-                self.db.real_span_map(file_id).span_for_range(token.text_range())
+        let span = match sa.file_id.file_id() {
+            Some(file_id) => self.db.real_span_map(file_id).span_for_range(token.text_range()),
+            None => {
+                stdx::never!();
+                return;
             }
-            base_db::span::HirFileIdRepr::MacroFile(macro_file) => cache
-                .entry(macro_file)
-                .or_insert_with(|| macro_file.expansion_info(self.db.upcast()))
-                .exp_map
-                .span_at(token.text_range().start()),
         };
 
+        let mut cache = self.expansion_info_cache.borrow_mut();
+        let mut mcache = self.macro_call_cache.borrow_mut();
         let def_map = sa.resolver.def_map();
-        let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)];
 
-        let mut process_expansion_for_token = |stack: &mut SmallVec<_>, macro_file| {
+        let mut process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
             let expansion_info = cache
                 .entry(macro_file)
                 .or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
 
             {
-                let InFile { file_id, value } = expansion_info.expanded();
-                self.cache(value, file_id);
+                let InMacroFile { file_id, value } = expansion_info.expanded();
+                self.cache(value, file_id.into());
             }
 
-            let mapped_tokens = expansion_info.map_range_down(span)?;
-            let len = stack.len();
+            let InMacroFile { file_id, value: mapped_tokens } =
+                expansion_info.map_range_down(span)?;
+            let mapped_tokens: SmallVec<[_; 2]> = mapped_tokens.collect();
 
-            // requeue the tokens we got from mapping our current token down
-            stack.extend(mapped_tokens.map(Into::into));
             // if the length changed we have found a mapping for the token
-            (stack.len() != len).then_some(())
+            let res = mapped_tokens.is_empty().not().then_some(());
+            // requeue the tokens we got from mapping our current token down
+            stack.push((HirFileId::from(file_id), mapped_tokens));
+            res
         };
 
-        // Remap the next token in the queue into a macro call its in, if it is not being remapped
-        // either due to not being in a macro-call or because its unused push it into the result vec,
-        // otherwise push the remapped tokens back into the queue as they can potentially be remapped again.
-        while let Some(token) = stack.pop() {
-            let was_not_remapped = (|| {
-                // First expand into attribute invocations
-
-                let containing_attribute_macro_call = self.with_ctx(|ctx| {
-                    token.value.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
-                        if item.attrs().next().is_none() {
-                            // Don't force populate the dyn cache for items that don't have an attribute anyways
-                            return None;
-                        }
-                        Some(ctx.item_to_macro_call(token.with_value(item.clone()))?)
-                    })
-                });
-                if let Some(call_id) = containing_attribute_macro_call {
-                    let file_id = call_id.as_macro_file();
-                    return process_expansion_for_token(&mut stack, file_id);
-                }
+        let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(sa.file_id, smallvec![token])];
+
+        while let Some((file_id, mut tokens)) = stack.pop() {
+            while let Some(token) = tokens.pop() {
+                let was_not_remapped = (|| {
+                    // First expand into attribute invocations
+                    let containing_attribute_macro_call = self.with_ctx(|ctx| {
+                        token.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
+                            if item.attrs().next().is_none() {
+                                // Don't force populate the dyn cache for items that don't have an attribute anyways
+                                return None;
+                            }
+                            Some((
+                                ctx.item_to_macro_call(InFile::new(file_id, item.clone()))?,
+                                item,
+                            ))
+                        })
+                    });
+                    if let Some((call_id, item)) = containing_attribute_macro_call {
+                        let file_id = call_id.as_macro_file();
+                        let attr_id = match self.db.lookup_intern_macro_call(call_id).kind {
+                            hir_expand::MacroCallKind::Attr { invoc_attr_index, .. } => {
+                                invoc_attr_index.ast_index()
+                            }
+                            _ => 0,
+                        };
+                        let text_range = item.syntax().text_range();
+                        let start = item
+                            .doc_comments_and_attrs()
+                            .nth(attr_id)
+                            .map(|attr| match attr {
+                                Either::Left(it) => it.syntax().text_range().start(),
+                                Either::Right(it) => it.syntax().text_range().start(),
+                            })
+                            .unwrap_or_else(|| text_range.start());
+                        let text_range = TextRange::new(start, text_range.end());
+                        // remove any other token in this macro input, all their mappings are the
+                        // same as this one
+                        tokens.retain(|t| !text_range.contains_range(t.text_range()));
+                        return process_expansion_for_token(&mut stack, file_id);
+                    }
 
-                // Then check for token trees, that means we are either in a function-like macro or
-                // secondary attribute inputs
-                let tt = token.value.parent_ancestors().map_while(ast::TokenTree::cast).last()?;
-                let parent = tt.syntax().parent()?;
+                    // Then check for token trees, that means we are either in a function-like macro or
+                    // secondary attribute inputs
+                    let tt = token.parent_ancestors().map_while(ast::TokenTree::cast).last()?;
+                    let parent = tt.syntax().parent()?;
 
-                if tt.left_delimiter_token().map_or(false, |it| it == token.value) {
-                    return None;
-                }
-                if tt.right_delimiter_token().map_or(false, |it| it == token.value) {
-                    return None;
-                }
+                    if tt.left_delimiter_token().map_or(false, |it| it == token) {
+                        return None;
+                    }
+                    if tt.right_delimiter_token().map_or(false, |it| it == token) {
+                        return None;
+                    }
 
-                if let Some(macro_call) = ast::MacroCall::cast(parent.clone()) {
-                    let mcall: hir_expand::files::InFileWrapper =
-                        token.with_value(macro_call);
-                    let file_id = match mcache.get(&mcall) {
-                        Some(&it) => it,
-                        None => {
-                            let it = sa.expand(self.db, mcall.as_ref())?;
-                            mcache.insert(mcall, it);
-                            it
-                        }
-                    };
-                    process_expansion_for_token(&mut stack, file_id)
-                } else if let Some(meta) = ast::Meta::cast(parent) {
-                    // attribute we failed expansion for earlier, this might be a derive invocation
-                    // or derive helper attribute
-                    let attr = meta.parent_attr()?;
-
-                    let adt = if let Some(adt) = attr.syntax().parent().and_then(ast::Adt::cast) {
-                        // this might be a derive, or a derive helper on an ADT
-                        let derive_call = self.with_ctx(|ctx| {
-                            // so try downmapping the token into the pseudo derive expansion
-                            // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
-                            ctx.attr_to_derive_macro_call(
-                                token.with_value(&adt),
-                                token.with_value(attr.clone()),
-                            )
-                            .map(|(_, call_id, _)| call_id)
-                        });
-
-                        match derive_call {
-                            Some(call_id) => {
-                                // resolved to a derive
-                                let file_id = call_id.as_macro_file();
-                                return process_expansion_for_token(&mut stack, file_id);
+                    if let Some(macro_call) = ast::MacroCall::cast(parent.clone()) {
+                        let mcall: hir_expand::files::InFileWrapper =
+                            InFile::new(file_id, macro_call);
+                        let file_id = match mcache.get(&mcall) {
+                            Some(&it) => it,
+                            None => {
+                                let it = sa.expand(self.db, mcall.as_ref())?;
+                                mcache.insert(mcall, it);
+                                it
                             }
-                            None => Some(adt),
-                        }
-                    } else {
-                        // Otherwise this could be a derive helper on a variant or field
-                        if let Some(field) = attr.syntax().parent().and_then(ast::RecordField::cast)
-                        {
-                            field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
-                        } else if let Some(field) =
-                            attr.syntax().parent().and_then(ast::TupleField::cast)
-                        {
-                            field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
-                        } else if let Some(variant) =
-                            attr.syntax().parent().and_then(ast::Variant::cast)
+                        };
+                        let text_range = tt.syntax().text_range();
+                        // remove any other token in this macro input, all their mappings are the
+                        // same as this one
+                        tokens.retain(|t| !text_range.contains_range(t.text_range()));
+                        process_expansion_for_token(&mut stack, file_id)
+                    } else if let Some(meta) = ast::Meta::cast(parent) {
+                        // attribute we failed expansion for earlier, this might be a derive invocation
+                        // or derive helper attribute
+                        let attr = meta.parent_attr()?;
+
+                        let adt = if let Some(adt) = attr.syntax().parent().and_then(ast::Adt::cast)
                         {
-                            variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast)
+                            // this might be a derive, or a derive helper on an ADT
+                            let derive_call = self.with_ctx(|ctx| {
+                                // so try downmapping the token into the pseudo derive expansion
+                                // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
+                                ctx.attr_to_derive_macro_call(
+                                    InFile::new(file_id, &adt),
+                                    InFile::new(file_id, attr.clone()),
+                                )
+                                .map(|(_, call_id, _)| call_id)
+                            });
+
+                            match derive_call {
+                                Some(call_id) => {
+                                    // resolved to a derive
+                                    let file_id = call_id.as_macro_file();
+                                    let text_range = attr.syntax().text_range();
+                                    // remove any other token in this macro input, all their mappings are the
+                                    // same as this one
+                                    tokens.retain(|t| !text_range.contains_range(t.text_range()));
+                                    return process_expansion_for_token(&mut stack, file_id);
+                                }
+                                None => Some(adt),
+                            }
                         } else {
-                            None
+                            // Otherwise this could be a derive helper on a variant or field
+                            if let Some(field) =
+                                attr.syntax().parent().and_then(ast::RecordField::cast)
+                            {
+                                field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
+                            } else if let Some(field) =
+                                attr.syntax().parent().and_then(ast::TupleField::cast)
+                            {
+                                field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
+                            } else if let Some(variant) =
+                                attr.syntax().parent().and_then(ast::Variant::cast)
+                            {
+                                variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast)
+                            } else {
+                                None
+                            }
+                        }?;
+                        if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(file_id, &adt))) {
+                            return None;
                         }
-                    }?;
-                    if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(token.file_id, &adt))) {
-                        return None;
-                    }
-                    // Not an attribute, nor a derive, so it's either a builtin or a derive helper
-                    // Try to resolve to a derive helper and downmap
-                    let attr_name = attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
-                    let id = self.db.ast_id_map(token.file_id).ast_id(&adt);
-                    let helpers =
-                        def_map.derive_helpers_in_scope(InFile::new(token.file_id, id))?;
-                    let mut res = None;
-                    for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) {
-                        res =
-                            res.or(process_expansion_for_token(&mut stack, derive.as_macro_file()));
+                        // Not an attribute, nor a derive, so it's either a builtin or a derive helper
+                        // Try to resolve to a derive helper and downmap
+                        let attr_name =
+                            attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
+                        let id = self.db.ast_id_map(file_id).ast_id(&adt);
+                        let helpers = def_map.derive_helpers_in_scope(InFile::new(file_id, id))?;
+                        let mut res = None;
+                        for (.., derive) in
+                            helpers.iter().filter(|(helper, ..)| *helper == attr_name)
+                        {
+                            res = res.or(process_expansion_for_token(
+                                &mut stack,
+                                derive.as_macro_file(),
+                            ));
+                        }
+                        res
+                    } else {
+                        None
                     }
-                    res
-                } else {
-                    None
-                }
-            })()
-            .is_none();
+                })()
+                .is_none();
 
-            if was_not_remapped && f(token).is_break() {
-                break;
+                if was_not_remapped && f(InFile::new(file_id, token)).is_break() {
+                    break;
+                }
             }
         }
     }

From 2508c2917c6ac093f8bbfdbb64886a8fd73b17e3 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 8 Dec 2023 16:36:41 +0100
Subject: [PATCH 51/62] Fallback to method resolution on unresolved field
 access with matching method name

---
 crates/hir-ty/src/infer/expr.rs   | 95 ++++++++++++++++++++-----------
 crates/hir/src/semantics.rs       | 32 +++++------
 crates/hir/src/source_analyzer.rs | 47 +++++++++------
 crates/ide-db/src/defs.rs         | 11 ++--
 crates/ide/src/hover/tests.rs     | 27 +++++++++
 5 files changed, 140 insertions(+), 72 deletions(-)

diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 24026202b74d..a5e77a12d8c5 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -579,7 +579,7 @@ impl InferenceContext<'_> {
                 }
                 ty
             }
-            Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name),
+            Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected),
             Expr::Await { expr } => {
                 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
                 self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
@@ -1456,7 +1456,13 @@ impl InferenceContext<'_> {
         })
     }
 
-    fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty {
+    fn infer_field_access(
+        &mut self,
+        tgt_expr: ExprId,
+        receiver: ExprId,
+        name: &Name,
+        expected: &Expectation,
+    ) -> Ty {
         let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
 
         if name.is_missing() {
@@ -1482,28 +1488,42 @@ impl InferenceContext<'_> {
                 ty
             }
             None => {
-                // no field found,
-                let method_with_same_name_exists = {
-                    self.get_traits_in_scope();
-
-                    let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
-                    method_resolution::lookup_method(
-                        self.db,
-                        &canonicalized_receiver.value,
-                        self.table.trait_env.clone(),
-                        self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
-                        VisibleFromModule::Filter(self.resolver.module()),
-                        name,
-                    )
-                    .is_some()
-                };
+                // no field found, lets attempt to resolve it like a function so that IDE things
+                // work out while people are typing
+                let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
+                let resolved = method_resolution::lookup_method(
+                    self.db,
+                    &canonicalized_receiver.value,
+                    self.table.trait_env.clone(),
+                    self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
+                    VisibleFromModule::Filter(self.resolver.module()),
+                    name,
+                );
                 self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField {
                     expr: tgt_expr,
-                    receiver: receiver_ty,
+                    receiver: receiver_ty.clone(),
                     name: name.clone(),
-                    method_with_same_name_exists,
+                    method_with_same_name_exists: resolved.is_some(),
                 });
-                self.err_ty()
+                match resolved {
+                    Some((adjust, func, _)) => {
+                        let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
+                        let generics = generics(self.db.upcast(), func.into());
+                        let substs = self.substs_for_method_call(generics, None);
+                        self.write_expr_adj(receiver, adjustments);
+                        self.write_method_resolution(tgt_expr, func, substs.clone());
+
+                        self.check_method_call(
+                            tgt_expr,
+                            &[],
+                            self.db.value_ty(func.into()),
+                            substs,
+                            ty,
+                            expected,
+                        )
+                    }
+                    None => self.err_ty(),
+                }
             }
         }
     }
@@ -1517,7 +1537,7 @@ impl InferenceContext<'_> {
         generic_args: Option<&GenericArgs>,
         expected: &Expectation,
     ) -> Ty {
-        let receiver_ty = self.infer_expr(receiver, &Expectation::none());
+        let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
         let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
 
         let resolved = method_resolution::lookup_method(
@@ -1568,23 +1588,32 @@ impl InferenceContext<'_> {
                 )
             }
         };
+        self.check_method_call(tgt_expr, args, method_ty, substs, receiver_ty, expected)
+    }
+
+    fn check_method_call(
+        &mut self,
+        tgt_expr: ExprId,
+        args: &[ExprId],
+        method_ty: Binders,
+        substs: Substitution,
+        receiver_ty: Ty,
+        expected: &Expectation,
+    ) -> Ty {
         let method_ty = method_ty.substitute(Interner, &substs);
         self.register_obligations_for_call(&method_ty);
-        let (formal_receiver_ty, param_tys, ret_ty, is_varargs) =
+        let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) =
             match method_ty.callable_sig(self.db) {
-                Some(sig) => {
+                Some(sig) => (
                     if !sig.params().is_empty() {
-                        (
-                            sig.params()[0].clone(),
-                            sig.params()[1..].to_vec(),
-                            sig.ret().clone(),
-                            sig.is_varargs,
-                        )
+                        (sig.params()[0].clone(), sig.params()[1..].to_vec())
                     } else {
-                        (self.err_ty(), Vec::new(), sig.ret().clone(), sig.is_varargs)
-                    }
-                }
-                None => (self.err_ty(), Vec::new(), self.err_ty(), true),
+                        (self.err_ty(), Vec::new())
+                    },
+                    sig.ret().clone(),
+                    sig.is_varargs,
+                ),
+                None => ((self.err_ty(), Vec::new()), self.err_ty(), true),
             };
         self.unify(&formal_receiver_ty, &receiver_ty);
 
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index cb52a71d9675..92fa76c96fbd 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -17,7 +17,7 @@ use hir_def::{
     nameres::MacroSubNs,
     resolver::{self, HasResolver, Resolver, TypeNs},
     type_ref::Mutability,
-    AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId,
+    AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
 };
 use hir_expand::{
     db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo, InMacroFile, MacroCallId,
@@ -198,20 +198,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast))
     }
 
-    pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option {
-        self.imp.resolve_method_call(call).map(Function::from)
-    }
-
-    /// Attempts to resolve this call expression as a method call falling back to resolving it as a field.
-    pub fn resolve_method_call_field_fallback(
-        &self,
-        call: &ast::MethodCallExpr,
-    ) -> Option> {
-        self.imp
-            .resolve_method_call_fallback(call)
-            .map(|it| it.map_left(Function::from).map_right(Field::from))
-    }
-
     pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option {
         self.imp.resolve_await_to_poll(await_expr).map(Function::from)
     }
@@ -1048,14 +1034,15 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(pat.syntax())?.binding_mode_of_pat(self.db, pat)
     }
 
-    fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option {
+    pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option {
         self.analyze(call.syntax())?.resolve_method_call(self.db, call)
     }
 
-    fn resolve_method_call_fallback(
+    /// Attempts to resolve this call expression as a method call falling back to resolving it as a field.
+    pub fn resolve_method_call_fallback(
         &self,
         call: &ast::MethodCallExpr,
-    ) -> Option> {
+    ) -> Option> {
         self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
     }
 
@@ -1087,6 +1074,13 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(field.syntax())?.resolve_field(self.db, field)
     }
 
+    pub fn resolve_field_fallback(
+        &self,
+        field: &ast::FieldExpr,
+    ) -> Option> {
+        self.analyze(field.syntax())?.resolve_field_fallback(self.db, field)
+    }
+
     pub fn resolve_record_field(
         &self,
         field: &ast::RecordExprField,
@@ -1298,7 +1292,7 @@ impl<'db> SemanticsImpl<'db> {
                     return None;
                 }
 
-                let func = self.resolve_method_call(method_call_expr).map(Function::from)?;
+                let func = self.resolve_method_call(method_call_expr)?;
                 let res = match func.self_param(self.db)?.access(self.db) {
                     Access::Shared | Access::Exclusive => true,
                     Access::Owned => false,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 7a31e6df1fae..73db6f8f0b86 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -280,25 +280,49 @@ impl SourceAnalyzer {
         &self,
         db: &dyn HirDatabase,
         call: &ast::MethodCallExpr,
-    ) -> Option {
+    ) -> Option {
         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).into())
     }
 
     pub(crate) fn resolve_method_call_fallback(
         &self,
         db: &dyn HirDatabase,
         call: &ast::MethodCallExpr,
-    ) -> Option> {
+    ) -> Option> {
         let expr_id = self.expr_id(db, &call.clone().into())?;
         let inference_result = self.infer.as_ref()?;
         match inference_result.method_resolution(expr_id) {
-            Some((f_in_trait, substs)) => {
-                Some(Either::Left(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs)))
-            }
-            None => inference_result.field_resolution(expr_id).map(Either::Right),
+            Some((f_in_trait, substs)) => Some(Either::Left(
+                self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into(),
+            )),
+            None => inference_result.field_resolution(expr_id).map(Into::into).map(Either::Right),
+        }
+    }
+
+    pub(crate) fn resolve_field(
+        &self,
+        db: &dyn HirDatabase,
+        field: &ast::FieldExpr,
+    ) -> Option {
+        let expr_id = self.expr_id(db, &field.clone().into())?;
+        self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
+    }
+
+    pub(crate) fn resolve_field_fallback(
+        &self,
+        db: &dyn HirDatabase,
+        field: &ast::FieldExpr,
+    ) -> Option> {
+        let expr_id = self.expr_id(db, &field.clone().into())?;
+        let inference_result = self.infer.as_ref()?;
+        match inference_result.field_resolution(expr_id) {
+            Some(field) => Some(Either::Left(field.into())),
+            None => inference_result.method_resolution(expr_id).map(|(f, substs)| {
+                Either::Right(self.resolve_impl_method_or_trait_def(db, f, substs).into())
+            }),
         }
     }
 
@@ -417,15 +441,6 @@ impl SourceAnalyzer {
         Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
     }
 
-    pub(crate) fn resolve_field(
-        &self,
-        db: &dyn HirDatabase,
-        field: &ast::FieldExpr,
-    ) -> Option {
-        let expr_id = self.expr_id(db, &field.clone().into())?;
-        self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
-    }
-
     pub(crate) fn resolve_record_field(
         &self,
         db: &dyn HirDatabase,
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index ef72fc3861a7..ded5d4e3db53 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -492,7 +492,7 @@ impl NameRefClass {
         match_ast! {
             match parent {
                 ast::MethodCallExpr(method_call) => {
-                    sema.resolve_method_call_field_fallback(&method_call)
+                    sema.resolve_method_call_fallback(&method_call)
                         .map(|it| {
                             it.map_left(Definition::Function)
                                 .map_right(Definition::Field)
@@ -500,9 +500,12 @@ impl NameRefClass {
                         })
                 },
                 ast::FieldExpr(field_expr) => {
-                    sema.resolve_field(&field_expr)
-                        .map(Definition::Field)
-                        .map(NameRefClass::Definition)
+                    sema.resolve_field_fallback(&field_expr)
+                    .map(|it| {
+                        it.map_left(Definition::Field)
+                            .map_right(Definition::Function)
+                            .either(NameRefClass::Definition, NameRefClass::Definition)
+                    })
                 },
                 ast::RecordPatField(record_pat_field) => {
                     sema.resolve_record_pat_field(&record_pat_field)
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 8c9d58671e69..d5ec336fc7ed 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -6698,3 +6698,30 @@ foo!(r"{$0aaaaa}");
         "#]],
     );
 }
+
+#[test]
+fn method_call_without_parens() {
+    check(
+        r#"
+struct S;
+impl S {
+    fn foo(&self, t: T) {}
+}
+
+fn main() {
+    S.foo$0;
+}
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test::S
+            ```
+
+            ```rust
+            fn foo(&self, t: T)
+            ```
+        "#]],
+    );
+}

From fe656368f129b67399302f0b4c2fc21f04a70cf2 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 8 Dec 2023 18:46:36 +0100
Subject: [PATCH 52/62] fix: Smaller spans for unresolved field and method
 diagnostics

---
 Cargo.lock                                    |  4 +-
 crates/hir-expand/src/files.rs                | 15 +++++
 .../src/handlers/unresolved_field.rs          | 25 ++++++---
 .../src/handlers/unresolved_method.rs         | 55 +++++++++++++++++--
 crates/ide-diagnostics/src/lib.rs             | 15 ++++-
 crates/syntax/Cargo.toml                      |  2 +-
 crates/syntax/src/ptr.rs                      |  1 +
 7 files changed, 99 insertions(+), 18 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index c6c1e1e3c968..46efbdd93c97 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1484,9 +1484,9 @@ dependencies = [
 
 [[package]]
 name = "rowan"
-version = "0.15.14"
+version = "0.15.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9672ea408d491b517a4dc370159ec6dd7cb5c5fd2f41b02883830339109ac76"
+checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
 dependencies = [
  "countme",
  "hashbrown",
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 7e55f6be1e41..89f0685d5b67 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -314,6 +314,21 @@ impl InFile {
         }
     }
 
+    pub fn original_node_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange {
+        match self.file_id.repr() {
+            HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value },
+            HirFileIdRepr::MacroFile(mac_file) => {
+                match ExpansionInfo::new(db, mac_file).map_node_range_up(db, self.value) {
+                    Some((it, SyntaxContextId::ROOT)) => it,
+                    _ => {
+                        let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
+                        loc.kind.original_call_range(db)
+                    }
+                }
+            }
+        }
+    }
+
     pub fn original_node_file_range_opt(
         self,
         db: &dyn db::ExpandDatabase,
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 0758706e45a2..321459412182 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -8,7 +8,7 @@ use ide_db::{
 use syntax::{ast, AstNode, AstPtr};
 use text_edit::TextEdit;
 
-use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+use crate::{adjusted_display_range_new, Diagnostic, DiagnosticCode, DiagnosticsContext};
 
 // Diagnostic: unresolved-field
 //
@@ -22,15 +22,24 @@ pub(crate) fn unresolved_field(
     } else {
         ""
     };
-    Diagnostic::new_with_syntax_node_ptr(
-        ctx,
+    Diagnostic::new(
         DiagnosticCode::RustcHardError("E0559"),
         format!(
             "no field `{}` on type `{}`{method_suffix}",
             d.name.display(ctx.sema.db),
             d.receiver.display(ctx.sema.db)
         ),
-        d.expr.clone().map(|it| it.into()),
+        adjusted_display_range_new(ctx, d.expr, &|expr| {
+            Some(
+                match expr {
+                    ast::Expr::MethodCallExpr(it) => it.name_ref(),
+                    ast::Expr::FieldExpr(it) => it.name_ref(),
+                    _ => None,
+                }?
+                .syntax()
+                .text_range(),
+            )
+        }),
     )
     .with_fixes(fixes(ctx, d))
     .experimental()
@@ -79,7 +88,7 @@ mod tests {
             r#"
 fn main() {
     ().foo;
- // ^^^^^^ error: no field `foo` on type `()`
+    // ^^^ error: no field `foo` on type `()`
 }
 "#,
         );
@@ -95,7 +104,7 @@ impl Foo {
 }
 fn foo() {
     Foo.bar;
- // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+     // ^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
 }
 "#,
         );
@@ -112,7 +121,7 @@ trait Bar {
 impl Bar for Foo {}
 fn foo() {
     Foo.bar;
- // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+     // ^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
 }
 "#,
         );
@@ -131,7 +140,7 @@ impl Bar for Foo {
 }
 fn foo() {
     Foo.bar;
- // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+     // ^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
 }
 "#,
         );
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index ae9f6744c40f..464b0a710ea7 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -8,7 +8,7 @@ use ide_db::{
 use syntax::{ast, AstNode, TextRange};
 use text_edit::TextEdit;
 
-use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+use crate::{adjusted_display_range_new, Diagnostic, DiagnosticCode, DiagnosticsContext};
 
 // Diagnostic: unresolved-method
 //
@@ -22,15 +22,24 @@ pub(crate) fn unresolved_method(
     } else {
         ""
     };
-    Diagnostic::new_with_syntax_node_ptr(
-        ctx,
+    Diagnostic::new(
         DiagnosticCode::RustcHardError("E0599"),
         format!(
             "no method `{}` on type `{}`{field_suffix}",
             d.name.display(ctx.sema.db),
             d.receiver.display(ctx.sema.db)
         ),
-        d.expr.clone().map(|it| it.into()),
+        adjusted_display_range_new(ctx, d.expr, &|expr| {
+            Some(
+                match expr {
+                    ast::Expr::MethodCallExpr(it) => it.name_ref(),
+                    ast::Expr::FieldExpr(it) => it.name_ref(),
+                    _ => None,
+                }?
+                .syntax()
+                .text_range(),
+            )
+        }),
     )
     .with_fixes(fixes(ctx, d))
     .experimental()
@@ -92,7 +101,41 @@ mod tests {
             r#"
 fn main() {
     ().foo();
- // ^^^^^^^^ error: no method `foo` on type `()`
+    // ^^^ error: no method `foo` on type `()`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn smoke_test_in_macro_def_site() {
+        check_diagnostics(
+            r#"
+macro_rules! m {
+    ($rcv:expr) => {
+        $rcv.foo()
+    }
+}
+fn main() {
+    m!(());
+ // ^^^^^^ error: no method `foo` on type `()`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn smoke_test_in_macro_call_site() {
+        check_diagnostics(
+            r#"
+macro_rules! m {
+    ($ident:ident) => {
+        ().$ident()
+    }
+}
+fn main() {
+    m!(foo);
+    // ^^^ error: no method `foo` on type `()`
 }
 "#,
         );
@@ -105,7 +148,7 @@ fn main() {
 struct Foo { bar: i32 }
 fn foo() {
     Foo { bar: i32 }.bar();
- // ^^^^^^^^^^^^^^^^^^^^^^ error: no method `bar` on type `Foo`, but a field with a similar name exists
+                  // ^^^ error: no method `bar` on type `Foo`, but a field with a similar name exists
 }
 "#,
         );
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 6cfd5f183208..6541bf605794 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -90,7 +90,7 @@ use stdx::never;
 use syntax::{
     algo::find_node_at_range,
     ast::{self, AstNode},
-    SyntaxNode, SyntaxNodePtr, TextRange,
+    AstPtr, SyntaxNode, SyntaxNodePtr, TextRange,
 };
 
 // FIXME: Make this an enum
@@ -584,3 +584,16 @@ fn adjusted_display_range(
             .unwrap_or(range),
     }
 }
+
+// FIXME Replace the one above with this one?
+fn adjusted_display_range_new(
+    ctx: &DiagnosticsContext<'_>,
+    diag_ptr: InFile>,
+    adj: &dyn Fn(N) -> Option,
+) -> FileRange {
+    let source_file = ctx.sema.parse_or_expand(diag_ptr.file_id);
+    let node = diag_ptr.value.to_node(&source_file);
+    diag_ptr
+        .with_value(adj(node).unwrap_or_else(|| diag_ptr.value.text_range()))
+        .original_node_file_range_rooted(ctx.sema.db)
+}
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 3b55921dc759..7a7c0d267fed 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -16,7 +16,7 @@ doctest = false
 cov-mark = "2.0.0-pre.1"
 either.workspace = true
 itertools.workspace = true
-rowan = "0.15.11"
+rowan = "0.15.15"
 rustc-hash = "1.1.0"
 once_cell = "1.17.0"
 indexmap.workspace = true
diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs
index 07641b203bb8..8750147ee11a 100644
--- a/crates/syntax/src/ptr.rs
+++ b/crates/syntax/src/ptr.rs
@@ -33,6 +33,7 @@ impl std::fmt::Debug for AstPtr {
     }
 }
 
+impl Copy for AstPtr {}
 impl Clone for AstPtr {
     fn clone(&self) -> AstPtr {
         AstPtr { raw: self.raw.clone(), _ty: PhantomData }

From 43878c24c847b9f937e132d92e13cc425177ab5e Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 8 Dec 2023 20:13:52 +0100
Subject: [PATCH 53/62] fix: Fix completion failing in format_args! with
 invalid template

---
 crates/hir-def/src/body/lower.rs              |  14 +-
 crates/hir-def/src/hir/format_args.rs         |  14 +-
 crates/ide-completion/src/tests/expression.rs | 154 ++++++++++++++++++
 crates/ide/src/view_hir.rs                    |   6 +-
 crates/ide/src/view_mir.rs                    |   6 +-
 crates/rust-analyzer/tests/slow-tests/tidy.rs |   1 +
 6 files changed, 187 insertions(+), 8 deletions(-)

diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index c22bd6e2e57c..c6a909320159 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -1611,7 +1611,11 @@ impl ExprCollector<'_> {
                     }
                 },
             ),
-            None => FormatArgs { template: Default::default(), arguments: args.finish() },
+            None => FormatArgs {
+                template: Default::default(),
+                arguments: args.finish(),
+                orphans: Default::default(),
+            },
         };
 
         // Create a list of all _unique_ (argument, format trait) combinations.
@@ -1750,7 +1754,13 @@ impl ExprCollector<'_> {
         });
         let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
             id: None,
-            statements: Box::default(),
+            // We collect the unused expressions here so that we still infer them instead of
+            // dropping them out of the expression tree
+            statements: fmt
+                .orphans
+                .into_iter()
+                .map(|expr| Statement::Expr { expr, has_semi: true })
+                .collect(),
             tail: Some(unsafe_arg_new),
         });
 
diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs
index 068abb27a25c..7fc33abc7c9a 100644
--- a/crates/hir-def/src/hir/format_args.rs
+++ b/crates/hir-def/src/hir/format_args.rs
@@ -3,6 +3,7 @@ use std::mem;
 
 use hir_expand::name::Name;
 use rustc_dependencies::parse_format as parse;
+use stdx::TupleExt;
 use syntax::{
     ast::{self, IsString},
     SmolStr, TextRange, TextSize,
@@ -14,6 +15,7 @@ use crate::hir::ExprId;
 pub struct FormatArgs {
     pub template: Box<[FormatArgsPiece]>,
     pub arguments: FormatArguments,
+    pub orphans: Vec,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -196,7 +198,11 @@ pub(crate) fn parse(
     let is_source_literal = parser.is_source_literal;
     if !parser.errors.is_empty() {
         // FIXME: Diagnose
-        return FormatArgs { template: Default::default(), arguments: args.finish() };
+        return FormatArgs {
+            template: Default::default(),
+            arguments: args.finish(),
+            orphans: vec![],
+        };
     }
 
     let to_span = |inner_span: parse::InnerSpan| {
@@ -419,7 +425,11 @@ pub(crate) fn parse(
         // FIXME: Diagnose
     }
 
-    FormatArgs { template: template.into_boxed_slice(), arguments: args.finish() }
+    FormatArgs {
+        template: template.into_boxed_slice(),
+        arguments: args.finish(),
+        orphans: unused.into_iter().map(TupleExt::head).collect(),
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index e6969c8db300..b4f936b35aea 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -1094,3 +1094,157 @@ pub struct UnstableButWeAreOnNightlyAnyway;
         "#]],
     );
 }
+
+#[test]
+fn inside_format_args_completions_work() {
+    check_empty(
+        r#"
+//- minicore: fmt
+struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+
+fn main() {
+    format_args!("{}", Foo.$0);
+}
+"#,
+        expect![[r#"
+            me foo()  fn(&self)
+            sn box    Box::new(expr)
+            sn call   function(expr)
+            sn dbg    dbg!(expr)
+            sn dbgr   dbg!(&expr)
+            sn match  match expr {}
+            sn ref    &expr
+            sn refm   &mut expr
+            sn unsafe unsafe {}
+        "#]],
+    );
+    check_empty(
+        r#"
+//- minicore: fmt
+struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+
+fn main() {
+    format_args!("{}", Foo.f$0);
+}
+"#,
+        expect![[r#"
+            me foo()  fn(&self)
+            sn box    Box::new(expr)
+            sn call   function(expr)
+            sn dbg    dbg!(expr)
+            sn dbgr   dbg!(&expr)
+            sn match  match expr {}
+            sn ref    &expr
+            sn refm   &mut expr
+            sn unsafe unsafe {}
+        "#]],
+    );
+}
+
+#[test]
+fn inside_faulty_format_args_completions_work() {
+    check_empty(
+        r#"
+//- minicore: fmt
+struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+
+fn main() {
+    format_args!("", Foo.$0);
+}
+"#,
+        expect![[r#"
+            me foo()  fn(&self)
+            sn box    Box::new(expr)
+            sn call   function(expr)
+            sn dbg    dbg!(expr)
+            sn dbgr   dbg!(&expr)
+            sn match  match expr {}
+            sn ref    &expr
+            sn refm   &mut expr
+            sn unsafe unsafe {}
+        "#]],
+    );
+    check_empty(
+        r#"
+//- minicore: fmt
+struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+
+fn main() {
+    format_args!("", Foo.f$0);
+}
+"#,
+        expect![[r#"
+            me foo()  fn(&self)
+            sn box    Box::new(expr)
+            sn call   function(expr)
+            sn dbg    dbg!(expr)
+            sn dbgr   dbg!(&expr)
+            sn match  match expr {}
+            sn ref    &expr
+            sn refm   &mut expr
+            sn unsafe unsafe {}
+        "#]],
+    );
+    check_empty(
+        r#"
+//- minicore: fmt
+struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+
+fn main() {
+    format_args!("{} {named} {captured} {named} {}", a, named = c, Foo.f$0);
+}
+"#,
+        expect![[r#"
+            me foo()  fn(&self)
+            sn box    Box::new(expr)
+            sn call   function(expr)
+            sn dbg    dbg!(expr)
+            sn dbgr   dbg!(&expr)
+            sn match  match expr {}
+            sn ref    &expr
+            sn refm   &mut expr
+            sn unsafe unsafe {}
+        "#]],
+    );
+    check_empty(
+        r#"
+//- minicore: fmt
+struct Foo;
+impl Foo {
+    fn foo(&self) {}
+}
+
+fn main() {
+    format_args!("{", Foo.f$0);
+}
+"#,
+        expect![[r#"
+            sn box    Box::new(expr)
+            sn call   function(expr)
+            sn dbg    dbg!(expr)
+            sn dbgr   dbg!(&expr)
+            sn if     if expr {}
+            sn match  match expr {}
+            sn not    !expr
+            sn ref    &expr
+            sn refm   &mut expr
+            sn unsafe unsafe {}
+            sn while  while expr {}
+        "#]],
+    );
+}
diff --git a/crates/ide/src/view_hir.rs b/crates/ide/src/view_hir.rs
index d2bbbf6d26ab..738b370a068e 100644
--- a/crates/ide/src/view_hir.rs
+++ b/crates/ide/src/view_hir.rs
@@ -1,7 +1,7 @@
 use hir::{DefWithBody, Semantics};
 use ide_db::base_db::FilePosition;
 use ide_db::RootDatabase;
-use syntax::{algo::find_node_at_offset, ast, AstNode};
+use syntax::{algo::ancestors_at_offset, ast, AstNode};
 
 // Feature: View Hir
 //
@@ -19,7 +19,9 @@ fn body_hir(db: &RootDatabase, position: FilePosition) -> Option {
     let sema = Semantics::new(db);
     let source_file = sema.parse(position.file_id);
 
-    let item = find_node_at_offset::(source_file.syntax(), position.offset)?;
+    let item = ancestors_at_offset(source_file.syntax(), position.offset)
+        .filter(|it| ast::MacroCall::can_cast(it.kind()))
+        .find_map(ast::Item::cast)?;
     let def: DefWithBody = match item {
         ast::Item::Fn(it) => sema.to_def(&it)?.into(),
         ast::Item::Const(it) => sema.to_def(&it)?.into(),
diff --git a/crates/ide/src/view_mir.rs b/crates/ide/src/view_mir.rs
index a36aba58bc0e..52ed420669a7 100644
--- a/crates/ide/src/view_mir.rs
+++ b/crates/ide/src/view_mir.rs
@@ -1,7 +1,7 @@
 use hir::{DefWithBody, Semantics};
 use ide_db::base_db::FilePosition;
 use ide_db::RootDatabase;
-use syntax::{algo::find_node_at_offset, ast, AstNode};
+use syntax::{algo::ancestors_at_offset, ast, AstNode};
 
 // Feature: View Mir
 //
@@ -18,7 +18,9 @@ fn body_mir(db: &RootDatabase, position: FilePosition) -> Option {
     let sema = Semantics::new(db);
     let source_file = sema.parse(position.file_id);
 
-    let item = find_node_at_offset::(source_file.syntax(), position.offset)?;
+    let item = ancestors_at_offset(source_file.syntax(), position.offset)
+        .filter(|it| ast::MacroCall::can_cast(it.kind()))
+        .find_map(ast::Item::cast)?;
     let def: DefWithBody = match item {
         ast::Item::Fn(it) => sema.to_def(&it)?.into(),
         ast::Item::Const(it) => sema.to_def(&it)?.into(),
diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs
index 45adbf5c573b..dba336ea7d6d 100644
--- a/crates/rust-analyzer/tests/slow-tests/tidy.rs
+++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs
@@ -250,6 +250,7 @@ fn check_dbg(path: &Path, text: &str) {
         // We have .dbg postfix
         "ide-completion/src/completions/postfix.rs",
         "ide-completion/src/completions/keyword.rs",
+        "ide-completion/src/tests/expression.rs",
         "ide-completion/src/tests/proc_macros.rs",
         // The documentation in string literals may contain anything for its own purposes
         "ide-completion/src/lib.rs",

From 3283a39a6cfa79fd40928a42af2d53f9332d7fad Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Sat, 9 Dec 2023 16:31:36 +0000
Subject: [PATCH 54/62] fix: tests

---
 crates/ide-completion/src/render.rs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 272999fe336d..090bca03a2da 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -1176,6 +1176,7 @@ fn main() { let _: m::Spam = S$0 }
                             is_private_editable: false,
                             postfix_match: None,
                             is_definite: false,
+                            bonus_score: 0,
                         },
                         trigger_call_info: true,
                     },
@@ -1204,6 +1205,7 @@ fn main() { let _: m::Spam = S$0 }
                             is_definite: false,
                         },
                         trigger_call_info: true,
+                        bonus_score: 0,
                     },
                 ]
             "#]],
@@ -2125,6 +2127,7 @@ fn foo() {
                             is_private_editable: false,
                             postfix_match: None,
                             is_definite: false,
+                            bonus_score: 0,
                         },
                     },
                 ]
@@ -2162,6 +2165,19 @@ fn main() {
                         ),
                         lookup: "foo",
                         detail: "fn() -> S",
+                        relevance: CompletionRelevance {
+                            exact_name_match: false,
+                            type_match: None,
+                            is_local: false,
+                            is_item_from_trait: false,
+                            is_name_already_imported: false,
+                            requires_import: false,
+                            is_op_method: false,
+                            is_private_editable: false,
+                            postfix_match: None,
+                            is_definite: false,
+                            bonus_score: 30,
+                        },
                         ref_match: "&@92",
                     },
                 ]

From ca880564a2eb11c17be67f34ac736476b95e1aa4 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Sat, 9 Dec 2023 16:43:32 +0000
Subject: [PATCH 55/62] fix: test

---
 crates/ide-completion/src/render.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 090bca03a2da..6e9efb9fa958 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -1203,9 +1203,9 @@ fn main() { let _: m::Spam = S$0 }
                             is_private_editable: false,
                             postfix_match: None,
                             is_definite: false,
+                            bonus_score: 0,
                         },
                         trigger_call_info: true,
-                        bonus_score: 0,
                     },
                 ]
             "#]],
@@ -1282,6 +1282,7 @@ fn foo() { A { the$0 } }
                             is_private_editable: false,
                             postfix_match: None,
                             is_definite: false,
+                            bonus_score: 0,
                         },
                     },
                 ]

From 2d58983d812d0aaeb63a7562043814e9fa5c9625 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Sat, 9 Dec 2023 16:48:10 +0000
Subject: [PATCH 56/62] undo debug change

---
 Cargo.toml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index d17fde0c1563..7cc6f8f1f048 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@ authors = ["rust-analyzer team"]
 [profile.dev]
 # Disabling debug info speeds up builds a bunch,
 # and we don't rely on it for debugging that much.
-debug = 2
+debug = 0
 
 [profile.dev.package]
 # These speed up local tests.
@@ -117,9 +117,9 @@ text-size = "1.1.1"
 tracing = "0.1.40"
 tracing-tree = "0.3.0"
 tracing-subscriber = { version = "0.3.18", default-features = false, features = [
-    "registry",
-    "fmt",
-    "tracing-log",
+  "registry",
+  "fmt",
+  "tracing-log",
 ] }
 triomphe = { version = "0.1.10", default-features = false, features = ["std"] }
 xshell = "0.2.5"

From b081f24f60c3e491b074e04ce92edbaa890d56e3 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Sat, 9 Dec 2023 17:56:36 +0000
Subject: [PATCH 57/62] save

---
 crates/ide-completion/src/completions.rs | 70 ++++++++++++------------
 crates/ide-completion/src/render.rs      | 27 +++++++++
 2 files changed, 62 insertions(+), 35 deletions(-)

diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index febc788c40a5..c073b635e9b5 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -593,40 +593,6 @@ impl Completions {
         }
         self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
     }
-
-    /// Sort the suggestions with `new` like functions first.
-    /// That means:
-    /// fn with no param that returns itself
-    /// fn with param that returns itself
-    pub(crate) fn sort_new_first(&mut self) {
-        // ToDo: Ensure these fn returns Self
-        fn maybe_new(item: &CompletionItem) -> bool {
-            item.detail.as_ref().map(|d| d.starts_with("fn() -> ")).unwrap_or_default()
-        }
-        fn maybe_new_with_args(item: &CompletionItem) -> bool {
-            item.detail
-                .as_ref()
-                .map(|d| d.starts_with("fn(") && d.contains("->") && !d.contains("&self"))
-                .unwrap_or_default()
-        }
-
-        fn maybe_builder(item: &CompletionItem) -> bool {
-            item.detail
-                .as_ref()
-                .map(|d| d.starts_with("fn() -> ") && d.contains("Builder"))
-                .unwrap_or_default()
-        }
-
-        for item in self.buf.iter_mut() {
-            if maybe_new(&item) {
-                item.bump_relevance_by(30);
-            } else if maybe_builder(&item) {
-                item.bump_relevance_by(20);
-            } else if maybe_new_with_args(&item) {
-                item.bump_relevance_by(10);
-            }
-        }
-    }
 }
 
 /// Calls the callback for each variant of the provided enum with the path to the variant.
@@ -730,7 +696,7 @@ pub(super) fn complete_name_ref(
                     snippet::complete_expr_snippet(acc, ctx, path_ctx, expr_ctx);
 
                     if matches!(ctx.token.kind(), syntax::SyntaxKind::COLON2) {
-                        acc.sort_new_first();
+                        bump_relevance_for_new_like_fns(acc);
                     }
                 }
                 PathKind::Type { location } => {
@@ -805,3 +771,37 @@ fn complete_patterns(
     pattern::complete_pattern(acc, ctx, pattern_ctx);
     record::complete_record_pattern_fields(acc, ctx, pattern_ctx);
 }
+
+/// Sort the suggestions with `new` like functions first.
+/// That means:
+/// fn with no param that returns itself
+/// fn with param that returns itself
+pub(crate) fn bump_relevance_for_new_like_fns(acc: &mut Completions) {
+    // ToDo: Ensure these fn returns Self
+    fn maybe_new(item: &CompletionItem) -> bool {
+        item.detail.as_ref().map(|d| d.starts_with("fn() -> ")).unwrap_or_default()
+    }
+    fn maybe_new_with_args(item: &CompletionItem) -> bool {
+        item.detail
+            .as_ref()
+            .map(|d| d.starts_with("fn(") && d.contains("->") && !d.contains("&self"))
+            .unwrap_or_default()
+    }
+
+    fn maybe_builder(item: &CompletionItem) -> bool {
+        item.detail
+            .as_ref()
+            .map(|d| d.starts_with("fn() -> ") && d.contains("Builder"))
+            .unwrap_or_default()
+    }
+
+    for item in acc.buf.iter_mut() {
+        if maybe_new(&item) {
+            item.bump_relevance_by(30);
+        } else if maybe_builder(&item) {
+            item.bump_relevance_by(20);
+        } else if maybe_new_with_args(&item) {
+            item.bump_relevance_by(10);
+        }
+    }
+}
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 6e9efb9fa958..c31a9b0d751d 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -2033,6 +2033,33 @@ fn test() {
                 me foo(…) [type_could_unify]
             "#]],
         );
+
+        check_relevance(
+            r#"
+struct A;
+struct ABuilder;
+impl A {
+    fn foo(&self) {}
+    fn new_1(input: u32) -> A { A }
+    fn new_2() -> Self { A }
+    fn aaaabuilder() -> ABuilder { A }
+    fn test() {
+        Self::$0;
+    }
+}
+"#,
+            // preference:
+            // fn with no param that returns itself
+            // builder like fn
+            // fn with param that returns itself
+            expect![[r#"
+                fn new_2() []
+                fn aaaabuilder() []
+                fn new_1(…) []
+                me foo(…) []
+                fn test() []
+            "#]],
+        );
     }
 
     #[test]

From 5150422b0295baac10d31e897cd62475178a5d41 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Wed, 13 Dec 2023 22:42:32 +0000
Subject: [PATCH 58/62] Refactor completion relevance calculation

---
 crates/ide-completion/src/completions.rs     | 38 --------------------
 crates/ide-completion/src/render.rs          | 33 +++--------------
 crates/ide-completion/src/render/function.rs | 30 ++++++++++++++++
 3 files changed, 35 insertions(+), 66 deletions(-)

diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index c073b635e9b5..7d38c638a8ed 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -694,10 +694,6 @@ pub(super) fn complete_name_ref(
                     dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx);
                     item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx);
                     snippet::complete_expr_snippet(acc, ctx, path_ctx, expr_ctx);
-
-                    if matches!(ctx.token.kind(), syntax::SyntaxKind::COLON2) {
-                        bump_relevance_for_new_like_fns(acc);
-                    }
                 }
                 PathKind::Type { location } => {
                     r#type::complete_type_path(acc, ctx, path_ctx, location);
@@ -771,37 +767,3 @@ fn complete_patterns(
     pattern::complete_pattern(acc, ctx, pattern_ctx);
     record::complete_record_pattern_fields(acc, ctx, pattern_ctx);
 }
-
-/// Sort the suggestions with `new` like functions first.
-/// That means:
-/// fn with no param that returns itself
-/// fn with param that returns itself
-pub(crate) fn bump_relevance_for_new_like_fns(acc: &mut Completions) {
-    // ToDo: Ensure these fn returns Self
-    fn maybe_new(item: &CompletionItem) -> bool {
-        item.detail.as_ref().map(|d| d.starts_with("fn() -> ")).unwrap_or_default()
-    }
-    fn maybe_new_with_args(item: &CompletionItem) -> bool {
-        item.detail
-            .as_ref()
-            .map(|d| d.starts_with("fn(") && d.contains("->") && !d.contains("&self"))
-            .unwrap_or_default()
-    }
-
-    fn maybe_builder(item: &CompletionItem) -> bool {
-        item.detail
-            .as_ref()
-            .map(|d| d.starts_with("fn() -> ") && d.contains("Builder"))
-            .unwrap_or_default()
-    }
-
-    for item in acc.buf.iter_mut() {
-        if maybe_new(&item) {
-            item.bump_relevance_by(30);
-        } else if maybe_builder(&item) {
-            item.bump_relevance_by(20);
-        } else if maybe_new_with_args(&item) {
-            item.bump_relevance_by(10);
-        }
-    }
-}
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index c31a9b0d751d..0a073563474f 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -17,7 +17,7 @@ use ide_db::{
     imports::import_assets::LocatedImport,
     RootDatabase, SnippetCap, SymbolKind,
 };
-use syntax::{AstNode, SmolStr, SyntaxKind, TextRange};
+use syntax::{AstNode, SmolStr, SyntaxKind, SyntaxToken, TextRange};
 use text_edit::TextEdit;
 
 use crate::{
@@ -72,6 +72,10 @@ impl<'a> RenderContext<'a> {
         self.completion.db
     }
 
+    fn token(&self) -> &SyntaxToken {
+        &self.completion.token
+    }
+
     fn source_range(&self) -> TextRange {
         self.completion.source_range()
     }
@@ -2033,33 +2037,6 @@ fn test() {
                 me foo(…) [type_could_unify]
             "#]],
         );
-
-        check_relevance(
-            r#"
-struct A;
-struct ABuilder;
-impl A {
-    fn foo(&self) {}
-    fn new_1(input: u32) -> A { A }
-    fn new_2() -> Self { A }
-    fn aaaabuilder() -> ABuilder { A }
-    fn test() {
-        Self::$0;
-    }
-}
-"#,
-            // preference:
-            // fn with no param that returns itself
-            // builder like fn
-            // fn with param that returns itself
-            expect![[r#"
-                fn new_2() []
-                fn aaaabuilder() []
-                fn new_1(…) []
-                me foo(…) []
-                fn test() []
-            "#]],
-        );
     }
 
     #[test]
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index d23ed71fdcc6..41518e1c3834 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -105,6 +105,7 @@ fn render(
         },
         exact_name_match: compute_exact_name_match(completion, &call),
         is_op_method,
+        bonus_score: calculate_bonus(&ctx, func, db),
         ..ctx.completion_relevance()
     });
 
@@ -153,6 +154,33 @@ fn render(
     item
 }
 
+/// When typing `::` of a type, the preferred orderer is:
+/// * Constructors: new like functions to be able to create the type,
+/// * Constructors that take args: Any other function that creates Self
+/// * Builder Methods: any builder methods available
+/// * Regular methods
+fn calculate_bonus(ctx: &RenderContext<'_>, func: hir::Function, db: &dyn HirDatabase) -> u32 {
+    if ctx.token().kind() != syntax::SyntaxKind::COLON2 || func.self_param(db).is_some() {
+        return 0;
+    }
+
+    let mut bonus = 0;
+
+    let has_args = !func.assoc_fn_params(db).is_empty();
+    let ret_type = func.ret_type(db);
+    if !has_args && !ret_type.is_unit() {
+        // fn() -> A
+        bonus += 30;
+    } else if has_args && !ret_type.is_unit() {
+        // fn(..) -> A
+        bonus += 20;
+    } else if !has_args && ret_type.display(db).to_string().ends_with("Builder") {
+        // -> [..]Builder
+        bonus += 10;
+    }
+    bonus
+}
+
 pub(super) fn add_call_parens<'b>(
     builder: &'b mut Builder,
     ctx: &CompletionContext<'_>,
@@ -213,6 +241,7 @@ pub(super) fn add_call_parens<'b>(
 
         (snippet, "(…)")
     };
+
     builder.label(SmolStr::from_iter([&name, label_suffix])).insert_snippet(cap, snippet)
 }
 
@@ -256,6 +285,7 @@ fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
     if !ret_ty.is_unit() {
         format_to!(detail, " -> {}", ret_ty.display(db));
     }
+
     detail
 }
 

From 2e6c660dacc5d5230ecc0ffd325bd5f602936a6a Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Wed, 13 Dec 2023 22:44:25 +0000
Subject: [PATCH 59/62] Remove unnecessary code and fix formatting

---
 crates/ide-completion/src/item.rs            | 4 ----
 crates/ide-completion/src/render/function.rs | 2 --
 crates/rust-analyzer/src/lsp/to_proto.rs     | 1 -
 3 files changed, 7 deletions(-)

diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index ad1bd603579d..af4d0657ccf3 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -391,10 +391,6 @@ impl CompletionItem {
             )
         })
     }
-
-    pub fn bump_relevance_by(&mut self, bonus: u32) {
-        self.relevance.bonus_score += bonus;
-    }
 }
 
 /// A helper to make `CompletionItem`s.
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 41518e1c3834..7a3523e6d73b 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -241,7 +241,6 @@ pub(super) fn add_call_parens<'b>(
 
         (snippet, "(…)")
     };
-
     builder.label(SmolStr::from_iter([&name, label_suffix])).insert_snippet(cap, snippet)
 }
 
@@ -285,7 +284,6 @@ fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
     if !ret_ty.is_unit() {
         format_to!(detail, " -> {}", ret_ty.display(db));
     }
-
     detail
 }
 
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index cd8befcc0868..dae560c5de12 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -349,7 +349,6 @@ fn completion_item(
         if relevance.is_relevant() && relevance.score() == max_relevance {
             res.preselect = Some(true);
         }
-
         // The relevance needs to be inverted to come up with a sort score
         // because the client will sort ascending.
         let sort_score = relevance.score() ^ 0xFF_FF_FF_FF;

From 0eb68ebbb48764e2f7790dd185c8c5e915fe6919 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Wed, 13 Dec 2023 22:55:29 +0000
Subject: [PATCH 60/62] Refactor render.rs: Rename test function to
 colon_complete_preferred_order_relevances

---
 crates/ide-completion/src/render.rs | 30 ++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 0a073563474f..33ac542eb0ed 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -2010,7 +2010,7 @@ fn main() {
     }
 
     #[test]
-    fn new_like_fns() {
+    fn colon_complete_preferred_order_relevances() {
         check_relevance(
             r#"
 struct A;
@@ -2022,6 +2022,34 @@ impl A {
     fn aaaabuilder() -> ABuilder { A }
 }
 
+fn test() {
+    let a = A::$0;
+}
+"#,
+            // preference:
+            // fn with no param that returns itself
+            // builder like fn
+            // fn with param that returns itself
+            expect![[r#"
+                fn new_2() [type_could_unify]
+                fn aaaabuilder() [type_could_unify]
+                fn new_1(…) [type_could_unify]
+                me foo(…) [type_could_unify]
+            "#]],
+        );
+
+        // Generic
+        check_relevance(
+            r#"
+struct A{item: T}
+struct ABuilder;
+impl A {
+    fn foo(&self) {}
+    fn new_1(input: u32, l: T) -> A { A }
+    fn new_2() -> Self { A { item: <_>::default()} }
+    fn aaaabuilder() -> ABuilder { A }
+}
+
 fn test() {
     let a = A::$0;
 }

From 444fa3b1655b394fcdc31c87de3e71c4f00ea756 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Wed, 13 Dec 2023 23:00:37 +0000
Subject: [PATCH 61/62] Fix preferred order of constructors and builder methods
 in render function

---
 crates/ide-completion/src/render/function.rs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 7a3523e6d73b..fe82869f3344 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -154,11 +154,12 @@ fn render(
     item
 }
 
-/// When typing `::` of a type, the preferred orderer is:
+/// When typing `::` of a type, the preferred order is:
 /// * Constructors: new like functions to be able to create the type,
 /// * Constructors that take args: Any other function that creates Self
 /// * Builder Methods: any builder methods available
-/// * Regular methods
+/// * Regular methods & Associated functions
+///
 fn calculate_bonus(ctx: &RenderContext<'_>, func: hir::Function, db: &dyn HirDatabase) -> u32 {
     if ctx.token().kind() != syntax::SyntaxKind::COLON2 || func.self_param(db).is_some() {
         return 0;

From 1eac62a4942ae89f2b215975028eef65f7983377 Mon Sep 17 00:00:00 2001
From: Mohammad Mustakim Ali 
Date: Wed, 13 Dec 2023 23:03:30 +0000
Subject: [PATCH 62/62] Refactor render.rs and function.rs

---
 crates/ide-completion/src/render.rs          | 4 ----
 crates/ide-completion/src/render/function.rs | 2 +-
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 33ac542eb0ed..e0490a3207b6 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -2054,10 +2054,6 @@ fn test() {
     let a = A::$0;
 }
 "#,
-            // preference:
-            // fn with no param that returns itself
-            // builder like fn
-            // fn with param that returns itself
             expect![[r#"
                 fn new_2() [type_could_unify]
                 fn aaaabuilder() [type_could_unify]
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index fe82869f3344..c6e94b2d69fb 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -156,8 +156,8 @@ fn render(
 
 /// When typing `::` of a type, the preferred order is:
 /// * Constructors: new like functions to be able to create the type,
+/// * Builder Methods,
 /// * Constructors that take args: Any other function that creates Self
-/// * Builder Methods: any builder methods available
 /// * Regular methods & Associated functions
 ///
 fn calculate_bonus(ctx: &RenderContext<'_>, func: hir::Function, db: &dyn HirDatabase) -> u32 {