From 7e0bd377c2ffe55a1f014ce932a7218a9b4dc639 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Wed, 20 Jul 2022 22:30:16 +0900 Subject: [PATCH 01/30] fix: don't replace default members' body --- .../src/handlers/add_missing_impl_members.rs | 17 ++++++++++------- .../ide-assists/src/utils/gen_trait_fn_body.rs | 11 ++++++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 6e6fd96a684fb..036bdd3a21f53 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -142,13 +142,16 @@ fn add_missing_impl_members_inner( Some(cap) => { let mut cursor = Cursor::Before(first_new_item.syntax()); let placeholder; - if let ast::AssocItem::Fn(func) = &first_new_item { - if try_gen_trait_body(ctx, func, &trait_, &impl_def).is_none() { - if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) - { - if m.syntax().text() == "todo!()" { - placeholder = m; - cursor = Cursor::Replace(placeholder.syntax()); + if let DefaultMethods::No = mode { + if let ast::AssocItem::Fn(func) = &first_new_item { + if try_gen_trait_body(ctx, func, &trait_, &impl_def).is_none() { + if let Some(m) = + func.syntax().descendants().find_map(ast::MacroCall::cast) + { + if m.syntax().text() == "todo!()" { + placeholder = m; + cursor = Cursor::Replace(placeholder.syntax()); + } } } } diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index ec4835969f886..43aaad3150ced 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -5,7 +5,7 @@ use syntax::{ ted, }; -/// Generate custom trait bodies where possible. +/// Generate custom trait bodies without default implementation where possible. /// /// Returns `Option` so that we can use `?` rather than `if let Some`. Returning /// `None` means that generating a custom trait body failed, and the body will remain @@ -28,6 +28,7 @@ pub(crate) fn gen_trait_fn_body( /// Generate a `Clone` impl based on the fields and members of the target type. fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { + debug_assert!(func.name().map_or(false, |name| name.text() == "clone")); fn gen_clone_call(target: ast::Expr) -> ast::Expr { let method = make::name_ref("clone"); make::expr_method_call(target, method, make::arg_list(None)) @@ -339,6 +340,7 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// Generate a `Hash` impl based on the fields and members of the target type. fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { + debug_assert!(func.name().map_or(false, |name| name.text() == "hash")); fn gen_hash_call(target: ast::Expr) -> ast::Stmt { let method = make::name_ref("hash"); let arg = make::expr_path(make::ext::ident_path("state")); @@ -394,9 +396,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// Generate a `PartialEq` impl based on the fields and members of the target type. fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - if func.name().map_or(false, |name| name.text() == "ne") { - return None; - } + debug_assert!(func.name().map_or(false, |name| name.text() == "eq")); fn gen_eq_chain(expr: Option, cmp: ast::Expr) -> Option { match expr { Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)), @@ -573,6 +573,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { + debug_assert!(func.name().map_or(false, |name| name.text() == "partial_cmp")); fn gen_partial_eq_match(match_target: ast::Expr) -> Option { let mut arms = vec![]; @@ -643,7 +644,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { make::block_expr(stmts.into_iter(), tail).indent(ast::edit::IndentLevel(1)) } - // No fields in the body means there's nothing to hash. + // No fields in the body means there's nothing to compare. None => { let expr = make::expr_literal("true").into(); make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)) From aeb07745d53a90cc8447c45328eed51e941d9307 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 23 Jul 2022 20:10:10 +0200 Subject: [PATCH 02/30] Spawn a proc-macro-srv instance per workspace --- crates/rust-analyzer/src/global_state.rs | 4 +-- crates/rust-analyzer/src/handlers.rs | 2 +- crates/rust-analyzer/src/reload.rs | 43 +++++++++++++----------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 4af60035a20e0..700e3e19bb96b 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -61,7 +61,7 @@ pub(crate) struct GlobalState { pub(crate) proc_macro_changed: bool, pub(crate) last_reported_status: Option, pub(crate) source_root_config: SourceRootConfig, - pub(crate) proc_macro_client: Option, + pub(crate) proc_macro_clients: Vec>, pub(crate) flycheck: Vec, pub(crate) flycheck_sender: Sender, @@ -151,7 +151,7 @@ impl GlobalState { proc_macro_changed: false, last_reported_status: None, source_root_config: SourceRootConfig::default(), - proc_macro_client: None, + proc_macro_clients: vec![], flycheck: Vec::new(), flycheck_sender, diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 520aa7d1dd487..deb777c952fdf 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -44,7 +44,7 @@ use crate::{ }; pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> { - state.proc_macro_client = None; + state.proc_macro_clients.clear(); state.proc_macro_changed = false; state.fetch_workspaces_queue.request_op("reload workspace request".to_string()); state.fetch_build_data_queue.request_op("reload workspace request".to_string()); diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 77125f88f473d..d69b2be25650b 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -303,18 +303,21 @@ impl GlobalState { let files_config = self.config.files(); let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude); - if self.proc_macro_client.is_none() { + if self.proc_macro_clients.is_empty() { if let Some((path, args)) = self.config.proc_macro_srv() { - match ProcMacroServer::spawn(path.clone(), args) { - Ok(it) => self.proc_macro_client = Some(it), - Err(err) => { - tracing::error!( - "Failed to run proc_macro_srv from path {}, error: {:?}", - path.display(), - err - ); - } - } + self.proc_macro_clients = (0..self.workspaces.len()) + .map(|_| match ProcMacroServer::spawn(path.clone(), args.clone()) { + Ok(it) => Some(it), + Err(err) => { + tracing::error!( + "Failed to run proc_macro_srv from path {}, error: {:?}", + path.display(), + err + ); + None + } + }) + .collect(); } } @@ -331,15 +334,7 @@ impl GlobalState { // Create crate graph from all the workspaces let crate_graph = { - let proc_macro_client = self.proc_macro_client.as_ref(); let dummy_replacements = self.config.dummy_replacements(); - let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| { - load_proc_macro( - proc_macro_client, - path, - dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(), - ) - }; let vfs = &mut self.vfs.write().0; let loader = &mut self.loader; @@ -359,7 +354,15 @@ impl GlobalState { }; let mut crate_graph = CrateGraph::default(); - for ws in self.workspaces.iter() { + for (idx, ws) in self.workspaces.iter().enumerate() { + let proc_macro_client = self.proc_macro_clients[idx].as_ref(); + let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| { + load_proc_macro( + proc_macro_client, + path, + dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(), + ) + }; crate_graph.extend(ws.to_crate_graph(&mut load_proc_macro, &mut load)); } crate_graph From 50b27e57baa83d63fecf77c712c345d480f2c3c6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 23 Jul 2022 20:24:01 +0200 Subject: [PATCH 03/30] Better error messages when the proc-macro-server fails to start --- crates/rust-analyzer/src/cli/load_cargo.rs | 6 +++--- crates/rust-analyzer/src/global_state.rs | 2 +- crates/rust-analyzer/src/reload.rs | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 4243af25ff1a1..0ada4b73e842d 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -60,9 +60,9 @@ pub fn load_workspace( let proc_macro_client = if load_config.with_proc_macro { let path = AbsPathBuf::assert(std::env::current_exe()?); - Some(ProcMacroServer::spawn(path, &["proc-macro"]).unwrap()) + Ok(ProcMacroServer::spawn(path, &["proc-macro"]).unwrap()) } else { - None + Err("proc macro server not started".to_owned()) }; let crate_graph = ws.to_crate_graph( @@ -89,7 +89,7 @@ pub fn load_workspace( if load_config.prefill_caches { host.analysis().parallel_prime_caches(1, |_| {})?; } - Ok((host, vfs, proc_macro_client)) + Ok((host, vfs, proc_macro_client.ok())) } fn load_crate_graph( diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 700e3e19bb96b..8f881cba4dbd7 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -61,7 +61,7 @@ pub(crate) struct GlobalState { pub(crate) proc_macro_changed: bool, pub(crate) last_reported_status: Option, pub(crate) source_root_config: SourceRootConfig, - pub(crate) proc_macro_clients: Vec>, + pub(crate) proc_macro_clients: Vec>, pub(crate) flycheck: Vec, pub(crate) flycheck_sender: Sender, diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index d69b2be25650b..e5802773e7473 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -306,16 +306,16 @@ impl GlobalState { if self.proc_macro_clients.is_empty() { if let Some((path, args)) = self.config.proc_macro_srv() { self.proc_macro_clients = (0..self.workspaces.len()) - .map(|_| match ProcMacroServer::spawn(path.clone(), args.clone()) { - Ok(it) => Some(it), - Err(err) => { - tracing::error!( + .map(|_| { + ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| { + let error = format!( "Failed to run proc_macro_srv from path {}, error: {:?}", path.display(), err ); - None - } + tracing::error!(error); + error + }) }) .collect(); } @@ -539,14 +539,14 @@ impl SourceRootConfig { /// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace` /// with an identity dummy expander. pub(crate) fn load_proc_macro( - server: Option<&ProcMacroServer>, + server: Result<&ProcMacroServer, &String>, path: &AbsPath, dummy_replace: &[Box], ) -> ProcMacroLoadResult { let res: Result, String> = (|| { let dylib = MacroDylib::new(path.to_path_buf()) .map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?; - let server = server.ok_or_else(|| format!("Proc-macro server not started"))?; + let server = server.map_err(ToOwned::to_owned)?; let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?; if vec.is_empty() { return Err("proc macro library returned no proc macros".to_string()); From fb063d360ce66e9f1316c3e64885e00344062e55 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Fri, 22 Jul 2022 18:12:21 +0900 Subject: [PATCH 04/30] Add `ellipsis` to HIR `RecordLit` --- crates/hir-def/src/body/lower.rs | 5 +++-- crates/hir-def/src/expr.rs | 1 + crates/hir-ty/src/diagnostics/expr.rs | 2 +- crates/hir-ty/src/infer/expr.rs | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index c3f261122784e..b43699de12030 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -378,9 +378,10 @@ impl ExprCollector<'_> { }) .collect(); let spread = nfl.spread().map(|s| self.collect_expr(s)); - Expr::RecordLit { path, fields, spread } + let ellipsis = nfl.dotdot_token().is_some(); + Expr::RecordLit { path, fields, spread, ellipsis } } else { - Expr::RecordLit { path, fields: Box::default(), spread: None } + Expr::RecordLit { path, fields: Box::default(), spread: None, ellipsis: false } }; self.alloc_expr(record_lit, syntax_ptr) diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index a991365d6bf49..145fe4c19db40 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -138,6 +138,7 @@ pub enum Expr { path: Option>, fields: Box<[RecordLitField]>, spread: Option, + ellipsis: bool, }, Field { expr: ExprId, diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 8cca522aef623..1fcb4c2976ebc 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -305,7 +305,7 @@ pub fn record_literal_missing_fields( expr: &Expr, ) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> { let (fields, exhaustive) = match expr { - Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()), + Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()), _ => return None, }; diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 2f33467072095..9debe835473a8 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -421,7 +421,7 @@ impl<'a> InferenceContext<'a> { } TyKind::Never.intern(Interner) } - Expr::RecordLit { path, fields, spread } => { + Expr::RecordLit { path, fields, spread, .. } => { let (ty, def_id) = self.resolve_variant(path.as_deref(), false); if let Some(variant) = def_id { self.write_variant_resolution(tgt_expr.into(), variant); From 5f3f4284ddb63d703eb717f75d16365e5a1442e5 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Fri, 22 Jul 2022 18:16:46 +0200 Subject: [PATCH 05/30] Don't run slow tests in Rust CI, only RA CI --- crates/test-utils/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index dedfbd7afef42..8a9cfb6c22e45 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -8,9 +8,9 @@ #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +mod assert_linear; pub mod bench_fixture; mod fixture; -mod assert_linear; use std::{ collections::BTreeMap, @@ -391,7 +391,8 @@ fn main() { /// also creates a file at `./target/.slow_tests_cookie` which serves as a flag /// that slow tests did run. pub fn skip_slow_tests() -> bool { - let should_skip = std::env::var("CI").is_err() && std::env::var("RUN_SLOW_TESTS").is_err(); + let should_skip = (std::env::var("CI").is_err() && std::env::var("RUN_SLOW_TESTS").is_err()) + || std::env::var("SKIP_SLOW_TESTS").is_ok(); if should_skip { eprintln!("ignoring slow test"); } else { @@ -475,7 +476,7 @@ pub fn ensure_file_contents(file: &Path, contents: &str) { pub fn try_ensure_file_contents(file: &Path, contents: &str) -> Result<(), ()> { match std::fs::read_to_string(file) { Ok(old_contents) if normalize_newlines(&old_contents) == normalize_newlines(contents) => { - return Ok(()) + return Ok(()); } _ => (), } From 0bffdf262716a9067b27063aacc559128259211b Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sat, 23 Jul 2022 02:06:11 +0200 Subject: [PATCH 06/30] Disable all source-gen tests at compile time --- crates/ide-assists/Cargo.toml | 3 +++ crates/ide-assists/src/tests/sourcegen.rs | 16 ++++++++++++++-- crates/ide-diagnostics/Cargo.toml | 3 +++ crates/ide-diagnostics/src/tests/sourcegen.rs | 7 +++++++ crates/ide/Cargo.toml | 3 +++ crates/rust-analyzer/Cargo.toml | 7 ++++++- .../rust-analyzer/tests/slow-tests/sourcegen.rs | 6 ++++++ crates/rust-analyzer/tests/slow-tests/tidy.rs | 7 ++++++- crates/sourcegen/Cargo.toml | 3 +++ crates/syntax/Cargo.toml | 3 +++ crates/syntax/src/tests.rs | 3 ++- crates/syntax/src/tests/ast_src.rs | 7 +++++++ 12 files changed, 63 insertions(+), 5 deletions(-) diff --git a/crates/ide-assists/Cargo.toml b/crates/ide-assists/Cargo.toml index 51e43d21cb03d..fca09d384c6ef 100644 --- a/crates/ide-assists/Cargo.toml +++ b/crates/ide-assists/Cargo.toml @@ -26,3 +26,6 @@ hir = { path = "../hir", version = "0.0.0" } test-utils = { path = "../test-utils" } sourcegen = { path = "../sourcegen" } expect-test = "1.4.0" + +[features] +in-rust-tree = [] diff --git a/crates/ide-assists/src/tests/sourcegen.rs b/crates/ide-assists/src/tests/sourcegen.rs index d45e54186bb67..97d5b2cbbae77 100644 --- a/crates/ide-assists/src/tests/sourcegen.rs +++ b/crates/ide-assists/src/tests/sourcegen.rs @@ -1,9 +1,12 @@ //! Generates `assists.md` documentation. +#[cfg(not(feature = "in-rust-tree"))] use std::{fmt, fs, path::Path}; +#[cfg(not(feature = "in-rust-tree"))] use test_utils::project_root; +#[cfg(not(feature = "in-rust-tree"))] #[test] fn sourcegen_assists_docs() { let assists = Assist::collect(); @@ -59,6 +62,8 @@ r#####" fs::write(dst, contents).unwrap(); } } + +#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] struct Section { doc: String, @@ -66,6 +71,7 @@ struct Section { after: String, } +#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] struct Assist { id: String, @@ -73,6 +79,7 @@ struct Assist { sections: Vec
, } +#[cfg(not(feature = "in-rust-tree"))] impl Assist { fn collect() -> Vec { let handlers_dir = project_root().join("crates/ide-assists/src/handlers"); @@ -104,9 +111,11 @@ impl Assist { while lines.peek().is_some() { let doc = take_until(lines.by_ref(), "```").trim().to_string(); assert!( - (doc.chars().next().unwrap().is_ascii_uppercase() && doc.ends_with('.')) || assist.sections.len() > 0, + (doc.chars().next().unwrap().is_ascii_uppercase() && doc.ends_with('.')) + || assist.sections.len() > 0, "\n\n{}: assist docs should be proper sentences, with capitalization and a full stop at the end.\n\n{}\n\n", - &assist.id, doc, + &assist.id, + doc, ); let before = take_until(lines.by_ref(), "```"); @@ -135,6 +144,7 @@ impl Assist { } } +#[cfg(not(feature = "in-rust-tree"))] impl fmt::Display for Assist { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let _ = writeln!( @@ -169,6 +179,7 @@ impl fmt::Display for Assist { } } +#[cfg(not(feature = "in-rust-tree"))] fn hide_hash_comments(text: &str) -> String { text.split('\n') // want final newline .filter(|&it| !(it.starts_with("# ") || it == "#")) @@ -176,6 +187,7 @@ fn hide_hash_comments(text: &str) -> String { .collect() } +#[cfg(not(feature = "in-rust-tree"))] fn reveal_hash_comments(text: &str) -> String { text.split('\n') // want final newline .map(|it| { diff --git a/crates/ide-diagnostics/Cargo.toml b/crates/ide-diagnostics/Cargo.toml index a79adca4cdba7..e221425edd5bf 100644 --- a/crates/ide-diagnostics/Cargo.toml +++ b/crates/ide-diagnostics/Cargo.toml @@ -29,3 +29,6 @@ expect-test = "1.4.0" test-utils = { path = "../test-utils" } sourcegen = { path = "../sourcegen" } + +[features] +in-rust-tree = [] diff --git a/crates/ide-diagnostics/src/tests/sourcegen.rs b/crates/ide-diagnostics/src/tests/sourcegen.rs index ec6558a46efb4..24bf6c3589491 100644 --- a/crates/ide-diagnostics/src/tests/sourcegen.rs +++ b/crates/ide-diagnostics/src/tests/sourcegen.rs @@ -1,9 +1,12 @@ //! Generates `assists.md` documentation. +#[cfg(not(feature = "in-rust-tree"))] use std::{fmt, fs, io, path::PathBuf}; +#[cfg(not(feature = "in-rust-tree"))] use sourcegen::project_root; +#[cfg(not(feature = "in-rust-tree"))] #[test] fn sourcegen_diagnostic_docs() { let diagnostics = Diagnostic::collect().unwrap(); @@ -14,6 +17,7 @@ fn sourcegen_diagnostic_docs() { fs::write(&dst, &contents).unwrap(); } +#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] struct Diagnostic { id: String, @@ -21,6 +25,7 @@ struct Diagnostic { doc: String, } +#[cfg(not(feature = "in-rust-tree"))] impl Diagnostic { fn collect() -> io::Result> { let handlers_dir = project_root().join("crates/ide-diagnostics/src/handlers"); @@ -51,6 +56,7 @@ impl Diagnostic { } } +#[cfg(not(feature = "in-rust-tree"))] fn is_valid_diagnostic_name(diagnostic: &str) -> Result<(), String> { let diagnostic = diagnostic.trim(); if diagnostic.find(char::is_whitespace).is_some() { @@ -66,6 +72,7 @@ fn is_valid_diagnostic_name(diagnostic: &str) -> Result<(), String> { Ok(()) } +#[cfg(not(feature = "in-rust-tree"))] impl fmt::Display for Diagnostic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index 95a54b75e2850..0e9771cd2ebaf 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml @@ -42,3 +42,6 @@ toolchain = { path = "../toolchain", version = "0.0.0" } [dev-dependencies] test-utils = { path = "../test-utils" } expect-test = "1.4.0" + +[features] +in-rust-tree = ["ide-assists/in-rust-tree", "ide-diagnostics/in-rust-tree"] diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 41205f2584a97..07771d1b392ce 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -84,4 +84,9 @@ mbe = { path = "../mbe" } [features] jemalloc = ["jemallocator", "profile/jemalloc"] force-always-assert = ["always-assert/force"] -in-rust-tree = ["proc-macro-srv/sysroot-abi"] +in-rust-tree = [ + "proc-macro-srv/sysroot-abi", + "sourcegen/in-rust-tree", + "ide/in-rust-tree", + "syntax/in-rust-tree" +] diff --git a/crates/rust-analyzer/tests/slow-tests/sourcegen.rs b/crates/rust-analyzer/tests/slow-tests/sourcegen.rs index e6ac018a05fea..3c1f8a304ec7b 100644 --- a/crates/rust-analyzer/tests/slow-tests/sourcegen.rs +++ b/crates/rust-analyzer/tests/slow-tests/sourcegen.rs @@ -1,7 +1,9 @@ //! Generates `assists.md` documentation. +#[cfg(not(feature = "in-rust-tree"))] use std::{fmt, fs, io, path::PathBuf}; +#[cfg(not(feature = "in-rust-tree"))] #[test] fn sourcegen_feature_docs() { let features = Feature::collect().unwrap(); @@ -17,6 +19,7 @@ fn sourcegen_feature_docs() { fs::write(&dst, &contents).unwrap(); } +#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] struct Feature { id: String, @@ -24,6 +27,7 @@ struct Feature { doc: String, } +#[cfg(not(feature = "in-rust-tree"))] impl Feature { fn collect() -> io::Result> { let crates_dir = sourcegen::project_root().join("crates"); @@ -54,6 +58,7 @@ impl Feature { } } +#[cfg(not(feature = "in-rust-tree"))] fn is_valid_feature_name(feature: &str) -> Result<(), String> { 'word: for word in feature.split_whitespace() { for short in ["to", "and"] { @@ -73,6 +78,7 @@ fn is_valid_feature_name(feature: &str) -> Result<(), String> { Ok(()) } +#[cfg(not(feature = "in-rust-tree"))] impl fmt::Display for Feature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs index dc3c5539c2cb4..18f95925d9a49 100644 --- a/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -3,8 +3,12 @@ use std::{ path::{Path, PathBuf}, }; -use xshell::{cmd, Shell}; +use xshell::Shell; +#[cfg(not(feature = "in-rust-tree"))] +use xshell::cmd; + +#[cfg(not(feature = "in-rust-tree"))] #[test] fn check_code_formatting() { let sh = &Shell::new().unwrap(); @@ -168,6 +172,7 @@ See https://github.com/rust-lang/rust-clippy/issues/5537 for discussion. } } +#[cfg(not(feature = "in-rust-tree"))] #[test] fn check_licenses() { let sh = &Shell::new().unwrap(); diff --git a/crates/sourcegen/Cargo.toml b/crates/sourcegen/Cargo.toml index e75867e2d81cf..a84110d940bc7 100644 --- a/crates/sourcegen/Cargo.toml +++ b/crates/sourcegen/Cargo.toml @@ -11,3 +11,6 @@ doctest = false [dependencies] xshell = "0.2.2" + +[features] +in-rust-tree = [] diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index a56c9dec401e9..0e2dec386ff77 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -34,3 +34,6 @@ ungrammar = "1.16.1" test-utils = { path = "../test-utils" } sourcegen = { path = "../sourcegen" } + +[features] +in-rust-tree = [] diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs index 0611143e2afef..ed6430f536113 100644 --- a/crates/syntax/src/tests.rs +++ b/crates/syntax/src/tests.rs @@ -1,5 +1,6 @@ -mod sourcegen_ast; mod ast_src; +#[cfg(not(feature = "in-rust-tree"))] +mod sourcegen_ast; use std::{ fs, diff --git a/crates/syntax/src/tests/ast_src.rs b/crates/syntax/src/tests/ast_src.rs index cf5be1c30fba2..93959d4ed796e 100644 --- a/crates/syntax/src/tests/ast_src.rs +++ b/crates/syntax/src/tests/ast_src.rs @@ -1,5 +1,6 @@ //! Defines input for code generation process. +#[cfg(not(feature = "in-rust-tree"))] pub(crate) struct KindsSrc<'a> { pub(crate) punct: &'a [(&'a str, &'a str)], pub(crate) keywords: &'a [&'a str], @@ -9,6 +10,7 @@ pub(crate) struct KindsSrc<'a> { pub(crate) nodes: &'a [&'a str], } +#[cfg(not(feature = "in-rust-tree"))] pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { punct: &[ (";", "SEMICOLON"), @@ -216,6 +218,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { ], }; +#[cfg(not(feature = "in-rust-tree"))] #[derive(Default, Debug)] pub(crate) struct AstSrc { pub(crate) tokens: Vec, @@ -223,6 +226,7 @@ pub(crate) struct AstSrc { pub(crate) enums: Vec, } +#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] pub(crate) struct AstNodeSrc { pub(crate) doc: Vec, @@ -231,18 +235,21 @@ pub(crate) struct AstNodeSrc { pub(crate) fields: Vec, } +#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug, Eq, PartialEq)] pub(crate) enum Field { Token(String), Node { name: String, ty: String, cardinality: Cardinality }, } +#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug, Eq, PartialEq)] pub(crate) enum Cardinality { Optional, Many, } +#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] pub(crate) struct AstEnumSrc { pub(crate) doc: Vec, From b351e115d68e0da23070767551b7155cbd4fbe7c Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sat, 23 Jul 2022 17:23:13 +0200 Subject: [PATCH 07/30] Move cfg attrs up to the mod definitions to disable sourcegen --- crates/ide-assists/src/tests.rs | 3 ++- crates/ide-assists/src/tests/sourcegen.rs | 9 --------- crates/ide-diagnostics/src/tests.rs | 1 + crates/ide-diagnostics/src/tests/sourcegen.rs | 7 ------- crates/rust-analyzer/tests/slow-tests/main.rs | 5 +++-- crates/rust-analyzer/tests/slow-tests/sourcegen.rs | 6 ------ crates/syntax/src/tests.rs | 1 + crates/syntax/src/tests/ast_src.rs | 7 ------- 8 files changed, 7 insertions(+), 32 deletions(-) diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index 249a56b4ae3c1..9cd66c6b3b07b 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -1,5 +1,6 @@ -mod sourcegen; mod generated; +#[cfg(not(feature = "in-rust-tree"))] +mod sourcegen; use expect_test::expect; use hir::{db::DefDatabase, Semantics}; diff --git a/crates/ide-assists/src/tests/sourcegen.rs b/crates/ide-assists/src/tests/sourcegen.rs index 97d5b2cbbae77..070b83d3c16b1 100644 --- a/crates/ide-assists/src/tests/sourcegen.rs +++ b/crates/ide-assists/src/tests/sourcegen.rs @@ -1,12 +1,9 @@ //! Generates `assists.md` documentation. -#[cfg(not(feature = "in-rust-tree"))] use std::{fmt, fs, path::Path}; -#[cfg(not(feature = "in-rust-tree"))] use test_utils::project_root; -#[cfg(not(feature = "in-rust-tree"))] #[test] fn sourcegen_assists_docs() { let assists = Assist::collect(); @@ -63,7 +60,6 @@ r#####" } } -#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] struct Section { doc: String, @@ -71,7 +67,6 @@ struct Section { after: String, } -#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] struct Assist { id: String, @@ -79,7 +74,6 @@ struct Assist { sections: Vec
, } -#[cfg(not(feature = "in-rust-tree"))] impl Assist { fn collect() -> Vec { let handlers_dir = project_root().join("crates/ide-assists/src/handlers"); @@ -144,7 +138,6 @@ impl Assist { } } -#[cfg(not(feature = "in-rust-tree"))] impl fmt::Display for Assist { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let _ = writeln!( @@ -179,7 +172,6 @@ impl fmt::Display for Assist { } } -#[cfg(not(feature = "in-rust-tree"))] fn hide_hash_comments(text: &str) -> String { text.split('\n') // want final newline .filter(|&it| !(it.starts_with("# ") || it == "#")) @@ -187,7 +179,6 @@ fn hide_hash_comments(text: &str) -> String { .collect() } -#[cfg(not(feature = "in-rust-tree"))] fn reveal_hash_comments(text: &str) -> String { text.split('\n') // want final newline .map(|it| { diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index 7cd79c7ceef58..7312bca32fed9 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "in-rust-tree"))] mod sourcegen; use expect_test::Expect; diff --git a/crates/ide-diagnostics/src/tests/sourcegen.rs b/crates/ide-diagnostics/src/tests/sourcegen.rs index 24bf6c3589491..ec6558a46efb4 100644 --- a/crates/ide-diagnostics/src/tests/sourcegen.rs +++ b/crates/ide-diagnostics/src/tests/sourcegen.rs @@ -1,12 +1,9 @@ //! Generates `assists.md` documentation. -#[cfg(not(feature = "in-rust-tree"))] use std::{fmt, fs, io, path::PathBuf}; -#[cfg(not(feature = "in-rust-tree"))] use sourcegen::project_root; -#[cfg(not(feature = "in-rust-tree"))] #[test] fn sourcegen_diagnostic_docs() { let diagnostics = Diagnostic::collect().unwrap(); @@ -17,7 +14,6 @@ fn sourcegen_diagnostic_docs() { fs::write(&dst, &contents).unwrap(); } -#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] struct Diagnostic { id: String, @@ -25,7 +21,6 @@ struct Diagnostic { doc: String, } -#[cfg(not(feature = "in-rust-tree"))] impl Diagnostic { fn collect() -> io::Result> { let handlers_dir = project_root().join("crates/ide-diagnostics/src/handlers"); @@ -56,7 +51,6 @@ impl Diagnostic { } } -#[cfg(not(feature = "in-rust-tree"))] fn is_valid_diagnostic_name(diagnostic: &str) -> Result<(), String> { let diagnostic = diagnostic.trim(); if diagnostic.find(char::is_whitespace).is_some() { @@ -72,7 +66,6 @@ fn is_valid_diagnostic_name(diagnostic: &str) -> Result<(), String> { Ok(()) } -#[cfg(not(feature = "in-rust-tree"))] impl fmt::Display for Diagnostic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index eef76343dc0da..4cc46af1b17c5 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -10,10 +10,11 @@ #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#[cfg(not(feature = "in-rust-tree"))] mod sourcegen; -mod tidy; -mod testdir; mod support; +mod testdir; +mod tidy; use std::{collections::HashMap, path::PathBuf, time::Instant}; diff --git a/crates/rust-analyzer/tests/slow-tests/sourcegen.rs b/crates/rust-analyzer/tests/slow-tests/sourcegen.rs index 3c1f8a304ec7b..e6ac018a05fea 100644 --- a/crates/rust-analyzer/tests/slow-tests/sourcegen.rs +++ b/crates/rust-analyzer/tests/slow-tests/sourcegen.rs @@ -1,9 +1,7 @@ //! Generates `assists.md` documentation. -#[cfg(not(feature = "in-rust-tree"))] use std::{fmt, fs, io, path::PathBuf}; -#[cfg(not(feature = "in-rust-tree"))] #[test] fn sourcegen_feature_docs() { let features = Feature::collect().unwrap(); @@ -19,7 +17,6 @@ fn sourcegen_feature_docs() { fs::write(&dst, &contents).unwrap(); } -#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] struct Feature { id: String, @@ -27,7 +24,6 @@ struct Feature { doc: String, } -#[cfg(not(feature = "in-rust-tree"))] impl Feature { fn collect() -> io::Result> { let crates_dir = sourcegen::project_root().join("crates"); @@ -58,7 +54,6 @@ impl Feature { } } -#[cfg(not(feature = "in-rust-tree"))] fn is_valid_feature_name(feature: &str) -> Result<(), String> { 'word: for word in feature.split_whitespace() { for short in ["to", "and"] { @@ -78,7 +73,6 @@ fn is_valid_feature_name(feature: &str) -> Result<(), String> { Ok(()) } -#[cfg(not(feature = "in-rust-tree"))] impl fmt::Display for Feature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs index ed6430f536113..58fba8cfa8f05 100644 --- a/crates/syntax/src/tests.rs +++ b/crates/syntax/src/tests.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "in-rust-tree"))] mod ast_src; #[cfg(not(feature = "in-rust-tree"))] mod sourcegen_ast; diff --git a/crates/syntax/src/tests/ast_src.rs b/crates/syntax/src/tests/ast_src.rs index 93959d4ed796e..cf5be1c30fba2 100644 --- a/crates/syntax/src/tests/ast_src.rs +++ b/crates/syntax/src/tests/ast_src.rs @@ -1,6 +1,5 @@ //! Defines input for code generation process. -#[cfg(not(feature = "in-rust-tree"))] pub(crate) struct KindsSrc<'a> { pub(crate) punct: &'a [(&'a str, &'a str)], pub(crate) keywords: &'a [&'a str], @@ -10,7 +9,6 @@ pub(crate) struct KindsSrc<'a> { pub(crate) nodes: &'a [&'a str], } -#[cfg(not(feature = "in-rust-tree"))] pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { punct: &[ (";", "SEMICOLON"), @@ -218,7 +216,6 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { ], }; -#[cfg(not(feature = "in-rust-tree"))] #[derive(Default, Debug)] pub(crate) struct AstSrc { pub(crate) tokens: Vec, @@ -226,7 +223,6 @@ pub(crate) struct AstSrc { pub(crate) enums: Vec, } -#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] pub(crate) struct AstNodeSrc { pub(crate) doc: Vec, @@ -235,21 +231,18 @@ pub(crate) struct AstNodeSrc { pub(crate) fields: Vec, } -#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug, Eq, PartialEq)] pub(crate) enum Field { Token(String), Node { name: String, ty: String, cardinality: Cardinality }, } -#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug, Eq, PartialEq)] pub(crate) enum Cardinality { Optional, Many, } -#[cfg(not(feature = "in-rust-tree"))] #[derive(Debug)] pub(crate) struct AstEnumSrc { pub(crate) doc: Vec, From 20eb2ddb2ed6eee92f2ca29e7f2767fe37ab779d Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 23 Jul 2022 15:37:46 -0500 Subject: [PATCH 08/30] Small fixups - use `path` instead of `paths` - don't mark rust-analyzer as an optional tool - print the cargo command that's run in the proc-macro-test build script this originally was part of a change to fix `test --stage 0 rust-analyzer`, but I'm going to leave that for a separate PR so it's easier to review. --- crates/proc-macro-test/build.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/proc-macro-test/build.rs b/crates/proc-macro-test/build.rs index cd99eea5ae3f9..c90144509dec5 100644 --- a/crates/proc-macro-test/build.rs +++ b/crates/proc-macro-test/build.rs @@ -62,7 +62,7 @@ fn main() { Command::new(toolchain::cargo()) }; - let output = cmd + cmd .current_dir(&staging_dir) .args(&["build", "-p", "proc-macro-test-impl", "--message-format", "json"]) // Explicit override the target directory to avoid using the same one which the parent @@ -70,9 +70,11 @@ fn main() { // This can happen when `CARGO_TARGET_DIR` is set or global config forces all cargo // instance to use the same target directory. .arg("--target-dir") - .arg(&target_dir) - .output() - .unwrap(); + .arg(&target_dir); + + println!("Running {:?}", cmd); + + let output = cmd.output().unwrap(); if !output.status.success() { println!("proc-macro-test-impl failed to build"); println!("============ stdout ============"); From 74998e46e9eb4cd1bb112e5652bb9bb40c6c7ab3 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 24 Jul 2022 14:05:35 +0200 Subject: [PATCH 09/30] Fix .gitattributes for test_data --- .gitattributes | 3 +- .../validation/0045_ambiguous_trait_object.rs | 12 +- .../validation/0046_mutable_const_item.rs | 2 +- .../parser/validation/invalid_let_expr.rast | 432 +++++++++--------- 4 files changed, 225 insertions(+), 224 deletions(-) diff --git a/.gitattributes b/.gitattributes index 3b3d2d0d656d3..cb87b5d01385a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,8 @@ * text=auto eol=lf + # git grep shouldn't match entries in this benchmark data bench_data/** binary -crates/syntax/test_data/** -text eof=LF + # Older git versions try to fix line endings on images, this prevents it. *.png binary *.jpg binary diff --git a/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rs b/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rs index 3a73d81bb5dc6..0a5958f25f7ab 100644 --- a/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rs +++ b/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rs @@ -1,6 +1,6 @@ -type Foo<'a> = &'a dyn Send + Sync; -type Foo = *const dyn Send + Sync; -type Foo = fn() -> dyn Send + 'static; -fn main() { - let b = (&a) as &dyn Add + Other; -} +type Foo<'a> = &'a dyn Send + Sync; +type Foo = *const dyn Send + Sync; +type Foo = fn() -> dyn Send + 'static; +fn main() { + let b = (&a) as &dyn Add + Other; +} diff --git a/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rs b/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rs index b34336f3f15d0..ccab6bccfaa12 100644 --- a/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rs +++ b/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rs @@ -1 +1 @@ -const mut FOO: () = (); +const mut FOO: () = (); diff --git a/crates/syntax/test_data/parser/validation/invalid_let_expr.rast b/crates/syntax/test_data/parser/validation/invalid_let_expr.rast index 5b37b59783204..9e1e4886401a1 100644 --- a/crates/syntax/test_data/parser/validation/invalid_let_expr.rast +++ b/crates/syntax/test_data/parser/validation/invalid_let_expr.rast @@ -1,216 +1,216 @@ -SOURCE_FILE@0..282 - FN@0..281 - FN_KW@0..2 "fn" - WHITESPACE@2..3 " " - NAME@3..6 - IDENT@3..6 "foo" - PARAM_LIST@6..8 - L_PAREN@6..7 "(" - R_PAREN@7..8 ")" - WHITESPACE@8..9 " " - BLOCK_EXPR@9..281 - STMT_LIST@9..281 - L_CURLY@9..10 "{" - WHITESPACE@10..15 "\n " - CONST@15..42 - CONST_KW@15..20 "const" - WHITESPACE@20..21 " " - UNDERSCORE@21..22 "_" - COLON@22..23 ":" - WHITESPACE@23..24 " " - TUPLE_TYPE@24..26 - L_PAREN@24..25 "(" - R_PAREN@25..26 ")" - WHITESPACE@26..27 " " - EQ@27..28 "=" - WHITESPACE@28..29 " " - LET_EXPR@29..41 - LET_KW@29..32 "let" - WHITESPACE@32..33 " " - WILDCARD_PAT@33..34 - UNDERSCORE@33..34 "_" - WHITESPACE@34..35 " " - EQ@35..36 "=" - WHITESPACE@36..37 " " - PATH_EXPR@37..41 - PATH@37..41 - PATH_SEGMENT@37..41 - NAME_REF@37..41 - IDENT@37..41 "None" - SEMICOLON@41..42 ";" - WHITESPACE@42..48 "\n\n " - LET_STMT@48..83 - LET_KW@48..51 "let" - WHITESPACE@51..52 " " - WILDCARD_PAT@52..53 - UNDERSCORE@52..53 "_" - WHITESPACE@53..54 " " - EQ@54..55 "=" - WHITESPACE@55..56 " " - IF_EXPR@56..82 - IF_KW@56..58 "if" - WHITESPACE@58..59 " " - LITERAL@59..63 - TRUE_KW@59..63 "true" - WHITESPACE@63..64 " " - BLOCK_EXPR@64..82 - STMT_LIST@64..82 - L_CURLY@64..65 "{" - WHITESPACE@65..66 " " - PAREN_EXPR@66..80 - L_PAREN@66..67 "(" - LET_EXPR@67..79 - LET_KW@67..70 "let" - WHITESPACE@70..71 " " - WILDCARD_PAT@71..72 - UNDERSCORE@71..72 "_" - WHITESPACE@72..73 " " - EQ@73..74 "=" - WHITESPACE@74..75 " " - PATH_EXPR@75..79 - PATH@75..79 - PATH_SEGMENT@75..79 - NAME_REF@75..79 - IDENT@75..79 "None" - R_PAREN@79..80 ")" - WHITESPACE@80..81 " " - R_CURLY@81..82 "}" - SEMICOLON@82..83 ";" - WHITESPACE@83..89 "\n\n " - IF_EXPR@89..279 - IF_KW@89..91 "if" - WHITESPACE@91..92 " " - BIN_EXPR@92..114 - LITERAL@92..96 - TRUE_KW@92..96 "true" - WHITESPACE@96..97 " " - AMP2@97..99 "&&" - WHITESPACE@99..100 " " - PAREN_EXPR@100..114 - L_PAREN@100..101 "(" - LET_EXPR@101..113 - LET_KW@101..104 "let" - WHITESPACE@104..105 " " - WILDCARD_PAT@105..106 - UNDERSCORE@105..106 "_" - WHITESPACE@106..107 " " - EQ@107..108 "=" - WHITESPACE@108..109 " " - PATH_EXPR@109..113 - PATH@109..113 - PATH_SEGMENT@109..113 - NAME_REF@109..113 - IDENT@109..113 "None" - R_PAREN@113..114 ")" - WHITESPACE@114..115 " " - BLOCK_EXPR@115..279 - STMT_LIST@115..279 - L_CURLY@115..116 "{" - WHITESPACE@116..125 "\n " - EXPR_STMT@125..140 - PAREN_EXPR@125..139 - L_PAREN@125..126 "(" - LET_EXPR@126..138 - LET_KW@126..129 "let" - WHITESPACE@129..130 " " - WILDCARD_PAT@130..131 - UNDERSCORE@130..131 "_" - WHITESPACE@131..132 " " - EQ@132..133 "=" - WHITESPACE@133..134 " " - PATH_EXPR@134..138 - PATH@134..138 - PATH_SEGMENT@134..138 - NAME_REF@134..138 - IDENT@134..138 "None" - R_PAREN@138..139 ")" - SEMICOLON@139..140 ";" - WHITESPACE@140..149 "\n " - WHILE_EXPR@149..273 - WHILE_KW@149..154 "while" - WHITESPACE@154..155 " " - LET_EXPR@155..167 - LET_KW@155..158 "let" - WHITESPACE@158..159 " " - WILDCARD_PAT@159..160 - UNDERSCORE@159..160 "_" - WHITESPACE@160..161 " " - EQ@161..162 "=" - WHITESPACE@162..163 " " - PATH_EXPR@163..167 - PATH@163..167 - PATH_SEGMENT@163..167 - NAME_REF@163..167 - IDENT@163..167 "None" - WHITESPACE@167..168 " " - BLOCK_EXPR@168..273 - STMT_LIST@168..273 - L_CURLY@168..169 "{" - WHITESPACE@169..182 "\n " - MATCH_EXPR@182..263 - MATCH_KW@182..187 "match" - WHITESPACE@187..188 " " - PATH_EXPR@188..192 - PATH@188..192 - PATH_SEGMENT@188..192 - NAME_REF@188..192 - IDENT@188..192 "None" - WHITESPACE@192..193 " " - MATCH_ARM_LIST@193..263 - L_CURLY@193..194 "{" - WHITESPACE@194..211 "\n " - MATCH_ARM@211..249 - WILDCARD_PAT@211..212 - UNDERSCORE@211..212 "_" - WHITESPACE@212..213 " " - MATCH_GUARD@213..228 - IF_KW@213..215 "if" - WHITESPACE@215..216 " " - LET_EXPR@216..228 - LET_KW@216..219 "let" - WHITESPACE@219..220 " " - WILDCARD_PAT@220..221 - UNDERSCORE@220..221 "_" - WHITESPACE@221..222 " " - EQ@222..223 "=" - WHITESPACE@223..224 " " - PATH_EXPR@224..228 - PATH@224..228 - PATH_SEGMENT@224..228 - NAME_REF@224..228 - IDENT@224..228 "None" - WHITESPACE@228..229 " " - FAT_ARROW@229..231 "=>" - WHITESPACE@231..232 " " - BLOCK_EXPR@232..249 - STMT_LIST@232..249 - L_CURLY@232..233 "{" - WHITESPACE@233..234 " " - LET_STMT@234..247 - LET_KW@234..237 "let" - WHITESPACE@237..238 " " - WILDCARD_PAT@238..239 - UNDERSCORE@238..239 "_" - WHITESPACE@239..240 " " - EQ@240..241 "=" - WHITESPACE@241..242 " " - PATH_EXPR@242..246 - PATH@242..246 - PATH_SEGMENT@242..246 - NAME_REF@242..246 - IDENT@242..246 "None" - SEMICOLON@246..247 ";" - WHITESPACE@247..248 " " - R_CURLY@248..249 "}" - WHITESPACE@249..262 "\n " - R_CURLY@262..263 "}" - WHITESPACE@263..272 "\n " - R_CURLY@272..273 "}" - WHITESPACE@273..278 "\n " - R_CURLY@278..279 "}" - WHITESPACE@279..280 "\n" - R_CURLY@280..281 "}" - WHITESPACE@281..282 "\n" -error 29..41: `let` expressions are not supported here -error 67..79: `let` expressions are not supported here -error 126..138: `let` expressions are not supported here +SOURCE_FILE@0..282 + FN@0..281 + FN_KW@0..2 "fn" + WHITESPACE@2..3 " " + NAME@3..6 + IDENT@3..6 "foo" + PARAM_LIST@6..8 + L_PAREN@6..7 "(" + R_PAREN@7..8 ")" + WHITESPACE@8..9 " " + BLOCK_EXPR@9..281 + STMT_LIST@9..281 + L_CURLY@9..10 "{" + WHITESPACE@10..15 "\n " + CONST@15..42 + CONST_KW@15..20 "const" + WHITESPACE@20..21 " " + UNDERSCORE@21..22 "_" + COLON@22..23 ":" + WHITESPACE@23..24 " " + TUPLE_TYPE@24..26 + L_PAREN@24..25 "(" + R_PAREN@25..26 ")" + WHITESPACE@26..27 " " + EQ@27..28 "=" + WHITESPACE@28..29 " " + LET_EXPR@29..41 + LET_KW@29..32 "let" + WHITESPACE@32..33 " " + WILDCARD_PAT@33..34 + UNDERSCORE@33..34 "_" + WHITESPACE@34..35 " " + EQ@35..36 "=" + WHITESPACE@36..37 " " + PATH_EXPR@37..41 + PATH@37..41 + PATH_SEGMENT@37..41 + NAME_REF@37..41 + IDENT@37..41 "None" + SEMICOLON@41..42 ";" + WHITESPACE@42..48 "\n\n " + LET_STMT@48..83 + LET_KW@48..51 "let" + WHITESPACE@51..52 " " + WILDCARD_PAT@52..53 + UNDERSCORE@52..53 "_" + WHITESPACE@53..54 " " + EQ@54..55 "=" + WHITESPACE@55..56 " " + IF_EXPR@56..82 + IF_KW@56..58 "if" + WHITESPACE@58..59 " " + LITERAL@59..63 + TRUE_KW@59..63 "true" + WHITESPACE@63..64 " " + BLOCK_EXPR@64..82 + STMT_LIST@64..82 + L_CURLY@64..65 "{" + WHITESPACE@65..66 " " + PAREN_EXPR@66..80 + L_PAREN@66..67 "(" + LET_EXPR@67..79 + LET_KW@67..70 "let" + WHITESPACE@70..71 " " + WILDCARD_PAT@71..72 + UNDERSCORE@71..72 "_" + WHITESPACE@72..73 " " + EQ@73..74 "=" + WHITESPACE@74..75 " " + PATH_EXPR@75..79 + PATH@75..79 + PATH_SEGMENT@75..79 + NAME_REF@75..79 + IDENT@75..79 "None" + R_PAREN@79..80 ")" + WHITESPACE@80..81 " " + R_CURLY@81..82 "}" + SEMICOLON@82..83 ";" + WHITESPACE@83..89 "\n\n " + IF_EXPR@89..279 + IF_KW@89..91 "if" + WHITESPACE@91..92 " " + BIN_EXPR@92..114 + LITERAL@92..96 + TRUE_KW@92..96 "true" + WHITESPACE@96..97 " " + AMP2@97..99 "&&" + WHITESPACE@99..100 " " + PAREN_EXPR@100..114 + L_PAREN@100..101 "(" + LET_EXPR@101..113 + LET_KW@101..104 "let" + WHITESPACE@104..105 " " + WILDCARD_PAT@105..106 + UNDERSCORE@105..106 "_" + WHITESPACE@106..107 " " + EQ@107..108 "=" + WHITESPACE@108..109 " " + PATH_EXPR@109..113 + PATH@109..113 + PATH_SEGMENT@109..113 + NAME_REF@109..113 + IDENT@109..113 "None" + R_PAREN@113..114 ")" + WHITESPACE@114..115 " " + BLOCK_EXPR@115..279 + STMT_LIST@115..279 + L_CURLY@115..116 "{" + WHITESPACE@116..125 "\n " + EXPR_STMT@125..140 + PAREN_EXPR@125..139 + L_PAREN@125..126 "(" + LET_EXPR@126..138 + LET_KW@126..129 "let" + WHITESPACE@129..130 " " + WILDCARD_PAT@130..131 + UNDERSCORE@130..131 "_" + WHITESPACE@131..132 " " + EQ@132..133 "=" + WHITESPACE@133..134 " " + PATH_EXPR@134..138 + PATH@134..138 + PATH_SEGMENT@134..138 + NAME_REF@134..138 + IDENT@134..138 "None" + R_PAREN@138..139 ")" + SEMICOLON@139..140 ";" + WHITESPACE@140..149 "\n " + WHILE_EXPR@149..273 + WHILE_KW@149..154 "while" + WHITESPACE@154..155 " " + LET_EXPR@155..167 + LET_KW@155..158 "let" + WHITESPACE@158..159 " " + WILDCARD_PAT@159..160 + UNDERSCORE@159..160 "_" + WHITESPACE@160..161 " " + EQ@161..162 "=" + WHITESPACE@162..163 " " + PATH_EXPR@163..167 + PATH@163..167 + PATH_SEGMENT@163..167 + NAME_REF@163..167 + IDENT@163..167 "None" + WHITESPACE@167..168 " " + BLOCK_EXPR@168..273 + STMT_LIST@168..273 + L_CURLY@168..169 "{" + WHITESPACE@169..182 "\n " + MATCH_EXPR@182..263 + MATCH_KW@182..187 "match" + WHITESPACE@187..188 " " + PATH_EXPR@188..192 + PATH@188..192 + PATH_SEGMENT@188..192 + NAME_REF@188..192 + IDENT@188..192 "None" + WHITESPACE@192..193 " " + MATCH_ARM_LIST@193..263 + L_CURLY@193..194 "{" + WHITESPACE@194..211 "\n " + MATCH_ARM@211..249 + WILDCARD_PAT@211..212 + UNDERSCORE@211..212 "_" + WHITESPACE@212..213 " " + MATCH_GUARD@213..228 + IF_KW@213..215 "if" + WHITESPACE@215..216 " " + LET_EXPR@216..228 + LET_KW@216..219 "let" + WHITESPACE@219..220 " " + WILDCARD_PAT@220..221 + UNDERSCORE@220..221 "_" + WHITESPACE@221..222 " " + EQ@222..223 "=" + WHITESPACE@223..224 " " + PATH_EXPR@224..228 + PATH@224..228 + PATH_SEGMENT@224..228 + NAME_REF@224..228 + IDENT@224..228 "None" + WHITESPACE@228..229 " " + FAT_ARROW@229..231 "=>" + WHITESPACE@231..232 " " + BLOCK_EXPR@232..249 + STMT_LIST@232..249 + L_CURLY@232..233 "{" + WHITESPACE@233..234 " " + LET_STMT@234..247 + LET_KW@234..237 "let" + WHITESPACE@237..238 " " + WILDCARD_PAT@238..239 + UNDERSCORE@238..239 "_" + WHITESPACE@239..240 " " + EQ@240..241 "=" + WHITESPACE@241..242 " " + PATH_EXPR@242..246 + PATH@242..246 + PATH_SEGMENT@242..246 + NAME_REF@242..246 + IDENT@242..246 "None" + SEMICOLON@246..247 ";" + WHITESPACE@247..248 " " + R_CURLY@248..249 "}" + WHITESPACE@249..262 "\n " + R_CURLY@262..263 "}" + WHITESPACE@263..272 "\n " + R_CURLY@272..273 "}" + WHITESPACE@273..278 "\n " + R_CURLY@278..279 "}" + WHITESPACE@279..280 "\n" + R_CURLY@280..281 "}" + WHITESPACE@281..282 "\n" +error 29..41: `let` expressions are not supported here +error 67..79: `let` expressions are not supported here +error 126..138: `let` expressions are not supported here From 64758bd48195241dbf39a63974ecbcdcd151f399 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 24 Jul 2022 22:32:49 +0900 Subject: [PATCH 10/30] Add info whether it's assignee expr to relevant HIR `Expr` variants --- crates/hir-def/src/body/lower.rs | 39 ++++++++++++++++++++++++++------ crates/hir-def/src/expr.rs | 11 +++++---- crates/hir-ty/src/infer/expr.rs | 16 ++++++------- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index b43699de12030..66f9c24e8724a 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -96,6 +96,7 @@ pub(super) fn lower( expander, name_to_pat_grouping: Default::default(), is_lowering_inside_or_pat: false, + is_lowering_assignee_expr: false, } .collect(params, body) } @@ -109,6 +110,7 @@ struct ExprCollector<'a> { // a poor-mans union-find? name_to_pat_grouping: FxHashMap>, is_lowering_inside_or_pat: bool, + is_lowering_assignee_expr: bool, } impl ExprCollector<'_> { @@ -283,7 +285,10 @@ impl ExprCollector<'_> { } else { Box::default() }; - self.alloc_expr(Expr::Call { callee, args }, syntax_ptr) + self.alloc_expr( + Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr }, + syntax_ptr, + ) } ast::Expr::MethodCallExpr(e) => { let receiver = self.collect_expr_opt(e.receiver()); @@ -359,6 +364,7 @@ impl ExprCollector<'_> { ast::Expr::RecordExpr(e) => { let path = e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); + let is_assignee_expr = self.is_lowering_assignee_expr; let record_lit = if let Some(nfl) = e.record_expr_field_list() { let fields = nfl .fields() @@ -379,9 +385,15 @@ impl ExprCollector<'_> { .collect(); let spread = nfl.spread().map(|s| self.collect_expr(s)); let ellipsis = nfl.dotdot_token().is_some(); - Expr::RecordLit { path, fields, spread, ellipsis } + Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr } } else { - Expr::RecordLit { path, fields: Box::default(), spread: None, ellipsis: false } + Expr::RecordLit { + path, + fields: Box::default(), + spread: None, + ellipsis: false, + is_assignee_expr, + } }; self.alloc_expr(record_lit, syntax_ptr) @@ -459,14 +471,21 @@ impl ExprCollector<'_> { ) } ast::Expr::BinExpr(e) => { + let op = e.op_kind(); + if let Some(ast::BinaryOp::Assignment { op: None }) = op { + self.is_lowering_assignee_expr = true; + } let lhs = self.collect_expr_opt(e.lhs()); + self.is_lowering_assignee_expr = false; let rhs = self.collect_expr_opt(e.rhs()); - let op = e.op_kind(); self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr) } ast::Expr::TupleExpr(e) => { let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect(); - self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) + self.alloc_expr( + Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr }, + syntax_ptr, + ) } ast::Expr::BoxExpr(e) => { let expr = self.collect_expr_opt(e.expr()); @@ -478,8 +497,14 @@ impl ExprCollector<'_> { match kind { ArrayExprKind::ElementList(e) => { - let exprs = e.map(|expr| self.collect_expr(expr)).collect(); - self.alloc_expr(Expr::Array(Array::ElementList(exprs)), syntax_ptr) + let elements = e.map(|expr| self.collect_expr(expr)).collect(); + self.alloc_expr( + Expr::Array(Array::ElementList { + elements, + is_assignee_expr: self.is_lowering_assignee_expr, + }), + syntax_ptr, + ) } ArrayExprKind::Repeat { initializer, repeat } => { let initializer = self.collect_expr_opt(initializer); diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index 145fe4c19db40..c1b3788acb7d3 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -110,6 +110,7 @@ pub enum Expr { Call { callee: ExprId, args: Box<[ExprId]>, + is_assignee_expr: bool, }, MethodCall { receiver: ExprId, @@ -139,6 +140,7 @@ pub enum Expr { fields: Box<[RecordLitField]>, spread: Option, ellipsis: bool, + is_assignee_expr: bool, }, Field { expr: ExprId, @@ -197,6 +199,7 @@ pub enum Expr { }, Tuple { exprs: Box<[ExprId]>, + is_assignee_expr: bool, }, Unsafe { body: ExprId, @@ -212,7 +215,7 @@ pub enum Expr { #[derive(Debug, Clone, Eq, PartialEq)] pub enum Array { - ElementList(Box<[ExprId]>), + ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool }, Repeat { initializer: ExprId, repeat: ExprId }, } @@ -286,7 +289,7 @@ impl Expr { f(*iterable); f(*body); } - Expr::Call { callee, args } => { + Expr::Call { callee, args, .. } => { f(*callee); args.iter().copied().for_each(f); } @@ -340,9 +343,9 @@ impl Expr { | Expr::Box { expr } => { f(*expr); } - Expr::Tuple { exprs } => exprs.iter().copied().for_each(f), + Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f), Expr::Array(a) => match a { - Array::ElementList(exprs) => exprs.iter().copied().for_each(f), + Array::ElementList { elements, .. } => elements.iter().copied().for_each(f), Array::Repeat { initializer, repeat } => { f(*initializer); f(*repeat) diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 9debe835473a8..d164e64a8be07 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -276,7 +276,7 @@ impl<'a> InferenceContext<'a> { closure_ty } - Expr::Call { callee, args } => { + Expr::Call { callee, args, .. } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone()); let mut res = None; @@ -693,7 +693,7 @@ impl<'a> InferenceContext<'a> { self.err_ty() } } - Expr::Tuple { exprs } => { + Expr::Tuple { exprs, .. } => { let mut tys = match expected .only_has_type(&mut self.table) .as_ref() @@ -724,12 +724,12 @@ impl<'a> InferenceContext<'a> { let expected = Expectation::has_type(elem_ty.clone()); let len = match array { - Array::ElementList(items) => { - for &expr in items.iter() { + Array::ElementList { elements, .. } => { + for &expr in elements.iter() { let cur_elem_ty = self.infer_expr_inner(expr, &expected); coerce.coerce(self, Some(expr), &cur_elem_ty); } - consteval::usize_const(Some(items.len() as u128)) + consteval::usize_const(Some(elements.len() as u128)) } &Array::Repeat { initializer, repeat } => { self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty)); @@ -850,7 +850,7 @@ impl<'a> InferenceContext<'a> { let rhs_ty = self.resolve_ty_shallow(rhs_ty); let ty = match &self.body[lhs] { - Expr::Tuple { exprs } => { + Expr::Tuple { exprs, .. } => { // We don't consider multiple ellipses. This is analogous to // `hir_def::body::lower::ExprCollector::collect_tuple_pat()`. let ellipsis = exprs.iter().position(|e| is_rest_expr(*e)); @@ -858,7 +858,7 @@ impl<'a> InferenceContext<'a> { self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs) } - Expr::Call { callee, args } => { + Expr::Call { callee, args, .. } => { // Tuple structs let path = match &self.body[*callee] { Expr::Path(path) => Some(path), @@ -872,7 +872,7 @@ impl<'a> InferenceContext<'a> { self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args) } - Expr::Array(Array::ElementList(elements)) => { + Expr::Array(Array::ElementList { elements, .. }) => { let elem_ty = match rhs_ty.kind(Interner) { TyKind::Array(st, _) => st.clone(), _ => self.err_ty(), From 805ac666ca8cb2b643026ff454951e9ce3834d94 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sun, 24 Jul 2022 22:34:53 +0900 Subject: [PATCH 11/30] fix: consider assignee expressions in record fields exhaustiveness check --- crates/hir-ty/src/diagnostics/expr.rs | 5 ++- .../src/handlers/missing_fields.rs | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 1fcb4c2976ebc..642e03edd2306 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -305,7 +305,10 @@ pub fn record_literal_missing_fields( expr: &Expr, ) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> { let (fields, exhaustive) = match expr { - Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()), + Expr::RecordLit { fields, spread, ellipsis, is_assignee_expr, .. } => { + let exhaustive = if *is_assignee_expr { !*ellipsis } else { spread.is_none() }; + (fields, exhaustive) + } _ => return None, }; diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index 30f903af50d5b..edb1fc0919c24 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -292,6 +292,37 @@ fn x(a: S) { ) } + #[test] + fn missing_record_expr_in_assignee_expr() { + check_diagnostics( + r" +struct S { s: usize, t: usize } +struct S2 { s: S, t: () } +struct T(S); +fn regular(a: S) { + let s; + S { s, .. } = a; +} +fn nested(a: S2) { + let s; + S2 { s: S { s, .. }, .. } = a; +} +fn in_tuple(a: (S,)) { + let s; + (S { s, .. },) = a; +} +fn in_array(a: [S;1]) { + let s; + [S { s, .. },] = a; +} +fn in_tuple_struct(a: T) { + let s; + T(S { s, .. }) = a; +} + ", + ); + } + #[test] fn range_mapping_out_of_macros() { check_fix( From ff317858c10350e5148a3ecadb383e91e6854d5f Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 24 Jul 2022 15:55:20 +0200 Subject: [PATCH 12/30] hir-def tests: sort results before comparing, since FxHashSet iteration order isn't guaranteed (And, in fact, it failed on i686) --- crates/hir-def/src/import_map.rs | 53 +++++++++++++++++--------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 000fe5ac7bb4b..252d669324382 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -516,6 +516,9 @@ mod tests { mark )) }) + // HashSet iteration order isn't defined - it's different on + // x86_64 and i686 at the very least + .sorted() .collect::(); expect.assert_eq(&actual) } @@ -831,10 +834,10 @@ mod tests { Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), expect![[r#" dep::fmt (t) - dep::fmt::Display::format_method (a) dep::fmt::Display (t) dep::fmt::Display::FMT_CONST (a) dep::fmt::Display::format_function (a) + dep::fmt::Display::format_method (a) "#]], ); } @@ -860,10 +863,10 @@ mod tests { "main", Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(), expect![[r#" - dep::fmt::Display::format_method (a) - dep::fmt::Display::FMT_CONST (a) - dep::fmt::Display::format_function (a) - "#]], + dep::fmt::Display::FMT_CONST (a) + dep::fmt::Display::format_function (a) + dep::fmt::Display::format_method (a) + "#]], ); check_search( @@ -920,13 +923,13 @@ mod tests { "main", Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), expect![[r#" - dep::fmt (t) - dep::format (f) - dep::Fmt (v) dep::Fmt (m) dep::Fmt (t) - dep::fmt::Display::fmt (a) + dep::Fmt (v) + dep::fmt (t) dep::fmt::Display (t) + dep::fmt::Display::fmt (a) + dep::format (f) "#]], ); @@ -935,10 +938,10 @@ mod tests { "main", Query::new("fmt".to_string()).search_mode(SearchMode::Equals), expect![[r#" - dep::fmt (t) - dep::Fmt (v) dep::Fmt (m) dep::Fmt (t) + dep::Fmt (v) + dep::fmt (t) dep::fmt::Display::fmt (a) "#]], ); @@ -948,12 +951,12 @@ mod tests { "main", Query::new("fmt".to_string()).search_mode(SearchMode::Contains), expect![[r#" - dep::fmt (t) - dep::Fmt (v) dep::Fmt (m) dep::Fmt (t) - dep::fmt::Display::fmt (a) + dep::Fmt (v) + dep::fmt (t) dep::fmt::Display (t) + dep::fmt::Display::fmt (a) "#]], ); } @@ -989,12 +992,12 @@ mod tests { "main", Query::new("fmt".to_string()), expect![[r#" - dep::fmt (t) - dep::Fmt (v) dep::Fmt (m) dep::Fmt (t) - dep::fmt::Display::fmt (a) + dep::Fmt (v) + dep::fmt (t) dep::fmt::Display (t) + dep::fmt::Display::fmt (a) "#]], ); @@ -1003,10 +1006,10 @@ mod tests { "main", Query::new("fmt".to_string()).name_only(), expect![[r#" - dep::fmt (t) - dep::Fmt (v) dep::Fmt (m) dep::Fmt (t) + dep::Fmt (v) + dep::fmt (t) dep::fmt::Display::fmt (a) "#]], ); @@ -1027,10 +1030,10 @@ mod tests { "main", Query::new("FMT".to_string()), expect![[r#" - dep::fmt (t) + dep::FMT (t) dep::FMT (v) + dep::fmt (t) dep::fmt (v) - dep::FMT (t) "#]], ); @@ -1068,10 +1071,10 @@ mod tests { "main", Query::new("".to_string()).limit(2), expect![[r#" - dep::fmt (t) + dep::Fmt (m) dep::Fmt (t) dep::Fmt (v) - dep::Fmt (m) + dep::fmt (t) "#]], ); } @@ -1091,10 +1094,10 @@ mod tests { "main", Query::new("FMT".to_string()), expect![[r#" - dep::fmt (t) + dep::FMT (t) dep::FMT (v) + dep::fmt (t) dep::fmt (v) - dep::FMT (t) "#]], ); From d8c0d88e4f0e491a1f04c0130eecf4426e013ea3 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 24 Jul 2022 16:04:20 +0200 Subject: [PATCH 13/30] Sort in DefMap::dump, since HashMap iteration order isn't defined --- crates/hir-def/src/nameres.rs | 15 +++++++-------- crates/hir-def/src/nameres/tests.rs | 20 ++++++++++---------- crates/hir-def/src/nameres/tests/globs.rs | 18 +++++++++--------- crates/hir-def/src/nameres/tests/macros.rs | 14 +++++++------- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index c67046dfdab55..756fd583af4d6 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -48,8 +48,8 @@ //! the result pub mod attr_resolution; -pub mod diagnostics; mod collector; +pub mod diagnostics; mod mod_resolution; mod path_resolution; mod proc_macro; @@ -57,10 +57,11 @@ mod proc_macro; #[cfg(test)] mod tests; -use std::sync::Arc; +use std::{cmp::Ord, sync::Arc}; use base_db::{CrateId, Edition, FileId}; use hir_expand::{name::Name, InFile, MacroDefId}; +use itertools::Itertools; use la_arena::Arena; use profile::Count; use rustc_hash::FxHashMap; @@ -333,11 +334,7 @@ impl DefMap { pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId { self.with_ancestor_maps(db, self.root, &mut |def_map, _module| { - if def_map.block.is_none() { - Some(def_map.module_id(def_map.root)) - } else { - None - } + if def_map.block.is_none() { Some(def_map.module_id(def_map.root)) } else { None } }) .expect("DefMap chain without root") } @@ -431,7 +428,9 @@ impl DefMap { map.modules[module].scope.dump(buf); - for (name, child) in map.modules[module].children.iter() { + for (name, child) in + map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0)) + { let path = format!("{}::{}", path, name); buf.push('\n'); go(buf, map, &path, *child); diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs index 80e5476071487..70dd2eb3ade69 100644 --- a/crates/hir-def/src/nameres/tests.rs +++ b/crates/hir-def/src/nameres/tests.rs @@ -648,11 +648,11 @@ mod b { a: t b: t - crate::b - T: v - crate::a T: t v + + crate::b + T: v "#]], ); } @@ -704,13 +704,13 @@ use crate::reex::*; reex: t tr: t - crate::tr - PrivTr: t - PubTr: t - crate::reex _: t _: t + + crate::tr + PrivTr: t + PubTr: t "#]], ); } @@ -920,14 +920,14 @@ use some_module::unknown_func; some_module: t unknown_func: v - crate::some_module - unknown_func: v - crate::other_module some_submodule: t crate::other_module::some_submodule unknown_func: v + + crate::some_module + unknown_func: v "#]], ) } diff --git a/crates/hir-def/src/nameres/tests/globs.rs b/crates/hir-def/src/nameres/tests/globs.rs index 17426d54d4057..b2a6a592cf38b 100644 --- a/crates/hir-def/src/nameres/tests/globs.rs +++ b/crates/hir-def/src/nameres/tests/globs.rs @@ -315,8 +315,13 @@ mod d { c: t d: t - crate::d - Y: t v + crate::a + foo: t + + crate::a::foo + X: t v + + crate::b foo: t crate::c @@ -325,14 +330,9 @@ mod d { crate::c::foo Y: t v - crate::b - foo: t - - crate::a + crate::d + Y: t v foo: t - - crate::a::foo - X: t v "#]], ); } diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index 520a6ae1cba5a..7b7f94c5333eb 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -439,15 +439,8 @@ macro_rules! baz { m7: t ok_double_macro_use_shadow: v - crate::m7 - crate::m1 - crate::m5 - m6: t - - crate::m5::m6 - crate::m2 crate::m3 @@ -462,6 +455,13 @@ macro_rules! baz { ok_shadow_deep: v crate::m3::m5 + + crate::m5 + m6: t + + crate::m5::m6 + + crate::m7 "#]], ); // FIXME: should not see `NotFoundBefore` From 77acb5c162957d9748d1f35261982591d9a85e22 Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Sun, 24 Jul 2022 00:29:07 -0500 Subject: [PATCH 14/30] fix: Autocomplete for struct fields includes receiver fixes #12857 --- crates/ide-completion/src/render.rs | 40 ++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 3f25b294e0180..9b25964a6086e 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -121,7 +121,7 @@ pub(crate) fn render_field( let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name).into()), + field_with_receiver(receiver.as_ref(), &name), ); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), @@ -132,7 +132,7 @@ pub(crate) fn render_field( .set_documentation(field.docs(ctx.db())) .set_deprecated(is_deprecated) .lookup_by(name.clone()); - item.insert_text(escaped_name); + item.insert_text(field_with_receiver(receiver.as_ref(), &escaped_name)); if let Some(receiver) = &dot_access.receiver { if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) { if let Some(ref_match) = compute_ref_match(ctx.completion, ty) { @@ -143,6 +143,11 @@ pub(crate) fn render_field( item.build() } +fn field_with_receiver(receiver: Option<&hir::Name>, field_name: &str) -> SmolStr { + receiver + .map_or_else(|| field_name.into(), |receiver| format!("{}.{}", receiver, field_name).into()) +} + pub(crate) fn render_tuple_field( ctx: RenderContext<'_>, receiver: Option, @@ -152,7 +157,7 @@ pub(crate) fn render_tuple_field( let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), + field_with_receiver(receiver.as_ref(), &field.to_string()), ); item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string()); item.build() @@ -1873,6 +1878,35 @@ impl r#trait for r#struct { type t$0 } struct r#struct {} trait r#trait { type r#type; } impl r#trait for r#struct { type r#type = $0; } +"#, + ) + } + + #[test] + fn field_access_includes_self() { + check_edit( + "length", + r#" +struct S { + length: i32 +} + +impl S { + fn some_fn(&self) { + let l = len$0 + } +} +"#, + r#" +struct S { + length: i32 +} + +impl S { + fn some_fn(&self) { + let l = self.length + } +} "#, ) } From 56c369db488361822ec4b2dbf954f6c48d384855 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 24 Jul 2022 16:11:05 +0200 Subject: [PATCH 15/30] Sort when iterating through CrateGraph --- crates/hir-def/src/import_map.rs | 33 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 252d669324382..688055e430bdf 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -167,11 +167,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| { let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public); - if per_ns.is_none() { - None - } else { - Some((name, per_ns)) - } + if per_ns.is_none() { None } else { Some((name, per_ns)) } }); for (name, per_ns) in visible_items { @@ -591,6 +587,7 @@ mod tests { Some(format!("{}:\n{:?}\n", name, map)) }) + .sorted() .collect::(); expect.assert_eq(&actual) @@ -624,15 +621,15 @@ mod tests { struct Priv; ", expect![[r#" + lib: + - Pub (t) + - Pub2 (t) + - Pub2 (v) main: - publ1 (t) - real_pu2 (t) - real_pub (t) - real_pub::Pub (t) - lib: - - Pub (t) - - Pub2 (t) - - Pub2 (v) "#]], ); } @@ -674,13 +671,13 @@ mod tests { pub struct S; ", expect![[r#" + lib: + - S (t) + - S (v) main: - m (t) - m::S (t) - m::S (v) - lib: - - S (t) - - S (v) "#]], ); } @@ -700,11 +697,11 @@ mod tests { } ", expect![[r#" + lib: + - pub_macro (m) main: - m (t) - m::pub_macro (m) - lib: - - pub_macro (m) "#]], ); } @@ -722,14 +719,14 @@ mod tests { } ", expect![[r#" - main: - - reexported_module (t) - - reexported_module::S (t) - - reexported_module::S (v) lib: - module (t) - module::S (t) - module::S (v) + main: + - reexported_module (t) + - reexported_module::S (t) + - reexported_module::S (v) "#]], ); } From dfe84494c142356a53f12279698f7bfc3b056481 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 24 Jul 2022 16:48:06 +0200 Subject: [PATCH 16/30] Make macros test order-resistant --- crates/hir-def/src/nameres/tests/macros.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index 7b7f94c5333eb..3ece1379ad774 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -1,4 +1,5 @@ use super::*; +use itertools::Itertools; #[test] fn macro_rules_are_globally_visible() { @@ -1171,11 +1172,15 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } ); let root = &def_map[def_map.root()].scope; - let actual = root.legacy_macros().map(|(name, _)| format!("{name}\n")).collect::(); + let actual = root + .legacy_macros() + .sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0)) + .map(|(name, _)| format!("{name}\n")) + .collect::(); expect![[r#" - macro20 legacy + macro20 proc_attr "#]] .assert_eq(&actual); From 71225c35bf86e639e3a3cfaf229a9c29f6815751 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Mon, 25 Jul 2022 18:40:35 +0900 Subject: [PATCH 17/30] Replace `debug_assert!` with `stdx::always!` --- crates/ide-assists/src/utils/gen_trait_fn_body.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index 43aaad3150ced..7a0c912959a12 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -28,7 +28,7 @@ pub(crate) fn gen_trait_fn_body( /// Generate a `Clone` impl based on the fields and members of the target type. fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - debug_assert!(func.name().map_or(false, |name| name.text() == "clone")); + stdx::always!(func.name().map_or(false, |name| name.text() == "clone")); fn gen_clone_call(target: ast::Expr) -> ast::Expr { let method = make::name_ref("clone"); make::expr_method_call(target, method, make::arg_list(None)) @@ -340,7 +340,7 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// Generate a `Hash` impl based on the fields and members of the target type. fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - debug_assert!(func.name().map_or(false, |name| name.text() == "hash")); + stdx::always!(func.name().map_or(false, |name| name.text() == "hash")); fn gen_hash_call(target: ast::Expr) -> ast::Stmt { let method = make::name_ref("hash"); let arg = make::expr_path(make::ext::ident_path("state")); @@ -396,7 +396,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// Generate a `PartialEq` impl based on the fields and members of the target type. fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - debug_assert!(func.name().map_or(false, |name| name.text() == "eq")); + stdx::always!(func.name().map_or(false, |name| name.text() == "eq")); fn gen_eq_chain(expr: Option, cmp: ast::Expr) -> Option { match expr { Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)), @@ -573,7 +573,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - debug_assert!(func.name().map_or(false, |name| name.text() == "partial_cmp")); + stdx::always!(func.name().map_or(false, |name| name.text() == "partial_cmp")); fn gen_partial_eq_match(match_target: ast::Expr) -> Option { let mut arms = vec![]; From dc9405081531d0f3e68d984290b4d752bee3b9bd Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Mon, 25 Jul 2022 14:18:28 +0200 Subject: [PATCH 18/30] revert nightly rustfmt formatting that accidentally slipped in cf. https://github.com/rust-lang/rust/pull/99603 cf. https://github.com/rust-lang/rust-analyzer/pull/12871#discussion_r928816339 --- crates/hir-def/src/import_map.rs | 6 +++++- crates/hir-def/src/nameres.rs | 6 +++++- crates/proc-macro-test/build.rs | 3 +-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 688055e430bdf..05e0ceb05a9a3 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -167,7 +167,11 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| { let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public); - if per_ns.is_none() { None } else { Some((name, per_ns)) } + if per_ns.is_none() { + None + } else { + Some((name, per_ns)) + } }); for (name, per_ns) in visible_items { diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 756fd583af4d6..3949fbb6e7b03 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -334,7 +334,11 @@ impl DefMap { pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId { self.with_ancestor_maps(db, self.root, &mut |def_map, _module| { - if def_map.block.is_none() { Some(def_map.module_id(def_map.root)) } else { None } + if def_map.block.is_none() { + Some(def_map.module_id(def_map.root)) + } else { + None + } }) .expect("DefMap chain without root") } diff --git a/crates/proc-macro-test/build.rs b/crates/proc-macro-test/build.rs index c90144509dec5..a80c962617bb3 100644 --- a/crates/proc-macro-test/build.rs +++ b/crates/proc-macro-test/build.rs @@ -62,8 +62,7 @@ fn main() { Command::new(toolchain::cargo()) }; - cmd - .current_dir(&staging_dir) + cmd.current_dir(&staging_dir) .args(&["build", "-p", "proc-macro-test-impl", "--message-format", "json"]) // Explicit override the target directory to avoid using the same one which the parent // cargo is using, or we'll deadlock. From 5f54ec0e859a020b125519d56ad2a08defd3ddf3 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sat, 23 Jul 2022 22:47:34 +0200 Subject: [PATCH 19/30] Add a proc-macro-srv-cli crate --- Cargo.lock | 7 +++++++ crates/proc-macro-srv-cli/Cargo.toml | 13 +++++++++++++ crates/proc-macro-srv-cli/src/main.rs | 5 +++++ 3 files changed, 25 insertions(+) create mode 100644 crates/proc-macro-srv-cli/Cargo.toml create mode 100644 crates/proc-macro-srv-cli/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 4c830006832c9..703f0e5b8af9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1198,6 +1198,13 @@ dependencies = [ "tt", ] +[[package]] +name = "proc-macro-srv-cli" +version = "0.0.0" +dependencies = [ + "proc-macro-srv", +] + [[package]] name = "proc-macro-test" version = "0.0.0" diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml new file mode 100644 index 0000000000000..959002f664a6c --- /dev/null +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "proc-macro-srv-cli" +version = "0.0.0" +description = "TBD" +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.57" + +[dependencies] +proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" } + +[features] +sysroot-abi = ["proc-macro-srv/sysroot-abi"] diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs new file mode 100644 index 0000000000000..ea16cfff4b990 --- /dev/null +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -0,0 +1,5 @@ +use proc_macro_srv::cli; + +fn main() { + cli::run().unwrap(); +} From fba6adfae48d55984934872a81d533dc405b7cd3 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 24 Jul 2022 00:48:20 +0200 Subject: [PATCH 20/30] Add doc comment to pass tidy check --- crates/proc-macro-srv-cli/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs index ea16cfff4b990..0f23a7daafa29 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -1,3 +1,5 @@ +//! A standalone binary for `proc-macro-srv`. + use proc_macro_srv::cli; fn main() { From fd1b64ec718d8483a98872bd91ee8f11b6893e7f Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 24 Jul 2022 14:50:30 +0200 Subject: [PATCH 21/30] Return result directly Co-authored-by: Lukas Wirth --- crates/proc-macro-srv-cli/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs index 0f23a7daafa29..8398f2ba6cb70 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -2,6 +2,6 @@ use proc_macro_srv::cli; -fn main() { - cli::run().unwrap(); +fn main() -> std::io::Result<()> { + cli::run() } From 4364531c2f3db41d9ba3a72f3e77ab26d16db8b0 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 24 Jul 2022 14:53:20 +0200 Subject: [PATCH 22/30] Name the binary 'proc-macro-srv' --- crates/proc-macro-srv-cli/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml index 959002f664a6c..badb594f06295 100644 --- a/crates/proc-macro-srv-cli/Cargo.toml +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -11,3 +11,7 @@ proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" } [features] sysroot-abi = ["proc-macro-srv/sysroot-abi"] + +[[bin]] +name = "proc-macro-srv" +path = "src/main.rs" From dadb83282d7dc7bfb408d93d4953019a328e8e2b Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Mon, 25 Jul 2022 16:07:41 +0200 Subject: [PATCH 23/30] Remember the difference between 'sysroot root' and 'sysroot src root', start looking in there for a rust-analyzer-proc-macro-srv binary --- crates/project-model/src/project_json.rs | 5 ++++ crates/project-model/src/sysroot.rs | 21 +++++++++++++---- crates/project-model/src/tests.rs | 7 ++++-- crates/project-model/src/workspace.rs | 12 +++++++--- crates/rust-analyzer/src/reload.rs | 30 ++++++++++++++++++++++-- 5 files changed, 63 insertions(+), 12 deletions(-) diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index a3c5ac167406d..63d1d0ace96b9 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -17,6 +17,9 @@ use crate::cfg_flag::CfgFlag; /// Roots and crates that compose this Rust project. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ProjectJson { + /// e.g. `path/to/sysroot` + pub(crate) sysroot: Option, + /// e.g. `path/to/sysroot/lib/rustlib/src/rust` pub(crate) sysroot_src: Option, project_root: AbsPathBuf, crates: Vec, @@ -52,6 +55,7 @@ impl ProjectJson { /// configuration. pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { ProjectJson { + sysroot: data.sysroot.map(|it| base.join(it)), sysroot_src: data.sysroot_src.map(|it| base.join(it)), project_root: base.to_path_buf(), crates: data @@ -122,6 +126,7 @@ impl ProjectJson { #[derive(Deserialize, Debug, Clone)] pub struct ProjectJsonData { + sysroot: Option, sysroot_src: Option, crates: Vec, } diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 52750f48969f7..b17a59b10bb57 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -15,6 +15,7 @@ use crate::{utf8_stdout, ManifestPath}; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Sysroot { root: AbsPathBuf, + src_root: AbsPathBuf, crates: Arena, } @@ -35,6 +36,15 @@ impl ops::Index for Sysroot { } impl Sysroot { + /// Returns sysroot directory, where `bin/`, `etc/`, `lib/`, `libexec/` + /// subfolder live, like: + /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library` + pub fn src_root(&self) -> &AbsPath { + &self.src_root + } + + /// Returns sysroot "src" directory, where stdlib sources are located, like: + /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu` pub fn root(&self) -> &AbsPath { &self.root } @@ -61,7 +71,7 @@ impl Sysroot { tracing::debug!("Discovering sysroot for {}", dir.display()); let sysroot_dir = discover_sysroot_dir(dir)?; let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir)?; - let res = Sysroot::load(sysroot_src_dir)?; + let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?; Ok(res) } @@ -71,14 +81,15 @@ impl Sysroot { discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir)) } - pub fn load(sysroot_src_dir: AbsPathBuf) -> Result { - let mut sysroot = Sysroot { root: sysroot_src_dir, crates: Arena::default() }; + pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result { + let mut sysroot = + Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() }; for path in SYSROOT_CRATES.trim().lines() { let name = path.split('/').last().unwrap(); let root = [format!("{}/src/lib.rs", path), format!("lib{}/lib.rs", path)] .into_iter() - .map(|it| sysroot.root.join(it)) + .map(|it| sysroot.src_root.join(it)) .filter_map(|it| ManifestPath::try_from(it).ok()) .find(|it| fs::metadata(it).is_ok()); @@ -119,7 +130,7 @@ impl Sysroot { }; anyhow::bail!( "could not find libcore in sysroot path `{}`{}", - sysroot.root.as_path().display(), + sysroot.src_root.as_path().display(), var_note, ); } diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index ddfea0ce4c4f1..e304a59c0180b 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -75,8 +75,11 @@ fn get_test_path(file: &str) -> PathBuf { fn get_fake_sysroot() -> Sysroot { let sysroot_path = get_test_path("fake-sysroot"); - let sysroot_src_dir = AbsPathBuf::assert(sysroot_path); - Sysroot::load(sysroot_src_dir).unwrap() + // there's no `libexec/` directory with a `proc-macro-srv` binary in that + // fake sysroot, so we give them both the same path: + let sysroot_dir = AbsPathBuf::assert(sysroot_path); + let sysroot_src_dir = sysroot_dir.clone(); + Sysroot::load(sysroot_dir, sysroot_src_dir).unwrap() } fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index de42458354566..63882466fa416 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -230,8 +230,14 @@ impl ProjectWorkspace { project_json: ProjectJson, target: Option<&str>, ) -> Result { - let sysroot = match &project_json.sysroot_src { - Some(path) => Some(Sysroot::load(path.clone())?), + let sysroot = match project_json.sysroot_src.clone() { + Some(sysroot_src) => { + // if `sysroot` isn't specified (only `sysroot_src`), we won't have + // a real sysroot path, that's fine. it's just used to discover + // the standalone `proc-macro-srv` binary. + let sysroot = project_json.sysroot.clone().unwrap_or_else(|| sysroot_src.clone()); + Some(Sysroot::load(sysroot, sysroot_src)?) + } None => None, }; let rustc_cfg = rustc_cfg::get(None, target); @@ -345,7 +351,7 @@ impl ProjectWorkspace { }) .chain(sysroot.iter().map(|sysroot| PackageRoot { is_local: false, - include: vec![sysroot.root().to_path_buf()], + include: vec![sysroot.src_root().to_path_buf()], exclude: Vec::new(), })) .chain(rustc.iter().flat_map(|rustc| { diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index e5802773e7473..4256c2a764a33 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -305,8 +305,34 @@ impl GlobalState { if self.proc_macro_clients.is_empty() { if let Some((path, args)) = self.config.proc_macro_srv() { - self.proc_macro_clients = (0..self.workspaces.len()) - .map(|_| { + self.proc_macro_clients = self + .workspaces + .iter() + .map(|ws| { + let mut path = path.clone(); + if let ProjectWorkspace::Cargo { sysroot, .. } = ws { + tracing::info!("Found a cargo workspace..."); + if let Some(sysroot) = sysroot.as_ref() { + tracing::info!("Found a cargo workspace with a sysroot..."); + let server_path = sysroot + .root() + .join("libexec") + .join("rust-analyzer-proc-macro-srv"); + if std::fs::metadata(&server_path).is_ok() { + tracing::info!( + "And the server exists at {}", + server_path.display() + ); + path = server_path; + } else { + tracing::info!( + "And the server does not exist at {}", + server_path.display() + ); + } + } + } + ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| { let error = format!( "Failed to run proc_macro_srv from path {}, error: {:?}", From 74a2fad5e6b6193842fe52b50ee174736efb266d Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Mon, 25 Jul 2022 16:22:39 +0200 Subject: [PATCH 24/30] Gate and rename binary, use it if it's in the sysroot --- crates/proc-macro-api/src/process.rs | 1 + crates/proc-macro-srv-cli/Cargo.toml | 2 +- crates/proc-macro-srv-cli/src/main.rs | 12 ++++++++++++ crates/rust-analyzer/src/reload.rs | 8 ++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index ff4c59447d879..c4018d3b39e77 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -86,6 +86,7 @@ fn mk_child( ) -> io::Result { Command::new(path.as_os_str()) .args(args) + .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::inherit()) diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml index badb594f06295..9d0da5dee9c10 100644 --- a/crates/proc-macro-srv-cli/Cargo.toml +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -13,5 +13,5 @@ proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" } sysroot-abi = ["proc-macro-srv/sysroot-abi"] [[bin]] -name = "proc-macro-srv" +name = "rust-analyzer-proc-macro-srv" path = "src/main.rs" diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs index 8398f2ba6cb70..ac9fa9f5a4ce5 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -3,5 +3,17 @@ use proc_macro_srv::cli; fn main() -> std::io::Result<()> { + let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE"); + match v.as_deref() { + Ok("this is unstable") => { + // very well, if you must + } + _ => { + eprintln!("If you're rust-analyzer, you can use this tool by exporting RUST_ANALYZER_INTERNALS_DO_NOT_USE='this is unstable'."); + eprintln!("If not, you probably shouldn't use this tool. But do what you want: I'm an error message, not a cop."); + std::process::exit(122); + } + } + cli::run() } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 4256c2a764a33..9ae361b034e28 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -309,7 +309,9 @@ impl GlobalState { .workspaces .iter() .map(|ws| { + let mut args = args.clone(); let mut path = path.clone(); + if let ProjectWorkspace::Cargo { sysroot, .. } = ws { tracing::info!("Found a cargo workspace..."); if let Some(sysroot) = sysroot.as_ref() { @@ -324,6 +326,7 @@ impl GlobalState { server_path.display() ); path = server_path; + args = vec![]; } else { tracing::info!( "And the server does not exist at {}", @@ -333,6 +336,11 @@ impl GlobalState { } } + tracing::info!( + "Using proc-macro server at {} with args {:?}", + path.display(), + args + ); ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| { let error = format!( "Failed to run proc_macro_srv from path {}, error: {:?}", From 696775153dffaa701bcabe67b5fe601db862c4f4 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Mon, 25 Jul 2022 16:59:10 +0200 Subject: [PATCH 25/30] Fix mix up in comments --- crates/project-model/src/sysroot.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index b17a59b10bb57..362bb0f5e79cd 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -36,19 +36,19 @@ impl ops::Index for Sysroot { } impl Sysroot { - /// Returns sysroot directory, where `bin/`, `etc/`, `lib/`, `libexec/` + /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/` /// subfolder live, like: - /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library` - pub fn src_root(&self) -> &AbsPath { - &self.src_root - } - - /// Returns sysroot "src" directory, where stdlib sources are located, like: /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu` pub fn root(&self) -> &AbsPath { &self.root } + /// Returns the sysroot "source" directory, where stdlib sources are located, like: + /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library` + pub fn src_root(&self) -> &AbsPath { + &self.src_root + } + pub fn public_deps(&self) -> impl Iterator + '_ { // core is added as a dependency before std in order to // mimic rustcs dependency order From 2c2520fbb48b977a805b46af79e4016a4394e719 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Mon, 25 Jul 2022 17:52:38 +0200 Subject: [PATCH 26/30] Allow specifying sysroot OR sysroot_src --- crates/project-model/src/workspace.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 63882466fa416..b144006b44e03 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -230,16 +230,26 @@ impl ProjectWorkspace { project_json: ProjectJson, target: Option<&str>, ) -> Result { - let sysroot = match project_json.sysroot_src.clone() { - Some(sysroot_src) => { - // if `sysroot` isn't specified (only `sysroot_src`), we won't have - // a real sysroot path, that's fine. it's just used to discover - // the standalone `proc-macro-srv` binary. - let sysroot = project_json.sysroot.clone().unwrap_or_else(|| sysroot_src.clone()); + let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) { + (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?), + (Some(sysroot), None) => { + // assume sysroot is structured like rustup's and guess `sysroot_src` + let sysroot_src = + sysroot.join("lib").join("rustlib").join("src").join("rust").join("library"); + Some(Sysroot::load(sysroot, sysroot_src)?) } - None => None, + (None, Some(sysroot_src)) => { + // assume sysroot is structured like rustup's and guess `sysroot` + let mut sysroot = sysroot_src.clone(); + for _ in 0..5 { + sysroot.pop(); + } + Some(Sysroot::load(sysroot, sysroot_src)?) + } + (None, None) => None, }; + let rustc_cfg = rustc_cfg::get(None, target); Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) } From 4e60db2d07b848d92a52d3fd9fa74e6a4f7f097f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 24 Jul 2022 12:04:15 +0200 Subject: [PATCH 27/30] feat: Downmap tokens inside derive helpers --- crates/hir-def/src/item_scope.rs | 35 ++++-- crates/hir-def/src/nameres.rs | 18 ++- crates/hir-def/src/nameres/collector.rs | 14 +-- crates/hir-def/src/resolver.rs | 6 +- crates/hir/src/semantics.rs | 132 ++++++++++++++-------- crates/hir/src/semantics/source_to_def.rs | 2 + crates/ide-db/src/rename.rs | 10 +- crates/ide-db/src/search.rs | 2 + crates/ide/src/hover.rs | 7 +- 9 files changed, 151 insertions(+), 75 deletions(-) diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index b98b2855cb084..579f803ea193a 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -66,10 +66,14 @@ pub struct ItemScope { attr_macros: FxHashMap, MacroCallId>, /// The derive macro invocations in this scope, keyed by the owner item over the actual derive attributes /// paired with the derive macro invocations for the specific attribute. - derive_macros: FxHashMap< - AstId, - SmallVec<[(AttrId, MacroCallId, SmallVec<[Option; 1]>); 1]>, - >, + derive_macros: FxHashMap, SmallVec<[DeriveMacroInvocation; 1]>>, +} + +#[derive(Debug, PartialEq, Eq)] +struct DeriveMacroInvocation { + attr_id: AttrId, + attr_call_id: MacroCallId, + derive_call_ids: SmallVec<[Option; 1]>, } pub(crate) static BUILTIN_SCOPE: Lazy> = Lazy::new(|| { @@ -210,12 +214,14 @@ impl ItemScope { &mut self, adt: AstId, call: MacroCallId, - attr_id: AttrId, + id: AttrId, idx: usize, ) { if let Some(derives) = self.derive_macros.get_mut(&adt) { - if let Some((.., invocs)) = derives.iter_mut().find(|&&mut (id, ..)| id == attr_id) { - invocs[idx] = Some(call); + if let Some(DeriveMacroInvocation { derive_call_ids, .. }) = + derives.iter_mut().find(|&&mut DeriveMacroInvocation { attr_id, .. }| id == attr_id) + { + derive_call_ids[idx] = Some(call); } } } @@ -227,10 +233,14 @@ impl ItemScope { &mut self, adt: AstId, attr_id: AttrId, - call_id: MacroCallId, + attr_call_id: MacroCallId, len: usize, ) { - self.derive_macros.entry(adt).or_default().push((attr_id, call_id, smallvec![None; len])); + self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation { + attr_id, + attr_call_id, + derive_call_ids: smallvec![None; len], + }); } pub(crate) fn derive_macro_invocs( @@ -242,7 +252,12 @@ impl ItemScope { ), > + '_ { self.derive_macros.iter().map(|(k, v)| { - (*k, v.iter().map(|&(attr_id, call_id, ref invocs)| (attr_id, call_id, &**invocs))) + ( + *k, + v.iter().map(|DeriveMacroInvocation { attr_id, attr_call_id, derive_call_ids }| { + (*attr_id, *attr_call_id, &**derive_call_ids) + }), + ) }) } diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 3949fbb6e7b03..8165055e09249 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -57,10 +57,10 @@ mod proc_macro; #[cfg(test)] mod tests; -use std::{cmp::Ord, sync::Arc}; +use std::{ops::Deref, sync::Arc}; use base_db::{CrateId, Edition, FileId}; -use hir_expand::{name::Name, InFile, MacroDefId}; +use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId}; use itertools::Itertools; use la_arena::Arena; use profile::Count; @@ -106,6 +106,9 @@ pub struct DefMap { fn_proc_macro_mapping: FxHashMap, /// The error that occurred when failing to load the proc-macro dll. proc_macro_loading_error: Option>, + /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper + /// attributes. + derive_helpers_in_scope: FxHashMap, Vec<(Name, MacroCallId)>>, /// Custom attributes registered with `#![register_attr]`. registered_attrs: Vec, @@ -275,6 +278,7 @@ impl DefMap { exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), proc_macro_loading_error: None, + derive_helpers_in_scope: FxHashMap::default(), prelude: None, root, modules, @@ -294,12 +298,19 @@ impl DefMap { pub fn modules(&self) -> impl Iterator + '_ { self.modules.iter() } + + pub fn derive_helpers_in_scope(&self, id: AstId) -> Option<&[(Name, MacroCallId)]> { + self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref) + } + pub fn registered_tools(&self) -> &[SmolStr] { &self.registered_tools } + pub fn registered_attrs(&self) -> &[SmolStr] { &self.registered_attrs } + pub fn root(&self) -> LocalModuleId { self.root } @@ -307,6 +318,7 @@ impl DefMap { pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option { self.fn_proc_macro_mapping.get(&id).copied() } + pub fn proc_macro_loading_error(&self) -> Option<&str> { self.proc_macro_loading_error.as_deref() } @@ -467,6 +479,7 @@ impl DefMap { registered_attrs, registered_tools, fn_proc_macro_mapping, + derive_helpers_in_scope, proc_macro_loading_error: _, block: _, edition: _, @@ -483,6 +496,7 @@ impl DefMap { registered_attrs.shrink_to_fit(); registered_tools.shrink_to_fit(); fn_proc_macro_mapping.shrink_to_fit(); + derive_helpers_in_scope.shrink_to_fit(); for (_, module) in modules.iter_mut() { module.children.shrink_to_fit(); module.scope.shrink_to_fit(); diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 67651e06413ca..e14d29952dd79 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -110,7 +110,6 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T proc_macros, from_glob_import: Default::default(), skip_attrs: Default::default(), - derive_helpers_in_scope: Default::default(), is_proc_macro, }; if tree_id.is_block() { @@ -258,9 +257,6 @@ struct DefCollector<'a> { /// This also stores the attributes to skip when we resolve derive helpers and non-macro /// non-builtin attributes in general. skip_attrs: FxHashMap, AttrId>, - /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper - /// attributes. - derive_helpers_in_scope: FxHashMap, Vec>, } impl DefCollector<'_> { @@ -1132,8 +1128,8 @@ impl DefCollector<'_> { }; if let Some(ident) = path.as_ident() { - if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id) { - if helpers.contains(ident) { + if let Some(helpers) = self.def_map.derive_helpers_in_scope.get(&ast_id) { + if helpers.iter().any(|(it, _)| it == ident) { cov_mark::hit!(resolved_derive_helper); // Resolved to derive helper. Collect the item's attributes again, // starting after the derive helper. @@ -1322,10 +1318,11 @@ impl DefCollector<'_> { if loc.def.krate != self.def_map.krate { let def_map = self.db.crate_def_map(loc.def.krate); if let Some(helpers) = def_map.exported_derives.get(&loc.def) { - self.derive_helpers_in_scope + self.def_map + .derive_helpers_in_scope .entry(ast_id.map(|it| it.upcast())) .or_default() - .extend(helpers.iter().cloned()); + .extend(helpers.iter().cloned().zip(std::iter::repeat(macro_call_id))); } } } @@ -2140,7 +2137,6 @@ mod tests { proc_macros: Default::default(), from_glob_import: Default::default(), skip_attrs: Default::default(), - derive_helpers_in_scope: Default::default(), is_proc_macro: false, }; collector.seed_with_top_level(); diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index c8d3052102f45..0c1ae8a064a3d 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -448,10 +448,14 @@ impl Resolver { } pub fn krate(&self) -> CrateId { + self.def_map().krate() + } + + pub fn def_map(&self) -> &DefMap { self.scopes .get(0) .and_then(|scope| match scope { - Scope::ModuleScope(m) => Some(m.def_map.krate()), + Scope::ModuleScope(m) => Some(&m.def_map), _ => None, }) .expect("module scope invariant violated") diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 043f2b7c24dcb..218e15989ad33 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -733,6 +733,8 @@ impl<'db> SemanticsImpl<'db> { Some(it) => it, None => return, }; + let def_map = sa.resolver.def_map(); + let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)]; let mut cache = self.expansion_info_cache.borrow_mut(); let mut mcache = self.macro_call_cache.borrow_mut(); @@ -764,7 +766,7 @@ impl<'db> SemanticsImpl<'db> { while let Some(token) = stack.pop() { self.db.unwind_if_cancelled(); let was_not_remapped = (|| { - // are we inside an attribute macro call + // 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() { @@ -784,53 +786,19 @@ impl<'db> SemanticsImpl<'db> { ); } - // or are we inside a function-like macro call - if let Some(tt) = - // FIXME replace map.while_some with take_while once stable - token - .value - .parent_ancestors() - .map(ast::TokenTree::cast) - .while_some() - .last() - { - let parent = tt.syntax().parent()?; - // check for derive attribute here - let macro_call = match_ast! { - match parent { - ast::MacroCall(mcall) => mcall, - // attribute we failed expansion for earlier, this might be a derive invocation - // so try downmapping the token into the pseudo derive expansion - // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works - ast::Meta(meta) => { - let attr = meta.parent_attr()?; - let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; - let call_id = self.with_ctx(|ctx| { - let (_, call_id, _) = ctx.attr_to_derive_macro_call( - token.with_value(&adt), - token.with_value(attr), - )?; - Some(call_id) - })?; - let file_id = call_id.as_file(); - return process_expansion_for_token( - &mut stack, - file_id, - Some(adt.into()), - token.as_ref(), - ); - }, - _ => return None, - } - }; + // 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()?; - 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.value) { + return None; + } + if tt.right_delimiter_token().map_or(false, |it| it == token.value) { + return None; + } + if let Some(macro_call) = ast::MacroCall::cast(parent.clone()) { let mcall = token.with_value(macro_call); let file_id = match mcache.get(&mcall) { Some(&it) => it, @@ -840,11 +808,75 @@ impl<'db> SemanticsImpl<'db> { it } }; - return process_expansion_for_token(&mut stack, file_id, None, token.as_ref()); + process_expansion_for_token(&mut stack, file_id, None, token.as_ref()) + } else if let Some(meta) = ast::Meta::cast(parent.clone()) { + // 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_file(); + return process_expansion_for_token( + &mut stack, + file_id, + Some(adt.into()), + token.as_ref(), + ); + } + 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) + { + variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast) + } else { + 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 item = Some(adt.into()); + 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_file(), + item.clone(), + token.as_ref(), + )); + } + res + } else { + None } - - // outside of a macro invocation so this is a "final" token - None })() .is_none(); diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 5c4cfa7b45a53..ba9a1cfb6b51c 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -247,6 +247,7 @@ impl SourceToDefCtx<'_, '_> { map[keys::ATTR_MACRO_CALL].get(&src.value).copied() } + /// (AttrId, derive attribute call id, derive call ids) pub(super) fn attr_to_derive_macro_call( &mut self, item: InFile<&ast::Adt>, @@ -257,6 +258,7 @@ impl SourceToDefCtx<'_, '_> { .get(&src.value) .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids)) } + pub(super) fn has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool { self.dyn_map(adt).as_ref().map_or(false, |map| !map[keys::DERIVE_MACRO_CALL].is_empty()) } diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index bb466e43e7521..dc1626f439673 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -316,14 +316,20 @@ 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(); + 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 + continue; + } let has_emitted_edit = match name { // 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_ref.syntax().text_range() == range => { + ast::NameLike::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.syntax().text_range() == range => { + ast::NameLike::Name(name) if name_range == range => { source_edit_from_name(&mut edit, name, new_name) } _ => false, diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index c75364084e36a..eb4fc36438120 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -54,7 +54,9 @@ impl IntoIterator for UsageSearchResult { #[derive(Debug, Clone)] 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 category: Option, } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index d8867cf783ad8..59c97f2dcf966 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -115,7 +115,12 @@ pub(crate) fn hover( }); } - let descended = sema.descend_into_macros_with_same_text(original_token.clone()); + let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))); + let descended = if in_attr { + [sema.descend_into_macros_with_kind_preference(original_token.clone())].into() + } else { + sema.descend_into_macros_with_same_text(original_token.clone()) + }; // FIXME: Definition should include known lints and the like instead of having this special case here let hovered_lint = descended.iter().find_map(|token| { From aa1491ecde22732329069773fed3e23f6b42e14d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 24 Jul 2022 14:05:37 +0200 Subject: [PATCH 28/30] Record derive helper attributes, resolve them in IDE layer --- crates/hir-def/src/lib.rs | 10 +-- crates/hir-def/src/nameres.rs | 9 ++- crates/hir-def/src/nameres/collector.rs | 50 ++++++++------- crates/hir-def/src/resolver.rs | 1 + crates/hir/src/lib.rs | 11 ++++ crates/hir/src/semantics.rs | 14 ++-- crates/hir/src/source_analyzer.rs | 64 +++++++++++++++---- crates/ide-completion/src/item.rs | 1 + crates/ide-db/src/defs.rs | 13 ++-- crates/ide-db/src/lib.rs | 1 + crates/ide-db/src/path_transform.rs | 3 +- crates/ide-db/src/rename.rs | 2 + crates/ide/src/doc_links.rs | 6 +- crates/ide/src/hover/render.rs | 2 + crates/ide/src/navigation_target.rs | 2 + crates/ide/src/signature_help.rs | 3 +- crates/ide/src/syntax_highlighting.rs | 1 + .../ide/src/syntax_highlighting/highlight.rs | 1 + crates/ide/src/syntax_highlighting/inject.rs | 1 + crates/ide/src/syntax_highlighting/tags.rs | 1 + crates/rust-analyzer/src/semantic_tokens.rs | 1 + crates/rust-analyzer/src/to_proto.rs | 5 +- 22 files changed, 144 insertions(+), 58 deletions(-) diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 0dd0a5861ef3c..56603f4b15456 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -934,11 +934,11 @@ fn derive_macro_as_call_id( derive_attr: AttrId, derive_pos: u32, krate: CrateId, - resolver: impl Fn(path::ModPath) -> Option, -) -> Result { - let def: MacroDefId = resolver(item_attr.path.clone()) + resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, +) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { + let (macro_id, def_id) = resolver(item_attr.path.clone()) .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; - let res = def.as_lazy_macro( + let call_id = def_id.as_lazy_macro( db.upcast(), krate, MacroCallKind::Derive { @@ -947,7 +947,7 @@ fn derive_macro_as_call_id( derive_attr_index: derive_attr.ast_index, }, ); - Ok(res) + Ok((macro_id, def_id, call_id)) } fn attr_macro_as_call_id( diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 8165055e09249..25fb302e87587 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -76,7 +76,7 @@ use crate::{ path::ModPath, per_ns::PerNs, visibility::Visibility, - AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, ModuleId, ProcMacroId, + AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId, }; /// Contains the results of (early) name resolution. @@ -108,7 +108,7 @@ pub struct DefMap { proc_macro_loading_error: Option>, /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper /// attributes. - derive_helpers_in_scope: FxHashMap, Vec<(Name, MacroCallId)>>, + derive_helpers_in_scope: FxHashMap, Vec<(Name, MacroId, MacroCallId)>>, /// Custom attributes registered with `#![register_attr]`. registered_attrs: Vec, @@ -299,7 +299,10 @@ impl DefMap { self.modules.iter() } - pub fn derive_helpers_in_scope(&self, id: AstId) -> Option<&[(Name, MacroCallId)]> { + pub fn derive_helpers_in_scope( + &self, + id: AstId, + ) -> Option<&[(Name, MacroId, MacroCallId)]> { self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref) } diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index e14d29952dd79..f394c541719f3 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -18,7 +18,7 @@ use hir_expand::{ ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, }; -use itertools::Itertools; +use itertools::{izip, Itertools}; use la_arena::Idx; use limit::Limit; use rustc_hash::{FxHashMap, FxHashSet}; @@ -1055,7 +1055,7 @@ impl DefCollector<'_> { }; let mut res = ReachedFixedPoint::Yes; macros.retain(|directive| { - let resolver = |path| { + let resolver2 = |path| { let resolved_res = self.def_map.resolve_path_fp_with_macro( self.db, ResolveMode::Other, @@ -1063,8 +1063,12 @@ impl DefCollector<'_> { &path, BuiltinShadowMode::Module, ); - resolved_res.resolved_def.take_macros().map(|it| macro_id_to_def_id(self.db, it)) + resolved_res + .resolved_def + .take_macros() + .map(|it| (it, macro_id_to_def_id(self.db, it))) }; + let resolver = |path| resolver2(path).map(|(_, it)| it); match &directive.kind { MacroDirectiveKind::FnLike { ast_id, expand_to } => { @@ -1083,21 +1087,37 @@ impl DefCollector<'_> { } } MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos } => { - let call_id = derive_macro_as_call_id( + let id = derive_macro_as_call_id( self.db, ast_id, *derive_attr, *derive_pos as u32, self.def_map.krate, - &resolver, + &resolver2, ); - if let Ok(call_id) = call_id { + + if let Ok((macro_id, def_id, call_id)) = id { self.def_map.modules[directive.module_id].scope.set_derive_macro_invoc( ast_id.ast_id, call_id, *derive_attr, *derive_pos, ); + // Record its helper attributes. + if def_id.krate != self.def_map.krate { + let def_map = self.db.crate_def_map(def_id.krate); + if let Some(helpers) = def_map.exported_derives.get(&def_id) { + self.def_map + .derive_helpers_in_scope + .entry(ast_id.ast_id.map(|it| it.upcast())) + .or_default() + .extend(izip!( + helpers.iter().cloned(), + iter::repeat(macro_id), + iter::repeat(call_id), + )); + } + } push_resolved(directive, call_id); res = ReachedFixedPoint::No; @@ -1129,7 +1149,7 @@ impl DefCollector<'_> { if let Some(ident) = path.as_ident() { if let Some(helpers) = self.def_map.derive_helpers_in_scope.get(&ast_id) { - if helpers.iter().any(|(it, _)| it == ident) { + if helpers.iter().any(|(it, ..)| it == ident) { cov_mark::hit!(resolved_derive_helper); // Resolved to derive helper. Collect the item's attributes again, // starting after the derive helper. @@ -1144,7 +1164,7 @@ impl DefCollector<'_> { }; if matches!( def, - MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. } + MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. } if expander.is_derive() ) { // Resolved to `#[derive]` @@ -1313,20 +1333,6 @@ impl DefCollector<'_> { self.def_map.diagnostics.push(diag); } - // If we've just resolved a derive, record its helper attributes. - if let MacroCallKind::Derive { ast_id, .. } = &loc.kind { - if loc.def.krate != self.def_map.krate { - let def_map = self.db.crate_def_map(loc.def.krate); - if let Some(helpers) = def_map.exported_derives.get(&loc.def) { - self.def_map - .derive_helpers_in_scope - .entry(ast_id.map(|it| it.upcast())) - .or_default() - .extend(helpers.iter().cloned().zip(std::iter::repeat(macro_call_id))); - } - } - } - // Then, fetch and process the item tree. This will reuse the expansion result from above. let item_tree = self.db.file_item_tree(file_id); let mod_dir = self.mod_dirs[&module_id].clone(); diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 0c1ae8a064a3d..3163fa0f93fa5 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -149,6 +149,7 @@ impl Resolver { self.resolve_module_path(db, path, BuiltinShadowMode::Module) } + // FIXME: This shouldn't exist pub fn resolve_module_path_in_trait_assoc_items( &self, db: &dyn DefDatabase, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 9ffbb3964cf11..86b5bd3c2c81f 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2252,6 +2252,17 @@ impl Local { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct DeriveHelper { + pub(crate) derive: MacroId, +} + +impl DeriveHelper { + pub fn derive(&self) -> Macro { + Macro { id: self.derive.into() } + } +} + // FIXME: Wrong name? This is could also be a registered attribute #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BuiltinAttr { diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 218e15989ad33..fc8f23f19ab91 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -29,9 +29,9 @@ use crate::{ db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{resolve_hir_path, SourceAnalyzer}, - Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource, - HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path, - ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef, + Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function, + HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, + Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -47,6 +47,7 @@ pub enum PathResolution { SelfType(Impl), BuiltinAttr(BuiltinAttr), ToolModule(ToolModule), + DeriveHelper(DeriveHelper), } impl PathResolution { @@ -71,6 +72,7 @@ impl PathResolution { PathResolution::BuiltinAttr(_) | PathResolution::ToolModule(_) | PathResolution::Local(_) + | PathResolution::DeriveHelper(_) | PathResolution::ConstParam(_) => None, PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), @@ -856,7 +858,9 @@ impl<'db> SemanticsImpl<'db> { 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(); @@ -865,7 +869,7 @@ impl<'db> SemanticsImpl<'db> { def_map.derive_helpers_in_scope(InFile::new(token.file_id, id))?; let item = Some(adt.into()); let mut res = None; - for (_, derive) in helpers.iter().filter(|(helper, _)| *helper == attr_name) { + for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) { res = res.or(process_expansion_for_token( &mut stack, derive.as_file(), diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index e89f8a542981c..d57a73ade3a64 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -43,8 +43,8 @@ use syntax::{ use crate::{ db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr, - BuiltinType, Callable, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct, - ToolModule, Trait, Type, TypeAlias, Variant, + BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static, + Struct, ToolModule, Trait, Type, TypeAlias, Variant, }; /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of @@ -429,19 +429,21 @@ impl SourceAnalyzer { } } - let is_path_of_attr = path + let meta_path = path .syntax() .ancestors() - .map(|it| it.kind()) - .take_while(|&kind| ast::Path::can_cast(kind) || ast::Meta::can_cast(kind)) + .take_while(|it| { + let kind = it.kind(); + ast::Path::can_cast(kind) || ast::Meta::can_cast(kind) + }) .last() - .map_or(false, ast::Meta::can_cast); + .and_then(ast::Meta::cast); // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are // trying to resolve foo::bar. if path.parent_path().is_some() { return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path) { - None if is_path_of_attr => { + None if meta_path.is_some() => { path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| { ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) .map(PathResolution::ToolModule) @@ -449,16 +451,50 @@ impl SourceAnalyzer { } res => res, }; - } else if is_path_of_attr { + } else if let Some(meta_path) = meta_path { // Case where we are resolving the final path segment of a path in an attribute // in this case we have to check for inert/builtin attributes and tools and prioritize // resolution of attributes over other namespaces - let name_ref = path.as_single_name_ref(); - let builtin = name_ref.as_ref().and_then(|name_ref| { - BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text()) - }); - if let Some(_) = builtin { - return builtin.map(PathResolution::BuiltinAttr); + if let Some(name_ref) = path.as_single_name_ref() { + let builtin = + BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text()); + if let Some(_) = builtin { + return builtin.map(PathResolution::BuiltinAttr); + } + + if let Some(attr) = meta_path.parent_attr() { + let adt = 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 let Some(adt) = adt { + let ast_id = db.ast_id_map(self.file_id).ast_id(&adt); + if let Some(helpers) = self + .resolver + .def_map() + .derive_helpers_in_scope(InFile::new(self.file_id, ast_id)) + { + // FIXME: Multiple derives can have the same helper + let name_ref = name_ref.as_name(); + if let Some(&(_, derive, _)) = + helpers.iter().find(|(name, ..)| *name == name_ref) + { + return Some(PathResolution::DeriveHelper(DeriveHelper { derive })); + } + } + } + } } return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) { Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))), diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index 27482ea489be7..27c3ccb35a1ea 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -292,6 +292,7 @@ impl CompletionItemKind { SymbolKind::Const => "ct", SymbolKind::ConstParam => "cp", SymbolKind::Derive => "de", + SymbolKind::DeriveHelper => "dh", SymbolKind::Enum => "en", SymbolKind::Field => "fd", SymbolKind::Function => "fn", diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 692a315720308..a9a78e6729049 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -7,9 +7,9 @@ use arrayvec::ArrayVec; use hir::{ - Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, Field, Function, - GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef, Name, - PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility, + Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field, + Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef, + Name, PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility, }; use stdx::impl_from; use syntax::{ @@ -37,6 +37,7 @@ pub enum Definition { Local(Local), GenericParam(GenericParam), Label(Label), + DeriveHelper(DeriveHelper), BuiltinAttr(BuiltinAttr), ToolModule(ToolModule), } @@ -69,6 +70,7 @@ impl Definition { Definition::Local(it) => it.module(db), Definition::GenericParam(it) => it.module(db), Definition::Label(it) => it.module(db), + Definition::DeriveHelper(it) => it.derive().module(db), Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => { return None } @@ -94,7 +96,8 @@ impl Definition { | Definition::SelfType(_) | Definition::Local(_) | Definition::GenericParam(_) - | Definition::Label(_) => return None, + | Definition::Label(_) + | Definition::DeriveHelper(_) => return None, }; Some(vis) } @@ -118,6 +121,7 @@ impl Definition { Definition::Label(it) => it.name(db), Definition::BuiltinAttr(_) => return None, // FIXME Definition::ToolModule(_) => return None, // FIXME + Definition::DeriveHelper(_) => return None, // FIXME }; Some(name) } @@ -500,6 +504,7 @@ impl From for Definition { PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr), PathResolution::ToolModule(tool) => Definition::ToolModule(tool), + PathResolution::DeriveHelper(helper) => Definition::DeriveHelper(helper), } } } diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index 26648b4d7638c..966bba616f627 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -173,6 +173,7 @@ pub enum SymbolKind { Const, ConstParam, Derive, + DeriveHelper, Enum, Field, Function, diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index d78b8758d6512..40af9e6fe2ad8 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -224,7 +224,8 @@ impl<'a> Ctx<'a> { | hir::PathResolution::SelfType(_) | hir::PathResolution::Def(_) | hir::PathResolution::BuiltinAttr(_) - | hir::PathResolution::ToolModule(_) => (), + | hir::PathResolution::ToolModule(_) + | hir::PathResolution::DeriveHelper(_) => (), } Some(()) } diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index dc1626f439673..517fe3f246d08 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -156,6 +156,8 @@ impl Definition { Definition::SelfType(_) => return None, Definition::BuiltinAttr(_) => return None, Definition::ToolModule(_) => return None, + // FIXME: This should be doable in theory + Definition::DeriveHelper(_) => return None, }; return res; diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index fed327f52be52..582e9fe7e808c 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -190,7 +190,8 @@ pub(crate) fn resolve_doc_path_for_def( | Definition::SelfType(_) | Definition::Local(_) | Definition::GenericParam(_) - | Definition::Label(_) => None, + | Definition::Label(_) + | Definition::DeriveHelper(_) => None, } .map(Definition::from) } @@ -515,7 +516,8 @@ fn filename_and_frag_for_def( | Definition::GenericParam(_) | Definition::Label(_) | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) => return None, + | Definition::ToolModule(_) + | Definition::DeriveHelper(_) => return None, }; Some((def, res, None)) diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 925aaa61cdd04..fcdb23fa18aae 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -370,6 +370,8 @@ pub(super) fn definition( // FIXME: We should be able to show more info about these Definition::BuiltinAttr(it) => return render_builtin_attr(db, it), Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))), + // FIXME: it.name(db) + Definition::DeriveHelper(_it) => ("derive-helper".to_owned(), None), }; let docs = match config.documentation { diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 1fb3b5ec3f38b..9f049e298ad11 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -196,6 +196,8 @@ impl TryToNav for Definition { Definition::BuiltinType(_) => None, Definition::ToolModule(_) => None, Definition::BuiltinAttr(_) => None, + // FIXME: The focus range should be set to the helper declaration + Definition::DeriveHelper(it) => it.derive().try_to_nav(db), } } } diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index ba287d13aec1d..fedc1a4358272 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -237,7 +237,8 @@ fn signature_help_for_generics( | hir::PathResolution::Local(_) | hir::PathResolution::TypeParam(_) | hir::PathResolution::ConstParam(_) - | hir::PathResolution::SelfType(_) => return None, + | hir::PathResolution::SelfType(_) + | hir::PathResolution::DeriveHelper(_) => return None, }; generic_def diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index d7ad6a7579953..eef717c16f2ef 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -107,6 +107,7 @@ pub struct HlRange { // builtinType:: Emitted for builtin types like `u32`, `str` and `f32`. // comment:: Emitted for comments. // constParameter:: Emitted for const parameters. +// deriveHelper:: Emitted for derive helper attributes. // enumMember:: Emitted for enum variants. // generic:: Emitted for generic tokens that have no mapping. // keyword:: Emitted for keywords. diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index fd3723ed45485..9395e914c43aa 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -472,6 +472,7 @@ fn highlight_def( Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)), Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)), Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)), + Definition::DeriveHelper(_) => Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)), }; let def_crate = def.krate(db); diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 94d573a30b041..f779a985a99ae 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -270,6 +270,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::Label(_) => SymbolKind::Label, Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr, Definition::ToolModule(_) => SymbolKind::ToolModule, + Definition::DeriveHelper(_) => SymbolKind::DeriveHelper, }; HlTag::Symbol(symbol) } diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index b900dadcfa3c9..5262770f30317 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -134,6 +134,7 @@ impl HlTag { SymbolKind::Const => "constant", SymbolKind::ConstParam => "const_param", SymbolKind::Derive => "derive", + SymbolKind::DeriveHelper => "derive_helper", SymbolKind::Enum => "enum", SymbolKind::Field => "field", SymbolKind::Function => "function", diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index 5fb945ea98727..6c78b5df1a705 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs @@ -54,6 +54,7 @@ define_semantic_token_types![ (COMPARISON, "comparison"), (CONST_PARAMETER, "constParameter"), (DERIVE, "derive"), + (DERIVE_HELPER, "deriveHelper"), (DOT, "dot"), (ESCAPE_SEQUENCE, "escapeSequence"), (FORMAT_SPECIFIER, "formatSpecifier"), diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 9c8e618b6ed05..7f4fa57fa1e09 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -53,7 +53,8 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { SymbolKind::Macro | SymbolKind::BuiltinAttr | SymbolKind::Attribute - | SymbolKind::Derive => lsp_types::SymbolKind::FUNCTION, + | SymbolKind::Derive + | SymbolKind::DeriveHelper => lsp_types::SymbolKind::FUNCTION, SymbolKind::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE, SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => { lsp_types::SymbolKind::TYPE_PARAMETER @@ -117,6 +118,7 @@ pub(crate) fn completion_item_kind( SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT, SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION, + SymbolKind::DeriveHelper => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM, SymbolKind::Field => lsp_types::CompletionItemKind::FIELD, SymbolKind::Function => lsp_types::CompletionItemKind::FUNCTION, @@ -561,6 +563,7 @@ fn semantic_token_type_and_modifiers( HlTag::Symbol(symbol) => match symbol { SymbolKind::Attribute => semantic_tokens::ATTRIBUTE, SymbolKind::Derive => semantic_tokens::DERIVE, + SymbolKind::DeriveHelper => semantic_tokens::DERIVE_HELPER, SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE, SymbolKind::Impl => semantic_tokens::TYPE_ALIAS, SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, From ddad2847ab9a59d4b2fc7afb976a261c8c914686 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 24 Jul 2022 14:32:39 +0200 Subject: [PATCH 29/30] Allow name querying for derive helpers --- crates/hir-def/src/data.rs | 19 +++++++++++++------ crates/hir-def/src/nameres.rs | 4 ++-- crates/hir/src/lib.rs | 15 +++++++++++++++ crates/hir/src/source_analyzer.rs | 13 ++++++++++--- crates/ide-db/src/defs.rs | 2 +- crates/ide-db/src/search.rs | 8 ++++---- crates/ide/src/hover/render.rs | 3 +-- crates/ide/src/syntax_highlighting.rs | 7 +++++++ 8 files changed, 53 insertions(+), 18 deletions(-) diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 4309411419334..35c8708955a77 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -12,7 +12,7 @@ use crate::{ db::DefDatabase, intern::Interned, item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId}, - nameres::{attr_resolution::ResolvedAttr, DefMap}, + nameres::{attr_resolution::ResolvedAttr, proc_macro::ProcMacroKind, DefMap}, type_ref::{TraitRef, TypeBound, TypeRef}, visibility::RawVisibility, AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, @@ -348,7 +348,8 @@ impl MacroRulesData { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProcMacroData { pub name: Name, - // FIXME: Record deriver helper here? + /// Derive helpers, if this is a derive + pub helpers: Option>, } impl ProcMacroData { @@ -360,17 +361,23 @@ impl ProcMacroData { let item_tree = loc.id.item_tree(db); let makro = &item_tree[loc.id.value]; - let name = if let Some(def) = item_tree + let (name, helpers) = if let Some(def) = item_tree .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) .parse_proc_macro_decl(&makro.name) { - def.name + ( + def.name, + match def.kind { + ProcMacroKind::CustomDerive { helpers } => Some(helpers), + ProcMacroKind::FnLike | ProcMacroKind::Attr => None, + }, + ) } else { // eeeh... stdx::never!("proc macro declaration is not a proc macro"); - makro.name.clone() + (makro.name.clone(), None) }; - Arc::new(ProcMacroData { name }) + Arc::new(ProcMacroData { name, helpers }) } } diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 25fb302e87587..45f631936d2ab 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -48,11 +48,11 @@ //! the result pub mod attr_resolution; -mod collector; +pub mod proc_macro; pub mod diagnostics; +mod collector; mod mod_resolution; mod path_resolution; -mod proc_macro; #[cfg(test)] mod tests; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 86b5bd3c2c81f..d4925455d7bd2 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2255,12 +2255,27 @@ impl Local { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct DeriveHelper { pub(crate) derive: MacroId, + pub(crate) idx: usize, } impl DeriveHelper { pub fn derive(&self) -> Macro { Macro { id: self.derive.into() } } + + pub fn name(&self, db: &dyn HirDatabase) -> Name { + match self.derive { + MacroId::Macro2Id(_) => None, + MacroId::MacroRulesId(_) => None, + MacroId::ProcMacroId(proc_macro) => db + .proc_macro_data(proc_macro) + .helpers + .as_ref() + .and_then(|it| it.get(self.idx)) + .cloned(), + } + .unwrap_or_else(|| Name::missing()) + } } // FIXME: Wrong name? This is could also be a registered attribute diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index d57a73ade3a64..1eb51b20c356b 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -35,6 +35,7 @@ use hir_ty::{ method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt, TyKind, TyLoweringContext, }; +use itertools::Itertools; use smallvec::SmallVec; use syntax::{ ast::{self, AstNode}, @@ -487,10 +488,16 @@ impl SourceAnalyzer { { // FIXME: Multiple derives can have the same helper let name_ref = name_ref.as_name(); - if let Some(&(_, derive, _)) = - helpers.iter().find(|(name, ..)| *name == name_ref) + for (macro_id, mut helpers) in + helpers.iter().group_by(|(_, macro_id, ..)| macro_id).into_iter() { - return Some(PathResolution::DeriveHelper(DeriveHelper { derive })); + if let Some(idx) = helpers.position(|(name, ..)| *name == name_ref) + { + return Some(PathResolution::DeriveHelper(DeriveHelper { + derive: *macro_id, + idx, + })); + } } } } diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index a9a78e6729049..aeaca00ec65cc 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -121,7 +121,7 @@ impl Definition { Definition::Label(it) => it.name(db), Definition::BuiltinAttr(_) => return None, // FIXME Definition::ToolModule(_) => return None, // FIXME - Definition::DeriveHelper(_) => return None, // FIXME + Definition::DeriveHelper(it) => it.name(db), }; Some(name) } diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index eb4fc36438120..bd038cdaa0682 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -278,16 +278,16 @@ impl Definition { } } hir::MacroKind::BuiltIn => SearchScope::crate_graph(db), - // FIXME: We don't actually see derives in derive attributes as these do not - // expand to something that references the derive macro in the output. - // We could get around this by doing pseudo expansions for proc_macro_derive like we - // do for the derive attribute hir::MacroKind::Derive | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => { SearchScope::reverse_dependencies(db, module.krate()) } }; } + if let Definition::DeriveHelper(_) = self { + return SearchScope::reverse_dependencies(db, module.krate()); + } + let vis = self.visibility(db); if let Some(Visibility::Public) = vis { return SearchScope::reverse_dependencies(db, module.krate()); diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index fcdb23fa18aae..6c50a4e6adc0e 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -370,8 +370,7 @@ pub(super) fn definition( // FIXME: We should be able to show more info about these Definition::BuiltinAttr(it) => return render_builtin_attr(db, it), Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))), - // FIXME: it.name(db) - Definition::DeriveHelper(_it) => ("derive-helper".to_owned(), None), + Definition::DeriveHelper(it) => (format!("derive_helper {}", it.name(db)), None), }; let docs = match config.documentation { diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index eef717c16f2ef..d013d6f4b19ff 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -432,6 +432,13 @@ fn traverse( // let the editor do its highlighting for these tokens instead continue; } + if highlight.tag == HlTag::UnresolvedReference + && matches!(attr_or_derive_item, Some(AttrOrDerive::Derive(_)) if inside_attribute) + { + // do not emit unresolved references in derive helpers if the token mapping maps to + // something unresolvable. FIXME: There should be a way to prevent that + continue; + } if inside_attribute { highlight |= HlMod::Attribute } From 6ea7d82dcc83d71753189b5f4ce676a31a0e88ab Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Tue, 26 Jul 2022 16:25:48 +0200 Subject: [PATCH 30/30] Ship `rust-analyzer-proc-macro-srv` binary with dist::Rustc This builds `src/tools/rust-analyzer/crates/proc-macro-srv-cli` and ships it as part of Rustc's dist component. This allows rust-analyzer's proc macro support to work on all rustc versions (stable, beta and nightly) starting now. --- src/bootstrap/builder.rs | 1 + src/bootstrap/dist.rs | 12 +++++++++++ src/bootstrap/tool.rs | 44 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 8876f70c8c61c..0ab4824ac0a73 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -601,6 +601,7 @@ impl<'a> Builder<'a> { tool::Cargo, tool::Rls, tool::RustAnalyzer, + tool::RustAnalyzerProcMacroSrv, tool::RustDemangler, tool::Rustdoc, tool::Clippy, diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 95655c0ee3598..6291b204e485f 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -362,6 +362,18 @@ impl Step for Rustc { builder.install(&builder.rustdoc(compiler), &image.join("bin"), 0o755); + let ra_proc_macro_srv = builder + .ensure(tool::RustAnalyzerProcMacroSrv { + compiler: builder.compiler_for( + compiler.stage, + builder.config.build, + compiler.host, + ), + target: compiler.host, + }) + .expect("rust-analyzer-proc-macro-server always builds"); + builder.install(&ra_proc_macro_srv, &image.join("libexec"), 0o755); + let libdir_relative = builder.libdir_relative(compiler); // Copy runtime DLLs needed by the compiler diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 7c37621d417c9..839a6d27199f1 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -727,6 +727,50 @@ impl Step for RustAnalyzer { } } +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct RustAnalyzerProcMacroSrv { + pub compiler: Compiler, + pub target: TargetSelection, +} + +impl Step for RustAnalyzerProcMacroSrv { + type Output = Option; + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + let builder = run.builder; + run.path("src/tools/rust-analyzer").default_condition( + builder.config.extended + && builder + .config + .tools + .as_ref() + .map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")), + ) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(RustAnalyzerProcMacroSrv { + compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) -> Option { + builder.ensure(ToolBuild { + compiler: self.compiler, + target: self.target, + tool: "rust-analyzer-proc-macro-srv", + mode: Mode::ToolStd, + path: "src/tools/rust-analyzer/crates/proc-macro-srv-cli", + extra_features: vec!["proc-macro-srv/sysroot-abi".to_owned()], + is_optional_tool: false, + source_type: SourceType::InTree, + }) + } +} + macro_rules! tool_extended { (($sel:ident, $builder:ident), $($name:ident,